mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 00:03:43 +00:00
don't ICE when encountering an extern type field during validation
This commit is contained in:
parent
d03d6c0fea
commit
763e3131cc
@ -89,6 +89,8 @@ const_eval_exact_div_has_remainder =
|
||||
|
||||
const_eval_extern_static =
|
||||
cannot access extern static ({$did})
|
||||
const_eval_extern_type_field = `extern type` field does not have a known offset
|
||||
|
||||
const_eval_fn_ptr_call =
|
||||
function pointers need an RFC before allowed to be called in {const_eval_const_context}s
|
||||
const_eval_for_loop_into_iter_non_const =
|
||||
|
@ -386,33 +386,8 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
|
||||
CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
|
||||
);
|
||||
let res = ecx.load_mir(cid.instance.def, cid.promoted);
|
||||
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| {
|
||||
let (error, backtrace) = error.into_parts();
|
||||
backtrace.print_backtrace();
|
||||
|
||||
let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) {
|
||||
("static", String::new())
|
||||
} else {
|
||||
// If the current item has generics, we'd like to enrich the message with the
|
||||
// instance and its args: to show the actual compile-time values, in addition to
|
||||
// the expression, leading to the const eval error.
|
||||
let instance = &cid.instance;
|
||||
if !instance.args.is_empty() {
|
||||
let instance = with_no_trimmed_paths!(instance.to_string());
|
||||
("const_with_path", instance)
|
||||
} else {
|
||||
("const", String::new())
|
||||
}
|
||||
};
|
||||
|
||||
super::report(
|
||||
*ecx.tcx,
|
||||
error,
|
||||
DUMMY_SP,
|
||||
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
|
||||
|span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames },
|
||||
)
|
||||
})
|
||||
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
|
||||
.map_err(|error| report_eval_error(&ecx, cid, error))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -438,23 +413,60 @@ fn const_validate_mplace<'tcx>(
|
||||
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)
|
||||
// Instead of just reporting the `InterpError` via the usual machinery, we give a more targeted
|
||||
// error about the validation failure.
|
||||
.map_err(|error| report_validation_error(&ecx, error, alloc_id))?;
|
||||
.map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?;
|
||||
inner = true;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn report_validation_error<'tcx>(
|
||||
#[inline(never)]
|
||||
fn report_eval_error<'tcx>(
|
||||
ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
|
||||
cid: GlobalId<'tcx>,
|
||||
error: InterpErrorInfo<'tcx>,
|
||||
alloc_id: AllocId,
|
||||
) -> ErrorHandled {
|
||||
let (error, backtrace) = error.into_parts();
|
||||
backtrace.print_backtrace();
|
||||
|
||||
let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
|
||||
let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) {
|
||||
("static", String::new())
|
||||
} else {
|
||||
// If the current item has generics, we'd like to enrich the message with the
|
||||
// instance and its args: to show the actual compile-time values, in addition to
|
||||
// the expression, leading to the const eval error.
|
||||
let instance = &cid.instance;
|
||||
if !instance.args.is_empty() {
|
||||
let instance = with_no_trimmed_paths!(instance.to_string());
|
||||
("const_with_path", instance)
|
||||
} else {
|
||||
("const", String::new())
|
||||
}
|
||||
};
|
||||
|
||||
super::report(
|
||||
*ecx.tcx,
|
||||
error,
|
||||
DUMMY_SP,
|
||||
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
|
||||
|span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames },
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn report_validation_error<'tcx>(
|
||||
ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
|
||||
cid: GlobalId<'tcx>,
|
||||
error: InterpErrorInfo<'tcx>,
|
||||
alloc_id: AllocId,
|
||||
) -> ErrorHandled {
|
||||
if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) {
|
||||
// Some other error happened during validation, e.g. an unsupported operation.
|
||||
return report_eval_error(ecx, cid, error);
|
||||
}
|
||||
|
||||
let (error, backtrace) = error.into_parts();
|
||||
backtrace.print_backtrace();
|
||||
|
||||
let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
|
||||
let (size, align, _) = ecx.get_alloc_info(alloc_id);
|
||||
@ -465,6 +477,6 @@ fn report_validation_error<'tcx>(
|
||||
error,
|
||||
DUMMY_SP,
|
||||
|| crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()),
|
||||
move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes },
|
||||
move |span, frames| errors::ValidationFailure { span, ub_note: (), frames, raw_bytes },
|
||||
)
|
||||
}
|
||||
|
@ -425,7 +425,7 @@ pub struct ValidationFailure {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[note(const_eval_validation_failure_note)]
|
||||
pub ub_note: Option<()>,
|
||||
pub ub_note: (),
|
||||
#[subdiagnostic]
|
||||
pub frames: Vec<FrameNote>,
|
||||
#[subdiagnostic]
|
||||
@ -825,6 +825,7 @@ impl ReportErrorExt for UnsupportedOpInfo {
|
||||
use crate::fluent_generated::*;
|
||||
match self {
|
||||
UnsupportedOpInfo::Unsupported(s) => s.clone().into(),
|
||||
UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field,
|
||||
UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local,
|
||||
UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite,
|
||||
UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy,
|
||||
@ -845,7 +846,10 @@ impl ReportErrorExt for UnsupportedOpInfo {
|
||||
// `ReadPointerAsInt(Some(info))` is never printed anyway, it only serves as an error to
|
||||
// be further processed by validity checking which then turns it into something nice to
|
||||
// print. So it's not worth the effort of having diagnostics that can print the `info`.
|
||||
UnsizedLocal | Unsupported(_) | ReadPointerAsInt(_) => {}
|
||||
UnsizedLocal
|
||||
| UnsupportedOpInfo::ExternTypeField
|
||||
| Unsupported(_)
|
||||
| ReadPointerAsInt(_) => {}
|
||||
OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => {
|
||||
diag.arg("ptr", ptr);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ use rustc_target::abi::{self, VariantIdx};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::{
|
||||
throw_ub, throw_unsup_format, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
|
||||
throw_ub, throw_unsup, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
|
||||
Provenance, Scalar,
|
||||
};
|
||||
|
||||
@ -186,8 +186,8 @@ where
|
||||
(base_meta, offset)
|
||||
}
|
||||
None => {
|
||||
// We don't know the alignment of this field, so we cannot adjust.
|
||||
throw_unsup_format!("`extern type` does not have a known offset")
|
||||
// We cannot know the alignment of this field, so we cannot adjust.
|
||||
throw_unsup!(ExternTypeField)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -5,6 +5,7 @@
|
||||
//! to be const-safe.
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZero;
|
||||
|
||||
use either::{Left, Right};
|
||||
@ -17,7 +18,8 @@ use rustc_hir as hir;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::interpret::{
|
||||
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
|
||||
ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
|
||||
UnsupportedOpInfo, ValidationErrorInfo,
|
||||
ValidationErrorKind::{self, *},
|
||||
};
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
@ -26,8 +28,6 @@ use rustc_target::abi::{
|
||||
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
|
||||
};
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use super::{
|
||||
err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, AllocKind, CheckInAllocMsg,
|
||||
GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
|
||||
@ -1028,7 +1028,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
Err(err)
|
||||
if matches!(
|
||||
err.kind(),
|
||||
err_ub!(ValidationError { .. }) | InterpError::InvalidProgram(_)
|
||||
err_ub!(ValidationError { .. })
|
||||
| InterpError::InvalidProgram(_)
|
||||
| InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField)
|
||||
) =>
|
||||
{
|
||||
Err(err)
|
||||
|
@ -520,6 +520,8 @@ pub enum UnsupportedOpInfo {
|
||||
Unsupported(String),
|
||||
/// Unsized local variables.
|
||||
UnsizedLocal,
|
||||
/// Extern type field with an indeterminate offset.
|
||||
ExternTypeField,
|
||||
//
|
||||
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
|
||||
//
|
||||
|
@ -311,7 +311,9 @@ pub fn report_error<'tcx>(
|
||||
ResourceExhaustion(_) => "resource exhaustion",
|
||||
Unsupported(
|
||||
// We list only the ones that can actually happen.
|
||||
UnsupportedOpInfo::Unsupported(_) | UnsupportedOpInfo::UnsizedLocal,
|
||||
UnsupportedOpInfo::Unsupported(_)
|
||||
| UnsupportedOpInfo::UnsizedLocal
|
||||
| UnsupportedOpInfo::ExternTypeField,
|
||||
) => "unsupported operation",
|
||||
InvalidProgram(
|
||||
// We list only the ones that can actually happen.
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: unsupported operation: `extern type` does not have a known offset
|
||||
error: unsupported operation: `extern type` field does not have a known offset
|
||||
--> $DIR/extern-type-field-offset.rs:LL:CC
|
||||
|
|
||||
LL | let _field = &x.a;
|
||||
| ^^^^ `extern type` does not have a known offset
|
||||
| ^^^^ `extern type` field does not have a known offset
|
||||
|
|
||||
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
|
||||
= note: BACKTRACE:
|
||||
|
@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/issue-91827-extern-types-field-offset.rs:38:17
|
||||
|
|
||||
LL | let field = &x.a;
|
||||
| ^^^^ `extern type` does not have a known offset
|
||||
| ^^^^ `extern type` field does not have a known offset
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
#![feature(extern_types)]
|
||||
|
||||
extern {
|
||||
type Opaque;
|
||||
}
|
||||
|
||||
struct ThinDst {
|
||||
x: u8,
|
||||
tail: Opaque,
|
||||
}
|
||||
|
||||
const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) };
|
||||
//~^ERROR: evaluation of constant value failed
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,9 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/validation-ice-extern-type-field.rs:12:1
|
||||
|
|
||||
LL | const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) };
|
||||
| ^^^^^^^^^^^^^^^^^^ `extern type` field does not have a known offset
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
@ -9,15 +9,11 @@ LL | const _: *const Foo = 0 as _;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/stack-overflow-trait-infer-98842.rs:15:1
|
||||
|
|
||||
LL | const _: *const Foo = 0 as _;
|
||||
| ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation
|
||||
|
|
||||
= note: the raw bytes of the constant (size: 4, align: 4) {
|
||||
00 00 00 00 │ ....
|
||||
}
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -9,15 +9,11 @@ LL | const _: *const Foo = 0 as _;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/stack-overflow-trait-infer-98842.rs:15:1
|
||||
|
|
||||
LL | const _: *const Foo = 0 as _;
|
||||
| ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation
|
||||
|
|
||||
= note: the raw bytes of the constant (size: 8, align: 8) {
|
||||
00 00 00 00 00 00 00 00 │ ........
|
||||
}
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -13,6 +13,6 @@ struct Foo(<&'static Foo as ::core::ops::Deref>::Target);
|
||||
// and it will infinitely recurse somewhere trying to figure out the
|
||||
// size of this pointer (is my guess):
|
||||
const _: *const Foo = 0 as _;
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
|
||||
pub fn main() {}
|
||||
|
@ -1,25 +0,0 @@
|
||||
error[E0391]: cycle detected when computing layout of `Foo`
|
||||
|
|
||||
= note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`...
|
||||
= note: ...which again requires computing layout of `Foo`, completing the cycle
|
||||
note: cycle used when const-evaluating + checking `_`
|
||||
--> $DIR/stack-overflow-trait-infer-98842.rs:13:1
|
||||
|
|
||||
LL | const _: *const Foo = 0 as _;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/stack-overflow-trait-infer-98842.rs:13:1
|
||||
|
|
||||
LL | const _: *const Foo = 0 as _;
|
||||
| ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation
|
||||
|
|
||||
= note: the raw bytes of the constant (size: 8, align: 8) {
|
||||
00 00 00 00 00 00 00 00 │ ........
|
||||
}
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0080, E0391.
|
||||
For more information about an error, try `rustc --explain E0080`.
|
Loading…
Reference in New Issue
Block a user