rustc_hir_analysis/variance/
constraints.rs

1//! Constraint construction and representation
2//!
3//! The second pass over the HIR determines the set of constraints.
4//! We walk the set of items and, for each member, generate new constraints.
5
6use hir::def_id::{DefId, LocalDefId};
7use rustc_hir as hir;
8use rustc_hir::def::DefKind;
9use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, Ty, TyCtxt};
10use rustc_middle::{bug, span_bug};
11use tracing::{debug, instrument};
12
13use super::terms::VarianceTerm::*;
14use super::terms::*;
15
16pub(crate) struct ConstraintContext<'a, 'tcx> {
17    pub terms_cx: TermsContext<'a, 'tcx>,
18
19    // These are pointers to common `ConstantTerm` instances
20    covariant: VarianceTermPtr<'a>,
21    contravariant: VarianceTermPtr<'a>,
22    invariant: VarianceTermPtr<'a>,
23    bivariant: VarianceTermPtr<'a>,
24
25    pub constraints: Vec<Constraint<'a>>,
26}
27
28/// Declares that the variable `decl_id` appears in a location with
29/// variance `variance`.
30#[derive(Copy, Clone)]
31pub(crate) struct Constraint<'a> {
32    pub inferred: InferredIndex,
33    pub variance: &'a VarianceTerm<'a>,
34}
35
36/// To build constraints, we visit one item (type, trait) at a time
37/// and look at its contents. So e.g., if we have
38/// ```ignore (illustrative)
39/// struct Foo<T> {
40///     b: Bar<T>
41/// }
42/// ```
43/// then while we are visiting `Bar<T>`, the `CurrentItem` would have
44/// the `DefId` and the start of `Foo`'s inferreds.
45struct CurrentItem {
46    inferred_start: InferredIndex,
47}
48
49pub(crate) fn add_constraints_from_crate<'a, 'tcx>(
50    terms_cx: TermsContext<'a, 'tcx>,
51) -> ConstraintContext<'a, 'tcx> {
52    let tcx = terms_cx.tcx;
53    let covariant = terms_cx.arena.alloc(ConstantTerm(ty::Covariant));
54    let contravariant = terms_cx.arena.alloc(ConstantTerm(ty::Contravariant));
55    let invariant = terms_cx.arena.alloc(ConstantTerm(ty::Invariant));
56    let bivariant = terms_cx.arena.alloc(ConstantTerm(ty::Bivariant));
57    let mut constraint_cx = ConstraintContext {
58        terms_cx,
59        covariant,
60        contravariant,
61        invariant,
62        bivariant,
63        constraints: Vec::new(),
64    };
65
66    let crate_items = tcx.hir_crate_items(());
67
68    for def_id in crate_items.definitions() {
69        let def_kind = tcx.def_kind(def_id);
70        match def_kind {
71            DefKind::Struct | DefKind::Union | DefKind::Enum => {
72                constraint_cx.build_constraints_for_item(def_id);
73
74                let adt = tcx.adt_def(def_id);
75                for variant in adt.variants() {
76                    if let Some(ctor_def_id) = variant.ctor_def_id() {
77                        constraint_cx.build_constraints_for_item(ctor_def_id.expect_local());
78                    }
79                }
80            }
81            DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id),
82            DefKind::TyAlias if tcx.type_alias_is_lazy(def_id) => {
83                constraint_cx.build_constraints_for_item(def_id)
84            }
85            _ => {}
86        }
87    }
88
89    constraint_cx
90}
91
92impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
93    fn tcx(&self) -> TyCtxt<'tcx> {
94        self.terms_cx.tcx
95    }
96
97    fn build_constraints_for_item(&mut self, def_id: LocalDefId) {
98        let tcx = self.tcx();
99        debug!("build_constraints_for_item({})", tcx.def_path_str(def_id));
100
101        // Skip items with no generics - there's nothing to infer in them.
102        if tcx.generics_of(def_id).is_empty() {
103            return;
104        }
105
106        let inferred_start = self.terms_cx.inferred_starts[&def_id];
107        let current_item = &CurrentItem { inferred_start };
108        let ty = tcx.type_of(def_id).instantiate_identity();
109
110        // The type as returned by `type_of` is the underlying type and generally not a free alias.
111        // Therefore we need to check the `DefKind` first.
112        if let DefKind::TyAlias = tcx.def_kind(def_id)
113            && tcx.type_alias_is_lazy(def_id)
114        {
115            self.add_constraints_from_ty(current_item, ty, self.covariant);
116            return;
117        }
118
119        match ty.kind() {
120            ty::Adt(def, _) => {
121                // Not entirely obvious: constraints on structs/enums do not
122                // affect the variance of their type parameters. See discussion
123                // in comment at top of module.
124                //
125                // self.add_constraints_from_generics(generics);
126
127                for field in def.all_fields() {
128                    self.add_constraints_from_ty(
129                        current_item,
130                        tcx.type_of(field.did).instantiate_identity(),
131                        self.covariant,
132                    );
133                }
134            }
135
136            ty::FnDef(..) => {
137                self.add_constraints_from_sig(
138                    current_item,
139                    tcx.fn_sig(def_id).instantiate_identity(),
140                    self.covariant,
141                );
142            }
143
144            ty::Error(_) => {}
145
146            _ => {
147                span_bug!(
148                    tcx.def_span(def_id),
149                    "`build_constraints_for_item` unsupported for this item"
150                );
151            }
152        }
153    }
154
155    fn add_constraint(&mut self, current: &CurrentItem, index: u32, variance: VarianceTermPtr<'a>) {
156        debug!("add_constraint(index={}, variance={:?})", index, variance);
157        self.constraints.push(Constraint {
158            inferred: InferredIndex(current.inferred_start.0 + index as usize),
159            variance,
160        });
161    }
162
163    fn contravariant(&mut self, variance: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
164        self.xform(variance, self.contravariant)
165    }
166
167    fn invariant(&mut self, variance: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
168        self.xform(variance, self.invariant)
169    }
170
171    fn constant_term(&self, v: ty::Variance) -> VarianceTermPtr<'a> {
172        match v {
173            ty::Covariant => self.covariant,
174            ty::Invariant => self.invariant,
175            ty::Contravariant => self.contravariant,
176            ty::Bivariant => self.bivariant,
177        }
178    }
179
180    fn xform(&mut self, v1: VarianceTermPtr<'a>, v2: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
181        match (*v1, *v2) {
182            (_, ConstantTerm(ty::Covariant)) => {
183                // Applying a "covariant" transform is always a no-op
184                v1
185            }
186
187            (ConstantTerm(c1), ConstantTerm(c2)) => self.constant_term(c1.xform(c2)),
188
189            _ => &*self.terms_cx.arena.alloc(TransformTerm(v1, v2)),
190        }
191    }
192
193    #[instrument(level = "debug", skip(self, current))]
194    fn add_constraints_from_invariant_args(
195        &mut self,
196        current: &CurrentItem,
197        args: GenericArgsRef<'tcx>,
198        variance: VarianceTermPtr<'a>,
199    ) {
200        // Trait are always invariant so we can take advantage of that.
201        let variance_i = self.invariant(variance);
202
203        for k in args {
204            match k.unpack() {
205                GenericArgKind::Lifetime(lt) => {
206                    self.add_constraints_from_region(current, lt, variance_i)
207                }
208                GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i),
209                GenericArgKind::Const(val) => {
210                    self.add_constraints_from_const(current, val, variance_i)
211                }
212            }
213        }
214    }
215
216    /// Adds constraints appropriate for an instance of `ty` appearing
217    /// in a context with the generics defined in `generics` and
218    /// ambient variance `variance`
219    fn add_constraints_from_ty(
220        &mut self,
221        current: &CurrentItem,
222        ty: Ty<'tcx>,
223        variance: VarianceTermPtr<'a>,
224    ) {
225        debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance);
226
227        match *ty.kind() {
228            ty::Bool
229            | ty::Char
230            | ty::Int(_)
231            | ty::Uint(_)
232            | ty::Float(_)
233            | ty::Str
234            | ty::Never
235            | ty::Foreign(..) => {
236                // leaf type -- noop
237            }
238
239            ty::FnDef(..) | ty::Coroutine(..) | ty::Closure(..) | ty::CoroutineClosure(..) => {
240                bug!("Unexpected unnameable type in variance computation: {ty}");
241            }
242
243            ty::Ref(region, ty, mutbl) => {
244                self.add_constraints_from_region(current, region, variance);
245                self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance);
246            }
247
248            ty::Array(typ, len) => {
249                self.add_constraints_from_const(current, len, variance);
250                self.add_constraints_from_ty(current, typ, variance);
251            }
252
253            ty::Pat(typ, pat) => {
254                self.add_constraints_from_pat(current, variance, pat);
255                self.add_constraints_from_ty(current, typ, variance);
256            }
257
258            ty::Slice(typ) => {
259                self.add_constraints_from_ty(current, typ, variance);
260            }
261
262            ty::RawPtr(ty, mutbl) => {
263                self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance);
264            }
265
266            ty::Tuple(subtys) => {
267                for subty in subtys {
268                    self.add_constraints_from_ty(current, subty, variance);
269                }
270            }
271
272            ty::Adt(def, args) => {
273                self.add_constraints_from_args(current, def.did(), args, variance);
274            }
275
276            ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, ref data) => {
277                self.add_constraints_from_invariant_args(current, data.args, variance);
278            }
279
280            ty::Alias(ty::Free, ref data) => {
281                self.add_constraints_from_args(current, data.def_id, data.args, variance);
282            }
283
284            ty::Dynamic(data, r, _) => {
285                // The type `dyn Trait<T> +'a` is covariant w/r/t `'a`:
286                self.add_constraints_from_region(current, r, variance);
287
288                if let Some(poly_trait_ref) = data.principal() {
289                    self.add_constraints_from_invariant_args(
290                        current,
291                        poly_trait_ref.skip_binder().args,
292                        variance,
293                    );
294                }
295
296                for projection in data.projection_bounds() {
297                    match projection.skip_binder().term.unpack() {
298                        ty::TermKind::Ty(ty) => {
299                            self.add_constraints_from_ty(current, ty, self.invariant);
300                        }
301                        ty::TermKind::Const(c) => {
302                            self.add_constraints_from_const(current, c, self.invariant)
303                        }
304                    }
305                }
306            }
307
308            ty::Param(ref data) => {
309                self.add_constraint(current, data.index, variance);
310            }
311
312            ty::FnPtr(sig_tys, hdr) => {
313                self.add_constraints_from_sig(current, sig_tys.with(hdr), variance);
314            }
315
316            ty::UnsafeBinder(ty) => {
317                // FIXME(unsafe_binders): This is covariant, right?
318                self.add_constraints_from_ty(current, ty.skip_binder(), variance);
319            }
320
321            ty::Error(_) => {
322                // we encounter this when walking the trait references for object
323                // types, where we use Error as the Self type
324            }
325
326            ty::Placeholder(..) | ty::CoroutineWitness(..) | ty::Bound(..) | ty::Infer(..) => {
327                bug!("unexpected type encountered in variance inference: {}", ty);
328            }
329        }
330    }
331
332    fn add_constraints_from_pat(
333        &mut self,
334        current: &CurrentItem,
335        variance: VarianceTermPtr<'a>,
336        pat: ty::Pattern<'tcx>,
337    ) {
338        match *pat {
339            ty::PatternKind::Range { start, end } => {
340                self.add_constraints_from_const(current, start, variance);
341                self.add_constraints_from_const(current, end, variance);
342            }
343            ty::PatternKind::Or(patterns) => {
344                for pat in patterns {
345                    self.add_constraints_from_pat(current, variance, pat)
346                }
347            }
348        }
349    }
350
351    /// Adds constraints appropriate for a nominal type (enum, struct,
352    /// object, etc) appearing in a context with ambient variance `variance`
353    fn add_constraints_from_args(
354        &mut self,
355        current: &CurrentItem,
356        def_id: DefId,
357        args: GenericArgsRef<'tcx>,
358        variance: VarianceTermPtr<'a>,
359    ) {
360        debug!(
361            "add_constraints_from_args(def_id={:?}, args={:?}, variance={:?})",
362            def_id, args, variance
363        );
364
365        // We don't record `inferred_starts` entries for empty generics.
366        if args.is_empty() {
367            return;
368        }
369
370        let (local, remote) = if let Some(def_id) = def_id.as_local() {
371            (Some(self.terms_cx.inferred_starts[&def_id]), None)
372        } else {
373            (None, Some(self.tcx().variances_of(def_id)))
374        };
375
376        for (i, k) in args.iter().enumerate() {
377            let variance_decl = if let Some(InferredIndex(start)) = local {
378                // Parameter on an item defined within current crate:
379                // variance not yet inferred, so return a symbolic
380                // variance.
381                self.terms_cx.inferred_terms[start + i]
382            } else {
383                // Parameter on an item defined within another crate:
384                // variance already inferred, just look it up.
385                self.constant_term(remote.as_ref().unwrap()[i])
386            };
387            let variance_i = self.xform(variance, variance_decl);
388            debug!(
389                "add_constraints_from_args: variance_decl={:?} variance_i={:?}",
390                variance_decl, variance_i
391            );
392            match k.unpack() {
393                GenericArgKind::Lifetime(lt) => {
394                    self.add_constraints_from_region(current, lt, variance_i)
395                }
396                GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i),
397                GenericArgKind::Const(val) => {
398                    self.add_constraints_from_const(current, val, variance)
399                }
400            }
401        }
402    }
403
404    /// Adds constraints appropriate for a const expression `val`
405    /// in a context with ambient variance `variance`
406    fn add_constraints_from_const(
407        &mut self,
408        current: &CurrentItem,
409        c: ty::Const<'tcx>,
410        variance: VarianceTermPtr<'a>,
411    ) {
412        debug!("add_constraints_from_const(c={:?}, variance={:?})", c, variance);
413
414        match &c.kind() {
415            ty::ConstKind::Unevaluated(uv) => {
416                self.add_constraints_from_invariant_args(current, uv.args, variance);
417            }
418            _ => {}
419        }
420    }
421
422    /// Adds constraints appropriate for a function with signature
423    /// `sig` appearing in a context with ambient variance `variance`
424    fn add_constraints_from_sig(
425        &mut self,
426        current: &CurrentItem,
427        sig: ty::PolyFnSig<'tcx>,
428        variance: VarianceTermPtr<'a>,
429    ) {
430        let contra = self.contravariant(variance);
431        for &input in sig.skip_binder().inputs() {
432            self.add_constraints_from_ty(current, input, contra);
433        }
434        self.add_constraints_from_ty(current, sig.skip_binder().output(), variance);
435    }
436
437    /// Adds constraints appropriate for a region appearing in a
438    /// context with ambient variance `variance`
439    fn add_constraints_from_region(
440        &mut self,
441        current: &CurrentItem,
442        region: ty::Region<'tcx>,
443        variance: VarianceTermPtr<'a>,
444    ) {
445        match region.kind() {
446            ty::ReEarlyParam(ref data) => {
447                self.add_constraint(current, data.index, variance);
448            }
449
450            ty::ReStatic => {}
451
452            ty::ReBound(..) => {
453                // Either a higher-ranked region inside of a type or a
454                // late-bound function parameter.
455                //
456                // We do not compute constraints for either of these.
457            }
458
459            ty::ReError(_) => {}
460
461            ty::ReLateParam(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => {
462                // We don't expect to see anything but 'static or bound
463                // regions when visiting member types or method types.
464                bug!(
465                    "unexpected region encountered in variance \
466                      inference: {:?}",
467                    region
468                );
469            }
470        }
471    }
472
473    /// Adds constraints appropriate for a mutability-type pair
474    /// appearing in a context with ambient variance `variance`
475    fn add_constraints_from_mt(
476        &mut self,
477        current: &CurrentItem,
478        mt: &ty::TypeAndMut<'tcx>,
479        variance: VarianceTermPtr<'a>,
480    ) {
481        match mt.mutbl {
482            hir::Mutability::Mut => {
483                let invar = self.invariant(variance);
484                self.add_constraints_from_ty(current, mt.ty, invar);
485            }
486
487            hir::Mutability::Not => {
488                self.add_constraints_from_ty(current, mt.ty, variance);
489            }
490        }
491    }
492}