Skip to content

Commit a958314

Browse files
authored
Rollup merge of #69839 - RalfJung:miri-error-cleanup, r=oli-obk
Miri error reform Some time ago we started moving Miri errors into a few distinct categories, but we never classified all the old errors. That's what this PR does. ~~This is on top of #69762; [relative diff](RalfJung/rust@validity-errors...RalfJung:miri-error-cleanup).~~ r? @oli-obk Fixes rust-lang/const-eval#4
2 parents 23b79d8 + e219dd4 commit a958314

28 files changed

+420
-470
lines changed

src/librustc/mir/interpret/allocation.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub struct Allocation<Tag = (), Extra = ()> {
4141
/// The size of the allocation. Currently, must always equal `bytes.len()`.
4242
pub size: Size,
4343
/// The alignment of the allocation to detect unaligned reads.
44+
/// (`Align` guarantees that this is a power of two.)
4445
pub align: Align,
4546
/// `true` if the allocation is mutable.
4647
/// Also used by codegen to determine if a static should be put into mutable memory,
@@ -314,7 +315,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
314315
&self.get_bytes(cx, ptr, size_with_null)?[..size]
315316
}
316317
// This includes the case where `offset` is out-of-bounds to begin with.
317-
None => throw_unsup!(UnterminatedCString(ptr.erase_tag())),
318+
None => throw_ub!(UnterminatedCString(ptr.erase_tag())),
318319
})
319320
}
320321

@@ -573,7 +574,7 @@ impl<'tcx, Tag, Extra> Allocation<Tag, Extra> {
573574
fn check_defined(&self, ptr: Pointer<Tag>, size: Size) -> InterpResult<'tcx> {
574575
self.undef_mask
575576
.is_range_defined(ptr.offset, ptr.offset + size)
576-
.or_else(|idx| throw_unsup!(ReadUndefBytes(idx)))
577+
.or_else(|idx| throw_ub!(InvalidUndefBytes(Some(Pointer::new(ptr.alloc_id, idx)))))
577578
}
578579

579580
pub fn mark_definedness(&mut self, ptr: Pointer<Tag>, size: Size, new_state: bool) {

src/librustc/mir/interpret/error.rs

+117-186
Large diffs are not rendered by default.

src/librustc/mir/interpret/mod.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,13 @@ pub struct AllocId(pub u64);
161161

162162
impl fmt::Debug for AllocId {
163163
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
164-
write!(fmt, "alloc{}", self.0)
164+
fmt::Display::fmt(self, fmt)
165+
}
166+
}
167+
168+
impl fmt::Display for AllocId {
169+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170+
write!(f, "alloc{}", self.0)
165171
}
166172
}
167173

@@ -351,12 +357,6 @@ impl<'s> AllocDecodingSession<'s> {
351357
}
352358
}
353359

354-
impl fmt::Display for AllocId {
355-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356-
write!(f, "{}", self.0)
357-
}
358-
}
359-
360360
/// An allocation in the global (tcx-managed) memory can be either a function pointer,
361361
/// a static, or a "real" allocation with some data in it.
362362
#[derive(Debug, Clone, Eq, PartialEq, Hash, RustcDecodable, RustcEncodable, HashStable)]

src/librustc/mir/interpret/pointer.rs

-16
Original file line numberDiff line numberDiff line change
@@ -213,20 +213,4 @@ impl<'tcx, Tag> Pointer<Tag> {
213213
pub fn erase_tag(self) -> Pointer {
214214
Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () }
215215
}
216-
217-
/// Test if the pointer is "inbounds" of an allocation of the given size.
218-
/// A pointer is "inbounds" even if its offset is equal to the size; this is
219-
/// a "one-past-the-end" pointer.
220-
#[inline(always)]
221-
pub fn check_inbounds_alloc(
222-
self,
223-
allocation_size: Size,
224-
msg: CheckInAllocMsg,
225-
) -> InterpResult<'tcx, ()> {
226-
if self.offset > allocation_size {
227-
throw_unsup!(PointerOutOfBounds { ptr: self.erase_tag(), msg, allocation_size })
228-
} else {
229-
Ok(())
230-
}
231-
}
232216
}

src/librustc/mir/interpret/value.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -455,18 +455,19 @@ impl<'tcx, Tag> Scalar<Tag> {
455455
}
456456

457457
pub fn to_bool(self) -> InterpResult<'tcx, bool> {
458-
match self {
459-
Scalar::Raw { data: 0, size: 1 } => Ok(false),
460-
Scalar::Raw { data: 1, size: 1 } => Ok(true),
461-
_ => throw_unsup!(InvalidBool),
458+
let val = self.to_u8()?;
459+
match val {
460+
0 => Ok(false),
461+
1 => Ok(true),
462+
_ => throw_ub!(InvalidBool(val)),
462463
}
463464
}
464465

