diff --git a/example/cat/main.zig b/example/cat/main.zig index 2016e8a9c9..6c96b983e4 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -4,14 +4,11 @@ import "std.zig"; // Things to do to make this work: // * var args printing -// * %void type +// * update std API // * defer // * %return // * %% operator -// * make main return %void -// * how to reference error values %.Invalid // * cast err type to string -// * update std API pub %.Invalid; diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index 63b7a44d1b..c116eeea7b 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -2,8 +2,7 @@ export executable "hello"; import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { //stderr.print_str("Hello, world!\n"); print_str("Hello, world!\n"); - return 0; } diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index b2c9101f17..94882ec82e 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -5,7 +5,7 @@ extern { fn printf(__format: &const u8, ...) i32; } -export fn main(argc: i32, argv: &&u8, env: &&u8) i32 => { +export fn main(argc: i32, argv: &&u8) i32 => { printf(c"Hello, world!\n"); return 0; } diff --git a/src/all_types.hpp b/src/all_types.hpp index f358bd8bd6..af80b36f3f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -60,6 +60,11 @@ struct ConstPtrValue { uint64_t len; }; +struct ConstErrValue { + ErrorTableEntry *err; + ConstExprValue *payload; +}; + struct ConstExprValue { bool ok; // true if constant expression evalution worked bool depends_on_compile_var; @@ -70,8 +75,8 @@ struct ConstExprValue { bool x_bool; FnTableEntry *x_fn; TypeTableEntry *x_type; - ErrorTableEntry *x_err; ConstExprValue *x_maybe; + ConstErrValue x_err; ConstEnumValue x_enum; ConstStructValue x_struct; ConstArrayValue x_array; @@ -309,6 +314,7 @@ enum CastOp { CastOpIntWidenOrShorten, CastOpToUnknownSizeArray, CastOpMaybeWrap, + CastOpErrorWrap, CastOpPointerReinterpret, CastOpErrToInt, }; diff --git a/src/analyze.cpp b/src/analyze.cpp index c2fe9ba076..a137fdb3cb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1130,78 +1130,6 @@ static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTa return false; } -static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *parent_source_node, - AstNode **child_nodes, TypeTableEntry **child_types, int child_count) -{ - TypeTableEntry *prev_type = child_types[0]; - AstNode *prev_node = child_nodes[0]; - if (prev_type->id == TypeTableEntryIdInvalid) { - return prev_type; - } - for (int i = 1; i < child_count; i += 1) { - TypeTableEntry *cur_type = child_types[i]; - AstNode *cur_node = child_nodes[i]; - if (cur_type->id == TypeTableEntryIdInvalid) { - return cur_type; - } else if (prev_type->id == TypeTableEntryIdUnreachable) { - prev_type = cur_type; - prev_node = cur_node; - } else if (cur_type->id == TypeTableEntryIdUnreachable) { - continue; - } else if (prev_type->id == TypeTableEntryIdInt && - cur_type->id == TypeTableEntryIdInt && - prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) - { - if (cur_type->size_in_bits > prev_type->size_in_bits) { - prev_type = cur_type; - prev_node = cur_node; - } - } else if (prev_type->id == TypeTableEntryIdFloat && - cur_type->id == TypeTableEntryIdFloat) - { - if (cur_type->size_in_bits > prev_type->size_in_bits) { - prev_type = cur_type; - prev_node = cur_node; - } - } else if (prev_type->id == TypeTableEntryIdNumLitFloat && - cur_type->id == TypeTableEntryIdNumLitFloat) - { - continue; - } else if (prev_type->id == TypeTableEntryIdNumLitInt && - cur_type->id == TypeTableEntryIdNumLitInt) - { - continue; - } else if (prev_type->id == TypeTableEntryIdNumLitInt || - prev_type->id == TypeTableEntryIdNumLitFloat) - { - if (num_lit_fits_in_other_type(g, prev_node, cur_type)) { - prev_type = cur_type; - prev_node = cur_node; - continue; - } else { - return g->builtin_types.entry_invalid; - } - } else if (cur_type->id == TypeTableEntryIdNumLitInt || - cur_type->id == TypeTableEntryIdNumLitFloat) - { - if (num_lit_fits_in_other_type(g, cur_node, prev_type)) { - continue; - } else { - return g->builtin_types.entry_invalid; - } - } else if (prev_type == cur_type) { - continue; - } else { - add_node_error(g, parent_source_node, - buf_sprintf("incompatible types: '%s' and '%s'", - buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); - - return g->builtin_types.entry_invalid; - } - } - return prev_type; -} - static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == actual_type) return true; @@ -1257,6 +1185,92 @@ static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTable return false; } +static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *parent_source_node, + AstNode **child_nodes, TypeTableEntry **child_types, int child_count) +{ + TypeTableEntry *prev_type = child_types[0]; + AstNode *prev_node = child_nodes[0]; + if (prev_type->id == TypeTableEntryIdInvalid) { + return prev_type; + } + for (int i = 1; i < child_count; i += 1) { + TypeTableEntry *cur_type = child_types[i]; + AstNode *cur_node = child_nodes[i]; + if (cur_type->id == TypeTableEntryIdInvalid) { + return cur_type; + } else if (types_match_const_cast_only(prev_type, cur_type)) { + continue; + } else if (types_match_const_cast_only(cur_type, prev_type)) { + prev_type = cur_type; + prev_node = cur_node; + continue; + } else if (prev_type->id == TypeTableEntryIdUnreachable) { + prev_type = cur_type; + prev_node = cur_node; + } else if (cur_type->id == TypeTableEntryIdUnreachable) { + continue; + } else if (prev_type->id == TypeTableEntryIdInt && + cur_type->id == TypeTableEntryIdInt && + prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) + { + if (cur_type->size_in_bits > prev_type->size_in_bits) { + prev_type = cur_type; + prev_node = cur_node; + } + } else if (prev_type->id == TypeTableEntryIdFloat && + cur_type->id == TypeTableEntryIdFloat) + { + if (cur_type->size_in_bits > prev_type->size_in_bits) { + prev_type = cur_type; + prev_node = cur_node; + } + } else if (prev_type->id == TypeTableEntryIdError && + types_match_const_cast_only(prev_type->data.error.child_type, cur_type)) + { + continue; + } else if (cur_type->id == TypeTableEntryIdError && + types_match_const_cast_only(cur_type->data.error.child_type, prev_type)) + { + prev_type = cur_type; + prev_node = cur_node; + continue; + } else if (prev_type->id == TypeTableEntryIdNumLitFloat && + cur_type->id == TypeTableEntryIdNumLitFloat) + { + continue; + } else if (prev_type->id == TypeTableEntryIdNumLitInt && + cur_type->id == TypeTableEntryIdNumLitInt) + { + continue; + } else if (prev_type->id == TypeTableEntryIdNumLitInt || + prev_type->id == TypeTableEntryIdNumLitFloat) + { + if (num_lit_fits_in_other_type(g, prev_node, cur_type)) { + prev_type = cur_type; + prev_node = cur_node; + continue; + } else { + return g->builtin_types.entry_invalid; + } + } else if (cur_type->id == TypeTableEntryIdNumLitInt || + cur_type->id == TypeTableEntryIdNumLitFloat) + { + if (num_lit_fits_in_other_type(g, cur_node, prev_type)) { + continue; + } else { + return g->builtin_types.entry_invalid; + } + } else { + add_node_error(g, parent_source_node, + buf_sprintf("incompatible types: '%s' and '%s'", + buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); + + return g->builtin_types.entry_invalid; + } + } + return prev_type; +} + static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type, AstNode *literal_node, bool *reported_err) { @@ -1272,6 +1286,14 @@ static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_ return true; } + // implicit conversion from error child type to error type + if (expected_type->id == TypeTableEntryIdError && + types_match_with_implicit_cast(g, expected_type->data.error.child_type, actual_type, + literal_node, reported_err)) + { + return true; + } + // implicit widening conversion if (expected_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && @@ -1381,9 +1403,10 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEn if (!child_nodes[i]) { continue; } - Expr *expr = get_resolved_expr(child_nodes[i]); + AstNode **child_node = child_nodes[i]->parent_field; TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, block_context, - child_nodes[i], expected_type, child_types[i]); + *child_node, expected_type, child_types[i]); + Expr *expr = get_resolved_expr(*child_node); expr->type_entry = resolved_type; add_global_const_expr(g, expr); } @@ -1812,7 +1835,7 @@ static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, F static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, ErrorTableEntry *err) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; - expr->const_val.data.x_err = err; + expr->const_val.data.x_err.err = err; return get_error_type(g, g->builtin_types.entry_void); } @@ -2868,10 +2891,18 @@ static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *ex const_val->data.x_maybe = other_val; const_val->ok = true; break; - case CastOpErrToInt: - bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_err->value); + case CastOpErrorWrap: + const_val->data.x_err.err = nullptr; + const_val->data.x_err.payload = other_val; const_val->ok = true; break; + case CastOpErrToInt: + { + uint64_t value = other_val->data.x_err.err ? other_val->data.x_err.err->value : 0; + bignum_init_unsigned(&const_val->data.x_bignum, value); + const_val->ok = true; + break; + } } } @@ -2965,6 +2996,25 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B } } + // explicit cast from child type of error type to error type + if (wanted_type->id == TypeTableEntryIdError) { + if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) { + node->data.fn_call_expr.cast_op = CastOpErrorWrap; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } else if (actual_type->id == TypeTableEntryIdNumLitInt || + actual_type->id == TypeTableEntryIdNumLitFloat) + { + if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) { + node->data.fn_call_expr.cast_op = CastOpErrorWrap; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } else { + return g->builtin_types.entry_invalid; + } + } + } + // explicit cast from number literal to another type if (actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt) @@ -3579,6 +3629,42 @@ static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry } } +static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + BlockContext *child_context = new_block_context(node, context); + node->data.block.block_context = child_context; + TypeTableEntry *return_type = g->builtin_types.entry_void; + + for (int i = 0; i < node->data.block.statements.length; i += 1) { + AstNode *child = node->data.block.statements.at(i); + if (child->type == NodeTypeLabel) { + LabelTableEntry *label_entry = child->data.label.label_entry; + assert(label_entry); + label_entry->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable); + return_type = g->builtin_types.entry_void; + continue; + } + if (return_type->id == TypeTableEntryIdUnreachable) { + if (is_node_void_expr(child)) { + // {unreachable;void;void} is allowed. + // ignore void statements once we enter unreachable land. + analyze_expression(g, import, context, g->builtin_types.entry_void, child); + continue; + } + add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code")); + break; + } + bool is_last = (i == node->data.block.statements.length - 1); + TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; + return_type = analyze_expression(g, import, child_context, passed_expected_type, child); + if (!is_last && return_type->id == TypeTableEntryIdMetaType) { + add_node_error(g, child, buf_sprintf("expected expression, found type")); + } + } + return return_type; +} + // When you call analyze_expression, the node you pass might no longer be the child node // you thought it was due to implicit casting rewriting the AST. static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -3587,39 +3673,8 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, TypeTableEntry *return_type = nullptr; switch (node->type) { case NodeTypeBlock: - { - BlockContext *child_context = new_block_context(node, context); - node->data.block.block_context = child_context; - return_type = g->builtin_types.entry_void; - - for (int i = 0; i < node->data.block.statements.length; i += 1) { - AstNode *child = node->data.block.statements.at(i); - if (child->type == NodeTypeLabel) { - LabelTableEntry *label_entry = child->data.label.label_entry; - assert(label_entry); - label_entry->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable); - return_type = g->builtin_types.entry_void; - continue; - } - if (return_type->id == TypeTableEntryIdUnreachable) { - if (is_node_void_expr(child)) { - // {unreachable;void;void} is allowed. - // ignore void statements once we enter unreachable land. - analyze_expression(g, import, context, g->builtin_types.entry_void, child); - continue; - } - add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code")); - break; - } - bool is_last = (i == node->data.block.statements.length - 1); - TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; - return_type = analyze_expression(g, import, child_context, passed_expected_type, child); - if (!is_last && return_type->id == TypeTableEntryIdMetaType) { - add_node_error(g, child, buf_sprintf("expected expression, found type")); - } - } - break; - } + return_type = analyze_block_expr(g, import, context, expected_type, node); + break; case NodeTypeReturnExpr: return_type = analyze_return_expr(g, import, context, expected_type, node); diff --git a/src/codegen.cpp b/src/codegen.cpp index e2629a44e6..786127cd42 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -268,6 +268,26 @@ static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntr } } +static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeTableEntry *actual_type, + TypeTableEntry *wanted_type, LLVMValueRef expr_val) +{ + 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) { + add_debug_source_node(g, source_node); + return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); + } else { + add_debug_source_node(g, source_node); + return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); + } + } else { + assert(actual_type->size_in_bits > wanted_type->size_in_bits); + add_debug_source_node(g, source_node); + return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); + } +} + static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); @@ -288,7 +308,7 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { case CastOpErrToInt: assert(actual_type->id == TypeTableEntryIdError); if (actual_type->data.error.child_type->size_in_bits == 0) { - return expr_val; + return gen_widen_or_shorten(g, node, g->err_tag_type, wanted_type, expr_val); } else { zig_panic("TODO"); } @@ -309,6 +329,13 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { return cast_expr->tmp_ptr; } + case CastOpErrorWrap: + assert(wanted_type->id == TypeTableEntryIdError); + if (wanted_type->data.error.child_type->size_in_bits == 0) { + return LLVMConstNull(g->err_tag_type->type_ref); + } else { + zig_panic("TODO"); + } case CastOpPtrToInt: add_debug_source_node(g, node); return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); @@ -316,21 +343,7 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { add_debug_source_node(g, node); return LLVMBuildBitCast(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) { - add_debug_source_node(g, node); - return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); - } else { - add_debug_source_node(g, node); - return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); - } - } else { - assert(actual_type->size_in_bits > wanted_type->size_in_bits); - add_debug_source_node(g, node); - return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); - } + return gen_widen_or_shorten(g, node, actual_type, wanted_type, expr_val); case CastOpToUnknownSizeArray: { assert(cast_expr->tmp_ptr); @@ -1279,7 +1292,7 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV return nullptr; } - assert(!use_expr_value); + assert(!use_expr_value || then_type->id == TypeTableEntryIdError); LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); @@ -1292,7 +1305,12 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV LLVMBuildBr(g->builder, endif_block); LLVMPositionBuilderAtEnd(g->builder, endif_block); - return nullptr; + + if (use_expr_value) { + return LLVMConstNull(g->err_tag_type->type_ref); + } else { + return nullptr; + } } static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) { @@ -2132,7 +2150,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE } } else if (type_entry->id == TypeTableEntryIdError) { if (type_entry->data.error.child_type->size_in_bits == 0) { - return LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err->value, false); + uint64_t value = const_val->data.x_err.err ? const_val->data.x_err.err->value : 0; + return LLVMConstInt(g->err_tag_type->type_ref, value, false); } else { zig_panic("TODO"); } diff --git a/std/bootstrap.zig b/std/bootstrap.zig index e4ad0d9fc0..1fa8a0e184 100644 --- a/std/bootstrap.zig +++ b/std/bootstrap.zig @@ -29,5 +29,8 @@ fn call_main() unreachable => { const ptr = argv[i]; args[i] = ptr[0...strlen(ptr)]; } - exit(main(args)) + // TODO: replace the i32 cast with: + // main(args) %% exit(1) + // exit(0) + exit(i32(main(args))) } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index a112eeb43a..2af6d5d29f 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -114,7 +114,7 @@ import "syscall.zig"; fn empty_function_1() => {} fn empty_function_2() => { return; } -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { empty_function_1(); empty_function_2(); this_is_a_function(); @@ -136,9 +136,8 @@ fn another_function() => {} /// this is a documentation comment /// doc comment line 2 -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { print_str(/* mid-line comment /* nested */ */ "OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -147,10 +146,9 @@ pub fn main(args: [][]u8) i32 => { import "std.zig"; import "foo.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { private_function(); print_str("OK 2\n"); - return 0; } fn private_function() => { @@ -178,10 +176,9 @@ pub fn print_text() => { import "foo.zig"; import "bar.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { foo_function(); bar_function(); - return 0; } )SOURCE", "OK\nOK\n"); @@ -214,7 +211,7 @@ pub fn foo_function() bool => { add_simple_case("if statements", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { if (1 != 0) { print_str("1 is true\n"); } else { @@ -228,7 +225,6 @@ pub fn main(args: [][]u8) i32 => { if (!(0 != 0)) { print_str("!0 is true\n"); } - return 0; } )SOURCE", "1 is true\n!0 is true\n"); @@ -239,11 +235,10 @@ fn add(a: i32, b: i32) i32 => { a + b } -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { if (add(22, 11) == 33) { print_str("pass\n"); } - return 0; } )SOURCE", "pass\n"); @@ -261,41 +256,38 @@ done: return; } -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { loop(3); - return 0; } )SOURCE", "loop\nloop\nloop\n"); add_simple_case("local variables", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const a : i32 = 1; const b = i32(2); if (a + b == 3) { print_str("OK\n"); } - return 0; } )SOURCE", "OK\n"); add_simple_case("bool literals", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { 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"); add_simple_case("separate block scopes", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { if (true) { const no_conflict : i32 = 5; if (no_conflict == 5) { print_str("OK 1\n"); } @@ -306,16 +298,14 @@ pub fn main(args: [][]u8) i32 => { no_conflict }; if (c == 10) { print_str("OK 2\n"); } - return 0; } )SOURCE", "OK 1\nOK 2\n"); add_simple_case("void parameters", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { void_fun(1, void{}, 2); - return 0; } fn void_fun(a : i32, b : void, c : i32) => { @@ -333,7 +323,7 @@ struct Foo { b : i32, c : void, } -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const foo = Foo { .a = void{}, .b = 1, @@ -346,7 +336,6 @@ pub fn main(args: [][]u8) i32 => { print_str("BAD\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -354,7 +343,7 @@ pub fn main(args: [][]u8) i32 => { add_simple_case("void arrays", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var array: [4]void; array[0] = void{}; array[1] = array[2]; @@ -365,7 +354,6 @@ pub fn main(args: [][]u8) i32 => { print_str("BAD\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -373,27 +361,22 @@ pub fn main(args: [][]u8) i32 => { add_simple_case("mutable local variables", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var zero : i32 = 0; if (zero == 0) { print_str("zero\n"); } var i = i32(0); -loop_start: - if (i == 3) { - goto done; + while (i != 3) { + print_str("loop\n"); + i += 1; } - print_str("loop\n"); - i = i + 1; - goto loop_start; -done: - return 0; } )SOURCE", "zero\nloop\nloop\nloop\n"); add_simple_case("arrays", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var array : [5]i32; var i : i32 = 0; @@ -417,8 +400,6 @@ pub fn main(args: [][]u8) i32 => { if (get_array_len(array) != 5) { print_str("BAD\n"); } - - return 0; } fn get_array_len(a: []i32) isize => { a.len @@ -429,9 +410,8 @@ fn get_array_len(a: []i32) isize => { add_simple_case("hello world without libc", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { print_str("Hello, world!\n"); - return 0; } )SOURCE", "Hello, world!\n"); @@ -439,7 +419,7 @@ pub fn main(args: [][]u8) i32 => { add_simple_case("a + b + c", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { 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"); } @@ -454,14 +434,13 @@ pub fn main(args: [][]u8) i32 => { if (i32(7) != --(i32(7))) { print_str("BAD 12\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); add_simple_case("short circuit", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { if (true || { print_str("BAD 1\n"); false }) { print_str("OK 1\n"); } @@ -476,15 +455,13 @@ pub fn main(args: [][]u8) i32 => { } else { print_str("OK 4\n"); } - - return 0; } )SOURCE", "OK 1\nOK 2\nOK 3\nOK 4\n"); add_simple_case("modify operators", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var i : i32 = 0; i += 5; if (i != 5) { print_str("BAD +=\n"); } i -= 2; if (i != 3) { print_str("BAD -=\n"); } @@ -500,7 +477,6 @@ pub fn main(args: [][]u8) i32 => { i |= 3; if (i != 7) { print_str("BAD |=\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -636,7 +612,7 @@ export fn main(argc: i32, argv: &&u8) i32 => { add_simple_case("structs", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var foo : Foo; @memset(&foo, 0, @sizeof(Foo)); foo.a += 1; @@ -650,7 +626,6 @@ pub fn main(args: [][]u8) i32 => { test_byval_assign(); test_initializer(); print_str("OK\n"); - return 0; } struct Foo { a : i32, @@ -711,23 +686,25 @@ import "std.zig"; const g1 : i32 = 1233 + 1; var g2 : i32 = 0; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { if (g2 != 0) { print_str("BAD\n"); } g2 = g1; if (g2 != 1234) { print_str("BAD\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); add_simple_case("while loop", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var i : i32 = 0; while (i < 4) { print_str("loop\n"); i += 1; } + g(); +} +fn g() i32 => { return f(); } fn f() i32 => { @@ -739,7 +716,7 @@ fn f() i32 => { add_simple_case("continue and break", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var i : i32 = 0; while (true) { print_str("loop\n"); @@ -749,13 +726,12 @@ pub fn main(args: [][]u8) i32 => { } break; } - return 0; } )SOURCE", "loop\nloop\nloop\nloop\n"); add_simple_case("maybe type", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const x : ?bool = true; if (const y ?= x) { @@ -783,19 +759,16 @@ pub fn main(args: [][]u8) i32 => { if (num != 13) { print_str("BAD\n"); } - - return 0; } )SOURCE", "x is true\n"); add_simple_case("implicit cast after unreachable", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const x = outer(); if (x == 1234) { print_str("OK\n"); } - return 0; } fn inner() i32 => { 1234 } fn outer() isize => { @@ -807,11 +780,10 @@ fn outer() isize => { import "std.zig"; const x: u16 = 13; const z: @typeof(x) = 19; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const y: @typeof(x) = 120; print_u64(@sizeof(@typeof(y))); print_str("\n"); - return 0; } )SOURCE", "2\n"); @@ -823,20 +795,19 @@ struct Rand { r.seed } } -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const r = Rand {.seed = 1234}; if (r.get_seed() != 1234) { print_str("BAD seed\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); add_simple_case("pointer dereferencing", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var x = i32(3); const y = &x; @@ -849,7 +820,6 @@ pub fn main(args: [][]u8) i32 => { print_str("BAD\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -858,17 +828,16 @@ import "std.zig"; const ARRAY_SIZE : i8 = 20; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var array : [ARRAY_SIZE]u8; print_u64(@sizeof(@typeof(array))); print_str("\n"); - return 0; } )SOURCE", "20\n"); add_simple_case("@min_value() and @max_value()", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { print_str("max u8: "); print_u64(@max_value(u8)); print_str("\n"); @@ -932,8 +901,6 @@ pub fn main(args: [][]u8) i32 => { print_str("min i64: "); print_i64(@min_value(i64)); print_str("\n"); - - return 0; } )SOURCE", "max u8: 255\n" @@ -956,7 +923,7 @@ pub fn main(args: [][]u8) i32 => { add_simple_case("slicing", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var array : [20]i32; array[5] = 1234; @@ -977,18 +944,16 @@ pub fn main(args: [][]u8) i32 => { } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); add_simple_case("else if expression", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { if (f(1) == 1) { print_str("OK\n"); } - return 0; } fn f(c: u8) u8 => { if (c == 0) { @@ -1003,7 +968,7 @@ fn f(c: u8) u8 => { add_simple_case("overflow intrinsics", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var result: u8; if (!@add_with_overflow(u8, 250, 100, &result)) { print_str("BAD\n"); @@ -1015,13 +980,12 @@ pub fn main(args: [][]u8) i32 => { print_str("BAD\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); add_simple_case("memcpy and memset intrinsics", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { var foo : [20]u8; var bar : [20]u8; @@ -1033,7 +997,6 @@ pub fn main(args: [][]u8) i32 => { } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -1042,8 +1005,8 @@ import "std.zig"; const z : @typeof(stdin_fileno) = 0; const x : @typeof(y) = 1234; const y : u16 = 5678; -pub fn main(args: [][]u8) i32 => { - print_ok(x) +pub fn main(args: [][]u8) %void => { + var x : i32 = print_ok(x); } fn print_ok(val: @typeof(x)) @typeof(foo) => { print_str("OK\n"); @@ -1073,7 +1036,7 @@ enum Bar { D, } -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const foo1 = Foo.One(13); const foo2 = Foo.Two(Point { .x = 1234, .y = 5678, }); const bar = Bar.B; @@ -1098,15 +1061,13 @@ pub fn main(args: [][]u8) i32 => { } print_str("OK\n"); - - return 0; } )SOURCE", "OK\n"); add_simple_case("array literal", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const HEX_MULT = []u16{4096, 256, 16, 1}; if (HEX_MULT.len != 4) { @@ -1118,14 +1079,13 @@ pub fn main(args: [][]u8) i32 => { } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); add_simple_case("nested arrays", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const array_of_strings = [][]u8 {"hello", "this", "is", "my", "thing"}; var i: @typeof(array_of_strings.len) = 0; while (i < array_of_strings.len) { @@ -1133,14 +1093,13 @@ pub fn main(args: [][]u8) i32 => { print_str("\n"); i += 1; } - return 0; } )SOURCE", "hello\nthis\nis\nmy\nthing\n"); add_simple_case("for loops", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const array = []u8 {9, 8, 7, 6}; for (item, array) { print_u64(item); @@ -1159,20 +1118,18 @@ pub fn main(args: [][]u8) i32 => { print_i64(index); print_str("\n"); } - return 0; } )SOURCE", "9\n8\n7\n6\n0\n1\n2\n3\n9\n8\n7\n6\n0\n1\n2\n3\n"); add_simple_case("function pointers", R"SOURCE( import "std.zig"; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const fns = []@typeof(fn1) { fn1, fn2, fn3, fn4, }; for (f, fns) { print_u64(f()); print_str("\n"); } - return 0; } fn fn1() u32 => {5} @@ -1191,7 +1148,7 @@ enum Foo { D, } -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const foo = Foo.C; const val: i32 = switch (foo) { Foo.A => 1, @@ -1204,7 +1161,6 @@ pub fn main(args: [][]u8) i32 => { } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -1213,7 +1169,7 @@ import "std.zig"; const ten = 10; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const one = 1; const eleven = ten + one; @@ -1222,7 +1178,6 @@ pub fn main(args: [][]u8) i32 => { } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -1233,28 +1188,26 @@ struct Foo { y: bool, } var foo = Foo { .x = 13, .y = true, }; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { foo.x += 1; if (foo.x != 14) { print_str("BAD\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); add_simple_case("statically initialized array literal", R"SOURCE( import "std.zig"; const x = []u8{1,2,3,4}; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const y : [4]u8 = x; if (y[3] != 4) { print_str("BAD\n"); } print_str("OK\n"); - return 0; } )SOURCE", "OK\n"); @@ -1262,7 +1215,7 @@ pub fn main(args: [][]u8) i32 => { import "std.zig"; %.err1; %.err2; -pub fn main(args: [][]u8) i32 => { +pub fn main(args: [][]u8) %void => { const a = i32(%.err1); const b = i32(%.err2); if (a == b) { @@ -1270,7 +1223,6 @@ pub fn main(args: [][]u8) i32 => { } print_str("OK\n"); - return 0; } )SOURCE", "OK\n");