authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-03-11 14:22:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-14 21:42:43-07:00
log5fa057053ce32274b878077e5abff82335530fc8
tree79350e7a8e22ba6fa27477b15ef0c7d01cd79059
parent54426bdc82ad361bb580f9678dd23417c350282a

stage2 sema: Respect container_ty of parent ptrs

The core change here is that we no longer blindly trust that parent pointers (.elem_ptr, .field_ptr, .eu_payload_ptr, .union_payload_ptr) were derived from the "true" type of the underlying decl. When types diverge, direct dereference fails and we are forced to bitcast, as usual. In order to maximize our chances to have a successful bitcast, this includes several changes to the dereference procedure: - `root` is now `parent` and is the largest Value containing the dereference target, with the condition that its layout and the byte offset of the target within are both well-defined. - If the target cannot be dereferenced directly, because the pointers were not derived from the true type of the underlying decl, then it is returned as null. - `beginComptimePtrDeref` now accepts an optional array_ty param, which is used to directly dereference an array from an elem_ptr, if necessary. This allows us to dereference array types without well-defined layouts (e.g. `[N]?u8`) at an offset The load_ty also allows us to correctly "over-read" an .elem_ptr to an array of [N]T, if necessary. This makes direct dereference work for array types even in the presence of an offset, which is necessary if the array has no well-defined layout (e.g. loading from `[6]?u8`)

3 files changed, 239 insertions(+), 151 deletions(-)

