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