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}