| author | |
| committer | |
| log | 9eceba248511849ff77a90876424f9930bf7f9f9 |
| tree | d6691a25a828b44cb4c17365fb8549d339046810 |
| parent | a2a5d3c2885cacf16d55d7943d10a81a5dc31b8a |
| parent | 2f92d1a0264b6827cb67a55726c4c9a082337508 |
| signature |
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 | 852 | } |
| 853 | 853 | }; |
| 854 | 854 | |
| 855 | pub const RequiresComptime = enum { no, yes, unknown, wip }; | |
| 855 | pub const PropertyBoolean = enum { no, yes, unknown, wip }; | |
| 856 | 856 | |
| 857 | 857 | /// Represents the data that a struct declaration provides. |
| 858 | 858 | pub const Struct = struct { |
| ... | ... | @@ -884,7 +884,7 @@ pub const Struct = struct { |
| 884 | 884 | /// If false, resolving the fields is necessary to determine whether the type has only |
| 885 | 885 | /// one possible value. |
| 886 | 886 | known_non_opv: bool, |
| 887 | requires_comptime: RequiresComptime = .unknown, | |
| 887 | requires_comptime: PropertyBoolean = .unknown, | |
| 888 | 888 | |
| 889 | 889 | pub const Fields = std.StringArrayHashMapUnmanaged(Field); |
| 890 | 890 | |
| ... | ... | @@ -1089,6 +1089,8 @@ pub const EnumFull = struct { |
| 1089 | 1089 | namespace: Namespace, |
| 1090 | 1090 | /// Offset from `owner_decl`, points to the enum decl AST node. |
| 1091 | 1091 | node_offset: i32, |
| 1092 | /// true if zig inferred this tag type, false if user specified it | |
| 1093 | tag_ty_inferred: bool, | |
| 1092 | 1094 | |
| 1093 | 1095 | pub const NameMap = std.StringArrayHashMapUnmanaged(void); |
| 1094 | 1096 | pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false); |
| ... | ... | @@ -1132,7 +1134,7 @@ pub const Union = struct { |
| 1132 | 1134 | // which `have_layout` does not ensure. |
| 1133 | 1135 | fully_resolved, |
| 1134 | 1136 | }, |
| 1135 | requires_comptime: RequiresComptime = .unknown, | |
| 1137 | requires_comptime: PropertyBoolean = .unknown, | |
| 1136 | 1138 | |
| 1137 | 1139 | pub const Field = struct { |
| 1138 | 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 | 1615 | try pointee_ty.copy(anon_decl.arena()), |
| 1616 | 1616 | Value.undef, |
| 1617 | 1617 | ); |
| 1618 | if (iac.data.alignment != 0) { | |
| 1619 | try sema.resolveTypeLayout(block, src, pointee_ty); | |
| 1620 | } | |
| 1618 | 1621 | const ptr_ty = try Type.ptr(sema.arena, target, .{ |
| 1619 | 1622 | .pointee_type = pointee_ty, |
| 1620 | 1623 | .@"align" = iac.data.alignment, |
| ... | ... | @@ -1884,7 +1887,8 @@ fn zirEnumDecl( |
| 1884 | 1887 | |
| 1885 | 1888 | enum_obj.* = .{ |
| 1886 | 1889 | .owner_decl = new_decl, |
| 1887 | .tag_ty = Type.initTag(.@"null"), | |
| 1890 | .tag_ty = Type.@"null", | |
| 1891 | .tag_ty_inferred = true, | |
| 1888 | 1892 | .fields = .{}, |
| 1889 | 1893 | .values = .{}, |
| 1890 | 1894 | .node_offset = src.node_offset, |
| ... | ... | @@ -1907,6 +1911,7 @@ fn zirEnumDecl( |
| 1907 | 1911 | // TODO better source location |
| 1908 | 1912 | const ty = try sema.resolveType(block, src, tag_type_ref); |
| 1909 | 1913 | enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator); |
| 1914 | enum_obj.tag_ty_inferred = false; | |
| 1910 | 1915 | } |
| 1911 | 1916 | try new_decl.finalizeNewArena(&new_decl_arena); |
| 1912 | 1917 | return sema.analyzeDeclVal(block, src, new_decl); |
| ... | ... | @@ -1956,16 +1961,16 @@ fn zirEnumDecl( |
| 1956 | 1961 | |
| 1957 | 1962 | try wip_captures.finalize(); |
| 1958 | 1963 | |
| 1959 | const tag_ty = blk: { | |
| 1960 | if (tag_type_ref != .none) { | |
| 1961 | // TODO better source location | |
| 1962 | const ty = try sema.resolveType(block, src, tag_type_ref); | |
| 1963 | break :blk try ty.copy(new_decl_arena_allocator); | |
| 1964 | } | |
| 1964 | if (tag_type_ref != .none) { | |
| 1965 | // TODO better source location | |
| 1966 | const ty = try sema.resolveType(block, src, tag_type_ref); | |
| 1967 | enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator); | |
| 1968 | enum_obj.tag_ty_inferred = false; | |
| 1969 | } else { | |
| 1965 | 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); | |
| 1967 | }; | |
| 1968 | enum_obj.tag_ty = tag_ty; | |
| 1971 | enum_obj.tag_ty = try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits); | |
| 1972 | enum_obj.tag_ty_inferred = true; | |
| 1973 | } | |
| 1969 | 1974 | } |
| 1970 | 1975 | |
| 1971 | 1976 | try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); |
| ... | ... | @@ -2417,13 +2422,13 @@ fn zirAllocExtended( |
| 2417 | 2422 | try sema.validateVarType(block, ty_src, var_ty, false); |
| 2418 | 2423 | } |
| 2419 | 2424 | const target = sema.mod.getTarget(); |
| 2425 | try sema.requireRuntimeBlock(block, src); | |
| 2426 | try sema.resolveTypeLayout(block, src, var_ty); | |
| 2420 | 2427 | const ptr_type = try Type.ptr(sema.arena, target, .{ |
| 2421 | 2428 | .pointee_type = var_ty, |
| 2422 | 2429 | .@"align" = alignment, |
| 2423 | 2430 | .@"addrspace" = target_util.defaultAddressSpace(target, .local), |
| 2424 | 2431 | }); |
| 2425 | try sema.requireRuntimeBlock(block, src); | |
| 2426 | try sema.resolveTypeLayout(block, src, var_ty); | |
| 2427 | 2432 | return block.addTy(.alloc, ptr_type); |
| 2428 | 2433 | } |
| 2429 | 2434 | |
| ... | ... | @@ -5568,7 +5573,10 @@ fn analyzeOptionalPayloadPtr( |
| 5568 | 5573 | } |
| 5569 | 5574 | return sema.addConstant( |
| 5570 | 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 | 5582 | if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| { |
| ... | ... | @@ -5578,7 +5586,10 @@ fn analyzeOptionalPayloadPtr( |
| 5578 | 5586 | // The same Value represents the pointer to the optional and the payload. |
| 5579 | 5587 | return sema.addConstant( |
| 5580 | 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 | 5744 | } |
| 5734 | 5745 | return sema.addConstant( |
| 5735 | 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 | 5753 | if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| { |
| ... | ... | @@ -5743,7 +5757,10 @@ fn analyzeErrUnionPayloadPtr( |
| 5743 | 5757 | |
| 5744 | 5758 | return sema.addConstant( |
| 5745 | 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 | 6669 | field_ty_ptr, |
| 6653 | 6670 | try Value.Tag.field_ptr.create(sema.arena, .{ |
| 6654 | 6671 | .container_ptr = op_ptr_val, |
| 6672 | .container_ty = operand_ty, | |
| 6655 | 6673 | .field_index = field_index, |
| 6656 | 6674 | }), |
| 6657 | 6675 | ); |
| ... | ... | @@ -9638,7 +9656,7 @@ fn analyzePtrArithmetic( |
| 9638 | 9656 | if (air_tag == .ptr_sub) { |
| 9639 | 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 | 9660 | return sema.addConstant(new_ptr_ty, new_ptr_val); |
| 9643 | 9661 | } else break :rs offset_src; |
| 9644 | 9662 | } else break :rs ptr_src; |
| ... | ... | @@ -12592,6 +12610,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 12592 | 12610 | enum_obj.* = .{ |
| 12593 | 12611 | .owner_decl = new_decl, |
| 12594 | 12612 | .tag_ty = Type.initTag(.@"null"), |
| 12613 | .tag_ty_inferred = true, | |
| 12595 | 12614 | .fields = .{}, |
| 12596 | 12615 | .values = .{}, |
| 12597 | 12616 | .node_offset = src.node_offset, |
| ... | ... | @@ -15903,6 +15922,7 @@ fn finishFieldCallBind( |
| 15903 | 15922 | ptr_field_ty, |
| 15904 | 15923 | try Value.Tag.field_ptr.create(arena, .{ |
| 15905 | 15924 | .container_ptr = struct_ptr_val, |
| 15925 | .container_ty = ptr_ty.childType(), | |
| 15906 | 15926 | .field_index = field_index, |
| 15907 | 15927 | }), |
| 15908 | 15928 | ); |
| ... | ... | @@ -16065,6 +16085,7 @@ fn structFieldPtrByIndex( |
| 16065 | 16085 | ptr_field_ty, |
| 16066 | 16086 | try Value.Tag.field_ptr.create(sema.arena, .{ |
| 16067 | 16087 | .container_ptr = struct_ptr_val, |
| 16088 | .container_ty = struct_ptr_ty.childType(), | |
| 16068 | 16089 | .field_index = field_index, |
| 16069 | 16090 | }), |
| 16070 | 16091 | ); |
| ... | ... | @@ -16241,6 +16262,7 @@ fn unionFieldPtr( |
| 16241 | 16262 | ptr_field_ty, |
| 16242 | 16263 | try Value.Tag.field_ptr.create(arena, .{ |
| 16243 | 16264 | .container_ptr = union_ptr_val, |
| 16265 | .container_ty = union_ty, | |
| 16244 | 16266 | .field_index = field_index, |
| 16245 | 16267 | }), |
| 16246 | 16268 | ); |
| ... | ... | @@ -16333,7 +16355,7 @@ fn elemPtr( |
| 16333 | 16355 | const runtime_src = if (maybe_slice_val) |slice_val| rs: { |
| 16334 | 16356 | const index_val = maybe_index_val orelse break :rs elem_index_src; |
| 16335 | 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 | 16359 | return sema.addConstant(result_ty, elem_ptr); |
| 16338 | 16360 | } else array_ptr_src; |
| 16339 | 16361 | |
| ... | ... | @@ -16348,7 +16370,7 @@ fn elemPtr( |
| 16348 | 16370 | const ptr_val = maybe_ptr_val orelse break :rs array_ptr_src; |
| 16349 | 16371 | const index_val = maybe_index_val orelse break :rs elem_index_src; |
| 16350 | 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 | 16374 | return sema.addConstant(result_ty, elem_ptr); |
| 16353 | 16375 | }; |
| 16354 | 16376 | |
| ... | ... | @@ -16473,6 +16495,7 @@ fn tupleFieldPtr( |
| 16473 | 16495 | ptr_field_ty, |
| 16474 | 16496 | try Value.Tag.field_ptr.create(sema.arena, .{ |
| 16475 | 16497 | .container_ptr = tuple_ptr_val, |
| 16498 | .container_ty = tuple_ty, | |
| 16476 | 16499 | .field_index = field_index, |
| 16477 | 16500 | }), |
| 16478 | 16501 | ); |
| ... | ... | @@ -16563,7 +16586,7 @@ fn elemPtrArray( |
| 16563 | 16586 | const index_u64 = index_val.toUnsignedInt(); |
| 16564 | 16587 | // @intCast here because it would have been impossible to construct a value that |
| 16565 | 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 | 16590 | return sema.addConstant(result_ty, elem_ptr); |
| 16568 | 16591 | } |
| 16569 | 16592 | } |
| ... | ... | @@ -17569,6 +17592,14 @@ fn beginComptimePtrMutation( |
| 17569 | 17592 | src: LazySrcLoc, |
| 17570 | 17593 | ptr_val: Value, |
| 17571 | 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 | 17603 | switch (ptr_val.tag()) { |
| 17573 | 17604 | .decl_ref_mut => { |
| 17574 | 17605 | const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data; |
| ... | ... | @@ -17757,8 +17788,8 @@ fn beginComptimePtrMutation( |
| 17757 | 17788 | } |
| 17758 | 17789 | }, |
| 17759 | 17790 | .eu_payload_ptr => { |
| 17760 | const eu_ptr_val = ptr_val.castTag(.eu_payload_ptr).?.data; | |
| 17761 | var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr_val); | |
| 17791 | const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; | |
| 17792 | var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr); | |
| 17762 | 17793 | const payload_ty = parent.ty.errorUnionPayload(); |
| 17763 | 17794 | switch (parent.val.tag()) { |
| 17764 | 17795 | else => { |
| ... | ... | @@ -17790,8 +17821,8 @@ fn beginComptimePtrMutation( |
| 17790 | 17821 | } |
| 17791 | 17822 | }, |
| 17792 | 17823 | .opt_payload_ptr => { |
| 17793 | const opt_ptr_val = ptr_val.castTag(.opt_payload_ptr).?.data; | |
| 17794 | var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr_val); | |
| 17824 | const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; | |
| 17825 | var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr); | |
| 17795 | 17826 | const payload_ty = try parent.ty.optionalChildAlloc(sema.arena); |
| 17796 | 17827 | switch (parent.val.tag()) { |
| 17797 | 17828 | .undef, .null_value => { |
| ... | ... | @@ -17829,163 +17860,191 @@ fn beginComptimePtrMutation( |
| 17829 | 17860 | } |
| 17830 | 17861 | } |
| 17831 | 17862 | |
| 17832 | const ComptimePtrLoadKit = struct { | |
| 17833 | /// The Value of the Decl that owns this memory. | |
| 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, | |
| 17863 | const TypedValueAndOffset = struct { | |
| 17864 | tv: TypedValue, | |
| 17841 | 17865 | /// The starting byte offset of `val` from `root_val`. |
| 17842 | 17866 | /// If the type does not have a well-defined memory layout, this is null. |
| 17843 | byte_offset: ?usize, | |
| 17844 | /// Whether the `root_val` could be mutated by further | |
| 17867 | byte_offset: usize, | |
| 17868 | }; | |
| 17869 | ||
| 17870 | const 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 | 17878 | /// semantic analysis and a copy must be performed. |
| 17846 | 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 | }; |
| 17848 | 17884 | |
| 17849 | 17885 | const ComptimePtrLoadError = CompileError || error{ |
| 17850 | 17886 | RuntimeLoad, |
| 17851 | 17887 | }; |
| 17852 | 17888 | |
| 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. | |
| 17853 | 17891 | fn beginComptimePtrLoad( |
| 17854 | 17892 | sema: *Sema, |
| 17855 | 17893 | block: *Block, |
| 17856 | 17894 | src: LazySrcLoc, |
| 17857 | 17895 | ptr_val: Value, |
| 17896 | maybe_array_ty: ?Type, | |
| 17858 | 17897 | ) ComptimePtrLoadError!ComptimePtrLoadKit { |
| 17859 | 17898 | const target = sema.mod.getTarget(); |
| 17860 | switch (ptr_val.tag()) { | |
| 17861 | .decl_ref => { | |
| 17862 | const decl = ptr_val.castTag(.decl_ref).?.data; | |
| 17863 | const decl_val = try decl.value(); | |
| 17864 | if (decl_val.tag() == .variable) return error.RuntimeLoad; | |
| 17865 | return ComptimePtrLoadKit{ | |
| 17866 | .root_val = decl_val, | |
| 17867 | .root_ty = decl.ty, | |
| 17868 | .val = decl_val, | |
| 17869 | .ty = decl.ty, | |
| 17870 | .byte_offset = 0, | |
| 17871 | .is_mutable = false, | |
| 17899 | var deref: ComptimePtrLoadKit = switch (ptr_val.tag()) { | |
| 17900 | .decl_ref, | |
| 17901 | .decl_ref_mut, | |
| 17902 | => blk: { | |
| 17903 | const decl = switch (ptr_val.tag()) { | |
| 17904 | .decl_ref => ptr_val.castTag(.decl_ref).?.data, | |
| 17905 | .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl, | |
| 17906 | else => unreachable, | |
| 17872 | 17907 | }; |
| 17873 | }, | |
| 17874 | .decl_ref_mut => { | |
| 17875 | const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl; | |
| 17876 | const decl_val = try decl.value(); | |
| 17877 | if (decl_val.tag() == .variable) return error.RuntimeLoad; | |
| 17878 | return ComptimePtrLoadKit{ | |
| 17879 | .root_val = decl_val, | |
| 17880 | .root_ty = decl.ty, | |
| 17881 | .val = decl_val, | |
| 17882 | .ty = decl.ty, | |
| 17883 | .byte_offset = 0, | |
| 17884 | .is_mutable = true, | |
| 17908 | const is_mutable = ptr_val.tag() == .decl_ref_mut; | |
| 17909 | const decl_tv = try decl.typedValue(); | |
| 17910 | if (decl_tv.val.tag() == .variable) return error.RuntimeLoad; | |
| 17911 | ||
| 17912 | const layout_defined = decl.ty.hasWellDefinedLayout(); | |
| 17913 | break :blk ComptimePtrLoadKit{ | |
| 17914 | .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, | |
| 17915 | .pointee = decl_tv, | |
| 17916 | .is_mutable = is_mutable, | |
| 17917 | .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, | |
| 17885 | 17918 | }; |
| 17886 | 17919 | }, |
| 17887 | .elem_ptr => { | |
| 17920 | ||
| 17921 | .elem_ptr => blk: { | |
| 17888 | 17922 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; |
| 17889 | const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr); | |
| 17890 | switch (parent.ty.zigTypeTag()) { | |
| 17891 | .Array, .Vector => { | |
| 17892 | const check_len = parent.ty.arrayLenIncludingSentinel(); | |
| 17893 | if (elem_ptr.index >= check_len) { | |
| 17894 | // TODO have the parent include the decl so we can say "declared here" | |
| 17895 | return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{ | |
| 17896 | elem_ptr.index, check_len, | |
| 17897 | }); | |
| 17898 | } | |
| 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 | }); | |
| 17923 | const elem_ty = elem_ptr.elem_ty; | |
| 17924 | var deref = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr, null); | |
| 17925 | ||
| 17926 | if (elem_ptr.index != 0) { | |
| 17927 | if (elem_ty.hasWellDefinedLayout()) { | |
| 17928 | if (deref.parent) |*parent| { | |
| 17929 | // Update the byte offset (in-place) | |
| 17930 | const elem_size = try sema.typeAbiSize(block, src, elem_ty); | |
| 17931 | const offset = parent.byte_offset + elem_size * elem_ptr.index; | |
| 17932 | parent.byte_offset = try sema.usizeCast(block, src, offset); | |
| 17928 | 17933 | } |
| 17929 | return ComptimePtrLoadKit{ | |
| 17930 | .root_val = parent.root_val, | |
| 17931 | .root_ty = parent.root_ty, | |
| 17932 | .val = parent.val, | |
| 17933 | .ty = parent.ty, | |
| 17934 | .byte_offset = parent.byte_offset, | |
| 17935 | .is_mutable = parent.is_mutable, | |
| 17936 | }; | |
| 17937 | }, | |
| 17934 | } else { | |
| 17935 | deref.parent = null; | |
| 17936 | deref.ty_without_well_defined_layout = elem_ty; | |
| 17937 | } | |
| 17938 | } | |
| 17939 | ||
| 17940 | // If we're loading an elem_ptr that was derived from a different type | |
| 17941 | // than the true type of the underlying decl, we cannot deref directly | |
| 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 | 17982 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; |
| 17942 | const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr); | |
| 17943 | 17983 | const field_index = @intCast(u32, field_ptr.field_index); |
| 17944 | const byte_offset: ?usize = bo: { | |
| 17945 | if (try sema.typeRequiresComptime(block, src, parent.ty)) { | |
| 17946 | break :bo null; | |
| 17947 | } else { | |
| 17948 | if (parent.byte_offset) |off| { | |
| 17949 | try sema.resolveTypeLayout(block, src, parent.ty); | |
| 17950 | const field_offset = parent.ty.structFieldOffset(field_index, target); | |
| 17951 | break :bo try sema.usizeCast(block, src, off + field_offset); | |
| 17952 | } else { | |
| 17953 | break :bo null; | |
| 17954 | } | |
| 17984 | const field_ty = field_ptr.container_ty.structFieldType(field_index); | |
| 17985 | var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty); | |
| 17986 | ||
| 17987 | if (field_ptr.container_ty.hasWellDefinedLayout()) { | |
| 17988 | if (deref.parent) |*parent| { | |
| 17989 | // Update the byte offset (in-place) | |
| 17990 | try sema.resolveTypeLayout(block, src, field_ptr.container_ty); | |
| 17991 | const field_offset = field_ptr.container_ty.structFieldOffset(field_index, target); | |
| 17992 | parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset); | |
| 17955 | 17993 | } |
| 17956 | }; | |
| 17957 | return ComptimePtrLoadKit{ | |
| 17958 | .root_val = parent.root_val, | |
| 17959 | .root_ty = parent.root_ty, | |
| 17960 | .val = try parent.val.fieldValue(sema.arena, field_index), | |
| 17961 | .ty = parent.ty.structFieldType(field_index), | |
| 17962 | .byte_offset = byte_offset, | |
| 17963 | .is_mutable = parent.is_mutable, | |
| 17964 | }; | |
| 17965 | }, | |
| 17966 | .eu_payload_ptr => { | |
| 17967 | const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; | |
| 17968 | const parent = try beginComptimePtrLoad(sema, block, src, err_union_ptr); | |
| 17969 | return ComptimePtrLoadKit{ | |
| 17970 | .root_val = parent.root_val, | |
| 17971 | .root_ty = parent.root_ty, | |
| 17972 | .val = parent.val.castTag(.eu_payload).?.data, | |
| 17973 | .ty = parent.ty.errorUnionPayload(), | |
| 17974 | .byte_offset = null, | |
| 17975 | .is_mutable = parent.is_mutable, | |
| 17976 | }; | |
| 17994 | } else { | |
| 17995 | deref.parent = null; | |
| 17996 | deref.ty_without_well_defined_layout = field_ptr.container_ty; | |
| 17997 | } | |
| 17998 | ||
| 17999 | if (deref.pointee) |*tv| { | |
| 18000 | const coerce_in_mem_ok = | |
| 18001 | (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or | |
| 18002 | (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok; | |
| 18003 | if (coerce_in_mem_ok) { | |
| 18004 | deref.pointee = TypedValue{ | |
| 18005 | .ty = field_ty, | |
| 18006 | .val = try tv.val.fieldValue(sema.arena, field_index), | |
| 18007 | }; | |
| 18008 | break :blk deref; | |
| 18009 | } | |
| 18010 | } | |
| 18011 | deref.pointee = null; | |
| 18012 | break :blk deref; | |
| 17977 | 18013 | }, |
| 17978 | .opt_payload_ptr => { | |
| 17979 | const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; | |
| 17980 | const parent = try beginComptimePtrLoad(sema, block, src, opt_ptr); | |
| 17981 | return ComptimePtrLoadKit{ | |
| 17982 | .root_val = parent.root_val, | |
| 17983 | .root_ty = parent.root_ty, | |
| 17984 | .val = parent.val.castTag(.opt_payload).?.data, | |
| 17985 | .ty = try parent.ty.optionalChildAlloc(sema.arena), | |
| 17986 | .byte_offset = null, | |
| 17987 | .is_mutable = parent.is_mutable, | |
| 18014 | ||
| 18015 | .opt_payload_ptr, | |
| 18016 | .eu_payload_ptr, | |
| 18017 | => blk: { | |
| 18018 | const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data; | |
| 18019 | const payload_ty = switch (ptr_val.tag()) { | |
| 18020 | .eu_payload_ptr => payload_ptr.container_ty.errorUnionPayload(), | |
| 18021 | .opt_payload_ptr => try payload_ptr.container_ty.optionalChildAlloc(sema.arena), | |
| 18022 | else => unreachable, | |
| 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 | }, |
| 17990 | 18049 | |
| 17991 | 18050 | .zero, |
| ... | ... | @@ -18000,7 +18059,14 @@ fn beginComptimePtrLoad( |
| 18000 | 18059 | => return error.RuntimeLoad, |
| 18001 | 18060 | |
| 18002 | 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 | } |
| 18005 | 18071 | |
| 18006 | 18072 | fn bitCast( |
| ... | ... | @@ -21085,39 +21151,53 @@ pub fn analyzeAddrspace( |
| 21085 | 21151 | /// Asserts the value is a pointer and dereferences it. |
| 21086 | 21152 | /// Returns `null` if the pointer contents cannot be loaded at comptime. |
| 21087 | 21153 | fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { |
| 21088 | const target = sema.mod.getTarget(); | |
| 21089 | 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 | 21157 | error.RuntimeLoad => return null, |
| 21092 | 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. | |
| 21095 | // If the Type is in-memory coercable to `load_ty`, it may be returned without modifications. | |
| 21096 | const coerce_in_mem_ok = | |
| 21097 | (try sema.coerceInMemoryAllowed(block, load_ty, parent.ty, false, target, src, src)) == .ok or | |
| 21098 | (try sema.coerceInMemoryAllowed(block, parent.ty, load_ty, false, target, src, src)) == .ok; | |
| 21099 | if (coerce_in_mem_ok) { | |
| 21100 | if (parent.is_mutable) { | |
| 21101 | // The decl whose value we are obtaining here may be overwritten with | |
| 21102 | // a different value upon further semantic analysis, which would | |
| 21103 | // invalidate this memory. So we must copy here. | |
| 21104 | return try parent.val.copy(sema.arena); | |
| 21160 | ||
| 21161 | if (deref.pointee) |tv| { | |
| 21162 | const coerce_in_mem_ok = | |
| 21163 | (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or | |
| 21164 | (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok; | |
| 21165 | if (coerce_in_mem_ok) { | |
| 21166 | // We have a Value that lines up in virtual memory exactly with what we want to load, | |
| 21167 | // and it is in-memory coercible to load_ty. It may be returned without modifications. | |
| 21168 | if (deref.is_mutable) { | |
| 21169 | // The decl whose value we are obtaining here may be overwritten with | |
| 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 | } |
| 21108 | 21177 | |
| 21109 | // The type is not in-memory coercable, so it must be bitcasted according | |
| 21110 | // to the pointer type we are performing the load through. | |
| 21178 | // The type is not in-memory coercible or the direct dereference failed, so it must | |
| 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); | |
| 21111 | 21184 | |
| 21112 | // TODO emit a compile error if the types are not allowed to be bitcasted | |
| 21185 | // 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); | |
| 21113 | 21188 | |
| 21114 | if (parent.ty.abiSize(target) >= load_ty.abiSize(target)) { | |
| 21115 | // The Type it is stored as in the compiler has an ABI size greater or equal to | |
| 21116 | // the ABI size of `load_ty`. We may perform the bitcast based on | |
| 21117 | // `parent.val` alone (more efficient). | |
| 21118 | return try sema.bitCastVal(block, src, parent.val, parent.ty, load_ty, 0); | |
| 21189 | // If that fails, try to bit-cast from the largest parent value with a well-defined layout | |
| 21190 | if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(block, src, parent.tv.ty)) | |
| 21191 | return try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset); | |
| 21192 | ||
| 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 | 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 | } |
| 21123 | 21203 | |
| ... | ... | @@ -21395,6 +21475,12 @@ pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) |
| 21395 | 21475 | return true; |
| 21396 | 21476 | } |
| 21397 | 21477 | |
| 21478 | fn 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 | ||
| 21398 | 21484 | fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 { |
| 21399 | 21485 | try sema.resolveTypeLayout(block, src, ty); |
| 21400 | 21486 | const target = sema.mod.getTarget(); |
src/codegen/llvm.zig+64-99| ... | ... | @@ -1518,26 +1518,8 @@ pub const DeclGen = struct { |
| 1518 | 1518 | const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False); |
| 1519 | 1519 | return llvm_int.constIntToPtr(try dg.llvmType(tv.ty)); |
| 1520 | 1520 | }, |
| 1521 | .field_ptr, .opt_payload_ptr, .eu_payload_ptr => { | |
| 1522 | const parent = try dg.lowerParentPtr(tv.val, tv.ty); | |
| 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 | } | |
| 1521 | .field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => { | |
| 1522 | return dg.lowerParentPtr(tv.val, tv.ty.childType()); | |
| 1541 | 1523 | }, |
| 1542 | 1524 | .null_value, .zero => { |
| 1543 | 1525 | const llvm_type = try dg.llvmType(tv.ty); |
| ... | ... | @@ -2786,7 +2768,7 @@ pub const DeclGen = struct { |
| 2786 | 2768 | llvm_ptr: *const llvm.Value, |
| 2787 | 2769 | }; |
| 2788 | 2770 | |
| 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 | 2772 | decl.markAlive(); |
| 2791 | 2773 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 2792 | 2774 | .base = .{ .tag = .single_mut_pointer }, |
| ... | ... | @@ -2794,123 +2776,107 @@ pub const DeclGen = struct { |
| 2794 | 2776 | }; |
| 2795 | 2777 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| 2796 | 2778 | const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl); |
| 2797 | return ParentPtr{ | |
| 2798 | .llvm_ptr = llvm_ptr, | |
| 2799 | .ty = decl.ty, | |
| 2800 | }; | |
| 2779 | ||
| 2780 | if (ptr_child_ty.eql(decl.ty)) { | |
| 2781 | return llvm_ptr; | |
| 2782 | } else { | |
| 2783 | return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0)); | |
| 2784 | } | |
| 2801 | 2785 | } |
| 2802 | 2786 | |
| 2803 | fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, base_ty: Type) Error!ParentPtr { | |
| 2804 | switch (ptr_val.tag()) { | |
| 2787 | fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, ptr_child_ty: Type) Error!*const llvm.Value { | |
| 2788 | var bitcast_needed: bool = undefined; | |
| 2789 | const llvm_ptr = switch (ptr_val.tag()) { | |
| 2805 | 2790 | .decl_ref_mut => { |
| 2806 | 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 | 2794 | .decl_ref => { |
| 2810 | 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 | 2798 | .variable => { |
| 2814 | 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 | 2802 | .int_i64 => { |
| 2818 | 2803 | const int = ptr_val.castTag(.int_i64).?.data; |
| 2819 | 2804 | const llvm_usize = try dg.llvmType(Type.usize); |
| 2820 | 2805 | const llvm_int = llvm_usize.constInt(@bitCast(u64, int), .False); |
| 2821 | return ParentPtr{ | |
| 2822 | .llvm_ptr = llvm_int.constIntToPtr(try dg.llvmType(base_ty)), | |
| 2823 | .ty = base_ty, | |
| 2824 | }; | |
| 2806 | return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0)); | |
| 2825 | 2807 | }, |
| 2826 | 2808 | .int_u64 => { |
| 2827 | 2809 | const int = ptr_val.castTag(.int_u64).?.data; |
| 2828 | 2810 | const llvm_usize = try dg.llvmType(Type.usize); |
| 2829 | 2811 | const llvm_int = llvm_usize.constInt(int, .False); |
| 2830 | return ParentPtr{ | |
| 2831 | .llvm_ptr = llvm_int.constIntToPtr(try dg.llvmType(base_ty)), | |
| 2832 | .ty = base_ty, | |
| 2833 | }; | |
| 2812 | return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0)); | |
| 2834 | 2813 | }, |
| 2835 | .field_ptr => { | |
| 2814 | .field_ptr => blk: { | |
| 2836 | 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 | 2819 | const field_index = @intCast(u32, field_ptr.field_index); |
| 2839 | 2820 | const llvm_u32 = dg.context.intType(32); |
| 2840 | 2821 | const target = dg.module.getTarget(); |
| 2841 | switch (parent.ty.zigTypeTag()) { | |
| 2822 | switch (parent_ty.zigTypeTag()) { | |
| 2842 | 2823 | .Union => { |
| 2843 | const fields = parent.ty.unionFields(); | |
| 2844 | const layout = parent.ty.unionGetLayout(target); | |
| 2845 | const field_ty = fields.values()[field_index].ty; | |
| 2824 | bitcast_needed = true; | |
| 2825 | ||
| 2826 | const layout = parent_ty.unionGetLayout(target); | |
| 2846 | 2827 | if (layout.payload_size == 0) { |
| 2847 | 2828 | // In this case a pointer to the union and a pointer to any |
| 2848 | 2829 | // (void) payload is the same. |
| 2849 | return ParentPtr{ | |
| 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 | }; | |
| 2830 | break :blk parent_llvm_ptr; | |
| 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 | 2836 | const indices: [2]*const llvm.Value = .{ |
| 2866 | 2837 | llvm_u32.constInt(0, .False), |
| 2867 | 2838 | llvm_u32.constInt(llvm_pl_index, .False), |
| 2868 | 2839 | }; |
| 2869 | return ParentPtr{ | |
| 2870 | .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), | |
| 2871 | .ty = field_ty, | |
| 2872 | }; | |
| 2840 | break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len); | |
| 2873 | 2841 | }, |
| 2874 | 2842 | .Struct => { |
| 2843 | const field_ty = parent_ty.structFieldType(field_index); | |
| 2844 | bitcast_needed = !field_ty.eql(ptr_child_ty); | |
| 2845 | ||
| 2875 | 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 | 2848 | const indices: [2]*const llvm.Value = .{ |
| 2878 | 2849 | llvm_u32.constInt(0, .False), |
| 2879 | 2850 | llvm_u32.constInt(llvm_field_index, .False), |
| 2880 | 2851 | }; |
| 2881 | return ParentPtr{ | |
| 2882 | .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), | |
| 2883 | .ty = parent.ty.structFieldType(field_index), | |
| 2884 | }; | |
| 2852 | break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len); | |
| 2885 | 2853 | }, |
| 2886 | 2854 | else => unreachable, |
| 2887 | 2855 | } |
| 2888 | 2856 | }, |
| 2889 | .elem_ptr => { | |
| 2857 | .elem_ptr => blk: { | |
| 2890 | 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 | 2862 | const llvm_usize = try dg.llvmType(Type.usize); |
| 2893 | const indices: [2]*const llvm.Value = .{ | |
| 2894 | llvm_usize.constInt(0, .False), | |
| 2863 | const indices: [1]*const llvm.Value = .{ | |
| 2895 | 2864 | llvm_usize.constInt(elem_ptr.index, .False), |
| 2896 | 2865 | }; |
| 2897 | return ParentPtr{ | |
| 2898 | .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), | |
| 2899 | .ty = parent.ty.childType(), | |
| 2900 | }; | |
| 2866 | break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len); | |
| 2901 | 2867 | }, |
| 2902 | .opt_payload_ptr => { | |
| 2868 | .opt_payload_ptr => blk: { | |
| 2903 | 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 | 2871 | var buf: Type.Payload.ElemType = undefined; |
| 2906 | const payload_ty = parent.ty.optionalChild(&buf); | |
| 2907 | if (!payload_ty.hasRuntimeBitsIgnoreComptime() or parent.ty.isPtrLikeOptional()) { | |
| 2872 | ||
| 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 | 2877 | // In this case, we represent pointer to optional the same as pointer |
| 2909 | 2878 | // to the payload. |
| 2910 | return ParentPtr{ | |
| 2911 | .llvm_ptr = parent.llvm_ptr, | |
| 2912 | .ty = payload_ty, | |
| 2913 | }; | |
| 2879 | break :blk parent_llvm_ptr; | |
| 2914 | 2880 | } |
| 2915 | 2881 | |
| 2916 | 2882 | const llvm_u32 = dg.context.intType(32); |
| ... | ... | @@ -2918,22 +2884,19 @@ pub const DeclGen = struct { |
| 2918 | 2884 | llvm_u32.constInt(0, .False), |
| 2919 | 2885 | llvm_u32.constInt(0, .False), |
| 2920 | 2886 | }; |
| 2921 | return ParentPtr{ | |
| 2922 | .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), | |
| 2923 | .ty = payload_ty, | |
| 2924 | }; | |
| 2887 | break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len); | |
| 2925 | 2888 | }, |
| 2926 | .eu_payload_ptr => { | |
| 2889 | .eu_payload_ptr => blk: { | |
| 2927 | 2890 | const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; |
| 2928 | const parent = try dg.lowerParentPtr(eu_payload_ptr, base_ty); | |
| 2929 | const payload_ty = parent.ty.errorUnionPayload(); | |
| 2891 | const parent_llvm_ptr = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, eu_payload_ptr.container_ty); | |
| 2892 | ||
| 2893 | const payload_ty = eu_payload_ptr.container_ty.errorUnionPayload(); | |
| 2894 | bitcast_needed = !payload_ty.eql(ptr_child_ty); | |
| 2895 | ||
| 2930 | 2896 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 2931 | 2897 | // In this case, we represent pointer to error union the same as pointer |
| 2932 | 2898 | // to the payload. |
| 2933 | return ParentPtr{ | |
| 2934 | .llvm_ptr = parent.llvm_ptr, | |
| 2935 | .ty = payload_ty, | |
| 2936 | }; | |
| 2899 | break :blk parent_llvm_ptr; | |
| 2937 | 2900 | } |
| 2938 | 2901 | |
| 2939 | 2902 | const llvm_u32 = dg.context.intType(32); |
| ... | ... | @@ -2941,12 +2904,14 @@ pub const DeclGen = struct { |
| 2941 | 2904 | llvm_u32.constInt(0, .False), |
| 2942 | 2905 | llvm_u32.constInt(1, .False), |
| 2943 | 2906 | }; |
| 2944 | return ParentPtr{ | |
| 2945 | .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), | |
| 2946 | .ty = payload_ty, | |
| 2947 | }; | |
| 2907 | break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len); | |
| 2948 | 2908 | }, |
| 2949 | 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 | } |
| 2952 | 2917 |
src/type.zig+137-7| ... | ... | @@ -2173,6 +2173,128 @@ pub const Type = extern union { |
| 2173 | 2173 | }; |
| 2174 | 2174 | } |
| 2175 | 2175 | |
| 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 | 2298 | pub fn hasRuntimeBits(ty: Type) bool { |
| 2177 | 2299 | return hasRuntimeBitsAdvanced(ty, false); |
| 2178 | 2300 | } |
| ... | ... | @@ -3156,13 +3278,12 @@ pub const Type = extern union { |
| 3156 | 3278 | => return true, |
| 3157 | 3279 | |
| 3158 | 3280 | .optional => { |
| 3159 | var buf: Payload.ElemType = undefined; | |
| 3160 | const child_type = self.optionalChild(&buf); | |
| 3281 | const child_ty = self.castTag(.optional).?.data; | |
| 3161 | 3282 | // optionals of zero sized types behave like bools, not pointers |
| 3162 | if (!child_type.hasRuntimeBits()) return false; | |
| 3163 | if (child_type.zigTypeTag() != .Pointer) return false; | |
| 3283 | if (!child_ty.hasRuntimeBits()) return false; | |
| 3284 | if (child_ty.zigTypeTag() != .Pointer) return false; | |
| 3164 | 3285 | |
| 3165 | const info = child_type.ptrInfo().data; | |
| 3286 | const info = child_ty.ptrInfo().data; | |
| 3166 | 3287 | switch (info.size) { |
| 3167 | 3288 | .Slice, .C => return false, |
| 3168 | 3289 | .Many, .One => return !info.@"allowzero", |
| ... | ... | @@ -3263,6 +3384,7 @@ pub const Type = extern union { |
| 3263 | 3384 | /// For ?[*]T, returns T. |
| 3264 | 3385 | /// For *T, returns T. |
| 3265 | 3386 | /// For [*]T, returns T. |
| 3387 | /// For [N]T, returns T. | |
| 3266 | 3388 | /// For []T, returns T. |
| 3267 | 3389 | pub fn elemType2(ty: Type) Type { |
| 3268 | 3390 | return switch (ty.tag()) { |
| ... | ... | @@ -4256,6 +4378,13 @@ pub const Type = extern union { |
| 4256 | 4378 | }; |
| 4257 | 4379 | } |
| 4258 | 4380 | |
| 4381 | pub fn isArrayLike(ty: Type) bool { | |
| 4382 | return switch (ty.zigTypeTag()) { | |
| 4383 | .Array, .Vector => true, | |
| 4384 | else => false, | |
| 4385 | }; | |
| 4386 | } | |
| 4387 | ||
| 4259 | 4388 | pub fn isIndexable(ty: Type) bool { |
| 4260 | 4389 | return switch (ty.zigTypeTag()) { |
| 4261 | 4390 | .Array, .Vector => true, |
| ... | ... | @@ -4642,7 +4771,7 @@ pub const Type = extern union { |
| 4642 | 4771 | return field_offset.offset; |
| 4643 | 4772 | } |
| 4644 | 4773 | |
| 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 | }, |
| 4647 | 4776 | |
| 4648 | 4777 | .tuple, .anon_struct => { |
| ... | ... | @@ -4665,7 +4794,7 @@ pub const Type = extern union { |
| 4665 | 4794 | if (i == index) return offset; |
| 4666 | 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 | 4798 | return offset; |
| 4670 | 4799 | }, |
| 4671 | 4800 | |
| ... | ... | @@ -5345,6 +5474,7 @@ pub const Type = extern union { |
| 5345 | 5474 | pub const @"type" = initTag(.type); |
| 5346 | 5475 | pub const @"anyerror" = initTag(.anyerror); |
| 5347 | 5476 | pub const @"anyopaque" = initTag(.anyopaque); |
| 5477 | pub const @"null" = initTag(.@"null"); | |
| 5348 | 5478 | |
| 5349 | 5479 | pub fn ptr(arena: Allocator, target: Target, data: Payload.Pointer.Data) !Type { |
| 5350 | 5480 | var d = data; |
src/value.zig+82-39| ... | ... | @@ -268,12 +268,14 @@ pub const Value = extern union { |
| 268 | 268 | |
| 269 | 269 | .repeated, |
| 270 | 270 | .eu_payload, |
| 271 | .eu_payload_ptr, | |
| 272 | 271 | .opt_payload, |
| 273 | .opt_payload_ptr, | |
| 274 | 272 | .empty_array_sentinel, |
| 275 | 273 | => Payload.SubValue, |
| 276 | 274 | |
| 275 | .eu_payload_ptr, | |
| 276 | .opt_payload_ptr, | |
| 277 | => Payload.PayloadPtr, | |
| 278 | ||
| 277 | 279 | .bytes, |
| 278 | 280 | .enum_literal, |
| 279 | 281 | => Payload.Bytes, |
| ... | ... | @@ -479,6 +481,20 @@ pub const Value = extern union { |
| 479 | 481 | .variable => return self.copyPayloadShallow(arena, Payload.Variable), |
| 480 | 482 | .decl_ref => return self.copyPayloadShallow(arena, Payload.Decl), |
| 481 | 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 | 498 | .elem_ptr => { |
| 483 | 499 | const payload = self.castTag(.elem_ptr).?; |
| 484 | 500 | const new_payload = try arena.create(Payload.ElemPtr); |
| ... | ... | @@ -486,6 +502,7 @@ pub const Value = extern union { |
| 486 | 502 | .base = payload.base, |
| 487 | 503 | .data = .{ |
| 488 | 504 | .array_ptr = try payload.data.array_ptr.copy(arena), |
| 505 | .elem_ty = try payload.data.elem_ty.copy(arena), | |
| 489 | 506 | .index = payload.data.index, |
| 490 | 507 | }, |
| 491 | 508 | }; |
| ... | ... | @@ -498,6 +515,7 @@ pub const Value = extern union { |
| 498 | 515 | .base = payload.base, |
| 499 | 516 | .data = .{ |
| 500 | 517 | .container_ptr = try payload.data.container_ptr.copy(arena), |
| 518 | .container_ty = try payload.data.container_ty.copy(arena), | |
| 501 | 519 | .field_index = payload.data.field_index, |
| 502 | 520 | }, |
| 503 | 521 | }; |
| ... | ... | @@ -506,9 +524,7 @@ pub const Value = extern union { |
| 506 | 524 | .bytes => return self.copyPayloadShallow(arena, Payload.Bytes), |
| 507 | 525 | .repeated, |
| 508 | 526 | .eu_payload, |
| 509 | .eu_payload_ptr, | |
| 510 | 527 | .opt_payload, |
| 511 | .opt_payload_ptr, | |
| 512 | 528 | .empty_array_sentinel, |
| 513 | 529 | => { |
| 514 | 530 | const payload = self.cast(Payload.SubValue).?; |
| ... | ... | @@ -740,11 +756,11 @@ pub const Value = extern union { |
| 740 | 756 | .inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"), |
| 741 | 757 | .eu_payload_ptr => { |
| 742 | 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 | 761 | .opt_payload_ptr => { |
| 746 | 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 | 765 | .bound_fn => { |
| 750 | 766 | const bound_func = val.castTag(.bound_fn).?.data; |
| ... | ... | @@ -2162,8 +2178,8 @@ pub const Value = extern union { |
| 2162 | 2178 | .decl_ref_mut => true, |
| 2163 | 2179 | .elem_ptr => isComptimeMutablePtr(val.castTag(.elem_ptr).?.data.array_ptr), |
| 2164 | 2180 | .field_ptr => isComptimeMutablePtr(val.castTag(.field_ptr).?.data.container_ptr), |
| 2165 | .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data), | |
| 2166 | .opt_payload_ptr => isComptimeMutablePtr(val.castTag(.opt_payload_ptr).?.data), | |
| 2181 | .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data.container_ptr), | |
| 2182 | .opt_payload_ptr => isComptimeMutablePtr(val.castTag(.opt_payload_ptr).?.data.container_ptr), | |
| 2167 | 2183 | |
| 2168 | 2184 | else => false, |
| 2169 | 2185 | }; |
| ... | ... | @@ -2174,9 +2190,9 @@ pub const Value = extern union { |
| 2174 | 2190 | switch (val.tag()) { |
| 2175 | 2191 | .repeated => return val.castTag(.repeated).?.data.canMutateComptimeVarState(), |
| 2176 | 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 | 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 | 2196 | .aggregate => { |
| 2181 | 2197 | const fields = val.castTag(.aggregate).?.data; |
| 2182 | 2198 | for (fields) |field| { |
| ... | ... | @@ -2239,12 +2255,12 @@ pub const Value = extern union { |
| 2239 | 2255 | .eu_payload_ptr => { |
| 2240 | 2256 | const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; |
| 2241 | 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 | 2260 | .opt_payload_ptr => { |
| 2245 | 2261 | const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; |
| 2246 | 2262 | std.hash.autoHash(hasher, Value.Tag.opt_payload_ptr); |
| 2247 | hashPtr(opt_ptr, hasher); | |
| 2263 | hashPtr(opt_ptr.container_ptr, hasher); | |
| 2248 | 2264 | }, |
| 2249 | 2265 | |
| 2250 | 2266 | .zero, |
| ... | ... | @@ -2272,12 +2288,14 @@ pub const Value = extern union { |
| 2272 | 2288 | |
| 2273 | 2289 | .repeated, |
| 2274 | 2290 | .eu_payload, |
| 2275 | .eu_payload_ptr, | |
| 2276 | 2291 | .opt_payload, |
| 2277 | .opt_payload_ptr, | |
| 2278 | 2292 | .empty_array_sentinel, |
| 2279 | 2293 | => return markReferencedDeclsAlive(val.cast(Payload.SubValue).?.data), |
| 2280 | 2294 | |
| 2295 | .eu_payload_ptr, | |
| 2296 | .opt_payload_ptr, | |
| 2297 | => return markReferencedDeclsAlive(val.cast(Payload.PayloadPtr).?.data.container_ptr), | |
| 2298 | ||
| 2281 | 2299 | .slice => { |
| 2282 | 2300 | const slice = val.cast(Payload.Slice).?.data; |
| 2283 | 2301 | markReferencedDeclsAlive(slice.ptr); |
| ... | ... | @@ -2394,6 +2412,29 @@ pub const Value = extern union { |
| 2394 | 2412 | } |
| 2395 | 2413 | } |
| 2396 | 2414 | |
| 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 | 2438 | pub fn fieldValue(val: Value, allocator: Allocator, index: usize) error{OutOfMemory}!Value { |
| 2398 | 2439 | _ = allocator; |
| 2399 | 2440 | switch (val.tag()) { |
| ... | ... | @@ -2422,36 +2463,28 @@ pub const Value = extern union { |
| 2422 | 2463 | } |
| 2423 | 2464 | |
| 2424 | 2465 | /// Returns a pointer to the element value at the index. |
| 2425 | pub fn elemPtr(val: Value, arena: Allocator, index: usize) Allocator.Error!Value { | |
| 2426 | switch (val.tag()) { | |
| 2427 | .elem_ptr => { | |
| 2428 | const elem_ptr = val.castTag(.elem_ptr).?.data; | |
| 2466 | pub fn elemPtr(val: Value, ty: Type, arena: Allocator, index: usize) Allocator.Error!Value { | |
| 2467 | const elem_ty = ty.elemType2(); | |
| 2468 | const ptr_val = switch (val.tag()) { | |
| 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 | 2476 | return Tag.elem_ptr.create(arena, .{ |
| 2430 | 2477 | .array_ptr = elem_ptr.array_ptr, |
| 2478 | .elem_ty = elem_ptr.elem_ty, | |
| 2431 | 2479 | .index = elem_ptr.index + index, |
| 2432 | 2480 | }); |
| 2433 | }, | |
| 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 | }), | |
| 2481 | } | |
| 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 | } |
| 2456 | 2489 | |
| 2457 | 2490 | pub fn isUndef(self: Value) bool { |
| ... | ... | @@ -4144,12 +4177,21 @@ pub const Value = extern union { |
| 4144 | 4177 | }; |
| 4145 | 4178 | }; |
| 4146 | 4179 | |
| 4180 | pub const PayloadPtr = struct { | |
| 4181 | base: Payload, | |
| 4182 | data: struct { | |
| 4183 | container_ptr: Value, | |
| 4184 | container_ty: Type, | |
| 4185 | }, | |
| 4186 | }; | |
| 4187 | ||
| 4147 | 4188 | pub const ElemPtr = struct { |
| 4148 | 4189 | pub const base_tag = Tag.elem_ptr; |
| 4149 | 4190 | |
| 4150 | 4191 | base: Payload = Payload{ .tag = base_tag }, |
| 4151 | 4192 | data: struct { |
| 4152 | 4193 | array_ptr: Value, |
| 4194 | elem_ty: Type, | |
| 4153 | 4195 | index: usize, |
| 4154 | 4196 | }, |
| 4155 | 4197 | }; |
| ... | ... | @@ -4160,6 +4202,7 @@ pub const Value = extern union { |
| 4160 | 4202 | base: Payload = Payload{ .tag = base_tag }, |
| 4161 | 4203 | data: struct { |
| 4162 | 4204 | container_ptr: Value, |
| 4205 | container_ty: Type, | |
| 4163 | 4206 | field_index: usize, |
| 4164 | 4207 | }, |
| 4165 | 4208 | }; |
test/behavior.zig+1| ... | ... | @@ -62,6 +62,7 @@ test { |
| 62 | 62 | _ = @import("behavior/bugs/11100.zig"); |
| 63 | 63 | _ = @import("behavior/bugs/10970.zig"); |
| 64 | 64 | _ = @import("behavior/bugs/11046.zig"); |
| 65 | _ = @import("behavior/bugs/11139.zig"); | |
| 65 | 66 | _ = @import("behavior/bugs/11165.zig"); |
| 66 | 67 | _ = @import("behavior/call.zig"); |
| 67 | 68 | _ = @import("behavior/cast.zig"); |
test/behavior/bugs/11139.zig created+25| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const expect = std.testing.expect; | |
| 4 | ||
| 5 | test "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 | ||
| 14 | fn 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 | 871 | } |
| 872 | 872 | |
| 873 | 873 | test "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 | 875 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 876 | 876 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 877 | 877 | |
| ... | ... | @@ -887,7 +887,9 @@ test "peer cast *[N:x]T to *[N]T" { |
| 887 | 887 | } |
| 888 | 888 | |
| 889 | 889 | test "peer cast [*:x]T to [*]T" { |
| 890 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 890 | 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 | |
| 891 | 893 | |
| 892 | 894 | const S = struct { |
| 893 | 895 | fn doTheTest() !void { |
test/behavior/ptrcast.zig+91-1| ... | ... | @@ -21,8 +21,47 @@ fn testReinterpretBytesAsInteger() !void { |
| 21 | 21 | try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | test "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 | ||
| 34 | fn 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 | ||
| 42 | test "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 | ||
| 51 | fn 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 | ||
| 24 | 61 | test "reinterpret bytes of an array into an extern struct" { |
| 25 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 62 | 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 | |
| 26 | 65 | |
| 27 | 66 | try testReinterpretBytesAsExternStruct(); |
| 28 | 67 | comptime try testReinterpretBytesAsExternStruct(); |
| ... | ... | @@ -42,6 +81,57 @@ fn testReinterpretBytesAsExternStruct() !void { |
| 42 | 81 | try expect(val == 5); |
| 43 | 82 | } |
| 44 | 83 | |
| 84 | test "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 | ||
| 93 | fn 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 | ||
| 110 | test "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 | ||
| 45 | 135 | test "reinterpret struct field at comptime" { |
| 46 | 136 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 47 | 137 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |