diff --git a/src/AstGen.zig b/src/AstGen.zig index bb4469866677..3eddc6c1c554 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2466,6 +2466,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) { @@ -2528,6 +2529,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, @@ -5144,10 +5146,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, @@ -5212,7 +5220,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, @@ -5227,6 +5235,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`. @@ -5422,10 +5440,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 @@ -5594,7 +5618,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, @@ -5609,6 +5633,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`. @@ -10289,6 +10323,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 { @@ -10333,6 +10369,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, }; } diff --git a/src/Sema.zig b/src/Sema.zig index 97ea632647c4..81497d090cfd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -923,6 +923,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. @@ -1205,6 +1207,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" => { @@ -16135,6 +16142,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); @@ -17139,8 +17192,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 }; diff --git a/src/Zir.zig b/src/Zir.zig index 9881ee161750..f08d78f8f24a 100644 --- a/src/Zir.zig +++ b/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, }); }; diff --git a/src/print_zir.zig b/src/print_zir.zig index aab7444e0844..5d336b5a7346 100644 --- a/src/print_zir.zig +++ b/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),