diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 96e2a0ba618..197e8dd5341 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -186,6 +186,19 @@ pub struct Body<'tcx> { /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components. pub ignore_interior_mut_in_const_validation: bool, + /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. + /// + /// Note that this does not actually mean that this body is not computable right now. + /// The repeat count in the following example is polymorphic, but can still be evaluated + /// without knowing anything about the type parameter `T`. + /// + /// ```rust + /// fn test() { + /// let _ = [0; std::mem::size_of::<*mut T>()]; + /// } + /// ``` + pub is_polymorphic: bool, + predecessor_cache: PredecessorCache, } @@ -208,7 +221,7 @@ impl<'tcx> Body<'tcx> { local_decls.len() ); - Body { + let mut body = Body { phase: MirPhase::Build, basic_blocks, source_scopes, @@ -224,8 +237,11 @@ impl<'tcx> Body<'tcx> { span, required_consts: Vec::new(), ignore_interior_mut_in_const_validation: false, + is_polymorphic: false, predecessor_cache: PredecessorCache::new(), - } + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body } /// Returns a partially initialized MIR body containing only a list of basic blocks. @@ -234,7 +250,7 @@ impl<'tcx> Body<'tcx> { /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different /// crate. pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { - Body { + let mut body = Body { phase: MirPhase::Build, basic_blocks, source_scopes: IndexVec::new(), @@ -250,8 +266,11 @@ impl<'tcx> Body<'tcx> { generator_kind: None, var_debug_info: Vec::new(), ignore_interior_mut_in_const_validation: false, + is_polymorphic: false, predecessor_cache: PredecessorCache::new(), - } + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body } #[inline] diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a5c6dc042ab..989c6e6dbc2 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::ToPredicate; use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable}; use std::marker::PhantomData; +use super::const_evaluatable; use super::project; use super::select::SelectionContext; use super::wf; @@ -458,16 +459,17 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { - match self.selcx.infcx().const_eval_resolve( - obligation.param_env, + const_evaluatable::is_const_evaluatable( + self.selcx.infcx(), def_id, substs, - None, - Some(obligation.cause.span), - ) { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), - } + obligation.param_env, + obligation.cause.span, + ) + .map_or_else( + |e| ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))), + |()| ProcessResult::Changed(vec![]), + ) } ty::PredicateAtom::ConstEquate(c1, c2) => { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index fe406e88c52..49dac873cde 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -7,6 +7,7 @@ pub mod auto_trait; mod chalk_fulfill; pub mod codegen; mod coherence; +mod const_evaluatable; mod engine; pub mod error_reporting; mod fulfill; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4258d8e3010..5a1e1eb89a6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -6,6 +6,7 @@ use self::EvaluationResult::*; use self::SelectionCandidate::*; use super::coherence::{self, Conflict}; +use super::const_evaluatable; use super::project; use super::project::normalize_with_depth_to; use super::util; @@ -542,17 +543,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { - match self.tcx().const_eval_resolve( - obligation.param_env, + const_evaluatable::is_const_evaluatable( + self.infcx, def_id, substs, - None, - None, - ) { - Ok(_) => Ok(EvaluatedToOk), - Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), - Err(_) => Ok(EvaluatedToErr), - } + obligation.param_env, + obligation.cause.span, + ) + .map(|()| EvaluatedToOk) + .or_else(|e| match e { + ErrorHandled::TooGeneric => Ok(EvaluatedToAmbig), + _ => Ok(EvaluatedToErr), + }) } ty::PredicateAtom::ConstEquate(c1, c2) => { diff --git a/src/librustc_trait_selection/traits/const_evaluatable.rs b/src/librustc_trait_selection/traits/const_evaluatable.rs new file mode 100644 index 00000000000..eb0e7f16fa3 --- /dev/null +++ b/src/librustc_trait_selection/traits/const_evaluatable.rs @@ -0,0 +1,54 @@ +use rustc_middle::ty::{self, TypeFoldable}; +use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::subst::SubstsRef; +use rustc_span::Span; +use rustc_span::def_id::DefId; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_hir::def::DefKind; + +pub fn is_const_evaluatable<'cx, 'tcx>( + infcx: &InferCtxt<'cx, 'tcx>, + def: ty::WithOptConstParam, + substs: SubstsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, +) -> Result<(), ErrorHandled> +{ + let def_kind = infcx.tcx.def_kind(def.did); + match def_kind { + DefKind::AnonConst => { + let mir_body = if let Some(def) = def.as_const_arg() { + infcx.tcx.optimized_mir_of_const_arg(def) + } else { + infcx.tcx.optimized_mir(def.did) + }; + if mir_body.is_polymorphic { + return Err(ErrorHandled::TooGeneric); + } + } + _ => { + if substs.has_param_types_or_consts() { + return Err(ErrorHandled::TooGeneric); + } + } + } + + match infcx.const_eval_resolve( + param_env, + def, + substs, + None, + Some(span), + ) { + Ok(_) => Ok(()), + Err(err) => { + if matches!(err, ErrorHandled::TooGeneric) { + infcx.tcx.sess.delay_span_bug( + span, + &format!("ConstEvaluatable too generic: {:?}, {:?}, {:?}", def, substs, param_env), + ); + } + Err(err) + } + } +} \ No newline at end of file diff --git a/src/test/ui/const_evaluatable/associated_const.rs b/src/test/ui/const_evaluatable/associated_const.rs new file mode 100644 index 00000000000..a6777632254 --- /dev/null +++ b/src/test/ui/const_evaluatable/associated_const.rs @@ -0,0 +1,11 @@ +// check-pass +struct Foo(T); +impl Foo { + const VALUE: usize = std::mem::size_of::(); +} + +fn test() { + let _ = [0; Foo::::VALUE]; +} + +fn main() {} diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs index 5a528379b04..5fe526df5a7 100644 --- a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs @@ -1,5 +1,3 @@ -// run-pass - #![feature(arbitrary_enum_discriminant, core_intrinsics)] extern crate core; @@ -9,6 +7,7 @@ use core::intrinsics::discriminant_value; enum MyWeirdOption { None = 0, Some(T) = core::mem::size_of::<*mut T>(), + //~^ ERROR constant expression depends on a generic parameter } fn main() { diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr new file mode 100644 index 00000000000..9aba2ea543f --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-70453-polymorphic-ctfe.rs:9:15 + | +LL | Some(T) = core::mem::size_of::<*mut T>(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/issue-56445.rs b/src/test/ui/impl-trait/issue-56445.rs index a34d7bae3a6..6dd1648c9b8 100644 --- a/src/test/ui/impl-trait/issue-56445.rs +++ b/src/test/ui/impl-trait/issue-56445.rs @@ -5,8 +5,7 @@ use std::marker::PhantomData; -pub struct S<'a> -{ +pub struct S<'a> { pub m1: PhantomData<&'a u8>, pub m2: [u8; S::size()], } diff --git a/src/test/ui/lazy_normalization_consts/issue-73980.rs b/src/test/ui/lazy_normalization_consts/issue-73980.rs index 339b22c0b42..2e4cb9ff7a8 100644 --- a/src/test/ui/lazy_normalization_consts/issue-73980.rs +++ b/src/test/ui/lazy_normalization_consts/issue-73980.rs @@ -1,4 +1,3 @@ -// check-pass #![feature(lazy_normalization_consts)] #![allow(incomplete_features)] @@ -10,5 +9,6 @@ impl L { } impl X::S]> {} +//~^ ERROR constant expression depends on a generic parameter fn main() {} diff --git a/src/test/ui/lazy_normalization_consts/issue-73980.stderr b/src/test/ui/lazy_normalization_consts/issue-73980.stderr new file mode 100644 index 00000000000..5ca11bf55fc --- /dev/null +++ b/src/test/ui/lazy_normalization_consts/issue-73980.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-73980.rs:11:9 + | +LL | impl X::S]> {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error +