Commit 2fff25fd22

Cody Tapscott <topolarity@tapscott.me>
2022-07-09 00:09:56
stage2: Support initializing anonymous struct type
This commit adds support for initializing `.anon_struct` types. There is also some follow-up work to do for both tuples and structs regarding comptime fields, so this also adds some tests to keep track of that work.
1 parent 6f0807f
Changed files (3)
src
test
behavior
src/Sema.zig
@@ -3326,7 +3326,7 @@ fn zirValidateStructInit(
     switch (agg_ty.zigTypeTag()) {
         .Struct => return sema.validateStructInit(
             block,
-            agg_ty.castTag(.@"struct").?.data,
+            agg_ty,
             init_src,
             instrs,
             is_comptime,
@@ -3470,7 +3470,7 @@ fn validateUnionInit(
 fn validateStructInit(
     sema: *Sema,
     block: *Block,
-    struct_obj: *Module.Struct,
+    struct_ty: Type,
     init_src: LazySrcLoc,
     instrs: []const Zir.Inst.Index,
     is_comptime: bool,
@@ -3478,7 +3478,7 @@ fn validateStructInit(
     const gpa = sema.gpa;
 
     // Maps field index to field_ptr index of where it was already initialized.
-    const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());
+    const found_fields = try gpa.alloc(Zir.Inst.Index, struct_ty.structFieldCount());
     defer gpa.free(found_fields);
     mem.set(Zir.Inst.Index, found_fields, 0);
 
@@ -3490,8 +3490,7 @@ fn validateStructInit(
         const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
         struct_ptr_zir_ref = field_ptr_extra.lhs;
         const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
-        const field_index = struct_obj.fields.getIndex(field_name) orelse
-            return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
+        const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
         if (found_fields[field_index] != 0) {
             const other_field_ptr = found_fields[field_index];
             const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node;
@@ -3509,10 +3508,7 @@ fn validateStructInit(
 
     var root_msg: ?*Module.ErrorMsg = null;
 
-    const fields = struct_obj.fields.values();
     const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
-    const struct_ty = sema.typeOf(struct_ptr).childType();
-
     if ((is_comptime or block.is_comptime) and
         (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
     {
@@ -3522,10 +3518,9 @@ fn validateStructInit(
         for (found_fields) |field_ptr, i| {
             if (field_ptr != 0) continue;
 
-            const field = fields[i];
-            const field_name = struct_obj.fields.keys()[i];
-
-            if (field.default_val.tag() == .unreachable_value) {
+            const default_val = struct_ty.structFieldDefaultValue(i);
+            if (default_val.tag() == .unreachable_value) {
+                const field_name = struct_ty.structFieldName(i);
                 const template = "missing struct field: {s}";
                 const args = .{field_name};
                 if (root_msg) |msg| {
@@ -3536,22 +3531,25 @@ fn validateStructInit(
                 continue;
             }
 
-            const default_field_ptr = try sema.structFieldPtr(block, init_src, struct_ptr, field_name, init_src, struct_ty);
-            const init = try sema.addConstant(field.ty, field.default_val);
             const field_src = init_src; // TODO better source location
+            const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty);
+            const field_ty = sema.typeOf(default_field_ptr).childType();
+            const init = try sema.addConstant(field_ty, default_val);
             try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
         }
 
         if (root_msg) |msg| {
-            const mod = sema.mod;
-            const fqn = try struct_obj.getFullyQualifiedName(mod);
-            defer gpa.free(fqn);
-            try mod.errNoteNonLazy(
-                struct_obj.srcLoc(mod),
-                msg,
-                "struct '{s}' declared here",
-                .{fqn},
-            );
+            if (struct_ty.castTag(.@"struct")) |struct_obj| {
+                const mod = sema.mod;
+                const fqn = try struct_obj.data.getFullyQualifiedName(mod);
+                defer gpa.free(fqn);
+                try mod.errNoteNonLazy(
+                    struct_obj.data.srcLoc(mod),
+                    msg,
+                    "struct '{s}' declared here",
+                    .{fqn},
+                );
+            }
             return sema.failWithOwnedErrorMsg(block, msg);
         }
 
@@ -3566,17 +3564,16 @@ fn validateStructInit(
 
     // We collect the comptime field values in case the struct initialization
     // ends up being comptime-known.
-    const field_values = try sema.arena.alloc(Value, fields.len);
+    const field_values = try sema.arena.alloc(Value, struct_ty.structFieldCount());
 
     field: for (found_fields) |field_ptr, i| {
-        const field = fields[i];
-
         if (field_ptr != 0) {
             const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
             const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node };
 
             // Determine whether the value stored to this pointer is comptime-known.
-            if (try sema.typeHasOnePossibleValue(block, field_src, field.ty)) |opv| {
+            const field_ty = struct_ty.structFieldType(i);
+            if (try sema.typeHasOnePossibleValue(block, field_src, field_ty)) |opv| {
                 field_values[i] = opv;
                 continue;
             }
@@ -3648,9 +3645,9 @@ fn validateStructInit(
             continue :field;
         }
 
-        const field_name = struct_obj.fields.keys()[i];
-
-        if (field.default_val.tag() == .unreachable_value) {
+        const default_val = struct_ty.structFieldDefaultValue(i);
+        if (default_val.tag() == .unreachable_value) {
+            const field_name = struct_ty.structFieldName(i);
             const template = "missing struct field: {s}";
             const args = .{field_name};
             if (root_msg) |msg| {
@@ -3660,17 +3657,20 @@ fn validateStructInit(
             }
             continue;
         }
+        field_values[i] = default_val;
     }
 
     if (root_msg) |msg| {
-        const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
-        defer gpa.free(fqn);
-        try sema.mod.errNoteNonLazy(
-            struct_obj.srcLoc(sema.mod),
-            msg,
-            "struct '{s}' declared here",
-            .{fqn},
-        );
+        if (struct_ty.castTag(.@"struct")) |struct_obj| {
+            const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod);
+            defer gpa.free(fqn);
+            try sema.mod.errNoteNonLazy(
+                struct_obj.data.srcLoc(sema.mod),
+                msg,
+                "struct '{s}' declared here",
+                .{fqn},
+            );
+        }
         return sema.failWithOwnedErrorMsg(block, msg);
     }
 
@@ -3679,15 +3679,6 @@ fn validateStructInit(
         // instead a single `store` to the struct_ptr with a comptime struct value.
 
         block.instructions.shrinkRetainingCapacity(first_block_index);
-
-        // The `field_values` array has been populated for all the non-default struct
-        // fields. Here we fill in the default field values.
-        for (found_fields) |field_ptr, i| {
-            if (field_ptr != 0) continue;
-
-            field_values[i] = fields[i].default_val;
-        }
-
         const struct_val = try Value.Tag.aggregate.create(sema.arena, field_values);
         const struct_init = try sema.addConstant(struct_ty, struct_val);
         try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
@@ -3695,16 +3686,13 @@ fn validateStructInit(
     }
 
     // Our task is to insert `store` instructions for all the default field values.
-
     for (found_fields) |field_ptr, i| {
         if (field_ptr != 0) continue;
 
-        const field = fields[i];
-        const field_name = struct_obj.fields.keys()[i];
-        const default_field_ptr = try sema.structFieldPtr(block, init_src, struct_ptr, field_name, init_src, struct_ty);
-
-        const init = try sema.addConstant(field.ty, field.default_val);
         const field_src = init_src; // TODO better source location
+        const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty);
+        const field_ty = sema.typeOf(default_field_ptr).childType();
+        const init = try sema.addConstant(field_ty, field_values[i]);
         try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
     }
 }
@@ -13827,7 +13815,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
     const obj_ty = try sema.resolveType(block, src, inst_data.operand);
 
     switch (obj_ty.zigTypeTag()) {
-        .Struct => return structInitEmpty(sema, block, obj_ty, src, src),
+        .Struct => return sema.structInitEmpty(block, obj_ty, src, src),
         .Array => return arrayInitEmpty(sema, obj_ty),
         .Void => return sema.addConstant(obj_ty, Value.void),
         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
@@ -13844,29 +13832,13 @@ fn structInitEmpty(
     const gpa = sema.gpa;
     // This logic must be synchronized with that in `zirStructInit`.
     const struct_ty = try sema.resolveTypeFields(block, dest_src, obj_ty);
-    const struct_obj = struct_ty.castTag(.@"struct").?.data;
 
     // The init values to use for the struct instance.
-    const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
+    const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount());
     defer gpa.free(field_inits);
+    mem.set(Air.Inst.Ref, field_inits, .none);
 
-    var root_msg: ?*Module.ErrorMsg = null;
-
-    for (struct_obj.fields.values()) |field, i| {
-        if (field.default_val.tag() == .unreachable_value) {
-            const field_name = struct_obj.fields.keys()[i];
-            const template = "missing struct field: {s}";
-            const args = .{field_name};
-            if (root_msg) |msg| {
-                try sema.errNote(block, init_src, msg, template, args);
-            } else {
-                root_msg = try sema.errMsg(block, init_src, template, args);
-            }
-        } else {
-            field_inits[i] = try sema.addConstant(field.ty, field.default_val);
-        }
-    }
-    return sema.finishStructInit(block, dest_src, field_inits, root_msg, struct_obj, struct_ty, false);
+    return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false);
 }
 
 fn arrayInitEmpty(sema: *Sema, obj_ty: Type) CompileError!Air.Inst.Ref {
@@ -13936,19 +13908,18 @@ fn zirStructInit(
     const unresolved_struct_type = try sema.resolveType(block, src, first_field_type_extra.container_type);
     const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
 
-    if (resolved_ty.castTag(.@"struct")) |struct_payload| {
+    if (resolved_ty.zigTypeTag() == .Struct) {
         // This logic must be synchronized with that in `zirStructInitEmpty`.
-        const struct_obj = struct_payload.data;
 
         // Maps field index to field_type index of where it was already initialized.
         // For making sure all fields are accounted for and no fields are duplicated.
-        const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());
+        const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount());
         defer gpa.free(found_fields);
-        mem.set(Zir.Inst.Index, found_fields, 0);
 
         // The init values to use for the struct instance.
-        const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
+        const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount());
         defer gpa.free(field_inits);
+        mem.set(Air.Inst.Ref, field_inits, .none);
 
         var field_i: u32 = 0;
         var extra_index = extra.end;
@@ -13961,9 +13932,8 @@ fn zirStructInit(
             const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
             const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
             const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
-            const field_index = struct_obj.fields.getIndex(field_name) orelse
-                return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
-            if (found_fields[field_index] != 0) {
+            const field_index = try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
+            if (field_inits[field_index] != .none) {
                 const other_field_type = found_fields[field_index];
                 const other_field_type_data = zir_datas[other_field_type].pl_node;
                 const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node };
@@ -13979,27 +13949,7 @@ fn zirStructInit(
             field_inits[field_index] = try sema.resolveInst(item.data.init);
         }
 
-        var root_msg: ?*Module.ErrorMsg = null;
-
-        for (found_fields) |field_type_inst, i| {
-            if (field_type_inst != 0) continue;
-
-            // Check if the field has a default init.
-            const field = struct_obj.fields.values()[i];
-            if (field.default_val.tag() == .unreachable_value) {
-                const field_name = struct_obj.fields.keys()[i];
-                const template = "missing struct field: {s}";
-                const args = .{field_name};
-                if (root_msg) |msg| {
-                    try sema.errNote(block, src, msg, template, args);
-                } else {
-                    root_msg = try sema.errMsg(block, src, template, args);
-                }
-            } else {
-                field_inits[i] = try sema.addConstant(field.ty, field.default_val);
-            }
-        }
-        return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, resolved_ty, is_ref);
+        return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref);
     } else if (resolved_ty.zigTypeTag() == .Union) {
         if (extra.data.fields_len != 1) {
             return sema.fail(block, src, "union initialization expects exactly one field", .{});
@@ -14051,29 +14001,69 @@ fn zirStructInit(
 fn finishStructInit(
     sema: *Sema,
     block: *Block,
-    src: LazySrcLoc,
-    field_inits: []const Air.Inst.Ref,
-    root_msg: ?*Module.ErrorMsg,
-    struct_obj: *Module.Struct,
+    init_src: LazySrcLoc,
+    dest_src: LazySrcLoc,
+    field_inits: []Air.Inst.Ref,
     struct_ty: Type,
     is_ref: bool,
-) !Air.Inst.Ref {
+) CompileError!Air.Inst.Ref {
     const gpa = sema.gpa;
 
+    var root_msg: ?*Module.ErrorMsg = null;
+    if (struct_ty.isAnonStruct()) {
+        const struct_obj = struct_ty.castTag(.anon_struct).?.data;
+        for (struct_obj.values) |default_val, i| {
+            if (field_inits[i] != .none) continue;
+
+            if (default_val.tag() == .unreachable_value) {
+                const field_name = struct_obj.names[i];
+                const template = "missing struct field: {s}";
+                const args = .{field_name};
+                if (root_msg) |msg| {
+                    try sema.errNote(block, init_src, msg, template, args);
+                } else {
+                    root_msg = try sema.errMsg(block, init_src, template, args);
+                }
+            } else {
+                field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val);
+            }
+        }
+    } else {
+        const struct_obj = struct_ty.castTag(.@"struct").?.data;
+        for (struct_obj.fields.values()) |field, i| {
+            if (field_inits[i] != .none) continue;
+
+            if (field.default_val.tag() == .unreachable_value) {
+                const field_name = struct_obj.fields.keys()[i];
+                const template = "missing struct field: {s}";
+                const args = .{field_name};
+                if (root_msg) |msg| {
+                    try sema.errNote(block, init_src, msg, template, args);
+                } else {
+                    root_msg = try sema.errMsg(block, init_src, template, args);
+                }
+            } else {
+                field_inits[i] = try sema.addConstant(field.ty, field.default_val);
+            }
+        }
+    }
+
     if (root_msg) |msg| {
-        const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
-        defer gpa.free(fqn);
-        try sema.mod.errNoteNonLazy(
-            struct_obj.srcLoc(sema.mod),
-            msg,
-            "struct '{s}' declared here",
-            .{fqn},
-        );
+        if (struct_ty.castTag(.@"struct")) |struct_obj| {
+            const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod);
+            defer gpa.free(fqn);
+            try sema.mod.errNoteNonLazy(
+                struct_obj.data.srcLoc(sema.mod),
+                msg,
+                "struct '{s}' declared here",
+                .{fqn},
+            );
+        }
         return sema.failWithOwnedErrorMsg(block, msg);
     }
 
     const is_comptime = for (field_inits) |field_init| {
-        if (!(try sema.isComptimeKnown(block, src, field_init))) {
+        if (!(try sema.isComptimeKnown(block, dest_src, field_init))) {
             break false;
         }
     } else true;
@@ -14081,10 +14071,10 @@ fn finishStructInit(
     if (is_comptime) {
         const values = try sema.arena.alloc(Value, field_inits.len);
         for (field_inits) |field_init, i| {
-            values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
+            values[i] = (sema.resolveMaybeUndefVal(block, dest_src, field_init) catch unreachable).?;
         }
         const struct_val = try Value.Tag.aggregate.create(sema.arena, values);
-        return sema.addConstantMaybeRef(block, src, struct_ty, struct_val, is_ref);
+        return sema.addConstantMaybeRef(block, dest_src, struct_ty, struct_val, is_ref);
     }
 
     if (is_ref) {
@@ -14096,15 +14086,15 @@ fn finishStructInit(
         const alloc = try block.addTy(.alloc, alloc_ty);
         for (field_inits) |field_init, i_usize| {
             const i = @intCast(u32, i_usize);
-            const field_src = src;
-            const field_ptr = try sema.structFieldPtrByIndex(block, src, alloc, i, struct_obj, field_src);
-            try sema.storePtr(block, src, field_ptr, field_init);
+            const field_src = dest_src;
+            const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, alloc, i, field_src, struct_ty);
+            try sema.storePtr(block, dest_src, field_ptr, field_init);
         }
 
         return alloc;
     }
 
-    try sema.requireRuntimeBlock(block, src);
+    try sema.requireRuntimeBlock(block, dest_src);
     try sema.queueFullTypeResolution(struct_ty);
     return block.addAggregateInit(struct_ty, field_inits);
 }
@@ -19059,7 +19049,7 @@ fn structFieldPtr(
         return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
     const field_index = @intCast(u32, field_index_big);
 
-    return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_obj, field_name_src);
+    return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty);
 }
 
 fn structFieldPtrByIndex(
@@ -19068,9 +19058,14 @@ fn structFieldPtrByIndex(
     src: LazySrcLoc,
     struct_ptr: Air.Inst.Ref,
     field_index: u32,
-    struct_obj: *Module.Struct,
     field_src: LazySrcLoc,
+    struct_ty: Type,
 ) CompileError!Air.Inst.Ref {
+    if (struct_ty.isAnonStruct()) {
+        return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index);
+    }
+
+    const struct_obj = struct_ty.castTag(.@"struct").?.data;
     const field = struct_obj.fields.values()[field_index];
     const struct_ptr_ty = sema.typeOf(struct_ptr);
     const struct_ptr_ty_info = struct_ptr_ty.ptrInfo().data;
@@ -20356,7 +20351,7 @@ fn coerce(
         },
         .Struct => {
             if (inst == .empty_struct) {
-                return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src);
+                return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src);
             }
             if (inst_ty.isTupleOrAnonStruct()) {
                 return sema.coerceTupleToStruct(block, dest_ty, dest_ty_src, inst, inst_src);
@@ -25754,10 +25749,14 @@ fn structFieldIndex(
     field_src: LazySrcLoc,
 ) !u32 {
     const struct_ty = try sema.resolveTypeFields(block, field_src, unresolved_struct_ty);
-    const struct_obj = struct_ty.castTag(.@"struct").?.data;
-    const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
-        return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
-    return @intCast(u32, field_index_usize);
+    if (struct_ty.isAnonStruct()) {
+        return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src);
+    } else {
+        const struct_obj = struct_ty.castTag(.@"struct").?.data;
+        const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
+            return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
+        return @intCast(u32, field_index_usize);
+    }
 }
 
 fn anonStructFieldIndex(
src/type.zig
@@ -5432,6 +5432,24 @@ pub const Type = extern union {
         }
     }
 
+    pub fn structFieldDefaultValue(ty: Type, index: usize) Value {
+        switch (ty.tag()) {
+            .@"struct" => {
+                const struct_obj = ty.castTag(.@"struct").?.data;
+                return struct_obj.fields.values()[index].default_val;
+            },
+            .tuple => {
+                const tuple = ty.castTag(.tuple).?.data;
+                return tuple.values[index];
+            },
+            .anon_struct => {
+                const struct_obj = ty.castTag(.anon_struct).?.data;
+                return struct_obj.values[index];
+            },
+            else => unreachable,
+        }
+    }
+
     pub fn structFieldValueComptime(ty: Type, index: usize) ?Value {
         switch (ty.tag()) {
             .@"struct" => {
test/behavior/tuple.zig
@@ -197,3 +197,61 @@ test "initializing tuple with explicit type" {
     var a = T{ 0, 0 };
     _ = a;
 }
+
+test "initializing anon struct with explicit type" {
+    const T = @TypeOf(.{ .foo = @as(i32, 1), .bar = @as(i32, 2) });
+    var a = T{ .foo = 1, .bar = 2 };
+    _ = a;
+}
+
+test "fieldParentPtr of tuple" {
+    if (builtin.zig_backend != .stage1) return error.SkipZigTest;
+
+    var x: u32 = 0;
+    const tuple = .{ x, x };
+    try testing.expect(&tuple == @fieldParentPtr(@TypeOf(tuple), "1", &tuple[1]));
+}
+
+test "fieldParentPtr of anon struct" {
+    if (builtin.zig_backend != .stage1) return error.SkipZigTest;
+
+    var x: u32 = 0;
+    const anon_st = .{ .foo = x, .bar = x };
+    try testing.expect(&anon_st == @fieldParentPtr(@TypeOf(anon_st), "bar", &anon_st.bar));
+}
+
+test "offsetOf tuple" {
+    if (builtin.zig_backend != .stage1) return error.SkipZigTest;
+
+    var x: u32 = 0;
+    const T = @TypeOf(.{ x, x });
+
+    _ = @offsetOf(T, "1");
+}
+
+test "offsetOf anon struct" {
+    if (builtin.zig_backend != .stage1) return error.SkipZigTest;
+
+    var x: u32 = 0;
+    const T = @TypeOf(.{ .foo = x, .bar = x });
+
+    _ = @offsetOf(T, "bar");
+}
+
+test "initializing tuple with mixed comptime-runtime fields" {
+    if (true) return error.SkipZigTest; // TODO
+
+    var x: u32 = 15;
+    const T = @TypeOf(.{ @as(i32, -1234), @as(u32, 5678), x });
+    var a: T = .{ -1234, 5678, x + 1 };
+    _ = a;
+}
+
+test "initializing anon struct with mixed comptime-runtime fields" {
+    if (true) return error.SkipZigTest; // TODO
+
+    var x: u32 = 15;
+    const T = @TypeOf(.{ .foo = @as(i32, -1234), .bar = x });
+    var a: T = .{ .foo = -1234, .bar = x + 1 };
+    _ = a;
+}