From f77f07c55b52ba9a462065fff2f0bc74a0727641 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Fri, 24 Jan 2020 20:05:07 +1300 Subject: [PATCH 1/4] Canonicalize inputs to const eval where needed. --- src/librustc_infer/infer/mod.rs | 32 ++++++++++++++++++++++++++++ src/librustc_infer/traits/fulfill.rs | 30 ++++++++------------------ src/librustc_infer/traits/select.rs | 23 ++++++++------------ 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs index c9e58c2aa7347..93d48184b42ae 100644 --- a/src/librustc_infer/infer/mod.rs +++ b/src/librustc_infer/infer/mod.rs @@ -15,6 +15,8 @@ use rustc::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToTy use rustc::middle::free_region::RegionRelations; use rustc::middle::lang_items; use rustc::middle::region; +use rustc::mir; +use rustc::mir::interpret::ConstEvalResult; use rustc::session::config::BorrowckMode; use rustc::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use rustc::ty::fold::{TypeFoldable, TypeFolder}; @@ -63,6 +65,7 @@ pub mod resolve; mod sub; pub mod type_variable; +use crate::infer::canonical::OriginalQueryValues; pub use rustc::infer::unify_key; #[must_use] @@ -1565,6 +1568,35 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.universe.set(u); u } + + /// Resolves and evaluates a constant. + /// + /// The constant can be located on a trait like `::C`, in which case the given + /// substitutions and environment are used to resolve the constant. Alternatively if the + /// constant has generic parameters in scope the substitutions are used to evaluate the value of + /// the constant. For example in `fn foo() { let _ = [0; bar::()]; }` the repeat count + /// constant `bar::()` requires a substitution for `T`, if the substitution for `T` is still + /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is + /// returned. + /// + /// This handles inferences variables within both `param_env` and `substs` by + /// performing the operation on their respective canonical forms. + pub fn const_eval_resolve( + &self, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + promoted: Option, + span: Option, + ) -> ConstEvalResult<'tcx> { + let mut original_values = OriginalQueryValues::default(); + let canonical = self.canonicalize_query(&(param_env, substs), &mut original_values); + + let (param_env, substs) = canonical.value; + // The return value is the evaluated value which doesn't contain any reference to inference + // variables, thus we don't need to substitute back the original values. + self.tcx.const_eval_resolve(param_env, def_id, substs, promoted, span) + } } pub struct ShallowResolver<'a, 'tcx> { diff --git a/src/librustc_infer/traits/fulfill.rs b/src/librustc_infer/traits/fulfill.rs index 6055b0e74df54..28d3f26918019 100644 --- a/src/librustc_infer/traits/fulfill.rs +++ b/src/librustc_infer/traits/fulfill.rs @@ -510,27 +510,15 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::Predicate::ConstEvaluatable(def_id, substs) => { - if obligation.param_env.has_local_value() { - ProcessResult::Unchanged - } else { - if !substs.has_local_value() { - match self.selcx.tcx().const_eval_resolve( - obligation.param_env, - def_id, - substs, - None, - Some(obligation.cause.span), - ) { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => { - ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))) - } - } - } else { - pending_obligation.stalled_on = - substs.types().map(|ty| infer_ty(ty)).collect(); - ProcessResult::Unchanged - } + match self.selcx.infcx().const_eval_resolve( + obligation.param_env, + def_id, + substs, + None, + Some(obligation.cause.span), + ) { + Ok(_) => ProcessResult::Changed(vec![]), + Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), } } } diff --git a/src/librustc_infer/traits/select.rs b/src/librustc_infer/traits/select.rs index 371268b5ee471..4572b3c026e89 100644 --- a/src/librustc_infer/traits/select.rs +++ b/src/librustc_infer/traits/select.rs @@ -532,20 +532,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::Predicate::ConstEvaluatable(def_id, substs) => { - if !(obligation.param_env, substs).has_local_value() { - match self.tcx().const_eval_resolve( - obligation.param_env, - def_id, - substs, - None, - None, - ) { - Ok(_) => Ok(EvaluatedToOk), - Err(_) => Ok(EvaluatedToErr), - } - } else { - // Inference variables still left in param_env or substs. - Ok(EvaluatedToAmbig) + match self.tcx().const_eval_resolve( + obligation.param_env, + def_id, + substs, + None, + None, + ) { + Ok(_) => Ok(EvaluatedToOk), + Err(_) => Ok(EvaluatedToErr), } } } From b8523fd0ec19a70e9d02954d1e6e3145746b63d7 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Sat, 25 Jan 2020 12:32:58 +1300 Subject: [PATCH 2/4] Erase regions before before performing const eval, to improve caching. --- src/librustc/mir/interpret/queries.rs | 36 ++++++++++------------ src/librustc/query/mod.rs | 2 +- src/librustc_mir/interpret/eval_context.rs | 6 +--- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/librustc/mir/interpret/queries.rs b/src/librustc/mir/interpret/queries.rs index ed57f81e78216..46bf1d9695796 100644 --- a/src/librustc/mir/interpret/queries.rs +++ b/src/librustc/mir/interpret/queries.rs @@ -13,13 +13,13 @@ impl<'tcx> TyCtxt<'tcx> { pub fn const_eval_poly(self, def_id: DefId) -> ConstEvalResult<'tcx> { // In some situations def_id will have substitutions within scope, but they aren't allowed // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions - // into `const_eval` which will return `ErrorHandled::ToGeneric` if any og them are + // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are // encountered. let substs = InternalSubsts::identity_for_item(self, def_id); let instance = ty::Instance::new(def_id, substs); let cid = GlobalId { instance, promoted: None }; let param_env = self.param_env(def_id).with_reveal_all(); - self.const_eval_validated(param_env.and(cid)) + self.const_eval_global_id(param_env, cid, None) } /// Resolves and evaluates a constant. @@ -41,11 +41,8 @@ impl<'tcx> TyCtxt<'tcx> { ) -> ConstEvalResult<'tcx> { let instance = ty::Instance::resolve(self, param_env, def_id, substs); if let Some(instance) = instance { - if let Some(promoted) = promoted { - self.const_eval_promoted(param_env, instance, promoted) - } else { - self.const_eval_instance(param_env, instance, span) - } + let cid = GlobalId { instance, promoted }; + self.const_eval_global_id(param_env, cid, span) } else { Err(ErrorHandled::TooGeneric) } @@ -57,22 +54,23 @@ impl<'tcx> TyCtxt<'tcx> { instance: ty::Instance<'tcx>, span: Option, ) -> ConstEvalResult<'tcx> { - let cid = GlobalId { instance, promoted: None }; - if let Some(span) = span { - self.at(span).const_eval_validated(param_env.and(cid)) - } else { - self.const_eval_validated(param_env.and(cid)) - } + self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span) } - /// Evaluate a promoted constant. - pub fn const_eval_promoted( + /// Evaluate a constant. + pub fn const_eval_global_id( self, param_env: ty::ParamEnv<'tcx>, - instance: ty::Instance<'tcx>, - promoted: mir::Promoted, + cid: GlobalId<'tcx>, + span: Option, ) -> ConstEvalResult<'tcx> { - let cid = GlobalId { instance, promoted: Some(promoted) }; - self.const_eval_validated(param_env.and(cid)) + // 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(¶m_env.and(cid)); + if let Some(span) = span { + self.at(span).const_eval_validated(inputs) + } else { + self.const_eval_validated(inputs) + } } } diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 45ab3fc0b85a6..e119c214f3372 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -502,7 +502,7 @@ rustc_queries! { /// returns a proper constant that is usable by the rest of the compiler. /// /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, - /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_promoted`. + /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`. query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> ConstEvalResult<'tcx> { no_force diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index fc4ba4d6cd978..cce4b90e224e6 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -768,11 +768,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else { self.param_env }; - let val = if let Some(promoted) = gid.promoted { - self.tcx.const_eval_promoted(param_env, gid.instance, promoted)? - } else { - self.tcx.const_eval_instance(param_env, gid.instance, Some(self.tcx.span))? - }; + let val = self.tcx.const_eval_global_id(param_env, gid, Some(self.tcx.span))?; // Even though `ecx.const_eval` is called from `eval_const_to_op` we can never have a // recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not From ea18cd7c6473df1e0de19a36ecb81165ada721ab Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Sun, 26 Jan 2020 19:37:32 +1300 Subject: [PATCH 3/4] Change FIXME message to indicate plan to handle inference variables within this code path. --- src/librustc/ty/sty.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 9cf61ebe88a16..3e0c8e23302cd 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -2488,8 +2488,8 @@ impl<'tcx> Const<'tcx> { // HACK(eddyb) when substs contain e.g. inference variables, // attempt using identity substs instead, that will succeed // when the expression doesn't depend on any parameters. - // FIXME(eddyb) make `const_eval` a canonical query instead, - // that would properly handle inference variables in `substs`. + // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that + // we can call `infcx.const_eval_resolve` which handles inference variables. if substs.has_local_value() { let identity_substs = InternalSubsts::identity_for_item(tcx, did); // The `ParamEnv` needs to match the `identity_substs`. From ebfa2f448efcce8f8121b883ee876653fc098261 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Mon, 27 Jan 2020 20:42:09 +1300 Subject: [PATCH 4/4] Add regression test for issue 68477. --- .../incremental/const-generics/issue-68477.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/test/incremental/const-generics/issue-68477.rs diff --git a/src/test/incremental/const-generics/issue-68477.rs b/src/test/incremental/const-generics/issue-68477.rs new file mode 100644 index 0000000000000..925931bc4a635 --- /dev/null +++ b/src/test/incremental/const-generics/issue-68477.rs @@ -0,0 +1,23 @@ +// edition:2018 +// revisions:rpass1 +#![feature(const_generics)] + +const FOO: usize = 1; + +struct Container { + val: std::marker::PhantomData, + blah: [(); FOO] +} + +async fn dummy() {} + +async fn foo() { + let a: Container<&'static ()>; + dummy().await; +} + +fn is_send(_: T) {} + +fn main() { + is_send(foo()); +}