authorgravatar for 4678790+dweiller@users.noreply.github.comDominic <4678790+dweiller@users.noreply.github.com> 2023-04-30 23:50:08+10:00
committergravatar for 4678790+dweiller@users.noreply.github.comDominic <4678790+dweiller@users.noreply.github.com> 2023-05-07 15:55:21+10:00
log64e319f5555a25dbf4c4d7e65289f7f96d6588b4
tree773b95b9e4f6468714ea5a0daaf0f806d34df66b
parent7c8d60e814b985d3aab43c7c467f424596942ef9

add optional sentinel to slice_length ZIR


5 files changed, 129 insertions(+), 68 deletions(-)

src/AstGen.zig+39-3
...@@ -851,9 +851,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -851,9 +851,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
851 .slice => {851 .slice => {
852 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);852 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
853 const lhs_node = node_datas[node].lhs;853 const lhs_node = node_datas[node].lhs;
854 if (node_tags[lhs_node] == .slice_open and nodeIsTriviallyZero(tree, extra.start)) {854 const lhs_tag = node_tags[lhs_node];
855 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
856 const lhs_is_open_slice = lhs_tag == .slice_open or
857 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
858 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
855 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);859 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
856 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);860
861 const start = if (lhs_is_slice_sentinel) start: {
862 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
863 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
864 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
865
857 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);866 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
858 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;867 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
859 try emitDbgStmt(gz, cursor);868 try emitDbgStmt(gz, cursor);
...@@ -862,6 +871,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -862,6 +871,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
862 .start = start,871 .start = start,
863 .len = len,872 .len = len,
864 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),873 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
874 .sentinel = .none,
865 });875 });
866 return rvalue(gz, ri, result, node);876 return rvalue(gz, ri, result, node);
867 }877 }
...@@ -879,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -879,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
879 return rvalue(gz, ri, result, node);889 return rvalue(gz, ri, result, node);
880 },890 },
881 .slice_sentinel => {891 .slice_sentinel => {
892 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
893 const lhs_node = node_datas[node].lhs;
894 const lhs_tag = node_tags[lhs_node];
895 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
896 const lhs_is_open_slice = lhs_tag == .slice_open or
897 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
898 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
899 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
900
901 const start = if (lhs_is_slice_sentinel) start: {
902 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
903 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
904 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
905
906 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
907 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
908 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
909 try emitDbgStmt(gz, cursor);
910 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
911 .lhs = lhs,
912 .start = start,
913 .len = len,
914 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
915 .sentinel = sentinel,
916 });
917 return rvalue(gz, ri, result, node);
918 }
882 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);919 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
883920
884 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);921 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
885 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
886 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);922 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
887 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;923 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
888 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);924 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
src/Autodoc.zig+16-1
...@@ -1315,6 +1315,16 @@ fn walkInstruction(...@@ -1315,6 +1315,16 @@ fn walkInstruction(
1315 extra.data.len,1315 extra.data.len,
1316 false,1316 false,
1317 );1317 );
1318 var sentinel_opt: ?DocData.WalkResult = if (extra.data.sentinel != .none)
1319 try self.walkRef(
1320 file,
1321 parent_scope,
1322 parent_src,
1323 extra.data.sentinel,
1324 false,
1325 )
1326 else
1327 null;
13181328
1319 const lhs_index = self.exprs.items.len;1329 const lhs_index = self.exprs.items.len;
1320 try self.exprs.append(self.arena, lhs.expr);1330 try self.exprs.append(self.arena, lhs.expr);
...@@ -1322,7 +1332,12 @@ fn walkInstruction(...@@ -1322,7 +1332,12 @@ fn walkInstruction(
1322 try self.exprs.append(self.arena, start.expr);1332 try self.exprs.append(self.arena, start.expr);
1323 const len_index = self.exprs.items.len;1333 const len_index = self.exprs.items.len;
1324 try self.exprs.append(self.arena, len.expr);1334 try self.exprs.append(self.arena, len.expr);
1325 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = len_index } };1335 const sentinel_index = if (sentinel_opt) |sentinel| sentinel_index: {
1336 const index = self.exprs.items.len;
1337 try self.exprs.append(self.arena, sentinel.expr);
1338 break :sentinel_index index;
1339 } else null;
1340 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = len_index, .sentinel = sentinel_index } };
13261341
1327 return DocData.WalkResult{1342 return DocData.WalkResult{
1328 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,1343 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
src/Sema.zig+68-63
...@@ -9972,11 +9972,16 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9972,11 +9972,16 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9972 const array_ptr = try sema.resolveInst(extra.lhs);9972 const array_ptr = try sema.resolveInst(extra.lhs);
9973 const start = try sema.resolveInst(extra.start);9973 const start = try sema.resolveInst(extra.start);
9974 const len = try sema.resolveInst(extra.len);9974 const len = try sema.resolveInst(extra.len);
9975 const sentinel = try sema.resolveInst(extra.sentinel);
9975 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };9976 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
9976 const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset };9977 const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset };
9977 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };9978 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
9979 const sentinel_src: LazySrcLoc = if (sentinel == .none)
9980 .unneeded
9981 else
9982 .{ .node_offset_slice_sentinel = inst_data.src_node };
99789983
9979 return sema.analyzeSlice(block, src, array_ptr, start, len, .none, .unneeded, ptr_src, start_src, end_src, true);9984 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
9980}9985}
99819986
9982fn zirSwitchCapture(9987fn zirSwitchCapture(
...@@ -29283,17 +29288,15 @@ fn analyzeSlice(...@@ -29283,17 +29288,15 @@ fn analyzeSlice(
29283 // we might learn of the length because it is a comptime-known slice value.29288 // we might learn of the length because it is a comptime-known slice value.
29284 var end_is_len = uncasted_end_opt == .none;29289 var end_is_len = uncasted_end_opt == .none;
29285 const end = e: {29290 const end = e: {
29286 if (by_length and !end_is_len) {29291 if (array_ty.zigTypeTag() == .Array) {
29287 if (!block.wantSafety()) break :e undefined;
29288 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29289 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29290 const end = try sema.coerce(block, Type.usize, uncasted_end, end_src);
29291 break :e end;
29292 } else if (array_ty.zigTypeTag() == .Array) {
29293 const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());29292 const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());
2929429293
29295 if (!end_is_len) {29294 if (!end_is_len) {
29296 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);29295 const end = if (by_length) end: {
29296 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29297 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29298 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
29299 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29297 if (try sema.resolveMaybeUndefVal(end)) |end_val| {29300 if (try sema.resolveMaybeUndefVal(end)) |end_val| {
29298 const len_s_val = try Value.Tag.int_u64.create(29301 const len_s_val = try Value.Tag.int_u64.create(
29299 sema.arena,29302 sema.arena,
...@@ -29330,7 +29333,11 @@ fn analyzeSlice(...@@ -29330,7 +29333,11 @@ fn analyzeSlice(
29330 break :e try sema.addConstant(Type.usize, len_val);29333 break :e try sema.addConstant(Type.usize, len_val);
29331 } else if (slice_ty.isSlice()) {29334 } else if (slice_ty.isSlice()) {
29332 if (!end_is_len) {29335 if (!end_is_len) {
29333 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);29336 const end = if (by_length) end: {
29337 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29338 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29339 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
29340 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29334 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {29341 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
29335 if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {29342 if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {
29336 if (slice_val.isUndef()) {29343 if (slice_val.isUndef()) {
...@@ -29399,66 +29406,64 @@ fn analyzeSlice(...@@ -29399,66 +29406,64 @@ fn analyzeSlice(
29399 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;29406 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
2940029407
29401 // requirement: start <= end29408 // requirement: start <= end
29402 if (!by_length) {29409 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
29403 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {29410 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
29404 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {29411 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
29405 if (!(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {29412 return sema.fail(
29406 return sema.fail(29413 block,
29414 start_src,
29415 "start index {} is larger than end index {}",
29416 .{
29417 start_val.fmtValue(Type.usize, mod),
29418 end_val.fmtValue(Type.usize, mod),
29419 },
29420 );
29421 }
29422 if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: {
29423 const expected_sentinel = sentinel orelse break :sentinel_check;
29424 const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?;
29425 const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?;
29426 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
29427
29428 const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod);
29429 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false);
29430 const actual_sentinel = switch (res) {
29431 .runtime_load => break :sentinel_check,
29432 .val => |v| v,
29433 .needed_well_defined => |ty| return sema.fail(
29407 block,29434 block,
29408 start_src,29435 src,
29409 "start index {} is larger than end index {}",29436 "comptime dereference requires '{}' to have a well-defined layout, but it does not.",
29410 .{29437 .{ty.fmt(sema.mod)},
29411 start_val.fmtValue(Type.usize, mod),29438 ),
29412 end_val.fmtValue(Type.usize, mod),29439 .out_of_bounds => |ty| return sema.fail(
29413 },29440 block,
29414 );29441 end_src,
29415 }29442 "slice end index {d} exceeds bounds of containing decl of type '{}'",
29416 if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: {29443 .{ end_int, ty.fmt(sema.mod) },
29417 const expected_sentinel = sentinel orelse break :sentinel_check;29444 ),
29418 const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?;29445 };
29419 const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?;
29420 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
29421
29422 const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod);
29423 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false);
29424 const actual_sentinel = switch (res) {
29425 .runtime_load => break :sentinel_check,
29426 .val => |v| v,
29427 .needed_well_defined => |ty| return sema.fail(
29428 block,
29429 src,
29430 "comptime dereference requires '{}' to have a well-defined layout, but it does not.",
29431 .{ty.fmt(sema.mod)},
29432 ),
29433 .out_of_bounds => |ty| return sema.fail(
29434 block,
29435 end_src,
29436 "slice end index {d} exceeds bounds of containing decl of type '{}'",
29437 .{ end_int, ty.fmt(sema.mod) },
29438 ),
29439 };
2944029446
29441 if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) {29447 if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) {
29442 const msg = msg: {29448 const msg = msg: {
29443 const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{});29449 const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{});
29444 errdefer msg.destroy(sema.gpa);29450 errdefer msg.destroy(sema.gpa);
29445 try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{29451 try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{
29446 expected_sentinel.fmtValue(elem_ty, sema.mod),29452 expected_sentinel.fmtValue(elem_ty, sema.mod),
29447 actual_sentinel.fmtValue(elem_ty, sema.mod),29453 actual_sentinel.fmtValue(elem_ty, sema.mod),
29448 });29454 });
2944929455
29450 break :msg msg;29456 break :msg msg;
29451 };29457 };
29452 return sema.failWithOwnedErrorMsg(msg);29458 return sema.failWithOwnedErrorMsg(msg);
29453 }
29454 }29459 }
29455 }29460 }
29456 }29461 }
29462 }
2945729463
29458 if (block.wantSafety() and !block.is_comptime) {29464 if (!by_length and block.wantSafety() and !block.is_comptime) {
29459 // requirement: start <= end29465 // requirement: start <= end
29460 try sema.panicStartLargerThanEnd(block, start, end);29466 try sema.panicStartLargerThanEnd(block, start, end);
29461 }
29462 }29467 }
29463 const new_len = if (by_length)29468 const new_len = if (by_length)
29464 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)29469 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
src/Zir.zig+2-1
...@@ -570,7 +570,7 @@ pub const Inst = struct {...@@ -570,7 +570,7 @@ pub const Inst = struct {
570 /// Returns a pointer to the subslice.570 /// Returns a pointer to the subslice.
571 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.571 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
572 slice_sentinel,572 slice_sentinel,
573 /// Slice operation `array_ptr[start..][0..len]`. No sentinel.573 /// Slice operation `array_ptr[start..][0..len]`. Optional sentinel.
574 /// Returns a pointer to the subslice.574 /// Returns a pointer to the subslice.
575 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.575 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
576 slice_length,576 slice_length,
...@@ -2991,6 +2991,7 @@ pub const Inst = struct {...@@ -2991,6 +2991,7 @@ pub const Inst = struct {
2991 lhs: Ref,2991 lhs: Ref,
2992 start: Ref,2992 start: Ref,
2993 len: Ref,2993 len: Ref,
2994 sentinel: Ref,
2994 start_src_node_offset: i32,2995 start_src_node_offset: i32,
2995 };2996 };
29962997
src/print_zir.zig+4
...@@ -765,6 +765,10 @@ const Writer = struct {...@@ -765,6 +765,10 @@ const Writer = struct {
765 try self.writeInstRef(stream, extra.start);765 try self.writeInstRef(stream, extra.start);
766 try stream.writeAll(", ");766 try stream.writeAll(", ");
767 try self.writeInstRef(stream, extra.len);767 try self.writeInstRef(stream, extra.len);
768 if (extra.sentinel != .none) {
769 try stream.writeAll(", ");
770 try self.writeInstRef(stream, extra.sentinel);
771 }
768 try stream.writeAll(") ");772 try stream.writeAll(") ");
769 try self.writeSrc(stream, inst_data.src());773 try self.writeSrc(stream, inst_data.src());
770 }774 }