diff --git a/example/cat/main.zig b/example/cat/main.zig index 166182aff5..329e712612 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -13,7 +13,7 @@ pub fn main(args: [][]u8) -> %void { for (arg, args[1...]) { if (arg == "-") { catted_anything = true; - %return cat_stream(stdin); + cat_stream(stdin) %% |err| return err; } else if (arg[0] == '-') { return usage(exe); } else { @@ -24,11 +24,11 @@ pub fn main(args: [][]u8) -> %void { defer is.close(); catted_anything = true; - %return cat_stream(is); + cat_stream(is) %% |err| return err; } } if (!catted_anything) { - %return cat_stream(stdin) + cat_stream(stdin) %% |err| return err; } } diff --git a/src/all_types.hpp b/src/all_types.hpp index e831facfc8..599307cea9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1049,6 +1049,19 @@ struct BuiltinFnEntry { LLVMValueRef fn_val; }; +enum CIntType { + CIntTypeShort, + CIntTypeUShort, + CIntTypeInt, + CIntTypeUInt, + CIntTypeLong, + CIntTypeULong, + CIntTypeLongLong, + CIntTypeULongLong, + + CIntTypeCount, +}; + struct CodeGen { LLVMModuleRef module; ZigList errors; @@ -1072,7 +1085,7 @@ struct CodeGen { struct { TypeTableEntry *entry_bool; TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64] - TypeTableEntry *entry_c_int[8]; + TypeTableEntry *entry_c_int[CIntTypeCount]; TypeTableEntry *entry_c_long_double; TypeTableEntry *entry_u8; TypeTableEntry *entry_u16; @@ -1196,16 +1209,6 @@ struct BlockContext { Buf *c_import_buf; }; -enum CIntType { - CIntTypeShort, - CIntTypeUShort, - CIntTypeInt, - CIntTypeUInt, - CIntTypeLong, - CIntTypeULong, - CIntTypeLongLong, - CIntTypeULongLong, -}; #endif diff --git a/src/analyze.cpp b/src/analyze.cpp index 700a3e7a85..02b1c3010e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2159,6 +2159,7 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry &get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val; if (field_val->ok) { const_val->data.x_struct.fields[field_index] = field_val; + const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var; } else { const_val->ok = false; } @@ -2197,6 +2198,8 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val; if (elem_const_val->ok) { const_val->data.x_array.fields[i] = elem_const_val; + const_val->depends_on_compile_var = const_val->depends_on_compile_var || + elem_const_val->depends_on_compile_var; } else { const_val->ok = false; } @@ -2431,9 +2434,12 @@ static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, return g->builtin_types.entry_pure_error; } -static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value) { +static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value, + bool depends_on_compile_var) +{ Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; + expr->const_val.depends_on_compile_var = depends_on_compile_var; expr->const_val.data.x_bool = value; return g->builtin_types.entry_bool; } @@ -2817,7 +2823,8 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im zig_unreachable(); } - return resolve_expr_const_val_as_bool(g, node, answer); + bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; + return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var); } static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -2844,7 +2851,8 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i } bool answer = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op_type, op2_val->data.x_bool); - return resolve_expr_const_val_as_bool(g, node, answer); + bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; + return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var); } static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -3001,6 +3009,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; + const_val->depends_on_compile_var = op1_val->depends_on_compile_var || + op2_val->depends_on_compile_var; ConstExprValue *all_fields = allocate(2); ConstExprValue *ptr_field = &all_fields[0]; @@ -3444,51 +3454,111 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor return g->builtin_types.entry_unreachable; } -static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *then_block, AstNode *else_node, AstNode *parent_node) +static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node, + AstNode **then_node, AstNode **else_node, bool cond_is_const, bool cond_bool_val) { - TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, then_block); - - TypeTableEntry *else_type; - if (else_node) { - else_type = analyze_expression(g, import, context, expected_type, else_node); - } else { - else_type = resolve_type_compatibility(g, import, context, parent_node, expected_type, - g->builtin_types.entry_void); + if (!*else_node) { + *else_node = create_ast_void_node(g, import, node); + normalize_parent_ptrs(node); } + TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, *then_node); + TypeTableEntry *else_type = analyze_expression(g, import, context, expected_type, *else_node); + if (then_type->id == TypeTableEntryIdInvalid || else_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } + + TypeTableEntry *result_type; if (expected_type) { - return (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type; + result_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type; } else { - AstNode *op_nodes[] = {then_block, else_node}; + AstNode *op_nodes[] = {*then_node, *else_node}; TypeTableEntry *op_types[] = {then_type, else_type}; - return resolve_peer_type_compatibility(g, import, context, parent_node, op_nodes, op_types, 2); + result_type = resolve_peer_type_compatibility(g, import, context, node, op_nodes, op_types, 2); } + + if (!cond_is_const) { + return result_type; + } + + ConstExprValue *other_const_val; + if (cond_bool_val) { + other_const_val = &get_resolved_expr(*then_node)->const_val; + } else { + other_const_val = &get_resolved_expr(*else_node)->const_val; + } + if (!other_const_val->ok) { + return result_type; + } + + ConstExprValue *const_val = &get_resolved_expr(node)->const_val; + *const_val = *other_const_val; + return result_type; } static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { - analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_bool_expr.condition); + AstNode **cond = &node->data.if_bool_expr.condition; + TypeTableEntry *cond_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *cond); - return analyze_if_then_else(g, import, context, expected_type, - node->data.if_bool_expr.then_block, - node->data.if_bool_expr.else_node, - node); + if (cond_type->id == TypeTableEntryIdInvalid) { + return cond_type; + } + + ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val; + if (cond_val->ok && !cond_val->depends_on_compile_var) { + const char *str_val = cond_val->data.x_bool ? "true" : "false"; + add_node_error(g, first_executing_node(*cond), + buf_sprintf("condition is always %s; unnecessary if statement", str_val)); + } + + bool cond_is_const = cond_val->ok; + bool cond_bool_val = cond_val->data.x_bool; + + AstNode **then_node = &node->data.if_bool_expr.then_block; + AstNode **else_node = &node->data.if_bool_expr.else_node; + + return analyze_if(g, import, context, expected_type, node, + then_node, else_node, cond_is_const, cond_bool_val); } -static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, +static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeIfVarExpr); - BlockContext *child_context = new_block_context(node, context); + BlockContext *child_context = new_block_context(node, parent_context); analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true); + VariableTableEntry *var = node->data.if_var_expr.var_decl.variable; + if (var->type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } + AstNode *var_expr_node = node->data.if_var_expr.var_decl.expr; + ConstExprValue *var_const_val = &get_resolved_expr(var_expr_node)->const_val; + bool cond_is_const = var_const_val->ok; + bool cond_bool_val = cond_is_const ? (var_const_val->data.x_maybe != nullptr) : false; - return analyze_if_then_else(g, import, child_context, expected_type, - node->data.if_var_expr.then_block, node->data.if_var_expr.else_node, node); + + AstNode **then_node = &node->data.if_var_expr.then_block; + AstNode **else_node = &node->data.if_var_expr.else_node; + + return analyze_if(g, import, child_context, expected_type, + node, then_node, else_node, cond_is_const, cond_bool_val); +} + +static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) { + assert(int_type->id == TypeTableEntryIdInt); + + for (int i = 0; i < CIntTypeCount; i += 1) { + if (int_type == g->builtin_types.entry_c_int[i]) { + return true; + } + } + return false; } static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -3504,6 +3574,7 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor } else if (type_entry->id == TypeTableEntryIdInt) { ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; + const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry); if (is_max) { if (type_entry->data.integral.is_signed) { int64_t val; @@ -3558,7 +3629,7 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor zig_panic("TODO analyze_min_max_value float"); return type_entry; } else if (type_entry->id == TypeTableEntryIdBool) { - return resolve_expr_const_val_as_bool(g, node, is_max); + return resolve_expr_const_val_as_bool(g, node, is_max, false); } else { add_node_error(g, node, buf_sprintf(err_format, buf_ptr(&type_entry->name))); @@ -3573,6 +3644,8 @@ static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *ex if (!other_val->ok) { return; } + const_val->depends_on_compile_var = other_val->depends_on_compile_var; + assert(other_val != const_val); switch (node->data.fn_call_expr.cast_op) { case CastOpNoCast: @@ -4132,11 +4205,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry const_val->depends_on_compile_var = true; if (buf_eql_str(&var_name, "is_big_endian")) { - return resolve_expr_const_val_as_bool(g, node, g->is_big_endian); + return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true); } else if (buf_eql_str(&var_name, "is_release")) { - return resolve_expr_const_val_as_bool(g, node, g->is_release_build); + return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true); } else if (buf_eql_str(&var_name, "is_test")) { - return resolve_expr_const_val_as_bool(g, node, g->is_test_build); + return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true); } else { add_node_error(g, *str_node, buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name))); @@ -4353,7 +4426,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo } bool answer = !target_const_val->data.x_bool; - return resolve_expr_const_val_as_bool(g, node, answer); + return resolve_expr_const_val_as_bool(g, node, answer, target_const_val->depends_on_compile_var); } case PrefixOpBinNot: { @@ -4390,6 +4463,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; + const_val->depends_on_compile_var = target_const_val->depends_on_compile_var; bignum_negate(&const_val->data.x_bignum, &target_const_val->data.x_bignum); return expr_type; } else { @@ -4880,7 +4954,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, node->data.char_literal.value); break; case NodeTypeBoolLiteral: - return_type = resolve_expr_const_val_as_bool(g, node, node->data.bool_literal.value); + return_type = resolve_expr_const_val_as_bool(g, node, node->data.bool_literal.value, false); break; case NodeTypeNullLiteral: return_type = analyze_null_literal_expr(g, import, context, expected_type, node); diff --git a/src/codegen.cpp b/src/codegen.cpp index fb4c92dc4b..01ef7f2fef 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1737,81 +1737,60 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { zig_unreachable(); } -static LLVMValueRef gen_defer(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypeDefer); - - - return nullptr; -} - static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value, AstNode *then_node, AstNode *else_node) { + assert(then_node); + assert(else_node); + TypeTableEntry *then_type = get_expr_type(then_node); - bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable && - then_type->id != TypeTableEntryIdVoid); + TypeTableEntry *else_type = get_expr_type(else_node); - if (else_node) { - LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); - LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else"); - - LLVMBasicBlockRef endif_block; - bool then_endif_reachable = get_expr_type(then_node)->id != TypeTableEntryIdUnreachable; - bool else_endif_reachable = get_expr_type(else_node)->id != TypeTableEntryIdUnreachable; - if (then_endif_reachable || else_endif_reachable) { - endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); - } - - LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); - - LLVMPositionBuilderAtEnd(g->builder, then_block); - LLVMValueRef then_expr_result = gen_expr(g, then_node); - if (then_endif_reachable) { - LLVMBuildBr(g->builder, endif_block); - } - LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder); - - LLVMPositionBuilderAtEnd(g->builder, else_block); - LLVMValueRef else_expr_result = gen_expr(g, else_node); - if (else_endif_reachable) { - LLVMBuildBr(g->builder, endif_block); - } - LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder); - - if (then_endif_reachable || else_endif_reachable) { - LLVMPositionBuilderAtEnd(g->builder, endif_block); - if (use_expr_value) { - LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), ""); - LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result}; - LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block}; - LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); - - return phi; - } - } - - return nullptr; - } - - assert(!use_expr_value || then_type->id == TypeTableEntryIdErrorUnion); + bool use_then_value = type_has_bits(then_type); + bool use_else_value = type_has_bits(else_type); LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); - LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); + LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else"); - LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block); + LLVMBasicBlockRef endif_block; + bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable; + bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable; + if (then_endif_reachable || else_endif_reachable) { + endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); + } + + LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); LLVMPositionBuilderAtEnd(g->builder, then_block); - gen_expr(g, then_node); - if (get_expr_type(then_node)->id != TypeTableEntryIdUnreachable) + LLVMValueRef then_expr_result = gen_expr(g, then_node); + if (then_endif_reachable) { LLVMBuildBr(g->builder, endif_block); - - LLVMPositionBuilderAtEnd(g->builder, endif_block); - - if (use_expr_value) { - return LLVMConstNull(g->err_tag_type->type_ref); - } else { - return nullptr; } + LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, else_block); + LLVMValueRef else_expr_result = gen_expr(g, else_node); + if (else_endif_reachable) { + LLVMBuildBr(g->builder, endif_block); + } + LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder); + + if (then_endif_reachable || else_endif_reachable) { + LLVMPositionBuilderAtEnd(g->builder, endif_block); + if (use_then_value && use_else_value) { + LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), ""); + LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result}; + LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block}; + LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); + return phi; + } else if (use_then_value) { + return then_expr_result; + } else if (use_else_value) { + return else_expr_result; + } + } + + return nullptr; } static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) { @@ -1866,14 +1845,6 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { return return_value; } -//static int block_exit_path_count(BlockContext *block_context) { -// int sum = 0; -// for (int i = 0; i < BlockExitPathCount; i += 1) { -// sum += block_context->block_exit_paths[i] ? 1 : 0; -// } -// return sum; -//} - static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { assert(block_node->type == NodeTypeBlock); @@ -2553,7 +2524,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeReturnExpr: return gen_return_expr(g, node); case NodeTypeDefer: - return gen_defer(g, node); + // nothing to do + return nullptr; case NodeTypeVariableDeclaration: return gen_var_decl_expr(g, node); case NodeTypePrefixOpExpr: @@ -3191,6 +3163,7 @@ static const CIntTypeInfo c_int_type_infos[] = { static int get_c_type_size_in_bits(CodeGen *g, CIntType id) { // TODO other architectures besides x86_64 + // other operating systems besides linux switch (id) { case CIntTypeShort: case CIntTypeUShort: @@ -3203,6 +3176,8 @@ static int get_c_type_size_in_bits(CodeGen *g, CIntType id) { case CIntTypeLongLong: case CIntTypeULongLong: return 64; + case CIntTypeCount: + zig_unreachable(); } zig_unreachable(); } diff --git a/std/test_runner.zig b/std/test_runner.zig index 625915bb71..6715371cd2 100644 --- a/std/test_runner.zig +++ b/std/test_runner.zig @@ -16,6 +16,7 @@ pub fn run_tests() -> %void { %%stderr.print_str(" "); %%stderr.print_str(test_fn.name); %%stderr.print_str("..."); + %%stderr.flush(); test_fn.func(); diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 8234312370..fc60d1a3c4 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -225,26 +225,6 @@ pub fn foo_function() -> bool { )SOURCE"); } - add_simple_case("if statements", R"SOURCE( -import "std.zig"; - -pub fn main(args: [][]u8) -> %void { - if (1 != 0) { - %%stdout.printf("1 is true\n"); - } else { - %%stdout.printf("1 is false\n"); - } - if (0 != 0) { - %%stdout.printf("0 is true\n"); - } else if (1 - 1 != 0) { - %%stdout.printf("1 - 1 is true\n"); - } - if (!(0 != 0)) { - %%stdout.printf("!0 is true\n"); - } -} - )SOURCE", "1 is true\n!0 is true\n"); - add_simple_case("params", R"SOURCE( import "std.zig"; @@ -259,46 +239,6 @@ pub fn main(args: [][]u8) -> %void { } )SOURCE", "pass\n"); - add_simple_case("local variables", R"SOURCE( -import "std.zig"; - -pub fn main(args: [][]u8) -> %void { - const a : i32 = 1; - const b = i32(2); - if (a + b == 3) { - %%stdout.printf("OK\n"); - } -} - )SOURCE", "OK\n"); - - add_simple_case("bool literals", R"SOURCE( -import "std.zig"; - -pub fn main(args: [][]u8) -> %void { - if (true) { %%stdout.printf("OK 1\n"); } - if (false) { %%stdout.printf("BAD 1\n"); } - if (!true) { %%stdout.printf("BAD 2\n"); } - if (!false) { %%stdout.printf("OK 2\n"); } -} - )SOURCE", "OK 1\nOK 2\n"); - - add_simple_case("separate block scopes", R"SOURCE( -import "std.zig"; - -pub fn main(args: [][]u8) -> %void { - if (true) { - const no_conflict : i32 = 5; - if (no_conflict == 5) { %%stdout.printf("OK 1\n"); } - } - - const c = { - const no_conflict = i32(10); - no_conflict - }; - if (c == 10) { %%stdout.printf("OK 2\n"); } -} - )SOURCE", "OK 1\nOK 2\n"); - add_simple_case("void parameters", R"SOURCE( import "std.zig"; @@ -314,48 +254,6 @@ fn void_fun(a : i32, b : void, c : i32) { } )SOURCE", "OK\n"); - add_simple_case("void struct fields", R"SOURCE( -import "std.zig"; -struct Foo { - a : void, - b : i32, - c : void, -} -pub fn main(args: [][]u8) -> %void { - const foo = Foo { - .a = void{}, - .b = 1, - .c = void{}, - }; - if (foo.b != 1) { - %%stdout.printf("BAD\n"); - } - if (@sizeof(Foo) != 4) { - %%stdout.printf("BAD\n"); - } - %%stdout.printf("OK\n"); -} - - )SOURCE", "OK\n"); - - add_simple_case("void arrays", R"SOURCE( -import "std.zig"; - -pub fn main(args: [][]u8) -> %void { - var array: [4]void = undefined; - array[0] = void{}; - array[1] = array[2]; - if (@sizeof(@typeof(array)) != 0) { - %%stdout.printf("BAD sizeof\n"); - } - if (array.len != 4) { - %%stdout.printf("BAD len\n"); - } - %%stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - - add_simple_case("mutable local variables", R"SOURCE( import "std.zig"; @@ -414,27 +312,6 @@ pub fn main(args: [][]u8) -> %void { )SOURCE", "Hello, world!\n"); - add_simple_case("a + b + c", R"SOURCE( -import "std.zig"; - -pub fn main(args: [][]u8) -> %void { - if (false || false || false) { %%stdout.printf("BAD 1\n"); } - if (true && true && false) { %%stdout.printf("BAD 2\n"); } - if (1 | 2 | 4 != 7) { %%stdout.printf("BAD 3\n"); } - if (3 ^ 6 ^ 8 != 13) { %%stdout.printf("BAD 4\n"); } - if (7 & 14 & 28 != 4) { %%stdout.printf("BAD 5\n"); } - if (9 << 1 << 2 != 9 << 3) { %%stdout.printf("BAD 6\n"); } - if (90 >> 1 >> 2 != 90 >> 3) { %%stdout.printf("BAD 7\n"); } - if (100 - 1 + 1000 != 1099) { %%stdout.printf("BAD 8\n"); } - if (5 * 4 / 2 % 3 != 1) { %%stdout.printf("BAD 9\n"); } - if (i32(i32(5)) != 5) { %%stdout.printf("BAD 10\n"); } - if (!!false) { %%stdout.printf("BAD 11\n"); } - if (i32(7) != --(i32(7))) { %%stdout.printf("BAD 12\n"); } - - %%stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - add_simple_case("short circuit", R"SOURCE( import "std.zig"; @@ -729,39 +606,6 @@ pub fn main(args: [][]u8) -> %void { } )SOURCE", "loop\nloop\nloop\nloop\n"); - add_simple_case("maybe type", R"SOURCE( -import "std.zig"; -pub fn main(args: [][]u8) -> %void { - const x : ?bool = true; - - if (const y ?= x) { - if (y) { - %%stdout.printf("x is true\n"); - } else { - %%stdout.printf("x is false\n"); - } - } else { - %%stdout.printf("x is none\n"); - } - - const next_x : ?i32 = null; - - const z = next_x ?? 1234; - - if (z != 1234) { - %%stdout.printf("BAD\n"); - } - - const final_x : ?i32 = 13; - - const num = final_x ?? unreachable{}; - - if (num != 13) { - %%stdout.printf("BAD\n"); - } -} - )SOURCE", "x is true\n"); - add_simple_case("implicit cast after unreachable", R"SOURCE( import "std.zig"; pub fn main(args: [][]u8) -> %void { @@ -971,73 +815,6 @@ fn print_ok(val: @typeof(x)) -> @typeof(foo) { const foo : i32 = 0; )SOURCE", "OK\n"); - add_simple_case("enum type", R"SOURCE( -import "std.zig"; - -struct Point { - x: u64, - y: u64, -} - -enum Foo { - One: i32, - Two: Point, - Three: void, -} - -enum Bar { - A, - B, - C, - D, -} - -pub fn main(args: [][]u8) -> %void { - const foo1 = Foo.One(13); - const foo2 = Foo.Two(Point { .x = 1234, .y = 5678, }); - const bar = Bar.B; - - if (bar != Bar.B) { - %%stdout.printf("BAD 1\n"); - } - - if (@member_count(Foo) != 3) { - %%stdout.printf("BAD 2\n"); - } - - if (@member_count(Bar) != 4) { - %%stdout.printf("BAD 3\n"); - } - - if (@sizeof(Foo) != 24) { - %%stdout.printf("BAD 4\n"); - } - if (@sizeof(Bar) != 1) { - %%stdout.printf("BAD 5\n"); - } - - %%stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - - add_simple_case("array literal", R"SOURCE( -import "std.zig"; - -pub fn main(args: [][]u8) -> %void { - const HEX_MULT = []u16{4096, 256, 16, 1}; - - if (HEX_MULT.len != 4) { - %%stdout.printf("BAD\n"); - } - - if (HEX_MULT[1] != 256) { - %%stdout.printf("BAD\n"); - } - - %%stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - add_simple_case("nested arrays", R"SOURCE( import "std.zig"; @@ -1092,23 +869,6 @@ fn fn3() -> u32 {7} fn fn4() -> u32 {8} )SOURCE", "5\n6\n7\n8\n"); - add_simple_case("const number literal", R"SOURCE( -import "std.zig"; - -const ten = 10; - -pub fn main(args: [][]u8) -> %void { - const one = 1; - const eleven = ten + one; - - if (eleven != 11) { - %%stdout.printf("BAD\n"); - } - - %%stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - add_simple_case("statically initialized struct", R"SOURCE( import "std.zig"; struct Foo { @@ -1139,21 +899,6 @@ pub fn main(args: [][]u8) -> %void { } )SOURCE", "OK\n"); - add_simple_case("error values", R"SOURCE( -import "std.zig"; -error err1; -error err2; -pub fn main(args: [][]u8) -> %void { - const a = i32(error.err1); - const b = i32(error.err2); - if (a == b) { - %%stdout.printf("BAD\n"); - } - - %%stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - add_simple_case("return with implicit cast from while loop", R"SOURCE( import "std.zig"; pub fn main(args: [][]u8) -> %void { @@ -1988,6 +1733,13 @@ struct Foo { } fn get() -> isize { 1 } )SOURCE", 1, ".tmp_source.zig:3:9: error: unable to evaluate constant expression"); + + + add_compile_fail_case("unnecessary if statement", R"SOURCE( +fn f() { + if (true) { } +} + )SOURCE", 1, ".tmp_source.zig:3:9: error: condition is always true; unnecessary if statement"); } ////////////////////////////////////////////////////////////////////////////// diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 7a73807743..d117b19e8e 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -5,7 +5,6 @@ fn empty_function() {} - /** * multi line doc comment */ @@ -19,6 +18,200 @@ fn comments() { fn comments_f1(s: []u8) {} +#attribute("test") +fn if_statements() { + should_be_equal(1, 1); + first_eql_third(2, 1, 2); +} +fn should_be_equal(a: i32, b: i32) { + if (a != b) { + unreachable{}; + } else { + return; + } +} +fn first_eql_third(a: i32, b: i32, c: i32) { + if (a == b) { + unreachable{}; + } else if (b == c) { + unreachable{}; + } else if (a == c) { + return; + } else { + unreachable{}; + } +} + + +#attribute("test") +fn local_variables() { + test_loc_vars(2); +} +fn test_loc_vars(b: i32) { + const a: i32 = 1; + if (a + b != 3) unreachable{}; +} + +#attribute("test") +fn bool_literals() { + should_be_true(true); + should_be_false(false); +} +fn should_be_true(b: bool) { + if (!b) unreachable{}; +} +fn should_be_false(b: bool) { + if (b) unreachable{}; +} + + +#attribute("test") +fn separate_block_scopes() { + { + const no_conflict : i32 = 5; + assert(no_conflict == 5); + } + + const c = { + const no_conflict = i32(10); + no_conflict + }; + assert(c == 10); +} + + +#attribute("test") +fn void_struct_fields() { + const foo = VoidStructFieldsFoo { + .a = void{}, + .b = 1, + .c = void{}, + }; + assert(foo.b == 1); + assert(@sizeof(VoidStructFieldsFoo) == 4); +} +struct VoidStructFieldsFoo { + a : void, + b : i32, + c : void, +} + + +#attribute("test") +fn void_arrays() { + var array: [4]void = undefined; + array[0] = void{}; + array[1] = array[2]; + assert(@sizeof(@typeof(array)) == 0); + assert(array.len == 4); +} + + +#attribute("test") +fn three_expr_in_a_row() { + assert_false(false || false || false); + assert_false(true && true && false); + assert_false(1 | 2 | 4 != 7); + assert_false(3 ^ 6 ^ 8 != 13); + assert_false(7 & 14 & 28 != 4); + assert_false(9 << 1 << 2 != 9 << 3); + assert_false(90 >> 1 >> 2 != 90 >> 3); + assert_false(100 - 1 + 1000 != 1099); + assert_false(5 * 4 / 2 % 3 != 1); + assert_false(i32(i32(5)) != 5); + assert_false(!!false); + assert_false(i32(7) != --(i32(7))); +} +fn assert_false(b: bool) { + assert(!b); +} + + +#attribute("test") +fn maybe_type() { + const x : ?bool = true; + + if (const y ?= x) { + if (y) { + // OK + } else { + unreachable{}; + } + } else { + unreachable{}; + } + + const next_x : ?i32 = null; + + const z = next_x ?? 1234; + + assert(z == 1234); + + const final_x : ?i32 = 13; + + const num = final_x ?? unreachable{}; + + assert(num == 13); +} + + +#attribute("test") +fn enum_type() { + const foo1 = EnumTypeFoo.One(13); + const foo2 = EnumTypeFoo.Two(EnumType { .x = 1234, .y = 5678, }); + const bar = EnumTypeBar.B; + + assert(bar == EnumTypeBar.B); + assert(@member_count(EnumTypeFoo) == 3); + assert(@member_count(EnumTypeBar) == 4); + assert(@sizeof(EnumTypeFoo) == 24); + assert(@sizeof(EnumTypeBar) == 1); +} +struct EnumType { + x: u64, + y: u64, +} +enum EnumTypeFoo { + One: i32, + Two: EnumType, + Three: void, +} +enum EnumTypeBar { + A, + B, + C, + D, +} + + +#attribute("test") +fn array_literal() { + const HEX_MULT = []u16{4096, 256, 16, 1}; + + assert(HEX_MULT.len == 4); + assert(HEX_MULT[1] == 256); +} + + +#attribute("test") +fn const_number_literal() { + const one = 1; + const eleven = ten + one; + + assert(eleven == 11); +} +const ten = 10; + + +#attribute("test") +fn error_values() { + const a = i32(error.err1); + const b = i32(error.err2); + assert(a != b); +} +error err1; +error err2; + #attribute("test") @@ -42,11 +235,14 @@ fn call_struct_field(foo: Foo) -> i32 { #attribute("test") fn redefinition_of_error_values_allowed() { - if (error.AnError == error.SecondError) unreachable{} + should_be_not_equal(error.AnError, error.SecondError); } error AnError; error AnError; error SecondError; +fn should_be_not_equal(a: error, b: error) { + if (a == b) unreachable{} +} @@ -98,14 +294,14 @@ fn continue_in_for_loop() { fn cast_bool_to_int() { const t = true; const f = false; - if (i32(t) != i32(1)) unreachable{} - if (i32(f) != i32(0)) unreachable{} + assert(i32(t) == i32(1)); + assert(i32(f) == i32(0)); non_const_cast_bool_to_int(t, f); } fn non_const_cast_bool_to_int(t: bool, f: bool) { - if (i32(t) != i32(1)) unreachable{} - if (i32(f) != i32(0)) unreachable{} + assert(i32(t) == i32(1)); + assert(i32(f) == i32(0)); } @@ -240,7 +436,7 @@ fn const_expr_eval_on_single_expr_blocks_fn(x: i32, b: bool) -> i32 { #attribute("test") fn builtin_const_eval() { const x : i32 = @const_eval(1 + 2 + 3); - if (x != @const_eval(6)) unreachable{}; + assert(x == @const_eval(6)); } #attribute("test") @@ -279,3 +475,10 @@ struct ArrayDotLenConstExpr { y: [@const_eval(some_array.len)]u8, } const some_array = []u8 {0, 1, 2, 3}; + + + + +fn assert(b: bool) { + if (!b) unreachable{} +}