auto merge of #12510 : huonw/rust/fix-compiler-docs, r=alexcrichton

This includes blocks made by indentation, so they need to be changed to
explicitly have ```notrust ... ``` fences..
This commit is contained in:
bors 2014-02-23 18:06:54 -08:00
commit 7cc6b5e0a3
3 changed files with 326 additions and 226 deletions

View File

@ -56,26 +56,34 @@ it is safe with respect to the in-scope loans.
Throughout the docs we'll consider a simple subset of Rust in which
you can only borrow from lvalues, defined like so:
LV = x | LV.f | *LV
```notrust
LV = x | LV.f | *LV
```
Here `x` represents some variable, `LV.f` is a field reference,
and `*LV` is a pointer dereference. There is no auto-deref or other
niceties. This means that if you have a type like:
struct S { f: uint }
```notrust
struct S { f: uint }
```
and a variable `a: ~S`, then the rust expression `a.f` would correspond
to an `LV` of `(*a).f`.
Here is the formal grammar for the types we'll consider:
TY = () | S<'LT...> | ~TY | & 'LT MQ TY | @ MQ TY
MQ = mut | imm | const
```notrust
TY = () | S<'LT...> | ~TY | & 'LT MQ TY | @ MQ TY
MQ = mut | imm | const
```
Most of these types should be pretty self explanatory. Here `S` is a
struct name and we assume structs are declared like so:
SD = struct S<'LT...> { (f: TY)... }
```notrust
SD = struct S<'LT...> { (f: TY)... }
```
# Borrowing and loans
@ -85,13 +93,15 @@ struct name and we assume structs are declared like so:
Now, imagine we had a program like this:
struct Foo { f: uint, g: uint }
...
'a: {
let mut x: ~Foo = ...;
let y = &mut (*x).f;
x = ...;
}
```notrust
struct Foo { f: uint, g: uint }
...
'a: {
let mut x: ~Foo = ...;
let y = &mut (*x).f;
x = ...;
}
```
This is of course dangerous because mutating `x` will free the old
value and hence invalidate `y`. The borrow checker aims to prevent
@ -108,9 +118,11 @@ the borrow, and (3) a set of restrictions. In the code, `Loan` is a
struct defined in `middle::borrowck`. Formally, we define `LOAN` as
follows:
LOAN = (LV, LT, MQ, RESTRICTION*)
RESTRICTION = (LV, ACTION*)
ACTION = MUTATE | CLAIM | FREEZE
```notrust
LOAN = (LV, LT, MQ, RESTRICTION*)
RESTRICTION = (LV, ACTION*)
ACTION = MUTATE | CLAIM | FREEZE
```
Here the `LOAN` tuple defines the lvalue `LV` being borrowed; the
lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a
@ -139,10 +151,12 @@ To give you a better feeling for what kind of restrictions derived
from a loan, let's look at the loan `L` that would be issued as a
result of the borrow `&mut (*x).f` in the example above:
L = ((*x).f, 'a, mut, RS) where
RS = [((*x).f, [MUTATE, CLAIM, FREEZE]),
(*x, [MUTATE, CLAIM, FREEZE]),
(x, [MUTATE, CLAIM, FREEZE])]
```notrust
L = ((*x).f, 'a, mut, RS) where
RS = [((*x).f, [MUTATE, CLAIM, FREEZE]),
(*x, [MUTATE, CLAIM, FREEZE]),
(x, [MUTATE, CLAIM, FREEZE])]
```
The loan states that the expression `(*x).f` has been loaned as
mutable for the lifetime `'a`. Because the loan is mutable, that means
@ -200,10 +214,12 @@ conditions that it uses. For simplicity I will ignore const loans.
I will present the rules in a modified form of standard inference
rules, which looks as as follows:
PREDICATE(X, Y, Z) // Rule-Name
Condition 1
Condition 2
Condition 3
```notrust
PREDICATE(X, Y, Z) // Rule-Name
Condition 1
Condition 2
Condition 3
```
The initial line states the predicate that is to be satisfied. The
indented lines indicate the conditions that must be met for the
@ -274,12 +290,14 @@ Let's begin with the rules for variables, which state that if a
variable is declared as mutable, it may be borrowed any which way, but
otherwise the variable must be borrowed as immutable or const:
MUTABILITY(X, MQ) // M-Var-Mut
DECL(X) = mut
```notrust
MUTABILITY(X, MQ) // M-Var-Mut
DECL(X) = mut
MUTABILITY(X, MQ) // M-Var-Imm
DECL(X) = imm
MQ = imm | const
MUTABILITY(X, MQ) // M-Var-Imm
DECL(X) = imm
MQ = imm | const
```
### Checking mutability of owned content
@ -287,32 +305,38 @@ Fields and owned pointers inherit their mutability from
their base expressions, so both of their rules basically
delegate the check to the base expression `LV`:
MUTABILITY(LV.f, MQ) // M-Field
MUTABILITY(LV, MQ)
```notrust
MUTABILITY(LV.f, MQ) // M-Field
MUTABILITY(LV, MQ)
MUTABILITY(*LV, MQ) // M-Deref-Unique
TYPE(LV) = ~Ty
MUTABILITY(LV, MQ)
MUTABILITY(*LV, MQ) // M-Deref-Unique
TYPE(LV) = ~Ty
MUTABILITY(LV, MQ)
```
### Checking mutability of immutable pointer types
Immutable pointer types like `&T` and `@T` can only
be borrowed if MQ is immutable or const:
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty
MQ == imm | const
```notrust
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty
MQ == imm | const
MUTABILITY(*LV, MQ) // M-Deref-Managed-Imm
TYPE(LV) = @Ty
MQ == imm | const
MUTABILITY(*LV, MQ) // M-Deref-Managed-Imm
TYPE(LV) = @Ty
MQ == imm | const
```
### Checking mutability of mutable pointer types
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty
```notrust
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty
```
## Checking aliasability
@ -328,32 +352,40 @@ Rust code corresponding to this predicate is the function
Local variables are never aliasable as they are accessible only within
the stack frame.
```notrust
ALIASABLE(X, MQ) // M-Var-Mut
```
### Checking aliasable of owned content
Owned content is aliasable if it is found in an aliasable location:
ALIASABLE(LV.f, MQ) // M-Field
ALIASABLE(LV, MQ)
```notrust
ALIASABLE(LV.f, MQ) // M-Field
ALIASABLE(LV, MQ)
ALIASABLE(*LV, MQ) // M-Deref-Unique
ALIASABLE(LV, MQ)
ALIASABLE(*LV, MQ) // M-Deref-Unique
ALIASABLE(LV, MQ)
```
### Checking mutability of immutable pointer types
Immutable pointer types like `&T` are aliasable, and hence can only be
borrowed immutably:
ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty
```notrust
ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty
```
### Checking mutability of mutable pointer types
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty
```notrust
ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty
```
## Checking lifetime
@ -373,49 +405,63 @@ guaranteed to exist, presuming that no mutations occur.
The scope of a local variable is the block where it is declared:
SCOPE(X) = block where X is declared
```notrust
SCOPE(X) = block where X is declared
```
The scope of a field is the scope of the struct:
SCOPE(LV.f) = SCOPE(LV)
```notrust
SCOPE(LV.f) = SCOPE(LV)
```
The scope of a unique referent is the scope of the pointer, since
(barring mutation or moves) the pointer will not be freed until
the pointer itself `LV` goes out of scope:
SCOPE(*LV) = SCOPE(LV) if LV has type ~T
```notrust
SCOPE(*LV) = SCOPE(LV) if LV has type ~T
```
The scope of a managed referent is also the scope of the pointer. This
is a conservative approximation, since there may be other aliases fo
that same managed box that would cause it to live longer:
SCOPE(*LV) = SCOPE(LV) if LV has type @T
```notrust
SCOPE(*LV) = SCOPE(LV) if LV has type @T
```
The scope of a borrowed referent is the scope associated with the
pointer. This is a conservative approximation, since the data that
the pointer points at may actually live longer:
SCOPE(*LV) = LT if LV has type &'LT T or &'LT mut T
```notrust
SCOPE(*LV) = LT if LV has type &'LT T or &'LT mut T
```
### Checking lifetime of variables
The rule for variables states that a variable can only be borrowed a
lifetime `LT` that is a subregion of the variable's scope:
LIFETIME(X, LT, MQ) // L-Local
LT <= SCOPE(X)
```notrust
LIFETIME(X, LT, MQ) // L-Local
LT <= SCOPE(X)
```
### Checking lifetime for owned content
The lifetime of a field or owned pointer is the same as the lifetime
of its owner:
LIFETIME(LV.f, LT, MQ) // L-Field
LIFETIME(LV, LT, MQ)
```notrust
LIFETIME(LV.f, LT, MQ) // L-Field
LIFETIME(LV, LT, MQ)
LIFETIME(*LV, LT, MQ) // L-Deref-Send
TYPE(LV) = ~Ty
LIFETIME(LV, LT, MQ)
LIFETIME(*LV, LT, MQ) // L-Deref-Send
TYPE(LV) = ~Ty
LIFETIME(LV, LT, MQ)
```
### Checking lifetime for derefs of references
@ -425,9 +471,11 @@ lifetime. Therefore, the borrow is valid so long as the lifetime `LT`
of the borrow is shorter than the lifetime `LT'` of the pointer
itself:
LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed
TYPE(LV) = &LT' Ty OR &LT' mut Ty
LT <= LT'
```notrust
LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed
TYPE(LV) = &LT' Ty OR &LT' mut Ty
LT <= LT'
```
### Checking lifetime for derefs of managed, immutable pointers
@ -436,11 +484,13 @@ Managed pointers are valid so long as the data within them is
when the user guarantees such a root will exist. For this to be true,
three conditions must be met:
LIFETIME(*LV, LT, MQ) // L-Deref-Managed-Imm-User-Root
TYPE(LV) = @Ty
LT <= SCOPE(LV) // (1)
LV is immutable // (2)
LV is not moved or not movable // (3)
```notrust
LIFETIME(*LV, LT, MQ) // L-Deref-Managed-Imm-User-Root
TYPE(LV) = @Ty
LT <= SCOPE(LV) // (1)
LV is immutable // (2)
LV is not moved or not movable // (3)
```
Condition (1) guarantees that the managed box will be rooted for at
least the lifetime `LT` of the borrow, presuming that no mutation or
@ -468,10 +518,12 @@ borrow without crossing the exit from the scope `LT`.
The rule for compiler rooting is as follows:
LIFETIME(*LV, LT, MQ) // L-Deref-Managed-Imm-Compiler-Root
TYPE(LV) = @Ty
LT <= innermost enclosing loop/func
ROOT LV at *LV for LT
```notrust
LIFETIME(*LV, LT, MQ) // L-Deref-Managed-Imm-Compiler-Root
TYPE(LV) = @Ty
LT <= innermost enclosing loop/func
ROOT LV at *LV for LT
```
Here I have written `ROOT LV at *LV FOR LT` to indicate that the code
makes a note in a side-table that the box `LV` must be rooted into the
@ -490,9 +542,11 @@ for the lifetime of the loan".
Note that there is an initial set of restrictions: these restrictions
are computed based on the kind of borrow:
&mut LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE)
&LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM)
&const LV => RESTRICTIONS(LV, LT, [])
```notrust
&mut LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE)
&LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM)
&const LV => RESTRICTIONS(LV, LT, [])
```
The reasoning here is that a mutable borrow must be the only writer,
therefore it prevents other writes (`MUTATE`), mutable borrows
@ -505,7 +559,9 @@ moved out from under it, so no actions are forbidden.
The simplest case is a borrow of a local variable `X`:
RESTRICTIONS(X, LT, ACTIONS) = (X, ACTIONS) // R-Variable
```notrust
RESTRICTIONS(X, LT, ACTIONS) = (X, ACTIONS) // R-Variable
```
In such cases we just record the actions that are not permitted.
@ -514,8 +570,10 @@ In such cases we just record the actions that are not permitted.
Restricting a field is the same as restricting the owner of that
field:
RESTRICTIONS(LV.f, LT, ACTIONS) = RS, (LV.f, ACTIONS) // R-Field
RESTRICTIONS(LV, LT, ACTIONS) = RS
```notrust
RESTRICTIONS(LV.f, LT, ACTIONS) = RS, (LV.f, ACTIONS) // R-Field
RESTRICTIONS(LV, LT, ACTIONS) = RS
```
The reasoning here is as follows. If the field must not be mutated,
then you must not mutate the owner of the field either, since that
@ -535,9 +593,11 @@ must prevent the owned pointer `LV` from being mutated, which means
that we always add `MUTATE` and `CLAIM` to the restriction set imposed
on `LV`:
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer
TYPE(LV) = ~Ty
RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS
```notrust
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer
TYPE(LV) = ~Ty
RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS
```
### Restrictions for loans of immutable managed/borrowed referents
@ -550,14 +610,16 @@ restricting that path. Therefore, the rule for `&Ty` and `@Ty`
pointers always returns an empty set of restrictions, and it only
permits restricting `MUTATE` and `CLAIM` actions:
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Managed
TYPE(LV) = @Ty
ACTIONS subset of [MUTATE, CLAIM]
```notrust
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Managed
TYPE(LV) = @Ty
ACTIONS subset of [MUTATE, CLAIM]
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed
TYPE(LV) = &LT' Ty
LT <= LT' // (1)
ACTIONS subset of [MUTATE, CLAIM]
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed
TYPE(LV) = &LT' Ty
LT <= LT' // (1)
ACTIONS subset of [MUTATE, CLAIM]
```
The reason that we can restrict `MUTATE` and `CLAIM` actions even
without a restrictions list is that it is never legal to mutate nor to
@ -569,17 +631,21 @@ specify that the lifetime of the loan must be less than the lifetime
of the `&Ty` pointer. In simple cases, this clause is redundant, since
the `LIFETIME()` function will already enforce the required rule:
fn foo(point: &'a Point) -> &'static f32 {
&point.x // Error
}
```
fn foo(point: &'a Point) -> &'static f32 {
&point.x // Error
}
```
The above example fails to compile both because of clause (1) above
but also by the basic `LIFETIME()` check. However, in more advanced
examples involving multiple nested pointers, clause (1) is needed:
fn foo(point: &'a &'b mut Point) -> &'b f32 {
&point.x // Error
}
```
fn foo(point: &'a &'b mut Point) -> &'b f32 {
&point.x // Error
}
```
The `LIFETIME` rule here would accept `'b` because, in fact, the
*memory is* guaranteed to remain valid (i.e., not be freed) for the
@ -594,9 +660,11 @@ which is only `'a`, not `'b`. Hence this example yields an error.
As a final twist, consider the case of two nested *immutable*
pointers, rather than a mutable pointer within an immutable one:
fn foo(point: &'a &'b Point) -> &'b f32 {
&point.x // OK
}
```
fn foo(point: &'a &'b Point) -> &'b f32 {
&point.x // OK
}
```
This function is legal. The reason for this is that the inner pointer
(`*point : &'b Point`) is enough to guarantee the memory is immutable
@ -614,10 +682,12 @@ The rules pertaining to `LIFETIME` exist to ensure that we don't
create a borrowed pointer that outlives the memory it points at. So
`LIFETIME` prevents a function like this:
fn get_1<'a>() -> &'a int {
let x = 1;
&x
}
```
fn get_1<'a>() -> &'a int {
let x = 1;
&x
}
```
Here we would be returning a pointer into the stack. Clearly bad.
@ -632,10 +702,12 @@ after we return and hence the remaining code in `'a` cannot possibly
mutate it. This distinction is important for type checking functions
like this one:
fn inc_and_get<'a>(p: &'a mut Point) -> &'a int {
p.x += 1;
&p.x
}
```
fn inc_and_get<'a>(p: &'a mut Point) -> &'a int {
p.x += 1;
&p.x
}
```
In this case, we take in a `&mut` and return a frozen borrowed pointer
with the same lifetime. So long as the lifetime of the returned value
@ -661,8 +733,10 @@ Because moves from a `&const` or `@const` lvalue are never legal, it
is not necessary to add any restrictions at all to the final
result.
```notrust
RESTRICTIONS(*LV, LT, []) = [] // R-Deref-Freeze-Borrowed
TYPE(LV) = &const Ty or @const Ty
```
### Restrictions for loans of mutable borrowed referents
@ -675,10 +749,12 @@ while the new claimant is live.
The rule for mutable borrowed pointers is as follows:
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Mut-Borrowed
TYPE(LV) = &LT' mut Ty
LT <= LT' // (1)
RESTRICTIONS(LV, LT, ACTIONS) = RS // (2)
```notrust
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Mut-Borrowed
TYPE(LV) = &LT' mut Ty
LT <= LT' // (1)
RESTRICTIONS(LV, LT, ACTIONS) = RS // (2)
```
Let's examine the two numbered clauses:
@ -693,19 +769,21 @@ maximum of `LT'`.
Here is a concrete example of a bug this rule prevents:
// Test region-reborrow-from-shorter-mut-ref.rs:
fn copy_pointer<'a,'b,T>(x: &'a mut &'b mut T) -> &'b mut T {
&mut **p // ERROR due to clause (1)
}
fn main() {
let mut x = 1;
let mut y = &mut x; // <-'b-----------------------------+
// +-'a--------------------+ |
// v v |
let z = copy_borrowed_ptr(&mut y); // y is lent |
*y += 1; // Here y==z, so both should not be usable... |
*z += 1; // ...and yet they would be, but for clause 1. |
} <---------------------------------------------------------+
```
// Test region-reborrow-from-shorter-mut-ref.rs:
fn copy_pointer<'a,'b,T>(x: &'a mut &'b mut T) -> &'b mut T {
&mut **p // ERROR due to clause (1)
}
fn main() {
let mut x = 1;
let mut y = &mut x; // <-'b-----------------------------+
// +-'a--------------------+ |
// v v |
let z = copy_borrowed_ptr(&mut y); // y is lent |
*y += 1; // Here y==z, so both should not be usable... |
*z += 1; // ...and yet they would be, but for clause 1. |
} // <------------------------------------------------------+
```
Clause (2) propagates the restrictions on the referent to the pointer
itself. This is the same as with an owned pointer, though the
@ -719,12 +797,14 @@ ways to violate the rules is to move the base pointer to a new name
and access it via that new name, thus bypassing the restrictions on
the old name. Here is an example:
// src/test/compile-fail/borrowck-move-mut-base-ptr.rs
fn foo(t0: &mut int) {
let p: &int = &*t0; // Freezes `*t0`
let t1 = t0; //~ ERROR cannot move out of `t0`
*t1 = 22; // OK, not a write through `*t0`
}
```
// src/test/compile-fail/borrowck-move-mut-base-ptr.rs
fn foo(t0: &mut int) {
let p: &int = &*t0; // Freezes `*t0`
let t1 = t0; //~ ERROR cannot move out of `t0`
*t1 = 22; // OK, not a write through `*t0`
}
```
Remember that `&mut` pointers are linear, and hence `let t1 = t0` is a
move of `t0` -- or would be, if it were legal. Instead, we get an
@ -737,13 +817,15 @@ danger is to mutably borrow the base path. This can lead to two bad
scenarios. The most obvious is that the mutable borrow itself becomes
another path to access the same data, as shown here:
// src/test/compile-fail/borrowck-mut-borrow-of-mut-base-ptr.rs
fn foo<'a>(mut t0: &'a mut int,
mut t1: &'a mut int) {
let p: &int = &*t0; // Freezes `*t0`
let mut t2 = &mut t0; //~ ERROR cannot borrow `t0`
**t2 += 1; // Mutates `*t0`
}
```
// src/test/compile-fail/borrowck-mut-borrow-of-mut-base-ptr.rs
fn foo<'a>(mut t0: &'a mut int,
mut t1: &'a mut int) {
let p: &int = &*t0; // Freezes `*t0`
let mut t2 = &mut t0; //~ ERROR cannot borrow `t0`
**t2 += 1; // Mutates `*t0`
}
```
In this example, `**t2` is the same memory as `*t0`. Because `t2` is
an `&mut` pointer, `**t2` is a unique path and hence it would be
@ -756,13 +838,15 @@ of `t0`. Hence the claim `&mut t0` is illegal.
Another danger with an `&mut` pointer is that we could swap the `t0`
value away to create a new path:
// src/test/compile-fail/borrowck-swap-mut-base-ptr.rs
fn foo<'a>(mut t0: &'a mut int,
mut t1: &'a mut int) {
let p: &int = &*t0; // Freezes `*t0`
swap(&mut t0, &mut t1); //~ ERROR cannot borrow `t0`
*t1 = 22;
}
```
// src/test/compile-fail/borrowck-swap-mut-base-ptr.rs
fn foo<'a>(mut t0: &'a mut int,
mut t1: &'a mut int) {
let p: &int = &*t0; // Freezes `*t0`
swap(&mut t0, &mut t1); //~ ERROR cannot borrow `t0`
*t1 = 22;
}
```
This is illegal for the same reason as above. Note that if we added
back a swap operator -- as we used to have -- we would want to be very
@ -772,14 +856,16 @@ careful to ensure this example is still illegal.
referent is claimed, even freezing the base pointer can be dangerous,
as shown in the following example:
// src/test/compile-fail/borrowck-borrow-of-mut-base-ptr.rs
fn foo<'a>(mut t0: &'a mut int,
mut t1: &'a mut int) {
let p: &mut int = &mut *t0; // Claims `*t0`
let mut t2 = &t0; //~ ERROR cannot borrow `t0`
let q: &int = &*t2; // Freezes `*t0` but not through `*p`
*p += 1; // violates type of `*q`
}
```
// src/test/compile-fail/borrowck-borrow-of-mut-base-ptr.rs
fn foo<'a>(mut t0: &'a mut int,
mut t1: &'a mut int) {
let p: &mut int = &mut *t0; // Claims `*t0`
let mut t2 = &t0; //~ ERROR cannot borrow `t0`
let q: &int = &*t2; // Freezes `*t0` but not through `*p`
*p += 1; // violates type of `*q`
}
```
Here the problem is that `*t0` is claimed by `p`, and hence `p` wants
to be the controlling pointer through which mutation or freezes occur.
@ -793,26 +879,30 @@ which is clearly unsound.
However, it is not always unsafe to freeze the base pointer. In
particular, if the referent is frozen, there is no harm in it:
// src/test/run-pass/borrowck-borrow-of-mut-base-ptr-safe.rs
fn foo<'a>(mut t0: &'a mut int,
mut t1: &'a mut int) {
let p: &int = &*t0; // Freezes `*t0`
let mut t2 = &t0;
let q: &int = &*t2; // Freezes `*t0`, but that's ok...
let r: &int = &*t0; // ...after all, could do same thing directly.
}
```
// src/test/run-pass/borrowck-borrow-of-mut-base-ptr-safe.rs
fn foo<'a>(mut t0: &'a mut int,
mut t1: &'a mut int) {
let p: &int = &*t0; // Freezes `*t0`
let mut t2 = &t0;
let q: &int = &*t2; // Freezes `*t0`, but that's ok...
let r: &int = &*t0; // ...after all, could do same thing directly.
}
```
In this case, creating the alias `t2` of `t0` is safe because the only
thing `t2` can be used for is to further freeze `*t0`, which is
already frozen. In particular, we cannot assign to `*t0` through the
new alias `t2`, as demonstrated in this test case:
// src/test/run-pass/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs
fn foo(t0: & &mut int) {
let t1 = t0;
let p: &int = &**t0;
**t1 = 22; //~ ERROR cannot assign
}
```
// src/test/run-pass/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs
fn foo(t0: & &mut int) {
let t1 = t0;
let p: &int = &**t0;
**t1 = 22; //~ ERROR cannot assign
}
```
This distinction is reflected in the rules. When doing an `&mut`
borrow -- as in the first example -- the set `ACTIONS` will be
@ -876,22 +966,24 @@ moves/uninitializations of the variable that is being used.
Let's look at a simple example:
fn foo(a: ~int) {
let b: ~int; // Gen bit 0.
```
fn foo(a: ~int) {
let b: ~int; // Gen bit 0.
if cond { // Bits: 0
use(&*a);
b = a; // Gen bit 1, kill bit 0.
use(&*b);
} else {
// Bits: 0
}
// Bits: 0,1
use(&*a); // Error.
use(&*b); // Error.
if cond { // Bits: 0
use(&*a);
b = a; // Gen bit 1, kill bit 0.
use(&*b);
} else {
// Bits: 0
}
// Bits: 0,1
use(&*a); // Error.
use(&*b); // Error.
}
fn use(a: &int) { }
fn use(a: &int) { }
```
In this example, the variable `b` is created uninitialized. In one
branch of an `if`, we then move the variable `a` into `b`. Once we

View File

@ -45,11 +45,13 @@ There are several critical invariants which we maintain:
> types lie in between. The bottom type is then the Null type.
> So the tree looks like:
>
> Object
> / \
> String Other
> \ /
> (null)
> ```notrust
> Object
> / \
> String Other
> \ /
> (null)
> ```
>
> So the upper bound type is the "supertype" and the lower bound is the
> "subtype" (also, super and sub mean upper and lower in Latin, or something
@ -104,18 +106,20 @@ Pictorally, what this does is to take two distinct variables with
(hopefully not completely) distinct type ranges and produce one with
the intersection.
B.ub B.ub
/\ /
A.ub / \ A.ub /
/ \ / \ \ /
/ X \ UB
/ / \ \ / \
/ / / \ / /
\ \ / / \ /
\ X / LB
\ / \ / / \
\ / \ / / \
A.lb B.lb A.lb B.lb
```notrust
B.ub B.ub
/\ /
A.ub / \ A.ub /
/ \ / \ \ /
/ X \ UB
/ / \ \ / \
/ / / \ / /
\ \ / / \ /
\ X / LB
\ / \ / / \
\ / \ / / \
A.lb B.lb A.lb B.lb
```
### Option 2: Relate UB/LB
@ -125,20 +129,22 @@ bounds in such a way that, whatever happens, we know that A <: B will hold.
This can be achieved by ensuring that A.ub <: B.lb. In practice there
are two ways to do that, depicted pictorally here:
Before Option #1 Option #2
```notrust
Before Option #1 Option #2
B.ub B.ub B.ub
/\ / \ / \
A.ub / \ A.ub /(B')\ A.ub /(B')\
/ \ / \ \ / / \ / /
/ X \ __UB____/ UB /
/ / \ \ / | | /
/ / / \ / | | /
\ \ / / /(A')| | /
\ X / / LB ______LB/
\ / \ / / / \ / (A')/ \
\ / \ / \ / \ \ / \
A.lb B.lb A.lb B.lb A.lb B.lb
B.ub B.ub B.ub
/\ / \ / \
A.ub / \ A.ub /(B')\ A.ub /(B')\
/ \ / \ \ / / \ / /
/ X \ __UB____/ UB /
/ / \ \ / | | /
/ / / \ / | | /
\ \ / / /(A')| | /
\ X / / LB ______LB/
\ / \ / / / \ / (A')/ \
\ / \ / \ / \ \ / \
A.lb B.lb A.lb B.lb A.lb B.lb
```
In these diagrams, UB and LB are defined as before. As you can see,
the new ranges `A'` and `B'` are quite different from the range that
@ -158,13 +164,15 @@ course, it depends on the program.
The main case which fails today that I would like to support is:
fn foo<T>(x: T, y: T) { ... }
```notrust
fn foo<T>(x: T, y: T) { ... }
fn bar() {
let x: @mut int = @mut 3;
let y: @int = @3;
foo(x, y);
}
fn bar() {
let x: @mut int = @mut 3;
let y: @int = @3;
foo(x, y);
}
```
In principle, the inferencer ought to find that the parameter `T` to
`foo(x, y)` is `@const int`. Today, however, it does not; this is

View File

@ -59,7 +59,7 @@ associated with. It is only not `None` when the associated field has
an identifier in the source code. For example, the `x`s in the
following snippet
~~~ignore
~~~notrust
struct A { x : int }
struct B(int);
@ -83,7 +83,7 @@ variants, it is represented as a count of 0.
The following simplified `Eq` is used for in-code examples:
~~~ignore
~~~notrust
trait Eq {
fn eq(&self, other: &Self);
}
@ -101,7 +101,7 @@ above `Eq`, `A`, `B` and `C`.
When generating the `expr` for the `A` impl, the `SubstructureFields` is
~~~ignore
~~~notrust
Struct(~[FieldInfo {
span: <span of x>
name: Some(<ident of x>),
@ -112,7 +112,7 @@ Struct(~[FieldInfo {
For the `B` impl, called with `B(a)` and `B(b)`,
~~~ignore
~~~notrust
Struct(~[FieldInfo {
span: <span of `int`>,
name: None,
@ -126,7 +126,7 @@ Struct(~[FieldInfo {
When generating the `expr` for a call with `self == C0(a)` and `other
== C0(b)`, the SubstructureFields is
~~~ignore
~~~notrust
EnumMatching(0, <ast::Variant for C0>,
~[FieldInfo {
span: <span of int>
@ -138,7 +138,7 @@ EnumMatching(0, <ast::Variant for C0>,
For `C1 {x}` and `C1 {x}`,
~~~ignore
~~~notrust
EnumMatching(1, <ast::Variant for C1>,
~[FieldInfo {
span: <span of x>
@ -150,7 +150,7 @@ EnumMatching(1, <ast::Variant for C1>,
For `C0(a)` and `C1 {x}` ,
~~~ignore
~~~notrust
EnumNonMatching(~[(0, <ast::Variant for B0>,
~[(<span of int>, None, <expr for &a>)]),
(1, <ast::Variant for B1>,
@ -164,7 +164,7 @@ EnumNonMatching(~[(0, <ast::Variant for B0>,
A static method on the above would result in,
~~~~ignore
~~~~notrust
StaticStruct(<ast::StructDef of A>, Named(~[(<ident of x>, <span of x>)]))
StaticStruct(<ast::StructDef of B>, Unnamed(~[<span of x>]))
@ -625,7 +625,7 @@ impl<'a> MethodDef<'a> {
}
/**
~~~ignore
~~~
#[deriving(Eq)]
struct A { x: int, y: int }
@ -725,7 +725,7 @@ impl<'a> MethodDef<'a> {
}
/**
~~~ignore
~~~
#[deriving(Eq)]
enum A {
A1
@ -768,7 +768,7 @@ impl<'a> MethodDef<'a> {
/**
Creates the nested matches for an enum definition recursively, i.e.
~~~ignore
~~~notrust
match self {
Variant1 => match other { Variant1 => matching, Variant2 => nonmatching, ... },
Variant2 => match other { Variant1 => nonmatching, Variant2 => matching, ... },
@ -1172,7 +1172,7 @@ pub fn cs_fold(use_foldl: bool,
Call the method that is being derived on all the fields, and then
process the collected results. i.e.
~~~ignore
~~~
f(cx, span, ~[self_1.method(__arg_1_1, __arg_2_1),
self_2.method(__arg_1_2, __arg_2_2)])
~~~