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
26322632 .compile_error,
26332633 .ret_node,
26342634 .ret_load,
2635 .ret_tok,
2635 .ret_implicit,
26362636 .ret_err_value,
26372637 .@"unreachable",
26382638 .repeat,
......@@ -3914,9 +3914,8 @@ fn fnDecl(
39143914 // As our last action before the return, "pop" the error trace if needed
39153915 _ = try gz.addRestoreErrRetIndex(.ret, .always);
39163916
3917 // Since we are adding the return instruction here, we must handle the coercion.
3918 // We do this by using the `ret_tok` instruction.
3919 _ = try fn_gz.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node));
3917 // Add implicit return at end of function.
3918 _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
39203919 }
39213920
39223921 break :func try decl_gz.addFunc(.{
......@@ -4334,9 +4333,8 @@ fn testDecl(
43344333 // As our last action before the return, "pop" the error trace if needed
43354334 _ = try gz.addRestoreErrRetIndex(.ret, .always);
43364335
4337 // Since we are adding the return instruction here, we must handle the coercion.
4338 // We do this by using the `ret_tok` instruction.
4339 _ = try fn_block.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node));
4336 // Add implicit return at end of function.
4337 _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
43404338 }
43414339
43424340 const func_inst = try decl_block.addFunc(.{
src/Sema.zig+28-4
......@@ -1098,7 +1098,7 @@ fn analyzeBodyInner(
10981098 // These functions match the return type of analyzeBody so that we can
10991099 // tail call them here.
11001100 .compile_error => break sema.zirCompileError(block, inst),
1101 .ret_tok => break sema.zirRetTok(block, inst),
1101 .ret_implicit => break sema.zirRetImplicit(block, inst),
11021102 .ret_node => break sema.zirRetNode(block, inst),
11031103 .ret_load => break sema.zirRetLoad(block, inst),
11041104 .ret_err_value => break sema.zirRetErrValue(block, inst),
......@@ -16546,7 +16546,7 @@ fn zirRetErrValue(
1654616546 return sema.analyzeRet(block, result_inst, src);
1654716547}
1654816548
16549fn zirRetTok(
16549fn zirRetImplicit(
1655016550 sema: *Sema,
1655116551 block: *Block,
1655216552 inst: Zir.Inst.Index,
......@@ -16556,9 +16556,33 @@ fn zirRetTok(
1655616556
1655716557 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
1655816558 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);
1656216586}
1656316587
1656416588fn 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 {
519519 /// Includes an operand as the return value.
520520 /// Includes a token source location.
521521 /// Uses the `un_tok` union field.
522 ret_tok,
522 ret_implicit,
523523 /// Sends control flow back to the function's callee.
524524 /// The return operand is `error.foo` where `foo` is given by the string.
525525 /// If the current function has an inferred error set, the error given by the
......@@ -1256,7 +1256,7 @@ pub const Inst = struct {
12561256 .compile_error,
12571257 .ret_node,
12581258 .ret_load,
1259 .ret_tok,
1259 .ret_implicit,
12601260 .ret_err_value,
12611261 .@"unreachable",
12621262 .repeat,
......@@ -1530,7 +1530,7 @@ pub const Inst = struct {
15301530 .compile_error,
15311531 .ret_node,
15321532 .ret_load,
1533 .ret_tok,
1533 .ret_implicit,
15341534 .ret_err_value,
15351535 .ret_ptr,
15361536 .ret_type,
......@@ -1659,7 +1659,7 @@ pub const Inst = struct {
16591659 .ref = .un_tok,
16601660 .ret_node = .un_node,
16611661 .ret_load = .un_node,
1662 .ret_tok = .un_tok,
1662 .ret_implicit = .un_tok,
16631663 .ret_err_value = .str_tok,
16641664 .ret_err_value_code = .str_tok,
16651665 .ret_ptr = .node,
src/print_zir.zig+1-1
......@@ -235,7 +235,7 @@ const Writer = struct {
235235 => try self.writeUnNode(stream, inst),
236236
237237 .ref,
238 .ret_tok,
238 .ret_implicit,
239239 .closure_capture,
240240 .switch_capture_tag,
241241 => try self.writeUnTok(stream, inst),
src/type.zig+11
......@@ -160,6 +160,17 @@ pub const Type = extern union {
160160 }
161161 }
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
163174 pub fn isSelfComparable(ty: Type, is_equality_cmp: bool) bool {
164175 return switch (ty.zigTypeTag()) {
165176 .Int,
test/cases/aarch64-macos/hello_world_with_updates.1.zig+2-2
......@@ -2,5 +2,5 @@ pub export fn main() noreturn {}
22
33// error
44//
5// :1:32: error: function declared 'noreturn' returns
6// :1:22: note: 'noreturn' declared here
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :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 @@
1pub export fn _start() noreturn {}
1pub export fn main() noreturn {}
22
33// error
44//
5// :1:34: error: function declared 'noreturn' returns
6// :1:24: note: 'noreturn' declared here
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :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 {}
22
33// error
44//
5// :1:32: error: function declared 'noreturn' returns
6// :1:22: note: 'noreturn' declared here
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :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 {}
22
33// error
44//
5// :1:32: error: function declared 'noreturn' returns
6// :1:22: note: 'noreturn' declared here
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:32: note: control flow reaches end of body here