diff options
| author | Mazdak Farrokhzad <twingoow@gmail.com> | 2019-12-22 19:46:07 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-12-22 19:46:07 +0100 |
| commit | 97bee3a793e287162d8f10635d6769e8d28befab (patch) | |
| tree | 622673b10419d797b82ece48ca3b7f8fb15e022a /src/librustc | |
| parent | 0d2817a439c2ad9fe343f6347eb6d90947893363 (diff) | |
| parent | c010d843aacc32ed2bc03d36121aa7f6e08ef045 (diff) | |
| download | rust-97bee3a793e287162d8f10635d6769e8d28befab.tar.gz rust-97bee3a793e287162d8f10635d6769e8d28befab.zip | |
Rollup merge of #66877 - skinny121:const-eval-entry-points, r=oli-obk
Add simpler entry points to const eval for common usages. I found the `tcx.const_eval` API to be complex/awkward to work with, because of the inherent complexity from all of the different situations it is called from. Though it mainly used in one of the following ways: - Evaluates the value of a constant without any substitutions, e.g. evaluating a static, discriminant, etc. - Evaluates the value of a resolved instance of a constant. this happens when evaluating unevaluated constants or normalising trait constants. - Evaluates a promoted constant. This PR adds three new functions `const_eval_mono`, `const_eval_resolve`, and `const_eval_promoted` to `TyCtxt`, which each cater to one of the three ways `tcx.const_eval` is normally used.
Diffstat (limited to 'src/librustc')
| -rw-r--r-- | src/librustc/mir/interpret/mod.rs | 4 | ||||
| -rw-r--r-- | src/librustc/mir/interpret/queries.rs | 89 | ||||
| -rw-r--r-- | src/librustc/query/mod.rs | 11 | ||||
| -rw-r--r-- | src/librustc/traits/fulfill.rs | 29 | ||||
| -rw-r--r-- | src/librustc/traits/select.rs | 22 | ||||
| -rw-r--r-- | src/librustc/ty/mod.rs | 10 | ||||
| -rw-r--r-- | src/librustc/ty/sty.rs | 12 |
7 files changed, 120 insertions, 57 deletions
diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index fff876752db..80bac92d003 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -101,6 +101,7 @@ mod error; mod value; mod allocation; mod pointer; +mod queries; pub use self::error::{ InterpErrorInfo, InterpResult, InterpError, AssertMessage, ConstEvalErr, struct_error, @@ -116,9 +117,10 @@ pub use self::pointer::{Pointer, PointerArithmetic, CheckInAllocMsg}; use crate::mir; use crate::hir::def_id::DefId; -use crate::ty::{self, TyCtxt, Instance, subst::GenericArgKind}; +use crate::ty::{self, TyCtxt, Instance}; use crate::ty::codec::TyDecoder; use crate::ty::layout::{self, Size}; +use crate::ty::subst::GenericArgKind; use std::io; use std::fmt; use std::num::NonZeroU32; diff --git a/src/librustc/mir/interpret/queries.rs b/src/librustc/mir/interpret/queries.rs new file mode 100644 index 00000000000..5fd49a056d2 --- /dev/null +++ b/src/librustc/mir/interpret/queries.rs @@ -0,0 +1,89 @@ +use super::{ConstEvalResult, ErrorHandled, GlobalId}; + +use crate::mir; +use crate::hir::def_id::DefId; +use crate::ty::{self, TyCtxt}; +use crate::ty::subst::{InternalSubsts, SubstsRef}; +use syntax_pos::Span; + + +impl<'tcx> TyCtxt<'tcx> { + + /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts + /// that can't take any generic arguments like statics, const items or enum discriminants. If a + /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. + 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 + // 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); + self.const_eval_validated(param_env.and(cid)) + } + + /// Resolves and evaluates a constant. + /// + /// The constant can be located on a trait like `<A as B>::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<T>() { let _ = [0; bar::<T>()]; }` the repeat count + /// constant `bar::<T>()` 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. + pub fn const_eval_resolve( + self, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + span: Option<Span> + ) -> ConstEvalResult<'tcx> { + let instance = ty::Instance::resolve( + self, + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + self.const_eval_instance(param_env, instance, span) + } else { + Err(ErrorHandled::TooGeneric) + } + } + + pub fn const_eval_instance( + self, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + span: Option<Span> + ) -> 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)) + } + } + + /// Evaluate a promoted constant. + pub fn const_eval_promoted( + self, + instance: ty::Instance<'tcx>, + promoted: mir::Promoted + ) -> ConstEvalResult<'tcx> { + let cid = GlobalId { + instance, + promoted: Some(promoted), + }; + let param_env = ty::ParamEnv::reveal_all(); + self.const_eval_validated(param_env.and(cid)) + } +} diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index cc02165f605..a9dd856e758 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -448,7 +448,8 @@ rustc_queries! { /// /// **Do not use this** outside const eval. Const eval uses this to break query cycles /// during validation. Please add a comment to every use site explaining why using - /// `const_eval` isn't sufficient. + /// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable + /// form to be used outside of const eval. query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> ConstEvalRawResult<'tcx> { no_force @@ -460,7 +461,13 @@ rustc_queries! { /// Results of evaluating const items or constants embedded in /// other items (such as enum variant explicit discriminants). - query const_eval(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + /// + /// In contrast to `const_eval_raw` this performs some validation on the constant, and + /// 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`. + query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> ConstEvalResult<'tcx> { no_force desc { |tcx| diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 27731990d2b..3fd2415c83c 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -1,5 +1,4 @@ use crate::infer::{InferCtxt, ShallowResolver}; -use crate::mir::interpret::{GlobalId, ErrorHandled}; use crate::ty::{self, Ty, TypeFoldable, ToPolyTraitRef}; use crate::ty::error::ExpectedFound; use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; @@ -501,27 +500,13 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { ProcessResult::Unchanged } else { if !substs.has_local_value() { - let instance = ty::Instance::resolve( - self.selcx.tcx(), - obligation.param_env, - def_id, - substs, - ); - if let Some(instance) = instance { - let cid = GlobalId { - instance, - promoted: None, - }; - match self.selcx.tcx().at(obligation.cause.span) - .const_eval(obligation.param_env.and(cid)) { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => ProcessResult::Error( - CodeSelectionError(ConstEvalFailure(err))) - } - } else { - ProcessResult::Error(CodeSelectionError( - ConstEvalFailure(ErrorHandled::TooGeneric) - )) + match self.selcx.tcx().const_eval_resolve(obligation.param_env, + def_id, + substs, + Some(obligation.cause.span)) { + Ok(_) => ProcessResult::Changed(vec![]), + Err(err) => ProcessResult::Error( + CodeSelectionError(ConstEvalFailure(err))) } } else { pending_obligation.stalled_on = diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index f1cd002d19b..afc8a4d9e14 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -33,7 +33,6 @@ use crate::dep_graph::{DepKind, DepNodeIndex}; use crate::hir::def_id::DefId; use crate::infer::{CombinedSnapshot, InferCtxt, InferOk, PlaceholderMap, TypeFreshener}; use crate::middle::lang_items; -use crate::mir::interpret::GlobalId; use crate::ty::fast_reject; use crate::ty::relate::TypeRelation; use crate::ty::subst::{Subst, SubstsRef}; @@ -820,22 +819,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::Predicate::ConstEvaluatable(def_id, substs) => { - let tcx = self.tcx(); if !(obligation.param_env, substs).has_local_value() { - let param_env = obligation.param_env; - let instance = - ty::Instance::resolve(tcx, param_env, def_id, substs); - if let Some(instance) = instance { - let cid = GlobalId { - instance, - promoted: None, - }; - match self.tcx().const_eval(param_env.and(cid)) { - Ok(_) => Ok(EvaluatedToOk), - Err(_) => Ok(EvaluatedToErr), - } - } else { - Ok(EvaluatedToErr) + match self.tcx().const_eval_resolve(obligation.param_env, + def_id, + substs, + None) { + Ok(_) => Ok(EvaluatedToOk), + Err(_) => Ok(EvaluatedToErr), } } else { // Inference variables still left in param_env or substs. diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 37a443a2c74..30356d59a7c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -19,7 +19,7 @@ use crate::middle::cstore::CrateStoreDyn; use crate::middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem}; use crate::middle::resolve_lifetime::ObjectLifetimeDefault; use crate::mir::ReadOnlyBodyAndCache; -use crate::mir::interpret::{GlobalId, ErrorHandled}; +use crate::mir::interpret::ErrorHandled; use crate::mir::GeneratorLayout; use crate::session::CrateDisambiguator; use crate::traits::{self, Reveal}; @@ -2344,13 +2344,7 @@ impl<'tcx> AdtDef { pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option<Discr<'tcx>> { let param_env = tcx.param_env(expr_did); let repr_type = self.repr.discr_type(); - let substs = InternalSubsts::identity_for_item(tcx, expr_did); - let instance = ty::Instance::new(expr_did, substs); - let cid = GlobalId { - instance, - promoted: None - }; - match tcx.const_eval(param_env.and(cid)) { + match tcx.const_eval_poly(expr_did) { Ok(val) => { // FIXME: Find the right type and use it instead of `val.ty` here if let Some(b) = val.try_eval_bits(tcx, param_env, val.ty) { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 02efe8df03d..604dc03ea17 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -15,7 +15,7 @@ use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFolda use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv}; use crate::ty::layout::VariantIdx; use crate::util::captures::Captures; -use crate::mir::interpret::{Scalar, GlobalId}; +use crate::mir::interpret::Scalar; use polonius_engine::Atom; use rustc_index::vec::Idx; @@ -2340,13 +2340,9 @@ impl<'tcx> Const<'tcx> { let (param_env, substs) = param_env_and_substs.into_parts(); - // try to resolve e.g. associated constants to their definition on an impl - let instance = ty::Instance::resolve(tcx, param_env, did, substs)?; - let gid = GlobalId { - instance, - promoted: None, - }; - tcx.const_eval(param_env.and(gid)).ok() + // try to resolve e.g. associated constants to their definition on an impl, and then + // evaluate the const. + tcx.const_eval_resolve(param_env, did, substs, None).ok() }; match self.val { |
