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