1//! This module contains code to instantiate new values into a
2//! `Canonical<'tcx, T>`.
3//!
4//! For an overview of what canonicalization is and how it fits into
5//! rustc, check out the [chapter in the rustc dev guide][c].
6//!
7//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
89use rustc_macros::extension;
10use rustc_middle::ty::{
11self, DelayedMap, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
12TypeVisitableExt, TypeVisitor,
13};
14use rustc_type_ir::{TypeFlags, TypeVisitable};
1516use crate::infer::canonical::{Canonical, CanonicalVarValues};
1718/// FIXME(-Znext-solver): This or public because it is shared with the
19/// new trait solver implementation. We should deduplicate canonicalization.
20impl<'tcx, V> CanonicalExt<'tcx, V> for Canonical<'tcx, V> {
#[doc = " Instantiate the wrapped value, replacing each canonical value"]
#[doc = " with the value given in `var_values`."]
fn instantiate(&self, tcx: TyCtxt<'tcx>,
var_values: &CanonicalVarValues<'tcx>) -> V where
V: TypeFoldable<TyCtxt<'tcx>> {
self.instantiate_projected(tcx, var_values, |value| value.clone())
}
#[doc = " Allows one to apply a instantiation to some subset of"]
#[doc = " `self.value`. Invoke `projection_fn` with `self.value` to get"]
#[doc = " a value V that is expressed in terms of the same canonical"]
#[doc = " variables bound in `self` (usually this extracts from subset"]
#[doc = " of `self`). Apply the instantiation `var_values` to this value"]
#[doc = " V, replacing each of the canonical variables."]
fn instantiate_projected<T>(&self, tcx: TyCtxt<'tcx>,
var_values: &CanonicalVarValues<'tcx>,
projection_fn: impl FnOnce(&V) -> T) -> T where
T: TypeFoldable<TyCtxt<'tcx>> {
match (&self.var_kinds.len(), &var_values.len()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
let value = projection_fn(&self.value);
instantiate_value(tcx, var_values, value)
}
}#[extension(pub trait CanonicalExt<'tcx, V>)]21impl<'tcx, V> Canonical<'tcx, V> {
22/// Instantiate the wrapped value, replacing each canonical value
23 /// with the value given in `var_values`.
24fn instantiate(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
25where
26V: TypeFoldable<TyCtxt<'tcx>>,
27 {
28self.instantiate_projected(tcx, var_values, |value| value.clone())
29 }
3031/// Allows one to apply a instantiation to some subset of
32 /// `self.value`. Invoke `projection_fn` with `self.value` to get
33 /// a value V that is expressed in terms of the same canonical
34 /// variables bound in `self` (usually this extracts from subset
35 /// of `self`). Apply the instantiation `var_values` to this value
36 /// V, replacing each of the canonical variables.
37fn instantiate_projected<T>(
38&self,
39 tcx: TyCtxt<'tcx>,
40 var_values: &CanonicalVarValues<'tcx>,
41 projection_fn: impl FnOnce(&V) -> T,
42 ) -> T
43where
44T: TypeFoldable<TyCtxt<'tcx>>,
45 {
46assert_eq!(self.var_kinds.len(), var_values.len());
47let value = projection_fn(&self.value);
48instantiate_value(tcx, var_values, value)
49 }
50}
5152/// Instantiate the values from `var_values` into `value`. `var_values`
53/// must be values for the set of canonical variables that appear in
54/// `value`.
55pub(super) fn instantiate_value<'tcx, T>(
56 tcx: TyCtxt<'tcx>,
57 var_values: &CanonicalVarValues<'tcx>,
58 value: T,
59) -> T
60where
61T: TypeFoldable<TyCtxt<'tcx>>,
62{
63if var_values.var_values.is_empty() {
64return value;
65 }
6667value.fold_with(&mut CanonicalInstantiator {
68tcx,
69 var_values: var_values.var_values,
70 cache: Default::default(),
71 })
72}
7374/// Replaces the bound vars in a canonical binder with var values.
75struct CanonicalInstantiator<'tcx> {
76 tcx: TyCtxt<'tcx>,
7778// The values that the bound vars are are being instantiated with.
79var_values: ty::GenericArgsRef<'tcx>,
8081// Because we use `ty::BoundVarIndexKind::Canonical`, we can cache
82 // based only on the entire ty, not worrying about a `DebruijnIndex`
83cache: DelayedMap<Ty<'tcx>, Ty<'tcx>>,
84}
8586impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
87fn cx(&self) -> TyCtxt<'tcx> {
88self.tcx
89 }
9091fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
92match *t.kind() {
93 ty::Bound(ty::BoundVarIndexKind::Canonical, bound_ty) => {
94self.var_values[bound_ty.var.as_usize()].expect_ty()
95 }
96_ => {
97if !t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
98t99 } else if let Some(&t) = self.cache.get(&t) {
100t101 } else {
102let res = t.super_fold_with(self);
103if !self.cache.insert(t, res) {
::core::panicking::panic("assertion failed: self.cache.insert(t, res)")
};assert!(self.cache.insert(t, res));
104res105 }
106 }
107 }
108 }
109110fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
111match r.kind() {
112 ty::ReBound(ty::BoundVarIndexKind::Canonical, br) => {
113self.var_values[br.var.as_usize()].expect_region()
114 }
115_ => r,
116 }
117 }
118119fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
120match ct.kind() {
121 ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bound_const) => {
122self.var_values[bound_const.var.as_usize()].expect_const()
123 }
124_ => ct.super_fold_with(self),
125 }
126 }
127128fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
129if p.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { p.super_fold_with(self) } else { p }
130 }
131132fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
133if !c.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
134return c;
135 }
136137// Our cache key is `(clauses, var_values)`, but we also don't care about
138 // var values that aren't named in the clauses, since they can change without
139 // affecting the output. Since `ParamEnv`s are cached first, we compute the
140 // last var value that is mentioned in the clauses, and cut off the list so
141 // that we have more hits in the cache.
142143 // We also cache the computation of "highest var named by clauses" since that
144 // is both expensive (depending on the size of the clauses) and a pure function.
145let index = *self146 .tcx
147 .highest_var_in_clauses_cache
148 .lock()
149 .entry(c)
150 .or_insert_with(|| highest_var_in_clauses(c));
151let c_args = &self.var_values[..=index];
152153if let Some(c) = self.tcx.clauses_cache.lock().get(&(c, c_args)) {
154c155 } else {
156let folded = c.super_fold_with(self);
157self.tcx.clauses_cache.lock().insert((c, c_args), folded);
158folded159 }
160 }
161}
162163fn highest_var_in_clauses<'tcx>(c: ty::Clauses<'tcx>) -> usize {
164struct HighestVarInClauses {
165 max_var: usize,
166 }
167impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HighestVarInClauses {
168fn visit_ty(&mut self, t: Ty<'tcx>) {
169if let ty::Bound(ty::BoundVarIndexKind::Canonical, bound_ty) = *t.kind() {
170self.max_var = self.max_var.max(bound_ty.var.as_usize());
171 } else if t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
172t.super_visit_with(self);
173 }
174 }
175fn visit_region(&mut self, r: ty::Region<'tcx>) {
176if let ty::ReBound(ty::BoundVarIndexKind::Canonical, bound_region) = r.kind() {
177self.max_var = self.max_var.max(bound_region.var.as_usize());
178 }
179 }
180fn visit_const(&mut self, ct: ty::Const<'tcx>) {
181if let ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bound_const) = ct.kind() {
182self.max_var = self.max_var.max(bound_const.var.as_usize());
183 } else if ct.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
184ct.super_visit_with(self);
185 }
186 }
187 }
188let mut visitor = HighestVarInClauses { max_var: 0 };
189c.visit_with(&mut visitor);
190visitor.max_var
191}