| author | |
| committer | |
| log | ac630d354d51488d895bc14e26a069ae954ac5c6 |
| tree | ff99cc11334991a76d7ab0837112f00b8b1b0c3c |
| parent | a10277bd949d47370e427d4457d93e907de9a6f7 |
add explicit casting support from array to string8 files changed, 125 insertions(+), 52 deletions(-)
example/hello_world/hello.zig+5-8| ... | ... | @@ -1,12 +1,9 @@ |
| 1 | 1 | export executable "hello"; |
| 2 | 2 | |
| 3 | #link("c") | |
| 4 | extern { | |
| 5 | fn printf(__format: *const u8, ...) -> i32; | |
| 6 | fn exit(__status: i32) -> unreachable; | |
| 7 | } | |
| 3 | use "std.zig"; | |
| 8 | 4 | |
| 9 | export fn _start() -> unreachable { | |
| 10 | printf("Hello, world!\n"); | |
| 11 | exit(0); | |
| 5 | export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 { | |
| 6 | // TODO implicit coercion from array to string | |
| 7 | print_str("Hello, world!\n" as string); | |
| 8 | return 0; | |
| 12 | 9 | } |
example/hello_world/hello2.zig deleted-8| ... | ... | @@ -1,8 +0,0 @@ |
| 1 | export executable "hello"; | |
| 2 | ||
| 3 | use "std.zig"; | |
| 4 | ||
| 5 | export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { | |
| 6 | print_str("Hello, world!\n"); | |
| 7 | return 0; | |
| 8 | } |
example/hello_world/hello_libc.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | export executable "hello"; | |
| 2 | ||
| 3 | #link("c") | |
| 4 | extern { | |
| 5 | fn printf(__format: *const u8, ...) -> i32; | |
| 6 | fn exit(__status: i32) -> unreachable; | |
| 7 | } | |
| 8 | ||
| 9 | export fn _start() -> unreachable { | |
| 10 | printf("Hello, world!\n"); | |
| 11 | exit(0); | |
| 12 | } |
src/analyze.cpp+25-7| ... | ... | @@ -599,14 +599,17 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { |
| 599 | 599 | } |
| 600 | 600 | } |
| 601 | 601 | |
| 602 | static TypeStructField *get_struct_field(TypeTableEntry *struct_type, Buf *name) { | |
| 602 | static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructField **out_tsf, int *out_i) { | |
| 603 | 603 | for (int i = 0; i < struct_type->data.structure.field_count; i += 1) { |
| 604 | 604 | TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; |
| 605 | 605 | if (buf_eql_buf(type_struct_field->name, name)) { |
| 606 | return type_struct_field; | |
| 606 | *out_tsf = type_struct_field; | |
| 607 | *out_i = i; | |
| 608 | return; | |
| 607 | 609 | } |
| 608 | 610 | } |
| 609 | return nullptr; | |
| 611 | *out_tsf = nullptr; | |
| 612 | *out_i = -1; | |
| 610 | 613 | } |
| 611 | 614 | |
| 612 | 615 | static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| ... | ... | @@ -618,10 +621,15 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i |
| 618 | 621 | TypeTableEntry *return_type; |
| 619 | 622 | |
| 620 | 623 | if (struct_type->id == TypeTableEntryIdStruct) { |
| 624 | FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; | |
| 625 | ||
| 621 | 626 | Buf *field_name = &node->data.field_access_expr.field_name; |
| 622 | TypeStructField *type_struct_field = get_struct_field(struct_type, field_name); | |
| 623 | if (type_struct_field) { | |
| 624 | return_type = type_struct_field->type_entry; | |
| 627 | ||
| 628 | get_struct_field(struct_type, field_name, | |
| 629 | &codegen_field_access->type_struct_field, | |
| 630 | &codegen_field_access->field_index); | |
| 631 | if (codegen_field_access->type_struct_field) { | |
| 632 | return_type = codegen_field_access->type_struct_field->type_entry; | |
| 625 | 633 | } else { |
| 626 | 634 | add_node_error(g, node, |
| 627 | 635 | buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name))); |
| ... | ... | @@ -1022,14 +1030,24 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1022 | 1030 | break; |
| 1023 | 1031 | } |
| 1024 | 1032 | |
| 1033 | CastNode *cast_node = &node->codegen_node->data.cast_node; | |
| 1034 | ||
| 1025 | 1035 | // special casing this for now, TODO think about casting and do a general solution |
| 1026 | 1036 | if (wanted_type == g->builtin_types.entry_isize && |
| 1027 | 1037 | actual_type->id == TypeTableEntryIdPointer) |
| 1028 | 1038 | { |
| 1039 | cast_node->op = CastOpPtrToInt; | |
| 1029 | 1040 | return_type = wanted_type; |
| 1030 | } else if (wanted_type == g->builtin_types.entry_isize && | |
| 1041 | } else if (wanted_type->id == TypeTableEntryIdInt && | |
| 1031 | 1042 | actual_type->id == TypeTableEntryIdInt) |
| 1032 | 1043 | { |
| 1044 | cast_node->op = CastOpIntWidenOrShorten; | |
| 1045 | return_type = wanted_type; | |
| 1046 | } else if (wanted_type == g->builtin_types.entry_string && | |
| 1047 | actual_type->id == TypeTableEntryIdArray && | |
| 1048 | actual_type->data.array.child_type == g->builtin_types.entry_u8) | |
| 1049 | { | |
| 1050 | cast_node->op = CastOpArrayToString; | |
| 1033 | 1051 | return_type = wanted_type; |
| 1034 | 1052 | } else { |
| 1035 | 1053 | add_node_error(g, node, |
src/analyze.hpp+17| ... | ... | @@ -231,6 +231,21 @@ struct StructDeclNode { |
| 231 | 231 | TypeTableEntry *type_entry; |
| 232 | 232 | }; |
| 233 | 233 | |
| 234 | struct FieldAccessNode { | |
| 235 | int field_index; | |
| 236 | TypeStructField *type_struct_field; | |
| 237 | }; | |
| 238 | ||
| 239 | enum CastOp { | |
| 240 | CastOpPtrToInt, | |
| 241 | CastOpIntWidenOrShorten, | |
| 242 | CastOpArrayToString, | |
| 243 | }; | |
| 244 | ||
| 245 | struct CastNode { | |
| 246 | CastOp op; | |
| 247 | }; | |
| 248 | ||
| 234 | 249 | struct CodeGenNode { |
| 235 | 250 | union { |
| 236 | 251 | TypeNode type_node; // for NodeTypeType |
| ... | ... | @@ -240,6 +255,8 @@ struct CodeGenNode { |
| 240 | 255 | AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign |
| 241 | 256 | BlockNode block_node; // for NodeTypeBlock |
| 242 | 257 | StructDeclNode struct_decl_node; // for NodeTypeStructDecl |
| 258 | FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr | |
| 259 | CastNode cast_node; // for NodeTypeCastExpr | |
| 243 | 260 | } data; |
| 244 | 261 | ExprNode expr_node; // for all the expression nodes |
| 245 | 262 | }; |
src/codegen.cpp+63-23| ... | ... | @@ -197,6 +197,38 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { |
| 197 | 197 | return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); |
| 198 | 198 | } |
| 199 | 199 | |
| 200 | static LLVMValueRef gen_field_val(CodeGen *g, AstNode *node) { | |
| 201 | assert(node->type == NodeTypeFieldAccessExpr); | |
| 202 | ||
| 203 | LLVMValueRef struct_val = gen_expr(g, node->data.field_access_expr.struct_expr); | |
| 204 | assert(struct_val); | |
| 205 | ||
| 206 | FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; | |
| 207 | assert(codegen_field_access->field_index >= 0); | |
| 208 | ||
| 209 | return LLVMBuildExtractValue(g->builder, struct_val, codegen_field_access->field_index, ""); | |
| 210 | } | |
| 211 | ||
| 212 | /* | |
| 213 | static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node) { | |
| 214 | assert(node->type == NodeTypeFieldAccessExpr); | |
| 215 | ||
| 216 | LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr); | |
| 217 | ||
| 218 | assert(struct_ptr); | |
| 219 | ||
| 220 | FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; | |
| 221 | ||
| 222 | assert(codegen_field_access->field_index >= 0); | |
| 223 | ||
| 224 | LLVMValueRef indices[] = { | |
| 225 | LLVMConstInt(LLVMInt32Type(), 0, false), | |
| 226 | LLVMConstInt(LLVMInt32Type(), codegen_field_access->field_index, false) | |
| 227 | }; | |
| 228 | return LLVMBuildStructGEP(g->builder, struct_ptr, indices, 2, ""); | |
| 229 | } | |
| 230 | */ | |
| 231 | ||
| 200 | 232 | static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { |
| 201 | 233 | assert(node->type == NodeTypeArrayAccessExpr); |
| 202 | 234 | |
| ... | ... | @@ -208,12 +240,8 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { |
| 208 | 240 | assert(node->type == NodeTypeFieldAccessExpr); |
| 209 | 241 | |
| 210 | 242 | TypeTableEntry *struct_type = get_expr_type(node->data.field_access_expr.struct_expr); |
| 211 | LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr); | |
| 212 | 243 | Buf *name = &node->data.field_access_expr.field_name; |
| 213 | 244 | |
| 214 | // TODO add struct support | |
| 215 | (void)struct_ptr; | |
| 216 | ||
| 217 | 245 | if (struct_type->id == TypeTableEntryIdArray) { |
| 218 | 246 | if (buf_eql_str(name, "len")) { |
| 219 | 247 | return LLVMConstInt(g->builtin_types.entry_usize->type_ref, |
| ... | ... | @@ -221,6 +249,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { |
| 221 | 249 | } else { |
| 222 | 250 | zig_panic("gen_field_access_expr bad array field"); |
| 223 | 251 | } |
| 252 | } else if (struct_type->id == TypeTableEntryIdStruct) { | |
| 253 | /* | |
| 254 | LLVMValueRef ptr = gen_field_ptr(g, node); | |
| 255 | return LLVMBuildLoad(g->builder, ptr, ""); | |
| 256 | */ | |
| 257 | return gen_field_val(g, node); | |
| 224 | 258 | } else { |
| 225 | 259 | zig_panic("gen_field_access_expr bad struct type"); |
| 226 | 260 | } |
| ... | ... | @@ -259,30 +293,36 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { |
| 259 | 293 | TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr); |
| 260 | 294 | TypeTableEntry *wanted_type = get_expr_type(node); |
| 261 | 295 | |
| 262 | // this asserts are here only because no other casting codegen is supported currently | |
| 263 | assert(wanted_type == g->builtin_types.entry_isize); | |
| 264 | ||
| 265 | if (wanted_type->id == TypeTableEntryIdPointer) { | |
| 266 | return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 267 | } else if (wanted_type->id == TypeTableEntryIdInt) { | |
| 268 | if (actual_type->size_in_bits == wanted_type->size_in_bits) { | |
| 269 | if (actual_type->id == TypeTableEntryIdPointer) { | |
| 270 | return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 296 | CastNode *cast_node = &node->codegen_node->data.cast_node; | |
| 297 | ||
| 298 | switch (cast_node->op) { | |
| 299 | case CastOpPtrToInt: | |
| 300 | return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 301 | case CastOpIntWidenOrShorten: | |
| 302 | if (actual_type->size_in_bits == wanted_type->size_in_bits) { | |
| 303 | return expr_val; | |
| 304 | } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { | |
| 305 | if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) { | |
| 306 | return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 307 | } else { | |
| 308 | zig_panic("TODO gen_cast_expr sign mismatch"); | |
| 309 | } | |
| 271 | 310 | } else { |
| 272 | 311 | zig_panic("TODO gen_cast_expr"); |
| 273 | 312 | } |
| 274 | } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { | |
| 275 | if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) { | |
| 276 | return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 277 | } else { | |
| 278 | zig_panic("TODO gen_cast_expr sign mismatch"); | |
| 313 | case CastOpArrayToString: | |
| 314 | { | |
| 315 | LLVMValueRef struct_vals[] = { | |
| 316 | expr_val, | |
| 317 | LLVMConstInt(g->builtin_types.entry_usize->type_ref, actual_type->data.array.len, false) | |
| 318 | }; | |
| 319 | unsigned field_count = g->builtin_types.entry_string->data.structure.field_count; | |
| 320 | assert(field_count == 2); | |
| 321 | return LLVMConstNamedStruct(g->builtin_types.entry_string->type_ref, | |
| 322 | struct_vals, field_count); | |
| 279 | 323 | } |
| 280 | } else { | |
| 281 | zig_panic("TODO gen_cast_expr"); | |
| 282 | } | |
| 283 | } else { | |
| 284 | zig_panic("TODO gen_cast_expr"); | |
| 285 | 324 | } |
| 325 | zig_unreachable(); | |
| 286 | 326 | } |
| 287 | 327 | |
| 288 | 328 | static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) { |
std/std.zig+2-5| ... | ... | @@ -14,14 +14,11 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { |
| 14 | 14 | } |
| 15 | 15 | |
| 16 | 16 | // TODO error handling |
| 17 | // TODO zig strings instead of C strings | |
| 18 | 17 | // TODO handle buffering and flushing |
| 19 | 18 | // TODO non-i32 integer literals so we can remove the casts |
| 20 | 19 | // TODO constants for SYS_write and stdout_fileno |
| 21 | //pub fn print_str(str : string) -> isize { | |
| 22 | pub fn print_str(str : *const u8, len: isize) -> isize { | |
| 20 | pub fn print_str(str : string) -> isize { | |
| 23 | 21 | let SYS_write = 1; |
| 24 | 22 | let stdout_fileno = 1; |
| 25 | //return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); | |
| 26 | return syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len); | |
| 23 | return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); | |
| 27 | 24 | } |
test/run_tests.cpp+1-1| ... | ... | @@ -403,7 +403,7 @@ loop_2_end: |
| 403 | 403 | use "std.zig"; |
| 404 | 404 | |
| 405 | 405 | export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { |
| 406 | print_str(c"Hello, world!\n", 14 as isize); | |
| 406 | print_str("Hello, world!\n" as string); | |
| 407 | 407 | return 0; |
| 408 | 408 | } |
| 409 | 409 | )SOURCE", "Hello, world!\n"); |