authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-23 16:37:53-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-09-23 23:37:53+00:00
logba137783ed4152ebe8d7b98dca855c551fc924a7
tree2657d21ad497ec9d4e1050803e4f1b1e28063457
parent7b92d5f4052be651e9bc5cd4ad78a69ccbee865d
signaturebadge-check Signed by PGP key B5690EEEBB952194

forbid trivial local address returned from functions (#25333)

progress towards #25312

3 files changed, 34 insertions(+), 9 deletions(-)

lib/std/zig/AstGen.zig+23-8
...@@ -383,6 +383,8 @@ const ResultInfo = struct {...@@ -383,6 +383,8 @@ const ResultInfo = struct {
383 assignment,383 assignment,
384 /// No specific operator in particular.384 /// No specific operator in particular.
385 none,385 none,
386 /// The expression is operand to address-of which is the operand to a return expression.
387 return_addrof,
386 };388 };
387};389};
388390
...@@ -955,7 +957,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -955,7 +957,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
955 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node));957 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node));
956 break :rl .{ .ref_coerced_ty = res_ty_inst };958 break :rl .{ .ref_coerced_ty = res_ty_inst };
957 } else .ref;959 } else .ref;
958 const result = try expr(gz, scope, .{ .rl = operand_rl }, tree.nodeData(node).node);960 const operand_node = tree.nodeData(node).node;
961 const result = try expr(gz, scope, .{
962 .rl = operand_rl,
963 .ctx = switch (ri.ctx) {
964 .@"return" => .return_addrof,
965 else => .none,
966 },
967 }, operand_node);
959 return rvalue(gz, ri, result, node);968 return rvalue(gz, ri, result, node);
960 },969 },
961 .optional_type => {970 .optional_type => {
...@@ -8420,13 +8429,19 @@ fn localVarRef(...@@ -8420,13 +8429,19 @@ fn localVarRef(
8420 local_ptr.used = .fromToken(ident_token);8429 local_ptr.used = .fromToken(ident_token);
8421 }8430 }
84228431
8423 // Can't close over a runtime variable8432 if (!local_ptr.maybe_comptime and !gz.is_typeof) {
8424 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {8433 if (num_namespaces_out != 0) {
8425 const ident_name = try astgen.identifierTokenString(ident_token);8434 const ident_name = try astgen.identifierTokenString(ident_token);
8426 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{8435 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
8427 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),8436 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
8428 try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),8437 try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),
8429 });8438 });
8439 } else if (ri.ctx == .return_addrof) {
8440 const ident_name = try astgen.identifierTokenString(ident_token);
8441 return astgen.failNodeNotes(ident, "returning address of expired local variable '{s}'", .{ident_name}, &.{
8442 try astgen.errNoteTok(local_ptr.token_src, "declared runtime-known here", .{}),
8443 });
8444 }
8430 }8445 }
84318446
8432 switch (ri.rl) {8447 switch (ri.rl) {
test/behavior/fn.zig+2-1
...@@ -758,7 +758,8 @@ test "return undefined pointer from function, directly and by expired local" {...@@ -758,7 +758,8 @@ test "return undefined pointer from function, directly and by expired local" {
758 /// Semantically equivalent to `returnUndefPointer`.758 /// Semantically equivalent to `returnUndefPointer`.
759 fn returnStackPointer() *i32 {759 fn returnStackPointer() *i32 {
760 var stack_allocation: i32 = 1234;760 var stack_allocation: i32 = 1234;
761 return &stack_allocation;761 const ptr = &stack_allocation; // defeats ast-check detection
762 return ptr;
762 }763 }
763 };764 };
764765
test/cases/compile_errors/return_addr_of_local.zig created+9
...@@ -0,0 +1,9 @@
1export fn b() void {
2 var x: i32 = 1234;
3 return &x;
4}
5
6// error
7//
8// :3:13: error: returning address of expired local variable 'x'
9// :2:9: note: declared runtime-known here