Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn const eval queries into canonical queries #67717

6 changes: 6 additions & 0 deletions src/librustc/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ macro_rules! arena_types {
Vec<rustc::traits::query::outlives_bounds::OutlivesBound<'tcx>>
>
>,
[] resolve_vtables:
rustc::infer::canonical::Canonical<'tcx,
rustc::infer::canonical::QueryResponse<'tcx,
rustc::traits::Vtable<'tcx, ()>
>
>,
[] type_op_subtype:
rustc::infer::canonical::Canonical<'tcx,
rustc::infer::canonical::QueryResponse<'tcx, ()>
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
use crate::hir::map::DefPathHash;
use crate::ich::{Fingerprint, StableHashingContext};
use crate::mir;
use crate::mir::interpret::GlobalId;
use crate::mir::interpret::ConstEvalInput;
use crate::traits;
use crate::traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTraitGoal, CanonicalTyGoal,
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal,
};
Expand Down
11 changes: 3 additions & 8 deletions src/librustc/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ use crate::infer::InferCtxt;
use crate::ty::flags::FlagComputation;
use crate::ty::fold::{TypeFoldable, TypeFolder};
use crate::ty::subst::GenericArg;
use crate::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags};
use std::sync::atomic::Ordering;
use crate::ty::{self, BoundVar, InferConst, Ty, TyCtxt, TypeFlags};

use rustc_data_structures::fx::FxHashMap;
use rustc_index::vec::Idx;
use smallvec::SmallVec;
use std::sync::atomic::Ordering;

impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
/// Canonicalizes a query value `V`. When we canonicalize a query,
Expand Down Expand Up @@ -496,12 +496,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {

// Fast path: nothing that needs to be canonicalized.
if !value.has_type_flags(needs_canonical_flags) {
let canon_value = Canonical {
max_universe: ty::UniverseIndex::ROOT,
variables: List::empty(),
value: value.clone(),
};
return canon_value;
return Canonical::empty(value.clone());
}

let mut canonicalizer = Canonicalizer {
Expand Down
13 changes: 13 additions & 0 deletions src/librustc/infer/canonical/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,19 @@ impl<'tcx, R> Canonical<'tcx, QueryResponse<'tcx, R>> {
}
}

impl<'tcx, V: TypeFoldable<'tcx>> Canonical<'tcx, V> {
/// Construct a canonical `value` with an empty set of variables
/// in the `ROOT` universe.
pub fn empty(value: V) -> Canonical<'tcx, V> {
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved
assert!(!value.has_local_value() && !value.has_placeholders());
Canonical {
max_universe: ty::UniverseIndex::ROOT,
variables: List::empty(),
value: value.clone(),
}
}
}

impl<'tcx, V> Canonical<'tcx, V> {
/// Allows you to map the `value` of a canonical while keeping the
/// same set of bound variables.
Expand Down
19 changes: 19 additions & 0 deletions src/librustc/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ pub use self::allocation::{Allocation, AllocationExtra, Relocations, UndefMask};

pub use self::pointer::{CheckInAllocMsg, Pointer, PointerArithmetic};

use crate::infer::canonical::Canonical;
use crate::mir;
use crate::traits::Reveal;
use crate::ty::codec::TyDecoder;
use crate::ty::layout::{self, Size};
use crate::ty::subst::GenericArgKind;
Expand Down Expand Up @@ -147,6 +149,23 @@ pub struct GlobalId<'tcx> {
pub promoted: Option<mir::Promoted>,
}

/// The input type for const eval queries. Const eval queries are given both the `ParamEnv` in which
/// the constant is evaluated in and the identifier of the constant.
pub type ConstEvalInput<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>>;
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved

impl ConstEvalInput<'_> {
/// The `DefId` of the constant that is being evaluated.
pub fn def_id(&self) -> DefId {
self.value.value.instance.def_id()
}

pub fn with_reveal_user_facing(&self) -> Self {
let mut new_input = self.clone();
new_input.value.param_env.reveal = Reveal::UserFacing;
new_input
}
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)]
pub struct AllocId(pub u64);

