Merge pull request #12050 from topolarity/anon-struct-init

stage2: Support initializing anonymous struct type
This commit is contained in:
Veikka Tuominen 2022-07-09 18:30:07 +03:00 committed by GitHub
commit 3f11d1d56d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 247 additions and 183 deletions

View File

@ -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);
}
@ -15872,7 +15862,8 @@ fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
sema.src = .{ .node_offset_bin_op = inst_data.src_node };
const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
sema.src = src;
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@ -15882,50 +15873,38 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
const target = sema.mod.getTarget();
try sema.resolveTypeLayout(block, lhs_src, ty);
if (ty.tag() != .@"struct") {
const msg = msg: {
const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)});
errdefer msg.destroy(sema.gpa);
try sema.addDeclaredHereNote(msg, ty);
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
switch (ty.tag()) {
.@"struct", .tuple, .anon_struct => {},
else => {
const msg = msg: {
const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)});
errdefer msg.destroy(sema.gpa);
try sema.addDeclaredHereNote(msg, ty);
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
},
}
const fields = ty.structFields();
const index = fields.getIndex(field_name) orelse {
const msg = msg: {
const msg = try sema.errMsg(
block,
rhs_src,
"struct '{}' has no field '{s}'",
.{ ty.fmt(sema.mod), field_name },
);
errdefer msg.destroy(sema.gpa);
try sema.addDeclaredHereNote(msg, ty);
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
};
const field_index = if (ty.isTuple()) blk: {
if (mem.eql(u8, field_name, "len")) {
return sema.fail(block, src, "no offset available for 'len' field of tuple", .{});
}
break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src);
} else try sema.structFieldIndex(block, ty, field_name, rhs_src);
switch (ty.containerLayout()) {
.Packed => {
var bit_sum: u64 = 0;
const fields = ty.structFields();
for (fields.values()) |field, i| {
if (i == index) {
if (i == field_index) {
return bit_sum;
}
bit_sum += field.ty.bitSize(target);
} else unreachable;
},
else => {
var it = ty.iterateStructOffsets(target);
while (it.next()) |field_offset| {
if (field_offset.field == index) {
return field_offset.offset * 8;
}
} else unreachable;
},
else => return ty.structFieldOffset(field_index, target) * 8,
}
}
@ -17193,24 +17172,28 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
}
try sema.resolveTypeLayout(block, ty_src, struct_ty);
const struct_obj = struct_ty.castTag(.@"struct").?.data;
const field_index = struct_obj.fields.getIndex(field_name) orelse
return sema.failWithBadStructFieldAccess(block, struct_obj, name_src, field_name);
const field_index = if (struct_ty.isTuple()) blk: {
if (mem.eql(u8, field_name, "len")) {
return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
}
break :blk try sema.tupleFieldIndex(block, struct_ty, field_name, name_src);
} else try sema.structFieldIndex(block, struct_ty, field_name, name_src);
try sema.checkPtrOperand(block, ptr_src, field_ptr_ty);
const field = struct_obj.fields.values()[field_index];
const field_ptr_ty_info = field_ptr_ty.ptrInfo().data;
var ptr_ty_data: Type.Payload.Pointer.Data = .{
.pointee_type = field.ty,
.pointee_type = struct_ty.structFieldType(field_index),
.mutable = field_ptr_ty_info.mutable,
.@"addrspace" = field_ptr_ty_info.@"addrspace",
};
if (struct_obj.layout == .Packed) {
if (struct_ty.containerLayout() == .Packed) {
return sema.fail(block, src, "TODO handle packed structs with @fieldParentPtr", .{});
} else {
ptr_ty_data.@"align" = field.abi_align;
ptr_ty_data.@"align" = if (struct_ty.castTag(.@"struct")) |struct_obj| b: {
break :b struct_obj.data.fields.values()[field_index].abi_align;
} else 0;
}
const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
@ -19059,7 +19042,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 +19051,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;
@ -19231,17 +19219,13 @@ fn tupleFieldIndex(
field_name: []const u8,
field_name_src: LazySrcLoc,
) CompileError!u32 {
const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
return sema.fail(block, field_name_src, "tuple '{}' has no such field '{s}': {s}", .{
tuple_ty.fmt(sema.mod), field_name, @errorName(err),
});
};
if (field_index >= tuple_ty.structFieldCount()) {
return sema.fail(block, field_name_src, "tuple '{}' has no such field '{s}'", .{
tuple_ty.fmt(sema.mod), field_name,
});
}
return field_index;
if (std.fmt.parseUnsigned(u32, field_name, 10)) |field_index| {
if (field_index < tuple_ty.structFieldCount()) return field_index;
} else |_| {}
return sema.fail(block, field_name_src, "no field named '{s}' in tuple '{}'", .{
field_name, tuple_ty.fmt(sema.mod),
});
}
fn tupleFieldValByIndex(
@ -20356,7 +20340,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 +25738,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(
@ -25773,8 +25761,8 @@ fn anonStructFieldIndex(
return @intCast(u32, i);
}
}
return sema.fail(block, field_src, "anonymous struct '{}' has no such field '{s}'", .{
struct_ty.fmt(sema.mod), field_name,
return sema.fail(block, field_src, "no field named '{s}' in anonymous struct '{}'", .{
field_name, struct_ty.fmt(sema.mod),
});
}

View File

@ -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" => {

View File

@ -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 == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) 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 == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) 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" {
var x: u32 = 0;
const T = @TypeOf(.{ x, x });
_ = @offsetOf(T, "1");
}
test "offsetOf anon struct" {
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;
}

View File

@ -9,5 +9,5 @@ export fn foo() usize {
// backend=stage2
// target=native
//
// :5:27: error: struct 'tmp.Foo' has no field 'a'
// :5:27: error: no field named 'a' in struct 'tmp.Foo'
// :1:13: note: struct declared here