authorgravatar for william@sengir.comWilliam Sengir <william@sengir.com> 2022-03-17 09:12:24-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-20 18:04:38-04:00
loga5dc3f03423befa5a2ec1efc07f9bf26eae1a3d9
tree494c36d93970023f5361b2b702ba4621a1a807fc
parent9f25c8140cb859fcea7023362afcb29b1e4df41f

stage2: add safety checks for index out of bounds


1 files changed, 267 insertions(+), 129 deletions(-)

src/Sema.zig+267-129
......@@ -3235,7 +3235,7 @@ fn zirValidateArrayInit(
32353235 // any ZIR instructions at comptime; we need to do that here.
32363236 if (array_ty.sentinel()) |sentinel_val| {
32373237 const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len);
3238 const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, array_len_ref, init_src);
3238 const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, init_src, array_len_ref);
32393239 const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val);
32403240 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
32413241 }
......@@ -15746,6 +15746,7 @@ pub const PanicId = enum {
1574615746 cast_to_null,
1574715747 incorrect_alignment,
1574815748 invalid_error_code,
15749 index_out_of_bounds,
1574915750};
1575015751
1575115752fn addSafetyCheck(
......@@ -15867,6 +15868,7 @@ fn safetyPanic(
1586715868 .cast_to_null => "cast causes pointer to be null",
1586815869 .incorrect_alignment => "incorrect alignment",
1586915870 .invalid_error_code => "invalid error code",
15871 .index_out_of_bounds => "attempt to index out of bounds",
1587015872 };
1587115873
1587215874 const msg_inst = msg_inst: {
......@@ -16483,10 +16485,10 @@ fn structFieldPtr(
1648316485 return sema.analyzeRef(block, src, len_inst);
1648416486 }
1648516487 const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
16486 return sema.tupleFieldPtr(block, struct_ptr, field_index, src, field_name_src);
16488 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index);
1648716489 } else if (struct_ty.isAnonStruct()) {
1648816490 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
16489 return sema.tupleFieldPtr(block, struct_ptr, field_index, src, field_name_src);
16491 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index);
1649016492 }
1649116493
1649216494 const struct_obj = struct_ty.castTag(.@"struct").?.data;
......@@ -16806,67 +16808,55 @@ fn elemPtr(
1680616808 sema: *Sema,
1680716809 block: *Block,
1680816810 src: LazySrcLoc,
16809 array_ptr: Air.Inst.Ref,
16811 indexable_ptr: Air.Inst.Ref,
1681016812 elem_index: Air.Inst.Ref,
1681116813 elem_index_src: LazySrcLoc,
1681216814) CompileError!Air.Inst.Ref {
16813 const array_ptr_src = src; // TODO better source location
16814 const array_ptr_ty = sema.typeOf(array_ptr);
16815 const array_ty = switch (array_ptr_ty.zigTypeTag()) {
16816 .Pointer => array_ptr_ty.elemType(),
16817 else => return sema.fail(block, array_ptr_src, "expected pointer, found '{}'", .{array_ptr_ty}),
16815 const indexable_ptr_src = src; // TODO better source location
16816 const indexable_ptr_ty = sema.typeOf(indexable_ptr);
16817 const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) {
16818 .Pointer => indexable_ptr_ty.elemType(),
16819 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty}),
1681816820 };
16819 if (!array_ty.isIndexable()) {
16820 return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});
16821 if (!indexable_ty.isIndexable()) {
16822 return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty});
1682116823 }
1682216824
16823 switch (array_ty.zigTypeTag()) {
16825 switch (indexable_ty.zigTypeTag()) {
1682416826 .Pointer => {
16825 // In all below cases, we have to deref the ptr operand to get the actual array pointer.
16826 const array = try sema.analyzeLoad(block, array_ptr_src, array_ptr, array_ptr_src);
16827 // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
16828 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
1682716829 const target = sema.mod.getTarget();
16828 const result_ty = try array_ty.elemPtrType(sema.arena, target);
16829 switch (array_ty.ptrSize()) {
16830 .Slice => {
16831 const maybe_slice_val = try sema.resolveDefinedValue(block, array_ptr_src, array);
16832 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
16833 const runtime_src = if (maybe_slice_val) |slice_val| rs: {
16834 const index_val = maybe_index_val orelse break :rs elem_index_src;
16835 const index = @intCast(usize, index_val.toUnsignedInt());
16836 const elem_ptr = try slice_val.elemPtr(array_ty, sema.arena, index);
16837 return sema.addConstant(result_ty, elem_ptr);
16838 } else array_ptr_src;
16839
16840 try sema.requireRuntimeBlock(block, runtime_src);
16841 return block.addSliceElemPtr(array, elem_index, result_ty);
16842 },
16830 const result_ty = try indexable_ty.elemPtrType(sema.arena, target);
16831 switch (indexable_ty.ptrSize()) {
16832 .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index),
1684316833 .Many, .C => {
16844 const maybe_ptr_val = try sema.resolveDefinedValue(block, array_ptr_src, array);
16834 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable);
1684516835 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
1684616836
1684716837 const runtime_src = rs: {
16848 const ptr_val = maybe_ptr_val orelse break :rs array_ptr_src;
16838 const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;
1684916839 const index_val = maybe_index_val orelse break :rs elem_index_src;
1685016840 const index = @intCast(usize, index_val.toUnsignedInt());
16851 const elem_ptr = try ptr_val.elemPtr(array_ty, sema.arena, index);
16841 const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index);
1685216842 return sema.addConstant(result_ty, elem_ptr);
1685316843 };
1685416844
1685516845 try sema.requireRuntimeBlock(block, runtime_src);
16856 return block.addPtrElemPtr(array, elem_index, result_ty);
16846 return block.addPtrElemPtr(indexable, elem_index, result_ty);
1685716847 },
1685816848 .One => {
16859 assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
16860 return sema.elemPtrArray(block, array_ptr_src, array, elem_index, elem_index_src);
16849 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
16850 return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index);
1686116851 },
1686216852 }
1686316853 },
16864 .Array, .Vector => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src),
16854 .Array, .Vector => return sema.elemPtrArray(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index),
1686516855 .Struct => {
1686616856 // Tuple field access.
1686716857 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
1686816858 const index = @intCast(u32, index_val.toUnsignedInt());
16869 return sema.tupleFieldPtr(block, array_ptr, index, src, elem_index_src);
16859 return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index);
1687016860 },
1687116861 else => unreachable,
1687216862 }
......@@ -16876,90 +16866,66 @@ fn elemVal(
1687616866 sema: *Sema,
1687716867 block: *Block,
1687816868 src: LazySrcLoc,
16879 array: Air.Inst.Ref,
16869 indexable: Air.Inst.Ref,
1688016870 elem_index_uncasted: Air.Inst.Ref,
1688116871 elem_index_src: LazySrcLoc,
1688216872) CompileError!Air.Inst.Ref {
16883 const array_src = src; // TODO better source location
16884 const array_ty = sema.typeOf(array);
16873 const indexable_src = src; // TODO better source location
16874 const indexable_ty = sema.typeOf(indexable);
1688516875
16886 if (!array_ty.isIndexable()) {
16887 return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});
16876 if (!indexable_ty.isIndexable()) {
16877 return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty});
1688816878 }
1688916879
1689016880 // TODO in case of a vector of pointers, we need to detect whether the element
1689116881 // index is a scalar or vector instead of unconditionally casting to usize.
1689216882 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
1689316883
16894 switch (array_ty.zigTypeTag()) {
16895 .Pointer => switch (array_ty.ptrSize()) {
16896 .Slice => {
16897 const maybe_slice_val = try sema.resolveDefinedValue(block, array_src, array);
16898 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
16899 const runtime_src = if (maybe_slice_val) |slice_val| rs: {
16900 const index_val = maybe_index_val orelse break :rs elem_index_src;
16901 const index = @intCast(usize, index_val.toUnsignedInt());
16902
16903 const elem_ty = array_ty.elemType2();
16904
16905 var payload: Value.Payload.ElemPtr = .{ .data = .{
16906 .array_ptr = slice_val.slicePtr(),
16907 .elem_ty = elem_ty,
16908 .index = index,
16909 } };
16910 const elem_ptr_val = Value.initPayload(&payload.base);
16911
16912 if (try sema.pointerDeref(block, array_src, elem_ptr_val, array_ty)) |elem_val| {
16913 return sema.addConstant(elem_ty, elem_val);
16914 }
16915 break :rs array_src;
16916 } else array_src;
16917
16918 try sema.requireRuntimeBlock(block, runtime_src);
16919 return block.addBinOp(.slice_elem_val, array, elem_index);
16920 },
16884 switch (indexable_ty.zigTypeTag()) {
16885 .Pointer => switch (indexable_ty.ptrSize()) {
16886 .Slice => return sema.elemValSlice(block, indexable_src, indexable, elem_index_src, elem_index),
1692116887 .Many, .C => {
16922 const maybe_array_val = try sema.resolveDefinedValue(block, array_src, array);
16888 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
1692316889 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
1692416890
1692516891 const runtime_src = rs: {
16926 const array_val = maybe_array_val orelse break :rs array_src;
16892 const indexable_val = maybe_indexable_val orelse break :rs indexable_src;
1692716893 const index_val = maybe_index_val orelse break :rs elem_index_src;
1692816894 const index = @intCast(usize, index_val.toUnsignedInt());
16929 const elem_ty = array_ty.elemType2();
16895 const elem_ty = indexable_ty.elemType2();
1693016896
1693116897 var payload: Value.Payload.ElemPtr = .{ .data = .{
16932 .array_ptr = array_val,
16898 .array_ptr = indexable_val,
1693316899 .elem_ty = elem_ty,
1693416900 .index = index,
1693516901 } };
1693616902 const elem_ptr_val = Value.initPayload(&payload.base);
1693716903
16938 if (try sema.pointerDeref(block, array_src, elem_ptr_val, array_ty)) |elem_val| {
16904 if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| {
1693916905 return sema.addConstant(elem_ty, elem_val);
1694016906 }
16941 break :rs array_src;
16907 break :rs indexable_src;
1694216908 };
1694316909
1694416910 try sema.requireRuntimeBlock(block, runtime_src);
16945 return block.addBinOp(.ptr_elem_val, array, elem_index);
16911 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
1694616912 },
1694716913 .One => {
16948 assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
16949 const elem_ptr = try sema.elemPtr(block, array_src, array, elem_index, elem_index_src);
16950 return sema.analyzeLoad(block, array_src, elem_ptr, elem_index_src);
16914 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
16915 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src);
16916 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
1695116917 },
1695216918 },
16953 .Array => return elemValArray(sema, block, array, elem_index, array_src, elem_index_src),
16919 .Array => return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index),
1695416920 .Vector => {
1695516921 // TODO: If the index is a vector, the result should be a vector.
16956 return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);
16922 return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index);
1695716923 },
1695816924 .Struct => {
1695916925 // Tuple field access.
1696016926 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
1696116927 const index = @intCast(u32, index_val.toUnsignedInt());
16962 return tupleField(sema, block, array, index, array_src, elem_index_src);
16928 return tupleField(sema, block, indexable_src, indexable, elem_index_src, index);
1696316929 },
1696416930 else => unreachable,
1696516931 }
......@@ -16968,22 +16934,26 @@ fn elemVal(
1696816934fn tupleFieldPtr(
1696916935 sema: *Sema,
1697016936 block: *Block,
16937 tuple_ptr_src: LazySrcLoc,
1697116938 tuple_ptr: Air.Inst.Ref,
16972 field_index: u32,
16973 tuple_src: LazySrcLoc,
1697416939 field_index_src: LazySrcLoc,
16940 field_index: u32,
1697516941) CompileError!Air.Inst.Ref {
1697616942 const tuple_ptr_ty = sema.typeOf(tuple_ptr);
1697716943 const tuple_ty = tuple_ptr_ty.childType();
16978 const tuple = tuple_ty.tupleFields();
16944 const tuple_fields = tuple_ty.tupleFields();
16945
16946 if (tuple_fields.types.len == 0) {
16947 return sema.fail(block, field_index_src, "indexing into empty tuple", .{});
16948 }
1697916949
16980 if (field_index > tuple.types.len) {
16950 if (field_index >= tuple_fields.types.len) {
1698116951 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
16982 field_index, tuple.types.len,
16952 field_index, tuple_fields.types.len,
1698316953 });
1698416954 }
1698516955
16986 const field_ty = tuple.types[field_index];
16956 const field_ty = tuple_fields.types[field_index];
1698716957 const target = sema.mod.getTarget();
1698816958 const ptr_field_ty = try Type.ptr(sema.arena, target, .{
1698916959 .pointee_type = field_ty,
......@@ -16991,7 +16961,7 @@ fn tupleFieldPtr(
1699116961 .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(),
1699216962 });
1699316963
16994 if (try sema.resolveMaybeUndefVal(block, tuple_src, tuple_ptr)) |tuple_ptr_val| {
16964 if (try sema.resolveMaybeUndefVal(block, tuple_ptr_src, tuple_ptr)) |tuple_ptr_val| {
1699516965 return sema.addConstant(
1699616966 ptr_field_ty,
1699716967 try Value.Tag.field_ptr.create(sema.arena, .{
......@@ -17002,29 +16972,33 @@ fn tupleFieldPtr(
1700216972 );
1700316973 }
1700416974
17005 try sema.requireRuntimeBlock(block, tuple_src);
16975 try sema.requireRuntimeBlock(block, tuple_ptr_src);
1700616976 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
1700716977}
1700816978
1700916979fn tupleField(
1701016980 sema: *Sema,
1701116981 block: *Block,
17012 tuple: Air.Inst.Ref,
17013 field_index: u32,
1701416982 tuple_src: LazySrcLoc,
16983 tuple: Air.Inst.Ref,
1701516984 field_index_src: LazySrcLoc,
16985 field_index: u32,
1701616986) CompileError!Air.Inst.Ref {
1701716987 const tuple_ty = sema.typeOf(tuple);
17018 const tuple_info = tuple_ty.tupleFields();
16988 const tuple_fields = tuple_ty.tupleFields();
16989
16990 if (tuple_fields.types.len == 0) {
16991 return sema.fail(block, field_index_src, "indexing into empty tuple", .{});
16992 }
1701916993
17020 if (field_index > tuple_info.types.len) {
16994 if (field_index >= tuple_fields.types.len) {
1702116995 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
17022 field_index, tuple_info.types.len,
16996 field_index, tuple_fields.types.len,
1702316997 });
1702416998 }
1702516999
17026 const field_ty = tuple_info.types[field_index];
17027 const field_val = tuple_info.values[field_index];
17000 const field_ty = tuple_fields.types[field_index];
17001 const field_val = tuple_fields.values[field_index];
1702817002
1702917003 if (field_val.tag() != .unreachable_value) {
1703017004 return sema.addConstant(field_ty, field_val); // comptime field
......@@ -17043,57 +17017,221 @@ fn tupleField(
1704317017fn elemValArray(
1704417018 sema: *Sema,
1704517019 block: *Block,
17046 array: Air.Inst.Ref,
17047 elem_index: Air.Inst.Ref,
1704817020 array_src: LazySrcLoc,
17021 array: Air.Inst.Ref,
1704917022 elem_index_src: LazySrcLoc,
17023 elem_index: Air.Inst.Ref,
1705017024) CompileError!Air.Inst.Ref {
1705117025 const array_ty = sema.typeOf(array);
17052 if (try sema.resolveMaybeUndefVal(block, array_src, array)) |array_val| {
17053 const elem_ty = array_ty.childType();
17054 if (array_val.isUndef()) return sema.addConstUndef(elem_ty);
17055 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
17026 const array_sent = array_ty.sentinel() != null;
17027 const array_len = array_ty.arrayLen();
17028 const array_len_s = array_len + @boolToInt(array_sent);
17029 const elem_ty = array_ty.childType();
17030
17031 if (array_len_s == 0) {
17032 return sema.fail(block, elem_index_src, "indexing into empty array", .{});
17033 }
17034
17035 const maybe_undef_array_val = try sema.resolveMaybeUndefVal(block, array_src, array);
17036 // index must be defined since it can access out of bounds
17037 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
17038
17039 if (maybe_index_val) |index_val| {
17040 const index = @intCast(usize, index_val.toUnsignedInt());
17041 if (index >= array_len_s) {
17042 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
17043 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
17044 }
17045 }
17046 if (maybe_undef_array_val) |array_val| {
17047 if (array_val.isUndef()) {
17048 return sema.addConstUndef(elem_ty);
17049 }
1705617050 if (maybe_index_val) |index_val| {
1705717051 const index = @intCast(usize, index_val.toUnsignedInt());
17058 const len = array_ty.arrayLenIncludingSentinel();
17059 if (index >= len) {
17060 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}", .{
17061 index, len,
17062 });
17063 }
1706417052 const elem_val = try array_val.elemValue(sema.arena, index);
1706517053 return sema.addConstant(elem_ty, elem_val);
1706617054 }
1706717055 }
17068 try sema.requireRuntimeBlock(block, array_src);
17056
17057 const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src;
17058 try sema.requireRuntimeBlock(block, runtime_src);
17059 if (block.wantSafety()) {
17060 // Runtime check is only needed if unable to comptime check
17061 if (maybe_index_val == null) {
17062 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
17063 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
17064 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
17065 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
17066 }
17067 }
1706917068 return block.addBinOp(.array_elem_val, array, elem_index);
1707017069}
1707117070
1707217071fn elemPtrArray(
1707317072 sema: *Sema,
1707417073 block: *Block,
17075 src: LazySrcLoc,
17074 array_ptr_src: LazySrcLoc,
1707617075 array_ptr: Air.Inst.Ref,
17077 elem_index: Air.Inst.Ref,
1707817076 elem_index_src: LazySrcLoc,
17077 elem_index: Air.Inst.Ref,
1707917078) CompileError!Air.Inst.Ref {
17079 const target = sema.mod.getTarget();
1708017080 const array_ptr_ty = sema.typeOf(array_ptr);
17081 const array_ty = array_ptr_ty.childType();
17082 const array_sent = array_ty.sentinel() != null;
17083 const array_len = array_ty.arrayLen();
17084 const array_len_s = array_len + @boolToInt(array_sent);
17085 const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, target);
17086
17087 if (array_len_s == 0) {
17088 return sema.fail(block, elem_index_src, "indexing into empty array", .{});
17089 }
17090
17091 const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr);
17092 // index must be defined since it can index out of bounds
17093 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
17094
17095 if (maybe_index_val) |index_val| {
17096 const index = @intCast(usize, index_val.toUnsignedInt());
17097 if (index >= array_len_s) {
17098 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
17099 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
17100 }
17101 }
17102 if (maybe_undef_array_ptr_val) |array_ptr_val| {
17103 if (array_ptr_val.isUndef()) {
17104 return sema.addConstUndef(elem_ptr_ty);
17105 }
17106 if (maybe_index_val) |index_val| {
17107 const index = @intCast(usize, index_val.toUnsignedInt());
17108 const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index);
17109 return sema.addConstant(elem_ptr_ty, elem_ptr);
17110 }
17111 }
17112
17113 const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;
17114 try sema.requireRuntimeBlock(block, runtime_src);
17115 if (block.wantSafety()) {
17116 // Runtime check is only needed if unable to comptime check
17117 if (maybe_index_val == null) {
17118 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
17119 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
17120 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
17121 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
17122 }
17123 }
17124 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
17125}
17126
17127fn elemValSlice(
17128 sema: *Sema,
17129 block: *Block,
17130 slice_src: LazySrcLoc,
17131 slice: Air.Inst.Ref,
17132 elem_index_src: LazySrcLoc,
17133 elem_index: Air.Inst.Ref,
17134) CompileError!Air.Inst.Ref {
17135 const slice_ty = sema.typeOf(slice);
17136 const slice_sent = slice_ty.sentinel() != null;
17137 const elem_ty = slice_ty.elemType2();
17138 var runtime_src = slice_src;
17139
17140 // slice must be defined since it can dereferenced as null
17141 const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
17142 // index must be defined since it can index out of bounds
17143 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
17144
17145 if (maybe_slice_val) |slice_val| {
17146 runtime_src = elem_index_src;
17147 const slice_len = slice_val.sliceLen();
17148 const slice_len_s = slice_len + @boolToInt(slice_sent);
17149 if (slice_len_s == 0) {
17150 return sema.fail(block, elem_index_src, "indexing into empty slice", .{});
17151 }
17152 if (maybe_index_val) |index_val| {
17153 const index = @intCast(usize, index_val.toUnsignedInt());
17154 if (index >= slice_len_s) {
17155 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
17156 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
17157 }
17158 var elem_ptr_pl: Value.Payload.ElemPtr = .{ .data = .{
17159 .array_ptr = slice_val.slicePtr(),
17160 .elem_ty = elem_ty,
17161 .index = index,
17162 } };
17163 const elem_ptr_val = Value.initPayload(&elem_ptr_pl.base);
17164 if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| {
17165 return sema.addConstant(elem_ty, elem_val);
17166 }
17167 runtime_src = slice_src;
17168 }
17169 }
17170
17171 try sema.requireRuntimeBlock(block, runtime_src);
17172 if (block.wantSafety()) {
17173 const len_inst = if (maybe_slice_val) |slice_val|
17174 try sema.addIntUnsigned(Type.usize, slice_val.sliceLen())
17175 else
17176 try block.addTyOp(.slice_len, Type.usize, slice);
17177 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
17178 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
17179 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
17180 }
17181 return block.addBinOp(.slice_elem_val, slice, elem_index);
17182}
17183
17184fn elemPtrSlice(
17185 sema: *Sema,
17186 block: *Block,
17187 slice_src: LazySrcLoc,
17188 slice: Air.Inst.Ref,
17189 elem_index_src: LazySrcLoc,
17190 elem_index: Air.Inst.Ref,
17191) CompileError!Air.Inst.Ref {
1708117192 const target = sema.mod.getTarget();
17082 const result_ty = try array_ptr_ty.elemPtrType(sema.arena, target);
17083
17084 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
17085 if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {
17086 // Both array pointer and index are compile-time known.
17087 const index_u64 = index_val.toUnsignedInt();
17088 // @intCast here because it would have been impossible to construct a value that
17089 // required a larger index.
17090 const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, @intCast(usize, index_u64));
17091 return sema.addConstant(result_ty, elem_ptr);
17193 const slice_ty = sema.typeOf(slice);
17194 const slice_sent = slice_ty.sentinel() != null;
17195 const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, target);
17196
17197 const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice);
17198 // index must be defined since it can index out of bounds
17199 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
17200
17201 if (maybe_undef_slice_val) |slice_val| {
17202 if (slice_val.isUndef()) {
17203 return sema.addConstUndef(elem_ptr_ty);
17204 }
17205 const slice_len = slice_val.sliceLen();
17206 const slice_len_s = slice_len + @boolToInt(slice_sent);
17207 if (slice_len_s == 0) {
17208 return sema.fail(block, elem_index_src, "indexing into empty slice", .{});
17209 }
17210 if (maybe_index_val) |index_val| {
17211 const index = @intCast(usize, index_val.toUnsignedInt());
17212 if (index >= slice_len_s) {
17213 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
17214 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
17215 }
17216 const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index);
17217 return sema.addConstant(elem_ptr_ty, elem_ptr_val);
1709217218 }
1709317219 }
17094 // TODO safety check for array bounds
17095 try sema.requireRuntimeBlock(block, src);
17096 return block.addPtrElemPtr(array_ptr, elem_index, result_ty);
17220
17221 const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src;
17222 try sema.requireRuntimeBlock(block, runtime_src);
17223 if (block.wantSafety()) {
17224 const len_inst = len: {
17225 if (maybe_undef_slice_val) |slice_val|
17226 if (!slice_val.isUndef())
17227 break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen());
17228 break :len try block.addTyOp(.slice_len, Type.usize, slice);
17229 };
17230 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
17231 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
17232 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
17233 }
17234 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
1709717235}
1709817236
1709917237fn coerce(
......@@ -18000,7 +18138,7 @@ fn storePtr2(
1800018138 for (tuple.types) |_, i_usize| {
1800118139 const i = @intCast(u32, i_usize);
1800218140 const elem_src = operand_src; // TODO better source location
18003 const elem = try tupleField(sema, block, uncasted_operand, i, operand_src, elem_src);
18141 const elem = try tupleField(sema, block, operand_src, uncasted_operand, elem_src, i);
1800418142 const elem_index = try sema.addIntUnsigned(Type.usize, i);
1800518143 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src);
1800618144 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
......@@ -18885,7 +19023,7 @@ fn coerceArrayLike(
1888519023 try Value.Tag.int_u64.create(sema.arena, i),
1888619024 );
1888719025 const elem_src = inst_src; // TODO better source location
18888 const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);
19026 const elem_ref = try elemValArray(sema, block, inst_src, inst, elem_src, index_ref);
1888919027 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
1889019028 element_refs[i] = coerced;
1889119029 if (runtime_src == null) {
......@@ -18942,7 +19080,7 @@ fn coerceTupleToArray(
1894219080 for (element_vals) |*elem, i_usize| {
1894319081 const i = @intCast(u32, i_usize);
1894419082 const elem_src = inst_src; // TODO better source location
18945 const elem_ref = try tupleField(sema, block, inst, i, inst_src, elem_src);
19083 const elem_ref = try tupleField(sema, block, inst_src, inst, elem_src, i);
1894619084 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
1894719085 element_refs[i] = coerced;
1894819086 if (runtime_src == null) {
......@@ -19042,7 +19180,7 @@ fn coerceTupleToStruct(
1904219180 if (field.is_comptime) {
1904319181 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
1904419182 }
19045 const elem_ref = try tupleField(sema, block, inst, i, inst_src, field_src);
19183 const elem_ref = try tupleField(sema, block, inst_src, inst, field_src, i);
1904619184 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
1904719185 field_refs[field_index] = coerced;
1904819186 if (runtime_src == null) {