Commit feb90f6ed4

Andrew Kelley <andrew@ziglang.org>
2022-08-09 05:53:06
AstGen: emit debug stmt for try
This improves the following test case: ```zig pub fn main() !void { try foo(); } fn foo() !void { return error.Bad; } ``` The error return trace now points to the `try` token instead of pointing to the foo() function call, matching stage1. Closes #12308.
1 parent 0648177
Changed files (2)
src/AstGen.zig
@@ -5051,6 +5051,16 @@ fn tryExpr(
 
     if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});
 
+    // Ensure debug line/column information is emitted for this try expression.
+    // Then we will save the line/column so that we can emit another one that goes
+    // "backwards" because we want to evaluate the operand, but then put the debug
+    // info back at the try keyword for error return tracing.
+    if (!parent_gz.force_comptime) {
+        try emitDbgNode(parent_gz, node);
+    }
+    const try_line = astgen.source_line - parent_gz.decl_line;
+    const try_column = astgen.source_column;
+
     const operand_rl: ResultLoc = switch (rl) {
         .ref => .ref,
         else => .none,
@@ -5080,6 +5090,7 @@ fn tryExpr(
     };
     const err_code = try else_scope.addUnNode(err_tag, operand, node);
     try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
+    try emitDbgStmt(&else_scope, try_line, try_column);
     _ = try else_scope.addUnNode(.ret_node, err_code, node);
 
     try else_scope.setTryBody(try_inst, operand);
@@ -6614,13 +6625,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
         const defer_counts = countDefers(astgen, defer_outer, scope);
         if (!defer_counts.need_err_code) {
             try genDefers(gz, defer_outer, scope, .both_sans_err);
-            try emitRetDbgStmt(gz, ret_line, ret_column);
+            try emitDbgStmt(gz, ret_line, ret_column);
             _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
             return Zir.Inst.Ref.unreachable_value;
         }
         const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
         try genDefers(gz, defer_outer, scope, .{ .both = err_code });
-        try emitRetDbgStmt(gz, ret_line, ret_column);
+        try emitDbgStmt(gz, ret_line, ret_column);
         _ = try gz.addUnNode(.ret_node, err_code, node);
         return Zir.Inst.Ref.unreachable_value;
     }
@@ -6639,7 +6650,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
         .never => {
             // Returning a value that cannot be an error; skip error defers.
             try genDefers(gz, defer_outer, scope, .normal_only);
-            try emitRetDbgStmt(gz, ret_line, ret_column);
+            try emitDbgStmt(gz, ret_line, ret_column);
             try gz.addRet(rl, operand, node);
             return Zir.Inst.Ref.unreachable_value;
         },
@@ -6647,7 +6658,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
             // Value is always an error. Emit both error defers and regular defers.
             const err_code = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand;
             try genDefers(gz, defer_outer, scope, .{ .both = err_code });
-            try emitRetDbgStmt(gz, ret_line, ret_column);
+            try emitDbgStmt(gz, ret_line, ret_column);
             try gz.addRet(rl, operand, node);
             return Zir.Inst.Ref.unreachable_value;
         },
@@ -6656,7 +6667,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
             if (!defer_counts.have_err) {
                 // Only regular defers; no branch needed.
                 try genDefers(gz, defer_outer, scope, .normal_only);
-                try emitRetDbgStmt(gz, ret_line, ret_column);
+                try emitDbgStmt(gz, ret_line, ret_column);
                 try gz.addRet(rl, operand, node);
                 return Zir.Inst.Ref.unreachable_value;
             }
@@ -6670,7 +6681,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
             defer then_scope.unstack();
 
             try genDefers(&then_scope, defer_outer, scope, .normal_only);
-            try emitRetDbgStmt(gz, ret_line, ret_column);
+            try emitDbgStmt(&then_scope, ret_line, ret_column);
             try then_scope.addRet(rl, operand, node);
 
             var else_scope = gz.makeSubBlock(scope);
@@ -6680,7 +6691,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
                 .both = try else_scope.addUnNode(.err_union_code, result, node),
             };
             try genDefers(&else_scope, defer_outer, scope, which_ones);
-            try emitRetDbgStmt(gz, ret_line, ret_column);
+            try emitDbgStmt(&else_scope, ret_line, ret_column);
             try else_scope.addRet(rl, operand, node);
 
             try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
@@ -11698,7 +11709,7 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
     return @intCast(u32, count);
 }
 
-fn emitRetDbgStmt(gz: *GenZir, line: u32, column: u32) !void {
+fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void {
     if (gz.force_comptime) return;
 
     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
test/stack_traces.zig
@@ -22,6 +22,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
         .ReleaseSafe = .{
             .exclude_os = .{
                 .windows, // segfault
+                .linux, // defeated by aggressive inlining
             },
             .expect = 
             \\error: TheSkyIsFalling