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
you can only borrow from lvalues, defined like so:
```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:
```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:
```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:
```notrust
SD = struct S<'LT...> { (f: TY)... }
```
# Borrowing and loans
@ -85,6 +93,7 @@ struct name and we assume structs are declared like so:
Now, imagine we had a program like this:
```notrust
struct Foo { f: uint, g: uint }
...
'a: {
@ -92,6 +101,7 @@ Now, imagine we had a program like this:
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:
```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:
```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:
```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:
```notrust
MUTABILITY(X, MQ) // M-Var-Mut
DECL(X) = mut
MUTABILITY(X, MQ) // M-Var-Imm
DECL(X) = imm
MQ = imm | const
```
### Checking mutability of owned content
@ -287,18 +305,21 @@ 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`:
```notrust
MUTABILITY(LV.f, MQ) // M-Field
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:
```notrust
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty
MQ == imm | const
@ -306,13 +327,16 @@ be borrowed if MQ is immutable or 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:
```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:
```notrust
ALIASABLE(LV.f, MQ) // M-Field
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:
```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:
```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:
```notrust
SCOPE(X) = block where X is declared
```
The scope of a field is the scope of the struct:
```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:
```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:
```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:
```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:
```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:
```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)
```
### 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:
```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:
```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:
```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:
```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`:
```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:
```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`:
```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,6 +610,7 @@ 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:
```notrust
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Managed
TYPE(LV) = @Ty
ACTIONS subset of [MUTATE, CLAIM]
@ -558,6 +619,7 @@ permits restricting `MUTATE` and `CLAIM` actions:
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
}
```
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
}
```
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
}
```
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
}
```
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
}
```
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:
```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,6 +769,7 @@ 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)
@ -705,7 +782,8 @@ Here is a concrete example of a bug this rule prevents:
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`
}
```
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,6 +817,7 @@ 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) {
@ -744,6 +825,7 @@ another path to access the same data, as shown here:
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,6 +838,7 @@ 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) {
@ -763,6 +846,7 @@ value away to create a new path:
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,6 +856,7 @@ 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) {
@ -780,6 +865,7 @@ as shown in the following example:
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,6 +879,7 @@ 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) {
@ -801,18 +888,21 @@ particular, if the referent is frozen, there is no harm in it:
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
}
```
This distinction is reflected in the rules. When doing an `&mut`
borrow -- as in the first example -- the set `ACTIONS` will be
@ -876,6 +966,7 @@ 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.
@ -892,6 +983,7 @@ Let's look at a simple example:
}
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:
>
> ```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,6 +106,7 @@ Pictorally, what this does is to take two distinct variables with
(hopefully not completely) distinct type ranges and produce one with
the intersection.
```notrust
B.ub B.ub
/\ /
A.ub / \ A.ub /
@ -116,6 +119,7 @@ the intersection.
\ / \ / / \
\ / \ / / \
A.lb B.lb A.lb B.lb
```
### Option 2: Relate UB/LB
@ -125,6 +129,7 @@ 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:
```notrust
Before Option #1 Option #2
B.ub B.ub B.ub
@ -139,6 +144,7 @@ are two ways to do that, depicted pictorally here:
\ / \ / / / \ / (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,6 +164,7 @@ course, it depends on the program.
The main case which fails today that I would like to support is:
```notrust
fn foo<T>(x: T, y: T) { ... }
fn bar() {
@ -165,6 +172,7 @@ The main case which fails today that I would like to support is:
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)])
~~~