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(...@@ -19713,17 +19713,31 @@ fn analyzeSlice(
19713 if (!end_is_len) {19713 if (!end_is_len) {
19714 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);19714 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
19715 if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| {19715 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
19717 return sema.fail(19726 return sema.fail(
19718 block,19727 block,
19719 end_src,19728 end_src,
19720 "end index {} out of bounds for array of length {}",19729 "end index {} out of bounds for array of length {}{s}",
19721 .{19730 .{
19722 end_val.fmtValue(Type.usize, target),19731 end_val.fmtValue(Type.usize, target),
19723 len_val.fmtValue(Type.usize, target),19732 len_val.fmtValue(Type.usize, target),
19733 sentinel_label,
19724 },19734 },
19725 );19735 );
19726 }19736 }
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.
19727 if (end_val.eql(len_val, Type.usize, target)) {19741 if (end_val.eql(len_val, Type.usize, target)) {
19728 end_is_len = true;19742 end_is_len = true;
19729 }19743 }
...@@ -19737,22 +19751,37 @@ fn analyzeSlice(...@@ -19737,22 +19751,37 @@ fn analyzeSlice(
19737 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);19751 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
19738 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {19752 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
19739 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {19753 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
19754 const has_sentinel = slice_ty.sentinel() != null;
19740 var int_payload: Value.Payload.U64 = .{19755 var int_payload: Value.Payload.U64 = .{
19741 .base = .{ .tag = .int_u64 },19756 .base = .{ .tag = .int_u64 },
19742 .data = slice_val.sliceLen(target),19757 .data = slice_val.sliceLen(target) + @boolToInt(has_sentinel),
19743 };19758 };
19744 const slice_len_val = Value.initPayload(&int_payload.base);19759 const slice_len_val = Value.initPayload(&int_payload.base);
19745 if (end_val.compare(.gt, slice_len_val, Type.usize, target)) {19760 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
19746 return sema.fail(19766 return sema.fail(
19747 block,19767 block,
19748 end_src,19768 end_src,
19749 "end index {} out of bounds for slice of length {}",19769 "end index {} out of bounds for slice of length {d}{s}",
19750 .{19770 .{
19751 end_val.fmtValue(Type.usize, target),19771 end_val.fmtValue(Type.usize, target),
19752 slice_len_val.fmtValue(Type.usize, target),19772 slice_val.sliceLen(target),
19773 sentinel_label,
19753 },19774 },
19754 );19775 );
19755 }19776 }
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
19756 if (end_val.eql(slice_len_val, Type.usize, target)) {19785 if (end_val.eql(slice_len_val, Type.usize, target)) {
19757 end_is_len = true;19786 end_is_len = true;
19758 }19787 }
test/behavior/slice.zig+43
...@@ -640,3 +640,46 @@ test "slice sentinel access at comptime" {...@@ -640,3 +640,46 @@ test "slice sentinel access at comptime" {
640 try expect(slice0[slice0.len] == 0);640 try expect(slice0[slice0.len] == 0);
641 }641 }
642}642}
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 {...@@ -26,11 +26,16 @@ pub fn addCases(ctx: *TestContext) !void {
26 \\comptime {26 \\comptime {
27 \\ var array = [_:0]u8{ 1, 2, 3, 4 };27 \\ var array = [_:0]u8{ 1, 2, 3, 4 };
28 \\ var src_slice: [:0]u8 = &array;28 \\ var src_slice: [:0]u8 = &array;
29 \\ var slice = src_slice[2..5];29 \\ var slice = src_slice[2..6];
30 \\ _ = slice;30 \\ _ = slice;
31 \\}31 \\}
32 \\comptime {32 \\comptime {
33 \\ var array = [_:0]u8{ 1, 2, 3, 4 };33 \\ 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 };
34 \\ var slice = array[2..5];39 \\ var slice = array[2..5];
35 \\ _ = slice;40 \\ _ = slice;
36 \\}41 \\}
...@@ -40,9 +45,10 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -40,9 +45,10 @@ pub fn addCases(ctx: *TestContext) !void {
40 \\ _ = slice;45 \\ _ = slice;
41 \\}46 \\}
42 , &[_][]const u8{47 , &[_][]const u8{
43 ":4:26: error: end index 5 out of bounds for slice of length 4",48 ":4:26: error: end index 6 out of bounds for slice of length 4 +1 (sentinel)",
44 ":9:22: error: end index 5 out of bounds for array of length 4",49 ":9:22: error: end index 6 out of bounds for array of length 4 +1 (sentinel)",
45 ":14:22: error: start index 3 is larger than end index 2",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",
46 });52 });
47 }53 }
4854