diff --git a/src/Sema.zig b/src/Sema.zig index fe10810d57..7e87ebbf33 100644 --- a/src/Sema.zig +++ b/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; } diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 432524ebf7..9f3ba001cf 100644 --- a/test/behavior/slice.zig +++ b/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(); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c479dd9c88..5008bdd7b8 100644 --- a/test/compile_errors.zig +++ b/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", }); }