Skip to content

Commit 3389890

Browse files
committed
stage2: error return tracing handles ret better
Sema: insert an error return trace frame when appropriate when analyzing ret_load. Also optimize the instructions to avoid an unnecessary block sent to the backends. AstGen: always emit a dbg_stmt for return expressions, in between the defer instructions and the return instruction. This improves the following test case: ```zig pub fn main() !void { return foo(); } fn foo() !void { return error.Bad; } ``` The error return trace now points to the return token instead of pointing to the foo() function call, matching stage1.
1 parent 1a500b9 commit 3389890

File tree

2 files changed

+141
-53
lines changed

2 files changed

+141
-53
lines changed

src/AstGen.zig

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6573,6 +6573,16 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
65736573

65746574
if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});
65756575

6576+
// Ensure debug line/column information is emitted for this return expression.
6577+
// Then we will save the line/column so that we can emit another one that goes
6578+
// "backwards" because we want to evaluate the operand, but then put the debug
6579+
// info back at the return keyword for error return tracing.
6580+
if (!gz.force_comptime) {
6581+
try emitDbgNode(gz, node);
6582+
}
6583+
const ret_line = astgen.source_line - gz.decl_line;
6584+
const ret_column = astgen.source_column;
6585+
65766586
const defer_outer = &astgen.fn_block.?.base;
65776587

65786588
const operand_node = node_datas[node].lhs;
@@ -6591,11 +6601,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
65916601
const defer_counts = countDefers(astgen, defer_outer, scope);
65926602
if (!defer_counts.need_err_code) {
65936603
try genDefers(gz, defer_outer, scope, .both_sans_err);
6604+
try emitRetDbgStmt(gz, ret_line, ret_column);
65946605
_ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
65956606
return Zir.Inst.Ref.unreachable_value;
65966607
}
65976608
const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
65986609
try genDefers(gz, defer_outer, scope, .{ .both = err_code });
6610+
try emitRetDbgStmt(gz, ret_line, ret_column);
65996611
_ = try gz.addUnNode(.ret_node, err_code, node);
66006612
return Zir.Inst.Ref.unreachable_value;
66016613
}
@@ -6614,13 +6626,15 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66146626
.never => {
66156627
// Returning a value that cannot be an error; skip error defers.
66166628
try genDefers(gz, defer_outer, scope, .normal_only);
6629+
try emitRetDbgStmt(gz, ret_line, ret_column);
66176630
try gz.addRet(rl, operand, node);
66186631
return Zir.Inst.Ref.unreachable_value;
66196632
},
66206633
.always => {
66216634
// Value is always an error. Emit both error defers and regular defers.
66226635
const err_code = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand;
66236636
try genDefers(gz, defer_outer, scope, .{ .both = err_code });
6637+
try emitRetDbgStmt(gz, ret_line, ret_column);
66246638
try gz.addRet(rl, operand, node);
66256639
return Zir.Inst.Ref.unreachable_value;
66266640
},
@@ -6629,6 +6643,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66296643
if (!defer_counts.have_err) {
66306644
// Only regular defers; no branch needed.
66316645
try genDefers(gz, defer_outer, scope, .normal_only);
6646+
try emitRetDbgStmt(gz, ret_line, ret_column);
66326647
try gz.addRet(rl, operand, node);
66336648
return Zir.Inst.Ref.unreachable_value;
66346649
}
@@ -6642,6 +6657,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66426657
defer then_scope.unstack();
66436658

66446659
try genDefers(&then_scope, defer_outer, scope, .normal_only);
6660+
try emitRetDbgStmt(gz, ret_line, ret_column);
66456661
try then_scope.addRet(rl, operand, node);
66466662

66476663
var else_scope = gz.makeSubBlock(scope);
@@ -6651,6 +6667,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66516667
.both = try else_scope.addUnNode(.err_union_code, result, node),
66526668
};
66536669
try genDefers(&else_scope, defer_outer, scope, which_ones);
6670+
try emitRetDbgStmt(gz, ret_line, ret_column);
66546671
try else_scope.addRet(rl, operand, node);
66556672

