authorgravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2017-04-23 16:59:55-07:00
committergravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2017-04-23 21:15:15-07:00
logc6605cba8375871a145896f9cf1090e9c823214a
treef6309057804dc9fd6d5fa10feafb9d8f6a44a713
parent6de33ded81981554ccffc3ecbfcd5b0f628cf502

blocks check that their statements are void

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,6 +1738,7 @@ enum IrInstructionId {
1738 IrInstructionIdIntToErr,1738 IrInstructionIdIntToErr,
1739 IrInstructionIdErrToInt,1739 IrInstructionIdErrToInt,
1740 IrInstructionIdCheckSwitchProngs,1740 IrInstructionIdCheckSwitchProngs,
1741 IrInstructionIdCheckStatementIsVoid,
1741 IrInstructionIdTestType,1742 IrInstructionIdTestType,
1742 IrInstructionIdTypeName,1743 IrInstructionIdTypeName,
1743 IrInstructionIdCanImplicitCast,1744 IrInstructionIdCanImplicitCast,
...@@ -2435,6 +2436,12 @@ struct IrInstructionCheckSwitchProngs {...@@ -2435,6 +2436,12 @@ struct IrInstructionCheckSwitchProngs {
2435 size_t range_count;2436 size_t range_count;
2436};2437};
24372438
2439struct IrInstructionCheckStatementIsVoid {
2440 IrInstruction base;
2441
2442 IrInstruction *statement_value;
2443};
2444
2438struct IrInstructionTestType {2445struct IrInstructionTestType {
2439 IrInstruction base;2446 IrInstruction base;
24402447
src/codegen.cpp+1
...@@ -2885,6 +2885,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -2885,6 +2885,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
2885 case IrInstructionIdTestComptime:2885 case IrInstructionIdTestComptime:
2886 case IrInstructionIdGeneratedCode:2886 case IrInstructionIdGeneratedCode:
2887 case IrInstructionIdCheckSwitchProngs:2887 case IrInstructionIdCheckSwitchProngs:
2888 case IrInstructionIdCheckStatementIsVoid:
2888 case IrInstructionIdTestType:2889 case IrInstructionIdTestType:
2889 case IrInstructionIdTypeName:2890 case IrInstructionIdTypeName:
2890 case IrInstructionIdCanImplicitCast:2891 case IrInstructionIdCanImplicitCast:
src/ir.cpp+48-13
...@@ -510,6 +510,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProng...@@ -510,6 +510,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProng
510 return IrInstructionIdCheckSwitchProngs;510 return IrInstructionIdCheckSwitchProngs;
511}511}
512512
513static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckStatementIsVoid *) {
514 return IrInstructionIdCheckStatementIsVoid;
515}
516
513static constexpr IrInstructionId ir_instruction_id(IrInstructionTestType *) {517static constexpr IrInstructionId ir_instruction_id(IrInstructionTestType *) {
514 return IrInstructionIdTestType;518 return IrInstructionIdTestType;
515}519}
...@@ -2803,6 +2807,15 @@ static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionChec...@@ -2803,6 +2807,15 @@ static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionChec
2803 return nullptr;2807 return nullptr;
2804}2808}
28052809
2810static 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
2806static IrInstruction *ir_instruction_testtype_get_dep(IrInstructionTestType *instruction, size_t index) {2819static IrInstruction *ir_instruction_testtype_get_dep(IrInstructionTestType *instruction, size_t index) {
2807 switch (index) {2820 switch (index) {
2808 case 0: return instruction->type_value;2821 case 0: return instruction->type_value;
...@@ -3060,6 +3073,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t...@@ -3060,6 +3073,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
3060 return ir_instruction_errtoint_get_dep((IrInstructionErrToInt *) instruction, index);3073 return ir_instruction_errtoint_get_dep((IrInstructionErrToInt *) instruction, index);
3061 case IrInstructionIdCheckSwitchProngs:3074 case IrInstructionIdCheckSwitchProngs:
3062 return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index);3075 return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index);
3076 case IrInstructionIdCheckStatementIsVoid:
3077 return ir_instruction_checkstatementisvoid_get_dep((IrInstructionCheckStatementIsVoid *) instruction, index);
3063 case IrInstructionIdTestType:3078 case IrInstructionIdTestType:
3064 return ir_instruction_testtype_get_dep((IrInstructionTestType *) instruction, index);3079 return ir_instruction_testtype_get_dep((IrInstructionTestType *) instruction, index);
3065 case IrInstructionIdTypeName:3080 case IrInstructionIdTypeName:
...@@ -3333,6 +3348,18 @@ static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) {...@@ -3333,6 +3348,18 @@ static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) {
3333 return nullptr;3348 return nullptr;
3334}3349}
33353350
3351static 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
3336static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {3363static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {
3337 assert(block_node->type == NodeTypeBlock);3364 assert(block_node->type == NodeTypeBlock);
33383365
...@@ -3410,12 +3437,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode...@@ -3410,12 +3437,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
3410 return_value = statement_value;3437 return_value = statement_value;
3411 } else {3438 } else {
3412 // there are more statements ahead of this one. this statement's value must be void3439 // there are more statements ahead of this one. this statement's value must be void
3413 TypeTableEntry *instruction_type = statement_value->value.type;3440 if (statement_value != irb->codegen->invalid_instruction) {
3414 if (instruction_type &&3441 ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
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"));
3419 }3442 }
3420 }3443 }
3421 }3444 }
...@@ -12640,6 +12663,22 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira...@@ -12640,6 +12663,22 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
12640 return ira->codegen->builtin_types.entry_void;12663 return ira->codegen->builtin_types.entry_void;
12641}12664}
1264212665
12666static 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
12643static TypeTableEntry *ir_analyze_instruction_test_type(IrAnalyze *ira, IrInstructionTestType *instruction) {12682static TypeTableEntry *ir_analyze_instruction_test_type(IrAnalyze *ira, IrInstructionTestType *instruction) {
12644 IrInstruction *type_value = instruction->type_value->other;12683 IrInstruction *type_value = instruction->type_value->other;
12645 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);12684 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
...@@ -12987,6 +13026,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -12987,6 +13026,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
12987 return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction);13026 return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction);
12988 case IrInstructionIdCheckSwitchProngs:13027 case IrInstructionIdCheckSwitchProngs:
12989 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction);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 case IrInstructionIdTestType:13031 case IrInstructionIdTestType:
12991 return ir_analyze_instruction_test_type(ira, (IrInstructionTestType *)instruction);13032 return ir_analyze_instruction_test_type(ira, (IrInstructionTestType *)instruction);
12992 case IrInstructionIdCanImplicitCast:13033 case IrInstructionIdCanImplicitCast:
...@@ -13026,13 +13067,6 @@ static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *ins...@@ -13026,13 +13067,6 @@ static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *ins
13026 instruction_type->id == TypeTableEntryIdUnreachable);13067 instruction_type->id == TypeTableEntryIdUnreachable);
13027 instruction->other = instruction;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 }
1303613070
13037 return instruction_type;13071 return instruction_type;
13038}13072}
...@@ -13123,6 +13157,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -13123,6 +13157,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
13123 case IrInstructionIdBreakpoint:13157 case IrInstructionIdBreakpoint:
13124 case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free13158 case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free
13125 case IrInstructionIdCheckSwitchProngs:13159 case IrInstructionIdCheckSwitchProngs:
13160 case IrInstructionIdCheckStatementIsVoid:
13126 case IrInstructionIdSetGlobalAlign:13161 case IrInstructionIdSetGlobalAlign:
13127 case IrInstructionIdSetGlobalSection:13162 case IrInstructionIdSetGlobalSection:
13128 case IrInstructionIdSetGlobalLinkage:13163 case IrInstructionIdSetGlobalLinkage:
src/ir_print.cpp+9
...@@ -814,6 +814,12 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP...@@ -814,6 +814,12 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP
814 fprintf(irp->f, ")");814 fprintf(irp->f, ")");
815}815}
816816
817static 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
817static void ir_print_test_type(IrPrint *irp, IrInstructionTestType *instruction) {823static void ir_print_test_type(IrPrint *irp, IrInstructionTestType *instruction) {
818 fprintf(irp->f, "testtype ");824 fprintf(irp->f, "testtype ");
819 ir_print_other_instruction(irp, instruction->type_value);825 ir_print_other_instruction(irp, instruction->type_value);
...@@ -1149,6 +1155,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1149,6 +1155,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1149 case IrInstructionIdCheckSwitchProngs:1155 case IrInstructionIdCheckSwitchProngs:
1150 ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction);1156 ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction);
1151 break;1157 break;
1158 case IrInstructionIdCheckStatementIsVoid:
1159 ir_print_check_statement_is_void(irp, (IrInstructionCheckStatementIsVoid *)instruction);
1160 break;
1152 case IrInstructionIdTestType:1161 case IrInstructionIdTestType:
1153 ir_print_test_type(irp, (IrInstructionTestType *)instruction);1162 ir_print_test_type(irp, (IrInstructionTestType *)instruction);
1154 break;1163 break;
std/buf_map.zig+2-2
...@@ -31,14 +31,14 @@ pub const BufMap = struct {...@@ -31,14 +31,14 @@ pub const BufMap = struct {
31 test (self.hash_map.get(key)) |entry| {31 test (self.hash_map.get(key)) |entry| {
32 const value_copy = %return self.copy(value);32 const value_copy = %return self.copy(value);
33 %defer self.free(value_copy);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 self.free(entry.value);35 self.free(entry.value);
36 } else {36 } else {
37 const key_copy = %return self.copy(key);37 const key_copy = %return self.copy(key);
38 %defer self.free(key_copy);38 %defer self.free(key_copy);
39 const value_copy = %return self.copy(value);39 const value_copy = %return self.copy(value);
40 %defer self.free(value_copy);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 }
4444
std/buf_set.zig+1-1
...@@ -28,7 +28,7 @@ pub const BufSet = struct {...@@ -28,7 +28,7 @@ pub const BufSet = struct {
28 if (self.hash_map.get(key) == null) {28 if (self.hash_map.get(key) == null) {
29 const key_copy = %return self.copy(key);29 const key_copy = %return self.copy(key);
30 %defer self.free(key_copy);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 }
3434
std/build.zig+5-2
...@@ -415,20 +415,23 @@ pub const Builder = struct {...@@ -415,20 +415,23 @@ pub const Builder = struct {
415 .value = UserValue.Scalar { value },415 .value = UserValue.Scalar { value },
416 .used = false,416 .used = false,
417 })) |*prev_value| {417 })) |*prev_value| {
418 // option already exists
418 switch (prev_value.value) {419 switch (prev_value.value) {
419 UserValue.Scalar => |s| {420 UserValue.Scalar => |s| {
421 // turn it into a list
420 var list = List([]const u8).init(self.allocator);422 var list = List([]const u8).init(self.allocator);
421 %%list.append(s);423 %%list.append(s);
422 %%list.append(value);424 %%list.append(value);
423 %%self.user_input_options.put(name, UserInputOption {425 _ = %%self.user_input_options.put(name, UserInputOption {
424 .name = name,426 .name = name,
425 .value = UserValue.List { list },427 .value = UserValue.List { list },
426 .used = false,428 .used = false,
427 });429 });
428 },430 },
429 UserValue.List => |*list| {431 UserValue.List => |*list| {
432 // append to the list
430 %%list.append(value);433 %%list.append(value);
431 %%self.user_input_options.put(name, UserInputOption {434 _ = %%self.user_input_options.put(name, UserInputOption {
432 .name = name,435 .name = name,
433 .value = UserValue.List { *list },436 .value = UserValue.List { *list },
434 .used = false,437 .used = false,
std/debug.zig+1-1
...@@ -217,7 +217,7 @@ fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 {...@@ -217,7 +217,7 @@ fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 {
217fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 {217fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 {
218 const buf = %return global_allocator.alloc(u8, size);218 const buf = %return global_allocator.alloc(u8, size);
219 %defer global_allocator.free(buf);219 %defer global_allocator.free(buf);
220 %return in_stream.read(buf);220 if (size < %return in_stream.read(buf)) return error.Eof;
221 return buf;221 return buf;
222}222}
223223
std/fmt.zig+1-1
...@@ -386,7 +386,7 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: u...@@ -386,7 +386,7 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: u
386}386}
387387
388test "testParseU64DigitTooBig" {388test "testParseU64DigitTooBig" {
389 parseUnsigned(u64, "123a", 10) %% |err| {389 _ = parseUnsigned(u64, "123a", 10) %% |err| {
390 if (err == error.InvalidChar) return;390 if (err == error.InvalidChar) return;
391 unreachable;391 unreachable;
392 };392 };
test/cases/switch_prong_err_enum.zig+4-1
...@@ -22,6 +22,9 @@ fn doThing(form_id: u64) -> %FormValue {...@@ -22,6 +22,9 @@ fn doThing(form_id: u64) -> %FormValue {
22}22}
2323
24test "switchProngReturnsErrorEnum" {24test "switchProngReturnsErrorEnum" {
25 %%doThing(17);25 switch (%%doThing(17)) {
26 FormValue.Address => |payload| { assert(payload == 1); },
27 else => unreachable,
28 }
26 assert(read_count == 1);29 assert(read_count == 1);
27}30}
test/compile_errors.zig+1-1
...@@ -1346,7 +1346,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -1346,7 +1346,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
1346 \\ bar();1346 \\ bar();
1347 \\}1347 \\}
1348 \\fn bar() -> i32 { 0 }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");
13501350
1351 cases.add("integer literal on a non-comptime var",1351 cases.add("integer literal on a non-comptime var",
1352 \\export fn foo() {1352 \\export fn foo() {