]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/variance/constraints.rs
Auto merge of #86155 - alexcrichton:abort-on-unwind, r=nikomatsakis
[rust.git] / compiler / rustc_typeck / 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::itemlikevisit::ItemLikeVisitor;
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 ///
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     tcx.hir().krate().visit_all_item_likes(&mut constraint_cx);
66
67     constraint_cx
68 }
69
70 impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
71     fn visit_item(&mut self, item: &hir::Item<'_>) {
72         match item.kind {
73             hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
74                 self.visit_node_helper(item.hir_id());
75
76                 if let hir::VariantData::Tuple(..) = *struct_def {
77                     self.visit_node_helper(struct_def.ctor_hir_id().unwrap());
78                 }
79             }
80
81             hir::ItemKind::Enum(ref enum_def, _) => {
82                 self.visit_node_helper(item.hir_id());
83
84                 for variant in enum_def.variants {
85                     if let hir::VariantData::Tuple(..) = variant.data {
86                         self.visit_node_helper(variant.data.ctor_hir_id().unwrap());
87                     }
88                 }
89             }
90
91             hir::ItemKind::Fn(..) => {
92                 self.visit_node_helper(item.hir_id());
93             }
94
95             _ => {}
96         }
97     }
98
99     fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
100         if let hir::TraitItemKind::Fn(..) = trait_item.kind {
101             self.visit_node_helper(trait_item.hir_id());
102         }
103     }
104
105     fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
106         if let hir::ImplItemKind::Fn(..) = impl_item.kind {
107             self.visit_node_helper(impl_item.hir_id());
108         }
109     }
110
111     fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) {
112         if let hir::ForeignItemKind::Fn(..) = foreign_item.kind {
113             self.visit_node_helper(foreign_item.hir_id());
114         }
115     }
116 }
117
118 impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
119     fn visit_node_helper(&mut self, id: hir::HirId) {
120         let tcx = self.terms_cx.tcx;
121         let def_id = tcx.hir().local_def_id(id);
122         self.build_constraints_for_item(def_id);
123     }
124
125     fn tcx(&self) -> TyCtxt<'tcx> {
126         self.terms_cx.tcx
127     }
128
129     fn build_constraints_for_item(&mut self, def_id: LocalDefId) {
130         let tcx = self.tcx();
131         debug!("build_constraints_for_item({})", tcx.def_path_str(def_id.to_def_id()));
132
133         // Skip items with no generics - there's nothing to infer in them.
134         if tcx.generics_of(def_id).count() == 0 {
135             return;
136         }
137
138         let id = tcx.hir().local_def_id_to_hir_id(def_id);
139         let inferred_start = self.terms_cx.inferred_starts[&id];
140         let current_item = &CurrentItem { inferred_start };
141         match tcx.type_of(def_id).kind() {
142             ty::Adt(def, _) => {
143                 // Not entirely obvious: constraints on structs/enums do not
144                 // affect the variance of their type parameters. See discussion
145                 // in comment at top of module.
146                 //
147                 // self.add_constraints_from_generics(generics);
148
149                 for field in def.all_fields() {
150                     self.add_constraints_from_ty(
151                         current_item,
152                         tcx.type_of(field.did),
153                         self.covariant,
154                     );
155                 }
156             }
157
158             ty::FnDef(..) => {
159                 self.add_constraints_from_sig(current_item, tcx.fn_sig(def_id), self.covariant);
160             }
161
162             ty::Error(_) => {}
163             _ => {
164                 span_bug!(
165                     tcx.def_span(def_id),
166                     "`build_constraints_for_item` unsupported for this item"
167                 );
168             }
169         }
170     }
171
172     fn add_constraint(&mut self, current: &CurrentItem, index: u32, variance: VarianceTermPtr<'a>) {
173         debug!("add_constraint(index={}, variance={:?})", index, variance);
174         self.constraints.push(Constraint {
175             inferred: InferredIndex(current.inferred_start.0 + index as usize),
176             variance,
177         });
178     }
179
180     fn contravariant(&mut self, variance: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
181         self.xform(variance, self.contravariant)
182     }
183
184     fn invariant(&mut self, variance: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
185         self.xform(variance, self.invariant)
186     }
187
188     fn constant_term(&self, v: ty::Variance) -> VarianceTermPtr<'a> {
189         match v {
190             ty::Covariant => self.covariant,
191             ty::Invariant => self.invariant,
192             ty::Contravariant => self.contravariant,
193             ty::Bivariant => self.bivariant,
194         }
195     }
196
197     fn xform(&mut self, v1: VarianceTermPtr<'a>, v2: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
198         match (*v1, *v2) {
199             (_, ConstantTerm(ty::Covariant)) => {
200                 // Applying a "covariant" transform is always a no-op
201                 v1
202             }
203
204             (ConstantTerm(c1), ConstantTerm(c2)) => self.constant_term(c1.xform(c2)),
205
206             _ => &*self.terms_cx.arena.alloc(TransformTerm(v1, v2)),
207         }
208     }
209
210     #[instrument(level = "debug", skip(self, current))]
211     fn add_constraints_from_invariant_substs(
212         &mut self,
213         current: &CurrentItem,
214         substs: SubstsRef<'tcx>,
215         variance: VarianceTermPtr<'a>,
216     ) {
217         // Trait are always invariant so we can take advantage of that.
218         let variance_i = self.invariant(variance);
219
220         for k in substs {
221             match k.unpack() {
222                 GenericArgKind::Lifetime(lt) => {
223                     self.add_constraints_from_region(current, lt, variance_i)
224                 }
225                 GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i),
226                 GenericArgKind::Const(_) => {
227                     // Consts impose no constraints.
228                 }
229             }
230         }
231     }
232
233     /// Adds constraints appropriate for an instance of `ty` appearing
234     /// in a context with the generics defined in `generics` and
235     /// ambient variance `variance`
236     fn add_constraints_from_ty(
237         &mut self,
238         current: &CurrentItem,
239         ty: Ty<'tcx>,
240         variance: VarianceTermPtr<'a>,
241     ) {
242         debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance);
243
244         match *ty.kind() {
245             ty::Bool
246             | ty::Char
247             | ty::Int(_)
248             | ty::Uint(_)
249             | ty::Float(_)
250             | ty::Str
251             | ty::Never
252             | ty::Foreign(..) => {
253                 // leaf type -- noop
254             }
255
256             ty::FnDef(..) | ty::Generator(..) | ty::Closure(..) => {
257                 bug!("Unexpected closure type in variance computation");
258             }
259
260             ty::Ref(region, ty, mutbl) => {
261                 let contra = self.contravariant(variance);
262                 self.add_constraints_from_region(current, region, contra);
263                 self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance);
264             }
265
266             ty::Array(typ, _) => {
267                 self.add_constraints_from_ty(current, typ, variance);
268             }
269
270             ty::Slice(typ) => {
271                 self.add_constraints_from_ty(current, typ, variance);
272             }
273
274             ty::RawPtr(ref mt) => {
275                 self.add_constraints_from_mt(current, mt, variance);
276             }
277
278             ty::Tuple(subtys) => {
279                 for subty in subtys {
280                     self.add_constraints_from_ty(current, subty.expect_ty(), variance);
281                 }
282             }
283
284             ty::Adt(def, substs) => {
285                 self.add_constraints_from_substs(current, def.did, substs, variance);
286             }
287
288             ty::Projection(ref data) => {
289                 self.add_constraints_from_invariant_substs(current, data.substs, variance);
290             }
291
292             ty::Opaque(_, substs) => {
293                 self.add_constraints_from_invariant_substs(current, substs, variance);
294             }
295
296             ty::Dynamic(ref data, r) => {
297                 // The type `Foo<T+'a>` is contravariant w/r/t `'a`:
298                 let contra = self.contravariant(variance);
299                 self.add_constraints_from_region(current, r, contra);
300
301                 if let Some(poly_trait_ref) = data.principal() {
302                     self.add_constraints_from_invariant_substs(
303                         current,
304                         poly_trait_ref.skip_binder().substs,
305                         variance,
306                     );
307                 }
308
309                 for projection in data.projection_bounds() {
310                     self.add_constraints_from_ty(
311                         current,
312                         projection.skip_binder().ty,
313                         self.invariant,
314                     );
315                 }
316             }
317
318             ty::Param(ref data) => {
319                 self.add_constraint(current, data.index, variance);
320             }
321
322             ty::FnPtr(sig) => {
323                 self.add_constraints_from_sig(current, sig, variance);
324             }
325
326             ty::Error(_) => {
327                 // we encounter this when walking the trait references for object
328                 // types, where we use Error as the Self type
329             }
330
331             ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Bound(..) | ty::Infer(..) => {
332                 bug!(
333                     "unexpected type encountered in \
334                       variance inference: {}",
335                     ty
336                 );
337             }
338         }
339     }
340
341     /// Adds constraints appropriate for a nominal type (enum, struct,
342     /// object, etc) appearing in a context with ambient variance `variance`
343     fn add_constraints_from_substs(
344         &mut self,
345         current: &CurrentItem,
346         def_id: DefId,
347         substs: SubstsRef<'tcx>,
348         variance: VarianceTermPtr<'a>,
349     ) {
350         debug!(
351             "add_constraints_from_substs(def_id={:?}, substs={:?}, variance={:?})",
352             def_id, substs, variance
353         );
354
355         // We don't record `inferred_starts` entries for empty generics.
356         if substs.is_empty() {
357             return;
358         }
359
360         let (local, remote) = if let Some(def_id) = def_id.as_local() {
361             let id = self.tcx().hir().local_def_id_to_hir_id(def_id);
362             (Some(self.terms_cx.inferred_starts[&id]), None)
363         } else {
364             (None, Some(self.tcx().variances_of(def_id)))
365         };
366
367         for (i, k) in substs.iter().enumerate() {
368             let variance_decl = if let Some(InferredIndex(start)) = local {
369                 // Parameter on an item defined within current crate:
370                 // variance not yet inferred, so return a symbolic
371                 // variance.
372                 self.terms_cx.inferred_terms[start + i]
373             } else {
374                 // Parameter on an item defined within another crate:
375                 // variance already inferred, just look it up.
376                 self.constant_term(remote.as_ref().unwrap()[i])
377             };
378             let variance_i = self.xform(variance, variance_decl);
379             debug!(
380                 "add_constraints_from_substs: variance_decl={:?} variance_i={:?}",
381                 variance_decl, variance_i
382             );
383             match k.unpack() {
384                 GenericArgKind::Lifetime(lt) => {
385                     self.add_constraints_from_region(current, lt, variance_i)
386                 }
387                 GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i),
388                 GenericArgKind::Const(_) => {
389                     // Consts impose no constraints.
390                 }
391             }
392         }
393     }
394
395     /// Adds constraints appropriate for a function with signature
396     /// `sig` appearing in a context with ambient variance `variance`
397     fn add_constraints_from_sig(
398         &mut self,
399         current: &CurrentItem,
400         sig: ty::PolyFnSig<'tcx>,
401         variance: VarianceTermPtr<'a>,
402     ) {
403         let contra = self.contravariant(variance);
404         for &input in sig.skip_binder().inputs() {
405             self.add_constraints_from_ty(current, input, contra);
406         }
407         self.add_constraints_from_ty(current, sig.skip_binder().output(), variance);
408     }
409
410     /// Adds constraints appropriate for a region appearing in a
411     /// context with ambient variance `variance`
412     fn add_constraints_from_region(
413         &mut self,
414         current: &CurrentItem,
415         region: ty::Region<'tcx>,
416         variance: VarianceTermPtr<'a>,
417     ) {
418         match *region {
419             ty::ReEarlyBound(ref data) => {
420                 self.add_constraint(current, data.index, variance);
421             }
422
423             ty::ReStatic => {}
424
425             ty::ReLateBound(..) => {
426                 // Late-bound regions do not get substituted the same
427                 // way early-bound regions do, so we skip them here.
428             }
429
430             ty::ReFree(..)
431             | ty::ReVar(..)
432             | ty::RePlaceholder(..)
433             | ty::ReEmpty(_)
434             | ty::ReErased => {
435                 // We don't expect to see anything but 'static or bound
436                 // regions when visiting member types or method types.
437                 bug!(
438                     "unexpected region encountered in variance \
439                       inference: {:?}",
440                     region
441                 );
442             }
443         }
444     }
445
446     /// Adds constraints appropriate for a mutability-type pair
447     /// appearing in a context with ambient variance `variance`
448     fn add_constraints_from_mt(
449         &mut self,
450         current: &CurrentItem,
451         mt: &ty::TypeAndMut<'tcx>,
452         variance: VarianceTermPtr<'a>,
453     ) {
454         match mt.mutbl {
455             hir::Mutability::Mut => {
456                 let invar = self.invariant(variance);
457                 self.add_constraints_from_ty(current, mt.ty, invar);
458             }
459
460             hir::Mutability::Not => {
461                 self.add_constraints_from_ty(current, mt.ty, variance);
462             }
463         }
464     }
465 }