66566673
try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
@@ -11667,3 +11684,14 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
1166711684
}
1166811685
return @intCast(u32, count);
1166911686
}
11687+
11688+
fn emitRetDbgStmt(gz: *GenZir, line: u32, column: u32) !void {
11689+
if (gz.force_comptime) return;
11690+
11691+
_ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
11692+
.dbg_stmt = .{
11693+
.line = line,
11694+
.column = column,
11695+
},
11696+
} });
11697+
}

src/Sema.zig

Lines changed: 113 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14888,10 +14888,83 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
1488814888
const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
1488914889
return sema.analyzeRet(block, operand, src);
1489014890
}
14891+
14892+
if (sema.wantErrorReturnTracing()) {
14893+
const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
14894+
return retWithErrTracing(sema, block, src, is_non_err, .ret_load, ret_ptr);
14895+
}
14896+
1489114897
_ = try block.addUnOp(.ret_load, ret_ptr);
1489214898
return always_noreturn;
1489314899
}
1489414900

14901+
fn retWithErrTracing(
14902+
sema: *Sema,
14903+
block: *Block,
14904+
src: LazySrcLoc,
14905+
is_non_err: Air.Inst.Ref,
14906+
ret_tag: Air.Inst.Tag,
14907+
operand: Air.Inst.Ref,
14908+
) CompileError!Zir.Inst.Index {
14909+
const need_check = switch (is_non_err) {
14910+
.bool_true => {
14911+
_ = try block.addUnOp(ret_tag, operand);
14912+
return always_noreturn;
14913+
},
14914+
.bool_false => false,
14915+
else => true,
14916+
};
14917+
const gpa = sema.gpa;
14918+
const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
14919+
const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
14920+
const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty);
14921+
const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
14922+
const return_err_fn = try sema.getBuiltin(block, src, "returnError");
14923+
const args: [1]Air.Inst.Ref = .{err_return_trace};
14924+
14925+
if (!need_check) {
14926+
_ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null);
14927+
_ = try block.addUnOp(ret_tag, operand);
14928+
return always_noreturn;
14929+
}
14930+
14931+
var then_block = block.makeSubBlock();
14932+
defer then_block.instructions.deinit(gpa);
14933+
_ = try then_block.addUnOp(ret_tag, operand);
14934+
14935+
var else_block = block.makeSubBlock();
14936+
defer else_block.instructions.deinit(gpa);
14937+
_ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null);
14938+
_ = try else_block.addUnOp(ret_tag, operand);
14939+
14940+
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
14941+
then_block.instructions.items.len + else_block.instructions.items.len +
14942+
@typeInfo(Air.Block).Struct.fields.len + 1);
14943+
14944+
const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
14945+
.then_body_len = @intCast(u32, then_block.instructions.items.len),
14946+
.else_body_len = @intCast(u32, else_block.instructions.items.len),
14947+
});
14948+
sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items);
14949+
sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items);
14950+
14951+
_ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
14952+
.operand = is_non_err,
14953+
.payload = cond_br_payload,
14954+
} } });
14955+
14956+
return always_noreturn;
14957+
}
14958+
14959+
fn wantErrorReturnTracing(sema: *Sema) bool {
14960+
// TODO implement this feature in all the backends and then delete this check.
14961+
const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
14962+
14963+
return sema.fn_ret_ty.isError() and
14964+
sema.mod.comp.bin_file.options.error_return_tracing and
14965+
backend_supports_error_return_tracing;
14966+
}
14967+
1489514968
fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
1489614969
assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion);
1489714970

@@ -14915,8 +14988,6 @@ fn analyzeRet(
1491514988
uncasted_operand: Air.Inst.Ref,
1491614989
src: LazySrcLoc,
1491714990
) CompileError!Zir.Inst.Index {
14918-
const gpa = sema.gpa;
14919-
1492014991
// Special case for returning an error to an inferred error set; we need to
1492114992
// add the error tag to the inferred error set of the in-scope function, so
1492214993
// that the coercion below works correctly.
@@ -14934,65 +15005,18 @@ fn analyzeRet(
1493415005
return error.ComptimeReturn;
1493515006
}
1493615007
// We are inlining a function call; rewrite the `ret` as a `break`.
14937-
try inlining.merges.results.append(gpa, operand);
15008+
try inlining.merges.results.append(sema.gpa, operand);
1493815009
_ = try block.addBr(inlining.merges.block_inst, operand);
1493915010
return always_noreturn;
1494015011
}
1494115012

