diff --git a/src/Sema.zig b/src/Sema.zig index c0b49fa473..93dd3c4557 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7704,7 +7704,7 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs); // tuples are structs but they don't have a namespace - if (container_type.isTuple()) return Air.Inst.Ref.bool_false; + if (container_type.isTupleOrAnonStruct()) return Air.Inst.Ref.bool_false; const namespace = container_type.getNamespace() orelse return sema.fail( block, lhs_src, @@ -10615,15 +10615,19 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const layout = struct_ty.containerLayout(); const struct_field_vals = fv: { - if (struct_ty.castTag(.tuple)) |payload| { - const field_types = payload.data.types; + if (struct_ty.isTupleOrAnonStruct()) { + const tuple = struct_ty.tupleFields(); + const field_types = tuple.types; const struct_field_vals = try fields_anon_decl.arena().alloc(Value, field_types.len); for (struct_field_vals) |*struct_field_val, i| { const field_ty = field_types[i]; const name_val = v: { var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - const bytes = try std.fmt.allocPrintZ(anon_decl.arena(), "{d}", .{i}); + const bytes = if (struct_ty.castTag(.anon_struct)) |payload| + try anon_decl.arena().dupeZ(u8, payload.data.names[i]) + else + try std.fmt.allocPrintZ(anon_decl.arena(), "{d}", .{i}); const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), @@ -10632,7 +10636,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; const struct_field_fields = try fields_anon_decl.arena().create([5]Value); - const field_val = payload.data.values[i]; + const field_val = tuple.values[i]; const is_comptime = field_val.tag() != .unreachable_value; const opt_default_val = if (is_comptime) field_val else null; const default_val_ptr = try sema.optRefValue(block, src, field_ty, opt_default_val); @@ -11631,12 +11635,86 @@ fn finishStructInit( return block.addAggregateInit(struct_ty, field_inits); } -fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { +fn zirStructInitAnon( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + is_ref: bool, +) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); + const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); + const types = try sema.arena.alloc(Type, extra.data.fields_len); + const values = try sema.arena.alloc(Value, types.len); + const names = try sema.arena.alloc([]const u8, types.len); - _ = is_ref; - return sema.fail(block, src, "TODO: Sema.zirStructInitAnon", .{}); + const opt_runtime_src = rs: { + var runtime_src: ?LazySrcLoc = null; + var extra_index = extra.end; + for (types) |*field_ty, i| { + const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); + extra_index = item.end; + + names[i] = sema.code.nullTerminatedString(item.data.field_name); + const init = sema.resolveInst(item.data.init); + field_ty.* = sema.typeOf(init); + const init_src = src; // TODO better source location + if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| { + values[i] = init_val; + } else { + values[i] = Value.initTag(.unreachable_value); + runtime_src = init_src; + } + } + break :rs runtime_src; + }; + + const tuple_ty = try Type.Tag.anon_struct.create(sema.arena, .{ + .names = names, + .types = types, + .values = values, + }); + + const runtime_src = opt_runtime_src orelse { + const tuple_val = try Value.Tag.@"struct".create(sema.arena, values); + return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); + }; + + try sema.requireRuntimeBlock(block, runtime_src); + + if (is_ref) { + const target = sema.mod.getTarget(); + const alloc = try block.addTy(.alloc, tuple_ty); + var extra_index = extra.end; + for (types) |field_ty, i_usize| { + const i = @intCast(u32, i_usize); + const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); + extra_index = item.end; + + const field_ptr_ty = try Type.ptr(sema.arena, target, .{ + .mutable = true, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + .pointee_type = field_ty, + }); + if (values[i].tag() == .unreachable_value) { + const init = sema.resolveInst(item.data.init); + const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); + _ = try block.addBinOp(.store, field_ptr, init); + } + } + + return alloc; + } + + const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); + var extra_index = extra.end; + for (types) |_, i| { + const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); + extra_index = item.end; + element_refs[i] = sema.resolveInst(item.data.init); + } + + return block.addAggregateInit(tuple_ty, element_refs); } fn zirArrayInit( @@ -11764,8 +11842,10 @@ fn zirArrayInitAnon( .@"addrspace" = target_util.defaultAddressSpace(target, .local), .pointee_type = types[i], }); - const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); - _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand)); + if (values[i].tag() == .unreachable_value) { + const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); + _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand)); + } } return alloc; @@ -12126,7 +12206,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I .size = ptr_size, .mutable = !is_const_val.toBool(), .@"volatile" = is_volatile_val.toBool(), - .@"align" = @intCast(u8, alignment_val.toUnsignedInt()), // TODO: Validate this value. + .@"align" = @intCast(u16, alignment_val.toUnsignedInt()), // TODO: Validate this value. .@"addrspace" = address_space_val.toEnum(std.builtin.AddressSpace), .pointee_type = try child_ty.copy(sema.arena), .@"allowzero" = is_allowzero_val.toBool(), @@ -14842,29 +14922,43 @@ fn structFieldVal( assert(unresolved_struct_ty.zigTypeTag() == .Struct); const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty); - if (struct_ty.isTuple()) { - return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); + switch (struct_ty.tag()) { + .tuple, .empty_struct_literal => return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty), + .anon_struct => { + const anon_struct = struct_ty.castTag(.anon_struct).?.data; + + const field_index = for (anon_struct.names) |name, i| { + if (mem.eql(u8, name, field_name)) break @intCast(u32, i); + } else { + return sema.fail(block, field_name_src, "anonymous struct {} has no such field '{s}'", .{ + struct_ty, field_name, + }); + }; + return tupleFieldValByIndex(sema, block, src, struct_byval, field_index, struct_ty); + }, + .@"struct" => { + 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_name_src, field_name); + const field_index = @intCast(u32, field_index_usize); + const field = struct_obj.fields.values()[field_index]; + + if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| { + if (struct_val.isUndef()) return sema.addConstUndef(field.ty); + if ((try sema.typeHasOnePossibleValue(block, src, field.ty))) |opv| { + return sema.addConstant(field.ty, opv); + } + + const field_values = struct_val.castTag(.@"struct").?.data; + return sema.addConstant(field.ty, field_values[field_index]); + } + + try sema.requireRuntimeBlock(block, src); + return block.addStructFieldVal(struct_byval, field_index, field.ty); + }, + else => unreachable, } - - 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_name_src, field_name); - const field_index = @intCast(u32, field_index_usize); - const field = struct_obj.fields.values()[field_index]; - - if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| { - if (struct_val.isUndef()) return sema.addConstUndef(field.ty); - if ((try sema.typeHasOnePossibleValue(block, src, field.ty))) |opv| { - return sema.addConstant(field.ty, opv); - } - - const field_values = struct_val.castTag(.@"struct").?.data; - return sema.addConstant(field.ty, field_values[field_index]); - } - - try sema.requireRuntimeBlock(block, src); - return block.addStructFieldVal(struct_byval, field_index, field.ty); } fn tupleFieldVal( @@ -14901,7 +14995,7 @@ fn tupleFieldValByIndex( field_index: u32, tuple_ty: Type, ) CompileError!Air.Inst.Ref { - const tuple = tuple_ty.castTag(.tuple).?.data; + const tuple = tuple_ty.tupleFields(); const field_ty = tuple.types[field_index]; if (tuple.values[field_index].tag() != .unreachable_value) { @@ -18954,8 +19048,8 @@ pub fn typeHasOnePossibleValue( return Value.initTag(.empty_struct_value); }, - .tuple => { - const tuple = ty.castTag(.tuple).?.data; + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); for (tuple.values) |val| { if (val.tag() == .unreachable_value) { return null; // non-comptime field @@ -19583,8 +19677,8 @@ fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) C return sema.typeRequiresComptime(block, src, ty.optionalChild(&buf)); }, - .tuple => { - const tuple = ty.castTag(.tuple).?.data; + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); for (tuple.types) |field_ty, i| { const have_comptime_val = tuple.values[i].tag() != .unreachable_value; if (!have_comptime_val and try sema.typeRequiresComptime(block, src, field_ty)) { diff --git a/src/Zir.zig b/src/Zir.zig index b70b41e506..c528380b1d 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2850,7 +2850,9 @@ pub const Inst = struct { }; }; - /// Trailing is an item per field. + /// Trailing is an Item per field. + /// TODO make this instead array of inits followed by array of names because + /// it will be simpler Sema code and better for CPU cache. pub const StructInitAnon = struct { fields_len: u32, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 28654b905a..fa29a1aae3 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -958,7 +958,7 @@ pub const DeclGen = struct { // reference, we need to copy it here. gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); - if (t.isTuple()) { + if (t.isTupleOrAnonStruct()) { const tuple = t.tupleFields(); const llvm_struct_ty = dg.context.structCreateNamed(""); gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls @@ -2104,7 +2104,7 @@ pub const DeclGen = struct { var zig_big_align: u32 = 0; var llvm_big_align: u32 = 0; - if (ty.isTuple()) { + if (ty.isTupleOrAnonStruct()) { const tuple = ty.tupleFields(); var llvm_field_index: c_uint = 0; for (tuple.types) |field_ty, i| { @@ -5704,7 +5704,7 @@ fn isByRef(ty: Type) bool { .Struct => { // Packed structs are represented to LLVM as integers. if (ty.containerLayout() == .Packed) return false; - if (ty.isTuple()) { + if (ty.isTupleOrAnonStruct()) { const tuple = ty.tupleFields(); var count: usize = 0; for (tuple.values) |field_val, i| { diff --git a/src/type.zig b/src/type.zig index 8084da34de..6e58f7a42f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -131,6 +131,7 @@ pub const Type = extern union { .export_options, .extern_options, .tuple, + .anon_struct, => return .Struct, .enum_full, @@ -792,6 +793,42 @@ pub const Type = extern union { return true; }, + .anon_struct => { + const a_struct_obj = a.castTag(.anon_struct).?.data; + const b_struct_obj = (b.castTag(.anon_struct) orelse return false).data; + + if (a_struct_obj.types.len != b_struct_obj.types.len) return false; + + for (a_struct_obj.names) |a_name, i| { + const b_name = b_struct_obj.names[i]; + if (!std.mem.eql(u8, a_name, b_name)) return false; + } + + for (a_struct_obj.types) |a_ty, i| { + const b_ty = b_struct_obj.types[i]; + if (!eql(a_ty, b_ty)) return false; + } + + for (a_struct_obj.values) |a_val, i| { + const ty = a_struct_obj.types[i]; + const b_val = b_struct_obj.values[i]; + if (a_val.tag() == .unreachable_value) { + if (b_val.tag() == .unreachable_value) { + continue; + } else { + return false; + } + } else { + if (b_val.tag() == .unreachable_value) { + return false; + } else { + if (!Value.eql(a_val, b_val, ty)) return false; + } + } + } + + return true; + }, // we can't compare these based on tags because it wouldn't detect if, // for example, a was resolved into .@"struct" but b was one of these tags. @@ -1062,6 +1099,20 @@ pub const Type = extern union { field_val.hash(field_ty, hasher); } }, + .anon_struct => { + const struct_obj = ty.castTag(.anon_struct).?.data; + std.hash.autoHash(hasher, std.builtin.TypeId.Struct); + std.hash.autoHash(hasher, struct_obj.types.len); + + for (struct_obj.types) |field_ty, i| { + const field_name = struct_obj.names[i]; + const field_val = struct_obj.values[i]; + hasher.update(field_name); + hashWithHasher(field_ty, hasher); + if (field_val.tag() == .unreachable_value) continue; + field_val.hash(field_ty, hasher); + } + }, // we can't hash these based on tags because they wouldn't match the expanded version. .call_options, @@ -1279,6 +1330,26 @@ pub const Type = extern union { .values = values, }); }, + .anon_struct => { + const payload = self.castTag(.anon_struct).?.data; + const names = try allocator.alloc([]const u8, payload.names.len); + const types = try allocator.alloc(Type, payload.types.len); + const values = try allocator.alloc(Value, payload.values.len); + for (payload.names) |name, i| { + names[i] = try allocator.dupe(u8, name); + } + for (payload.types) |ty, i| { + types[i] = try ty.copy(allocator); + } + for (payload.values) |val, i| { + values[i] = try val.copy(allocator); + } + return Tag.anon_struct.create(allocator, .{ + .names = names, + .types = types, + .values = values, + }); + }, .function => { const payload = self.castTag(.function).?.data; const param_types = try allocator.alloc(Type, payload.param_types.len); @@ -1533,6 +1604,26 @@ pub const Type = extern union { try writer.writeAll("}"); return; }, + .anon_struct => { + const anon_struct = ty.castTag(.anon_struct).?.data; + try writer.writeAll("struct{"); + for (anon_struct.types) |field_ty, i| { + if (i != 0) try writer.writeAll(", "); + const val = anon_struct.values[i]; + if (val.tag() != .unreachable_value) { + try writer.writeAll("comptime "); + } + try writer.writeAll(anon_struct.names[i]); + try writer.writeAll(": "); + try field_ty.format("", .{}, writer); + if (val.tag() != .unreachable_value) { + try writer.writeAll(" = "); + try val.format("", .{}, writer); + } + } + try writer.writeAll("}"); + return; + }, .single_const_pointer => { const pointee_type = ty.castTag(.single_const_pointer).?.data; try writer.writeAll("*const "); @@ -2020,8 +2111,8 @@ pub const Type = extern union { return payload.error_set.hasRuntimeBits() or payload.payload.hasRuntimeBits(); }, - .tuple => { - const tuple = ty.castTag(.tuple).?.data; + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); for (tuple.types) |field_ty, i| { const val = tuple.values[i]; if (val.tag() != .unreachable_value) continue; // comptime field @@ -2292,8 +2383,8 @@ pub const Type = extern union { return big_align; }, - .tuple => { - const tuple = self.castTag(.tuple).?.data; + .tuple, .anon_struct => { + const tuple = self.tupleFields(); var big_align: u32 = 0; for (tuple.types) |field_ty, i| { const val = tuple.values[i]; @@ -2375,7 +2466,7 @@ pub const Type = extern union { .void, => 0, - .@"struct", .tuple => switch (self.containerLayout()) { + .@"struct", .tuple, .anon_struct => switch (self.containerLayout()) { .Packed => { const struct_obj = self.castTag(.@"struct").?.data; var buf: Type.Payload.Bits = undefined; @@ -2575,6 +2666,13 @@ pub const Type = extern union { .bound_fn => unreachable, .void => 0, + .bool, .u1 => 1, + .u8, .i8 => 8, + .i16, .u16, .f16 => 16, + .i32, .u32, .f32 => 32, + .i64, .u64, .f64 => 64, + .f80 => 80, + .u128, .i128, .f128 => 128, .@"struct" => { const field_count = ty.structFieldCount(); @@ -2595,8 +2693,13 @@ pub const Type = extern union { } }, - .tuple => { - @panic("TODO bitSize tuples"); + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); + var total: u64 = 0; + for (tuple.types) |field_ty| { + total += field_ty.bitSize(target); + } + return total; }, .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { @@ -2608,10 +2711,6 @@ pub const Type = extern union { @panic("TODO bitSize unions"); }, - .u8, .i8 => 8, - - .bool, .u1 => 1, - .vector => { const payload = ty.castTag(.vector).?.data; const elem_bit_size = payload.elem_type.bitSize(target); @@ -2634,11 +2733,6 @@ pub const Type = extern union { ); return payload.len * 8 * elem_size + payload.elem_type.bitSize(target); }, - .i16, .u16, .f16 => 16, - .i32, .u32, .f32 => 32, - .i64, .u64, .f64 => 64, - .f80 => 80, - .u128, .i128, .f128 => 128, .isize, .usize, @@ -3295,7 +3389,7 @@ pub const Type = extern union { pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout { return switch (ty.tag()) { - .tuple, .empty_struct_literal => .Auto, + .tuple, .empty_struct_literal, .anon_struct => .Auto, .@"struct" => ty.castTag(.@"struct").?.data.layout, .@"union" => ty.castTag(.@"union").?.data.layout, .union_tagged => ty.castTag(.union_tagged).?.data.layout, @@ -3369,6 +3463,7 @@ pub const Type = extern union { .array_u8 => ty.castTag(.array_u8).?.data, .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data, .tuple => ty.castTag(.tuple).?.data.types.len, + .anon_struct => ty.castTag(.anon_struct).?.data.types.len, .@"struct" => ty.castTag(.@"struct").?.data.fields.count(), else => unreachable, @@ -3383,6 +3478,7 @@ pub const Type = extern union { return switch (ty.tag()) { .vector => @intCast(u32, ty.castTag(.vector).?.data.len), .tuple => @intCast(u32, ty.castTag(.tuple).?.data.types.len), + .anon_struct => @intCast(u32, ty.castTag(.anon_struct).?.data.types.len), else => unreachable, }; } @@ -3849,8 +3945,8 @@ pub const Type = extern union { return Value.initTag(.empty_struct_value); }, - .tuple => { - const tuple = ty.castTag(.tuple).?.data; + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); for (tuple.values) |val| { if (val.tag() == .unreachable_value) { return null; // non-comptime field @@ -4048,8 +4144,8 @@ pub const Type = extern union { return ty.optionalChild(&buf).comptimeOnly(); }, - .tuple => { - const tuple = ty.castTag(.tuple).?.data; + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); for (tuple.types) |field_ty, i| { const have_comptime_val = tuple.values[i].tag() != .unreachable_value; if (!have_comptime_val and field_ty.comptimeOnly()) return true; @@ -4350,6 +4446,7 @@ pub const Type = extern union { }, .empty_struct, .empty_struct_literal => return 0, .tuple => return ty.castTag(.tuple).?.data.types.len, + .anon_struct => return ty.castTag(.anon_struct).?.data.types.len, else => unreachable, } } @@ -4366,6 +4463,7 @@ pub const Type = extern union { return union_obj.fields.values()[index].ty; }, .tuple => return ty.castTag(.tuple).?.data.types[index], + .anon_struct => return ty.castTag(.anon_struct).?.data.types[index], else => unreachable, } } @@ -4424,8 +4522,8 @@ pub const Type = extern union { return std.mem.alignForwardGeneric(u64, it.offset, it.big_align); }, - .tuple => { - const tuple = ty.castTag(.tuple).?.data; + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); var offset: u64 = 0; var big_align: u32 = 0; @@ -4700,6 +4798,8 @@ pub const Type = extern union { vector, /// Possible Value tags for this: @"struct" tuple, + /// Possible Value tags for this: @"struct" + anon_struct, pointer, single_const_pointer, single_mut_pointer, @@ -4846,6 +4946,7 @@ pub const Type = extern union { .enum_numbered => Payload.EnumNumbered, .empty_struct => Payload.ContainerScope, .tuple => Payload.Tuple, + .anon_struct => Payload.AnonStruct, }; } @@ -4869,12 +4970,26 @@ pub const Type = extern union { }; pub fn isTuple(ty: Type) bool { - return ty.tag() == .tuple or ty.tag() == .empty_struct_literal; + return switch (ty.tag()) { + .tuple, .empty_struct_literal => true, + else => false, + }; + } + + pub fn isTupleOrAnonStruct(ty: Type) bool { + return switch (ty.tag()) { + .tuple, .empty_struct_literal, .anon_struct => true, + else => false, + }; } pub fn tupleFields(ty: Type) Payload.Tuple.Data { return switch (ty.tag()) { .tuple => ty.castTag(.tuple).?.data, + .anon_struct => .{ + .types = ty.castTag(.anon_struct).?.data.types, + .values = ty.castTag(.anon_struct).?.data.values, + }, .empty_struct_literal => .{ .types = &.{}, .values = &.{} }, else => unreachable, }; @@ -5042,6 +5157,18 @@ pub const Type = extern union { }; }; + pub const AnonStruct = struct { + base: Payload = .{ .tag = .anon_struct }, + data: Data, + + pub const Data = struct { + names: []const []const u8, + types: []Type, + /// unreachable_value elements are used to indicate runtime-known. + values: []Value, + }; + }; + pub const Union = struct { base: Payload, data: *Module.Union, diff --git a/src/value.zig b/src/value.zig index a740a35b79..5db780c99b 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1895,7 +1895,7 @@ pub const Value = extern union { const a_field_vals = a.castTag(.@"struct").?.data; const b_field_vals = b.castTag(.@"struct").?.data; assert(a_field_vals.len == b_field_vals.len); - if (ty.isTuple()) { + if (ty.isTupleOrAnonStruct()) { const types = ty.tupleFields().types; assert(types.len == a_field_vals.len); for (types) |field_ty, i| { @@ -2031,7 +2031,7 @@ pub const Value = extern union { } }, .Struct => { - if (ty.isTuple()) { + if (ty.isTupleOrAnonStruct()) { const fields = ty.tupleFields(); for (fields.values) |field_val, i| { field_val.hash(fields.types[i], hasher); diff --git a/test/behavior.zig b/test/behavior.zig index ea861d75cd..d620c24711 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -65,19 +65,20 @@ test { _ = @import("behavior/inttoptr.zig"); _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/member_func.zig"); + _ = @import("behavior/muladd.zig"); _ = @import("behavior/namespace_depends_on_compile_var.zig"); _ = @import("behavior/null.zig"); _ = @import("behavior/optional.zig"); - _ = @import("behavior/prefetch.zig"); _ = @import("behavior/pointers.zig"); - _ = @import("behavior/pub_enum.zig"); + _ = @import("behavior/prefetch.zig"); _ = @import("behavior/ptrcast.zig"); - _ = @import("behavior/reflection.zig"); + _ = @import("behavior/pub_enum.zig"); _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); + _ = @import("behavior/reflection.zig"); _ = @import("behavior/slice.zig"); _ = @import("behavior/slice_sentinel_comptime.zig"); - _ = @import("behavior/struct.zig"); _ = @import("behavior/src.zig"); + _ = @import("behavior/struct.zig"); _ = @import("behavior/this.zig"); _ = @import("behavior/truncate.zig"); _ = @import("behavior/try.zig"); @@ -135,6 +136,7 @@ test { _ = @import("behavior/bugs/5398.zig"); _ = @import("behavior/bugs/5413.zig"); _ = @import("behavior/bugs/5487.zig"); + _ = @import("behavior/bugs/7003.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); _ = @import("behavior/switch_prong_err_enum.zig"); _ = @import("behavior/switch_prong_implicit_cast.zig"); @@ -156,14 +158,12 @@ test { _ = @import("behavior/bugs/3779.zig"); _ = @import("behavior/bugs/6456.zig"); _ = @import("behavior/bugs/6781.zig"); - _ = @import("behavior/bugs/7003.zig"); _ = @import("behavior/bugs/7027.zig"); _ = @import("behavior/bugs/7047.zig"); _ = @import("behavior/bugs/10147.zig"); _ = @import("behavior/const_slice_child.zig"); + _ = @import("behavior/export.zig"); _ = @import("behavior/export_self_referential_type_info.zig"); - _ = @import("behavior/misc.zig"); - _ = @import("behavior/muladd.zig"); _ = @import("behavior/select.zig"); _ = @import("behavior/shuffle.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index d47ec50f5d..fda8cfe745 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -972,22 +972,6 @@ test "enum literal casting to error union with payload enum" { try expect((try bar) == Bar.B); } -test "exporting enum type and value" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - const E = enum(c_int) { one, two }; - comptime { - @export(E, .{ .name = "E" }); - } - const e: E = .two; - comptime { - @export(e, .{ .name = "e" }); - } - }; - try expect(S.e == .two); -} - test "constant enum initialization with differing sizes" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/misc.zig b/test/behavior/export.zig similarity index 73% rename from test/behavior/misc.zig rename to test/behavior/export.zig index e6138d1298..9521c7cadb 100644 --- a/test/behavior/misc.zig +++ b/test/behavior/export.zig @@ -34,3 +34,17 @@ export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion) void { b; } } + +test "exporting enum type and value" { + const S = struct { + const E = enum(c_int) { one, two }; + comptime { + @export(E, .{ .name = "E" }); + } + const e: E = .two; + comptime { + @export(e, .{ .name = "e" }); + } + }; + try expect(S.e == .two); +} diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index 46e938e1a9..bf50541b56 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -2,6 +2,8 @@ const builtin = @import("builtin"); const expect = @import("std").testing.expect; test "@mulAdd" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + comptime try testMulAdd(); try testMulAdd(); } @@ -25,20 +27,40 @@ fn testMulAdd() !void { var c: f64 = 6.25; try expect(@mulAdd(f64, a, b, c) == 20); } - // { - // var a: f16 = 5.5; - // var b: f80 = 2.5; - // var c: f80 = 6.25; - // try expect(@mulAdd(f80, a, b, c) == 20); - // } +} + +test "@mulAdd f80" { + if (true) { + // https://github.com/ziglang/zig/issues/11030 + return error.SkipZigTest; + } + + comptime try testMulAdd80(); + try testMulAdd80(); +} + +fn testMulAdd80() !void { + var a: f16 = 5.5; + var b: f80 = 2.5; + var c: f80 = 6.25; + try expect(@mulAdd(f80, a, b, c) == 20); +} + +test "@mulAdd f128" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .macos and builtin.cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/9900 return error.SkipZigTest; } - { - var a: f16 = 5.5; - var b: f128 = 2.5; - var c: f128 = 6.25; - try expect(@mulAdd(f128, a, b, c) == 20); - } + + comptime try testMullAdd128(); + try testMullAdd128(); +} + +fn testMullAdd128() !void { + var a: f16 = 5.5; + var b: f128 = 2.5; + var c: f128 = 6.25; + try expect(@mulAdd(f128, a, b, c) == 20); } diff --git a/test/behavior/src.zig b/test/behavior/src.zig index b0e4f2ba70..017395a7c6 100644 --- a/test/behavior/src.zig +++ b/test/behavior/src.zig @@ -14,7 +14,9 @@ const builtin = @import("builtin"); const expect = std.testing.expect; test "@src" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + 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 try doTheTest(); } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 20123e8014..c2656a7393 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -930,7 +930,9 @@ test "anonymous struct literal syntax" { } test "fully anonymous struct" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -974,7 +976,7 @@ test "fully anonymous list literal" { comptime try S.doTheTest(); } -test "anonymous struct literal assigned to variable" { +test "tuple assigned to variable" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; @@ -995,7 +997,7 @@ test "comptime struct field" { comptime try expect(foo.b == 1234); } -test "anon struct literal field value initialized with fn call" { +test "tuple element initialized with fn call" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 3d4e80ce3e..daaacae31b 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -503,9 +503,13 @@ test "Struct.is_tuple for anon list literal" { } test "Struct.is_tuple for anon struct literal" { - if (builtin.zig_backend != .stage1) 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 - try expect(!@typeInfo(@TypeOf(.{ .a = 0 })).Struct.is_tuple); + const info = @typeInfo(@TypeOf(.{ .a = 0 })); + try expect(!info.Struct.is_tuple); + try expect(std.mem.eql(u8, info.Struct.fields[0].name, "a")); } test "StructField.is_comptime" {