Commit 5ea94e7715

Andrew Kelley <andrew@ziglang.org>
2022-03-14 20:28:52
stage2: rework Value storage of structs and arrays
Now they both use `Value.Tag.aggregate`. Additionally the LLVM backend now has implemented lowering of tuple values.
1 parent 1ebe3bd
src/codegen/c.zig
@@ -748,7 +748,7 @@ pub const DeclGen = struct {
                 else => unreachable,
             },
             .Struct => {
-                const field_vals = val.castTag(.@"struct").?.data;
+                const field_vals = val.castTag(.aggregate).?.data;
 
                 try writer.writeAll("(");
                 try dg.renderTypecast(writer, ty);
src/codegen/llvm.zig
@@ -1511,8 +1511,8 @@ pub const DeclGen = struct {
                         .True, // don't null terminate. bytes has the sentinel, if any.
                     );
                 },
-                .array => {
-                    const elem_vals = tv.val.castTag(.array).?.data;
+                .aggregate => {
+                    const elem_vals = tv.val.castTag(.aggregate).?.data;
                     const elem_ty = tv.ty.elemType();
                     const gpa = dg.gpa;
                     const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len);
@@ -1665,11 +1665,76 @@ pub const DeclGen = struct {
             },
             .Struct => {
                 const llvm_struct_ty = try dg.llvmType(tv.ty);
-                const field_vals = tv.val.castTag(.@"struct").?.data;
+                const field_vals = tv.val.castTag(.aggregate).?.data;
                 const gpa = dg.gpa;
-                const struct_obj = tv.ty.castTag(.@"struct").?.data;
                 const target = dg.module.getTarget();
 
+                if (tv.ty.isTupleOrAnonStruct()) {
+                    const tuple = tv.ty.tupleFields();
+                    var llvm_fields: std.ArrayListUnmanaged(*const llvm.Value) = .{};
+                    defer llvm_fields.deinit(gpa);
+
+                    try llvm_fields.ensureUnusedCapacity(gpa, tuple.types.len);
+
+                    comptime assert(struct_layout_version == 2);
+                    var offset: u64 = 0;
+                    var big_align: u32 = 0;
+                    var need_unnamed = false;
+
+                    for (tuple.types) |field_ty, i| {
+                        if (tuple.values[i].tag() != .unreachable_value) continue;
+                        if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue;
+
+                        const field_align = field_ty.abiAlignment(target);
+                        big_align = @maximum(big_align, field_align);
+                        const prev_offset = offset;
+                        offset = std.mem.alignForwardGeneric(u64, offset, field_align);
+
+                        const padding_len = offset - prev_offset;
+                        if (padding_len > 0) {
+                            const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
+                            // TODO make this and all other padding elsewhere in debug
+                            // builds be 0xaa not undef.
+                            llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef());
+                        }
+
+                        const field_llvm_val = try dg.genTypedValue(.{
+                            .ty = field_ty,
+                            .val = field_vals[i],
+                        });
+
+                        need_unnamed = need_unnamed or dg.isUnnamedType(field_ty, field_llvm_val);
+
+                        llvm_fields.appendAssumeCapacity(field_llvm_val);
+
+                        offset += field_ty.abiSize(target);
+                    }
+                    {
+                        const prev_offset = offset;
+                        offset = std.mem.alignForwardGeneric(u64, offset, big_align);
+                        const padding_len = offset - prev_offset;
+                        if (padding_len > 0) {
+                            const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
+                            llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef());
+                        }
+                    }
+
+                    if (need_unnamed) {
+                        return dg.context.constStruct(
+                            llvm_fields.items.ptr,
+                            @intCast(c_uint, llvm_fields.items.len),
+                            .False,
+                        );
+                    } else {
+                        return llvm_struct_ty.constNamedStruct(
+                            llvm_fields.items.ptr,
+                            @intCast(c_uint, llvm_fields.items.len),
+                        );
+                    }
+                }
+
+                const struct_obj = tv.ty.castTag(.@"struct").?.data;
+
                 if (struct_obj.layout == .Packed) {
                     const big_bits = struct_obj.packedIntegerBits(target);
                     const int_llvm_ty = dg.context.intType(big_bits);
@@ -1707,8 +1772,8 @@ pub const DeclGen = struct {
                 comptime assert(struct_layout_version == 2);
                 var offset: u64 = 0;
                 var big_align: u32 = 0;
-
                 var need_unnamed = false;
+
                 for (struct_obj.fields.values()) |field, i| {
                     if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue;
 
@@ -1854,10 +1919,10 @@ pub const DeclGen = struct {
                         @intCast(c_uint, llvm_elems.len),
                     );
                 },
-                .array => {
+                .aggregate => {
                     // Note, sentinel is not stored even if the type has a sentinel.
                     // The value includes the sentinel in those cases.
-                    const elem_vals = tv.val.castTag(.array).?.data;
+                    const elem_vals = tv.val.castTag(.aggregate).?.data;
                     const vector_len = @intCast(usize, tv.ty.arrayLen());
                     assert(vector_len == elem_vals.len or vector_len + 1 == elem_vals.len);
                     const elem_ty = tv.ty.elemType();
src/link/MachO.zig
@@ -3877,7 +3877,7 @@ fn needsPointerRebase(ty: Type, val: Value) bool {
         .Struct => {
             const fields = ty.structFields().values();
             if (fields.len == 0) return false;
-            if (val.castTag(.@"struct")) |payload| {
+            if (val.castTag(.aggregate)) |payload| {
                 const field_values = payload.data;
                 for (field_values) |field_val, i| {
                     if (needsPointerRebase(fields[i].ty, field_val)) return true;
src/codegen.zig
@@ -225,9 +225,9 @@ pub fn generateSymbol(
                     return Result{ .externally_managed = payload.data };
                 }
             },
-            .array => {
+            .aggregate => {
                 // TODO populate .debug_info for the array
-                const elem_vals = typed_value.val.castTag(.array).?.data;
+                const elem_vals = typed_value.val.castTag(.aggregate).?.data;
                 const elem_ty = typed_value.ty.elemType();
                 for (elem_vals) |elem_val| {
                     switch (try generateSymbol(bin_file, src_loc, .{
@@ -554,7 +554,7 @@ pub fn generateSymbol(
             }
 
             const struct_begin = code.items.len;
-            const field_vals = typed_value.val.castTag(.@"struct").?.data;
+            const field_vals = typed_value.val.castTag(.aggregate).?.data;
             for (field_vals) |field_val, index| {
                 const field_ty = typed_value.ty.structFieldType(index);
                 if (!field_ty.hasRuntimeBits()) continue;
src/Module.zig
@@ -5385,7 +5385,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
                 .len = test_fn_vals.len,
                 .elem_type = try tmp_test_fn_ty.copy(arena),
             }),
-            .val = try Value.Tag.array.create(arena, test_fn_vals),
+            .val = try Value.Tag.aggregate.create(arena, test_fn_vals),
         });
 
         // Add a dependency on each test name and function pointer.
@@ -5417,7 +5417,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
                 try Value.Tag.decl_ref.create(arena, test_decl), // func
                 Value.initTag(.null_value), // async_frame_size
             };
-            test_fn_vals[i] = try Value.Tag.@"struct".create(arena, field_vals);
+            test_fn_vals[i] = try Value.Tag.aggregate.create(arena, field_vals);
         }
 
         try array_decl.finalizeNewArena(&new_decl_arena);
src/Sema.zig
@@ -3119,7 +3119,7 @@ fn validateStructInit(
             field_values[i] = fields[i].default_val;
         }
 
-        const struct_val = try Value.Tag.@"struct".create(sema.arena, field_values);
+        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);
         return;
@@ -3246,7 +3246,7 @@ fn zirValidateArrayInit(
 
         block.instructions.shrinkRetainingCapacity(first_block_index);
 
-        const array_val = try Value.Tag.array.create(sema.arena, element_vals);
+        const array_val = try Value.Tag.aggregate.create(sema.arena, element_vals);
         const array_init = try sema.addConstant(array_ty, array_val);
         try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
     }
@@ -8175,7 +8175,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
             }
             return sema.addConstant(
                 operand_type,
-                try Value.Tag.array.create(sema.arena, elems),
+                try Value.Tag.aggregate.create(sema.arena, elems),
             );
         } else {
             const result_val = try val.bitwiseNot(scalar_type, sema.arena, target);
@@ -8239,7 +8239,7 @@ fn analyzeTupleCat(
     });
 
     const runtime_src = opt_runtime_src orelse {
-        const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
+        const tuple_val = try Value.Tag.aggregate.create(sema.arena, values);
         return sema.addConstant(tuple_ty, tuple_val);
     };
 
@@ -8334,7 +8334,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 .len = final_len,
                 .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()),
             });
-            const val = try Value.Tag.array.create(anon_decl.arena(), buf);
+            const val = try Value.Tag.aggregate.create(anon_decl.arena(), buf);
             const decl = try anon_decl.finish(ty, val);
             if (lhs_single_ptr or rhs_single_ptr) {
                 return sema.analyzeDeclRef(decl);
@@ -8419,7 +8419,7 @@ fn analyzeTupleMul(
     });
 
     const runtime_src = opt_runtime_src orelse {
-        const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
+        const tuple_val = try Value.Tag.aggregate.create(sema.arena, values);
         return sema.addConstant(tuple_ty, tuple_val);
     };
 
@@ -8506,7 +8506,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             if (mulinfo.sentinel) |sent| {
                 buf[final_len] = try sent.copy(anon_decl.arena());
             }
-            break :blk try Value.Tag.array.create(anon_decl.arena(), buf);
+            break :blk try Value.Tag.aggregate.create(anon_decl.arena(), buf);
         };
         const decl = try anon_decl.finish(final_ty, val);
         if (is_single_ptr) {
@@ -10186,7 +10186,7 @@ fn zirBuiltinSrc(
 
     return sema.addConstant(
         try sema.getBuiltinType(block, src, "SourceLocation"),
-        try Value.Tag.@"struct".create(sema.arena, field_values),
+        try Value.Tag.aggregate.create(sema.arena, field_values),
     );
 }
 
@@ -10289,7 +10289,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     // arg_type: ?type,
                     param_ty_val,
                 };
-                param_val.* = try Value.Tag.@"struct".create(params_anon_decl.arena(), param_fields);
+                param_val.* = try Value.Tag.aggregate.create(params_anon_decl.arena(), param_fields);
             }
 
             const args_val = v: {
@@ -10314,7 +10314,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         .len = param_vals.len,
                         .elem_type = param_info_decl.ty,
                     }),