Expand Down
69 changes: 59 additions & 10 deletions src/librustc/mir/interpret/queries.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::{ConstEvalResult, ErrorHandled, GlobalId};

use crate::infer::canonical::{Canonical, OriginalQueryValues};
use crate::infer::InferCtxt;
use crate::mir;
use crate::ty::subst::{InternalSubsts, SubstsRef};
use crate::ty::{self, TyCtxt};
Expand All @@ -19,7 +21,7 @@ impl<'tcx> TyCtxt<'tcx> {
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_validated(Canonical::empty(param_env.and(cid)))
}

/// Resolves and evaluates a constant.
Expand All @@ -38,25 +40,23 @@ impl<'tcx> TyCtxt<'tcx> {
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)
}
self.infer_ctxt()
.enter(|ref infcx| infcx.const_eval_resolve(param_env, def_id, substs, span))
}

/// Evaluates the constant represented by the instance.
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 };
let canonical = Canonical::empty(param_env.and(cid));
if let Some(span) = span {
self.at(span).const_eval_validated(param_env.and(cid))
self.at(span).const_eval_validated(canonical)
} else {
self.const_eval_validated(param_env.and(cid))
self.const_eval_validated(canonical)
}
}

Expand All @@ -68,6 +68,55 @@ impl<'tcx> TyCtxt<'tcx> {
) -> ConstEvalResult<'tcx> {
let cid = GlobalId { instance, promoted: Some(promoted) };
let param_env = ty::ParamEnv::reveal_all();
self.const_eval_validated(param_env.and(cid))
self.const_eval_validated(Canonical::empty(param_env.and(cid)))
}
}

impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// Evaluates the constant represented by the instance.
///
/// The given `ParamEnv` and `Instance` can contain inference variables from this inference
/// context.
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 };
let mut orig_values = OriginalQueryValues::default();
let canonical = self.canonicalize_query(&param_env.and(cid), &mut orig_values);
if let Some(span) = span {
self.tcx.at(span).const_eval_validated(canonical)
} else {
self.tcx.const_eval_validated(canonical)
}
}

/// 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.
///
/// The given `ParamEnv` and `substs` can contain inference variables from this inference
/// context.
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)
}
}
}
26 changes: 12 additions & 14 deletions src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::dep_graph::{DepKind, DepNode, RecoverKey, SerializedDepNodeIndex};
use crate::mir;
use crate::mir::interpret::GlobalId;
use crate::mir::interpret::ConstEvalInput;
use crate::traits;
use crate::traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTraitGoal, CanonicalTyGoal,
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal,
};
Expand Down Expand Up @@ -467,12 +467,12 @@ rustc_queries! {
/// during validation. Please add a comment to every use site explaining why using
/// `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>>)
query const_eval_raw(key: ConstEvalInput<'tcx>)
-> ConstEvalRawResult<'tcx> {
no_force
desc { |tcx|
"const-evaluating `{}`",
tcx.def_path_str(key.value.instance.def.def_id())
tcx.def_path_str(key.def_id())
}
}

Expand All @@ -484,12 +484,12 @@ rustc_queries! {
///
/// **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>>)
query const_eval_validated(key: ConstEvalInput<'tcx>)
-> ConstEvalResult<'tcx> {
no_force
desc { |tcx|
"const-evaluating + checking `{}`",
tcx.def_path_str(key.value.instance.def.def_id())
tcx.def_path_str(key.def_id())
}
cache_on_disk_if(_, opt_result) {
// Only store results without errors
Expand Down Expand Up @@ -592,17 +592,15 @@ rustc_queries! {
no_force
desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) }
}
}

Codegen {
query codegen_fulfill_obligation(
key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
) -> Vtable<'tcx, ()> {
/// Do not call this query directly: invoke `infcx.resolve_vtable()` instead.
query resolve_vtable(
goal: CanonicalTraitGoal<'tcx>
) -> Result<&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vtable<'tcx, ()>>>, NoSolution> {
no_force
cache_on_disk_if { true }
desc { |tcx|
"checking if `{}` fulfills its obligations",
tcx.def_path_str(key.1.def_id())
"resolving vtable for `{}`",
tcx.def_path_str(goal.value.value.def_id())
}
}
}
Expand Down
Loading