Merge pull request #22344 from Techatrix/slice-of-slice

fix incorrect slicing by length detection cases
This commit is contained in:
Andrew Kelley 2024-12-29 13:41:17 -05:00 committed by GitHub
commit 271452d225
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 111 additions and 80 deletions

View File

@ -876,100 +876,61 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
.for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false),
.slice_open => {
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
try emitDbgStmt(gz, cursor);
const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
.lhs = lhs,
.start = start,
});
return rvalue(gz, ri, result, node);
},
.slice => {
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
const lhs_node = node_datas[node].lhs;
const lhs_tag = node_tags[lhs_node];
const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
const lhs_is_open_slice = lhs_tag == .slice_open or
(lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
const start = if (lhs_is_slice_sentinel) start: {
const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
} else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
.slice_open,
.slice,
.slice_sentinel,
=> {
const full = tree.fullSlice(node).?;
if (full.ast.end != 0 and
node_tags[full.ast.sliced] == .slice_open and
nodeIsTriviallyZero(tree, full.ast.start))
{
const lhs_extra = tree.sliceOpen(full.ast.sliced).ast;
const lhs = try expr(gz, scope, .{ .rl = .ref }, lhs_extra.sliced);
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end);
const sentinel = if (full.ast.sentinel != 0) try expr(gz, scope, .{ .rl = .none }, full.ast.sentinel) else .none;
try emitDbgStmt(gz, cursor);
const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
.lhs = lhs,
.start = start,
.len = len,
.start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
.sentinel = .none,
});
return rvalue(gz, ri, result, node);
}
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
try emitDbgStmt(gz, cursor);
const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
.lhs = lhs,
.start = start,
.end = end,
});
return rvalue(gz, ri, result, node);
},
.slice_sentinel => {
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
const lhs_node = node_datas[node].lhs;
const lhs_tag = node_tags[lhs_node];
const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
const lhs_is_open_slice = lhs_tag == .slice_open or
(lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
const start = if (lhs_is_slice_sentinel) start: {
const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
} else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
try emitDbgStmt(gz, cursor);
const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
.lhs = lhs,
.start = start,
.len = len,
.start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
.start_src_node_offset = gz.nodeIndexToRelative(full.ast.sliced),
.sentinel = sentinel,
});
return rvalue(gz, ri, result, node);
}
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
const lhs = try expr(gz, scope, .{ .rl = .ref }, full.ast.sliced);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start);
const end = if (full.ast.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end) else .none;
const sentinel = if (full.ast.sentinel != 0) try expr(gz, scope, .{ .rl = .none }, full.ast.sentinel) else .none;
try emitDbgStmt(gz, cursor);
const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
.lhs = lhs,
.start = start,
.end = end,
.sentinel = sentinel,
});
return rvalue(gz, ri, result, node);
if (sentinel != .none) {
const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
.lhs = lhs,
.start = start,
.end = end,
.sentinel = sentinel,
});
return rvalue(gz, ri, result, node);
} else if (end != .none) {
const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
.lhs = lhs,
.start = start,
.end = end,
});
return rvalue(gz, ri, result, node);
} else {
const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
.lhs = lhs,
.start = start,
});
return rvalue(gz, ri, result, node);
}
},
.deref => {

View File

@ -98,6 +98,40 @@ test "comptime slice of slice preserves comptime var" {
}
}
test "open slice of open slice with sentinel" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
var slice: [:0]const u8 = "hello";
_ = &slice;
comptime assert(@TypeOf(slice[0..][0.. :0]) == [:0]const u8);
try expect(slice[0..][0.. :0].len == 5);
try expect(slice[0..][0.. :0][0] == 'h');
try expect(slice[0..][0.. :0][5] == 0);
comptime assert(@TypeOf(slice[1..][0.. :0]) == [:0]const u8);
try expect(slice[1..][0.. :0].len == 4);
try expect(slice[1..][0.. :0][0] == 'e');
try expect(slice[1..][0.. :0][4] == 0);
}
test "open slice with sentinel of slice with end index" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
var slice: [:0]const u8 = "hello";
_ = &slice;
comptime assert(@TypeOf(slice[0.. :0][0..5]) == *const [5]u8);
try expect(slice[0.. :0][0..5].len == 5);
try expect(slice[0.. :0][0..5][0] == 'h');
try expect(slice[0.. :0][0..5][4] == 'o');
comptime assert(@TypeOf(slice[0.. :0][0..5 :0]) == *const [5:0]u8);
try expect(slice[0.. :0][0..5 :0].len == 5);
try expect(slice[0.. :0][0..5 :0][0] == 'h');
try expect(slice[0.. :0][0..5 :0][5] == 0);
}
test "slice of type" {
comptime {
var types_array = [_]type{ i32, f64, type };

View File

@ -0,0 +1,18 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = stack_trace;
if (std.mem.eql(u8, message, "sentinel mismatch: expected 1, found 3")) {
std.process.exit(0);
}
std.process.exit(1);
}
pub fn main() !void {
var buf: [4:0]u8 = .{ 1, 2, 3, 4 };
const slice = buf[0..][0..2 :1];
_ = slice;
return error.TestFailed;
}
// run
// backend=llvm
// target=native

View File

@ -0,0 +1,18 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = stack_trace;
if (std.mem.eql(u8, message, "sentinel mismatch: expected 1, found 0")) {
std.process.exit(0);
}
std.process.exit(1);
}
pub fn main() !void {
var buf: [4:0]u8 = .{ 1, 2, 3, 4 };
const slice = buf[0.. :1][0..2];
_ = slice;
return error.TestFailed;
}
// run
// backend=llvm
// target=native