mirror of
https://github.com/ziglang/zig.git
synced 2026-02-17 06:49:23 +00:00
Sema: improve error for mismatched type in implicit return
Closes #2653
This commit is contained in:
parent
e2509ddbe6
commit
f20e449fd6
@ -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(.{
|
||||
|
||||
32
src/Sema.zig
32
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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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),
|
||||
|
||||
11
src/type.zig
11
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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
17
test/cases/compile_errors/type_error_in_implicit_return.zig
Normal file
17
test/cases/compile_errors/type_error_in_implicit_return.zig
Normal file
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user