diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index 93fa9ba8ae..31954f55e6 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -3,7 +3,6 @@ export executable "hello"; use "std.zig"; pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - // TODO implicit coercion from array to string - print_str("Hello, world!\n" as string); + print_str("Hello, world!\n"); return 0; } diff --git a/src/analyze.cpp b/src/analyze.cpp index cba9f955ae..68b5719d87 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -618,7 +618,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, zig_unreachable(); } -static TypeTableEntry *resolve_type_compatibility(CodeGen *g, AstNode *node, +static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *context, AstNode *node, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == nullptr) @@ -642,8 +642,21 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, AstNode *node, expected_type->data.integral.is_signed == actual_type->data.integral.is_signed && expected_type->size_in_bits > actual_type->size_in_bits) { - node->codegen_node->expr_node.cast_type = expected_type; + node->codegen_node->expr_node.implicit_cast.type = expected_type; node->codegen_node->expr_node.implicit_cast.op = CastOpIntWidenOrShorten; + node->codegen_node->expr_node.implicit_cast.source_node = node; + return expected_type; + } + + // implicit constant sized array to string conversion + if (expected_type == g->builtin_types.entry_string && + actual_type->id == TypeTableEntryIdArray && + actual_type->data.array.child_type == g->builtin_types.entry_u8) + { + node->codegen_node->expr_node.implicit_cast.type = expected_type; + node->codegen_node->expr_node.implicit_cast.op = CastOpArrayToString; + node->codegen_node->expr_node.implicit_cast.source_node = node; + context->cast_expr_alloca_list.append(&node->codegen_node->expr_node.implicit_cast); return expected_type; } @@ -655,7 +668,8 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, AstNode *node, return g->builtin_types.entry_invalid; } -static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, AstNode *parent_node, +static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, BlockContext *block_context, + AstNode *parent_node, AstNode *child1, AstNode *child2, TypeTableEntry *type1, TypeTableEntry *type2) { @@ -668,8 +682,8 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, AstNode *pare return parent_type; } - resolve_type_compatibility(g, child1, parent_type, type1); - resolve_type_compatibility(g, child2, parent_type, type2); + resolve_type_compatibility(g, block_context, child1, parent_type, type1); + resolve_type_compatibility(g, block_context, child2, parent_type, type2); return parent_type; } @@ -874,6 +888,8 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B } CastNode *cast_node = &node->codegen_node->data.cast_node; + cast_node->source_node = node; + cast_node->type = wanted_type; // special casing this for now, TODO think about casting and do a general solution if (wanted_type == g->builtin_types.entry_isize && @@ -891,7 +907,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B actual_type->data.array.child_type == g->builtin_types.entry_u8) { cast_node->op = CastOpArrayToString; - context->cast_expr_alloca_list.append(node); + context->cast_expr_alloca_list.append(cast_node); return wanted_type; } else if (actual_type->id == TypeTableEntryIdNumberLiteral && num_lit_fits_in_other_type(g, actual_type, wanted_type)) @@ -1022,10 +1038,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, } case BinOpTypeBoolOr: case BinOpTypeBoolAnd: - analyze_expression(g, import, context, g->builtin_types.entry_bool, - node->data.bin_op_expr.op1); - analyze_expression(g, import, context, g->builtin_types.entry_bool, - node->data.bin_op_expr.op2); + analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.bin_op_expr.op1); + analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.bin_op_expr.op2); return g->builtin_types.entry_bool; case BinOpTypeCmpEq: case BinOpTypeCmpNotEq: @@ -1188,8 +1202,8 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE return nullptr; } -static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *node) +static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, + BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) { TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind]; if (node->data.number_literal.overflow) { @@ -1199,7 +1213,7 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry } else if (expected_type) { NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; assert(!codegen_num_lit->resolved_type); - codegen_num_lit->resolved_type = resolve_type_compatibility(g, node, expected_type, num_lit_type); + codegen_num_lit->resolved_type = resolve_type_compatibility(g, block_context, node, expected_type, num_lit_type); return codegen_num_lit->resolved_type; } else { return num_lit_type; @@ -1260,7 +1274,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, actual_return_type = g->builtin_types.entry_invalid; } - resolve_type_compatibility(g, node, expected_return_type, actual_return_type); + resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type); } else { add_node_error(g, node, buf_sprintf("return expression outside function definition")); } @@ -1453,14 +1467,14 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, else_type = analyze_expression(g, import, context, expected_type, node->data.if_expr.else_node); } else { else_type = g->builtin_types.entry_void; - else_type = resolve_type_compatibility(g, node, expected_type, else_type); + else_type = resolve_type_compatibility(g, context, node, expected_type, else_type); } if (expected_type) { return_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type; } else { - return_type = resolve_peer_type_compatibility(g, node, + return_type = resolve_peer_type_compatibility(g, context, node, node->data.if_expr.then_block, node->data.if_expr.else_node, then_type, else_type); } @@ -1482,7 +1496,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, zig_unreachable(); } assert(return_type); - resolve_type_compatibility(g, node, expected_type, return_type); + resolve_type_compatibility(g, context, node, expected_type, return_type); node->codegen_node->expr_node.type_entry = return_type; node->codegen_node->expr_node.block_context = context; diff --git a/src/analyze.hpp b/src/analyze.hpp index 71fdc3bfb5..4a18026a54 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -17,6 +17,7 @@ struct FnTableEntry; struct BlockContext; struct TypeTableEntry; struct VariableTableEntry; +struct CastNode; struct TypeTableEntryPointer { TypeTableEntry *pointer_child; @@ -213,7 +214,7 @@ struct BlockContext { FnTableEntry *fn_entry; // null at the module scope BlockContext *parent; // null when this is the root HashMap variable_table; - ZigList cast_expr_alloca_list; + ZigList cast_expr_alloca_list; LLVMZigDIScope *di_scope; }; @@ -261,6 +262,8 @@ struct CastNode { // if op is CastOpArrayToString, this will be a pointer to // the string struct on the stack LLVMValueRef ptr; + TypeTableEntry *type; + AstNode *source_node; }; struct ExprNode { @@ -270,7 +273,6 @@ struct ExprNode { BlockContext *block_context; // may be null for no cast - TypeTableEntry *cast_type; CastNode implicit_cast; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index f81d07faa6..c51b137f1d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1039,7 +1039,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { assert(node->codegen_node); TypeTableEntry *actual_type = node->codegen_node->expr_node.type_entry; - TypeTableEntry *cast_type = node->codegen_node->expr_node.cast_type; + TypeTableEntry *cast_type = node->codegen_node->expr_node.implicit_cast.type; return cast_type ? gen_bare_cast(g, node, val, actual_type, cast_type, &node->codegen_node->expr_node.implicit_cast) : val; @@ -1248,12 +1248,9 @@ static void do_code_gen(CodeGen *g) { // allocate structs which are the result of casts for (int cea_i = 0; cea_i < block_context->cast_expr_alloca_list.length; cea_i += 1) { - AstNode *cast_expr_node = block_context->cast_expr_alloca_list.at(cea_i); - assert(cast_expr_node->type == NodeTypeCastExpr); - CastNode *cast_codegen = &cast_expr_node->codegen_node->data.cast_node; - TypeTableEntry *type_entry = get_type_for_type_node(g, cast_expr_node->data.cast_expr.type); - add_debug_source_node(g, cast_expr_node); - cast_codegen->ptr = LLVMBuildAlloca(g->builder, type_entry->type_ref, ""); + CastNode *cast_node = block_context->cast_expr_alloca_list.at(cea_i); + add_debug_source_node(g, cast_node->source_node); + cast_node->ptr = LLVMBuildAlloca(g->builder, cast_node->type->type_ref, ""); } } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 2a7746c4d0..66df737845 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -121,7 +121,7 @@ static void add_compiling_test_cases(void) { } fn this_is_a_function() -> unreachable { - print_str("OK\n" as string); + print_str("OK\n"); exit(0); } )SOURCE", "OK\n"); @@ -137,7 +137,7 @@ static void add_compiling_test_cases(void) { /// this is a documentation comment /// doc comment line 2 pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - print_str(/* mid-line comment /* nested */ */ "OK\n" as string); + print_str(/* mid-line comment /* nested */ */ "OK\n"); return 0; } )SOURCE", "OK\n"); @@ -185,17 +185,17 @@ static void add_compiling_test_cases(void) { pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { if 1 != 0 { - print_str("1 is true\n" as string); + print_str("1 is true\n"); } else { - print_str("1 is false\n" as string); + print_str("1 is false\n"); } if 0 != 0 { - print_str("0 is true\n" as string); + print_str("0 is true\n"); } else if 1 - 1 != 0 { - print_str("1 - 1 is true\n" as string); + print_str("1 - 1 is true\n"); } if !(0 != 0) { - print_str("!0 is true\n" as string); + print_str("!0 is true\n"); } return 0; } @@ -210,7 +210,7 @@ static void add_compiling_test_cases(void) { pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { if add(22, 11) == 33 { - print_str("pass\n" as string); + print_str("pass\n"); } return 0; } @@ -223,7 +223,7 @@ static void add_compiling_test_cases(void) { if a == 0 { goto done; } - print_str("loop\n" as string); + print_str("loop\n"); loop(a - 1); done: @@ -243,7 +243,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { const a : i32 = 1; const b = 2 as i32; if (a + b == 3) { - print_str("OK\n" as string); + print_str("OK\n"); } return 0; } @@ -253,10 +253,10 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { use "std.zig"; pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - if (true) { print_str("OK 1\n" as string); } - if (false) { print_str("BAD 1\n" as string); } - if (!true) { print_str("BAD 2\n" as string); } - if (!false) { print_str("OK 2\n" as string); } + if (true) { print_str("OK 1\n"); } + if (false) { print_str("BAD 1\n"); } + if (!true) { print_str("BAD 2\n"); } + if (!false) { print_str("OK 2\n"); } return 0; } )SOURCE", "OK 1\nOK 2\n"); @@ -267,14 +267,14 @@ use "std.zig"; pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { if (true) { const no_conflict : i32 = 5; - if (no_conflict == 5) { print_str("OK 1\n" as string); } + if (no_conflict == 5) { print_str("OK 1\n"); } } const c = { const no_conflict = 10 as i32; no_conflict }; - if (c == 10) { print_str("OK 2\n" as string); } + if (c == 10) { print_str("OK 2\n"); } return 0; } )SOURCE", "OK 1\nOK 2\n"); @@ -290,7 +290,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { fn void_fun(a : i32, b : void, c : i32) { const v = b; const vv : void = if (a == 1) {v} else {}; - if (a + c == 3) { print_str("OK\n" as string); } + if (a + c == 3) { print_str("OK\n"); } return vv; } )SOURCE", "OK\n"); @@ -300,14 +300,14 @@ use "std.zig"; pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { var zero : i32; - if (zero == 0) { print_str("zero\n" as string); } + if (zero == 0) { print_str("zero\n"); } var i = 0 as i32; loop_start: if i == 3 { goto done; } - print_str("loop\n" as string); + print_str("loop\n"); i = i + 1; goto loop_start; done: @@ -346,7 +346,7 @@ loop_2_start: loop_2_end: if accumulator == 15 { - print_str("OK\n" as string); + print_str("OK\n"); } return 0; @@ -358,7 +358,7 @@ loop_2_end: use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - print_str("Hello, world!\n" as string); + print_str("Hello, world!\n"); return 0; } )SOURCE", "Hello, world!\n"); @@ -368,20 +368,20 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - if false || false || false { print_str("BAD 1\n" as string); } - if true && true && false { print_str("BAD 2\n" as string); } - if 1 | 2 | 4 != 7 { print_str("BAD 3\n" as string); } - if 3 ^ 6 ^ 8 != 13 { print_str("BAD 4\n" as string); } - if 7 & 14 & 28 != 4 { print_str("BAD 5\n" as string); } - if 9 << 1 << 2 != 9 << 3 { print_str("BAD 6\n" as string); } - if 90 >> 1 >> 2 != 90 >> 3 { print_str("BAD 7\n" as string); } - if 100 - 1 + 1000 != 1099 { print_str("BAD 8\n" as string); } - if 5 * 4 / 2 % 3 != 1 { print_str("BAD 9\n" as string); } - if 5 as i32 as i32 != 5 { print_str("BAD 10\n" as string); } - if !!false { print_str("BAD 11\n" as string); } - if 7 != --7 { print_str("BAD 12\n" as string); } + if false || false || false { print_str("BAD 1\n"); } + if true && true && false { print_str("BAD 2\n"); } + if 1 | 2 | 4 != 7 { print_str("BAD 3\n"); } + if 3 ^ 6 ^ 8 != 13 { print_str("BAD 4\n"); } + if 7 & 14 & 28 != 4 { print_str("BAD 5\n"); } + if 9 << 1 << 2 != 9 << 3 { print_str("BAD 6\n"); } + if 90 >> 1 >> 2 != 90 >> 3 { print_str("BAD 7\n"); } + if 100 - 1 + 1000 != 1099 { print_str("BAD 8\n"); } + if 5 * 4 / 2 % 3 != 1 { print_str("BAD 9\n"); } + if 5 as i32 as i32 != 5 { print_str("BAD 10\n"); } + if !!false { print_str("BAD 11\n"); } + if 7 != --7 { print_str("BAD 12\n"); } - print_str("OK\n" as string); + print_str("OK\n"); return 0; } )SOURCE", "OK\n"); @@ -390,19 +390,19 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - if true || { print_str("BAD 1\n" as string); false } { - print_str("OK 1\n" as string); + if true || { print_str("BAD 1\n"); false } { + print_str("OK 1\n"); } - if false || { print_str("OK 2\n" as string); false } { - print_str("BAD 2\n" as string); + if false || { print_str("OK 2\n"); false } { + print_str("BAD 2\n"); } - if true && { print_str("OK 3\n" as string); false } { - print_str("BAD 3\n" as string); + if true && { print_str("OK 3\n"); false } { + print_str("BAD 3\n"); } - if false && { print_str("BAD 4\n" as string); false } { + if false && { print_str("BAD 4\n"); false } { } else { - print_str("OK 4\n" as string); + print_str("OK 4\n"); } return 0; @@ -414,20 +414,20 @@ use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { var i : i32 = 0; - i += 5; if i != 5 { print_str("BAD +=\n" as string); } - i -= 2; if i != 3 { print_str("BAD -=\n" as string); } - i *= 20; if i != 60 { print_str("BAD *=\n" as string); } - i /= 3; if i != 20 { print_str("BAD /=\n" as string); } - i %= 11; if i != 9 { print_str("BAD %=\n" as string); } - i <<= 1; if i != 18 { print_str("BAD <<=\n" as string); } - i >>= 2; if i != 4 { print_str("BAD >>=\n" as string); } + i += 5; if i != 5 { print_str("BAD +=\n"); } + i -= 2; if i != 3 { print_str("BAD -=\n"); } + i *= 20; if i != 60 { print_str("BAD *=\n"); } + i /= 3; if i != 20 { print_str("BAD /=\n"); } + i %= 11; if i != 9 { print_str("BAD %=\n"); } + i <<= 1; if i != 18 { print_str("BAD <<=\n"); } + i >>= 2; if i != 4 { print_str("BAD >>=\n"); } i = 6; - i &= 5; if i != 4 { print_str("BAD &=\n" as string); } - i ^= 6; if i != 2 { print_str("BAD ^=\n" as string); } + i &= 5; if i != 4 { print_str("BAD &=\n"); } + i ^= 6; if i != 2 { print_str("BAD ^=\n"); } i = 6; - i |= 3; if i != 7 { print_str("BAD |=\n" as string); } + i |= 3; if i != 7 { print_str("BAD |=\n"); } - print_str("OK\n" as string); + print_str("OK\n"); return 0; } )SOURCE", "OK\n"); @@ -578,7 +578,7 @@ struct Foo { } fn test_foo(foo : Foo) { if foo.b { - print_str("OK\n" as string); + print_str("OK\n"); } } )SOURCE", "OK\n"); @@ -590,10 +590,10 @@ const g1 : i32 = 1233 + 1; var g2 : i32; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - if g2 != 0 { print_str("BAD\n" as string); } + if g2 != 0 { print_str("BAD\n"); } g2 = g1; - if g2 != 1234 { print_str("BAD\n" as string); } - print_str("OK\n" as string); + if g2 != 1234 { print_str("BAD\n"); } + print_str("OK\n"); return 0; } )SOURCE", "OK\n");