-                    try Value.Tag.array.create(
+                    try Value.Tag.aggregate.create(
                         params_anon_decl.arena(),
                         param_vals,
                     ),
@@ -10345,7 +10345,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Fn)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10364,7 +10364,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Int)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10377,7 +10377,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Float)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10412,7 +10412,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Pointer)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10430,7 +10430,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Array)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10446,7 +10446,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Vector)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10459,7 +10459,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Optional)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10509,7 +10509,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         name_val,
                     };
 
-                    field_val.* = try Value.Tag.@"struct".create(
+                    field_val.* = try Value.Tag.aggregate.create(
                         fields_anon_decl.arena(),
                         error_field_fields,
                     );
@@ -10525,7 +10525,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         .len = vals.len,
                         .elem_type = error_field_ty,
                     }),
-                    try Value.Tag.array.create(
+                    try Value.Tag.aggregate.create(
                         fields_anon_decl.arena(),
                         vals,
                     ),
@@ -10559,7 +10559,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ErrorUnion)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10618,7 +10618,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     // value: comptime_int,
                     int_val,
                 };
-                field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), enum_field_fields);
+                field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), enum_field_fields);
             }
 
             const fields_val = v: {
@@ -10627,7 +10627,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         .len = enum_field_vals.len,
                         .elem_type = enum_field_ty,
                     }),