src/Sema.zig+209-151
...@@ -12609,6 +12609,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12609,6 +12609,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12609 enum_obj.* = .{12609 enum_obj.* = .{
12610 .owner_decl = new_decl,12610 .owner_decl = new_decl,
12611 .tag_ty = Type.initTag(.@"null"),12611 .tag_ty = Type.initTag(.@"null"),
12612 .tag_ty_inferred = true,
12612 .fields = .{},12613 .fields = .{},
12613 .values = .{},12614 .values = .{},
12614 .node_offset = src.node_offset,12615 .node_offset = src.node_offset,
...@@ -17590,6 +17591,14 @@ fn beginComptimePtrMutation(...@@ -17590,6 +17591,14 @@ fn beginComptimePtrMutation(
17590 src: LazySrcLoc,17591 src: LazySrcLoc,
17591 ptr_val: Value,17592 ptr_val: Value,
17592) CompileError!ComptimePtrMutationKit {17593) CompileError!ComptimePtrMutationKit {
17594
17595 // TODO: Update this to behave like `beginComptimePtrLoad` and properly check/use
17596 // `container_ty` and `array_ty`, instead of trusting that the parent decl type
17597 // matches the type used to derive the elem_ptr/field_ptr/etc.
17598 //
17599 // This is needed because the types will not match if the pointer we're mutating
17600 // through is reinterpreting comptime memory.
17601
17593 switch (ptr_val.tag()) {17602 switch (ptr_val.tag()) {
17594 .decl_ref_mut => {17603 .decl_ref_mut => {
17595 const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;17604 const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;
...@@ -17850,163 +17859,191 @@ fn beginComptimePtrMutation(...@@ -17850,163 +17859,191 @@ fn beginComptimePtrMutation(
17850 }17859 }
17851}17860}
1785217861
17853const ComptimePtrLoadKit = struct {17862const TypedValueAndOffset = struct {
17854 /// The Value of the Decl that owns this memory.17863 tv: TypedValue,
17855 root_val: Value,
17856 /// The Type of the Decl that owns this memory.
17857 root_ty: Type,
17858 /// Parent Value.
17859 val: Value,
17860 /// The Type of the parent Value.
17861 ty: Type,
17862 /// The starting byte offset of `val` from `root_val`.17864 /// The starting byte offset of `val` from `root_val`.
17863 /// If the type does not have a well-defined memory layout, this is null.17865 /// If the type does not have a well-defined memory layout, this is null.
17864 byte_offset: ?usize,17866 byte_offset: usize,
17865 /// Whether the `root_val` could be mutated by further17867};
17868
17869const ComptimePtrLoadKit = struct {
17870 /// The Value and Type corresponding to the target of the provided pointer.
17871 /// If a direct dereference is not possible, this is null.
17872 target: ?TypedValue,
17873 /// The largest parent Value containing `target` and having a well-defined memory layout.
17874 /// This is used for bitcasting, if direct dereferencing failed (i.e. `target` is null).
17875 parent: ?TypedValueAndOffset,
17876 /// Whether the `target` could be mutated by further
17866 /// semantic analysis and a copy must be performed.17877 /// semantic analysis and a copy must be performed.
17867 is_mutable: bool,17878 is_mutable: bool,
17879 /// If the root decl could not be used as `parent`, this is the type that
17880 /// caused that by not having a well-defined layout
17881 ty_without_well_defined_layout: ?Type,
17868};17882};
1786917883
17870const ComptimePtrLoadError = CompileError || error{17884const ComptimePtrLoadError = CompileError || error{
17871 RuntimeLoad,17885 RuntimeLoad,
17872};17886};
1787317887
17888/// If `maybe_array_ty` is provided, it will be used to directly dereference an
17889/// .elem_ptr of type T to a value of [N]T, if necessary.
17874fn beginComptimePtrLoad(17890fn beginComptimePtrLoad(
17875 sema: *Sema,17891 sema: *Sema,
17876 block: *Block,17892 block: *Block,
17877 src: LazySrcLoc,17893 src: LazySrcLoc,
17878 ptr_val: Value,17894 ptr_val: Value,
17895 maybe_array_ty: ?Type,
17879) ComptimePtrLoadError!ComptimePtrLoadKit {17896) ComptimePtrLoadError!ComptimePtrLoadKit {
17880 const target = sema.mod.getTarget();17897 const target = sema.mod.getTarget();
17881 switch (ptr_val.tag()) {17898 var deref: ComptimePtrLoadKit = switch (ptr_val.tag()) {
17882 .decl_ref => {17899 .decl_ref,
17883 const decl = ptr_val.castTag(.decl_ref).?.data;17900 .decl_ref_mut,
17884 const decl_val = try decl.value();17901 => blk: {
17885 if (decl_val.tag() == .variable) return error.RuntimeLoad;17902 const decl = switch (ptr_val.tag()) {
17886 return ComptimePtrLoadKit{17903 .decl_ref => ptr_val.castTag(.decl_ref).?.data,
17887 .root_val = decl_val,17904 .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl,
17888 .root_ty = decl.ty,17905 else => unreachable,
17889 .val = decl_val,
17890 .ty = decl.ty,
17891 .byte_offset = 0,
17892 .is_mutable = false,
17893 };17906 };
17894 },17907 const is_mutable = ptr_val.tag() == .decl_ref_mut;
17895 .decl_ref_mut => {17908 const decl_tv = try decl.typedValue();
17896 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;17909 if (decl_tv.val.tag() == .variable) return error.RuntimeLoad;
17897 const decl_val = try decl.value();17910
17898 if (decl_val.tag() == .variable) return error.RuntimeLoad;17911 const layout_defined = try sema.typeHasWellDefinedLayout(block, src, decl.ty);
17899 return ComptimePtrLoadKit{17912 break :blk ComptimePtrLoadKit{
17900 .root_val = decl_val,17913 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
17901 .root_ty = decl.ty,17914 .target = decl_tv,
17902 .val = decl_val,17915 .is_mutable = is_mutable,
17903 .ty = decl.ty,17916 .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
17904 .byte_offset = 0,
17905 .is_mutable = true,
17906 };17917 };
17907 },17918 },
17908 .elem_ptr => {17919
17920 .elem_ptr => blk: {
17909 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;17921 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
17910 const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr);17922 const elem_ty = elem_ptr.elem_ty;
17911 switch (parent.ty.zigTypeTag()) {17923 var deref = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr, null);
17912 .Array, .Vector => {17924
17913 const check_len = parent.ty.arrayLenIncludingSentinel();17925 if (elem_ptr.index != 0) {
17914 if (elem_ptr.index >= check_len) {17926 if (try sema.typeHasWellDefinedLayout(block, src, elem_ty)) {
17915 // TODO have the parent include the decl so we can say "declared here"17927 if (deref.parent) |*parent| {
17916 return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{17928 // Update the byte offset (in-place)
17917 elem_ptr.index, check_len,17929 const elem_size = try sema.typeAbiSize(block, src, elem_ty);
17918 });17930 const offset = parent.byte_offset + elem_size * elem_ptr.index;
17931 parent.byte_offset = try sema.usizeCast(block, src, offset);
17919 }17932 }
17920 const elem_ty = parent.ty.childType();17933 } else {
17921 const byte_offset: ?usize = bo: {17934 deref.parent = null;
17922 if (try sema.typeRequiresComptime(block, src, elem_ty)) {17935 deref.ty_without_well_defined_layout = elem_ty;
17923 break :bo null;17936 }
17924 } else {17937 }
17925 if (parent.byte_offset) |off| {17938
17926 try sema.resolveTypeLayout(block, src, elem_ty);17939 // If we're loading an elem_ptr that was derived from a different type
17927 const elem_size = elem_ty.abiSize(target);17940 // than the true type of the underlying decl, we cannot deref directly
17928 break :bo try sema.usizeCast(block, src, off + elem_size * elem_ptr.index);17941 const ty_matches = if (deref.target != null and deref.target.?.ty.isArrayLike()) x: {
17929 } else {17942 const deref_elem_ty = deref.target.?.ty.childType();
17930 break :bo null;17943 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
17931 }17944 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
17932 }17945 } else false;
17933 };17946 if (!ty_matches) {
17934 return ComptimePtrLoadKit{17947 deref.target = null;
17935 .root_val = parent.root_val,17948 break :blk deref;
17936 .root_ty = parent.root_ty,
17937 .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
17938 .ty = elem_ty,
17939 .byte_offset = byte_offset,
17940 .is_mutable = parent.is_mutable,
17941 };
17942 },
17943 else => {
17944 if (elem_ptr.index != 0) {
17945 // TODO have the parent include the decl so we can say "declared here"
17946 return sema.fail(block, src, "out of bounds comptime load of index {d}", .{
17947 elem_ptr.index,
17948 });
17949 }
17950 return ComptimePtrLoadKit{
17951 .root_val = parent.root_val,
17952 .root_ty = parent.root_ty,
17953 .val = parent.val,
17954 .ty = parent.ty,
17955 .byte_offset = parent.byte_offset,
17956 .is_mutable = parent.is_mutable,
17957 };
17958 },
17959 }17949 }
17950
17951 var array_tv = deref.target.?;
17952 const check_len = array_tv.ty.arrayLenIncludingSentinel();
17953 if (elem_ptr.index >= check_len) {
17954 // TODO have the deref include the decl so we can say "declared here"
17955 return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{
17956 elem_ptr.index, check_len,
17957 });
17958 }
17959
17960 if (maybe_array_ty) |load_ty| {
17961 // It's possible that we're loading a [N]T, in which case we'd like to slice
17962 // the target array directly from our parent array.
17963 if (load_ty.isArrayLike() and load_ty.childType().eql(elem_ty)) {
17964 const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel());
17965 deref.target = if (elem_ptr.index + N <= check_len) TypedValue{
17966 .ty = try Type.array(sema.arena, N, null, elem_ty),
17967 .val = try array_tv.val.sliceArray(sema.arena, elem_ptr.index, elem_ptr.index + N),
17968 } else null;
17969 break :blk deref;
17970 }
17971 }
17972
17973 deref.target = .{
17974 .ty = elem_ty,
17975 .val = try array_tv.val.elemValue(sema.arena, elem_ptr.index),
17976 };
17977 break :blk deref;
17960 },17978 },
17961 .field_ptr => {17979
17980 .field_ptr => blk: {
17962 const field_ptr = ptr_val.castTag(.field_ptr).?.data;17981 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
17963 const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr);
17964 const field_index = @intCast(u32, field_ptr.field_index);17982 const field_index = @intCast(u32, field_ptr.field_index);
17965 const byte_offset: ?usize = bo: {17983 const field_ty = field_ptr.container_ty.structFieldType(field_index);
17966 if (try sema.typeRequiresComptime(block, src, parent.ty)) {17984 var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
17967 break :bo null;17985
17968 } else {17986 if (try sema.typeHasWellDefinedLayout(block, src, field_ptr.container_ty)) {
17969 if (parent.byte_offset) |off| {17987 if (deref.parent) |*parent| {
17970 try sema.resolveTypeLayout(block, src, parent.ty);17988 // Update the byte offset (in-place)
17971 const field_offset = parent.ty.structFieldOffset(field_index, target);17989 try sema.resolveTypeLayout(block, src, field_ptr.container_ty);
17972 break :bo try sema.usizeCast(block, src, off + field_offset);17990 const field_offset = field_ptr.container_ty.structFieldOffset(field_index, target);
17973 } else {17991 parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset);
17974 break :bo null;
17975 }
17976 }17992 }
17977 };17993 } else {
17978 return ComptimePtrLoadKit{17994 deref.parent = null;
17979 .root_val = parent.root_val,17995 deref.ty_without_well_defined_layout = field_ptr.container_ty;
17980 .root_ty = parent.root_ty,17996 }
17981 .val = try parent.val.fieldValue(sema.arena, field_index),17997
17982 .ty = parent.ty.structFieldType(field_index),17998 if (deref.target) |*tv| {
17983 .byte_offset = byte_offset,17999 const coerce_in_mem_ok =
17984 .is_mutable = parent.is_mutable,18000 (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
17985 };18001 (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok;
17986 },18002 if (coerce_in_mem_ok) {
17987 .eu_payload_ptr => {18003 deref.target = TypedValue{
17988 const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;18004 .ty = field_ty,
17989 const parent = try beginComptimePtrLoad(sema, block, src, err_union_ptr.container_ptr);18005 .val = try tv.val.fieldValue(sema.arena, field_index),
17990 return ComptimePtrLoadKit{18006 };
17991 .root_val = parent.root_val,18007 break :blk deref;
17992 .root_ty = parent.root_ty,18008 }
17993 .val = parent.val.castTag(.eu_payload).?.data,18009 }
17994 .ty = parent.ty.errorUnionPayload(),18010 deref.target = null;
17995 .byte_offset = null,18011 break :blk deref;
17996 .is_mutable = parent.is_mutable,
17997 };
17998 },18012 },
17999 .opt_payload_ptr => {18013
18000 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;18014 .opt_payload_ptr,
18001 const parent = try beginComptimePtrLoad(sema, block, src, opt_ptr.container_ptr);18015 .eu_payload_ptr,
18002 return ComptimePtrLoadKit{18016 => blk: {
18003 .root_val = parent.root_val,18017 const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data;
18004 .root_ty = parent.root_ty,18018 const payload_ty = switch (ptr_val.tag()) {
18005 .val = parent.val.castTag(.opt_payload).?.data,18019 .eu_payload_ptr => payload_ptr.container_ty.errorUnionPayload(),
18006 .ty = try parent.ty.optionalChildAlloc(sema.arena),18020 .opt_payload_ptr => try payload_ptr.container_ty.optionalChildAlloc(sema.arena),
18007 .byte_offset = null,18021 else => unreachable,
18008 .is_mutable = parent.is_mutable,
18009 };18022 };
18023 var deref = try beginComptimePtrLoad(sema, block, src, payload_ptr.container_ptr, payload_ptr.container_ty);
18024
18025 // eu_payload_ptr and opt_payload_ptr never have a well-defined layout
18026 if (deref.parent != null) {
18027 deref.parent = null;
18028 deref.ty_without_well_defined_layout = payload_ptr.container_ty;
18029 }
18030
18031 if (deref.target) |*tv| {
18032 const coerce_in_mem_ok =
18033 (try sema.coerceInMemoryAllowed(block, payload_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
18034 (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok;
18035 if (coerce_in_mem_ok) {
18036 const payload_val = switch (ptr_val.tag()) {
18037 .eu_payload_ptr => tv.val.castTag(.eu_payload).?.data,
18038 .opt_payload_ptr => tv.val.castTag(.opt_payload).?.data,
18039 else => unreachable,
18040 };
18041 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
18042 break :blk deref;
18043 }
18044 }
18045 deref.target = null;
18046 break :blk deref;
18010 },18047 },
1801118048
18012 .zero,18049 .zero,
...@@ -18021,7 +18058,14 @@ fn beginComptimePtrLoad(...@@ -18021,7 +18058,14 @@ fn beginComptimePtrLoad(
18021 => return error.RuntimeLoad,18058 => return error.RuntimeLoad,
1802218059
18023 else => unreachable,18060 else => unreachable,
18061 };
18062
18063 if (deref.target) |tv| {
18064 if (deref.parent == null and tv.ty.hasWellDefinedLayout()) {
18065 deref.parent = .{ .tv = tv, .byte_offset = 0 };
18066 }
18024 }18067 }
18068 return deref;
18025}18069}
1802618070
18027fn bitCast(18071fn bitCast(
...@@ -21106,39 +21150,53 @@ pub fn analyzeAddrspace(...@@ -21106,39 +21150,53 @@ pub fn analyzeAddrspace(
21106/// Asserts the value is a pointer and dereferences it.21150/// Asserts the value is a pointer and dereferences it.
21107/// Returns `null` if the pointer contents cannot be loaded at comptime.21151/// Returns `null` if the pointer contents cannot be loaded at comptime.
21108fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {21152fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
21109 const target = sema.mod.getTarget();
21110 const load_ty = ptr_ty.childType();21153 const load_ty = ptr_ty.childType();
21111 const parent = sema.beginComptimePtrLoad(block, src, ptr_val) catch |err| switch (err) {21154 const target = sema.mod.getTarget();
21155 const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
21112 error.RuntimeLoad => return null,21156 error.RuntimeLoad => return null,
21113 else => |e| return e,21157 else => |e| return e,
21114 };21158 };
21115 // We have a Value that lines up in virtual memory exactly with what we want to load.21159
21116 // If the Type is in-memory coercable to `load_ty`, it may be returned without modifications.21160 if (deref.target) |tv| {
21117 const coerce_in_mem_ok =21161 const coerce_in_mem_ok =
21118 (try sema.coerceInMemoryAllowed(block, load_ty, parent.ty, false, target, src, src)) == .ok or21162 (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or
21119 (try sema.coerceInMemoryAllowed(block, parent.ty, load_ty, false, target, src, src)) == .ok;21163 (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;
21120 if (coerce_in_mem_ok) {21164 if (coerce_in_mem_ok) {
21121 if (parent.is_mutable) {21165 // We have a Value that lines up in virtual memory exactly with what we want to load,
21122 // The decl whose value we are obtaining here may be overwritten with21166 // and it is in-memory coercible to load_ty. It may be returned without modifications.
21123 // a different value upon further semantic analysis, which would21167 if (deref.is_mutable) {
21124 // invalidate this memory. So we must copy here.21168 // The decl whose value we are obtaining here may be overwritten with
21125 return try parent.val.copy(sema.arena);21169 // a different value upon further semantic analysis, which would
21170 // invalidate this memory. So we must copy here.
21171 return try tv.val.copy(sema.arena);
21172 }
21173 return tv.val;
21126 }21174 }
21127 return parent.val;
21128 }21175 }
2112921176
21130 // The type is not in-memory coercable, so it must be bitcasted according21177 // The type is not in-memory coercible or the direct dereference failed, so it must
21131 // to the pointer type we are performing the load through.21178 // be bitcast according to the pointer type we are performing the load through.
21179 if (!(try sema.typeHasWellDefinedLayout(block, src, load_ty)))
21180 return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty});
21181
21182 const load_sz = try sema.typeAbiSize(block, src, load_ty);
21183
21184 // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
21185 if (deref.target) |tv| if (load_sz <= try sema.typeAbiSize(block, src, tv.ty))
21186 return try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0);
2113221187
21133 // TODO emit a compile error if the types are not allowed to be bitcasted21188 // If that fails, try to bit-cast from the largest parent value with a well-defined layout
21189 if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(block, src, parent.tv.ty))
21190 return try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset);
2113421191
21135 if (parent.ty.abiSize(target) >= load_ty.abiSize(target)) {21192 if (deref.ty_without_well_defined_layout) |bad_ty| {
21136 // The Type it is stored as in the compiler has an ABI size greater or equal to21193 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
21137 // the ABI size of `load_ty`. We may perform the bitcast based on21194 // is that some type we encountered when de-referencing does not have a well-defined layout.
21138 // `parent.val` alone (more efficient).21195 return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty});
21139 return try sema.bitCastVal(block, src, parent.val, parent.ty, load_ty, 0);
21140 } else {21196 } else {
21141 return try sema.bitCastVal(block, src, parent.root_val, parent.root_ty, load_ty, parent.byte_offset.?);21197 // If all encountered types had well-defined layouts, the parent is the root decl and it just
21198 // wasn't big enough for the load.
21199 return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty, deref.parent.?.tv.ty });
21142 }21200 }
21143}21201}
2114421202
src/type.zig+7
...@@ -4400,6 +4400,13 @@ pub const Type = extern union {...@@ -4400,6 +4400,13 @@ pub const Type = extern union {
4400 };4400 };
4401 }4401 }
44024402
4403 pub fn isArrayLike(ty: Type) bool {
4404 return switch (ty.zigTypeTag()) {
4405 .Array, .Vector => true,
4406 else => false,
4407 };
4408 }
4409
4403 pub fn isIndexable(ty: Type) bool {4410 pub fn isIndexable(ty: Type) bool {
4404 return switch (ty.zigTypeTag()) {4411 return switch (ty.zigTypeTag()) {
4405 .Array, .Vector => true,4412 .Array, .Vector => true,
src/value.zig+23
...@@ -2412,6 +2412,29 @@ pub const Value = extern union {...@@ -2412,6 +2412,29 @@ pub const Value = extern union {
2412 }2412 }
2413 }2413 }
24142414
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 .array => Tag.array.create(arena, val.castTag(.array).?.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
2415 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 {
2416 _ = allocator;2439 _ = allocator;
2417 switch (val.tag()) {2440 switch (val.tag()) {