diff options
| author | Ralf Jung <post@ralfj.de> | 2025-05-12 13:34:46 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2025-06-26 18:09:47 +0200 |
| commit | 492526548d8741ed477dbf9f0079ce59177e62b5 (patch) | |
| tree | 8a2377caccdd0b0440051d22decd32b6b880019b /compiler/rustc_middle/src/mir/interpret | |
| parent | 8f21a5c92ea55c348c275a1bc4fedbdf181e0d64 (diff) | |
const-eval: allow constants to refer to mutable/external memory, but reject such constants as patterns
Diffstat (limited to 'compiler/rustc_middle/src/mir/interpret')
| -rw-r--r-- | compiler/rustc_middle/src/mir/interpret/error.rs | 52 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/interpret/mod.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/interpret/queries.rs | 120 |
3 files changed, 122 insertions, 54 deletions
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 6ff3cac049b..41a166083d0 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -35,7 +35,7 @@ impl From<ReportedErrorInfo> for ErrorHandled { } impl ErrorHandled { - pub fn with_span(self, span: Span) -> Self { + pub(crate) fn with_span(self, span: Span) -> Self { match self { ErrorHandled::Reported(err, _span) => ErrorHandled::Reported(err, span), ErrorHandled::TooGeneric(_span) => ErrorHandled::TooGeneric(span), @@ -94,14 +94,51 @@ impl From<ReportedErrorInfo> for ErrorGuaranteed { } } +/// An error type for the `const_to_valtree` query. Some error should be reported with a "use-site span", +/// which means the query cannot emit the error, so those errors are represented as dedicated variants here. +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum ValTreeCreationError<'tcx> { + /// The constant is too big to be valtree'd. + NodesOverflow, + /// The constant references mutable or external memory, so it cannot be valtree'd. + InvalidConst, + /// Values of this type, or this particular value, are not supported as valtrees. + NonSupportedType(Ty<'tcx>), + /// The error has already been handled by const evaluation. + ErrorHandled(ErrorHandled), +} + +impl<'tcx> From<ErrorHandled> for ValTreeCreationError<'tcx> { + fn from(err: ErrorHandled) -> Self { + ValTreeCreationError::ErrorHandled(err) + } +} + +impl<'tcx> From<InterpErrorInfo<'tcx>> for ValTreeCreationError<'tcx> { + fn from(err: InterpErrorInfo<'tcx>) -> Self { + // An error ocurred outside the const-eval query, as part of constructing the valtree. We + // don't currently preserve the details of this error, since `InterpErrorInfo` cannot be put + // into a query result and it can only be access of some mutable or external memory. + let (_kind, backtrace) = err.into_parts(); + backtrace.print_backtrace(); + ValTreeCreationError::InvalidConst + } +} + +impl<'tcx> ValTreeCreationError<'tcx> { + pub(crate) fn with_span(self, span: Span) -> Self { + use ValTreeCreationError::*; + match self { + ErrorHandled(handled) => ErrorHandled(handled.with_span(span)), + other => other, + } + } +} + pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>; pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; -/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed -/// because the value contains something of type `ty` that is not valtree-compatible. -/// The caller can then show an appropriate error; the query does not have the -/// necessary context to give good user-facing errors for this case. -pub type EvalToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>; +pub type EvalToValTreeResult<'tcx> = Result<ValTree<'tcx>, ValTreeCreationError<'tcx>>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); @@ -450,10 +487,9 @@ pub enum ValidationErrorKind<'tcx> { ptr_kind: PointerKind, ty: Ty<'tcx>, }, - ConstRefToMutable, - ConstRefToExtern, MutableRefToImmutable, UnsafeCellInImmutable, + MutableRefInConst, NullFnPtr, NeverVal, NullablePtrOutOfRange { diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index c2438af6a1e..ea2f84d46d7 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -38,8 +38,8 @@ pub use self::error::{ EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, - ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, - ValidationErrorKind, interp_ok, + ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValTreeCreationError, + ValidationErrorInfo, ValidationErrorKind, interp_ok, }; pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance}; pub use self::value::Scalar; diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 97db45a70d7..e25f35c59c2 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -5,11 +5,11 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; use super::{ - ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, GlobalId, - ReportedErrorInfo, + ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, GlobalId, ReportedErrorInfo, }; -use crate::mir; -use crate::ty::{self, GenericArgs, TyCtxt, TypeVisitableExt}; +use crate::mir::interpret::ValTreeCreationError; +use crate::ty::{self, ConstToValTreeResult, GenericArgs, TyCtxt, TypeVisitableExt}; +use crate::{error, mir}; impl<'tcx> TyCtxt<'tcx> { /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts @@ -92,7 +92,7 @@ impl<'tcx> TyCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, ct: ty::UnevaluatedConst<'tcx>, span: Span, - ) -> EvalToValTreeResult<'tcx> { + ) -> ConstToValTreeResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference // variables. We reject those here since `resolve` // would fail otherwise. @@ -103,47 +103,54 @@ impl<'tcx> TyCtxt<'tcx> { bug!("did not expect inference variables here"); } - match ty::Instance::try_resolve(self, typing_env, ct.def, ct.args) { - Ok(Some(instance)) => { - let cid = GlobalId { instance, promoted: None }; - self.const_eval_global_id_for_typeck(typing_env, cid, span).inspect(|_| { - // We are emitting the lint here instead of in `is_const_evaluatable` - // as we normalize obligations before checking them, and normalization - // uses this function to evaluate this constant. - // - // @lcnr believes that successfully evaluating even though there are - // used generic parameters is a bug of evaluation, so checking for it - // here does feel somewhat sensible. - if !self.features().generic_const_exprs() - && ct.args.has_non_region_param() - // We only FCW for anon consts as repeat expr counts with anon consts are the only place - // that we have a back compat hack for. We don't need to check this is a const argument - // as only anon consts as const args should get evaluated "for the type system". - // - // If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc - // consts in pattern positions. #140447 - && self.def_kind(instance.def_id()) == DefKind::AnonConst - { - let mir_body = self.mir_for_ctfe(instance.def_id()); - if mir_body.is_polymorphic { - let Some(local_def_id) = ct.def.as_local() else { return }; - self.node_span_lint( - lint::builtin::CONST_EVALUATABLE_UNCHECKED, - self.local_def_id_to_hir_id(local_def_id), - self.def_span(ct.def), - |lint| { lint.primary_message("cannot use constants which depend on generic parameters in types"); }, - ) - } - } - }) - } + let cid = match ty::Instance::try_resolve(self, typing_env, ct.def, ct.args) { + Ok(Some(instance)) => GlobalId { instance, promoted: None }, // For errors during resolution, we deliberately do not point at the usage site of the constant, // since for these errors the place the constant is used shouldn't matter. - Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)), + Ok(None) => return Err(ErrorHandled::TooGeneric(DUMMY_SP).into()), Err(err) => { - Err(ErrorHandled::Reported(ReportedErrorInfo::non_const_eval_error(err), DUMMY_SP)) + return Err(ErrorHandled::Reported( + ReportedErrorInfo::non_const_eval_error(err), + DUMMY_SP, + ) + .into()); } - } + }; + + self.const_eval_global_id_for_typeck(typing_env, cid, span).inspect(|_| { + // We are emitting the lint here instead of in `is_const_evaluatable` + // as we normalize obligations before checking them, and normalization + // uses this function to evaluate this constant. + // + // @lcnr believes that successfully evaluating even though there are + // used generic parameters is a bug of evaluation, so checking for it + // here does feel somewhat sensible. + if !self.features().generic_const_exprs() + && ct.args.has_non_region_param() + // We only FCW for anon consts as repeat expr counts with anon consts are the only place + // that we have a back compat hack for. We don't need to check this is a const argument + // as only anon consts as const args should get evaluated "for the type system". + // + // If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc + // consts in pattern positions. #140447 + && self.def_kind(cid.instance.def_id()) == DefKind::AnonConst + { + let mir_body = self.mir_for_ctfe(cid.instance.def_id()); + if mir_body.is_polymorphic { + let Some(local_def_id) = ct.def.as_local() else { return }; + self.node_span_lint( + lint::builtin::CONST_EVALUATABLE_UNCHECKED, + self.local_def_id_to_hir_id(local_def_id), + self.def_span(ct.def), + |lint| { + lint.primary_message( + "cannot use constants which depend on generic parameters in types", + ); + }, + ) + } + } + }) } pub fn const_eval_instance( @@ -182,17 +189,42 @@ impl<'tcx> TyCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, cid: GlobalId<'tcx>, span: Span, - ) -> EvalToValTreeResult<'tcx> { + ) -> ConstToValTreeResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(typing_env.with_post_analysis_normalized(self).as_query_input(cid)); debug!(?inputs); - if !span.is_dummy() { + let res = if !span.is_dummy() { // The query doesn't know where it is being invoked, so we need to fix the span. self.at(span).eval_to_valtree(inputs).map_err(|e| e.with_span(span)) } else { self.eval_to_valtree(inputs) + }; + match res { + Ok(valtree) => Ok(Ok(valtree)), + Err(err) => { + match err { + // Let the caller decide how to handle this. + ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)), + // Report the others. + ValTreeCreationError::NodesOverflow => { + let handled = self.dcx().emit_err(error::MaxNumNodesInValtree { + span, + global_const_id: cid.display(self), + }); + Err(ReportedErrorInfo::allowed_in_infallible(handled).into()) + } + ValTreeCreationError::InvalidConst => { + let handled = self.dcx().emit_err(error::InvalidConstInValtree { + span, + global_const_id: cid.display(self), + }); + Err(ReportedErrorInfo::allowed_in_infallible(handled).into()) + } + ValTreeCreationError::ErrorHandled(handled) => Err(handled), + } + } } } } |
