From fa74a9e6aa525a285cfd530cdea2ddeb9fca013c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Jul 2024 16:13:55 +0200 Subject: [PATCH] valtree construction: keep track of which type was valtree-incompatible --- compiler/rustc_codegen_ssa/src/mir/constant.rs | 5 +++-- compiler/rustc_const_eval/src/const_eval/mod.rs | 10 +++++----- .../rustc_const_eval/src/const_eval/valtrees.rs | 16 ++++++++-------- compiler/rustc_infer/src/infer/mod.rs | 6 +++--- compiler/rustc_middle/src/mir/interpret/error.rs | 8 +++++--- compiler/rustc_middle/src/query/erase.rs | 7 ++++--- compiler/rustc_middle/src/ty/consts.rs | 3 +-- compiler/rustc_mir_build/src/thir/pattern/mod.rs | 7 +++---- .../rustc_trait_selection/src/solve/delegate.rs | 4 ++-- .../src/traits/auto_trait.rs | 4 ++-- .../clippy/clippy_lints/src/non_copy_const.rs | 2 +- 11 files changed, 37 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 822f5c2c44a..35e9a3b7dc2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -37,13 +37,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn eval_unevaluated_mir_constant_to_valtree( &self, constant: &mir::ConstOperand<'tcx>, - ) -> Result>, ErrorHandled> { + ) -> Result, Ty<'tcx>>, ErrorHandled> { let uv = match self.monomorphize(constant.const_) { mir::Const::Unevaluated(uv, _) => uv.shrink(), mir::Const::Ty(_, c) => match c.kind() { // A constant that came from a const generic but was then used as an argument to old-style // simd_shuffle (passing as argument instead of as a generic param). - rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Some(valtree)), + rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)), other => span_bug!(constant.span, "{other:#?}"), }, // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate @@ -70,6 +70,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = self .eval_unevaluated_mir_constant_to_valtree(constant) .ok() + .map(|x| x.ok()) .flatten() .map(|val| { let field_ty = ty.builtin_index().unwrap(); diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 4ae4816e33a..3a6dc81eff1 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -27,15 +27,15 @@ pub(crate) use valtrees::{eval_to_valtree, valtree_to_const_value}; // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. const VALTREE_MAX_NODES: usize = 100000; -pub(crate) enum ValTreeCreationError { +pub(crate) enum ValTreeCreationError<'tcx> { NodesOverflow, /// Values of this type, or this particular value, are not supported as valtrees. - NonSupportedType, + NonSupportedType(Ty<'tcx>), } -pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError>; +pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError<'tcx>>; -impl From> for ValTreeCreationError { - fn from(err: InterpErrorInfo<'_>) -> Self { +impl<'tcx> From> for ValTreeCreationError<'tcx> { + fn from(err: InterpErrorInfo<'tcx>) -> Self { ty::tls::with(|tcx| { bug!( "Unexpected Undefined Behavior error during valtree construction: {}", diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 2e8ad445cf5..3bc01510730 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -120,13 +120,13 @@ fn const_to_valtree_inner<'tcx>( // We could allow wide raw pointers where both sides are integers in the future, // but for now we reject them. if matches!(val.layout.abi, Abi::ScalarPair(..)) { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); } let val = val.to_scalar(); // We are in the CTFE machine, so ptr-to-int casts will fail. // This can only be `Ok` if `val` already is an integer. let Ok(val) = val.try_to_scalar_int() else { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); }; // It's just a ScalarInt! Ok(ty::ValTree::Leaf(val)) @@ -134,7 +134,7 @@ fn const_to_valtree_inner<'tcx>( // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // agree with runtime equality tests. - ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType), + ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Ref(_, _, _) => { let derefd_place = ecx.deref_pointer(place)?; @@ -148,7 +148,7 @@ fn const_to_valtree_inner<'tcx>( // resolving their backing type, even if we can do that at const eval time. We may // hypothetically be able to allow `dyn StructuralPartialEq` trait objects in the future, // but it is unclear if this is useful. - ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), + ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Tuple(elem_tys) => { branches(ecx, place, elem_tys.len(), None, num_nodes) @@ -156,7 +156,7 @@ fn const_to_valtree_inner<'tcx>( ty::Adt(def, _) => { if def.is_union() { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); } else if def.variants().is_empty() { bug!("uninhabited types should have errored and never gotten converted to valtree") } @@ -180,7 +180,7 @@ fn const_to_valtree_inner<'tcx>( | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) - | ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType), + | ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType(ty)), } } @@ -251,7 +251,7 @@ pub(crate) fn eval_to_valtree<'tcx>( let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes); match valtree_result { - Ok(valtree) => Ok(Some(valtree)), + Ok(valtree) => Ok(Ok(valtree)), Err(err) => { let did = cid.instance.def_id(); let global_const_id = cid.display(tcx); @@ -262,7 +262,7 @@ pub(crate) fn eval_to_valtree<'tcx>( tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id }); Err(handled.into()) } - ValTreeCreationError::NonSupportedType => Ok(None), + ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)), } } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index cfef1f13015..c9073d8c23e 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1427,17 +1427,17 @@ impl<'tcx> InferCtxt<'tcx> { span: Span, ) -> Result, ErrorHandled> { match self.const_eval_resolve(param_env, unevaluated, span) { - Ok(Some(val)) => Ok(ty::Const::new_value( + Ok(Ok(val)) => Ok(ty::Const::new_value( self.tcx, val, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), )), - Ok(None) => { + Ok(Err(bad_ty)) => { let tcx = self.tcx; let def_id = unevaluated.def; span_bug!( tcx.def_span(def_id), - "unable to construct a constant value for the unevaluated constant {:?}", + "unable to construct a valtree for the unevaluated constant {:?}: type {bad_ty} is not valtree-compatible", unevaluated ); } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 6a8498abaf9..9df19565ab3 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -90,9 +90,11 @@ TrivialTypeTraversalImpls! { ErrorHandled } pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalStaticInitializerRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; -/// `Ok(None)` indicates the constant was fine, but the valtree couldn't be constructed. -/// This is needed in `thir::pattern::lower_inline_const`. -pub type EvalToValTreeResult<'tcx> = Result>, ErrorHandled>; +/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed +/// because the value containts something of type `ty` that is not valtree-compatible. +/// The caller can then show an appropriate error; the query does not have the +/// necssary context to give good user-facing errors for this case. +pub type EvalToValTreeResult<'tcx> = Result, Ty<'tcx>>, ErrorHandled>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 301c9911b44..d9fa5b02f7f 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -157,9 +157,10 @@ impl EraseType for Result, mir::interpret::ErrorHandled> { type Result = [u8; size_of::, mir::interpret::ErrorHandled>>()]; } -impl EraseType for Result>, mir::interpret::ErrorHandled> { - type Result = - [u8; size_of::>, mir::interpret::ErrorHandled>>()]; +impl EraseType for Result, Ty<'_>>, mir::interpret::ErrorHandled> { + type Result = [u8; size_of::< + Result, Ty<'static>>, mir::interpret::ErrorHandled>, + >()]; } impl EraseType for Result<&'_ ty::List>, ty::util::AlwaysRequiresDrop> { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 32d01d07c17..d4c39a25a1f 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -328,8 +328,7 @@ impl<'tcx> Const<'tcx> { let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env); // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - let Some(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? - else { + let Ok(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? else { // This can happen when we run on ill-typed code. let e = tcx.dcx().span_delayed_bug( span, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index fd778ef78a3..25b7d24de39 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -581,8 +581,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .tcx .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span) .map(|val| match val { - Some(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), - None => mir::Const::Val( + Ok(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), + Err(_) => mir::Const::Val( self.tcx .const_eval_global_id(param_env_reveal_all, cid, span) .expect("const_eval_global_id_for_typeck should have already failed"), @@ -682,8 +682,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // First try using a valtree in order to destructure the constant into a pattern. // FIXME: replace "try to do a thing, then fall back to another thing" // but something more principled, like a trait query checking whether this can be turned into a valtree. - if let Ok(Some(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) - { + if let Ok(Ok(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) { let subpattern = self.const_to_pat( Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), id, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f67518a577e..ade26a40920 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -87,12 +87,12 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< ) -> Option> { use rustc_middle::mir::interpret::ErrorHandled; match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) { - Ok(Some(val)) => Some(ty::Const::new_value( + Ok(Ok(val)) => Some(ty::Const::new_value( self.tcx, val, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), )), - Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None, + Ok(Err(_)) | Err(ErrorHandled::TooGeneric(_)) => None, Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())), } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 1d32ef2ccd9..796f7fd5a54 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -765,8 +765,8 @@ impl<'tcx> AutoTraitFinder<'tcx> { unevaluated, obligation.cause.span, ) { - Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), - Ok(None) => { + Ok(Ok(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), + Ok(Err(_)) => { let tcx = self.tcx; let reported = tcx.dcx().emit_err(UnableToConstructConstantValue { diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 09225ac3246..6f5505e8a63 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -235,7 +235,7 @@ impl<'tcx> NonCopyConst<'tcx> { fn is_value_unfrozen_raw( cx: &LateContext<'tcx>, - result: Result>, ErrorHandled>, + result: Result, Ty<'tcx>>, ErrorHandled>, ty: Ty<'tcx>, ) -> bool { result.map_or_else(