11# Member constraints
22
33A member constraint ` 'm member of ['c_1..'c_N] ` expresses that the
4- region ` 'm ` must be * equal* to some ** choice regions** ` 'c_i ` (for
5- some ` i ` ). These constraints cannot be expressed by users, but they
6- arise from ` impl Trait ` due to its lifetime capture rules. Consider a
7- function such as the following:
4+ region ` 'm ` must be * equal* to some ** choice regions** ` 'c_i ` (for some ` i ` ).
5+ These constraints cannot be expressed by users, but they
6+ arise from ` impl Trait ` due to its lifetime capture rules.
7+ Consider a function such as the following:
88
99``` rust,ignore
1010fn make(a: &'a u32, b: &'b u32) -> impl Trait<'a, 'b> { .. }
1111```
1212
1313Here, the true return type (often called the "hidden type") is only
14- permitted to capture the lifetimes ` 'a ` or ` 'b ` . You can kind of see
14+ permitted to capture the lifetimes ` 'a ` or ` 'b ` .
15+ You can kind of see
1516this more clearly by desugaring that ` impl Trait ` return type into its
1617more explicit form:
1718
@@ -23,16 +24,17 @@ fn make(a: &'a u32, b: &'b u32) -> MakeReturn<'a, 'b> { .. }
2324Here, the idea is that the hidden type must be some type that could
2425have been written in place of the ` impl Trait<'x, 'y> ` -- but clearly
2526such a type can only reference the regions ` 'x ` or ` 'y ` (or
26- ` 'static ` !), as those are the only names in scope. This limitation is
27+ ` 'static ` !), as those are the only names in scope.
28+ This limitation is
2729then translated into a restriction to only access ` 'a ` or ` 'b ` because
2830we are returning ` MakeReturn<'a, 'b> ` , where ` 'x ` and ` 'y ` have been
2931replaced with ` 'a ` and ` 'b ` respectively.
3032
3133## Detailed example
3234
3335To help us explain member constraints in more detail, let's spell out
34- the ` make ` example in a bit more detail. First off, let's assume that
35- you have some dummy trait:
36+ the ` make ` example in a bit more detail.
37+ First off, let's assume that you have some dummy trait:
3638
3739``` rust,ignore
3840trait Trait<'a, 'b> { }
@@ -49,8 +51,8 @@ fn make(a: &'a u32, b: &'b u32) -> MakeReturn<'a, 'b> {
4951```
5052
5153What happens in this case is that the return type will be ` (&'0 u32, &'1 u32) ` ,
52- where ` '0 ` and ` '1 ` are fresh region variables. We will have the following
53- region constraints:
54+ where ` '0 ` and ` '1 ` are fresh region variables.
55+ We will have the following region constraints:
5456
5557``` txt
5658'0 live at {L}
@@ -67,11 +69,11 @@ return tuple is constructed to where it is returned (in fact, `'0` and
6769` '1 ` might have slightly different liveness sets, but that's not very
6870interesting to the point we are illustrating here).
6971
70- The ` 'a: '0 ` and ` 'b: '1 ` constraints arise from subtyping. When we
71- construct the ` (a, b) ` value, it will be assigned type `(&'0 u32, &'1
72+ The ` 'a: '0 ` and ` 'b: '1 ` constraints arise from subtyping.
73+ When we construct the ` (a, b) ` value, it will be assigned type `(&'0 u32, &'1
7274u32)` -- the region variables reflect that the lifetimes of these
73- references could be made smaller. For this value to be created from
74- ` a ` and ` b ` , however, we do require that:
75+ references could be made smaller.
76+ For this value to be created from ` a ` and ` b ` , however, we do require that:
7577
7678``` txt
7779(&'a u32, &'b u32) <: (&'0 u32, &'1 u32)
@@ -82,35 +84,39 @@ which means in turn that `&'a u32 <: &'0 u32` and hence that `'a: '0`
8284
8385Note that if we ignore member constraints, the value of ` '0 ` would be
8486inferred to some subset of the function body (from the liveness
85- constraints, which we did not write explicitly). It would never become
87+ constraints, which we did not write explicitly).
88+ It would never become
8689` 'a ` , because there is no need for it too -- we have a constraint that
87- ` 'a: '0 ` , but that just puts a "cap" on how * large* ` '0 ` can grow to
88- become. Since we compute the * minimal* value that we can, we are happy
89- to leave ` '0 ` as being just equal to the liveness set. This is where
90- member constraints come in.
90+ ` 'a: '0 ` , but that just puts a "cap" on how * large* ` '0 ` can grow to become.
91+ Since we compute the * minimal* value that we can, we are happy
92+ to leave ` '0 ` as being just equal to the liveness set.
93+ This is where member constraints come in.
9194
9295## Choices are always lifetime parameters
9396
9497At present, the "choice" regions from a member constraint are always lifetime
95- parameters from the current function. As of <!-- date-check --> October 2021 ,
98+ parameters from the current function. As of <!-- date-check --> March 2026 ,
9699this falls out from the placement of impl Trait, though in the future it may not
97- be the case. We take some advantage of this fact, as it simplifies the current
98- code. In particular, we don't have to consider a case like `'0 member of [ '1,
100+ be the case.
101+ We take some advantage of this fact, as it simplifies the current code.
102+ In particular, we don't have to consider a case like `'0 member of [ '1,
99103'static] ` , in which the value of both ` '0` and ` '1` are being inferred and hence
100- changing. See [ rust-lang/rust #61773 ] [ #61773 ] for more information.
104+ changing.
105+ See [ rust-lang/rust #61773 ] [ #61773 ] for more information.
101106
102107[ #61773 ] : https://github.com/rust-lang/rust/issues/61773
103108
104109## Applying member constraints
105110
106- Member constraints are a bit more complex than other forms of
107- constraints. This is because they have a "or" quality to them -- that
111+ Member constraints are a bit more complex than other forms of constraints.
112+ This is because they have a "or" quality to them -- that
108113is, they describe multiple choices that we must select from. E.g., in
109114our example constraint ` '0 member of ['a, 'b, 'static] ` , it might be
110- that ` '0 ` is equal to ` 'a ` , ` 'b ` , * or* ` 'static ` . How can we pick the
111- correct one? What we currently do is to look for a * minimal choice*
112- -- if we find one, then we will grow ` '0 ` to be equal to that minimal
113- choice. To find that minimal choice, we take two factors into
115+ that ` '0 ` is equal to ` 'a ` , ` 'b ` , * or* ` 'static ` .
116+ How can we pick the correct one?
117+ What we currently do is to look for a * minimal choice*
118+ -- if we find one, then we will grow ` '0 ` to be equal to that minimal choice.
119+ To find that minimal choice, we take two factors into
114120consideration: lower and upper bounds.
115121
116122### Lower bounds
@@ -121,30 +127,34 @@ apply member constraints, we've already *computed* the lower bounds of
121127` '0 ` because we computed its minimal value (or at least, the lower
122128bounds considering everything but member constraints).
123129
124- Let ` LB ` be the current value of ` '0 ` . We know then that ` '0: LB ` must
125- hold, whatever the final value of ` '0 ` is. Therefore, we can rule out
130+ Let ` LB ` be the current value of ` '0 ` .
131+ We know then that ` '0: LB ` must hold, whatever the final value of ` '0 ` is.
132+ Therefore, we can rule out
126133any choice ` 'choice ` where ` 'choice: LB ` does not hold.
127134
128- Unfortunately, in our example, this is not very helpful. The lower
129- bound for ` '0 ` will just be the liveness set ` {L} ` , and we know that
130- all the lifetime parameters outlive that set. So we are left with the
131- same set of choices here. (But in other examples, particularly those
135+ Unfortunately, in our example, this is not very helpful.
136+ The lower bound for ` '0 ` will just be the liveness set ` {L} ` , and we know that
137+ all the lifetime parameters outlive that set.
138+ So we are left with the same set of choices here.
139+ (But in other examples, particularly those
132140with different variance, lower bound constraints may be relevant.)
133141
134142### Upper bounds
135143
136144The * upper bounds* are those lifetimes that * must outlive* ` '0 ` --
137145i.e., that ` '0 ` must be * smaller* than. In our example, this would be
138- ` 'a ` , because we have the constraint that ` 'a: '0 ` . In more complex
139- examples, the chain may be more indirect.
146+ ` 'a ` , because we have the constraint that ` 'a: '0 ` .
147+ In more complex examples, the chain may be more indirect.
140148
141149We can use upper bounds to rule out members in a very similar way to
142- lower bounds. If UB is some upper bound, then we know that `UB:
150+ lower bounds.
151+ If UB is some upper bound, then we know that `UB:
143152'0` must hold, so we can rule out any choice ` 'choice` where ` UB:
144153'choice` does not hold.
145154
146155In our example, we would be able to reduce our choice set from `[ 'a,
147- 'b, 'static] ` to just ` [ 'a] ` . This is because ` '0` has an upper bound
156+ 'b, 'static] ` to just ` [ 'a] `.
157+ This is because ` '0 ` has an upper bound
148158of ` 'a ` , and neither ` 'a: 'b ` nor ` 'a: 'static ` is known to hold.
149159
150160(For notes on how we collect upper bounds in the implementation, see
@@ -153,39 +163,45 @@ of `'a`, and neither `'a: 'b` nor `'a: 'static` is known to hold.
153163### Minimal choice
154164
155165After applying lower and upper bounds, we can still sometimes have
156- multiple possibilities. For example, imagine a variant of our example
157- using types with the opposite variance. In that case, we would have
158- the constraint ` '0: 'a ` instead of ` 'a: '0 ` . Hence the current value
159- of ` '0 ` would be ` {L, 'a} ` . Using this as a lower bound, we would be
166+ multiple possibilities.
167+ For example, imagine a variant of our example
168+ using types with the opposite variance.
169+ In that case, we would have the constraint ` '0: 'a ` instead of ` 'a: '0 ` .
170+ Hence the current value of ` '0 ` would be ` {L, 'a} ` .
171+ Using this as a lower bound, we would be
160172able to narrow down the member choices to ` ['a, 'static] ` because `'b:
161- 'a` is not known to hold (but ` 'a: 'a` and ` 'static: 'a` do hold). We
162- would not have any upper bounds, so that would be our final set of choices.
173+ 'a` is not known to hold (but ` 'a: 'a` and ` 'static: 'a` do hold).
174+ We would not have any upper bounds, so that would be our final set of choices.
163175
164176In that case, we apply the ** minimal choice** rule -- basically, if
165- one of our choices if smaller than the others, we can use that. In
166- this case, we would opt for ` 'a ` (and not ` 'static ` ).
177+ one of our choices if smaller than the others, we can use that.
178+ In this case, we would opt for ` 'a ` (and not ` 'static ` ).
167179
168180This choice is consistent with the general 'flow' of region
169181propagation, which always aims to compute a minimal value for the
170- region being inferred. However, it is somewhat arbitrary.
182+ region being inferred.
183+ However, it is somewhat arbitrary.
171184
172185<a id =" collecting " ></a >
173186
174187### Collecting upper bounds in the implementation
175188
176189In practice, computing upper bounds is a bit inconvenient, because our
177- data structures are setup for the opposite. What we do is to compute
190+ data structures are setup for the opposite.
191+ What we do is to compute
178192the ** reverse SCC graph** (we do this lazily and cache the result) --
179- that is, a graph where ` 'a: 'b ` induces an edge `SCC('b) ->
180- SCC('a)`. Like the normal SCC graph, this is a DAG. We can then do a
181- depth-first search starting from ` SCC('0) ` in this graph. This will
182- take us to all the SCCs that must outlive ` '0 ` .
193+ that is, a graph where ` 'a: 'b ` induces an edge ` SCC('b) -> SCC('a) ` .
194+ Like the normal SCC graph, this is a DAG.
195+ We can then do a depth-first search starting from ` SCC('0) ` in this graph.
196+ This will take us to all the SCCs that must outlive ` '0 ` .
183197
184198One wrinkle is that, as we walk the "upper bound" SCCs, their values
185- will not yet have been fully computed. However, we ** have** already
199+ will not yet have been fully computed.
200+ However, we ** have** already
186201applied their liveness constraints, so we have some information about
187- their value. In particular, for any regions representing lifetime
202+ their value.
203+ In particular, for any regions representing lifetime
188204parameters, their value will contain themselves (i.e., the initial
189- value for ` 'a ` includes ` 'a ` and the value for ` 'b ` contains ` 'b ` ). So
190- we can collect all of the lifetime parameters that are reachable,
205+ value for ` 'a ` includes ` 'a ` and the value for ` 'b ` contains ` 'b ` ).
206+ So we can collect all of the lifetime parameters that are reachable,
191207which is precisely what we are interested in.
0 commit comments