| 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 | 21482 | const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 21483 | 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 | 21486 | const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime-known"); |
| 21487 | 21487 | const field_ptr = try sema.resolveInst(extra.field_ptr); |
| 21488 | 21488 | const field_ptr_ty = sema.typeOf(field_ptr); |
| 21489 | 21489 | |
| 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)}); | |
| 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: { | |
| 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 | }; | |
| 21501 | 21509 | |
| 21502 | if (struct_ty.structFieldIsComptime(field_index)) { | |
| 21510 | if (parent_ty.zigTypeTag() == .Struct and parent_ty.structFieldIsComptime(field_index)) { | |
| 21503 | 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 | 21515 | const field_ptr_ty_info = field_ptr_ty.ptrInfo().data; |
| 21508 | 21516 | |
| 21509 | 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 | 21519 | .mutable = field_ptr_ty_info.mutable, |
| 21512 | 21520 | .@"addrspace" = field_ptr_ty_info.@"addrspace", |
| 21513 | 21521 | }; |
| 21514 | 21522 | |
| 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", .{}); | |
| 21517 | 21525 | } 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 | }; | |
| 21521 | 21535 | } |
| 21522 | 21536 | |
| 21523 | 21537 | const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); |
| 21524 | 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 | 21541 | const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); |
| 21528 | 21542 | |
| 21529 | 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 | 21554 | field_name, |
| 21541 | 21555 | field_index, |
| 21542 | 21556 | payload.data.field_index, |
| 21543 | struct_ty.fmt(sema.mod), | |
| 21557 | parent_ty.fmt(sema.mod), | |
| 21544 | 21558 | }, |
| 21545 | 21559 | ); |
| 21546 | 21560 | errdefer msg.destroy(sema.gpa); |
| 21547 | try sema.addDeclaredHereNote(msg, struct_ty); | |
| 21561 | try sema.addDeclaredHereNote(msg, parent_ty); | |
| 21548 | 21562 | break :msg msg; |
| 21549 | 21563 | }; |
| 21550 | 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 | 2973 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2974 | 2974 | const field_ptr = try self.resolveInst(extra.field_ptr); |
| 2975 | 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 | 2981 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*)); |
| 2977 | 2982 | switch (field_ptr) { |
| 2978 | 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 | 4944 | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr}); |
| 4945 | 4945 | |
| 4946 | 4946 | 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); | |
| 4949 | 4949 | |
| 4950 | 4950 | const result = if (field_offset != 0) result: { |
| 4951 | 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 | 5367 | } |
| 5368 | 5368 | |
| 5369 | 5369 | const struct_ptr_ty = f.air.typeOfIndex(inst); |
| 5370 | ||
| 5370 | 5371 | const field_ptr_ty = f.air.typeOf(extra.field_ptr); |
| 5371 | 5372 | const field_ptr_val = try f.resolveInst(extra.field_ptr); |
| 5372 | 5373 | try reap(f, inst, &.{extra.field_ptr}); |
| 5373 | 5374 | |
| 5374 | 5375 | const target = f.object.dg.module.getTarget(); |
| 5375 | 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 | 5382 | const field_offset = struct_ty.structFieldOffset(extra.field_index, target); |
| 5377 | 5383 | |
| 5378 | 5384 | var field_offset_pl = Value.Payload.I64{ |
src/codegen/llvm.zig+2-2| ... | ... | @@ -6020,8 +6020,8 @@ pub const FuncGen = struct { |
| 6020 | 6020 | const field_ptr = try self.resolveInst(extra.field_ptr); |
| 6021 | 6021 | |
| 6022 | 6022 | 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); | |
| 6025 | 6025 | |
| 6026 | 6026 | const res_ty = try self.dg.lowerType(self.air.getRefType(ty_pl.ty)); |
| 6027 | 6027 | if (field_offset == 0) { |
test/behavior/field_parent_ptr.zig+81| ... | ... | @@ -44,3 +44,84 @@ fn testParentFieldPtrFirst(a: *const bool) !void { |
| 44 | 44 | try expect(base == &foo); |
| 45 | 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 | 7 | // backend=llvm |
| 8 | 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' |