authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-12-02 20:56:40+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-12-03 00:48:03+02:00
logf20e449fd6ad63c58e76670f230783d0dd399b93
tree80090a8144b0a05cf3df5a09867a0b609a592d93
parente2509ddbe69a56bb1f4a56b46946b2a706d5aabe

Sema: improve error for mismatched type in implicit return

Closes #2653

11 files changed, 75 insertions(+), 34 deletions(-)

src/AstGen.zig+5-7
...@@ -2632,7 +2632,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2632,7 +2632,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2632 .compile_error,2632 .compile_error,
2633 .ret_node,2633 .ret_node,
2634 .ret_load,2634 .ret_load,
2635 .ret_tok,2635 .ret_implicit,
2636 .ret_err_value,2636 .ret_err_value,
2637 .@"unreachable",2637 .@"unreachable",
2638 .repeat,2638 .repeat,
...@@ -3914,9 +3914,8 @@ fn fnDecl(...@@ -3914,9 +3914,8 @@ fn fnDecl(
3914 // As our last action before the return, "pop" the error trace if needed3914 // As our last action before the return, "pop" the error trace if needed
3915 _ = try gz.addRestoreErrRetIndex(.ret, .always);3915 _ = try gz.addRestoreErrRetIndex(.ret, .always);
39163916
3917 // Since we are adding the return instruction here, we must handle the coercion.3917 // Add implicit return at end of function.
3918 // We do this by using the `ret_tok` instruction.3918 _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
3919 _ = try fn_gz.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node));
3920 }3919 }
39213920
3922 break :func try decl_gz.addFunc(.{3921 break :func try decl_gz.addFunc(.{
...@@ -4334,9 +4333,8 @@ fn testDecl(...@@ -4334,9 +4333,8 @@ fn testDecl(
4334 // As our last action before the return, "pop" the error trace if needed4333 // As our last action before the return, "pop" the error trace if needed
4335 _ = try gz.addRestoreErrRetIndex(.ret, .always);4334 _ = try gz.addRestoreErrRetIndex(.ret, .always);
43364335
4337 // Since we are adding the return instruction here, we must handle the coercion.4336 // Add implicit return at end of function.
4338 // We do this by using the `ret_tok` instruction.4337 _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
4339 _ = try fn_block.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node));
4340 }4338 }
43414339
4342 const func_inst = try decl_block.addFunc(.{4340 const func_inst = try decl_block.addFunc(.{
src/Sema.zig+28-4
...@@ -1098,7 +1098,7 @@ fn analyzeBodyInner(...@@ -1098,7 +1098,7 @@ fn analyzeBodyInner(
1098 // These functions match the return type of analyzeBody so that we can1098 // These functions match the return type of analyzeBody so that we can
1099 // tail call them here.1099 // tail call them here.
1100 .compile_error => break sema.zirCompileError(block, inst),1100 .compile_error => break sema.zirCompileError(block, inst),
1101 .ret_tok => break sema.zirRetTok(block, inst),1101 .ret_implicit => break sema.zirRetImplicit(block, inst),
1102 .ret_node => break sema.zirRetNode(block, inst),1102 .ret_node => break sema.zirRetNode(block, inst),
1103 .ret_load => break sema.zirRetLoad(block, inst),1103 .ret_load => break sema.zirRetLoad(block, inst),
1104 .ret_err_value => break sema.zirRetErrValue(block, inst),1104 .ret_err_value => break sema.zirRetErrValue(block, inst),
...@@ -16546,7 +16546,7 @@ fn zirRetErrValue(...@@ -16546,7 +16546,7 @@ fn zirRetErrValue(
16546 return sema.analyzeRet(block, result_inst, src);16546 return sema.analyzeRet(block, result_inst, src);
16547}16547}
1654816548
16549fn zirRetTok(16549fn zirRetImplicit(
16550 sema: *Sema,16550 sema: *Sema,
16551 block: *Block,16551 block: *Block,
16552 inst: Zir.Inst.Index,16552 inst: Zir.Inst.Index,
...@@ -16556,9 +16556,33 @@ fn zirRetTok(...@@ -16556,9 +16556,33 @@ fn zirRetTok(
1655616556
16557 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;16557 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
16558 const operand = try sema.resolveInst(inst_data.operand);16558 const operand = try sema.resolveInst(inst_data.operand);
16559 const src = inst_data.src();
1656016559
16561 return sema.analyzeRet(block, operand, src);16560 const r_brace_src = inst_data.src();
16561 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
16562 const base_tag = sema.fn_ret_ty.baseZigTypeTag();
16563 if (base_tag == .NoReturn) {
16564 const msg = msg: {
16565 const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{
16566 sema.fn_ret_ty.fmt(sema.mod),
16567 });
16568 errdefer msg.destroy(sema.gpa);
16569 try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{});
16570 break :msg msg;
16571 };
16572 return sema.failWithOwnedErrorMsg(msg);
16573 } else if (base_tag != .Void) {
16574 const msg = msg: {
16575 const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{
16576 sema.fn_ret_ty.fmt(sema.mod),
16577 });
16578 errdefer msg.destroy(sema.gpa);
16579 try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{});
16580 break :msg msg;
16581 };
16582 return sema.failWithOwnedErrorMsg(msg);
16583 }
16584
16585 return sema.analyzeRet(block, operand, .unneeded);
16562}16586}
1656316587
16564fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {16588fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
src/Zir.zig+4-4
...@@ -519,7 +519,7 @@ pub const Inst = struct {...@@ -519,7 +519,7 @@ pub const Inst = struct {
519 /// Includes an operand as the return value.519 /// Includes an operand as the return value.
520 /// Includes a token source location.520 /// Includes a token source location.
521 /// Uses the `un_tok` union field.521 /// Uses the `un_tok` union field.
522 ret_tok,522 ret_implicit,
523 /// Sends control flow back to the function's callee.523 /// Sends control flow back to the function's callee.
524 /// The return operand is `error.foo` where `foo` is given by the string.524 /// The return operand is `error.foo` where `foo` is given by the string.
525 /// If the current function has an inferred error set, the error given by the525 /// If the current function has an inferred error set, the error given by the
...@@ -1256,7 +1256,7 @@ pub const Inst = struct {...@@ -1256,7 +1256,7 @@ pub const Inst = struct {
1256 .compile_error,1256 .compile_error,
1257 .ret_node,1257 .ret_node,
1258 .ret_load,1258 .ret_load,
1259 .ret_tok,1259 .ret_implicit,
1260 .ret_err_value,1260 .ret_err_value,
1261 .@"unreachable",1261 .@"unreachable",
1262 .repeat,1262 .repeat,
...@@ -1530,7 +1530,7 @@ pub const Inst = struct {...@@ -1530,7 +1530,7 @@ pub const Inst = struct {
1530 .compile_error,1530 .compile_error,
1531 .ret_node,1531 .ret_node,
1532 .ret_load,1532 .ret_load,
1533 .ret_tok,1533 .ret_implicit,
1534 .ret_err_value,1534 .ret_err_value,
1535 .ret_ptr,1535 .ret_ptr,
1536 .ret_type,1536 .ret_type,
...@@ -1659,7 +1659,7 @@ pub const Inst = struct {...@@ -1659,7 +1659,7 @@ pub const Inst = struct {
1659 .ref = .un_tok,1659 .ref = .un_tok,
1660 .ret_node = .un_node,1660 .ret_node = .un_node,
1661 .ret_load = .un_node,1661 .ret_load = .un_node,
1662 .ret_tok = .un_tok,1662 .ret_implicit = .un_tok,
1663 .ret_err_value = .str_tok,1663 .ret_err_value = .str_tok,
1664 .ret_err_value_code = .str_tok,1664 .ret_err_value_code = .str_tok,
1665 .ret_ptr = .node,1665 .ret_ptr = .node,
src/print_zir.zig+1-1
...@@ -235,7 +235,7 @@ const Writer = struct {...@@ -235,7 +235,7 @@ const Writer = struct {
235 => try self.writeUnNode(stream, inst),235 => try self.writeUnNode(stream, inst),
236236
237 .ref,237 .ref,
238 .ret_tok,238 .ret_implicit,
239 .closure_capture,239 .closure_capture,
240 .switch_capture_tag,240 .switch_capture_tag,
241 => try self.writeUnTok(stream, inst),241 => try self.writeUnTok(stream, inst),
src/type.zig+11
...@@ -160,6 +160,17 @@ pub const Type = extern union {...@@ -160,6 +160,17 @@ pub const Type = extern union {
160 }160 }
161 }161 }
162162
163 pub fn baseZigTypeTag(self: Type) std.builtin.TypeId {
164 return switch (self.zigTypeTag()) {
165 .ErrorUnion => self.errorUnionPayload().baseZigTypeTag(),
166 .Optional => {
167 var buf: Payload.ElemType = undefined;
168 return self.optionalChild(&buf).baseZigTypeTag();
169 },
170 else => |t| t,
171 };
172 }
173
163 pub fn isSelfComparable(ty: Type, is_equality_cmp: bool) bool {174 pub fn isSelfComparable(ty: Type, is_equality_cmp: bool) bool {
164 return switch (ty.zigTypeTag()) {175 return switch (ty.zigTypeTag()) {
165 .Int,176 .Int,
test/cases/aarch64-macos/hello_world_with_updates.1.zig+2-2
...@@ -2,5 +2,5 @@ pub export fn main() noreturn {}...@@ -2,5 +2,5 @@ pub export fn main() noreturn {}
22
3// error3// error
4//4//
5// :1:32: error: function declared 'noreturn' returns5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:22: note: 'noreturn' declared here6// :1:32: note: control flow reaches end of body here
test/cases/compile_errors/control_reaches_end_of_non-void_function.zig deleted-9
...@@ -1,9 +0,0 @@
1fn a() i32 {}
2export fn entry() void { _ = a(); }
3
4// error
5// backend=stage2
6// target=native
7//
8// :1:13: error: expected type 'i32', found 'void'
9// :1:8: note: function return type declared here
test/cases/compile_errors/type_error_in_implicit_return.zig created+17
...@@ -0,0 +1,17 @@
1fn f1(x: bool) u32 {
2 if (x) return 1;
3}
4fn f2() noreturn {}
5pub export fn entry() void {
6 _ = f1(true);
7 _ = f2();
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :1:16: error: function with non-void return type 'u32' implicitly returns
15// :3:1: note: control flow reaches end of body here
16// :4:9: error: function declared 'noreturn' implicitly returns
17// :4:19: note: control flow reaches end of body here
test/cases/x86_64-linux/hello_world_with_updates.1.zig+3-3
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1pub export fn _start() noreturn {}1pub export fn main() noreturn {}
22
3// error3// error
4//4//
5// :1:34: error: function declared 'noreturn' returns5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:24: note: 'noreturn' declared here6// :1:32: note: control flow reaches end of body here
test/cases/x86_64-macos/hello_world_with_updates.1.zig+2-2
...@@ -2,5 +2,5 @@ pub export fn main() noreturn {}...@@ -2,5 +2,5 @@ pub export fn main() noreturn {}
22
3// error3// error
4//4//
5// :1:32: error: function declared 'noreturn' returns5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:22: note: 'noreturn' declared here6// :1:32: note: control flow reaches end of body here
test/cases/x86_64-windows/hello_world_with_updates.1.zig+2-2
...@@ -2,5 +2,5 @@ pub export fn main() noreturn {}...@@ -2,5 +2,5 @@ pub export fn main() noreturn {}
22
3// error3// error
4//4//
5// :1:32: error: function declared 'noreturn' returns5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:22: note: 'noreturn' declared here6// :1:32: note: control flow reaches end of body here