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(...@@ -3235,7 +3235,7 @@ fn zirValidateArrayInit(
3235 // any ZIR instructions at comptime; we need to do that here.3235 // any ZIR instructions at comptime; we need to do that here.
3236 if (array_ty.sentinel()) |sentinel_val| {3236 if (array_ty.sentinel()) |sentinel_val| {
3237 const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len);3237 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);
3239 const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val);3239 const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val);
3240 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);3240 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
3241 }3241 }
...@@ -15746,6 +15746,7 @@ pub const PanicId = enum {...@@ -15746,6 +15746,7 @@ pub const PanicId = enum {
15746 cast_to_null,15746 cast_to_null,
15747 incorrect_alignment,15747 incorrect_alignment,
15748 invalid_error_code,15748 invalid_error_code,
15749 index_out_of_bounds,
15749};15750};
1575015751
15751fn addSafetyCheck(15752fn addSafetyCheck(
...@@ -15867,6 +15868,7 @@ fn safetyPanic(...@@ -15867,6 +15868,7 @@ fn safetyPanic(
15867 .cast_to_null => "cast causes pointer to be null",15868 .cast_to_null => "cast causes pointer to be null",
15868 .incorrect_alignment => "incorrect alignment",15869 .incorrect_alignment => "incorrect alignment",
15869 .invalid_error_code => "invalid error code",15870 .invalid_error_code => "invalid error code",
15871 .index_out_of_bounds => "attempt to index out of bounds",
15870 };15872 };
1587115873
15872 const msg_inst = msg_inst: {15874 const msg_inst = msg_inst: {
...@@ -16483,10 +16485,10 @@ fn structFieldPtr(...@@ -16483,10 +16485,10 @@ fn structFieldPtr(
16483 return sema.analyzeRef(block, src, len_inst);16485 return sema.analyzeRef(block, src, len_inst);
16484 }16486 }
16485 const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);16487 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);
16487 } else if (struct_ty.isAnonStruct()) {16489 } else if (struct_ty.isAnonStruct()) {
16488 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);16490 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);
16490 }16492 }
1649116493
16492 const struct_obj = struct_ty.castTag(.@"struct").?.data;16494 const struct_obj = struct_ty.castTag(.@"struct").?.data;
...@@ -16806,67 +16808,55 @@ fn elemPtr(...@@ -16806,67 +16808,55 @@ fn elemPtr(
16806 sema: *Sema,16808 sema: *Sema,
16807 block: *Block,16809 block: *Block,
16808 src: LazySrcLoc,16810 src: LazySrcLoc,
16809 array_ptr: Air.Inst.Ref,16811 indexable_ptr: Air.Inst.Ref,
16810 elem_index: Air.Inst.Ref,16812 elem_index: Air.Inst.Ref,
16811 elem_index_src: LazySrcLoc,16813 elem_index_src: LazySrcLoc,
16812) CompileError!Air.Inst.Ref {16814) CompileError!Air.Inst.Ref {
16813 const array_ptr_src = src; // TODO better source location16815 const indexable_ptr_src = src; // TODO better source location
16814 const array_ptr_ty = sema.typeOf(array_ptr);16816 const indexable_ptr_ty = sema.typeOf(indexable_ptr);
16815 const array_ty = switch (array_ptr_ty.zigTypeTag()) {16817 const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) {
16816 .Pointer => array_ptr_ty.elemType(),16818 .Pointer => indexable_ptr_ty.elemType(),
16817 else => return sema.fail(block, array_ptr_src, "expected pointer, found '{}'", .{array_ptr_ty}),16819 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty}),
16818 };16820 };
16819 if (!array_ty.isIndexable()) {16821 if (!indexable_ty.isIndexable()) {
16820 return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});16822 return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty});
16821 }16823 }
1682216824
16823 switch (array_ty.zigTypeTag()) {16825 switch (indexable_ty.zigTypeTag()) {
16824 .Pointer => {16826 .Pointer => {
16825 // In all below cases, we have to deref the ptr operand to get the actual array pointer.16827 // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
16826 const array = try sema.analyzeLoad(block, array_ptr_src, array_ptr, array_ptr_src);16828 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
16827 const target = sema.mod.getTarget();16829 const target = sema.mod.getTarget();
16828 const result_ty = try array_ty.elemPtrType(sema.arena, target);16830 const result_ty = try indexable_ty.elemPtrType(sema.arena, target);
16829 switch (array_ty.ptrSize()) {16831 switch (indexable_ty.ptrSize()) {
16830 .Slice => {16832 .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index),
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 },
16843 .Many, .C => {16833 .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);
16845 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);16835 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
1684616836
16847 const runtime_src = rs: {16837 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;
16849 const index_val = maybe_index_val orelse break :rs elem_index_src;16839 const index_val = maybe_index_val orelse break :rs elem_index_src;
16850 const index = @intCast(usize, index_val.toUnsignedInt());16840 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);
16852 return sema.addConstant(result_ty, elem_ptr);16842 return sema.addConstant(result_ty, elem_ptr);
16853 };16843 };
1685416844
16855 try sema.requireRuntimeBlock(block, runtime_src);16845 try sema.requireRuntimeBlock(block, runtime_src);
16856 return block.addPtrElemPtr(array, elem_index, result_ty);16846 return block.addPtrElemPtr(indexable, elem_index, result_ty);
16857 },16847 },
16858 .One => {16848 .One => {
16859 assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable16849 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
16860 return sema.elemPtrArray(block, array_ptr_src, array, elem_index, elem_index_src);16850 return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index);
16861 },16851 },
16862 }16852 }
16863 },16853 },
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),
16865 .Struct => {16855 .Struct => {
16866 // Tuple field access.16856 // Tuple field access.
16867 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);16857 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
16868 const index = @intCast(u32, index_val.toUnsignedInt());16858 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);
16870 },16860 },
16871 else => unreachable,16861 else => unreachable,
16872 }16862 }
...@@ -16876,90 +16866,66 @@ fn elemVal(...@@ -16876,90 +16866,66 @@ fn elemVal(
16876 sema: *Sema,16866 sema: *Sema,
16877 block: *Block,16867 block: *Block,
16878 src: LazySrcLoc,16868 src: LazySrcLoc,
16879 array: Air.Inst.Ref,16869 indexable: Air.Inst.Ref,
16880 elem_index_uncasted: Air.Inst.Ref,16870 elem_index_uncasted: Air.Inst.Ref,
16881 elem_index_src: LazySrcLoc,16871 elem_index_src: LazySrcLoc,
16882) CompileError!Air.Inst.Ref {16872) CompileError!Air.Inst.Ref {
16883 const array_src = src; // TODO better source location16873 const indexable_src = src; // TODO better source location
16884 const array_ty = sema.typeOf(array);16874 const indexable_ty = sema.typeOf(indexable);
1688516875
16886 if (!array_ty.isIndexable()) {16876 if (!indexable_ty.isIndexable()) {
16887 return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});16877 return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty});
16888 }16878 }
1688916879
16890 // TODO in case of a vector of pointers, we need to detect whether the element16880 // TODO in case of a vector of pointers, we need to detect whether the element
16891 // index is a scalar or vector instead of unconditionally casting to usize.16881 // index is a scalar or vector instead of unconditionally casting to usize.
16892 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);16882 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
1689316883
16894 switch (array_ty.zigTypeTag()) {16884 switch (indexable_ty.zigTypeTag()) {
16895 .Pointer => switch (array_ty.ptrSize()) {16885 .Pointer => switch (indexable_ty.ptrSize()) {
16896 .Slice => {16886 .Slice => return sema.elemValSlice(block, indexable_src, indexable, elem_index_src, elem_index),
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 },
16921 .Many, .C => {16887 .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);
16923 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);16889 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
1692416890
16925 const runtime_src = rs: {16891 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;
16927 const index_val = maybe_index_val orelse break :rs elem_index_src;16893 const index_val = maybe_index_val orelse break :rs elem_index_src;
16928 const index = @intCast(usize, index_val.toUnsignedInt());16894 const index = @intCast(usize, index_val.toUnsignedInt());
16929 const elem_ty = array_ty.elemType2();16895 const elem_ty = indexable_ty.elemType2();
1693016896
16931 var payload: Value.Payload.ElemPtr = .{ .data = .{16897 var payload: Value.Payload.ElemPtr = .{ .data = .{
16932 .array_ptr = array_val,16898 .array_ptr = indexable_val,
16933 .elem_ty = elem_ty,16899 .elem_ty = elem_ty,
16934 .index = index,16900 .index = index,
16935 } };16901 } };
16936 const elem_ptr_val = Value.initPayload(&payload.base);16902 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| {
16939 return sema.addConstant(elem_ty, elem_val);16905 return sema.addConstant(elem_ty, elem_val);
16940 }16906 }
16941 break :rs array_src;16907 break :rs indexable_src;
16942 };16908 };
1694316909
16944 try sema.requireRuntimeBlock(block, runtime_src);16910 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);
16946 },16912 },
16947 .One => {16913 .One => {
16948 assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable16914 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
16949 const elem_ptr = try sema.elemPtr(block, array_src, array, elem_index, elem_index_src);16915 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src);
16950 return sema.analyzeLoad(block, array_src, elem_ptr, elem_index_src);16916 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
16951 },16917 },
16952 },16918 },
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),
16954 .Vector => {16920 .Vector => {
16955 // TODO: If the index is a vector, the result should be a vector.16921 // 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);
16957 },16923 },
16958 .Struct => {16924 .Struct => {
16959 // Tuple field access.16925 // Tuple field access.
16960 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);16926 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
16961 const index = @intCast(u32, index_val.toUnsignedInt());16927 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);
16963 },16929 },
16964 else => unreachable,16930 else => unreachable,
16965 }16931 }
...@@ -16968,22 +16934,26 @@ fn elemVal(...@@ -16968,22 +16934,26 @@ fn elemVal(
16968fn tupleFieldPtr(16934fn tupleFieldPtr(
16969 sema: *Sema,16935 sema: *Sema,
16970 block: *Block,16936 block: *Block,
16937 tuple_ptr_src: LazySrcLoc,
16971 tuple_ptr: Air.Inst.Ref,16938 tuple_ptr: Air.Inst.Ref,
16972 field_index: u32,
16973 tuple_src: LazySrcLoc,
16974 field_index_src: LazySrcLoc,16939 field_index_src: LazySrcLoc,
16940 field_index: u32,
16975) CompileError!Air.Inst.Ref {16941) CompileError!Air.Inst.Ref {
16976 const tuple_ptr_ty = sema.typeOf(tuple_ptr);16942 const tuple_ptr_ty = sema.typeOf(tuple_ptr);
16977 const tuple_ty = tuple_ptr_ty.childType();16943 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) {
16981 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{16951 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,
16983 });16953 });
16984 }16954 }
1698516955
16986 const field_ty = tuple.types[field_index];16956 const field_ty = tuple_fields.types[field_index];
16987 const target = sema.mod.getTarget();16957 const target = sema.mod.getTarget();
16988 const ptr_field_ty = try Type.ptr(sema.arena, target, .{16958 const ptr_field_ty = try Type.ptr(sema.arena, target, .{
16989 .pointee_type = field_ty,16959 .pointee_type = field_ty,
...@@ -16991,7 +16961,7 @@ fn tupleFieldPtr(...@@ -16991,7 +16961,7 @@ fn tupleFieldPtr(
16991 .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(),16961 .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(),
16992 });16962 });
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| {
16995 return sema.addConstant(16965 return sema.addConstant(
16996 ptr_field_ty,16966 ptr_field_ty,
16997 try Value.Tag.field_ptr.create(sema.arena, .{16967 try Value.Tag.field_ptr.create(sema.arena, .{
...@@ -17002,29 +16972,33 @@ fn tupleFieldPtr(...@@ -17002,29 +16972,33 @@ fn tupleFieldPtr(
17002 );16972 );
17003 }16973 }
1700416974
17005 try sema.requireRuntimeBlock(block, tuple_src);16975 try sema.requireRuntimeBlock(block, tuple_ptr_src);
17006 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);16976 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
17007}16977}
1700816978
17009fn tupleField(16979fn tupleField(
17010 sema: *Sema,16980 sema: *Sema,
17011 block: *Block,16981 block: *Block,
17012 tuple: Air.Inst.Ref,
17013 field_index: u32,
17014 tuple_src: LazySrcLoc,16982 tuple_src: LazySrcLoc,
16983 tuple: Air.Inst.Ref,
17015 field_index_src: LazySrcLoc,16984 field_index_src: LazySrcLoc,
16985 field_index: u32,
17016) CompileError!Air.Inst.Ref {16986) CompileError!Air.Inst.Ref {
17017 const tuple_ty = sema.typeOf(tuple);16987 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) {
17021 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{16995 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,
17023 });16997 });
17024 }16998 }
1702516999
17026 const field_ty = tuple_info.types[field_index];17000 const field_ty = tuple_fields.types[field_index];
17027 const field_val = tuple_info.values[field_index];17001 const field_val = tuple_fields.values[field_index];
1702817002
17029 if (field_val.tag() != .unreachable_value) {17003 if (field_val.tag() != .unreachable_value) {
17030 return sema.addConstant(field_ty, field_val); // comptime field17004 return sema.addConstant(field_ty, field_val); // comptime field
...@@ -17043,57 +17017,221 @@ fn tupleField(...@@ -17043,57 +17017,221 @@ fn tupleField(
17043fn elemValArray(17017fn elemValArray(
17044 sema: *Sema,17018 sema: *Sema,
17045 block: *Block,17019 block: *Block,
17046 array: Air.Inst.Ref,
17047 elem_index: Air.Inst.Ref,
17048 array_src: LazySrcLoc,17020 array_src: LazySrcLoc,
17021 array: Air.Inst.Ref,
17049 elem_index_src: LazySrcLoc,17022 elem_index_src: LazySrcLoc,
17023 elem_index: Air.Inst.Ref,
17050) CompileError!Air.Inst.Ref {17024) CompileError!Air.Inst.Ref {
17051 const array_ty = sema.typeOf(array);17025 const array_ty = sema.typeOf(array);
17052 if (try sema.resolveMaybeUndefVal(block, array_src, array)) |array_val| {17026 const array_sent = array_ty.sentinel() != null;
17053 const elem_ty = array_ty.childType();17027 const array_len = array_ty.arrayLen();
17054 if (array_val.isUndef()) return sema.addConstUndef(elem_ty);17028 const array_len_s = array_len + @boolToInt(array_sent);
17055 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);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 }
17056 if (maybe_index_val) |index_val| {17050 if (maybe_index_val) |index_val| {
17057 const index = @intCast(usize, index_val.toUnsignedInt());17051 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 }
17064 const elem_val = try array_val.elemValue(sema.arena, index);17052 const elem_val = try array_val.elemValue(sema.arena, index);
17065 return sema.addConstant(elem_ty, elem_val);17053 return sema.addConstant(elem_ty, elem_val);
17066 }17054 }
17067 }17055 }
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 }
17069 return block.addBinOp(.array_elem_val, array, elem_index);17068 return block.addBinOp(.array_elem_val, array, elem_index);
17070}17069}
1707117070
17072fn elemPtrArray(17071fn elemPtrArray(
17073 sema: *Sema,17072 sema: *Sema,
17074 block: *Block,17073 block: *Block,
17075 src: LazySrcLoc,17074 array_ptr_src: LazySrcLoc,
17076 array_ptr: Air.Inst.Ref,17075 array_ptr: Air.Inst.Ref,
17077 elem_index: Air.Inst.Ref,
17078 elem_index_src: LazySrcLoc,17076 elem_index_src: LazySrcLoc,
17077 elem_index: Air.Inst.Ref,
17079) CompileError!Air.Inst.Ref {17078) CompileError!Air.Inst.Ref {
17079 const target = sema.mod.getTarget();
17080 const array_ptr_ty = sema.typeOf(array_ptr);17080 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 {
17081 const target = sema.mod.getTarget();17192 const target = sema.mod.getTarget();
17082 const result_ty = try array_ptr_ty.elemPtrType(sema.arena, target);17193 const slice_ty = sema.typeOf(slice);
1708317194 const slice_sent = slice_ty.sentinel() != null;
17084 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {17195 const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, target);
17085 if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {17196
17086 // Both array pointer and index are compile-time known.17197 const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice);
17087 const index_u64 = index_val.toUnsignedInt();17198 // index must be defined since it can index out of bounds
17088 // @intCast here because it would have been impossible to construct a value that17199 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
17089 // required a larger index.17200
17090 const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, @intCast(usize, index_u64));17201 if (maybe_undef_slice_val) |slice_val| {
17091 return sema.addConstant(result_ty, elem_ptr);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);
17092 }17218 }
17093 }17219 }
17094 // TODO safety check for array bounds17220
17095 try sema.requireRuntimeBlock(block, src);17221 const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src;
17096 return block.addPtrElemPtr(array_ptr, elem_index, result_ty);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);
17097}17235}
1709817236
17099fn coerce(17237fn coerce(
...@@ -18000,7 +18138,7 @@ fn storePtr2(...@@ -18000,7 +18138,7 @@ fn storePtr2(
18000 for (tuple.types) |_, i_usize| {18138 for (tuple.types) |_, i_usize| {
18001 const i = @intCast(u32, i_usize);18139 const i = @intCast(u32, i_usize);
18002 const elem_src = operand_src; // TODO better source location18140 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);
18004 const elem_index = try sema.addIntUnsigned(Type.usize, i);18142 const elem_index = try sema.addIntUnsigned(Type.usize, i);
18005 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src);18143 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src);
18006 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);18144 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
...@@ -18885,7 +19023,7 @@ fn coerceArrayLike(...@@ -18885,7 +19023,7 @@ fn coerceArrayLike(
18885 try Value.Tag.int_u64.create(sema.arena, i),19023 try Value.Tag.int_u64.create(sema.arena, i),
18886 );19024 );
18887 const elem_src = inst_src; // TODO better source location19025 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);
18889 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);19027 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
18890 element_refs[i] = coerced;19028 element_refs[i] = coerced;
18891 if (runtime_src == null) {19029 if (runtime_src == null) {
...@@ -18942,7 +19080,7 @@ fn coerceTupleToArray(...@@ -18942,7 +19080,7 @@ fn coerceTupleToArray(
18942 for (element_vals) |*elem, i_usize| {19080 for (element_vals) |*elem, i_usize| {
18943 const i = @intCast(u32, i_usize);19081 const i = @intCast(u32, i_usize);
18944 const elem_src = inst_src; // TODO better source location19082 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);
18946 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);19084 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
18947 element_refs[i] = coerced;19085 element_refs[i] = coerced;
18948 if (runtime_src == null) {19086 if (runtime_src == null) {
...@@ -19042,7 +19180,7 @@ fn coerceTupleToStruct(...@@ -19042,7 +19180,7 @@ fn coerceTupleToStruct(
19042 if (field.is_comptime) {19180 if (field.is_comptime) {
19043 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});19181 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
19044 }19182 }
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);
19046 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);19184 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
19047 field_refs[field_index] = coerced;19185 field_refs[field_index] = coerced;
19048 if (runtime_src == null) {19186 if (runtime_src == null) {