aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Sema.zig396
1 files changed, 267 insertions, 129 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index eb14542a97..e7c6ae0719 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -3235,7 +3235,7 @@ fn zirValidateArrayInit(
// any ZIR instructions at comptime; we need to do that here.
if (array_ty.sentinel()) |sentinel_val| {
const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len);
- const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, array_len_ref, init_src);
+ const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, init_src, array_len_ref);
const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val);
try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
}
@@ -15746,6 +15746,7 @@ pub const PanicId = enum {
cast_to_null,
incorrect_alignment,
invalid_error_code,
+ index_out_of_bounds,
};
fn addSafetyCheck(
@@ -15867,6 +15868,7 @@ fn safetyPanic(
.cast_to_null => "cast causes pointer to be null",
.incorrect_alignment => "incorrect alignment",
.invalid_error_code => "invalid error code",
+ .index_out_of_bounds => "attempt to index out of bounds",
};
const msg_inst = msg_inst: {
@@ -16483,10 +16485,10 @@ fn structFieldPtr(
return sema.analyzeRef(block, src, len_inst);
}
const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
- return sema.tupleFieldPtr(block, struct_ptr, field_index, src, field_name_src);
+ return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index);
} else if (struct_ty.isAnonStruct()) {
const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
- return sema.tupleFieldPtr(block, struct_ptr, field_index, src, field_name_src);
+ return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index);
}
const struct_obj = struct_ty.castTag(.@"struct").?.data;
@@ -16806,67 +16808,55 @@ fn elemPtr(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- array_ptr: Air.Inst.Ref,
+ indexable_ptr: Air.Inst.Ref,
elem_index: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
- const array_ptr_src = src; // TODO better source location
- const array_ptr_ty = sema.typeOf(array_ptr);
- const array_ty = switch (array_ptr_ty.zigTypeTag()) {
- .Pointer => array_ptr_ty.elemType(),
- else => return sema.fail(block, array_ptr_src, "expected pointer, found '{}'", .{array_ptr_ty}),
+ const indexable_ptr_src = src; // TODO better source location
+ const indexable_ptr_ty = sema.typeOf(indexable_ptr);
+ const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) {
+ .Pointer => indexable_ptr_ty.elemType(),
+ else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty}),
};
- if (!array_ty.isIndexable()) {
- return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});
+ if (!indexable_ty.isIndexable()) {
+ return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty});
}
- switch (array_ty.zigTypeTag()) {
+ switch (indexable_ty.zigTypeTag()) {
.Pointer => {
- // In all below cases, we have to deref the ptr operand to get the actual array pointer.
- const array = try sema.analyzeLoad(block, array_ptr_src, array_ptr, array_ptr_src);
+ // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
+ const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
const target = sema.mod.getTarget();
- const result_ty = try array_ty.elemPtrType(sema.arena, target);
- switch (array_ty.ptrSize()) {
- .Slice => {
- const maybe_slice_val = try sema.resolveDefinedValue(block, array_ptr_src, array);
- const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
- const runtime_src = if (maybe_slice_val) |slice_val| rs: {
- const index_val = maybe_index_val orelse break :rs elem_index_src;
- const index = @intCast(usize, index_val.toUnsignedInt());
- const elem_ptr = try slice_val.elemPtr(array_ty, sema.arena, index);
- return sema.addConstant(result_ty, elem_ptr);
- } else array_ptr_src;
-
- try sema.requireRuntimeBlock(block, runtime_src);
- return block.addSliceElemPtr(array, elem_index, result_ty);
- },
+ const result_ty = try indexable_ty.elemPtrType(sema.arena, target);
+ switch (indexable_ty.ptrSize()) {
+ .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index),
.Many, .C => {
- const maybe_ptr_val = try sema.resolveDefinedValue(block, array_ptr_src, array);
+ const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable);
const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
const runtime_src = rs: {
- const ptr_val = maybe_ptr_val orelse break :rs array_ptr_src;
+ const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;
const index_val = maybe_index_val orelse break :rs elem_index_src;
const index = @intCast(usize, index_val.toUnsignedInt());
- const elem_ptr = try ptr_val.elemPtr(array_ty, sema.arena, index);
+ const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index);
return sema.addConstant(result_ty, elem_ptr);
};
try sema.requireRuntimeBlock(block, runtime_src);
- return block.addPtrElemPtr(array, elem_index, result_ty);
+ return block.addPtrElemPtr(indexable, elem_index, result_ty);
},
.One => {
- assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
- return sema.elemPtrArray(block, array_ptr_src, array, elem_index, elem_index_src);
+ assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
+ return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index);
},
}
},
- .Array, .Vector => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src),
+ .Array, .Vector => return sema.elemPtrArray(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index),
.Struct => {
// Tuple field access.
const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
const index = @intCast(u32, index_val.toUnsignedInt());
- return sema.tupleFieldPtr(block, array_ptr, index, src, elem_index_src);
+ return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index);
},
else => unreachable,
}
@@ -16876,90 +16866,66 @@ fn elemVal(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- array: Air.Inst.Ref,
+ indexable: Air.Inst.Ref,
elem_index_uncasted: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
- const array_src = src; // TODO better source location
- const array_ty = sema.typeOf(array);
+ const indexable_src = src; // TODO better source location
+ const indexable_ty = sema.typeOf(indexable);
- if (!array_ty.isIndexable()) {
- return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});
+ if (!indexable_ty.isIndexable()) {
+ return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty});
}
// TODO in case of a vector of pointers, we need to detect whether the element
// index is a scalar or vector instead of unconditionally casting to usize.
const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
- switch (array_ty.zigTypeTag()) {
- .Pointer => switch (array_ty.ptrSize()) {
- .Slice => {
- const maybe_slice_val = try sema.resolveDefinedValue(block, array_src, array);
- const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
- const runtime_src = if (maybe_slice_val) |slice_val| rs: {
- const index_val = maybe_index_val orelse break :rs elem_index_src;
- const index = @intCast(usize, index_val.toUnsignedInt());
-
- const elem_ty = array_ty.elemType2();
-
- var payload: Value.Payload.ElemPtr = .{ .data = .{
- .array_ptr = slice_val.slicePtr(),
- .elem_ty = elem_ty,
- .index = index,
- } };
- const elem_ptr_val = Value.initPayload(&payload.base);
-
- if (try sema.pointerDeref(block, array_src, elem_ptr_val, array_ty)) |elem_val| {
- return sema.addConstant(elem_ty, elem_val);
- }
- break :rs array_src;
- } else array_src;
-
- try sema.requireRuntimeBlock(block, runtime_src);
- return block.addBinOp(.slice_elem_val, array, elem_index);
- },
+ switch (indexable_ty.zigTypeTag()) {
+ .Pointer => switch (indexable_ty.ptrSize()) {
+ .Slice => return sema.elemValSlice(block, indexable_src, indexable, elem_index_src, elem_index),
.Many, .C => {
- const maybe_array_val = try sema.resolveDefinedValue(block, array_src, array);
+ const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
const runtime_src = rs: {
- const array_val = maybe_array_val orelse break :rs array_src;
+ const indexable_val = maybe_indexable_val orelse break :rs indexable_src;
const index_val = maybe_index_val orelse break :rs elem_index_src;
const index = @intCast(usize, index_val.toUnsignedInt());
- const elem_ty = array_ty.elemType2();
+ const elem_ty = indexable_ty.elemType2();
var payload: Value.Payload.ElemPtr = .{ .data = .{
- .array_ptr = array_val,
+ .array_ptr = indexable_val,
.elem_ty = elem_ty,
.index = index,
} };
const elem_ptr_val = Value.initPayload(&payload.base);
- if (try sema.pointerDeref(block, array_src, elem_ptr_val, array_ty)) |elem_val| {
+ if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| {
return sema.addConstant(elem_ty, elem_val);
}
- break :rs array_src;
+ break :rs indexable_src;
};
try sema.requireRuntimeBlock(block, runtime_src);
- return block.addBinOp(.ptr_elem_val, array, elem_index);
+ return block.addBinOp(.ptr_elem_val, indexable, elem_index);
},
.One => {
- assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
- const elem_ptr = try sema.elemPtr(block, array_src, array, elem_index, elem_index_src);
- return sema.analyzeLoad(block, array_src, elem_ptr, elem_index_src);
+ assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
+ const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src);
+ return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
},
},
- .Array => return elemValArray(sema, block, array, elem_index, array_src, elem_index_src),
+ .Array => return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index),
.Vector => {
// TODO: If the index is a vector, the result should be a vector.
- return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);
+ return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index);
},
.Struct => {
// Tuple field access.
const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
const index = @intCast(u32, index_val.toUnsignedInt());
- return tupleField(sema, block, array, index, array_src, elem_index_src);
+ return tupleField(sema, block, indexable_src, indexable, elem_index_src, index);
},
else => unreachable,
}
@@ -16968,22 +16934,26 @@ fn elemVal(
fn tupleFieldPtr(
sema: *Sema,
block: *Block,
+ tuple_ptr_src: LazySrcLoc,
tuple_ptr: Air.Inst.Ref,
- field_index: u32,
- tuple_src: LazySrcLoc,
field_index_src: LazySrcLoc,
+ field_index: u32,
) CompileError!Air.Inst.Ref {
const tuple_ptr_ty = sema.typeOf(tuple_ptr);
const tuple_ty = tuple_ptr_ty.childType();
- const tuple = tuple_ty.tupleFields();
+ const tuple_fields = tuple_ty.tupleFields();
+
+ if (tuple_fields.types.len == 0) {
+ return sema.fail(block, field_index_src, "indexing into empty tuple", .{});
+ }
- if (field_index > tuple.types.len) {
+ if (field_index >= tuple_fields.types.len) {
return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
- field_index, tuple.types.len,
+ field_index, tuple_fields.types.len,
});
}
- const field_ty = tuple.types[field_index];
+ const field_ty = tuple_fields.types[field_index];
const target = sema.mod.getTarget();
const ptr_field_ty = try Type.ptr(sema.arena, target, .{
.pointee_type = field_ty,
@@ -16991,7 +16961,7 @@ fn tupleFieldPtr(
.@"addrspace" = tuple_ptr_ty.ptrAddressSpace(),
});
- if (try sema.resolveMaybeUndefVal(block, tuple_src, tuple_ptr)) |tuple_ptr_val| {
+ if (try sema.resolveMaybeUndefVal(block, tuple_ptr_src, tuple_ptr)) |tuple_ptr_val| {
return sema.addConstant(
ptr_field_ty,
try Value.Tag.field_ptr.create(sema.arena, .{
@@ -17002,29 +16972,33 @@ fn tupleFieldPtr(
);
}
- try sema.requireRuntimeBlock(block, tuple_src);
+ try sema.requireRuntimeBlock(block, tuple_ptr_src);
return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
}
fn tupleField(
sema: *Sema,
block: *Block,
- tuple: Air.Inst.Ref,
- field_index: u32,
tuple_src: LazySrcLoc,
+ tuple: Air.Inst.Ref,
field_index_src: LazySrcLoc,
+ field_index: u32,
) CompileError!Air.Inst.Ref {
const tuple_ty = sema.typeOf(tuple);
- const tuple_info = tuple_ty.tupleFields();
+ const tuple_fields = tuple_ty.tupleFields();
+
+ if (tuple_fields.types.len == 0) {
+ return sema.fail(block, field_index_src, "indexing into empty tuple", .{});
+ }
- if (field_index > tuple_info.types.len) {
+ if (field_index >= tuple_fields.types.len) {
return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
- field_index, tuple_info.types.len,
+ field_index, tuple_fields.types.len,
});
}
- const field_ty = tuple_info.types[field_index];
- const field_val = tuple_info.values[field_index];
+ const field_ty = tuple_fields.types[field_index];
+ const field_val = tuple_fields.values[field_index];
if (field_val.tag() != .unreachable_value) {
return sema.addConstant(field_ty, field_val); // comptime field
@@ -17043,57 +17017,221 @@ fn tupleField(
fn elemValArray(
sema: *Sema,
block: *Block,
- array: Air.Inst.Ref,
- elem_index: Air.Inst.Ref,
array_src: LazySrcLoc,
+ array: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
) CompileError!Air.Inst.Ref {
const array_ty = sema.typeOf(array);
- if (try sema.resolveMaybeUndefVal(block, array_src, array)) |array_val| {
- const elem_ty = array_ty.childType();
- if (array_val.isUndef()) return sema.addConstUndef(elem_ty);
- const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
+ const array_sent = array_ty.sentinel() != null;
+ const array_len = array_ty.arrayLen();
+ const array_len_s = array_len + @boolToInt(array_sent);
+ const elem_ty = array_ty.childType();
+
+ if (array_len_s == 0) {
+ return sema.fail(block, elem_index_src, "indexing into empty array", .{});
+ }
+
+ const maybe_undef_array_val = try sema.resolveMaybeUndefVal(block, array_src, array);
+ // index must be defined since it can access out of bounds
+ const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
+
+ if (maybe_index_val) |index_val| {
+ const index = @intCast(usize, index_val.toUnsignedInt());
+ if (index >= array_len_s) {
+ const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
+ return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
+ }
+ }
+ if (maybe_undef_array_val) |array_val| {
+ if (array_val.isUndef()) {
+ return sema.addConstUndef(elem_ty);
+ }
if (maybe_index_val) |index_val| {
const index = @intCast(usize, index_val.toUnsignedInt());
- const len = array_ty.arrayLenIncludingSentinel();
- if (index >= len) {
- return sema.fail(block, elem_index_src, "index {d} outside array of length {d}", .{
- index, len,
- });
- }
const elem_val = try array_val.elemValue(sema.arena, index);
return sema.addConstant(elem_ty, elem_val);
}
}
- try sema.requireRuntimeBlock(block, array_src);
+
+ const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src;
+ try sema.requireRuntimeBlock(block, runtime_src);
+ if (block.wantSafety()) {
+ // Runtime check is only needed if unable to comptime check
+ if (maybe_index_val == null) {
+ const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
+ const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
+ const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
+ try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
+ }
+ }
return block.addBinOp(.array_elem_val, array, elem_index);
}
fn elemPtrArray(
sema: *Sema,
block: *Block,
- src: LazySrcLoc,
+ array_ptr_src: LazySrcLoc,
array_ptr: Air.Inst.Ref,
- elem_index: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
) CompileError!Air.Inst.Ref {
+ const target = sema.mod.getTarget();
const array_ptr_ty = sema.typeOf(array_ptr);
+ const array_ty = array_ptr_ty.childType();
+ const array_sent = array_ty.sentinel() != null;
+ const array_len = array_ty.arrayLen();
+ const array_len_s = array_len + @boolToInt(array_sent);
+ const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, target);
+
+ if (array_len_s == 0) {
+ return sema.fail(block, elem_index_src, "indexing into empty array", .{});
+ }
+
+ const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr);
+ // index must be defined since it can index out of bounds
+ const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
+
+ if (maybe_index_val) |index_val| {
+ const index = @intCast(usize, index_val.toUnsignedInt());
+ if (index >= array_len_s) {
+ const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
+ return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
+ }
+ }
+ if (maybe_undef_array_ptr_val) |array_ptr_val| {
+ if (array_ptr_val.isUndef()) {
+ return sema.addConstUndef(elem_ptr_ty);
+ }
+ if (maybe_index_val) |index_val| {
+ const index = @intCast(usize, index_val.toUnsignedInt());
+ const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index);
+ return sema.addConstant(elem_ptr_ty, elem_ptr);
+ }
+ }
+
+ const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;
+ try sema.requireRuntimeBlock(block, runtime_src);
+ if (block.wantSafety()) {
+ // Runtime check is only needed if unable to comptime check
+ if (maybe_index_val == null) {
+ const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
+ const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
+ const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
+ try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
+ }
+ }
+ return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
+}
+
+fn elemValSlice(
+ sema: *Sema,
+ block: *Block,
+ slice_src: LazySrcLoc,
+ slice: Air.Inst.Ref,
+ elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
+) CompileError!Air.Inst.Ref {
+ const slice_ty = sema.typeOf(slice);
+ const slice_sent = slice_ty.sentinel() != null;
+ const elem_ty = slice_ty.elemType2();
+ var runtime_src = slice_src;
+
+ // slice must be defined since it can dereferenced as null
+ const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
+ // index must be defined since it can index out of bounds
+ const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
+
+ if (maybe_slice_val) |slice_val| {
+ runtime_src = elem_index_src;
+ const slice_len = slice_val.sliceLen();
+ const slice_len_s = slice_len + @boolToInt(slice_sent);
+ if (slice_len_s == 0) {
+ return sema.fail(block, elem_index_src, "indexing into empty slice", .{});
+ }
+ if (maybe_index_val) |index_val| {
+ const index = @intCast(usize, index_val.toUnsignedInt());
+ if (index >= slice_len_s) {
+ const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
+ return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
+ }
+ var elem_ptr_pl: Value.Payload.ElemPtr = .{ .data = .{
+ .array_ptr = slice_val.slicePtr(),
+ .elem_ty = elem_ty,
+ .index = index,
+ } };
+ const elem_ptr_val = Value.initPayload(&elem_ptr_pl.base);
+ if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| {
+ return sema.addConstant(elem_ty, elem_val);
+ }
+ runtime_src = slice_src;
+ }
+ }
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ if (block.wantSafety()) {
+ const len_inst = if (maybe_slice_val) |slice_val|
+ try sema.addIntUnsigned(Type.usize, slice_val.sliceLen())
+ else
+ try block.addTyOp(.slice_len, Type.usize, slice);
+ const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
+ const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
+ try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
+ }
+ return block.addBinOp(.slice_elem_val, slice, elem_index);
+}
+
+fn elemPtrSlice(
+ sema: *Sema,
+ block: *Block,
+ slice_src: LazySrcLoc,
+ slice: Air.Inst.Ref,
+ elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
+) CompileError!Air.Inst.Ref {
const target = sema.mod.getTarget();
- const result_ty = try array_ptr_ty.elemPtrType(sema.arena, target);
-
- if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
- if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {
- // Both array pointer and index are compile-time known.
- const index_u64 = index_val.toUnsignedInt();
- // @intCast here because it would have been impossible to construct a value that
- // required a larger index.
- const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, @intCast(usize, index_u64));
- return sema.addConstant(result_ty, elem_ptr);
+ const slice_ty = sema.typeOf(slice);
+ const slice_sent = slice_ty.sentinel() != null;
+ const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, target);
+
+ const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice);
+ // index must be defined since it can index out of bounds
+ const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
+
+ if (maybe_undef_slice_val) |slice_val| {
+ if (slice_val.isUndef()) {
+ return sema.addConstUndef(elem_ptr_ty);
+ }
+ const slice_len = slice_val.sliceLen();
+ const slice_len_s = slice_len + @boolToInt(slice_sent);
+ if (slice_len_s == 0) {
+ return sema.fail(block, elem_index_src, "indexing into empty slice", .{});
+ }
+ if (maybe_index_val) |index_val| {
+ const index = @intCast(usize, index_val.toUnsignedInt());
+ if (index >= slice_len_s) {
+ const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
+ return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
+ }
+ const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index);
+ return sema.addConstant(elem_ptr_ty, elem_ptr_val);
}
}
- // TODO safety check for array bounds
- try sema.requireRuntimeBlock(block, src);
- return block.addPtrElemPtr(array_ptr, elem_index, result_ty);
+
+ const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src;
+ try sema.requireRuntimeBlock(block, runtime_src);
+ if (block.wantSafety()) {
+ const len_inst = len: {
+ if (maybe_undef_slice_val) |slice_val|
+ if (!slice_val.isUndef())
+ break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen());
+ break :len try block.addTyOp(.slice_len, Type.usize, slice);
+ };
+ const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
+ const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
+ try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
+ }
+ return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
}
fn coerce(
@@ -18000,7 +18138,7 @@ fn storePtr2(
for (tuple.types) |_, i_usize| {
const i = @intCast(u32, i_usize);
const elem_src = operand_src; // TODO better source location
- const elem = try tupleField(sema, block, uncasted_operand, i, operand_src, elem_src);
+ const elem = try tupleField(sema, block, operand_src, uncasted_operand, elem_src, i);
const elem_index = try sema.addIntUnsigned(Type.usize, i);
const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src);
try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
@@ -18885,7 +19023,7 @@ fn coerceArrayLike(
try Value.Tag.int_u64.create(sema.arena, i),
);
const elem_src = inst_src; // TODO better source location
- const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);
+ const elem_ref = try elemValArray(sema, block, inst_src, inst, elem_src, index_ref);
const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
element_refs[i] = coerced;
if (runtime_src == null) {
@@ -18942,7 +19080,7 @@ fn coerceTupleToArray(
for (element_vals) |*elem, i_usize| {
const i = @intCast(u32, i_usize);
const elem_src = inst_src; // TODO better source location
- const elem_ref = try tupleField(sema, block, inst, i, inst_src, elem_src);
+ const elem_ref = try tupleField(sema, block, inst_src, inst, elem_src, i);
const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
element_refs[i] = coerced;
if (runtime_src == null) {
@@ -19042,7 +19180,7 @@ fn coerceTupleToStruct(
if (field.is_comptime) {
return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
}
- const elem_ref = try tupleField(sema, block, inst, i, inst_src, field_src);
+ const elem_ref = try tupleField(sema, block, inst_src, inst, field_src, i);
const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
field_refs[field_index] = coerced;
if (runtime_src == null) {