Skip to content

Commit 05da5b3

Browse files
ifreundVexu
authored andcommitted
Sema: implement @fieldParentPtr for unions
1 parent dc1f50e commit 05da5b3

File tree

7 files changed

+131
-25
lines changed

7 files changed

+131
-25
lines changed

src/Sema.zig

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21482,48 +21482,62 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
2148221482
const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2148321483
const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
2148421484

21485-
const struct_ty = try sema.resolveType(block, ty_src, extra.parent_type);
21485+
const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type);
2148621486
const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime-known");
2148721487
const field_ptr = try sema.resolveInst(extra.field_ptr);
2148821488
const field_ptr_ty = sema.typeOf(field_ptr);
2148921489

21490-
if (struct_ty.zigTypeTag() != .Struct) {
21491-
return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(sema.mod)});
21490+
if (parent_ty.zigTypeTag() != .Struct and parent_ty.zigTypeTag() != .Union) {
21491+
return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)});
2149221492
}
21493-
try sema.resolveTypeLayout(struct_ty);
21493+
try sema.resolveTypeLayout(parent_ty);
2149421494

21495-
const field_index = if (struct_ty.isTuple()) blk: {
21496-
if (mem.eql(u8, field_name, "len")) {
21497-
return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
21498-
}
21499-
break :blk try sema.tupleFieldIndex(block, struct_ty, field_name, name_src);
21500-
} else try sema.structFieldIndex(block, struct_ty, field_name, name_src);
21495+
const field_index = switch (parent_ty.zigTypeTag()) {
21496+
.Struct => blk: {
21497+
if (parent_ty.isTuple()) {
21498+
if (mem.eql(u8, field_name, "len")) {
21499+
return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
21500+
}
21501+
break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src);
21502+
} else {
21503+
break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src);
21504+
}
21505+
},
21506+
.Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src),
21507+
else => unreachable,
21508+
};
2150121509

21502-
if (struct_ty.structFieldIsComptime(field_index)) {
21510+
if (parent_ty.zigTypeTag() == .Struct and parent_ty.structFieldIsComptime(field_index)) {
2150321511
return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{});
2150421512
}
2150521513

2150621514
try sema.checkPtrOperand(block, ptr_src, field_ptr_ty);
2150721515
const field_ptr_ty_info = field_ptr_ty.ptrInfo().data;
2150821516

2150921517
var ptr_ty_data: Type.Payload.Pointer.Data = .{
21510-
.pointee_type = struct_ty.structFieldType(field_index),
21518+
.pointee_type = parent_ty.structFieldType(field_index),
2151121519
.mutable = field_ptr_ty_info.mutable,
2151221520
.@"addrspace" = field_ptr_ty_info.@"addrspace",
2151321521
};
2151421522

21515-
if (struct_ty.containerLayout() == .Packed) {
21516-
return sema.fail(block, src, "TODO handle packed structs with @fieldParentPtr", .{});
21523+
if (parent_ty.containerLayout() == .Packed) {
21524+
return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{});
2151721525
} else {
21518-
ptr_ty_data.@"align" = if (struct_ty.castTag(.@"struct")) |struct_obj| b: {
21519-
break :b struct_obj.data.fields.values()[field_index].abi_align;
21520-
} else 0;
21526+
ptr_ty_data.@"align" = blk: {
21527+
if (parent_ty.castTag(.@"struct")) |struct_obj| {
21528+
break :blk struct_obj.data.fields.values()[field_index].abi_align;
21529+
} else if (parent_ty.cast(Type.Payload.Union)) |union_obj| {
21530+
break :blk union_obj.data.fields.values()[field_index].abi_align;
21531+
} else {
21532+
break :blk 0;
21533+
}
21534+
};
2152121535
}
2152221536

2152321537
const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
2152421538
const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);
2152521539

21526-
ptr_ty_data.pointee_type = struct_ty;
21540+
ptr_ty_data.pointee_type = parent_ty;
2152721541
const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
2152821542

2152921543
if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {
@@ -21540,11 +21554,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
2154021554
field_name,
2154121555
field_index,
2154221556
payload.data.field_index,
21543-
struct_ty.fmt(sema.mod),
21557+
parent_ty.fmt(sema.mod),
2154421558
},
2154521559
);
2154621560
errdefer msg.destroy(sema.gpa);
21547-
try sema.addDeclaredHereNote(msg, struct_ty);
21561+
try sema.addDeclaredHereNote(msg, parent_ty);
2154821562
break :msg msg;
2154921563
};
2155021564
return sema.failWithOwnedErrorMsg(msg);

src/arch/arm/CodeGen.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2973,6 +2973,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
29732973
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
29742974
const field_ptr = try self.resolveInst(extra.field_ptr);
29752975
const struct_ty = self.air.getRefType(ty_pl.ty).childType();
2976+
2977+
if (struct_ty.zigTypeTag() == .Union) {
2978+
return self.fail("TODO implement @fieldParentPtr codegen for unions", .{});
2979+
}
2980+
29762981
const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*));
29772982
switch (field_ptr) {
29782983
.ptr_stack_offset => |off| {

src/arch/wasm/CodeGen.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4944,8 +4944,8 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49444944
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr});
49454945