-                    try Value.Tag.array.create(
+                    try Value.Tag.aggregate.create(
                         fields_anon_decl.arena(),
                         enum_field_vals,
                     ),
@@ -10659,7 +10659,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Enum)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10717,7 +10717,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     // alignment: comptime_int,
                     try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
                 };
-                field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), union_field_fields);
+                field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), union_field_fields);
             }
 
             const fields_val = v: {
@@ -10726,7 +10726,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         .len = union_field_vals.len,
                         .elem_type = union_field_ty,
                     }),
-                    try Value.Tag.array.create(
+                    try Value.Tag.aggregate.create(
                         fields_anon_decl.arena(),
                         try fields_anon_decl.arena().dupe(Value, union_field_vals),
                     ),
@@ -10761,7 +10761,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Union)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10827,7 +10827,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                             // alignment: comptime_int,
                             try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
                         };
-                        struct_field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), struct_field_fields);
+                        struct_field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields);
                     }
                     break :fv struct_field_vals;
                 }
@@ -10871,7 +10871,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         // alignment: comptime_int,
                         try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
                     };
-                    field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), struct_field_fields);
+                    field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields);
                 }
                 break :fv struct_field_vals;
             };
@@ -10882,7 +10882,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         .len = struct_field_vals.len,
                         .elem_type = struct_field_ty,
                     }),
-                    try Value.Tag.array.create(
+                    try Value.Tag.aggregate.create(
                         fields_anon_decl.arena(),
                         try fields_anon_decl.arena().dupe(Value, struct_field_vals),
                     ),
@@ -10911,7 +10911,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Struct)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10931,7 +10931,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 type_info_ty,
                 try Value.Tag.@"union".create(sema.arena, .{
                     .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Opaque)),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                    .val = try Value.Tag.aggregate.create(sema.arena, field_values),
                 }),
             );
         },
@@ -10987,7 +10987,7 @@ fn typeInfoDecls(
             //is_pub: bool,
             Value.makeBool(decl.is_pub),
         };
-        decls_val.* = try Value.Tag.@"struct".create(decls_anon_decl.arena(), fields);
+        decls_val.* = try Value.Tag.aggregate.create(decls_anon_decl.arena(), fields);
     }
 
     const new_decl = try decls_anon_decl.finish(
@@ -10995,7 +10995,7 @@ fn typeInfoDecls(
             .len = decls_vals.len,
             .elem_type = declaration_ty,
         }),
-        try Value.Tag.array.create(
+        try Value.Tag.aggregate.create(
             decls_anon_decl.arena(),
             try decls_anon_decl.arena().dupe(Value, decls_vals),
         ),
@@ -11816,7 +11816,7 @@ fn finishStructInit(
         for (field_inits) |field_init, i| {
             values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
         }
-        const struct_val = try Value.Tag.@"struct".create(sema.arena, values);
+        const struct_val = try Value.Tag.aggregate.create(sema.arena, values);
         return sema.addConstantMaybeRef(block, src, struct_ty, struct_val, is_ref);
     }
 
@@ -11877,7 +11877,7 @@ fn zirStructInitAnon(
     });
 
     const runtime_src = opt_runtime_src orelse {
-        const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
+        const tuple_val = try Value.Tag.aggregate.create(sema.arena, values);
         return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref);
     };
 
@@ -11974,7 +11974,7 @@ fn zirArrayInit(
             elem_vals[i] = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?;
         }
 
