Skip to content
This repository was archived by the owner on Nov 26, 2025. It is now read-only.

Commit 683a7da

Browse files
authored
detect self-referential initializers
1 parent d7f1262 commit 683a7da

File tree

2 files changed

+61
-5
lines changed

2 files changed

+61
-5
lines changed

src/Translator.zig

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub const QualTypeHashContext = struct {
6262
pub const Error = std.mem.Allocator.Error;
6363
pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
6464
pub const TypeError = Error || error{UnsupportedType};
65-
pub const TransError = TypeError || error{UnsupportedTranslation};
65+
pub const TransError = TypeError || error{ UnsupportedTranslation, SelfReferential };
6666

6767
const Translator = @This();
6868

@@ -117,6 +117,10 @@ typedefs: std.StringArrayHashMapUnmanaged(void) = .empty,
117117
/// The lhs lval of a compound assignment expression.
118118
compound_assign_dummy: ?ZigNode = null,
119119

120+
/// Set of variables whose initializers are currently being translated.
121+
/// Used to detect self-referential initializers.
122+
wip_var_inits: std.AutoHashMapUnmanaged(Node.Index, void) = .empty,
123+
120124
pub fn getMangle(t: *Translator) u32 {
121125
t.mangle_count += 1;
122126
return t.mangle_count;
@@ -242,6 +246,7 @@ pub fn translate(options: Options) mem.Allocator.Error![]u8 {
242246
translator.anonymous_record_field_names.deinit(gpa);
243247
translator.typedefs.deinit(gpa);
244248
translator.global_scope.deinit();
249+
translator.wip_var_inits.deinit(gpa);
245250
}
246251

247252
try translator.prepopulateGlobalNameTable();
@@ -421,7 +426,7 @@ fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void {
421426

422427
.variable => |variable| {
423428
if (variable.definition != null) return;
424-
try t.transVarDecl(scope, variable);
429+
try t.transVarDecl(scope, variable, decl);
425430
},
426431
.static_assert => |static_assert| {
427432
try t.transStaticAssert(&t.global_scope.base, static_assert);
@@ -818,6 +823,7 @@ fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!voi
818823

819824
t.transCompoundStmtInline(body_stmt, &block_scope) catch |err| switch (err) {
820825
error.OutOfMemory => |e| return e,
826+
error.SelfReferential => unreachable,
821827
error.UnsupportedTranslation,
822828
error.UnsupportedType,
823829
=> {
@@ -834,7 +840,7 @@ fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!voi
834840
return t.addTopLevelDecl(fn_name, proto_node);
835841
}
836842

837-
fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!void {
843+
fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable, decl_node: Node.Index) Error!void {
838844
const base_name = t.tree.tokSlice(variable.name_tok);
839845
const toplevel = scope.id == .root;
840846
const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
@@ -872,13 +878,21 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo
872878
var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const");
873879
var is_extern = variable.storage_class == .@"extern";
874880

881+
var self_referential = false;
875882
const init_node = init: {
876883
if (variable.initializer) |init| {
877884
const maybe_literal = init.get(t.tree);
885+
if (!toplevel) try t.wip_var_inits.putNoClobber(t.gpa, decl_node, {});
886+
defer _ = t.wip_var_inits.remove(decl_node);
887+
878888
const init_node = (if (maybe_literal == .string_literal_expr)
879889
t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node)
880890
else
881891
t.transExprCoercing(scope, init, .used)) catch |err| switch (err) {
892+
error.SelfReferential => {
893+
self_referential = true;
894+
break :init ZigTag.undefined_literal.init();
895+
},
882896
error.UnsupportedTranslation, error.UnsupportedType => {
883897
return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
884898
},
@@ -933,7 +947,7 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo
933947
const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;
934948
var node = try ZigTag.var_decl.create(t.arena, .{
935949
.is_pub = toplevel,
936-
.is_const = is_const,
950+
.is_const = is_const and !self_referential,
937951
.is_extern = is_extern,
938952
.is_export = toplevel and variable.storage_class == .auto and linkage == .strong,
939953
.is_threadlocal = variable.thread_local,
@@ -951,6 +965,21 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo
951965
node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node });
952966
}
953967
try scope.appendNode(node);
968+
if (self_referential) {
969+
var deferred_init = t.transExprCoercing(scope, variable.initializer.?, .used) catch |err| switch (err) {
970+
error.SelfReferential => unreachable,
971+
error.UnsupportedTranslation, error.UnsupportedType => {
972+
return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
973+
},
974+
else => |e| return e,
975+
};
976+
if (!variable.qt.is(t.comp, .bool) and deferred_init.isBoolRes()) {
977+
deferred_init = try ZigTag.int_from_bool.create(t.arena, deferred_init);
978+
}
979+
const varname = try ZigTag.identifier.create(t.arena, name);
980+
const assign = try ZigTag.assign.create(t.arena, .{ .lhs = varname, .rhs = deferred_init });
981+
try scope.appendNode(assign);
982+
}
954983
try bs.discardVariable(name);
955984

956985
if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| {
@@ -1058,6 +1087,7 @@ fn transEnumDecl(t: *Translator, scope: *Scope, enum_qt: QualType) Error!void {
10581087

10591088
fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAssert) Error!void {
10601089
const condition = t.transExpr(scope, static_assert.cond, .used) catch |err| switch (err) {
1090+
error.SelfReferential => unreachable,
10611091
error.UnsupportedTranslation, error.UnsupportedType => {
10621092
return try t.warn(&t.global_scope.base, static_assert.cond.tok(t.tree), "unable to translate _Static_assert condition", .{});
10631093
},
@@ -1593,7 +1623,7 @@ fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode
15931623
return ZigTag.declaration.init();
15941624
},
15951625
.variable => |variable| {
1596-
try t.transVarDecl(scope, variable);
1626+
try t.transVarDecl(scope, variable, stmt);
15971627
return ZigTag.declaration.init();
15981628
},
15991629
.switch_stmt => |switch_stmt| return t.transSwitch(scope, switch_stmt),
@@ -2607,6 +2637,8 @@ fn transPointerCastExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransEr
26072637
}
26082638

26092639
fn transDeclRefExpr(t: *Translator, scope: *Scope, decl_ref: Node.DeclRef) TransError!ZigNode {
2640+
if (t.wip_var_inits.contains(decl_ref.decl)) return error.SelfReferential;
2641+
26102642
const name = t.tree.tokSlice(decl_ref.name_tok);
26112643
const maybe_alias = scope.getAlias(name);
26122644
const mangled_name = maybe_alias orelse name;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
int *a = (int*)(&a + 1); // TODO result missing casts
2+
3+
extern void *alloc(unsigned long long);
4+
void foo(void) {
5+
int b = b + 1;
6+
int *c = alloc(sizeof(*c));
7+
const double *d = alloc(sizeof(*d));
8+
}
9+
10+
// translate
11+
//
12+
// pub export var a: [*c]c_int = @ptrCast(@alignCast((&a) + @as(usize, @bitCast(@as(isize, @intCast(@as(c_int, 1)))))));
13+
// pub extern fn alloc(c_ulonglong) ?*anyopaque;
14+
// pub export fn foo() void {
15+
// var b: c_int = undefined;
16+
// b = b + @as(c_int, 1);
17+
// _ = &b;
18+
// var c: [*c]c_int = undefined;
19+
// c = @ptrCast(@alignCast(alloc(@sizeOf(@TypeOf(c.*)))));
20+
// _ = &c;
21+
// var d: [*c]const f64 = undefined;
22+
// d = @ptrCast(@alignCast(alloc(@sizeOf(@TypeOf(d.*)))));
23+
// _ = &d;
24+
// }

0 commit comments

Comments
 (0)