1
Fork 0

Update rustc/syntax docs now that rustdoc lexes all non-notrust code blocks.

This includes blocks made by indentation, so they need to be changed to
explicitly have ```notrust ... ``` fences..
This commit is contained in:
Huon Wilson 2014-02-24 12:29:45 +11:00
parent cbed3321f5
commit b48833d6db
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 Throughout the docs we'll consider a simple subset of Rust in which
you can only borrow from lvalues, defined like so: 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, Here `x` represents some variable, `LV.f` is a field reference,
and `*LV` is a pointer dereference. There is no auto-deref or other and `*LV` is a pointer dereference. There is no auto-deref or other
niceties. This means that if you have a type like: 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 and a variable `a: ~S`, then the rust expression `a.f` would correspond
to an `LV` of `(*a).f`. to an `LV` of `(*a).f`.
Here is the formal grammar for the types we'll consider: Here is the formal grammar for the types we'll consider:
TY = () | S<'LT...> | ~TY | & 'LT MQ TY | @ MQ TY ```notrust
MQ = mut | imm | const 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 Most of these types should be pretty self explanatory. Here `S` is a
struct name and we assume structs are declared like so: 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 # 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: Now, imagine we had a program like this:
struct Foo { f: uint, g: uint } ```notrust
... struct Foo { f: uint, g: uint }
'a: { ...
let mut x: ~Foo = ...; 'a: {
let y = &mut (*x).f; let mut x: ~Foo = ...;
x = ...; let y = &mut (*x).f;
} x = ...;
}
```
This is of course dangerous because mutating `x` will free the old This is of course dangerous because mutating `x` will free the old
value and hence invalidate `y`. The borrow checker aims to prevent 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 struct defined in `middle::borrowck`. Formally, we define `LOAN` as
follows: follows:
LOAN = (LV, LT, MQ, RESTRICTION*) ```notrust
RESTRICTION = (LV, ACTION*) LOAN = (LV, LT, MQ, RESTRICTION*)
ACTION = MUTATE | CLAIM | FREEZE RESTRICTION = (LV, ACTION*)
ACTION = MUTATE | CLAIM | FREEZE
```
Here the `LOAN` tuple defines the lvalue `LV` being borrowed; the Here the `LOAN` tuple defines the lvalue `LV` being borrowed; the
lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a 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 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: result of the borrow `&mut (*x).f` in the example above:
L = ((*x).f, 'a, mut, RS) where ```notrust
RS = [((*x).f, [MUTATE, CLAIM, FREEZE]), L = ((*x).f, 'a, mut, RS) where
(*x, [MUTATE, CLAIM, FREEZE]), RS = [((*x).f, [MUTATE, CLAIM, FREEZE]),
(x, [MUTATE, CLAIM, FREEZE])] (*x, [MUTATE, CLAIM, FREEZE]),
(x, [MUTATE, CLAIM, FREEZE])]
```
The loan states that the expression `(*x).f` has been loaned as The loan states that the expression `(*x).f` has been loaned as
mutable for the lifetime `'a`. Because the loan is mutable, that means 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 I will present the rules in a modified form of standard inference
rules, which looks as as follows: rules, which looks as as follows:
PREDICATE(X, Y, Z) // Rule-Name ```notrust
Condition 1 PREDICATE(X, Y, Z) // Rule-Name
Condition 2 Condition 1
Condition 3 Condition 2
Condition 3
```
The initial line states the predicate that is to be satisfied. The The initial line states the predicate that is to be satisfied. The
indented lines indicate the conditions that must be met for 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 variable is declared as mutable, it may be borrowed any which way, but
otherwise the variable must be borrowed as immutable or const: otherwise the variable must be borrowed as immutable or const:
MUTABILITY(X, MQ) // M-Var-Mut ```notrust
DECL(X) = mut MUTABILITY(X, MQ) // M-Var-Mut
DECL(X) = mut
MUTABILITY(X, MQ) // M-Var-Imm MUTABILITY(X, MQ) // M-Var-Imm
DECL(X) = imm DECL(X) = imm
MQ = imm | const MQ = imm | const
```
### Checking mutability of owned content ### 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 their base expressions, so both of their rules basically
delegate the check to the base expression `LV`: delegate the check to the base expression `LV`:
MUTABILITY(LV.f, MQ) // M-Field ```notrust
MUTABILITY(LV, MQ) MUTABILITY(LV.f, MQ) // M-Field
MUTABILITY(LV, MQ)
MUTABILITY(*LV, MQ) // M-Deref-Unique MUTABILITY(*LV, MQ) // M-Deref-Unique
TYPE(LV) = ~Ty TYPE(LV) = ~Ty
MUTABILITY(LV, MQ) MUTABILITY(LV, MQ)
```
### Checking mutability of immutable pointer types ### Checking mutability of immutable pointer types
Immutable pointer types like `&T` and `@T` can only Immutable pointer types like `&T` and `@T` can only
be borrowed if MQ is immutable or const: be borrowed if MQ is immutable or const:
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Imm ```notrust
TYPE(LV) = &Ty MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Imm
MQ == imm | const TYPE(LV) = &Ty
MQ == imm | const
MUTABILITY(*LV, MQ) // M-Deref-Managed-Imm MUTABILITY(*LV, MQ) // M-Deref-Managed-Imm
TYPE(LV) = @Ty TYPE(LV) = @Ty
MQ == imm | const MQ == imm | const
```
### Checking mutability of mutable pointer types ### Checking mutability of mutable pointer types
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut: `&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut ```notrust
TYPE(LV) = &mut Ty MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty
```
## Checking aliasability ## 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 Local variables are never aliasable as they are accessible only within
the stack frame. the stack frame.
```notrust
ALIASABLE(X, MQ) // M-Var-Mut ALIASABLE(X, MQ) // M-Var-Mut
```
### Checking aliasable of owned content ### Checking aliasable of owned content
Owned content is aliasable if it is found in an aliasable location: Owned content is aliasable if it is found in an aliasable location:
ALIASABLE(LV.f, MQ) // M-Field ```notrust
ALIASABLE(LV, MQ) ALIASABLE(LV.f, MQ) // M-Field
ALIASABLE(LV, MQ)
ALIASABLE(*LV, MQ) // M-Deref-Unique ALIASABLE(*LV, MQ) // M-Deref-Unique
ALIASABLE(LV, MQ) ALIASABLE(LV, MQ)
```
### Checking mutability of immutable pointer types ### Checking mutability of immutable pointer types
Immutable pointer types like `&T` are aliasable, and hence can only be Immutable pointer types like `&T` are aliasable, and hence can only be
borrowed immutably: borrowed immutably:
ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm ```notrust
TYPE(LV) = &Ty ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty
```
### Checking mutability of mutable pointer types ### Checking mutability of mutable pointer types
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut: `&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut ```notrust
TYPE(LV) = &mut Ty ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty
```
## Checking lifetime ## 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: 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: 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 The scope of a unique referent is the scope of the pointer, since
(barring mutation or moves) the pointer will not be freed until (barring mutation or moves) the pointer will not be freed until
the pointer itself `LV` goes out of scope: 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 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 is a conservative approximation, since there may be other aliases fo
that same managed box that would cause it to live longer: 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 The scope of a borrowed referent is the scope associated with the
pointer. This is a conservative approximation, since the data that pointer. This is a conservative approximation, since the data that
the pointer points at may actually live longer: 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 ### Checking lifetime of variables
The rule for variables states that a variable can only be borrowed a 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 `LT` that is a subregion of the variable's scope:
LIFETIME(X, LT, MQ) // L-Local ```notrust
LT <= SCOPE(X) LIFETIME(X, LT, MQ) // L-Local
LT <= SCOPE(X)
```
### Checking lifetime for owned content ### Checking lifetime for owned content
The lifetime of a field or owned pointer is the same as the lifetime The lifetime of a field or owned pointer is the same as the lifetime
of its owner: of its owner:
LIFETIME(LV.f, LT, MQ) // L-Field ```notrust
LIFETIME(LV, LT, MQ) LIFETIME(LV.f, LT, MQ) // L-Field
LIFETIME(LV, LT, MQ)
LIFETIME(*LV, LT, MQ) // L-Deref-Send LIFETIME(*LV, LT, MQ) // L-Deref-Send
TYPE(LV) = ~Ty TYPE(LV) = ~Ty
LIFETIME(LV, LT, MQ) LIFETIME(LV, LT, MQ)
```
### Checking lifetime for derefs of references ### 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 of the borrow is shorter than the lifetime `LT'` of the pointer
itself: itself:
LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed ```notrust
TYPE(LV) = &LT' Ty OR &LT' mut Ty LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed
LT <= LT' TYPE(LV) = &LT' Ty OR &LT' mut Ty
LT <= LT'
```
### Checking lifetime for derefs of managed, immutable pointers ### 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, when the user guarantees such a root will exist. For this to be true,
three conditions must be met: three conditions must be met:
LIFETIME(*LV, LT, MQ) // L-Deref-Managed-Imm-User-Root ```notrust
TYPE(LV) = @Ty LIFETIME(*LV, LT, MQ) // L-Deref-Managed-Imm-User-Root
LT <= SCOPE(LV) // (1) TYPE(LV) = @Ty
LV is immutable // (2) LT <= SCOPE(LV) // (1)
LV is not moved or not movable // (3) LV is immutable // (2)
LV is not moved or not movable // (3)
```
Condition (1) guarantees that the managed box will be rooted for at Condition (1) guarantees that the managed box will be rooted for at
least the lifetime `LT` of the borrow, presuming that no mutation or 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: The rule for compiler rooting is as follows:
LIFETIME(*LV, LT, MQ) // L-Deref-Managed-Imm-Compiler-Root ```notrust
TYPE(LV) = @Ty LIFETIME(*LV, LT, MQ) // L-Deref-Managed-Imm-Compiler-Root
LT <= innermost enclosing loop/func TYPE(LV) = @Ty
ROOT LV at *LV for LT 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 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 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 Note that there is an initial set of restrictions: these restrictions
are computed based on the kind of borrow: are computed based on the kind of borrow:
&mut LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE) ```notrust
&LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM) &mut LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE)
&const LV => RESTRICTIONS(LV, LT, []) &LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM)
&const LV => RESTRICTIONS(LV, LT, [])
```
The reasoning here is that a mutable borrow must be the only writer, The reasoning here is that a mutable borrow must be the only writer,
therefore it prevents other writes (`MUTATE`), mutable borrows 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`: 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. 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 Restricting a field is the same as restricting the owner of that
field: field:
RESTRICTIONS(LV.f, LT, ACTIONS) = RS, (LV.f, ACTIONS) // R-Field ```notrust
RESTRICTIONS(LV, LT, ACTIONS) = RS 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, 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 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 that we always add `MUTATE` and `CLAIM` to the restriction set imposed
on `LV`: on `LV`:
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer ```notrust
TYPE(LV) = ~Ty RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer
RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS TYPE(LV) = ~Ty
RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS
```
### Restrictions for loans of immutable managed/borrowed referents ### 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 pointers always returns an empty set of restrictions, and it only
permits restricting `MUTATE` and `CLAIM` actions: permits restricting `MUTATE` and `CLAIM` actions:
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Managed ```notrust
TYPE(LV) = @Ty RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Managed
ACTIONS subset of [MUTATE, CLAIM] TYPE(LV) = @Ty
ACTIONS subset of [MUTATE, CLAIM]
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed
TYPE(LV) = &LT' Ty TYPE(LV) = &LT' Ty
LT <= LT' // (1) LT <= LT' // (1)
ACTIONS subset of [MUTATE, CLAIM] ACTIONS subset of [MUTATE, CLAIM]
```
The reason that we can restrict `MUTATE` and `CLAIM` actions even 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 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 of the `&Ty` pointer. In simple cases, this clause is redundant, since
the `LIFETIME()` function will already enforce the required rule: 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 The above example fails to compile both because of clause (1) above
but also by the basic `LIFETIME()` check. However, in more advanced but also by the basic `LIFETIME()` check. However, in more advanced
examples involving multiple nested pointers, clause (1) is needed: 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 The `LIFETIME` rule here would accept `'b` because, in fact, the
*memory is* guaranteed to remain valid (i.e., not be freed) for 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* As a final twist, consider the case of two nested *immutable*
pointers, rather than a mutable pointer within an immutable one: 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 This function is legal. The reason for this is that the inner pointer
(`*point : &'b Point`) is enough to guarantee the memory is immutable (`*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 create a borrowed pointer that outlives the memory it points at. So
`LIFETIME` prevents a function like this: `LIFETIME` prevents a function like this:
fn get_1<'a>() -> &'a int { ```
let x = 1; fn get_1<'a>() -> &'a int {
&x let x = 1;
} &x
}
```
Here we would be returning a pointer into the stack. Clearly bad. 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 mutate it. This distinction is important for type checking functions
like this one: like this one:
fn inc_and_get<'a>(p: &'a mut Point) -> &'a int { ```
p.x += 1; fn inc_and_get<'a>(p: &'a mut Point) -> &'a int {
&p.x p.x += 1;
} &p.x
}
```
In this case, we take in a `&mut` and return a frozen borrowed pointer 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 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 is not necessary to add any restrictions at all to the final
result. result.
```notrust
RESTRICTIONS(*LV, LT, []) = [] // R-Deref-Freeze-Borrowed RESTRICTIONS(*LV, LT, []) = [] // R-Deref-Freeze-Borrowed
TYPE(LV) = &const Ty or @const Ty TYPE(LV) = &const Ty or @const Ty
```
### Restrictions for loans of mutable borrowed referents ### 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: The rule for mutable borrowed pointers is as follows:
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Mut-Borrowed ```notrust
TYPE(LV) = &LT' mut Ty RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Mut-Borrowed
LT <= LT' // (1) TYPE(LV) = &LT' mut Ty
RESTRICTIONS(LV, LT, ACTIONS) = RS // (2) LT <= LT' // (1)
RESTRICTIONS(LV, LT, ACTIONS) = RS // (2)
```
Let's examine the two numbered clauses: 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: 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 { // Test region-reborrow-from-shorter-mut-ref.rs:
&mut **p // ERROR due to clause (1) 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; fn main() {
let mut y = &mut x; // <-'b-----------------------------+ let mut x = 1;
// +-'a--------------------+ | let mut y = &mut x; // <-'b-----------------------------+
// v v | // +-'a--------------------+ |
let z = copy_borrowed_ptr(&mut y); // y is lent | // v v |
*y += 1; // Here y==z, so both should not be usable... | let z = copy_borrowed_ptr(&mut y); // y is lent |
*z += 1; // ...and yet they would be, but for clause 1. | *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 Clause (2) propagates the restrictions on the referent to the pointer
itself. This is the same as with an owned pointer, though the 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 and access it via that new name, thus bypassing the restrictions on
the old name. Here is an example: the old name. Here is an example:
// src/test/compile-fail/borrowck-move-mut-base-ptr.rs ```
fn foo(t0: &mut int) { // src/test/compile-fail/borrowck-move-mut-base-ptr.rs
let p: &int = &*t0; // Freezes `*t0` fn foo(t0: &mut int) {
let t1 = t0; //~ ERROR cannot move out of `t0` let p: &int = &*t0; // Freezes `*t0`
*t1 = 22; // OK, not a write through `*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 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 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 scenarios. The most obvious is that the mutable borrow itself becomes
another path to access the same data, as shown here: 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, // src/test/compile-fail/borrowck-mut-borrow-of-mut-base-ptr.rs
mut t1: &'a mut int) { fn foo<'a>(mut t0: &'a mut int,
let p: &int = &*t0; // Freezes `*t0` mut t1: &'a mut int) {
let mut t2 = &mut t0; //~ ERROR cannot borrow `t0` let p: &int = &*t0; // Freezes `*t0`
**t2 += 1; // Mutates `*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 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 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` Another danger with an `&mut` pointer is that we could swap the `t0`
value away to create a new path: 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, // src/test/compile-fail/borrowck-swap-mut-base-ptr.rs
mut t1: &'a mut int) { fn foo<'a>(mut t0: &'a mut int,
let p: &int = &*t0; // Freezes `*t0` mut t1: &'a mut int) {
swap(&mut t0, &mut t1); //~ ERROR cannot borrow `t0` let p: &int = &*t0; // Freezes `*t0`
*t1 = 22; 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 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 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, referent is claimed, even freezing the base pointer can be dangerous,
as shown in the following example: 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, // src/test/compile-fail/borrowck-borrow-of-mut-base-ptr.rs
mut t1: &'a mut int) { fn foo<'a>(mut t0: &'a mut int,
let p: &mut int = &mut *t0; // Claims `*t0` mut t1: &'a mut int) {
let mut t2 = &t0; //~ ERROR cannot borrow `t0` let p: &mut int = &mut *t0; // Claims `*t0`
let q: &int = &*t2; // Freezes `*t0` but not through `*p` let mut t2 = &t0; //~ ERROR cannot borrow `t0`
*p += 1; // violates type of `*q` 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 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. 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 However, it is not always unsafe to freeze the base pointer. In
particular, if the referent is frozen, there is no harm in it: 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, // src/test/run-pass/borrowck-borrow-of-mut-base-ptr-safe.rs
mut t1: &'a mut int) { fn foo<'a>(mut t0: &'a mut int,
let p: &int = &*t0; // Freezes `*t0` mut t1: &'a mut int) {
let mut t2 = &t0; let p: &int = &*t0; // Freezes `*t0`
let q: &int = &*t2; // Freezes `*t0`, but that's ok... let mut t2 = &t0;
let r: &int = &*t0; // ...after all, could do same thing directly. 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 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 thing `t2` can be used for is to further freeze `*t0`, which is
already frozen. In particular, we cannot assign to `*t0` through the already frozen. In particular, we cannot assign to `*t0` through the
new alias `t2`, as demonstrated in this test case: 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) { // src/test/run-pass/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs
let t1 = t0; fn foo(t0: & &mut int) {
let p: &int = &**t0; let t1 = t0;
**t1 = 22; //~ ERROR cannot assign let p: &int = &**t0;
} **t1 = 22; //~ ERROR cannot assign
}
```
This distinction is reflected in the rules. When doing an `&mut` This distinction is reflected in the rules. When doing an `&mut`
borrow -- as in the first example -- the set `ACTIONS` will be 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: 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 if cond { // Bits: 0
use(&*a); use(&*a);
b = a; // Gen bit 1, kill bit 0. b = a; // Gen bit 1, kill bit 0.
use(&*b); use(&*b);
} else { } else {
// Bits: 0 // Bits: 0
}
// Bits: 0,1
use(&*a); // Error.
use(&*b); // Error.
} }
// 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 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 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. > types lie in between. The bottom type is then the Null type.
> So the tree looks like: > So the tree looks like:
> >
> Object > ```notrust
> / \ > Object
> String Other > / \
> \ / > String Other
> (null) > \ /
> (null)
> ```
> >
> So the upper bound type is the "supertype" and the lower bound is the > 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 > "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 (hopefully not completely) distinct type ranges and produce one with
the intersection. the intersection.
B.ub B.ub ```notrust
/\ / B.ub B.ub
A.ub / \ A.ub / /\ /
/ \ / \ \ / A.ub / \ A.ub /
/ X \ UB / \ / \ \ /
/ / \ \ / \ / X \ UB
/ / / \ / / / / \ \ / \
\ \ / / \ / / / / \ / /
\ X / LB \ \ / / \ /
\ / \ / / \ \ X / LB
\ / \ / / \ \ / \ / / \
A.lb B.lb A.lb B.lb \ / \ / / \
A.lb B.lb A.lb B.lb
```
### Option 2: Relate UB/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 This can be achieved by ensuring that A.ub <: B.lb. In practice there
are two ways to do that, depicted pictorally here: 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 B.ub B.ub B.ub
/\ / \ / \ /\ / \ / \
A.ub / \ A.ub /(B')\ A.ub /(B')\ A.ub / \ A.ub /(B')\ A.ub /(B')\
/ \ / \ \ / / \ / / / \ / \ \ / / \ / /
/ X \ __UB____/ UB / / X \ __UB____/ UB /
/ / \ \ / | | / / / \ \ / | | /
/ / / \ / | | / / / / \ / | | /
\ \ / / /(A')| | / \ \ / / /(A')| | /
\ X / / LB ______LB/ \ X / / LB ______LB/
\ / \ / / / \ / (A')/ \ \ / \ / / / \ / (A')/ \
\ / \ / \ / \ \ / \ \ / \ / \ / \ \ / \
A.lb B.lb A.lb B.lb A.lb B.lb 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, 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 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: 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() { fn bar() {
let x: @mut int = @mut 3; let x: @mut int = @mut 3;
let y: @int = @3; let y: @int = @3;
foo(x, y); foo(x, y);
} }
```
In principle, the inferencer ought to find that the parameter `T` to 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 `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 an identifier in the source code. For example, the `x`s in the
following snippet following snippet
~~~ignore ~~~notrust
struct A { x : int } struct A { x : int }
struct B(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: The following simplified `Eq` is used for in-code examples:
~~~ignore ~~~notrust
trait Eq { trait Eq {
fn eq(&self, other: &Self); 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 When generating the `expr` for the `A` impl, the `SubstructureFields` is
~~~ignore ~~~notrust
Struct(~[FieldInfo { Struct(~[FieldInfo {
span: <span of x> span: <span of x>
name: Some(<ident of x>), name: Some(<ident of x>),
@ -112,7 +112,7 @@ Struct(~[FieldInfo {
For the `B` impl, called with `B(a)` and `B(b)`, For the `B` impl, called with `B(a)` and `B(b)`,
~~~ignore ~~~notrust
Struct(~[FieldInfo { Struct(~[FieldInfo {
span: <span of `int`>, span: <span of `int`>,
name: None, name: None,
@ -126,7 +126,7 @@ Struct(~[FieldInfo {
When generating the `expr` for a call with `self == C0(a)` and `other When generating the `expr` for a call with `self == C0(a)` and `other
== C0(b)`, the SubstructureFields is == C0(b)`, the SubstructureFields is
~~~ignore ~~~notrust
EnumMatching(0, <ast::Variant for C0>, EnumMatching(0, <ast::Variant for C0>,
~[FieldInfo { ~[FieldInfo {
span: <span of int> span: <span of int>
@ -138,7 +138,7 @@ EnumMatching(0, <ast::Variant for C0>,
For `C1 {x}` and `C1 {x}`, For `C1 {x}` and `C1 {x}`,
~~~ignore ~~~notrust
EnumMatching(1, <ast::Variant for C1>, EnumMatching(1, <ast::Variant for C1>,
~[FieldInfo { ~[FieldInfo {
span: <span of x> span: <span of x>
@ -150,7 +150,7 @@ EnumMatching(1, <ast::Variant for C1>,
For `C0(a)` and `C1 {x}` , For `C0(a)` and `C1 {x}` ,
~~~ignore ~~~notrust
EnumNonMatching(~[(0, <ast::Variant for B0>, EnumNonMatching(~[(0, <ast::Variant for B0>,
~[(<span of int>, None, <expr for &a>)]), ~[(<span of int>, None, <expr for &a>)]),
(1, <ast::Variant for B1>, (1, <ast::Variant for B1>,
@ -164,7 +164,7 @@ EnumNonMatching(~[(0, <ast::Variant for B0>,
A static method on the above would result in, 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 A>, Named(~[(<ident of x>, <span of x>)]))
StaticStruct(<ast::StructDef of B>, Unnamed(~[<span of x>])) StaticStruct(<ast::StructDef of B>, Unnamed(~[<span of x>]))
@ -625,7 +625,7 @@ impl<'a> MethodDef<'a> {
} }
/** /**
~~~ignore ~~~
#[deriving(Eq)] #[deriving(Eq)]
struct A { x: int, y: int } struct A { x: int, y: int }
@ -725,7 +725,7 @@ impl<'a> MethodDef<'a> {
} }
/** /**
~~~ignore ~~~
#[deriving(Eq)] #[deriving(Eq)]
enum A { enum A {
A1 A1
@ -768,7 +768,7 @@ impl<'a> MethodDef<'a> {
/** /**
Creates the nested matches for an enum definition recursively, i.e. Creates the nested matches for an enum definition recursively, i.e.
~~~ignore ~~~notrust
match self { match self {
Variant1 => match other { Variant1 => matching, Variant2 => nonmatching, ... }, Variant1 => match other { Variant1 => matching, Variant2 => nonmatching, ... },
Variant2 => match other { Variant1 => nonmatching, Variant2 => matching, ... }, 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 Call the method that is being derived on all the fields, and then
process the collected results. i.e. process the collected results. i.e.
~~~ignore ~~~
f(cx, span, ~[self_1.method(__arg_1_1, __arg_2_1), f(cx, span, ~[self_1.method(__arg_1_1, __arg_2_1),
self_2.method(__arg_1_2, __arg_2_2)]) self_2.method(__arg_1_2, __arg_2_2)])
~~~ ~~~