mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
Merge pull request #12050 from topolarity/anon-struct-init
stage2: Support initializing anonymous struct type
This commit is contained in:
commit
3f11d1d56d
352
src/Sema.zig
352
src/Sema.zig
@ -3326,7 +3326,7 @@ fn zirValidateStructInit(
|
||||
switch (agg_ty.zigTypeTag()) {
|
||||
.Struct => return sema.validateStructInit(
|
||||
block,
|
||||
agg_ty.castTag(.@"struct").?.data,
|
||||
agg_ty,
|
||||
init_src,
|
||||
instrs,
|
||||
is_comptime,
|
||||
@ -3470,7 +3470,7 @@ fn validateUnionInit(
|
||||
fn validateStructInit(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
struct_obj: *Module.Struct,
|
||||
struct_ty: Type,
|
||||
init_src: LazySrcLoc,
|
||||
instrs: []const Zir.Inst.Index,
|
||||
is_comptime: bool,
|
||||
@ -3478,7 +3478,7 @@ fn validateStructInit(
|
||||
const gpa = sema.gpa;
|
||||
|
||||
// Maps field index to field_ptr index of where it was already initialized.
|
||||
const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());
|
||||
const found_fields = try gpa.alloc(Zir.Inst.Index, struct_ty.structFieldCount());
|
||||
defer gpa.free(found_fields);
|
||||
mem.set(Zir.Inst.Index, found_fields, 0);
|
||||
|
||||
@ -3490,8 +3490,7 @@ fn validateStructInit(
|
||||
const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
|
||||
struct_ptr_zir_ref = field_ptr_extra.lhs;
|
||||
const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
|
||||
const field_index = struct_obj.fields.getIndex(field_name) orelse
|
||||
return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
|
||||
const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
|
||||
if (found_fields[field_index] != 0) {
|
||||
const other_field_ptr = found_fields[field_index];
|
||||
const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node;
|
||||
@ -3509,10 +3508,7 @@ fn validateStructInit(
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
const fields = struct_obj.fields.values();
|
||||
const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
|
||||
const struct_ty = sema.typeOf(struct_ptr).childType();
|
||||
|
||||
if ((is_comptime or block.is_comptime) and
|
||||
(try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
|
||||
{
|
||||
@ -3522,10 +3518,9 @@ fn validateStructInit(
|
||||
for (found_fields) |field_ptr, i| {
|
||||
if (field_ptr != 0) continue;
|
||||
|
||||
const field = fields[i];
|
||||
const field_name = struct_obj.fields.keys()[i];
|
||||
|
||||
if (field.default_val.tag() == .unreachable_value) {
|
||||
const default_val = struct_ty.structFieldDefaultValue(i);
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
const field_name = struct_ty.structFieldName(i);
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
@ -3536,22 +3531,25 @@ fn validateStructInit(
|
||||
continue;
|
||||
}
|
||||
|
||||
const default_field_ptr = try sema.structFieldPtr(block, init_src, struct_ptr, field_name, init_src, struct_ty);
|
||||
const init = try sema.addConstant(field.ty, field.default_val);
|
||||
const field_src = init_src; // TODO better source location
|
||||
const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty);
|
||||
const field_ty = sema.typeOf(default_field_ptr).childType();
|
||||
const init = try sema.addConstant(field_ty, default_val);
|
||||
try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
|
||||
}
|
||||
|
||||
if (root_msg) |msg| {
|
||||
const mod = sema.mod;
|
||||
const fqn = try struct_obj.getFullyQualifiedName(mod);
|
||||
defer gpa.free(fqn);
|
||||
try mod.errNoteNonLazy(
|
||||
struct_obj.srcLoc(mod),
|
||||
msg,
|
||||
"struct '{s}' declared here",
|
||||
.{fqn},
|
||||
);
|
||||
if (struct_ty.castTag(.@"struct")) |struct_obj| {
|
||||
const mod = sema.mod;
|
||||
const fqn = try struct_obj.data.getFullyQualifiedName(mod);
|
||||
defer gpa.free(fqn);
|
||||
try mod.errNoteNonLazy(
|
||||
struct_obj.data.srcLoc(mod),
|
||||
msg,
|
||||
"struct '{s}' declared here",
|
||||
.{fqn},
|
||||
);
|
||||
}
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
|
||||
@ -3566,17 +3564,16 @@ fn validateStructInit(
|
||||
|
||||
// We collect the comptime field values in case the struct initialization
|
||||
// ends up being comptime-known.
|
||||
const field_values = try sema.arena.alloc(Value, fields.len);
|
||||
const field_values = try sema.arena.alloc(Value, struct_ty.structFieldCount());
|
||||
|
||||
field: for (found_fields) |field_ptr, i| {
|
||||
const field = fields[i];
|
||||
|
||||
if (field_ptr != 0) {
|
||||
const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
|
||||
const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node };
|
||||
|
||||
// Determine whether the value stored to this pointer is comptime-known.
|
||||
if (try sema.typeHasOnePossibleValue(block, field_src, field.ty)) |opv| {
|
||||
const field_ty = struct_ty.structFieldType(i);
|
||||
if (try sema.typeHasOnePossibleValue(block, field_src, field_ty)) |opv| {
|
||||
field_values[i] = opv;
|
||||
continue;
|
||||
}
|
||||
@ -3648,9 +3645,9 @@ fn validateStructInit(
|
||||
continue :field;
|
||||
}
|
||||
|
||||
const field_name = struct_obj.fields.keys()[i];
|
||||
|
||||
if (field.default_val.tag() == .unreachable_value) {
|
||||
const default_val = struct_ty.structFieldDefaultValue(i);
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
const field_name = struct_ty.structFieldName(i);
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
@ -3660,17 +3657,20 @@ fn validateStructInit(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
field_values[i] = default_val;
|
||||
}
|
||||
|
||||
if (root_msg) |msg| {
|
||||
const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
|
||||
defer gpa.free(fqn);
|
||||
try sema.mod.errNoteNonLazy(
|
||||
struct_obj.srcLoc(sema.mod),
|
||||
msg,
|
||||
"struct '{s}' declared here",
|
||||
.{fqn},
|
||||
);
|
||||
if (struct_ty.castTag(.@"struct")) |struct_obj| {
|
||||
const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod);
|
||||
defer gpa.free(fqn);
|
||||
try sema.mod.errNoteNonLazy(
|
||||
struct_obj.data.srcLoc(sema.mod),
|
||||
msg,
|
||||
"struct '{s}' declared here",
|
||||
.{fqn},
|
||||
);
|
||||
}
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
|
||||
@ -3679,15 +3679,6 @@ fn validateStructInit(
|
||||
// instead a single `store` to the struct_ptr with a comptime struct value.
|
||||
|
||||
block.instructions.shrinkRetainingCapacity(first_block_index);
|
||||
|
||||
// The `field_values` array has been populated for all the non-default struct
|
||||
// fields. Here we fill in the default field values.
|
||||
for (found_fields) |field_ptr, i| {
|
||||
if (field_ptr != 0) continue;
|
||||
|
||||
field_values[i] = fields[i].default_val;
|
||||
}
|
||||
|
||||
const struct_val = try Value.Tag.aggregate.create(sema.arena, field_values);
|
||||
const struct_init = try sema.addConstant(struct_ty, struct_val);
|
||||
try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
|
||||
@ -3695,16 +3686,13 @@ fn validateStructInit(
|
||||
}
|
||||
|
||||
// Our task is to insert `store` instructions for all the default field values.
|
||||
|
||||
for (found_fields) |field_ptr, i| {
|
||||
if (field_ptr != 0) continue;
|
||||
|
||||
const field = fields[i];
|
||||
const field_name = struct_obj.fields.keys()[i];
|
||||
const default_field_ptr = try sema.structFieldPtr(block, init_src, struct_ptr, field_name, init_src, struct_ty);
|
||||
|
||||
const init = try sema.addConstant(field.ty, field.default_val);
|
||||
const field_src = init_src; // TODO better source location
|
||||
const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty);
|
||||
const field_ty = sema.typeOf(default_field_ptr).childType();
|
||||
const init = try sema.addConstant(field_ty, field_values[i]);
|
||||
try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
|
||||
}
|
||||
}
|
||||
@ -13827,7 +13815,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
|
||||
const obj_ty = try sema.resolveType(block, src, inst_data.operand);
|
||||
|
||||
switch (obj_ty.zigTypeTag()) {
|
||||
.Struct => return structInitEmpty(sema, block, obj_ty, src, src),
|
||||
.Struct => return sema.structInitEmpty(block, obj_ty, src, src),
|
||||
.Array => return arrayInitEmpty(sema, obj_ty),
|
||||
.Void => return sema.addConstant(obj_ty, Value.void),
|
||||
else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
|
||||
@ -13844,29 +13832,13 @@ fn structInitEmpty(
|
||||
const gpa = sema.gpa;
|
||||
// This logic must be synchronized with that in `zirStructInit`.
|
||||
const struct_ty = try sema.resolveTypeFields(block, dest_src, obj_ty);
|
||||
const struct_obj = struct_ty.castTag(.@"struct").?.data;
|
||||
|
||||
// The init values to use for the struct instance.
|
||||
const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
|
||||
const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount());
|
||||
defer gpa.free(field_inits);
|
||||
mem.set(Air.Inst.Ref, field_inits, .none);
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
for (struct_obj.fields.values()) |field, i| {
|
||||
if (field.default_val.tag() == .unreachable_value) {
|
||||
const field_name = struct_obj.fields.keys()[i];
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, args);
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, args);
|
||||
}
|
||||
} else {
|
||||
field_inits[i] = try sema.addConstant(field.ty, field.default_val);
|
||||
}
|
||||
}
|
||||
return sema.finishStructInit(block, dest_src, field_inits, root_msg, struct_obj, struct_ty, false);
|
||||
return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false);
|
||||
}
|
||||
|
||||
fn arrayInitEmpty(sema: *Sema, obj_ty: Type) CompileError!Air.Inst.Ref {
|
||||
@ -13936,19 +13908,18 @@ fn zirStructInit(
|
||||
const unresolved_struct_type = try sema.resolveType(block, src, first_field_type_extra.container_type);
|
||||
const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
|
||||
|
||||
if (resolved_ty.castTag(.@"struct")) |struct_payload| {
|
||||
if (resolved_ty.zigTypeTag() == .Struct) {
|
||||
// This logic must be synchronized with that in `zirStructInitEmpty`.
|
||||
const struct_obj = struct_payload.data;
|
||||
|
||||
// Maps field index to field_type index of where it was already initialized.
|
||||
// For making sure all fields are accounted for and no fields are duplicated.
|
||||
const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());
|
||||
const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount());
|
||||
defer gpa.free(found_fields);
|
||||
mem.set(Zir.Inst.Index, found_fields, 0);
|
||||
|
||||
// The init values to use for the struct instance.
|
||||
const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
|
||||
const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount());
|
||||
defer gpa.free(field_inits);
|
||||
mem.set(Air.Inst.Ref, field_inits, .none);
|
||||
|
||||
var field_i: u32 = 0;
|
||||
var extra_index = extra.end;
|
||||
@ -13961,9 +13932,8 @@ fn zirStructInit(
|
||||
const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
|
||||
const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
|
||||
const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
|
||||
const field_index = struct_obj.fields.getIndex(field_name) orelse
|
||||
return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
|
||||
if (found_fields[field_index] != 0) {
|
||||
const field_index = try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
|
||||
if (field_inits[field_index] != .none) {
|
||||
const other_field_type = found_fields[field_index];
|
||||
const other_field_type_data = zir_datas[other_field_type].pl_node;
|
||||
const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node };
|
||||
@ -13979,27 +13949,7 @@ fn zirStructInit(
|
||||
field_inits[field_index] = try sema.resolveInst(item.data.init);
|
||||
}
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
for (found_fields) |field_type_inst, i| {
|
||||
if (field_type_inst != 0) continue;
|
||||
|
||||
// Check if the field has a default init.
|
||||
const field = struct_obj.fields.values()[i];
|
||||
if (field.default_val.tag() == .unreachable_value) {
|
||||
const field_name = struct_obj.fields.keys()[i];
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, src, msg, template, args);
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, src, template, args);
|
||||
}
|
||||
} else {
|
||||
field_inits[i] = try sema.addConstant(field.ty, field.default_val);
|
||||
}
|
||||
}
|
||||
return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, resolved_ty, is_ref);
|
||||
return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref);
|
||||
} else if (resolved_ty.zigTypeTag() == .Union) {
|
||||
if (extra.data.fields_len != 1) {
|
||||
return sema.fail(block, src, "union initialization expects exactly one field", .{});
|
||||
@ -14051,29 +14001,69 @@ fn zirStructInit(
|
||||
fn finishStructInit(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
field_inits: []const Air.Inst.Ref,
|
||||
root_msg: ?*Module.ErrorMsg,
|
||||
struct_obj: *Module.Struct,
|
||||
init_src: LazySrcLoc,
|
||||
dest_src: LazySrcLoc,
|
||||
field_inits: []Air.Inst.Ref,
|
||||
struct_ty: Type,
|
||||
is_ref: bool,
|
||||
) !Air.Inst.Ref {
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const gpa = sema.gpa;
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
if (struct_ty.isAnonStruct()) {
|
||||
const struct_obj = struct_ty.castTag(.anon_struct).?.data;
|
||||
for (struct_obj.values) |default_val, i| {
|
||||
if (field_inits[i] != .none) continue;
|
||||
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
const field_name = struct_obj.names[i];
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, args);
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, args);
|
||||
}
|
||||
} else {
|
||||
field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const struct_obj = struct_ty.castTag(.@"struct").?.data;
|
||||
for (struct_obj.fields.values()) |field, i| {
|
||||
if (field_inits[i] != .none) continue;
|
||||
|
||||
if (field.default_val.tag() == .unreachable_value) {
|
||||
const field_name = struct_obj.fields.keys()[i];
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, args);
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, args);
|
||||
}
|
||||
} else {
|
||||
field_inits[i] = try sema.addConstant(field.ty, field.default_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root_msg) |msg| {
|
||||
const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
|
||||
defer gpa.free(fqn);
|
||||
try sema.mod.errNoteNonLazy(
|
||||
struct_obj.srcLoc(sema.mod),
|
||||
msg,
|
||||
"struct '{s}' declared here",
|
||||
.{fqn},
|
||||
);
|
||||
if (struct_ty.castTag(.@"struct")) |struct_obj| {
|
||||
const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod);
|
||||
defer gpa.free(fqn);
|
||||
try sema.mod.errNoteNonLazy(
|
||||
struct_obj.data.srcLoc(sema.mod),
|
||||
msg,
|
||||
"struct '{s}' declared here",
|
||||
.{fqn},
|
||||
);
|
||||
}
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
|
||||
const is_comptime = for (field_inits) |field_init| {
|
||||
if (!(try sema.isComptimeKnown(block, src, field_init))) {
|
||||
if (!(try sema.isComptimeKnown(block, dest_src, field_init))) {
|
||||
break false;
|
||||
}
|
||||
} else true;
|
||||
@ -14081,10 +14071,10 @@ fn finishStructInit(
|
||||
if (is_comptime) {
|
||||
const values = try sema.arena.alloc(Value, field_inits.len);
|
||||
for (field_inits) |field_init, i| {
|
||||
values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
|
||||
values[i] = (sema.resolveMaybeUndefVal(block, dest_src, field_init) catch unreachable).?;
|
||||
}
|
||||
const struct_val = try Value.Tag.aggregate.create(sema.arena, values);
|
||||
return sema.addConstantMaybeRef(block, src, struct_ty, struct_val, is_ref);
|
||||
return sema.addConstantMaybeRef(block, dest_src, struct_ty, struct_val, is_ref);
|
||||
}
|
||||
|
||||
if (is_ref) {
|
||||
@ -14096,15 +14086,15 @@ fn finishStructInit(
|
||||
const alloc = try block.addTy(.alloc, alloc_ty);
|
||||
for (field_inits) |field_init, i_usize| {
|
||||
const i = @intCast(u32, i_usize);
|
||||
const field_src = src;
|
||||
const field_ptr = try sema.structFieldPtrByIndex(block, src, alloc, i, struct_obj, field_src);
|
||||
try sema.storePtr(block, src, field_ptr, field_init);
|
||||
const field_src = dest_src;
|
||||
const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, alloc, i, field_src, struct_ty);
|
||||
try sema.storePtr(block, dest_src, field_ptr, field_init);
|
||||
}
|
||||
|
||||
return alloc;
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
try sema.requireRuntimeBlock(block, dest_src);
|
||||
try sema.queueFullTypeResolution(struct_ty);
|
||||
return block.addAggregateInit(struct_ty, field_inits);
|
||||
}
|
||||
@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
18
src/type.zig
18
src/type.zig
@ -5432,6 +5432,24 @@ pub const Type = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn structFieldDefaultValue(ty: Type, index: usize) Value {
|
||||
switch (ty.tag()) {
|
||||
.@"struct" => {
|
||||
const struct_obj = ty.castTag(.@"struct").?.data;
|
||||
return struct_obj.fields.values()[index].default_val;
|
||||
},
|
||||
.tuple => {
|
||||
const tuple = ty.castTag(.tuple).?.data;
|
||||
return tuple.values[index];
|
||||
},
|
||||
.anon_struct => {
|
||||
const struct_obj = ty.castTag(.anon_struct).?.data;
|
||||
return struct_obj.values[index];
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn structFieldValueComptime(ty: Type, index: usize) ?Value {
|
||||
switch (ty.tag()) {
|
||||
.@"struct" => {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user