diff --git a/src/AstGen.zig b/src/AstGen.zig index 1ec0cfa78f..4e571ffda9 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2632,7 +2632,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .compile_error, .ret_node, .ret_load, - .ret_tok, + .ret_implicit, .ret_err_value, .@"unreachable", .repeat, @@ -3914,9 +3914,8 @@ fn fnDecl( // As our last action before the return, "pop" the error trace if needed _ = try gz.addRestoreErrRetIndex(.ret, .always); - // Since we are adding the return instruction here, we must handle the coercion. - // We do this by using the `ret_tok` instruction. - _ = try fn_gz.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node)); + // Add implicit return at end of function. + _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); } break :func try decl_gz.addFunc(.{ @@ -4334,9 +4333,8 @@ fn testDecl( // As our last action before the return, "pop" the error trace if needed _ = try gz.addRestoreErrRetIndex(.ret, .always); - // Since we are adding the return instruction here, we must handle the coercion. - // We do this by using the `ret_tok` instruction. - _ = try fn_block.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node)); + // Add implicit return at end of function. + _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); } const func_inst = try decl_block.addFunc(.{ diff --git a/src/Sema.zig b/src/Sema.zig index ad89408c26..bd7bdb48cb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1098,7 +1098,7 @@ fn analyzeBodyInner( // These functions match the return type of analyzeBody so that we can // tail call them here. .compile_error => break sema.zirCompileError(block, inst), - .ret_tok => break sema.zirRetTok(block, inst), + .ret_implicit => break sema.zirRetImplicit(block, inst), .ret_node => break sema.zirRetNode(block, inst), .ret_load => break sema.zirRetLoad(block, inst), .ret_err_value => break sema.zirRetErrValue(block, inst), @@ -16546,7 +16546,7 @@ fn zirRetErrValue( return sema.analyzeRet(block, result_inst, src); } -fn zirRetTok( +fn zirRetImplicit( sema: *Sema, block: *Block, inst: Zir.Inst.Index, @@ -16556,9 +16556,33 @@ fn zirRetTok( const inst_data = sema.code.instructions.items(.data)[inst].un_tok; const operand = try sema.resolveInst(inst_data.operand); - const src = inst_data.src(); - return sema.analyzeRet(block, operand, src); + const r_brace_src = inst_data.src(); + const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; + const base_tag = sema.fn_ret_ty.baseZigTypeTag(); + if (base_tag == .NoReturn) { + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{ + sema.fn_ret_ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } else if (base_tag != .Void) { + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{ + sema.fn_ret_ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + + return sema.analyzeRet(block, operand, .unneeded); } fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { diff --git a/src/Zir.zig b/src/Zir.zig index c7e1d22d22..ed425ea73e 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -519,7 +519,7 @@ pub const Inst = struct { /// Includes an operand as the return value. /// Includes a token source location. /// Uses the `un_tok` union field. - ret_tok, + ret_implicit, /// Sends control flow back to the function's callee. /// The return operand is `error.foo` where `foo` is given by the string. /// If the current function has an inferred error set, the error given by the @@ -1256,7 +1256,7 @@ pub const Inst = struct { .compile_error, .ret_node, .ret_load, - .ret_tok, + .ret_implicit, .ret_err_value, .@"unreachable", .repeat, @@ -1530,7 +1530,7 @@ pub const Inst = struct { .compile_error, .ret_node, .ret_load, - .ret_tok, + .ret_implicit, .ret_err_value, .ret_ptr, .ret_type, @@ -1659,7 +1659,7 @@ pub const Inst = struct { .ref = .un_tok, .ret_node = .un_node, .ret_load = .un_node, - .ret_tok = .un_tok, + .ret_implicit = .un_tok, .ret_err_value = .str_tok, .ret_err_value_code = .str_tok, .ret_ptr = .node, diff --git a/src/print_zir.zig b/src/print_zir.zig index 9135c22010..542f0e977d 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -235,7 +235,7 @@ const Writer = struct { => try self.writeUnNode(stream, inst), .ref, - .ret_tok, + .ret_implicit, .closure_capture, .switch_capture_tag, => try self.writeUnTok(stream, inst), diff --git a/src/type.zig b/src/type.zig index 46b33a34fa..d36bd285f8 100644 --- a/src/type.zig +++ b/src/type.zig @@ -160,6 +160,17 @@ pub const Type = extern union { } } + pub fn baseZigTypeTag(self: Type) std.builtin.TypeId { + return switch (self.zigTypeTag()) { + .ErrorUnion => self.errorUnionPayload().baseZigTypeTag(), + .Optional => { + var buf: Payload.ElemType = undefined; + return self.optionalChild(&buf).baseZigTypeTag(); + }, + else => |t| t, + }; + } + pub fn isSelfComparable(ty: Type, is_equality_cmp: bool) bool { return switch (ty.zigTypeTag()) { .Int, diff --git a/test/cases/aarch64-macos/hello_world_with_updates.1.zig b/test/cases/aarch64-macos/hello_world_with_updates.1.zig index e18a4c6a1e..dcf18bbf87 100644 --- a/test/cases/aarch64-macos/hello_world_with_updates.1.zig +++ b/test/cases/aarch64-macos/hello_world_with_updates.1.zig @@ -2,5 +2,5 @@ pub export fn main() noreturn {} // error // -// :1:32: error: function declared 'noreturn' returns -// :1:22: note: 'noreturn' declared here +// :1:22: error: function declared 'noreturn' implicitly returns +// :1:32: note: control flow reaches end of body here diff --git a/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig b/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig deleted file mode 100644 index c92b6b0927..0000000000 --- a/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig +++ /dev/null @@ -1,9 +0,0 @@ -fn a() i32 {} -export fn entry() void { _ = a(); } - -// error -// backend=stage2 -// target=native -// -// :1:13: error: expected type 'i32', found 'void' -// :1:8: note: function return type declared here diff --git a/test/cases/compile_errors/type_error_in_implicit_return.zig b/test/cases/compile_errors/type_error_in_implicit_return.zig new file mode 100644 index 0000000000..8c8d498c97 --- /dev/null +++ b/test/cases/compile_errors/type_error_in_implicit_return.zig @@ -0,0 +1,17 @@ +fn f1(x: bool) u32 { + if (x) return 1; +} +fn f2() noreturn {} +pub export fn entry() void { + _ = f1(true); + _ = f2(); +} + +// error +// backend=stage2 +// target=native +// +// :1:16: error: function with non-void return type 'u32' implicitly returns +// :3:1: note: control flow reaches end of body here +// :4:9: error: function declared 'noreturn' implicitly returns +// :4:19: note: control flow reaches end of body here diff --git a/test/cases/x86_64-linux/hello_world_with_updates.1.zig b/test/cases/x86_64-linux/hello_world_with_updates.1.zig index 1f1a6a9682..dcf18bbf87 100644 --- a/test/cases/x86_64-linux/hello_world_with_updates.1.zig +++ b/test/cases/x86_64-linux/hello_world_with_updates.1.zig @@ -1,6 +1,6 @@ -pub export fn _start() noreturn {} +pub export fn main() noreturn {} // error // -// :1:34: error: function declared 'noreturn' returns -// :1:24: note: 'noreturn' declared here +// :1:22: error: function declared 'noreturn' implicitly returns +// :1:32: note: control flow reaches end of body here diff --git a/test/cases/x86_64-macos/hello_world_with_updates.1.zig b/test/cases/x86_64-macos/hello_world_with_updates.1.zig index e18a4c6a1e..dcf18bbf87 100644 --- a/test/cases/x86_64-macos/hello_world_with_updates.1.zig +++ b/test/cases/x86_64-macos/hello_world_with_updates.1.zig @@ -2,5 +2,5 @@ pub export fn main() noreturn {} // error // -// :1:32: error: function declared 'noreturn' returns -// :1:22: note: 'noreturn' declared here +// :1:22: error: function declared 'noreturn' implicitly returns +// :1:32: note: control flow reaches end of body here diff --git a/test/cases/x86_64-windows/hello_world_with_updates.1.zig b/test/cases/x86_64-windows/hello_world_with_updates.1.zig index e18a4c6a1e..dcf18bbf87 100644 --- a/test/cases/x86_64-windows/hello_world_with_updates.1.zig +++ b/test/cases/x86_64-windows/hello_world_with_updates.1.zig @@ -2,5 +2,5 @@ pub export fn main() noreturn {} // error // -// :1:32: error: function declared 'noreturn' returns -// :1:22: note: 'noreturn' declared here +// :1:22: error: function declared 'noreturn' implicitly returns +// :1:32: note: control flow reaches end of body here