1
- /*
1
+ /*!
2
2
3
- Region resolution. This pass runs before typechecking and resolves region
4
- names to the appropriate block.
5
-
6
- This seems to be as good a place as any to explain in detail how
7
- region naming, representation, and type check works.
8
-
9
- ### Naming and so forth
10
-
11
- We really want regions to be very lightweight to use. Therefore,
12
- unlike other named things, the scopes for regions are not explicitly
13
- declared: instead, they are implicitly defined. Functions declare new
14
- scopes: if the function is not a bare function, then as always it
15
- inherits the names in scope from the outer scope. Within a function
16
- declaration, new names implicitly declare new region variables. Outside
17
- of function declarations, new names are illegal. To make this more
18
- concrete, here is an example:
19
-
20
- fn foo(s: &a.S, t: &b.T) {
21
- let s1: &a.S = s; // a refers to the same a as in the decl
22
- let t1: &c.T = t; // illegal: cannot introduce new name here
23
- }
24
-
25
- The code in this file is what actually handles resolving these names.
26
- It creates a couple of maps that map from the AST node representing a
27
- region ptr type to the resolved form of its region parameter. If new
28
- names are introduced where they shouldn't be, then an error is
29
- reported.
30
-
31
- If regions are not given an explicit name, then the behavior depends
32
- a bit on the context. Within a function declaration, all unnamed regions
33
- are mapped to a single, anonymous parameter. That is, a function like:
34
-
35
- fn foo(s: &S) -> &S { s }
36
-
37
- is equivalent to a declaration like:
38
-
39
- fn foo(s: &a.S) -> &a.S { s }
40
-
41
- Within a function body or other non-binding context, an unnamed region
42
- reference is mapped to a fresh region variable whose value can be
43
- inferred as normal.
44
-
45
- The resolved form of regions is `ty::region`. Before I can explain
46
- why this type is setup the way it is, I have to digress a little bit
47
- into some ill-explained type theory.
48
-
49
- ### Universal Quantification
50
-
51
- Regions are more complex than type parameters because, unlike type
52
- parameters, they can be universally quantified within a type. To put
53
- it another way, you cannot (at least at the time of this writing) have
54
- a variable `x` of type `fn<T>(T) -> T`. You can have an *item* of
55
- type `fn<T>(T) -> T`, but whenever it is referenced within a method,
56
- that type parameter `T` is replaced with a concrete type *variable*
57
- `$T`. To make this more concrete, imagine this code:
58
-
59
- fn identity<T>(x: T) -> T { x }
60
- let f = identity; // f has type fn($T) -> $T
61
- f(3u); // $T is bound to uint
62
- f(3); // Type error
63
-
64
- You can see here that a type error will result because the type of `f`
65
- (as opposed to the type of `identity`) is not universally quantified
66
- over `$T`. That's fancy math speak for saying that the type variable
67
- `$T` refers to a specific type that may not yet be known, unlike the
68
- type parameter `T` which refers to some type which will never be
69
- known.
70
-
71
- Anyway, regions work differently. If you have an item of type
72
- `fn(&a.T) -> &a.T` and you reference it, its type remains the same:
73
- only when the function *is called* is `&a` instantiated with a
74
- concrete region variable. This means you could call it twice and give
75
- different values for `&a` each time.
76
-
77
- This more general form is possible for regions because they do not
78
- impact code generation. We do not need to monomorphize functions
79
- differently just because they contain region pointers. In fact, we
80
- don't really do *anything* differently.
81
-
82
- ### Representing regions; or, why do I care about all that?
83
-
84
- The point of this discussion is that the representation of regions
85
- must distinguish between a *bound* reference to a region and a *free*
86
- reference. A bound reference is one which will be replaced with a
87
- fresh type variable when the function is called, like the type
88
- parameter `T` in `identity`. They can only appear within function
89
- types. A free reference is a region that may not yet be concretely
90
- known, like the variable `$T`.
91
-
92
- To see why we must distinguish them carefully, consider this program:
93
-
94
- fn item1(s: &a.S) {
95
- let choose = fn@(s1: &a.S) -> &a.S {
96
- if some_cond { s } else { s1 }
97
- };
98
- }
99
-
100
- Here, the variable `s1: &a.S` that appears within the `fn@` is a free
101
- reference to `a`. That is, when you call `choose()`, you don't
102
- replace `&a` with a fresh region variable, but rather you expect `s1`
103
- to be in the same region as the parameter `s`.
104
-
105
- But in this program, this is not the case at all:
106
-
107
- fn item2() {
108
- let identity = fn@(s1: &a.S) -> &a.S { s1 };
109
- }
110
-
111
- To distinguish between these two cases, `ty::region` contains two
112
- variants: `re_bound` and `re_free`. In `item1()`, the outer reference
113
- to `&a` would be `re_bound(rid_param("a", 0u))`, and the inner reference
114
- would be `re_free(rid_param("a", 0u))`. In `item2()`, the inner reference
115
- would be `re_bound(rid_param("a", 0u))`.
116
-
117
- #### Implications for typeck
118
-
119
- In typeck, whenever we call a function, we must go over and replace
120
- all references to `re_bound()` regions within its parameters with
121
- fresh type variables (we do not, however, replace bound regions within
122
- nested function types, as those nested functions have not yet been
123
- called).
124
-
125
- Also, when we typecheck the *body* of an item, we must replace all
126
- `re_bound` references with `re_free` references. This means that the
127
- region in the type of the argument `s` in `item1()` *within `item1()`*
128
- is not `re_bound(re_param("a", 0u))` but rather `re_free(re_param("a",
129
- 0u))`. This is because, for any particular *invocation of `item1()`*,
130
- `&a` will be bound to some specific region, and hence it is no longer
131
- bound.
3
+ This file actually contains two passes related to regions. The first
4
+ pass builds up the `region_map`, which describes the parent links in
5
+ the region hierarchy. The second pass infers which types must be
6
+ region parameterized.
132
7
133
8
*/
134
9
@@ -153,10 +28,10 @@ type binding = {node_id: ast::node_id,
153
28
name : ~str ,
154
29
br : ty:: bound_region } ;
155
30
156
- // Mapping from a block/expr/binding to the innermost scope that
157
- // bounds its lifetime. For a block/expression, this is the lifetime
158
- // in which it will be evaluated. For a binding, this is the lifetime
159
- // in which is in scope.
31
+ /// Mapping from a block/expr/binding to the innermost scope that
32
+ /// bounds its lifetime. For a block/expression, this is the lifetime
33
+ /// in which it will be evaluated. For a binding, this is the lifetime
34
+ /// in which is in scope.
160
35
type region_map = hashmap < ast:: node_id , ast:: node_id > ;
161
36
162
37
type ctxt = {
@@ -198,8 +73,8 @@ type ctxt = {
198
73
parent : parent
199
74
} ;
200
75
201
- // Returns true if `subscope` is equal to or is lexically nested inside
202
- // `superscope` and false otherwise.
76
+ /// Returns true if `subscope` is equal to or is lexically nested inside
77
+ /// `superscope` and false otherwise.
203
78
fn scope_contains ( region_map : region_map , superscope : ast:: node_id ,
204
79
subscope : ast:: node_id ) -> bool {
205
80
let mut subscope = subscope;
@@ -212,6 +87,9 @@ fn scope_contains(region_map: region_map, superscope: ast::node_id,
212
87
ret true;
213
88
}
214
89
90
+ /// Finds the nearest common ancestor (if any) of two scopes. That
91
+ /// is, finds the smallest scope which is greater than or equal to
92
+ /// both `scope_a` and `scope_b`.
215
93
fn nearest_common_ancestor ( region_map : region_map , scope_a : ast:: node_id ,
216
94
scope_b : ast:: node_id ) -> option < ast:: node_id > {
217
95
@@ -262,6 +140,7 @@ fn nearest_common_ancestor(region_map: region_map, scope_a: ast::node_id,
262
140
}
263
141
}
264
142
143
+ /// Extracts that current parent from cx, failing if there is none.
265
144
fn parent_id ( cx : ctxt , span : span ) -> ast:: node_id {
266
145
alt cx. parent {
267
146
none {
@@ -273,6 +152,7 @@ fn parent_id(cx: ctxt, span: span) -> ast::node_id {
273
152
}
274
153
}
275
154
155
+ /// Records the current parent (if any) as the parent of `child_id`.
276
156
fn record_parent ( cx : ctxt , child_id : ast:: node_id ) {
277
157
alt cx. parent {
278
158
none { /* no-op */ }
0 commit comments