-        const array_val = try Value.Tag.array.create(sema.arena, elem_vals);
+        const array_val = try Value.Tag.aggregate.create(sema.arena, elem_vals);
         return sema.addConstantMaybeRef(block, src, array_ty, array_val, is_ref);
     };
 
@@ -12043,7 +12043,7 @@ fn zirArrayInitAnon(
     });
 
     const runtime_src = opt_runtime_src orelse {
-        const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
+        const tuple_val = try Value.Tag.aggregate.create(sema.arena, values);
         return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref);
     };
 
@@ -12250,7 +12250,7 @@ fn zirUnaryMath(
                 }
                 return sema.addConstant(
                     result_ty,
-                    try Value.Tag.array.create(sema.arena, elems),
+                    try Value.Tag.aggregate.create(sema.arena, elems),
                 );
             }
 
@@ -12350,7 +12350,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
         .AnyFrame => return Air.Inst.Ref.anyframe_type,
         .EnumLiteral => return Air.Inst.Ref.enum_literal_type,
         .Int => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // TODO use reflection instead of magic numbers here
             const signedness_val = struct_val[0];
             const bits_val = struct_val[1];
@@ -12364,7 +12364,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             return sema.addType(ty);
         },
         .Vector => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // TODO use reflection instead of magic numbers here
             const len_val = struct_val[0];
             const child_val = struct_val[1];
@@ -12377,7 +12377,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             return sema.addType(ty);
         },
         .Float => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // TODO use reflection instead of magic numbers here
             // bits: comptime_int,
             const bits_val = struct_val[0];
@@ -12394,7 +12394,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             return sema.addType(ty);
         },
         .Pointer => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // TODO use reflection instead of magic numbers here
             const size_val = struct_val[0];
             const is_const_val = struct_val[1];
@@ -12436,7 +12436,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             return sema.addType(ty);
         },
         .Array => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // TODO use reflection instead of magic numbers here
             // len: comptime_int,
             const len_val = struct_val[0];
@@ -12460,7 +12460,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             return sema.addType(ty);
         },
         .Optional => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // TODO use reflection instead of magic numbers here
             // child: type,
             const child_val = struct_val[0];
@@ -12472,7 +12472,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             return sema.addType(ty);
         },
         .ErrorUnion => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // TODO use reflection instead of magic numbers here
             // error_set: type,
             const error_set_val = struct_val[0];
@@ -12495,12 +12495,12 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const slice_val = payload_val.castTag(.slice).?.data;
             const decl = slice_val.ptr.pointerDecl().?;
             try sema.ensureDeclAnalyzed(decl);
-            const array_val = decl.val.castTag(.array).?.data;
+            const array_val = decl.val.castTag(.aggregate).?.data;
 
             var names: Module.ErrorSet.NameMap = .{};
             try names.ensureUnusedCapacity(sema.arena, array_val.len);
             for (array_val) |elem_val| {
-                const struct_val = elem_val.castTag(.@"struct").?.data;
+                const struct_val = elem_val.castTag(.aggregate).?.data;
                 // TODO use reflection instead of magic numbers here
                 // error_set: type,
                 const name_val = struct_val[0];
@@ -12516,7 +12516,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
         },
         .Struct => {
             // TODO use reflection instead of magic numbers here
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // layout: containerlayout,
             const layout_val = struct_val[0];
             // fields: []const enumfield,
@@ -12537,7 +12537,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                 try sema.reifyStruct(block, inst, src, layout_val, fields_val);
         },
         .Enum => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // TODO use reflection instead of magic numbers here
             // layout: ContainerLayout,
             const layout_val = struct_val[0];
@@ -12617,9 +12617,9 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                     .ty = enum_obj.tag_ty,
                 });
 
-                const array_vals = decl.val.castTag(.array).?.data;
+                const array_vals = decl.val.castTag(.aggregate).?.data;
                 for (array_vals) |elem_val| {
-                    const field_struct_val = elem_val.castTag(.@"struct").?.data;
+                    const field_struct_val = elem_val.castTag(.aggregate).?.data;
                     // TODO use reflection instead of magic numbers here
                     // name: []const u8
                     const name_val = field_struct_val[0];
@@ -12648,7 +12648,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             return sema.analyzeDeclVal(block, src, new_decl);
         },
         .Opaque => {
-            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
             // decls: []const Declaration,
             const decls_val = struct_val[0];
 
@@ -12694,7 +12694,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
         .Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}),
         .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),
         .BoundFn => @panic("TODO delete BoundFn from the language"),
-        .Frame => return sema.fail(block, src, "TODO: Sema.zirReify for Frame", .{}),
+        .Frame => @panic("TODO implement https://github.com/ziglang/zig/issues/10710"),
     }
 }
 
