Commit a36f4ee290
Changed files (3)
src
test
behavior
src/Sema.zig
@@ -19713,17 +19713,31 @@ fn analyzeSlice(
if (!end_is_len) {
const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| {
- if (end_val.compare(.gt, len_val, Type.usize, target)) {
+ const len_s_val = try Value.Tag.int_u64.create(
+ sema.arena,
+ array_ty.arrayLenIncludingSentinel(),
+ );
+ if (end_val.compare(.gt, len_s_val, Type.usize, target)) {
+ const sentinel_label: []const u8 = if (array_ty.sentinel() != null)
+ " +1 (sentinel)"
+ else
+ "";
+
return sema.fail(
block,
end_src,
- "end index {} out of bounds for array of length {}",
+ "end index {} out of bounds for array of length {}{s}",
.{
end_val.fmtValue(Type.usize, target),
len_val.fmtValue(Type.usize, target),
+ sentinel_label,
},
);
}
+
+ // end_is_len is only true if we are NOT using the sentinel
+ // length. For sentinel-length, we don't want the type to
+ // contain the sentinel.
if (end_val.eql(len_val, Type.usize, target)) {
end_is_len = true;
}
@@ -19737,22 +19751,37 @@ fn analyzeSlice(
const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
+ const has_sentinel = slice_ty.sentinel() != null;
var int_payload: Value.Payload.U64 = .{
.base = .{ .tag = .int_u64 },
- .data = slice_val.sliceLen(target),
+ .data = slice_val.sliceLen(target) + @boolToInt(has_sentinel),
};
const slice_len_val = Value.initPayload(&int_payload.base);
if (end_val.compare(.gt, slice_len_val, Type.usize, target)) {
+ const sentinel_label: []const u8 = if (has_sentinel)
+ " +1 (sentinel)"
+ else
+ "";
+
return sema.fail(
block,
end_src,
- "end index {} out of bounds for slice of length {}",
+ "end index {} out of bounds for slice of length {d}{s}",
.{
end_val.fmtValue(Type.usize, target),
- slice_len_val.fmtValue(Type.usize, target),
+ slice_val.sliceLen(target),
+ sentinel_label,
},
);
}
+
+ // If the slice has a sentinel, we subtract one so that
+ // end_is_len is only true if it equals the length WITHOUT
+ // the sentinel, so we don't add a sentinel type.
+ if (has_sentinel) {
+ int_payload.data -= 1;
+ }
+
if (end_val.eql(slice_len_val, Type.usize, target)) {
end_is_len = true;
}
test/behavior/slice.zig
@@ -640,3 +640,46 @@ test "slice sentinel access at comptime" {
try expect(slice0[slice0.len] == 0);
}
}
+
+test "slicing array with sentinel as end index" {
+ // Doesn't work in stage1
+ if (builtin.zig_backend == .stage1) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+
+ const S = struct {
+ fn do() !void {
+ var array = [_:0]u8{ 1, 2, 3, 4 };
+ var slice = array[4..5];
+ try expect(slice.len == 1);
+ try expect(slice[0] == 0);
+ try expect(@TypeOf(slice) == *[1]u8);
+ }
+ };
+
+ try S.do();
+ comptime try S.do();
+}
+
+test "slicing slice with sentinel as end index" {
+ // Doesn't work in stage1
+ if (builtin.zig_backend == .stage1) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+
+ const S = struct {
+ fn do() !void {
+ var array = [_:0]u8{ 1, 2, 3, 4 };
+ var src_slice: [:0]u8 = &array;
+ var slice = src_slice[4..5];
+ try expect(slice.len == 1);
+ try expect(slice[0] == 0);
+ try expect(@TypeOf(slice) == *[1]u8);
+ }
+ };
+
+ try S.do();
+ comptime try S.do();
+}
test/compile_errors.zig
@@ -26,11 +26,16 @@ pub fn addCases(ctx: *TestContext) !void {
\\comptime {
\\ var array = [_:0]u8{ 1, 2, 3, 4 };
\\ var src_slice: [:0]u8 = &array;
- \\ var slice = src_slice[2..5];
+ \\ var slice = src_slice[2..6];
\\ _ = slice;
\\}
\\comptime {
\\ var array = [_:0]u8{ 1, 2, 3, 4 };
+ \\ var slice = array[2..6];
+ \\ _ = slice;
+ \\}
+ \\comptime {
+ \\ var array = [_]u8{ 1, 2, 3, 4 };
\\ var slice = array[2..5];
\\ _ = slice;
\\}
@@ -40,9 +45,10 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = slice;
\\}
, &[_][]const u8{
- ":4:26: error: end index 5 out of bounds for slice of length 4",
- ":9:22: error: end index 5 out of bounds for array of length 4",
- ":14:22: error: start index 3 is larger than end index 2",
+ ":4:26: error: end index 6 out of bounds for slice of length 4 +1 (sentinel)",
+ ":9:22: error: end index 6 out of bounds for array of length 4 +1 (sentinel)",
+ ":14:22: error: end index 5 out of bounds for array of length 4",
+ ":19:22: error: start index 3 is larger than end index 2",
});
}