465466
pub fn to_char(self) -> InterpResult<'tcx, char> {
466467
let val = self.to_u32()?;
467468
match ::std::char::from_u32(val) {
468469
Some(c) => Ok(c),
469-
None => throw_unsup!(InvalidChar(val as u128)),
470+
None => throw_ub!(InvalidChar(val)),
470471
}
471472
}
472473

@@ -609,7 +610,7 @@ impl<'tcx, Tag> ScalarMaybeUndef<Tag> {
609610
pub fn not_undef(self) -> InterpResult<'static, Scalar<Tag>> {
610611
match self {
611612
ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
612-
ScalarMaybeUndef::Undef => throw_unsup!(ReadUndefBytes(Size::ZERO)),
613+
ScalarMaybeUndef::Undef => throw_ub!(InvalidUndefBytes(None)),
613614
}
614615
}
615616

src/librustc_mir/const_eval/machine.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
240240
Ok(Some(match ecx.load_mir(instance.def, None) {
241241
Ok(body) => *body,
242242
Err(err) => {
243-
if let err_unsup!(NoMirFor(ref path)) = err.kind {
243+
if let err_unsup!(NoMirFor(did)) = err.kind {
244+
let path = ecx.tcx.def_path_str(did);
244245
return Err(ConstEvalErrKind::NeedsRfc(format!(
245246
"calling extern function `{}`",
246247
path

src/librustc_mir/interpret/eval_context.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ pub enum LocalValue<Tag = (), Id = AllocId> {
138138
impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
139139
pub fn access(&self) -> InterpResult<'tcx, Operand<Tag>> {
140140
match self.value {
141-
LocalValue::Dead => throw_unsup!(DeadLocal),
141+
LocalValue::Dead => throw_ub!(DeadLocal),
142142
LocalValue::Uninitialized => {
143143
bug!("The type checker should prevent reading from a never-written local")
144144
}
@@ -152,7 +152,7 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
152152
&mut self,
153153
) -> InterpResult<'tcx, Result<&mut LocalValue<Tag>, MemPlace<Tag>>> {
154154
match self.value {
155-
LocalValue::Dead => throw_unsup!(DeadLocal),
155+
LocalValue::Dead => throw_ub!(DeadLocal),
156156
LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)),
157157
ref mut local @ LocalValue::Live(Operand::Immediate(_))
158158
| ref mut local @ LocalValue::Uninitialized => Ok(Ok(local)),
@@ -326,7 +326,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
326326
if self.tcx.is_mir_available(did) {
327327
Ok(self.tcx.optimized_mir(did).unwrap_read_only())
328328
} else {
329-
throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id)))
329+
throw_unsup!(NoMirFor(def_id))
330330
}
331331
}
332332
_ => Ok(self.tcx.instance_mir(instance)),

src/librustc_mir/interpret/intern.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
327327
if let Err(error) = interned {
328328
// This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
329329
// to read enum discriminants in order to find references in enum variant fields.
330-
if let err_unsup!(ValidationFailure(_)) = error.kind {
330+
if let err_ub!(ValidationFailure(_)) = error.kind {
331331
let err = crate::const_eval::error_to_const_error(&ecx, error);
332332
match err.struct_error(
333333
ecx.tcx,
@@ -390,7 +390,7 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
390390
}
391391
} else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) {
392392
// dangling pointer
393-
throw_unsup!(ValidationFailure("encountered dangling pointer in final constant".into()))
393+
throw_ub_format!("encountered dangling pointer in final constant")
394394
} else if ecx.tcx.alloc_map.lock().get(alloc_id).is_none() {
395395
// We have hit an `AllocId` that is neither in local or global memory and isn't marked
396396
// as dangling by local memory.

src/librustc_mir/interpret/intrinsics.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
135135
let bits = self.force_bits(val, layout_of.size)?;
136136
let kind = match layout_of.abi {
137137
ty::layout::Abi::Scalar(ref scalar) => scalar.value,
138-
_ => throw_unsup!(TypeNotPrimitive(ty)),
138+
_ => bug!("{} called on invalid type {:?}", intrinsic_name, ty),
139139
};
140140
let (nonzero, intrinsic_name) = match intrinsic_name {
141141
sym::cttz_nonzero => (true, sym::cttz),
@@ -246,9 +246,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
246246
let layout = self.layout_of(substs.type_at(0))?;
247247
let r_val = self.force_bits(r.to_scalar()?, layout.size)?;
248248
if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name {
249-
throw_ub_format!("Overflowing shift by {} in `{}`", r_val, intrinsic_name);
249+
throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name);
250250
} else {
251-
throw_ub_format!("Overflow executing `{}`", intrinsic_name);
251+
throw_ub_format!("overflow executing `{}`", intrinsic_name);
252252
}
253253
}
254254
self.write_scalar(val, dest)?;

src/librustc_mir/interpret/machine.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,10 @@ pub trait Machine<'mir, 'tcx>: Sized {
286286
int: u64,
287287
) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
288288
Err((if int == 0 {
289-
err_unsup!(InvalidNullPointerUsage)
289+
// This is UB, seriously.
290+
err_ub!(InvalidIntPointerUsage(0))
290291
} else {
292+
// This is just something we cannot support during const-eval.
291293
err_unsup!(ReadBytesAsPointer)
292294
})
293295
.into())

src/librustc_mir/interpret/memory.rs

+48-34
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
215215
kind: MemoryKind<M::MemoryKinds>,
216216
) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
217217
if ptr.offset.bytes() != 0 {
218-
throw_unsup!(ReallocateNonBasePtr)
218+
throw_ub_format!(
219+
"reallocating {:?} which does not point to the beginning of an object",
220+
ptr
221+
);
219222
}
220223

221224
// For simplicities' sake, we implement reallocate as "alloc, copy, dealloc".
@@ -251,37 +254,43 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
251254
trace!("deallocating: {}", ptr.alloc_id);
252255

253256
if ptr.offset.bytes() != 0 {
254-
throw_unsup!(DeallocateNonBasePtr)
257+
throw_ub_format!(
258+
"deallocating {:?} which does not point to the beginning of an object",
259+
ptr
260+
);
255261
}
256262

257263
let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) {
258264
Some(alloc) => alloc,
259265
None => {
260266
// Deallocating static memory -- always an error
261267
return Err(match self.tcx.alloc_map.lock().get(ptr.alloc_id) {
262-
Some(GlobalAlloc::Function(..)) => err_unsup!(DeallocatedWrongMemoryKind(
263-
"function".to_string(),
264-
format!("{:?}", kind),
265-
)),
266-
Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => err_unsup!(
267-
DeallocatedWrongMemoryKind("static".to_string(), format!("{:?}", kind))
268-
),
269-
None => err_unsup!(DoubleFree),
268+
Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"),
269+
Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => {
270+
err_ub_format!("deallocating static memory")
271+
}
272+
None => err_ub!(PointerUseAfterFree(ptr.alloc_id)),
270273
}
271274
.into());
272275
}
273276
};
274277

275278
if alloc_kind != kind {
276-
throw_unsup!(DeallocatedWrongMemoryKind(
277-
format!("{:?}", alloc_kind),
278-
format!("{:?}", kind),
279-
))
279+
throw_ub_format!(
280+
"deallocating `{:?}` memory using `{:?}` deallocation operation",
281+
alloc_kind,
282+
kind
283+
);
280284
}
281285
if let Some((size, align)) = old_size_and_align {
282286
if size != alloc.size || align != alloc.align {
283-
let bytes = alloc.size;
284-
throw_unsup!(IncorrectAllocationInformation(size, bytes, align, alloc.align))
287+
throw_ub_format!(
288+
"incorrect layout on deallocation: allocation has size {} and alignment {}, but gave size {} and alignment {}",
289+
alloc.size.bytes(),
290+
alloc.align.bytes(),
291+
size.bytes(),
292+
align.bytes(),
293+
)
285294
}
286295
}
287296

@@ -338,7 +347,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
338347
} else {
339348
// The biggest power of two through which `offset` is divisible.
340349
let offset_pow2 = 1 << offset.trailing_zeros();
341-
throw_unsup!(AlignmentCheckFailed {
350+
throw_ub!(AlignmentCheckFailed {
342351
has: Align::from_bytes(offset_pow2).unwrap(),
343352
required: align,
344353
})
@@ -360,7 +369,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
360369
assert!(size.bytes() == 0);
361370
// Must be non-NULL.
362371
if bits == 0 {
363-
throw_unsup!(InvalidNullPointerUsage)
372+
throw_ub!(InvalidIntPointerUsage(0))
364373
}
365374
// Must be aligned.
366375
if let Some(align) = align {
@@ -375,7 +384,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
375384
// It is sufficient to check this for the end pointer. The addition
376385
// checks for overflow.
377386
let end_ptr = ptr.offset(size, self)?;
378-
end_ptr.check_inbounds_alloc(allocation_size, msg)?;
387+
if end_ptr.offset > allocation_size {
388+
// equal is okay!
389+
throw_ub!(PointerOutOfBounds { ptr: end_ptr.erase_tag(), msg, allocation_size })
390+
}
379391
// Test align. Check this last; if both bounds and alignment are violated
380392
// we want the error to be about the bounds.
381393
if let Some(align) = align {
@@ -385,7 +397,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
385397
// got picked we might be aligned even if this check fails.
386398
// We instead have to fall back to converting to an integer and checking
387399
// the "real" alignment.
388-
throw_unsup!(AlignmentCheckFailed { has: alloc_align, required: align });
400+
throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
389401
}
390402
check_offset_align(ptr.offset.bytes(), align)?;
391403
}
@@ -402,7 +414,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
402414
let (size, _align) = self
403415
.get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)
404416
.expect("alloc info with MaybeDead cannot fail");
405-
ptr.check_inbounds_alloc(size, CheckInAllocMsg::NullPointerTest).is_err()
417+
// If the pointer is out-of-bounds, it may be null.
418+
// Note that one-past-the-end (offset == size) is still inbounds, and never null.
419+
ptr.offset > size
406420
}
407421
}
408422

@@ -432,13 +446,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
432446
let alloc = tcx.alloc_map.lock().get(id);
433447
let alloc = match alloc {
434448
Some(GlobalAlloc::Memory(mem)) => Cow::Borrowed(mem),
435-
Some(GlobalAlloc::Function(..)) => throw_unsup!(DerefFunctionPointer),
436-
None => throw_unsup!(DanglingPointerDeref),
449+
Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
450+
None => throw_ub!(PointerUseAfterFree(id)),
437451
Some(GlobalAlloc::Static(def_id)) => {
438452
// We got a "lazy" static that has not been computed yet.
439453
if tcx.is_foreign_item(def_id) {
440454
trace!("get_static_alloc: foreign item {:?}", def_id);
441-
throw_unsup!(ReadForeignStatic)
455+
throw_unsup!(ReadForeignStatic(def_id))
442456
}
443457
trace!("get_static_alloc: Need to compute {:?}", def_id);
444458
let instance = Instance::mono(tcx.tcx, def_id);
@@ -524,7 +538,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
524538
// to give us a cheap reference.
525539
let alloc = Self::get_static_alloc(memory_extra, tcx, id)?;
526540
if alloc.mutability == Mutability::Not {
527-
throw_unsup!(ModifiedConstantMemory)
541+
throw_ub!(WriteToReadOnly(id))
528542
}
529543
match M::STATIC_KIND {
530544
Some(kind) => Ok((MemoryKind::Machine(kind), alloc.into_owned())),
@@ -538,7 +552,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
538552
Ok(a) => {
539553
let a = &mut a.1;
540554
if a.mutability == Mutability::Not {
541-
throw_unsup!(ModifiedConstantMemory)
555+
throw_ub!(WriteToReadOnly(id))
542556
}
543557
Ok(a)
544558
}
@@ -568,7 +582,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
568582
if self.get_fn_alloc(id).is_some() {
569583
return if let AllocCheck::Dereferenceable = liveness {
570584
// The caller requested no function pointers.
571-
throw_unsup!(DerefFunctionPointer)
585+
throw_ub!(DerefFunctionPointer(id))
572586
} else {
573587
Ok((Size::ZERO, Align::from_bytes(1).unwrap()))
574588
};
@@ -596,12 +610,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
596610
if let AllocCheck::MaybeDead = liveness {
597611
// Deallocated pointers are allowed, we should be able to find
598612
// them in the map.
599-
Ok(*self.dead_alloc_map.get(&id).expect(
600-
"deallocated pointers should all be recorded in \
601-
`dead_alloc_map`",
602-
))
613+
Ok(*self
614+
.dead_alloc_map
615+
.get(&id)
616+
.expect("deallocated pointers should all be recorded in `dead_alloc_map`"))
603617
} else {
604-
throw_unsup!(DanglingPointerDeref)
618+
throw_ub!(PointerUseAfterFree(id))
605619
}
606620
}
607621
}
@@ -626,10 +640,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
626640
) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
627641
let ptr = self.force_ptr(ptr)?; // We definitely need a pointer value.
628642
if ptr.offset.bytes() != 0 {
629-
throw_unsup!(InvalidFunctionPointer)
643+
throw_ub!(InvalidFunctionPointer(ptr.erase_tag()))
630644
}
631645
let id = M::canonical_alloc_id(self, ptr.alloc_id);
632-
self.get_fn_alloc(id).ok_or_else(|| err_unsup!(ExecuteMemory).into())
646+
self.get_fn_alloc(id).ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into())
633647
}
634648

635649
pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {

0 commit comments

Comments
 (0)