Commit 5316a00a18
Changed files (4)
src/AstGen.zig
@@ -2471,6 +2471,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.try_ptr,
//.try_inline,
//.try_ptr_inline,
+ .save_err_ret_index,
=> break :b false,
.extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
@@ -2533,6 +2534,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.validate_array_init_ty,
.validate_struct_init_ty,
.validate_deref,
+ .restore_err_ret_index,
=> break :b true,
.@"defer" => unreachable,
@@ -5152,10 +5154,16 @@ fn orelseCatchExpr(
const astgen = parent_gz.astgen;
const tree = astgen.tree;
+ const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
+
var block_scope = parent_gz.makeSubBlock(scope);
block_scope.setBreakResultLoc(rl);
defer block_scope.unstack();
+ if (do_err_trace) {
+ block_scope.saved_err_trace_index = try parent_gz.addNode(.save_err_ret_index, node);
+ }
+
const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
.ref => .ref,
else => .none,
@@ -5220,7 +5228,7 @@ fn orelseCatchExpr(
// instructions or not.
const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
- return finishThenElseBlock(
+ const result = try finishThenElseBlock(
parent_gz,
rl,
node,
@@ -5235,6 +5243,16 @@ fn orelseCatchExpr(
block,
break_tag,
);
+ if (do_err_trace) {
+ _ = try parent_gz.add(.{
+ .tag = .restore_err_ret_index,
+ .data = .{ .un_node = .{
+ .operand = parent_gz.saved_err_trace_index,
+ .src_node = parent_gz.nodeIndexToRelative(node),
+ } },
+ });
+ }
+ return result;
}
/// Supports `else_scope` stacked on `then_scope` stacked on `block_scope`. Unstacks `else_scope` then `then_scope`.
@@ -5430,10 +5448,16 @@ fn ifExpr(
const tree = astgen.tree;
const token_tags = tree.tokens.items(.tag);
+ const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
+
var block_scope = parent_gz.makeSubBlock(scope);
block_scope.setBreakResultLoc(rl);
defer block_scope.unstack();
+ if (do_err_trace) {
+ block_scope.saved_err_trace_index = try parent_gz.addNode(.save_err_ret_index, node);
+ }
+
const payload_is_ref = if (if_full.payload_token) |payload_token|
token_tags[payload_token] == .asterisk
else
@@ -5602,7 +5626,7 @@ fn ifExpr(
};
const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
- return finishThenElseBlock(
+ const result = try finishThenElseBlock(
parent_gz,
rl,
node,
@@ -5617,6 +5641,16 @@ fn ifExpr(
block,
break_tag,
);
+ if (do_err_trace) {
+ _ = try parent_gz.add(.{
+ .tag = .restore_err_ret_index,
+ .data = .{ .un_node = .{
+ .operand = parent_gz.saved_err_trace_index,
+ .src_node = parent_gz.nodeIndexToRelative(node),
+ } },
+ });
+ }
+ return result;
}
/// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
@@ -10300,6 +10334,8 @@ const GenZir = struct {
/// Keys are the raw instruction index, values are the closure_capture instruction.
captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
+ saved_err_trace_index: Zir.Inst.Ref = .none,
+
const unstacked_top = std.math.maxInt(usize);
/// Call unstack before adding any new instructions to containing GenZir.
fn unstack(self: *GenZir) void {
@@ -10344,6 +10380,7 @@ const GenZir = struct {
.any_defer_node = gz.any_defer_node,
.instructions = gz.instructions,
.instructions_top = gz.instructions.items.len,
+ .saved_err_trace_index = gz.saved_err_trace_index,
};
}
src/print_zir.zig
@@ -232,6 +232,7 @@ const Writer = struct {
.validate_deref,
.overflow_arithmetic_ptr,
.check_comptime_control_flow,
+ .restore_err_ret_index,
=> try self.writeUnNode(stream, inst),
.ref,
@@ -405,6 +406,7 @@ const Writer = struct {
.alloc_inferred_comptime_mut,
.ret_ptr,
.ret_type,
+ .save_err_ret_index,
=> try self.writeNode(stream, inst),
.error_value,
@@ -440,7 +442,7 @@ const Writer = struct {
.dbg_block_begin,
.dbg_block_end,
- => try stream.writeAll("))"),
+ => try stream.writeAll(")"),
.closure_get => try self.writeInstNode(stream, inst),
src/Sema.zig
@@ -926,6 +926,8 @@ fn analyzeBodyInner(
.ret_ptr => try sema.zirRetPtr(block, inst),
.ret_type => try sema.addType(sema.fn_ret_ty),
+ .save_err_ret_index => try sema.zirSaveErrRetIndex(block, inst),
+
// Instructions that we know to *always* be noreturn based solely on their tag.
// These functions match the return type of analyzeBody so that we can
// tail call them here.
@@ -1208,6 +1210,11 @@ fn analyzeBodyInner(
i += 1;
continue;
},
+ .restore_err_ret_index => {
+ try sema.zirRestoreErrRetIndex(block, inst);
+ i += 1;
+ continue;
+ },
// Special case instructions to handle comptime control flow.
.@"break" => {
@@ -16176,6 +16183,52 @@ fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
backend_supports_error_return_tracing;
}
+fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].node;
+ const src = LazySrcLoc.nodeOffset(inst_data);
+
+ // This is only relevant at runtime.
+ if (block.is_comptime) return Air.Inst.Ref.zero_usize;
+
+ const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
+ const ok = sema.owner_func.?.calls_or_awaits_errorable_fn and
+ sema.mod.comp.bin_file.options.error_return_tracing and
+ backend_supports_error_return_tracing;
+ if (!ok) return Air.Inst.Ref.zero_usize;
+
+ const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
+ const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
+ const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty);
+ const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
+ return sema.fieldVal(block, src, err_return_trace, "index", src);
+}
+
+fn zirRestoreErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+
+ // This is only relevant at runtime.
+ if (block.is_comptime) return;
+
+ const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
+ const ok = sema.owner_func.?.calls_or_awaits_errorable_fn and
+ sema.mod.comp.bin_file.options.error_return_tracing and
+ backend_supports_error_return_tracing;
+ if (!ok) return;
+
+ const operand = if (inst_data.operand != .none)
+ try sema.resolveInst(inst_data.operand)
+ else
+ .zero_usize;
+
+ const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
+ const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
+ const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty);
+ const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
+ const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, "index", src, stack_trace_ty, true);
+ try sema.storePtr2(block, src, field_ptr, src, operand, src, .store);
+}
+
fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion);
@@ -17181,8 +17234,6 @@ fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const src = inst_data.src();
- _ = src;
const operand = try sema.resolveInst(inst_data.operand);
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
src/Zir.zig
@@ -988,6 +988,15 @@ pub const Inst = struct {
/// Uses the `err_defer_code` union field.
defer_err_code,
+ /// Saves the current error return case if it exists,
+ /// otherwise just returns zero.
+ /// Uses the `node` union field.
+ save_err_ret_index,
+ /// Sets error return trace to zero if no operand is given,
+ /// otherwise sets the value to the given amount.
+ /// Uses the `un_node` union field.
+ restore_err_ret_index,
+
/// The ZIR instruction tag is one of the `Extended` ones.
/// Uses the `extended` union field.
extended,
@@ -1236,6 +1245,8 @@ pub const Inst = struct {
//.try_ptr_inline,
.@"defer",
.defer_err_code,
+ .save_err_ret_index,
+ .restore_err_ret_index,
=> false,
.@"break",
@@ -1305,6 +1316,7 @@ pub const Inst = struct {
.check_comptime_control_flow,
.@"defer",
.defer_err_code,
+ .restore_err_ret_index,
=> true,
.param,
@@ -1530,6 +1542,7 @@ pub const Inst = struct {
.try_ptr,
//.try_inline,
//.try_ptr_inline,
+ .save_err_ret_index,
=> false,
.extended => switch (data.extended.opcode) {
@@ -1810,6 +1823,9 @@ pub const Inst = struct {
.@"defer" = .@"defer",
.defer_err_code = .defer_err_code,
+ .save_err_ret_index = .node,
+ .restore_err_ret_index = .un_node,
+
.extended = .extended,
});
};