mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 06:45:24 +00:00
Sema: check comptime slice sentinel
This commit is contained in:
parent
d26d696ee0
commit
02dc073260
96
src/Sema.zig
96
src/Sema.zig
@ -25150,7 +25150,10 @@ fn analyzeSlice(
|
||||
if (!end_is_len) {
|
||||
const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
|
||||
if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
|
||||
if (try sema.resolveMaybeUndefVal(block, src, ptr_or_slice)) |slice_val| {
|
||||
if (slice_val.isUndef()) {
|
||||
return sema.fail(block, src, "slice of undefined", .{});
|
||||
}
|
||||
const has_sentinel = slice_ty.sentinel() != null;
|
||||
var int_payload: Value.Payload.U64 = .{
|
||||
.base = .{ .tag = .int_u64 },
|
||||
@ -25213,8 +25216,8 @@ fn analyzeSlice(
|
||||
};
|
||||
|
||||
// requirement: start <= end
|
||||
if (try sema.resolveDefinedValue(block, src, end)) |end_val| {
|
||||
if (try sema.resolveDefinedValue(block, src, start)) |start_val| {
|
||||
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
|
||||
if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
|
||||
if (try sema.compare(block, src, start_val, .gt, end_val, Type.usize)) {
|
||||
return sema.fail(
|
||||
block,
|
||||
@ -25226,6 +25229,45 @@ fn analyzeSlice(
|
||||
},
|
||||
);
|
||||
}
|
||||
if (try sema.resolveMaybeUndefVal(block, ptr_src, 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,
|
||||
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;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27866,9 +27908,36 @@ pub fn analyzeAddrspace(
|
||||
/// Returns `null` if the pointer contents cannot be loaded at comptime.
|
||||
fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
|
||||
const load_ty = ptr_ty.childType();
|
||||
const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty, true);
|
||||
switch (res) {
|
||||
.runtime_load => return null,
|
||||
.val => |v| return 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,
|
||||
src,
|
||||
"dereference of '{}' exceeds bounds of containing decl of type '{}'",
|
||||
.{ ptr_ty.fmt(sema.mod), ty.fmt(sema.mod) },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const DerefResult = union(enum) {
|
||||
runtime_load,
|
||||
val: Value,
|
||||
needed_well_defined: Type,
|
||||
out_of_bounds: Type,
|
||||
};
|
||||
|
||||
fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type, want_mutable: bool) CompileError!DerefResult {
|
||||
const target = sema.mod.getTarget();
|
||||
const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
|
||||
error.RuntimeLoad => return null,
|
||||
error.RuntimeLoad => return DerefResult{ .runtime_load = {} },
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
@ -27879,39 +27948,40 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr
|
||||
if (coerce_in_mem_ok) {
|
||||
// We have a Value that lines up in virtual memory exactly with what we want to load,
|
||||
// and it is in-memory coercible to load_ty. It may be returned without modifications.
|
||||
if (deref.is_mutable) {
|
||||
if (deref.is_mutable and want_mutable) {
|
||||
// The decl whose value we are obtaining here may be overwritten with
|
||||
// a different value upon further semantic analysis, which would
|
||||
// invalidate this memory. So we must copy here.
|
||||
return try tv.val.copy(sema.arena);
|
||||
return DerefResult{ .val = try tv.val.copy(sema.arena) };
|
||||
}
|
||||
return tv.val;
|
||||
return DerefResult{ .val = tv.val };
|
||||
}
|
||||
}
|
||||
|
||||
// The type is not in-memory coercible or the direct dereference failed, so it must
|
||||
// be bitcast according to the pointer type we are performing the load through.
|
||||
if (!load_ty.hasWellDefinedLayout())
|
||||
return sema.fail(block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", .{load_ty.fmt(sema.mod)});
|
||||
if (!load_ty.hasWellDefinedLayout()) {
|
||||
return DerefResult{ .needed_well_defined = load_ty };
|
||||
}
|
||||
|
||||
const load_sz = try sema.typeAbiSize(block, src, load_ty);
|
||||
|
||||
// Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
|
||||
if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(block, src, tv.ty))
|
||||
return try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0);
|
||||
return DerefResult{ .val = try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0) };
|
||||
|
||||
// If that fails, try to bit-cast from the largest parent value with a well-defined layout
|
||||
if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(block, src, parent.tv.ty))
|
||||
return try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset);
|
||||
return DerefResult{ .val = try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset) };
|
||||
|
||||
if (deref.ty_without_well_defined_layout) |bad_ty| {
|
||||
// We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
|
||||
// is that some type we encountered when de-referencing does not have a well-defined layout.
|
||||
return sema.fail(block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", .{bad_ty.fmt(sema.mod)});
|
||||
return DerefResult{ .needed_well_defined = bad_ty };
|
||||
} else {
|
||||
// If all encountered types had well-defined layouts, the parent is the root decl and it just
|
||||
// wasn't big enough for the load.
|
||||
return sema.fail(block, src, "dereference of '{}' exceeds bounds of containing decl of type '{}'", .{ ptr_ty.fmt(sema.mod), deref.parent.?.tv.ty.fmt(sema.mod) });
|
||||
return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -55,13 +55,20 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: slice-sentinel does not match memory at target index
|
||||
// :12:29: error: slice-sentinel does not match memory at target index
|
||||
// :20:29: error: slice-sentinel does not match memory at target index
|
||||
// :28:29: error: slice-sentinel does not match memory at target index
|
||||
// :36:29: error: slice-sentinel does not match memory at target index
|
||||
// :44:29: error: slice-sentinel does not match memory at target index
|
||||
// :52:29: error: slice-sentinel does not match memory at target index
|
||||
// :4:29: error: value in memory does not match slice sentinel
|
||||
// :4:29: note: expected '0', found '100'
|
||||
// :12:29: error: value in memory does not match slice sentinel
|
||||
// :12:29: note: expected '0', found '100'
|
||||
// :20:29: error: value in memory does not match slice sentinel
|
||||
// :20:29: note: expected '0', found '100'
|
||||
// :28:29: error: value in memory does not match slice sentinel
|
||||
// :28:29: note: expected '0', found '100'
|
||||
// :36:29: error: value in memory does not match slice sentinel
|
||||
// :36:29: note: expected '0', found '100'
|
||||
// :44:29: error: value in memory does not match slice sentinel
|
||||
// :44:29: note: expected '0', found '100'
|
||||
// :52:29: error: value in memory does not match slice sentinel
|
||||
// :52:29: note: expected '0', found '100'
|
||||
@ -55,13 +55,20 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: slice-sentinel does not match memory at target index
|
||||
// :12:29: error: slice-sentinel does not match memory at target index
|
||||
// :20:29: error: slice-sentinel does not match memory at target index
|
||||
// :28:29: error: slice-sentinel does not match memory at target index
|
||||
// :36:29: error: slice-sentinel does not match memory at target index
|
||||
// :44:29: error: slice-sentinel does not match memory at target index
|
||||
// :52:29: error: slice-sentinel does not match memory at target index
|
||||
// :4:29: error: value in memory does not match slice sentinel
|
||||
// :4:29: note: expected '0', found '100'
|
||||
// :12:29: error: value in memory does not match slice sentinel
|
||||
// :12:29: note: expected '0', found '100'
|
||||
// :20:29: error: value in memory does not match slice sentinel
|
||||
// :20:29: note: expected '0', found '100'
|
||||
// :28:29: error: value in memory does not match slice sentinel
|
||||
// :28:29: note: expected '0', found '100'
|
||||
// :36:29: error: value in memory does not match slice sentinel
|
||||
// :36:29: note: expected '0', found '100'
|
||||
// :44:29: error: value in memory does not match slice sentinel
|
||||
// :44:29: note: expected '0', found '100'
|
||||
// :52:29: error: value in memory does not match slice sentinel
|
||||
// :52:29: note: expected '0', found '100'
|
||||
@ -55,13 +55,20 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: slice-sentinel does not match target-sentinel
|
||||
// :12:29: error: slice-sentinel does not match target-sentinel
|
||||
// :20:29: error: slice-sentinel does not match target-sentinel
|
||||
// :28:29: error: slice-sentinel does not match target-sentinel
|
||||
// :36:29: error: slice-sentinel does not match target-sentinel
|
||||
// :44:29: error: slice-sentinel does not match target-sentinel
|
||||
// :52:29: error: slice-sentinel does not match target-sentinel
|
||||
// :4:29: error: value in memory does not match slice sentinel
|
||||
// :4:29: note: expected '255', found '0'
|
||||
// :12:29: error: value in memory does not match slice sentinel
|
||||
// :12:29: note: expected '255', found '0'
|
||||
// :20:29: error: value in memory does not match slice sentinel
|
||||
// :20:29: note: expected '255', found '0'
|
||||
// :28:29: error: value in memory does not match slice sentinel
|
||||
// :28:29: note: expected '255', found '0'
|
||||
// :36:29: error: value in memory does not match slice sentinel
|
||||
// :36:29: note: expected '255', found '0'
|
||||
// :44:29: error: value in memory does not match slice sentinel
|
||||
// :44:29: note: expected '255', found '0'
|
||||
// :52:29: error: value in memory does not match slice sentinel
|
||||
// :52:29: note: expected '255', found '0'
|
||||
@ -55,13 +55,13 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: out of bounds slice
|
||||
// :12:29: error: out of bounds slice
|
||||
// :20:29: error: out of bounds slice
|
||||
// :28:29: error: out of bounds slice
|
||||
// :36:29: error: out of bounds slice
|
||||
// :44:29: error: out of bounds slice
|
||||
// :52:29: error: out of bounds slice
|
||||
// :4:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :12:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :20:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :28:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :36:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :44:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :52:33: error: end index 15 out of bounds for slice of length 14
|
||||
@ -55,13 +55,13 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: slice-sentinel is out of bounds
|
||||
// :12:29: error: slice-sentinel is out of bounds
|
||||
// :20:29: error: slice-sentinel is out of bounds
|
||||
// :28:29: error: slice-sentinel is out of bounds
|
||||
// :36:29: error: slice-sentinel is out of bounds
|
||||
// :44:29: error: slice-sentinel is out of bounds
|
||||
// :52:29: error: slice-sentinel is out of bounds
|
||||
// :4:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :12:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :20:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :28:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :36:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :44:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :52:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
@ -5,7 +5,7 @@ comptime {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:3:14: error: slice of undefined
|
||||
// :3:14: error: slice of undefined
|
||||
Loading…
x
Reference in New Issue
Block a user