authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-15 00:43:26-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-15 00:43:26-04:00
log9eceba248511849ff77a90876424f9930bf7f9f9
treed6691a25a828b44cb4c17365fb8549d339046810
parenta2a5d3c2885cacf16d55d7943d10a81a5dc31b8a
parent2f92d1a0264b6827cb67a55726c4c9a082337508
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11128 from topolarity/comptime-memory-reinterp

stage2: Track parent type for `.elem_ptr`, `.field_ptr`, and `.*_payload_ptr`

9 files changed, 670 insertions(+), 326 deletions(-)

src/Module.zig+5-3
...@@ -852,7 +852,7 @@ pub const ErrorSet = struct {...@@ -852,7 +852,7 @@ pub const ErrorSet = struct {
852 }852 }
853};853};
854854
855pub const RequiresComptime = enum { no, yes, unknown, wip };855pub const PropertyBoolean = enum { no, yes, unknown, wip };
856856
857/// Represents the data that a struct declaration provides.857/// Represents the data that a struct declaration provides.
858pub const Struct = struct {858pub const Struct = struct {
...@@ -884,7 +884,7 @@ pub const Struct = struct {...@@ -884,7 +884,7 @@ pub const Struct = struct {
884 /// If false, resolving the fields is necessary to determine whether the type has only884 /// If false, resolving the fields is necessary to determine whether the type has only
885 /// one possible value.885 /// one possible value.
886 known_non_opv: bool,886 known_non_opv: bool,
887 requires_comptime: RequiresComptime = .unknown,887 requires_comptime: PropertyBoolean = .unknown,
888888
889 pub const Fields = std.StringArrayHashMapUnmanaged(Field);889 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
890890
...@@ -1089,6 +1089,8 @@ pub const EnumFull = struct {...@@ -1089,6 +1089,8 @@ pub const EnumFull = struct {
1089 namespace: Namespace,1089 namespace: Namespace,
1090 /// Offset from `owner_decl`, points to the enum decl AST node.1090 /// Offset from `owner_decl`, points to the enum decl AST node.
1091 node_offset: i32,1091 node_offset: i32,
1092 /// true if zig inferred this tag type, false if user specified it
1093 tag_ty_inferred: bool,
10921094
1093 pub const NameMap = std.StringArrayHashMapUnmanaged(void);1095 pub const NameMap = std.StringArrayHashMapUnmanaged(void);
1094 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);1096 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);
...@@ -1132,7 +1134,7 @@ pub const Union = struct {...@@ -1132,7 +1134,7 @@ pub const Union = struct {
1132 // which `have_layout` does not ensure.1134 // which `have_layout` does not ensure.
1133 fully_resolved,1135 fully_resolved,
1134 },1136 },
1135 requires_comptime: RequiresComptime = .unknown,1137 requires_comptime: PropertyBoolean = .unknown,
11361138
1137 pub const Field = struct {1139 pub const Field = struct {
1138 /// undefined until `status` is `have_field_types` or `have_layout`.1140 /// undefined until `status` is `have_field_types` or `have_layout`.
src/Sema.zig+261-175
...@@ -1615,6 +1615,9 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -1615,6 +1615,9 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
1615 try pointee_ty.copy(anon_decl.arena()),1615 try pointee_ty.copy(anon_decl.arena()),
1616 Value.undef,1616 Value.undef,
1617 );1617 );
1618 if (iac.data.alignment != 0) {
1619 try sema.resolveTypeLayout(block, src, pointee_ty);
1620 }
1618 const ptr_ty = try Type.ptr(sema.arena, target, .{1621 const ptr_ty = try Type.ptr(sema.arena, target, .{
1619 .pointee_type = pointee_ty,1622 .pointee_type = pointee_ty,
1620 .@"align" = iac.data.alignment,1623 .@"align" = iac.data.alignment,
...@@ -1884,7 +1887,8 @@ fn zirEnumDecl(...@@ -1884,7 +1887,8 @@ fn zirEnumDecl(
18841887
1885 enum_obj.* = .{1888 enum_obj.* = .{
1886 .owner_decl = new_decl,1889 .owner_decl = new_decl,
1887 .tag_ty = Type.initTag(.@"null"),1890 .tag_ty = Type.@"null",
1891 .tag_ty_inferred = true,
1888 .fields = .{},1892 .fields = .{},
1889 .values = .{},1893 .values = .{},
1890 .node_offset = src.node_offset,1894 .node_offset = src.node_offset,
...@@ -1907,6 +1911,7 @@ fn zirEnumDecl(...@@ -1907,6 +1911,7 @@ fn zirEnumDecl(
1907 // TODO better source location1911 // TODO better source location
1908 const ty = try sema.resolveType(block, src, tag_type_ref);1912 const ty = try sema.resolveType(block, src, tag_type_ref);
1909 enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);1913 enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
1914 enum_obj.tag_ty_inferred = false;
1910 }1915 }
1911 try new_decl.finalizeNewArena(&new_decl_arena);1916 try new_decl.finalizeNewArena(&new_decl_arena);
1912 return sema.analyzeDeclVal(block, src, new_decl);1917 return sema.analyzeDeclVal(block, src, new_decl);
...@@ -1956,16 +1961,16 @@ fn zirEnumDecl(...@@ -1956,16 +1961,16 @@ fn zirEnumDecl(
19561961
1957 try wip_captures.finalize();1962 try wip_captures.finalize();
19581963
1959 const tag_ty = blk: {1964 if (tag_type_ref != .none) {
1960 if (tag_type_ref != .none) {1965 // TODO better source location
1961 // TODO better source location1966 const ty = try sema.resolveType(block, src, tag_type_ref);
1962 const ty = try sema.resolveType(block, src, tag_type_ref);1967 enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
1963 break :blk try ty.copy(new_decl_arena_allocator);1968 enum_obj.tag_ty_inferred = false;
1964 }1969 } else {
1965 const bits = std.math.log2_int_ceil(usize, fields_len);1970 const bits = std.math.log2_int_ceil(usize, fields_len);
1966 break :blk try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits);1971 enum_obj.tag_ty = try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits);
1967 };1972 enum_obj.tag_ty_inferred = true;
1968 enum_obj.tag_ty = tag_ty;1973 }
1969 }1974 }
19701975
1971 try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);1976 try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
...@@ -2417,13 +2422,13 @@ fn zirAllocExtended(...@@ -2417,13 +2422,13 @@ fn zirAllocExtended(
2417 try sema.validateVarType(block, ty_src, var_ty, false);2422 try sema.validateVarType(block, ty_src, var_ty, false);
2418 }2423 }
2419 const target = sema.mod.getTarget();2424 const target = sema.mod.getTarget();
2425 try sema.requireRuntimeBlock(block, src);
2426 try sema.resolveTypeLayout(block, src, var_ty);
2420 const ptr_type = try Type.ptr(sema.arena, target, .{2427 const ptr_type = try Type.ptr(sema.arena, target, .{
2421 .pointee_type = var_ty,2428 .pointee_type = var_ty,
2422 .@"align" = alignment,2429 .@"align" = alignment,
2423 .@"addrspace" = target_util.defaultAddressSpace(target, .local),2430 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
2424 });2431 });
2425 try sema.requireRuntimeBlock(block, src);
2426 try sema.resolveTypeLayout(block, src, var_ty);
2427 return block.addTy(.alloc, ptr_type);2432 return block.addTy(.alloc, ptr_type);
2428 }2433 }
24292434
...@@ -5568,7 +5573,10 @@ fn analyzeOptionalPayloadPtr(...@@ -5568,7 +5573,10 @@ fn analyzeOptionalPayloadPtr(
5568 }5573 }
5569 return sema.addConstant(5574 return sema.addConstant(
5570 child_pointer,5575 child_pointer,
5571 try Value.Tag.opt_payload_ptr.create(sema.arena, ptr_val),5576 try Value.Tag.opt_payload_ptr.create(sema.arena, .{
5577 .container_ptr = ptr_val,
5578 .container_ty = optional_ptr_ty.childType(),
5579 }),
5572 );5580 );
5573 }5581 }
5574 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {5582 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
...@@ -5578,7 +5586,10 @@ fn analyzeOptionalPayloadPtr(...@@ -5578,7 +5586,10 @@ fn analyzeOptionalPayloadPtr(
5578 // The same Value represents the pointer to the optional and the payload.5586 // The same Value represents the pointer to the optional and the payload.
5579 return sema.addConstant(5587 return sema.addConstant(
5580 child_pointer,5588 child_pointer,
5581 try Value.Tag.opt_payload_ptr.create(sema.arena, ptr_val),5589 try Value.Tag.opt_payload_ptr.create(sema.arena, .{
5590 .container_ptr = ptr_val,
5591 .container_ty = optional_ptr_ty.childType(),
5592 }),
5582 );5593 );
5583 }5594 }
5584 }5595 }
...@@ -5733,7 +5744,10 @@ fn analyzeErrUnionPayloadPtr(...@@ -5733,7 +5744,10 @@ fn analyzeErrUnionPayloadPtr(
5733 }5744 }
5734 return sema.addConstant(5745 return sema.addConstant(
5735 operand_pointer_ty,5746 operand_pointer_ty,
5736 try Value.Tag.eu_payload_ptr.create(sema.arena, ptr_val),5747 try Value.Tag.eu_payload_ptr.create(sema.arena, .{
5748 .container_ptr = ptr_val,
5749 .container_ty = operand_ty.elemType(),
5750 }),
5737 );5751 );
5738 }5752 }
5739 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {5753 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
...@@ -5743,7 +5757,10 @@ fn analyzeErrUnionPayloadPtr(...@@ -5743,7 +5757,10 @@ fn analyzeErrUnionPayloadPtr(
57435757
5744 return sema.addConstant(5758 return sema.addConstant(
5745 operand_pointer_ty,5759 operand_pointer_ty,
5746 try Value.Tag.eu_payload_ptr.create(sema.arena, ptr_val),5760 try Value.Tag.eu_payload_ptr.create(sema.arena, .{
5761 .container_ptr = ptr_val,
5762 .container_ty = operand_ty.elemType(),
5763 }),
5747 );5764 );
5748 }5765 }
5749 }5766 }
...@@ -6652,6 +6669,7 @@ fn zirSwitchCapture(...@@ -6652,6 +6669,7 @@ fn zirSwitchCapture(
6652 field_ty_ptr,6669 field_ty_ptr,
6653 try Value.Tag.field_ptr.create(sema.arena, .{6670 try Value.Tag.field_ptr.create(sema.arena, .{
6654 .container_ptr = op_ptr_val,6671 .container_ptr = op_ptr_val,
6672 .container_ty = operand_ty,
6655 .field_index = field_index,6673 .field_index = field_index,
6656 }),6674 }),
6657 );6675 );
...@@ -9638,7 +9656,7 @@ fn analyzePtrArithmetic(...@@ -9638,7 +9656,7 @@ fn analyzePtrArithmetic(
9638 if (air_tag == .ptr_sub) {9656 if (air_tag == .ptr_sub) {
9639 return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});9657 return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
9640 }9658 }
9641 const new_ptr_val = try ptr_val.elemPtr(sema.arena, offset_int);9659 const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int);
9642 return sema.addConstant(new_ptr_ty, new_ptr_val);9660 return sema.addConstant(new_ptr_ty, new_ptr_val);
9643 } else break :rs offset_src;9661 } else break :rs offset_src;
9644 } else break :rs ptr_src;9662 } else break :rs ptr_src;
...@@ -12592,6 +12610,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12592,6 +12610,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12592 enum_obj.* = .{12610 enum_obj.* = .{
12593 .owner_decl = new_decl,12611 .owner_decl = new_decl,
12594 .tag_ty = Type.initTag(.@"null"),12612 .tag_ty = Type.initTag(.@"null"),
12613 .tag_ty_inferred = true,
12595 .fields = .{},12614 .fields = .{},
12596 .values = .{},12615 .values = .{},
12597 .node_offset = src.node_offset,12616 .node_offset = src.node_offset,
...@@ -15903,6 +15922,7 @@ fn finishFieldCallBind(...@@ -15903,6 +15922,7 @@ fn finishFieldCallBind(
15903 ptr_field_ty,15922 ptr_field_ty,
15904 try Value.Tag.field_ptr.create(arena, .{15923 try Value.Tag.field_ptr.create(arena, .{
15905 .container_ptr = struct_ptr_val,15924 .container_ptr = struct_ptr_val,
15925 .container_ty = ptr_ty.childType(),
15906 .field_index = field_index,15926 .field_index = field_index,
15907 }),15927 }),
15908 );15928 );
...@@ -16065,6 +16085,7 @@ fn structFieldPtrByIndex(...@@ -16065,6 +16085,7 @@ fn structFieldPtrByIndex(
16065 ptr_field_ty,16085 ptr_field_ty,
16066 try Value.Tag.field_ptr.create(sema.arena, .{16086 try Value.Tag.field_ptr.create(sema.arena, .{
16067 .container_ptr = struct_ptr_val,16087 .container_ptr = struct_ptr_val,
16088 .container_ty = struct_ptr_ty.childType(),
16068 .field_index = field_index,16089 .field_index = field_index,
16069 }),16090 }),
16070 );16091 );
...@@ -16241,6 +16262,7 @@ fn unionFieldPtr(...@@ -16241,6 +16262,7 @@ fn unionFieldPtr(
16241 ptr_field_ty,16262 ptr_field_ty,
16242 try Value.Tag.field_ptr.create(arena, .{16263 try Value.Tag.field_ptr.create(arena, .{
16243 .container_ptr = union_ptr_val,16264 .container_ptr = union_ptr_val,
16265 .container_ty = union_ty,
16244 .field_index = field_index,16266 .field_index = field_index,
16245 }),16267 }),
16246 );16268 );
...@@ -16333,7 +16355,7 @@ fn elemPtr(...@@ -16333,7 +16355,7 @@ fn elemPtr(
16333 const runtime_src = if (maybe_slice_val) |slice_val| rs: {16355 const runtime_src = if (maybe_slice_val) |slice_val| rs: {
16334 const index_val = maybe_index_val orelse break :rs elem_index_src;16356 const index_val = maybe_index_val orelse break :rs elem_index_src;
16335 const index = @intCast(usize, index_val.toUnsignedInt());16357 const index = @intCast(usize, index_val.toUnsignedInt());
16336 const elem_ptr = try slice_val.elemPtr(sema.arena, index);16358 const elem_ptr = try slice_val.elemPtr(array_ty, sema.arena, index);
16337 return sema.addConstant(result_ty, elem_ptr);16359 return sema.addConstant(result_ty, elem_ptr);
16338 } else array_ptr_src;16360 } else array_ptr_src;
1633916361
...@@ -16348,7 +16370,7 @@ fn elemPtr(...@@ -16348,7 +16370,7 @@ fn elemPtr(
16348 const ptr_val = maybe_ptr_val orelse break :rs array_ptr_src;16370 const ptr_val = maybe_ptr_val orelse break :rs array_ptr_src;
16349 const index_val = maybe_index_val orelse break :rs elem_index_src;16371 const index_val = maybe_index_val orelse break :rs elem_index_src;
16350 const index = @intCast(usize, index_val.toUnsignedInt());16372 const index = @intCast(usize, index_val.toUnsignedInt());
16351 const elem_ptr = try ptr_val.elemPtr(sema.arena, index);16373 const elem_ptr = try ptr_val.elemPtr(array_ty, sema.arena, index);
16352 return sema.addConstant(result_ty, elem_ptr);16374 return sema.addConstant(result_ty, elem_ptr);
16353 };16375 };
1635416376
...@@ -16473,6 +16495,7 @@ fn tupleFieldPtr(...@@ -16473,6 +16495,7 @@ fn tupleFieldPtr(
16473 ptr_field_ty,16495 ptr_field_ty,
16474 try Value.Tag.field_ptr.create(sema.arena, .{16496 try Value.Tag.field_ptr.create(sema.arena, .{
16475 .container_ptr = tuple_ptr_val,16497 .container_ptr = tuple_ptr_val,
16498 .container_ty = tuple_ty,
16476 .field_index = field_index,16499 .field_index = field_index,
16477 }),16500 }),
16478 );16501 );
...@@ -16563,7 +16586,7 @@ fn elemPtrArray(...@@ -16563,7 +16586,7 @@ fn elemPtrArray(
16563 const index_u64 = index_val.toUnsignedInt();16586 const index_u64 = index_val.toUnsignedInt();
16564 // @intCast here because it would have been impossible to construct a value that16587 // @intCast here because it would have been impossible to construct a value that
16565 // required a larger index.16588 // required a larger index.
16566 const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64));16589 const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, @intCast(usize, index_u64));
16567 return sema.addConstant(result_ty, elem_ptr);16590 return sema.addConstant(result_ty, elem_ptr);
16568 }16591 }
16569 }16592 }
...@@ -17569,6 +17592,14 @@ fn beginComptimePtrMutation(...@@ -17569,6 +17592,14 @@ fn beginComptimePtrMutation(
17569 src: LazySrcLoc,17592 src: LazySrcLoc,
17570 ptr_val: Value,17593 ptr_val: Value,
17571) CompileError!ComptimePtrMutationKit {17594) CompileError!ComptimePtrMutationKit {
17595
17596 // TODO: Update this to behave like `beginComptimePtrLoad` and properly check/use
17597 // `container_ty` and `array_ty`, instead of trusting that the parent decl type
17598 // matches the type used to derive the elem_ptr/field_ptr/etc.
17599 //
17600 // This is needed because the types will not match if the pointer we're mutating
17601 // through is reinterpreting comptime memory.
17602
17572 switch (ptr_val.tag()) {17603 switch (ptr_val.tag()) {
17573 .decl_ref_mut => {17604 .decl_ref_mut => {
17574 const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;17605 const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;
...@@ -17757,8 +17788,8 @@ fn beginComptimePtrMutation(...@@ -17757,8 +17788,8 @@ fn beginComptimePtrMutation(
17757 }17788 }
17758 },17789 },
17759 .eu_payload_ptr => {17790 .eu_payload_ptr => {
17760 const eu_ptr_val = ptr_val.castTag(.eu_payload_ptr).?.data;17791 const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
17761 var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr_val);17792 var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr);
17762 const payload_ty = parent.ty.errorUnionPayload();17793 const payload_ty = parent.ty.errorUnionPayload();
17763 switch (parent.val.tag()) {17794 switch (parent.val.tag()) {
17764 else => {17795 else => {
...@@ -17790,8 +17821,8 @@ fn beginComptimePtrMutation(...@@ -17790,8 +17821,8 @@ fn beginComptimePtrMutation(
17790 }17821 }
17791 },17822 },
17792 .opt_payload_ptr => {17823 .opt_payload_ptr => {
17793 const opt_ptr_val = ptr_val.castTag(.opt_payload_ptr).?.data;17824 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
17794 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr_val);17825 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr);
17795 const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);17826 const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
17796 switch (parent.val.tag()) {17827 switch (parent.val.tag()) {
17797 .undef, .null_value => {17828 .undef, .null_value => {
...@@ -17829,163 +17860,191 @@ fn beginComptimePtrMutation(...@@ -17829,163 +17860,191 @@ fn beginComptimePtrMutation(
17829 }17860 }
17830}17861}
1783117862
17832const ComptimePtrLoadKit = struct {17863const TypedValueAndOffset = struct {
17833 /// The Value of the Decl that owns this memory.17864 tv: TypedValue,
17834 root_val: Value,
17835 /// The Type of the Decl that owns this memory.
17836 root_ty: Type,
17837 /// Parent Value.
17838 val: Value,
17839 /// The Type of the parent Value.
17840 ty: Type,
17841 /// The starting byte offset of `val` from `root_val`.17865 /// The starting byte offset of `val` from `root_val`.
17842 /// If the type does not have a well-defined memory layout, this is null.17866 /// If the type does not have a well-defined memory layout, this is null.
17843 byte_offset: ?usize,17867 byte_offset: usize,
17844 /// Whether the `root_val` could be mutated by further17868};
17869
17870const ComptimePtrLoadKit = struct {
17871 /// The Value and Type corresponding to the pointee of the provided pointer.
17872 /// If a direct dereference is not possible, this is null.
17873 pointee: ?TypedValue,
17874 /// The largest parent Value containing `pointee` and having a well-defined memory layout.
17875 /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null).
17876 parent: ?TypedValueAndOffset,
17877 /// Whether the `pointee` could be mutated by further
17845 /// semantic analysis and a copy must be performed.17878 /// semantic analysis and a copy must be performed.
17846 is_mutable: bool,17879 is_mutable: bool,
17880 /// If the root decl could not be used as `parent`, this is the type that
17881 /// caused that by not having a well-defined layout
17882 ty_without_well_defined_layout: ?Type,
17847};17883};
1784817884
17849const ComptimePtrLoadError = CompileError || error{17885const ComptimePtrLoadError = CompileError || error{
17850 RuntimeLoad,17886 RuntimeLoad,
17851};17887};
1785217888
17889/// If `maybe_array_ty` is provided, it will be used to directly dereference an
17890/// .elem_ptr of type T to a value of [N]T, if necessary.
17853fn beginComptimePtrLoad(17891fn beginComptimePtrLoad(
17854 sema: *Sema,17892 sema: *Sema,
17855 block: *Block,17893 block: *Block,
17856 src: LazySrcLoc,17894 src: LazySrcLoc,
17857 ptr_val: Value,17895 ptr_val: Value,
17896 maybe_array_ty: ?Type,
17858) ComptimePtrLoadError!ComptimePtrLoadKit {17897) ComptimePtrLoadError!ComptimePtrLoadKit {
17859 const target = sema.mod.getTarget();17898 const target = sema.mod.getTarget();
17860 switch (ptr_val.tag()) {17899 var deref: ComptimePtrLoadKit = switch (ptr_val.tag()) {
17861 .decl_ref => {17900 .decl_ref,
17862 const decl = ptr_val.castTag(.decl_ref).?.data;17901 .decl_ref_mut,
17863 const decl_val = try decl.value();17902 => blk: {
17864 if (decl_val.tag() == .variable) return error.RuntimeLoad;17903 const decl = switch (ptr_val.tag()) {
17865 return ComptimePtrLoadKit{17904 .decl_ref => ptr_val.castTag(.decl_ref).?.data,
17866 .root_val = decl_val,17905 .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl,
17867 .root_ty = decl.ty,17906 else => unreachable,
17868 .val = decl_val,
17869 .ty = decl.ty,
17870 .byte_offset = 0,
17871 .is_mutable = false,
17872 };17907 };
17873 },17908 const is_mutable = ptr_val.tag() == .decl_ref_mut;
17874 .decl_ref_mut => {17909 const decl_tv = try decl.typedValue();
17875 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;17910 if (decl_tv.val.tag() == .variable) return error.RuntimeLoad;
17876 const decl_val = try decl.value();17911
17877 if (decl_val.tag() == .variable) return error.RuntimeLoad;17912 const layout_defined = decl.ty.hasWellDefinedLayout();
17878 return ComptimePtrLoadKit{17913 break :blk ComptimePtrLoadKit{
17879 .root_val = decl_val,17914 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
17880 .root_ty = decl.ty,17915 .pointee = decl_tv,
17881 .val = decl_val,17916 .is_mutable = is_mutable,
17882 .ty = decl.ty,17917 .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
17883 .byte_offset = 0,
17884 .is_mutable = true,
17885 };17918 };
17886 },17919 },
17887 .elem_ptr => {17920
17921 .elem_ptr => blk: {
17888 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;17922 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
17889 const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr);17923 const elem_ty = elem_ptr.elem_ty;
17890 switch (parent.ty.zigTypeTag()) {17924 var deref = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr, null);
17891 .Array, .Vector => {17925
17892 const check_len = parent.ty.arrayLenIncludingSentinel();17926 if (elem_ptr.index != 0) {
17893 if (elem_ptr.index >= check_len) {17927 if (elem_ty.hasWellDefinedLayout()) {
17894 // TODO have the parent include the decl so we can say "declared here"17928 if (deref.parent) |*parent| {
17895 return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{17929 // Update the byte offset (in-place)
17896 elem_ptr.index, check_len,17930 const elem_size = try sema.typeAbiSize(block, src, elem_ty);
17897 });17931 const offset = parent.byte_offset + elem_size * elem_ptr.index;
17898 }17932 parent.byte_offset = try sema.usizeCast(block, src, offset);
17899 const elem_ty = parent.ty.childType();
17900 const byte_offset: ?usize = bo: {
17901 if (try sema.typeRequiresComptime(block, src, elem_ty)) {
17902 break :bo null;
17903 } else {
17904 if (parent.byte_offset) |off| {
17905 try sema.resolveTypeLayout(block, src, elem_ty);
17906 const elem_size = elem_ty.abiSize(target);
17907 break :bo try sema.usizeCast(block, src, off + elem_size * elem_ptr.index);
17908 } else {
17909 break :bo null;
17910 }
17911 }
17912 };
17913 return ComptimePtrLoadKit{
17914 .root_val = parent.root_val,
17915 .root_ty = parent.root_ty,
17916 .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
17917 .ty = elem_ty,
17918 .byte_offset = byte_offset,
17919 .is_mutable = parent.is_mutable,
17920 };
17921 },
17922 else => {
17923 if (elem_ptr.index != 0) {
17924 // TODO have the parent include the decl so we can say "declared here"
17925 return sema.fail(block, src, "out of bounds comptime load of index {d}", .{
17926 elem_ptr.index,
17927 });
17928 }17933 }
17929 return ComptimePtrLoadKit{17934 } else {
17930 .root_val = parent.root_val,17935 deref.parent = null;
17931 .root_ty = parent.root_ty,17936 deref.ty_without_well_defined_layout = elem_ty;
17932 .val = parent.val,17937 }
17933 .ty = parent.ty,17938 }
17934 .byte_offset = parent.byte_offset,17939
17935 .is_mutable = parent.is_mutable,17940 // If we're loading an elem_ptr that was derived from a different type
17936 };17941 // than the true type of the underlying decl, we cannot deref directly
17937 },17942 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayLike()) x: {
17943 const deref_elem_ty = deref.pointee.?.ty.childType();
17944 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
17945 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
17946 } else false;
17947 if (!ty_matches) {
17948 deref.pointee = null;
17949 break :blk deref;
17938 }17950 }
17951
17952 var array_tv = deref.pointee.?;
17953 const check_len = array_tv.ty.arrayLenIncludingSentinel();
17954 if (elem_ptr.index >= check_len) {
17955 // TODO have the deref include the decl so we can say "declared here"
17956 return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{
17957 elem_ptr.index, check_len,
17958 });
17959 }
17960
17961 if (maybe_array_ty) |load_ty| {
17962 // It's possible that we're loading a [N]T, in which case we'd like to slice
17963 // the pointee array directly from our parent array.
17964 if (load_ty.isArrayLike() and load_ty.childType().eql(elem_ty)) {
17965 const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel());
17966 deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{
17967 .ty = try Type.array(sema.arena, N, null, elem_ty),
17968 .val = try array_tv.val.sliceArray(sema.arena, elem_ptr.index, elem_ptr.index + N),
17969 } else null;
17970 break :blk deref;
17971 }
17972 }
17973
17974 deref.pointee = .{
17975 .ty = elem_ty,
17976 .val = try array_tv.val.elemValue(sema.arena, elem_ptr.index),
17977 };
17978 break :blk deref;
17939 },17979 },
17940 .field_ptr => {17980
17981 .field_ptr => blk: {
17941 const field_ptr = ptr_val.castTag(.field_ptr).?.data;17982 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
17942 const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr);
17943 const field_index = @intCast(u32, field_ptr.field_index);17983 const field_index = @intCast(u32, field_ptr.field_index);
17944 const byte_offset: ?usize = bo: {17984 const field_ty = field_ptr.container_ty.structFieldType(field_index);
17945 if (try sema.typeRequiresComptime(block, src, parent.ty)) {17985 var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
17946 break :bo null;17986
17947 } else {17987 if (field_ptr.container_ty.hasWellDefinedLayout()) {
17948 if (parent.byte_offset) |off| {17988 if (deref.parent) |*parent| {
17949 try sema.resolveTypeLayout(block, src, parent.ty);17989 // Update the byte offset (in-place)
17950 const field_offset = parent.ty.structFieldOffset(field_index, target);17990 try sema.resolveTypeLayout(block, src, field_ptr.container_ty);
17951 break :bo try sema.usizeCast(block, src, off + field_offset);17991 const field_offset = field_ptr.container_ty.structFieldOffset(field_index, target);
17952 } else {17992 parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset);
17953 break :bo null;
17954 }
17955 }17993 }
17956 };17994 } else {
17957 return ComptimePtrLoadKit{17995 deref.parent = null;
17958 .root_val = parent.root_val,17996 deref.ty_without_well_defined_layout = field_ptr.container_ty;
17959 .root_ty = parent.root_ty,17997 }
17960 .val = try parent.val.fieldValue(sema.arena, field_index),17998
17961 .ty = parent.ty.structFieldType(field_index),17999 if (deref.pointee) |*tv| {
17962 .byte_offset = byte_offset,18000 const coerce_in_mem_ok =
17963 .is_mutable = parent.is_mutable,18001 (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
17964 };18002 (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok;
17965 },18003 if (coerce_in_mem_ok) {
17966 .eu_payload_ptr => {18004 deref.pointee = TypedValue{
17967 const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;18005 .ty = field_ty,
17968 const parent = try beginComptimePtrLoad(sema, block, src, err_union_ptr);18006 .val = try tv.val.fieldValue(sema.arena, field_index),
17969 return ComptimePtrLoadKit{18007 };
17970 .root_val = parent.root_val,18008 break :blk deref;
17971 .root_ty = parent.root_ty,18009 }
17972 .val = parent.val.castTag(.eu_payload).?.data,18010 }
17973 .ty = parent.ty.errorUnionPayload(),18011 deref.pointee = null;
17974 .byte_offset = null,18012 break :blk deref;
17975 .is_mutable = parent.is_mutable,
17976 };
17977 },18013 },
17978 .opt_payload_ptr => {18014
17979 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;18015 .opt_payload_ptr,
17980 const parent = try beginComptimePtrLoad(sema, block, src, opt_ptr);18016 .eu_payload_ptr,
17981 return ComptimePtrLoadKit{18017 => blk: {
17982 .root_val = parent.root_val,18018 const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data;
17983 .root_ty = parent.root_ty,18019 const payload_ty = switch (ptr_val.tag()) {
17984 .val = parent.val.castTag(.opt_payload).?.data,18020 .eu_payload_ptr => payload_ptr.container_ty.errorUnionPayload(),
17985 .ty = try parent.ty.optionalChildAlloc(sema.arena),18021 .opt_payload_ptr => try payload_ptr.container_ty.optionalChildAlloc(sema.arena),
17986 .byte_offset = null,18022 else => unreachable,
17987 .is_mutable = parent.is_mutable,
17988 };18023 };
18024 var deref = try beginComptimePtrLoad(sema, block, src, payload_ptr.container_ptr, payload_ptr.container_ty);
18025
18026 // eu_payload_ptr and opt_payload_ptr never have a well-defined layout
18027 if (deref.parent != null) {
18028 deref.parent = null;
18029 deref.ty_without_well_defined_layout = payload_ptr.container_ty;
18030 }
18031
18032 if (deref.pointee) |*tv| {
18033 const coerce_in_mem_ok =
18034 (try sema.coerceInMemoryAllowed(block, payload_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
18035 (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok;
18036 if (coerce_in_mem_ok) {
18037 const payload_val = switch (ptr_val.tag()) {
18038 .eu_payload_ptr => tv.val.castTag(.eu_payload).?.data,
18039 .opt_payload_ptr => tv.val.castTag(.opt_payload).?.data,
18040 else => unreachable,
18041 };
18042 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
18043 break :blk deref;
18044 }
18045 }
18046 deref.pointee = null;
18047 break :blk deref;
17989 },18048 },
1799018049
17991 .zero,18050 .zero,
...@@ -18000,7 +18059,14 @@ fn beginComptimePtrLoad(...@@ -18000,7 +18059,14 @@ fn beginComptimePtrLoad(
18000 => return error.RuntimeLoad,18059 => return error.RuntimeLoad,
1800118060
18002 else => unreachable,18061 else => unreachable,
18062 };
18063
18064 if (deref.pointee) |tv| {
18065 if (deref.parent == null and tv.ty.hasWellDefinedLayout()) {
18066 deref.parent = .{ .tv = tv, .byte_offset = 0 };
18067 }
18003 }18068 }
18069 return deref;
18004}18070}
1800518071
18006fn bitCast(18072fn bitCast(
...@@ -21085,39 +21151,53 @@ pub fn analyzeAddrspace(...@@ -21085,39 +21151,53 @@ pub fn analyzeAddrspace(
21085/// Asserts the value is a pointer and dereferences it.21151/// Asserts the value is a pointer and dereferences it.
21086/// Returns `null` if the pointer contents cannot be loaded at comptime.21152/// Returns `null` if the pointer contents cannot be loaded at comptime.
21087fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {21153fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
21088 const target = sema.mod.getTarget();
21089 const load_ty = ptr_ty.childType();21154 const load_ty = ptr_ty.childType();
21090 const parent = sema.beginComptimePtrLoad(block, src, ptr_val) catch |err| switch (err) {21155 const target = sema.mod.getTarget();
21156 const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
21091 error.RuntimeLoad => return null,21157 error.RuntimeLoad => return null,
21092 else => |e| return e,21158 else => |e| return e,
21093 };21159 };
21094 // We have a Value that lines up in virtual memory exactly with what we want to load.21160
21095 // If the Type is in-memory coercable to `load_ty`, it may be returned without modifications.21161 if (deref.pointee) |tv| {
21096 const coerce_in_mem_ok =21162 const coerce_in_mem_ok =
21097 (try sema.coerceInMemoryAllowed(block, load_ty, parent.ty, false, target, src, src)) == .ok or21163 (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or
21098 (try sema.coerceInMemoryAllowed(block, parent.ty, load_ty, false, target, src, src)) == .ok;21164 (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;
21099 if (coerce_in_mem_ok) {21165 if (coerce_in_mem_ok) {
21100 if (parent.is_mutable) {21166 // We have a Value that lines up in virtual memory exactly with what we want to load,
21101 // The decl whose value we are obtaining here may be overwritten with21167 // and it is in-memory coercible to load_ty. It may be returned without modifications.
21102 // a different value upon further semantic analysis, which would21168 if (deref.is_mutable) {
21103 // invalidate this memory. So we must copy here.21169 // The decl whose value we are obtaining here may be overwritten with
21104 return try parent.val.copy(sema.arena);21170 // a different value upon further semantic analysis, which would
21171 // invalidate this memory. So we must copy here.
21172 return try tv.val.copy(sema.arena);
21173 }
21174 return tv.val;
21105 }21175 }
21106 return parent.val;
21107 }21176 }
2110821177
21109 // The type is not in-memory coercable, so it must be bitcasted according21178 // The type is not in-memory coercible or the direct dereference failed, so it must
21110 // to the pointer type we are performing the load through.21179 // be bitcast according to the pointer type we are performing the load through.
21180 if (!load_ty.hasWellDefinedLayout())
21181 return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty});
21182
21183 const load_sz = try sema.typeAbiSize(block, src, load_ty);
2111121184
21112 // TODO emit a compile error if the types are not allowed to be bitcasted21185 // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
21186 if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(block, src, tv.ty))
21187 return try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0);
2111321188
21114 if (parent.ty.abiSize(target) >= load_ty.abiSize(target)) {21189 // If that fails, try to bit-cast from the largest parent value with a well-defined layout
21115 // The Type it is stored as in the compiler has an ABI size greater or equal to21190 if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(block, src, parent.tv.ty))
21116 // the ABI size of `load_ty`. We may perform the bitcast based on21191 return try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset);
21117 // `parent.val` alone (more efficient).21192
21118 return try sema.bitCastVal(block, src, parent.val, parent.ty, load_ty, 0);21193 if (deref.ty_without_well_defined_layout) |bad_ty| {
21194 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
21195 // is that some type we encountered when de-referencing does not have a well-defined layout.
21196 return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty});
21119 } else {21197 } else {
21120 return try sema.bitCastVal(block, src, parent.root_val, parent.root_ty, load_ty, parent.byte_offset.?);21198 // If all encountered types had well-defined layouts, the parent is the root decl and it just
21199 // wasn't big enough for the load.
21200 return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty, deref.parent.?.tv.ty });
21121 }21201 }
21122}21202}
2112321203
...@@ -21395,6 +21475,12 @@ pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)...@@ -21395,6 +21475,12 @@ pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
21395 return true;21475 return true;
21396}21476}
2139721477
21478fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 {
21479 try sema.resolveTypeLayout(block, src, ty);
21480 const target = sema.mod.getTarget();
21481 return ty.abiSize(target);
21482}
21483
21398fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 {21484fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 {
21399 try sema.resolveTypeLayout(block, src, ty);21485 try sema.resolveTypeLayout(block, src, ty);
21400 const target = sema.mod.getTarget();21486 const target = sema.mod.getTarget();
src/codegen/llvm.zig+64-99
...@@ -1518,26 +1518,8 @@ pub const DeclGen = struct {...@@ -1518,26 +1518,8 @@ pub const DeclGen = struct {
1518 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);1518 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
1519 return llvm_int.constIntToPtr(try dg.llvmType(tv.ty));1519 return llvm_int.constIntToPtr(try dg.llvmType(tv.ty));
1520 },1520 },
1521 .field_ptr, .opt_payload_ptr, .eu_payload_ptr => {1521 .field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => {
1522 const parent = try dg.lowerParentPtr(tv.val, tv.ty);1522 return dg.lowerParentPtr(tv.val, tv.ty.childType());
1523 return parent.llvm_ptr.constBitCast(try dg.llvmType(tv.ty));
1524 },
1525 .elem_ptr => {
1526 const elem_ptr = tv.val.castTag(.elem_ptr).?.data;
1527 const parent = try dg.lowerParentPtr(elem_ptr.array_ptr, tv.ty);
1528 const llvm_usize = try dg.llvmType(Type.usize);
1529 if (parent.llvm_ptr.typeOf().getElementType().getTypeKind() == .Array) {
1530 const indices: [2]*const llvm.Value = .{
1531 llvm_usize.constInt(0, .False),
1532 llvm_usize.constInt(elem_ptr.index, .False),
1533 };
1534 return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
1535 } else {
1536 const indices: [1]*const llvm.Value = .{
1537 llvm_usize.constInt(elem_ptr.index, .False),
1538 };
1539 return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
1540 }
1541 },1523 },
1542 .null_value, .zero => {1524 .null_value, .zero => {
1543 const llvm_type = try dg.llvmType(tv.ty);1525 const llvm_type = try dg.llvmType(tv.ty);
...@@ -2786,7 +2768,7 @@ pub const DeclGen = struct {...@@ -2786,7 +2768,7 @@ pub const DeclGen = struct {
2786 llvm_ptr: *const llvm.Value,2768 llvm_ptr: *const llvm.Value,
2787 };2769 };
27882770
2789 fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl) Error!ParentPtr {2771 fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl, ptr_child_ty: Type) Error!*const llvm.Value {
2790 decl.markAlive();2772 decl.markAlive();
2791 var ptr_ty_payload: Type.Payload.ElemType = .{2773 var ptr_ty_payload: Type.Payload.ElemType = .{
2792 .base = .{ .tag = .single_mut_pointer },2774 .base = .{ .tag = .single_mut_pointer },
...@@ -2794,123 +2776,107 @@ pub const DeclGen = struct {...@@ -2794,123 +2776,107 @@ pub const DeclGen = struct {
2794 };2776 };
2795 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);2777 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2796 const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl);2778 const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl);
2797 return ParentPtr{2779
2798 .llvm_ptr = llvm_ptr,2780 if (ptr_child_ty.eql(decl.ty)) {
2799 .ty = decl.ty,2781 return llvm_ptr;
2800 };2782 } else {
2783 return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0));
2784 }
2801 }2785 }
28022786
2803 fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, base_ty: Type) Error!ParentPtr {2787 fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, ptr_child_ty: Type) Error!*const llvm.Value {
2804 switch (ptr_val.tag()) {2788 var bitcast_needed: bool = undefined;
2789 const llvm_ptr = switch (ptr_val.tag()) {
2805 .decl_ref_mut => {2790 .decl_ref_mut => {
2806 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;2791 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
2807 return dg.lowerParentPtrDecl(ptr_val, decl);2792 return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
2808 },2793 },
2809 .decl_ref => {2794 .decl_ref => {
2810 const decl = ptr_val.castTag(.decl_ref).?.data;2795 const decl = ptr_val.castTag(.decl_ref).?.data;
2811 return dg.lowerParentPtrDecl(ptr_val, decl);2796 return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
2812 },2797 },
2813 .variable => {2798 .variable => {
2814 const decl = ptr_val.castTag(.variable).?.data.owner_decl;2799 const decl = ptr_val.castTag(.variable).?.data.owner_decl;
2815 return dg.lowerParentPtrDecl(ptr_val, decl);2800 return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
2816 },2801 },
2817 .int_i64 => {2802 .int_i64 => {
2818 const int = ptr_val.castTag(.int_i64).?.data;2803 const int = ptr_val.castTag(.int_i64).?.data;
2819 const llvm_usize = try dg.llvmType(Type.usize);2804 const llvm_usize = try dg.llvmType(Type.usize);
2820 const llvm_int = llvm_usize.constInt(@bitCast(u64, int), .False);2805 const llvm_int = llvm_usize.constInt(@bitCast(u64, int), .False);
2821 return ParentPtr{2806 return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0));
2822 .llvm_ptr = llvm_int.constIntToPtr(try dg.llvmType(base_ty)),
2823 .ty = base_ty,
2824 };
2825 },2807 },
2826 .int_u64 => {2808 .int_u64 => {
2827 const int = ptr_val.castTag(.int_u64).?.data;2809 const int = ptr_val.castTag(.int_u64).?.data;
2828 const llvm_usize = try dg.llvmType(Type.usize);2810 const llvm_usize = try dg.llvmType(Type.usize);
2829 const llvm_int = llvm_usize.constInt(int, .False);2811 const llvm_int = llvm_usize.constInt(int, .False);
2830 return ParentPtr{2812 return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0));
2831 .llvm_ptr = llvm_int.constIntToPtr(try dg.llvmType(base_ty)),
2832 .ty = base_ty,
2833 };
2834 },2813 },
2835 .field_ptr => {2814 .field_ptr => blk: {
2836 const field_ptr = ptr_val.castTag(.field_ptr).?.data;2815 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
2837 const parent = try dg.lowerParentPtr(field_ptr.container_ptr, base_ty);2816 const parent_llvm_ptr = try dg.lowerParentPtr(field_ptr.container_ptr, field_ptr.container_ty);
2817 const parent_ty = field_ptr.container_ty;
2818
2838 const field_index = @intCast(u32, field_ptr.field_index);2819 const field_index = @intCast(u32, field_ptr.field_index);
2839 const llvm_u32 = dg.context.intType(32);2820 const llvm_u32 = dg.context.intType(32);
2840 const target = dg.module.getTarget();2821 const target = dg.module.getTarget();
2841 switch (parent.ty.zigTypeTag()) {2822 switch (parent_ty.zigTypeTag()) {
2842 .Union => {2823 .Union => {
2843 const fields = parent.ty.unionFields();2824 bitcast_needed = true;
2844 const layout = parent.ty.unionGetLayout(target);2825
2845 const field_ty = fields.values()[field_index].ty;2826 const layout = parent_ty.unionGetLayout(target);
2846 if (layout.payload_size == 0) {2827 if (layout.payload_size == 0) {
2847 // In this case a pointer to the union and a pointer to any2828 // In this case a pointer to the union and a pointer to any
2848 // (void) payload is the same.2829 // (void) payload is the same.
2849 return ParentPtr{2830 break :blk parent_llvm_ptr;
2850 .llvm_ptr = parent.llvm_ptr,
2851 .ty = field_ty,
2852 };
2853 }
2854 if (layout.tag_size == 0) {
2855 const indices: [2]*const llvm.Value = .{
2856 llvm_u32.constInt(0, .False),
2857 llvm_u32.constInt(0, .False),
2858 };
2859 return ParentPtr{
2860 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2861 .ty = field_ty,
2862 };
2863 }2831 }
2864 const llvm_pl_index = @boolToInt(layout.tag_align >= layout.payload_align);2832 const llvm_pl_index = if (layout.tag_size == 0)
2833 0
2834 else
2835 @boolToInt(layout.tag_align >= layout.payload_align);
2865 const indices: [2]*const llvm.Value = .{2836 const indices: [2]*const llvm.Value = .{
2866 llvm_u32.constInt(0, .False),2837 llvm_u32.constInt(0, .False),
2867 llvm_u32.constInt(llvm_pl_index, .False),2838 llvm_u32.constInt(llvm_pl_index, .False),
2868 };2839 };
2869 return ParentPtr{2840 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
2870 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2871 .ty = field_ty,
2872 };
2873 },2841 },
2874 .Struct => {2842 .Struct => {
2843 const field_ty = parent_ty.structFieldType(field_index);
2844 bitcast_needed = !field_ty.eql(ptr_child_ty);
2845
2875 var ty_buf: Type.Payload.Pointer = undefined;2846 var ty_buf: Type.Payload.Pointer = undefined;
2876 const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?;2847 const llvm_field_index = llvmFieldIndex(parent_ty, field_index, target, &ty_buf).?;
2877 const indices: [2]*const llvm.Value = .{2848 const indices: [2]*const llvm.Value = .{
2878 llvm_u32.constInt(0, .False),2849 llvm_u32.constInt(0, .False),
2879 llvm_u32.constInt(llvm_field_index, .False),2850 llvm_u32.constInt(llvm_field_index, .False),
2880 };2851 };
2881 return ParentPtr{2852 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
2882 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2883 .ty = parent.ty.structFieldType(field_index),
2884 };
2885 },2853 },
2886 else => unreachable,2854 else => unreachable,
2887 }2855 }
2888 },2856 },
2889 .elem_ptr => {2857 .elem_ptr => blk: {
2890 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;2858 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
2891 const parent = try dg.lowerParentPtr(elem_ptr.array_ptr, base_ty);2859 const parent_llvm_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);
2860 bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty);
2861
2892 const llvm_usize = try dg.llvmType(Type.usize);2862 const llvm_usize = try dg.llvmType(Type.usize);
2893 const indices: [2]*const llvm.Value = .{2863 const indices: [1]*const llvm.Value = .{
2894 llvm_usize.constInt(0, .False),
2895 llvm_usize.constInt(elem_ptr.index, .False),2864 llvm_usize.constInt(elem_ptr.index, .False),
2896 };2865 };
2897 return ParentPtr{2866 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
2898 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2899 .ty = parent.ty.childType(),
2900 };
2901 },2867 },
2902 .opt_payload_ptr => {2868 .opt_payload_ptr => blk: {
2903 const opt_payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;2869 const opt_payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
2904 const parent = try dg.lowerParentPtr(opt_payload_ptr, base_ty);2870 const parent_llvm_ptr = try dg.lowerParentPtr(opt_payload_ptr.container_ptr, opt_payload_ptr.container_ty);
2905 var buf: Type.Payload.ElemType = undefined;2871 var buf: Type.Payload.ElemType = undefined;
2906 const payload_ty = parent.ty.optionalChild(&buf);2872
2907 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or parent.ty.isPtrLikeOptional()) {2873 const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf);
2874 bitcast_needed = !payload_ty.eql(ptr_child_ty);
2875
2876 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) {
2908 // In this case, we represent pointer to optional the same as pointer2877 // In this case, we represent pointer to optional the same as pointer
2909 // to the payload.2878 // to the payload.
2910 return ParentPtr{2879 break :blk parent_llvm_ptr;
2911 .llvm_ptr = parent.llvm_ptr,
2912 .ty = payload_ty,
2913 };
2914 }2880 }
29152881
2916 const llvm_u32 = dg.context.intType(32);2882 const llvm_u32 = dg.context.intType(32);
...@@ -2918,22 +2884,19 @@ pub const DeclGen = struct {...@@ -2918,22 +2884,19 @@ pub const DeclGen = struct {
2918 llvm_u32.constInt(0, .False),2884 llvm_u32.constInt(0, .False),
2919 llvm_u32.constInt(0, .False),2885 llvm_u32.constInt(0, .False),
2920 };2886 };
2921 return ParentPtr{2887 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
2922 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2923 .ty = payload_ty,
2924 };
2925 },2888 },
2926 .eu_payload_ptr => {2889 .eu_payload_ptr => blk: {
2927 const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;2890 const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
2928 const parent = try dg.lowerParentPtr(eu_payload_ptr, base_ty);2891 const parent_llvm_ptr = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, eu_payload_ptr.container_ty);
2929 const payload_ty = parent.ty.errorUnionPayload();2892
2893 const payload_ty = eu_payload_ptr.container_ty.errorUnionPayload();
2894 bitcast_needed = !payload_ty.eql(ptr_child_ty);
2895
2930 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {2896 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
2931 // In this case, we represent pointer to error union the same as pointer2897 // In this case, we represent pointer to error union the same as pointer
2932 // to the payload.2898 // to the payload.
2933 return ParentPtr{2899 break :blk parent_llvm_ptr;
2934 .llvm_ptr = parent.llvm_ptr,
2935 .ty = payload_ty,
2936 };
2937 }2900 }
29382901
2939 const llvm_u32 = dg.context.intType(32);2902 const llvm_u32 = dg.context.intType(32);
...@@ -2941,12 +2904,14 @@ pub const DeclGen = struct {...@@ -2941,12 +2904,14 @@ pub const DeclGen = struct {
2941 llvm_u32.constInt(0, .False),2904 llvm_u32.constInt(0, .False),
2942 llvm_u32.constInt(1, .False),2905 llvm_u32.constInt(1, .False),
2943 };2906 };
2944 return ParentPtr{2907 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
2945 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2946 .ty = payload_ty,
2947 };
2948 },2908 },
2949 else => unreachable,2909 else => unreachable,
2910 };
2911 if (bitcast_needed) {
2912 return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0));
2913 } else {
2914 return llvm_ptr;
2950 }2915 }
2951 }2916 }
29522917
src/type.zig+137-7
...@@ -2173,6 +2173,128 @@ pub const Type = extern union {...@@ -2173,6 +2173,128 @@ pub const Type = extern union {
2173 };2173 };
2174 }2174 }
21752175
2176 /// true if and only if the type has a well-defined memory layout
2177 /// readFrom/writeToMemory are supported only for types with a well-
2178 /// defined memory layout
2179 pub fn hasWellDefinedLayout(ty: Type) bool {
2180 return switch (ty.tag()) {
2181 .u1,
2182 .u8,
2183 .i8,
2184 .u16,
2185 .i16,
2186 .u32,
2187 .i32,
2188 .u64,
2189 .i64,
2190 .u128,
2191 .i128,
2192 .usize,
2193 .isize,
2194 .c_short,
2195 .c_ushort,
2196 .c_int,
2197 .c_uint,
2198 .c_long,
2199 .c_ulong,
2200 .c_longlong,
2201 .c_ulonglong,
2202 .c_longdouble,
2203 .f16,
2204 .f32,
2205 .f64,
2206 .f80,
2207 .f128,
2208 .bool,
2209 .void,
2210 .manyptr_u8,
2211 .manyptr_const_u8,
2212 .manyptr_const_u8_sentinel_0,
2213 .array_u8,
2214 .array_u8_sentinel_0,
2215 .int_signed,
2216 .int_unsigned,
2217 .pointer,
2218 .single_const_pointer,
2219 .single_mut_pointer,
2220 .many_const_pointer,
2221 .many_mut_pointer,
2222 .c_const_pointer,
2223 .c_mut_pointer,
2224 .single_const_pointer_to_comptime_int,
2225 .enum_numbered,
2226 .vector,
2227 .optional_single_mut_pointer,
2228 .optional_single_const_pointer,
2229 => true,
2230
2231 .anyopaque,
2232 .anyerror,
2233 .noreturn,
2234 .@"null",
2235 .@"anyframe",
2236 .@"undefined",
2237 .atomic_order,
2238 .atomic_rmw_op,
2239 .calling_convention,
2240 .address_space,
2241 .float_mode,
2242 .reduce_op,
2243 .call_options,
2244 .prefetch_options,
2245 .export_options,
2246 .extern_options,
2247 .error_set,
2248 .error_set_single,
2249 .error_set_inferred,
2250 .error_set_merged,
2251 .@"opaque",
2252 .generic_poison,
2253 .type,
2254 .comptime_int,
2255 .comptime_float,
2256 .enum_literal,
2257 .type_info,
2258 // These are function bodies, not function pointers.
2259 .fn_noreturn_no_args,
2260 .fn_void_no_args,
2261 .fn_naked_noreturn_no_args,
2262 .fn_ccc_void_no_args,
2263 .function,
2264 .const_slice_u8,
2265 .const_slice_u8_sentinel_0,
2266 .const_slice,
2267 .mut_slice,
2268 .enum_simple,
2269 .error_union,
2270 .anyerror_void_error_union,
2271 .anyframe_T,
2272 .tuple,
2273 .anon_struct,
2274 .empty_struct_literal,
2275 .empty_struct,
2276 => false,
2277
2278 .enum_full,
2279 .enum_nonexhaustive,
2280 => !ty.cast(Payload.EnumFull).?.data.tag_ty_inferred,
2281
2282 .var_args_param => unreachable,
2283 .inferred_alloc_mut => unreachable,
2284 .inferred_alloc_const => unreachable,
2285 .bound_fn => unreachable,
2286
2287 .array,
2288 .array_sentinel,
2289 => ty.childType().hasWellDefinedLayout(),
2290
2291 .optional => ty.isPtrLikeOptional(),
2292 .@"struct" => ty.castTag(.@"struct").?.data.layout != .Auto,
2293 .@"union" => ty.castTag(.@"union").?.data.layout != .Auto,
2294 .union_tagged => false,
2295 };
2296 }
2297
2176 pub fn hasRuntimeBits(ty: Type) bool {2298 pub fn hasRuntimeBits(ty: Type) bool {
2177 return hasRuntimeBitsAdvanced(ty, false);2299 return hasRuntimeBitsAdvanced(ty, false);
2178 }2300 }
...@@ -3156,13 +3278,12 @@ pub const Type = extern union {...@@ -3156,13 +3278,12 @@ pub const Type = extern union {
3156 => return true,3278 => return true,
31573279
3158 .optional => {3280 .optional => {
3159 var buf: Payload.ElemType = undefined;3281 const child_ty = self.castTag(.optional).?.data;
3160 const child_type = self.optionalChild(&buf);
3161 // optionals of zero sized types behave like bools, not pointers3282 // optionals of zero sized types behave like bools, not pointers
3162 if (!child_type.hasRuntimeBits()) return false;3283 if (!child_ty.hasRuntimeBits()) return false;
3163 if (child_type.zigTypeTag() != .Pointer) return false;3284 if (child_ty.zigTypeTag() != .Pointer) return false;
31643285
3165 const info = child_type.ptrInfo().data;3286 const info = child_ty.ptrInfo().data;
3166 switch (info.size) {3287 switch (info.size) {
3167 .Slice, .C => return false,3288 .Slice, .C => return false,
3168 .Many, .One => return !info.@"allowzero",3289 .Many, .One => return !info.@"allowzero",
...@@ -3263,6 +3384,7 @@ pub const Type = extern union {...@@ -3263,6 +3384,7 @@ pub const Type = extern union {
3263 /// For ?[*]T, returns T.3384 /// For ?[*]T, returns T.
3264 /// For *T, returns T.3385 /// For *T, returns T.
3265 /// For [*]T, returns T.3386 /// For [*]T, returns T.
3387 /// For [N]T, returns T.
3266 /// For []T, returns T.3388 /// For []T, returns T.
3267 pub fn elemType2(ty: Type) Type {3389 pub fn elemType2(ty: Type) Type {
3268 return switch (ty.tag()) {3390 return switch (ty.tag()) {
...@@ -4256,6 +4378,13 @@ pub const Type = extern union {...@@ -4256,6 +4378,13 @@ pub const Type = extern union {
4256 };4378 };
4257 }4379 }
42584380
4381 pub fn isArrayLike(ty: Type) bool {
4382 return switch (ty.zigTypeTag()) {
4383 .Array, .Vector => true,
4384 else => false,
4385 };
4386 }
4387
4259 pub fn isIndexable(ty: Type) bool {4388 pub fn isIndexable(ty: Type) bool {
4260 return switch (ty.zigTypeTag()) {4389 return switch (ty.zigTypeTag()) {
4261 .Array, .Vector => true,4390 .Array, .Vector => true,
...@@ -4642,7 +4771,7 @@ pub const Type = extern union {...@@ -4642,7 +4771,7 @@ pub const Type = extern union {
4642 return field_offset.offset;4771 return field_offset.offset;
4643 }4772 }
46444773
4645 return std.mem.alignForwardGeneric(u64, it.offset, it.big_align);4774 return std.mem.alignForwardGeneric(u64, it.offset, @maximum(it.big_align, 1));
4646 },4775 },
46474776
4648 .tuple, .anon_struct => {4777 .tuple, .anon_struct => {
...@@ -4665,7 +4794,7 @@ pub const Type = extern union {...@@ -4665,7 +4794,7 @@ pub const Type = extern union {
4665 if (i == index) return offset;4794 if (i == index) return offset;
4666 offset += field_ty.abiSize(target);4795 offset += field_ty.abiSize(target);
4667 }4796 }
4668 offset = std.mem.alignForwardGeneric(u64, offset, big_align);4797 offset = std.mem.alignForwardGeneric(u64, offset, @maximum(big_align, 1));
4669 return offset;4798 return offset;
4670 },4799 },
46714800
...@@ -5345,6 +5474,7 @@ pub const Type = extern union {...@@ -5345,6 +5474,7 @@ pub const Type = extern union {
5345 pub const @"type" = initTag(.type);5474 pub const @"type" = initTag(.type);
5346 pub const @"anyerror" = initTag(.anyerror);5475 pub const @"anyerror" = initTag(.anyerror);
5347 pub const @"anyopaque" = initTag(.anyopaque);5476 pub const @"anyopaque" = initTag(.anyopaque);
5477 pub const @"null" = initTag(.@"null");
53485478
5349 pub fn ptr(arena: Allocator, target: Target, data: Payload.Pointer.Data) !Type {5479 pub fn ptr(arena: Allocator, target: Target, data: Payload.Pointer.Data) !Type {
5350 var d = data;5480 var d = data;
src/value.zig+82-39
...@@ -268,12 +268,14 @@ pub const Value = extern union {...@@ -268,12 +268,14 @@ pub const Value = extern union {
268268
269 .repeated,269 .repeated,
270 .eu_payload,270 .eu_payload,
271 .eu_payload_ptr,
272 .opt_payload,271 .opt_payload,
273 .opt_payload_ptr,
274 .empty_array_sentinel,272 .empty_array_sentinel,
275 => Payload.SubValue,273 => Payload.SubValue,
276274
275 .eu_payload_ptr,
276 .opt_payload_ptr,
277 => Payload.PayloadPtr,
278
277 .bytes,279 .bytes,
278 .enum_literal,280 .enum_literal,
279 => Payload.Bytes,281 => Payload.Bytes,
...@@ -479,6 +481,20 @@ pub const Value = extern union {...@@ -479,6 +481,20 @@ pub const Value = extern union {
479 .variable => return self.copyPayloadShallow(arena, Payload.Variable),481 .variable => return self.copyPayloadShallow(arena, Payload.Variable),
480 .decl_ref => return self.copyPayloadShallow(arena, Payload.Decl),482 .decl_ref => return self.copyPayloadShallow(arena, Payload.Decl),
481 .decl_ref_mut => return self.copyPayloadShallow(arena, Payload.DeclRefMut),483 .decl_ref_mut => return self.copyPayloadShallow(arena, Payload.DeclRefMut),
484 .eu_payload_ptr,
485 .opt_payload_ptr,
486 => {
487 const payload = self.cast(Payload.PayloadPtr).?;
488 const new_payload = try arena.create(Payload.PayloadPtr);
489 new_payload.* = .{
490 .base = payload.base,
491 .data = .{
492 .container_ptr = try payload.data.container_ptr.copy(arena),
493 .container_ty = try payload.data.container_ty.copy(arena),
494 },
495 };
496 return Value{ .ptr_otherwise = &new_payload.base };
497 },
482 .elem_ptr => {498 .elem_ptr => {
483 const payload = self.castTag(.elem_ptr).?;499 const payload = self.castTag(.elem_ptr).?;
484 const new_payload = try arena.create(Payload.ElemPtr);500 const new_payload = try arena.create(Payload.ElemPtr);
...@@ -486,6 +502,7 @@ pub const Value = extern union {...@@ -486,6 +502,7 @@ pub const Value = extern union {
486 .base = payload.base,502 .base = payload.base,
487 .data = .{503 .data = .{
488 .array_ptr = try payload.data.array_ptr.copy(arena),504 .array_ptr = try payload.data.array_ptr.copy(arena),
505 .elem_ty = try payload.data.elem_ty.copy(arena),
489 .index = payload.data.index,506 .index = payload.data.index,
490 },507 },
491 };508 };
...@@ -498,6 +515,7 @@ pub const Value = extern union {...@@ -498,6 +515,7 @@ pub const Value = extern union {
498 .base = payload.base,515 .base = payload.base,
499 .data = .{516 .data = .{
500 .container_ptr = try payload.data.container_ptr.copy(arena),517 .container_ptr = try payload.data.container_ptr.copy(arena),
518 .container_ty = try payload.data.container_ty.copy(arena),
501 .field_index = payload.data.field_index,519 .field_index = payload.data.field_index,
502 },520 },
503 };521 };
...@@ -506,9 +524,7 @@ pub const Value = extern union {...@@ -506,9 +524,7 @@ pub const Value = extern union {
506 .bytes => return self.copyPayloadShallow(arena, Payload.Bytes),524 .bytes => return self.copyPayloadShallow(arena, Payload.Bytes),
507 .repeated,525 .repeated,
508 .eu_payload,526 .eu_payload,
509 .eu_payload_ptr,
510 .opt_payload,527 .opt_payload,
511 .opt_payload_ptr,
512 .empty_array_sentinel,528 .empty_array_sentinel,
513 => {529 => {
514 const payload = self.cast(Payload.SubValue).?;530 const payload = self.cast(Payload.SubValue).?;
...@@ -740,11 +756,11 @@ pub const Value = extern union {...@@ -740,11 +756,11 @@ pub const Value = extern union {
740 .inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"),756 .inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"),
741 .eu_payload_ptr => {757 .eu_payload_ptr => {
742 try out_stream.writeAll("(eu_payload_ptr)");758 try out_stream.writeAll("(eu_payload_ptr)");
743 val = val.castTag(.eu_payload_ptr).?.data;759 val = val.castTag(.eu_payload_ptr).?.data.container_ptr;
744 },760 },
745 .opt_payload_ptr => {761 .opt_payload_ptr => {
746 try out_stream.writeAll("(opt_payload_ptr)");762 try out_stream.writeAll("(opt_payload_ptr)");
747 val = val.castTag(.opt_payload_ptr).?.data;763 val = val.castTag(.opt_payload_ptr).?.data.container_ptr;
748 },764 },
749 .bound_fn => {765 .bound_fn => {
750 const bound_func = val.castTag(.bound_fn).?.data;766 const bound_func = val.castTag(.bound_fn).?.data;
...@@ -2162,8 +2178,8 @@ pub const Value = extern union {...@@ -2162,8 +2178,8 @@ pub const Value = extern union {
2162 .decl_ref_mut => true,2178 .decl_ref_mut => true,
2163 .elem_ptr => isComptimeMutablePtr(val.castTag(.elem_ptr).?.data.array_ptr),2179 .elem_ptr => isComptimeMutablePtr(val.castTag(.elem_ptr).?.data.array_ptr),
2164 .field_ptr => isComptimeMutablePtr(val.castTag(.field_ptr).?.data.container_ptr),2180 .field_ptr => isComptimeMutablePtr(val.castTag(.field_ptr).?.data.container_ptr),
2165 .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data),2181 .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data.container_ptr),
2166 .opt_payload_ptr => isComptimeMutablePtr(val.castTag(.opt_payload_ptr).?.data),2182 .opt_payload_ptr => isComptimeMutablePtr(val.castTag(.opt_payload_ptr).?.data.container_ptr),
21672183
2168 else => false,2184 else => false,
2169 };2185 };
...@@ -2174,9 +2190,9 @@ pub const Value = extern union {...@@ -2174,9 +2190,9 @@ pub const Value = extern union {
2174 switch (val.tag()) {2190 switch (val.tag()) {
2175 .repeated => return val.castTag(.repeated).?.data.canMutateComptimeVarState(),2191 .repeated => return val.castTag(.repeated).?.data.canMutateComptimeVarState(),
2176 .eu_payload => return val.castTag(.eu_payload).?.data.canMutateComptimeVarState(),2192 .eu_payload => return val.castTag(.eu_payload).?.data.canMutateComptimeVarState(),
2177 .eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.canMutateComptimeVarState(),2193 .eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.container_ptr.canMutateComptimeVarState(),
2178 .opt_payload => return val.castTag(.opt_payload).?.data.canMutateComptimeVarState(),2194 .opt_payload => return val.castTag(.opt_payload).?.data.canMutateComptimeVarState(),
2179 .opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.canMutateComptimeVarState(),2195 .opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.container_ptr.canMutateComptimeVarState(),
2180 .aggregate => {2196 .aggregate => {
2181 const fields = val.castTag(.aggregate).?.data;2197 const fields = val.castTag(.aggregate).?.data;
2182 for (fields) |field| {2198 for (fields) |field| {
...@@ -2239,12 +2255,12 @@ pub const Value = extern union {...@@ -2239,12 +2255,12 @@ pub const Value = extern union {
2239 .eu_payload_ptr => {2255 .eu_payload_ptr => {
2240 const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;2256 const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
2241 std.hash.autoHash(hasher, Value.Tag.eu_payload_ptr);2257 std.hash.autoHash(hasher, Value.Tag.eu_payload_ptr);
2242 hashPtr(err_union_ptr, hasher);2258 hashPtr(err_union_ptr.container_ptr, hasher);
2243 },2259 },
2244 .opt_payload_ptr => {2260 .opt_payload_ptr => {
2245 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;2261 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
2246 std.hash.autoHash(hasher, Value.Tag.opt_payload_ptr);2262 std.hash.autoHash(hasher, Value.Tag.opt_payload_ptr);
2247 hashPtr(opt_ptr, hasher);2263 hashPtr(opt_ptr.container_ptr, hasher);
2248 },2264 },
22492265
2250 .zero,2266 .zero,
...@@ -2272,12 +2288,14 @@ pub const Value = extern union {...@@ -2272,12 +2288,14 @@ pub const Value = extern union {
22722288
2273 .repeated,2289 .repeated,
2274 .eu_payload,2290 .eu_payload,
2275 .eu_payload_ptr,
2276 .opt_payload,2291 .opt_payload,
2277 .opt_payload_ptr,
2278 .empty_array_sentinel,2292 .empty_array_sentinel,
2279 => return markReferencedDeclsAlive(val.cast(Payload.SubValue).?.data),2293 => return markReferencedDeclsAlive(val.cast(Payload.SubValue).?.data),
22802294
2295 .eu_payload_ptr,
2296 .opt_payload_ptr,
2297 => return markReferencedDeclsAlive(val.cast(Payload.PayloadPtr).?.data.container_ptr),
2298
2281 .slice => {2299 .slice => {
2282 const slice = val.cast(Payload.Slice).?.data;2300 const slice = val.cast(Payload.Slice).?.data;
2283 markReferencedDeclsAlive(slice.ptr);2301 markReferencedDeclsAlive(slice.ptr);
...@@ -2394,6 +2412,29 @@ pub const Value = extern union {...@@ -2394,6 +2412,29 @@ pub const Value = extern union {
2394 }2412 }
2395 }2413 }
23962414
2415 // Asserts that the provided start/end are in-bounds.
2416 pub fn sliceArray(val: Value, arena: Allocator, start: usize, end: usize) error{OutOfMemory}!Value {
2417 return switch (val.tag()) {
2418 .empty_array_sentinel => if (start == 0 and end == 1) val else Value.initTag(.empty_array),
2419 .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]),
2420 .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]),
2421 .slice => sliceArray(val.castTag(.slice).?.data.ptr, arena, start, end),
2422
2423 .decl_ref => sliceArray(val.castTag(.decl_ref).?.data.val, arena, start, end),
2424 .decl_ref_mut => sliceArray(val.castTag(.decl_ref_mut).?.data.decl.val, arena, start, end),
2425 .elem_ptr => blk: {
2426 const elem_ptr = val.castTag(.elem_ptr).?.data;
2427 break :blk sliceArray(elem_ptr.array_ptr, arena, start + elem_ptr.index, end + elem_ptr.index);
2428 },
2429
2430 .repeated,
2431 .the_only_possible_value,
2432 => val,
2433
2434 else => unreachable,
2435 };
2436 }
2437
2397 pub fn fieldValue(val: Value, allocator: Allocator, index: usize) error{OutOfMemory}!Value {2438 pub fn fieldValue(val: Value, allocator: Allocator, index: usize) error{OutOfMemory}!Value {
2398 _ = allocator;2439 _ = allocator;
2399 switch (val.tag()) {2440 switch (val.tag()) {
...@@ -2422,36 +2463,28 @@ pub const Value = extern union {...@@ -2422,36 +2463,28 @@ pub const Value = extern union {
2422 }2463 }
24232464
2424 /// Returns a pointer to the element value at the index.2465 /// Returns a pointer to the element value at the index.
2425 pub fn elemPtr(val: Value, arena: Allocator, index: usize) Allocator.Error!Value {2466 pub fn elemPtr(val: Value, ty: Type, arena: Allocator, index: usize) Allocator.Error!Value {
2426 switch (val.tag()) {2467 const elem_ty = ty.elemType2();
2427 .elem_ptr => {2468 const ptr_val = switch (val.tag()) {
2428 const elem_ptr = val.castTag(.elem_ptr).?.data;2469 .slice => val.castTag(.slice).?.data.ptr,
2470 else => val,
2471 };
2472
2473 if (ptr_val.tag() == .elem_ptr) {
2474 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
2475 if (elem_ptr.elem_ty.eql(elem_ty)) {
2429 return Tag.elem_ptr.create(arena, .{2476 return Tag.elem_ptr.create(arena, .{
2430 .array_ptr = elem_ptr.array_ptr,2477 .array_ptr = elem_ptr.array_ptr,
2478 .elem_ty = elem_ptr.elem_ty,
2431 .index = elem_ptr.index + index,2479 .index = elem_ptr.index + index,
2432 });2480 });
2433 },2481 }
2434 .slice => {
2435 const ptr_val = val.castTag(.slice).?.data.ptr;
2436 switch (ptr_val.tag()) {
2437 .elem_ptr => {
2438 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
2439 return Tag.elem_ptr.create(arena, .{
2440 .array_ptr = elem_ptr.array_ptr,
2441 .index = elem_ptr.index + index,
2442 });
2443 },
2444 else => return Tag.elem_ptr.create(arena, .{
2445 .array_ptr = ptr_val,
2446 .index = index,
2447 }),
2448 }
2449 },
2450 else => return Tag.elem_ptr.create(arena, .{
2451 .array_ptr = val,
2452 .index = index,
2453 }),
2454 }2482 }
2483 return Tag.elem_ptr.create(arena, .{
2484 .array_ptr = ptr_val,
2485 .elem_ty = elem_ty,
2486 .index = index,
2487 });
2455 }2488 }
24562489
2457 pub fn isUndef(self: Value) bool {2490 pub fn isUndef(self: Value) bool {
...@@ -4144,12 +4177,21 @@ pub const Value = extern union {...@@ -4144,12 +4177,21 @@ pub const Value = extern union {
4144 };4177 };
4145 };4178 };
41464179
4180 pub const PayloadPtr = struct {
4181 base: Payload,
4182 data: struct {
4183 container_ptr: Value,
4184 container_ty: Type,
4185 },
4186 };
4187
4147 pub const ElemPtr = struct {4188 pub const ElemPtr = struct {
4148 pub const base_tag = Tag.elem_ptr;4189 pub const base_tag = Tag.elem_ptr;
41494190
4150 base: Payload = Payload{ .tag = base_tag },4191 base: Payload = Payload{ .tag = base_tag },
4151 data: struct {4192 data: struct {
4152 array_ptr: Value,4193 array_ptr: Value,
4194 elem_ty: Type,
4153 index: usize,4195 index: usize,
4154 },4196 },
4155 };4197 };
...@@ -4160,6 +4202,7 @@ pub const Value = extern union {...@@ -4160,6 +4202,7 @@ pub const Value = extern union {
4160 base: Payload = Payload{ .tag = base_tag },4202 base: Payload = Payload{ .tag = base_tag },
4161 data: struct {4203 data: struct {
4162 container_ptr: Value,4204 container_ptr: Value,
4205 container_ty: Type,
4163 field_index: usize,4206 field_index: usize,
4164 },4207 },
4165 };4208 };
test/behavior.zig+1
...@@ -62,6 +62,7 @@ test {...@@ -62,6 +62,7 @@ test {
62 _ = @import("behavior/bugs/11100.zig");62 _ = @import("behavior/bugs/11100.zig");
63 _ = @import("behavior/bugs/10970.zig");63 _ = @import("behavior/bugs/10970.zig");
64 _ = @import("behavior/bugs/11046.zig");64 _ = @import("behavior/bugs/11046.zig");
65 _ = @import("behavior/bugs/11139.zig");
65 _ = @import("behavior/bugs/11165.zig");66 _ = @import("behavior/bugs/11165.zig");
66 _ = @import("behavior/call.zig");67 _ = @import("behavior/call.zig");
67 _ = @import("behavior/cast.zig");68 _ = @import("behavior/cast.zig");
test/behavior/bugs/11139.zig created+25
...@@ -0,0 +1,25 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test "store array of array of structs at comptime" {
6 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9
10 try expect(storeArrayOfArrayOfStructs() == 15);
11 comptime try expect(storeArrayOfArrayOfStructs() == 15);
12}
13
14fn storeArrayOfArrayOfStructs() u8 {
15 const S = struct {
16 x: u8,
17 };
18
19 var cases = [_][1]S{
20 [_]S{
21 S{ .x = 15 },
22 },
23 };
24 return cases[0][0].x;
25}
test/behavior/cast.zig+4-2
...@@ -871,7 +871,7 @@ test "peer cast [N:x]T to [N]T" {...@@ -871,7 +871,7 @@ test "peer cast [N:x]T to [N]T" {
871}871}
872872
873test "peer cast *[N:x]T to *[N]T" {873test "peer cast *[N:x]T to *[N]T" {
874 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;874 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
875 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO875 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
876 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO876 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
877877
...@@ -887,7 +887,9 @@ test "peer cast *[N:x]T to *[N]T" {...@@ -887,7 +887,9 @@ test "peer cast *[N:x]T to *[N]T" {
887}887}
888888
889test "peer cast [*:x]T to [*]T" {889test "peer cast [*:x]T to [*]T" {
890 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO890 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
891 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
892 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
891893
892 const S = struct {894 const S = struct {
893 fn doTheTest() !void {895 fn doTheTest() !void {
test/behavior/ptrcast.zig+91-1
...@@ -21,8 +21,47 @@ fn testReinterpretBytesAsInteger() !void {...@@ -21,8 +21,47 @@ fn testReinterpretBytesAsInteger() !void {
21 try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);21 try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);
22}22}
2323
24test "reinterpret an array over multiple elements, with no well-defined layout" {
25 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
26 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
28 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
29
30 try testReinterpretWithOffsetAndNoWellDefinedLayout();
31 comptime try testReinterpretWithOffsetAndNoWellDefinedLayout();
32}
33
34fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
35 const bytes: ?[5]?u8 = [5]?u8{ 0x12, 0x34, 0x56, 0x78, 0x9a };
36 const ptr = &bytes.?[1];
37 const copy: [4]?u8 = @ptrCast(*const [4]?u8, ptr).*;
38 _ = copy;
39 //try expect(@ptrCast(*align(1)?u8, bytes[1..5]).* == );
40}
41
42test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
43 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
44 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
46
47 try testReinterpretStructWrappedBytesAsInteger();
48 comptime try testReinterpretStructWrappedBytesAsInteger();
49}
50
51fn testReinterpretStructWrappedBytesAsInteger() !void {
52 const S = struct { bytes: [5:0]u8 };
53 const obj = S{ .bytes = "\x12\x34\x56\x78\xab".* };
54 const expected = switch (native_endian) {
55 .Little => 0xab785634,
56 .Big => 0x345678ab,
57 };
58 try expect(@ptrCast(*align(1) const u32, obj.bytes[1..5]).* == expected);
59}
60
24test "reinterpret bytes of an array into an extern struct" {61test "reinterpret bytes of an array into an extern struct" {
25 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO62 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
63 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
64 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2665
27 try testReinterpretBytesAsExternStruct();66 try testReinterpretBytesAsExternStruct();
28 comptime try testReinterpretBytesAsExternStruct();67 comptime try testReinterpretBytesAsExternStruct();
...@@ -42,6 +81,57 @@ fn testReinterpretBytesAsExternStruct() !void {...@@ -42,6 +81,57 @@ fn testReinterpretBytesAsExternStruct() !void {
42 try expect(val == 5);81 try expect(val == 5);
43}82}
4483
84test "reinterpret bytes of an extern struct into another" {
85 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
86 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
88
89 try testReinterpretExternStructAsExternStruct();
90 comptime try testReinterpretExternStructAsExternStruct();
91}
92
93fn testReinterpretExternStructAsExternStruct() !void {
94 const S1 = extern struct {
95 a: u8,
96 b: u16,
97 c: u8,
98 };
99 comptime var bytes align(2) = S1{ .a = 0, .b = 0, .c = 5 };
100
101 const S2 = extern struct {
102 a: u32 align(2),
103 c: u8,
104 };
105 var ptr = @ptrCast(*const S2, &bytes);
106 var val = ptr.c;
107 try expect(val == 5);
108}
109
110test "lower reinterpreted comptime field ptr" {
111 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
112 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
113 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
114 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
115 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
116 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
117
118 // Test lowering a field ptr
119 comptime var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 };
120 const S = extern struct {
121 a: u32 align(2),
122 c: u8,
123 };
124 comptime var ptr = @ptrCast(*const S, &bytes);
125 var val = &ptr.c;
126 try expect(val.* == 5);
127
128 // Test lowering an elem ptr
129 comptime var src_value = S{ .a = 15, .c = 5 };
130 comptime var ptr2 = @ptrCast(*[@sizeOf(S)]u8, &src_value);
131 var val2 = &ptr2[4];
132 try expect(val2.* == 5);
133}
134
45test "reinterpret struct field at comptime" {135test "reinterpret struct field at comptime" {
46 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO136 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
47 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO137 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO