From 2e40a1d22e6837fdfbe8d56065a7ca03a5270601 Mon Sep 17 00:00:00 2001 From: Techatrix Date: Sun, 29 Dec 2024 00:47:24 +0100 Subject: [PATCH 1/3] simplify AstGen handling of slicing syntax --- lib/std/zig/AstGen.zig | 108 +++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 70 deletions(-) diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 0f3c527a65ca..4ec5d3c60eff 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -876,100 +876,68 @@ 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]; + .slice_open, + .slice, + .slice_sentinel, + => { + const full = tree.fullSlice(node).?; + const lhs_tag = node_tags[full.ast.sliced]; 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); + (lhs_is_slice_sentinel and tree.fullSlice(full.ast.sliced).?.ast.end == 0); + if (node_tags[node] != .slice_open and + lhs_is_open_slice and + nodeIsTriviallyZero(tree, full.ast.start)) + { + const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[full.ast.sliced].lhs); const start = if (lhs_is_slice_sentinel) start: { - const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); + const lhs_extra = tree.extraData(node_datas[full.ast.sliced].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); + } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[full.ast.sliced].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 len = 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_length, node, Zir.Inst.SliceLength{ .lhs = lhs, .start = start, .len = len, - .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), - .sentinel = .none, + .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 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); + 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_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{ + if (sentinel != .none) { + const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ .lhs = lhs, .start = start, - .len = len, - .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), + .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); } - 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 = 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_sentinel, node, Zir.Inst.SliceSentinel{ - .lhs = lhs, - .start = start, - .end = end, - .sentinel = sentinel, - }); - return rvalue(gz, ri, result, node); }, .deref => { From 5d51d4474a0c61f08c264c03ecbdf651d91afe82 Mon Sep 17 00:00:00 2001 From: Techatrix Date: Sun, 29 Dec 2024 05:53:47 +0100 Subject: [PATCH 2/3] fix slice of slice with sentinel but no end index example: ```zig test { var foo = ""; _ = foo[0..][0.. :0]; } ``` A `.slice_sentinel` ast node may not have an end index. --- lib/std/zig/AstGen.zig | 4 ++-- test/behavior/slice.zig | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 4ec5d3c60eff..90d88d0d780e 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -885,7 +885,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE 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.fullSlice(full.ast.sliced).?.ast.end == 0); - if (node_tags[node] != .slice_open and + if (full.ast.end != 0 and lhs_is_open_slice and nodeIsTriviallyZero(tree, full.ast.start)) { @@ -897,7 +897,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[full.ast.sliced].rhs); const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); - const len = if (full.ast.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.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{ diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 4f5367ad852f..7c03bb8a204a 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -98,6 +98,23 @@ 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 "slice of type" { comptime { var types_array = [_]type{ i32, f64, type }; From 5b6326ec6540d1b7af8de1176f657672be72a1e4 Mon Sep 17 00:00:00 2001 From: Techatrix Date: Sun, 29 Dec 2024 06:47:15 +0100 Subject: [PATCH 3/3] fix slice of slice with sentinel on the lhs slice example: ```zig test { var foo: [2:0]u8 = .{ 1, 2 }; _ = foo[0.. :1][0..2]; } ``` A `.slice_open` ast node will not have a end index nor sentinel. --- lib/std/zig/AstGen.zig | 15 ++++----------- test/behavior/slice.zig | 17 +++++++++++++++++ ...lice by length sentinel mismatch on lhs.zig | 18 ++++++++++++++++++ ...lice by length sentinel mismatch on rhs.zig | 18 ++++++++++++++++++ 4 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 test/cases/safety/slice by length sentinel mismatch on lhs.zig create mode 100644 test/cases/safety/slice by length sentinel mismatch on rhs.zig diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 90d88d0d780e..6e424b597f3c 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -881,21 +881,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice_sentinel, => { const full = tree.fullSlice(node).?; - const lhs_tag = node_tags[full.ast.sliced]; - 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.fullSlice(full.ast.sliced).?.ast.end == 0); if (full.ast.end != 0 and - lhs_is_open_slice and + node_tags[full.ast.sliced] == .slice_open and nodeIsTriviallyZero(tree, full.ast.start)) { - const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[full.ast.sliced].lhs); - - const start = if (lhs_is_slice_sentinel) start: { - const lhs_extra = tree.extraData(node_datas[full.ast.sliced].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[full.ast.sliced].rhs); + 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 = 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; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 7c03bb8a204a..52680b87f35e 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -115,6 +115,23 @@ test "open slice of open slice with sentinel" { 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 }; diff --git a/test/cases/safety/slice by length sentinel mismatch on lhs.zig b/test/cases/safety/slice by length sentinel mismatch on lhs.zig new file mode 100644 index 000000000000..70fa97f48ab9 --- /dev/null +++ b/test/cases/safety/slice by length sentinel mismatch on lhs.zig @@ -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 diff --git a/test/cases/safety/slice by length sentinel mismatch on rhs.zig b/test/cases/safety/slice by length sentinel mismatch on rhs.zig new file mode 100644 index 000000000000..d3cff477289c --- /dev/null +++ b/test/cases/safety/slice by length sentinel mismatch on rhs.zig @@ -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