From 2fff25fd220632cc6943680d3d6bbb1f21fa141f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 8 Jul 2022 15:09:56 -0700 Subject: [PATCH 1/4] stage2: Support initializing anonymous struct type This commit adds support for initializing `.anon_struct` types. There is also some follow-up work to do for both tuples and structs regarding comptime fields, so this also adds some tests to keep track of that work. --- src/Sema.zig | 257 ++++++++++++++++++++-------------------- src/type.zig | 18 +++ test/behavior/tuple.zig | 58 +++++++++ 3 files changed, 204 insertions(+), 129 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 0f504c6c1d15..791430c738d4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3326,7 +3326,7 @@ fn zirValidateStructInit( switch (agg_ty.zigTypeTag()) { .Struct => return sema.validateStructInit( block, - agg_ty.castTag(.@"struct").?.data, + agg_ty, init_src, instrs, is_comptime, @@ -3470,7 +3470,7 @@ fn validateUnionInit( fn validateStructInit( sema: *Sema, block: *Block, - struct_obj: *Module.Struct, + struct_ty: Type, init_src: LazySrcLoc, instrs: []const Zir.Inst.Index, is_comptime: bool, @@ -3478,7 +3478,7 @@ fn validateStructInit( const gpa = sema.gpa; // Maps field index to field_ptr index of where it was already initialized. - const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count()); + const found_fields = try gpa.alloc(Zir.Inst.Index, struct_ty.structFieldCount()); defer gpa.free(found_fields); mem.set(Zir.Inst.Index, found_fields, 0); @@ -3490,8 +3490,7 @@ fn validateStructInit( const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; struct_ptr_zir_ref = field_ptr_extra.lhs; const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start); - const field_index = struct_obj.fields.getIndex(field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); + const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); if (found_fields[field_index] != 0) { const other_field_ptr = found_fields[field_index]; const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node; @@ -3509,10 +3508,7 @@ fn validateStructInit( var root_msg: ?*Module.ErrorMsg = null; - const fields = struct_obj.fields.values(); const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref); - const struct_ty = sema.typeOf(struct_ptr).childType(); - if ((is_comptime or block.is_comptime) and (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null) { @@ -3522,10 +3518,9 @@ fn validateStructInit( for (found_fields) |field_ptr, i| { if (field_ptr != 0) continue; - const field = fields[i]; - const field_name = struct_obj.fields.keys()[i]; - - if (field.default_val.tag() == .unreachable_value) { + const default_val = struct_ty.structFieldDefaultValue(i); + if (default_val.tag() == .unreachable_value) { + const field_name = struct_ty.structFieldName(i); const template = "missing struct field: {s}"; const args = .{field_name}; if (root_msg) |msg| { @@ -3536,22 +3531,25 @@ fn validateStructInit( continue; } - const default_field_ptr = try sema.structFieldPtr(block, init_src, struct_ptr, field_name, init_src, struct_ty); - const init = try sema.addConstant(field.ty, field.default_val); const field_src = init_src; // TODO better source location + const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty); + const field_ty = sema.typeOf(default_field_ptr).childType(); + const init = try sema.addConstant(field_ty, default_val); try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); } if (root_msg) |msg| { - const mod = sema.mod; - const fqn = try struct_obj.getFullyQualifiedName(mod); - defer gpa.free(fqn); - try mod.errNoteNonLazy( - struct_obj.srcLoc(mod), - msg, - "struct '{s}' declared here", - .{fqn}, - ); + if (struct_ty.castTag(.@"struct")) |struct_obj| { + const mod = sema.mod; + const fqn = try struct_obj.data.getFullyQualifiedName(mod); + defer gpa.free(fqn); + try mod.errNoteNonLazy( + struct_obj.data.srcLoc(mod), + msg, + "struct '{s}' declared here", + .{fqn}, + ); + } return sema.failWithOwnedErrorMsg(block, msg); } @@ -3566,17 +3564,16 @@ fn validateStructInit( // We collect the comptime field values in case the struct initialization // ends up being comptime-known. - const field_values = try sema.arena.alloc(Value, fields.len); + const field_values = try sema.arena.alloc(Value, struct_ty.structFieldCount()); field: for (found_fields) |field_ptr, i| { - const field = fields[i]; - if (field_ptr != 0) { const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node }; // Determine whether the value stored to this pointer is comptime-known. - if (try sema.typeHasOnePossibleValue(block, field_src, field.ty)) |opv| { + const field_ty = struct_ty.structFieldType(i); + if (try sema.typeHasOnePossibleValue(block, field_src, field_ty)) |opv| { field_values[i] = opv; continue; } @@ -3648,9 +3645,9 @@ fn validateStructInit( continue :field; } - const field_name = struct_obj.fields.keys()[i]; - - if (field.default_val.tag() == .unreachable_value) { + const default_val = struct_ty.structFieldDefaultValue(i); + if (default_val.tag() == .unreachable_value) { + const field_name = struct_ty.structFieldName(i); const template = "missing struct field: {s}"; const args = .{field_name}; if (root_msg) |msg| { @@ -3660,17 +3657,20 @@ fn validateStructInit( } continue; } + field_values[i] = default_val; } if (root_msg) |msg| { - const fqn = try struct_obj.getFullyQualifiedName(sema.mod); - defer gpa.free(fqn); - try sema.mod.errNoteNonLazy( - struct_obj.srcLoc(sema.mod), - msg, - "struct '{s}' declared here", - .{fqn}, - ); + if (struct_ty.castTag(.@"struct")) |struct_obj| { + const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod); + defer gpa.free(fqn); + try sema.mod.errNoteNonLazy( + struct_obj.data.srcLoc(sema.mod), + msg, + "struct '{s}' declared here", + .{fqn}, + ); + } return sema.failWithOwnedErrorMsg(block, msg); } @@ -3679,15 +3679,6 @@ fn validateStructInit( // instead a single `store` to the struct_ptr with a comptime struct value. block.instructions.shrinkRetainingCapacity(first_block_index); - - // The `field_values` array has been populated for all the non-default struct - // fields. Here we fill in the default field values. - for (found_fields) |field_ptr, i| { - if (field_ptr != 0) continue; - - field_values[i] = fields[i].default_val; - } - const struct_val = try Value.Tag.aggregate.create(sema.arena, field_values); const struct_init = try sema.addConstant(struct_ty, struct_val); try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store); @@ -3695,16 +3686,13 @@ fn validateStructInit( } // Our task is to insert `store` instructions for all the default field values. - for (found_fields) |field_ptr, i| { if (field_ptr != 0) continue; - const field = fields[i]; - const field_name = struct_obj.fields.keys()[i]; - const default_field_ptr = try sema.structFieldPtr(block, init_src, struct_ptr, field_name, init_src, struct_ty); - - const init = try sema.addConstant(field.ty, field.default_val); const field_src = init_src; // TODO better source location + const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty); + const field_ty = sema.typeOf(default_field_ptr).childType(); + const init = try sema.addConstant(field_ty, field_values[i]); try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); } } @@ -13827,7 +13815,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const obj_ty = try sema.resolveType(block, src, inst_data.operand); switch (obj_ty.zigTypeTag()) { - .Struct => return structInitEmpty(sema, block, obj_ty, src, src), + .Struct => return sema.structInitEmpty(block, obj_ty, src, src), .Array => return arrayInitEmpty(sema, obj_ty), .Void => return sema.addConstant(obj_ty, Value.void), else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), @@ -13844,29 +13832,13 @@ fn structInitEmpty( const gpa = sema.gpa; // This logic must be synchronized with that in `zirStructInit`. const struct_ty = try sema.resolveTypeFields(block, dest_src, obj_ty); - const struct_obj = struct_ty.castTag(.@"struct").?.data; // The init values to use for the struct instance. - const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count()); + const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount()); defer gpa.free(field_inits); + mem.set(Air.Inst.Ref, field_inits, .none); - var root_msg: ?*Module.ErrorMsg = null; - - for (struct_obj.fields.values()) |field, i| { - if (field.default_val.tag() == .unreachable_value) { - const field_name = struct_obj.fields.keys()[i]; - const template = "missing struct field: {s}"; - const args = .{field_name}; - if (root_msg) |msg| { - try sema.errNote(block, init_src, msg, template, args); - } else { - root_msg = try sema.errMsg(block, init_src, template, args); - } - } else { - field_inits[i] = try sema.addConstant(field.ty, field.default_val); - } - } - return sema.finishStructInit(block, dest_src, field_inits, root_msg, struct_obj, struct_ty, false); + return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false); } fn arrayInitEmpty(sema: *Sema, obj_ty: Type) CompileError!Air.Inst.Ref { @@ -13936,19 +13908,18 @@ fn zirStructInit( const unresolved_struct_type = try sema.resolveType(block, src, first_field_type_extra.container_type); const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type); - if (resolved_ty.castTag(.@"struct")) |struct_payload| { + if (resolved_ty.zigTypeTag() == .Struct) { // This logic must be synchronized with that in `zirStructInitEmpty`. - const struct_obj = struct_payload.data; // Maps field index to field_type index of where it was already initialized. // For making sure all fields are accounted for and no fields are duplicated. - const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count()); + const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount()); defer gpa.free(found_fields); - mem.set(Zir.Inst.Index, found_fields, 0); // The init values to use for the struct instance. - const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count()); + const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount()); defer gpa.free(field_inits); + mem.set(Air.Inst.Ref, field_inits, .none); var field_i: u32 = 0; var extra_index = extra.end; @@ -13961,9 +13932,8 @@ fn zirStructInit( const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node }; const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); - const field_index = struct_obj.fields.getIndex(field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); - if (found_fields[field_index] != 0) { + const field_index = try sema.structFieldIndex(block, resolved_ty, field_name, field_src); + if (field_inits[field_index] != .none) { const other_field_type = found_fields[field_index]; const other_field_type_data = zir_datas[other_field_type].pl_node; const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node }; @@ -13979,27 +13949,7 @@ fn zirStructInit( field_inits[field_index] = try sema.resolveInst(item.data.init); } - var root_msg: ?*Module.ErrorMsg = null; - - for (found_fields) |field_type_inst, i| { - if (field_type_inst != 0) continue; - - // Check if the field has a default init. - const field = struct_obj.fields.values()[i]; - if (field.default_val.tag() == .unreachable_value) { - const field_name = struct_obj.fields.keys()[i]; - const template = "missing struct field: {s}"; - const args = .{field_name}; - if (root_msg) |msg| { - try sema.errNote(block, src, msg, template, args); - } else { - root_msg = try sema.errMsg(block, src, template, args); - } - } else { - field_inits[i] = try sema.addConstant(field.ty, field.default_val); - } - } - return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, resolved_ty, is_ref); + return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref); } else if (resolved_ty.zigTypeTag() == .Union) { if (extra.data.fields_len != 1) { return sema.fail(block, src, "union initialization expects exactly one field", .{}); @@ -14051,29 +14001,69 @@ fn zirStructInit( fn finishStructInit( sema: *Sema, block: *Block, - src: LazySrcLoc, - field_inits: []const Air.Inst.Ref, - root_msg: ?*Module.ErrorMsg, - struct_obj: *Module.Struct, + init_src: LazySrcLoc, + dest_src: LazySrcLoc, + field_inits: []Air.Inst.Ref, struct_ty: Type, is_ref: bool, -) !Air.Inst.Ref { +) CompileError!Air.Inst.Ref { const gpa = sema.gpa; + var root_msg: ?*Module.ErrorMsg = null; + if (struct_ty.isAnonStruct()) { + const struct_obj = struct_ty.castTag(.anon_struct).?.data; + for (struct_obj.values) |default_val, i| { + if (field_inits[i] != .none) continue; + + if (default_val.tag() == .unreachable_value) { + const field_name = struct_obj.names[i]; + const template = "missing struct field: {s}"; + const args = .{field_name}; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, args); + } else { + root_msg = try sema.errMsg(block, init_src, template, args); + } + } else { + field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val); + } + } + } else { + const struct_obj = struct_ty.castTag(.@"struct").?.data; + for (struct_obj.fields.values()) |field, i| { + if (field_inits[i] != .none) continue; + + if (field.default_val.tag() == .unreachable_value) { + const field_name = struct_obj.fields.keys()[i]; + const template = "missing struct field: {s}"; + const args = .{field_name}; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, args); + } else { + root_msg = try sema.errMsg(block, init_src, template, args); + } + } else { + field_inits[i] = try sema.addConstant(field.ty, field.default_val); + } + } + } + if (root_msg) |msg| { - const fqn = try struct_obj.getFullyQualifiedName(sema.mod); - defer gpa.free(fqn); - try sema.mod.errNoteNonLazy( - struct_obj.srcLoc(sema.mod), - msg, - "struct '{s}' declared here", - .{fqn}, - ); + if (struct_ty.castTag(.@"struct")) |struct_obj| { + const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod); + defer gpa.free(fqn); + try sema.mod.errNoteNonLazy( + struct_obj.data.srcLoc(sema.mod), + msg, + "struct '{s}' declared here", + .{fqn}, + ); + } return sema.failWithOwnedErrorMsg(block, msg); } const is_comptime = for (field_inits) |field_init| { - if (!(try sema.isComptimeKnown(block, src, field_init))) { + if (!(try sema.isComptimeKnown(block, dest_src, field_init))) { break false; } } else true; @@ -14081,10 +14071,10 @@ fn finishStructInit( if (is_comptime) { const values = try sema.arena.alloc(Value, field_inits.len); for (field_inits) |field_init, i| { - values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; + values[i] = (sema.resolveMaybeUndefVal(block, dest_src, field_init) catch unreachable).?; } const struct_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstantMaybeRef(block, src, struct_ty, struct_val, is_ref); + return sema.addConstantMaybeRef(block, dest_src, struct_ty, struct_val, is_ref); } if (is_ref) { @@ -14096,15 +14086,15 @@ fn finishStructInit( const alloc = try block.addTy(.alloc, alloc_ty); for (field_inits) |field_init, i_usize| { const i = @intCast(u32, i_usize); - const field_src = src; - const field_ptr = try sema.structFieldPtrByIndex(block, src, alloc, i, struct_obj, field_src); - try sema.storePtr(block, src, field_ptr, field_init); + const field_src = dest_src; + const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, alloc, i, field_src, struct_ty); + try sema.storePtr(block, dest_src, field_ptr, field_init); } return alloc; } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, dest_src); try sema.queueFullTypeResolution(struct_ty); return block.addAggregateInit(struct_ty, field_inits); } @@ -19059,7 +19049,7 @@ fn structFieldPtr( return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); const field_index = @intCast(u32, field_index_big); - return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_obj, field_name_src); + return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty); } fn structFieldPtrByIndex( @@ -19068,9 +19058,14 @@ fn structFieldPtrByIndex( src: LazySrcLoc, struct_ptr: Air.Inst.Ref, field_index: u32, - struct_obj: *Module.Struct, field_src: LazySrcLoc, + struct_ty: Type, ) CompileError!Air.Inst.Ref { + if (struct_ty.isAnonStruct()) { + return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index); + } + + const struct_obj = struct_ty.castTag(.@"struct").?.data; const field = struct_obj.fields.values()[field_index]; const struct_ptr_ty = sema.typeOf(struct_ptr); const struct_ptr_ty_info = struct_ptr_ty.ptrInfo().data; @@ -20356,7 +20351,7 @@ fn coerce( }, .Struct => { if (inst == .empty_struct) { - return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src); + return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src); } if (inst_ty.isTupleOrAnonStruct()) { return sema.coerceTupleToStruct(block, dest_ty, dest_ty_src, inst, inst_src); @@ -25754,10 +25749,14 @@ fn structFieldIndex( field_src: LazySrcLoc, ) !u32 { const struct_ty = try sema.resolveTypeFields(block, field_src, unresolved_struct_ty); - const struct_obj = struct_ty.castTag(.@"struct").?.data; - const field_index_usize = struct_obj.fields.getIndex(field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); - return @intCast(u32, field_index_usize); + if (struct_ty.isAnonStruct()) { + return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src); + } else { + const struct_obj = struct_ty.castTag(.@"struct").?.data; + const field_index_usize = struct_obj.fields.getIndex(field_name) orelse + return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); + return @intCast(u32, field_index_usize); + } } fn anonStructFieldIndex( diff --git a/src/type.zig b/src/type.zig index 9552d734ba9f..1de4dd5ae01a 100644 --- a/src/type.zig +++ b/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" => { diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 13772865ef06..7cec421588b9 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -197,3 +197,61 @@ test "initializing tuple with explicit type" { var a = T{ 0, 0 }; _ = a; } + +test "initializing anon struct with explicit type" { + const T = @TypeOf(.{ .foo = @as(i32, 1), .bar = @as(i32, 2) }); + var a = T{ .foo = 1, .bar = 2 }; + _ = a; +} + +test "fieldParentPtr of tuple" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; + + var x: u32 = 0; + const tuple = .{ x, x }; + try testing.expect(&tuple == @fieldParentPtr(@TypeOf(tuple), "1", &tuple[1])); +} + +test "fieldParentPtr of anon struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; + + var x: u32 = 0; + const anon_st = .{ .foo = x, .bar = x }; + try testing.expect(&anon_st == @fieldParentPtr(@TypeOf(anon_st), "bar", &anon_st.bar)); +} + +test "offsetOf tuple" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; + + var x: u32 = 0; + const T = @TypeOf(.{ x, x }); + + _ = @offsetOf(T, "1"); +} + +test "offsetOf anon struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; + + var x: u32 = 0; + const T = @TypeOf(.{ .foo = x, .bar = x }); + + _ = @offsetOf(T, "bar"); +} + +test "initializing tuple with mixed comptime-runtime fields" { + if (true) return error.SkipZigTest; // TODO + + var x: u32 = 15; + const T = @TypeOf(.{ @as(i32, -1234), @as(u32, 5678), x }); + var a: T = .{ -1234, 5678, x + 1 }; + _ = a; +} + +test "initializing anon struct with mixed comptime-runtime fields" { + if (true) return error.SkipZigTest; // TODO + + var x: u32 = 15; + const T = @TypeOf(.{ .foo = @as(i32, -1234), .bar = x }); + var a: T = .{ .foo = -1234, .bar = x + 1 }; + _ = a; +} From 51a8f52e6cdd53321572830efe3ac75cb09b5a09 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 8 Jul 2022 17:29:32 -0700 Subject: [PATCH 2/4] stage2: Support `@offsetOf` for tuples and anon structs --- src/Sema.zig | 54 ++++++++----------- test/behavior/tuple.zig | 6 --- .../offsetOf-bad_field_name.zig | 2 +- 3 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 791430c738d4..0528b31b915b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -15872,50 +15872,40 @@ 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()) b: { + if (std.fmt.parseUnsigned(u32, field_name, 10)) |idx| { + if (idx < ty.structFieldCount()) break :b idx; + } else |_| {} + return sema.fail(block, rhs_src, "tuple '{}' has no such field '{s}'", .{ + ty.fmt(sema.mod), field_name, + }); + } 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, } } diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 7cec421588b9..aa73dbc98ba8 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -221,20 +221,14 @@ test "fieldParentPtr of anon struct" { } test "offsetOf tuple" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; - var x: u32 = 0; const T = @TypeOf(.{ x, x }); - _ = @offsetOf(T, "1"); } test "offsetOf anon struct" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; - var x: u32 = 0; const T = @TypeOf(.{ .foo = x, .bar = x }); - _ = @offsetOf(T, "bar"); } diff --git a/test/cases/compile_errors/offsetOf-bad_field_name.zig b/test/cases/compile_errors/offsetOf-bad_field_name.zig index 5beee8fe2e09..eb04da3c6816 100644 --- a/test/cases/compile_errors/offsetOf-bad_field_name.zig +++ b/test/cases/compile_errors/offsetOf-bad_field_name.zig @@ -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 From c830606bbee72054749d4bf56e9215cac7785afa Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 8 Jul 2022 17:30:06 -0700 Subject: [PATCH 3/4] stage2: Support `@fieldParentPtr` for tuples and anon structs --- src/Sema.zig | 20 +++++++++++++------- test/behavior/tuple.zig | 10 ++++++++-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 0528b31b915b..78a133124364 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17173,24 +17173,30 @@ 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()) b: { + if (std.fmt.parseUnsigned(u32, field_name, 10)) |idx| { + if (idx < struct_ty.structFieldCount()) break :b idx; + } else |_| {} + return sema.fail(block, name_src, "tuple '{}' has no such field '{s}'", .{ + struct_ty.fmt(sema.mod), field_name, + }); + } 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); diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index aa73dbc98ba8..2442ae36293a 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -205,7 +205,10 @@ test "initializing anon struct with explicit type" { } test "fieldParentPtr of tuple" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + 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 }; @@ -213,7 +216,10 @@ test "fieldParentPtr of tuple" { } test "fieldParentPtr of anon struct" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + 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 }; From 5b36b0ed0c6d9f6dbe7ec7261cef7802283eaa82 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 9 Jul 2022 14:38:32 +0300 Subject: [PATCH 4/4] Sema: use `tupleFieldIndex` --- src/Sema.zig | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 78a133124364..0e7c1c5937f8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -15862,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; @@ -15885,13 +15886,11 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 }, } - const field_index = if (ty.isTuple()) b: { - if (std.fmt.parseUnsigned(u32, field_name, 10)) |idx| { - if (idx < ty.structFieldCount()) break :b idx; - } else |_| {} - return sema.fail(block, rhs_src, "tuple '{}' has no such field '{s}'", .{ - ty.fmt(sema.mod), field_name, - }); + 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()) { @@ -17173,13 +17172,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr } try sema.resolveTypeLayout(block, ty_src, struct_ty); - const field_index = if (struct_ty.isTuple()) b: { - if (std.fmt.parseUnsigned(u32, field_name, 10)) |idx| { - if (idx < struct_ty.structFieldCount()) break :b idx; - } else |_| {} - return sema.fail(block, name_src, "tuple '{}' has no such field '{s}'", .{ - struct_ty.fmt(sema.mod), 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); @@ -19222,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( @@ -25768,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), }); }