rustc_infer/infer/relate/
type_relating.rs

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