rustc_hir_analysis/outlives/utils.rs
1use rustc_data_structures::fx::FxIndexMap;
2use rustc_middle::ty::outlives::{Component, push_outlives_components};
3use rustc_middle::ty::{self, GenericArg, GenericArgKind, Region, Ty, TyCtxt};
4use rustc_middle::{bug, span_bug};
5use rustc_span::Span;
6use smallvec::smallvec;
7
8/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
9/// must be added to the struct header.
10pub(crate) type RequiredPredicates<'tcx> = FxIndexMap<ty::ArgOutlivesPredicate<'tcx>, Span>;
11
12/// Given a requirement `T: 'a` or `'b: 'a`, deduce the
13/// outlives_component and add it to `required_predicates`
14pub(crate) fn insert_outlives_predicate<'tcx>(
15 tcx: TyCtxt<'tcx>,
16 arg: GenericArg<'tcx>,
17 outlived_region: Region<'tcx>,
18 span: Span,
19 required_predicates: &mut RequiredPredicates<'tcx>,
20) {
21 // If the `'a` region is bound within the field type itself, we
22 // don't want to propagate this constraint to the header.
23 if !is_free_region(outlived_region) {
24 return;
25 }
26
27 match arg.kind() {
28 GenericArgKind::Type(ty) => {
29 // `T: 'outlived_region` for some type `T`
30 // But T could be a lot of things:
31 // e.g., if `T = &'b u32`, then `'b: 'outlived_region` is
32 // what we want to add.
33 //
34 // Or if within `struct Foo<U>` you had `T = Vec<U>`, then
35 // we would want to add `U: 'outlived_region`
36 let mut components = smallvec![];
37 push_outlives_components(tcx, ty, &mut components);
38 for component in components {
39 match component {
40 Component::Region(r) => {
41 // This would arise from something like:
42 //
43 // ```
44 // struct Foo<'a, 'b> {
45 // x: &'a &'b u32
46 // }
47 // ```
48 //
49 // Here `outlived_region = 'a` and `kind = &'b
50 // u32`. Decomposing `&'b u32` into
51 // components would yield `'b`, and we add the
52 // where clause that `'b: 'a`.
53 insert_outlives_predicate(
54 tcx,
55 r.into(),
56 outlived_region,
57 span,
58 required_predicates,
59 );
60 }
61
62 Component::Param(param_ty) => {
63 // param_ty: ty::ParamTy
64 // This would arise from something like:
65 //
66 // ```
67 // struct Foo<'a, U> {
68 // x: &'a Vec<U>
69 // }
70 // ```
71 //
72 // Here `outlived_region = 'a` and `kind =
73 // Vec<U>`. Decomposing `Vec<U>` into
74 // components would yield `U`, and we add the
75 // where clause that `U: 'a`.
76 let ty: Ty<'tcx> = param_ty.to_ty(tcx);
77 required_predicates
78 .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
79 .or_insert(span);
80 }
81
82 Component::Placeholder(_) => {
83 span_bug!(span, "Should not deduce placeholder outlives component");
84 }
85
86 Component::Alias(alias_ty) => {
87 // This would either arise from something like:
88 //
89 // ```
90 // struct Foo<'a, T: Iterator> {
91 // x: &'a <T as Iterator>::Item
92 // }
93 // ```
94 //
95 // or:
96 //
97 // ```rust
98 // type Opaque<T> = impl Sized;
99 // fn defining<T>() -> Opaque<T> {}
100 // struct Ss<'a, T>(&'a Opaque<T>);
101 // ```
102 //
103 // Here we want to add an explicit `where <T as Iterator>::Item: 'a`
104 // or `Opaque<T>: 'a` depending on the alias kind.
105 let ty = alias_ty.to_ty(tcx);
106 required_predicates
107 .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
108 .or_insert(span);
109 }
110
111 Component::EscapingAlias(_) => {
112 // As above, but the projection involves
113 // late-bound regions. Therefore, the WF
114 // requirement is not checked in type definition
115 // but at fn call site, so ignore it.
116 //
117 // ```
118 // struct Foo<'a, T: Iterator> {
119 // x: for<'b> fn(<&'b T as Iterator>::Item)
120 // // ^^^^^^^^^^^^^^^^^^^^^^^^^
121 // }
122 // ```
123 //
124 // Since `'b` is not in scope on `Foo`, can't
125 // do anything here, ignore it.
126 }
127
128 Component::UnresolvedInferenceVariable(_) => bug!("not using infcx"),
129 }
130 }
131 }
132
133 GenericArgKind::Lifetime(r) => {
134 if !is_free_region(r) {
135 return;
136 }
137 required_predicates.entry(ty::OutlivesPredicate(arg, outlived_region)).or_insert(span);
138 }
139
140 GenericArgKind::Const(_) => {
141 // Generic consts don't impose any constraints.
142 }
143 }
144}
145
146fn is_free_region(region: Region<'_>) -> bool {
147 // First, screen for regions that might appear in a type header.
148 match region.kind() {
149 // These correspond to `T: 'a` relationships:
150 //
151 // struct Foo<'a, T> {
152 // field: &'a T, // this would generate a ReEarlyParam referencing `'a`
153 // }
154 //
155 // We care about these, so fall through.
156 ty::ReEarlyParam(_) => true,
157
158 // These correspond to `T: 'static` relationships which can be
159 // rather surprising.
160 //
161 // struct Foo<'a, T> {
162 // field: &'static T, // this would generate a ReStatic
163 // }
164 ty::ReStatic => false,
165
166 // Late-bound regions can appear in `fn` types:
167 //
168 // struct Foo<T> {
169 // field: for<'b> fn(&'b T) // e.g., 'b here
170 // }
171 //
172 // The type above might generate a `T: 'b` bound, but we can
173 // ignore it. We can't name this lifetime pn the struct header anyway.
174 ty::ReBound(..) => false,
175
176 ty::ReError(_) => false,
177
178 // These regions don't appear in types from type declarations:
179 ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReLateParam(..) => {
180 bug!("unexpected region in outlives inference: {:?}", region);
181 }
182 }
183}