diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index ea24b4d921..68c43902cd 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -1,12 +1,9 @@ export executable "hello"; -#link("c") -extern { - fn printf(__format: *const u8, ...) -> i32; - fn exit(__status: i32) -> unreachable; -} +use "std.zig"; -export fn _start() -> unreachable { - printf("Hello, world!\n"); - exit(0); +export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 { + // TODO implicit coercion from array to string + print_str("Hello, world!\n" as string); + return 0; } diff --git a/example/hello_world/hello2.zig b/example/hello_world/hello2.zig deleted file mode 100644 index 37ee9e86c3..0000000000 --- a/example/hello_world/hello2.zig +++ /dev/null @@ -1,8 +0,0 @@ -export executable "hello"; - -use "std.zig"; - -export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { - print_str("Hello, world!\n"); - return 0; -} diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig new file mode 100644 index 0000000000..ea24b4d921 --- /dev/null +++ b/example/hello_world/hello_libc.zig @@ -0,0 +1,12 @@ +export executable "hello"; + +#link("c") +extern { + fn printf(__format: *const u8, ...) -> i32; + fn exit(__status: i32) -> unreachable; +} + +export fn _start() -> unreachable { + printf("Hello, world!\n"); + exit(0); +} diff --git a/src/analyze.cpp b/src/analyze.cpp index f8144c70e2..923e340c05 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -599,14 +599,17 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { } } -static TypeStructField *get_struct_field(TypeTableEntry *struct_type, Buf *name) { +static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructField **out_tsf, int *out_i) { for (int i = 0; i < struct_type->data.structure.field_count; i += 1) { TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; if (buf_eql_buf(type_struct_field->name, name)) { - return type_struct_field; + *out_tsf = type_struct_field; + *out_i = i; + return; } } - return nullptr; + *out_tsf = nullptr; + *out_i = -1; } 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 TypeTableEntry *return_type; if (struct_type->id == TypeTableEntryIdStruct) { + FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; + Buf *field_name = &node->data.field_access_expr.field_name; - TypeStructField *type_struct_field = get_struct_field(struct_type, field_name); - if (type_struct_field) { - return_type = type_struct_field->type_entry; + + get_struct_field(struct_type, field_name, + &codegen_field_access->type_struct_field, + &codegen_field_access->field_index); + if (codegen_field_access->type_struct_field) { + return_type = codegen_field_access->type_struct_field->type_entry; } else { add_node_error(g, node, 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, break; } + CastNode *cast_node = &node->codegen_node->data.cast_node; + // special casing this for now, TODO think about casting and do a general solution if (wanted_type == g->builtin_types.entry_isize && actual_type->id == TypeTableEntryIdPointer) { + cast_node->op = CastOpPtrToInt; return_type = wanted_type; - } else if (wanted_type == g->builtin_types.entry_isize && + } else if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt) { + cast_node->op = CastOpIntWidenOrShorten; + return_type = wanted_type; + } else if (wanted_type == g->builtin_types.entry_string && + actual_type->id == TypeTableEntryIdArray && + actual_type->data.array.child_type == g->builtin_types.entry_u8) + { + cast_node->op = CastOpArrayToString; return_type = wanted_type; } else { add_node_error(g, node, diff --git a/src/analyze.hpp b/src/analyze.hpp index 47411b35ff..f7ccbf18d9 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -231,6 +231,21 @@ struct StructDeclNode { TypeTableEntry *type_entry; }; +struct FieldAccessNode { + int field_index; + TypeStructField *type_struct_field; +}; + +enum CastOp { + CastOpPtrToInt, + CastOpIntWidenOrShorten, + CastOpArrayToString, +}; + +struct CastNode { + CastOp op; +}; + struct CodeGenNode { union { TypeNode type_node; // for NodeTypeType @@ -240,6 +255,8 @@ struct CodeGenNode { AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign BlockNode block_node; // for NodeTypeBlock StructDeclNode struct_decl_node; // for NodeTypeStructDecl + FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr + CastNode cast_node; // for NodeTypeCastExpr } data; ExprNode expr_node; // for all the expression nodes }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 368e85edb1..c43d3f997d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -197,6 +197,38 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); } +static LLVMValueRef gen_field_val(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeFieldAccessExpr); + + LLVMValueRef struct_val = gen_expr(g, node->data.field_access_expr.struct_expr); + assert(struct_val); + + FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; + assert(codegen_field_access->field_index >= 0); + + return LLVMBuildExtractValue(g->builder, struct_val, codegen_field_access->field_index, ""); +} + +/* +static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeFieldAccessExpr); + + LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr); + + assert(struct_ptr); + + FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node; + + assert(codegen_field_access->field_index >= 0); + + LLVMValueRef indices[] = { + LLVMConstInt(LLVMInt32Type(), 0, false), + LLVMConstInt(LLVMInt32Type(), codegen_field_access->field_index, false) + }; + return LLVMBuildStructGEP(g->builder, struct_ptr, indices, 2, ""); +} +*/ + static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeArrayAccessExpr); @@ -208,12 +240,8 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFieldAccessExpr); TypeTableEntry *struct_type = get_expr_type(node->data.field_access_expr.struct_expr); - LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr); Buf *name = &node->data.field_access_expr.field_name; - // TODO add struct support - (void)struct_ptr; - if (struct_type->id == TypeTableEntryIdArray) { if (buf_eql_str(name, "len")) { return LLVMConstInt(g->builtin_types.entry_usize->type_ref, @@ -221,6 +249,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { } else { zig_panic("gen_field_access_expr bad array field"); } + } else if (struct_type->id == TypeTableEntryIdStruct) { + /* + LLVMValueRef ptr = gen_field_ptr(g, node); + return LLVMBuildLoad(g->builder, ptr, ""); + */ + return gen_field_val(g, node); } else { zig_panic("gen_field_access_expr bad struct type"); } @@ -259,30 +293,36 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr); TypeTableEntry *wanted_type = get_expr_type(node); - // this asserts are here only because no other casting codegen is supported currently - assert(wanted_type == g->builtin_types.entry_isize); + CastNode *cast_node = &node->codegen_node->data.cast_node; - if (wanted_type->id == TypeTableEntryIdPointer) { - return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); - } else if (wanted_type->id == TypeTableEntryIdInt) { - if (actual_type->size_in_bits == wanted_type->size_in_bits) { - if (actual_type->id == TypeTableEntryIdPointer) { - return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); + switch (cast_node->op) { + case CastOpPtrToInt: + return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpIntWidenOrShorten: + if (actual_type->size_in_bits == wanted_type->size_in_bits) { + return expr_val; + } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { + if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) { + return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); + } else { + zig_panic("TODO gen_cast_expr sign mismatch"); + } } else { zig_panic("TODO gen_cast_expr"); } - } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { - if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) { - return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); - } else { - zig_panic("TODO gen_cast_expr sign mismatch"); + case CastOpArrayToString: + { + LLVMValueRef struct_vals[] = { + expr_val, + LLVMConstInt(g->builtin_types.entry_usize->type_ref, actual_type->data.array.len, false) + }; + unsigned field_count = g->builtin_types.entry_string->data.structure.field_count; + assert(field_count == 2); + return LLVMConstNamedStruct(g->builtin_types.entry_string->type_ref, + struct_vals, field_count); } - } else { - zig_panic("TODO gen_cast_expr"); - } - } else { - zig_panic("TODO gen_cast_expr"); } + zig_unreachable(); } static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) { diff --git a/std/std.zig b/std/std.zig index a05a21650f..ebb6e9e2b5 100644 --- a/std/std.zig +++ b/std/std.zig @@ -14,14 +14,11 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { } // TODO error handling -// TODO zig strings instead of C strings // TODO handle buffering and flushing // TODO non-i32 integer literals so we can remove the casts // TODO constants for SYS_write and stdout_fileno -//pub fn print_str(str : string) -> isize { -pub fn print_str(str : *const u8, len: isize) -> isize { +pub fn print_str(str : string) -> isize { let SYS_write = 1; let stdout_fileno = 1; - //return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); - return syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len); + return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 7dee8c745e..8adbe0b6d7 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -403,7 +403,7 @@ loop_2_end: use "std.zig"; export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { - print_str(c"Hello, world!\n", 14 as isize); + print_str("Hello, world!\n" as string); return 0; } )SOURCE", "Hello, world!\n");