authorgravatar for mitchell.hashimoto@gmail.comMitchell Hashimoto <mitchell.hashimoto@gmail.com> 2022-03-23 09:40:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-23 17:08:08-04:00
loga36f4ee290fa9f3f1515e8aa9bd2bb0f0117c505
tree48684ef68021b319f9ed9844c5222e998a21b9e5
parentf27d3409bdbe4a2b1b59f0b7336e582488e35986

stage2: able to slice to sentinel index at comptime

The runtime behavior allowed this in both stage1 and stage2, but stage1 fails with index out of bounds during comptime. This behavior makes sense to support, and comptime behavior should match runtime behavior. I implement this fix only in stage2.

3 files changed, 87 insertions(+), 9 deletions(-)

src/Sema.zig+34-5
......@@ -19713,17 +19713,31 @@ fn analyzeSlice(
1971319713 if (!end_is_len) {
1971419714 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
1971519715 if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| {
19716 if (end_val.compare(.gt, len_val, Type.usize, target)) {
19716 const len_s_val = try Value.Tag.int_u64.create(
19717 sema.arena,
19718 array_ty.arrayLenIncludingSentinel(),
19719 );
19720 if (end_val.compare(.gt, len_s_val, Type.usize, target)) {
19721 const sentinel_label: []const u8 = if (array_ty.sentinel() != null)
19722 " +1 (sentinel)"
19723 else
19724 "";
19725
1971719726 return sema.fail(
1971819727 block,
1971919728 end_src,
19720 "end index {} out of bounds for array of length {}",
19729 "end index {} out of bounds for array of length {}{s}",
1972119730 .{
1972219731 end_val.fmtValue(Type.usize, target),
1972319732 len_val.fmtValue(Type.usize, target),
19733 sentinel_label,
1972419734 },
1972519735 );
1972619736 }
19737
19738 // end_is_len is only true if we are NOT using the sentinel
19739 // length. For sentinel-length, we don't want the type to
19740 // contain the sentinel.
1972719741 if (end_val.eql(len_val, Type.usize, target)) {
1972819742 end_is_len = true;
1972919743 }
......@@ -19737,22 +19751,37 @@ fn analyzeSlice(
1973719751 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
1973819752 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
1973919753 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
19754 const has_sentinel = slice_ty.sentinel() != null;
1974019755 var int_payload: Value.Payload.U64 = .{
1974119756 .base = .{ .tag = .int_u64 },
19742 .data = slice_val.sliceLen(target),
19757 .data = slice_val.sliceLen(target) + @boolToInt(has_sentinel),
1974319758 };
1974419759 const slice_len_val = Value.initPayload(&int_payload.base);
1974519760 if (end_val.compare(.gt, slice_len_val, Type.usize, target)) {
19761 const sentinel_label: []const u8 = if (has_sentinel)
19762 " +1 (sentinel)"
19763 else
19764 "";
19765
1974619766 return sema.fail(
1974719767 block,
1974819768 end_src,
19749 "end index {} out of bounds for slice of length {}",
19769 "end index {} out of bounds for slice of length {d}{s}",
1975019770 .{
1975119771 end_val.fmtValue(Type.usize, target),
19752 slice_len_val.fmtValue(Type.usize, target),
19772 slice_val.sliceLen(target),
19773 sentinel_label,
1975319774 },
1975419775 );
1975519776 }
19777
19778 // If the slice has a sentinel, we subtract one so that
19779 // end_is_len is only true if it equals the length WITHOUT
19780 // the sentinel, so we don't add a sentinel type.
19781 if (has_sentinel) {
19782 int_payload.data -= 1;
19783 }
19784
1975619785 if (end_val.eql(slice_len_val, Type.usize, target)) {
1975719786 end_is_len = true;
1975819787 }
test/behavior/slice.zig+43
......@@ -640,3 +640,46 @@ test "slice sentinel access at comptime" {
640640 try expect(slice0[slice0.len] == 0);
641641 }
642642}
643
644test "slicing array with sentinel as end index" {
645 // Doesn't work in stage1
646 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
647 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
648 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
649 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
650
651 const S = struct {
652 fn do() !void {
653 var array = [_:0]u8{ 1, 2, 3, 4 };
654 var slice = array[4..5];
655 try expect(slice.len == 1);
656 try expect(slice[0] == 0);
657 try expect(@TypeOf(slice) == *[1]u8);
658 }
659 };
660
661 try S.do();
662 comptime try S.do();
663}
664
665test "slicing slice with sentinel as end index" {
666 // Doesn't work in stage1
667 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
668 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
669 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
670 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
671
672 const S = struct {
673 fn do() !void {
674 var array = [_:0]u8{ 1, 2, 3, 4 };
675 var src_slice: [:0]u8 = &array;
676 var slice = src_slice[4..5];
677 try expect(slice.len == 1);
678 try expect(slice[0] == 0);
679 try expect(@TypeOf(slice) == *[1]u8);
680 }
681 };
682
683 try S.do();
684 comptime try S.do();
685}
test/compile_errors.zig+10-4
......@@ -26,11 +26,16 @@ pub fn addCases(ctx: *TestContext) !void {
2626 \\comptime {
2727 \\ var array = [_:0]u8{ 1, 2, 3, 4 };
2828 \\ var src_slice: [:0]u8 = &array;
29 \\ var slice = src_slice[2..5];
29 \\ var slice = src_slice[2..6];
3030 \\ _ = slice;
3131 \\}
3232 \\comptime {
3333 \\ var array = [_:0]u8{ 1, 2, 3, 4 };
34 \\ var slice = array[2..6];
35 \\ _ = slice;
36 \\}
37 \\comptime {
38 \\ var array = [_]u8{ 1, 2, 3, 4 };
3439 \\ var slice = array[2..5];
3540 \\ _ = slice;
3641 \\}
......@@ -40,9 +45,10 @@ pub fn addCases(ctx: *TestContext) !void {
4045 \\ _ = slice;
4146 \\}
4247 , &[_][]const u8{
43 ":4:26: error: end index 5 out of bounds for slice of length 4",
44 ":9:22: error: end index 5 out of bounds for array of length 4",
45 ":14:22: error: start index 3 is larger than end index 2",
48 ":4:26: error: end index 6 out of bounds for slice of length 4 +1 (sentinel)",
49 ":9:22: error: end index 6 out of bounds for array of length 4 +1 (sentinel)",
50 ":14:22: error: end index 5 out of bounds for array of length 4",
51 ":19:22: error: start index 3 is larger than end index 2",
4652 });
4753 }
4854