Skip to content

Commit ba13778

Browse files
authored
forbid trivial local address returned from functions (#25333)
progress towards #25312
1 parent 7b92d5f commit ba13778

3 files changed

Lines changed: 34 additions & 9 deletions

File tree

lib/std/zig/AstGen.zig

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ const ResultInfo = struct {
383383
assignment,
384384
/// No specific operator in particular.
385385
none,
386+
/// The expression is operand to address-of which is the operand to a return expression.
387+
return_addrof,
386388
};
387389
};
388390

@@ -955,7 +957,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
955957
_ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node));
956958
break :rl .{ .ref_coerced_ty = res_ty_inst };
957959
} 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);
959968
return rvalue(gz, ri, result, node);
960969
},
961970
.optional_type => {
@@ -8420,13 +8429,19 @@ fn localVarRef(
84208429
local_ptr.used = .fromToken(ident_token);
84218430
}
84228431

8423-
// Can't close over a runtime variable
8424-
if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
8425-
const ident_name = try astgen.identifierTokenString(ident_token);
8426-
return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
8427-
try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
8428-
try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),
8429-
});
8432+
if (!local_ptr.maybe_comptime and !gz.is_typeof) {
8433+
if (num_namespaces_out != 0) {
8434+
const ident_name = try astgen.identifierTokenString(ident_token);
8435+
return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
8436+
try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
8437+
try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),
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+
}
84308445
}
84318446

84328447
switch (ri.rl) {

test/behavior/fn.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,8 @@ test "return undefined pointer from function, directly and by expired local" {
758758
/// Semantically equivalent to `returnUndefPointer`.
759759
fn returnStackPointer() *i32 {
760760
var stack_allocation: i32 = 1234;
761-
return &stack_allocation;
761+
const ptr = &stack_allocation; // defeats ast-check detection
762+
return ptr;
762763
}
763764
};
764765

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export 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

0 commit comments

Comments
 (0)