rustc_type_ir/relate/
solver_relating.rs

1use tracing::{debug, instrument};
2
3use self::combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys};
4use crate::data_structures::DelayedSet;
5pub use crate::relate::*;
6use crate::solve::Goal;
7use crate::{self as ty, InferCtxtLike, Interner};
8
9pub trait RelateExt: InferCtxtLike {
10    fn relate<T: Relate<Self::Interner>>(
11        &self,
12        param_env: <Self::Interner as Interner>::ParamEnv,
13        lhs: T,
14        variance: ty::Variance,
15        rhs: T,
16        span: <Self::Interner as Interner>::Span,
17    ) -> Result<
18        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
19        TypeError<Self::Interner>,
20    >;
21
22    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
23        &self,
24        param_env: <Self::Interner as Interner>::ParamEnv,
25        lhs: T,
26        rhs: T,
27        span: <Self::Interner as Interner>::Span,
28    ) -> Result<
29        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
30        TypeError<Self::Interner>,
31    >;
32}
33
34impl<Infcx: InferCtxtLike> RelateExt for Infcx {
35    fn relate<T: Relate<Self::Interner>>(
36        &self,
37        param_env: <Self::Interner as Interner>::ParamEnv,
38        lhs: T,
39        variance: ty::Variance,
40        rhs: T,
41        span: <Self::Interner as Interner>::Span,
42    ) -> Result<
43        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
44        TypeError<Self::Interner>,
45    > {
46        let mut relate =
47            SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env, span);
48        relate.relate(lhs, rhs)?;
49        Ok(relate.goals)
50    }
51
52    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
53        &self,
54        param_env: <Self::Interner as Interner>::ParamEnv,
55        lhs: T,
56        rhs: T,
57        span: <Self::Interner as Interner>::Span,
58    ) -> Result<
59        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
60        TypeError<Self::Interner>,
61    > {
62        let mut relate = SolverRelating::new(
63            self,
64            StructurallyRelateAliases::Yes,
65            ty::Invariant,
66            param_env,
67            span,
68        );
69        relate.relate(lhs, rhs)?;
70        Ok(relate.goals)
71    }
72}
73
74/// Enforce that `a` is equal to or a subtype of `b`.
75pub struct SolverRelating<'infcx, Infcx, I: Interner> {
76    infcx: &'infcx Infcx,
77    // Immutable fields.
78    structurally_relate_aliases: StructurallyRelateAliases,
79    param_env: I::ParamEnv,
80    span: I::Span,
81    // Mutable fields.
82    ambient_variance: ty::Variance,
83    goals: Vec<Goal<I, I::Predicate>>,
84    /// The cache only tracks the `ambient_variance` as it's the
85    /// only field which is mutable and which meaningfully changes
86    /// the result when relating types.
87    ///
88    /// The cache does not track whether the state of the
89    /// `Infcx` has been changed or whether we've added any
90    /// goals to `self.goals`. Whether a goal is added once or multiple
91    /// times is not really meaningful.
92    ///
93    /// Changes in the inference state may delay some type inference to
94    /// the next fulfillment loop. Given that this loop is already
95    /// necessary, this is also not a meaningful change. Consider
96    /// the following three relations:
97    /// ```text
98    /// Vec<?0> sub Vec<?1>
99    /// ?0 eq u32
100    /// Vec<?0> sub Vec<?1>
101    /// ```
102    /// Without a cache, the second `Vec<?0> sub Vec<?1>` would eagerly
103    /// constrain `?1` to `u32`. When using the cache entry from the
104    /// first time we've related these types, this only happens when
105    /// later proving the `Subtype(?0, ?1)` goal from the first relation.
106    cache: DelayedSet<(ty::Variance, I::Ty, I::Ty)>,
107}
108
109impl<'infcx, Infcx, I> SolverRelating<'infcx, Infcx, I>
110where
111    Infcx: InferCtxtLike<Interner = I>,
112    I: Interner,
113{
114    pub fn new(
115        infcx: &'infcx Infcx,
116        structurally_relate_aliases: StructurallyRelateAliases,
117        ambient_variance: ty::Variance,
118        param_env: I::ParamEnv,
119        span: I::Span,
120    ) -> Self {
121        SolverRelating {
122            infcx,
123            structurally_relate_aliases,
124            span,
125            ambient_variance,
126            param_env,
127            goals: vec![],
128            cache: Default::default(),
129        }
130    }
131}
132
133impl<Infcx, I> TypeRelation<I> for SolverRelating<'_, Infcx, I>
134where
135    Infcx: InferCtxtLike<Interner = I>,
136    I: Interner,
137{
138    fn cx(&self) -> I {
139        self.infcx.cx()
140    }
141
142    fn relate_item_args(
143        &mut self,
144        item_def_id: I::DefId,
145        a_arg: I::GenericArgs,
146        b_arg: I::GenericArgs,
147    ) -> RelateResult<I, I::GenericArgs> {
148        if self.ambient_variance == ty::Invariant {
149            // Avoid fetching the variance if we are in an invariant
150            // context; no need, and it can induce dependency cycles
151            // (e.g., #41849).
152            relate_args_invariantly(self, a_arg, b_arg)
153        } else {
154            let tcx = self.cx();
155            let opt_variances = tcx.variances_of(item_def_id);
156            relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, false)
157        }
158    }
159
160    fn relate_with_variance<T: Relate<I>>(
161        &mut self,
162        variance: ty::Variance,
163        _info: VarianceDiagInfo<I>,
164        a: T,
165        b: T,
166    ) -> RelateResult<I, T> {
167        let old_ambient_variance = self.ambient_variance;
168        self.ambient_variance = self.ambient_variance.xform(variance);
169        debug!(?self.ambient_variance, "new ambient variance");
170
171        let r = if self.ambient_variance == ty::Bivariant { Ok(a) } else { self.relate(a, b) };
172
173        self.ambient_variance = old_ambient_variance;
174        r
175    }
176
177    #[instrument(skip(self), level = "trace")]
178    fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult<I, I::Ty> {
179        if a == b {
180            return Ok(a);
181        }
182
183        let infcx = self.infcx;
184        let a = infcx.shallow_resolve(a);
185        let b = infcx.shallow_resolve(b);
186
187        if self.cache.contains(&(self.ambient_variance, a, b)) {
188            return Ok(a);
189        }
190
191        match (a.kind(), b.kind()) {
192            (ty::Infer(ty::TyVar(a_id)), ty::Infer(ty::TyVar(b_id))) => {
193                match self.ambient_variance {
194                    ty::Covariant => {
195                        // can't make progress on `A <: B` if both A and B are
196                        // type variables, so record an obligation.
197                        self.goals.push(Goal::new(
198                            self.cx(),
199                            self.param_env,
200                            ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
201                                a_is_expected: true,
202                                a,
203                                b,
204                            })),
205                        ));
206                    }
207                    ty::Contravariant => {
208                        // can't make progress on `B <: A` if both A and B are
209                        // type variables, so record an obligation.
210                        self.goals.push(Goal::new(
211                            self.cx(),
212                            self.param_env,
213                            ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
214                                a_is_expected: false,
215                                a: b,
216                                b: a,
217                            })),
218                        ));
219                    }
220                    ty::Invariant => {
221                        infcx.equate_ty_vids_raw(a_id, b_id);
222                    }
223                    ty::Bivariant => {
224                        unreachable!("Expected bivariance to be handled in relate_with_variance")
225                    }
226                }
227            }
228
229            (ty::Infer(ty::TyVar(a_vid)), _) => {
230                infcx.instantiate_ty_var_raw(self, true, a_vid, self.ambient_variance, b)?;
231            }
232            (_, ty::Infer(ty::TyVar(b_vid))) => {
233                infcx.instantiate_ty_var_raw(
234                    self,
235                    false,
236                    b_vid,
237                    self.ambient_variance.xform(ty::Contravariant),
238                    a,
239                )?;
240            }
241
242            _ => {
243                super_combine_tys(self.infcx, self, a, b)?;
244            }
245        }
246
247        assert!(self.cache.insert((self.ambient_variance, a, b)));
248
249        Ok(a)
250    }
251
252    #[instrument(skip(self), level = "trace")]
253    fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult<I, I::Region> {
254        match self.ambient_variance {
255            // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
256            ty::Covariant => self.infcx.sub_regions(b, a, self.span),
257            // Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b)
258            ty::Contravariant => self.infcx.sub_regions(a, b, self.span),
259            ty::Invariant => self.infcx.equate_regions(a, b, self.span),
260            ty::Bivariant => {
261                unreachable!("Expected bivariance to be handled in relate_with_variance")
262            }
263        }
264
265        Ok(a)
266    }
267
268    #[instrument(skip(self), level = "trace")]
269    fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult<I, I::Const> {
270        super_combine_consts(self.infcx, self, a, b)
271    }
272
273    fn binders<T>(
274        &mut self,
275        a: ty::Binder<I, T>,
276        b: ty::Binder<I, T>,
277    ) -> RelateResult<I, ty::Binder<I, T>>
278    where
279        T: Relate<I>,
280    {
281        // If they're equal, then short-circuit.
282        if a == b {
283            return Ok(a);
284        }
285
286        // If they have no bound vars, relate normally.
287        if let Some(a_inner) = a.no_bound_vars() {
288            if let Some(b_inner) = b.no_bound_vars() {
289                self.relate(a_inner, b_inner)?;
290                return Ok(a);
291            }
292        };
293
294        match self.ambient_variance {
295            // Checks whether `for<..> sub <: for<..> sup` holds.
296            //
297            // For this to hold, **all** instantiations of the super type
298            // have to be a super type of **at least one** instantiation of
299            // the subtype.
300            //
301            // This is implemented by first entering a new universe.
302            // We then replace all bound variables in `sup` with placeholders,
303            // and all bound variables in `sub` with inference vars.
304            // We can then just relate the two resulting types as normal.
305            //
306            // Note: this is a subtle algorithm. For a full explanation, please see
307            // the [rustc dev guide][rd]
308            //
309            // [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html
310            ty::Covariant => {
311                self.infcx.enter_forall(b, |b| {
312                    let a = self.infcx.instantiate_binder_with_infer(a);
313                    self.relate(a, b)
314                })?;
315            }
316            ty::Contravariant => {
317                self.infcx.enter_forall(a, |a| {
318                    let b = self.infcx.instantiate_binder_with_infer(b);
319                    self.relate(a, b)
320                })?;
321            }
322
323            // When **equating** binders, we check that there is a 1-to-1
324            // correspondence between the bound vars in both types.
325            //
326            // We do so by separately instantiating one of the binders with
327            // placeholders and the other with inference variables and then
328            // equating the instantiated types.
329            //
330            // We want `for<..> A == for<..> B` -- therefore we want
331            // `exists<..> A == for<..> B` and `exists<..> B == for<..> A`.
332            // Check if `exists<..> A == for<..> B`
333            ty::Invariant => {
334                self.infcx.enter_forall(b, |b| {
335                    let a = self.infcx.instantiate_binder_with_infer(a);
336                    self.relate(a, b)
337                })?;
338
339                // Check if `exists<..> B == for<..> A`.
340                self.infcx.enter_forall(a, |a| {
341                    let b = self.infcx.instantiate_binder_with_infer(b);
342                    self.relate(a, b)
343                })?;
344            }
345            ty::Bivariant => {
346                unreachable!("Expected bivariance to be handled in relate_with_variance")
347            }
348        }
349        Ok(a)
350    }
351}
352
353impl<Infcx, I> PredicateEmittingRelation<Infcx> for SolverRelating<'_, Infcx, I>
354where
355    Infcx: InferCtxtLike<Interner = I>,
356    I: Interner,
357{
358    fn span(&self) -> I::Span {
359        Span::dummy()
360    }
361
362    fn param_env(&self) -> I::ParamEnv {
363        self.param_env
364    }
365
366    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
367        self.structurally_relate_aliases
368    }
369
370    fn register_predicates(
371        &mut self,
372        obligations: impl IntoIterator<Item: ty::Upcast<I, I::Predicate>>,
373    ) {
374        self.goals.extend(
375            obligations.into_iter().map(|pred| Goal::new(self.infcx.cx(), self.param_env, pred)),
376        );
377    }
378
379    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>) {
380        self.goals.extend(obligations);
381    }
382
383    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty) {
384        self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
385            ty::Covariant => ty::PredicateKind::AliasRelate(
386                a.into(),
387                b.into(),
388                ty::AliasRelationDirection::Subtype,
389            ),
390            // a :> b is b <: a
391            ty::Contravariant => ty::PredicateKind::AliasRelate(
392                b.into(),
393                a.into(),
394                ty::AliasRelationDirection::Subtype,
395            ),
396            ty::Invariant => ty::PredicateKind::AliasRelate(
397                a.into(),
398                b.into(),
399                ty::AliasRelationDirection::Equate,
400            ),
401            ty::Bivariant => {
402                unreachable!("Expected bivariance to be handled in relate_with_variance")
403            }
404        })]);
405    }
406}