]> git.proxmox.com Git - rustc.git/blame - src/doc/rustc-dev-guide/src/borrow_check/region_inference/member_constraints.md
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / src / doc / rustc-dev-guide / src / borrow_check / region_inference / member_constraints.md
CommitLineData
dc9dc135
XL
1# Member constraints
2
6a06907d
XL
3<!-- toc -->
4
dc9dc135
XL
5A member constraint `'m member of ['c_1..'c_N]` expresses that the
6region `'m` must be *equal* to some **choice regions** `'c_i` (for
7some `i`). These constraints cannot be expressed by users, but they
8arise from `impl Trait` due to its lifetime capture rules. Consider a
9function such as the following:
10
416331ca 11```rust,ignore
dc9dc135
XL
12fn make(a: &'a u32, b: &'b u32) -> impl Trait<'a, 'b> { .. }
13```
14
15Here, the true return type (often called the "hidden type") is only
16permitted to capture the lifetimes `'a` or `'b`. You can kind of see
17this more clearly by desugaring that `impl Trait` return type into its
18more explicit form:
19
416331ca 20```rust,ignore
dc9dc135
XL
21type MakeReturn<'x, 'y> = impl Trait<'x, 'y>;
22fn make(a: &'a u32, b: &'b u32) -> MakeReturn<'a, 'b> { .. }
23```
24
25Here, the idea is that the hidden type must be some type that could
26have been written in place of the `impl Trait<'x, 'y>` -- but clearly
27such a type can only reference the regions `'x` or `'y` (or
28`'static`!), as those are the only names in scope. This limitation is
29then translated into a restriction to only access `'a` or `'b` because
30we are returning `MakeReturn<'a, 'b>`, where `'x` and `'y` have been
31replaced with `'a` and `'b` respectively.
32
33## Detailed example
34
35To help us explain member constraints in more detail, let's spell out
36the `make` example in a bit more detail. First off, let's assume that
37you have some dummy trait:
38
416331ca 39```rust,ignore
dc9dc135
XL
40trait Trait<'a, 'b> { }
41impl<T> Trait<'_, '_> for T { }
42```
43
44and this is the `make` function (in desugared form):
45
416331ca 46```rust,ignore
dc9dc135
XL
47type MakeReturn<'x, 'y> = impl Trait<'x, 'y>;
48fn make(a: &'a u32, b: &'b u32) -> MakeReturn<'a, 'b> {
49 (a, b)
50}
51```
52
53What happens in this case is that the return type will be `(&'0 u32, &'1 u32)`,
54where `'0` and `'1` are fresh region variables. We will have the following
55region constraints:
56
416331ca 57```txt
dc9dc135
XL
58'0 live at {L}
59'1 live at {L}
60'a: '0
61'b: '1
62'0 member of ['a, 'b, 'static]
63'1 member of ['a, 'b, 'static]
64```
65
66Here the "liveness set" `{L}` corresponds to that subset of the body
67where `'0` and `'1` are live -- basically the point from where the
68return tuple is constructed to where it is returned (in fact, `'0` and
69`'1` might have slightly different liveness sets, but that's not very
70interesting to the point we are illustrating here).
71
72The `'a: '0` and `'b: '1` constraints arise from subtyping. When we
73construct the `(a, b)` value, it will be assigned type `(&'0 u32, &'1
74u32)` -- the region variables reflect that the lifetimes of these
75references could be made smaller. For this value to be created from
76`a` and `b`, however, we do require that:
77
416331ca 78```txt
dc9dc135
XL
79(&'a u32, &'b u32) <: (&'0 u32, &'1 u32)
80```
81
82which means in turn that `&'a u32 <: &'0 u32` and hence that `'a: '0`
83(and similarly that `&'b u32 <: &'1 u32`, `'b: '1`).
84
85Note that if we ignore member constraints, the value of `'0` would be
86inferred to some subset of the function body (from the liveness
87constraints, which we did not write explicitly). It would never become
88`'a`, because there is no need for it too -- we have a constraint that
89`'a: '0`, but that just puts a "cap" on how *large* `'0` can grow to
90become. Since we compute the *minimal* value that we can, we are happy
91to leave `'0` as being just equal to the liveness set. This is where
92member constraints come in.
93
94## Choices are always lifetime parameters
95
6a06907d
XL
96At present, the "choice" regions from a member constraint are always lifetime
97parameters from the current function. As of <!-- date: 2021-01 --> January 2021,
98this falls out from the placement of impl Trait, though in the future it may not
99be the case. We take some advantage of this fact, as it simplifies the current
100code. In particular, we don't have to consider a case like `'0 member of ['1,
101'static]`, in which the value of both `'0` and `'1` are being inferred and hence
102changing. See [rust-lang/rust#61773][#61773] for more information.
dc9dc135
XL
103
104[#61773]: https://github.com/rust-lang/rust/issues/61773
105
106## Applying member constraints
107
108Member constraints are a bit more complex than other forms of
109constraints. This is because they have a "or" quality to them -- that
110is, they describe multiple choices that we must select from. E.g., in
111our example constraint `'0 member of ['a, 'b, 'static]`, it might be
112that `'0` is equal to `'a`, `'b`, *or* `'static`. How can we pick the
113correct one? What we currently do is to look for a *minimal choice*
114-- if we find one, then we will grow `'0` to be equal to that minimal
115choice. To find that minimal choice, we take two factors into
116consideration: lower and upper bounds.
117
118### Lower bounds
119
120The *lower bounds* are those lifetimes that `'0` *must outlive* --
121i.e., that `'0` must be larger than. In fact, when it comes time to
122apply member constraints, we've already *computed* the lower bounds of
123`'0` because we computed its minimal value (or at least, the lower
124bounds considering everything but member constraints).
125
126Let `LB` be the current value of `'0`. We know then that `'0: LB` must
127hold, whatever the final value of `'0` is. Therefore, we can rule out
128any choice `'choice` where `'choice: LB` does not hold.
129
130Unfortunately, in our example, this is not very helpful. The lower
131bound for `'0` will just be the liveness set `{L}`, and we know that
132all the lifetime parameters outlive that set. So we are left with the
133same set of choices here. (But in other examples, particularly those
134with different variance, lower bound constraints may be relevant.)
135
136### Upper bounds
137
138The *upper bounds* are those lifetimes that *must outlive* `'0` --
139i.e., that `'0` must be *smaller* than. In our example, this would be
140`'a`, because we have the constraint that `'a: '0`. In more complex
141examples, the chain may be more indirect.
142
143We can use upper bounds to rule out members in a very similar way to
6a06907d 144lower bounds. If UB is some upper bound, then we know that `UB:
dc9dc135
XL
145'0` must hold, so we can rule out any choice `'choice` where `UB:
146'choice` does not hold.
147
148In our example, we would be able to reduce our choice set from `['a,
149'b, 'static]` to just `['a]`. This is because `'0` has an upper bound
150of `'a`, and neither `'a: 'b` nor `'a: 'static` is known to hold.
151
152(For notes on how we collect upper bounds in the implementation, see
153[the section below](#collecting).)
154
155### Minimal choice
156
157After applying lower and upper bounds, we can still sometimes have
158multiple possibilities. For example, imagine a variant of our example
159using types with the opposite variance. In that case, we would have
160the constraint `'0: 'a` instead of `'a: '0`. Hence the current value
161of `'0` would be `{L, 'a}`. Using this as a lower bound, we would be
162able to narrow down the member choices to `['a, 'static]` because `'b:
163'a` is not known to hold (but `'a: 'a` and `'static: 'a` do hold). We
164would not have any upper bounds, so that would be our final set of choices.
165
166In that case, we apply the **minimal choice** rule -- basically, if
167one of our choices if smaller than the others, we can use that. In
168this case, we would opt for `'a` (and not `'static`).
169
170This choice is consistent with the general 'flow' of region
171propagation, which always aims to compute a minimal value for the
172region being inferred. However, it is somewhat arbitrary.
173
174<a name="collecting"></a>
175
176### Collecting upper bounds in the implementation
177
178In practice, computing upper bounds is a bit inconvenient, because our
179data structures are setup for the opposite. What we do is to compute
416331ca 180the **reverse SCC graph** (we do this lazily and cache the result) --
dc9dc135
XL
181that is, a graph where `'a: 'b` induces an edge `SCC('b) ->
182SCC('a)`. Like the normal SCC graph, this is a DAG. We can then do a
183depth-first search starting from `SCC('0)` in this graph. This will
416331ca 184take us to all the SCCs that must outlive `'0`.
dc9dc135
XL
185
186One wrinkle is that, as we walk the "upper bound" SCCs, their values
187will not yet have been fully computed. However, we **have** already
188applied their liveness constraints, so we have some information about
189their value. In particular, for any regions representing lifetime
190parameters, their value will contain themselves (i.e., the initial
191value for `'a` includes `'a` and the value for `'b` contains `'b`). So
192we can collect all of the lifetime parameters that are reachable,
193which is precisely what we are interested in.