mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 05:20:34 +00:00
add optional sentinel to slice_length ZIR
This commit is contained in:
parent
7c8d60e814
commit
64e319f555
@ -851,9 +851,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
||||
.slice => {
|
||||
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
|
||||
const lhs_node = node_datas[node].lhs;
|
||||
if (node_tags[lhs_node] == .slice_open and nodeIsTriviallyZero(tree, extra.start)) {
|
||||
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 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
|
||||
|
||||
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;
|
||||
try emitDbgStmt(gz, cursor);
|
||||
@ -862,6 +871,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
||||
.start = start,
|
||||
.len = len,
|
||||
.start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
|
||||
.sentinel = .none,
|
||||
});
|
||||
return rvalue(gz, ri, result, node);
|
||||
}
|
||||
@ -879,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
||||
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),
|
||||
.sentinel = sentinel,
|
||||
});
|
||||
return rvalue(gz, ri, result, node);
|
||||
}
|
||||
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
|
||||
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
|
||||
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);
|
||||
|
||||
@ -1315,6 +1315,16 @@ fn walkInstruction(
|
||||
extra.data.len,
|
||||
false,
|
||||
);
|
||||
var sentinel_opt: ?DocData.WalkResult = if (extra.data.sentinel != .none)
|
||||
try self.walkRef(
|
||||
file,
|
||||
parent_scope,
|
||||
parent_src,
|
||||
extra.data.sentinel,
|
||||
false,
|
||||
)
|
||||
else
|
||||
null;
|
||||
|
||||
const lhs_index = self.exprs.items.len;
|
||||
try self.exprs.append(self.arena, lhs.expr);
|
||||
@ -1322,7 +1332,12 @@ fn walkInstruction(
|
||||
try self.exprs.append(self.arena, start.expr);
|
||||
const len_index = self.exprs.items.len;
|
||||
try self.exprs.append(self.arena, len.expr);
|
||||
self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = len_index } };
|
||||
const sentinel_index = if (sentinel_opt) |sentinel| sentinel_index: {
|
||||
const index = self.exprs.items.len;
|
||||
try self.exprs.append(self.arena, sentinel.expr);
|
||||
break :sentinel_index index;
|
||||
} else null;
|
||||
self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = len_index, .sentinel = sentinel_index } };
|
||||
|
||||
return DocData.WalkResult{
|
||||
.typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
|
||||
|
||||
131
src/Sema.zig
131
src/Sema.zig
@ -9972,11 +9972,16 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
const array_ptr = try sema.resolveInst(extra.lhs);
|
||||
const start = try sema.resolveInst(extra.start);
|
||||
const len = try sema.resolveInst(extra.len);
|
||||
const sentinel = try sema.resolveInst(extra.sentinel);
|
||||
const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
|
||||
const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset };
|
||||
const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
|
||||
const sentinel_src: LazySrcLoc = if (sentinel == .none)
|
||||
.unneeded
|
||||
else
|
||||
.{ .node_offset_slice_sentinel = inst_data.src_node };
|
||||
|
||||
return sema.analyzeSlice(block, src, array_ptr, start, len, .none, .unneeded, ptr_src, start_src, end_src, true);
|
||||
return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
|
||||
}
|
||||
|
||||
fn zirSwitchCapture(
|
||||
@ -29283,17 +29288,15 @@ fn analyzeSlice(
|
||||
// we might learn of the length because it is a comptime-known slice value.
|
||||
var end_is_len = uncasted_end_opt == .none;
|
||||
const end = e: {
|
||||
if (by_length and !end_is_len) {
|
||||
if (!block.wantSafety()) break :e undefined;
|
||||
const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
|
||||
const end = try sema.coerce(block, Type.usize, uncasted_end, end_src);
|
||||
break :e end;
|
||||
} else if (array_ty.zigTypeTag() == .Array) {
|
||||
if (array_ty.zigTypeTag() == .Array) {
|
||||
const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());
|
||||
|
||||
if (!end_is_len) {
|
||||
const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
const end = if (by_length) end: {
|
||||
const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
|
||||
break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
|
||||
} else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
if (try sema.resolveMaybeUndefVal(end)) |end_val| {
|
||||
const len_s_val = try Value.Tag.int_u64.create(
|
||||
sema.arena,
|
||||
@ -29330,7 +29333,11 @@ fn analyzeSlice(
|
||||
break :e try sema.addConstant(Type.usize, len_val);
|
||||
} else if (slice_ty.isSlice()) {
|
||||
if (!end_is_len) {
|
||||
const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
const end = if (by_length) end: {
|
||||
const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
|
||||
break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
|
||||
} else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
|
||||
if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {
|
||||
if (slice_val.isUndef()) {
|
||||
@ -29399,66 +29406,64 @@ fn analyzeSlice(
|
||||
const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
|
||||
|
||||
// requirement: start <= end
|
||||
if (!by_length) {
|
||||
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
|
||||
if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
|
||||
if (!(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
|
||||
return sema.fail(
|
||||
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
|
||||
if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
|
||||
if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
|
||||
return sema.fail(
|
||||
block,
|
||||
start_src,
|
||||
"start index {} is larger than end index {}",
|
||||
.{
|
||||
start_val.fmtValue(Type.usize, mod),
|
||||
end_val.fmtValue(Type.usize, mod),
|
||||
},
|
||||
);
|
||||
}
|
||||
if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: {
|
||||
const expected_sentinel = sentinel orelse break :sentinel_check;
|
||||
const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?;
|
||||
const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?;
|
||||
const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
|
||||
|
||||
const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod);
|
||||
const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false);
|
||||
const actual_sentinel = switch (res) {
|
||||
.runtime_load => break :sentinel_check,
|
||||
.val => |v| v,
|
||||
.needed_well_defined => |ty| return sema.fail(
|
||||
block,
|
||||
start_src,
|
||||
"start index {} is larger than end index {}",
|
||||
.{
|
||||
start_val.fmtValue(Type.usize, mod),
|
||||
end_val.fmtValue(Type.usize, mod),
|
||||
},
|
||||
);
|
||||
}
|
||||
if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: {
|
||||
const expected_sentinel = sentinel orelse break :sentinel_check;
|
||||
const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?;
|
||||
const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?;
|
||||
const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
|
||||
src,
|
||||
"comptime dereference requires '{}' to have a well-defined layout, but it does not.",
|
||||
.{ty.fmt(sema.mod)},
|
||||
),
|
||||
.out_of_bounds => |ty| return sema.fail(
|
||||
block,
|
||||
end_src,
|
||||
"slice end index {d} exceeds bounds of containing decl of type '{}'",
|
||||
.{ end_int, ty.fmt(sema.mod) },
|
||||
),
|
||||
};
|
||||
|
||||
const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod);
|
||||
const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false);
|
||||
const actual_sentinel = switch (res) {
|
||||
.runtime_load => break :sentinel_check,
|
||||
.val => |v| v,
|
||||
.needed_well_defined => |ty| return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"comptime dereference requires '{}' to have a well-defined layout, but it does not.",
|
||||
.{ty.fmt(sema.mod)},
|
||||
),
|
||||
.out_of_bounds => |ty| return sema.fail(
|
||||
block,
|
||||
end_src,
|
||||
"slice end index {d} exceeds bounds of containing decl of type '{}'",
|
||||
.{ end_int, ty.fmt(sema.mod) },
|
||||
),
|
||||
if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{
|
||||
expected_sentinel.fmtValue(elem_ty, sema.mod),
|
||||
actual_sentinel.fmtValue(elem_ty, sema.mod),
|
||||
});
|
||||
|
||||
break :msg msg;
|
||||
};
|
||||
|
||||
if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{
|
||||
expected_sentinel.fmtValue(elem_ty, sema.mod),
|
||||
actual_sentinel.fmtValue(elem_ty, sema.mod),
|
||||
});
|
||||
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
}
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (block.wantSafety() and !block.is_comptime) {
|
||||
// requirement: start <= end
|
||||
try sema.panicStartLargerThanEnd(block, start, end);
|
||||
}
|
||||
if (!by_length and block.wantSafety() and !block.is_comptime) {
|
||||
// requirement: start <= end
|
||||
try sema.panicStartLargerThanEnd(block, start, end);
|
||||
}
|
||||
const new_len = if (by_length)
|
||||
try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
|
||||
|
||||
@ -570,7 +570,7 @@ pub const Inst = struct {
|
||||
/// Returns a pointer to the subslice.
|
||||
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
|
||||
slice_sentinel,
|
||||
/// Slice operation `array_ptr[start..][0..len]`. No sentinel.
|
||||
/// Slice operation `array_ptr[start..][0..len]`. Optional sentinel.
|
||||
/// Returns a pointer to the subslice.
|
||||
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
|
||||
slice_length,
|
||||
@ -2991,6 +2991,7 @@ pub const Inst = struct {
|
||||
lhs: Ref,
|
||||
start: Ref,
|
||||
len: Ref,
|
||||
sentinel: Ref,
|
||||
start_src_node_offset: i32,
|
||||
};
|
||||
|
||||
|
||||
@ -765,6 +765,10 @@ const Writer = struct {
|
||||
try self.writeInstRef(stream, extra.start);
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, extra.len);
|
||||
if (extra.sentinel != .none) {
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, extra.sentinel);
|
||||
}
|
||||
try stream.writeAll(") ");
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user