49464946
const field_ptr = try func.resolveInst(extra.field_ptr);
4947-
const struct_ty = func.air.getRefType(ty_pl.ty).childType();
4948-
const field_offset = struct_ty.structFieldOffset(extra.field_index, func.target);
4947+
const parent_ty = func.air.getRefType(ty_pl.ty).childType();
4948+
const field_offset = parent_ty.structFieldOffset(extra.field_index, func.target);
49494949

49504950
const result = if (field_offset != 0) result: {
49514951
const base = try func.buildPointerOffset(field_ptr, 0, .new);

src/codegen/c.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5367,12 +5367,18 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
53675367
}
53685368

53695369
const struct_ptr_ty = f.air.typeOfIndex(inst);
5370+
53705371
const field_ptr_ty = f.air.typeOf(extra.field_ptr);
53715372
const field_ptr_val = try f.resolveInst(extra.field_ptr);
53725373
try reap(f, inst, &.{extra.field_ptr});
53735374

53745375
const target = f.object.dg.module.getTarget();
53755376
const struct_ty = struct_ptr_ty.childType();
5377+
5378+
if (struct_ty.zigTypeTag() == .Union) {
5379+
return f.fail("TODO: CBE: @fieldParentPtr for unions", .{});
5380+
}
5381+
53765382
const field_offset = struct_ty.structFieldOffset(extra.field_index, target);
53775383

53785384
var field_offset_pl = Value.Payload.I64{

src/codegen/llvm.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6020,8 +6020,8 @@ pub const FuncGen = struct {
60206020
const field_ptr = try self.resolveInst(extra.field_ptr);
60216021

60226022
const target = self.dg.module.getTarget();
6023-
const struct_ty = self.air.getRefType(ty_pl.ty).childType();
6024-
const field_offset = struct_ty.structFieldOffset(extra.field_index, target);
6023+
const parent_ty = self.air.getRefType(ty_pl.ty).childType();
6024+
const field_offset = parent_ty.structFieldOffset(extra.field_index, target);
60256025

60266026
const res_ty = try self.dg.lowerType(self.air.getRefType(ty_pl.ty));
60276027
if (field_offset == 0) {

test/behavior/field_parent_ptr.zig

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,84 @@ fn testParentFieldPtrFirst(a: *const bool) !void {
4444
try expect(base == &foo);
4545
try expect(&base.a == a);
4646
}
47+
48+
test "@fieldParentPtr untagged union" {
49+
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
50+
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
51+
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
52+
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
53+
54+
try testFieldParentPtrUnion(&bar.c);
55+
comptime try testFieldParentPtrUnion(&bar.c);
56+
}
57+
58+
const Bar = union(enum) {
59+
a: bool,
60+
b: f32,
61+
c: i32,
62+
d: i32,
63+
};
64+
65+
const bar = Bar{ .c = 42 };
66+
67+
fn testFieldParentPtrUnion(c: *const i32) !void {
68+
try expect(c == &bar.c);
69+
70+
const base = @fieldParentPtr(Bar, "c", c);
71+
try expect(base == &bar);
72+
try expect(&base.c == c);
73+
}
74+
75+
test "@fieldParentPtr tagged union" {
76+
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
77+
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
78+
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
79+
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
80+
81+
try testFieldParentPtrTaggedUnion(&bar_tagged.c);
82+
comptime try testFieldParentPtrTaggedUnion(&bar_tagged.c);
83+
}
84+
85+
const BarTagged = union(enum) {
86+
a: bool,
87+
b: f32,
88+
c: i32,
89+
d: i32,
90+
};
91+
92+
const bar_tagged = BarTagged{ .c = 42 };
93+
94+
fn testFieldParentPtrTaggedUnion(c: *const i32) !void {
95+
try expect(c == &bar_tagged.c);
96+
97+
const base = @fieldParentPtr(BarTagged, "c", c);
98+
try expect(base == &bar_tagged);
99+
try expect(&base.c == c);
100+
}
101+
102+
test "@fieldParentPtr extern union" {
103+
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
104+
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
105+
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
106+
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
107+
108+
try testFieldParentPtrExternUnion(&bar_extern.c);
109+
comptime try testFieldParentPtrExternUnion(&bar_extern.c);
110+
}
111+
112+
const BarExtern = extern union {
113+
a: bool,
114+
b: f32,
115+
c: i32,
116+
d: i32,
117+
};
118+
119+
const bar_extern = BarExtern{ .c = 42 };
120+
121+
fn testFieldParentPtrExternUnion(c: *const i32) !void {
122+
try expect(c == &bar_extern.c);
123+
124+
const base = @fieldParentPtr(BarExtern, "c", c);
125+
try expect(base == &bar_extern);
126+
try expect(&base.c == c);
127+
}

test/cases/compile_errors/fieldParentPtr-non_struct.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo {
77
// backend=llvm
88
// target=native
99
//
10-
// :3:28: error: expected struct type, found 'i32'
10+
// :3:28: error: expected struct or union type, found 'i32'

0 commit comments

Comments
 (0)