Skip to main content

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::SmallVec::new()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                        bug_impl(Some(span),
    format_args!("Should not deduce placeholder outlives component"),
    Location::caller());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_impl(None, format_args!("not using infcx"), Location::caller())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_impl(None,
    format_args!("unexpected region in outlives inference: {0:?}", region),
    Location::caller());bug!("unexpected region in outlives inference: {:?}", region);
180        }
181    }
182}