@@ -62,7 +62,7 @@ pub const QualTypeHashContext = struct {
6262pub const Error = std .mem .Allocator .Error ;
6363pub const MacroProcessingError = Error || error {UnexpectedMacroToken };
6464pub const TypeError = Error || error {UnsupportedType };
65- pub const TransError = TypeError || error {UnsupportedTranslation };
65+ pub const TransError = TypeError || error { UnsupportedTranslation , SelfReferential };
6666
6767const Translator = @This ();
6868
@@ -117,6 +117,10 @@ typedefs: std.StringArrayHashMapUnmanaged(void) = .empty,
117117/// The lhs lval of a compound assignment expression.
118118compound_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+
120124pub 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
10591088fn 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
26092639fn 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 ;
0 commit comments