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