1494215013
try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
1494315014

14944-
// TODO implement this feature in all the backends and then delete this check.
14945-
const backend_supports_error_return_tracing =
14946-
sema.mod.comp.bin_file.options.use_llvm;
14947-
14948-
if (sema.fn_ret_ty.isError() and
14949-
sema.mod.comp.bin_file.options.error_return_tracing and
14950-
backend_supports_error_return_tracing)
14951-
ret_err: {
15015+
if (sema.wantErrorReturnTracing()) {
1495215016
// Avoid adding a frame to the error return trace in case the value is comptime-known
1495315017
// to be not an error.
14954-
const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
14955-
const need_check = switch (is_non_err) {
14956-
.bool_true => break :ret_err,
14957-
.bool_false => false,
14958-
else => true,
14959-
};
14960-
14961-
const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
14962-
const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
14963-
const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty);
14964-
const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
14965-
const return_err_fn = try sema.getBuiltin(block, src, "returnError");
14966-
const args: [1]Air.Inst.Ref = .{err_return_trace};
14967-
14968-
if (!need_check) {
14969-
_ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null);
14970-
break :ret_err;
14971-
}
14972-
14973-
const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
14974-
try sema.air_instructions.append(gpa, .{
14975-
.tag = .block,
14976-
.data = .{ .ty_pl = .{
14977-
.ty = .void_type,
14978-
.payload = undefined,
14979-
} },
14980-
});
14981-
14982-
var child_block = block.makeSubBlock();
14983-
defer child_block.instructions.deinit(gpa);
14984-
14985-
var then_block = child_block.makeSubBlock();
14986-
defer then_block.instructions.deinit(gpa);
14987-
_ = try then_block.addUnOp(.ret, operand);
14988-
14989-
var else_block = child_block.makeSubBlock();
14990-
defer else_block.instructions.deinit(gpa);
14991-
_ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null);
14992-
_ = try else_block.addUnOp(.ret, operand);
14993-
14994-
_ = try finishCondBr(sema, block, &child_block, &then_block, &else_block, is_non_err, block_inst);
14995-
return always_noreturn;
15018+
const is_non_err = try sema.analyzeIsNonErr(block, src, operand);
15019+
return retWithErrTracing(sema, block, src, is_non_err, .ret, operand);
1499615020
}
1499715021

1499815022
_ = try block.addUnOp(.ret, operand);
@@ -25474,6 +25498,27 @@ fn analyzeIsNull(
2547425498
return block.addUnOp(air_tag, operand);
2547525499
}
2547625500

25501+
fn analyzePtrIsNonErrComptimeOnly(
25502+
sema: *Sema,
25503+
block: *Block,
25504+
src: LazySrcLoc,
25505+
operand: Air.Inst.Ref,
25506+
) CompileError!Air.Inst.Ref {
25507+
const ptr_ty = sema.typeOf(operand);
25508+
assert(ptr_ty.zigTypeTag() == .Pointer);
25509+
const child_ty = ptr_ty.childType();
25510+
25511+
const child_tag = child_ty.zigTypeTag();
25512+
if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true;
25513+
if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false;
25514+
assert(child_tag == .ErrorUnion);
25515+
25516+
_ = block;
25517+
_ = src;
25518+
25519+
return Air.Inst.Ref.none;
25520+
}
25521+
2547725522
fn analyzeIsNonErrComptimeOnly(
2547825523
sema: *Sema,
2547925524
block: *Block,
@@ -25572,6 +25617,21 @@ fn analyzeIsNonErr(
2557225617
}
2557325618
}
2557425619

25620+
fn analyzePtrIsNonErr(
25621+
sema: *Sema,
25622+
block: *Block,
25623+
src: LazySrcLoc,
25624+
operand: Air.Inst.Ref,
25625+
) CompileError!Air.Inst.Ref {
25626+
const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
25627+
if (result == .none) {
25628+
try sema.requireRuntimeBlock(block, src, null);
25629+
return block.addUnOp(.is_non_err_ptr, operand);
25630+
} else {
25631+
return result;
25632+
}
25633+
}
25634+
2557525635
fn analyzeSlice(
2557625636
sema: *Sema,
2557725637
block: *Block,

0 commit comments

Comments
 (0)