From 19fc5f4fb29a525252b2eaf3f6388d07f97bd32f Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreply.github.com> Date: Tue, 31 Dec 2024 16:07:08 +1100 Subject: [PATCH] Sema: disallow slicing many-item pointer with different sentinel This change prevents adding or changing the sentinel in the type of a many-item pointer via the slicing syntax `ptr[a.. :S]`. --- src/Sema.zig | 32 +++++++++++++++++++ test/behavior/slice.zig | 3 -- ...f_many-item_pointer_preserves_sentinel.zig | 18 +++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 test/cases/compile_errors/slice_of_many-item_pointer_preserves_sentinel.zig diff --git a/src/Sema.zig b/src/Sema.zig index 94bf21e03b..9345c4bcac 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -31876,6 +31876,38 @@ fn analyzeSlice( break :e try sema.coerce(block, .usize, uncasted_end, end_src); } else break :e try sema.coerce(block, .usize, uncasted_end_opt, end_src); } + + // when slicing a many-item pointer, if a sentinel `S` is provided as in `ptr[a.. :S]`, it + // must match the sentinel of `@TypeOf(ptr)`. + sentinel_check: { + if (sentinel_opt == .none) break :sentinel_check; + const provided = provided: { + const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); + try checkSentinelType(sema, block, sentinel_src, elem_ty); + break :provided try sema.resolveConstDefinedValue( + block, + sentinel_src, + casted, + .{ .simple = .slice_sentinel }, + ); + }; + + if (ptr_sentinel) |current| { + if (provided.toIntern() == current.toIntern()) break :sentinel_check; + } + + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(sentinel_src, "sentinel-terminated slicing of many-item pointer must match existing sentinel", .{}); + errdefer msg.destroy(sema.gpa); + if (ptr_sentinel) |current| { + try sema.errNote(sentinel_src, msg, "expected sentinel '{f}', found '{f}'", .{ current.fmtValue(pt), provided.fmtValue(pt) }); + } else { + try sema.errNote(ptr_src, msg, "type '{f}' does not have a sentinel", .{slice_ty.fmt(pt)}); + } + try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{}); + break :msg msg; + }); + } return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); }; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 25b501e841..22e4e06dfe 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -628,9 +628,6 @@ test "slice syntax resulting in pointer-to-array" { comptime assert(@TypeOf(ptr[1..][0..2]) == *[2]u8); comptime assert(@TypeOf(ptr[1..][0..4]) == *[4]u8); comptime assert(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8); - comptime assert(@TypeOf(ptr[1.. :0][0..2]) == *[2]u8); - comptime assert(@TypeOf(ptr[1.. :0][0..4]) == *[4]u8); - comptime assert(@TypeOf(ptr[1.. :0][0..2 :4]) == *[2:4]u8); var ptr_z: [*:0]u8 = &array; comptime assert(@TypeOf(ptr_z[1..][0..2]) == *[2]u8); diff --git a/test/cases/compile_errors/slice_of_many-item_pointer_preserves_sentinel.zig b/test/cases/compile_errors/slice_of_many-item_pointer_preserves_sentinel.zig new file mode 100644 index 0000000000..02fbb5289d --- /dev/null +++ b/test/cases/compile_errors/slice_of_many-item_pointer_preserves_sentinel.zig @@ -0,0 +1,18 @@ +comptime { + var ptr: [*]const u8 = undefined; + _ = ptr[0.. :0]; +} + +comptime { + var ptrz: [*:0]const u8 = undefined; + _ = ptrz[0.. :1]; +} + +// error +// +// :3:18: error: sentinel-terminated slicing of many-item pointer must match existing sentinel +// :3:9: note: type '[*]const u8' does not have a sentinel +// :3:12: note: use @ptrCast to cast pointer sentinel +// :8:19: error: sentinel-terminated slicing of many-item pointer must match existing sentinel +// :8:19: note: expected sentinel '0', found '1' +// :8:13: note: use @ptrCast to cast pointer sentinel