authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2023-02-20 23:31:48+01:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-02-21 15:57:13+02:00
log05da5b32a820c031001098034840940964f41a81
treead6dfd90922e168d536f8adba3f97af2e1843e31
parentdc1f50e505105cabe1ed53951ca612778d6019ee

Sema: implement @fieldParentPtr for unions


7 files changed, 131 insertions(+), 25 deletions(-)

src/Sema.zig+34-20
...@@ -21482,24 +21482,32 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -21482,24 +21482,32 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
21482 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };21482 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
21483 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };21483 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);
21486 const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime-known");21486 const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime-known");
21487 const field_ptr = try sema.resolveInst(extra.field_ptr);21487 const field_ptr = try sema.resolveInst(extra.field_ptr);
21488 const field_ptr_ty = sema.typeOf(field_ptr);21488 const field_ptr_ty = sema.typeOf(field_ptr);
2148921489
21490 if (struct_ty.zigTypeTag() != .Struct) {21490 if (parent_ty.zigTypeTag() != .Struct and parent_ty.zigTypeTag() != .Union) {
21491 return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(sema.mod)});21491 return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)});
21492 }21492 }
21493 try sema.resolveTypeLayout(struct_ty);21493 try sema.resolveTypeLayout(parent_ty);
2149421494
21495 const field_index = if (struct_ty.isTuple()) blk: {21495 const field_index = switch (parent_ty.zigTypeTag()) {
21496 if (mem.eql(u8, field_name, "len")) {21496 .Struct => blk: {
21497 return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});21497 if (parent_ty.isTuple()) {
21498 }21498 if (mem.eql(u8, field_name, "len")) {
21499 break :blk try sema.tupleFieldIndex(block, struct_ty, field_name, name_src);21499 return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
21500 } else try sema.structFieldIndex(block, struct_ty, field_name, name_src);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)) {
21503 return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{});21511 return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{});
21504 }21512 }
2150521513
...@@ -21507,23 +21515,29 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -21507,23 +21515,29 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
21507 const field_ptr_ty_info = field_ptr_ty.ptrInfo().data;21515 const field_ptr_ty_info = field_ptr_ty.ptrInfo().data;
2150821516
21509 var ptr_ty_data: Type.Payload.Pointer.Data = .{21517 var ptr_ty_data: Type.Payload.Pointer.Data = .{
21510 .pointee_type = struct_ty.structFieldType(field_index),21518 .pointee_type = parent_ty.structFieldType(field_index),
21511 .mutable = field_ptr_ty_info.mutable,21519 .mutable = field_ptr_ty_info.mutable,
21512 .@"addrspace" = field_ptr_ty_info.@"addrspace",21520 .@"addrspace" = field_ptr_ty_info.@"addrspace",
21513 };21521 };
2151421522
21515 if (struct_ty.containerLayout() == .Packed) {21523 if (parent_ty.containerLayout() == .Packed) {
21516 return sema.fail(block, src, "TODO handle packed structs with @fieldParentPtr", .{});21524 return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{});
21517 } else {21525 } else {
21518 ptr_ty_data.@"align" = if (struct_ty.castTag(.@"struct")) |struct_obj| b: {21526 ptr_ty_data.@"align" = blk: {
21519 break :b struct_obj.data.fields.values()[field_index].abi_align;21527 if (parent_ty.castTag(.@"struct")) |struct_obj| {
21520 } else 0;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 };
21521 }21535 }
2152221536
21523 const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);21537 const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
21524 const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);21538 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;
21527 const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);21541 const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
2152821542
21529 if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {21543 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...@@ -21540,11 +21554,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
21540 field_name,21554 field_name,
21541 field_index,21555 field_index,
21542 payload.data.field_index,21556 payload.data.field_index,
21543 struct_ty.fmt(sema.mod),21557 parent_ty.fmt(sema.mod),
21544 },21558 },
21545 );21559 );
21546 errdefer msg.destroy(sema.gpa);21560 errdefer msg.destroy(sema.gpa);
21547 try sema.addDeclaredHereNote(msg, struct_ty);21561 try sema.addDeclaredHereNote(msg, parent_ty);
21548 break :msg msg;21562 break :msg msg;
21549 };21563 };
21550 return sema.failWithOwnedErrorMsg(msg);21564 return sema.failWithOwnedErrorMsg(msg);
src/arch/arm/CodeGen.zig+5
...@@ -2973,6 +2973,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2973,6 +2973,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
2973 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2973 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2974 const field_ptr = try self.resolveInst(extra.field_ptr);2974 const field_ptr = try self.resolveInst(extra.field_ptr);
2975 const struct_ty = self.air.getRefType(ty_pl.ty).childType();2975 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
2976 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*));2981 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*));
2977 switch (field_ptr) {2982 switch (field_ptr) {
2978 .ptr_stack_offset => |off| {2983 .ptr_stack_offset => |off| {
src/arch/wasm/CodeGen.zig+2-2
...@@ -4944,8 +4944,8 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4944,8 +4944,8 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4944 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr});4944 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr});
49454945
4946 const field_ptr = try func.resolveInst(extra.field_ptr);4946 const field_ptr = try func.resolveInst(extra.field_ptr);
4947 const struct_ty = func.air.getRefType(ty_pl.ty).childType();4947 const parent_ty = func.air.getRefType(ty_pl.ty).childType();
4948 const field_offset = struct_ty.structFieldOffset(extra.field_index, func.target);4948 const field_offset = parent_ty.structFieldOffset(extra.field_index, func.target);
49494949
4950 const result = if (field_offset != 0) result: {4950 const result = if (field_offset != 0) result: {
4951 const base = try func.buildPointerOffset(field_ptr, 0, .new);4951 const base = try func.buildPointerOffset(field_ptr, 0, .new);
src/codegen/c.zig+6
...@@ -5367,12 +5367,18 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5367,12 +5367,18 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5367 }5367 }
53685368
5369 const struct_ptr_ty = f.air.typeOfIndex(inst);5369 const struct_ptr_ty = f.air.typeOfIndex(inst);
5370
5370 const field_ptr_ty = f.air.typeOf(extra.field_ptr);5371 const field_ptr_ty = f.air.typeOf(extra.field_ptr);
5371 const field_ptr_val = try f.resolveInst(extra.field_ptr);5372 const field_ptr_val = try f.resolveInst(extra.field_ptr);
5372 try reap(f, inst, &.{extra.field_ptr});5373 try reap(f, inst, &.{extra.field_ptr});
53735374
5374 const target = f.object.dg.module.getTarget();5375 const target = f.object.dg.module.getTarget();
5375 const struct_ty = struct_ptr_ty.childType();5376 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
5376 const field_offset = struct_ty.structFieldOffset(extra.field_index, target);5382 const field_offset = struct_ty.structFieldOffset(extra.field_index, target);
53775383
5378 var field_offset_pl = Value.Payload.I64{5384 var field_offset_pl = Value.Payload.I64{
src/codegen/llvm.zig+2-2
...@@ -6020,8 +6020,8 @@ pub const FuncGen = struct {...@@ -6020,8 +6020,8 @@ pub const FuncGen = struct {
6020 const field_ptr = try self.resolveInst(extra.field_ptr);6020 const field_ptr = try self.resolveInst(extra.field_ptr);
60216021
6022 const target = self.dg.module.getTarget();6022 const target = self.dg.module.getTarget();
6023 const struct_ty = self.air.getRefType(ty_pl.ty).childType();6023 const parent_ty = self.air.getRefType(ty_pl.ty).childType();
6024 const field_offset = struct_ty.structFieldOffset(extra.field_index, target);6024 const field_offset = parent_ty.structFieldOffset(extra.field_index, target);
60256025
6026 const res_ty = try self.dg.lowerType(self.air.getRefType(ty_pl.ty));6026 const res_ty = try self.dg.lowerType(self.air.getRefType(ty_pl.ty));
6027 if (field_offset == 0) {6027 if (field_offset == 0) {
test/behavior/field_parent_ptr.zig+81
...@@ -44,3 +44,84 @@ fn testParentFieldPtrFirst(a: *const bool) !void {...@@ -44,3 +44,84 @@ fn testParentFieldPtrFirst(a: *const bool) !void {
44 try expect(base == &foo);44 try expect(base == &foo);
45 try expect(&base.a == a);45 try expect(&base.a == a);
46}46}
47
48test "@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
58const Bar = union(enum) {
59 a: bool,
60 b: f32,
61 c: i32,
62 d: i32,
63};
64
65const bar = Bar{ .c = 42 };
66
67fn 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
75test "@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
85const BarTagged = union(enum) {
86 a: bool,
87 b: f32,
88 c: i32,
89 d: i32,
90};
91
92const bar_tagged = BarTagged{ .c = 42 };
93
94fn 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
102test "@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
112const BarExtern = extern union {
113 a: bool,
114 b: f32,
115 c: i32,
116 d: i32,
117};
118
119const bar_extern = BarExtern{ .c = 42 };
120
121fn 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+1-1
...@@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo {...@@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo {
7// backend=llvm7// backend=llvm
8// target=native8// target=native
9//9//
10// :3:28: error: expected struct type, found 'i32'10// :3:28: error: expected struct or union type, found 'i32'