rustc_hir_analysis/outlives/
utils.rs

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