@@ -12717,7 +12717,7 @@ fn reifyTuple(
     var i: usize = 0;
     while (i < fields_len) : (i += 1) {
         const elem_val = try fields_val.elemValue(sema.arena, i);
-        const field_struct_val = elem_val.castTag(.@"struct").?.data;
+        const field_struct_val = elem_val.castTag(.aggregate).?.data;
         // TODO use reflection instead of magic numbers here
         // name: []const u8
         const name_val = field_struct_val[0];
@@ -12818,7 +12818,7 @@ fn reifyStruct(
     var i: usize = 0;
     while (i < fields_len) : (i += 1) {
         const elem_val = try fields_val.elemValue(sema.arena, i);
-        const field_struct_val = elem_val.castTag(.@"struct").?.data;
+        const field_struct_val = elem_val.castTag(.aggregate).?.data;
         // TODO use reflection instead of magic numbers here
         // name: []const u8
         const name_val = field_struct_val[0];
@@ -13219,7 +13219,7 @@ fn zirBitCount(
                 }
                 return sema.addConstant(
                     result_ty,
-                    try Value.Tag.array.create(sema.arena, elems),
+                    try Value.Tag.aggregate.create(sema.arena, elems),
                 );
             } else {
                 try sema.requireRuntimeBlock(block, operand_src);
@@ -13985,7 +13985,7 @@ fn analyzeShuffle(
                     values[i] = try b_val.elemValue(sema.arena, unsigned);
                 }
             }
-            const res_val = try Value.Tag.array.create(sema.arena, values);
+            const res_val = try Value.Tag.aggregate.create(sema.arena, values);
             return sema.addConstant(res_ty, res_val);
         }
     }
@@ -14008,7 +14008,7 @@ fn analyzeShuffle(
         while (i < max_len) : (i += 1) {
             expand_mask_values[i] = Value.negative_one;
         }
-        const expand_mask = try Value.Tag.array.create(sema.arena, expand_mask_values);
+        const expand_mask = try Value.Tag.aggregate.create(sema.arena, expand_mask_values);
 
         if (a_len < b_len) {
             const undef = try sema.addConstUndef(a_ty);
@@ -14454,7 +14454,7 @@ fn zirMinMax(
         }
         return sema.addConstant(
             simd_op.result_ty,
-            try Value.Tag.array.create(sema.arena, elems),
+            try Value.Tag.aggregate.create(sema.arena, elems),
         );
     } else rs: {
         if (simd_op.rhs_val) |rhs_val| {
@@ -16016,7 +16016,7 @@ fn structFieldVal(
                     return sema.addConstant(field.ty, opv);
                 }
 
-                const field_values = struct_val.castTag(.@"struct").?.data;
+                const field_values = struct_val.castTag(.aggregate).?.data;
                 return sema.addConstant(field.ty, field_values[field_index]);
             }
 
@@ -16084,7 +16084,7 @@ fn tupleFieldValByIndex(
         if ((try sema.typeHasOnePossibleValue(block, src, field_ty))) |opv| {
             return sema.addConstant(field_ty, opv);
         }
-        const field_values = tuple_val.castTag(.@"struct").?.data;
+        const field_values = tuple_val.castTag(.aggregate).?.data;
         return sema.addConstant(field_ty, field_values[field_index]);
     }
 
@@ -16414,7 +16414,7 @@ fn tupleField(
 
     if (try sema.resolveMaybeUndefVal(block, tuple_src, tuple)) |tuple_val| {
         if (tuple_val.isUndef()) return sema.addConstUndef(field_ty);
-        const field_values = tuple_val.castTag(.@"struct").?.data;
+        const field_values = tuple_val.castTag(.aggregate).?.data;
         return sema.addConstant(field_ty, field_values[field_index]);
     }
 
@@ -17510,7 +17510,7 @@ fn beginComptimePtrMutation(
                             const elems = try arena.alloc(Value, array_len_including_sentinel);
                             mem.set(Value, elems, Value.undef);
 
-                            parent.val.* = try Value.Tag.array.create(arena, elems);
+                            parent.val.* = try Value.Tag.aggregate.create(arena, elems);
 
                             return ComptimePtrMutationKit{
                                 .decl_ref_mut = parent.decl_ref_mut,
@@ -17537,7 +17537,7 @@ fn beginComptimePtrMutation(
                                 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
                             }
 
-                            parent.val.* = try Value.Tag.array.create(arena, elems);
+                            parent.val.* = try Value.Tag.aggregate.create(arena, elems);
 
                             return ComptimePtrMutationKit{
                                 .decl_ref_mut = parent.decl_ref_mut,
@@ -17562,7 +17562,7 @@ fn beginComptimePtrMutation(
                             const elems = try arena.alloc(Value, array_len_including_sentinel);
                             mem.set(Value, elems, repeated_val);
 
-                            parent.val.* = try Value.Tag.array.create(arena, elems);
+                            parent.val.* = try Value.Tag.aggregate.create(arena, elems);
 
                             return ComptimePtrMutationKit{
                                 .decl_ref_mut = parent.decl_ref_mut,
@@ -17571,9 +17571,9 @@ fn beginComptimePtrMutation(
                             };
                         },
 
-                        .array => return ComptimePtrMutationKit{
+                        .aggregate => return ComptimePtrMutationKit{
                             .decl_ref_mut = parent.decl_ref_mut,
-                            .val = &parent.val.castTag(.array).?.data[elem_ptr.index],
+                            .val = &parent.val.castTag(.aggregate).?.data[elem_ptr.index],
                             .ty = elem_ty,
                         },
 
@@ -17613,7 +17613,7 @@ fn beginComptimePtrMutation(
                             const fields = try arena.alloc(Value, parent.ty.structFieldCount());
                             mem.set(Value, fields, Value.undef);
 
-                            parent.val.* = try Value.Tag.@"struct".create(arena, fields);
+                            parent.val.* = try Value.Tag.aggregate.create(arena, fields);
 
                             return ComptimePtrMutationKit{
                                 .decl_ref_mut = parent.decl_ref_mut,
@@ -17639,9 +17639,9 @@ fn beginComptimePtrMutation(
                         else => unreachable,
                     }
                 },
-                .@"struct" => return ComptimePtrMutationKit{
+                .aggregate => return ComptimePtrMutationKit{
                     .decl_ref_mut = parent.decl_ref_mut,
-                    .val = &parent.val.castTag(.@"struct").?.data[field_index],
+                    .val = &parent.val.castTag(.aggregate).?.data[field_index],
                     .ty = field_ty,
                 },
                 .@"union" => {
@@ -18209,7 +18209,7 @@ fn coerceArrayLike(
 
     return sema.addConstant(
         dest_ty,
-        try Value.Tag.array.create(sema.arena, element_vals),
+        try Value.Tag.aggregate.create(sema.arena, element_vals),
     );
 }
 
@@ -18266,7 +18266,7 @@ fn coerceTupleToArray(
 
     return sema.addConstant(
         dest_ty,
-        try Value.Tag.array.create(sema.arena, element_vals),
+        try Value.Tag.aggregate.create(sema.arena, element_vals),
     );
 }
 
@@ -18397,7 +18397,7 @@ fn coerceTupleToStruct(
 
     return sema.addConstant(
         struct_ty,
-        try Value.Tag.@"struct".create(sema.arena, field_vals),
+        try Value.Tag.aggregate.create(sema.arena, field_vals),
     );
 }
 
src/type.zig
@@ -3568,6 +3568,8 @@ pub const Type = extern union {
             .const_slice_u8,
             .const_slice,
             .mut_slice,
+            .tuple,
+            .empty_struct_literal,
             => return null,
 
             .pointer => return self.castTag(.pointer).?.data.sentinel,
src/value.zig
@@ -127,10 +127,6 @@ pub const Value = extern union {
         /// This value is repeated some number of times. The amount of times to repeat
         /// is stored externally.
         repeated,
-        /// Each element stored as a `Value`.
-        /// In the case of sentinel-terminated arrays, the sentinel value *is* stored,
-        /// so the slice length will be one more than the type's array length.
-        array,
         /// An array with length 0 but it has a sentinel.
         empty_array_sentinel,
         /// Pointer and length as sub `Value` objects.
@@ -162,8 +158,11 @@ pub const Value = extern union {
         opt_payload,
         /// A pointer to the payload of an optional, based on a pointer to an optional.
         opt_payload_ptr,
-        /// An instance of a struct.
-        @"struct",
+        /// An instance of a struct, array, or vector.
+        /// Each element/field stored as a `Value`.
+        /// In the case of sentinel-terminated arrays, the sentinel value *is* stored,
+        /// so the slice length will be one more than the type's array length.
+        aggregate,
         /// An instance of a union.
         @"union",
         /// This is a special value that tracks a set of types that have been stored
@@ -279,7 +278,6 @@ pub const Value = extern union {
                 .enum_literal,
                 => Payload.Bytes,
 
-                .array => Payload.Array,
                 .slice => Payload.Slice,
 
                 .enum_field_index => Payload.U32,
@@ -301,7 +299,7 @@ pub const Value = extern union {
                 .@"error" => Payload.Error,
                 .inferred_alloc => Payload.InferredAlloc,
                 .inferred_alloc_comptime => Payload.InferredAllocComptime,
-                .@"struct" => Payload.Struct,
+                .aggregate => Payload.Aggregate,
                 .@"union" => Payload.Union,
                 .bound_fn => Payload.BoundFn,
             };
@@ -521,18 +519,6 @@ pub const Value = extern union {
                 };
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
-            .array => {
-                const payload = self.castTag(.array).?;
-                const new_payload = try arena.create(Payload.Array);
-                new_payload.* = .{
-                    .base = payload.base,
-                    .data = try arena.alloc(Value, payload.data.len),
-                };
-                for (new_payload.data) |*elem, i| {
-                    elem.* = try payload.data[i].copy(arena);
-                }
-                return Value{ .ptr_otherwise = &new_payload.base };
-            },
             .slice => {
                 const payload = self.castTag(.slice).?;
                 const new_payload = try arena.create(Payload.Slice);
@@ -562,15 +548,15 @@ pub const Value = extern union {
             .enum_field_index => return self.copyPayloadShallow(arena, Payload.U32),
             .@"error" => return self.copyPayloadShallow(arena, Payload.Error),
 
-            .@"struct" => {
-                const old_field_values = self.castTag(.@"struct").?.data;
-                const new_payload = try arena.create(Payload.Struct);
+            .aggregate => {
+                const payload = self.castTag(.aggregate).?;
+                const new_payload = try arena.create(Payload.Aggregate);
                 new_payload.* = .{
-                    .base = .{ .tag = .@"struct" },
-                    .data = try arena.alloc(Value, old_field_values.len),
+                    .base = payload.base,
+                    .data = try arena.alloc(Value, payload.data.len),
                 };
-                for (old_field_values) |old_field_val, i| {
-                    new_payload.data[i] = try old_field_val.copy(arena);
+                for (new_payload.data) |*elem, i| {
+                    elem.* = try payload.data[i].copy(arena);
                 }
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
@@ -677,8 +663,8 @@ pub const Value = extern union {
             .abi_align_default => return out_stream.writeAll("(default ABI alignment)"),
 
             .empty_struct_value => return out_stream.writeAll("struct {}{}"),
-            .@"struct" => {
-                return out_stream.writeAll("(struct value)");
+            .aggregate => {
+                return out_stream.writeAll("(aggregate)");
             },
             .@"union" => {
                 return out_stream.writeAll("(union value)");
@@ -733,7 +719,6 @@ pub const Value = extern union {
                 try out_stream.writeAll("(repeated) ");
                 val = val.castTag(.repeated).?.data;
             },
-            .array => return out_stream.writeAll("(array)"),
             .empty_array_sentinel => return out_stream.writeAll("(empty array with sentinel)"),
             .slice => return out_stream.writeAll("(slice)"),
             .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}),
@@ -1087,7 +1072,7 @@ pub const Value = extern union {
                 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
                 .Extern => {
                     const fields = ty.structFields().values();
-                    const field_vals = val.castTag(.@"struct").?.data;
+                    const field_vals = val.castTag(.aggregate).?.data;
                     for (fields) |field, i| {
                         const off = @intCast(usize, ty.structFieldOffset(i, target));
                         writeToMemory(field_vals[i], field.ty, target, buffer[off..]);
@@ -1110,7 +1095,7 @@ pub const Value = extern union {
     fn packedStructToInt(val: Value, ty: Type, target: Target, buf: []std.math.big.Limb) BigIntConst {
         var bigint = BigIntMutable.init(buf, 0);
         const fields = ty.structFields().values();
-        const field_vals = val.castTag(.@"struct").?.data;
+        const field_vals = val.castTag(.aggregate).?.data;
         var bits: u16 = 0;
         // TODO allocate enough heap space instead of using this buffer
         // on the stack.
@@ -1185,7 +1170,7 @@ pub const Value = extern union {
                     elem.* = try readFromMemory(elem_ty, target, buffer[offset..], arena);
                     offset += @intCast(usize, elem_size);
                 }
-                return Tag.array.create(arena, elems);
+                return Tag.aggregate.create(arena, elems);
             },
             .Struct => switch (ty.containerLayout()) {
                 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
@@ -1196,7 +1181,7 @@ pub const Value = extern union {
                         const off = @intCast(usize, ty.structFieldOffset(i, target));
                         field_vals[i] = try readFromMemory(field.ty, target, buffer[off..], arena);
                     }
-                    return Tag.@"struct".create(arena, field_vals);
+                    return Tag.aggregate.create(arena, field_vals);
                 },
                 .Packed => {
                     const endian = target.cpu.arch.endian();
@@ -1250,7 +1235,7 @@ pub const Value = extern union {
                 else => unreachable,
             };
         }
-        return Tag.@"struct".create(arena, field_vals);
+        return Tag.aggregate.create(arena, field_vals);
     }
 
     fn bitCastBigIntToFloat(
@@ -1827,9 +1812,9 @@ pub const Value = extern union {
                 assert(op == .eq);
                 return lhs.castTag(.repeated).?.data.compareWithZero(.eq);
             },
-            .array => {
+            .aggregate => {
                 assert(op == .eq);
-                for (lhs.cast(Payload.Array).?.data) |elem_val| {
+                for (lhs.castTag(.aggregate).?.data) |elem_val| {
                     if (!elem_val.compareWithZero(.eq)) return false;
                 }
                 return true;
@@ -1898,29 +1883,16 @@ pub const Value = extern union {
             },
             .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
             .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
-            .array => {
-                const a_array = a.castTag(.array).?.data;
-                const b_array = b.castTag(.array).?.data;
-
-                if (a_array.len != b_array.len) return false;
-
-                const elem_ty = ty.childType();
-                for (a_array) |a_elem, i| {
-                    const b_elem = b_array[i];
-
-                    if (!eql(a_elem, b_elem, elem_ty)) return false;
-                }
-                return true;
-            },
             .function => {
                 const a_payload = a.castTag(.function).?.data;
                 const b_payload = b.castTag(.function).?.data;
                 return a_payload == b_payload;
             },
-            .@"struct" => {
-                const a_field_vals = a.castTag(.@"struct").?.data;
-                const b_field_vals = b.castTag(.@"struct").?.data;
+            .aggregate => {
+                const a_field_vals = a.castTag(.aggregate).?.data;
+                const b_field_vals = b.castTag(.aggregate).?.data;
                 assert(a_field_vals.len == b_field_vals.len);
+
                 if (ty.isTupleOrAnonStruct()) {
                     const types = ty.tupleFields().types;
                     assert(types.len == a_field_vals.len);
@@ -1929,10 +1901,21 @@ pub const Value = extern union {
                     }
                     return true;
                 }
-                const fields = ty.structFields().values();
-                assert(fields.len == a_field_vals.len);
-                for (fields) |field, i| {
-                    if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false;
+
+                if (ty.zigTypeTag() == .Struct) {
+                    const fields = ty.structFields().values();
+                    assert(fields.len == a_field_vals.len);
+                    for (fields) |field, i| {
+                        if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false;
+                    }
+                    return true;
+                }
+
+                const elem_ty = ty.childType();
+                for (a_field_vals) |a_elem, i| {
+                    const b_elem = b_field_vals[i];
+
+                    if (!eql(a_elem, b_elem, elem_ty)) return false;
                 }
                 return true;
             },
@@ -2002,7 +1985,7 @@ pub const Value = extern union {
             },
             .Struct => {
                 // A tuple can be represented with .empty_struct_value,
-                // the_one_possible_value, .@"struct" in which case we could
+                // the_one_possible_value, .aggregate in which case we could
                 // end up here and the values are equal if the type has zero fields.
                 return ty.structFieldCount() != 0;
             },
@@ -2072,8 +2055,8 @@ pub const Value = extern union {
                             field.default_val.hash(field.ty, hasher);
                         }
                     },
-                    .@"struct" => {
-                        const field_values = val.castTag(.@"struct").?.data;
+                    .aggregate => {
+                        const field_values = val.castTag(.aggregate).?.data;
                         for (field_values) |field_val, i| {
                             field_val.hash(fields[i].ty, hasher);
                         }
@@ -2190,19 +2173,12 @@ pub const Value = extern union {
         if (val.isComptimeMutablePtr()) return true;
         switch (val.tag()) {
             .repeated => return val.castTag(.repeated).?.data.canMutateComptimeVarState(),
-            .array => {
-                const elems = val.cast(Payload.Array).?.data;
-                for (elems) |elem| {
-                    if (elem.canMutateComptimeVarState()) return true;
-                }
-                return false;
-            },
             .eu_payload => return val.castTag(.eu_payload).?.data.canMutateComptimeVarState(),
             .eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.canMutateComptimeVarState(),
             .opt_payload => return val.castTag(.opt_payload).?.data.canMutateComptimeVarState(),
             .opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.canMutateComptimeVarState(),
-            .@"struct" => {
-                const fields = val.cast(Payload.Struct).?.data;
+            .aggregate => {
+                const fields = val.castTag(.aggregate).?.data;
                 for (fields) |field| {
                     if (field.canMutateComptimeVarState()) return true;
                 }
@@ -2302,11 +2278,6 @@ pub const Value = extern union {
             .empty_array_sentinel,
             => return markReferencedDeclsAlive(val.cast(Payload.SubValue).?.data),
 
-            .array => {
-                for (val.cast(Payload.Array).?.data) |elem_val| {
-                    markReferencedDeclsAlive(elem_val);
-                }
-            },
             .slice => {
                 const slice = val.cast(Payload.Slice).?.data;
                 markReferencedDeclsAlive(slice.ptr);
@@ -2321,8 +2292,8 @@ pub const Value = extern union {
                 const field_ptr = val.cast(Payload.FieldPtr).?.data;
                 return markReferencedDeclsAlive(field_ptr.container_ptr);
             },
-            .@"struct" => {
-                for (val.cast(Payload.Struct).?.data) |field_val| {
+            .aggregate => {
+                for (val.castTag(.aggregate).?.data) |field_val| {
                     markReferencedDeclsAlive(field_val);
                 }
             },
@@ -2405,7 +2376,7 @@ pub const Value = extern union {
             // No matter the index; all the elements are the same!
             .repeated => return val.castTag(.repeated).?.data,
 
-            .array => return val.castTag(.array).?.data[index],
+            .aggregate => return val.castTag(.aggregate).?.data[index],
             .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(index, arena, buffer),
 
             .decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer),
@@ -2426,8 +2397,8 @@ pub const Value = extern union {
     pub fn fieldValue(val: Value, allocator: Allocator, index: usize) error{OutOfMemory}!Value {
         _ = allocator;
         switch (val.tag()) {
-            .@"struct" => {
-                const field_values = val.castTag(.@"struct").?.data;
+            .aggregate => {
+                const field_values = val.castTag(.aggregate).?.data;
                 return field_values[index];
             },
             .@"union" => {
@@ -4199,8 +4170,10 @@ pub const Value = extern union {
             data: []const u8,
         };
 
-        pub const Array = struct {
+        pub const Aggregate = struct {
             base: Payload,
+            /// Field values. The types are according to the struct or array type.
+            /// The length is provided here so that copying a Value does not depend on the Type.
             data: []Value,
         };
 
@@ -4298,15 +4271,6 @@ pub const Value = extern union {
             },
         };
 
-        pub const Struct = struct {
-            pub const base_tag = Tag.@"struct";
-
-            base: Payload = .{ .tag = base_tag },
-            /// Field values. The types are according to the struct type.
-            /// The length is provided here so that copying a Value does not depend on the Type.
-            data: []Value,
-        };
-
         pub const Union = struct {
             pub const base_tag = Tag.@"union";
 
test/behavior/tuple.zig
@@ -129,7 +129,11 @@ test "tuple initializer for var" {
 }
 
 test "array-like initializer for tuple types" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
 
     const T = @Type(.{
         .Struct = .{