| author | |
| committer | |
| log | c6605cba8375871a145896f9cf1090e9c823214a |
| tree | f6309057804dc9fd6d5fa10feafb9d8f6a44a713 |
| parent | 6de33ded81981554ccffc3ecbfcd5b0f628cf502 |
closes #291
This changes the error message "return value ignored" to "expression value is ignored".
This is because this error also applies to {1;}, which has no function calls.
Also fix ignored expression values in std and test.
This caught a bug in debug.readAllocBytes where an early Eof error would have been missed.
See #219.11 files changed, 80 insertions(+), 22 deletions(-)
src/all_types.hpp+7| ... | ... | @@ -1738,6 +1738,7 @@ enum IrInstructionId { |
| 1738 | 1738 | IrInstructionIdIntToErr, |
| 1739 | 1739 | IrInstructionIdErrToInt, |
| 1740 | 1740 | IrInstructionIdCheckSwitchProngs, |
| 1741 | IrInstructionIdCheckStatementIsVoid, | |
| 1741 | 1742 | IrInstructionIdTestType, |
| 1742 | 1743 | IrInstructionIdTypeName, |
| 1743 | 1744 | IrInstructionIdCanImplicitCast, |
| ... | ... | @@ -2435,6 +2436,12 @@ struct IrInstructionCheckSwitchProngs { |
| 2435 | 2436 | size_t range_count; |
| 2436 | 2437 | }; |
| 2437 | 2438 | |
| 2439 | struct IrInstructionCheckStatementIsVoid { | |
| 2440 | IrInstruction base; | |
| 2441 | ||
| 2442 | IrInstruction *statement_value; | |
| 2443 | }; | |
| 2444 | ||
| 2438 | 2445 | struct IrInstructionTestType { |
| 2439 | 2446 | IrInstruction base; |
| 2440 | 2447 |
src/codegen.cpp+1| ... | ... | @@ -2885,6 +2885,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 2885 | 2885 | case IrInstructionIdTestComptime: |
| 2886 | 2886 | case IrInstructionIdGeneratedCode: |
| 2887 | 2887 | case IrInstructionIdCheckSwitchProngs: |
| 2888 | case IrInstructionIdCheckStatementIsVoid: | |
| 2888 | 2889 | case IrInstructionIdTestType: |
| 2889 | 2890 | case IrInstructionIdTypeName: |
| 2890 | 2891 | case IrInstructionIdCanImplicitCast: |
src/ir.cpp+48-13| ... | ... | @@ -510,6 +510,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProng |
| 510 | 510 | return IrInstructionIdCheckSwitchProngs; |
| 511 | 511 | } |
| 512 | 512 | |
| 513 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckStatementIsVoid *) { | |
| 514 | return IrInstructionIdCheckStatementIsVoid; | |
| 515 | } | |
| 516 | ||
| 513 | 517 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTestType *) { |
| 514 | 518 | return IrInstructionIdTestType; |
| 515 | 519 | } |
| ... | ... | @@ -2803,6 +2807,15 @@ static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionChec |
| 2803 | 2807 | return nullptr; |
| 2804 | 2808 | } |
| 2805 | 2809 | |
| 2810 | static IrInstruction *ir_instruction_checkstatementisvoid_get_dep(IrInstructionCheckStatementIsVoid *instruction, | |
| 2811 | size_t index) | |
| 2812 | { | |
| 2813 | switch (index) { | |
| 2814 | case 0: return instruction->statement_value; | |
| 2815 | default: return nullptr; | |
| 2816 | } | |
| 2817 | } | |
| 2818 | ||
| 2806 | 2819 | static IrInstruction *ir_instruction_testtype_get_dep(IrInstructionTestType *instruction, size_t index) { |
| 2807 | 2820 | switch (index) { |
| 2808 | 2821 | case 0: return instruction->type_value; |
| ... | ... | @@ -3060,6 +3073,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 3060 | 3073 | return ir_instruction_errtoint_get_dep((IrInstructionErrToInt *) instruction, index); |
| 3061 | 3074 | case IrInstructionIdCheckSwitchProngs: |
| 3062 | 3075 | return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index); |
| 3076 | case IrInstructionIdCheckStatementIsVoid: | |
| 3077 | return ir_instruction_checkstatementisvoid_get_dep((IrInstructionCheckStatementIsVoid *) instruction, index); | |
| 3063 | 3078 | case IrInstructionIdTestType: |
| 3064 | 3079 | return ir_instruction_testtype_get_dep((IrInstructionTestType *) instruction, index); |
| 3065 | 3080 | case IrInstructionIdTypeName: |
| ... | ... | @@ -3333,6 +3348,18 @@ static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) { |
| 3333 | 3348 | return nullptr; |
| 3334 | 3349 | } |
| 3335 | 3350 | |
| 3351 | static IrInstruction *ir_build_check_statement_is_void(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 3352 | IrInstruction* statement_value) | |
| 3353 | { | |
| 3354 | IrInstructionCheckStatementIsVoid *instruction = ir_build_instruction<IrInstructionCheckStatementIsVoid>( | |
| 3355 | irb, scope, source_node); | |
| 3356 | instruction->statement_value = statement_value; | |
| 3357 | ||
| 3358 | ir_ref_instruction(statement_value, irb->current_basic_block); | |
| 3359 | ||
| 3360 | return &instruction->base; | |
| 3361 | } | |
| 3362 | ||
| 3336 | 3363 | static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) { |
| 3337 | 3364 | assert(block_node->type == NodeTypeBlock); |
| 3338 | 3365 | |
| ... | ... | @@ -3410,12 +3437,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode |
| 3410 | 3437 | return_value = statement_value; |
| 3411 | 3438 | } else { |
| 3412 | 3439 | // there are more statements ahead of this one. this statement's value must be void |
| 3413 | TypeTableEntry *instruction_type = statement_value->value.type; | |
| 3414 | if (instruction_type && | |
| 3415 | instruction_type->id != TypeTableEntryIdInvalid && | |
| 3416 | instruction_type->id != TypeTableEntryIdVoid && | |
| 3417 | instruction_type->id != TypeTableEntryIdUnreachable) { | |
| 3418 | add_node_error(irb->codegen, statement_node, buf_sprintf("expression valued ignored")); | |
| 3440 | if (statement_value != irb->codegen->invalid_instruction) { | |
| 3441 | ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value)); | |
| 3419 | 3442 | } |
| 3420 | 3443 | } |
| 3421 | 3444 | } |
| ... | ... | @@ -12640,6 +12663,22 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira |
| 12640 | 12663 | return ira->codegen->builtin_types.entry_void; |
| 12641 | 12664 | } |
| 12642 | 12665 | |
| 12666 | static TypeTableEntry *ir_analyze_instruction_check_statement_is_void(IrAnalyze *ira, | |
| 12667 | IrInstructionCheckStatementIsVoid *instruction) | |
| 12668 | { | |
| 12669 | IrInstruction *statement_value = instruction->statement_value; | |
| 12670 | TypeTableEntry *statement_type = statement_value->value.type; | |
| 12671 | if (type_is_invalid(statement_type)) | |
| 12672 | return ira->codegen->builtin_types.entry_invalid; | |
| 12673 | ||
| 12674 | if (statement_type->id != TypeTableEntryIdVoid) { | |
| 12675 | ir_add_error(ira, &instruction->base, buf_sprintf("expression value is ignored")); | |
| 12676 | } | |
| 12677 | ||
| 12678 | ir_build_const_from(ira, &instruction->base); | |
| 12679 | return ira->codegen->builtin_types.entry_void; | |
| 12680 | } | |
| 12681 | ||
| 12643 | 12682 | static TypeTableEntry *ir_analyze_instruction_test_type(IrAnalyze *ira, IrInstructionTestType *instruction) { |
| 12644 | 12683 | IrInstruction *type_value = instruction->type_value->other; |
| 12645 | 12684 | TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); |
| ... | ... | @@ -12987,6 +13026,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 12987 | 13026 | return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction); |
| 12988 | 13027 | case IrInstructionIdCheckSwitchProngs: |
| 12989 | 13028 | return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction); |
| 13029 | case IrInstructionIdCheckStatementIsVoid: | |
| 13030 | return ir_analyze_instruction_check_statement_is_void(ira, (IrInstructionCheckStatementIsVoid *)instruction); | |
| 12990 | 13031 | case IrInstructionIdTestType: |
| 12991 | 13032 | return ir_analyze_instruction_test_type(ira, (IrInstructionTestType *)instruction); |
| 12992 | 13033 | case IrInstructionIdCanImplicitCast: |
| ... | ... | @@ -13026,13 +13067,6 @@ static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *ins |
| 13026 | 13067 | instruction_type->id == TypeTableEntryIdUnreachable); |
| 13027 | 13068 | instruction->other = instruction; |
| 13028 | 13069 | } |
| 13029 | if (instruction_type->id != TypeTableEntryIdInvalid && | |
| 13030 | instruction_type->id != TypeTableEntryIdVoid && | |
| 13031 | instruction_type->id != TypeTableEntryIdUnreachable && | |
| 13032 | instruction->ref_count == 0) | |
| 13033 | { | |
| 13034 | ir_add_error(ira, instruction, buf_sprintf("return value ignored")); | |
| 13035 | } | |
| 13036 | 13070 | |
| 13037 | 13071 | return instruction_type; |
| 13038 | 13072 | } |
| ... | ... | @@ -13123,6 +13157,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 13123 | 13157 | case IrInstructionIdBreakpoint: |
| 13124 | 13158 | case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free |
| 13125 | 13159 | case IrInstructionIdCheckSwitchProngs: |
| 13160 | case IrInstructionIdCheckStatementIsVoid: | |
| 13126 | 13161 | case IrInstructionIdSetGlobalAlign: |
| 13127 | 13162 | case IrInstructionIdSetGlobalSection: |
| 13128 | 13163 | case IrInstructionIdSetGlobalLinkage: |
src/ir_print.cpp+9| ... | ... | @@ -814,6 +814,12 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP |
| 814 | 814 | fprintf(irp->f, ")"); |
| 815 | 815 | } |
| 816 | 816 | |
| 817 | static void ir_print_check_statement_is_void(IrPrint *irp, IrInstructionCheckStatementIsVoid *instruction) { | |
| 818 | fprintf(irp->f, "@checkStatementIsVoid("); | |
| 819 | ir_print_other_instruction(irp, instruction->statement_value); | |
| 820 | fprintf(irp->f, ")"); | |
| 821 | } | |
| 822 | ||
| 817 | 823 | static void ir_print_test_type(IrPrint *irp, IrInstructionTestType *instruction) { |
| 818 | 824 | fprintf(irp->f, "testtype "); |
| 819 | 825 | ir_print_other_instruction(irp, instruction->type_value); |
| ... | ... | @@ -1149,6 +1155,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1149 | 1155 | case IrInstructionIdCheckSwitchProngs: |
| 1150 | 1156 | ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction); |
| 1151 | 1157 | break; |
| 1158 | case IrInstructionIdCheckStatementIsVoid: | |
| 1159 | ir_print_check_statement_is_void(irp, (IrInstructionCheckStatementIsVoid *)instruction); | |
| 1160 | break; | |
| 1152 | 1161 | case IrInstructionIdTestType: |
| 1153 | 1162 | ir_print_test_type(irp, (IrInstructionTestType *)instruction); |
| 1154 | 1163 | break; |
std/buf_map.zig+2-2| ... | ... | @@ -31,14 +31,14 @@ pub const BufMap = struct { |
| 31 | 31 | test (self.hash_map.get(key)) |entry| { |
| 32 | 32 | const value_copy = %return self.copy(value); |
| 33 | 33 | %defer self.free(value_copy); |
| 34 | %return self.hash_map.put(key, value_copy); | |
| 34 | _ = %return self.hash_map.put(key, value_copy); | |
| 35 | 35 | self.free(entry.value); |
| 36 | 36 | } else { |
| 37 | 37 | const key_copy = %return self.copy(key); |
| 38 | 38 | %defer self.free(key_copy); |
| 39 | 39 | const value_copy = %return self.copy(value); |
| 40 | 40 | %defer self.free(value_copy); |
| 41 | %return self.hash_map.put(key_copy, value_copy); | |
| 41 | _ = %return self.hash_map.put(key_copy, value_copy); | |
| 42 | 42 | } |
| 43 | 43 | } |
| 44 | 44 |
std/buf_set.zig+1-1| ... | ... | @@ -28,7 +28,7 @@ pub const BufSet = struct { |
| 28 | 28 | if (self.hash_map.get(key) == null) { |
| 29 | 29 | const key_copy = %return self.copy(key); |
| 30 | 30 | %defer self.free(key_copy); |
| 31 | %return self.hash_map.put(key_copy, {}); | |
| 31 | _ = %return self.hash_map.put(key_copy, {}); | |
| 32 | 32 | } |
| 33 | 33 | } |
| 34 | 34 |
std/build.zig+5-2| ... | ... | @@ -415,20 +415,23 @@ pub const Builder = struct { |
| 415 | 415 | .value = UserValue.Scalar { value }, |
| 416 | 416 | .used = false, |
| 417 | 417 | })) |*prev_value| { |
| 418 | // option already exists | |
| 418 | 419 | switch (prev_value.value) { |
| 419 | 420 | UserValue.Scalar => |s| { |
| 421 | // turn it into a list | |
| 420 | 422 | var list = List([]const u8).init(self.allocator); |
| 421 | 423 | %%list.append(s); |
| 422 | 424 | %%list.append(value); |
| 423 | %%self.user_input_options.put(name, UserInputOption { | |
| 425 | _ = %%self.user_input_options.put(name, UserInputOption { | |
| 424 | 426 | .name = name, |
| 425 | 427 | .value = UserValue.List { list }, |
| 426 | 428 | .used = false, |
| 427 | 429 | }); |
| 428 | 430 | }, |
| 429 | 431 | UserValue.List => |*list| { |
| 432 | // append to the list | |
| 430 | 433 | %%list.append(value); |
| 431 | %%self.user_input_options.put(name, UserInputOption { | |
| 434 | _ = %%self.user_input_options.put(name, UserInputOption { | |
| 432 | 435 | .name = name, |
| 433 | 436 | .value = UserValue.List { *list }, |
| 434 | 437 | .used = false, |
std/debug.zig+1-1| ... | ... | @@ -217,7 +217,7 @@ fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 { |
| 217 | 217 | fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 { |
| 218 | 218 | const buf = %return global_allocator.alloc(u8, size); |
| 219 | 219 | %defer global_allocator.free(buf); |
| 220 | %return in_stream.read(buf); | |
| 220 | if (size < %return in_stream.read(buf)) return error.Eof; | |
| 221 | 221 | return buf; |
| 222 | 222 | } |
| 223 | 223 |
std/fmt.zig+1-1| ... | ... | @@ -386,7 +386,7 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: u |
| 386 | 386 | } |
| 387 | 387 | |
| 388 | 388 | test "testParseU64DigitTooBig" { |
| 389 | parseUnsigned(u64, "123a", 10) %% |err| { | |
| 389 | _ = parseUnsigned(u64, "123a", 10) %% |err| { | |
| 390 | 390 | if (err == error.InvalidChar) return; |
| 391 | 391 | unreachable; |
| 392 | 392 | }; |
test/cases/switch_prong_err_enum.zig+4-1| ... | ... | @@ -22,6 +22,9 @@ fn doThing(form_id: u64) -> %FormValue { |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | 24 | test "switchProngReturnsErrorEnum" { |
| 25 | %%doThing(17); | |
| 25 | switch (%%doThing(17)) { | |
| 26 | FormValue.Address => |payload| { assert(payload == 1); }, | |
| 27 | else => unreachable, | |
| 28 | } | |
| 26 | 29 | assert(read_count == 1); |
| 27 | 30 | } |
test/compile_errors.zig+1-1| ... | ... | @@ -1346,7 +1346,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 1346 | 1346 | \\ bar(); |
| 1347 | 1347 | \\} |
| 1348 | 1348 | \\fn bar() -> i32 { 0 } |
| 1349 | , ".tmp_source.zig:2:8: error: return value ignored"); | |
| 1349 | , ".tmp_source.zig:2:8: error: expression value is ignored"); | |
| 1350 | 1350 | |
| 1351 | 1351 | cases.add("integer literal on a non-comptime var", |
| 1352 | 1352 | \\export fn foo() { |