| author | |
| committer | |
| log | 05da5b32a820c031001098034840940964f41a81 |
| tree | ad6dfd90922e168d536f8adba3f97af2e1843e31 |
| parent | dc1f50e505105cabe1ed53951ca612778d6019ee |
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 }; |
| 21484 | 21484 | ||
| 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); |
| 21489 | 21489 | ||
| 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); |
| 21494 | 21494 | ||
| 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 | }; | ||
| 21501 | 21509 | ||
| 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 | } |
| 21505 | 21513 | ||
| ... | @@ -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; |
| 21508 | 21516 | ||
| 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 | }; |
| 21514 | 21522 | ||
| 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 | } |
| 21522 | 21536 | ||
| 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); |
| 21525 | 21539 | ||
| 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); |
| 21528 | 21542 | ||
| 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}); |
| 4945 | 4945 | ||
| 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); |
| 4949 | 4949 | ||
| 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 | } |
| 5368 | 5368 | ||
| 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}); |
| 5373 | 5374 | ||
| 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); |
| 5377 | 5383 | ||
| 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); |
| 6021 | 6021 | ||
| 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); |
| 6025 | 6025 | ||
| 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 | |||
| 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+1-1| ... | @@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo { | ... | @@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo { |
| 7 | // backend=llvm | 7 | // backend=llvm |
| 8 | // target=native | 8 | // target=native |
| 9 | // | 9 | // |
| 10 | // :3:28: error: expected struct type, found 'i32' | 10 | // :3:28: error: expected struct or union type, found 'i32' |