rollup merge of #19329: steveklabnik/doc_style_cleanup2

This commit is contained in:
Alex Crichton 2014-11-26 11:00:36 -08:00
commit e8d743ec1d
119 changed files with 6857 additions and 8077 deletions

View File

@ -8,58 +8,56 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Generic hashing support.
*
* This module provides a generic way to compute the hash of a value. The
* simplest way to make a type hashable is to use `#[deriving(Hash)]`:
*
* # Example
*
* ```rust
* use std::hash;
* use std::hash::Hash;
*
* #[deriving(Hash)]
* struct Person {
* id: uint,
* name: String,
* phone: u64,
* }
*
* let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
* let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
*
* assert!(hash::hash(&person1) != hash::hash(&person2));
* ```
*
* If you need more control over how a value is hashed, you need to implement
* the trait `Hash`:
*
* ```rust
* use std::hash;
* use std::hash::Hash;
* use std::hash::sip::SipState;
*
* struct Person {
* id: uint,
* name: String,
* phone: u64,
* }
*
* impl Hash for Person {
* fn hash(&self, state: &mut SipState) {
* self.id.hash(state);
* self.phone.hash(state);
* }
* }
*
* let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
* let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
*
* assert!(hash::hash(&person1) == hash::hash(&person2));
* ```
*/
//! Generic hashing support.
//!
//! This module provides a generic way to compute the hash of a value. The
//! simplest way to make a type hashable is to use `#[deriving(Hash)]`:
//!
//! # Example
//!
//! ```rust
//! use std::hash;
//! use std::hash::Hash;
//!
//! #[deriving(Hash)]
//! struct Person {
//! id: uint,
//! name: String,
//! phone: u64,
//! }
//!
//! let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
//! let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
//!
//! assert!(hash::hash(&person1) != hash::hash(&person2));
//! ```
//!
//! If you need more control over how a value is hashed, you need to implement
//! the trait `Hash`:
//!
//! ```rust
//! use std::hash;
//! use std::hash::Hash;
//! use std::hash::sip::SipState;
//!
//! struct Person {
//! id: uint,
//! name: String,
//! phone: u64,
//! }
//!
//! impl Hash for Person {
//! fn hash(&self, state: &mut SipState) {
//! self.id.hash(state);
//! self.phone.hash(state);
//! }
//! }
//!
//! let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
//! let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
//!
//! assert!(hash::hash(&person1) == hash::hash(&person2));
//! ```
#![allow(unused_must_use)]

View File

@ -8,18 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! The `Clone` trait for types that cannot be 'implicitly copied'
In Rust, some simple types are "implicitly copyable" and when you
assign them or pass them as arguments, the receiver will get a copy,
leaving the original value in place. These types do not require
allocation to copy and do not have finalizers (i.e. they do not
contain owned boxes or implement `Drop`), so the compiler considers
them cheap and safe to copy. For other types copies must be made
explicitly, by convention implementing the `Clone` trait and calling
the `clone` method.
*/
//! The `Clone` trait for types that cannot be 'implicitly copied'
//!
//! In Rust, some simple types are "implicitly copyable" and when you
//! assign them or pass them as arguments, the receiver will get a copy,
//! leaving the original value in place. These types do not require
//! allocation to copy and do not have finalizers (i.e. they do not
//! contain owned boxes or implement `Drop`), so the compiler considers
//! them cheap and safe to copy. For other types copies must be made
//! explicitly, by convention implementing the `Clone` trait and calling
//! the `clone` method.
#![unstable]

View File

@ -8,27 +8,25 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
The Finally trait provides a method, `finally` on
stack closures that emulates Java-style try/finally blocks.
Using the `finally` method is sometimes convenient, but the type rules
prohibit any shared, mutable state between the "try" case and the
"finally" case. For advanced cases, the `try_finally` function can
also be used. See that function for more details.
# Example
```
use std::finally::Finally;
(|| {
// ...
}).finally(|| {
// this code is always run
})
```
*/
//! The Finally trait provides a method, `finally` on
//! stack closures that emulates Java-style try/finally blocks.
//!
//! Using the `finally` method is sometimes convenient, but the type rules
//! prohibit any shared, mutable state between the "try" case and the
//! "finally" case. For advanced cases, the `try_finally` function can
//! also be used. See that function for more details.
//!
//! # Example
//!
//! ```
//! use std::finally::Finally;
//!
//! (|| {
//! // ...
//! }).finally(|| {
//! // this code is always run
//! })
//! ```
#![experimental]

View File

@ -8,38 +8,36 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! rustc compiler intrinsics.
The corresponding definitions are in librustc/middle/trans/foreign.rs.
# Volatiles
The volatile intrinsics provide operations intended to act on I/O
memory, which are guaranteed to not be reordered by the compiler
across other volatile intrinsics. See the LLVM documentation on
[[volatile]].
[volatile]: http://llvm.org/docs/LangRef.html#volatile-memory-accesses
# Atomics
The atomic intrinsics provide common atomic operations on machine
words, with multiple possible memory orderings. They obey the same
semantics as C++11. See the LLVM documentation on [[atomics]].
[atomics]: http://llvm.org/docs/Atomics.html
A quick refresher on memory ordering:
* Acquire - a barrier for acquiring a lock. Subsequent reads and writes
take place after the barrier.
* Release - a barrier for releasing a lock. Preceding reads and writes
take place before the barrier.
* Sequentially consistent - sequentially consistent operations are
guaranteed to happen in order. This is the standard mode for working
with atomic types and is equivalent to Java's `volatile`.
*/
//! rustc compiler intrinsics.
//!
//! The corresponding definitions are in librustc/middle/trans/foreign.rs.
//!
//! # Volatiles
//!
//! The volatile intrinsics provide operations intended to act on I/O
//! memory, which are guaranteed to not be reordered by the compiler
//! across other volatile intrinsics. See the LLVM documentation on
//! [[volatile]].
//!
//! [volatile]: http://llvm.org/docs/LangRef.html#volatile-memory-accesses
//!
//! # Atomics
//!
//! The atomic intrinsics provide common atomic operations on machine
//! words, with multiple possible memory orderings. They obey the same
//! semantics as C++11. See the LLVM documentation on [[atomics]].
//!
//! [atomics]: http://llvm.org/docs/Atomics.html
//!
//! A quick refresher on memory ordering:
//!
//! * Acquire - a barrier for acquiring a lock. Subsequent reads and writes
//! take place after the barrier.
//! * Release - a barrier for releasing a lock. Preceding reads and writes
//! take place before the barrier.
//! * Sequentially consistent - sequentially consistent operations are
//! guaranteed to happen in order. This is the standard mode for working
//! with atomic types and is equivalent to Java's `volatile`.
#![experimental]
#![allow(missing_docs)]

View File

@ -8,55 +8,51 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Composable external iterators
# The `Iterator` trait
This module defines Rust's core iteration trait. The `Iterator` trait has one
unimplemented method, `next`. All other methods are derived through default
methods to perform operations such as `zip`, `chain`, `enumerate`, and `fold`.
The goal of this module is to unify iteration across all containers in Rust.
An iterator can be considered as a state machine which is used to track which
element will be yielded next.
There are various extensions also defined in this module to assist with various
types of iteration, such as the `DoubleEndedIterator` for iterating in reverse,
the `FromIterator` trait for creating a container from an iterator, and much
more.
## Rust's `for` loop
The special syntax used by rust's `for` loop is based around the `Iterator`
trait defined in this module. For loops can be viewed as a syntactical expansion
into a `loop`, for example, the `for` loop in this example is essentially
translated to the `loop` below.
```rust
let values = vec![1i, 2, 3];
// "Syntactical sugar" taking advantage of an iterator
for &x in values.iter() {
println!("{}", x);
}
// Rough translation of the iteration without a `for` iterator.
let mut it = values.iter();
loop {
match it.next() {
Some(&x) => {
println!("{}", x);
}
None => { break }
}
}
```
This `for` loop syntax can be applied to any iterator over any type.
*/
//! Composable external iterators
//!
//! # The `Iterator` trait
//!
//! This module defines Rust's core iteration trait. The `Iterator` trait has one
//! unimplemented method, `next`. All other methods are derived through default
//! methods to perform operations such as `zip`, `chain`, `enumerate`, and `fold`.
//!
//! The goal of this module is to unify iteration across all containers in Rust.
//! An iterator can be considered as a state machine which is used to track which
//! element will be yielded next.
//!
//! There are various extensions also defined in this module to assist with various
//! types of iteration, such as the `DoubleEndedIterator` for iterating in reverse,
//! the `FromIterator` trait for creating a container from an iterator, and much
//! more.
//!
//! ## Rust's `for` loop
//!
//! The special syntax used by rust's `for` loop is based around the `Iterator`
//! trait defined in this module. For loops can be viewed as a syntactical expansion
//! into a `loop`, for example, the `for` loop in this example is essentially
//! translated to the `loop` below.
//!
//! ```rust
//! let values = vec![1i, 2, 3];
//!
//! // "Syntactical sugar" taking advantage of an iterator
//! for &x in values.iter() {
//! println!("{}", x);
//! }
//!
//! // Rough translation of the iteration without a `for` iterator.
//! let mut it = values.iter();
//! loop {
//! match it.next() {
//! Some(&x) => {
//! println!("{}", x);
//! }
//! None => { break }
//! }
//! }
//! ```
//!
//! This `for` loop syntax can be applied to any iterator over any type.
pub use self::MinMaxResult::*;

View File

@ -8,17 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Primitive traits representing basic 'kinds' of types
Rust types can be classified in various useful ways according to
intrinsic properties of the type. These classifications, often called
'kinds', are represented as traits.
They cannot be implemented by user code, but are instead implemented
by the compiler automatically for the types to which they apply.
*/
//! Primitive traits representing basic 'kinds' of types
//!
//! Rust types can be classified in various useful ways according to
//! intrinsic properties of the type. These classifications, often called
//! 'kinds', are represented as traits.
//!
//! They cannot be implemented by user code, but are instead implemented
//! by the compiler automatically for the types to which they apply.
/// Types able to be transferred across task boundaries.
#[lang="send"]

View File

@ -8,52 +8,48 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
*
* Overloadable operators
*
* Implementing these traits allows you to get an effect similar to
* overloading operators.
*
* The values for the right hand side of an operator are automatically
* borrowed, so `a + b` is sugar for `a.add(&b)`.
*
* All of these traits are imported by the prelude, so they are available in
* every Rust program.
*
* # Example
*
* This example creates a `Point` struct that implements `Add` and `Sub`, and then
* demonstrates adding and subtracting two `Point`s.
*
* ```rust
* #[deriving(Show)]
* struct Point {
* x: int,
* y: int
* }
*
* impl Add<Point, Point> for Point {
* fn add(&self, other: &Point) -> Point {
* Point {x: self.x + other.x, y: self.y + other.y}
* }
* }
*
* impl Sub<Point, Point> for Point {
* fn sub(&self, other: &Point) -> Point {
* Point {x: self.x - other.x, y: self.y - other.y}
* }
* }
* fn main() {
* println!("{}", Point {x: 1, y: 0} + Point {x: 2, y: 3});
* println!("{}", Point {x: 1, y: 0} - Point {x: 2, y: 3});
* }
* ```
*
* See the documentation for each trait for a minimum implementation that prints
* something to the screen.
*
*/
//! Overloadable operators
//!
//! Implementing these traits allows you to get an effect similar to
//! overloading operators.
//!
//! The values for the right hand side of an operator are automatically
//! borrowed, so `a + b` is sugar for `a.add(&b)`.
//!
//! All of these traits are imported by the prelude, so they are available in
//! every Rust program.
//!
//! # Example
//!
//! This example creates a `Point` struct that implements `Add` and `Sub`, and then
//! demonstrates adding and subtracting two `Point`s.
//!
//! ```rust
//! #[deriving(Show)]
//! struct Point {
//! x: int,
//! y: int
//! }
//!
//! impl Add<Point, Point> for Point {
//! fn add(&self, other: &Point) -> Point {
//! Point {x: self.x + other.x, y: self.y + other.y}
//! }
//! }
//!
//! impl Sub<Point, Point> for Point {
//! fn sub(&self, other: &Point) -> Point {
//! Point {x: self.x - other.x, y: self.y - other.y}
//! }
//! }
//! fn main() {
//! println!("{}", Point {x: 1, y: 0} + Point {x: 2, y: 3});
//! println!("{}", Point {x: 1, y: 0} - Point {x: 2, y: 3});
//! }
//! ```
//!
//! See the documentation for each trait for a minimum implementation that prints
//! something to the screen.
use kinds::Sized;

View File

@ -8,15 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Simple [DEFLATE][def]-based compression. This is a wrapper around the
[`miniz`][mz] library, which is a one-file pure-C implementation of zlib.
[def]: https://en.wikipedia.org/wiki/DEFLATE
[mz]: https://code.google.com/p/miniz/
*/
//! Simple [DEFLATE][def]-based compression. This is a wrapper around the
//! [`miniz`][mz] library, which is a one-file pure-C implementation of zlib.
//!
//! [def]: https://en.wikipedia.org/wiki/DEFLATE
//! [mz]: https://code.google.com/p/miniz/
#![crate_name = "flate"]
#![experimental]

View File

@ -8,260 +8,258 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! Generate files suitable for use with [Graphviz](http://www.graphviz.org/)
The `render` function generates output (e.g. an `output.dot` file) for
use with [Graphviz](http://www.graphviz.org/) by walking a labelled
graph. (Graphviz can then automatically lay out the nodes and edges
of the graph, and also optionally render the graph as an image or
other [output formats](
http://www.graphviz.org/content/output-formats), such as SVG.)
Rather than impose some particular graph data structure on clients,
this library exposes two traits that clients can implement on their
own structs before handing them over to the rendering function.
Note: This library does not yet provide access to the full
expressiveness of the [DOT language](
http://www.graphviz.org/doc/info/lang.html). For example, there are
many [attributes](http://www.graphviz.org/content/attrs) related to
providing layout hints (e.g. left-to-right versus top-down, which
algorithm to use, etc). The current intention of this library is to
emit a human-readable .dot file with very regular structure suitable
for easy post-processing.
# Examples
The first example uses a very simple graph representation: a list of
pairs of ints, representing the edges (the node set is implicit).
Each node label is derived directly from the int representing the node,
while the edge labels are all empty strings.
This example also illustrates how to use `CowVec` to return
an owned vector or a borrowed slice as appropriate: we construct the
node vector from scratch, but borrow the edge list (rather than
constructing a copy of all the edges from scratch).
The output from this example renders five nodes, with the first four
forming a diamond-shaped acyclic graph and then pointing to the fifth
which is cyclic.
```rust
use graphviz as dot;
type Nd = int;
type Ed = (int,int);
struct Edges(Vec<Ed>);
pub fn render_to<W:Writer>(output: &mut W) {
let edges = Edges(vec!((0,1), (0,2), (1,3), (2,3), (3,4), (4,4)));
dot::render(&edges, output).unwrap()
}
impl<'a> dot::Labeller<'a, Nd, Ed> for Edges {
fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example1").unwrap() }
fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
dot::Id::new(format!("N{}", *n)).unwrap()
}
}
impl<'a> dot::GraphWalk<'a, Nd, Ed> for Edges {
fn nodes(&self) -> dot::Nodes<'a,Nd> {
// (assumes that |N| \approxeq |E|)
let &Edges(ref v) = self;
let mut nodes = Vec::with_capacity(v.len());
for &(s,t) in v.iter() {
nodes.push(s); nodes.push(t);
}
nodes.sort();
nodes.dedup();
nodes.into_cow()
}
fn edges(&'a self) -> dot::Edges<'a,Ed> {
let &Edges(ref edges) = self;
edges.as_slice().into_cow()
}
fn source(&self, e: &Ed) -> Nd { let &(s,_) = e; s }
fn target(&self, e: &Ed) -> Nd { let &(_,t) = e; t }
}
# pub fn main() { render_to(&mut Vec::new()) }
```
```no_run
# pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
pub fn main() {
use std::io::File;
let mut f = File::create(&Path::new("example1.dot"));
render_to(&mut f)
}
```
Output from first example (in `example1.dot`):
```ignore
digraph example1 {
N0[label="N0"];
N1[label="N1"];
N2[label="N2"];
N3[label="N3"];
N4[label="N4"];
N0 -> N1[label=""];
N0 -> N2[label=""];
N1 -> N3[label=""];
N2 -> N3[label=""];
N3 -> N4[label=""];
N4 -> N4[label=""];
}
```
The second example illustrates using `node_label` and `edge_label` to
add labels to the nodes and edges in the rendered graph. The graph
here carries both `nodes` (the label text to use for rendering a
particular node), and `edges` (again a list of `(source,target)`
indices).
This example also illustrates how to use a type (in this case the edge
type) that shares substructure with the graph: the edge type here is a
direct reference to the `(source,target)` pair stored in the graph's
internal vector (rather than passing around a copy of the pair
itself). Note that this implies that `fn edges(&'a self)` must
construct a fresh `Vec<&'a (uint,uint)>` from the `Vec<(uint,uint)>`
edges stored in `self`.
Since both the set of nodes and the set of edges are always
constructed from scratch via iterators, we use the `collect()` method
from the `Iterator` trait to collect the nodes and edges into freshly
constructed growable `Vec` values (rather use the `into_cow`
from the `IntoCow` trait as was used in the first example
above).
The output from this example renders four nodes that make up the
Hasse-diagram for the subsets of the set `{x, y}`. Each edge is
labelled with the &sube; character (specified using the HTML character
entity `&sube`).
```rust
use graphviz as dot;
type Nd = uint;
type Ed<'a> = &'a (uint, uint);
struct Graph { nodes: Vec<&'static str>, edges: Vec<(uint,uint)> }
pub fn render_to<W:Writer>(output: &mut W) {
let nodes = vec!("{x,y}","{x}","{y}","{}");
let edges = vec!((0,1), (0,2), (1,3), (2,3));
let graph = Graph { nodes: nodes, edges: edges };
dot::render(&graph, output).unwrap()
}
impl<'a> dot::Labeller<'a, Nd, Ed<'a>> for Graph {
fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example2").unwrap() }
fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
dot::Id::new(format!("N{}", n)).unwrap()
}
fn node_label<'a>(&'a self, n: &Nd) -> dot::LabelText<'a> {
dot::LabelStr(self.nodes[*n].as_slice().into_cow())
}
fn edge_label<'a>(&'a self, _: &Ed) -> dot::LabelText<'a> {
dot::LabelStr("&sube;".into_cow())
}
}
impl<'a> dot::GraphWalk<'a, Nd, Ed<'a>> for Graph {
fn nodes(&self) -> dot::Nodes<'a,Nd> { range(0,self.nodes.len()).collect() }
fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { self.edges.iter().collect() }
fn source(&self, e: &Ed) -> Nd { let & &(s,_) = e; s }
fn target(&self, e: &Ed) -> Nd { let & &(_,t) = e; t }
}
# pub fn main() { render_to(&mut Vec::new()) }
```
```no_run
# pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
pub fn main() {
use std::io::File;
let mut f = File::create(&Path::new("example2.dot"));
render_to(&mut f)
}
```
The third example is similar to the second, except now each node and
edge now carries a reference to the string label for each node as well
as that node's index. (This is another illustration of how to share
structure with the graph itself, and why one might want to do so.)
The output from this example is the same as the second example: the
Hasse-diagram for the subsets of the set `{x, y}`.
```rust
use graphviz as dot;
type Nd<'a> = (uint, &'a str);
type Ed<'a> = (Nd<'a>, Nd<'a>);
struct Graph { nodes: Vec<&'static str>, edges: Vec<(uint,uint)> }
pub fn render_to<W:Writer>(output: &mut W) {
let nodes = vec!("{x,y}","{x}","{y}","{}");
let edges = vec!((0,1), (0,2), (1,3), (2,3));
let graph = Graph { nodes: nodes, edges: edges };
dot::render(&graph, output).unwrap()
}
impl<'a> dot::Labeller<'a, Nd<'a>, Ed<'a>> for Graph {
fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example3").unwrap() }
fn node_id(&'a self, n: &Nd<'a>) -> dot::Id<'a> {
dot::Id::new(format!("N{}", n.val0())).unwrap()
}
fn node_label<'a>(&'a self, n: &Nd<'a>) -> dot::LabelText<'a> {
let &(i, _) = n;
dot::LabelStr(self.nodes[i].as_slice().into_cow())
}
fn edge_label<'a>(&'a self, _: &Ed<'a>) -> dot::LabelText<'a> {
dot::LabelStr("&sube;".into_cow())
}
}
impl<'a> dot::GraphWalk<'a, Nd<'a>, Ed<'a>> for Graph {
fn nodes(&'a self) -> dot::Nodes<'a,Nd<'a>> {
self.nodes.iter().map(|s|s.as_slice()).enumerate().collect()
}
fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> {
self.edges.iter()
.map(|&(i,j)|((i, self.nodes[i].as_slice()),
(j, self.nodes[j].as_slice())))
.collect()
}
fn source(&self, e: &Ed<'a>) -> Nd<'a> { let &(s,_) = e; s }
fn target(&self, e: &Ed<'a>) -> Nd<'a> { let &(_,t) = e; t }
}
# pub fn main() { render_to(&mut Vec::new()) }
```
```no_run
# pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
pub fn main() {
use std::io::File;
let mut f = File::create(&Path::new("example3.dot"));
render_to(&mut f)
}
```
# References
* [Graphviz](http://www.graphviz.org/)
* [DOT language](http://www.graphviz.org/doc/info/lang.html)
*/
//! Generate files suitable for use with [Graphviz](http://www.graphviz.org/)
//!
//! The `render` function generates output (e.g. an `output.dot` file) for
//! use with [Graphviz](http://www.graphviz.org/) by walking a labelled
//! graph. (Graphviz can then automatically lay out the nodes and edges
//! of the graph, and also optionally render the graph as an image or
//! other [output formats](
//! http://www.graphviz.org/content/output-formats), such as SVG.)
//!
//! Rather than impose some particular graph data structure on clients,
//! this library exposes two traits that clients can implement on their
//! own structs before handing them over to the rendering function.
//!
//! Note: This library does not yet provide access to the full
//! expressiveness of the [DOT language](
//! http://www.graphviz.org/doc/info/lang.html). For example, there are
//! many [attributes](http://www.graphviz.org/content/attrs) related to
//! providing layout hints (e.g. left-to-right versus top-down, which
//! algorithm to use, etc). The current intention of this library is to
//! emit a human-readable .dot file with very regular structure suitable
//! for easy post-processing.
//!
//! # Examples
//!
//! The first example uses a very simple graph representation: a list of
//! pairs of ints, representing the edges (the node set is implicit).
//! Each node label is derived directly from the int representing the node,
//! while the edge labels are all empty strings.
//!
//! This example also illustrates how to use `CowVec` to return
//! an owned vector or a borrowed slice as appropriate: we construct the
//! node vector from scratch, but borrow the edge list (rather than
//! constructing a copy of all the edges from scratch).
//!
//! The output from this example renders five nodes, with the first four
//! forming a diamond-shaped acyclic graph and then pointing to the fifth
//! which is cyclic.
//!
//! ```rust
//! use graphviz as dot;
//!
//! type Nd = int;
//! type Ed = (int,int);
//! struct Edges(Vec<Ed>);
//!
//! pub fn render_to<W:Writer>(output: &mut W) {
//! let edges = Edges(vec!((0,1), (0,2), (1,3), (2,3), (3,4), (4,4)));
//! dot::render(&edges, output).unwrap()
//! }
//!
//! impl<'a> dot::Labeller<'a, Nd, Ed> for Edges {
//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example1").unwrap() }
//!
//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
//! dot::Id::new(format!("N{}", *n)).unwrap()
//! }
//! }
//!
//! impl<'a> dot::GraphWalk<'a, Nd, Ed> for Edges {
//! fn nodes(&self) -> dot::Nodes<'a,Nd> {
//! // (assumes that |N| \approxeq |E|)
//! let &Edges(ref v) = self;
//! let mut nodes = Vec::with_capacity(v.len());
//! for &(s,t) in v.iter() {
//! nodes.push(s); nodes.push(t);
//! }
//! nodes.sort();
//! nodes.dedup();
//! nodes.into_cow()
//! }
//!
//! fn edges(&'a self) -> dot::Edges<'a,Ed> {
//! let &Edges(ref edges) = self;
//! edges.as_slice().into_cow()
//! }
//!
//! fn source(&self, e: &Ed) -> Nd { let &(s,_) = e; s }
//!
//! fn target(&self, e: &Ed) -> Nd { let &(_,t) = e; t }
//! }
//!
//! # pub fn main() { render_to(&mut Vec::new()) }
//! ```
//!
//! ```no_run
//! # pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
//! pub fn main() {
//! use std::io::File;
//! let mut f = File::create(&Path::new("example1.dot"));
//! render_to(&mut f)
//! }
//! ```
//!
//! Output from first example (in `example1.dot`):
//!
//! ```ignore
//! digraph example1 {
//! N0[label="N0"];
//! N1[label="N1"];
//! N2[label="N2"];
//! N3[label="N3"];
//! N4[label="N4"];
//! N0 -> N1[label=""];
//! N0 -> N2[label=""];
//! N1 -> N3[label=""];
//! N2 -> N3[label=""];
//! N3 -> N4[label=""];
//! N4 -> N4[label=""];
//! }
//! ```
//!
//! The second example illustrates using `node_label` and `edge_label` to
//! add labels to the nodes and edges in the rendered graph. The graph
//! here carries both `nodes` (the label text to use for rendering a
//! particular node), and `edges` (again a list of `(source,target)`
//! indices).
//!
//! This example also illustrates how to use a type (in this case the edge
//! type) that shares substructure with the graph: the edge type here is a
//! direct reference to the `(source,target)` pair stored in the graph's
//! internal vector (rather than passing around a copy of the pair
//! itself). Note that this implies that `fn edges(&'a self)` must
//! construct a fresh `Vec<&'a (uint,uint)>` from the `Vec<(uint,uint)>`
//! edges stored in `self`.
//!
//! Since both the set of nodes and the set of edges are always
//! constructed from scratch via iterators, we use the `collect()` method
//! from the `Iterator` trait to collect the nodes and edges into freshly
//! constructed growable `Vec` values (rather use the `into_cow`
//! from the `IntoCow` trait as was used in the first example
//! above).
//!
//! The output from this example renders four nodes that make up the
//! Hasse-diagram for the subsets of the set `{x, y}`. Each edge is
//! labelled with the &sube; character (specified using the HTML character
//! entity `&sube`).
//!
//! ```rust
//! use graphviz as dot;
//!
//! type Nd = uint;
//! type Ed<'a> = &'a (uint, uint);
//! struct Graph { nodes: Vec<&'static str>, edges: Vec<(uint,uint)> }
//!
//! pub fn render_to<W:Writer>(output: &mut W) {
//! let nodes = vec!("{x,y}","{x}","{y}","{}");
//! let edges = vec!((0,1), (0,2), (1,3), (2,3));
//! let graph = Graph { nodes: nodes, edges: edges };
//!
//! dot::render(&graph, output).unwrap()
//! }
//!
//! impl<'a> dot::Labeller<'a, Nd, Ed<'a>> for Graph {
//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example2").unwrap() }
//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
//! dot::Id::new(format!("N{}", n)).unwrap()
//! }
//! fn node_label<'a>(&'a self, n: &Nd) -> dot::LabelText<'a> {
//! dot::LabelStr(self.nodes[*n].as_slice().into_cow())
//! }
//! fn edge_label<'a>(&'a self, _: &Ed) -> dot::LabelText<'a> {
//! dot::LabelStr("&sube;".into_cow())
//! }
//! }
//!
//! impl<'a> dot::GraphWalk<'a, Nd, Ed<'a>> for Graph {
//! fn nodes(&self) -> dot::Nodes<'a,Nd> { range(0,self.nodes.len()).collect() }
//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { self.edges.iter().collect() }
//! fn source(&self, e: &Ed) -> Nd { let & &(s,_) = e; s }
//! fn target(&self, e: &Ed) -> Nd { let & &(_,t) = e; t }
//! }
//!
//! # pub fn main() { render_to(&mut Vec::new()) }
//! ```
//!
//! ```no_run
//! # pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
//! pub fn main() {
//! use std::io::File;
//! let mut f = File::create(&Path::new("example2.dot"));
//! render_to(&mut f)
//! }
//! ```
//!
//! The third example is similar to the second, except now each node and
//! edge now carries a reference to the string label for each node as well
//! as that node's index. (This is another illustration of how to share
//! structure with the graph itself, and why one might want to do so.)
//!
//! The output from this example is the same as the second example: the
//! Hasse-diagram for the subsets of the set `{x, y}`.
//!
//! ```rust
//! use graphviz as dot;
//!
//! type Nd<'a> = (uint, &'a str);
//! type Ed<'a> = (Nd<'a>, Nd<'a>);
//! struct Graph { nodes: Vec<&'static str>, edges: Vec<(uint,uint)> }
//!
//! pub fn render_to<W:Writer>(output: &mut W) {
//! let nodes = vec!("{x,y}","{x}","{y}","{}");
//! let edges = vec!((0,1), (0,2), (1,3), (2,3));
//! let graph = Graph { nodes: nodes, edges: edges };
//!
//! dot::render(&graph, output).unwrap()
//! }
//!
//! impl<'a> dot::Labeller<'a, Nd<'a>, Ed<'a>> for Graph {
//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example3").unwrap() }
//! fn node_id(&'a self, n: &Nd<'a>) -> dot::Id<'a> {
//! dot::Id::new(format!("N{}", n.val0())).unwrap()
//! }
//! fn node_label<'a>(&'a self, n: &Nd<'a>) -> dot::LabelText<'a> {
//! let &(i, _) = n;
//! dot::LabelStr(self.nodes[i].as_slice().into_cow())
//! }
//! fn edge_label<'a>(&'a self, _: &Ed<'a>) -> dot::LabelText<'a> {
//! dot::LabelStr("&sube;".into_cow())
//! }
//! }
//!
//! impl<'a> dot::GraphWalk<'a, Nd<'a>, Ed<'a>> for Graph {
//! fn nodes(&'a self) -> dot::Nodes<'a,Nd<'a>> {
//! self.nodes.iter().map(|s|s.as_slice()).enumerate().collect()
//! }
//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> {
//! self.edges.iter()
//! .map(|&(i,j)|((i, self.nodes[i].as_slice()),
//! (j, self.nodes[j].as_slice())))
//! .collect()
//! }
//! fn source(&self, e: &Ed<'a>) -> Nd<'a> { let &(s,_) = e; s }
//! fn target(&self, e: &Ed<'a>) -> Nd<'a> { let &(_,t) = e; t }
//! }
//!
//! # pub fn main() { render_to(&mut Vec::new()) }
//! ```
//!
//! ```no_run
//! # pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
//! pub fn main() {
//! use std::io::File;
//! let mut f = File::create(&Path::new("example3.dot"));
//! render_to(&mut f)
//! }
//! ```
//!
//! # References
//!
//! * [Graphviz](http://www.graphviz.org/)
//!
//! * [DOT language](http://www.graphviz.org/doc/info/lang.html)
#![crate_name = "graphviz"]
#![experimental]

View File

@ -19,59 +19,57 @@
html_root_url = "http://doc.rust-lang.org/nightly/",
html_playground_url = "http://play.rust-lang.org/")]
/*!
* Bindings for the C standard library and other platform libraries
*
* **NOTE:** These are *architecture and libc* specific. On Linux, these
* bindings are only correct for glibc.
*
* This module contains bindings to the C standard library, organized into
* modules by their defining standard. Additionally, it contains some assorted
* platform-specific definitions. For convenience, most functions and types
* are reexported, so `use libc::*` will import the available C bindings as
* appropriate for the target platform. The exact set of functions available
* are platform specific.
*
* *Note:* Because these definitions are platform-specific, some may not appear
* in the generated documentation.
*
* We consider the following specs reasonably normative with respect to
* interoperating with the C standard library (libc/msvcrt):
*
* * ISO 9899:1990 ('C95', 'ANSI C', 'Standard C'), NA1, 1995.
* * ISO 9899:1999 ('C99' or 'C9x').
* * ISO 9945:1988 / IEEE 1003.1-1988 ('POSIX.1').
* * ISO 9945:2001 / IEEE 1003.1-2001 ('POSIX:2001', 'SUSv3').
* * ISO 9945:2008 / IEEE 1003.1-2008 ('POSIX:2008', 'SUSv4').
*
* Note that any reference to the 1996 revision of POSIX, or any revs between
* 1990 (when '88 was approved at ISO) and 2001 (when the next actual
* revision-revision happened), are merely additions of other chapters (1b and
* 1c) outside the core interfaces.
*
* Despite having several names each, these are *reasonably* coherent
* point-in-time, list-of-definition sorts of specs. You can get each under a
* variety of names but will wind up with the same definition in each case.
*
* See standards(7) in linux-manpages for more details.
*
* Our interface to these libraries is complicated by the non-universality of
* conformance to any of them. About the only thing universally supported is
* the first (C95), beyond that definitions quickly become absent on various
* platforms.
*
* We therefore wind up dividing our module-space up (mostly for the sake of
* sanity while editing, filling-in-details and eliminating duplication) into
* definitions common-to-all (held in modules named c95, c99, posix88, posix01
* and posix08) and definitions that appear only on *some* platforms (named
* 'extra'). This would be things like significant OSX foundation kit, or Windows
* library kernel32.dll, or various fancy glibc, Linux or BSD extensions.
*
* In addition to the per-platform 'extra' modules, we define a module of
* 'common BSD' libc routines that never quite made it into POSIX but show up
* in multiple derived systems. This is the 4.4BSD r2 / 1995 release, the final
* one from Berkeley after the lawsuits died down and the CSRG dissolved.
*/
//! Bindings for the C standard library and other platform libraries
//!
//! **NOTE:** These are *architecture and libc* specific. On Linux, these
//! bindings are only correct for glibc.
//!
//! This module contains bindings to the C standard library, organized into
//! modules by their defining standard. Additionally, it contains some assorted
//! platform-specific definitions. For convenience, most functions and types
//! are reexported, so `use libc::*` will import the available C bindings as
//! appropriate for the target platform. The exact set of functions available
//! are platform specific.
//!
//! *Note:* Because these definitions are platform-specific, some may not appear
//! in the generated documentation.
//!
//! We consider the following specs reasonably normative with respect to
//! interoperating with the C standard library (libc/msvcrt):
//!
//! * ISO 9899:1990 ('C95', 'ANSI C', 'Standard C'), NA1, 1995.
//! * ISO 9899:1999 ('C99' or 'C9x').
//! * ISO 9945:1988 / IEEE 1003.1-1988 ('POSIX.1').
//! * ISO 9945:2001 / IEEE 1003.1-2001 ('POSIX:2001', 'SUSv3').
//! * ISO 9945:2008 / IEEE 1003.1-2008 ('POSIX:2008', 'SUSv4').
//!
//! Note that any reference to the 1996 revision of POSIX, or any revs between
//! 1990 (when '88 was approved at ISO) and 2001 (when the next actual
//! revision-revision happened), are merely additions of other chapters (1b and
//! 1c) outside the core interfaces.
//!
//! Despite having several names each, these are *reasonably* coherent
//! point-in-time, list-of-definition sorts of specs. You can get each under a
//! variety of names but will wind up with the same definition in each case.
//!
//! See standards(7) in linux-manpages for more details.
//!
//! Our interface to these libraries is complicated by the non-universality of
//! conformance to any of them. About the only thing universally supported is
//! the first (C95), beyond that definitions quickly become absent on various
//! platforms.
//!
//! We therefore wind up dividing our module-space up (mostly for the sake of
//! sanity while editing, filling-in-details and eliminating duplication) into
//! definitions common-to-all (held in modules named c95, c99, posix88, posix01
//! and posix08) and definitions that appear only on *some* platforms (named
//! 'extra'). This would be things like significant OSX foundation kit, or Windows
//! library kernel32.dll, or various fancy glibc, Linux or BSD extensions.
//!
//! In addition to the per-platform 'extra' modules, we define a module of
//! 'common BSD' libc routines that never quite made it into POSIX but show up
//! in multiple derived systems. This is the 4.4BSD r2 / 1995 release, the final
//! one from Berkeley after the lawsuits died down and the CSRG dissolved.
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

View File

@ -8,17 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Sampling from random distributions.
This is a generalization of `Rand` to allow parameters to control the
exact properties of the generated values, e.g. the mean and standard
deviation of a normal distribution. The `Sample` trait is the most
general, and allows for generating values that change some state
internally. The `IndependentSample` trait is for generating values
that do not need to record state.
*/
//! Sampling from random distributions.
//!
//! This is a generalization of `Rand` to allow parameters to control the
//! exact properties of the generated values, e.g. the mean and standard
//! deviation of a normal distribution. The `Sample` trait is the most
//! general, and allows for generating values that change some state
//! internally. The `IndependentSample` trait is for generating values
//! that do not need to record state.
#![experimental]

View File

@ -8,15 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
The Rust compiler.
# Note
This API is completely unstable and subject to change.
*/
//! The Rust compiler.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.
#![crate_name = "rustc"]
#![experimental]

View File

@ -196,53 +196,38 @@ fn reserve_id_range(sess: &Session,
}
impl<'a, 'b, 'tcx> DecodeContext<'a, 'b, 'tcx> {
/// Translates an internal id, meaning a node id that is known to refer to some part of the
/// item currently being inlined, such as a local variable or argument. All naked node-ids
/// that appear in types have this property, since if something might refer to an external item
/// we would use a def-id to allow for the possibility that the item resides in another crate.
pub fn tr_id(&self, id: ast::NodeId) -> ast::NodeId {
/*!
* Translates an internal id, meaning a node id that is known
* to refer to some part of the item currently being inlined,
* such as a local variable or argument. All naked node-ids
* that appear in types have this property, since if something
* might refer to an external item we would use a def-id to
* allow for the possibility that the item resides in another
* crate.
*/
// from_id_range should be non-empty
assert!(!self.from_id_range.empty());
(id - self.from_id_range.min + self.to_id_range.min)
}
/// Translates an EXTERNAL def-id, converting the crate number from the one used in the encoded
/// data to the current crate numbers.. By external, I mean that it be translated to a
/// reference to the item in its original crate, as opposed to being translated to a reference
/// to the inlined version of the item. This is typically, but not always, what you want,
/// because most def-ids refer to external things like types or other fns that may or may not
/// be inlined. Note that even when the inlined function is referencing itself recursively, we
/// would want `tr_def_id` for that reference--- conceptually the function calls the original,
/// non-inlined version, and trans deals with linking that recursive call to the inlined copy.
///
/// However, there are a *few* cases where def-ids are used but we know that the thing being
/// referenced is in fact *internal* to the item being inlined. In those cases, you should use
/// `tr_intern_def_id()` below.
pub fn tr_def_id(&self, did: ast::DefId) -> ast::DefId {
/*!
* Translates an EXTERNAL def-id, converting the crate number
* from the one used in the encoded data to the current crate
* numbers.. By external, I mean that it be translated to a
* reference to the item in its original crate, as opposed to
* being translated to a reference to the inlined version of
* the item. This is typically, but not always, what you
* want, because most def-ids refer to external things like
* types or other fns that may or may not be inlined. Note
* that even when the inlined function is referencing itself
* recursively, we would want `tr_def_id` for that
* reference--- conceptually the function calls the original,
* non-inlined version, and trans deals with linking that
* recursive call to the inlined copy.
*
* However, there are a *few* cases where def-ids are used but
* we know that the thing being referenced is in fact *internal*
* to the item being inlined. In those cases, you should use
* `tr_intern_def_id()` below.
*/
decoder::translate_def_id(self.cdata, did)
}
pub fn tr_intern_def_id(&self, did: ast::DefId) -> ast::DefId {
/*!
* Translates an INTERNAL def-id, meaning a def-id that is
* known to refer to some part of the item currently being
* inlined. In that case, we want to convert the def-id to
* refer to the current crate and to the new, inlined node-id.
*/
/// Translates an INTERNAL def-id, meaning a def-id that is
/// known to refer to some part of the item currently being
/// inlined. In that case, we want to convert the def-id to
/// refer to the current crate and to the new, inlined node-id.
pub fn tr_intern_def_id(&self, did: ast::DefId) -> ast::DefId {
assert_eq!(did.krate, ast::LOCAL_CRATE);
ast::DefId { krate: ast::LOCAL_CRATE, node: self.tr_id(did.node) }
}
@ -1780,43 +1765,40 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
}
}
/// Converts a def-id that appears in a type. The correct
/// translation will depend on what kind of def-id this is.
/// This is a subtle point: type definitions are not
/// inlined into the current crate, so if the def-id names
/// a nominal type or type alias, then it should be
/// translated to refer to the source crate.
///
/// However, *type parameters* are cloned along with the function
/// they are attached to. So we should translate those def-ids
/// to refer to the new, cloned copy of the type parameter.
/// We only see references to free type parameters in the body of
/// an inlined function. In such cases, we need the def-id to
/// be a local id so that the TypeContents code is able to lookup
/// the relevant info in the ty_param_defs table.
///
/// *Region parameters*, unfortunately, are another kettle of fish.
/// In such cases, def_id's can appear in types to distinguish
/// shadowed bound regions and so forth. It doesn't actually
/// matter so much what we do to these, since regions are erased
/// at trans time, but it's good to keep them consistent just in
/// case. We translate them with `tr_def_id()` which will map
/// the crate numbers back to the original source crate.
///
/// Unboxed closures are cloned along with the function being
/// inlined, and all side tables use interned node IDs, so we
/// translate their def IDs accordingly.
///
/// It'd be really nice to refactor the type repr to not include
/// def-ids so that all these distinctions were unnecessary.
fn convert_def_id(&mut self,
dcx: &DecodeContext,
source: tydecode::DefIdSource,
did: ast::DefId)
-> ast::DefId {
/*!
* Converts a def-id that appears in a type. The correct
* translation will depend on what kind of def-id this is.
* This is a subtle point: type definitions are not
* inlined into the current crate, so if the def-id names
* a nominal type or type alias, then it should be
* translated to refer to the source crate.
*
* However, *type parameters* are cloned along with the function
* they are attached to. So we should translate those def-ids
* to refer to the new, cloned copy of the type parameter.
* We only see references to free type parameters in the body of
* an inlined function. In such cases, we need the def-id to
* be a local id so that the TypeContents code is able to lookup
* the relevant info in the ty_param_defs table.
*
* *Region parameters*, unfortunately, are another kettle of fish.
* In such cases, def_id's can appear in types to distinguish
* shadowed bound regions and so forth. It doesn't actually
* matter so much what we do to these, since regions are erased
* at trans time, but it's good to keep them consistent just in
* case. We translate them with `tr_def_id()` which will map
* the crate numbers back to the original source crate.
*
* Unboxed closures are cloned along with the function being
* inlined, and all side tables use interned node IDs, so we
* translate their def IDs accordingly.
*
* It'd be really nice to refactor the type repr to not include
* def-ids so that all these distinctions were unnecessary.
*/
let r = match source {
NominalType | TypeWithId | RegionParameter => dcx.tr_def_id(did),
TypeParameter | UnboxedClosureSource => dcx.tr_intern_def_id(did)

View File

@ -684,16 +684,13 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
return ret;
}
/// Reports an error if `expr` (which should be a path)
/// is using a moved/uninitialized value
fn check_if_path_is_moved(&self,
id: ast::NodeId,
span: Span,
use_kind: MovedValueUseKind,
lp: &Rc<LoanPath<'tcx>>) {
/*!
* Reports an error if `expr` (which should be a path)
* is using a moved/uninitialized value
*/
debug!("check_if_path_is_moved(id={}, use_kind={}, lp={})",
id, use_kind, lp.repr(self.bccx.tcx));
let base_lp = owned_ptr_base_path_rc(lp);
@ -708,30 +705,29 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
});
}
/// Reports an error if assigning to `lp` will use a
/// moved/uninitialized value. Mainly this is concerned with
/// detecting derefs of uninitialized pointers.
///
/// For example:
///
/// ```
/// let a: int;
/// a = 10; // ok, even though a is uninitialized
///
/// struct Point { x: uint, y: uint }
/// let p: Point;
/// p.x = 22; // ok, even though `p` is uninitialized
///
/// let p: ~Point;
/// (*p).x = 22; // not ok, p is uninitialized, can't deref
/// ```
fn check_if_assigned_path_is_moved(&self,
id: ast::NodeId,
span: Span,
use_kind: MovedValueUseKind,
lp: &Rc<LoanPath<'tcx>>)
{
/*!
* Reports an error if assigning to `lp` will use a
* moved/uninitialized value. Mainly this is concerned with
* detecting derefs of uninitialized pointers.
*
* For example:
*
* let a: int;
* a = 10; // ok, even though a is uninitialized
*
* struct Point { x: uint, y: uint }
* let p: Point;
* p.x = 22; // ok, even though `p` is uninitialized
*
* let p: ~Point;
* (*p).x = 22; // not ok, p is uninitialized, can't deref
*/
match lp.kind {
LpVar(_) | LpUpvar(_) => {
// assigning to `x` does not require that `x` is initialized

File diff suppressed because it is too large Load Diff

View File

@ -8,13 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
//! Helper routines used for fragmenting structural paths due to moves for
//! tracking drop obligations. Please see the extensive comments in the
//! section "Structural fragments" in `doc.rs`.
Helper routines used for fragmenting structural paths due to moves for
tracking drop obligations. Please see the extensive comments in the
section "Structural fragments" in `doc.rs`.
*/
use self::Fragment::*;
use session::config;
@ -176,16 +173,12 @@ pub fn instrument_move_fragments<'tcx>(this: &MoveData<'tcx>,
instrument_all_paths("assigned_leaf_path", &fragments.assigned_leaf_paths);
}
/// Normalizes the fragment sets in `this`; i.e., removes duplicate entries, constructs the set of
/// parents, and constructs the left-over fragments.
///
/// Note: "left-over fragments" means paths that were not directly referenced in moves nor
/// assignments, but must nonetheless be tracked as potential drop obligations.
pub fn fixup_fragment_sets<'tcx>(this: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) {
/*!
* Normalizes the fragment sets in `this`; i.e., removes
* duplicate entries, constructs the set of parents, and
* constructs the left-over fragments.
*
* Note: "left-over fragments" means paths that were not
* directly referenced in moves nor assignments, but must
* nonetheless be tracked as potential drop obligations.
*/
let mut fragments = this.fragments.borrow_mut();
@ -283,18 +276,14 @@ pub fn fixup_fragment_sets<'tcx>(this: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) {
}
}
/// Adds all of the precisely-tracked siblings of `lp` as potential move paths of interest. For
/// example, if `lp` represents `s.x.j`, then adds moves paths for `s.x.i` and `s.x.k`, the
/// siblings of `s.x.j`.
fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
tcx: &ty::ctxt<'tcx>,
gathered_fragments: &mut Vec<Fragment>,
lp: Rc<LoanPath<'tcx>>,
origin_id: Option<ast::NodeId>) {
/*!
* Adds all of the precisely-tracked siblings of `lp` as
* potential move paths of interest. For example, if `lp`
* represents `s.x.j`, then adds moves paths for `s.x.i` and
* `s.x.k`, the siblings of `s.x.j`.
*/
match lp.kind {
LpVar(_) | LpUpvar(..) => {} // Local variables have no siblings.
@ -343,6 +332,8 @@ fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
}
}
/// We have determined that `origin_lp` destructures to LpExtend(parent, original_field_name).
/// Based on this, add move paths for all of the siblings of `origin_lp`.
fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>,
tcx: &ty::ctxt<'tcx>,
gathered_fragments: &mut Vec<Fragment>,
@ -353,12 +344,6 @@ fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>,
origin_id: Option<ast::NodeId>,
enum_variant_info: Option<(ast::DefId,
Rc<LoanPath<'tcx>>)>) {
/*!
* We have determined that `origin_lp` destructures to
* LpExtend(parent, original_field_name). Based on this,
* add move paths for all of the siblings of `origin_lp`.
*/
let parent_ty = parent_lp.to_type();
let add_fragment_sibling_local = |field_name| {
@ -454,6 +439,8 @@ fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>,
}
}
/// Adds the single sibling `LpExtend(parent, new_field_name)` of `origin_lp` (the original
/// loan-path).
fn add_fragment_sibling_core<'tcx>(this: &MoveData<'tcx>,
tcx: &ty::ctxt<'tcx>,
gathered_fragments: &mut Vec<Fragment>,
@ -461,10 +448,6 @@ fn add_fragment_sibling_core<'tcx>(this: &MoveData<'tcx>,
mc: mc::MutabilityCategory,
new_field_name: mc::FieldName,
origin_lp: &Rc<LoanPath<'tcx>>) -> MovePathIndex {
/*!
* Adds the single sibling `LpExtend(parent, new_field_name)`
* of `origin_lp` (the original loan-path).
*/
let opt_variant_did = match parent.kind {
LpDowncast(_, variant_did) => Some(variant_did),
LpVar(..) | LpUpvar(..) | LpExtend(..) => None,

View File

@ -8,9 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Computes moves.
*/
//! Computes moves.
use middle::borrowck::*;
use middle::borrowck::LoanPathKind::*;

View File

@ -8,10 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* This module implements the check that the lifetime of a borrow
* does not exceed the lifetime of the value being borrowed.
*/
//! This module implements the check that the lifetime of a borrow
//! does not exceed the lifetime of the value being borrowed.
use middle::borrowck::*;
use middle::expr_use_visitor as euv;

View File

@ -225,6 +225,9 @@ fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
/// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
/// reports an error. This may entail taking out loans, which will be added to the
/// `req_loan_map`.
fn guarantee_valid(&mut self,
borrow_id: ast::NodeId,
borrow_span: Span,
@ -232,12 +235,6 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
req_kind: ty::BorrowKind,
loan_region: ty::Region,
cause: euv::LoanCause) {
/*!
* Guarantees that `addr_of(cmt)` will be valid for the duration of
* `static_scope_r`, or reports an error. This may entail taking
* out loans, which will be added to the `req_loan_map`.
*/
debug!("guarantee_valid(borrow_id={}, cmt={}, \
req_mutbl={}, loan_region={})",
borrow_id,

View File

@ -8,9 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Computes the restrictions that result from a borrow.
*/
//! Computes the restrictions that result from a borrow.
pub use self::RestrictionResult::*;

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! See doc.rs for a thorough explanation of the borrow checker */
//! See doc.rs for a thorough explanation of the borrow checker
#![allow(non_camel_case_types)]

View File

@ -8,12 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Data structures used for tracking moves. Please see the extensive
comments in the section "Moves and initialization" in `doc.rs`.
*/
//! Data structures used for tracking moves. Please see the extensive
//! comments in the section "Moves and initialization" in `doc.rs`.
pub use self::MoveKind::*;
@ -297,15 +293,11 @@ impl<'tcx> MoveData<'tcx> {
self.path_parent(index) == InvalidMovePathIndex
}
/// Returns the existing move path index for `lp`, if any, and otherwise adds a new index for
/// `lp` and any of its base paths that do not yet have an index.
pub fn move_path(&self,
tcx: &ty::ctxt<'tcx>,
lp: Rc<LoanPath<'tcx>>) -> MovePathIndex {
/*!
* Returns the existing move path index for `lp`, if any,
* and otherwise adds a new index for `lp` and any of its
* base paths that do not yet have an index.
*/
match self.path_map.borrow().get(&lp) {
Some(&index) => {
return index;
@ -370,13 +362,10 @@ impl<'tcx> MoveData<'tcx> {
result
}
/// Adds any existing move path indices for `lp` and any base paths of `lp` to `result`, but
/// does not add new move paths
fn add_existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>,
result: &mut Vec<MovePathIndex>) {
/*!
* Adds any existing move path indices for `lp` and any base
* paths of `lp` to `result`, but does not add new move paths
*/
match self.path_map.borrow().get(lp).cloned() {
Some(index) => {
self.each_base_path(index, |p| {
@ -397,16 +386,12 @@ impl<'tcx> MoveData<'tcx> {
}
/// Adds a new move entry for a move of `lp` that occurs at location `id` with kind `kind`.
pub fn add_move(&self,
tcx: &ty::ctxt<'tcx>,
lp: Rc<LoanPath<'tcx>>,
id: ast::NodeId,
kind: MoveKind) {
/*!
* Adds a new move entry for a move of `lp` that occurs at
* location `id` with kind `kind`.
*/
debug!("add_move(lp={}, id={}, kind={})",
lp.repr(tcx),
id,
@ -428,6 +413,8 @@ impl<'tcx> MoveData<'tcx> {
});
}
/// Adds a new record for an assignment to `lp` that occurs at location `id` with the given
/// `span`.
pub fn add_assignment(&self,
tcx: &ty::ctxt<'tcx>,
lp: Rc<LoanPath<'tcx>>,
@ -435,11 +422,6 @@ impl<'tcx> MoveData<'tcx> {
span: Span,
assignee_id: ast::NodeId,
mode: euv::MutateMode) {
/*!
* Adds a new record for an assignment to `lp` that occurs at
* location `id` with the given `span`.
*/
debug!("add_assignment(lp={}, assign_id={}, assignee_id={}",
lp.repr(tcx), assign_id, assignee_id);
@ -473,18 +455,16 @@ impl<'tcx> MoveData<'tcx> {
}
}
/// Adds a new record for a match of `base_lp`, downcast to
/// variant `lp`, that occurs at location `pattern_id`. (One
/// should be able to recover the span info from the
/// `pattern_id` and the ast_map, I think.)
pub fn add_variant_match(&self,
tcx: &ty::ctxt<'tcx>,
lp: Rc<LoanPath<'tcx>>,
pattern_id: ast::NodeId,
base_lp: Rc<LoanPath<'tcx>>,
mode: euv::MatchMode) {
/*!
* Adds a new record for a match of `base_lp`, downcast to
* variant `lp`, that occurs at location `pattern_id`. (One
* should be able to recover the span info from the
* `pattern_id` and the ast_map, I think.)
*/
debug!("add_variant_match(lp={}, pattern_id={})",
lp.repr(tcx), pattern_id);
@ -507,18 +487,15 @@ impl<'tcx> MoveData<'tcx> {
fragments::fixup_fragment_sets(self, tcx)
}
/// Adds the gen/kills for the various moves and
/// assignments into the provided data flow contexts.
/// Moves are generated by moves and killed by assignments and
/// scoping. Assignments are generated by assignment to variables and
/// killed by scoping. See `doc.rs` for more details.
fn add_gen_kills(&self,
tcx: &ty::ctxt<'tcx>,
dfcx_moves: &mut MoveDataFlow,
dfcx_assign: &mut AssignDataFlow) {
/*!
* Adds the gen/kills for the various moves and
* assignments into the provided data flow contexts.
* Moves are generated by moves and killed by assignments and
* scoping. Assignments are generated by assignment to variables and
* killed by scoping. See `doc.rs` for more details.
*/
for (i, the_move) in self.moves.borrow().iter().enumerate() {
dfcx_moves.add_gen(the_move.id, i);
}
@ -695,18 +672,14 @@ impl<'a, 'tcx> FlowedMoveData<'a, 'tcx> {
ret
}
/// Iterates through each move of `loan_path` (or some base path of `loan_path`) that *may*
/// have occurred on entry to `id` without an intervening assignment. In other words, any moves
/// that would invalidate a reference to `loan_path` at location `id`.
pub fn each_move_of(&self,
id: ast::NodeId,
loan_path: &Rc<LoanPath<'tcx>>,
f: |&Move, &LoanPath<'tcx>| -> bool)
-> bool {
/*!
* Iterates through each move of `loan_path` (or some base path
* of `loan_path`) that *may* have occurred on entry to `id` without
* an intervening assignment. In other words, any moves that
* would invalidate a reference to `loan_path` at location `id`.
*/
// Bad scenarios:
//
// 1. Move of `a.b.c`, use of `a.b.c`
@ -755,17 +728,13 @@ impl<'a, 'tcx> FlowedMoveData<'a, 'tcx> {
})
}
/// Iterates through every assignment to `loan_path` that may have occurred on entry to `id`.
/// `loan_path` must be a single variable.
pub fn each_assignment_of(&self,
id: ast::NodeId,
loan_path: &Rc<LoanPath<'tcx>>,
f: |&Assignment| -> bool)
-> bool {
/*!
* Iterates through every assignment to `loan_path` that
* may have occurred on entry to `id`. `loan_path` must be
* a single variable.
*/
let loan_path_index = {
match self.move_data.existing_move_path(loan_path) {
Some(i) => i,

View File

@ -8,12 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Module that constructs a control-flow graph representing an item.
Uses `Graph` as the underlying representation.
*/
//! Module that constructs a control-flow graph representing an item.
//! Uses `Graph` as the underlying representation.
use middle::graph;
use middle::ty;

View File

@ -9,12 +9,10 @@
// except according to those terms.
/*!
* A module for propagating forward dataflow information. The analysis
* assumes that the items to be propagated can be represented as bits
* and thus uses bitvectors. Your job is simply to specify the so-called
* GEN and KILL bits for each expression.
*/
//! A module for propagating forward dataflow information. The analysis
//! assumes that the items to be propagated can be represented as bits
//! and thus uses bitvectors. Your job is simply to specify the so-called
//! GEN and KILL bits for each expression.
pub use self::EntryOrExit::*;

View File

@ -8,11 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* A different sort of visitor for walking fn bodies. Unlike the
* normal visitor, which just walks the entire body in one shot, the
* `ExprUseVisitor` determines how expressions are being used.
*/
//! A different sort of visitor for walking fn bodies. Unlike the
//! normal visitor, which just walks the entire body in one shot, the
//! `ExprUseVisitor` determines how expressions are being used.
pub use self::MutateMode::*;
pub use self::LoanCause::*;
@ -716,12 +714,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
}
}
/// Indicates that the value of `blk` will be consumed, meaning either copied or moved
/// depending on its type.
fn walk_block(&mut self, blk: &ast::Block) {
/*!
* Indicates that the value of `blk` will be consumed,
* meaning either copied or moved depending on its type.
*/
debug!("walk_block(blk.id={})", blk.id);
for stmt in blk.stmts.iter() {
@ -821,16 +816,12 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
}
}
/// Autoderefs for overloaded Deref calls in fact reference their receiver. That is, if we have
/// `(*x)` where `x` is of type `Rc<T>`, then this in fact is equivalent to `x.deref()`. Since
/// `deref()` is declared with `&self`, this is an autoref of `x`.
fn walk_autoderefs(&mut self,
expr: &ast::Expr,
autoderefs: uint) {
/*!
* Autoderefs for overloaded Deref calls in fact reference
* their receiver. That is, if we have `(*x)` where `x` is of
* type `Rc<T>`, then this in fact is equivalent to
* `x.deref()`. Since `deref()` is declared with `&self`, this
* is an autoref of `x`.
*/
debug!("walk_autoderefs expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs);
for i in range(0, autoderefs) {

View File

@ -33,26 +33,20 @@ pub enum SimplifiedType {
ParameterSimplifiedType,
}
/// Tries to simplify a type by dropping type parameters, deref'ing away any reference types, etc.
/// The idea is to get something simple that we can use to quickly decide if two types could unify
/// during method lookup.
///
/// If `can_simplify_params` is false, then we will fail to simplify type parameters entirely. This
/// is useful when those type parameters would be instantiated with fresh type variables, since
/// then we can't say much about whether two types would unify. Put another way,
/// `can_simplify_params` should be true if type parameters appear free in `ty` and `false` if they
/// are to be considered bound.
pub fn simplify_type(tcx: &ty::ctxt,
ty: Ty,
can_simplify_params: bool)
-> Option<SimplifiedType>
{
/*!
* Tries to simplify a type by dropping type parameters, deref'ing
* away any reference types, etc. The idea is to get something
* simple that we can use to quickly decide if two types could
* unify during method lookup.
*
* If `can_simplify_params` is false, then we will fail to
* simplify type parameters entirely. This is useful when those
* type parameters would be instantiated with fresh type
* variables, since then we can't say much about whether two types
* would unify. Put another way, `can_simplify_params` should be
* true if type parameters appear free in `ty` and `false` if they
* are to be considered bound.
*/
match ty.sty {
ty::ty_bool => Some(BoolSimplifiedType),
ty::ty_char => Some(CharSimplifiedType),

View File

@ -8,31 +8,27 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
A graph module for use in dataflow, region resolution, and elsewhere.
# Interface details
You customize the graph by specifying a "node data" type `N` and an
"edge data" type `E`. You can then later gain access (mutable or
immutable) to these "user-data" bits. Currently, you can only add
nodes or edges to the graph. You cannot remove or modify them once
added. This could be changed if we have a need.
# Implementation details
The main tricky thing about this code is the way that edges are
stored. The edges are stored in a central array, but they are also
threaded onto two linked lists for each node, one for incoming edges
and one for outgoing edges. Note that every edge is a member of some
incoming list and some outgoing list. Basically you can load the
first index of the linked list from the node data structures (the
field `first_edge`) and then, for each edge, load the next index from
the field `next_edge`). Each of those fields is an array that should
be indexed by the direction (see the type `Direction`).
*/
//! A graph module for use in dataflow, region resolution, and elsewhere.
//!
//! # Interface details
//!
//! You customize the graph by specifying a "node data" type `N` and an
//! "edge data" type `E`. You can then later gain access (mutable or
//! immutable) to these "user-data" bits. Currently, you can only add
//! nodes or edges to the graph. You cannot remove or modify them once
//! added. This could be changed if we have a need.
//!
//! # Implementation details
//!
//! The main tricky thing about this code is the way that edges are
//! stored. The edges are stored in a central array, but they are also
//! threaded onto two linked lists for each node, one for incoming edges
//! and one for outgoing edges. Note that every edge is a member of some
//! incoming list and some outgoing list. Basically you can load the
//! first index of the linked list from the node data structures (the
//! field `first_edge`) and then, for each edge, load the next index from
//! the field `next_edge`). Each of those fields is an array that should
//! be indexed by the direction (see the type `Direction`).
#![allow(dead_code)] // still WIP

View File

@ -8,105 +8,103 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* A classic liveness analysis based on dataflow over the AST. Computes,
* for each local variable in a function, whether that variable is live
* at a given point. Program execution points are identified by their
* id.
*
* # Basic idea
*
* The basic model is that each local variable is assigned an index. We
* represent sets of local variables using a vector indexed by this
* index. The value in the vector is either 0, indicating the variable
* is dead, or the id of an expression that uses the variable.
*
* We conceptually walk over the AST in reverse execution order. If we
* find a use of a variable, we add it to the set of live variables. If
* we find an assignment to a variable, we remove it from the set of live
* variables. When we have to merge two flows, we take the union of
* those two flows---if the variable is live on both paths, we simply
* pick one id. In the event of loops, we continue doing this until a
* fixed point is reached.
*
* ## Checking initialization
*
* At the function entry point, all variables must be dead. If this is
* not the case, we can report an error using the id found in the set of
* live variables, which identifies a use of the variable which is not
* dominated by an assignment.
*
* ## Checking moves
*
* After each explicit move, the variable must be dead.
*
* ## Computing last uses
*
* Any use of the variable where the variable is dead afterwards is a
* last use.
*
* # Implementation details
*
* The actual implementation contains two (nested) walks over the AST.
* The outer walk has the job of building up the ir_maps instance for the
* enclosing function. On the way down the tree, it identifies those AST
* nodes and variable IDs that will be needed for the liveness analysis
* and assigns them contiguous IDs. The liveness id for an AST node is
* called a `live_node` (it's a newtype'd uint) and the id for a variable
* is called a `variable` (another newtype'd uint).
*
* On the way back up the tree, as we are about to exit from a function
* declaration we allocate a `liveness` instance. Now that we know
* precisely how many nodes and variables we need, we can allocate all
* the various arrays that we will need to precisely the right size. We then
* perform the actual propagation on the `liveness` instance.
*
* This propagation is encoded in the various `propagate_through_*()`
* methods. It effectively does a reverse walk of the AST; whenever we
* reach a loop node, we iterate until a fixed point is reached.
*
* ## The `Users` struct
*
* At each live node `N`, we track three pieces of information for each
* variable `V` (these are encapsulated in the `Users` struct):
*
* - `reader`: the `LiveNode` ID of some node which will read the value
* that `V` holds on entry to `N`. Formally: a node `M` such
* that there exists a path `P` from `N` to `M` where `P` does not
* write `V`. If the `reader` is `invalid_node()`, then the current
* value will never be read (the variable is dead, essentially).
*
* - `writer`: the `LiveNode` ID of some node which will write the
* variable `V` and which is reachable from `N`. Formally: a node `M`
* such that there exists a path `P` from `N` to `M` and `M` writes
* `V`. If the `writer` is `invalid_node()`, then there is no writer
* of `V` that follows `N`.
*
* - `used`: a boolean value indicating whether `V` is *used*. We
* distinguish a *read* from a *use* in that a *use* is some read that
* is not just used to generate a new value. For example, `x += 1` is
* a read but not a use. This is used to generate better warnings.
*
* ## Special Variables
*
* We generate various special variables for various, well, special purposes.
* These are described in the `specials` struct:
*
* - `exit_ln`: a live node that is generated to represent every 'exit' from
* the function, whether it be by explicit return, panic, or other means.
*
* - `fallthrough_ln`: a live node that represents a fallthrough
*
* - `no_ret_var`: a synthetic variable that is only 'read' from, the
* fallthrough node. This allows us to detect functions where we fail
* to return explicitly.
* - `clean_exit_var`: a synthetic variable that is only 'read' from the
* fallthrough node. It is only live if the function could converge
* via means other than an explicit `return` expression. That is, it is
* only dead if the end of the function's block can never be reached.
* It is the responsibility of typeck to ensure that there are no
* `return` expressions in a function declared as diverging.
*/
//! A classic liveness analysis based on dataflow over the AST. Computes,
//! for each local variable in a function, whether that variable is live
//! at a given point. Program execution points are identified by their
//! id.
//!
//! # Basic idea
//!
//! The basic model is that each local variable is assigned an index. We
//! represent sets of local variables using a vector indexed by this
//! index. The value in the vector is either 0, indicating the variable
//! is dead, or the id of an expression that uses the variable.
//!
//! We conceptually walk over the AST in reverse execution order. If we
//! find a use of a variable, we add it to the set of live variables. If
//! we find an assignment to a variable, we remove it from the set of live
//! variables. When we have to merge two flows, we take the union of
//! those two flows---if the variable is live on both paths, we simply
//! pick one id. In the event of loops, we continue doing this until a
//! fixed point is reached.
//!
//! ## Checking initialization
//!
//! At the function entry point, all variables must be dead. If this is
//! not the case, we can report an error using the id found in the set of
//! live variables, which identifies a use of the variable which is not
//! dominated by an assignment.
//!
//! ## Checking moves
//!
//! After each explicit move, the variable must be dead.
//!
//! ## Computing last uses
//!
//! Any use of the variable where the variable is dead afterwards is a
//! last use.
//!
//! # Implementation details
//!
//! The actual implementation contains two (nested) walks over the AST.
//! The outer walk has the job of building up the ir_maps instance for the
//! enclosing function. On the way down the tree, it identifies those AST
//! nodes and variable IDs that will be needed for the liveness analysis
//! and assigns them contiguous IDs. The liveness id for an AST node is
//! called a `live_node` (it's a newtype'd uint) and the id for a variable
//! is called a `variable` (another newtype'd uint).
//!
//! On the way back up the tree, as we are about to exit from a function
//! declaration we allocate a `liveness` instance. Now that we know
//! precisely how many nodes and variables we need, we can allocate all
//! the various arrays that we will need to precisely the right size. We then
//! perform the actual propagation on the `liveness` instance.
//!
//! This propagation is encoded in the various `propagate_through_*()`
//! methods. It effectively does a reverse walk of the AST; whenever we
//! reach a loop node, we iterate until a fixed point is reached.
//!
//! ## The `Users` struct
//!
//! At each live node `N`, we track three pieces of information for each
//! variable `V` (these are encapsulated in the `Users` struct):
//!
//! - `reader`: the `LiveNode` ID of some node which will read the value
//! that `V` holds on entry to `N`. Formally: a node `M` such
//! that there exists a path `P` from `N` to `M` where `P` does not
//! write `V`. If the `reader` is `invalid_node()`, then the current
//! value will never be read (the variable is dead, essentially).
//!
//! - `writer`: the `LiveNode` ID of some node which will write the
//! variable `V` and which is reachable from `N`. Formally: a node `M`
//! such that there exists a path `P` from `N` to `M` and `M` writes
//! `V`. If the `writer` is `invalid_node()`, then there is no writer
//! of `V` that follows `N`.
//!
//! - `used`: a boolean value indicating whether `V` is *used*. We
//! distinguish a *read* from a *use* in that a *use* is some read that
//! is not just used to generate a new value. For example, `x += 1` is
//! a read but not a use. This is used to generate better warnings.
//!
//! ## Special Variables
//!
//! We generate various special variables for various, well, special purposes.
//! These are described in the `specials` struct:
//!
//! - `exit_ln`: a live node that is generated to represent every 'exit' from
//! the function, whether it be by explicit return, panic, or other means.
//!
//! - `fallthrough_ln`: a live node that represents a fallthrough
//!
//! - `no_ret_var`: a synthetic variable that is only 'read' from, the
//! fallthrough node. This allows us to detect functions where we fail
//! to return explicitly.
//! - `clean_exit_var`: a synthetic variable that is only 'read' from the
//! fallthrough node. It is only live if the function could converge
//! via means other than an explicit `return` expression. That is, it is
//! only dead if the end of the function's block can never be reached.
//! It is the responsibility of typeck to ensure that there are no
//! `return` expressions in a function declared as diverging.
use self::LoopKind::*;
use self::LiveNodeKind::*;
use self::VarKind::*;

View File

@ -8,57 +8,55 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* # Categorization
*
* The job of the categorization module is to analyze an expression to
* determine what kind of memory is used in evaluating it (for example,
* where dereferences occur and what kind of pointer is dereferenced;
* whether the memory is mutable; etc)
*
* Categorization effectively transforms all of our expressions into
* expressions of the following forms (the actual enum has many more
* possibilities, naturally, but they are all variants of these base
* forms):
*
* E = rvalue // some computed rvalue
* | x // address of a local variable or argument
* | *E // deref of a ptr
* | E.comp // access to an interior component
*
* Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
* address where the result is to be found. If Expr is an lvalue, then this
* is the address of the lvalue. If Expr is an rvalue, this is the address of
* some temporary spot in memory where the result is stored.
*
* Now, cat_expr() classifies the expression Expr and the address A=ToAddr(Expr)
* as follows:
*
* - cat: what kind of expression was this? This is a subset of the
* full expression forms which only includes those that we care about
* for the purpose of the analysis.
* - mutbl: mutability of the address A
* - ty: the type of data found at the address A
*
* The resulting categorization tree differs somewhat from the expressions
* themselves. For example, auto-derefs are explicit. Also, an index a[b] is
* decomposed into two operations: a dereference to reach the array data and
* then an index to jump forward to the relevant item.
*
* ## By-reference upvars
*
* One part of the translation which may be non-obvious is that we translate
* closure upvars into the dereference of a borrowed pointer; this more closely
* resembles the runtime translation. So, for example, if we had:
*
* let mut x = 3;
* let y = 5;
* let inc = || x += y;
*
* Then when we categorize `x` (*within* the closure) we would yield a
* result of `*x'`, effectively, where `x'` is a `cat_upvar` reference
* tied to `x`. The type of `x'` will be a borrowed pointer.
*/
//! # Categorization
//!
//! The job of the categorization module is to analyze an expression to
//! determine what kind of memory is used in evaluating it (for example,
//! where dereferences occur and what kind of pointer is dereferenced;
//! whether the memory is mutable; etc)
//!
//! Categorization effectively transforms all of our expressions into
//! expressions of the following forms (the actual enum has many more
//! possibilities, naturally, but they are all variants of these base
//! forms):
//!
//! E = rvalue // some computed rvalue
//! | x // address of a local variable or argument
//! | *E // deref of a ptr
//! | E.comp // access to an interior component
//!
//! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
//! address where the result is to be found. If Expr is an lvalue, then this
//! is the address of the lvalue. If Expr is an rvalue, this is the address of
//! some temporary spot in memory where the result is stored.
//!
//! Now, cat_expr() classifies the expression Expr and the address A=ToAddr(Expr)
//! as follows:
//!
//! - cat: what kind of expression was this? This is a subset of the
//! full expression forms which only includes those that we care about
//! for the purpose of the analysis.
//! - mutbl: mutability of the address A
//! - ty: the type of data found at the address A
//!
//! The resulting categorization tree differs somewhat from the expressions
//! themselves. For example, auto-derefs are explicit. Also, an index a[b] is
//! decomposed into two operations: a dereference to reach the array data and
//! then an index to jump forward to the relevant item.
//!
//! ## By-reference upvars
//!
//! One part of the translation which may be non-obvious is that we translate
//! closure upvars into the dereference of a borrowed pointer; this more closely
//! resembles the runtime translation. So, for example, if we had:
//!
//! let mut x = 3;
//! let y = 5;
//! let inc = || x += y;
//!
//! Then when we categorize `x` (*within* the closure) we would yield a
//! result of `*x'`, effectively, where `x'` is a `cat_upvar` reference
//! tied to `x`. The type of `x'` will be a borrowed pointer.
#![allow(non_camel_case_types)]
@ -1056,20 +1054,17 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
}
}
/// Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is the cmt for `P`, `slice_pat` is
/// the pattern `Q`, returns:
///
/// * a cmt for `Q`
/// * the mutability and region of the slice `Q`
///
/// These last two bits of info happen to be things that borrowck needs.
pub fn cat_slice_pattern(&self,
vec_cmt: cmt<'tcx>,
slice_pat: &ast::Pat)
-> McResult<(cmt<'tcx>, ast::Mutability, ty::Region)> {
/*!
* Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is
* the cmt for `P`, `slice_pat` is the pattern `Q`, returns:
* - a cmt for `Q`
* - the mutability and region of the slice `Q`
*
* These last two bits of info happen to be things that
* borrowck needs.
*/
let slice_ty = if_ok!(self.node_ty(slice_pat.id));
let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(),
slice_pat,
@ -1077,17 +1072,13 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
let cmt_slice = self.cat_index(slice_pat, self.deref_vec(slice_pat, vec_cmt));
return Ok((cmt_slice, slice_mutbl, slice_r));
/// In a pattern like [a, b, ..c], normally `c` has slice type, but if you have [a, b,
/// ..ref c], then the type of `ref c` will be `&&[]`, so to extract the slice details we
/// have to recurse through rptrs.
fn vec_slice_info(tcx: &ty::ctxt,
pat: &ast::Pat,
slice_ty: Ty)
-> (ast::Mutability, ty::Region) {
/*!
* In a pattern like [a, b, ..c], normally `c` has slice type,
* but if you have [a, b, ..ref c], then the type of `ref c`
* will be `&&[]`, so to extract the slice details we have
* to recurse through rptrs.
*/
match slice_ty.sty {
ty::ty_rptr(r, ref mt) => match mt.ty.sty {
ty::ty_vec(_, None) => (mt.mutbl, r),
@ -1426,13 +1417,9 @@ impl<'tcx> cmt_<'tcx> {
}
}
/// Returns `Some(_)` if this lvalue represents a freely aliasable pointer type.
pub fn freely_aliasable(&self, ctxt: &ty::ctxt<'tcx>)
-> Option<AliasableReason> {
/*!
* Returns `Some(_)` if this lvalue represents a freely aliasable
* pointer type.
*/
// Maybe non-obvious: copied upvars can only be considered
// non-aliasable in once closures, since any other kind can be
// aliased and eventually recused.

View File

@ -8,18 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
This file actually contains two passes related to regions. The first
pass builds up the `scope_map`, which describes the parent links in
the region hierarchy. The second pass infers which types must be
region parameterized.
Most of the documentation on regions can be found in
`middle/typeck/infer/region_inference.rs`
*/
//! This file actually contains two passes related to regions. The first
//! pass builds up the `scope_map`, which describes the parent links in
//! the region hierarchy. The second pass infers which types must be
//! region parameterized.
//!
//! Most of the documentation on regions can be found in
//! `middle/typeck/infer/region_inference.rs`
use session::Session;
use middle::ty::{mod, Ty, FreeRegion};
@ -169,14 +164,10 @@ impl RegionMaps {
self.rvalue_scopes.borrow_mut().insert(var, lifetime);
}
/// Records that a scope is a TERMINATING SCOPE. Whenever we create automatic temporaries --
/// e.g. by an expression like `a().f` -- they will be freed within the innermost terminating
/// scope.
pub fn mark_as_terminating_scope(&self, scope_id: CodeExtent) {
/*!
* Records that a scope is a TERMINATING SCOPE. Whenever we
* create automatic temporaries -- e.g. by an
* expression like `a().f` -- they will be freed within
* the innermost terminating scope.
*/
debug!("record_terminating_scope(scope_id={})", scope_id);
self.terminating_scopes.borrow_mut().insert(scope_id);
}
@ -195,10 +186,8 @@ impl RegionMaps {
}
}
/// Returns the lifetime of the local variable `var_id`
pub fn var_scope(&self, var_id: ast::NodeId) -> CodeExtent {
/*!
* Returns the lifetime of the local variable `var_id`
*/
match self.var_map.borrow().get(&var_id) {
Some(&r) => r,
None => { panic!("no enclosing scope for id {}", var_id); }
@ -255,15 +244,12 @@ impl RegionMaps {
self.is_subscope_of(scope2, scope1)
}
/// Returns true if `subscope` is equal to or is lexically nested inside `superscope` and false
/// otherwise.
pub fn is_subscope_of(&self,
subscope: CodeExtent,
superscope: CodeExtent)
-> bool {
/*!
* Returns true if `subscope` is equal to or is lexically
* nested inside `superscope` and false otherwise.
*/
let mut s = subscope;
while superscope != s {
match self.scope_map.borrow().get(&s) {
@ -283,27 +269,20 @@ impl RegionMaps {
return true;
}
/// Determines whether two free regions have a subregion relationship
/// by walking the graph encoded in `free_region_map`. Note that
/// it is possible that `sub != sup` and `sub <= sup` and `sup <= sub`
/// (that is, the user can give two different names to the same lifetime).
pub fn sub_free_region(&self, sub: FreeRegion, sup: FreeRegion) -> bool {
/*!
* Determines whether two free regions have a subregion relationship
* by walking the graph encoded in `free_region_map`. Note that
* it is possible that `sub != sup` and `sub <= sup` and `sup <= sub`
* (that is, the user can give two different names to the same lifetime).
*/
can_reach(&*self.free_region_map.borrow(), sub, sup)
}
/// Determines whether one region is a subregion of another. This is intended to run *after
/// inference* and sadly the logic is somewhat duplicated with the code in infer.rs.
pub fn is_subregion_of(&self,
sub_region: ty::Region,
super_region: ty::Region)
-> bool {
/*!
* Determines whether one region is a subregion of another. This is
* intended to run *after inference* and sadly the logic is somewhat
* duplicated with the code in infer.rs.
*/
debug!("is_subregion_of(sub_region={}, super_region={})",
sub_region, super_region);
@ -343,16 +322,12 @@ impl RegionMaps {
}
}
/// Finds the nearest common ancestor (if any) of two scopes. That is, finds the smallest
/// scope which is greater than or equal to both `scope_a` and `scope_b`.
pub fn nearest_common_ancestor(&self,
scope_a: CodeExtent,
scope_b: CodeExtent)
-> Option<CodeExtent> {
/*!
* Finds the nearest common ancestor (if any) of two scopes. That
* is, finds the smallest scope which is greater than or equal to
* both `scope_a` and `scope_b`.
*/
if scope_a == scope_b { return Some(scope_a); }
let a_ancestors = ancestors_of(self, scope_a);
@ -679,18 +654,15 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &ast::Local) {
visit::walk_local(visitor, local);
/// True if `pat` match the `P&` nonterminal:
///
/// P& = ref X
/// | StructName { ..., P&, ... }
/// | VariantName(..., P&, ...)
/// | [ ..., P&, ... ]
/// | ( ..., P&, ... )
/// | box P&
fn is_binding_pat(pat: &ast::Pat) -> bool {
/*!
* True if `pat` match the `P&` nonterminal:
*
* P& = ref X
* | StructName { ..., P&, ... }
* | VariantName(..., P&, ...)
* | [ ..., P&, ... ]
* | ( ..., P&, ... )
* | box P&
*/
match pat.node {
ast::PatIdent(ast::BindByRef(_), _, _) => true,
@ -717,35 +689,27 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &ast::Local) {
}
}
/// True if `ty` is a borrowed pointer type like `&int` or `&[...]`.
fn is_borrowed_ty(ty: &ast::Ty) -> bool {
/*!
* True if `ty` is a borrowed pointer type
* like `&int` or `&[...]`.
*/
match ty.node {
ast::TyRptr(..) => true,
_ => false
}
}
/// If `expr` matches the `E&` grammar, then records an extended rvalue scope as appropriate:
///
/// E& = & ET
/// | StructName { ..., f: E&, ... }
/// | [ ..., E&, ... ]
/// | ( ..., E&, ... )
/// | {...; E&}
/// | box E&
/// | E& as ...
/// | ( E& )
fn record_rvalue_scope_if_borrow_expr(visitor: &mut RegionResolutionVisitor,
expr: &ast::Expr,
blk_id: CodeExtent) {
/*!
* If `expr` matches the `E&` grammar, then records an extended
* rvalue scope as appropriate:
*
* E& = & ET
* | StructName { ..., f: E&, ... }
* | [ ..., E&, ... ]
* | ( ..., E&, ... )
* | {...; E&}
* | box E&
* | E& as ...
* | ( E& )
*/
match expr.node {
ast::ExprAddrOf(_, ref subexpr) => {
record_rvalue_scope_if_borrow_expr(visitor, &**subexpr, blk_id);
@ -785,29 +749,24 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &ast::Local) {
}
}
/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by
/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that
/// case, the "temporary lifetime" or `expr` is extended to be the block enclosing the `let`
/// statement.
///
/// More formally, if `expr` matches the grammar `ET`, record the rvalue scope of the matching
/// `<rvalue>` as `blk_id`:
///
/// ET = *ET
/// | ET[...]
/// | ET.f
/// | (ET)
/// | <rvalue>
///
/// Note: ET is intended to match "rvalues or lvalues based on rvalues".
fn record_rvalue_scope<'a>(visitor: &mut RegionResolutionVisitor,
expr: &'a ast::Expr,
blk_scope: CodeExtent) {
/*!
* Applied to an expression `expr` if `expr` -- or something
* owned or partially owned by `expr` -- is going to be
* indirectly referenced by a variable in a let statement. In
* that case, the "temporary lifetime" or `expr` is extended
* to be the block enclosing the `let` statement.
*
* More formally, if `expr` matches the grammar `ET`, record
* the rvalue scope of the matching `<rvalue>` as `blk_id`:
*
* ET = *ET
* | ET[...]
* | ET.f
* | (ET)
* | <rvalue>
*
* Note: ET is intended to match "rvalues or
* lvalues based on rvalues".
*/
let mut expr = expr;
loop {
// Note: give all the expressions matching `ET` with the

View File

@ -8,14 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Name resolution for lifetimes.
*
* Name resolution for lifetimes follows MUCH simpler rules than the
* full resolve. For example, lifetime names are never exported or
* used between functions, and they operate in a purely top-down
* way. Therefore we break lifetime name resolution into a separate pass.
*/
//! Name resolution for lifetimes.
//!
//! Name resolution for lifetimes follows MUCH simpler rules than the
//! full resolve. For example, lifetime names are never exported or
//! used between functions, and they operate in a purely top-down
//! way. Therefore we break lifetime name resolution into a separate pass.
pub use self::DefRegion::*;
use self::ScopeChain::*;
@ -247,34 +245,27 @@ impl<'a> LifetimeContext<'a> {
}
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
///
/// Handles visiting fns and methods. These are a bit complicated because we must distinguish
/// early- vs late-bound lifetime parameters. We do this by checking which lifetimes appear
/// within type bounds; those are early bound lifetimes, and the rest are late bound.
///
/// For example:
///
/// fn foo<'a,'b,'c,T:Trait<'b>>(...)
///
/// Here `'a` and `'c` are late bound but `'b` is early bound. Note that early- and late-bound
/// lifetimes may be interspersed together.
///
/// If early bound lifetimes are present, we separate them into their own list (and likewise
/// for late bound). They will be numbered sequentially, starting from the lowest index that is
/// already in scope (for a fn item, that will be 0, but for a method it might not be). Late
/// bound lifetimes are resolved by name and associated with a binder id (`binder_id`), so the
/// ordering is not important there.
fn visit_early_late(&mut self,
early_space: subst::ParamSpace,
generics: &ast::Generics,
walk: |&mut LifetimeContext|) {
/*!
* Handles visiting fns and methods. These are a bit
* complicated because we must distinguish early- vs late-bound
* lifetime parameters. We do this by checking which lifetimes
* appear within type bounds; those are early bound lifetimes,
* and the rest are late bound.
*
* For example:
*
* fn foo<'a,'b,'c,T:Trait<'b>>(...)
*
* Here `'a` and `'c` are late bound but `'b` is early
* bound. Note that early- and late-bound lifetimes may be
* interspersed together.
*
* If early bound lifetimes are present, we separate them into
* their own list (and likewise for late bound). They will be
* numbered sequentially, starting from the lowest index that
* is already in scope (for a fn item, that will be 0, but for
* a method it might not be). Late bound lifetimes are
* resolved by name and associated with a binder id (`binder_id`), so
* the ordering is not important there.
*/
let referenced_idents = early_bound_lifetime_names(generics);
debug!("visit_early_late: referenced_idents={}",
@ -472,13 +463,9 @@ pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> Vec<ast::Lifeti
.collect()
}
/// Given a set of generic declarations, returns a list of names containing all early bound
/// lifetime names for those generics. (In fact, this list may also contain other names.)
fn early_bound_lifetime_names(generics: &ast::Generics) -> Vec<ast::Name> {
/*!
* Given a set of generic declarations, returns a list of names
* containing all early bound lifetime names for those
* generics. (In fact, this list may also contain other names.)
*/
// Create two lists, dividing the lifetimes into early/late bound.
// Initially, all of them are considered late, but we will move
// things from late into early as we go if we find references to

View File

@ -128,26 +128,18 @@ pub fn self_ty(&self) -> Option<Ty<'tcx>> {
Substs { types: types, regions: ErasedRegions }
}
/// Since ErasedRegions are only to be used in trans, most of the compiler can use this method
/// to easily access the set of region substitutions.
pub fn regions<'a>(&'a self) -> &'a VecPerParamSpace<ty::Region> {
/*!
* Since ErasedRegions are only to be used in trans, most of
* the compiler can use this method to easily access the set
* of region substitutions.
*/
match self.regions {
ErasedRegions => panic!("Erased regions only expected in trans"),
NonerasedRegions(ref r) => r
}
}
/// Since ErasedRegions are only to be used in trans, most of the compiler can use this method
/// to easily access the set of region substitutions.
pub fn mut_regions<'a>(&'a mut self) -> &'a mut VecPerParamSpace<ty::Region> {
/*!
* Since ErasedRegions are only to be used in trans, most of
* the compiler can use this method to easily access the set
* of region substitutions.
*/
match self.regions {
ErasedRegions => panic!("Erased regions only expected in trans"),
NonerasedRegions(ref mut r) => r
@ -681,59 +673,49 @@ impl<'a,'tcx> SubstFolder<'a,'tcx> {
self.shift_regions_through_binders(ty)
}
/// It is sometimes necessary to adjust the debruijn indices during substitution. This occurs
/// when we are substituting a type with escaping regions into a context where we have passed
/// through region binders. That's quite a mouthful. Let's see an example:
///
/// ```
/// type Func<A> = fn(A);
/// type MetaFunc = for<'a> fn(Func<&'a int>)
/// ```
///
/// The type `MetaFunc`, when fully expanded, will be
///
/// for<'a> fn(fn(&'a int))
/// ^~ ^~ ^~~
/// | | |
/// | | DebruijnIndex of 2
/// Binders
///
/// Here the `'a` lifetime is bound in the outer function, but appears as an argument of the
/// inner one. Therefore, that appearance will have a DebruijnIndex of 2, because we must skip
/// over the inner binder (remember that we count Debruijn indices from 1). However, in the
/// definition of `MetaFunc`, the binder is not visible, so the type `&'a int` will have a
/// debruijn index of 1. It's only during the substitution that we can see we must increase the
/// depth by 1 to account for the binder that we passed through.
///
/// As a second example, consider this twist:
///
/// ```
/// type FuncTuple<A> = (A,fn(A));
/// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a int>)
/// ```
///
/// Here the final type will be:
///
/// for<'a> fn((&'a int, fn(&'a int)))
/// ^~~ ^~~
/// | |
/// DebruijnIndex of 1 |
/// DebruijnIndex of 2
///
/// As indicated in the diagram, here the same type `&'a int` is substituted once, but in the
/// first case we do not increase the Debruijn index and in the second case we do. The reason
/// is that only in the second case have we passed through a fn binder.
fn shift_regions_through_binders(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
/*!
* It is sometimes necessary to adjust the debruijn indices
* during substitution. This occurs when we are substituting a
* type with escaping regions into a context where we have
* passed through region binders. That's quite a
* mouthful. Let's see an example:
*
* ```
* type Func<A> = fn(A);
* type MetaFunc = for<'a> fn(Func<&'a int>)
* ```
*
* The type `MetaFunc`, when fully expanded, will be
*
* for<'a> fn(fn(&'a int))
* ^~ ^~ ^~~
* | | |
* | | DebruijnIndex of 2
* Binders
*
* Here the `'a` lifetime is bound in the outer function, but
* appears as an argument of the inner one. Therefore, that
* appearance will have a DebruijnIndex of 2, because we must
* skip over the inner binder (remember that we count Debruijn
* indices from 1). However, in the definition of `MetaFunc`,
* the binder is not visible, so the type `&'a int` will have
* a debruijn index of 1. It's only during the substitution
* that we can see we must increase the depth by 1 to account
* for the binder that we passed through.
*
* As a second example, consider this twist:
*
* ```
* type FuncTuple<A> = (A,fn(A));
* type MetaFuncTuple = for<'a> fn(FuncTuple<&'a int>)
* ```
*
* Here the final type will be:
*
* for<'a> fn((&'a int, fn(&'a int)))
* ^~~ ^~~
* | |
* DebruijnIndex of 1 |
* DebruijnIndex of 2
*
* As indicated in the diagram, here the same type `&'a int`
* is substituted once, but in the first case we do not
* increase the Debruijn index and in the second case we
* do. The reason is that only in the second case have we
* passed through a fn binder.
*/
debug!("shift_regions(ty={}, region_binders_passed={}, type_has_escaping_regions={})",
ty.repr(self.tcx()), self.region_binders_passed, ty::type_has_escaping_regions(ty));

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! See `doc.rs` for high-level documentation */
//! See `doc.rs` for high-level documentation
use super::SelectionContext;
use super::Obligation;

View File

@ -8,403 +8,399 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# TRAIT RESOLUTION
This document describes the general process and points out some non-obvious
things.
## Major concepts
Trait resolution is the process of pairing up an impl with each
reference to a trait. So, for example, if there is a generic function like:
fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> { ... }
and then a call to that function:
let v: Vec<int> = clone_slice([1, 2, 3].as_slice())
it is the job of trait resolution to figure out (in which case)
whether there exists an impl of `int : Clone`
Note that in some cases, like generic functions, we may not be able to
find a specific impl, but we can figure out that the caller must
provide an impl. To see what I mean, consider the body of `clone_slice`:
fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> {
let mut v = Vec::new();
for e in x.iter() {
v.push((*e).clone()); // (*)
}
}
The line marked `(*)` is only legal if `T` (the type of `*e`)
implements the `Clone` trait. Naturally, since we don't know what `T`
is, we can't find the specific impl; but based on the bound `T:Clone`,
we can say that there exists an impl which the caller must provide.
We use the term *obligation* to refer to a trait reference in need of
an impl.
## Overview
Trait resolution consists of three major parts:
- SELECTION: Deciding how to resolve a specific obligation. For
example, selection might decide that a specific obligation can be
resolved by employing an impl which matches the self type, or by
using a parameter bound. In the case of an impl, Selecting one
obligation can create *nested obligations* because of where clauses
on the impl itself. It may also require evaluating those nested
obligations to resolve ambiguities.
- FULFILLMENT: The fulfillment code is what tracks that obligations
are completely fulfilled. Basically it is a worklist of obligations
to be selected: once selection is successful, the obligation is
removed from the worklist and any nested obligations are enqueued.
- COHERENCE: The coherence checks are intended to ensure that there
are never overlapping impls, where two impls could be used with
equal precedence.
## Selection
Selection is the process of deciding whether an obligation can be
resolved and, if so, how it is to be resolved (via impl, where clause, etc).
The main interface is the `select()` function, which takes an obligation
and returns a `SelectionResult`. There are three possible outcomes:
- `Ok(Some(selection))` -- yes, the obligation can be resolved, and
`selection` indicates how. If the impl was resolved via an impl,
then `selection` may also indicate nested obligations that are required
by the impl.
- `Ok(None)` -- we are not yet sure whether the obligation can be
resolved or not. This happens most commonly when the obligation
contains unbound type variables.
- `Err(err)` -- the obligation definitely cannot be resolved due to a
type error, or because there are no impls that could possibly apply,
etc.
The basic algorithm for selection is broken into two big phases:
candidate assembly and confirmation.
### Candidate assembly
Searches for impls/where-clauses/etc that might
possibly be used to satisfy the obligation. Each of those is called
a candidate. To avoid ambiguity, we want to find exactly one
candidate that is definitively applicable. In some cases, we may not
know whether an impl/where-clause applies or not -- this occurs when
the obligation contains unbound inference variables.
The basic idea for candidate assembly is to do a first pass in which
we identify all possible candidates. During this pass, all that we do
is try and unify the type parameters. (In particular, we ignore any
nested where clauses.) Presuming that this unification succeeds, the
impl is added as a candidate.
Once this first pass is done, we can examine the set of candidates. If
it is a singleton set, then we are done: this is the only impl in
scope that could possibly apply. Otherwise, we can winnow down the set
of candidates by using where clauses and other conditions. If this
reduced set yields a single, unambiguous entry, we're good to go,
otherwise the result is considered ambiguous.
#### The basic process: Inferring based on the impls we see
This process is easier if we work through some examples. Consider
the following trait:
```
trait Convert<Target> {
fn convert(&self) -> Target;
}
```
This trait just has one method. It's about as simple as it gets. It
converts from the (implicit) `Self` type to the `Target` type. If we
wanted to permit conversion between `int` and `uint`, we might
implement `Convert` like so:
```rust
impl Convert<uint> for int { ... } // int -> uint
impl Convert<int> for uint { ... } // uint -> uint
```
Now imagine there is some code like the following:
```rust
let x: int = ...;
let y = x.convert();
```
The call to convert will generate a trait reference `Convert<$Y> for
int`, where `$Y` is the type variable representing the type of
`y`. When we match this against the two impls we can see, we will find
that only one remains: `Convert<uint> for int`. Therefore, we can
select this impl, which will cause the type of `$Y` to be unified to
`uint`. (Note that while assembling candidates, we do the initial
unifications in a transaction, so that they don't affect one another.)
There are tests to this effect in src/test/run-pass:
traits-multidispatch-infer-convert-source-and-target.rs
traits-multidispatch-infer-convert-target.rs
#### Winnowing: Resolving ambiguities
But what happens if there are multiple impls where all the types
unify? Consider this example:
```rust
trait Get {
fn get(&self) -> Self;
}
impl<T:Copy> Get for T {
fn get(&self) -> T { *self }
}
impl<T:Get> Get for Box<T> {
fn get(&self) -> Box<T> { box get_it(&**self) }
}
```
What happens when we invoke `get_it(&box 1_u16)`, for example? In this
case, the `Self` type is `Box<u16>` -- that unifies with both impls,
because the first applies to all types, and the second to all
boxes. In the olden days we'd have called this ambiguous. But what we
do now is do a second *winnowing* pass that considers where clauses
and attempts to remove candidates -- in this case, the first impl only
applies if `Box<u16> : Copy`, which doesn't hold. After winnowing,
then, we are left with just one candidate, so we can proceed. There is
a test of this in `src/test/run-pass/traits-conditional-dispatch.rs`.
#### Matching
The subroutines that decide whether a particular impl/where-clause/etc
applies to a particular obligation. At the moment, this amounts to
unifying the self types, but in the future we may also recursively
consider some of the nested obligations, in the case of an impl.
#### Lifetimes and selection
Because of how that lifetime inference works, it is not possible to
give back immediate feedback as to whether a unification or subtype
relationship between lifetimes holds or not. Therefore, lifetime
matching is *not* considered during selection. This is reflected in
the fact that subregion assignment is infallible. This may yield
lifetime constraints that will later be found to be in error (in
contrast, the non-lifetime-constraints have already been checked
during selection and can never cause an error, though naturally they
may lead to other errors downstream).
#### Where clauses
Besides an impl, the other major way to resolve an obligation is via a
where clause. The selection process is always given a *parameter
environment* which contains a list of where clauses, which are
basically obligations that can assume are satisfiable. We will iterate
over that list and check whether our current obligation can be found
in that list, and if so it is considered satisfied. More precisely, we
want to check whether there is a where-clause obligation that is for
the same trait (or some subtrait) and for which the self types match,
using the definition of *matching* given above.
Consider this simple example:
trait A1 { ... }
trait A2 : A1 { ... }
trait B { ... }
fn foo<X:A2+B> { ... }
Clearly we can use methods offered by `A1`, `A2`, or `B` within the
body of `foo`. In each case, that will incur an obligation like `X :
A1` or `X : A2`. The parameter environment will contain two
where-clauses, `X : A2` and `X : B`. For each obligation, then, we
search this list of where-clauses. To resolve an obligation `X:A1`,
we would note that `X:A2` implies that `X:A1`.
### Confirmation
Confirmation unifies the output type parameters of the trait with the
values found in the obligation, possibly yielding a type error. If we
return to our example of the `Convert` trait from the previous
section, confirmation is where an error would be reported, because the
impl specified that `T` would be `uint`, but the obligation reported
`char`. Hence the result of selection would be an error.
### Selection during translation
During type checking, we do not store the results of trait selection.
We simply wish to verify that trait selection will succeed. Then
later, at trans time, when we have all concrete types available, we
can repeat the trait selection. In this case, we do not consider any
where-clauses to be in scope. We know that therefore each resolution
will resolve to a particular impl.
One interesting twist has to do with nested obligations. In general, in trans,
we only need to do a "shallow" selection for an obligation. That is, we wish to
identify which impl applies, but we do not (yet) need to decide how to select
any nested obligations. Nonetheless, we *do* currently do a complete resolution,
and that is because it can sometimes inform the results of type inference. That is,
we do not have the full substitutions in terms of the type varibales of the impl available
to us, so we must run trait selection to figure everything out.
Here is an example:
trait Foo { ... }
impl<U,T:Bar<U>> Foo for Vec<T> { ... }
impl Bar<uint> for int { ... }
After one shallow round of selection for an obligation like `Vec<int>
: Foo`, we would know which impl we want, and we would know that
`T=int`, but we do not know the type of `U`. We must select the
nested obligation `int : Bar<U>` to find out that `U=uint`.
It would be good to only do *just as much* nested resolution as
necessary. Currently, though, we just do a full resolution.
## Method matching
Method dispach follows a slightly different path than normal trait
selection. This is because it must account for the transformed self
type of the receiver and various other complications. The procedure is
described in `select.rs` in the "METHOD MATCHING" section.
# Caching and subtle considerations therewith
In general we attempt to cache the results of trait selection. This
is a somewhat complex process. Part of the reason for this is that we
want to be able to cache results even when all the types in the trait
reference are not fully known. In that case, it may happen that the
trait selection process is also influencing type variables, so we have
to be able to not only cache the *result* of the selection process,
but *replay* its effects on the type variables.
## An example
The high-level idea of how the cache works is that we first replace
all unbound inference variables with skolemized versions. Therefore,
if we had a trait reference `uint : Foo<$1>`, where `$n` is an unbound
inference variable, we might replace it with `uint : Foo<%0>`, where
`%n` is a skolemized type. We would then look this up in the cache.
If we found a hit, the hit would tell us the immediate next step to
take in the selection process: i.e., apply impl #22, or apply where
clause `X : Foo<Y>`. Let's say in this case there is no hit.
Therefore, we search through impls and where clauses and so forth, and
we come to the conclusion that the only possible impl is this one,
with def-id 22:
impl Foo<int> for uint { ... } // Impl #22
We would then record in the cache `uint : Foo<%0> ==>
ImplCandidate(22)`. Next we would confirm `ImplCandidate(22)`, which
would (as a side-effect) unify `$1` with `int`.
Now, at some later time, we might come along and see a `uint :
Foo<$3>`. When skolemized, this would yield `uint : Foo<%0>`, just as
before, and hence the cache lookup would succeed, yielding
`ImplCandidate(22)`. We would confirm `ImplCandidate(22)` which would
(as a side-effect) unify `$3` with `int`.
## Where clauses and the local vs global cache
One subtle interaction is that the results of trait lookup will vary
depending on what where clauses are in scope. Therefore, we actually
have *two* caches, a local and a global cache. The local cache is
attached to the `ParameterEnvironment` and the global cache attached
to the `tcx`. We use the local cache whenever the result might depend
on the where clauses that are in scope. The determination of which
cache to use is done by the method `pick_candidate_cache` in
`select.rs`.
There are two cases where we currently use the local cache. The
current rules are probably more conservative than necessary.
### Trait references that involve parameter types
The most obvious case where you need the local environment is
when the trait reference includes parameter types. For example,
consider the following function:
impl<T> Vec<T> {
fn foo(x: T)
where T : Foo
{ ... }
fn bar(x: T)
{ ... }
}
If there is an obligation `T : Foo`, or `int : Bar<T>`, or whatever,
clearly the results from `foo` and `bar` are potentially different,
since the set of where clauses in scope are different.
### Trait references with unbound variables when where clauses are in scope
There is another less obvious interaction which involves unbound variables
where *only* where clauses are in scope (no impls). This manifested as
issue #18209 (`run-pass/trait-cache-issue-18209.rs`). Consider
this snippet:
```
pub trait Foo {
fn load_from() -> Box<Self>;
fn load() -> Box<Self> {
Foo::load_from()
}
}
```
The default method will incur an obligation `$0 : Foo` from the call
to `load_from`. If there are no impls, this can be eagerly resolved to
`VtableParam(Self : Foo)` and cached. Because the trait reference
doesn't involve any parameters types (only the resolution does), this
result was stored in the global cache, causing later calls to
`Foo::load_from()` to get nonsense.
To fix this, we always use the local cache if there are unbound
variables and where clauses in scope. This is more conservative than
necessary as far as I can tell. However, it still seems to be a simple
rule and I observe ~99% hit rate on rustc, so it doesn't seem to hurt
us in particular.
Here is an example of the kind of subtle case that I would be worried
about with a more complex rule (although this particular case works
out ok). Imagine the trait reference doesn't directly reference a
where clause, but the where clause plays a role in the winnowing
phase. Something like this:
```
pub trait Foo<T> { ... }
pub trait Bar { ... }
impl<U,T:Bar> Foo<U> for T { ... } // Impl A
impl Foo<char> for uint { ... } // Impl B
```
Now, in some function, we have no where clauses in scope, and we have
an obligation `$1 : Foo<$0>`. We might then conclude that `$0=char`
and `$1=uint`: this is because for impl A to apply, `uint:Bar` would
have to hold, and we know it does not or else the coherence check
would have failed. So we might enter into our global cache: `$1 :
Foo<$0> => Impl B`. Then we come along in a different scope, where a
generic type `A` is around with the bound `A:Bar`. Now suddenly the
impl is viable.
The flaw in this imaginary DOOMSDAY SCENARIO is that we would not
currently conclude that `$1 : Foo<$0>` implies that `$0 == uint` and
`$1 == char`, even though it is true that (absent type parameters)
there is no other type the user could enter. However, it is not
*completely* implausible that we *could* draw this conclusion in the
future; we wouldn't have to guess types, in particular, we could be
led by the impls.
*/
//! # TRAIT RESOLUTION
//!
//! This document describes the general process and points out some non-obvious
//! things.
//!
//! ## Major concepts
//!
//! Trait resolution is the process of pairing up an impl with each
//! reference to a trait. So, for example, if there is a generic function like:
//!
//! fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> { ... }
//!
//! and then a call to that function:
//!
//! let v: Vec<int> = clone_slice([1, 2, 3].as_slice())
//!
//! it is the job of trait resolution to figure out (in which case)
//! whether there exists an impl of `int : Clone`
//!
//! Note that in some cases, like generic functions, we may not be able to
//! find a specific impl, but we can figure out that the caller must
//! provide an impl. To see what I mean, consider the body of `clone_slice`:
//!
//! fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> {
//! let mut v = Vec::new();
//! for e in x.iter() {
//! v.push((*e).clone()); // (*)
//! }
//! }
//!
//! The line marked `(*)` is only legal if `T` (the type of `*e`)
//! implements the `Clone` trait. Naturally, since we don't know what `T`
//! is, we can't find the specific impl; but based on the bound `T:Clone`,
//! we can say that there exists an impl which the caller must provide.
//!
//! We use the term *obligation* to refer to a trait reference in need of
//! an impl.
//!
//! ## Overview
//!
//! Trait resolution consists of three major parts:
//!
//! - SELECTION: Deciding how to resolve a specific obligation. For
//! example, selection might decide that a specific obligation can be
//! resolved by employing an impl which matches the self type, or by
//! using a parameter bound. In the case of an impl, Selecting one
//! obligation can create *nested obligations* because of where clauses
//! on the impl itself. It may also require evaluating those nested
//! obligations to resolve ambiguities.
//!
//! - FULFILLMENT: The fulfillment code is what tracks that obligations
//! are completely fulfilled. Basically it is a worklist of obligations
//! to be selected: once selection is successful, the obligation is
//! removed from the worklist and any nested obligations are enqueued.
//!
//! - COHERENCE: The coherence checks are intended to ensure that there
//! are never overlapping impls, where two impls could be used with
//! equal precedence.
//!
//! ## Selection
//!
//! Selection is the process of deciding whether an obligation can be
//! resolved and, if so, how it is to be resolved (via impl, where clause, etc).
//! The main interface is the `select()` function, which takes an obligation
//! and returns a `SelectionResult`. There are three possible outcomes:
//!
//! - `Ok(Some(selection))` -- yes, the obligation can be resolved, and
//! `selection` indicates how. If the impl was resolved via an impl,
//! then `selection` may also indicate nested obligations that are required
//! by the impl.
//!
//! - `Ok(None)` -- we are not yet sure whether the obligation can be
//! resolved or not. This happens most commonly when the obligation
//! contains unbound type variables.
//!
//! - `Err(err)` -- the obligation definitely cannot be resolved due to a
//! type error, or because there are no impls that could possibly apply,
//! etc.
//!
//! The basic algorithm for selection is broken into two big phases:
//! candidate assembly and confirmation.
//!
//! ### Candidate assembly
//!
//! Searches for impls/where-clauses/etc that might
//! possibly be used to satisfy the obligation. Each of those is called
//! a candidate. To avoid ambiguity, we want to find exactly one
//! candidate that is definitively applicable. In some cases, we may not
//! know whether an impl/where-clause applies or not -- this occurs when
//! the obligation contains unbound inference variables.
//!
//! The basic idea for candidate assembly is to do a first pass in which
//! we identify all possible candidates. During this pass, all that we do
//! is try and unify the type parameters. (In particular, we ignore any
//! nested where clauses.) Presuming that this unification succeeds, the
//! impl is added as a candidate.
//!
//! Once this first pass is done, we can examine the set of candidates. If
//! it is a singleton set, then we are done: this is the only impl in
//! scope that could possibly apply. Otherwise, we can winnow down the set
//! of candidates by using where clauses and other conditions. If this
//! reduced set yields a single, unambiguous entry, we're good to go,
//! otherwise the result is considered ambiguous.
//!
//! #### The basic process: Inferring based on the impls we see
//!
//! This process is easier if we work through some examples. Consider
//! the following trait:
//!
//! ```
//! trait Convert<Target> {
//! fn convert(&self) -> Target;
//! }
//! ```
//!
//! This trait just has one method. It's about as simple as it gets. It
//! converts from the (implicit) `Self` type to the `Target` type. If we
//! wanted to permit conversion between `int` and `uint`, we might
//! implement `Convert` like so:
//!
//! ```rust
//! impl Convert<uint> for int { ... } // int -> uint
//! impl Convert<int> for uint { ... } // uint -> uint
//! ```
//!
//! Now imagine there is some code like the following:
//!
//! ```rust
//! let x: int = ...;
//! let y = x.convert();
//! ```
//!
//! The call to convert will generate a trait reference `Convert<$Y> for
//! int`, where `$Y` is the type variable representing the type of
//! `y`. When we match this against the two impls we can see, we will find
//! that only one remains: `Convert<uint> for int`. Therefore, we can
//! select this impl, which will cause the type of `$Y` to be unified to
//! `uint`. (Note that while assembling candidates, we do the initial
//! unifications in a transaction, so that they don't affect one another.)
//!
//! There are tests to this effect in src/test/run-pass:
//!
//! traits-multidispatch-infer-convert-source-and-target.rs
//! traits-multidispatch-infer-convert-target.rs
//!
//! #### Winnowing: Resolving ambiguities
//!
//! But what happens if there are multiple impls where all the types
//! unify? Consider this example:
//!
//! ```rust
//! trait Get {
//! fn get(&self) -> Self;
//! }
//!
//! impl<T:Copy> Get for T {
//! fn get(&self) -> T { *self }
//! }
//!
//! impl<T:Get> Get for Box<T> {
//! fn get(&self) -> Box<T> { box get_it(&**self) }
//! }
//! ```
//!
//! What happens when we invoke `get_it(&box 1_u16)`, for example? In this
//! case, the `Self` type is `Box<u16>` -- that unifies with both impls,
//! because the first applies to all types, and the second to all
//! boxes. In the olden days we'd have called this ambiguous. But what we
//! do now is do a second *winnowing* pass that considers where clauses
//! and attempts to remove candidates -- in this case, the first impl only
//! applies if `Box<u16> : Copy`, which doesn't hold. After winnowing,
//! then, we are left with just one candidate, so we can proceed. There is
//! a test of this in `src/test/run-pass/traits-conditional-dispatch.rs`.
//!
//! #### Matching
//!
//! The subroutines that decide whether a particular impl/where-clause/etc
//! applies to a particular obligation. At the moment, this amounts to
//! unifying the self types, but in the future we may also recursively
//! consider some of the nested obligations, in the case of an impl.
//!
//! #### Lifetimes and selection
//!
//! Because of how that lifetime inference works, it is not possible to
//! give back immediate feedback as to whether a unification or subtype
//! relationship between lifetimes holds or not. Therefore, lifetime
//! matching is *not* considered during selection. This is reflected in
//! the fact that subregion assignment is infallible. This may yield
//! lifetime constraints that will later be found to be in error (in
//! contrast, the non-lifetime-constraints have already been checked
//! during selection and can never cause an error, though naturally they
//! may lead to other errors downstream).
//!
//! #### Where clauses
//!
//! Besides an impl, the other major way to resolve an obligation is via a
//! where clause. The selection process is always given a *parameter
//! environment* which contains a list of where clauses, which are
//! basically obligations that can assume are satisfiable. We will iterate
//! over that list and check whether our current obligation can be found
//! in that list, and if so it is considered satisfied. More precisely, we
//! want to check whether there is a where-clause obligation that is for
//! the same trait (or some subtrait) and for which the self types match,
//! using the definition of *matching* given above.
//!
//! Consider this simple example:
//!
//! trait A1 { ... }
//! trait A2 : A1 { ... }
//!
//! trait B { ... }
//!
//! fn foo<X:A2+B> { ... }
//!
//! Clearly we can use methods offered by `A1`, `A2`, or `B` within the
//! body of `foo`. In each case, that will incur an obligation like `X :
//! A1` or `X : A2`. The parameter environment will contain two
//! where-clauses, `X : A2` and `X : B`. For each obligation, then, we
//! search this list of where-clauses. To resolve an obligation `X:A1`,
//! we would note that `X:A2` implies that `X:A1`.
//!
//! ### Confirmation
//!
//! Confirmation unifies the output type parameters of the trait with the
//! values found in the obligation, possibly yielding a type error. If we
//! return to our example of the `Convert` trait from the previous
//! section, confirmation is where an error would be reported, because the
//! impl specified that `T` would be `uint`, but the obligation reported
//! `char`. Hence the result of selection would be an error.
//!
//! ### Selection during translation
//!
//! During type checking, we do not store the results of trait selection.
//! We simply wish to verify that trait selection will succeed. Then
//! later, at trans time, when we have all concrete types available, we
//! can repeat the trait selection. In this case, we do not consider any
//! where-clauses to be in scope. We know that therefore each resolution
//! will resolve to a particular impl.
//!
//! One interesting twist has to do with nested obligations. In general, in trans,
//! we only need to do a "shallow" selection for an obligation. That is, we wish to
//! identify which impl applies, but we do not (yet) need to decide how to select
//! any nested obligations. Nonetheless, we *do* currently do a complete resolution,
//! and that is because it can sometimes inform the results of type inference. That is,
//! we do not have the full substitutions in terms of the type varibales of the impl available
//! to us, so we must run trait selection to figure everything out.
//!
//! Here is an example:
//!
//! trait Foo { ... }
//! impl<U,T:Bar<U>> Foo for Vec<T> { ... }
//!
//! impl Bar<uint> for int { ... }
//!
//! After one shallow round of selection for an obligation like `Vec<int>
//! : Foo`, we would know which impl we want, and we would know that
//! `T=int`, but we do not know the type of `U`. We must select the
//! nested obligation `int : Bar<U>` to find out that `U=uint`.
//!
//! It would be good to only do *just as much* nested resolution as
//! necessary. Currently, though, we just do a full resolution.
//!
//! ## Method matching
//!
//! Method dispach follows a slightly different path than normal trait
//! selection. This is because it must account for the transformed self
//! type of the receiver and various other complications. The procedure is
//! described in `select.rs` in the "METHOD MATCHING" section.
//!
//! # Caching and subtle considerations therewith
//!
//! In general we attempt to cache the results of trait selection. This
//! is a somewhat complex process. Part of the reason for this is that we
//! want to be able to cache results even when all the types in the trait
//! reference are not fully known. In that case, it may happen that the
//! trait selection process is also influencing type variables, so we have
//! to be able to not only cache the *result* of the selection process,
//! but *replay* its effects on the type variables.
//!
//! ## An example
//!
//! The high-level idea of how the cache works is that we first replace
//! all unbound inference variables with skolemized versions. Therefore,
//! if we had a trait reference `uint : Foo<$1>`, where `$n` is an unbound
//! inference variable, we might replace it with `uint : Foo<%0>`, where
//! `%n` is a skolemized type. We would then look this up in the cache.
//! If we found a hit, the hit would tell us the immediate next step to
//! take in the selection process: i.e., apply impl #22, or apply where
//! clause `X : Foo<Y>`. Let's say in this case there is no hit.
//! Therefore, we search through impls and where clauses and so forth, and
//! we come to the conclusion that the only possible impl is this one,
//! with def-id 22:
//!
//! impl Foo<int> for uint { ... } // Impl #22
//!
//! We would then record in the cache `uint : Foo<%0> ==>
//! ImplCandidate(22)`. Next we would confirm `ImplCandidate(22)`, which
//! would (as a side-effect) unify `$1` with `int`.
//!
//! Now, at some later time, we might come along and see a `uint :
//! Foo<$3>`. When skolemized, this would yield `uint : Foo<%0>`, just as
//! before, and hence the cache lookup would succeed, yielding
//! `ImplCandidate(22)`. We would confirm `ImplCandidate(22)` which would
//! (as a side-effect) unify `$3` with `int`.
//!
//! ## Where clauses and the local vs global cache
//!
//! One subtle interaction is that the results of trait lookup will vary
//! depending on what where clauses are in scope. Therefore, we actually
//! have *two* caches, a local and a global cache. The local cache is
//! attached to the `ParameterEnvironment` and the global cache attached
//! to the `tcx`. We use the local cache whenever the result might depend
//! on the where clauses that are in scope. The determination of which
//! cache to use is done by the method `pick_candidate_cache` in
//! `select.rs`.
//!
//! There are two cases where we currently use the local cache. The
//! current rules are probably more conservative than necessary.
//!
//! ### Trait references that involve parameter types
//!
//! The most obvious case where you need the local environment is
//! when the trait reference includes parameter types. For example,
//! consider the following function:
//!
//! impl<T> Vec<T> {
//! fn foo(x: T)
//! where T : Foo
//! { ... }
//!
//! fn bar(x: T)
//! { ... }
//! }
//!
//! If there is an obligation `T : Foo`, or `int : Bar<T>`, or whatever,
//! clearly the results from `foo` and `bar` are potentially different,
//! since the set of where clauses in scope are different.
//!
//! ### Trait references with unbound variables when where clauses are in scope
//!
//! There is another less obvious interaction which involves unbound variables
//! where *only* where clauses are in scope (no impls). This manifested as
//! issue #18209 (`run-pass/trait-cache-issue-18209.rs`). Consider
//! this snippet:
//!
//! ```
//! pub trait Foo {
//! fn load_from() -> Box<Self>;
//! fn load() -> Box<Self> {
//! Foo::load_from()
//! }
//! }
//! ```
//!
//! The default method will incur an obligation `$0 : Foo` from the call
//! to `load_from`. If there are no impls, this can be eagerly resolved to
//! `VtableParam(Self : Foo)` and cached. Because the trait reference
//! doesn't involve any parameters types (only the resolution does), this
//! result was stored in the global cache, causing later calls to
//! `Foo::load_from()` to get nonsense.
//!
//! To fix this, we always use the local cache if there are unbound
//! variables and where clauses in scope. This is more conservative than
//! necessary as far as I can tell. However, it still seems to be a simple
//! rule and I observe ~99% hit rate on rustc, so it doesn't seem to hurt
//! us in particular.
//!
//! Here is an example of the kind of subtle case that I would be worried
//! about with a more complex rule (although this particular case works
//! out ok). Imagine the trait reference doesn't directly reference a
//! where clause, but the where clause plays a role in the winnowing
//! phase. Something like this:
//!
//! ```
//! pub trait Foo<T> { ... }
//! pub trait Bar { ... }
//! impl<U,T:Bar> Foo<U> for T { ... } // Impl A
//! impl Foo<char> for uint { ... } // Impl B
//! ```
//!
//! Now, in some function, we have no where clauses in scope, and we have
//! an obligation `$1 : Foo<$0>`. We might then conclude that `$0=char`
//! and `$1=uint`: this is because for impl A to apply, `uint:Bar` would
//! have to hold, and we know it does not or else the coherence check
//! would have failed. So we might enter into our global cache: `$1 :
//! Foo<$0> => Impl B`. Then we come along in a different scope, where a
//! generic type `A` is around with the bound `A:Bar`. Now suddenly the
//! impl is viable.
//!
//! The flaw in this imaginary DOOMSDAY SCENARIO is that we would not
//! currently conclude that `$1 : Foo<$0>` implies that `$0 == uint` and
//! `$1 == char`, even though it is true that (absent type parameters)
//! there is no other type the user could enter. However, it is not
//! *completely* implausible that we *could* draw this conclusion in the
//! future; we wouldn't have to guess types, in particular, we could be
//! led by the impls.

View File

@ -79,20 +79,16 @@ impl<'tcx> FulfillmentContext<'tcx> {
}
}
/// Attempts to select obligations that were registered since the call to a selection routine.
/// This is used by the type checker to eagerly attempt to resolve obligations in hopes of
/// gaining type information. It'd be equally valid to use `select_where_possible` but it
/// results in `O(n^2)` performance (#18208).
pub fn select_new_obligations<'a>(&mut self,
infcx: &InferCtxt<'a,'tcx>,
param_env: &ty::ParameterEnvironment<'tcx>,
typer: &Typer<'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>>
{
/*!
* Attempts to select obligations that were registered since
* the call to a selection routine. This is used by the type checker
* to eagerly attempt to resolve obligations in hopes of gaining
* type information. It'd be equally valid to use `select_where_possible`
* but it results in `O(n^2)` performance (#18208).
*/
let mut selcx = SelectionContext::new(infcx, param_env, typer);
self.select(&mut selcx, true)
}
@ -111,16 +107,13 @@ impl<'tcx> FulfillmentContext<'tcx> {
self.trait_obligations[]
}
/// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it
/// only attempts to select obligations that haven't been seen before.
fn select<'a>(&mut self,
selcx: &mut SelectionContext<'a, 'tcx>,
only_new_obligations: bool)
-> Result<(),Vec<FulfillmentError<'tcx>>>
{
/*!
* Attempts to select obligations using `selcx`. If
* `only_new_obligations` is true, then it only attempts to
* select obligations that haven't been seen before.
*/
debug!("select({} obligations, only_new_obligations={}) start",
self.trait_obligations.len(),
only_new_obligations);

View File

@ -8,9 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Trait Resolution. See doc.rs.
*/
//! Trait Resolution. See doc.rs.
pub use self::SelectionError::*;
pub use self::FulfillmentErrorCode::*;
@ -214,6 +212,10 @@ pub struct VtableParamData<'tcx> {
pub bound: Rc<ty::TraitRef<'tcx>>,
}
/// Matches the self type of the inherent impl `impl_def_id`
/// against `self_ty` and returns the resulting resolution. This
/// routine may modify the surrounding type context (for example,
/// it may unify variables).
pub fn select_inherent_impl<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
param_env: &ty::ParameterEnvironment<'tcx>,
typer: &Typer<'tcx>,
@ -223,13 +225,6 @@ pub fn select_inherent_impl<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
-> SelectionResult<'tcx,
VtableImplData<'tcx, Obligation<'tcx>>>
{
/*!
* Matches the self type of the inherent impl `impl_def_id`
* against `self_ty` and returns the resulting resolution. This
* routine may modify the surrounding type context (for example,
* it may unify variables).
*/
// This routine is only suitable for inherent impls. This is
// because it does not attempt to unify the output type parameters
// from the trait ref against the values from the obligation.
@ -244,53 +239,41 @@ pub fn select_inherent_impl<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
selcx.select_inherent_impl(impl_def_id, cause, self_ty)
}
/// True if neither the trait nor self type is local. Note that `impl_def_id` must refer to an impl
/// of a trait, not an inherent impl.
pub fn is_orphan_impl(tcx: &ty::ctxt,
impl_def_id: ast::DefId)
-> bool
{
/*!
* True if neither the trait nor self type is local. Note that
* `impl_def_id` must refer to an impl of a trait, not an inherent
* impl.
*/
!coherence::impl_is_local(tcx, impl_def_id)
}
/// True if there exist types that satisfy both of the two given impls.
pub fn overlapping_impls(infcx: &InferCtxt,
impl1_def_id: ast::DefId,
impl2_def_id: ast::DefId)
-> bool
{
/*!
* True if there exist types that satisfy both of the two given impls.
*/
coherence::impl_can_satisfy(infcx, impl1_def_id, impl2_def_id) &&
coherence::impl_can_satisfy(infcx, impl2_def_id, impl1_def_id)
}
/// Given generic bounds from an impl like:
///
/// impl<A:Foo, B:Bar+Qux> ...
///
/// along with the bindings for the types `A` and `B` (e.g., `<A=A0, B=B0>`), yields a result like
///
/// [[Foo for A0, Bar for B0, Qux for B0], [], []]
///
/// Expects that `generic_bounds` have already been fully substituted, late-bound regions liberated
/// and so forth, so that they are in the same namespace as `type_substs`.
pub fn obligations_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>,
cause: ObligationCause<'tcx>,
generic_bounds: &ty::GenericBounds<'tcx>,
type_substs: &subst::VecPerParamSpace<Ty<'tcx>>)
-> subst::VecPerParamSpace<Obligation<'tcx>>
{
/*!
* Given generic bounds from an impl like:
*
* impl<A:Foo, B:Bar+Qux> ...
*
* along with the bindings for the types `A` and `B` (e.g.,
* `<A=A0, B=B0>`), yields a result like
*
* [[Foo for A0, Bar for B0, Qux for B0], [], []]
*
* Expects that `generic_bounds` have already been fully
* substituted, late-bound regions liberated and so forth,
* so that they are in the same namespace as `type_substs`.
*/
util::obligations_for_generics(tcx, cause, 0, generic_bounds, type_substs)
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! See `doc.rs` for high-level documentation */
//! See `doc.rs` for high-level documentation
#![allow(dead_code)] // FIXME -- just temporarily
pub use self::MethodMatchResult::*;
@ -199,15 +199,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// is `Vec<Foo>:Iterable<Bar>`, but the impl specifies
// `impl<T> Iterable<T> for Vec<T>`, than an error would result.
/// Evaluates whether the obligation can be satisfied. Returns an indication of whether the
/// obligation can be satisfied and, if so, by what means. Never affects surrounding typing
/// environment.
pub fn select(&mut self, obligation: &Obligation<'tcx>)
-> SelectionResult<'tcx, Selection<'tcx>> {
/*!
* Evaluates whether the obligation can be satisfied. Returns
* an indication of whether the obligation can be satisfied
* and, if so, by what means. Never affects surrounding typing
* environment.
*/
debug!("select({})", obligation.repr(self.tcx()));
assert!(!obligation.trait_ref.has_escaping_regions());
@ -251,15 +247,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// The result is "true" if the obligation *may* hold and "false" if
// we can be sure it does not.
/// Evaluates whether the obligation `obligation` can be satisfied (by any means).
pub fn evaluate_obligation(&mut self,
obligation: &Obligation<'tcx>)
-> bool
{
/*!
* Evaluates whether the obligation `obligation` can be
* satisfied (by any means).
*/
debug!("evaluate_obligation({})",
obligation.repr(self.tcx()));
assert!(!obligation.trait_ref.has_escaping_regions());
@ -385,17 +377,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
/// Evaluates whether the impl with id `impl_def_id` could be applied to the self type
/// `obligation_self_ty`. This can be used either for trait or inherent impls.
pub fn evaluate_impl(&mut self,
impl_def_id: ast::DefId,
obligation: &Obligation<'tcx>)
-> bool
{
/*!
* Evaluates whether the impl with id `impl_def_id` could be
* applied to the self type `obligation_self_ty`. This can be
* used either for trait or inherent impls.
*/
debug!("evaluate_impl(impl_def_id={}, obligation={})",
impl_def_id.repr(self.tcx()),
obligation.repr(self.tcx()));
@ -433,23 +421,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// the body of `evaluate_method_obligation()` for more details on
// the algorithm.
/// Determine whether a trait-method is applicable to a receiver of
/// type `rcvr_ty`. *Does not affect the inference state.*
///
/// - `rcvr_ty` -- type of the receiver
/// - `xform_self_ty` -- transformed self type declared on the method, with `Self`
/// to a fresh type variable
/// - `obligation` -- a reference to the trait where the method is declared, with
/// the input types on the trait replaced with fresh type variables
pub fn evaluate_method_obligation(&mut self,
rcvr_ty: Ty<'tcx>,
xform_self_ty: Ty<'tcx>,
obligation: &Obligation<'tcx>)
-> MethodMatchResult
{
/*!
* Determine whether a trait-method is applicable to a receiver of
* type `rcvr_ty`. *Does not affect the inference state.*
*
* - `rcvr_ty` -- type of the receiver
* - `xform_self_ty` -- transformed self type declared on the method, with `Self`
* to a fresh type variable
* - `obligation` -- a reference to the trait where the method is declared, with
* the input types on the trait replaced with fresh type variables
*/
// Here is the situation. We have a trait method declared (say) like so:
//
// trait TheTrait {
@ -561,19 +546,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
/// Given the successful result of a method match, this function "confirms" the result, which
/// basically repeats the various matching operations, but outside of any snapshot so that
/// their effects are committed into the inference state.
pub fn confirm_method_match(&mut self,
rcvr_ty: Ty<'tcx>,
xform_self_ty: Ty<'tcx>,
obligation: &Obligation<'tcx>,
data: MethodMatchedData)
{
/*!
* Given the successful result of a method match, this
* function "confirms" the result, which basically repeats the
* various matching operations, but outside of any snapshot so
* that their effects are committed into the inference state.
*/
let is_ok = match data {
PreciseMethodMatch => {
self.match_method_precise(rcvr_ty, xform_self_ty, obligation).is_ok()
@ -595,17 +576,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
/// Implements the *precise method match* procedure described in
/// `evaluate_method_obligation()`.
fn match_method_precise(&mut self,
rcvr_ty: Ty<'tcx>,
xform_self_ty: Ty<'tcx>,
obligation: &Obligation<'tcx>)
-> Result<(),()>
{
/*!
* Implements the *precise method match* procedure described in
* `evaluate_method_obligation()`.
*/
self.infcx.commit_if_ok(|| {
match self.infcx.sub_types(false, infer::RelateSelfType(obligation.cause.span),
rcvr_ty, xform_self_ty) {
@ -621,18 +599,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
})
}
/// Assembles a list of potentially applicable impls using the *coercive match* procedure
/// described in `evaluate_method_obligation()`.
fn assemble_method_candidates_from_impls(&mut self,
rcvr_ty: Ty<'tcx>,
xform_self_ty: Ty<'tcx>,
obligation: &Obligation<'tcx>)
-> Vec<ast::DefId>
{
/*!
* Assembles a list of potentially applicable impls using the
* *coercive match* procedure described in
* `evaluate_method_obligation()`.
*/
let mut candidates = Vec::new();
let all_impls = self.all_impls(obligation.trait_ref.def_id);
@ -648,6 +622,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidates
}
/// Applies the *coercive match* procedure described in `evaluate_method_obligation()` to a
/// particular impl.
fn match_method_coerce(&mut self,
impl_def_id: ast::DefId,
rcvr_ty: Ty<'tcx>,
@ -655,11 +631,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &Obligation<'tcx>)
-> Result<Substs<'tcx>, ()>
{
/*!
* Applies the *coercive match* procedure described in
* `evaluate_method_obligation()` to a particular impl.
*/
// This is almost always expected to succeed. It
// causes the impl's self-type etc to be unified with
// the type variable that is shared between
@ -681,6 +652,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(substs)
}
/// A version of `winnow_impl` applicable to coerice method matching. This is basically the
/// same as `winnow_impl` but it uses the method matching procedure and is specific to impls.
fn winnow_method_impl(&mut self,
impl_def_id: ast::DefId,
rcvr_ty: Ty<'tcx>,
@ -688,13 +661,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &Obligation<'tcx>)
-> bool
{
/*!
* A version of `winnow_impl` applicable to coerice method
* matching. This is basically the same as `winnow_impl` but
* it uses the method matching procedure and is specific to
* impls.
*/
debug!("winnow_method_impl: impl_def_id={} rcvr_ty={} xform_self_ty={} obligation={}",
impl_def_id.repr(self.tcx()),
rcvr_ty.repr(self.tcx()),
@ -960,19 +926,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(candidates)
}
/// Given an obligation like `<SomeTrait for T>`, search the obligations that the caller
/// supplied to find out whether it is listed among them.
///
/// Never affects inference environment.
fn assemble_candidates_from_caller_bounds(&mut self,
obligation: &Obligation<'tcx>,
candidates: &mut CandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
{
/*!
* Given an obligation like `<SomeTrait for T>`, search the obligations
* that the caller supplied to find out whether it is listed among
* them.
*
* Never affects inference environment.
*/
debug!("assemble_candidates_from_caller_bounds({})",
obligation.repr(self.tcx()));
@ -1000,22 +962,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(())
}
/// Check for the artificial impl that the compiler will create for an obligation like `X :
/// FnMut<..>` where `X` is an unboxed closure type.
///
/// Note: the type parameters on an unboxed closure candidate are modeled as *output* type
/// parameters and hence do not affect whether this trait is a match or not. They will be
/// unified during the confirmation step.
fn assemble_unboxed_candidates(&mut self,
obligation: &Obligation<'tcx>,
candidates: &mut CandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
{
/*!
* Check for the artificial impl that the compiler will create
* for an obligation like `X : FnMut<..>` where `X` is an
* unboxed closure type.
*
* Note: the type parameters on an unboxed closure candidate
* are modeled as *output* type parameters and hence do not
* affect whether this trait is a match or not. They will be
* unified during the confirmation step.
*/
let tcx = self.tcx();
let kind = if Some(obligation.trait_ref.def_id) == tcx.lang_items.fn_trait() {
ty::FnUnboxedClosureKind
@ -1058,15 +1015,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(())
}
/// Search for impls that might apply to `obligation`.
fn assemble_candidates_from_impls(&mut self,
obligation: &Obligation<'tcx>,
candidates: &mut CandidateSet<'tcx>)
-> Result<(), SelectionError<'tcx>>
{
/*!
* Search for impls that might apply to `obligation`.
*/
let all_impls = self.all_impls(obligation.trait_ref.def_id);
for &impl_def_id in all_impls.iter() {
self.infcx.probe(|| {
@ -1090,17 +1044,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// attempt to evaluate recursive bounds to see if they are
// satisfied.
/// Further evaluate `candidate` to decide whether all type parameters match and whether nested
/// obligations are met. Returns true if `candidate` remains viable after this further
/// scrutiny.
fn winnow_candidate<'o>(&mut self,
stack: &ObligationStack<'o, 'tcx>,
candidate: &Candidate<'tcx>)
-> EvaluationResult
{
/*!
* Further evaluate `candidate` to decide whether all type parameters match
* and whether nested obligations are met. Returns true if `candidate` remains
* viable after this further scrutiny.
*/
debug!("winnow_candidate: candidate={}", candidate.repr(self.tcx()));
self.infcx.probe(|| {
let candidate = (*candidate).clone();
@ -1127,37 +1078,35 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
result
}
/// Returns true if `candidate_i` should be dropped in favor of `candidate_j`.
///
/// This is generally true if either:
/// - candidate i and candidate j are equivalent; or,
/// - candidate i is a conrete impl and candidate j is a where clause bound,
/// and the concrete impl is applicable to the types in the where clause bound.
///
/// The last case refers to cases where there are blanket impls (often conditional
/// blanket impls) as well as a where clause. This can come down to one of two cases:
///
/// - The impl is truly unconditional (it has no where clauses
/// of its own), in which case the where clause is
/// unnecessary, because coherence requires that we would
/// pick that particular impl anyhow (at least so long as we
/// don't have specialization).
///
/// - The impl is conditional, in which case we may not have winnowed it out
/// because we don't know if the conditions apply, but the where clause is basically
/// telling us taht there is some impl, though not necessarily the one we see.
///
/// In both cases we prefer to take the where clause, which is
/// essentially harmless. See issue #18453 for more details of
/// a case where doing the opposite caused us harm.
fn candidate_should_be_dropped_in_favor_of<'o>(&mut self,
stack: &ObligationStack<'o, 'tcx>,
candidate_i: &Candidate<'tcx>,
candidate_j: &Candidate<'tcx>)
-> bool
{
/*!
* Returns true if `candidate_i` should be dropped in favor of `candidate_j`.
* This is generally true if either:
* - candidate i and candidate j are equivalent; or,
* - candidate i is a conrete impl and candidate j is a where clause bound,
* and the concrete impl is applicable to the types in the where clause bound.
*
* The last case refers to cases where there are blanket impls (often conditional
* blanket impls) as well as a where clause. This can come down to one of two cases:
*
* - The impl is truly unconditional (it has no where clauses
* of its own), in which case the where clause is
* unnecessary, because coherence requires that we would
* pick that particular impl anyhow (at least so long as we
* don't have specialization).
*
* - The impl is conditional, in which case we may not have winnowed it out
* because we don't know if the conditions apply, but the where clause is basically
* telling us taht there is some impl, though not necessarily the one we see.
*
* In both cases we prefer to take the where clause, which is
* essentially harmless. See issue #18453 for more details of
* a case where doing the opposite caused us harm.
*/
match (candidate_i, candidate_j) {
(&ImplCandidate(impl_def_id), &ParamCandidate(ref vt)) => {
debug!("Considering whether to drop param {} in favor of impl {}",
@ -1846,26 +1795,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
/// Determines whether the self type declared against
/// `impl_def_id` matches `obligation_self_ty`. If successful,
/// returns the substitutions used to make them match. See
/// `match_impl()`. For example, if `impl_def_id` is declared
/// as:
///
/// impl<T:Copy> Foo for ~T { ... }
///
/// and `obligation_self_ty` is `int`, we'd back an `Err(_)`
/// result. But if `obligation_self_ty` were `~int`, we'd get
/// back `Ok(T=int)`.
fn match_inherent_impl(&mut self,
impl_def_id: ast::DefId,
obligation_cause: ObligationCause,
obligation_self_ty: Ty<'tcx>)
-> Result<Substs<'tcx>,()>
{
/*!
* Determines whether the self type declared against
* `impl_def_id` matches `obligation_self_ty`. If successful,
* returns the substitutions used to make them match. See
* `match_impl()`. For example, if `impl_def_id` is declared
* as:
*
* impl<T:Copy> Foo for ~T { ... }
*
* and `obligation_self_ty` is `int`, we'd back an `Err(_)`
* result. But if `obligation_self_ty` were `~int`, we'd get
* back `Ok(T=int)`.
*/
// Create fresh type variables for each type parameter declared
// on the impl etc.
let impl_substs = util::fresh_substs_for_impl(self.infcx,
@ -1926,6 +1872,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// the output type parameters from the obligation with those found
// on the impl/bound, which may yield type errors.
/// Relates the output type parameters from an impl to the
/// trait. This may lead to type errors. The confirmation step
/// is separated from the main match procedure because these
/// type errors do not cause us to select another impl.
///
/// As an example, consider matching the obligation
/// `Iterator<char> for Elems<int>` using the following impl:
///
/// impl<T> Iterator<T> for Elems<T> { ... }
///
/// The match phase will succeed with substitution `T=int`.
/// The confirm step will then try to unify `int` and `char`
/// and yield an error.
fn confirm_impl_vtable(&mut self,
impl_def_id: ast::DefId,
obligation_cause: ObligationCause<'tcx>,
@ -1933,22 +1892,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
substs: &Substs<'tcx>)
-> Result<(), SelectionError<'tcx>>
{
/*!
* Relates the output type parameters from an impl to the
* trait. This may lead to type errors. The confirmation step
* is separated from the main match procedure because these
* type errors do not cause us to select another impl.
*
* As an example, consider matching the obligation
* `Iterator<char> for Elems<int>` using the following impl:
*
* impl<T> Iterator<T> for Elems<T> { ... }
*
* The match phase will succeed with substitution `T=int`.
* The confirm step will then try to unify `int` and `char`
* and yield an error.
*/
let impl_trait_ref = ty::impl_trait_ref(self.tcx(),
impl_def_id).unwrap();
let impl_trait_ref = impl_trait_ref.subst(self.tcx(),
@ -1956,38 +1899,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.confirm(obligation_cause, obligation_trait_ref, impl_trait_ref)
}
/// After we have determined which impl applies, and with what substitutions, there is one last
/// step. We have to go back and relate the "output" type parameters from the obligation to the
/// types that are specified in the impl.
///
/// For example, imagine we have:
///
/// impl<T> Iterator<T> for Vec<T> { ... }
///
/// and our obligation is `Iterator<Foo> for Vec<int>` (note the mismatch in the obligation
/// types). Up until this step, no error would be reported: the self type is `Vec<int>`, and
/// that matches `Vec<T>` with the substitution `T=int`. At this stage, we could then go and
/// check that the type parameters to the `Iterator` trait match. (In terms of the parameters,
/// the `expected_trait_ref` here would be `Iterator<int> for Vec<int>`, and the
/// `obligation_trait_ref` would be `Iterator<Foo> for Vec<int>`.
///
/// Note that this checking occurs *after* the impl has selected, because these output type
/// parameters should not affect the selection of the impl. Therefore, if there is a mismatch,
/// we report an error to the user.
fn confirm(&mut self,
obligation_cause: ObligationCause,
obligation_trait_ref: Rc<ty::TraitRef<'tcx>>,
expected_trait_ref: Rc<ty::TraitRef<'tcx>>)
-> Result<(), SelectionError<'tcx>>
{
/*!
* After we have determined which impl applies, and with what
* substitutions, there is one last step. We have to go back
* and relate the "output" type parameters from the obligation
* to the types that are specified in the impl.
*
* For example, imagine we have:
*
* impl<T> Iterator<T> for Vec<T> { ... }
*
* and our obligation is `Iterator<Foo> for Vec<int>` (note
* the mismatch in the obligation types). Up until this step,
* no error would be reported: the self type is `Vec<int>`,
* and that matches `Vec<T>` with the substitution `T=int`.
* At this stage, we could then go and check that the type
* parameters to the `Iterator` trait match.
* (In terms of the parameters, the `expected_trait_ref`
* here would be `Iterator<int> for Vec<int>`, and the
* `obligation_trait_ref` would be `Iterator<Foo> for Vec<int>`.
*
* Note that this checking occurs *after* the impl has
* selected, because these output type parameters should not
* affect the selection of the impl. Therefore, if there is a
* mismatch, we report an error to the user.
*/
let origin = infer::RelateOutputImplTypes(obligation_cause.span);
let obligation_trait_ref = obligation_trait_ref.clone();
@ -2017,11 +1952,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
/// Returns set of all impls for a given trait.
fn all_impls(&self, trait_def_id: ast::DefId) -> Vec<ast::DefId> {
/*!
* Returns set of all impls for a given trait.
*/
ty::populate_implementations_for_trait_if_necessary(self.tcx(),
trait_def_id);
match self.tcx().trait_impls.borrow().get(&trait_def_id) {

View File

@ -42,22 +42,18 @@ pub fn supertraits<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
trait_ref: Rc<ty::TraitRef<'tcx>>)
-> Supertraits<'cx, 'tcx>
{
/*!
* Returns an iterator over the trait reference `T` and all of its
* supertrait references. May contain duplicates. In general
* the ordering is not defined.
*
* Example:
*
* ```
* trait Foo { ... }
* trait Bar : Foo { ... }
* trait Baz : Bar+Foo { ... }
* ```
*
* `supertraits(Baz)` yields `[Baz, Bar, Foo, Foo]` in some order.
*/
/// Returns an iterator over the trait reference `T` and all of its supertrait references. May
/// contain duplicates. In general the ordering is not defined.
///
/// Example:
///
/// ```
/// trait Foo { ... }
/// trait Bar : Foo { ... }
/// trait Baz : Bar+Foo { ... }
/// ```
///
/// `supertraits(Baz)` yields `[Baz, Bar, Foo, Foo]` in some order.
transitive_bounds(tcx, &[trait_ref])
}
@ -97,12 +93,8 @@ impl<'cx, 'tcx> Supertraits<'cx, 'tcx> {
self.stack.push(entry);
}
/// Returns the path taken through the trait supertraits to reach the current point.
pub fn indices(&self) -> Vec<uint> {
/*!
* Returns the path taken through the trait supertraits to
* reach the current point.
*/
self.stack.iter().map(|e| e.position).collect()
}
}
@ -171,6 +163,7 @@ impl<'tcx> fmt::Show for VtableParamData<'tcx> {
}
}
/// See `super::obligations_for_generics`
pub fn obligations_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>,
cause: ObligationCause<'tcx>,
recursion_depth: uint,
@ -178,7 +171,6 @@ pub fn obligations_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>,
type_substs: &VecPerParamSpace<Ty<'tcx>>)
-> VecPerParamSpace<Obligation<'tcx>>
{
/*! See `super::obligations_for_generics` */
debug!("obligations_for_generics(generic_bounds={}, type_substs={})",
generic_bounds.repr(tcx), type_substs.repr(tcx));
@ -272,20 +264,15 @@ pub fn obligation_for_builtin_bound<'tcx>(
}
}
/// Starting from a caller obligation `caller_bound` (which has coordinates `space`/`i` in the list
/// of caller obligations), search through the trait and supertraits to find one where `test(d)` is
/// true, where `d` is the def-id of the trait/supertrait. If any is found, return `Some(p)` where
/// `p` is the path to that trait/supertrait. Else `None`.
pub fn search_trait_and_supertraits_from_bound<'tcx>(tcx: &ty::ctxt<'tcx>,
caller_bound: Rc<ty::TraitRef<'tcx>>,
test: |ast::DefId| -> bool)
-> Option<VtableParamData<'tcx>>
{
/*!
* Starting from a caller obligation `caller_bound` (which has
* coordinates `space`/`i` in the list of caller obligations),
* search through the trait and supertraits to find one where
* `test(d)` is true, where `d` is the def-id of the
* trait/supertrait. If any is found, return `Some(p)` where `p`
* is the path to that trait/supertrait. Else `None`.
*/
for bound in transitive_bounds(tcx, &[caller_bound]) {
if test(bound.def_id) {
let vtable_param = VtableParamData { bound: bound };

View File

@ -671,39 +671,29 @@ pub fn type_has_late_bound_regions(ty: Ty) -> bool {
ty.flags.intersects(HAS_RE_LATE_BOUND)
}
/// An "escaping region" is a bound region whose binder is not part of `t`.
///
/// So, for example, consider a type like the following, which has two binders:
///
/// for<'a> fn(x: for<'b> fn(&'a int, &'b int))
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope
///
/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the
/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
/// fn type*, that type has an escaping region: `'a`.
///
/// Note that what I'm calling an "escaping region" is often just called a "free region". However,
/// we already use the term "free region". It refers to the regions that we use to represent bound
/// regions on a fn definition while we are typechecking its body.
///
/// To clarify, conceptually there is no particular difference between an "escaping" region and a
/// "free" region. However, there is a big difference in practice. Basically, when "entering" a
/// binding level, one is generally required to do some sort of processing to a bound region, such
/// as replacing it with a fresh/skolemized region, or making an entry in the environment to
/// represent the scope to which it is attached, etc. An escaping region represents a bound region
/// for which this processing has not yet been done.
pub fn type_has_escaping_regions(ty: Ty) -> bool {
/*!
* An "escaping region" is a bound region whose binder is not part of `t`.
*
* So, for example, consider a type like the following, which has two
* binders:
*
* for<'a> fn(x: for<'b> fn(&'a int, &'b int))
* ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
* ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope
*
* This type has *bound regions* (`'a`, `'b`), but it does not
* have escaping regions, because the binders of both `'a` and
* `'b` are part of the type itself. However, if we consider the
* *inner fn type*, that type has an escaping region: `'a`.
*
* Note that what I'm calling an "escaping region" is often just
* called a "free region". However, we already use the term "free
* region". It refers to the regions that we use to represent
* bound regions on a fn definition while we are typechecking its
* body.
*
* To clarify, conceptually there is no particular difference
* between an "escaping" region and a "free" region. However,
* there is a big difference in practice. Basically, when
* "entering" a binding level, one is generally required to do
* some sort of processing to a bound region, such as replacing it
* with a fresh/skolemized region, or making an entry in the
* environment to represent the scope to which it is attached,
* etc. An escaping region represents a bound region for which
* this processing has not yet been done.
*/
type_escapes_depth(ty, 0)
}
@ -1236,11 +1226,8 @@ pub fn all_builtin_bounds() -> BuiltinBounds {
set
}
/// An existential bound that does not implement any traits.
pub fn region_existential_bound(r: ty::Region) -> ExistentialBounds {
/*!
* An existential bound that does not implement any traits.
*/
ty::ExistentialBounds { region_bound: r,
builtin_bounds: empty_builtin_bounds() }
}
@ -1820,12 +1807,9 @@ impl FlagComputation {
}
}
/// Adds the flags/depth from a set of types that appear within the current type, but within a
/// region binder.
fn add_bound_computation(&mut self, computation: &FlagComputation) {
/*!
* Adds the flags/depth from a set of types that appear within
* the current type, but within a region binder.
*/
self.add_flags(computation.flags);
// The types that contributed to `computation` occured within
@ -2559,38 +2543,26 @@ impl TypeContents {
self.intersects(TC::NeedsDrop)
}
/// Includes only those bits that still apply when indirected through a `Box` pointer
pub fn owned_pointer(&self) -> TypeContents {
/*!
* Includes only those bits that still apply
* when indirected through a `Box` pointer
*/
TC::OwnsOwned | (
*self & (TC::OwnsAll | TC::ReachesAll))
}
/// Includes only those bits that still apply when indirected through a reference (`&`)
pub fn reference(&self, bits: TypeContents) -> TypeContents {
/*!
* Includes only those bits that still apply
* when indirected through a reference (`&`)
*/
bits | (
*self & TC::ReachesAll)
}
/// Includes only those bits that still apply when indirected through a managed pointer (`@`)
pub fn managed_pointer(&self) -> TypeContents {
/*!
* Includes only those bits that still apply
* when indirected through a managed pointer (`@`)
*/
TC::Managed | (
*self & TC::ReachesAll)
}
/// Includes only those bits that still apply when indirected through an unsafe pointer (`*`)
pub fn unsafe_pointer(&self) -> TypeContents {
/*!
* Includes only those bits that still apply
* when indirected through an unsafe pointer (`*`)
*/
*self & TC::ReachesAll
}
@ -2867,14 +2839,10 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents {
}
}
/// Type contents due to containing a reference with the region `region` and borrow kind `bk`
fn borrowed_contents(region: ty::Region,
mutbl: ast::Mutability)
-> TypeContents {
/*!
* Type contents due to containing a reference
* with the region `region` and borrow kind `bk`
*/
let b = match mutbl {
ast::MutMutable => TC::ReachesMutable | TC::OwnsAffine,
ast::MutImmutable => TC::None,
@ -3632,20 +3600,16 @@ pub fn expr_ty_opt<'tcx>(cx: &ctxt<'tcx>, expr: &ast::Expr) -> Option<Ty<'tcx>>
return node_id_to_type_opt(cx, expr.id);
}
/// Returns the type of `expr`, considering any `AutoAdjustment`
/// entry recorded for that expression.
///
/// It would almost certainly be better to store the adjusted ty in with
/// the `AutoAdjustment`, but I opted not to do this because it would
/// require serializing and deserializing the type and, although that's not
/// hard to do, I just hate that code so much I didn't want to touch it
/// unless it was to fix it properly, which seemed a distraction from the
/// task at hand! -nmatsakis
pub fn expr_ty_adjusted<'tcx>(cx: &ctxt<'tcx>, expr: &ast::Expr) -> Ty<'tcx> {
/*!
*
* Returns the type of `expr`, considering any `AutoAdjustment`
* entry recorded for that expression.
*
* It would almost certainly be better to store the adjusted ty in with
* the `AutoAdjustment`, but I opted not to do this because it would
* require serializing and deserializing the type and, although that's not
* hard to do, I just hate that code so much I didn't want to touch it
* unless it was to fix it properly, which seemed a distraction from the
* task at hand! -nmatsakis
*/
adjust_ty(cx, expr.span, expr.id, expr_ty(cx, expr),
cx.adjustments.borrow().get(&expr.id),
|method_call| cx.method_map.borrow().get(&method_call).map(|method| method.ty))
@ -3691,6 +3655,7 @@ pub fn local_var_name_str(cx: &ctxt, id: NodeId) -> InternedString {
}
}
/// See `expr_ty_adjusted`
pub fn adjust_ty<'tcx>(cx: &ctxt<'tcx>,
span: Span,
expr_id: ast::NodeId,
@ -3698,7 +3663,6 @@ pub fn adjust_ty<'tcx>(cx: &ctxt<'tcx>,
adjustment: Option<&AutoAdjustment<'tcx>>,
method_type: |typeck::MethodCall| -> Option<Ty<'tcx>>)
-> Ty<'tcx> {
/*! See `expr_ty_adjusted` */
match unadjusted_ty.sty {
ty_err => return unadjusted_ty,
@ -4112,16 +4076,11 @@ pub fn ty_sort_string<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> String {
}
}
/// Explains the source of a type err in a short, human readable way. This is meant to be placed
/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()`
/// afterwards to present additional details, particularly when it comes to lifetime-related
/// errors.
pub fn type_err_to_str<'tcx>(cx: &ctxt<'tcx>, err: &type_err<'tcx>) -> String {
/*!
*
* Explains the source of a type err in a short,
* human readable way. This is meant to be placed in
* parentheses after some larger message. You should
* also invoke `note_and_explain_type_err()` afterwards
* to present additional details, particularly when
* it comes to lifetime-related errors. */
fn tstore_to_closure(s: &TraitStore) -> String {
match s {
&UniqTraitStore => "proc".to_string(),
@ -4336,21 +4295,16 @@ pub fn provided_trait_methods<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
}
}
/// Helper for looking things up in the various maps that are populated during typeck::collect
/// (e.g., `cx.impl_or_trait_items`, `cx.tcache`, etc). All of these share the pattern that if the
/// id is local, it should have been loaded into the map by the `typeck::collect` phase. If the
/// def-id is external, then we have to go consult the crate loading code (and cache the result for
/// the future).
fn lookup_locally_or_in_crate_store<V:Clone>(
descr: &str,
def_id: ast::DefId,
map: &mut DefIdMap<V>,
load_external: || -> V) -> V {
/*!
* Helper for looking things up in the various maps
* that are populated during typeck::collect (e.g.,
* `cx.impl_or_trait_items`, `cx.tcache`, etc). All of these share
* the pattern that if the id is local, it should have
* been loaded into the map by the `typeck::collect` phase.
* If the def-id is external, then we have to go consult
* the crate loading code (and cache the result for the future).
*/
match map.get(&def_id).cloned() {
Some(v) => { return v; }
None => { }
@ -5222,19 +5176,16 @@ pub fn each_bound_trait_and_supertraits<'tcx>(tcx: &ctxt<'tcx>,
return true;
}
/// Given a type which must meet the builtin bounds and trait bounds, returns a set of lifetimes
/// which the type must outlive.
///
/// Requires that trait definitions have been processed.
pub fn required_region_bounds<'tcx>(tcx: &ctxt<'tcx>,
region_bounds: &[ty::Region],
builtin_bounds: BuiltinBounds,
trait_bounds: &[Rc<TraitRef<'tcx>>])
-> Vec<ty::Region>
{
/*!
* Given a type which must meet the builtin bounds and trait
* bounds, returns a set of lifetimes which the type must outlive.
*
* Requires that trait definitions have been processed.
*/
let mut all_bounds = Vec::new();
debug!("required_region_bounds(builtin_bounds={}, trait_bounds={})",
@ -5620,13 +5571,9 @@ impl Variance {
}
}
/// Construct a parameter environment suitable for static contexts or other contexts where there
/// are no free type/lifetime parameters in scope.
pub fn empty_parameter_environment<'tcx>() -> ParameterEnvironment<'tcx> {
/*!
* Construct a parameter environment suitable for static contexts
* or other contexts where there are no free type/lifetime
* parameters in scope.
*/
ty::ParameterEnvironment { free_substs: Substs::empty(),
bounds: VecPerParamSpace::empty(),
caller_obligations: VecPerParamSpace::empty(),
@ -5634,6 +5581,7 @@ pub fn empty_parameter_environment<'tcx>() -> ParameterEnvironment<'tcx> {
selection_cache: traits::SelectionCache::new(), }
}
/// See `ParameterEnvironment` struct def'n for details
pub fn construct_parameter_environment<'tcx>(
tcx: &ctxt<'tcx>,
span: Span,
@ -5641,7 +5589,6 @@ pub fn construct_parameter_environment<'tcx>(
free_id: ast::NodeId)
-> ParameterEnvironment<'tcx>
{
/*! See `ParameterEnvironment` struct def'n for details */
//
// Construct the free substs.
@ -5770,15 +5717,11 @@ impl BorrowKind {
}
}
/// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow
/// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a
/// mutability that is stronger than necessary so that it at least *would permit* the borrow in
/// question.
pub fn to_mutbl_lossy(self) -> ast::Mutability {
/*!
* Returns a mutability `m` such that an `&m T` pointer could
* be used to obtain this borrow kind. Because borrow kinds
* are richer than mutabilities, we sometimes have to pick a
* mutability that is stronger than necessary so that it at
* least *would permit* the borrow in question.
*/
match self {
MutBorrow => ast::MutMutable,
ImmBorrow => ast::MutImmutable,
@ -5943,6 +5886,8 @@ impl<'tcx> AutoDerefRef<'tcx> {
}
}
/// Replace any late-bound regions bound in `value` with free variants attached to scope-id
/// `scope_id`.
pub fn liberate_late_bound_regions<'tcx, HR>(
tcx: &ty::ctxt<'tcx>,
scope: region::CodeExtent,
@ -5950,31 +5895,23 @@ pub fn liberate_late_bound_regions<'tcx, HR>(
-> HR
where HR : HigherRankedFoldable<'tcx>
{
/*!
* Replace any late-bound regions bound in `value` with free variants
* attached to scope-id `scope_id`.
*/
replace_late_bound_regions(
tcx, value,
|br, _| ty::ReFree(ty::FreeRegion{scope: scope, bound_region: br})).0
}
/// Replace any late-bound regions bound in `value` with `'static`. Useful in trans but also
/// method lookup and a few other places where precise region relationships are not required.
pub fn erase_late_bound_regions<'tcx, HR>(
tcx: &ty::ctxt<'tcx>,
value: &HR)
-> HR
where HR : HigherRankedFoldable<'tcx>
{
/*!
* Replace any late-bound regions bound in `value` with `'static`.
* Useful in trans but also method lookup and a few other places
* where precise region relationships are not required.
*/
replace_late_bound_regions(tcx, value, |_, _| ty::ReStatic).0
}
/// Replaces the late-bound-regions in `value` that are bound by `value`.
pub fn replace_late_bound_regions<'tcx, HR>(
tcx: &ty::ctxt<'tcx>,
value: &HR,
@ -5982,10 +5919,6 @@ pub fn replace_late_bound_regions<'tcx, HR>(
-> (HR, FnvHashMap<ty::BoundRegion,ty::Region>)
where HR : HigherRankedFoldable<'tcx>
{
/*!
* Replaces the late-bound-regions in `value` that are bound by `value`.
*/
debug!("replace_late_bound_regions({})", value.repr(tcx));
let mut map = FnvHashMap::new();

View File

@ -8,33 +8,31 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Generalized type folding mechanism. The setup is a bit convoluted
* but allows for convenient usage. Let T be an instance of some
* "foldable type" (one which implements `TypeFoldable`) and F be an
* instance of a "folder" (a type which implements `TypeFolder`). Then
* the setup is intended to be:
*
* T.fold_with(F) --calls--> F.fold_T(T) --calls--> super_fold_T(F, T)
*
* This way, when you define a new folder F, you can override
* `fold_T()` to customize the behavior, and invoke `super_fold_T()`
* to get the original behavior. Meanwhile, to actually fold
* something, you can just write `T.fold_with(F)`, which is
* convenient. (Note that `fold_with` will also transparently handle
* things like a `Vec<T>` where T is foldable and so on.)
*
* In this ideal setup, the only function that actually *does*
* anything is `super_fold_T`, which traverses the type `T`. Moreover,
* `super_fold_T` should only ever call `T.fold_with()`.
*
* In some cases, we follow a degenerate pattern where we do not have
* a `fold_T` nor `super_fold_T` method. Instead, `T.fold_with`
* traverses the structure directly. This is suboptimal because the
* behavior cannot be overriden, but it's much less work to implement.
* If you ever *do* need an override that doesn't exist, it's not hard
* to convert the degenerate pattern into the proper thing.
*/
//! Generalized type folding mechanism. The setup is a bit convoluted
//! but allows for convenient usage. Let T be an instance of some
//! "foldable type" (one which implements `TypeFoldable`) and F be an
//! instance of a "folder" (a type which implements `TypeFolder`). Then
//! the setup is intended to be:
//!
//! T.fold_with(F) --calls--> F.fold_T(T) --calls--> super_fold_T(F, T)
//!
//! This way, when you define a new folder F, you can override
//! `fold_T()` to customize the behavior, and invoke `super_fold_T()`
//! to get the original behavior. Meanwhile, to actually fold
//! something, you can just write `T.fold_with(F)`, which is
//! convenient. (Note that `fold_with` will also transparently handle
//! things like a `Vec<T>` where T is foldable and so on.)
//!
//! In this ideal setup, the only function that actually *does*
//! anything is `super_fold_T`, which traverses the type `T`. Moreover,
//! `super_fold_T` should only ever call `T.fold_with()`.
//!
//! In some cases, we follow a degenerate pattern where we do not have
//! a `fold_T` nor `super_fold_T` method. Instead, `T.fold_with`
//! traverses the structure directly. This is suboptimal because the
//! behavior cannot be overriden, but it's much less work to implement.
//! If you ever *do* need an override that doesn't exist, it's not hard
//! to convert the degenerate pattern into the proper thing.
use middle::subst;
use middle::subst::VecPerParamSpace;

View File

@ -8,46 +8,44 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Conversion from AST representation of types to the ty.rs
* representation. The main routine here is `ast_ty_to_ty()`: each use
* is parameterized by an instance of `AstConv` and a `RegionScope`.
*
* The parameterization of `ast_ty_to_ty()` is because it behaves
* somewhat differently during the collect and check phases,
* particularly with respect to looking up the types of top-level
* items. In the collect phase, the crate context is used as the
* `AstConv` instance; in this phase, the `get_item_ty()` function
* triggers a recursive call to `ty_of_item()` (note that
* `ast_ty_to_ty()` will detect recursive types and report an error).
* In the check phase, when the FnCtxt is used as the `AstConv`,
* `get_item_ty()` just looks up the item type in `tcx.tcache`.
*
* The `RegionScope` trait controls what happens when the user does
* not specify a region in some location where a region is required
* (e.g., if the user writes `&Foo` as a type rather than `&'a Foo`).
* See the `rscope` module for more details.
*
* Unlike the `AstConv` trait, the region scope can change as we descend
* the type. This is to accommodate the fact that (a) fn types are binding
* scopes and (b) the default region may change. To understand case (a),
* consider something like:
*
* type foo = { x: &a.int, y: |&a.int| }
*
* The type of `x` is an error because there is no region `a` in scope.
* In the type of `y`, however, region `a` is considered a bound region
* as it does not already appear in scope.
*
* Case (b) says that if you have a type:
* type foo<'a> = ...;
* type bar = fn(&foo, &a.foo)
* The fully expanded version of type bar is:
* type bar = fn(&'foo &, &a.foo<'a>)
* Note that the self region for the `foo` defaulted to `&` in the first
* case but `&a` in the second. Basically, defaults that appear inside
* an rptr (`&r.T`) use the region `r` that appears in the rptr.
*/
//! Conversion from AST representation of types to the ty.rs
//! representation. The main routine here is `ast_ty_to_ty()`: each use
//! is parameterized by an instance of `AstConv` and a `RegionScope`.
//!
//! The parameterization of `ast_ty_to_ty()` is because it behaves
//! somewhat differently during the collect and check phases,
//! particularly with respect to looking up the types of top-level
//! items. In the collect phase, the crate context is used as the
//! `AstConv` instance; in this phase, the `get_item_ty()` function
//! triggers a recursive call to `ty_of_item()` (note that
//! `ast_ty_to_ty()` will detect recursive types and report an error).
//! In the check phase, when the FnCtxt is used as the `AstConv`,
//! `get_item_ty()` just looks up the item type in `tcx.tcache`.
//!
//! The `RegionScope` trait controls what happens when the user does
//! not specify a region in some location where a region is required
//! (e.g., if the user writes `&Foo` as a type rather than `&'a Foo`).
//! See the `rscope` module for more details.
//!
//! Unlike the `AstConv` trait, the region scope can change as we descend
//! the type. This is to accommodate the fact that (a) fn types are binding
//! scopes and (b) the default region may change. To understand case (a),
//! consider something like:
//!
//! type foo = { x: &a.int, y: |&a.int| }
//!
//! The type of `x` is an error because there is no region `a` in scope.
//! In the type of `y`, however, region `a` is considered a bound region
//! as it does not already appear in scope.
//!
//! Case (b) says that if you have a type:
//! type foo<'a> = ...;
//! type bar = fn(&foo, &a.foo)
//! The fully expanded version of type bar is:
//! type bar = fn(&'foo &, &a.foo<'a>)
//! Note that the self region for the `foo` defaulted to `&` in the first
//! case but `&a` in the second. Basically, defaults that appear inside
//! an rptr (`&r.T`) use the region `r` that appears in the rptr.
use middle::const_eval;
use middle::def;
use middle::resolve_lifetime as rl;
@ -202,6 +200,8 @@ pub fn opt_ast_region_to_region<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
r
}
/// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`,
/// returns an appropriate set of substitutions for this particular reference to `I`.
fn ast_path_substs_for_ty<'tcx,AC,RS>(
this: &AC,
rscope: &RS,
@ -212,12 +212,6 @@ fn ast_path_substs_for_ty<'tcx,AC,RS>(
-> Substs<'tcx>
where AC: AstConv<'tcx>, RS: RegionScope
{
/*!
* Given a path `path` that refers to an item `I` with the
* declared generics `decl_generics`, returns an appropriate
* set of substitutions for this particular reference to `I`.
*/
let tcx = this.tcx();
// ast_path_substs() is only called to convert paths that are
@ -423,6 +417,9 @@ pub fn instantiate_poly_trait_ref<'tcx,AC,RS>(
instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref, self_ty)
}
/// Instantiates the path for the given trait reference, assuming that it's bound to a valid trait
/// type. Returns the def_id for the defining trait. Fails if the type is a type other than a trait
/// type.
pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
rscope: &RS,
ast_trait_ref: &ast::TraitRef,
@ -431,12 +428,6 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
where AC: AstConv<'tcx>,
RS: RegionScope
{
/*!
* Instantiates the path for the given trait reference, assuming that
* it's bound to a valid trait type. Returns the def_id for the defining
* trait. Fails if the type is a type other than a trait type.
*/
match lookup_def_tcx(this.tcx(),
ast_trait_ref.path.span,
ast_trait_ref.ref_id) {
@ -1339,6 +1330,10 @@ pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
}
}
/// Given an existential type like `Foo+'a+Bar`, this routine converts the `'a` and `Bar` intos an
/// `ExistentialBounds` struct. The `main_trait_refs` argument specifies the `Foo` -- it is absent
/// for closures. Eventually this should all be normalized, I think, so that there is no "main
/// trait ref" and instead we just have a flat list of bounds as the existential type.
pub fn conv_existential_bounds<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
this: &AC,
rscope: &RS,
@ -1347,16 +1342,6 @@ pub fn conv_existential_bounds<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
ast_bounds: &[ast::TyParamBound])
-> ty::ExistentialBounds
{
/*!
* Given an existential type like `Foo+'a+Bar`, this routine
* converts the `'a` and `Bar` intos an `ExistentialBounds`
* struct. The `main_trait_refs` argument specifies the `Foo` --
* it is absent for closures. Eventually this should all be
* normalized, I think, so that there is no "main trait ref" and
* instead we just have a flat list of bounds as the existential
* type.
*/
let ast_bound_refs: Vec<&ast::TyParamBound> =
ast_bounds.iter().collect();
@ -1453,6 +1438,10 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx, AC, RS>(
}
}
/// Given the bounds on a type parameter / existential type, determines what single region bound
/// (if any) we can use to summarize this type. The basic idea is that we will use the bound the
/// user provided, if they provided one, and otherwise search the supertypes of trait bounds for
/// region bounds. It may be that we can derive no bound at all, in which case we return `None`.
pub fn compute_opt_region_bound<'tcx>(tcx: &ty::ctxt<'tcx>,
span: Span,
builtin_bounds: ty::BuiltinBounds,
@ -1460,16 +1449,6 @@ pub fn compute_opt_region_bound<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_bounds: &[Rc<ty::TraitRef<'tcx>>])
-> Option<ty::Region>
{
/*!
* Given the bounds on a type parameter / existential type,
* determines what single region bound (if any) we can use to
* summarize this type. The basic idea is that we will use the
* bound the user provided, if they provided one, and otherwise
* search the supertypes of trait bounds for region bounds. It may
* be that we can derive no bound at all, in which case we return
* `None`.
*/
if region_bounds.len() > 1 {
tcx.sess.span_err(
region_bounds[1].span,
@ -1516,6 +1495,9 @@ pub fn compute_opt_region_bound<'tcx>(tcx: &ty::ctxt<'tcx>,
return Some(r);
}
/// A version of `compute_opt_region_bound` for use where some region bound is required
/// (existential types, basically). Reports an error if no region bound can be derived and we are
/// in an `rscope` that does not provide a default.
fn compute_region_bound<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
this: &AC,
rscope: &RS,
@ -1525,13 +1507,6 @@ fn compute_region_bound<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
trait_bounds: &[Rc<ty::TraitRef<'tcx>>])
-> ty::Region
{
/*!
* A version of `compute_opt_region_bound` for use where some
* region bound is required (existential types,
* basically). Reports an error if no region bound can be derived
* and we are in an `rscope` that does not provide a default.
*/
match compute_opt_region_bound(this.tcx(), span, builtin_bounds,
region_bounds, trait_bounds) {
Some(r) => r,
@ -1555,17 +1530,13 @@ pub struct PartitionedBounds<'a> {
pub region_bounds: Vec<&'a ast::Lifetime>,
}
/// Divides a list of bounds from the AST into three groups: builtin bounds (Copy, Sized etc),
/// general trait bounds, and region bounds.
pub fn partition_bounds<'a>(tcx: &ty::ctxt,
_span: Span,
ast_bounds: &'a [&ast::TyParamBound])
-> PartitionedBounds<'a>
{
/*!
* Divides a list of bounds from the AST into three groups:
* builtin bounds (Copy, Sized etc), general trait bounds,
* and region bounds.
*/
let mut builtin_bounds = ty::empty_builtin_bounds();
let mut region_bounds = Vec::new();
let mut trait_bounds = Vec::new();

View File

@ -8,9 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Code for type-checking closure expressions.
*/
//! Code for type-checking closure expressions.
use super::check_fn;
use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation};

View File

@ -189,22 +189,17 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
///////////////////////////////////////////////////////////////////////////
//
/// Returns a set of substitutions for the method *receiver* where all type and region
/// parameters are instantiated with fresh variables. This substitution does not include any
/// parameters declared on the method itself.
///
/// Note that this substitution may include late-bound regions from the impl level. If so,
/// these are instantiated later in the `instantiate_method_sig` routine.
fn fresh_receiver_substs(&mut self,
self_ty: Ty<'tcx>,
pick: &probe::Pick<'tcx>)
-> (subst::Substs<'tcx>, MethodOrigin<'tcx>)
{
/*!
* Returns a set of substitutions for the method *receiver*
* where all type and region parameters are instantiated with
* fresh variables. This substitution does not include any
* parameters declared on the method itself.
*
* Note that this substitution may include late-bound regions
* from the impl level. If so, these are instantiated later in
* the `instantiate_method_sig` routine.
*/
match pick.kind {
probe::InherentImplPick(impl_def_id) => {
assert!(ty::impl_trait_ref(self.tcx(), impl_def_id).is_none(),
@ -478,14 +473,11 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
///////////////////////////////////////////////////////////////////////////
// RECONCILIATION
/// When we select a method with an `&mut self` receiver, we have to go convert any
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
/// respectively.
fn fixup_derefs_on_method_receiver_if_necessary(&self,
method_callee: &MethodCallee) {
/*!
* When we select a method with an `&mut self` receiver, we have to go
* convert any auto-derefs, indices, etc from `Deref` and `Index` into
* `DerefMut` and `IndexMut` respectively.
*/
let sig = match method_callee.ty.sty {
ty::ty_bare_fn(ref f) => f.sig.clone(),
ty::ty_closure(ref f) => f.sig.clone(),

View File

@ -8,119 +8,114 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Method lookup
Method lookup can be rather complex due to the interaction of a number
of factors, such as self types, autoderef, trait lookup, etc. This
file provides an overview of the process. More detailed notes are in
the code itself, naturally.
One way to think of method lookup is that we convert an expression of
the form:
receiver.method(...)
into a more explicit UFCS form:
Trait::method(ADJ(receiver), ...) // for a trait call
ReceiverType::method(ADJ(receiver), ...) // for an inherent method call
Here `ADJ` is some kind of adjustment, which is typically a series of
autoderefs and then possibly an autoref (e.g., `&**receiver`). However
we sometimes do other adjustments and coercions along the way, in
particular unsizing (e.g., converting from `[T, ..n]` to `[T]`).
## The Two Phases
Method lookup is divided into two major phases: probing (`probe.rs`)
and confirmation (`confirm.rs`). The probe phase is when we decide
what method to call and how to adjust the receiver. The confirmation
phase "applies" this selection, updating the side-tables, unifying
type variables, and otherwise doing side-effectful things.
One reason for this division is to be more amenable to caching. The
probe phase produces a "pick" (`probe::Pick`), which is designed to be
cacheable across method-call sites. Therefore, it does not include
inference variables or other information.
## Probe phase
The probe phase (`probe.rs`) decides what method is being called and
how to adjust the receiver.
### Steps
The first thing that the probe phase does is to create a series of
*steps*. This is done by progressively dereferencing the receiver type
until it cannot be deref'd anymore, as well as applying an optional
"unsize" step. So if the receiver has type `Rc<Box<[T, ..3]>>`, this
might yield:
Rc<Box<[T, ..3]>>
Box<[T, ..3]>
[T, ..3]
[T]
### Candidate assembly
We then search along those steps to create a list of *candidates*. A
`Candidate` is a method item that might plausibly be the method being
invoked. For each candidate, we'll derive a "transformed self type"
that takes into account explicit self.
Candidates are grouped into two kinds, inherent and extension.
**Inherent candidates** are those that are derived from the
type of the receiver itself. So, if you have a receiver of some
nominal type `Foo` (e.g., a struct), any methods defined within an
impl like `impl Foo` are inherent methods. Nothing needs to be
imported to use an inherent method, they are associated with the type
itself (note that inherent impls can only be defined in the same
module as the type itself).
FIXME: Inherent candidates are not always derived from impls. If you
have a trait object, such as a value of type `Box<ToString>`, then the
trait methods (`to_string()`, in this case) are inherently associated
with it. Another case is type parameters, in which case the methods of
their bounds are inherent. However, this part of the rules is subject
to change: when DST's "impl Trait for Trait" is complete, trait object
dispatch could be subsumed into trait matching, and the type parameter
behavior should be reconsidered in light of where clauses.
**Extension candidates** are derived from imported traits. If I have
the trait `ToString` imported, and I call `to_string()` on a value of
type `T`, then we will go off to find out whether there is an impl of
`ToString` for `T`. These kinds of method calls are called "extension
methods". They can be defined in any module, not only the one that
defined `T`. Furthermore, you must import the trait to call such a
method.
So, let's continue our example. Imagine that we were calling a method
`foo` with the receiver `Rc<Box<[T, ..3]>>` and there is a trait `Foo`
that defines it with `&self` for the type `Rc<U>` as well as a method
on the type `Box` that defines `Foo` but with `&mut self`. Then we
might have two candidates:
&Rc<Box<[T, ..3]>> from the impl of `Foo` for `Rc<U>` where `U=Box<T, ..3]>
&mut Box<[T, ..3]>> from the inherent impl on `Box<U>` where `U=[T, ..3]`
### Candidate search
Finally, to actually pick the method, we will search down the steps,
trying to match the receiver type against the candidate types. At
each step, we also consider an auto-ref and auto-mut-ref to see whether
that makes any of the candidates match. We pick the first step where
we find a match.
In the case of our example, the first step is `Rc<Box<[T, ..3]>>`,
which does not itself match any candidate. But when we autoref it, we
get the type `&Rc<Box<[T, ..3]>>` which does match. We would then
recursively consider all where-clauses that appear on the impl: if
those match (or we cannot rule out that they do), then this is the
method we would pick. Otherwise, we would continue down the series of
steps.
*/
//! # Method lookup
//!
//! Method lookup can be rather complex due to the interaction of a number
//! of factors, such as self types, autoderef, trait lookup, etc. This
//! file provides an overview of the process. More detailed notes are in
//! the code itself, naturally.
//!
//! One way to think of method lookup is that we convert an expression of
//! the form:
//!
//! receiver.method(...)
//!
//! into a more explicit UFCS form:
//!
//! Trait::method(ADJ(receiver), ...) // for a trait call
//! ReceiverType::method(ADJ(receiver), ...) // for an inherent method call
//!
//! Here `ADJ` is some kind of adjustment, which is typically a series of
//! autoderefs and then possibly an autoref (e.g., `&**receiver`). However
//! we sometimes do other adjustments and coercions along the way, in
//! particular unsizing (e.g., converting from `[T, ..n]` to `[T]`).
//!
//! ## The Two Phases
//!
//! Method lookup is divided into two major phases: probing (`probe.rs`)
//! and confirmation (`confirm.rs`). The probe phase is when we decide
//! what method to call and how to adjust the receiver. The confirmation
//! phase "applies" this selection, updating the side-tables, unifying
//! type variables, and otherwise doing side-effectful things.
//!
//! One reason for this division is to be more amenable to caching. The
//! probe phase produces a "pick" (`probe::Pick`), which is designed to be
//! cacheable across method-call sites. Therefore, it does not include
//! inference variables or other information.
//!
//! ## Probe phase
//!
//! The probe phase (`probe.rs`) decides what method is being called and
//! how to adjust the receiver.
//!
//! ### Steps
//!
//! The first thing that the probe phase does is to create a series of
//! *steps*. This is done by progressively dereferencing the receiver type
//! until it cannot be deref'd anymore, as well as applying an optional
//! "unsize" step. So if the receiver has type `Rc<Box<[T, ..3]>>`, this
//! might yield:
//!
//! Rc<Box<[T, ..3]>>
//! Box<[T, ..3]>
//! [T, ..3]
//! [T]
//!
//! ### Candidate assembly
//!
//! We then search along those steps to create a list of *candidates*. A
//! `Candidate` is a method item that might plausibly be the method being
//! invoked. For each candidate, we'll derive a "transformed self type"
//! that takes into account explicit self.
//!
//! Candidates are grouped into two kinds, inherent and extension.
//!
//! **Inherent candidates** are those that are derived from the
//! type of the receiver itself. So, if you have a receiver of some
//! nominal type `Foo` (e.g., a struct), any methods defined within an
//! impl like `impl Foo` are inherent methods. Nothing needs to be
//! imported to use an inherent method, they are associated with the type
//! itself (note that inherent impls can only be defined in the same
//! module as the type itself).
//!
//! FIXME: Inherent candidates are not always derived from impls. If you
//! have a trait object, such as a value of type `Box<ToString>`, then the
//! trait methods (`to_string()`, in this case) are inherently associated
//! with it. Another case is type parameters, in which case the methods of
//! their bounds are inherent. However, this part of the rules is subject
//! to change: when DST's "impl Trait for Trait" is complete, trait object
//! dispatch could be subsumed into trait matching, and the type parameter
//! behavior should be reconsidered in light of where clauses.
//!
//! **Extension candidates** are derived from imported traits. If I have
//! the trait `ToString` imported, and I call `to_string()` on a value of
//! type `T`, then we will go off to find out whether there is an impl of
//! `ToString` for `T`. These kinds of method calls are called "extension
//! methods". They can be defined in any module, not only the one that
//! defined `T`. Furthermore, you must import the trait to call such a
//! method.
//!
//! So, let's continue our example. Imagine that we were calling a method
//! `foo` with the receiver `Rc<Box<[T, ..3]>>` and there is a trait `Foo`
//! that defines it with `&self` for the type `Rc<U>` as well as a method
//! on the type `Box` that defines `Foo` but with `&mut self`. Then we
//! might have two candidates:
//!
//! &Rc<Box<[T, ..3]>> from the impl of `Foo` for `Rc<U>` where `U=Box<T, ..3]>
//! &mut Box<[T, ..3]>> from the inherent impl on `Box<U>` where `U=[T, ..3]`
//!
//! ### Candidate search
//!
//! Finally, to actually pick the method, we will search down the steps,
//! trying to match the receiver type against the candidate types. At
//! each step, we also consider an auto-ref and auto-mut-ref to see whether
//! that makes any of the candidates match. We pick the first step where
//! we find a match.
//!
//! In the case of our example, the first step is `Rc<Box<[T, ..3]>>`,
//! which does not itself match any candidate. But when we autoref it, we
//! get the type `&Rc<Box<[T, ..3]>>` which does match. We would then
//! recursively consider all where-clauses that appear on the impl: if
//! those match (or we cannot rule out that they do), then this is the
//! method we would pick. Otherwise, we would continue down the series of
//! steps.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! Method lookup: the secret sauce of Rust. See `doc.rs`. */
//! Method lookup: the secret sauce of Rust. See `doc.rs`.
use middle::subst;
use middle::subst::{Subst};
@ -56,6 +56,7 @@ pub enum CandidateSource {
type MethodIndex = uint; // just for doc purposes
/// Determines whether the type `self_ty` supports a method name `method_name` or not.
pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
method_name: ast::Name,
@ -63,10 +64,6 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr_id: ast::NodeId)
-> bool
{
/*!
* Determines whether the type `self_ty` supports a method name `method_name` or not.
*/
match probe::probe(fcx, span, method_name, self_ty, call_expr_id) {
Ok(_) => true,
Err(NoMatch(_)) => false,
@ -74,6 +71,20 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
/// Performs method lookup. If lookup is successful, it will return the callee and store an
/// appropriate adjustment for the self-expr. In some cases it may report an error (e.g., invoking
/// the `drop` method).
///
/// # Arguments
///
/// Given a method call like `foo.bar::<T1,...Tn>(...)`:
///
/// * `fcx`: the surrounding `FnCtxt` (!)
/// * `span`: the span for the method call
/// * `method_name`: the name of the method being called (`bar`)
/// * `self_ty`: the (unadjusted) type of the self expression (`foo`)
/// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`)
/// * `self_expr`: the self expression (`foo`)
pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
method_name: ast::Name,
@ -83,23 +94,6 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
self_expr: &ast::Expr)
-> Result<MethodCallee<'tcx>, MethodError>
{
/*!
* Performs method lookup. If lookup is successful, it will return the callee
* and store an appropriate adjustment for the self-expr. In some cases it may
* report an error (e.g., invoking the `drop` method).
*
* # Arguments
*
* Given a method call like `foo.bar::<T1,...Tn>(...)`:
*
* - `fcx`: the surrounding `FnCtxt` (!)
* - `span`: the span for the method call
* - `method_name`: the name of the method being called (`bar`)
* - `self_ty`: the (unadjusted) type of the self expression (`foo`)
* - `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`)
* - `self_expr`: the self expression (`foo`)
*/
debug!("lookup(method_name={}, self_ty={}, call_expr={}, self_expr={})",
method_name.repr(fcx.tcx()),
self_ty.repr(fcx.tcx()),
@ -124,6 +118,15 @@ pub fn lookup_in_trait<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
self_ty, opt_input_types)
}
/// `lookup_in_trait_adjusted` is used for overloaded operators. It does a very narrow slice of
/// what the normal probe/confirm path does. In particular, it doesn't really do any probing: it
/// simply constructs an obligation for a particular trait with the given self-type and checks
/// whether that trait is implemented.
///
/// FIXME(#18741) -- It seems likely that we can consolidate some of this code with the other
/// method-lookup code. In particular, autoderef on index is basically identical to autoderef with
/// normal probes, except that the test also looks for built-in indexing. Also, the second half of
/// this method is basically the same as confirmation.
pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
span: Span,
self_expr: Option<&'a ast::Expr>,
@ -134,21 +137,6 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
opt_input_types: Option<Vec<Ty<'tcx>>>)
-> Option<MethodCallee<'tcx>>
{
/*!
* `lookup_in_trait_adjusted` is used for overloaded operators. It
* does a very narrow slice of what the normal probe/confirm path
* does. In particular, it doesn't really do any probing: it
* simply constructs an obligation for a particular trait with the
* given self-type and checks whether that trait is implemented.
*
* FIXME(#18741) -- It seems likely that we can consolidate some of this
* code with the other method-lookup code. In particular,
* autoderef on index is basically identical to autoderef with
* normal probes, except that the test also looks for built-in
* indexing. Also, the second half of this method is basically
* the same as confirmation.
*/
debug!("lookup_in_trait_adjusted(self_ty={}, self_expr={}, m_name={}, trait_def_id={})",
self_ty.repr(fcx.tcx()),
self_expr.repr(fcx.tcx()),
@ -408,16 +396,13 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
/// Find method with name `method_name` defined in `trait_def_id` and return it, along with its
/// index (or `None`, if no such method).
fn trait_method<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId,
method_name: ast::Name)
-> Option<(uint, Rc<ty::Method<'tcx>>)>
{
/*!
* Find method with name `method_name` defined in `trait_def_id` and return it,
* along with its index (or `None`, if no such method).
*/
let trait_items = ty::trait_items(tcx, trait_def_id);
trait_items
.iter()

View File

@ -807,33 +807,26 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
})
}
/// Sometimes we get in a situation where we have multiple probes that are all impls of the
/// same trait, but we don't know which impl to use. In this case, since in all cases the
/// external interface of the method can be determined from the trait, it's ok not to decide.
/// We can basically just collapse all of the probes for various impls into one where-clause
/// probe. This will result in a pending obligation so when more type-info is available we can
/// make the final decision.
///
/// Example (`src/test/run-pass/method-two-trait-defer-resolution-1.rs`):
///
/// ```
/// trait Foo { ... }
/// impl Foo for Vec<int> { ... }
/// impl Foo for Vec<uint> { ... }
/// ```
///
/// Now imagine the receiver is `Vec<_>`. It doesn't really matter at this time which impl we
/// use, so it's ok to just commit to "using the method from the trait Foo".
fn collapse_candidates_to_trait_pick(&self,
probes: &[&Candidate<'tcx>])
-> Option<Pick<'tcx>> {
/*!
* Sometimes we get in a situation where we have multiple
* probes that are all impls of the same trait, but we don't
* know which impl to use. In this case, since in all cases
* the external interface of the method can be determined from
* the trait, it's ok not to decide. We can basically just
* collapse all of the probes for various impls into one
* where-clause probe. This will result in a pending
* obligation so when more type-info is available we can make
* the final decision.
*
* Example (`src/test/run-pass/method-two-trait-defer-resolution-1.rs`):
*
* ```
* trait Foo { ... }
* impl Foo for Vec<int> { ... }
* impl Foo for Vec<uint> { ... }
* ```
*
* Now imagine the receiver is `Vec<_>`. It doesn't really
* matter at this time which impl we use, so it's ok to just
* commit to "using the method from the trait Foo".
*/
// Do all probes correspond to the same trait?
let trait_data = match probes[0].to_trait_data() {
Some(data) => data,
@ -952,36 +945,27 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
subst::Substs::new(type_vars, region_placeholders)
}
/// Replace late-bound-regions bound by `value` with `'static` using
/// `ty::erase_late_bound_regions`.
///
/// This is only a reasonable thing to do during the *probe* phase, not the *confirm* phase, of
/// method matching. It is reasonable during the probe phase because we don't consider region
/// relationships at all. Therefore, we can just replace all the region variables with 'static
/// rather than creating fresh region variables. This is nice for two reasons:
///
/// 1. Because the numbers of the region variables would otherwise be fairly unique to this
/// particular method call, it winds up creating fewer types overall, which helps for memory
/// usage. (Admittedly, this is a rather small effect, though measureable.)
///
/// 2. It makes it easier to deal with higher-ranked trait bounds, because we can replace any
/// late-bound regions with 'static. Otherwise, if we were going to replace late-bound
/// regions with actual region variables as is proper, we'd have to ensure that the same
/// region got replaced with the same variable, which requires a bit more coordination
/// and/or tracking the substitution and
/// so forth.
fn erase_late_bound_regions<T>(&self, value: &T) -> T
where T : HigherRankedFoldable<'tcx>
{
/*!
* Replace late-bound-regions bound by `value` with `'static`
* using `ty::erase_late_bound_regions`.
*
* This is only a reasonable thing to do during the *probe*
* phase, not the *confirm* phase, of method matching. It is
* reasonable during the probe phase because we don't consider
* region relationships at all. Therefore, we can just replace
* all the region variables with 'static rather than creating
* fresh region variables. This is nice for two reasons:
*
* 1. Because the numbers of the region variables would
* otherwise be fairly unique to this particular method
* call, it winds up creating fewer types overall, which
* helps for memory usage. (Admittedly, this is a rather
* small effect, though measureable.)
*
* 2. It makes it easier to deal with higher-ranked trait
* bounds, because we can replace any late-bound regions
* with 'static. Otherwise, if we were going to replace
* late-bound regions with actual region variables as is
* proper, we'd have to ensure that the same region got
* replaced with the same variable, which requires a bit
* more coordination and/or tracking the substitution and
* so forth.
*/
ty::erase_late_bound_regions(self.tcx(), value)
}
}
@ -1000,16 +984,13 @@ fn impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
.and_then(|item| item.as_opt_method())
}
/// Find method with name `method_name` defined in `trait_def_id` and return it, along with its
/// index (or `None`, if no such method).
fn trait_method<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId,
method_name: ast::Name)
-> Option<(uint, Rc<ty::Method<'tcx>>)>
{
/*!
* Find method with name `method_name` defined in `trait_def_id` and return it,
* along with its index (or `None`, if no such method).
*/
let trait_items = ty::trait_items(tcx, trait_def_id);
trait_items
.iter()

View File

@ -486,6 +486,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
}
/// Helper used by check_bare_fn and check_expr_fn. Does the grungy work of checking a function
/// body and returns the function context used for that purpose, since in the case of a fn item
/// there is still a bit more to do.
///
/// * ...
/// * inherited: other fields inherited from the enclosing fn (if any)
fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
fn_style: ast::FnStyle,
fn_style_id: ast::NodeId,
@ -495,16 +501,6 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
body: &ast::Block,
inherited: &'a Inherited<'a, 'tcx>)
-> FnCtxt<'a, 'tcx> {
/*!
* Helper used by check_bare_fn and check_expr_fn. Does the
* grungy work of checking a function body and returns the
* function context used for that purpose, since in the case of a
* fn item there is still a bit more to do.
*
* - ...
* - inherited: other fields inherited from the enclosing fn (if any)
*/
let tcx = ccx.tcx;
let err_count_on_creation = tcx.sess.err_count();
@ -701,19 +697,17 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
}
}
/// Type checks a method body.
///
/// # Parameters
///
/// * `item_generics`: generics defined on the impl/trait that contains
/// the method
/// * `self_bound`: bound for the `Self` type parameter, if any
/// * `method`: the method definition
fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
item_generics: &ty::Generics<'tcx>,
method: &ast::Method) {
/*!
* Type checks a method body.
*
* # Parameters
* - `item_generics`: generics defined on the impl/trait that contains
* the method
* - `self_bound`: bound for the `Self` type parameter, if any
* - `method`: the method definition
*/
debug!("check_method_body(item_generics={}, method.id={})",
item_generics.repr(ccx.tcx),
method.id);
@ -1220,6 +1214,33 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
// parameters.
infcx.resolve_regions_and_report_errors();
/// Check that region bounds on impl method are the same as those on the trait. In principle,
/// it could be ok for there to be fewer region bounds on the impl method, but this leads to an
/// annoying corner case that is painful to handle (described below), so for now we can just
/// forbid it.
///
/// Example (see `src/test/compile-fail/regions-bound-missing-bound-in-impl.rs`):
///
/// ```
/// trait Foo<'a> {
/// fn method1<'b>();
/// fn method2<'b:'a>();
/// }
///
/// impl<'a> Foo<'a> for ... {
/// fn method1<'b:'a>() { .. case 1, definitely bad .. }
/// fn method2<'b>() { .. case 2, could be ok .. }
/// }
/// ```
///
/// The "definitely bad" case is case #1. Here, the impl adds an extra constraint not present
/// in the trait.
///
/// The "maybe bad" case is case #2. Here, the impl adds an extra constraint not present in the
/// trait. We could in principle allow this, but it interacts in a complex way with early/late
/// bound resolution of lifetimes. Basically the presence or absence of a lifetime bound
/// affects whether the lifetime is early/late bound, and right now the code breaks if the
/// trait has an early bound lifetime parameter and the method does not.
fn check_region_bounds_on_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
span: Span,
impl_m: &ty::Method<'tcx>,
@ -1230,37 +1251,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
impl_to_skol_substs: &Substs<'tcx>)
-> bool
{
/*!
Check that region bounds on impl method are the same as those
on the trait. In principle, it could be ok for there to be
fewer region bounds on the impl method, but this leads to an
annoying corner case that is painful to handle (described
below), so for now we can just forbid it.
Example (see
`src/test/compile-fail/regions-bound-missing-bound-in-impl.rs`):
trait Foo<'a> {
fn method1<'b>();
fn method2<'b:'a>();
}
impl<'a> Foo<'a> for ... {
fn method1<'b:'a>() { .. case 1, definitely bad .. }
fn method2<'b>() { .. case 2, could be ok .. }
}
The "definitely bad" case is case #1. Here, the impl adds an
extra constraint not present in the trait.
The "maybe bad" case is case #2. Here, the impl adds an extra
constraint not present in the trait. We could in principle
allow this, but it interacts in a complex way with early/late
bound resolution of lifetimes. Basically the presence or
absence of a lifetime bound affects whether the lifetime is
early/late bound, and right now the code breaks if the trait
has an early bound lifetime parameter and the method does not.
*/
@ -1768,23 +1758,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// Returns the type of `def_id` with all generics replaced by by fresh type/region variables.
/// Also returns the substitution from the type parameters on `def_id` to the fresh variables.
/// Registers any trait obligations specified on `def_id` at the same time.
///
/// Note that function is only intended to be used with types (notably, not impls). This is
/// because it doesn't do any instantiation of late-bound regions.
pub fn instantiate_type(&self,
span: Span,
def_id: ast::DefId)
-> TypeAndSubsts<'tcx>
{
/*!
* Returns the type of `def_id` with all generics replaced by
* by fresh type/region variables. Also returns the
* substitution from the type parameters on `def_id` to the
* fresh variables. Registers any trait obligations specified
* on `def_id` at the same time.
*
* Note that function is only intended to be used with types
* (notably, not impls). This is because it doesn't do any
* instantiation of late-bound regions.
*/
let polytype =
ty::lookup_item_type(self.tcx(), def_id);
let substs =
@ -1884,26 +1868,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// Fetch type of `expr` after applying adjustments that have been recorded in the fcx.
pub fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> {
/*!
* Fetch type of `expr` after applying adjustments that
* have been recorded in the fcx.
*/
let adjustments = self.inh.adjustments.borrow();
let adjustment = adjustments.get(&expr.id);
self.adjust_expr_ty(expr, adjustment)
}
/// Apply `adjustment` to the type of `expr`
pub fn adjust_expr_ty(&self,
expr: &ast::Expr,
adjustment: Option<&ty::AutoAdjustment<'tcx>>)
-> Ty<'tcx>
{
/*!
* Apply `adjustment` to the type of `expr`
*/
let raw_ty = self.expr_ty(expr);
let raw_ty = self.infcx().shallow_resolve(raw_ty);
ty::adjust_ty(self.tcx(),
@ -2011,16 +1988,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.infcx().report_mismatched_types(sp, e, a, err)
}
/// Registers an obligation for checking later, during regionck, that the type `ty` must
/// outlive the region `r`.
pub fn register_region_obligation(&self,
origin: infer::SubregionOrigin<'tcx>,
ty: Ty<'tcx>,
r: ty::Region)
{
/*!
* Registers an obligation for checking later, during
* regionck, that the type `ty` must outlive the region `r`.
*/
let mut region_obligations = self.inh.region_obligations.borrow_mut();
let region_obligation = RegionObligation { sub_region: r,
sup_type: ty,
@ -2043,31 +2017,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
/// type/region parameter was instantiated (`substs`), creates and registers suitable
/// trait/region obligations.
///
/// For example, if there is a function:
///
/// ```
/// fn foo<'a,T:'a>(...)
/// ```
///
/// and a reference:
///
/// ```
/// let f = foo;
/// ```
///
/// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
/// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
pub fn add_obligations_for_parameters(&self,
cause: traits::ObligationCause<'tcx>,
substs: &Substs<'tcx>,
generic_bounds: &ty::GenericBounds<'tcx>)
{
/*!
* Given a fully substituted set of bounds (`generic_bounds`),
* and the values with which each type/region parameter was
* instantiated (`substs`), creates and registers suitable
* trait/region obligations.
*
* For example, if there is a function:
*
* fn foo<'a,T:'a>(...)
*
* and a reference:
*
* let f = foo;
*
* Then we will create a fresh region variable `'$0` and a
* fresh type variable `$1` for `'a` and `T`. This routine
* will add a region obligation `$1:'$0` and register it
* locally.
*/
assert!(!generic_bounds.has_escaping_regions());
debug!("add_obligations_for_parameters(substs={}, generic_bounds={})",
@ -2158,22 +2130,17 @@ pub enum LvaluePreference {
NoPreference
}
/// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop` to decide
/// whether to terminate the loop. Returns the final type and number of derefs that it performed.
///
/// Note: this method does not modify the adjustments table. The caller is responsible for
/// inserting an AutoAdjustment record into the `fcx` using one of the suitable methods.
pub fn autoderef<'a, 'tcx, T>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
base_ty: Ty<'tcx>,
expr_id: Option<ast::NodeId>,
mut lvalue_pref: LvaluePreference,
should_stop: |Ty<'tcx>, uint| -> Option<T>)
-> (Ty<'tcx>, uint, Option<T>) {
/*!
* Executes an autoderef loop for the type `t`. At each step, invokes
* `should_stop` to decide whether to terminate the loop. Returns
* the final type and number of derefs that it performed.
*
* Note: this method does not modify the adjustments table. The caller is
* responsible for inserting an AutoAdjustment record into the `fcx`
* using one of the suitable methods.
*/
let mut t = base_ty;
for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) {
let resolved_t = structurally_resolved_type(fcx, sp, t);
@ -2304,19 +2271,14 @@ fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
make_overloaded_lvalue_return_type(fcx, method_call, method)
}
/// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait returns a type of `&T`, but the
/// actual type we assign to the *expression* is `T`. So this function just peels off the return
/// type by one layer to yield `T`. It also inserts the `method-callee` into the method map.
fn make_overloaded_lvalue_return_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
method_call: Option<MethodCall>,
method: Option<MethodCallee<'tcx>>)
-> Option<ty::mt<'tcx>>
{
/*!
* For the overloaded lvalue expressions (`*x`, `x[3]`), the trait
* returns a type of `&T`, but the actual type we assign to the
* *expression* is `T`. So this function just peels off the return
* type by one layer to yield `T`. It also inserts the
* `method-callee` into the method map.
*/
match method {
Some(method) => {
let ref_ty = ty::ty_fn_ret(method.ty);
@ -2378,6 +2340,8 @@ fn autoderef_for_index<'a, 'tcx, T>(fcx: &FnCtxt<'a, 'tcx>,
}
}
/// Autoderefs `base_expr`, looking for a `Slice` impl. If it finds one, installs the relevant
/// method info and returns the result type (else None).
fn try_overloaded_slice<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
method_call: MethodCall,
expr: &ast::Expr,
@ -2388,12 +2352,6 @@ fn try_overloaded_slice<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
mutbl: ast::Mutability)
-> Option<Ty<'tcx>> // return type is result of slice
{
/*!
* Autoderefs `base_expr`, looking for a `Slice` impl. If it
* finds one, installs the relevant method info and returns the
* result type (else None).
*/
let lvalue_pref = match mutbl {
ast::MutMutable => PreferMutLvalue,
ast::MutImmutable => NoPreference
@ -2434,6 +2392,8 @@ fn try_overloaded_slice<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
})
}
/// Checks for a `Slice` (or `SliceMut`) impl at the relevant level of autoderef. If it finds one,
/// installs method info and returns type of method (else None).
fn try_overloaded_slice_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
method_call: MethodCall,
expr: &ast::Expr,
@ -2446,12 +2406,6 @@ fn try_overloaded_slice_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// result type is type of method being called
-> Option<Ty<'tcx>>
{
/*!
* Checks for a `Slice` (or `SliceMut`) impl at the relevant level
* of autoderef. If it finds one, installs method info and returns
* type of method (else None).
*/
let method = if mutbl == ast::MutMutable {
// Try `SliceMut` first, if preferred.
match fcx.tcx().lang_items.slice_mut_trait() {
@ -2508,6 +2462,10 @@ fn try_overloaded_slice_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
})
}
/// To type-check `base_expr[index_expr]`, we progressively autoderef (and otherwise adjust)
/// `base_expr`, looking for a type which either supports builtin indexing or overloaded indexing.
/// This loop implements one step in that search; the autoderef loop is implemented by
/// `autoderef_for_index`.
fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
method_call: MethodCall,
expr: &ast::Expr,
@ -2517,13 +2475,6 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
lvalue_pref: LvaluePreference)
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
{
/*!
* To type-check `base_expr[index_expr]`, we progressively autoderef (and otherwise adjust)
* `base_expr`, looking for a type which either supports builtin indexing or overloaded
* indexing. This loop implements one step in that search; the autoderef loop is implemented
* by `autoderef_for_index`.
*/
debug!("try_index_step(expr={}, base_expr.id={}, adjusted_ty={}, adjustment={})",
expr.repr(fcx.tcx()),
base_expr.repr(fcx.tcx()),
@ -2710,6 +2661,8 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
/// Generic function that factors out common logic from function calls, method calls and overloaded
/// operators.
fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
sp: Span,
fn_inputs: &[Ty<'tcx>],
@ -2718,12 +2671,6 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
deref_args: DerefArgs,
variadic: bool,
tuple_arguments: TupleArgumentsFlag) {
/*!
*
* Generic function that factors out common logic from
* function calls, method calls and overloaded operators.
*/
let tcx = fcx.ccx.tcx;
// Grab the argument types, supplying fresh type variables
@ -5287,6 +5234,15 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
/// Finds the parameters that the user provided and adds them to `substs`. If too many
/// parameters are provided, then reports an error and clears the output vector.
///
/// We clear the output vector because that will cause the `adjust_XXX_parameters()` later to
/// use inference variables. This seems less likely to lead to derived errors.
///
/// Note that we *do not* check for *too few* parameters here. Due to the presence of defaults
/// etc that is more complicated. I wanted however to do the reporting of *too many* parameters
/// here because we can easily use the precise span of the N+1'th parameter.
fn push_explicit_parameters_from_segment_to_substs<'a, 'tcx>(
fcx: &FnCtxt<'a, 'tcx>,
space: subst::ParamSpace,
@ -5296,23 +5252,6 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
segment: &ast::PathSegment,
substs: &mut Substs<'tcx>)
{
/*!
* Finds the parameters that the user provided and adds them
* to `substs`. If too many parameters are provided, then
* reports an error and clears the output vector.
*
* We clear the output vector because that will cause the
* `adjust_XXX_parameters()` later to use inference
* variables. This seems less likely to lead to derived
* errors.
*
* Note that we *do not* check for *too few* parameters here.
* Due to the presence of defaults etc that is more
* complicated. I wanted however to do the reporting of *too
* many* parameters here because we can easily use the precise
* span of the N+1'th parameter.
*/
match segment.parameters {
ast::AngleBracketedParameters(ref data) => {
push_explicit_angle_bracketed_parameters_from_segment_to_substs(
@ -5371,6 +5310,12 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
/// As with
/// `push_explicit_angle_bracketed_parameters_from_segment_to_substs`,
/// but intended for `Foo(A,B) -> C` form. This expands to
/// roughly the same thing as `Foo<(A,B),C>`. One important
/// difference has to do with the treatment of anonymous
/// regions, which are translated into bound regions (NYI).
fn push_explicit_parenthesized_parameters_from_segment_to_substs<'a, 'tcx>(
fcx: &FnCtxt<'a, 'tcx>,
space: subst::ParamSpace,
@ -5379,15 +5324,6 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
data: &ast::ParenthesizedParameterData,
substs: &mut Substs<'tcx>)
{
/*!
* As with
* `push_explicit_angle_bracketed_parameters_from_segment_to_substs`,
* but intended for `Foo(A,B) -> C` form. This expands to
* roughly the same thing as `Foo<(A,B),C>`. One important
* difference has to do with the treatment of anonymous
* regions, which are translated into bound regions (NYI).
*/
let type_count = type_defs.len(space);
if type_count < 2 {
span_err!(fcx.tcx().sess, span, E0167,

View File

@ -8,115 +8,111 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
The region check is a final pass that runs over the AST after we have
inferred the type constraints but before we have actually finalized
the types. Its purpose is to embed a variety of region constraints.
Inserting these constraints as a separate pass is good because (1) it
localizes the code that has to do with region inference and (2) often
we cannot know what constraints are needed until the basic types have
been inferred.
### Interaction with the borrow checker
In general, the job of the borrowck module (which runs later) is to
check that all soundness criteria are met, given a particular set of
regions. The job of *this* module is to anticipate the needs of the
borrow checker and infer regions that will satisfy its requirements.
It is generally true that the inference doesn't need to be sound,
meaning that if there is a bug and we inferred bad regions, the borrow
checker should catch it. This is not entirely true though; for
example, the borrow checker doesn't check subtyping, and it doesn't
check that region pointers are always live when they are used. It
might be worthwhile to fix this so that borrowck serves as a kind of
verification step -- that would add confidence in the overall
correctness of the compiler, at the cost of duplicating some type
checks and effort.
### Inferring the duration of borrows, automatic and otherwise
Whenever we introduce a borrowed pointer, for example as the result of
a borrow expression `let x = &data`, the lifetime of the pointer `x`
is always specified as a region inference variable. `regionck` has the
job of adding constraints such that this inference variable is as
narrow as possible while still accommodating all uses (that is, every
dereference of the resulting pointer must be within the lifetime).
#### Reborrows
Generally speaking, `regionck` does NOT try to ensure that the data
`data` will outlive the pointer `x`. That is the job of borrowck. The
one exception is when "re-borrowing" the contents of another borrowed
pointer. For example, imagine you have a borrowed pointer `b` with
lifetime L1 and you have an expression `&*b`. The result of this
expression will be another borrowed pointer with lifetime L2 (which is
an inference variable). The borrow checker is going to enforce the
constraint that L2 < L1, because otherwise you are re-borrowing data
for a lifetime larger than the original loan. However, without the
routines in this module, the region inferencer would not know of this
dependency and thus it might infer the lifetime of L2 to be greater
than L1 (issue #3148).
There are a number of troublesome scenarios in the tests
`region-dependent-*.rs`, but here is one example:
struct Foo { i: int }
struct Bar { foo: Foo }
fn get_i(x: &'a Bar) -> &'a int {
let foo = &x.foo; // Lifetime L1
&foo.i // Lifetime L2
}
Note that this comes up either with `&` expressions, `ref`
bindings, and `autorefs`, which are the three ways to introduce
a borrow.
The key point here is that when you are borrowing a value that
is "guaranteed" by a borrowed pointer, you must link the
lifetime of that borrowed pointer (L1, here) to the lifetime of
the borrow itself (L2). What do I mean by "guaranteed" by a
borrowed pointer? I mean any data that is reached by first
dereferencing a borrowed pointer and then either traversing
interior offsets or owned pointers. We say that the guarantor
of such data it the region of the borrowed pointer that was
traversed. This is essentially the same as the ownership
relation, except that a borrowed pointer never owns its
contents.
### Inferring borrow kinds for upvars
Whenever there is a closure expression, we need to determine how each
upvar is used. We do this by initially assigning each upvar an
immutable "borrow kind" (see `ty::BorrowKind` for details) and then
"escalating" the kind as needed. The borrow kind proceeds according to
the following lattice:
ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
So, for example, if we see an assignment `x = 5` to an upvar `x`, we
will promote its borrow kind to mutable borrow. If we see an `&mut x`
we'll do the same. Naturally, this applies not just to the upvar, but
to everything owned by `x`, so the result is the same for something
like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
struct). These adjustments are performed in
`adjust_upvar_borrow_kind()` (you can trace backwards through the code
from there).
The fact that we are inferring borrow kinds as we go results in a
semi-hacky interaction with mem-categorization. In particular,
mem-categorization will query the current borrow kind as it
categorizes, and we'll return the *current* value, but this may get
adjusted later. Therefore, in this module, we generally ignore the
borrow kind (and derived mutabilities) that are returned from
mem-categorization, since they may be inaccurate. (Another option
would be to use a unification scheme, where instead of returning a
concrete borrow kind like `ty::ImmBorrow`, we return a
`ty::InferBorrow(upvar_id)` or something like that, but this would
then mean that all later passes would have to check for these figments
and report an error, and it just seems like more mess in the end.)
*/
//! The region check is a final pass that runs over the AST after we have
//! inferred the type constraints but before we have actually finalized
//! the types. Its purpose is to embed a variety of region constraints.
//! Inserting these constraints as a separate pass is good because (1) it
//! localizes the code that has to do with region inference and (2) often
//! we cannot know what constraints are needed until the basic types have
//! been inferred.
//!
//! ### Interaction with the borrow checker
//!
//! In general, the job of the borrowck module (which runs later) is to
//! check that all soundness criteria are met, given a particular set of
//! regions. The job of *this* module is to anticipate the needs of the
//! borrow checker and infer regions that will satisfy its requirements.
//! It is generally true that the inference doesn't need to be sound,
//! meaning that if there is a bug and we inferred bad regions, the borrow
//! checker should catch it. This is not entirely true though; for
//! example, the borrow checker doesn't check subtyping, and it doesn't
//! check that region pointers are always live when they are used. It
//! might be worthwhile to fix this so that borrowck serves as a kind of
//! verification step -- that would add confidence in the overall
//! correctness of the compiler, at the cost of duplicating some type
//! checks and effort.
//!
//! ### Inferring the duration of borrows, automatic and otherwise
//!
//! Whenever we introduce a borrowed pointer, for example as the result of
//! a borrow expression `let x = &data`, the lifetime of the pointer `x`
//! is always specified as a region inference variable. `regionck` has the
//! job of adding constraints such that this inference variable is as
//! narrow as possible while still accommodating all uses (that is, every
//! dereference of the resulting pointer must be within the lifetime).
//!
//! #### Reborrows
//!
//! Generally speaking, `regionck` does NOT try to ensure that the data
//! `data` will outlive the pointer `x`. That is the job of borrowck. The
//! one exception is when "re-borrowing" the contents of another borrowed
//! pointer. For example, imagine you have a borrowed pointer `b` with
//! lifetime L1 and you have an expression `&*b`. The result of this
//! expression will be another borrowed pointer with lifetime L2 (which is
//! an inference variable). The borrow checker is going to enforce the
//! constraint that L2 < L1, because otherwise you are re-borrowing data
//! for a lifetime larger than the original loan. However, without the
//! routines in this module, the region inferencer would not know of this
//! dependency and thus it might infer the lifetime of L2 to be greater
//! than L1 (issue #3148).
//!
//! There are a number of troublesome scenarios in the tests
//! `region-dependent-*.rs`, but here is one example:
//!
//! struct Foo { i: int }
//! struct Bar { foo: Foo }
//! fn get_i(x: &'a Bar) -> &'a int {
//! let foo = &x.foo; // Lifetime L1
//! &foo.i // Lifetime L2
//! }
//!
//! Note that this comes up either with `&` expressions, `ref`
//! bindings, and `autorefs`, which are the three ways to introduce
//! a borrow.
//!
//! The key point here is that when you are borrowing a value that
//! is "guaranteed" by a borrowed pointer, you must link the
//! lifetime of that borrowed pointer (L1, here) to the lifetime of
//! the borrow itself (L2). What do I mean by "guaranteed" by a
//! borrowed pointer? I mean any data that is reached by first
//! dereferencing a borrowed pointer and then either traversing
//! interior offsets or owned pointers. We say that the guarantor
//! of such data it the region of the borrowed pointer that was
//! traversed. This is essentially the same as the ownership
//! relation, except that a borrowed pointer never owns its
//! contents.
//!
//! ### Inferring borrow kinds for upvars
//!
//! Whenever there is a closure expression, we need to determine how each
//! upvar is used. We do this by initially assigning each upvar an
//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then
//! "escalating" the kind as needed. The borrow kind proceeds according to
//! the following lattice:
//!
//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
//!
//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
//! will promote its borrow kind to mutable borrow. If we see an `&mut x`
//! we'll do the same. Naturally, this applies not just to the upvar, but
//! to everything owned by `x`, so the result is the same for something
//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
//! struct). These adjustments are performed in
//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
//! from there).
//!
//! The fact that we are inferring borrow kinds as we go results in a
//! semi-hacky interaction with mem-categorization. In particular,
//! mem-categorization will query the current borrow kind as it
//! categorizes, and we'll return the *current* value, but this may get
//! adjusted later. Therefore, in this module, we generally ignore the
//! borrow kind (and derived mutabilities) that are returned from
//! mem-categorization, since they may be inaccurate. (Another option
//! would be to use a unification scheme, where instead of returning a
//! concrete borrow kind like `ty::ImmBorrow`, we return a
//! `ty::InferBorrow(upvar_id)` or something like that, but this would
//! then mean that all later passes would have to check for these figments
//! and report an error, and it just seems like more mess in the end.)
use middle::def;
use middle::mem_categorization as mc;
@ -177,15 +173,11 @@ pub fn regionck_fn(fcx: &FnCtxt, id: ast::NodeId, blk: &ast::Block) {
fcx.infcx().resolve_regions_and_report_errors();
}
/// Checks that the types in `component_tys` are well-formed. This will add constraints into the
/// region graph. Does *not* run `resolve_regions_and_report_errors` and so forth.
pub fn regionck_ensure_component_tys_wf<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
component_tys: &[Ty<'tcx>]) {
/*!
* Checks that the types in `component_tys` are well-formed.
* This will add constraints into the region graph.
* Does *not* run `resolve_regions_and_report_errors` and so forth.
*/
let mut rcx = Rcx::new(fcx, 0);
for &component_ty in component_tys.iter() {
// Check that each type outlives the empty region. Since the
@ -239,12 +231,8 @@ pub struct Rcx<'a, 'tcx: 'a> {
maybe_links: MaybeLinkMap<'tcx>
}
/// Returns the validity region of `def` -- that is, how long is `def` valid?
fn region_of_def(fcx: &FnCtxt, def: def::Def) -> ty::Region {
/*!
* Returns the validity region of `def` -- that is, how long
* is `def` valid?
*/
let tcx = fcx.tcx();
match def {
def::DefLocal(node_id) => {
@ -283,35 +271,30 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
old_scope
}
/// Try to resolve the type for the given node, returning t_err if an error results. Note that
/// we never care about the details of the error, the same error will be detected and reported
/// in the writeback phase.
///
/// Note one important point: we do not attempt to resolve *region variables* here. This is
/// because regionck is essentially adding constraints to those region variables and so may yet
/// influence how they are resolved.
///
/// Consider this silly example:
///
/// ```
/// fn borrow(x: &int) -> &int {x}
/// fn foo(x: @int) -> int { // block: B
/// let b = borrow(x); // region: <R0>
/// *b
/// }
/// ```
///
/// Here, the region of `b` will be `<R0>`. `<R0>` is constrainted to be some subregion of the
/// block B and some superregion of the call. If we forced it now, we'd choose the smaller
/// region (the call). But that would make the *b illegal. Since we don't resolve, the type
/// of b will be `&<R0>.int` and then `*b` will require that `<R0>` be bigger than the let and
/// the `*b` expression, so we will effectively resolve `<R0>` to be the block B.
pub fn resolve_type(&self, unresolved_ty: Ty<'tcx>) -> Ty<'tcx> {
/*!
* Try to resolve the type for the given node, returning
* t_err if an error results. Note that we never care
* about the details of the error, the same error will be
* detected and reported in the writeback phase.
*
* Note one important point: we do not attempt to resolve
* *region variables* here. This is because regionck is
* essentially adding constraints to those region variables
* and so may yet influence how they are resolved.
*
* Consider this silly example:
*
* fn borrow(x: &int) -> &int {x}
* fn foo(x: @int) -> int { // block: B
* let b = borrow(x); // region: <R0>
* *b
* }
*
* Here, the region of `b` will be `<R0>`. `<R0>` is
* constrainted to be some subregion of the block B and some
* superregion of the call. If we forced it now, we'd choose
* the smaller region (the call). But that would make the *b
* illegal. Since we don't resolve, the type of b will be
* `&<R0>.int` and then `*b` will require that `<R0>` be
* bigger than the let and the `*b` expression, so we will
* effectively resolve `<R0>` to be the block B.
*/
match resolve_type(self.fcx.infcx(), None, unresolved_ty,
resolve_and_force_all_but_regions) {
Ok(t) => t,
@ -384,25 +367,19 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
}
}
/// This method populates the region map's `free_region_map`. It walks over the transformed
/// argument and return types for each function just before we check the body of that function,
/// looking for types where you have a borrowed pointer to other borrowed data (e.g., `&'a &'b
/// [uint]`. We do not allow references to outlive the things they point at, so we can assume
/// that `'a <= 'b`. This holds for both the argument and return types, basically because, on
/// the caller side, the caller is responsible for checking that the type of every expression
/// (including the actual values for the arguments, as well as the return type of the fn call)
/// is well-formed.
///
/// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs`
fn relate_free_regions(&mut self,
fn_sig_tys: &[Ty<'tcx>],
body_id: ast::NodeId) {
/*!
* This method populates the region map's `free_region_map`.
* It walks over the transformed argument and return types for
* each function just before we check the body of that
* function, looking for types where you have a borrowed
* pointer to other borrowed data (e.g., `&'a &'b [uint]`. We
* do not allow references to outlive the things they point
* at, so we can assume that `'a <= 'b`. This holds for both
* the argument and return types, basically because, on the caller
* side, the caller is responsible for checking that the type of
* every expression (including the actual values for the arguments,
* as well as the return type of the fn call) is well-formed.
*
* Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs`
*/
debug!("relate_free_regions >>");
let tcx = self.tcx();
@ -921,19 +898,15 @@ fn check_expr_fn_block(rcx: &mut Rcx,
_ => {}
}
/// Make sure that the type of all free variables referenced inside a closure/proc outlive the
/// closure/proc's lifetime bound. This is just a special case of the usual rules about closed
/// over values outliving the object's lifetime bound.
fn ensure_free_variable_types_outlive_closure_bound(
rcx: &mut Rcx,
bounds: ty::ExistentialBounds,
expr: &ast::Expr,
freevars: &[ty::Freevar])
{
/*!
* Make sure that the type of all free variables referenced
* inside a closure/proc outlive the closure/proc's lifetime
* bound. This is just a special case of the usual rules about
* closed over values outliving the object's lifetime bound.
*/
let tcx = rcx.fcx.ccx.tcx;
debug!("ensure_free_variable_types_outlive_closure_bound({}, {})",
@ -984,18 +957,14 @@ fn check_expr_fn_block(rcx: &mut Rcx,
}
}
/// Make sure that all free variables referenced inside the closure outlive the closure's
/// lifetime bound. Also, create an entry in the upvar_borrows map with a region.
fn constrain_free_variables_in_by_ref_closure(
rcx: &mut Rcx,
region_bound: ty::Region,
expr: &ast::Expr,
freevars: &[ty::Freevar])
{
/*!
* Make sure that all free variables referenced inside the
* closure outlive the closure's lifetime bound. Also, create
* an entry in the upvar_borrows map with a region.
*/
let tcx = rcx.fcx.ccx.tcx;
let infcx = rcx.fcx.infcx();
debug!("constrain_free_variables({}, {})",
@ -1183,15 +1152,12 @@ fn constrain_call<'a, I: Iterator<&'a ast::Expr>>(rcx: &mut Rcx,
}
}
/// Invoked on any auto-dereference that occurs. Checks that if this is a region pointer being
/// dereferenced, the lifetime of the pointer includes the deref expr.
fn constrain_autoderefs<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
deref_expr: &ast::Expr,
derefs: uint,
mut derefd_ty: Ty<'tcx>) {
/*!
* Invoked on any auto-dereference that occurs. Checks that if
* this is a region pointer being dereferenced, the lifetime of
* the pointer includes the deref expr.
*/
let r_deref_expr = ty::ReScope(CodeExtent::from_node_id(deref_expr.id));
for i in range(0u, derefs) {
debug!("constrain_autoderefs(deref_expr=?, derefd_ty={}, derefs={}/{}",
@ -1259,16 +1225,12 @@ pub fn mk_subregion_due_to_dereference(rcx: &mut Rcx,
}
/// Invoked on any index expression that occurs. Checks that if this is a slice being indexed, the
/// lifetime of the pointer includes the deref expr.
fn constrain_index<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
index_expr: &ast::Expr,
indexed_ty: Ty<'tcx>)
{
/*!
* Invoked on any index expression that occurs. Checks that if
* this is a slice being indexed, the lifetime of the pointer
* includes the deref expr.
*/
debug!("constrain_index(index_expr=?, indexed_ty={}",
rcx.fcx.infcx().ty_to_string(indexed_ty));
@ -1286,18 +1248,14 @@ fn constrain_index<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
}
}
/// Guarantees that any lifetimes which appear in the type of the node `id` (after applying
/// adjustments) are valid for at least `minimum_lifetime`
fn type_of_node_must_outlive<'a, 'tcx>(
rcx: &mut Rcx<'a, 'tcx>,
origin: infer::SubregionOrigin<'tcx>,
id: ast::NodeId,
minimum_lifetime: ty::Region)
{
/*!
* Guarantees that any lifetimes which appear in the type of
* the node `id` (after applying adjustments) are valid for at
* least `minimum_lifetime`
*/
let tcx = rcx.fcx.tcx();
// Try to resolve the type. If we encounter an error, then typeck
@ -1314,14 +1272,10 @@ fn type_of_node_must_outlive<'a, 'tcx>(
type_must_outlive(rcx, origin, ty, minimum_lifetime);
}
/// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the
/// resulting pointer is linked to the lifetime of its guarantor (if any).
fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr,
mutability: ast::Mutability, base: &ast::Expr) {
/*!
* Computes the guarantor for an expression `&base` and then
* ensures that the lifetime of the resulting pointer is linked
* to the lifetime of its guarantor (if any).
*/
debug!("link_addr_of(base=?)");
let cmt = {
@ -1331,13 +1285,10 @@ fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr,
link_region_from_node_type(rcx, expr.span, expr.id, mutability, cmt);
}
/// Computes the guarantors for any ref bindings in a `let` and
/// then ensures that the lifetime of the resulting pointer is
/// linked to the lifetime of the initialization expression.
fn link_local(rcx: &Rcx, local: &ast::Local) {
/*!
* Computes the guarantors for any ref bindings in a `let` and
* then ensures that the lifetime of the resulting pointer is
* linked to the lifetime of the initialization expression.
*/
debug!("regionck::for_local()");
let init_expr = match local.init {
None => { return; }
@ -1348,12 +1299,10 @@ fn link_local(rcx: &Rcx, local: &ast::Local) {
link_pattern(rcx, mc, discr_cmt, &*local.pat);
}
/// Computes the guarantors for any ref bindings in a match and
/// then ensures that the lifetime of the resulting pointer is
/// linked to the lifetime of its guarantor (if any).
fn link_match(rcx: &Rcx, discr: &ast::Expr, arms: &[ast::Arm]) {
/*!
* Computes the guarantors for any ref bindings in a match and
* then ensures that the lifetime of the resulting pointer is
* linked to the lifetime of its guarantor (if any).
*/
debug!("regionck::for_match()");
let mc = mc::MemCategorizationContext::new(rcx);
@ -1366,15 +1315,12 @@ fn link_match(rcx: &Rcx, discr: &ast::Expr, arms: &[ast::Arm]) {
}
}
/// Link lifetimes of any ref bindings in `root_pat` to the pointers found in the discriminant, if
/// needed.
fn link_pattern<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
mc: mc::MemCategorizationContext<Rcx<'a, 'tcx>>,
discr_cmt: mc::cmt<'tcx>,
root_pat: &ast::Pat) {
/*!
* Link lifetimes of any ref bindings in `root_pat` to
* the pointers found in the discriminant, if needed.
*/
let _ = mc.cat_pattern(discr_cmt, root_pat, |mc, sub_cmt, sub_pat| {
match sub_pat.node {
// `ref x` pattern
@ -1400,14 +1346,12 @@ fn link_pattern<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
});
}
/// Link lifetime of borrowed pointer resulting from autoref to lifetimes in the value being
/// autoref'd.
fn link_autoref(rcx: &Rcx,
expr: &ast::Expr,
autoderefs: uint,
autoref: &ty::AutoRef) {
/*!
* Link lifetime of borrowed pointer resulting from autoref
* to lifetimes in the value being autoref'd.
*/
debug!("link_autoref(autoref={})", autoref);
let mc = mc::MemCategorizationContext::new(rcx);
@ -1424,15 +1368,11 @@ fn link_autoref(rcx: &Rcx,
}
}
/// Computes the guarantor for cases where the `expr` is being passed by implicit reference and
/// must outlive `callee_scope`.
fn link_by_ref(rcx: &Rcx,
expr: &ast::Expr,
callee_scope: CodeExtent) {
/*!
* Computes the guarantor for cases where the `expr` is
* being passed by implicit reference and must outlive
* `callee_scope`.
*/
let tcx = rcx.tcx();
debug!("link_by_ref(expr={}, callee_scope={})",
expr.repr(tcx), callee_scope);
@ -1442,17 +1382,13 @@ fn link_by_ref(rcx: &Rcx,
link_region(rcx, expr.span, borrow_region, ty::ImmBorrow, expr_cmt);
}
/// Like `link_region()`, except that the region is extracted from the type of `id`, which must be
/// some reference (`&T`, `&str`, etc).
fn link_region_from_node_type<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
span: Span,
id: ast::NodeId,
mutbl: ast::Mutability,
cmt_borrowed: mc::cmt<'tcx>) {
/*!
* Like `link_region()`, except that the region is
* extracted from the type of `id`, which must be some
* reference (`&T`, `&str`, etc).
*/
let rptr_ty = rcx.resolve_node_type(id);
if !ty::type_is_error(rptr_ty) {
let tcx = rcx.fcx.ccx.tcx;
@ -1463,19 +1399,14 @@ fn link_region_from_node_type<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
}
}
/// Informs the inference engine that `borrow_cmt` is being borrowed with kind `borrow_kind` and
/// lifetime `borrow_region`. In order to ensure borrowck is satisfied, this may create constraints
/// between regions, as explained in `link_reborrowed_region()`.
fn link_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
span: Span,
borrow_region: ty::Region,
borrow_kind: ty::BorrowKind,
borrow_cmt: mc::cmt<'tcx>) {
/*!
* Informs the inference engine that `borrow_cmt` is being
* borrowed with kind `borrow_kind` and lifetime `borrow_region`.
* In order to ensure borrowck is satisfied, this may create
* constraints between regions, as explained in
* `link_reborrowed_region()`.
*/
let mut borrow_cmt = borrow_cmt;
let mut borrow_kind = borrow_kind;
@ -1525,6 +1456,46 @@ fn link_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
}
}
/// This is the most complicated case: the path being borrowed is
/// itself the referent of a borrowed pointer. Let me give an
/// example fragment of code to make clear(er) the situation:
///
/// let r: &'a mut T = ...; // the original reference "r" has lifetime 'a
/// ...
/// &'z *r // the reborrow has lifetime 'z
///
/// Now, in this case, our primary job is to add the inference
/// constraint that `'z <= 'a`. Given this setup, let's clarify the
/// parameters in (roughly) terms of the example:
///
/// A borrow of: `& 'z bk * r` where `r` has type `& 'a bk T`
/// borrow_region ^~ ref_region ^~
/// borrow_kind ^~ ref_kind ^~
/// ref_cmt ^
///
/// Here `bk` stands for some borrow-kind (e.g., `mut`, `uniq`, etc).
///
/// Unfortunately, there are some complications beyond the simple
/// scenario I just painted:
///
/// 1. The reference `r` might in fact be a "by-ref" upvar. In that
/// case, we have two jobs. First, we are inferring whether this reference
/// should be an `&T`, `&mut T`, or `&uniq T` reference, and we must
/// adjust that based on this borrow (e.g., if this is an `&mut` borrow,
/// then `r` must be an `&mut` reference). Second, whenever we link
/// two regions (here, `'z <= 'a`), we supply a *cause*, and in this
/// case we adjust the cause to indicate that the reference being
/// "reborrowed" is itself an upvar. This provides a nicer error message
/// should something go wrong.
///
/// 2. There may in fact be more levels of reborrowing. In the
/// example, I said the borrow was like `&'z *r`, but it might
/// in fact be a borrow like `&'z **q` where `q` has type `&'a
/// &'b mut T`. In that case, we want to ensure that `'z <= 'a`
/// and `'z <= 'b`. This is explained more below.
///
/// The return value of this function indicates whether we need to
/// recurse and process `ref_cmt` (see case 2 above).
fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
span: Span,
borrow_region: ty::Region,
@ -1535,49 +1506,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
note: mc::Note)
-> Option<(mc::cmt<'tcx>, ty::BorrowKind)>
{
/*!
* This is the most complicated case: the path being borrowed is
* itself the referent of a borrowed pointer. Let me give an
* example fragment of code to make clear(er) the situation:
*
* let r: &'a mut T = ...; // the original reference "r" has lifetime 'a
* ...
* &'z *r // the reborrow has lifetime 'z
*
* Now, in this case, our primary job is to add the inference
* constraint that `'z <= 'a`. Given this setup, let's clarify the
* parameters in (roughly) terms of the example:
*
* A borrow of: `& 'z bk * r` where `r` has type `& 'a bk T`
* borrow_region ^~ ref_region ^~
* borrow_kind ^~ ref_kind ^~
* ref_cmt ^
*
* Here `bk` stands for some borrow-kind (e.g., `mut`, `uniq`, etc).
*
* Unfortunately, there are some complications beyond the simple
* scenario I just painted:
*
* 1. The reference `r` might in fact be a "by-ref" upvar. In that
* case, we have two jobs. First, we are inferring whether this reference
* should be an `&T`, `&mut T`, or `&uniq T` reference, and we must
* adjust that based on this borrow (e.g., if this is an `&mut` borrow,
* then `r` must be an `&mut` reference). Second, whenever we link
* two regions (here, `'z <= 'a`), we supply a *cause*, and in this
* case we adjust the cause to indicate that the reference being
* "reborrowed" is itself an upvar. This provides a nicer error message
* should something go wrong.
*
* 2. There may in fact be more levels of reborrowing. In the
* example, I said the borrow was like `&'z *r`, but it might
* in fact be a borrow like `&'z **q` where `q` has type `&'a
* &'b mut T`. In that case, we want to ensure that `'z <= 'a`
* and `'z <= 'b`. This is explained more below.
*
* The return value of this function indicates whether we need to
* recurse and process `ref_cmt` (see case 2 above).
*/
// Possible upvar ID we may need later to create an entry in the
// maybe link map.
@ -1715,27 +1643,19 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
}
}
/// Adjusts the inferred borrow_kind as needed to account for upvars that are assigned to in an
/// assignment expression.
fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx,
lhs: &ast::Expr) {
/*!
* Adjusts the inferred borrow_kind as needed to account
* for upvars that are assigned to in an assignment
* expression.
*/
let mc = mc::MemCategorizationContext::new(rcx);
let cmt = ignore_err!(mc.cat_expr(lhs));
adjust_upvar_borrow_kind_for_mut(rcx, cmt);
}
/// Indicates that `cmt` is being directly mutated (e.g., assigned to). If cmt contains any by-ref
/// upvars, this implies that those upvars must be borrowed using an `&mut` borow.
fn adjust_upvar_borrow_kind_for_mut<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
cmt: mc::cmt<'tcx>) {
/*!
* Indicates that `cmt` is being directly mutated (e.g., assigned
* to). If cmt contains any by-ref upvars, this implies that
* those upvars must be borrowed using an `&mut` borow.
*/
let mut cmt = cmt;
loop {
debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
@ -1834,16 +1754,12 @@ fn adjust_upvar_borrow_kind_for_unique<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, cmt: mc::c
}
}
/// Indicates that the borrow_kind of `outer_upvar_id` must permit a reborrowing with the
/// borrow_kind of `inner_upvar_id`. This occurs in nested closures, see comment above at the call
/// to this function.
fn link_upvar_borrow_kind_for_nested_closures(rcx: &mut Rcx,
inner_upvar_id: ty::UpvarId,
outer_upvar_id: ty::UpvarId) {
/*!
* Indicates that the borrow_kind of `outer_upvar_id` must
* permit a reborrowing with the borrow_kind of `inner_upvar_id`.
* This occurs in nested closures, see comment above at the call to
* this function.
*/
debug!("link_upvar_borrow_kind: inner_upvar_id={} outer_upvar_id={}",
inner_upvar_id, outer_upvar_id);
@ -1867,18 +1783,14 @@ fn adjust_upvar_borrow_kind_for_loan(rcx: &Rcx,
adjust_upvar_borrow_kind(rcx, upvar_id, upvar_borrow, kind)
}
/// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
/// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
/// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
/// some particular use.
fn adjust_upvar_borrow_kind(rcx: &Rcx,
upvar_id: ty::UpvarId,
upvar_borrow: &mut ty::UpvarBorrow,
kind: ty::BorrowKind) {
/*!
* We infer the borrow_kind with which to borrow upvars in a stack
* closure. The borrow_kind basically follows a lattice of
* `imm < unique-imm < mut`, moving from left to right as needed (but never
* right to left). Here the argument `mutbl` is the borrow_kind that
* is required by some particular use.
*/
debug!("adjust_upvar_borrow_kind: id={} kind=({} -> {})",
upvar_id, upvar_borrow.kind, kind);
@ -1911,15 +1823,12 @@ fn adjust_upvar_borrow_kind(rcx: &Rcx,
}
}
/// Ensures that all borrowed data reachable via `ty` outlives `region`.
fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
origin: infer::SubregionOrigin<'tcx>,
ty: Ty<'tcx>,
region: ty::Region)
{
/*!
* Ensures that all borrowed data reachable via `ty` outlives `region`.
*/
debug!("type_must_outlive(ty={}, region={})",
ty.repr(rcx.tcx()),
region.repr(rcx.tcx()));

View File

@ -33,18 +33,14 @@ struct Wf<'a, 'tcx: 'a> {
out: Vec<WfConstraint<'tcx>>,
}
/// This routine computes the well-formedness constraints that must hold for the type `ty` to
/// appear in a context with lifetime `outer_region`
pub fn region_wf_constraints<'tcx>(
tcx: &ty::ctxt<'tcx>,
ty: Ty<'tcx>,
outer_region: ty::Region)
-> Vec<WfConstraint<'tcx>>
{
/*!
* This routine computes the well-formedness constraints that must
* hold for the type `ty` to appear in a context with lifetime
* `outer_region`
*/
let mut stack = Vec::new();
stack.push((outer_region, None));
let mut wf = Wf { tcx: tcx,
@ -168,12 +164,9 @@ impl<'a, 'tcx> Wf<'a, 'tcx> {
self.stack.pop().unwrap();
}
/// Pushes a constraint that `r_b` must outlive the top region on the stack.
fn push_region_constraint_from_top(&mut self,
r_b: ty::Region) {
/*!
* Pushes a constraint that `r_b` must outlive the
* top region on the stack.
*/
// Indicates that we have found borrowed content with a lifetime
// of at least `r_b`. This adds a constraint that `r_b` must
@ -192,30 +185,26 @@ impl<'a, 'tcx> Wf<'a, 'tcx> {
self.push_sub_region_constraint(opt_ty, r_a, r_b);
}
/// Pushes a constraint that `r_a <= r_b`, due to `opt_ty`
fn push_sub_region_constraint(&mut self,
opt_ty: Option<Ty<'tcx>>,
r_a: ty::Region,
r_b: ty::Region) {
/*! Pushes a constraint that `r_a <= r_b`, due to `opt_ty` */
self.out.push(RegionSubRegionConstraint(opt_ty, r_a, r_b));
}
/// Pushes a constraint that `param_ty` must outlive the top region on the stack.
fn push_param_constraint_from_top(&mut self,
param_ty: ty::ParamTy) {
/*!
* Pushes a constraint that `param_ty` must outlive the
* top region on the stack.
*/
let &(region, opt_ty) = self.stack.last().unwrap();
self.push_param_constraint(region, opt_ty, param_ty);
}
/// Pushes a constraint that `region <= param_ty`, due to `opt_ty`
fn push_param_constraint(&mut self,
region: ty::Region,
opt_ty: Option<Ty<'tcx>>,
param_ty: ty::ParamTy) {
/*! Pushes a constraint that `region <= param_ty`, due to `opt_ty` */
self.out.push(RegionSubParamConstraint(opt_ty, region, param_ty));
}

View File

@ -168,17 +168,14 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
}
}
// Returns a vec of error messages. If hte vec is empty - no errors!
/// Returns a vec of error messages. If hte vec is empty - no errors!
///
/// There are some limitations to calling functions through an object, because (a) the self
/// type is not known (that's the whole point of a trait instance, after all, to obscure the
/// self type) and (b) the call must go through a vtable and hence cannot be monomorphized.
fn check_object_safety_of_method<'tcx>(tcx: &ty::ctxt<'tcx>,
method: &ty::Method<'tcx>)
-> Vec<String> {
/*!
* There are some limitations to calling functions through an
* object, because (a) the self type is not known
* (that's the whole point of a trait instance, after all, to
* obscure the self type) and (b) the call must go through a
* vtable and hence cannot be monomorphized.
*/
let mut msgs = Vec::new();
let method_name = method.name.repr(tcx);
@ -455,8 +452,8 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
/// Select as many obligations as we can at present.
pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt) {
/*! Select as many obligations as we can at present. */
match
fcx.inh.fulfillment_cx
@ -468,14 +465,10 @@ pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt) {
}
}
/// Try to select any fcx obligation that we haven't tried yet, in an effort to improve inference.
/// You could just call `select_fcx_obligations_where_possible` except that it leads to repeated
/// work.
pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
/*!
* Try to select any fcx obligation that we haven't tried yet,
* in an effort to improve inference. You could just call
* `select_fcx_obligations_where_possible` except that it leads
* to repeated work.
*/
match
fcx.inh.fulfillment_cx
.borrow_mut()

View File

@ -38,24 +38,18 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
CheckTypeWellFormedVisitor { ccx: ccx, cache: HashSet::new() }
}
/// Checks that the field types (in a struct def'n) or argument types (in an enum def'n) are
/// well-formed, meaning that they do not require any constraints not declared in the struct
/// definition itself. For example, this definition would be illegal:
///
/// struct Ref<'a, T> { x: &'a T }
///
/// because the type did not declare that `T:'a`.
///
/// We do this check as a pre-pass before checking fn bodies because if these constraints are
/// not included it frequently leads to confusing errors in fn bodies. So it's better to check
/// the types first.
fn check_item_well_formed(&mut self, item: &ast::Item) {
/*!
* Checks that the field types (in a struct def'n) or
* argument types (in an enum def'n) are well-formed,
* meaning that they do not require any constraints not
* declared in the struct definition itself.
* For example, this definition would be illegal:
*
* struct Ref<'a, T> { x: &'a T }
*
* because the type did not declare that `T:'a`.
*
* We do this check as a pre-pass before checking fn bodies
* because if these constraints are not included it frequently
* leads to confusing errors in fn bodies. So it's better to check
* the types first.
*/
let ccx = self.ccx;
debug!("check_item_well_formed(it.id={}, it.ident={})",
item.id,
@ -107,16 +101,12 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
regionck::regionck_item(&fcx, item);
}
/// In a type definition, we check that to ensure that the types of the fields are well-formed.
fn check_type_defn(&mut self,
item: &ast::Item,
lookup_fields: for<'fcx> |&FnCtxt<'fcx, 'tcx>|
-> Vec<AdtVariant<'tcx>>)
{
/*!
* In a type definition, we check that to ensure that the types of the fields are
* well-formed.
*/
self.with_fcx(item, |this, fcx| {
let variants = lookup_fields(fcx);
let mut bounds_checker = BoundsChecker::new(fcx,
@ -282,22 +272,16 @@ impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
cache: cache, binding_count: 0 }
}
/// Given a trait ref like `A : Trait<B>`, where `Trait` is defined as (say):
///
/// trait Trait<B:OtherTrait> : Copy { ... }
///
/// This routine will check that `B : OtherTrait` and `A : Trait<B>`. It will also recursively
/// check that the types `A` and `B` are well-formed.
///
/// Note that it does not (currently, at least) check that `A : Copy` (that check is delegated
/// to the point where impl `A : Trait<B>` is implemented).
pub fn check_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>) {
/*!
* Given a trait ref like `A : Trait<B>`, where `Trait` is
* defined as (say):
*
* trait Trait<B:OtherTrait> : Copy { ... }
*
* This routine will check that `B : OtherTrait` and `A :
* Trait<B>`. It will also recursively check that the types
* `A` and `B` are well-formed.
*
* Note that it does not (currently, at least)
* check that `A : Copy` (that check is delegated to the point
* where impl `A : Trait<B>` is implemented).
*/
let trait_def = ty::lookup_trait_def(self.fcx.tcx(), trait_ref.def_id);
let bounds = trait_def.generics.to_bounds(self.tcx(), &trait_ref.substs);

View File

@ -477,17 +477,13 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
}
}
/// Substitutes the values for the receiver's type parameters that are found in method, leaving the
/// method's type parameters intact.
pub fn make_substs_for_receiver_types<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_ref: &ty::TraitRef<'tcx>,
method: &ty::Method<'tcx>)
-> subst::Substs<'tcx>
{
/*!
* Substitutes the values for the receiver's type parameters
* that are found in method, leaving the method's type parameters
* intact.
*/
let meth_tps: Vec<Ty> =
method.generics.types.get_slice(subst::FnSpace)
.iter()

View File

@ -8,10 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Orphan checker: every impl either implements a trait defined in this
* crate or pertains to a type defined in this crate.
*/
//! Orphan checker: every impl either implements a trait defined in this
//! crate or pertains to a type defined in this crate.
use middle::traits;
use middle::ty;

View File

@ -8,10 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Overlap: No two impls for the same trait are implemented for the
* same type.
*/
//! Overlap: No two impls for the same trait are implemented for the
//! same type.
use middle::traits;
use middle::ty;

View File

@ -1944,6 +1944,9 @@ fn get_or_create_type_parameter_def<'tcx,AC>(this: &AC,
def
}
/// Translate the AST's notion of ty param bounds (which are an enum consisting of a newtyped Ty or
/// a region) to ty's notion of ty param bounds, which can either be user-defined traits, or the
/// built-in trait (formerly known as kind): Send.
fn compute_bounds<'tcx,AC>(this: &AC,
name_of_bounded_thing: ast::Name,
param_ty: ty::ParamTy,
@ -1953,13 +1956,6 @@ fn compute_bounds<'tcx,AC>(this: &AC,
where_clause: &ast::WhereClause)
-> ty::ParamBounds<'tcx>
where AC: AstConv<'tcx> {
/*!
* Translate the AST's notion of ty param bounds (which are an
* enum consisting of a newtyped Ty or a region) to ty's
* notion of ty param bounds, which can either be user-defined
* traits, or the built-in trait (formerly known as kind): Send.
*/
let mut param_bounds = conv_param_bounds(this,
span,
param_ty,
@ -2040,16 +2036,13 @@ fn conv_param_bounds<'tcx,AC>(this: &AC,
}
}
/// Merges the bounds declared on a type parameter with those found from where clauses into a
/// single list.
fn merge_param_bounds<'a>(tcx: &ty::ctxt,
param_ty: ty::ParamTy,
ast_bounds: &'a [ast::TyParamBound],
where_clause: &'a ast::WhereClause)
-> Vec<&'a ast::TyParamBound> {
/*!
* Merges the bounds declared on a type parameter with those
* found from where clauses into a single list.
*/
let mut result = Vec::new();
for ast_bound in ast_bounds.iter() {

View File

@ -8,61 +8,57 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Type Coercion
Under certain circumstances we will coerce from one type to another,
for example by auto-borrowing. This occurs in situations where the
compiler has a firm 'expected type' that was supplied from the user,
and where the actual type is similar to that expected type in purpose
but not in representation (so actual subtyping is inappropriate).
## Reborrowing
Note that if we are expecting a reference, we will *reborrow*
even if the argument provided was already a reference. This is
useful for freezing mut/const things (that is, when the expected is &T
but you have &const T or &mut T) and also for avoiding the linearity
of mut things (when the expected is &mut T and you have &mut T). See
the various `src/test/run-pass/coerce-reborrow-*.rs` tests for
examples of where this is useful.
## Subtle note
When deciding what type coercions to consider, we do not attempt to
resolve any type variables we may encounter. This is because `b`
represents the expected type "as the user wrote it", meaning that if
the user defined a generic function like
fn foo<A>(a: A, b: A) { ... }
and then we wrote `foo(&1, @2)`, we will not auto-borrow
either argument. In older code we went to some lengths to
resolve the `b` variable, which could mean that we'd
auto-borrow later arguments but not earlier ones, which
seems very confusing.
## Subtler note
However, right now, if the user manually specifies the
values for the type variables, as so:
foo::<&int>(@1, @2)
then we *will* auto-borrow, because we can't distinguish this from a
function that declared `&int`. This is inconsistent but it's easiest
at the moment. The right thing to do, I think, is to consider the
*unsubstituted* type when deciding whether to auto-borrow, but the
*substituted* type when considering the bounds and so forth. But most
of our methods don't give access to the unsubstituted type, and
rightly so because they'd be error-prone. So maybe the thing to do is
to actually determine the kind of coercions that should occur
separately and pass them in. Or maybe it's ok as is. Anyway, it's
sort of a minor point so I've opted to leave it for later---after all
we may want to adjust precisely when coercions occur.
*/
//! # Type Coercion
//!
//! Under certain circumstances we will coerce from one type to another,
//! for example by auto-borrowing. This occurs in situations where the
//! compiler has a firm 'expected type' that was supplied from the user,
//! and where the actual type is similar to that expected type in purpose
//! but not in representation (so actual subtyping is inappropriate).
//!
//! ## Reborrowing
//!
//! Note that if we are expecting a reference, we will *reborrow*
//! even if the argument provided was already a reference. This is
//! useful for freezing mut/const things (that is, when the expected is &T
//! but you have &const T or &mut T) and also for avoiding the linearity
//! of mut things (when the expected is &mut T and you have &mut T). See
//! the various `src/test/run-pass/coerce-reborrow-*.rs` tests for
//! examples of where this is useful.
//!
//! ## Subtle note
//!
//! When deciding what type coercions to consider, we do not attempt to
//! resolve any type variables we may encounter. This is because `b`
//! represents the expected type "as the user wrote it", meaning that if
//! the user defined a generic function like
//!
//! fn foo<A>(a: A, b: A) { ... }
//!
//! and then we wrote `foo(&1, @2)`, we will not auto-borrow
//! either argument. In older code we went to some lengths to
//! resolve the `b` variable, which could mean that we'd
//! auto-borrow later arguments but not earlier ones, which
//! seems very confusing.
//!
//! ## Subtler note
//!
//! However, right now, if the user manually specifies the
//! values for the type variables, as so:
//!
//! foo::<&int>(@1, @2)
//!
//! then we *will* auto-borrow, because we can't distinguish this from a
//! function that declared `&int`. This is inconsistent but it's easiest
//! at the moment. The right thing to do, I think, is to consider the
//! *unsubstituted* type when deciding whether to auto-borrow, but the
//! *substituted* type when considering the bounds and so forth. But most
//! of our methods don't give access to the unsubstituted type, and
//! rightly so because they'd be error-prone. So maybe the thing to do is
//! to actually determine the kind of coercions that should occur
//! separately and pass them in. Or maybe it's ok as is. Anyway, it's
//! sort of a minor point so I've opted to leave it for later---after all
//! we may want to adjust precisely when coercions occur.
use middle::subst;
use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe};
@ -512,14 +508,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
}
}
/// Attempts to coerce from a bare Rust function (`extern "Rust" fn`) into a closure or a
/// `proc`.
fn coerce_from_bare_fn(&self, a: Ty<'tcx>, fn_ty_a: &ty::BareFnTy<'tcx>, b: Ty<'tcx>)
-> CoerceResult<'tcx> {
/*!
*
* Attempts to coerce from a bare Rust function (`extern
* "Rust" fn`) into a closure or a `proc`.
*/
self.unpack_actual_value(b, |sty_b| {
debug!("coerce_from_bare_fn(a={}, b={})",

View File

@ -642,21 +642,16 @@ impl<'f, 'tcx> CombineFields<'f, 'tcx> {
Ok(())
}
/// Attempts to generalize `ty` for the type variable `for_vid`. This checks for cycle -- that
/// is, whether the type `ty` references `for_vid`. If `make_region_vars` is true, it will also
/// replace all regions with fresh variables. Returns `ty_err` in the case of a cycle, `Ok`
/// otherwise.
fn generalize(&self,
ty: Ty<'tcx>,
for_vid: ty::TyVid,
make_region_vars: bool)
-> cres<'tcx, Ty<'tcx>>
{
/*!
* Attempts to generalize `ty` for the type variable
* `for_vid`. This checks for cycle -- that is, whether the
* type `ty` references `for_vid`. If `make_region_vars` is
* true, it will also replace all regions with fresh
* variables. Returns `ty_err` in the case of a cycle, `Ok`
* otherwise.
*/
let mut generalize = Generalizer { infcx: self.infcx,
span: self.trace.origin.span(),
for_vid: for_vid,

View File

@ -8,244 +8,240 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Type inference engine
This is loosely based on standard HM-type inference, but with an
extension to try and accommodate subtyping. There is nothing
principled about this extension; it's sound---I hope!---but it's a
heuristic, ultimately, and does not guarantee that it finds a valid
typing even if one exists (in fact, there are known scenarios where it
fails, some of which may eventually become problematic).
## Key idea
The main change is that each type variable T is associated with a
lower-bound L and an upper-bound U. L and U begin as bottom and top,
respectively, but gradually narrow in response to new constraints
being introduced. When a variable is finally resolved to a concrete
type, it can (theoretically) select any type that is a supertype of L
and a subtype of U.
There are several critical invariants which we maintain:
- the upper-bound of a variable only becomes lower and the lower-bound
only becomes higher over time;
- the lower-bound L is always a subtype of the upper bound U;
- the lower-bound L and upper-bound U never refer to other type variables,
but only to types (though those types may contain type variables).
> An aside: if the terms upper- and lower-bound confuse you, think of
> "supertype" and "subtype". The upper-bound is a "supertype"
> (super=upper in Latin, or something like that anyway) and the lower-bound
> is a "subtype" (sub=lower in Latin). I find it helps to visualize
> a simple class hierarchy, like Java minus interfaces and
> primitive types. The class Object is at the root (top) and other
> types lie in between. The bottom type is then the Null type.
> So the tree looks like:
>
> ```text
> 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
> like that anyway).
## Satisfying constraints
At a primitive level, there is only one form of constraint that the
inference understands: a subtype relation. So the outside world can
say "make type A a subtype of type B". If there are variables
involved, the inferencer will adjust their upper- and lower-bounds as
needed to ensure that this relation is satisfied. (We also allow "make
type A equal to type B", but this is translated into "A <: B" and "B
<: A")
As stated above, we always maintain the invariant that type bounds
never refer to other variables. This keeps the inference relatively
simple, avoiding the scenario of having a kind of graph where we have
to pump constraints along and reach a fixed point, but it does impose
some heuristics in the case where the user is relating two type
variables A <: B.
Combining two variables such that variable A will forever be a subtype
of variable B is the trickiest part of the algorithm because there is
often no right choice---that is, the right choice will depend on
future constraints which we do not yet know. The problem comes about
because both A and B have bounds that can be adjusted in the future.
Let's look at some of the cases that can come up.
Imagine, to start, the best case, where both A and B have an upper and
lower bound (that is, the bounds are not top nor bot respectively). In
that case, if we're lucky, A.ub <: B.lb, and so we know that whatever
A and B should become, they will forever have the desired subtyping
relation. We can just leave things as they are.
### Option 1: Unify
However, suppose that A.ub is *not* a subtype of B.lb. In
that case, we must make a decision. One option is to unify A
and B so that they are one variable whose bounds are:
UB = GLB(A.ub, B.ub)
LB = LUB(A.lb, B.lb)
(Note that we will have to verify that LB <: UB; if it does not, the
types are not intersecting and there is an error) In that case, A <: B
holds trivially because A==B. However, we have now lost some
flexibility, because perhaps the user intended for A and B to end up
as different types and not the same type.
Pictorally, what this does is to take two distinct variables with
(hopefully not completely) distinct type ranges and produce one with
the intersection.
```text
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
Another option is to keep A and B as distinct variables but set their
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:
```text
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
```
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
would be produced by unifying the variables.
### What we do now
Our current technique is to *try* (transactionally) to relate the
existing bounds of A and B, if there are any (i.e., if `UB(A) != top
&& LB(B) != bot`). If that succeeds, we're done. If it fails, then
we merge A and B into same variable.
This is not clearly the correct course. For example, if `UB(A) !=
top` but `LB(B) == bot`, we could conceivably set `LB(B)` to `UB(A)`
and leave the variables unmerged. This is sometimes the better
course, it depends on the program.
The main case which fails today that I would like to support is:
```text
fn foo<T>(x: T, y: T) { ... }
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
because the type variable `T` is merged with the type variable for
`X`, and thus inherits its UB/LB of `@mut int`. This leaves no
flexibility for `T` to later adjust to accommodate `@int`.
### What to do when not all bounds are present
In the prior discussion we assumed that A.ub was not top and B.lb was
not bot. Unfortunately this is rarely the case. Often type variables
have "lopsided" bounds. For example, if a variable in the program has
been initialized but has not been used, then its corresponding type
variable will have a lower bound but no upper bound. When that
variable is then used, we would like to know its upper bound---but we
don't have one! In this case we'll do different things depending on
how the variable is being used.
## Transactional support
Whenever we adjust merge variables or adjust their bounds, we always
keep a record of the old value. This allows the changes to be undone.
## Regions
I've only talked about type variables here, but region variables
follow the same principle. They have upper- and lower-bounds. A
region A is a subregion of a region B if A being valid implies that B
is valid. This basically corresponds to the block nesting structure:
the regions for outer block scopes are superregions of those for inner
block scopes.
## Integral and floating-point type variables
There is a third variety of type variable that we use only for
inferring the types of unsuffixed integer literals. Integral type
variables differ from general-purpose type variables in that there's
no subtyping relationship among the various integral types, so instead
of associating each variable with an upper and lower bound, we just
use simple unification. Each integer variable is associated with at
most one integer type. Floating point types are handled similarly to
integral types.
## GLB/LUB
Computing the greatest-lower-bound and least-upper-bound of two
types/regions is generally straightforward except when type variables
are involved. In that case, we follow a similar "try to use the bounds
when possible but otherwise merge the variables" strategy. In other
words, `GLB(A, B)` where `A` and `B` are variables will often result
in `A` and `B` being merged and the result being `A`.
## Type coercion
We have a notion of assignability which differs somewhat from
subtyping; in particular it may cause region borrowing to occur. See
the big comment later in this file on Type Coercion for specifics.
### In conclusion
I showed you three ways to relate `A` and `B`. There are also more,
of course, though I'm not sure if there are any more sensible options.
The main point is that there are various options, each of which
produce a distinct range of types for `A` and `B`. Depending on what
the correct values for A and B are, one of these options will be the
right choice: but of course we don't know the right values for A and B
yet, that's what we're trying to find! In our code, we opt to unify
(Option #1).
# Implementation details
We make use of a trait-like implementation strategy to consolidate
duplicated code between subtypes, GLB, and LUB computations. See the
section on "Type Combining" below for details.
*/
//! # Type inference engine
//!
//! This is loosely based on standard HM-type inference, but with an
//! extension to try and accommodate subtyping. There is nothing
//! principled about this extension; it's sound---I hope!---but it's a
//! heuristic, ultimately, and does not guarantee that it finds a valid
//! typing even if one exists (in fact, there are known scenarios where it
//! fails, some of which may eventually become problematic).
//!
//! ## Key idea
//!
//! The main change is that each type variable T is associated with a
//! lower-bound L and an upper-bound U. L and U begin as bottom and top,
//! respectively, but gradually narrow in response to new constraints
//! being introduced. When a variable is finally resolved to a concrete
//! type, it can (theoretically) select any type that is a supertype of L
//! and a subtype of U.
//!
//! There are several critical invariants which we maintain:
//!
//! - the upper-bound of a variable only becomes lower and the lower-bound
//! only becomes higher over time;
//! - the lower-bound L is always a subtype of the upper bound U;
//! - the lower-bound L and upper-bound U never refer to other type variables,
//! but only to types (though those types may contain type variables).
//!
//! > An aside: if the terms upper- and lower-bound confuse you, think of
//! > "supertype" and "subtype". The upper-bound is a "supertype"
//! > (super=upper in Latin, or something like that anyway) and the lower-bound
//! > is a "subtype" (sub=lower in Latin). I find it helps to visualize
//! > a simple class hierarchy, like Java minus interfaces and
//! > primitive types. The class Object is at the root (top) and other
//! > types lie in between. The bottom type is then the Null type.
//! > So the tree looks like:
//! >
//! > ```text
//! > 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
//! > like that anyway).
//!
//! ## Satisfying constraints
//!
//! At a primitive level, there is only one form of constraint that the
//! inference understands: a subtype relation. So the outside world can
//! say "make type A a subtype of type B". If there are variables
//! involved, the inferencer will adjust their upper- and lower-bounds as
//! needed to ensure that this relation is satisfied. (We also allow "make
//! type A equal to type B", but this is translated into "A <: B" and "B
//! <: A")
//!
//! As stated above, we always maintain the invariant that type bounds
//! never refer to other variables. This keeps the inference relatively
//! simple, avoiding the scenario of having a kind of graph where we have
//! to pump constraints along and reach a fixed point, but it does impose
//! some heuristics in the case where the user is relating two type
//! variables A <: B.
//!
//! Combining two variables such that variable A will forever be a subtype
//! of variable B is the trickiest part of the algorithm because there is
//! often no right choice---that is, the right choice will depend on
//! future constraints which we do not yet know. The problem comes about
//! because both A and B have bounds that can be adjusted in the future.
//! Let's look at some of the cases that can come up.
//!
//! Imagine, to start, the best case, where both A and B have an upper and
//! lower bound (that is, the bounds are not top nor bot respectively). In
//! that case, if we're lucky, A.ub <: B.lb, and so we know that whatever
//! A and B should become, they will forever have the desired subtyping
//! relation. We can just leave things as they are.
//!
//! ### Option 1: Unify
//!
//! However, suppose that A.ub is *not* a subtype of B.lb. In
//! that case, we must make a decision. One option is to unify A
//! and B so that they are one variable whose bounds are:
//!
//! UB = GLB(A.ub, B.ub)
//! LB = LUB(A.lb, B.lb)
//!
//! (Note that we will have to verify that LB <: UB; if it does not, the
//! types are not intersecting and there is an error) In that case, A <: B
//! holds trivially because A==B. However, we have now lost some
//! flexibility, because perhaps the user intended for A and B to end up
//! as different types and not the same type.
//!
//! Pictorally, what this does is to take two distinct variables with
//! (hopefully not completely) distinct type ranges and produce one with
//! the intersection.
//!
//! ```text
//! 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
//!
//! Another option is to keep A and B as distinct variables but set their
//! 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:
//!
//! ```text
//! 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
//! ```
//!
//! 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
//! would be produced by unifying the variables.
//!
//! ### What we do now
//!
//! Our current technique is to *try* (transactionally) to relate the
//! existing bounds of A and B, if there are any (i.e., if `UB(A) != top
//! && LB(B) != bot`). If that succeeds, we're done. If it fails, then
//! we merge A and B into same variable.
//!
//! This is not clearly the correct course. For example, if `UB(A) !=
//! top` but `LB(B) == bot`, we could conceivably set `LB(B)` to `UB(A)`
//! and leave the variables unmerged. This is sometimes the better
//! course, it depends on the program.
//!
//! The main case which fails today that I would like to support is:
//!
//! ```text
//! fn foo<T>(x: T, y: T) { ... }
//!
//! 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
//! because the type variable `T` is merged with the type variable for
//! `X`, and thus inherits its UB/LB of `@mut int`. This leaves no
//! flexibility for `T` to later adjust to accommodate `@int`.
//!
//! ### What to do when not all bounds are present
//!
//! In the prior discussion we assumed that A.ub was not top and B.lb was
//! not bot. Unfortunately this is rarely the case. Often type variables
//! have "lopsided" bounds. For example, if a variable in the program has
//! been initialized but has not been used, then its corresponding type
//! variable will have a lower bound but no upper bound. When that
//! variable is then used, we would like to know its upper bound---but we
//! don't have one! In this case we'll do different things depending on
//! how the variable is being used.
//!
//! ## Transactional support
//!
//! Whenever we adjust merge variables or adjust their bounds, we always
//! keep a record of the old value. This allows the changes to be undone.
//!
//! ## Regions
//!
//! I've only talked about type variables here, but region variables
//! follow the same principle. They have upper- and lower-bounds. A
//! region A is a subregion of a region B if A being valid implies that B
//! is valid. This basically corresponds to the block nesting structure:
//! the regions for outer block scopes are superregions of those for inner
//! block scopes.
//!
//! ## Integral and floating-point type variables
//!
//! There is a third variety of type variable that we use only for
//! inferring the types of unsuffixed integer literals. Integral type
//! variables differ from general-purpose type variables in that there's
//! no subtyping relationship among the various integral types, so instead
//! of associating each variable with an upper and lower bound, we just
//! use simple unification. Each integer variable is associated with at
//! most one integer type. Floating point types are handled similarly to
//! integral types.
//!
//! ## GLB/LUB
//!
//! Computing the greatest-lower-bound and least-upper-bound of two
//! types/regions is generally straightforward except when type variables
//! are involved. In that case, we follow a similar "try to use the bounds
//! when possible but otherwise merge the variables" strategy. In other
//! words, `GLB(A, B)` where `A` and `B` are variables will often result
//! in `A` and `B` being merged and the result being `A`.
//!
//! ## Type coercion
//!
//! We have a notion of assignability which differs somewhat from
//! subtyping; in particular it may cause region borrowing to occur. See
//! the big comment later in this file on Type Coercion for specifics.
//!
//! ### In conclusion
//!
//! I showed you three ways to relate `A` and `B`. There are also more,
//! of course, though I'm not sure if there are any more sensible options.
//! The main point is that there are various options, each of which
//! produce a distinct range of types for `A` and `B`. Depending on what
//! the correct values for A and B are, one of these options will be the
//! right choice: but of course we don't know the right values for A and B
//! yet, that's what we're trying to find! In our code, we opt to unify
//! (Option #1).
//!
//! # Implementation details
//!
//! We make use of a trait-like implementation strategy to consolidate
//! duplicated code between subtypes, GLB, and LUB computations. See the
//! section on "Type Combining" below for details.

View File

@ -8,56 +8,53 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
//! Error Reporting Code for the inference engine
//!
//! Because of the way inference, and in particular region inference,
//! works, it often happens that errors are not detected until far after
//! the relevant line of code has been type-checked. Therefore, there is
//! an elaborate system to track why a particular constraint in the
//! inference graph arose so that we can explain to the user what gave
//! rise to a particular error.
//!
//! The basis of the system are the "origin" types. An "origin" is the
//! reason that a constraint or inference variable arose. There are
//! different "origin" enums for different kinds of constraints/variables
//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
//! a span, but also more information so that we can generate a meaningful
//! error message.
//!
//! Having a catalogue of all the different reasons an error can arise is
//! also useful for other reasons, like cross-referencing FAQs etc, though
//! we are not really taking advantage of this yet.
//!
//! # Region Inference
//!
//! Region inference is particularly tricky because it always succeeds "in
//! the moment" and simply registers a constraint. Then, at the end, we
//! can compute the full graph and report errors, so we need to be able to
//! store and later report what gave rise to the conflicting constraints.
//!
//! # Subtype Trace
//!
//! Determing whether `T1 <: T2` often involves a number of subtypes and
//! subconstraints along the way. A "TypeTrace" is an extended version
//! of an origin that traces the types and other values that were being
//! compared. It is not necessarily comprehensive (in fact, at the time of
//! this writing it only tracks the root values being compared) but I'd
//! like to extend it to include significant "waypoints". For example, if
//! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2
//! <: T4` fails, I'd like the trace to include enough information to say
//! "in the 2nd element of the tuple". Similarly, failures when comparing
//! arguments or return types in fn types should be able to cite the
//! specific position, etc.
//!
//! # Reality vs plan
//!
//! Of course, there is still a LOT of code in typeck that has yet to be
//! ported to this system, and which relies on string concatenation at the
//! time of error detection.
Error Reporting Code for the inference engine
Because of the way inference, and in particular region inference,
works, it often happens that errors are not detected until far after
the relevant line of code has been type-checked. Therefore, there is
an elaborate system to track why a particular constraint in the
inference graph arose so that we can explain to the user what gave
rise to a particular error.
The basis of the system are the "origin" types. An "origin" is the
reason that a constraint or inference variable arose. There are
different "origin" enums for different kinds of constraints/variables
(e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
a span, but also more information so that we can generate a meaningful
error message.
Having a catalogue of all the different reasons an error can arise is
also useful for other reasons, like cross-referencing FAQs etc, though
we are not really taking advantage of this yet.
# Region Inference
Region inference is particularly tricky because it always succeeds "in
the moment" and simply registers a constraint. Then, at the end, we
can compute the full graph and report errors, so we need to be able to
store and later report what gave rise to the conflicting constraints.
# Subtype Trace
Determing whether `T1 <: T2` often involves a number of subtypes and
subconstraints along the way. A "TypeTrace" is an extended version
of an origin that traces the types and other values that were being
compared. It is not necessarily comprehensive (in fact, at the time of
this writing it only tracks the root values being compared) but I'd
like to extend it to include significant "waypoints". For example, if
you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2
<: T4` fails, I'd like the trace to include enough information to say
"in the 2nd element of the tuple". Similarly, failures when comparing
arguments or return types in fn types should be able to cite the
specific position, etc.
# Reality vs plan
Of course, there is still a LOT of code in typeck that has yet to be
ported to this system, and which relies on string concatenation at the
time of error detection.
*/
use self::FreshOrKept::*;
use std::collections::HashSet;
@ -391,11 +388,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
ty::note_and_explain_type_err(self.tcx, terr);
}
/// Returns a string of the form "expected `{}`, found `{}`", or None if this is a derived
/// error.
fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<String> {
/*!
* Returns a string of the form "expected `{}`, found `{}`",
* or None if this is a derived error.
*/
match *values {
infer::Types(ref exp_found) => self.expected_found_str(exp_found),
infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found)

View File

@ -8,408 +8,404 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Skolemization and functions
One of the trickiest and most subtle aspects of regions is dealing
with higher-ranked things which include bound region variables, such
as function types. I strongly suggest that if you want to understand
the situation, you read this paper (which is, admittedly, very long,
but you don't have to read the whole thing):
http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
Although my explanation will never compete with SPJ's (for one thing,
his is approximately 100 pages), I will attempt to explain the basic
problem and also how we solve it. Note that the paper only discusses
subtyping, not the computation of LUB/GLB.
The problem we are addressing is that there is a kind of subtyping
between functions with bound region parameters. Consider, for
example, whether the following relation holds:
for<'a> fn(&'a int) <: for<'b> fn(&'b int)? (Yes, a => b)
The answer is that of course it does. These two types are basically
the same, except that in one we used the name `a` and one we used
the name `b`.
In the examples that follow, it becomes very important to know whether
a lifetime is bound in a function type (that is, is a lifetime
parameter) or appears free (is defined in some outer scope).
Therefore, from now on I will always write the bindings explicitly,
using the Rust syntax `for<'a> fn(&'a int)` to indicate that `a` is a
lifetime parameter.
Now let's consider two more function types. Here, we assume that the
`'b` lifetime is defined somewhere outside and hence is not a lifetime
parameter bound by the function type (it "appears free"):
for<'a> fn(&'a int) <: fn(&'b int)? (Yes, a => b)
This subtyping relation does in fact hold. To see why, you have to
consider what subtyping means. One way to look at `T1 <: T2` is to
say that it means that it is always ok to treat an instance of `T1` as
if it had the type `T2`. So, with our functions, it is always ok to
treat a function that can take pointers with any lifetime as if it
were a function that can only take a pointer with the specific
lifetime `'b`. After all, `'b` is a lifetime, after all, and
the function can take values of any lifetime.
You can also look at subtyping as the *is a* relationship. This amounts
to the same thing: a function that accepts pointers with any lifetime
*is a* function that accepts pointers with some specific lifetime.
So, what if we reverse the order of the two function types, like this:
fn(&'b int) <: for<'a> fn(&'a int)? (No)
Does the subtyping relationship still hold? The answer of course is
no. In this case, the function accepts *only the lifetime `'b`*,
so it is not reasonable to treat it as if it were a function that
accepted any lifetime.
What about these two examples:
for<'a,'b> fn(&'a int, &'b int) <: for<'a> fn(&'a int, &'a int)? (Yes)
for<'a> fn(&'a int, &'a int) <: for<'a,'b> fn(&'a int, &'b int)? (No)
Here, it is true that functions which take two pointers with any two
lifetimes can be treated as if they only accepted two pointers with
the same lifetime, but not the reverse.
## The algorithm
Here is the algorithm we use to perform the subtyping check:
1. Replace all bound regions in the subtype with new variables
2. Replace all bound regions in the supertype with skolemized
equivalents. A "skolemized" region is just a new fresh region
name.
3. Check that the parameter and return types match as normal
4. Ensure that no skolemized regions 'leak' into region variables
visible from "the outside"
Let's walk through some examples and see how this algorithm plays out.
#### First example
We'll start with the first example, which was:
1. for<'a> fn(&'a T) <: for<'b> fn(&'b T)? Yes: a -> b
After steps 1 and 2 of the algorithm we will have replaced the types
like so:
1. fn(&'A T) <: fn(&'x T)?
Here the upper case `&A` indicates a *region variable*, that is, a
region whose value is being inferred by the system. I also replaced
`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
to indicate skolemized region names. We can assume they don't appear
elsewhere. Note that neither the sub- nor the supertype bind any
region names anymore (as indicated by the absence of `<` and `>`).
The next step is to check that the parameter types match. Because
parameters are contravariant, this means that we check whether:
&'x T <: &'A T
Region pointers are contravariant so this implies that
&A <= &x
must hold, where `<=` is the subregion relationship. Processing
*this* constrain simply adds a constraint into our graph that `&A <=
&x` and is considered successful (it can, for example, be satisfied by
choosing the value `&x` for `&A`).
So far we have encountered no error, so the subtype check succeeds.
#### The third example
Now let's look first at the third example, which was:
3. fn(&'a T) <: for<'b> fn(&'b T)? No!
After steps 1 and 2 of the algorithm we will have replaced the types
like so:
3. fn(&'a T) <: fn(&'x T)?
This looks pretty much the same as before, except that on the LHS
`'a` was not bound, and hence was left as-is and not replaced with
a variable. The next step is again to check that the parameter types
match. This will ultimately require (as before) that `'a` <= `&x`
must hold: but this does not hold. `self` and `x` are both distinct
free regions. So the subtype check fails.
#### Checking for skolemization leaks
You may be wondering about that mysterious last step in the algorithm.
So far it has not been relevant. The purpose of that last step is to
catch something like *this*:
for<'a> fn() -> fn(&'a T) <: fn() -> for<'b> fn(&'b T)? No.
Here the function types are the same but for where the binding occurs.
The subtype returns a function that expects a value in precisely one
region. The supertype returns a function that expects a value in any
region. If we allow an instance of the subtype to be used where the
supertype is expected, then, someone could call the fn and think that
the return value has type `fn<b>(&'b T)` when it really has type
`fn(&'a T)` (this is case #3, above). Bad.
So let's step through what happens when we perform this subtype check.
We first replace the bound regions in the subtype (the supertype has
no bound regions). This gives us:
fn() -> fn(&'A T) <: fn() -> for<'b> fn(&'b T)?
Now we compare the return types, which are covariant, and hence we have:
fn(&'A T) <: for<'b> fn(&'b T)?
Here we skolemize the bound region in the supertype to yield:
fn(&'A T) <: fn(&'x T)?
And then proceed to compare the argument types:
&'x T <: &'A T
'A <= 'x
Finally, this is where it gets interesting! This is where an error
*should* be reported. But in fact this will not happen. The reason why
is that `A` is a variable: we will infer that its value is the fresh
region `x` and think that everything is happy. In fact, this behavior
is *necessary*, it was key to the first example we walked through.
The difference between this example and the first one is that the variable
`A` already existed at the point where the skolemization occurred. In
the first example, you had two functions:
for<'a> fn(&'a T) <: for<'b> fn(&'b T)
and hence `&A` and `&x` were created "together". In general, the
intention of the skolemized names is that they are supposed to be
fresh names that could never be equal to anything from the outside.
But when inference comes into play, we might not be respecting this
rule.
So the way we solve this is to add a fourth step that examines the
constraints that refer to skolemized names. Basically, consider a
non-directed verison of the constraint graph. Let `Tainted(x)` be the
set of all things reachable from a skolemized variable `x`.
`Tainted(x)` should not contain any regions that existed before the
step at which the skolemization was performed. So this case here
would fail because `&x` was created alone, but is relatable to `&A`.
## Computing the LUB and GLB
The paper I pointed you at is written for Haskell. It does not
therefore considering subtyping and in particular does not consider
LUB or GLB computation. We have to consider this. Here is the
algorithm I implemented.
First though, let's discuss what we are trying to compute in more
detail. The LUB is basically the "common supertype" and the GLB is
"common subtype"; one catch is that the LUB should be the
*most-specific* common supertype and the GLB should be *most general*
common subtype (as opposed to any common supertype or any common
subtype).
Anyway, to help clarify, here is a table containing some function
pairs and their LUB/GLB (for conciseness, in this table, I'm just
including the lifetimes here, not the rest of the types, and I'm
writing `fn<>` instead of `for<> fn`):
```
Type 1 Type 2 LUB GLB
fn<'a>('a) fn('X) fn('X) fn<'a>('a)
fn('a) fn('X) -- fn<'a>('a)
fn<'a,'b>('a, 'b) fn<'x>('x, 'x) fn<'a>('a, 'a) fn<'a,'b>('a, 'b)
fn<'a,'b>('a, 'b, 'a) fn<'x,'y>('x, 'y, 'y) fn<'a>('a, 'a, 'a) fn<'a,'b,'c>('a,'b,'c)
```
### Conventions
I use lower-case letters (e.g., `&a`) for bound regions and upper-case
letters for free regions (`&A`). Region variables written with a
dollar-sign (e.g., `$a`). I will try to remember to enumerate the
bound-regions on the fn type as well (e.g., `for<'a> fn(&a)`).
### High-level summary
Both the LUB and the GLB algorithms work in a similar fashion. They
begin by replacing all bound regions (on both sides) with fresh region
inference variables. Therefore, both functions are converted to types
that contain only free regions. We can then compute the LUB/GLB in a
straightforward way, as described in `combine.rs`. This results in an
interim type T. The algorithms then examine the regions that appear
in T and try to, in some cases, replace them with bound regions to
yield the final result.
To decide whether to replace a region `R` that appears in `T` with a
bound region, the algorithms make use of two bits of information.
First is a set `V` that contains all region variables created as part
of the LUB/GLB computation. `V` will contain the region variables
created to replace the bound regions in the input types, but it also
contains 'intermediate' variables created to represent the LUB/GLB of
individual regions. Basically, when asked to compute the LUB/GLB of a
region variable with another region, the inferencer cannot oblige
immediately since the values of that variables are not known.
Therefore, it creates a new variable that is related to the two
regions. For example, the LUB of two variables `$x` and `$y` is a
fresh variable `$z` that is constrained such that `$x <= $z` and `$y
<= $z`. So `V` will contain these intermediate variables as well.
The other important factor in deciding how to replace a region in T is
the function `Tainted($r)` which, for a region variable, identifies
all regions that the region variable is related to in some way
(`Tainted()` made an appearance in the subtype computation as well).
### LUB
The LUB algorithm proceeds in three steps:
1. Replace all bound regions (on both sides) with fresh region
inference variables.
2. Compute the LUB "as normal", meaning compute the GLB of each
pair of argument types and the LUB of the return types and
so forth. Combine those to a new function type `F`.
3. Replace each region `R` that appears in `F` as follows:
- Let `V` be the set of variables created during the LUB
computational steps 1 and 2, as described in the previous section.
- If `R` is not in `V`, replace `R` with itself.
- If `Tainted(R)` contains a region that is not in `V`,
replace `R` with itself.
- Otherwise, select the earliest variable in `Tainted(R)` that originates
from the left-hand side and replace `R` with the bound region that
this variable was a replacement for.
So, let's work through the simplest example: `fn(&A)` and `for<'a> fn(&a)`.
In this case, `&a` will be replaced with `$a` and the interim LUB type
`fn($b)` will be computed, where `$b=GLB(&A,$a)`. Therefore, `V =
{$a, $b}` and `Tainted($b) = { $b, $a, &A }`. When we go to replace
`$b`, we find that since `&A \in Tainted($b)` is not a member of `V`,
we leave `$b` as is. When region inference happens, `$b` will be
resolved to `&A`, as we wanted.
Let's look at a more complex one: `fn(&a, &b)` and `fn(&x, &x)`. In
this case, we'll end up with a (pre-replacement) LUB type of `fn(&g,
&h)` and a graph that looks like:
```
$a $b *--$x
\ \ / /
\ $h-* /
$g-----------*
```
Here `$g` and `$h` are fresh variables that are created to represent
the LUB/GLB of things requiring inference. This means that `V` and
`Tainted` will look like:
```
V = {$a, $b, $g, $h, $x}
Tainted($g) = Tainted($h) = { $a, $b, $h, $g, $x }
```
Therefore we replace both `$g` and `$h` with `$a`, and end up
with the type `fn(&a, &a)`.
### GLB
The procedure for computing the GLB is similar. The difference lies
in computing the replacements for the various variables. For each
region `R` that appears in the type `F`, we again compute `Tainted(R)`
and examine the results:
1. If `R` is not in `V`, it is not replaced.
2. Else, if `Tainted(R)` contains only variables in `V`, and it
contains exactly one variable from the LHS and one variable from
the RHS, then `R` can be mapped to the bound version of the
variable from the LHS.
3. Else, if `Tainted(R)` contains no variable from the LHS and no
variable from the RHS, then `R` can be mapped to itself.
4. Else, `R` is mapped to a fresh bound variable.
These rules are pretty complex. Let's look at some examples to see
how they play out.
Out first example was `fn(&a)` and `fn(&X)`. In this case, `&a` will
be replaced with `$a` and we will ultimately compute a
(pre-replacement) GLB type of `fn($g)` where `$g=LUB($a,&X)`.
Therefore, `V={$a,$g}` and `Tainted($g)={$g,$a,&X}. To find the
replacement for `$g` we consult the rules above:
- Rule (1) does not apply because `$g \in V`
- Rule (2) does not apply because `&X \in Tainted($g)`
- Rule (3) does not apply because `$a \in Tainted($g)`
- Hence, by rule (4), we replace `$g` with a fresh bound variable `&z`.
So our final result is `fn(&z)`, which is correct.
The next example is `fn(&A)` and `fn(&Z)`. In this case, we will again
have a (pre-replacement) GLB of `fn(&g)`, where `$g = LUB(&A,&Z)`.
Therefore, `V={$g}` and `Tainted($g) = {$g, &A, &Z}`. In this case,
by rule (3), `$g` is mapped to itself, and hence the result is
`fn($g)`. This result is correct (in this case, at least), but it is
indicative of a case that *can* lead us into concluding that there is
no GLB when in fact a GLB does exist. See the section "Questionable
Results" below for more details.
The next example is `fn(&a, &b)` and `fn(&c, &c)`. In this case, as
before, we'll end up with `F=fn($g, $h)` where `Tainted($g) =
Tainted($h) = {$g, $h, $a, $b, $c}`. Only rule (4) applies and hence
we'll select fresh bound variables `y` and `z` and wind up with
`fn(&y, &z)`.
For the last example, let's consider what may seem trivial, but is
not: `fn(&a, &a)` and `fn(&b, &b)`. In this case, we'll get `F=fn($g,
$h)` where `Tainted($g) = {$g, $a, $x}` and `Tainted($h) = {$h, $a,
$x}`. Both of these sets contain exactly one bound variable from each
side, so we'll map them both to `&a`, resulting in `fn(&a, &a)`, which
is the desired result.
### Shortcomings and correctness
You may be wondering whether this algorithm is correct. The answer is
"sort of". There are definitely cases where they fail to compute a
result even though a correct result exists. I believe, though, that
if they succeed, then the result is valid, and I will attempt to
convince you. The basic argument is that the "pre-replacement" step
computes a set of constraints. The replacements, then, attempt to
satisfy those constraints, using bound identifiers where needed.
For now I will briefly go over the cases for LUB/GLB and identify
their intent:
- LUB:
- The region variables that are substituted in place of bound regions
are intended to collect constraints on those bound regions.
- If Tainted(R) contains only values in V, then this region is unconstrained
and can therefore be generalized, otherwise it cannot.
- GLB:
- The region variables that are substituted in place of bound regions
are intended to collect constraints on those bound regions.
- If Tainted(R) contains exactly one variable from each side, and
only variables in V, that indicates that those two bound regions
must be equated.
- Otherwise, if Tainted(R) references any variables from left or right
side, then it is trying to combine a bound region with a free one or
multiple bound regions, so we need to select fresh bound regions.
Sorry this is more of a shorthand to myself. I will try to write up something
more convincing in the future.
#### Where are the algorithms wrong?
- The pre-replacement computation can fail even though using a
bound-region would have succeeded.
- We will compute GLB(fn(fn($a)), fn(fn($b))) as fn($c) where $c is the
GLB of $a and $b. But if inference finds that $a and $b must be mapped
to regions without a GLB, then this is effectively a failure to compute
the GLB. However, the result `fn<$c>(fn($c))` is a valid GLB.
*/
//! # Skolemization and functions
//!
//! One of the trickiest and most subtle aspects of regions is dealing
//! with higher-ranked things which include bound region variables, such
//! as function types. I strongly suggest that if you want to understand
//! the situation, you read this paper (which is, admittedly, very long,
//! but you don't have to read the whole thing):
//!
//! http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
//!
//! Although my explanation will never compete with SPJ's (for one thing,
//! his is approximately 100 pages), I will attempt to explain the basic
//! problem and also how we solve it. Note that the paper only discusses
//! subtyping, not the computation of LUB/GLB.
//!
//! The problem we are addressing is that there is a kind of subtyping
//! between functions with bound region parameters. Consider, for
//! example, whether the following relation holds:
//!
//! for<'a> fn(&'a int) <: for<'b> fn(&'b int)? (Yes, a => b)
//!
//! The answer is that of course it does. These two types are basically
//! the same, except that in one we used the name `a` and one we used
//! the name `b`.
//!
//! In the examples that follow, it becomes very important to know whether
//! a lifetime is bound in a function type (that is, is a lifetime
//! parameter) or appears free (is defined in some outer scope).
//! Therefore, from now on I will always write the bindings explicitly,
//! using the Rust syntax `for<'a> fn(&'a int)` to indicate that `a` is a
//! lifetime parameter.
//!
//! Now let's consider two more function types. Here, we assume that the
//! `'b` lifetime is defined somewhere outside and hence is not a lifetime
//! parameter bound by the function type (it "appears free"):
//!
//! for<'a> fn(&'a int) <: fn(&'b int)? (Yes, a => b)
//!
//! This subtyping relation does in fact hold. To see why, you have to
//! consider what subtyping means. One way to look at `T1 <: T2` is to
//! say that it means that it is always ok to treat an instance of `T1` as
//! if it had the type `T2`. So, with our functions, it is always ok to
//! treat a function that can take pointers with any lifetime as if it
//! were a function that can only take a pointer with the specific
//! lifetime `'b`. After all, `'b` is a lifetime, after all, and
//! the function can take values of any lifetime.
//!
//! You can also look at subtyping as the *is a* relationship. This amounts
//! to the same thing: a function that accepts pointers with any lifetime
//! *is a* function that accepts pointers with some specific lifetime.
//!
//! So, what if we reverse the order of the two function types, like this:
//!
//! fn(&'b int) <: for<'a> fn(&'a int)? (No)
//!
//! Does the subtyping relationship still hold? The answer of course is
//! no. In this case, the function accepts *only the lifetime `'b`*,
//! so it is not reasonable to treat it as if it were a function that
//! accepted any lifetime.
//!
//! What about these two examples:
//!
//! for<'a,'b> fn(&'a int, &'b int) <: for<'a> fn(&'a int, &'a int)? (Yes)
//! for<'a> fn(&'a int, &'a int) <: for<'a,'b> fn(&'a int, &'b int)? (No)
//!
//! Here, it is true that functions which take two pointers with any two
//! lifetimes can be treated as if they only accepted two pointers with
//! the same lifetime, but not the reverse.
//!
//! ## The algorithm
//!
//! Here is the algorithm we use to perform the subtyping check:
//!
//! 1. Replace all bound regions in the subtype with new variables
//! 2. Replace all bound regions in the supertype with skolemized
//! equivalents. A "skolemized" region is just a new fresh region
//! name.
//! 3. Check that the parameter and return types match as normal
//! 4. Ensure that no skolemized regions 'leak' into region variables
//! visible from "the outside"
//!
//! Let's walk through some examples and see how this algorithm plays out.
//!
//! #### First example
//!
//! We'll start with the first example, which was:
//!
//! 1. for<'a> fn(&'a T) <: for<'b> fn(&'b T)? Yes: a -> b
//!
//! After steps 1 and 2 of the algorithm we will have replaced the types
//! like so:
//!
//! 1. fn(&'A T) <: fn(&'x T)?
//!
//! Here the upper case `&A` indicates a *region variable*, that is, a
//! region whose value is being inferred by the system. I also replaced
//! `&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
//! to indicate skolemized region names. We can assume they don't appear
//! elsewhere. Note that neither the sub- nor the supertype bind any
//! region names anymore (as indicated by the absence of `<` and `>`).
//!
//! The next step is to check that the parameter types match. Because
//! parameters are contravariant, this means that we check whether:
//!
//! &'x T <: &'A T
//!
//! Region pointers are contravariant so this implies that
//!
//! &A <= &x
//!
//! must hold, where `<=` is the subregion relationship. Processing
//! *this* constrain simply adds a constraint into our graph that `&A <=
//! &x` and is considered successful (it can, for example, be satisfied by
//! choosing the value `&x` for `&A`).
//!
//! So far we have encountered no error, so the subtype check succeeds.
//!
//! #### The third example
//!
//! Now let's look first at the third example, which was:
//!
//! 3. fn(&'a T) <: for<'b> fn(&'b T)? No!
//!
//! After steps 1 and 2 of the algorithm we will have replaced the types
//! like so:
//!
//! 3. fn(&'a T) <: fn(&'x T)?
//!
//! This looks pretty much the same as before, except that on the LHS
//! `'a` was not bound, and hence was left as-is and not replaced with
//! a variable. The next step is again to check that the parameter types
//! match. This will ultimately require (as before) that `'a` <= `&x`
//! must hold: but this does not hold. `self` and `x` are both distinct
//! free regions. So the subtype check fails.
//!
//! #### Checking for skolemization leaks
//!
//! You may be wondering about that mysterious last step in the algorithm.
//! So far it has not been relevant. The purpose of that last step is to
//! catch something like *this*:
//!
//! for<'a> fn() -> fn(&'a T) <: fn() -> for<'b> fn(&'b T)? No.
//!
//! Here the function types are the same but for where the binding occurs.
//! The subtype returns a function that expects a value in precisely one
//! region. The supertype returns a function that expects a value in any
//! region. If we allow an instance of the subtype to be used where the
//! supertype is expected, then, someone could call the fn and think that
//! the return value has type `fn<b>(&'b T)` when it really has type
//! `fn(&'a T)` (this is case #3, above). Bad.
//!
//! So let's step through what happens when we perform this subtype check.
//! We first replace the bound regions in the subtype (the supertype has
//! no bound regions). This gives us:
//!
//! fn() -> fn(&'A T) <: fn() -> for<'b> fn(&'b T)?
//!
//! Now we compare the return types, which are covariant, and hence we have:
//!
//! fn(&'A T) <: for<'b> fn(&'b T)?
//!
//! Here we skolemize the bound region in the supertype to yield:
//!
//! fn(&'A T) <: fn(&'x T)?
//!
//! And then proceed to compare the argument types:
//!
//! &'x T <: &'A T
//! 'A <= 'x
//!
//! Finally, this is where it gets interesting! This is where an error
//! *should* be reported. But in fact this will not happen. The reason why
//! is that `A` is a variable: we will infer that its value is the fresh
//! region `x` and think that everything is happy. In fact, this behavior
//! is *necessary*, it was key to the first example we walked through.
//!
//! The difference between this example and the first one is that the variable
//! `A` already existed at the point where the skolemization occurred. In
//! the first example, you had two functions:
//!
//! for<'a> fn(&'a T) <: for<'b> fn(&'b T)
//!
//! and hence `&A` and `&x` were created "together". In general, the
//! intention of the skolemized names is that they are supposed to be
//! fresh names that could never be equal to anything from the outside.
//! But when inference comes into play, we might not be respecting this
//! rule.
//!
//! So the way we solve this is to add a fourth step that examines the
//! constraints that refer to skolemized names. Basically, consider a
//! non-directed verison of the constraint graph. Let `Tainted(x)` be the
//! set of all things reachable from a skolemized variable `x`.
//! `Tainted(x)` should not contain any regions that existed before the
//! step at which the skolemization was performed. So this case here
//! would fail because `&x` was created alone, but is relatable to `&A`.
//!
//! ## Computing the LUB and GLB
//!
//! The paper I pointed you at is written for Haskell. It does not
//! therefore considering subtyping and in particular does not consider
//! LUB or GLB computation. We have to consider this. Here is the
//! algorithm I implemented.
//!
//! First though, let's discuss what we are trying to compute in more
//! detail. The LUB is basically the "common supertype" and the GLB is
//! "common subtype"; one catch is that the LUB should be the
//! *most-specific* common supertype and the GLB should be *most general*
//! common subtype (as opposed to any common supertype or any common
//! subtype).
//!
//! Anyway, to help clarify, here is a table containing some function
//! pairs and their LUB/GLB (for conciseness, in this table, I'm just
//! including the lifetimes here, not the rest of the types, and I'm
//! writing `fn<>` instead of `for<> fn`):
//!
//! ```
//! Type 1 Type 2 LUB GLB
//! fn<'a>('a) fn('X) fn('X) fn<'a>('a)
//! fn('a) fn('X) -- fn<'a>('a)
//! fn<'a,'b>('a, 'b) fn<'x>('x, 'x) fn<'a>('a, 'a) fn<'a,'b>('a, 'b)
//! fn<'a,'b>('a, 'b, 'a) fn<'x,'y>('x, 'y, 'y) fn<'a>('a, 'a, 'a) fn<'a,'b,'c>('a,'b,'c)
//! ```
//!
//! ### Conventions
//!
//! I use lower-case letters (e.g., `&a`) for bound regions and upper-case
//! letters for free regions (`&A`). Region variables written with a
//! dollar-sign (e.g., `$a`). I will try to remember to enumerate the
//! bound-regions on the fn type as well (e.g., `for<'a> fn(&a)`).
//!
//! ### High-level summary
//!
//! Both the LUB and the GLB algorithms work in a similar fashion. They
//! begin by replacing all bound regions (on both sides) with fresh region
//! inference variables. Therefore, both functions are converted to types
//! that contain only free regions. We can then compute the LUB/GLB in a
//! straightforward way, as described in `combine.rs`. This results in an
//! interim type T. The algorithms then examine the regions that appear
//! in T and try to, in some cases, replace them with bound regions to
//! yield the final result.
//!
//! To decide whether to replace a region `R` that appears in `T` with a
//! bound region, the algorithms make use of two bits of information.
//! First is a set `V` that contains all region variables created as part
//! of the LUB/GLB computation. `V` will contain the region variables
//! created to replace the bound regions in the input types, but it also
//! contains 'intermediate' variables created to represent the LUB/GLB of
//! individual regions. Basically, when asked to compute the LUB/GLB of a
//! region variable with another region, the inferencer cannot oblige
//! immediately since the values of that variables are not known.
//! Therefore, it creates a new variable that is related to the two
//! regions. For example, the LUB of two variables `$x` and `$y` is a
//! fresh variable `$z` that is constrained such that `$x <= $z` and `$y
//! <= $z`. So `V` will contain these intermediate variables as well.
//!
//! The other important factor in deciding how to replace a region in T is
//! the function `Tainted($r)` which, for a region variable, identifies
//! all regions that the region variable is related to in some way
//! (`Tainted()` made an appearance in the subtype computation as well).
//!
//! ### LUB
//!
//! The LUB algorithm proceeds in three steps:
//!
//! 1. Replace all bound regions (on both sides) with fresh region
//! inference variables.
//! 2. Compute the LUB "as normal", meaning compute the GLB of each
//! pair of argument types and the LUB of the return types and
//! so forth. Combine those to a new function type `F`.
//! 3. Replace each region `R` that appears in `F` as follows:
//! - Let `V` be the set of variables created during the LUB
//! computational steps 1 and 2, as described in the previous section.
//! - If `R` is not in `V`, replace `R` with itself.
//! - If `Tainted(R)` contains a region that is not in `V`,
//! replace `R` with itself.
//! - Otherwise, select the earliest variable in `Tainted(R)` that originates
//! from the left-hand side and replace `R` with the bound region that
//! this variable was a replacement for.
//!
//! So, let's work through the simplest example: `fn(&A)` and `for<'a> fn(&a)`.
//! In this case, `&a` will be replaced with `$a` and the interim LUB type
//! `fn($b)` will be computed, where `$b=GLB(&A,$a)`. Therefore, `V =
//! {$a, $b}` and `Tainted($b) = { $b, $a, &A }`. When we go to replace
//! `$b`, we find that since `&A \in Tainted($b)` is not a member of `V`,
//! we leave `$b` as is. When region inference happens, `$b` will be
//! resolved to `&A`, as we wanted.
//!
//! Let's look at a more complex one: `fn(&a, &b)` and `fn(&x, &x)`. In
//! this case, we'll end up with a (pre-replacement) LUB type of `fn(&g,
//! &h)` and a graph that looks like:
//!
//! ```
//! $a $b *--$x
//! \ \ / /
//! \ $h-* /
//! $g-----------*
//! ```
//!
//! Here `$g` and `$h` are fresh variables that are created to represent
//! the LUB/GLB of things requiring inference. This means that `V` and
//! `Tainted` will look like:
//!
//! ```
//! V = {$a, $b, $g, $h, $x}
//! Tainted($g) = Tainted($h) = { $a, $b, $h, $g, $x }
//! ```
//!
//! Therefore we replace both `$g` and `$h` with `$a`, and end up
//! with the type `fn(&a, &a)`.
//!
//! ### GLB
//!
//! The procedure for computing the GLB is similar. The difference lies
//! in computing the replacements for the various variables. For each
//! region `R` that appears in the type `F`, we again compute `Tainted(R)`
//! and examine the results:
//!
//! 1. If `R` is not in `V`, it is not replaced.
//! 2. Else, if `Tainted(R)` contains only variables in `V`, and it
//! contains exactly one variable from the LHS and one variable from
//! the RHS, then `R` can be mapped to the bound version of the
//! variable from the LHS.
//! 3. Else, if `Tainted(R)` contains no variable from the LHS and no
//! variable from the RHS, then `R` can be mapped to itself.
//! 4. Else, `R` is mapped to a fresh bound variable.
//!
//! These rules are pretty complex. Let's look at some examples to see
//! how they play out.
//!
//! Out first example was `fn(&a)` and `fn(&X)`. In this case, `&a` will
//! be replaced with `$a` and we will ultimately compute a
//! (pre-replacement) GLB type of `fn($g)` where `$g=LUB($a,&X)`.
//! Therefore, `V={$a,$g}` and `Tainted($g)={$g,$a,&X}. To find the
//! replacement for `$g` we consult the rules above:
//! - Rule (1) does not apply because `$g \in V`
//! - Rule (2) does not apply because `&X \in Tainted($g)`
//! - Rule (3) does not apply because `$a \in Tainted($g)`
//! - Hence, by rule (4), we replace `$g` with a fresh bound variable `&z`.
//! So our final result is `fn(&z)`, which is correct.
//!
//! The next example is `fn(&A)` and `fn(&Z)`. In this case, we will again
//! have a (pre-replacement) GLB of `fn(&g)`, where `$g = LUB(&A,&Z)`.
//! Therefore, `V={$g}` and `Tainted($g) = {$g, &A, &Z}`. In this case,
//! by rule (3), `$g` is mapped to itself, and hence the result is
//! `fn($g)`. This result is correct (in this case, at least), but it is
//! indicative of a case that *can* lead us into concluding that there is
//! no GLB when in fact a GLB does exist. See the section "Questionable
//! Results" below for more details.
//!
//! The next example is `fn(&a, &b)` and `fn(&c, &c)`. In this case, as
//! before, we'll end up with `F=fn($g, $h)` where `Tainted($g) =
//! Tainted($h) = {$g, $h, $a, $b, $c}`. Only rule (4) applies and hence
//! we'll select fresh bound variables `y` and `z` and wind up with
//! `fn(&y, &z)`.
//!
//! For the last example, let's consider what may seem trivial, but is
//! not: `fn(&a, &a)` and `fn(&b, &b)`. In this case, we'll get `F=fn($g,
//! $h)` where `Tainted($g) = {$g, $a, $x}` and `Tainted($h) = {$h, $a,
//! $x}`. Both of these sets contain exactly one bound variable from each
//! side, so we'll map them both to `&a`, resulting in `fn(&a, &a)`, which
//! is the desired result.
//!
//! ### Shortcomings and correctness
//!
//! You may be wondering whether this algorithm is correct. The answer is
//! "sort of". There are definitely cases where they fail to compute a
//! result even though a correct result exists. I believe, though, that
//! if they succeed, then the result is valid, and I will attempt to
//! convince you. The basic argument is that the "pre-replacement" step
//! computes a set of constraints. The replacements, then, attempt to
//! satisfy those constraints, using bound identifiers where needed.
//!
//! For now I will briefly go over the cases for LUB/GLB and identify
//! their intent:
//!
//! - LUB:
//! - The region variables that are substituted in place of bound regions
//! are intended to collect constraints on those bound regions.
//! - If Tainted(R) contains only values in V, then this region is unconstrained
//! and can therefore be generalized, otherwise it cannot.
//! - GLB:
//! - The region variables that are substituted in place of bound regions
//! are intended to collect constraints on those bound regions.
//! - If Tainted(R) contains exactly one variable from each side, and
//! only variables in V, that indicates that those two bound regions
//! must be equated.
//! - Otherwise, if Tainted(R) references any variables from left or right
//! side, then it is trying to combine a bound region with a free one or
//! multiple bound regions, so we need to select fresh bound regions.
//!
//! Sorry this is more of a shorthand to myself. I will try to write up something
//! more convincing in the future.
//!
//! #### Where are the algorithms wrong?
//!
//! - The pre-replacement computation can fail even though using a
//! bound-region would have succeeded.
//! - We will compute GLB(fn(fn($a)), fn(fn($b))) as fn($c) where $c is the
//! GLB of $a and $b. But if inference finds that $a and $b must be mapped
//! to regions without a GLB, then this is effectively a failure to compute
//! the GLB. However, the result `fn<$c>(fn($c))` is a valid GLB.

View File

@ -8,10 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Helper routines for higher-ranked things. See the `doc` module at
* the end of the file for details.
*/
//! Helper routines for higher-ranked things. See the `doc` module at
//! the end of the file for details.
use middle::ty::{mod, Ty, replace_late_bound_regions};
use middle::typeck::infer::{mod, combine, cres, InferCtxt};

View File

@ -8,28 +8,26 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* # Lattice Variables
*
* This file contains generic code for operating on inference variables
* that are characterized by an upper- and lower-bound. The logic and
* reasoning is explained in detail in the large comment in `infer.rs`.
*
* The code in here is defined quite generically so that it can be
* applied both to type variables, which represent types being inferred,
* and fn variables, which represent function types being inferred.
* It may eventually be applied to their types as well, who knows.
* In some cases, the functions are also generic with respect to the
* operation on the lattice (GLB vs LUB).
*
* Although all the functions are generic, we generally write the
* comments in a way that is specific to type variables and the LUB
* operation. It's just easier that way.
*
* In general all of the functions are defined parametrically
* over a `LatticeValue`, which is a value defined with respect to
* a lattice.
*/
//! # Lattice Variables
//!
//! This file contains generic code for operating on inference variables
//! that are characterized by an upper- and lower-bound. The logic and
//! reasoning is explained in detail in the large comment in `infer.rs`.
//!
//! The code in here is defined quite generically so that it can be
//! applied both to type variables, which represent types being inferred,
//! and fn variables, which represent function types being inferred.
//! It may eventually be applied to their types as well, who knows.
//! In some cases, the functions are also generic with respect to the
//! operation on the lattice (GLB vs LUB).
//!
//! Although all the functions are generic, we generally write the
//! comments in a way that is specific to type variables and the LUB
//! operation. It's just easier that way.
//!
//! In general all of the functions are defined parametrically
//! over a `LatticeValue`, which is a value defined with respect to
//! a lattice.
use middle::ty::{TyVar};
use middle::ty::{mod, Ty};

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! See doc.rs for documentation */
//! See doc.rs for documentation
#![allow(non_camel_case_types)]
@ -305,6 +305,8 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>)
}
}
/// Computes the least upper-bound of `a` and `b`. If this is not possible, reports an error and
/// returns ty::err.
pub fn common_supertype<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
origin: TypeOrigin,
a_is_expected: bool,
@ -312,11 +314,6 @@ pub fn common_supertype<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
b: Ty<'tcx>)
-> Ty<'tcx>
{
/*!
* Computes the least upper-bound of `a` and `b`. If this is
* not possible, reports an error and returns ty::err.
*/
debug!("common_supertype({}, {})",
a.repr(cx.tcx), b.repr(cx.tcx));
@ -754,17 +751,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
.collect()
}
/// Given a set of generics defined on a type or impl, returns a substitution mapping each
/// type/region parameter to a fresh inference variable.
pub fn fresh_substs_for_generics(&self,
span: Span,
generics: &ty::Generics<'tcx>)
-> subst::Substs<'tcx>
{
/*!
* Given a set of generics defined on a type or impl, returns
* a substitution mapping each type/region parameter to a
* fresh inference variable.
*/
let type_params =
generics.types.map(
|_| self.next_ty_var());
@ -774,18 +767,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
subst::Substs::new(type_params, region_params)
}
/// Given a set of generics defined on a trait, returns a substitution mapping each output
/// type/region parameter to a fresh inference variable, and mapping the self type to
/// `self_ty`.
pub fn fresh_substs_for_trait(&self,
span: Span,
generics: &ty::Generics<'tcx>,
self_ty: Ty<'tcx>)
-> subst::Substs<'tcx>
{
/*!
* Given a set of generics defined on a trait, returns a
* substitution mapping each output type/region parameter to a
* fresh inference variable, and mapping the self type to
* `self_ty`.
*/
assert!(generics.types.len(subst::SelfSpace) == 1);
assert!(generics.types.len(subst::FnSpace) == 0);

View File

@ -8,371 +8,367 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Region inference module.
# Terminology
Note that we use the terms region and lifetime interchangeably,
though the term `lifetime` is preferred.
# Introduction
Region inference uses a somewhat more involved algorithm than type
inference. It is not the most efficient thing ever written though it
seems to work well enough in practice (famous last words). The reason
that we use a different algorithm is because, unlike with types, it is
impractical to hand-annotate with regions (in some cases, there aren't
even the requisite syntactic forms). So we have to get it right, and
it's worth spending more time on a more involved analysis. Moreover,
regions are a simpler case than types: they don't have aggregate
structure, for example.
Unlike normal type inference, which is similar in spirit to H-M and thus
works progressively, the region type inference works by accumulating
constraints over the course of a function. Finally, at the end of
processing a function, we process and solve the constraints all at
once.
The constraints are always of one of three possible forms:
- ConstrainVarSubVar(R_i, R_j) states that region variable R_i
must be a subregion of R_j
- ConstrainRegSubVar(R, R_i) states that the concrete region R
(which must not be a variable) must be a subregion of the varibale R_i
- ConstrainVarSubReg(R_i, R) is the inverse
# Building up the constraints
Variables and constraints are created using the following methods:
- `new_region_var()` creates a new, unconstrained region variable;
- `make_subregion(R_i, R_j)` states that R_i is a subregion of R_j
- `lub_regions(R_i, R_j) -> R_k` returns a region R_k which is
the smallest region that is greater than both R_i and R_j
- `glb_regions(R_i, R_j) -> R_k` returns a region R_k which is
the greatest region that is smaller than both R_i and R_j
The actual region resolution algorithm is not entirely
obvious, though it is also not overly complex.
## Snapshotting
It is also permitted to try (and rollback) changes to the graph. This
is done by invoking `start_snapshot()`, which returns a value. Then
later you can call `rollback_to()` which undoes the work.
Alternatively, you can call `commit()` which ends all snapshots.
Snapshots can be recursive---so you can start a snapshot when another
is in progress, but only the root snapshot can "commit".
# Resolving constraints
The constraint resolution algorithm is not super complex but also not
entirely obvious. Here I describe the problem somewhat abstractly,
then describe how the current code works. There may be other, smarter
ways of doing this with which I am unfamiliar and can't be bothered to
research at the moment. - NDM
## The problem
Basically our input is a directed graph where nodes can be divided
into two categories: region variables and concrete regions. Each edge
`R -> S` in the graph represents a constraint that the region `R` is a
subregion of the region `S`.
Region variable nodes can have arbitrary degree. There is one region
variable node per region variable.
Each concrete region node is associated with some, well, concrete
region: e.g., a free lifetime, or the region for a particular scope.
Note that there may be more than one concrete region node for a
particular region value. Moreover, because of how the graph is built,
we know that all concrete region nodes have either in-degree 1 or
out-degree 1.
Before resolution begins, we build up the constraints in a hashmap
that maps `Constraint` keys to spans. During resolution, we construct
the actual `Graph` structure that we describe here.
## Our current algorithm
We divide region variables into two groups: Expanding and Contracting.
Expanding region variables are those that have a concrete region
predecessor (direct or indirect). Contracting region variables are
all others.
We first resolve the values of Expanding region variables and then
process Contracting ones. We currently use an iterative, fixed-point
procedure (but read on, I believe this could be replaced with a linear
walk). Basically we iterate over the edges in the graph, ensuring
that, if the source of the edge has a value, then this value is a
subregion of the target value. If the target does not yet have a
value, it takes the value from the source. If the target already had
a value, then the resulting value is Least Upper Bound of the old and
new values. When we are done, each Expanding node will have the
smallest region that it could possibly have and still satisfy the
constraints.
We next process the Contracting nodes. Here we again iterate over the
edges, only this time we move values from target to source (if the
source is a Contracting node). For each contracting node, we compute
its value as the GLB of all its successors. Basically contracting
nodes ensure that there is overlap between their successors; we will
ultimately infer the largest overlap possible.
# The Region Hierarchy
## Without closures
Let's first consider the region hierarchy without thinking about
closures, because they add a lot of complications. The region
hierarchy *basically* mirrors the lexical structure of the code.
There is a region for every piece of 'evaluation' that occurs, meaning
every expression, block, and pattern (patterns are considered to
"execute" by testing the value they are applied to and creating any
relevant bindings). So, for example:
fn foo(x: int, y: int) { // -+
// +------------+ // |
// | +-----+ // |
// | +-+ +-+ +-+ // |
// | | | | | | | // |
// v v v v v v v // |
let z = x + y; // |
... // |
} // -+
fn bar() { ... }
In this example, there is a region for the fn body block as a whole,
and then a subregion for the declaration of the local variable.
Within that, there are sublifetimes for the assignment pattern and
also the expression `x + y`. The expression itself has sublifetimes
for evaluating `x` and `y`.
## Function calls
Function calls are a bit tricky. I will describe how we handle them
*now* and then a bit about how we can improve them (Issue #6268).
Consider a function call like `func(expr1, expr2)`, where `func`,
`arg1`, and `arg2` are all arbitrary expressions. Currently,
we construct a region hierarchy like:
+----------------+
| |
+--+ +---+ +---+|
v v v v v vv
func(expr1, expr2)
Here you can see that the call as a whole has a region and the
function plus arguments are subregions of that. As a side-effect of
this, we get a lot of spurious errors around nested calls, in
particular when combined with `&mut` functions. For example, a call
like this one
self.foo(self.bar())
where both `foo` and `bar` are `&mut self` functions will always yield
an error.
Here is a more involved example (which is safe) so we can see what's
going on:
struct Foo { f: uint, g: uint }
...
fn add(p: &mut uint, v: uint) {
*p += v;
}
...
fn inc(p: &mut uint) -> uint {
*p += 1; *p
}
fn weird() {
let mut x: Box<Foo> = box Foo { ... };
'a: add(&mut (*x).f,
'b: inc(&mut (*x).f)) // (..)
}
The important part is the line marked `(..)` which contains a call to
`add()`. The first argument is a mutable borrow of the field `f`. The
second argument also borrows the field `f`. Now, in the current borrow
checker, the first borrow is given the lifetime of the call to
`add()`, `'a`. The second borrow is given the lifetime of `'b` of the
call to `inc()`. Because `'b` is considered to be a sublifetime of
`'a`, an error is reported since there are two co-existing mutable
borrows of the same data.
However, if we were to examine the lifetimes a bit more carefully, we
can see that this error is unnecessary. Let's examine the lifetimes
involved with `'a` in detail. We'll break apart all the steps involved
in a call expression:
'a: {
'a_arg1: let a_temp1: ... = add;
'a_arg2: let a_temp2: &'a mut uint = &'a mut (*x).f;
'a_arg3: let a_temp3: uint = {
let b_temp1: ... = inc;
let b_temp2: &'b = &'b mut (*x).f;
'b_call: b_temp1(b_temp2)
};
'a_call: a_temp1(a_temp2, a_temp3) // (**)
}
Here we see that the lifetime `'a` includes a number of substatements.
In particular, there is this lifetime I've called `'a_call` that
corresponds to the *actual execution of the function `add()`*, after
all arguments have been evaluated. There is a corresponding lifetime
`'b_call` for the execution of `inc()`. If we wanted to be precise
about it, the lifetime of the two borrows should be `'a_call` and
`'b_call` respectively, since the references that were created
will not be dereferenced except during the execution itself.
However, this model by itself is not sound. The reason is that
while the two references that are created will never be used
simultaneously, it is still true that the first reference is
*created* before the second argument is evaluated, and so even though
it will not be *dereferenced* during the evaluation of the second
argument, it can still be *invalidated* by that evaluation. Consider
this similar but unsound example:
struct Foo { f: uint, g: uint }
...
fn add(p: &mut uint, v: uint) {
*p += v;
}
...
fn consume(x: Box<Foo>) -> uint {
x.f + x.g
}
fn weird() {
let mut x: Box<Foo> = box Foo { ... };
'a: add(&mut (*x).f, consume(x)) // (..)
}
In this case, the second argument to `add` actually consumes `x`, thus
invalidating the first argument.
So, for now, we exclude the `call` lifetimes from our model.
Eventually I would like to include them, but we will have to make the
borrow checker handle this situation correctly. In particular, if
there is a reference created whose lifetime does not enclose
the borrow expression, we must issue sufficient restrictions to ensure
that the pointee remains valid.
## Adding closures
The other significant complication to the region hierarchy is
closures. I will describe here how closures should work, though some
of the work to implement this model is ongoing at the time of this
writing.
The body of closures are type-checked along with the function that
creates them. However, unlike other expressions that appear within the
function body, it is not entirely obvious when a closure body executes
with respect to the other expressions. This is because the closure
body will execute whenever the closure is called; however, we can
never know precisely when the closure will be called, especially
without some sort of alias analysis.
However, we can place some sort of limits on when the closure
executes. In particular, the type of every closure `fn:'r K` includes
a region bound `'r`. This bound indicates the maximum lifetime of that
closure; once we exit that region, the closure cannot be called
anymore. Therefore, we say that the lifetime of the closure body is a
sublifetime of the closure bound, but the closure body itself is unordered
with respect to other parts of the code.
For example, consider the following fragment of code:
'a: {
let closure: fn:'a() = || 'b: {
'c: ...
};
'd: ...
}
Here we have four lifetimes, `'a`, `'b`, `'c`, and `'d`. The closure
`closure` is bounded by the lifetime `'a`. The lifetime `'b` is the
lifetime of the closure body, and `'c` is some statement within the
closure body. Finally, `'d` is a statement within the outer block that
created the closure.
We can say that the closure body `'b` is a sublifetime of `'a` due to
the closure bound. By the usual lexical scoping conventions, the
statement `'c` is clearly a sublifetime of `'b`, and `'d` is a
sublifetime of `'d`. However, there is no ordering between `'c` and
`'d` per se (this kind of ordering between statements is actually only
an issue for dataflow; passes like the borrow checker must assume that
closures could execute at any time from the moment they are created
until they go out of scope).
### Complications due to closure bound inference
There is only one problem with the above model: in general, we do not
actually *know* the closure bounds during region inference! In fact,
closure bounds are almost always region variables! This is very tricky
because the inference system implicitly assumes that we can do things
like compute the LUB of two scoped lifetimes without needing to know
the values of any variables.
Here is an example to illustrate the problem:
fn identify<T>(x: T) -> T { x }
fn foo() { // 'foo is the function body
'a: {
let closure = identity(|| 'b: {
'c: ...
});
'd: closure();
}
'e: ...;
}
In this example, the closure bound is not explicit. At compile time,
we will create a region variable (let's call it `V0`) to represent the
closure bound.
The primary difficulty arises during the constraint propagation phase.
Imagine there is some variable with incoming edges from `'c` and `'d`.
This means that the value of the variable must be `LUB('c,
'd)`. However, without knowing what the closure bound `V0` is, we
can't compute the LUB of `'c` and `'d`! Any we don't know the closure
bound until inference is done.
The solution is to rely on the fixed point nature of inference.
Basically, when we must compute `LUB('c, 'd)`, we just use the current
value for `V0` as the closure's bound. If `V0`'s binding should
change, then we will do another round of inference, and the result of
`LUB('c, 'd)` will change.
One minor implication of this is that the graph does not in fact track
the full set of dependencies between edges. We cannot easily know
whether the result of a LUB computation will change, since there may
be indirect dependencies on other variables that are not reflected on
the graph. Therefore, we must *always* iterate over all edges when
doing the fixed point calculation, not just those adjacent to nodes
whose values have changed.
Were it not for this requirement, we could in fact avoid fixed-point
iteration altogether. In that universe, we could instead first
identify and remove strongly connected components (SCC) in the graph.
Note that such components must consist solely of region variables; all
of these variables can effectively be unified into a single variable.
Once SCCs are removed, we are left with a DAG. At this point, we
could walk the DAG in topological order once to compute the expanding
nodes, and again in reverse topological order to compute the
contracting nodes. However, as I said, this does not work given the
current treatment of closure bounds, but perhaps in the future we can
address this problem somehow and make region inference somewhat more
efficient. Note that this is solely a matter of performance, not
expressiveness.
### Skolemization
For a discussion on skolemization and higher-ranked subtyping, please
see the module `middle::typeck::infer::higher_ranked::doc`.
*/
//! Region inference module.
//!
//! # Terminology
//!
//! Note that we use the terms region and lifetime interchangeably,
//! though the term `lifetime` is preferred.
//!
//! # Introduction
//!
//! Region inference uses a somewhat more involved algorithm than type
//! inference. It is not the most efficient thing ever written though it
//! seems to work well enough in practice (famous last words). The reason
//! that we use a different algorithm is because, unlike with types, it is
//! impractical to hand-annotate with regions (in some cases, there aren't
//! even the requisite syntactic forms). So we have to get it right, and
//! it's worth spending more time on a more involved analysis. Moreover,
//! regions are a simpler case than types: they don't have aggregate
//! structure, for example.
//!
//! Unlike normal type inference, which is similar in spirit to H-M and thus
//! works progressively, the region type inference works by accumulating
//! constraints over the course of a function. Finally, at the end of
//! processing a function, we process and solve the constraints all at
//! once.
//!
//! The constraints are always of one of three possible forms:
//!
//! - ConstrainVarSubVar(R_i, R_j) states that region variable R_i
//! must be a subregion of R_j
//! - ConstrainRegSubVar(R, R_i) states that the concrete region R
//! (which must not be a variable) must be a subregion of the varibale R_i
//! - ConstrainVarSubReg(R_i, R) is the inverse
//!
//! # Building up the constraints
//!
//! Variables and constraints are created using the following methods:
//!
//! - `new_region_var()` creates a new, unconstrained region variable;
//! - `make_subregion(R_i, R_j)` states that R_i is a subregion of R_j
//! - `lub_regions(R_i, R_j) -> R_k` returns a region R_k which is
//! the smallest region that is greater than both R_i and R_j
//! - `glb_regions(R_i, R_j) -> R_k` returns a region R_k which is
//! the greatest region that is smaller than both R_i and R_j
//!
//! The actual region resolution algorithm is not entirely
//! obvious, though it is also not overly complex.
//!
//! ## Snapshotting
//!
//! It is also permitted to try (and rollback) changes to the graph. This
//! is done by invoking `start_snapshot()`, which returns a value. Then
//! later you can call `rollback_to()` which undoes the work.
//! Alternatively, you can call `commit()` which ends all snapshots.
//! Snapshots can be recursive---so you can start a snapshot when another
//! is in progress, but only the root snapshot can "commit".
//!
//! # Resolving constraints
//!
//! The constraint resolution algorithm is not super complex but also not
//! entirely obvious. Here I describe the problem somewhat abstractly,
//! then describe how the current code works. There may be other, smarter
//! ways of doing this with which I am unfamiliar and can't be bothered to
//! research at the moment. - NDM
//!
//! ## The problem
//!
//! Basically our input is a directed graph where nodes can be divided
//! into two categories: region variables and concrete regions. Each edge
//! `R -> S` in the graph represents a constraint that the region `R` is a
//! subregion of the region `S`.
//!
//! Region variable nodes can have arbitrary degree. There is one region
//! variable node per region variable.
//!
//! Each concrete region node is associated with some, well, concrete
//! region: e.g., a free lifetime, or the region for a particular scope.
//! Note that there may be more than one concrete region node for a
//! particular region value. Moreover, because of how the graph is built,
//! we know that all concrete region nodes have either in-degree 1 or
//! out-degree 1.
//!
//! Before resolution begins, we build up the constraints in a hashmap
//! that maps `Constraint` keys to spans. During resolution, we construct
//! the actual `Graph` structure that we describe here.
//!
//! ## Our current algorithm
//!
//! We divide region variables into two groups: Expanding and Contracting.
//! Expanding region variables are those that have a concrete region
//! predecessor (direct or indirect). Contracting region variables are
//! all others.
//!
//! We first resolve the values of Expanding region variables and then
//! process Contracting ones. We currently use an iterative, fixed-point
//! procedure (but read on, I believe this could be replaced with a linear
//! walk). Basically we iterate over the edges in the graph, ensuring
//! that, if the source of the edge has a value, then this value is a
//! subregion of the target value. If the target does not yet have a
//! value, it takes the value from the source. If the target already had
//! a value, then the resulting value is Least Upper Bound of the old and
//! new values. When we are done, each Expanding node will have the
//! smallest region that it could possibly have and still satisfy the
//! constraints.
//!
//! We next process the Contracting nodes. Here we again iterate over the
//! edges, only this time we move values from target to source (if the
//! source is a Contracting node). For each contracting node, we compute
//! its value as the GLB of all its successors. Basically contracting
//! nodes ensure that there is overlap between their successors; we will
//! ultimately infer the largest overlap possible.
//!
//! # The Region Hierarchy
//!
//! ## Without closures
//!
//! Let's first consider the region hierarchy without thinking about
//! closures, because they add a lot of complications. The region
//! hierarchy *basically* mirrors the lexical structure of the code.
//! There is a region for every piece of 'evaluation' that occurs, meaning
//! every expression, block, and pattern (patterns are considered to
//! "execute" by testing the value they are applied to and creating any
//! relevant bindings). So, for example:
//!
//! fn foo(x: int, y: int) { // -+
//! // +------------+ // |
//! // | +-----+ // |
//! // | +-+ +-+ +-+ // |
//! // | | | | | | | // |
//! // v v v v v v v // |
//! let z = x + y; // |
//! ... // |
//! } // -+
//!
//! fn bar() { ... }
//!
//! In this example, there is a region for the fn body block as a whole,
//! and then a subregion for the declaration of the local variable.
//! Within that, there are sublifetimes for the assignment pattern and
//! also the expression `x + y`. The expression itself has sublifetimes
//! for evaluating `x` and `y`.
//!
//! ## Function calls
//!
//! Function calls are a bit tricky. I will describe how we handle them
//! *now* and then a bit about how we can improve them (Issue #6268).
//!
//! Consider a function call like `func(expr1, expr2)`, where `func`,
//! `arg1`, and `arg2` are all arbitrary expressions. Currently,
//! we construct a region hierarchy like:
//!
//! +----------------+
//! | |
//! +--+ +---+ +---+|
//! v v v v v vv
//! func(expr1, expr2)
//!
//! Here you can see that the call as a whole has a region and the
//! function plus arguments are subregions of that. As a side-effect of
//! this, we get a lot of spurious errors around nested calls, in
//! particular when combined with `&mut` functions. For example, a call
//! like this one
//!
//! self.foo(self.bar())
//!
//! where both `foo` and `bar` are `&mut self` functions will always yield
//! an error.
//!
//! Here is a more involved example (which is safe) so we can see what's
//! going on:
//!
//! struct Foo { f: uint, g: uint }
//! ...
//! fn add(p: &mut uint, v: uint) {
//! *p += v;
//! }
//! ...
//! fn inc(p: &mut uint) -> uint {
//! *p += 1; *p
//! }
//! fn weird() {
//! let mut x: Box<Foo> = box Foo { ... };
//! 'a: add(&mut (*x).f,
//! 'b: inc(&mut (*x).f)) // (..)
//! }
//!
//! The important part is the line marked `(..)` which contains a call to
//! `add()`. The first argument is a mutable borrow of the field `f`. The
//! second argument also borrows the field `f`. Now, in the current borrow
//! checker, the first borrow is given the lifetime of the call to
//! `add()`, `'a`. The second borrow is given the lifetime of `'b` of the
//! call to `inc()`. Because `'b` is considered to be a sublifetime of
//! `'a`, an error is reported since there are two co-existing mutable
//! borrows of the same data.
//!
//! However, if we were to examine the lifetimes a bit more carefully, we
//! can see that this error is unnecessary. Let's examine the lifetimes
//! involved with `'a` in detail. We'll break apart all the steps involved
//! in a call expression:
//!
//! 'a: {
//! 'a_arg1: let a_temp1: ... = add;
//! 'a_arg2: let a_temp2: &'a mut uint = &'a mut (*x).f;
//! 'a_arg3: let a_temp3: uint = {
//! let b_temp1: ... = inc;
//! let b_temp2: &'b = &'b mut (*x).f;
//! 'b_call: b_temp1(b_temp2)
//! };
//! 'a_call: a_temp1(a_temp2, a_temp3) // (**)
//! }
//!
//! Here we see that the lifetime `'a` includes a number of substatements.
//! In particular, there is this lifetime I've called `'a_call` that
//! corresponds to the *actual execution of the function `add()`*, after
//! all arguments have been evaluated. There is a corresponding lifetime
//! `'b_call` for the execution of `inc()`. If we wanted to be precise
//! about it, the lifetime of the two borrows should be `'a_call` and
//! `'b_call` respectively, since the references that were created
//! will not be dereferenced except during the execution itself.
//!
//! However, this model by itself is not sound. The reason is that
//! while the two references that are created will never be used
//! simultaneously, it is still true that the first reference is
//! *created* before the second argument is evaluated, and so even though
//! it will not be *dereferenced* during the evaluation of the second
//! argument, it can still be *invalidated* by that evaluation. Consider
//! this similar but unsound example:
//!
//! struct Foo { f: uint, g: uint }
//! ...
//! fn add(p: &mut uint, v: uint) {
//! *p += v;
//! }
//! ...
//! fn consume(x: Box<Foo>) -> uint {
//! x.f + x.g
//! }
//! fn weird() {
//! let mut x: Box<Foo> = box Foo { ... };
//! 'a: add(&mut (*x).f, consume(x)) // (..)
//! }
//!
//! In this case, the second argument to `add` actually consumes `x`, thus
//! invalidating the first argument.
//!
//! So, for now, we exclude the `call` lifetimes from our model.
//! Eventually I would like to include them, but we will have to make the
//! borrow checker handle this situation correctly. In particular, if
//! there is a reference created whose lifetime does not enclose
//! the borrow expression, we must issue sufficient restrictions to ensure
//! that the pointee remains valid.
//!
//! ## Adding closures
//!
//! The other significant complication to the region hierarchy is
//! closures. I will describe here how closures should work, though some
//! of the work to implement this model is ongoing at the time of this
//! writing.
//!
//! The body of closures are type-checked along with the function that
//! creates them. However, unlike other expressions that appear within the
//! function body, it is not entirely obvious when a closure body executes
//! with respect to the other expressions. This is because the closure
//! body will execute whenever the closure is called; however, we can
//! never know precisely when the closure will be called, especially
//! without some sort of alias analysis.
//!
//! However, we can place some sort of limits on when the closure
//! executes. In particular, the type of every closure `fn:'r K` includes
//! a region bound `'r`. This bound indicates the maximum lifetime of that
//! closure; once we exit that region, the closure cannot be called
//! anymore. Therefore, we say that the lifetime of the closure body is a
//! sublifetime of the closure bound, but the closure body itself is unordered
//! with respect to other parts of the code.
//!
//! For example, consider the following fragment of code:
//!
//! 'a: {
//! let closure: fn:'a() = || 'b: {
//! 'c: ...
//! };
//! 'd: ...
//! }
//!
//! Here we have four lifetimes, `'a`, `'b`, `'c`, and `'d`. The closure
//! `closure` is bounded by the lifetime `'a`. The lifetime `'b` is the
//! lifetime of the closure body, and `'c` is some statement within the
//! closure body. Finally, `'d` is a statement within the outer block that
//! created the closure.
//!
//! We can say that the closure body `'b` is a sublifetime of `'a` due to
//! the closure bound. By the usual lexical scoping conventions, the
//! statement `'c` is clearly a sublifetime of `'b`, and `'d` is a
//! sublifetime of `'d`. However, there is no ordering between `'c` and
//! `'d` per se (this kind of ordering between statements is actually only
//! an issue for dataflow; passes like the borrow checker must assume that
//! closures could execute at any time from the moment they are created
//! until they go out of scope).
//!
//! ### Complications due to closure bound inference
//!
//! There is only one problem with the above model: in general, we do not
//! actually *know* the closure bounds during region inference! In fact,
//! closure bounds are almost always region variables! This is very tricky
//! because the inference system implicitly assumes that we can do things
//! like compute the LUB of two scoped lifetimes without needing to know
//! the values of any variables.
//!
//! Here is an example to illustrate the problem:
//!
//! fn identify<T>(x: T) -> T { x }
//!
//! fn foo() { // 'foo is the function body
//! 'a: {
//! let closure = identity(|| 'b: {
//! 'c: ...
//! });
//! 'd: closure();
//! }
//! 'e: ...;
//! }
//!
//! In this example, the closure bound is not explicit. At compile time,
//! we will create a region variable (let's call it `V0`) to represent the
//! closure bound.
//!
//! The primary difficulty arises during the constraint propagation phase.
//! Imagine there is some variable with incoming edges from `'c` and `'d`.
//! This means that the value of the variable must be `LUB('c,
//! 'd)`. However, without knowing what the closure bound `V0` is, we
//! can't compute the LUB of `'c` and `'d`! Any we don't know the closure
//! bound until inference is done.
//!
//! The solution is to rely on the fixed point nature of inference.
//! Basically, when we must compute `LUB('c, 'd)`, we just use the current
//! value for `V0` as the closure's bound. If `V0`'s binding should
//! change, then we will do another round of inference, and the result of
//! `LUB('c, 'd)` will change.
//!
//! One minor implication of this is that the graph does not in fact track
//! the full set of dependencies between edges. We cannot easily know
//! whether the result of a LUB computation will change, since there may
//! be indirect dependencies on other variables that are not reflected on
//! the graph. Therefore, we must *always* iterate over all edges when
//! doing the fixed point calculation, not just those adjacent to nodes
//! whose values have changed.
//!
//! Were it not for this requirement, we could in fact avoid fixed-point
//! iteration altogether. In that universe, we could instead first
//! identify and remove strongly connected components (SCC) in the graph.
//! Note that such components must consist solely of region variables; all
//! of these variables can effectively be unified into a single variable.
//! Once SCCs are removed, we are left with a DAG. At this point, we
//! could walk the DAG in topological order once to compute the expanding
//! nodes, and again in reverse topological order to compute the
//! contracting nodes. However, as I said, this does not work given the
//! current treatment of closure bounds, but perhaps in the future we can
//! address this problem somehow and make region inference somewhat more
//! efficient. Note that this is solely a matter of performance, not
//! expressiveness.
//!
//! ### Skolemization
//!
//! For a discussion on skolemization and higher-ranked subtyping, please
//! see the module `middle::typeck::infer::higher_ranked::doc`.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! See doc.rs */
//! See doc.rs
pub use self::Constraint::*;
pub use self::Verify::*;
@ -597,15 +597,10 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
.collect()
}
/// Computes all regions that have been related to `r0` in any way since the mark `mark` was
/// made---`r0` itself will be the first entry. This is used when checking whether skolemized
/// regions are being improperly related to other regions.
pub fn tainted(&self, mark: RegionMark, r0: Region) -> Vec<Region> {
/*!
* Computes all regions that have been related to `r0` in any
* way since the mark `mark` was made---`r0` itself will be
* the first entry. This is used when checking whether
* skolemized regions are being improperly related to other
* regions.
*/
debug!("tainted(mark={}, r0={})", mark, r0.repr(self.tcx));
let _indenter = indenter();
@ -781,16 +776,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
}
/// Computes a region that encloses both free region arguments. Guarantee that if the same two
/// regions are given as argument, in any order, a consistent result is returned.
fn lub_free_regions(&self,
a: &FreeRegion,
b: &FreeRegion) -> ty::Region
{
/*!
* Computes a region that encloses both free region arguments.
* Guarantee that if the same two regions are given as argument,
* in any order, a consistent result is returned.
*/
return match a.cmp(b) {
Less => helper(self, a, b),
Greater => helper(self, b, a),
@ -882,16 +873,13 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
}
/// Computes a region that is enclosed by both free region arguments, if any. Guarantees that
/// if the same two regions are given as argument, in any order, a consistent result is
/// returned.
fn glb_free_regions(&self,
a: &FreeRegion,
b: &FreeRegion) -> cres<'tcx, ty::Region>
{
/*!
* Computes a region that is enclosed by both free region arguments,
* if any. Guarantees that if the same two regions are given as argument,
* in any order, a consistent result is returned.
*/
return match a.cmp(b) {
Less => helper(self, a, b),
Greater => helper(self, b, a),

View File

@ -8,37 +8,27 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Skolemization is the process of replacing unknown variables with
* fresh types. The idea is that the type, after skolemization,
* contains no inference variables but instead contains either a value
* for each variable or fresh "arbitrary" types wherever a variable
* would have been.
*
* Skolemization is used primarily to get a good type for inserting
* into a cache. The result summarizes what the type inferencer knows
* "so far". The primary place it is used right now is in the trait
* matching algorithm, which needs to be able to cache whether an
* `impl` self type matches some other type X -- *without* affecting
* `X`. That means if that if the type `X` is in fact an unbound type
* variable, we want the match to be regarded as ambiguous, because
* depending on what type that type variable is ultimately assigned,
* the match may or may not succeed.
*
* Note that you should be careful not to allow the output of
* skolemization to leak to the user in error messages or in any other
* form. Skolemization is only really useful as an internal detail.
*
* __An important detail concerning regions.__ The skolemizer also
* replaces *all* regions with 'static. The reason behind this is
* that, in general, we do not take region relationships into account
* when making type-overloaded decisions. This is important because of
* the design of the region inferencer, which is not based on
* unification but rather on accumulating and then solving a set of
* constraints. In contrast, the type inferencer assigns a value to
* each type variable only once, and it does so as soon as it can, so
* it is reasonable to ask what the type inferencer knows "so far".
*/
//! Skolemization is the process of replacing unknown variables with fresh types. The idea is that
//! the type, after skolemization, contains no inference variables but instead contains either a
//! value for each variable or fresh "arbitrary" types wherever a variable would have been.
//!
//! Skolemization is used primarily to get a good type for inserting into a cache. The result
//! summarizes what the type inferencer knows "so far". The primary place it is used right now is
//! in the trait matching algorithm, which needs to be able to cache whether an `impl` self type
//! matches some other type X -- *without* affecting `X`. That means if that if the type `X` is in
//! fact an unbound type variable, we want the match to be regarded as ambiguous, because depending
//! on what type that type variable is ultimately assigned, the match may or may not succeed.
//!
//! Note that you should be careful not to allow the output of skolemization to leak to the user in
//! error messages or in any other form. Skolemization is only really useful as an internal detail.
//!
//! __An important detail concerning regions.__ The skolemizer also replaces *all* regions with
//! 'static. The reason behind this is that, in general, we do not take region relationships into
//! account when making type-overloaded decisions. This is important because of the design of the
//! region inferencer, which is not based on unification but rather on accumulating and then
//! solving a set of constraints. In contrast, the type inferencer assigns a value to each type
//! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type
//! inferencer knows "so far".
use middle::ty::{mod, Ty};
use middle::ty_fold;

View File

@ -72,12 +72,10 @@ impl<'tcx> TypeVariableTable<'tcx> {
self.values.get(vid.index).diverging
}
/// Records that `a <: b`, `a :> b`, or `a == b`, depending on `dir`.
///
/// Precondition: neither `a` nor `b` are known.
pub fn relate_vars(&mut self, a: ty::TyVid, dir: RelationDir, b: ty::TyVid) {
/*!
* Records that `a <: b`, `a :> b`, or `a == b`, depending on `dir`.
*
* Precondition: neither `a` nor `b` are known.
*/
if a != b {
self.relations(a).push((dir, b));
@ -86,19 +84,15 @@ impl<'tcx> TypeVariableTable<'tcx> {
}
}
/// Instantiates `vid` with the type `ty` and then pushes an entry onto `stack` for each of the
/// relations of `vid` to other variables. The relations will have the form `(ty, dir, vid1)`
/// where `vid1` is some other variable id.
pub fn instantiate_and_push(
&mut self,
vid: ty::TyVid,
ty: Ty<'tcx>,
stack: &mut Vec<(Ty<'tcx>, RelationDir, ty::TyVid)>)
{
/*!
* Instantiates `vid` with the type `ty` and then pushes an
* entry onto `stack` for each of the relations of `vid` to
* other variables. The relations will have the form `(ty,
* dir, vid1)` where `vid1` is some other variable id.
*/
let old_value = {
let value_ptr = &mut self.values.get_mut(vid.index).value;
mem::replace(value_ptr, Known(ty))

View File

@ -134,13 +134,9 @@ impl<'tcx, V:PartialEq+Clone+Repr<'tcx>, K:UnifyKey<'tcx, V>> UnificationTable<K
k
}
/// Find the root node for `vid`. This uses the standard union-find algorithm with path
/// compression: http://en.wikipedia.org/wiki/Disjoint-set_data_structure
pub fn get(&mut self, tcx: &ty::ctxt, vid: K) -> Node<K,V> {
/*!
* Find the root node for `vid`. This uses the standard
* union-find algorithm with path compression:
* http://en.wikipedia.org/wiki/Disjoint-set_data_structure
*/
let index = vid.index();
let value = (*self.values.get(index)).clone();
match value {
@ -165,16 +161,13 @@ impl<'tcx, V:PartialEq+Clone+Repr<'tcx>, K:UnifyKey<'tcx, V>> UnificationTable<K
}
}
/// Sets the value for `vid` to `new_value`. `vid` MUST be a root node! Also, we must be in the
/// middle of a snapshot.
pub fn set(&mut self,
tcx: &ty::ctxt<'tcx>,
key: K,
new_value: VarValue<K,V>)
{
/*!
* Sets the value for `vid` to `new_value`. `vid` MUST be a
* root node! Also, we must be in the middle of a snapshot.
*/
assert!(self.is_root(&key));
debug!("Updating variable {} to {}",
@ -184,19 +177,15 @@ impl<'tcx, V:PartialEq+Clone+Repr<'tcx>, K:UnifyKey<'tcx, V>> UnificationTable<K
self.values.set(key.index(), new_value);
}
/// Either redirects node_a to node_b or vice versa, depending on the relative rank. Returns
/// the new root and rank. You should then update the value of the new root to something
/// suitable.
pub fn unify(&mut self,
tcx: &ty::ctxt<'tcx>,
node_a: &Node<K,V>,
node_b: &Node<K,V>)
-> (K, uint)
{
/*!
* Either redirects node_a to node_b or vice versa, depending
* on the relative rank. Returns the new root and rank. You
* should then update the value of the new root to something
* suitable.
*/
debug!("unify(node_a(id={}, rank={}), node_b(id={}, rank={}))",
node_a.key.repr(tcx),
node_a.rank,
@ -270,19 +259,15 @@ pub trait InferCtxtMethodsForSimplyUnifiableTypes<'tcx, V:SimplyUnifiable<'tcx>,
impl<'a,'tcx,V:SimplyUnifiable<'tcx>,K:UnifyKey<'tcx, Option<V>>>
InferCtxtMethodsForSimplyUnifiableTypes<'tcx, V, K> for InferCtxt<'a, 'tcx>
{
/// Unifies two simple keys. Because simple keys do not have any subtyping relationships, if
/// both keys have already been associated with a value, then those two values must be the
/// same.
fn simple_vars(&self,
a_is_expected: bool,
a_id: K,
b_id: K)
-> ures<'tcx>
{
/*!
* Unifies two simple keys. Because simple keys do
* not have any subtyping relationships, if both keys
* have already been associated with a value, then those two
* values must be the same.
*/
let tcx = self.tcx;
let table = UnifyKey::unification_table(self);
let node_a = table.borrow_mut().get(tcx, a_id);
@ -316,19 +301,14 @@ impl<'a,'tcx,V:SimplyUnifiable<'tcx>,K:UnifyKey<'tcx, Option<V>>>
return Ok(())
}
/// Sets the value of the key `a_id` to `b`. Because simple keys do not have any subtyping
/// relationships, if `a_id` already has a value, it must be the same as `b`.
fn simple_var_t(&self,
a_is_expected: bool,
a_id: K,
b: V)
-> ures<'tcx>
{
/*!
* Sets the value of the key `a_id` to `b`. Because
* simple keys do not have any subtyping relationships,
* if `a_id` already has a value, it must be the same as
* `b`.
*/
let tcx = self.tcx;
let table = UnifyKey::unification_table(self);
let node_a = table.borrow_mut().get(tcx, a_id);

View File

@ -8,189 +8,186 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
//! This file infers the variance of type and lifetime parameters. The
//! algorithm is taken from Section 4 of the paper "Taming the Wildcards:
//! Combining Definition- and Use-Site Variance" published in PLDI'11 and
//! written by Altidor et al., and hereafter referred to as The Paper.
//!
//! This inference is explicitly designed *not* to consider the uses of
//! types within code. To determine the variance of type parameters
//! defined on type `X`, we only consider the definition of the type `X`
//! and the definitions of any types it references.
//!
//! We only infer variance for type parameters found on *types*: structs,
//! enums, and traits. We do not infer variance for type parameters found
//! on fns or impls. This is because those things are not type definitions
//! and variance doesn't really make sense in that context.
//!
//! It is worth covering what variance means in each case. For structs and
//! enums, I think it is fairly straightforward. The variance of the type
//! or lifetime parameters defines whether `T<A>` is a subtype of `T<B>`
//! (resp. `T<'a>` and `T<'b>`) based on the relationship of `A` and `B`
//! (resp. `'a` and `'b`). (FIXME #3598 -- we do not currently make use of
//! the variances we compute for type parameters.)
//!
//! ### Variance on traits
//!
//! The meaning of variance for trait parameters is more subtle and worth
//! expanding upon. There are in fact two uses of the variance values we
//! compute.
//!
//! #### Trait variance and object types
//!
//! The first is for object types. Just as with structs and enums, we can
//! decide the subtyping relationship between two object types `&Trait<A>`
//! and `&Trait<B>` based on the relationship of `A` and `B`. Note that
//! for object types we ignore the `Self` type parameter -- it is unknown,
//! and the nature of dynamic dispatch ensures that we will always call a
//! function that is expected the appropriate `Self` type. However, we
//! must be careful with the other type parameters, or else we could end
//! up calling a function that is expecting one type but provided another.
//!
//! To see what I mean, consider a trait like so:
//!
//! trait ConvertTo<A> {
//! fn convertTo(&self) -> A;
//! }
//!
//! Intuitively, If we had one object `O=&ConvertTo<Object>` and another
//! `S=&ConvertTo<String>`, then `S <: O` because `String <: Object`
//! (presuming Java-like "string" and "object" types, my go to examples
//! for subtyping). The actual algorithm would be to compare the
//! (explicit) type parameters pairwise respecting their variance: here,
//! the type parameter A is covariant (it appears only in a return
//! position), and hence we require that `String <: Object`.
//!
//! You'll note though that we did not consider the binding for the
//! (implicit) `Self` type parameter: in fact, it is unknown, so that's
//! good. The reason we can ignore that parameter is precisely because we
//! don't need to know its value until a call occurs, and at that time (as
//! you said) the dynamic nature of virtual dispatch means the code we run
//! will be correct for whatever value `Self` happens to be bound to for
//! the particular object whose method we called. `Self` is thus different
//! from `A`, because the caller requires that `A` be known in order to
//! know the return type of the method `convertTo()`. (As an aside, we
//! have rules preventing methods where `Self` appears outside of the
//! receiver position from being called via an object.)
//!
//! #### Trait variance and vtable resolution
//!
//! But traits aren't only used with objects. They're also used when
//! deciding whether a given impl satisfies a given trait bound. To set the
//! scene here, imagine I had a function:
//!
//! fn convertAll<A,T:ConvertTo<A>>(v: &[T]) {
//! ...
//! }
//!
//! Now imagine that I have an implementation of `ConvertTo` for `Object`:
//!
//! impl ConvertTo<int> for Object { ... }
//!
//! And I want to call `convertAll` on an array of strings. Suppose
//! further that for whatever reason I specifically supply the value of
//! `String` for the type parameter `T`:
//!
//! let mut vector = ~["string", ...];
//! convertAll::<int, String>(v);
//!
//! Is this legal? To put another way, can we apply the `impl` for
//! `Object` to the type `String`? The answer is yes, but to see why
//! we have to expand out what will happen:
//!
//! - `convertAll` will create a pointer to one of the entries in the
//! vector, which will have type `&String`
//! - It will then call the impl of `convertTo()` that is intended
//! for use with objects. This has the type:
//!
//! fn(self: &Object) -> int
//!
//! It is ok to provide a value for `self` of type `&String` because
//! `&String <: &Object`.
//!
//! OK, so intuitively we want this to be legal, so let's bring this back
//! to variance and see whether we are computing the correct result. We
//! must first figure out how to phrase the question "is an impl for
//! `Object,int` usable where an impl for `String,int` is expected?"
//!
//! Maybe it's helpful to think of a dictionary-passing implementation of
//! type classes. In that case, `convertAll()` takes an implicit parameter
//! representing the impl. In short, we *have* an impl of type:
//!
//! V_O = ConvertTo<int> for Object
//!
//! and the function prototype expects an impl of type:
//!
//! V_S = ConvertTo<int> for String
//!
//! As with any argument, this is legal if the type of the value given
//! (`V_O`) is a subtype of the type expected (`V_S`). So is `V_O <: V_S`?
//! The answer will depend on the variance of the various parameters. In
//! this case, because the `Self` parameter is contravariant and `A` is
//! covariant, it means that:
//!
//! V_O <: V_S iff
//! int <: int
//! String <: Object
//!
//! These conditions are satisfied and so we are happy.
//!
//! ### The algorithm
//!
//! The basic idea is quite straightforward. We iterate over the types
//! defined and, for each use of a type parameter X, accumulate a
//! constraint indicating that the variance of X must be valid for the
//! variance of that use site. We then iteratively refine the variance of
//! X until all constraints are met. There is *always* a sol'n, because at
//! the limit we can declare all type parameters to be invariant and all
//! constraints will be satisfied.
//!
//! As a simple example, consider:
//!
//! enum Option<A> { Some(A), None }
//! enum OptionalFn<B> { Some(|B|), None }
//! enum OptionalMap<C> { Some(|C| -> C), None }
//!
//! Here, we will generate the constraints:
//!
//! 1. V(A) <= +
//! 2. V(B) <= -
//! 3. V(C) <= +
//! 4. V(C) <= -
//!
//! These indicate that (1) the variance of A must be at most covariant;
//! (2) the variance of B must be at most contravariant; and (3, 4) the
//! variance of C must be at most covariant *and* contravariant. All of these
//! results are based on a variance lattice defined as follows:
//!
//! * Top (bivariant)
//! - +
//! o Bottom (invariant)
//!
//! Based on this lattice, the solution V(A)=+, V(B)=-, V(C)=o is the
//! optimal solution. Note that there is always a naive solution which
//! just declares all variables to be invariant.
//!
//! You may be wondering why fixed-point iteration is required. The reason
//! is that the variance of a use site may itself be a function of the
//! variance of other type parameters. In full generality, our constraints
//! take the form:
//!
//! V(X) <= Term
//! Term := + | - | * | o | V(X) | Term x Term
//!
//! Here the notation V(X) indicates the variance of a type/region
//! parameter `X` with respect to its defining class. `Term x Term`
//! represents the "variance transform" as defined in the paper:
//!
//! If the variance of a type variable `X` in type expression `E` is `V2`
//! and the definition-site variance of the [corresponding] type parameter
//! of a class `C` is `V1`, then the variance of `X` in the type expression
//! `C<E>` is `V3 = V1.xform(V2)`.
This file infers the variance of type and lifetime parameters. The
algorithm is taken from Section 4 of the paper "Taming the Wildcards:
Combining Definition- and Use-Site Variance" published in PLDI'11 and
written by Altidor et al., and hereafter referred to as The Paper.
This inference is explicitly designed *not* to consider the uses of
types within code. To determine the variance of type parameters
defined on type `X`, we only consider the definition of the type `X`
and the definitions of any types it references.
We only infer variance for type parameters found on *types*: structs,
enums, and traits. We do not infer variance for type parameters found
on fns or impls. This is because those things are not type definitions
and variance doesn't really make sense in that context.
It is worth covering what variance means in each case. For structs and
enums, I think it is fairly straightforward. The variance of the type
or lifetime parameters defines whether `T<A>` is a subtype of `T<B>`
(resp. `T<'a>` and `T<'b>`) based on the relationship of `A` and `B`
(resp. `'a` and `'b`). (FIXME #3598 -- we do not currently make use of
the variances we compute for type parameters.)
### Variance on traits
The meaning of variance for trait parameters is more subtle and worth
expanding upon. There are in fact two uses of the variance values we
compute.
#### Trait variance and object types
The first is for object types. Just as with structs and enums, we can
decide the subtyping relationship between two object types `&Trait<A>`
and `&Trait<B>` based on the relationship of `A` and `B`. Note that
for object types we ignore the `Self` type parameter -- it is unknown,
and the nature of dynamic dispatch ensures that we will always call a
function that is expected the appropriate `Self` type. However, we
must be careful with the other type parameters, or else we could end
up calling a function that is expecting one type but provided another.
To see what I mean, consider a trait like so:
trait ConvertTo<A> {
fn convertTo(&self) -> A;
}
Intuitively, If we had one object `O=&ConvertTo<Object>` and another
`S=&ConvertTo<String>`, then `S <: O` because `String <: Object`
(presuming Java-like "string" and "object" types, my go to examples
for subtyping). The actual algorithm would be to compare the
(explicit) type parameters pairwise respecting their variance: here,
the type parameter A is covariant (it appears only in a return
position), and hence we require that `String <: Object`.
You'll note though that we did not consider the binding for the
(implicit) `Self` type parameter: in fact, it is unknown, so that's
good. The reason we can ignore that parameter is precisely because we
don't need to know its value until a call occurs, and at that time (as
you said) the dynamic nature of virtual dispatch means the code we run
will be correct for whatever value `Self` happens to be bound to for
the particular object whose method we called. `Self` is thus different
from `A`, because the caller requires that `A` be known in order to
know the return type of the method `convertTo()`. (As an aside, we
have rules preventing methods where `Self` appears outside of the
receiver position from being called via an object.)
#### Trait variance and vtable resolution
But traits aren't only used with objects. They're also used when
deciding whether a given impl satisfies a given trait bound. To set the
scene here, imagine I had a function:
fn convertAll<A,T:ConvertTo<A>>(v: &[T]) {
...
}
Now imagine that I have an implementation of `ConvertTo` for `Object`:
impl ConvertTo<int> for Object { ... }
And I want to call `convertAll` on an array of strings. Suppose
further that for whatever reason I specifically supply the value of
`String` for the type parameter `T`:
let mut vector = ~["string", ...];
convertAll::<int, String>(v);
Is this legal? To put another way, can we apply the `impl` for
`Object` to the type `String`? The answer is yes, but to see why
we have to expand out what will happen:
- `convertAll` will create a pointer to one of the entries in the
vector, which will have type `&String`
- It will then call the impl of `convertTo()` that is intended
for use with objects. This has the type:
fn(self: &Object) -> int
It is ok to provide a value for `self` of type `&String` because
`&String <: &Object`.
OK, so intuitively we want this to be legal, so let's bring this back
to variance and see whether we are computing the correct result. We
must first figure out how to phrase the question "is an impl for
`Object,int` usable where an impl for `String,int` is expected?"
Maybe it's helpful to think of a dictionary-passing implementation of
type classes. In that case, `convertAll()` takes an implicit parameter
representing the impl. In short, we *have* an impl of type:
V_O = ConvertTo<int> for Object
and the function prototype expects an impl of type:
V_S = ConvertTo<int> for String
As with any argument, this is legal if the type of the value given
(`V_O`) is a subtype of the type expected (`V_S`). So is `V_O <: V_S`?
The answer will depend on the variance of the various parameters. In
this case, because the `Self` parameter is contravariant and `A` is
covariant, it means that:
V_O <: V_S iff
int <: int
String <: Object
These conditions are satisfied and so we are happy.
### The algorithm
The basic idea is quite straightforward. We iterate over the types
defined and, for each use of a type parameter X, accumulate a
constraint indicating that the variance of X must be valid for the
variance of that use site. We then iteratively refine the variance of
X until all constraints are met. There is *always* a sol'n, because at
the limit we can declare all type parameters to be invariant and all
constraints will be satisfied.
As a simple example, consider:
enum Option<A> { Some(A), None }
enum OptionalFn<B> { Some(|B|), None }
enum OptionalMap<C> { Some(|C| -> C), None }
Here, we will generate the constraints:
1. V(A) <= +
2. V(B) <= -
3. V(C) <= +
4. V(C) <= -
These indicate that (1) the variance of A must be at most covariant;
(2) the variance of B must be at most contravariant; and (3, 4) the
variance of C must be at most covariant *and* contravariant. All of these
results are based on a variance lattice defined as follows:
* Top (bivariant)
- +
o Bottom (invariant)
Based on this lattice, the solution V(A)=+, V(B)=-, V(C)=o is the
optimal solution. Note that there is always a naive solution which
just declares all variables to be invariant.
You may be wondering why fixed-point iteration is required. The reason
is that the variance of a use site may itself be a function of the
variance of other type parameters. In full generality, our constraints
take the form:
V(X) <= Term
Term := + | - | * | o | V(X) | Term x Term
Here the notation V(X) indicates the variance of a type/region
parameter `X` with respect to its defining class. `Term x Term`
represents the "variance transform" as defined in the paper:
If the variance of a type variable `X` in type expression `E` is `V2`
and the definition-site variance of the [corresponding] type parameter
of a class `C` is `V1`, then the variance of `X` in the type expression
`C<E>` is `V3 = V1.xform(V2)`.
*/
use self::VarianceTerm::*;
use self::ParamKind::*;
@ -626,6 +623,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
return result;
}
/// Returns a variance term representing the declared variance of the type/region parameter
/// with the given id.
fn declared_variance(&self,
param_def_id: ast::DefId,
item_def_id: ast::DefId,
@ -633,11 +632,6 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
space: ParamSpace,
index: uint)
-> VarianceTermPtr<'a> {
/*!
* Returns a variance term representing the declared variance of
* the type/region parameter with the given id.
*/
assert_eq!(param_def_id.krate, item_def_id.krate);
if self.invariant_lang_items[kind as uint] == Some(item_def_id) {

View File

@ -8,54 +8,52 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Infrastructure for compiler plugins.
*
* Plugins are Rust libraries which extend the behavior of `rustc`
* in various ways.
*
* Plugin authors will use the `Registry` type re-exported by
* this module, along with its methods. The rest of the module
* is for use by `rustc` itself.
*
* To define a plugin, build a dylib crate with a
* `#[plugin_registrar]` function:
*
* ```rust,ignore
* #![crate_name = "myplugin"]
* #![crate_type = "dylib"]
* #![feature(plugin_registrar)]
*
* extern crate rustc;
*
* use rustc::plugin::Registry;
*
* #[plugin_registrar]
* pub fn plugin_registrar(reg: &mut Registry) {
* reg.register_macro("mymacro", expand_mymacro);
* }
*
* fn expand_mymacro(...) { // details elided
* ```
*
* WARNING: We currently don't check that the registrar function
* has the appropriate type!
*
* To use a plugin while compiling another crate:
*
* ```rust
* #![feature(phase)]
*
* #[phase(plugin)]
* extern crate myplugin;
* ```
*
* If you also need the plugin crate available at runtime, use
* `phase(plugin, link)`.
*
* See [the compiler plugin guide](../../guide-plugin.html)
* for more examples.
*/
//! Infrastructure for compiler plugins.
//!
//! Plugins are Rust libraries which extend the behavior of `rustc`
//! in various ways.
//!
//! Plugin authors will use the `Registry` type re-exported by
//! this module, along with its methods. The rest of the module
//! is for use by `rustc` itself.
//!
//! To define a plugin, build a dylib crate with a
//! `#[plugin_registrar]` function:
//!
//! ```rust,ignore
//! #![crate_name = "myplugin"]
//! #![crate_type = "dylib"]
//! #![feature(plugin_registrar)]
//!
//! extern crate rustc;
//!
//! use rustc::plugin::Registry;
//!
//! #[plugin_registrar]
//! pub fn plugin_registrar(reg: &mut Registry) {
//! reg.register_macro("mymacro", expand_mymacro);
//! }
//!
//! fn expand_mymacro(...) { // details elided
//! ```
//!
//! WARNING: We currently don't check that the registrar function
//! has the appropriate type!
//!
//! To use a plugin while compiling another crate:
//!
//! ```rust
//! #![feature(phase)]
//!
//! #[phase(plugin)]
//! extern crate myplugin;
//! ```
//!
//! If you also need the plugin crate available at runtime, use
//! `phase(plugin, link)`.
//!
//! See [the compiler plugin guide](../../guide-plugin.html)
//! for more examples.
pub use self::registry::Registry;

View File

@ -122,24 +122,20 @@ pub fn block_query(b: &ast::Block, p: |&ast::Expr| -> bool) -> bool {
return v.flag;
}
// K: Eq + Hash<S>, V, S, H: Hasher<S>
/// K: Eq + Hash<S>, V, S, H: Hasher<S>
///
/// Determines whether there exists a path from `source` to `destination`. The graph is defined by
/// the `edges_map`, which maps from a node `S` to a list of its adjacent nodes `T`.
///
/// Efficiency note: This is implemented in an inefficient way because it is typically invoked on
/// very small graphs. If the graphs become larger, a more efficient graph representation and
/// algorithm would probably be advised.
pub fn can_reach<S,H:Hasher<S>,T:Eq+Clone+Hash<S>>(
edges_map: &HashMap<T,Vec<T>,H>,
source: T,
destination: T)
-> bool
{
/*!
* Determines whether there exists a path from `source` to
* `destination`. The graph is defined by the `edges_map`, which
* maps from a node `S` to a list of its adjacent nodes `T`.
*
* Efficiency note: This is implemented in an inefficient way
* because it is typically invoked on very small graphs. If the graphs
* become larger, a more efficient graph representation and algorithm
* would probably be advised.
*/
if source == destination {
return true;
}

View File

@ -65,12 +65,9 @@ pub fn note_and_explain_region(cx: &ctxt,
}
}
/// When a free region is associated with `item`, how should we describe the item in the error
/// message.
fn item_scope_tag(item: &ast::Item) -> &'static str {
/*!
* When a free region is associated with `item`, how should we describe
* the item in the error message.
*/
match item.node {
ast::ItemImpl(..) => "impl",
ast::ItemStruct(..) => "struct",

View File

@ -8,21 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* A utility class for implementing "snapshottable" things; a
* snapshottable data structure permits you to take a snapshot (via
* `start_snapshot`) and then, after making some changes, elect either
* to rollback to the start of the snapshot or commit those changes.
*
* This vector is intended to be used as part of an abstraction, not
* serve as a complete abstraction on its own. As such, while it will
* roll back most changes on its own, it also supports a `get_mut`
* operation that gives you an abitrary mutable pointer into the
* vector. To ensure that any changes you make this with this pointer
* are rolled back, you must invoke `record` to record any changes you
* make and also supplying a delegate capable of reversing those
* changes.
*/
//! A utility class for implementing "snapshottable" things; a snapshottable data structure permits
//! you to take a snapshot (via `start_snapshot`) and then, after making some changes, elect either
//! to rollback to the start of the snapshot or commit those changes.
//!
//! This vector is intended to be used as part of an abstraction, not serve as a complete
//! abstraction on its own. As such, while it will roll back most changes on its own, it also
//! supports a `get_mut` operation that gives you an abitrary mutable pointer into the vector. To
//! ensure that any changes you make this with this pointer are rolled back, you must invoke
//! `record` to record any changes you make and also supplying a delegate capable of reversing
//! those changes.
use self::UndoLog::*;
use std::kinds::marker;
@ -98,23 +93,16 @@ impl<T,U,D:SnapshotVecDelegate<T,U>> SnapshotVec<T,U,D> {
&self.values[index]
}
/// Returns a mutable pointer into the vec; whatever changes you make here cannot be undone
/// automatically, so you should be sure call `record()` with some sort of suitable undo
/// action.
pub fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut T {
/*!
* Returns a mutable pointer into the vec; whatever changes
* you make here cannot be undone automatically, so you should
* be sure call `record()` with some sort of suitable undo
* action.
*/
&mut self.values[index]
}
/// Updates the element at the given index. The old value will saved (and perhaps restored) if
/// a snapshot is active.
pub fn set(&mut self, index: uint, new_elem: T) {
/*!
* Updates the element at the given index. The old value will
* saved (and perhaps restored) if a snapshot is active.
*/
let old_elem = mem::replace(&mut self.values[index], new_elem);
if self.in_snapshot() {
self.undo_log.push(SetElem(index, old_elem));

View File

@ -8,15 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
The Rust compiler.
# Note
This API is completely unstable and subject to change.
*/
//! The Rust compiler.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.
#![crate_name = "rustc_trans"]
#![experimental]

View File

@ -8,11 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Standalone Tests for the Inference Module
*/
//! # Standalone Tests for the Inference Module
use driver::diagnostic;
use driver::diagnostic::Emitter;
@ -537,12 +533,10 @@ fn glb_bound_static() {
})
}
/// Test substituting a bound region into a function, which introduces another level of binding.
/// This requires adjusting the Debruijn index.
#[test]
fn subst_ty_renumber_bound() {
/*!
* Test substituting a bound region into a function, which introduces another
* level of binding. This requires adjusting the Debruijn index.
*/
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
// Situation:
@ -575,13 +569,10 @@ fn subst_ty_renumber_bound() {
})
}
/// Test substituting a bound region into a function, which introduces another level of binding.
/// This requires adjusting the Debruijn index.
#[test]
fn subst_ty_renumber_some_bounds() {
/*!
* Test substituting a bound region into a function, which introduces another
* level of binding. This requires adjusting the Debruijn index.
*/
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
// Situation:
// Theta = [A -> &'a foo]
@ -615,12 +606,9 @@ fn subst_ty_renumber_some_bounds() {
})
}
/// Test that we correctly compute whether a type has escaping regions or not.
#[test]
fn escaping() {
/*!
* Test that we correctly compute whether a type has escaping
* regions or not.
*/
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
// Situation:
@ -658,12 +646,10 @@ fn escaping() {
})
}
/// Test applying a substitution where the value being substituted for an early-bound region is a
/// late-bound region.
#[test]
fn subst_region_renumber_region() {
/*!
* Test applying a substitution where the value being substituted
* for an early-bound region is a late-bound region.
*/
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
let re_bound1 = env.re_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1));

View File

@ -8,183 +8,179 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
*
* # Compilation of match statements
*
* I will endeavor to explain the code as best I can. I have only a loose
* understanding of some parts of it.
*
* ## Matching
*
* The basic state of the code is maintained in an array `m` of `Match`
* objects. Each `Match` describes some list of patterns, all of which must
* match against the current list of values. If those patterns match, then
* the arm listed in the match is the correct arm. A given arm may have
* multiple corresponding match entries, one for each alternative that
* remains. As we proceed these sets of matches are adjusted by the various
* `enter_XXX()` functions, each of which adjusts the set of options given
* some information about the value which has been matched.
*
* So, initially, there is one value and N matches, each of which have one
* constituent pattern. N here is usually the number of arms but may be
* greater, if some arms have multiple alternatives. For example, here:
*
* enum Foo { A, B(int), C(uint, uint) }
* match foo {
* A => ...,
* B(x) => ...,
* C(1u, 2) => ...,
* C(_) => ...
* }
*
* The value would be `foo`. There would be four matches, each of which
* contains one pattern (and, in one case, a guard). We could collect the
* various options and then compile the code for the case where `foo` is an
* `A`, a `B`, and a `C`. When we generate the code for `C`, we would (1)
* drop the two matches that do not match a `C` and (2) expand the other two
* into two patterns each. In the first case, the two patterns would be `1u`
* and `2`, and the in the second case the _ pattern would be expanded into
* `_` and `_`. The two values are of course the arguments to `C`.
*
* Here is a quick guide to the various functions:
*
* - `compile_submatch()`: The main workhouse. It takes a list of values and
* a list of matches and finds the various possibilities that could occur.
*
* - `enter_XXX()`: modifies the list of matches based on some information
* about the value that has been matched. For example,
* `enter_rec_or_struct()` adjusts the values given that a record or struct
* has been matched. This is an infallible pattern, so *all* of the matches
* must be either wildcards or record/struct patterns. `enter_opt()`
* handles the fallible cases, and it is correspondingly more complex.
*
* ## Bindings
*
* We store information about the bound variables for each arm as part of the
* per-arm `ArmData` struct. There is a mapping from identifiers to
* `BindingInfo` structs. These structs contain the mode/id/type of the
* binding, but they also contain an LLVM value which points at an alloca
* called `llmatch`. For by value bindings that are Copy, we also create
* an extra alloca that we copy the matched value to so that any changes
* we do to our copy is not reflected in the original and vice-versa.
* We don't do this if it's a move since the original value can't be used
* and thus allowing us to cheat in not creating an extra alloca.
*
* The `llmatch` binding always stores a pointer into the value being matched
* which points at the data for the binding. If the value being matched has
* type `T`, then, `llmatch` will point at an alloca of type `T*` (and hence
* `llmatch` has type `T**`). So, if you have a pattern like:
*
* let a: A = ...;
* let b: B = ...;
* match (a, b) { (ref c, d) => { ... } }
*
* For `c` and `d`, we would generate allocas of type `C*` and `D*`
* respectively. These are called the `llmatch`. As we match, when we come
* up against an identifier, we store the current pointer into the
* corresponding alloca.
*
* Once a pattern is completely matched, and assuming that there is no guard
* pattern, we will branch to a block that leads to the body itself. For any
* by-value bindings, this block will first load the ptr from `llmatch` (the
* one of type `D*`) and then load a second time to get the actual value (the
* one of type `D`). For by ref bindings, the value of the local variable is
* simply the first alloca.
*
* So, for the example above, we would generate a setup kind of like this:
*
* +-------+
* | Entry |
* +-------+
* |
* +--------------------------------------------+
* | llmatch_c = (addr of first half of tuple) |
* | llmatch_d = (addr of second half of tuple) |
* +--------------------------------------------+
* |
* +--------------------------------------+
* | *llbinding_d = **llmatch_d |
* +--------------------------------------+
*
* If there is a guard, the situation is slightly different, because we must
* execute the guard code. Moreover, we need to do so once for each of the
* alternatives that lead to the arm, because if the guard fails, they may
* have different points from which to continue the search. Therefore, in that
* case, we generate code that looks more like:
*
* +-------+
* | Entry |
* +-------+
* |
* +-------------------------------------------+
* | llmatch_c = (addr of first half of tuple) |
* | llmatch_d = (addr of first half of tuple) |
* +-------------------------------------------+
* |
* +-------------------------------------------------+
* | *llbinding_d = **llmatch_d |
* | check condition |
* | if false { goto next case } |
* | if true { goto body } |
* +-------------------------------------------------+
*
* The handling for the cleanups is a bit... sensitive. Basically, the body
* is the one that invokes `add_clean()` for each binding. During the guard
* evaluation, we add temporary cleanups and revoke them after the guard is
* evaluated (it could fail, after all). Note that guards and moves are
* just plain incompatible.
*
* Some relevant helper functions that manage bindings:
* - `create_bindings_map()`
* - `insert_lllocals()`
*
*
* ## Notes on vector pattern matching.
*
* Vector pattern matching is surprisingly tricky. The problem is that
* the structure of the vector isn't fully known, and slice matches
* can be done on subparts of it.
*
* The way that vector pattern matches are dealt with, then, is as
* follows. First, we make the actual condition associated with a
* vector pattern simply a vector length comparison. So the pattern
* [1, .. x] gets the condition "vec len >= 1", and the pattern
* [.. x] gets the condition "vec len >= 0". The problem here is that
* having the condition "vec len >= 1" hold clearly does not mean that
* only a pattern that has exactly that condition will match. This
* means that it may well be the case that a condition holds, but none
* of the patterns matching that condition match; to deal with this,
* when doing vector length matches, we have match failures proceed to
* the next condition to check.
*
* There are a couple more subtleties to deal with. While the "actual"
* condition associated with vector length tests is simply a test on
* the vector length, the actual vec_len Opt entry contains more
* information used to restrict which matches are associated with it.
* So that all matches in a submatch are matching against the same
* values from inside the vector, they are split up by how many
* elements they match at the front and at the back of the vector. In
* order to make sure that arms are properly checked in order, even
* with the overmatching conditions, each vec_len Opt entry is
* associated with a range of matches.
* Consider the following:
*
* match &[1, 2, 3] {
* [1, 1, .. _] => 0,
* [1, 2, 2, .. _] => 1,
* [1, 2, 3, .. _] => 2,
* [1, 2, .. _] => 3,
* _ => 4
* }
* The proper arm to match is arm 2, but arms 0 and 3 both have the
* condition "len >= 2". If arm 3 was lumped in with arm 0, then the
* wrong branch would be taken. Instead, vec_len Opts are associated
* with a contiguous range of matches that have the same "shape".
* This is sort of ugly and requires a bunch of special handling of
* vec_len options.
*
*/
//! # Compilation of match statements
//!
//! I will endeavor to explain the code as best I can. I have only a loose
//! understanding of some parts of it.
//!
//! ## Matching
//!
//! The basic state of the code is maintained in an array `m` of `Match`
//! objects. Each `Match` describes some list of patterns, all of which must
//! match against the current list of values. If those patterns match, then
//! the arm listed in the match is the correct arm. A given arm may have
//! multiple corresponding match entries, one for each alternative that
//! remains. As we proceed these sets of matches are adjusted by the various
//! `enter_XXX()` functions, each of which adjusts the set of options given
//! some information about the value which has been matched.
//!
//! So, initially, there is one value and N matches, each of which have one
//! constituent pattern. N here is usually the number of arms but may be
//! greater, if some arms have multiple alternatives. For example, here:
//!
//! enum Foo { A, B(int), C(uint, uint) }
//! match foo {
//! A => ...,
//! B(x) => ...,
//! C(1u, 2) => ...,
//! C(_) => ...
//! }
//!
//! The value would be `foo`. There would be four matches, each of which
//! contains one pattern (and, in one case, a guard). We could collect the
//! various options and then compile the code for the case where `foo` is an
//! `A`, a `B`, and a `C`. When we generate the code for `C`, we would (1)
//! drop the two matches that do not match a `C` and (2) expand the other two
//! into two patterns each. In the first case, the two patterns would be `1u`
//! and `2`, and the in the second case the _ pattern would be expanded into
//! `_` and `_`. The two values are of course the arguments to `C`.
//!
//! Here is a quick guide to the various functions:
//!
//! - `compile_submatch()`: The main workhouse. It takes a list of values and
//! a list of matches and finds the various possibilities that could occur.
//!
//! - `enter_XXX()`: modifies the list of matches based on some information
//! about the value that has been matched. For example,
//! `enter_rec_or_struct()` adjusts the values given that a record or struct
//! has been matched. This is an infallible pattern, so *all* of the matches
//! must be either wildcards or record/struct patterns. `enter_opt()`
//! handles the fallible cases, and it is correspondingly more complex.
//!
//! ## Bindings
//!
//! We store information about the bound variables for each arm as part of the
//! per-arm `ArmData` struct. There is a mapping from identifiers to
//! `BindingInfo` structs. These structs contain the mode/id/type of the
//! binding, but they also contain an LLVM value which points at an alloca
//! called `llmatch`. For by value bindings that are Copy, we also create
//! an extra alloca that we copy the matched value to so that any changes
//! we do to our copy is not reflected in the original and vice-versa.
//! We don't do this if it's a move since the original value can't be used
//! and thus allowing us to cheat in not creating an extra alloca.
//!
//! The `llmatch` binding always stores a pointer into the value being matched
//! which points at the data for the binding. If the value being matched has
//! type `T`, then, `llmatch` will point at an alloca of type `T*` (and hence
//! `llmatch` has type `T**`). So, if you have a pattern like:
//!
//! let a: A = ...;
//! let b: B = ...;
//! match (a, b) { (ref c, d) => { ... } }
//!
//! For `c` and `d`, we would generate allocas of type `C*` and `D*`
//! respectively. These are called the `llmatch`. As we match, when we come
//! up against an identifier, we store the current pointer into the
//! corresponding alloca.
//!
//! Once a pattern is completely matched, and assuming that there is no guard
//! pattern, we will branch to a block that leads to the body itself. For any
//! by-value bindings, this block will first load the ptr from `llmatch` (the
//! one of type `D*`) and then load a second time to get the actual value (the
//! one of type `D`). For by ref bindings, the value of the local variable is
//! simply the first alloca.
//!
//! So, for the example above, we would generate a setup kind of like this:
//!
//! +-------+
//! | Entry |
//! +-------+
//! |
//! +--------------------------------------------+
//! | llmatch_c = (addr of first half of tuple) |
//! | llmatch_d = (addr of second half of tuple) |
//! +--------------------------------------------+
//! |
//! +--------------------------------------+
//! | *llbinding_d = **llmatch_d |
//! +--------------------------------------+
//!
//! If there is a guard, the situation is slightly different, because we must
//! execute the guard code. Moreover, we need to do so once for each of the
//! alternatives that lead to the arm, because if the guard fails, they may
//! have different points from which to continue the search. Therefore, in that
//! case, we generate code that looks more like:
//!
//! +-------+
//! | Entry |
//! +-------+
//! |
//! +-------------------------------------------+
//! | llmatch_c = (addr of first half of tuple) |
//! | llmatch_d = (addr of first half of tuple) |
//! +-------------------------------------------+
//! |
//! +-------------------------------------------------+
//! | *llbinding_d = **llmatch_d |
//! | check condition |
//! | if false { goto next case } |
//! | if true { goto body } |
//! +-------------------------------------------------+
//!
//! The handling for the cleanups is a bit... sensitive. Basically, the body
//! is the one that invokes `add_clean()` for each binding. During the guard
//! evaluation, we add temporary cleanups and revoke them after the guard is
//! evaluated (it could fail, after all). Note that guards and moves are
//! just plain incompatible.
//!
//! Some relevant helper functions that manage bindings:
//! - `create_bindings_map()`
//! - `insert_lllocals()`
//!
//!
//! ## Notes on vector pattern matching.
//!
//! Vector pattern matching is surprisingly tricky. The problem is that
//! the structure of the vector isn't fully known, and slice matches
//! can be done on subparts of it.
//!
//! The way that vector pattern matches are dealt with, then, is as
//! follows. First, we make the actual condition associated with a
//! vector pattern simply a vector length comparison. So the pattern
//! [1, .. x] gets the condition "vec len >= 1", and the pattern
//! [.. x] gets the condition "vec len >= 0". The problem here is that
//! having the condition "vec len >= 1" hold clearly does not mean that
//! only a pattern that has exactly that condition will match. This
//! means that it may well be the case that a condition holds, but none
//! of the patterns matching that condition match; to deal with this,
//! when doing vector length matches, we have match failures proceed to
//! the next condition to check.
//!
//! There are a couple more subtleties to deal with. While the "actual"
//! condition associated with vector length tests is simply a test on
//! the vector length, the actual vec_len Opt entry contains more
//! information used to restrict which matches are associated with it.
//! So that all matches in a submatch are matching against the same
//! values from inside the vector, they are split up by how many
//! elements they match at the front and at the back of the vector. In
//! order to make sure that arms are properly checked in order, even
//! with the overmatching conditions, each vec_len Opt entry is
//! associated with a range of matches.
//! Consider the following:
//!
//! match &[1, 2, 3] {
//! [1, 1, .. _] => 0,
//! [1, 2, 2, .. _] => 1,
//! [1, 2, 3, .. _] => 2,
//! [1, 2, .. _] => 3,
//! _ => 4
//! }
//! The proper arm to match is arm 2, but arms 0 and 3 both have the
//! condition "len >= 2". If arm 3 was lumped in with arm 0, then the
//! wrong branch would be taken. Instead, vec_len Opts are associated
//! with a contiguous range of matches that have the same "shape".
//! This is sort of ugly and requires a bunch of special handling of
//! vec_len options.
pub use self::BranchKind::*;
pub use self::OptResult::*;
@ -617,12 +613,9 @@ fn extract_variant_args<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ExtractedBlock { vals: args, bcx: bcx }
}
/// Helper for converting from the ValueRef that we pass around in the match code, which is always
/// an lvalue, into a Datum. Eventually we should just pass around a Datum and be done with it.
fn match_datum<'tcx>(val: ValueRef, left_ty: Ty<'tcx>) -> Datum<'tcx, Lvalue> {
/*!
* Helper for converting from the ValueRef that we pass around in
* the match code, which is always an lvalue, into a Datum. Eventually
* we should just pass around a Datum and be done with it.
*/
Datum::new(val, left_ty, Lvalue)
}
@ -828,15 +821,11 @@ fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
}
}
/// For each binding in `data.bindings_map`, adds an appropriate entry into the `fcx.lllocals` map
fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
bindings_map: &BindingsMap<'tcx>,
cs: Option<cleanup::ScopeId>)
-> Block<'blk, 'tcx> {
/*!
* For each binding in `data.bindings_map`, adds an appropriate entry into
* the `fcx.lllocals` map
*/
for (&ident, &binding_info) in bindings_map.iter() {
let llval = match binding_info.trmode {
// By value mut binding for a copy type: load from the ptr
@ -1413,13 +1402,11 @@ fn trans_match_inner<'blk, 'tcx>(scope_cx: Block<'blk, 'tcx>,
return bcx;
}
/// Generates code for a local variable declaration like `let <pat>;` or `let <pat> =
/// <opt_init_expr>`.
pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
local: &ast::Local)
-> Block<'blk, 'tcx> {
/*!
* Generates code for a local variable declaration like
* `let <pat>;` or `let <pat> = <opt_init_expr>`.
*/
let _icx = push_ctxt("match::store_local");
let mut bcx = bcx;
let tcx = bcx.tcx();
@ -1479,24 +1466,21 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
}
/// Generates code for argument patterns like `fn foo(<pat>: T)`.
/// Creates entries in the `lllocals` map for each of the bindings
/// in `pat`.
///
/// # Arguments
///
/// - `pat` is the argument pattern
/// - `llval` is a pointer to the argument value (in other words,
/// if the argument type is `T`, then `llval` is a `T*`). In some
/// cases, this code may zero out the memory `llval` points at.
pub fn store_arg<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
pat: &ast::Pat,
arg: Datum<'tcx, Rvalue>,
arg_scope: cleanup::ScopeId)
-> Block<'blk, 'tcx> {
/*!
* Generates code for argument patterns like `fn foo(<pat>: T)`.
* Creates entries in the `lllocals` map for each of the bindings
* in `pat`.
*
* # Arguments
*
* - `pat` is the argument pattern
* - `llval` is a pointer to the argument value (in other words,
* if the argument type is `T`, then `llval` is a `T*`). In some
* cases, this code may zero out the memory `llval` points at.
*/
let _icx = push_ctxt("match::store_arg");
match simple_identifier(&*pat) {
@ -1580,26 +1564,23 @@ fn mk_binding_alloca<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>,
bcx
}
/// A simple version of the pattern matching code that only handles
/// irrefutable patterns. This is used in let/argument patterns,
/// not in match statements. Unifying this code with the code above
/// sounds nice, but in practice it produces very inefficient code,
/// since the match code is so much more general. In most cases,
/// LLVM is able to optimize the code, but it causes longer compile
/// times and makes the generated code nigh impossible to read.
///
/// # Arguments
/// - bcx: starting basic block context
/// - pat: the irrefutable pattern being matched.
/// - val: the value being matched -- must be an lvalue (by ref, with cleanup)
fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
pat: &ast::Pat,
val: ValueRef,
cleanup_scope: cleanup::ScopeId)
-> Block<'blk, 'tcx> {
/*!
* A simple version of the pattern matching code that only handles
* irrefutable patterns. This is used in let/argument patterns,
* not in match statements. Unifying this code with the code above
* sounds nice, but in practice it produces very inefficient code,
* since the match code is so much more general. In most cases,
* LLVM is able to optimize the code, but it causes longer compile
* times and makes the generated code nigh impossible to read.
*
* # Arguments
* - bcx: starting basic block context
* - pat: the irrefutable pattern being matched.
* - val: the value being matched -- must be an lvalue (by ref, with cleanup)
*/
debug!("bind_irrefutable_pat(bcx={}, pat={})",
bcx.to_str(),
pat.repr(bcx.tcx()));

View File

@ -8,40 +8,38 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* # Representation of Algebraic Data Types
*
* This module determines how to represent enums, structs, and tuples
* based on their monomorphized types; it is responsible both for
* choosing a representation and translating basic operations on
* values of those types. (Note: exporting the representations for
* debuggers is handled in debuginfo.rs, not here.)
*
* Note that the interface treats everything as a general case of an
* enum, so structs/tuples/etc. have one pseudo-variant with
* discriminant 0; i.e., as if they were a univariant enum.
*
* Having everything in one place will enable improvements to data
* structure representation; possibilities include:
*
* - User-specified alignment (e.g., cacheline-aligning parts of
* concurrently accessed data structures); LLVM can't represent this
* directly, so we'd have to insert padding fields in any structure
* that might contain one and adjust GEP indices accordingly. See
* issue #4578.
*
* - Store nested enums' discriminants in the same word. Rather, if
* some variants start with enums, and those enums representations
* have unused alignment padding between discriminant and body, the
* outer enum's discriminant can be stored there and those variants
* can start at offset 0. Kind of fancy, and might need work to
* make copies of the inner enum type cooperate, but it could help
* with `Option` or `Result` wrapped around another enum.
*
* - Tagged pointers would be neat, but given that any type can be
* used unboxed and any field can have pointers (including mutable)
* taken to it, implementing them for Rust seems difficult.
*/
//! # Representation of Algebraic Data Types
//!
//! This module determines how to represent enums, structs, and tuples
//! based on their monomorphized types; it is responsible both for
//! choosing a representation and translating basic operations on
//! values of those types. (Note: exporting the representations for
//! debuggers is handled in debuginfo.rs, not here.)
//!
//! Note that the interface treats everything as a general case of an
//! enum, so structs/tuples/etc. have one pseudo-variant with
//! discriminant 0; i.e., as if they were a univariant enum.
//!
//! Having everything in one place will enable improvements to data
//! structure representation; possibilities include:
//!
//! - User-specified alignment (e.g., cacheline-aligning parts of
//! concurrently accessed data structures); LLVM can't represent this
//! directly, so we'd have to insert padding fields in any structure
//! that might contain one and adjust GEP indices accordingly. See
//! issue #4578.
//!
//! - Store nested enums' discriminants in the same word. Rather, if
//! some variants start with enums, and those enums representations
//! have unused alignment padding between discriminant and body, the
//! outer enum's discriminant can be stored there and those variants
//! can start at offset 0. Kind of fancy, and might need work to
//! make copies of the inner enum type cooperate, but it could help
//! with `Option` or `Result` wrapped around another enum.
//!
//! - Tagged pointers would be neat, but given that any type can be
//! used unboxed and any field can have pointers (including mutable)
//! taken to it, implementing them for Rust seems difficult.
#![allow(unsigned_negation)]

View File

@ -8,9 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Translation of inline assembly.
*/
//! # Translation of inline assembly.
use llvm;
use trans::build::*;

View File

@ -1050,14 +1050,11 @@ pub fn load_if_immediate<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
return v;
}
/// Helper for loading values from memory. Does the necessary conversion if the in-memory type
/// differs from the type used for SSA values. Also handles various special cases where the type
/// gives us better information about what we are loading.
pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
ptr: ValueRef, t: Ty<'tcx>) -> ValueRef {
/*!
* Helper for loading values from memory. Does the necessary conversion if
* the in-memory type differs from the type used for SSA values. Also
* handles various special cases where the type gives us better information
* about what we are loading.
*/
if type_is_zero_size(cx.ccx(), t) {
C_undef(type_of::type_of(cx.ccx(), t))
} else if ty::type_is_bool(t) {
@ -1071,11 +1068,9 @@ pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
}
}
/// Helper for storing values in memory. Does the necessary conversion if the in-memory type
/// differs from the type used for SSA values.
pub fn store_ty(cx: Block, v: ValueRef, dst: ValueRef, t: Ty) {
/*!
* Helper for storing values in memory. Does the necessary conversion if
* the in-memory type differs from the type used for SSA values.
*/
if ty::type_is_bool(t) {
Store(cx, ZExt(cx, v, Type::i8(cx.ccx())), dst);
} else {

View File

@ -8,13 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Handles translation of callees as well as other call-related
* things. Callees are a superset of normal rust values and sometimes
* have different representations. In particular, top-level fn items
* and methods are represented as just a fn ptr and not a full
* closure.
*/
//! Handles translation of callees as well as other call-related
//! things. Callees are a superset of normal rust values and sometimes
//! have different representations. In particular, top-level fn items
//! and methods are represented as just a fn ptr and not a full
//! closure.
pub use self::AutorefArg::*;
pub use self::CalleeData::*;
@ -220,13 +218,9 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr)
}
}
/// Translates a reference (with id `ref_id`) to the fn/method with id `def_id` into a function
/// pointer. This may require monomorphization or inlining.
pub fn trans_fn_ref(bcx: Block, def_id: ast::DefId, node: ExprOrMethodCall) -> ValueRef {
/*!
* Translates a reference (with id `ref_id`) to the fn/method
* with id `def_id` into a function pointer. This may require
* monomorphization or inlining.
*/
let _icx = push_ctxt("trans_fn_ref");
let substs = node_id_substs(bcx, node);
@ -398,6 +392,17 @@ pub fn trans_unboxing_shim<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
llfn
}
/// Translates a reference to a fn/method item, monomorphizing and
/// inlining as it goes.
///
/// # Parameters
///
/// - `bcx`: the current block where the reference to the fn occurs
/// - `def_id`: def id of the fn or method item being referenced
/// - `node`: node id of the reference to the fn/method, if applicable.
/// This parameter may be zero; but, if so, the resulting value may not
/// have the right type, so it must be cast before being used.
/// - `substs`: values for each of the fn/method's parameters
pub fn trans_fn_ref_with_substs<'blk, 'tcx>(
bcx: Block<'blk, 'tcx>, //
def_id: ast::DefId, // def id of fn
@ -405,20 +410,6 @@ pub fn trans_fn_ref_with_substs<'blk, 'tcx>(
substs: subst::Substs<'tcx>) // vtables for the call
-> ValueRef
{
/*!
* Translates a reference to a fn/method item, monomorphizing and
* inlining as it goes.
*
* # Parameters
*
* - `bcx`: the current block where the reference to the fn occurs
* - `def_id`: def id of the fn or method item being referenced
* - `node`: node id of the reference to the fn/method, if applicable.
* This parameter may be zero; but, if so, the resulting value may not
* have the right type, so it must be cast before being used.
* - `substs`: values for each of the fn/method's parameters
*/
let _icx = push_ctxt("trans_fn_ref_with_substs");
let ccx = bcx.ccx();
let tcx = bcx.tcx();
@ -668,6 +659,16 @@ pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
dest)
}
/// This behemoth of a function translates function calls. Unfortunately, in order to generate more
/// efficient LLVM output at -O0, it has quite a complex signature (refactoring this into two
/// functions seems like a good idea).
///
/// In particular, for lang items, it is invoked with a dest of None, and in that case the return
/// value contains the result of the fn. The lang item must not return a structural type or else
/// all heck breaks loose.
///
/// For non-lang items, `dest` is always Some, and hence the result is written into memory
/// somewhere. Nonetheless we return the actual return value of the function.
pub fn trans_call_inner<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
call_info: Option<NodeInfo>,
callee_ty: Ty<'tcx>,
@ -677,22 +678,6 @@ pub fn trans_call_inner<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
args: CallArgs<'a, 'tcx>,
dest: Option<expr::Dest>)
-> Result<'blk, 'tcx> {
/*!
* This behemoth of a function translates function calls.
* Unfortunately, in order to generate more efficient LLVM
* output at -O0, it has quite a complex signature (refactoring
* this into two functions seems like a good idea).
*
* In particular, for lang items, it is invoked with a dest of
* None, and in that case the return value contains the result of
* the fn. The lang item must not return a structural type or else
* all heck breaks loose.
*
* For non-lang items, `dest` is always Some, and hence the result
* is written into memory somewhere. Nonetheless we return the
* actual return value of the function.
*/
// Introduce a temporary cleanup scope that will contain cleanups
// for the arguments while they are being evaluated. The purpose
// this cleanup is to ensure that, should a panic occur while

View File

@ -8,10 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Code pertaining to cleanup of temporaries as well as execution of
* drop glue. See discussion in `doc.rs` for a high-level summary.
*/
//! Code pertaining to cleanup of temporaries as well as execution of
//! drop glue. See discussion in `doc.rs` for a high-level summary.
pub use self::ScopeId::*;
pub use self::CleanupScopeKind::*;
@ -114,12 +112,8 @@ pub enum ScopeId {
}
impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
/// Invoked when we start to trans the code contained within a new cleanup scope.
fn push_ast_cleanup_scope(&self, debug_loc: NodeInfo) {
/*!
* Invoked when we start to trans the code contained
* within a new cleanup scope.
*/
debug!("push_ast_cleanup_scope({})",
self.ccx.tcx().map.node_to_string(debug_loc.id));
@ -189,16 +183,12 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
CustomScopeIndex { index: index }
}
/// Removes the cleanup scope for id `cleanup_scope`, which must be at the top of the cleanup
/// stack, and generates the code to do its cleanups for normal exit.
fn pop_and_trans_ast_cleanup_scope(&self,
bcx: Block<'blk, 'tcx>,
cleanup_scope: ast::NodeId)
-> Block<'blk, 'tcx> {
/*!
* Removes the cleanup scope for id `cleanup_scope`, which
* must be at the top of the cleanup stack, and generates the
* code to do its cleanups for normal exit.
*/
debug!("pop_and_trans_ast_cleanup_scope({})",
self.ccx.tcx().map.node_to_string(cleanup_scope));
@ -208,15 +198,11 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.trans_scope_cleanups(bcx, &scope)
}
/// Removes the loop cleanup scope for id `cleanup_scope`, which must be at the top of the
/// cleanup stack. Does not generate any cleanup code, since loop scopes should exit by
/// branching to a block generated by `normal_exit_block`.
fn pop_loop_cleanup_scope(&self,
cleanup_scope: ast::NodeId) {
/*!
* Removes the loop cleanup scope for id `cleanup_scope`, which
* must be at the top of the cleanup stack. Does not generate
* any cleanup code, since loop scopes should exit by
* branching to a block generated by `normal_exit_block`.
*/
debug!("pop_loop_cleanup_scope({})",
self.ccx.tcx().map.node_to_string(cleanup_scope));
@ -225,29 +211,21 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
let _ = self.pop_scope();
}
/// Removes the top cleanup scope from the stack without executing its cleanups. The top
/// cleanup scope must be the temporary scope `custom_scope`.
fn pop_custom_cleanup_scope(&self,
custom_scope: CustomScopeIndex) {
/*!
* Removes the top cleanup scope from the stack without
* executing its cleanups. The top cleanup scope must
* be the temporary scope `custom_scope`.
*/
debug!("pop_custom_cleanup_scope({})", custom_scope.index);
assert!(self.is_valid_to_pop_custom_scope(custom_scope));
let _ = self.pop_scope();
}
/// Removes the top cleanup scope from the stack, which must be a temporary scope, and
/// generates the code to do its cleanups for normal exit.
fn pop_and_trans_custom_cleanup_scope(&self,
bcx: Block<'blk, 'tcx>,
custom_scope: CustomScopeIndex)
-> Block<'blk, 'tcx> {
/*!
* Removes the top cleanup scope from the stack, which must be
* a temporary scope, and generates the code to do its
* cleanups for normal exit.
*/
debug!("pop_and_trans_custom_cleanup_scope({})", custom_scope);
assert!(self.is_valid_to_pop_custom_scope(custom_scope));
@ -255,11 +233,8 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.trans_scope_cleanups(bcx, &scope)
}
/// Returns the id of the top-most loop scope
fn top_loop_scope(&self) -> ast::NodeId {
/*!
* Returns the id of the top-most loop scope
*/
for scope in self.scopes.borrow().iter().rev() {
match scope.kind {
LoopScopeKind(id, _) => {
@ -271,24 +246,17 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.ccx.sess().bug("no loop scope found");
}
/// Returns a block to branch to which will perform all pending cleanups and then
/// break/continue (depending on `exit`) out of the loop with id `cleanup_scope`
fn normal_exit_block(&'blk self,
cleanup_scope: ast::NodeId,
exit: uint) -> BasicBlockRef {
/*!
* Returns a block to branch to which will perform all pending
* cleanups and then break/continue (depending on `exit`) out
* of the loop with id `cleanup_scope`
*/
self.trans_cleanups_to_exit_scope(LoopExit(cleanup_scope, exit))
}
/// Returns a block to branch to which will perform all pending cleanups and then return from
/// this function
fn return_exit_block(&'blk self) -> BasicBlockRef {
/*!
* Returns a block to branch to which will perform all pending
* cleanups and then return from this function
*/
self.trans_cleanups_to_exit_scope(ReturnExit)
}
@ -306,15 +274,11 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.schedule_clean(cleanup_scope, drop as CleanupObj);
}
/// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty`
fn schedule_drop_mem(&self,
cleanup_scope: ScopeId,
val: ValueRef,
ty: Ty<'tcx>) {
/*!
* Schedules a (deep) drop of `val`, which is a pointer to an
* instance of `ty`
*/
if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; }
let drop = box DropValue {
is_immediate: false,
@ -332,15 +296,11 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.schedule_clean(cleanup_scope, drop as CleanupObj);
}
/// Schedules a (deep) drop and zero-ing of `val`, which is a pointer to an instance of `ty`
fn schedule_drop_and_zero_mem(&self,
cleanup_scope: ScopeId,
val: ValueRef,
ty: Ty<'tcx>) {
/*!
* Schedules a (deep) drop and zero-ing of `val`, which is a pointer
* to an instance of `ty`
*/
if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; }
let drop = box DropValue {
is_immediate: false,
@ -359,13 +319,11 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.schedule_clean(cleanup_scope, drop as CleanupObj);
}
/// Schedules a (deep) drop of `val`, which is an instance of `ty`
fn schedule_drop_immediate(&self,
cleanup_scope: ScopeId,
val: ValueRef,
ty: Ty<'tcx>) {
/*!
* Schedules a (deep) drop of `val`, which is an instance of `ty`
*/
if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; }
let drop = box DropValue {
@ -384,16 +342,12 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.schedule_clean(cleanup_scope, drop as CleanupObj);
}
/// Schedules a call to `free(val)`. Note that this is a shallow operation.
fn schedule_free_value(&self,
cleanup_scope: ScopeId,
val: ValueRef,
heap: Heap,
content_ty: Ty<'tcx>) {
/*!
* Schedules a call to `free(val)`. Note that this is a shallow
* operation.
*/
let drop = box FreeValue { ptr: val, heap: heap, content_ty: content_ty };
debug!("schedule_free_value({}, val={}, heap={})",
@ -404,17 +358,13 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.schedule_clean(cleanup_scope, drop as CleanupObj);
}
/// Schedules a call to `free(val)`. Note that this is a shallow operation.
fn schedule_free_slice(&self,
cleanup_scope: ScopeId,
val: ValueRef,
size: ValueRef,
align: ValueRef,
heap: Heap) {
/*!
* Schedules a call to `free(val)`. Note that this is a shallow
* operation.
*/
let drop = box FreeSlice { ptr: val, size: size, align: align, heap: heap };
debug!("schedule_free_slice({}, val={}, heap={})",
@ -434,15 +384,12 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
}
}
/// Schedules a cleanup to occur upon exit from `cleanup_scope`. If `cleanup_scope` is not
/// provided, then the cleanup is scheduled in the topmost scope, which must be a temporary
/// scope.
fn schedule_clean_in_ast_scope(&self,
cleanup_scope: ast::NodeId,
cleanup: CleanupObj<'tcx>) {
/*!
* Schedules a cleanup to occur upon exit from `cleanup_scope`.
* If `cleanup_scope` is not provided, then the cleanup is scheduled
* in the topmost scope, which must be a temporary scope.
*/
debug!("schedule_clean_in_ast_scope(cleanup_scope={})",
cleanup_scope);
@ -462,14 +409,10 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.ccx.tcx().map.node_to_string(cleanup_scope)).as_slice());
}
/// Schedules a cleanup to occur in the top-most scope, which must be a temporary scope.
fn schedule_clean_in_custom_scope(&self,
custom_scope: CustomScopeIndex,
cleanup: CleanupObj<'tcx>) {
/*!
* Schedules a cleanup to occur in the top-most scope,
* which must be a temporary scope.
*/
debug!("schedule_clean_in_custom_scope(custom_scope={})",
custom_scope.index);
@ -481,22 +424,14 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
scope.clear_cached_exits();
}
/// Returns true if there are pending cleanups that should execute on panic.
fn needs_invoke(&self) -> bool {
/*!
* Returns true if there are pending cleanups that should
* execute on panic.
*/
self.scopes.borrow().iter().rev().any(|s| s.needs_invoke())
}
/// Returns a basic block to branch to in the event of a panic. This block will run the panic
/// cleanups and eventually invoke the LLVM `Resume` instruction.
fn get_landing_pad(&'blk self) -> BasicBlockRef {
/*!
* Returns a basic block to branch to in the event of a panic.
* This block will run the panic cleanups and eventually
* invoke the LLVM `Resume` instruction.
*/
let _icx = base::push_ctxt("get_landing_pad");
debug!("get_landing_pad");
@ -529,10 +464,8 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
}
impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
/// Returns the id of the current top-most AST scope, if any.
fn top_ast_scope(&self) -> Option<ast::NodeId> {
/*!
* Returns the id of the current top-most AST scope, if any.
*/
for scope in self.scopes.borrow().iter().rev() {
match scope.kind {
CustomScopeKind | LoopScopeKind(..) => {}
@ -559,10 +492,10 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
(*scopes)[custom_scope.index].kind.is_temp()
}
/// Generates the cleanups for `scope` into `bcx`
fn trans_scope_cleanups(&self, // cannot borrow self, will recurse
bcx: Block<'blk, 'tcx>,
scope: &CleanupScope<'blk, 'tcx>) -> Block<'blk, 'tcx> {
/*! Generates the cleanups for `scope` into `bcx` */
let mut bcx = bcx;
if !bcx.unreachable.get() {
@ -593,37 +526,31 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
f(self.scopes.borrow().last().unwrap())
}
/// Used when the caller wishes to jump to an early exit, such as a return, break, continue, or
/// unwind. This function will generate all cleanups between the top of the stack and the exit
/// `label` and return a basic block that the caller can branch to.
///
/// For example, if the current stack of cleanups were as follows:
///
/// AST 22
/// Custom 1
/// AST 23
/// Loop 23
/// Custom 2
/// AST 24
///
/// and the `label` specifies a break from `Loop 23`, then this function would generate a
/// series of basic blocks as follows:
///
/// Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk
///
/// where `break_blk` is the block specified in `Loop 23` as the target for breaks. The return
/// value would be the first basic block in that sequence (`Cleanup(AST 24)`). The caller could
/// then branch to `Cleanup(AST 24)` and it will perform all cleanups and finally branch to the
/// `break_blk`.
fn trans_cleanups_to_exit_scope(&'blk self,
label: EarlyExitLabel)
-> BasicBlockRef {
/*!
* Used when the caller wishes to jump to an early exit, such
* as a return, break, continue, or unwind. This function will
* generate all cleanups between the top of the stack and the
* exit `label` and return a basic block that the caller can
* branch to.
*
* For example, if the current stack of cleanups were as follows:
*
* AST 22
* Custom 1
* AST 23
* Loop 23
* Custom 2
* AST 24
*
* and the `label` specifies a break from `Loop 23`, then this
* function would generate a series of basic blocks as follows:
*
* Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk
*
* where `break_blk` is the block specified in `Loop 23` as
* the target for breaks. The return value would be the first
* basic block in that sequence (`Cleanup(AST 24)`). The
* caller could then branch to `Cleanup(AST 24)` and it will
* perform all cleanups and finally branch to the `break_blk`.
*/
debug!("trans_cleanups_to_exit_scope label={} scopes={}",
label, self.scopes_len());
@ -756,20 +683,15 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
prev_llbb
}
/// Creates a landing pad for the top scope, if one does not exist. The landing pad will
/// perform all cleanups necessary for an unwind and then `resume` to continue error
/// propagation:
///
/// landing_pad -> ... cleanups ... -> [resume]
///
/// (The cleanups and resume instruction are created by `trans_cleanups_to_exit_scope()`, not
/// in this function itself.)
fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef {
/*!
* Creates a landing pad for the top scope, if one does not
* exist. The landing pad will perform all cleanups necessary
* for an unwind and then `resume` to continue error
* propagation:
*
* landing_pad -> ... cleanups ... -> [resume]
*
* (The cleanups and resume instruction are created by
* `trans_cleanups_to_exit_scope()`, not in this function
* itself.)
*/
let pad_bcx;
debug!("get_or_create_landing_pad");
@ -883,19 +805,15 @@ impl<'blk, 'tcx> CleanupScope<'blk, 'tcx> {
cleanup_block: blk });
}
/// True if this scope has cleanups that need unwinding
fn needs_invoke(&self) -> bool {
/*! True if this scope has cleanups that need unwinding */
self.cached_landing_pad.is_some() ||
self.cleanups.iter().any(|c| c.must_unwind())
}
/// Returns a suitable name to use for the basic block that handles this cleanup scope
fn block_name(&self, prefix: &str) -> String {
/*!
* Returns a suitable name to use for the basic block that
* handles this cleanup scope
*/
match self.kind {
CustomScopeKind => format!("{}_custom_", prefix),
AstScopeKind(id) => format!("{}_ast_{}_", prefix, id),
@ -930,14 +848,10 @@ impl<'blk, 'tcx> CleanupScopeKind<'blk, 'tcx> {
}
}
/// If this is a loop scope with id `id`, return the early exit block `exit`, else `None`
fn early_exit_block(&self,
id: ast::NodeId,
exit: uint) -> Option<BasicBlockRef> {
/*!
* If this is a loop scope with id `id`, return the early
* exit block `exit`, else `None`
*/
match *self {
LoopScopeKind(i, ref exits) if id == i => Some(exits[exit].llbb),
_ => None,

View File

@ -386,6 +386,15 @@ impl<'a, 'tcx> ClosureEnv<'a, 'tcx> {
}
}
/// Translates the body of a closure expression.
///
/// - `store`
/// - `decl`
/// - `body`
/// - `id`: The id of the closure expression.
/// - `cap_clause`: information about captured variables, if any.
/// - `dest`: where to write the closure value, which must be a
/// (fn ptr, env) pair
pub fn trans_expr_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
store: ty::TraitStore,
decl: &ast::FnDecl,
@ -393,19 +402,6 @@ pub fn trans_expr_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
id: ast::NodeId,
dest: expr::Dest)
-> Block<'blk, 'tcx> {
/*!
*
* Translates the body of a closure expression.
*
* - `store`
* - `decl`
* - `body`
* - `id`: The id of the closure expression.
* - `cap_clause`: information about captured variables, if any.
* - `dest`: where to write the closure value, which must be a
(fn ptr, env) pair
*/
let _icx = push_ctxt("closure::trans_expr_fn");
let dest_addr = match dest {

View File

@ -95,26 +95,19 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
}
}
/// Identify types which have size zero at runtime.
pub fn type_is_zero_size<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
/*!
* Identify types which have size zero at runtime.
*/
use trans::machine::llsize_of_alloc;
use trans::type_of::sizing_type_of;
let llty = sizing_type_of(ccx, ty);
llsize_of_alloc(ccx, llty) == 0
}
/// Identifies types which we declare to be equivalent to `void` in C for the purpose of function
/// return types. These are `()`, bot, and uninhabited enums. Note that all such types are also
/// zero-size, but not all zero-size types use a `void` return type (in order to aid with C ABI
/// compatibility).
pub fn return_type_is_void(ccx: &CrateContext, ty: Ty) -> bool {
/*!
* Identifies types which we declare to be equivalent to `void`
* in C for the purpose of function return types. These are
* `()`, bot, and uninhabited enums. Note that all such types
* are also zero-size, but not all zero-size types use a `void`
* return type (in order to aid with C ABI compatibility).
*/
ty::type_is_nil(ty) || ty::type_is_empty(ccx.tcx(), ty)
}
@ -768,19 +761,14 @@ pub fn expr_ty_adjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ex: &ast::Expr) -> T
monomorphize_type(bcx, ty::expr_ty_adjusted(bcx.tcx(), ex))
}
/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we
/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should
/// guarantee to us that all nested obligations *could be* resolved if we wanted to.
pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
span: Span,
trait_ref: Rc<ty::TraitRef<'tcx>>)
-> traits::Vtable<'tcx, ()>
{
/*!
* Attempts to resolve an obligation. The result is a shallow
* vtable resolution -- meaning that we do not (necessarily) resolve
* all nested obligations on the impl. Note that type check should
* guarantee to us that all nested obligations *could be* resolved
* if we wanted to.
*/
let tcx = ccx.tcx();
// Remove any references to regions; this helps improve caching.

View File

@ -8,10 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* See the section on datums in `doc.rs` for an overview of what
* Datums are and how they are intended to be used.
*/
//! See the section on datums in `doc.rs` for an overview of what Datums are and how they are
//! intended to be used.
pub use self::Expr::*;
pub use self::RvalueMode::*;
@ -105,6 +103,10 @@ pub fn immediate_rvalue_bcx<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
/// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
/// it. The memory will be dropped upon exit from `scope`. The callback `populate` should
/// initialize the memory. If `zero` is true, the space will be zeroed when it is allocated; this
/// is not necessary unless `bcx` does not dominate the end of `scope`.
pub fn lvalue_scratch_datum<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>,
ty: Ty<'tcx>,
name: &str,
@ -114,15 +116,6 @@ pub fn lvalue_scratch_datum<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>,
populate: |A, Block<'blk, 'tcx>, ValueRef|
-> Block<'blk, 'tcx>)
-> DatumBlock<'blk, 'tcx, Lvalue> {
/*!
* Allocates temporary space on the stack using alloca() and
* returns a by-ref Datum pointing to it. The memory will be
* dropped upon exit from `scope`. The callback `populate` should
* initialize the memory. If `zero` is true, the space will be
* zeroed when it is allocated; this is not necessary unless `bcx`
* does not dominate the end of `scope`.
*/
let scratch = if zero {
alloca_zeroed(bcx, ty, name)
} else {
@ -138,33 +131,24 @@ pub fn lvalue_scratch_datum<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>,
DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue))
}
/// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
/// it. If `zero` is true, the space will be zeroed when it is allocated; this is normally not
/// necessary, but in the case of automatic rooting in match statements it is possible to have
/// temporaries that may not get initialized if a certain arm is not taken, so we must zero them.
/// You must arrange any cleanups etc yourself!
pub fn rvalue_scratch_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ty: Ty<'tcx>,
name: &str)
-> Datum<'tcx, Rvalue> {
/*!
* Allocates temporary space on the stack using alloca() and
* returns a by-ref Datum pointing to it. If `zero` is true, the
* space will be zeroed when it is allocated; this is normally not
* necessary, but in the case of automatic rooting in match
* statements it is possible to have temporaries that may not get
* initialized if a certain arm is not taken, so we must zero
* them. You must arrange any cleanups etc yourself!
*/
let llty = type_of::type_of(bcx.ccx(), ty);
let scratch = alloca(bcx, llty, name);
Datum::new(scratch, ty, Rvalue::new(ByRef))
}
/// Indicates the "appropriate" mode for this value, which is either by ref or by value, depending
/// on whether type is immediate or not.
pub fn appropriate_rvalue_mode<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ty: Ty<'tcx>) -> RvalueMode {
/*!
* Indicates the "appropriate" mode for this value,
* which is either by ref or by value, depending
* on whether type is immediate or not.
*/
if type_is_immediate(ccx, ty) {
ByValue
} else {
@ -226,17 +210,13 @@ impl KindOps for Rvalue {
}
impl KindOps for Lvalue {
/// If an lvalue is moved, we must zero out the memory in which it resides so as to cancel
/// cleanup. If an @T lvalue is copied, we must increment the reference count.
fn post_store<'blk, 'tcx>(&self,
bcx: Block<'blk, 'tcx>,
val: ValueRef,
ty: Ty<'tcx>)
-> Block<'blk, 'tcx> {
/*!
* If an lvalue is moved, we must zero out the memory in which
* it resides so as to cancel cleanup. If an @T lvalue is
* copied, we must increment the reference count.
*/
if ty::type_needs_drop(bcx.tcx(), ty) {
// cancel cleanup of affine values by zeroing out
let () = zero_mem(bcx, val, ty);
@ -280,31 +260,24 @@ impl KindOps for Expr {
}
impl<'tcx> Datum<'tcx, Rvalue> {
/// Schedules a cleanup for this datum in the given scope. That means that this datum is no
/// longer an rvalue datum; hence, this function consumes the datum and returns the contained
/// ValueRef.
pub fn add_clean<'a>(self,
fcx: &FunctionContext<'a, 'tcx>,
scope: cleanup::ScopeId)
-> ValueRef {
/*!
* Schedules a cleanup for this datum in the given scope.
* That means that this datum is no longer an rvalue datum;
* hence, this function consumes the datum and returns the
* contained ValueRef.
*/
add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty);
self.val
}
/// Returns an lvalue datum (that is, a by ref datum with cleanup scheduled). If `self` is not
/// already an lvalue, cleanup will be scheduled in the temporary scope for `expr_id`.
pub fn to_lvalue_datum_in_scope<'blk>(self,
bcx: Block<'blk, 'tcx>,
name: &str,
scope: cleanup::ScopeId)
-> DatumBlock<'blk, 'tcx, Lvalue> {
/*!
* Returns an lvalue datum (that is, a by ref datum with
* cleanup scheduled). If `self` is not already an lvalue,
* cleanup will be scheduled in the temporary scope for `expr_id`.
*/
let fcx = bcx.fcx;
match self.kind.mode {
@ -371,22 +344,16 @@ impl<'tcx> Datum<'tcx, Expr> {
}
}
/// Asserts that this datum *is* an lvalue and returns it.
#[allow(dead_code)] // potentially useful
pub fn assert_lvalue(self, bcx: Block) -> Datum<'tcx, Lvalue> {
/*!
* Asserts that this datum *is* an lvalue and returns it.
*/
self.match_kind(
|d| d,
|_| bcx.sess().bug("assert_lvalue given rvalue"))
}
/// Asserts that this datum *is* an lvalue and returns it.
pub fn assert_rvalue(self, bcx: Block) -> Datum<'tcx, Rvalue> {
/*!
* Asserts that this datum *is* an lvalue and returns it.
*/
self.match_kind(
|_| bcx.sess().bug("assert_rvalue given lvalue"),
|r| r)
@ -408,14 +375,11 @@ impl<'tcx> Datum<'tcx, Expr> {
}
}
/// Arranges cleanup for `self` if it is an rvalue. Use when you are done working with a value
/// that may need drop.
pub fn add_clean_if_rvalue<'blk>(self,
bcx: Block<'blk, 'tcx>,
expr_id: ast::NodeId) {
/*!
* Arranges cleanup for `self` if it is an rvalue. Use when
* you are done working with a value that may need drop.
*/
self.match_kind(
|_| { /* Nothing to do, cleanup already arranged */ },
|r| {
@ -424,16 +388,12 @@ impl<'tcx> Datum<'tcx, Expr> {
})
}
/// Ensures that `self` will get cleaned up, if it is not an lvalue already.
pub fn clean<'blk>(self,
bcx: Block<'blk, 'tcx>,
name: &'static str,
expr_id: ast::NodeId)
-> Block<'blk, 'tcx> {
/*!
* Ensures that `self` will get cleaned up, if it is not an lvalue
* already.
*/
self.to_lvalue_datum(bcx, name, expr_id).bcx
}
@ -454,15 +414,11 @@ impl<'tcx> Datum<'tcx, Expr> {
})
}
/// Ensures that we have an rvalue datum (that is, a datum with no cleanup scheduled).
pub fn to_rvalue_datum<'blk>(self,
bcx: Block<'blk, 'tcx>,
name: &'static str)
-> DatumBlock<'blk, 'tcx, Rvalue> {
/*!
* Ensures that we have an rvalue datum (that is, a datum with
* no cleanup scheduled).
*/
self.match_kind(
|l| {
let mut bcx = bcx;
@ -489,12 +445,9 @@ impl<'tcx> Datum<'tcx, Expr> {
/// such as extracting the field from a struct or a particular element
/// from an array.
impl<'tcx> Datum<'tcx, Lvalue> {
/// Converts a datum into a by-ref value. The datum type must be one which is always passed by
/// reference.
pub fn to_llref(self) -> ValueRef {
/*!
* Converts a datum into a by-ref value. The datum type must
* be one which is always passed by reference.
*/
self.val
}
@ -541,40 +494,30 @@ impl<'tcx, K: KindOps + fmt::Show> Datum<'tcx, K> {
Datum { val: val, ty: ty, kind: kind.to_expr_kind() }
}
/// Moves or copies this value into a new home, as appropriate depending on the type of the
/// datum. This method consumes the datum, since it would be incorrect to go on using the datum
/// if the value represented is affine (and hence the value is moved).
pub fn store_to<'blk>(self,
bcx: Block<'blk, 'tcx>,
dst: ValueRef)
-> Block<'blk, 'tcx> {
/*!
* Moves or copies this value into a new home, as appropriate
* depending on the type of the datum. This method consumes
* the datum, since it would be incorrect to go on using the
* datum if the value represented is affine (and hence the value
* is moved).
*/
self.shallow_copy_raw(bcx, dst);
self.kind.post_store(bcx, self.val, self.ty)
}
/// Helper function that performs a shallow copy of this value into `dst`, which should be a
/// pointer to a memory location suitable for `self.ty`. `dst` should contain uninitialized
/// memory (either newly allocated, zeroed, or dropped).
///
/// This function is private to datums because it leaves memory in an unstable state, where the
/// source value has been copied but not zeroed. Public methods are `store_to` (if you no
/// longer need the source value) or `shallow_copy` (if you wish the source value to remain
/// valid).
fn shallow_copy_raw<'blk>(&self,
bcx: Block<'blk, 'tcx>,
dst: ValueRef)
-> Block<'blk, 'tcx> {
/*!
* Helper function that performs a shallow copy of this value
* into `dst`, which should be a pointer to a memory location
* suitable for `self.ty`. `dst` should contain uninitialized
* memory (either newly allocated, zeroed, or dropped).
*
* This function is private to datums because it leaves memory
* in an unstable state, where the source value has been
* copied but not zeroed. Public methods are `store_to`
* (if you no longer need the source value) or `shallow_copy`
* (if you wish the source value to remain valid).
*/
let _icx = push_ctxt("copy_to_no_check");
if type_is_zero_size(bcx.ccx(), self.ty) {
@ -590,17 +533,13 @@ impl<'tcx, K: KindOps + fmt::Show> Datum<'tcx, K> {
return bcx;
}
/// Copies the value into a new location. This function always preserves the existing datum as
/// a valid value. Therefore, it does not consume `self` and, also, cannot be applied to affine
/// values (since they must never be duplicated).
pub fn shallow_copy<'blk>(&self,
bcx: Block<'blk, 'tcx>,
dst: ValueRef)
-> Block<'blk, 'tcx> {
/*!
* Copies the value into a new location. This function always
* preserves the existing datum as a valid value. Therefore,
* it does not consume `self` and, also, cannot be applied to
* affine values (since they must never be duplicated).
*/
assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty));
self.shallow_copy_raw(bcx, dst)
}
@ -613,23 +552,17 @@ impl<'tcx, K: KindOps + fmt::Show> Datum<'tcx, K> {
self.kind)
}
//! See the `appropriate_rvalue_mode()` function
pub fn appropriate_rvalue_mode<'a>(&self, ccx: &CrateContext<'a, 'tcx>)
-> RvalueMode {
/*! See the `appropriate_rvalue_mode()` function */
appropriate_rvalue_mode(ccx, self.ty)
}
/// Converts `self` into a by-value `ValueRef`. Consumes this datum (i.e., absolves you of
/// responsibility to cleanup the value). For this to work, the value must be something
/// scalar-ish (like an int or a pointer) which (1) does not require drop glue and (2) is
/// naturally passed around by value, and not by reference.
pub fn to_llscalarish<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef {
/*!
* Converts `self` into a by-value `ValueRef`. Consumes this
* datum (i.e., absolves you of responsibility to cleanup the
* value). For this to work, the value must be something
* scalar-ish (like an int or a pointer) which (1) does not
* require drop glue and (2) is naturally passed around by
* value, and not by reference.
*/
assert!(!ty::type_needs_drop(bcx.tcx(), self.ty));
assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue);
if self.kind.is_by_ref() {

View File

@ -8,181 +8,180 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Debug Info Module
This module serves the purpose of generating debug symbols. We use LLVM's
[source level debugging](http://llvm.org/docs/SourceLevelDebugging.html)
features for generating the debug information. The general principle is this:
Given the right metadata in the LLVM IR, the LLVM code generator is able to
create DWARF debug symbols for the given code. The
[metadata](http://llvm.org/docs/LangRef.html#metadata-type) is structured much
like DWARF *debugging information entries* (DIE), representing type information
such as datatype layout, function signatures, block layout, variable location
and scope information, etc. It is the purpose of this module to generate correct
metadata and insert it into the LLVM IR.
As the exact format of metadata trees may change between different LLVM
versions, we now use LLVM
[DIBuilder](http://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) to
create metadata where possible. This will hopefully ease the adaption of this
module to future LLVM versions.
The public API of the module is a set of functions that will insert the correct
metadata into the LLVM IR when called with the right parameters. The module is
thus driven from an outside client with functions like
`debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`.
Internally the module will try to reuse already created metadata by utilizing a
cache. The way to get a shared metadata node when needed is thus to just call
the corresponding function in this module:
let file_metadata = file_metadata(crate_context, path);
The function will take care of probing the cache for an existing node for that
exact file path.
All private state used by the module is stored within either the
CrateDebugContext struct (owned by the CrateContext) or the FunctionDebugContext
(owned by the FunctionContext).
This file consists of three conceptual sections:
1. The public interface of the module
2. Module-internal metadata creation functions
3. Minor utility functions
## Recursive Types
Some kinds of types, such as structs and enums can be recursive. That means that
the type definition of some type X refers to some other type which in turn
(transitively) refers to X. This introduces cycles into the type referral graph.
A naive algorithm doing an on-demand, depth-first traversal of this graph when
describing types, can get trapped in an endless loop when it reaches such a
cycle.
For example, the following simple type for a singly-linked list...
```
struct List {
value: int,
tail: Option<Box<List>>,
}
```
will generate the following callstack with a naive DFS algorithm:
```
describe(t = List)
describe(t = int)
describe(t = Option<Box<List>>)
describe(t = Box<List>)
describe(t = List) // at the beginning again...
...
```
To break cycles like these, we use "forward declarations". That is, when the
algorithm encounters a possibly recursive type (any struct or enum), it
immediately creates a type description node and inserts it into the cache
*before* describing the members of the type. This type description is just a
stub (as type members are not described and added to it yet) but it allows the
algorithm to already refer to the type. After the stub is inserted into the
cache, the algorithm continues as before. If it now encounters a recursive
reference, it will hit the cache and does not try to describe the type anew.
This behaviour is encapsulated in the 'RecursiveTypeDescription' enum, which
represents a kind of continuation, storing all state needed to continue
traversal at the type members after the type has been registered with the cache.
(This implementation approach might be a tad over-engineered and may change in
the future)
## Source Locations and Line Information
In addition to data type descriptions the debugging information must also allow
to map machine code locations back to source code locations in order to be useful.
This functionality is also handled in this module. The following functions allow
to control source mappings:
+ set_source_location()
+ clear_source_location()
+ start_emitting_source_locations()
`set_source_location()` allows to set the current source location. All IR
instructions created after a call to this function will be linked to the given
source location, until another location is specified with
`set_source_location()` or the source location is cleared with
`clear_source_location()`. In the later case, subsequent IR instruction will not
be linked to any source location. As you can see, this is a stateful API
(mimicking the one in LLVM), so be careful with source locations set by previous
calls. It's probably best to not rely on any specific state being present at a
given point in code.
One topic that deserves some extra attention is *function prologues*. At the
beginning of a function's machine code there are typically a few instructions
for loading argument values into allocas and checking if there's enough stack
space for the function to execute. This *prologue* is not visible in the source
code and LLVM puts a special PROLOGUE END marker into the line table at the
first non-prologue instruction of the function. In order to find out where the
prologue ends, LLVM looks for the first instruction in the function body that is
linked to a source location. So, when generating prologue instructions we have
to make sure that we don't emit source location information until the 'real'
function body begins. For this reason, source location emission is disabled by
default for any new function being translated and is only activated after a call
to the third function from the list above, `start_emitting_source_locations()`.
This function should be called right before regularly starting to translate the
top-level block of the given function.
There is one exception to the above rule: `llvm.dbg.declare` instruction must be
linked to the source location of the variable being declared. For function
parameters these `llvm.dbg.declare` instructions typically occur in the middle
of the prologue, however, they are ignored by LLVM's prologue detection. The
`create_argument_metadata()` and related functions take care of linking the
`llvm.dbg.declare` instructions to the correct source locations even while
source location emission is still disabled, so there is no need to do anything
special with source location handling here.
## Unique Type Identification
In order for link-time optimization to work properly, LLVM needs a unique type
identifier that tells it across compilation units which types are the same as
others. This type identifier is created by TypeMap::get_unique_type_id_of_type()
using the following algorithm:
(1) Primitive types have their name as ID
(2) Structs, enums and traits have a multipart identifier
(1) The first part is the SVH (strict version hash) of the crate they were
originally defined in
(2) The second part is the ast::NodeId of the definition in their original
crate
(3) The final part is a concatenation of the type IDs of their concrete type
arguments if they are generic types.
(3) Tuple-, pointer and function types are structurally identified, which means
that they are equivalent if their component types are equivalent (i.e. (int,
int) is the same regardless in which crate it is used).
This algorithm also provides a stable ID for types that are defined in one crate
but instantiated from metadata within another crate. We just have to take care
to always map crate and node IDs back to the original crate context.
As a side-effect these unique type IDs also help to solve a problem arising from
lifetime parameters. Since lifetime parameters are completely omitted in
debuginfo, more than one `Ty` instance may map to the same debuginfo type
metadata, that is, some struct `Struct<'a>` may have N instantiations with
different concrete substitutions for `'a`, and thus there will be N `Ty`
instances for the type `Struct<'a>` even though it is not generic otherwise.
Unfortunately this means that we cannot use `ty::type_id()` as cheap identifier
for type metadata---we have done this in the past, but it led to unnecessary
metadata duplication in the best case and LLVM assertions in the worst. However,
the unique type ID as described above *can* be used as identifier. Since it is
comparatively expensive to construct, though, `ty::type_id()` is still used
additionally as an optimization for cases where the exact same type has been
seen before (which is most of the time). */
//! # Debug Info Module
//!
//! This module serves the purpose of generating debug symbols. We use LLVM's
//! [source level debugging](http://llvm.org/docs/SourceLevelDebugging.html)
//! features for generating the debug information. The general principle is this:
//!
//! Given the right metadata in the LLVM IR, the LLVM code generator is able to
//! create DWARF debug symbols for the given code. The
//! [metadata](http://llvm.org/docs/LangRef.html#metadata-type) is structured much
//! like DWARF *debugging information entries* (DIE), representing type information
//! such as datatype layout, function signatures, block layout, variable location
//! and scope information, etc. It is the purpose of this module to generate correct
//! metadata and insert it into the LLVM IR.
//!
//! As the exact format of metadata trees may change between different LLVM
//! versions, we now use LLVM
//! [DIBuilder](http://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) to
//! create metadata where possible. This will hopefully ease the adaption of this
//! module to future LLVM versions.
//!
//! The public API of the module is a set of functions that will insert the correct
//! metadata into the LLVM IR when called with the right parameters. The module is
//! thus driven from an outside client with functions like
//! `debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`.
//!
//! Internally the module will try to reuse already created metadata by utilizing a
//! cache. The way to get a shared metadata node when needed is thus to just call
//! the corresponding function in this module:
//!
//! let file_metadata = file_metadata(crate_context, path);
//!
//! The function will take care of probing the cache for an existing node for that
//! exact file path.
//!
//! All private state used by the module is stored within either the
//! CrateDebugContext struct (owned by the CrateContext) or the FunctionDebugContext
//! (owned by the FunctionContext).
//!
//! This file consists of three conceptual sections:
//! 1. The public interface of the module
//! 2. Module-internal metadata creation functions
//! 3. Minor utility functions
//!
//!
//! ## Recursive Types
//!
//! Some kinds of types, such as structs and enums can be recursive. That means that
//! the type definition of some type X refers to some other type which in turn
//! (transitively) refers to X. This introduces cycles into the type referral graph.
//! A naive algorithm doing an on-demand, depth-first traversal of this graph when
//! describing types, can get trapped in an endless loop when it reaches such a
//! cycle.
//!
//! For example, the following simple type for a singly-linked list...
//!
//! ```
//! struct List {
//! value: int,
//! tail: Option<Box<List>>,
//! }
//! ```
//!
//! will generate the following callstack with a naive DFS algorithm:
//!
//! ```
//! describe(t = List)
//! describe(t = int)
//! describe(t = Option<Box<List>>)
//! describe(t = Box<List>)
//! describe(t = List) // at the beginning again...
//! ...
//! ```
//!
//! To break cycles like these, we use "forward declarations". That is, when the
//! algorithm encounters a possibly recursive type (any struct or enum), it
//! immediately creates a type description node and inserts it into the cache
//! *before* describing the members of the type. This type description is just a
//! stub (as type members are not described and added to it yet) but it allows the
//! algorithm to already refer to the type. After the stub is inserted into the
//! cache, the algorithm continues as before. If it now encounters a recursive
//! reference, it will hit the cache and does not try to describe the type anew.
//!
//! This behaviour is encapsulated in the 'RecursiveTypeDescription' enum, which
//! represents a kind of continuation, storing all state needed to continue
//! traversal at the type members after the type has been registered with the cache.
//! (This implementation approach might be a tad over-engineered and may change in
//! the future)
//!
//!
//! ## Source Locations and Line Information
//!
//! In addition to data type descriptions the debugging information must also allow
//! to map machine code locations back to source code locations in order to be useful.
//! This functionality is also handled in this module. The following functions allow
//! to control source mappings:
//!
//! + set_source_location()
//! + clear_source_location()
//! + start_emitting_source_locations()
//!
//! `set_source_location()` allows to set the current source location. All IR
//! instructions created after a call to this function will be linked to the given
//! source location, until another location is specified with
//! `set_source_location()` or the source location is cleared with
//! `clear_source_location()`. In the later case, subsequent IR instruction will not
//! be linked to any source location. As you can see, this is a stateful API
//! (mimicking the one in LLVM), so be careful with source locations set by previous
//! calls. It's probably best to not rely on any specific state being present at a
//! given point in code.
//!
//! One topic that deserves some extra attention is *function prologues*. At the
//! beginning of a function's machine code there are typically a few instructions
//! for loading argument values into allocas and checking if there's enough stack
//! space for the function to execute. This *prologue* is not visible in the source
//! code and LLVM puts a special PROLOGUE END marker into the line table at the
//! first non-prologue instruction of the function. In order to find out where the
//! prologue ends, LLVM looks for the first instruction in the function body that is
//! linked to a source location. So, when generating prologue instructions we have
//! to make sure that we don't emit source location information until the 'real'
//! function body begins. For this reason, source location emission is disabled by
//! default for any new function being translated and is only activated after a call
//! to the third function from the list above, `start_emitting_source_locations()`.
//! This function should be called right before regularly starting to translate the
//! top-level block of the given function.
//!
//! There is one exception to the above rule: `llvm.dbg.declare` instruction must be
//! linked to the source location of the variable being declared. For function
//! parameters these `llvm.dbg.declare` instructions typically occur in the middle
//! of the prologue, however, they are ignored by LLVM's prologue detection. The
//! `create_argument_metadata()` and related functions take care of linking the
//! `llvm.dbg.declare` instructions to the correct source locations even while
//! source location emission is still disabled, so there is no need to do anything
//! special with source location handling here.
//!
//! ## Unique Type Identification
//!
//! In order for link-time optimization to work properly, LLVM needs a unique type
//! identifier that tells it across compilation units which types are the same as
//! others. This type identifier is created by TypeMap::get_unique_type_id_of_type()
//! using the following algorithm:
//!
//! (1) Primitive types have their name as ID
//! (2) Structs, enums and traits have a multipart identifier
//!
//! (1) The first part is the SVH (strict version hash) of the crate they were
//! originally defined in
//!
//! (2) The second part is the ast::NodeId of the definition in their original
//! crate
//!
//! (3) The final part is a concatenation of the type IDs of their concrete type
//! arguments if they are generic types.
//!
//! (3) Tuple-, pointer and function types are structurally identified, which means
//! that they are equivalent if their component types are equivalent (i.e. (int,
//! int) is the same regardless in which crate it is used).
//!
//! This algorithm also provides a stable ID for types that are defined in one crate
//! but instantiated from metadata within another crate. We just have to take care
//! to always map crate and node IDs back to the original crate context.
//!
//! As a side-effect these unique type IDs also help to solve a problem arising from
//! lifetime parameters. Since lifetime parameters are completely omitted in
//! debuginfo, more than one `Ty` instance may map to the same debuginfo type
//! metadata, that is, some struct `Struct<'a>` may have N instantiations with
//! different concrete substitutions for `'a`, and thus there will be N `Ty`
//! instances for the type `Struct<'a>` even though it is not generic otherwise.
//! Unfortunately this means that we cannot use `ty::type_id()` as cheap identifier
//! for type metadata---we have done this in the past, but it led to unnecessary
//! metadata duplication in the best case and LLVM assertions in the worst. However,
//! the unique type ID as described above *can* be used as identifier. Since it is
//! comparatively expensive to construct, though, `ty::type_id()` is still used
//! additionally as an optimization for cases where the exact same type has been
//! seen before (which is most of the time).
use self::FunctionDebugContextRepr::*;
use self::VariableAccess::*;
use self::VariableKind::*;

View File

@ -8,230 +8,226 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Documentation for the trans module
This module contains high-level summaries of how the various modules
in trans work. It is a work in progress. For detailed comments,
naturally, you can refer to the individual modules themselves.
## The Expr module
The expr module handles translation of expressions. The most general
translation routine is `trans()`, which will translate an expression
into a datum. `trans_into()` is also available, which will translate
an expression and write the result directly into memory, sometimes
avoiding the need for a temporary stack slot. Finally,
`trans_to_lvalue()` is available if you'd like to ensure that the
result has cleanup scheduled.
Internally, each of these functions dispatches to various other
expression functions depending on the kind of expression. We divide
up expressions into:
- **Datum expressions:** Those that most naturally yield values.
Examples would be `22`, `box x`, or `a + b` (when not overloaded).
- **DPS expressions:** Those that most naturally write into a location
in memory. Examples would be `foo()` or `Point { x: 3, y: 4 }`.
- **Statement expressions:** That that do not generate a meaningful
result. Examples would be `while { ... }` or `return 44`.
## The Datum module
A `Datum` encapsulates the result of evaluating a Rust expression. It
contains a `ValueRef` indicating the result, a `Ty` describing
the Rust type, but also a *kind*. The kind indicates whether the datum
has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of
rvalues -- whether or not the value is "by ref" or "by value".
The datum API is designed to try and help you avoid memory errors like
forgetting to arrange cleanup or duplicating a value. The type of the
datum incorporates the kind, and thus reflects whether it has cleanup
scheduled:
- `Datum<Lvalue>` -- by ref, cleanup scheduled
- `Datum<Rvalue>` -- by value or by ref, no cleanup scheduled
- `Datum<Expr>` -- either `Datum<Lvalue>` or `Datum<Rvalue>`
Rvalue and expr datums are noncopyable, and most of the methods on
datums consume the datum itself (with some notable exceptions). This
reflects the fact that datums may represent affine values which ought
to be consumed exactly once, and if you were to try to (for example)
store an affine value multiple times, you would be duplicating it,
which would certainly be a bug.
Some of the datum methods, however, are designed to work only on
copyable values such as ints or pointers. Those methods may borrow the
datum (`&self`) rather than consume it, but they always include
assertions on the type of the value represented to check that this
makes sense. An example is `shallow_copy()`, which duplicates
a datum value.
Translating an expression always yields a `Datum<Expr>` result, but
the methods `to_[lr]value_datum()` can be used to coerce a
`Datum<Expr>` into a `Datum<Lvalue>` or `Datum<Rvalue>` as
needed. Coercing to an lvalue is fairly common, and generally occurs
whenever it is necessary to inspect a value and pull out its
subcomponents (for example, a match, or indexing expression). Coercing
to an rvalue is more unusual; it occurs when moving values from place
to place, such as in an assignment expression or parameter passing.
### Lvalues in detail
An lvalue datum is one for which cleanup has been scheduled. Lvalue
datums are always located in memory, and thus the `ValueRef` for an
LLVM value is always a pointer to the actual Rust value. This means
that if the Datum has a Rust type of `int`, then the LLVM type of the
`ValueRef` will be `int*` (pointer to int).
Because lvalues already have cleanups scheduled, the memory must be
zeroed to prevent the cleanup from taking place (presuming that the
Rust type needs drop in the first place, otherwise it doesn't
matter). The Datum code automatically performs this zeroing when the
value is stored to a new location, for example.
Lvalues usually result from evaluating lvalue expressions. For
example, evaluating a local variable `x` yields an lvalue, as does a
reference to a field like `x.f` or an index `x[i]`.
Lvalue datums can also arise by *converting* an rvalue into an lvalue.
This is done with the `to_lvalue_datum` method defined on
`Datum<Expr>`. Basically this method just schedules cleanup if the
datum is an rvalue, possibly storing the value into a stack slot first
if needed. Converting rvalues into lvalues occurs in constructs like
`&foo()` or `match foo() { ref x => ... }`, where the user is
implicitly requesting a temporary.
Somewhat surprisingly, not all lvalue expressions yield lvalue datums
when trans'd. Ultimately the reason for this is to micro-optimize
the resulting LLVM. For example, consider the following code:
fn foo() -> Box<int> { ... }
let x = *foo();
The expression `*foo()` is an lvalue, but if you invoke `expr::trans`,
it will return an rvalue datum. See `deref_once` in expr.rs for
more details.
### Rvalues in detail
Rvalues datums are values with no cleanup scheduled. One must be
careful with rvalue datums to ensure that cleanup is properly
arranged, usually by converting to an lvalue datum or by invoking the
`add_clean` method.
### Scratch datums
Sometimes you need some temporary scratch space. The functions
`[lr]value_scratch_datum()` can be used to get temporary stack
space. As their name suggests, they yield lvalues and rvalues
respectively. That is, the slot from `lvalue_scratch_datum` will have
cleanup arranged, and the slot from `rvalue_scratch_datum` does not.
## The Cleanup module
The cleanup module tracks what values need to be cleaned up as scopes
are exited, either via panic or just normal control flow. The basic
idea is that the function context maintains a stack of cleanup scopes
that are pushed/popped as we traverse the AST tree. There is typically
at least one cleanup scope per AST node; some AST nodes may introduce
additional temporary scopes.
Cleanup items can be scheduled into any of the scopes on the stack.
Typically, when a scope is popped, we will also generate the code for
each of its cleanups at that time. This corresponds to a normal exit
from a block (for example, an expression completing evaluation
successfully without panic). However, it is also possible to pop a
block *without* executing its cleanups; this is typically used to
guard intermediate values that must be cleaned up on panic, but not
if everything goes right. See the section on custom scopes below for
more details.
Cleanup scopes come in three kinds:
- **AST scopes:** each AST node in a function body has a corresponding
AST scope. We push the AST scope when we start generate code for an AST
node and pop it once the AST node has been fully generated.
- **Loop scopes:** loops have an additional cleanup scope. Cleanups are
never scheduled into loop scopes; instead, they are used to record the
basic blocks that we should branch to when a `continue` or `break` statement
is encountered.
- **Custom scopes:** custom scopes are typically used to ensure cleanup
of intermediate values.
### When to schedule cleanup
Although the cleanup system is intended to *feel* fairly declarative,
it's still important to time calls to `schedule_clean()` correctly.
Basically, you should not schedule cleanup for memory until it has
been initialized, because if an unwind should occur before the memory
is fully initialized, then the cleanup will run and try to free or
drop uninitialized memory. If the initialization itself produces
byproducts that need to be freed, then you should use temporary custom
scopes to ensure that those byproducts will get freed on unwind. For
example, an expression like `box foo()` will first allocate a box in the
heap and then call `foo()` -- if `foo()` should panic, this box needs
to be *shallowly* freed.
### Long-distance jumps
In addition to popping a scope, which corresponds to normal control
flow exiting the scope, we may also *jump out* of a scope into some
earlier scope on the stack. This can occur in response to a `return`,
`break`, or `continue` statement, but also in response to panic. In
any of these cases, we will generate a series of cleanup blocks for
each of the scopes that is exited. So, if the stack contains scopes A
... Z, and we break out of a loop whose corresponding cleanup scope is
X, we would generate cleanup blocks for the cleanups in X, Y, and Z.
After cleanup is done we would branch to the exit point for scope X.
But if panic should occur, we would generate cleanups for all the
scopes from A to Z and then resume the unwind process afterwards.
To avoid generating tons of code, we cache the cleanup blocks that we
create for breaks, returns, unwinds, and other jumps. Whenever a new
cleanup is scheduled, though, we must clear these cached blocks. A
possible improvement would be to keep the cached blocks but simply
generate a new block which performs the additional cleanup and then
branches to the existing cached blocks.
### AST and loop cleanup scopes
AST cleanup scopes are pushed when we begin and end processing an AST
node. They are used to house cleanups related to rvalue temporary that
get referenced (e.g., due to an expression like `&Foo()`). Whenever an
AST scope is popped, we always trans all the cleanups, adding the cleanup
code after the postdominator of the AST node.
AST nodes that represent breakable loops also push a loop scope; the
loop scope never has any actual cleanups, it's just used to point to
the basic blocks where control should flow after a "continue" or
"break" statement. Popping a loop scope never generates code.
### Custom cleanup scopes
Custom cleanup scopes are used for a variety of purposes. The most
common though is to handle temporary byproducts, where cleanup only
needs to occur on panic. The general strategy is to push a custom
cleanup scope, schedule *shallow* cleanups into the custom scope, and
then pop the custom scope (without transing the cleanups) when
execution succeeds normally. This way the cleanups are only trans'd on
unwind, and only up until the point where execution succeeded, at
which time the complete value should be stored in an lvalue or some
other place where normal cleanup applies.
To spell it out, here is an example. Imagine an expression `box expr`.
We would basically:
1. Push a custom cleanup scope C.
2. Allocate the box.
3. Schedule a shallow free in the scope C.
4. Trans `expr` into the box.
5. Pop the scope C.
6. Return the box as an rvalue.
This way, if a panic occurs while transing `expr`, the custom
cleanup scope C is pushed and hence the box will be freed. The trans
code for `expr` itself is responsible for freeing any other byproducts
that may be in play.
*/
//! # Documentation for the trans module
//!
//! This module contains high-level summaries of how the various modules
//! in trans work. It is a work in progress. For detailed comments,
//! naturally, you can refer to the individual modules themselves.
//!
//! ## The Expr module
//!
//! The expr module handles translation of expressions. The most general
//! translation routine is `trans()`, which will translate an expression
//! into a datum. `trans_into()` is also available, which will translate
//! an expression and write the result directly into memory, sometimes
//! avoiding the need for a temporary stack slot. Finally,
//! `trans_to_lvalue()` is available if you'd like to ensure that the
//! result has cleanup scheduled.
//!
//! Internally, each of these functions dispatches to various other
//! expression functions depending on the kind of expression. We divide
//! up expressions into:
//!
//! - **Datum expressions:** Those that most naturally yield values.
//! Examples would be `22`, `box x`, or `a + b` (when not overloaded).
//! - **DPS expressions:** Those that most naturally write into a location
//! in memory. Examples would be `foo()` or `Point { x: 3, y: 4 }`.
//! - **Statement expressions:** That that do not generate a meaningful
//! result. Examples would be `while { ... }` or `return 44`.
//!
//! ## The Datum module
//!
//! A `Datum` encapsulates the result of evaluating a Rust expression. It
//! contains a `ValueRef` indicating the result, a `Ty` describing
//! the Rust type, but also a *kind*. The kind indicates whether the datum
//! has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of
//! rvalues -- whether or not the value is "by ref" or "by value".
//!
//! The datum API is designed to try and help you avoid memory errors like
//! forgetting to arrange cleanup or duplicating a value. The type of the
//! datum incorporates the kind, and thus reflects whether it has cleanup
//! scheduled:
//!
//! - `Datum<Lvalue>` -- by ref, cleanup scheduled
//! - `Datum<Rvalue>` -- by value or by ref, no cleanup scheduled
//! - `Datum<Expr>` -- either `Datum<Lvalue>` or `Datum<Rvalue>`
//!
//! Rvalue and expr datums are noncopyable, and most of the methods on
//! datums consume the datum itself (with some notable exceptions). This
//! reflects the fact that datums may represent affine values which ought
//! to be consumed exactly once, and if you were to try to (for example)
//! store an affine value multiple times, you would be duplicating it,
//! which would certainly be a bug.
//!
//! Some of the datum methods, however, are designed to work only on
//! copyable values such as ints or pointers. Those methods may borrow the
//! datum (`&self`) rather than consume it, but they always include
//! assertions on the type of the value represented to check that this
//! makes sense. An example is `shallow_copy()`, which duplicates
//! a datum value.
//!
//! Translating an expression always yields a `Datum<Expr>` result, but
//! the methods `to_[lr]value_datum()` can be used to coerce a
//! `Datum<Expr>` into a `Datum<Lvalue>` or `Datum<Rvalue>` as
//! needed. Coercing to an lvalue is fairly common, and generally occurs
//! whenever it is necessary to inspect a value and pull out its
//! subcomponents (for example, a match, or indexing expression). Coercing
//! to an rvalue is more unusual; it occurs when moving values from place
//! to place, such as in an assignment expression or parameter passing.
//!
//! ### Lvalues in detail
//!
//! An lvalue datum is one for which cleanup has been scheduled. Lvalue
//! datums are always located in memory, and thus the `ValueRef` for an
//! LLVM value is always a pointer to the actual Rust value. This means
//! that if the Datum has a Rust type of `int`, then the LLVM type of the
//! `ValueRef` will be `int*` (pointer to int).
//!
//! Because lvalues already have cleanups scheduled, the memory must be
//! zeroed to prevent the cleanup from taking place (presuming that the
//! Rust type needs drop in the first place, otherwise it doesn't
//! matter). The Datum code automatically performs this zeroing when the
//! value is stored to a new location, for example.
//!
//! Lvalues usually result from evaluating lvalue expressions. For
//! example, evaluating a local variable `x` yields an lvalue, as does a
//! reference to a field like `x.f` or an index `x[i]`.
//!
//! Lvalue datums can also arise by *converting* an rvalue into an lvalue.
//! This is done with the `to_lvalue_datum` method defined on
//! `Datum<Expr>`. Basically this method just schedules cleanup if the
//! datum is an rvalue, possibly storing the value into a stack slot first
//! if needed. Converting rvalues into lvalues occurs in constructs like
//! `&foo()` or `match foo() { ref x => ... }`, where the user is
//! implicitly requesting a temporary.
//!
//! Somewhat surprisingly, not all lvalue expressions yield lvalue datums
//! when trans'd. Ultimately the reason for this is to micro-optimize
//! the resulting LLVM. For example, consider the following code:
//!
//! fn foo() -> Box<int> { ... }
//! let x = *foo();
//!
//! The expression `*foo()` is an lvalue, but if you invoke `expr::trans`,
//! it will return an rvalue datum. See `deref_once` in expr.rs for
//! more details.
//!
//! ### Rvalues in detail
//!
//! Rvalues datums are values with no cleanup scheduled. One must be
//! careful with rvalue datums to ensure that cleanup is properly
//! arranged, usually by converting to an lvalue datum or by invoking the
//! `add_clean` method.
//!
//! ### Scratch datums
//!
//! Sometimes you need some temporary scratch space. The functions
//! `[lr]value_scratch_datum()` can be used to get temporary stack
//! space. As their name suggests, they yield lvalues and rvalues
//! respectively. That is, the slot from `lvalue_scratch_datum` will have
//! cleanup arranged, and the slot from `rvalue_scratch_datum` does not.
//!
//! ## The Cleanup module
//!
//! The cleanup module tracks what values need to be cleaned up as scopes
//! are exited, either via panic or just normal control flow. The basic
//! idea is that the function context maintains a stack of cleanup scopes
//! that are pushed/popped as we traverse the AST tree. There is typically
//! at least one cleanup scope per AST node; some AST nodes may introduce
//! additional temporary scopes.
//!
//! Cleanup items can be scheduled into any of the scopes on the stack.
//! Typically, when a scope is popped, we will also generate the code for
//! each of its cleanups at that time. This corresponds to a normal exit
//! from a block (for example, an expression completing evaluation
//! successfully without panic). However, it is also possible to pop a
//! block *without* executing its cleanups; this is typically used to
//! guard intermediate values that must be cleaned up on panic, but not
//! if everything goes right. See the section on custom scopes below for
//! more details.
//!
//! Cleanup scopes come in three kinds:
//! - **AST scopes:** each AST node in a function body has a corresponding
//! AST scope. We push the AST scope when we start generate code for an AST
//! node and pop it once the AST node has been fully generated.
//! - **Loop scopes:** loops have an additional cleanup scope. Cleanups are
//! never scheduled into loop scopes; instead, they are used to record the
//! basic blocks that we should branch to when a `continue` or `break` statement
//! is encountered.
//! - **Custom scopes:** custom scopes are typically used to ensure cleanup
//! of intermediate values.
//!
//! ### When to schedule cleanup
//!
//! Although the cleanup system is intended to *feel* fairly declarative,
//! it's still important to time calls to `schedule_clean()` correctly.
//! Basically, you should not schedule cleanup for memory until it has
//! been initialized, because if an unwind should occur before the memory
//! is fully initialized, then the cleanup will run and try to free or
//! drop uninitialized memory. If the initialization itself produces
//! byproducts that need to be freed, then you should use temporary custom
//! scopes to ensure that those byproducts will get freed on unwind. For
//! example, an expression like `box foo()` will first allocate a box in the
//! heap and then call `foo()` -- if `foo()` should panic, this box needs
//! to be *shallowly* freed.
//!
//! ### Long-distance jumps
//!
//! In addition to popping a scope, which corresponds to normal control
//! flow exiting the scope, we may also *jump out* of a scope into some
//! earlier scope on the stack. This can occur in response to a `return`,
//! `break`, or `continue` statement, but also in response to panic. In
//! any of these cases, we will generate a series of cleanup blocks for
//! each of the scopes that is exited. So, if the stack contains scopes A
//! ... Z, and we break out of a loop whose corresponding cleanup scope is
//! X, we would generate cleanup blocks for the cleanups in X, Y, and Z.
//! After cleanup is done we would branch to the exit point for scope X.
//! But if panic should occur, we would generate cleanups for all the
//! scopes from A to Z and then resume the unwind process afterwards.
//!
//! To avoid generating tons of code, we cache the cleanup blocks that we
//! create for breaks, returns, unwinds, and other jumps. Whenever a new
//! cleanup is scheduled, though, we must clear these cached blocks. A
//! possible improvement would be to keep the cached blocks but simply
//! generate a new block which performs the additional cleanup and then
//! branches to the existing cached blocks.
//!
//! ### AST and loop cleanup scopes
//!
//! AST cleanup scopes are pushed when we begin and end processing an AST
//! node. They are used to house cleanups related to rvalue temporary that
//! get referenced (e.g., due to an expression like `&Foo()`). Whenever an
//! AST scope is popped, we always trans all the cleanups, adding the cleanup
//! code after the postdominator of the AST node.
//!
//! AST nodes that represent breakable loops also push a loop scope; the
//! loop scope never has any actual cleanups, it's just used to point to
//! the basic blocks where control should flow after a "continue" or
//! "break" statement. Popping a loop scope never generates code.
//!
//! ### Custom cleanup scopes
//!
//! Custom cleanup scopes are used for a variety of purposes. The most
//! common though is to handle temporary byproducts, where cleanup only
//! needs to occur on panic. The general strategy is to push a custom
//! cleanup scope, schedule *shallow* cleanups into the custom scope, and
//! then pop the custom scope (without transing the cleanups) when
//! execution succeeds normally. This way the cleanups are only trans'd on
//! unwind, and only up until the point where execution succeeded, at
//! which time the complete value should be stored in an lvalue or some
//! other place where normal cleanup applies.
//!
//! To spell it out, here is an example. Imagine an expression `box expr`.
//! We would basically:
//!
//! 1. Push a custom cleanup scope C.
//! 2. Allocate the box.
//! 3. Schedule a shallow free in the scope C.
//! 4. Trans `expr` into the box.
//! 5. Pop the scope C.
//! 6. Return the box as an rvalue.
//!
//! This way, if a panic occurs while transing `expr`, the custom
//! cleanup scope C is pushed and hence the box will be freed. The trans
//! code for `expr` itself is responsible for freeing any other byproducts
//! that may be in play.

View File

@ -8,28 +8,26 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* # Translation of Expressions
*
* Public entry points:
*
* - `trans_into(bcx, expr, dest) -> bcx`: evaluates an expression,
* storing the result into `dest`. This is the preferred form, if you
* can manage it.
*
* - `trans(bcx, expr) -> DatumBlock`: evaluates an expression, yielding
* `Datum` with the result. You can then store the datum, inspect
* the value, etc. This may introduce temporaries if the datum is a
* structural type.
*
* - `trans_to_lvalue(bcx, expr, "...") -> DatumBlock`: evaluates an
* expression and ensures that the result has a cleanup associated with it,
* creating a temporary stack slot if necessary.
*
* - `trans_local_var -> Datum`: looks up a local variable or upvar.
*
* See doc.rs for more comments.
*/
//! # Translation of Expressions
//!
//! Public entry points:
//!
//! - `trans_into(bcx, expr, dest) -> bcx`: evaluates an expression,
//! storing the result into `dest`. This is the preferred form, if you
//! can manage it.
//!
//! - `trans(bcx, expr) -> DatumBlock`: evaluates an expression, yielding
//! `Datum` with the result. You can then store the datum, inspect
//! the value, etc. This may introduce temporaries if the datum is a
//! structural type.
//!
//! - `trans_to_lvalue(bcx, expr, "...") -> DatumBlock`: evaluates an
//! expression and ensures that the result has a cleanup associated with it,
//! creating a temporary stack slot if necessary.
//!
//! - `trans_local_var -> Datum`: looks up a local variable or upvar.
//!
//! See doc.rs for more comments.
#![allow(non_camel_case_types)]
@ -82,15 +80,12 @@ impl Dest {
}
}
/// This function is equivalent to `trans(bcx, expr).store_to_dest(dest)` but it may generate
/// better optimized LLVM code.
pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &ast::Expr,
dest: Dest)
-> Block<'blk, 'tcx> {
/*!
* This function is equivalent to `trans(bcx, expr).store_to_dest(dest)`
* but it may generate better optimized LLVM code.
*/
let mut bcx = bcx;
if bcx.tcx().adjustments.borrow().contains_key(&expr.id) {
@ -124,16 +119,12 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
bcx.fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id)
}
/// Translates an expression, returning a datum (and new block) encapsulating the result. When
/// possible, it is preferred to use `trans_into`, as that may avoid creating a temporary on the
/// stack.
pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &ast::Expr)
-> DatumBlock<'blk, 'tcx, Expr> {
/*!
* Translates an expression, returning a datum (and new block)
* encapsulating the result. When possible, it is preferred to
* use `trans_into`, as that may avoid creating a temporary on
* the stack.
*/
debug!("trans(expr={})", bcx.expr_to_string(expr));
let mut bcx = bcx;
@ -157,15 +148,12 @@ pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef {
GEPi(bcx, fat_ptr, &[0u, abi::FAT_PTR_ADDR])
}
/// Helper for trans that apply adjustments from `expr` to `datum`, which should be the unadjusted
/// translation of `expr`.
fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &ast::Expr,
datum: Datum<'tcx, Expr>)
-> DatumBlock<'blk, 'tcx, Expr> {
/*!
* Helper for trans that apply adjustments from `expr` to `datum`,
* which should be the unadjusted translation of `expr`.
*/
let mut bcx = bcx;
let mut datum = datum;
let adjustment = match bcx.tcx().adjustments.borrow().get(&expr.id).cloned() {
@ -480,34 +468,27 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
}
/// Translates an expression in "lvalue" mode -- meaning that it returns a reference to the memory
/// that the expr represents.
///
/// If this expression is an rvalue, this implies introducing a temporary. In other words,
/// something like `x().f` is translated into roughly the equivalent of
///
/// { tmp = x(); tmp.f }
pub fn trans_to_lvalue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &ast::Expr,
name: &str)
-> DatumBlock<'blk, 'tcx, Lvalue> {
/*!
* Translates an expression in "lvalue" mode -- meaning that it
* returns a reference to the memory that the expr represents.
*
* If this expression is an rvalue, this implies introducing a
* temporary. In other words, something like `x().f` is
* translated into roughly the equivalent of
*
* { tmp = x(); tmp.f }
*/
let mut bcx = bcx;
let datum = unpack_datum!(bcx, trans(bcx, expr));
return datum.to_lvalue_datum(bcx, name, expr.id);
}
/// A version of `trans` that ignores adjustments. You almost certainly do not want to call this
/// directly.
fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &ast::Expr)
-> DatumBlock<'blk, 'tcx, Expr> {
/*!
* A version of `trans` that ignores adjustments. You almost
* certainly do not want to call this directly.
*/
let mut bcx = bcx;
debug!("trans_unadjusted(expr={})", bcx.expr_to_string(expr));
@ -1218,14 +1199,10 @@ fn trans_def_fn_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
DatumBlock::new(bcx, Datum::new(llfn, fn_ty, RvalueExpr(Rvalue::new(ByValue))))
}
/// Translates a reference to a local variable or argument. This always results in an lvalue datum.
pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
def: def::Def)
-> Datum<'tcx, Lvalue> {
/*!
* Translates a reference to a local variable or argument.
* This always results in an lvalue datum.
*/
let _icx = push_ctxt("trans_local_var");
match def {
@ -1262,18 +1239,14 @@ pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
}
/// Helper for enumerating the field types of structs, enums, or records. The optional node ID here
/// is the node ID of the path identifying the enum variant in use. If none, this cannot possibly
/// an enum variant (so, if it is and `node_id_opt` is none, this function panics).
pub fn with_field_tys<'tcx, R>(tcx: &ty::ctxt<'tcx>,
ty: Ty<'tcx>,
node_id_opt: Option<ast::NodeId>,
op: |ty::Disr, (&[ty::field<'tcx>])| -> R)
-> R {
/*!
* Helper for enumerating the field types of structs, enums, or records.
* The optional node ID here is the node ID of the path identifying the enum
* variant in use. If none, this cannot possibly an enum variant (so, if it
* is and `node_id_opt` is none, this function panics).
*/
match ty.sty {
ty::ty_struct(did, ref substs) => {
op(0, struct_fields(tcx, did, substs).as_slice())
@ -2185,24 +2158,18 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
return r;
/// We microoptimize derefs of owned pointers a bit here. Basically, the idea is to make the
/// deref of an rvalue result in an rvalue. This helps to avoid intermediate stack slots in the
/// resulting LLVM. The idea here is that, if the `Box<T>` pointer is an rvalue, then we can
/// schedule a *shallow* free of the `Box<T>` pointer, and then return a ByRef rvalue into the
/// pointer. Because the free is shallow, it is legit to return an rvalue, because we know that
/// the contents are not yet scheduled to be freed. The language rules ensure that the contents
/// will be used (or moved) before the free occurs.
fn deref_owned_pointer<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &ast::Expr,
datum: Datum<'tcx, Expr>,
content_ty: Ty<'tcx>)
-> DatumBlock<'blk, 'tcx, Expr> {
/*!
* We microoptimize derefs of owned pointers a bit here.
* Basically, the idea is to make the deref of an rvalue
* result in an rvalue. This helps to avoid intermediate stack
* slots in the resulting LLVM. The idea here is that, if the
* `Box<T>` pointer is an rvalue, then we can schedule a *shallow*
* free of the `Box<T>` pointer, and then return a ByRef rvalue
* into the pointer. Because the free is shallow, it is legit
* to return an rvalue, because we know that the contents are
* not yet scheduled to be freed. The language rules ensure that the
* contents will be used (or moved) before the free occurs.
*/
match datum.kind {
RvalueExpr(Rvalue { mode: ByRef }) => {
let scope = cleanup::temporary_scope(bcx.tcx(), expr.id);

View File

@ -161,14 +161,10 @@ pub fn register_static(ccx: &CrateContext,
}
}
/// Registers a foreign function found in a library. Just adds a LLVM global.
pub fn register_foreign_item_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
abi: Abi, fty: Ty<'tcx>,
name: &str) -> ValueRef {
/*!
* Registers a foreign function found in a library.
* Just adds a LLVM global.
*/
debug!("register_foreign_item_fn(abi={}, \
ty={}, \
name={})",
@ -201,6 +197,20 @@ pub fn register_foreign_item_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
llfn
}
/// Prepares a call to a native function. This requires adapting
/// from the Rust argument passing rules to the native rules.
///
/// # Parameters
///
/// - `callee_ty`: Rust type for the function we are calling
/// - `llfn`: the function pointer we are calling
/// - `llretptr`: where to store the return value of the function
/// - `llargs_rust`: a list of the argument values, prepared
/// as they would be if calling a Rust function
/// - `passed_arg_tys`: Rust type for the arguments. Normally we
/// can derive these from callee_ty but in the case of variadic
/// functions passed_arg_tys will include the Rust type of all
/// the arguments including the ones not specified in the fn's signature.
pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
callee_ty: Ty<'tcx>,
llfn: ValueRef,
@ -208,23 +218,6 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
llargs_rust: &[ValueRef],
passed_arg_tys: Vec<Ty<'tcx>>)
-> Block<'blk, 'tcx> {
/*!
* Prepares a call to a native function. This requires adapting
* from the Rust argument passing rules to the native rules.
*
* # Parameters
*
* - `callee_ty`: Rust type for the function we are calling
* - `llfn`: the function pointer we are calling
* - `llretptr`: where to store the return value of the function
* - `llargs_rust`: a list of the argument values, prepared
* as they would be if calling a Rust function
* - `passed_arg_tys`: Rust type for the arguments. Normally we
* can derive these from callee_ty but in the case of variadic
* functions passed_arg_tys will include the Rust type of all
* the arguments including the ones not specified in the fn's signature.
*/
let ccx = bcx.ccx();
let tcx = bcx.tcx();
@ -832,17 +825,13 @@ pub fn link_name(i: &ast::ForeignItem) -> InternedString {
}
}
/// The ForeignSignature is the LLVM types of the arguments/return type of a function. Note that
/// these LLVM types are not quite the same as the LLVM types would be for a native Rust function
/// because foreign functions just plain ignore modes. They also don't pass aggregate values by
/// pointer like we do.
fn foreign_signature<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
fn_sig: &ty::FnSig<'tcx>, arg_tys: &[Ty<'tcx>])
-> LlvmSignature {
/*!
* The ForeignSignature is the LLVM types of the arguments/return type
* of a function. Note that these LLVM types are not quite the same
* as the LLVM types would be for a native Rust function because foreign
* functions just plain ignore modes. They also don't pass aggregate
* values by pointer like we do.
*/
let llarg_tys = arg_tys.iter().map(|&arg| arg_type_of(ccx, arg)).collect();
let (llret_ty, ret_def) = match fn_sig.output {
ty::FnConverging(ret_ty) =>

View File

@ -375,28 +375,21 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
}
/// Creates a concatenated set of substitutions which includes those from the impl and those from
/// the method. This are some subtle complications here. Statically, we have a list of type
/// parameters like `[T0, T1, T2, M1, M2, M3]` where `Tn` are type parameters that appear on the
/// receiver. For example, if the receiver is a method parameter `A` with a bound like
/// `trait<B,C,D>` then `Tn` would be `[B,C,D]`.
///
/// The weird part is that the type `A` might now be bound to any other type, such as `foo<X>`.
/// In that case, the vector we want is: `[X, M1, M2, M3]`. Therefore, what we do now is to slice
/// off the method type parameters and append them to the type parameters from the type that the
/// receiver is mapped to.
fn combine_impl_and_methods_tps<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
node: ExprOrMethodCall,
rcvr_substs: subst::Substs<'tcx>)
-> subst::Substs<'tcx>
{
/*!
* Creates a concatenated set of substitutions which includes
* those from the impl and those from the method. This are
* some subtle complications here. Statically, we have a list
* of type parameters like `[T0, T1, T2, M1, M2, M3]` where
* `Tn` are type parameters that appear on the receiver. For
* example, if the receiver is a method parameter `A` with a
* bound like `trait<B,C,D>` then `Tn` would be `[B,C,D]`.
*
* The weird part is that the type `A` might now be bound to
* any other type, such as `foo<X>`. In that case, the vector
* we want is: `[X, M1, M2, M3]`. Therefore, what we do now is
* to slice off the method type parameters and append them to
* the type parameters from the type that the receiver is
* mapped to.
*/
let ccx = bcx.ccx();
let node_substs = node_id_substs(bcx, node);
@ -420,21 +413,16 @@ fn combine_impl_and_methods_tps<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
}
/// Create a method callee where the method is coming from a trait object (e.g., Box<Trait> type).
/// In this case, we must pull the fn pointer out of the vtable that is packaged up with the
/// object. Objects are represented as a pair, so we first evaluate the self expression and then
/// extract the self data and vtable out of the pair.
fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
method_ty: Ty<'tcx>,
n_method: uint,
self_expr: &ast::Expr,
arg_cleanup_scope: cleanup::ScopeId)
-> Callee<'blk, 'tcx> {
/*!
* Create a method callee where the method is coming from a trait
* object (e.g., Box<Trait> type). In this case, we must pull the fn
* pointer out of the vtable that is packaged up with the object.
* Objects are represented as a pair, so we first evaluate the self
* expression and then extract the self data and vtable out of the
* pair.
*/
let _icx = push_ctxt("meth::trans_trait_callee");
let mut bcx = bcx;
@ -464,16 +452,13 @@ fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
trans_trait_callee_from_llval(bcx, method_ty, n_method, llval)
}
/// Same as `trans_trait_callee()` above, except that it is given a by-ref pointer to the object
/// pair.
pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
callee_ty: Ty<'tcx>,
n_method: uint,
llpair: ValueRef)
-> Callee<'blk, 'tcx> {
/*!
* Same as `trans_trait_callee()` above, except that it is given
* a by-ref pointer to the object pair.
*/
let _icx = push_ctxt("meth::trans_trait_callee");
let ccx = bcx.ccx();
@ -729,19 +714,15 @@ fn emit_vtable_methods<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}).collect()
}
/// Generates the code to convert from a pointer (`Box<T>`, `&T`, etc) into an object
/// (`Box<Trait>`, `&Trait`, etc). This means creating a pair where the first word is the vtable
/// and the second word is the pointer.
pub fn trans_trait_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
datum: Datum<'tcx, Expr>,
id: ast::NodeId,
trait_ref: Rc<ty::TraitRef<'tcx>>,
dest: expr::Dest)
-> Block<'blk, 'tcx> {
/*!
* Generates the code to convert from a pointer (`Box<T>`, `&T`, etc)
* into an object (`Box<Trait>`, `&Trait`, etc). This means creating a
* pair where the first word is the vtable and the second word is
* the pointer.
*/
let mut bcx = bcx;
let _icx = push_ctxt("meth::trans_trait_cast");

View File

@ -134,17 +134,13 @@ pub fn trans_fixed_vstore<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
};
}
/// &[...] allocates memory on the stack and writes the values into it, returning the vector (the
/// caller must make the reference). "..." is similar except that the memory can be statically
/// allocated and we return a reference (strings are always by-ref).
pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
slice_expr: &ast::Expr,
content_expr: &ast::Expr)
-> DatumBlock<'blk, 'tcx, Expr> {
/*!
* &[...] allocates memory on the stack and writes the values into it,
* returning the vector (the caller must make the reference). "..." is
* similar except that the memory can be statically allocated and we return
* a reference (strings are always by-ref).
*/
let fcx = bcx.fcx;
let ccx = fcx.ccx;
let mut bcx = bcx;
@ -208,17 +204,13 @@ pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
immediate_rvalue_bcx(bcx, llfixed, vec_ty).to_expr_datumblock()
}
/// Literal strings translate to slices into static memory. This is different from
/// trans_slice_vstore() above because it doesn't need to copy the content anywhere.
pub fn trans_lit_str<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
lit_expr: &ast::Expr,
str_lit: InternedString,
dest: Dest)
-> Block<'blk, 'tcx> {
/*!
* Literal strings translate to slices into static memory. This is
* different from trans_slice_vstore() above because it doesn't need to copy
* the content anywhere.
*/
debug!("trans_lit_str(lit_expr={}, dest={})",
bcx.expr_to_string(lit_expr),
dest.to_string(bcx.ccx()));
@ -382,15 +374,12 @@ pub fn elements_required(bcx: Block, content_expr: &ast::Expr) -> uint {
}
}
/// Converts a fixed-length vector into the slice pair. The vector should be stored in `llval`
/// which should be by ref.
pub fn get_fixed_base_and_len(bcx: Block,
llval: ValueRef,
vec_length: uint)
-> (ValueRef, ValueRef) {
/*!
* Converts a fixed-length vector into the slice pair.
* The vector should be stored in `llval` which should be by ref.
*/
let ccx = bcx.ccx();
let base = expr::get_dataptr(bcx, llval);
@ -406,18 +395,13 @@ fn get_slice_base_and_len(bcx: Block,
(base, len)
}
/// Converts a vector into the slice pair. The vector should be stored in `llval` which should be
/// by-reference. If you have a datum, you would probably prefer to call
/// `Datum::get_base_and_len()` which will handle any conversions for you.
pub fn get_base_and_len(bcx: Block,
llval: ValueRef,
vec_ty: Ty)
-> (ValueRef, ValueRef) {
/*!
* Converts a vector into the slice pair. The vector should be
* stored in `llval` which should be by-reference. If you have a
* datum, you would probably prefer to call
* `Datum::get_base_and_len()` which will handle any conversions
* for you.
*/
let ccx = bcx.ccx();
match vec_ty.sty {

View File

@ -8,68 +8,64 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
C-string manipulation and management
This modules provides the basic methods for creating and manipulating
null-terminated strings for use with FFI calls (back to C). Most C APIs require
that the string being passed to them is null-terminated, and by default rust's
string types are *not* null terminated.
The other problem with translating Rust strings to C strings is that Rust
strings can validly contain a null-byte in the middle of the string (0 is a
valid Unicode codepoint). This means that not all Rust strings can actually be
translated to C strings.
# Creation of a C string
A C string is managed through the `CString` type defined in this module. It
"owns" the internal buffer of characters and will automatically deallocate the
buffer when the string is dropped. The `ToCStr` trait is implemented for `&str`
and `&[u8]`, but the conversions can fail due to some of the limitations
explained above.
This also means that currently whenever a C string is created, an allocation
must be performed to place the data elsewhere (the lifetime of the C string is
not tied to the lifetime of the original string/data buffer). If C strings are
heavily used in applications, then caching may be advisable to prevent
unnecessary amounts of allocations.
Be carefull to remember that the memory is managed by C allocator API and not
by Rust allocator API.
That means that the CString pointers should be freed with C allocator API
if you intend to do that on your own, as the behaviour if you free them with
Rust's allocator API is not well defined
An example of creating and using a C string would be:
```rust
extern crate libc;
extern {
fn puts(s: *const libc::c_char);
}
fn main() {
let my_string = "Hello, world!";
// Allocate the C string with an explicit local that owns the string. The
// `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope.
let my_c_string = my_string.to_c_str();
unsafe {
puts(my_c_string.as_ptr());
}
// Don't save/return the pointer to the C string, the `c_buffer` will be
// deallocated when this block returns!
my_string.with_c_str(|c_buffer| {
unsafe { puts(c_buffer); }
});
}
```
*/
//! C-string manipulation and management
//!
//! This modules provides the basic methods for creating and manipulating
//! null-terminated strings for use with FFI calls (back to C). Most C APIs require
//! that the string being passed to them is null-terminated, and by default rust's
//! string types are *not* null terminated.
//!
//! The other problem with translating Rust strings to C strings is that Rust
//! strings can validly contain a null-byte in the middle of the string (0 is a
//! valid Unicode codepoint). This means that not all Rust strings can actually be
//! translated to C strings.
//!
//! # Creation of a C string
//!
//! A C string is managed through the `CString` type defined in this module. It
//! "owns" the internal buffer of characters and will automatically deallocate the
//! buffer when the string is dropped. The `ToCStr` trait is implemented for `&str`
//! and `&[u8]`, but the conversions can fail due to some of the limitations
//! explained above.
//!
//! This also means that currently whenever a C string is created, an allocation
//! must be performed to place the data elsewhere (the lifetime of the C string is
//! not tied to the lifetime of the original string/data buffer). If C strings are
//! heavily used in applications, then caching may be advisable to prevent
//! unnecessary amounts of allocations.
//!
//! Be carefull to remember that the memory is managed by C allocator API and not
//! by Rust allocator API.
//! That means that the CString pointers should be freed with C allocator API
//! if you intend to do that on your own, as the behaviour if you free them with
//! Rust's allocator API is not well defined
//!
//! An example of creating and using a C string would be:
//!
//! ```rust
//! extern crate libc;
//!
//! extern {
//! fn puts(s: *const libc::c_char);
//! }
//!
//! fn main() {
//! let my_string = "Hello, world!";
//!
//! // Allocate the C string with an explicit local that owns the string. The
//! // `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope.
//! let my_c_string = my_string.to_c_str();
//! unsafe {
//! puts(my_c_string.as_ptr());
//! }
//!
//! // Don't save/return the pointer to the C string, the `c_buffer` will be
//! // deallocated when this block returns!
//! my_string.with_c_str(|c_buffer| {
//! unsafe { puts(c_buffer); }
//! });
//! }
//! ```
use collections::string::String;
use collections::hash;

View File

@ -14,183 +14,180 @@
#![forbid(non_camel_case_types)]
#![allow(missing_docs)]
/*!
JSON parsing and serialization
# What is JSON?
JSON (JavaScript Object Notation) is a way to write data in Javascript.
Like XML, it allows to encode structured data in a text format that can be easily read by humans.
Its simple syntax and native compatibility with JavaScript have made it a widely used format.
Data types that can be encoded are JavaScript types (see the `Json` enum for more details):
* `Boolean`: equivalent to rust's `bool`
* `Number`: equivalent to rust's `f64`
* `String`: equivalent to rust's `String`
* `Array`: equivalent to rust's `Vec<T>`, but also allowing objects of different types in the same
array
* `Object`: equivalent to rust's `Treemap<String, json::Json>`
* `Null`
An object is a series of string keys mapping to values, in `"key": value` format.
Arrays are enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }).
A simple JSON document encoding a person, his/her age, address and phone numbers could look like:
```ignore
{
"FirstName": "John",
"LastName": "Doe",
"Age": 43,
"Address": {
"Street": "Downing Street 10",
"City": "London",
"Country": "Great Britain"
},
"PhoneNumbers": [
"+44 1234567",
"+44 2345678"
]
}
```
# Rust Type-based Encoding and Decoding
Rust provides a mechanism for low boilerplate encoding & decoding of values to and from JSON via
the serialization API.
To be able to encode a piece of data, it must implement the `serialize::Encodable` trait.
To be able to decode a piece of data, it must implement the `serialize::Decodable` trait.
The Rust compiler provides an annotation to automatically generate the code for these traits:
`#[deriving(Decodable, Encodable)]`
The JSON API provides an enum `json::Json` and a trait `ToJson` to encode objects.
The `ToJson` trait provides a `to_json` method to convert an object into a `json::Json` value.
A `json::Json` value can be encoded as a string or buffer using the functions described above.
You can also use the `json::Encoder` object, which implements the `Encoder` trait.
When using `ToJson` the `Encodable` trait implementation is not mandatory.
# Examples of use
## Using Autoserialization
Create a struct called `TestStruct` and serialize and deserialize it to and from JSON using the
serialization API, using the derived serialization code.
```rust
extern crate serialize;
use serialize::json;
// Automatically generate `Decodable` and `Encodable` trait implementations
#[deriving(Decodable, Encodable)]
pub struct TestStruct {
data_int: u8,
data_str: String,
data_vector: Vec<u8>,
}
fn main() {
let object = TestStruct {
data_int: 1,
data_str: "toto".to_string(),
data_vector: vec![2,3,4,5],
};
// Serialize using `json::encode`
let encoded = json::encode(&object);
// Deserialize using `json::decode`
let decoded: TestStruct = json::decode(encoded.as_slice()).unwrap();
}
```
## Using the `ToJson` trait
The examples above use the `ToJson` trait to generate the JSON string, which is required
for custom mappings.
### Simple example of `ToJson` usage
```rust
extern crate serialize;
use serialize::json::{mod, ToJson, Json};
// A custom data structure
struct ComplexNum {
a: f64,
b: f64,
}
// JSON value representation
impl ToJson for ComplexNum {
fn to_json(&self) -> json::Json {
Json::String(format!("{}+{}i", self.a, self.b))
}
}
// Only generate `Encodable` trait implementation
#[deriving(Encodable)]
pub struct ComplexNumRecord {
uid: u8,
dsc: String,
val: json::Json,
}
fn main() {
let num = ComplexNum { a: 0.0001, b: 12.539 };
let data: String = json::encode(&ComplexNumRecord{
uid: 1,
dsc: "test".to_string(),
val: num.to_json(),
});
println!("data: {}", data);
// data: {"uid":1,"dsc":"test","val":"0.0001+12.539j"};
}
```
### Verbose example of `ToJson` usage
```rust
extern crate serialize;
use std::collections::TreeMap;
use serialize::json::{mod, ToJson, Json};
// Only generate `Decodable` trait implementation
#[deriving(Decodable)]
pub struct TestStruct {
data_int: u8,
data_str: String,
data_vector: Vec<u8>,
}
// Specify encoding method manually
impl ToJson for TestStruct {
fn to_json(&self) -> json::Json {
let mut d = TreeMap::new();
// All standard types implement `to_json()`, so use it
d.insert("data_int".to_string(), self.data_int.to_json());
d.insert("data_str".to_string(), self.data_str.to_json());
d.insert("data_vector".to_string(), self.data_vector.to_json());
Json::Object(d)
}
}
fn main() {
// Serialize using `ToJson`
let input_data = TestStruct {
data_int: 1,
data_str: "toto".to_string(),
data_vector: vec![2,3,4,5],
};
let json_obj: Json = input_data.to_json();
let json_str: String = json_obj.to_string();
// Deserialize like before
let decoded: TestStruct = json::decode(json_str.as_slice()).unwrap();
}
```
*/
//! JSON parsing and serialization
//!
//! # What is JSON?
//!
//! JSON (JavaScript Object Notation) is a way to write data in Javascript.
//! Like XML, it allows to encode structured data in a text format that can be easily read by humans
//! Its simple syntax and native compatibility with JavaScript have made it a widely used format.
//!
//! Data types that can be encoded are JavaScript types (see the `Json` enum for more details):
//!
//! * `Boolean`: equivalent to rust's `bool`
//! * `Number`: equivalent to rust's `f64`
//! * `String`: equivalent to rust's `String`
//! * `Array`: equivalent to rust's `Vec<T>`, but also allowing objects of different types in the
//! same array
//! * `Object`: equivalent to rust's `Treemap<String, json::Json>`
//! * `Null`
//!
//! An object is a series of string keys mapping to values, in `"key": value` format.
//! Arrays are enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }).
//! A simple JSON document encoding a person, his/her age, address and phone numbers could look like
//!
//! ```ignore
//! {
//! "FirstName": "John",
//! "LastName": "Doe",
//! "Age": 43,
//! "Address": {
//! "Street": "Downing Street 10",
//! "City": "London",
//! "Country": "Great Britain"
//! },
//! "PhoneNumbers": [
//! "+44 1234567",
//! "+44 2345678"
//! ]
//! }
//! ```
//!
//! # Rust Type-based Encoding and Decoding
//!
//! Rust provides a mechanism for low boilerplate encoding & decoding of values to and from JSON via
//! the serialization API.
//! To be able to encode a piece of data, it must implement the `serialize::Encodable` trait.
//! To be able to decode a piece of data, it must implement the `serialize::Decodable` trait.
//! The Rust compiler provides an annotation to automatically generate the code for these traits:
//! `#[deriving(Decodable, Encodable)]`
//!
//! The JSON API provides an enum `json::Json` and a trait `ToJson` to encode objects.
//! The `ToJson` trait provides a `to_json` method to convert an object into a `json::Json` value.
//! A `json::Json` value can be encoded as a string or buffer using the functions described above.
//! You can also use the `json::Encoder` object, which implements the `Encoder` trait.
//!
//! When using `ToJson` the `Encodable` trait implementation is not mandatory.
//!
//! # Examples of use
//!
//! ## Using Autoserialization
//!
//! Create a struct called `TestStruct` and serialize and deserialize it to and from JSON using the
//! serialization API, using the derived serialization code.
//!
//! ```rust
//! extern crate serialize;
//! use serialize::json;
//!
//! // Automatically generate `Decodable` and `Encodable` trait implementations
//! #[deriving(Decodable, Encodable)]
//! pub struct TestStruct {
//! data_int: u8,
//! data_str: String,
//! data_vector: Vec<u8>,
//! }
//!
//! fn main() {
//! let object = TestStruct {
//! data_int: 1,
//! data_str: "toto".to_string(),
//! data_vector: vec![2,3,4,5],
//! };
//!
//! // Serialize using `json::encode`
//! let encoded = json::encode(&object);
//!
//! // Deserialize using `json::decode`
//! let decoded: TestStruct = json::decode(encoded.as_slice()).unwrap();
//! }
//! ```
//!
//! ## Using the `ToJson` trait
//!
//! The examples above use the `ToJson` trait to generate the JSON string, which is required
//! for custom mappings.
//!
//! ### Simple example of `ToJson` usage
//!
//! ```rust
//! extern crate serialize;
//! use serialize::json::{mod, ToJson, Json};
//!
//! // A custom data structure
//! struct ComplexNum {
//! a: f64,
//! b: f64,
//! }
//!
//! // JSON value representation
//! impl ToJson for ComplexNum {
//! fn to_json(&self) -> Json {
//! Json::String(format!("{}+{}i", self.a, self.b))
//! }
//! }
//!
//! // Only generate `Encodable` trait implementation
//! #[deriving(Encodable)]
//! pub struct ComplexNumRecord {
//! uid: u8,
//! dsc: String,
//! val: Json,
//! }
//!
//! fn main() {
//! let num = ComplexNum { a: 0.0001, b: 12.539 };
//! let data: String = json::encode(&ComplexNumRecord{
//! uid: 1,
//! dsc: "test".to_string(),
//! val: num.to_json(),
//! });
//! println!("data: {}", data);
//! // data: {"uid":1,"dsc":"test","val":"0.0001+12.539j"};
//! }
//! ```
//!
//! ### Verbose example of `ToJson` usage
//!
//! ```rust
//! extern crate serialize;
//! use std::collections::TreeMap;
//! use serialize::json::{mod, Json, ToJson};
//!
//! // Only generate `Decodable` trait implementation
//! #[deriving(Decodable)]
//! pub struct TestStruct {
//! data_int: u8,
//! data_str: String,
//! data_vector: Vec<u8>,
//! }
//!
//! // Specify encoding method manually
//! impl ToJson for TestStruct {
//! fn to_json(&self) -> Json {
//! let mut d = TreeMap::new();
//! // All standard types implement `to_json()`, so use it
//! d.insert("data_int".to_string(), self.data_int.to_json());
//! d.insert("data_str".to_string(), self.data_str.to_json());
//! d.insert("data_vector".to_string(), self.data_vector.to_json());
//! Json::Object(d)
//! }
//! }
//!
//! fn main() {
//! // Serialize using `ToJson`
//! let input_data = TestStruct {
//! data_int: 1,
//! data_str: "toto".to_string(),
//! data_vector: vec![2,3,4,5],
//! };
//! let json_obj: Json = input_data.to_json();
//! let json_str: String = json_obj.to_string();
//!
//! // Deserialize like before
//! let decoded: TestStruct = json::decode(json_str.as_slice()).unwrap();
//! }
//! ```
use self::JsonEvent::*;
use self::StackElement::*;

View File

@ -8,13 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Dynamic library facilities.
A simple wrapper over the platform's dynamic library facilities
*/
//! Dynamic library facilities.
//!
//! A simple wrapper over the platform's dynamic library facilities
#![experimental]
#![allow(missing_docs)]

View File

@ -10,392 +10,388 @@
//
// ignore-lexer-test FIXME #15679
/*!
Utilities for formatting and printing strings
This module contains the runtime support for the `format!` syntax extension.
This macro is implemented in the compiler to emit calls to this module in order
to format arguments at runtime into strings and streams.
The functions contained in this module should not normally be used in everyday
use cases of `format!`. The assumptions made by these functions are unsafe for
all inputs, and the compiler performs a large amount of validation on the
arguments to `format!` in order to ensure safety at runtime. While it is
possible to call these functions directly, it is not recommended to do so in the
general case.
## Usage
The `format!` macro is intended to be familiar to those coming from C's
printf/fprintf functions or Python's `str.format` function. In its current
revision, the `format!` macro returns a `String` type which is the result of
the formatting. In the future it will also be able to pass in a stream to
format arguments directly while performing minimal allocations.
Some examples of the `format!` extension are:
```rust
# fn main() {
format!("Hello"); // => "Hello"
format!("Hello, {}!", "world"); // => "Hello, world!"
format!("The number is {}", 1i); // => "The number is 1"
format!("{}", (3i, 4i)); // => "(3, 4)"
format!("{value}", value=4i); // => "4"
format!("{} {}", 1i, 2u); // => "1 2"
# }
```
From these, you can see that the first argument is a format string. It is
required by the compiler for this to be a string literal; it cannot be a
variable passed in (in order to perform validity checking). The compiler will
then parse the format string and determine if the list of arguments provided is
suitable to pass to this format string.
### Positional parameters
Each formatting argument is allowed to specify which value argument it's
referencing, and if omitted it is assumed to be "the next argument". For
example, the format string `{} {} {}` would take three parameters, and they
would be formatted in the same order as they're given. The format string
`{2} {1} {0}`, however, would format arguments in reverse order.
Things can get a little tricky once you start intermingling the two types of
positional specifiers. The "next argument" specifier can be thought of as an
iterator over the argument. Each time a "next argument" specifier is seen, the
iterator advances. This leads to behavior like this:
```rust
format!("{1} {} {0} {}", 1i, 2i); // => "2 1 1 2"
```
The internal iterator over the argument has not been advanced by the time the
first `{}` is seen, so it prints the first argument. Then upon reaching the
second `{}`, the iterator has advanced forward to the second argument.
Essentially, parameters which explicitly name their argument do not affect
parameters which do not name an argument in terms of positional specifiers.
A format string is required to use all of its arguments, otherwise it is a
compile-time error. You may refer to the same argument more than once in the
format string, although it must always be referred to with the same type.
### Named parameters
Rust itself does not have a Python-like equivalent of named parameters to a
function, but the `format!` macro is a syntax extension which allows it to
leverage named parameters. Named parameters are listed at the end of the
argument list and have the syntax:
```text
identifier '=' expression
```
For example, the following `format!` expressions all use named argument:
```rust
# fn main() {
format!("{argument}", argument = "test"); // => "test"
format!("{name} {}", 1i, name = 2i); // => "2 1"
format!("{a} {c} {b}", a="a", b=(), c=3i); // => "a 3 ()"
# }
```
It is illegal to put positional parameters (those without names) after arguments
which have names. Like with positional parameters, it is illegal to provide
named parameters that are unused by the format string.
### Argument types
Each argument's type is dictated by the format string. It is a requirement that every argument is
only ever referred to by one type. For example, this is an invalid format string:
```text
{0:x} {0:o}
```
This is invalid because the first argument is both referred to as a hexidecimal as well as an
octal.
There are various parameters which do require a particular type, however. Namely if the syntax
`{:.*}` is used, then the number of characters to print precedes the actual object being formatted,
and the number of characters must have the type `uint`. Although a `uint` can be printed with
`{}`, it is illegal to reference an argument as such. For example this is another invalid
format string:
```text
{:.*} {0}
```
### Formatting traits
When requesting that an argument be formatted with a particular type, you are
actually requesting that an argument ascribes to a particular trait. This allows
multiple actual types to be formatted via `{:x}` (like `i8` as well as `int`).
The current mapping of types to traits is:
* *nothing* `Show`
* `o` `Octal`
* `x` `LowerHex`
* `X` `UpperHex`
* `p` `Pointer`
* `b` `Binary`
* `e` `LowerExp`
* `E` `UpperExp`
What this means is that any type of argument which implements the
`std::fmt::Binary` trait can then be formatted with `{:b}`. Implementations are
provided for these traits for a number of primitive types by the standard
library as well. If no format is specified (as in `{}` or `{:6}`), then the
format trait used is the `Show` trait. This is one of the more commonly
implemented traits when formatting a custom type.
When implementing a format trait for your own type, you will have to implement a
method of the signature:
```rust
# use std::fmt;
# struct Foo; // our custom type
# impl fmt::Show for Foo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
# write!(f, "testing, testing")
# } }
```
Your type will be passed as `self` by-reference, and then the function should
emit output into the `f.buf` stream. It is up to each format trait
implementation to correctly adhere to the requested formatting parameters. The
values of these parameters will be listed in the fields of the `Formatter`
struct. In order to help with this, the `Formatter` struct also provides some
helper methods.
Additionally, the return value of this function is `fmt::Result` which is a
typedef to `Result<(), IoError>` (also known as `IoResult<()>`). Formatting
implementations should ensure that they return errors from `write!` correctly
(propagating errors upward).
An example of implementing the formatting traits would look
like:
```rust
use std::fmt;
use std::f64;
use std::num::Float;
struct Vector2D {
x: int,
y: int,
}
impl fmt::Show for Vector2D {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// The `f` value implements the `Writer` trait, which is what the
// write! macro is expecting. Note that this formatting ignores the
// various flags provided to format strings.
write!(f, "({}, {})", self.x, self.y)
}
}
// Different traits allow different forms of output of a type. The meaning of
// this format is to print the magnitude of a vector.
impl fmt::Binary for Vector2D {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let magnitude = (self.x * self.x + self.y * self.y) as f64;
let magnitude = magnitude.sqrt();
// Respect the formatting flags by using the helper method
// `pad_integral` on the Formatter object. See the method documentation
// for details, and the function `pad` can be used to pad strings.
let decimals = f.precision().unwrap_or(3);
let string = f64::to_str_exact(magnitude, decimals);
f.pad_integral(true, "", string.as_bytes())
}
}
fn main() {
let myvector = Vector2D { x: 3, y: 4 };
println!("{}", myvector); // => "(3, 4)"
println!("{:10.3b}", myvector); // => " 5.000"
}
```
### Related macros
There are a number of related macros in the `format!` family. The ones that are
currently implemented are:
```ignore
format! // described above
write! // first argument is a &mut io::Writer, the destination
writeln! // same as write but appends a newline
print! // the format string is printed to the standard output
println! // same as print but appends a newline
format_args! // described below.
```
#### `write!`
This and `writeln` are two macros which are used to emit the format string to a
specified stream. This is used to prevent intermediate allocations of format
strings and instead directly write the output. Under the hood, this function is
actually invoking the `write` function defined in this module. Example usage is:
```rust
# #![allow(unused_must_use)]
use std::io;
let mut w = Vec::new();
write!(&mut w as &mut io::Writer, "Hello {}!", "world");
```
#### `print!`
This and `println` emit their output to stdout. Similarly to the `write!` macro,
the goal of these macros is to avoid intermediate allocations when printing
output. Example usage is:
```rust
print!("Hello {}!", "world");
println!("I have a newline {}", "character at the end");
```
#### `format_args!`
This is a curious macro which is used to safely pass around
an opaque object describing the format string. This object
does not require any heap allocations to create, and it only
references information on the stack. Under the hood, all of
the related macros are implemented in terms of this. First
off, some example usage is:
```
use std::fmt;
use std::io;
# #[allow(unused_must_use)]
# fn main() {
format_args!(fmt::format, "this returns {}", "String");
let some_writer: &mut io::Writer = &mut io::stdout();
format_args!(|args| { write!(some_writer, "{}", args) }, "print with a {}", "closure");
fn my_fmt_fn(args: &fmt::Arguments) {
write!(&mut io::stdout(), "{}", args);
}
format_args!(my_fmt_fn, "or a {} too", "function");
# }
```
The first argument of the `format_args!` macro is a function (or closure) which
takes one argument of type `&fmt::Arguments`. This structure can then be
passed to the `write` and `format` functions inside this module in order to
process the format string. The goal of this macro is to even further prevent
intermediate allocations when dealing formatting strings.
For example, a logging library could use the standard formatting syntax, but it
would internally pass around this structure until it has been determined where
output should go to.
It is unsafe to programmatically create an instance of `fmt::Arguments` because
the operations performed when executing a format string require the compile-time
checks provided by the compiler. The `format_args!` macro is the only method of
safely creating these structures, but they can be unsafely created with the
constructor provided.
## Syntax
The syntax for the formatting language used is drawn from other languages, so it
should not be too alien. Arguments are formatted with python-like syntax,
meaning that arguments are surrounded by `{}` instead of the C-like `%`. The
actual grammar for the formatting syntax is:
```text
format_string := <text> [ format <text> ] *
format := '{' [ argument ] [ ':' format_spec ] '}'
argument := integer | identifier
format_spec := [[fill]align][sign]['#'][0][width]['.' precision][type]
fill := character
align := '<' | '^' | '>'
sign := '+' | '-'
width := count
precision := count | '*'
type := identifier | ''
count := parameter | integer
parameter := integer '$'
```
## Formatting Parameters
Each argument being formatted can be transformed by a number of formatting
parameters (corresponding to `format_spec` in the syntax above). These
parameters affect the string representation of what's being formatted. This
syntax draws heavily from Python's, so it may seem a bit familiar.
### Fill/Alignment
The fill character is provided normally in conjunction with the `width`
parameter. This indicates that if the value being formatted is smaller than
`width` some extra characters will be printed around it. The extra characters
are specified by `fill`, and the alignment can be one of two options:
* `<` - the argument is left-aligned in `width` columns
* `^` - the argument is center-aligned in `width` columns
* `>` - the argument is right-aligned in `width` columns
### Sign/#/0
These can all be interpreted as flags for a particular formatter.
* '+' - This is intended for numeric types and indicates that the sign should
always be printed. Positive signs are never printed by default, and the
negative sign is only printed by default for the `Signed` trait. This
flag indicates that the correct sign (+ or -) should always be printed.
* '-' - Currently not used
* '#' - This flag is indicates that the "alternate" form of printing should be
used. By default, this only applies to the integer formatting traits and
performs like:
* `x` - precedes the argument with a "0x"
* `X` - precedes the argument with a "0x"
* `t` - precedes the argument with a "0b"
* `o` - precedes the argument with a "0o"
* '0' - This is used to indicate for integer formats that the padding should
both be done with a `0` character as well as be sign-aware. A format
like `{:08d}` would yield `00000001` for the integer `1`, while the same
format would yield `-0000001` for the integer `-1`. Notice that the
negative version has one fewer zero than the positive version.
### Width
This is a parameter for the "minimum width" that the format should take up. If
the value's string does not fill up this many characters, then the padding
specified by fill/alignment will be used to take up the required space.
The default fill/alignment for non-numerics is a space and left-aligned. The
defaults for numeric formatters is also a space but with right-alignment. If the
'0' flag is specified for numerics, then the implicit fill character is '0'.
The value for the width can also be provided as a `uint` in the list of
parameters by using the `2$` syntax indicating that the second argument is a
`uint` specifying the width.
### Precision
For non-numeric types, this can be considered a "maximum width". If the
resulting string is longer than this width, then it is truncated down to this
many characters and only those are emitted.
For integral types, this has no meaning currently.
For floating-point types, this indicates how many digits after the decimal point
should be printed.
## Escaping
The literal characters `{` and `}` may be included in a string by preceding them
with the same character. For example, the `{` character is escaped with `{{` and
the `}` character is escaped with `}}`.
*/
//! Utilities for formatting and printing strings
//!
//! This module contains the runtime support for the `format!` syntax extension.
//! This macro is implemented in the compiler to emit calls to this module in order
//! to format arguments at runtime into strings and streams.
//!
//! The functions contained in this module should not normally be used in everyday
//! use cases of `format!`. The assumptions made by these functions are unsafe for
//! all inputs, and the compiler performs a large amount of validation on the
//! arguments to `format!` in order to ensure safety at runtime. While it is
//! possible to call these functions directly, it is not recommended to do so in the
//! general case.
//!
//! ## Usage
//!
//! The `format!` macro is intended to be familiar to those coming from C's
//! printf/fprintf functions or Python's `str.format` function. In its current
//! revision, the `format!` macro returns a `String` type which is the result of
//! the formatting. In the future it will also be able to pass in a stream to
//! format arguments directly while performing minimal allocations.
//!
//! Some examples of the `format!` extension are:
//!
//! ```rust
//! # fn main() {
//! format!("Hello"); // => "Hello"
//! format!("Hello, {}!", "world"); // => "Hello, world!"
//! format!("The number is {}", 1i); // => "The number is 1"
//! format!("{}", (3i, 4i)); // => "(3, 4)"
//! format!("{value}", value=4i); // => "4"
//! format!("{} {}", 1i, 2u); // => "1 2"
//! # }
//! ```
//!
//! From these, you can see that the first argument is a format string. It is
//! required by the compiler for this to be a string literal; it cannot be a
//! variable passed in (in order to perform validity checking). The compiler will
//! then parse the format string and determine if the list of arguments provided is
//! suitable to pass to this format string.
//!
//! ### Positional parameters
//!
//! Each formatting argument is allowed to specify which value argument it's
//! referencing, and if omitted it is assumed to be "the next argument". For
//! example, the format string `{} {} {}` would take three parameters, and they
//! would be formatted in the same order as they're given. The format string
//! `{2} {1} {0}`, however, would format arguments in reverse order.
//!
//! Things can get a little tricky once you start intermingling the two types of
//! positional specifiers. The "next argument" specifier can be thought of as an
//! iterator over the argument. Each time a "next argument" specifier is seen, the
//! iterator advances. This leads to behavior like this:
//!
//! ```rust
//! format!("{1} {} {0} {}", 1i, 2i); // => "2 1 1 2"
//! ```
//!
//! The internal iterator over the argument has not been advanced by the time the
//! first `{}` is seen, so it prints the first argument. Then upon reaching the
//! second `{}`, the iterator has advanced forward to the second argument.
//! Essentially, parameters which explicitly name their argument do not affect
//! parameters which do not name an argument in terms of positional specifiers.
//!
//! A format string is required to use all of its arguments, otherwise it is a
//! compile-time error. You may refer to the same argument more than once in the
//! format string, although it must always be referred to with the same type.
//!
//! ### Named parameters
//!
//! Rust itself does not have a Python-like equivalent of named parameters to a
//! function, but the `format!` macro is a syntax extension which allows it to
//! leverage named parameters. Named parameters are listed at the end of the
//! argument list and have the syntax:
//!
//! ```text
//! identifier '=' expression
//! ```
//!
//! For example, the following `format!` expressions all use named argument:
//!
//! ```rust
//! # fn main() {
//! format!("{argument}", argument = "test"); // => "test"
//! format!("{name} {}", 1i, name = 2i); // => "2 1"
//! format!("{a} {c} {b}", a="a", b=(), c=3i); // => "a 3 ()"
//! # }
//! ```
//!
//! It is illegal to put positional parameters (those without names) after arguments
//! which have names. Like with positional parameters, it is illegal to provide
//! named parameters that are unused by the format string.
//!
//! ### Argument types
//!
//! Each argument's type is dictated by the format string. It is a requirement that every argument is
//! only ever referred to by one type. For example, this is an invalid format string:
//!
//! ```text
//! {0:x} {0:o}
//! ```
//!
//! This is invalid because the first argument is both referred to as a hexidecimal as well as an
//! octal.
//!
//! There are various parameters which do require a particular type, however. Namely if the syntax
//! `{:.*}` is used, then the number of characters to print precedes the actual object being formatted,
//! and the number of characters must have the type `uint`. Although a `uint` can be printed with
//! `{}`, it is illegal to reference an argument as such. For example this is another invalid
//! format string:
//!
//! ```text
//! {:.*} {0}
//! ```
//!
//! ### Formatting traits
//!
//! When requesting that an argument be formatted with a particular type, you are
//! actually requesting that an argument ascribes to a particular trait. This allows
//! multiple actual types to be formatted via `{:x}` (like `i8` as well as `int`).
//! The current mapping of types to traits is:
//!
//! * *nothing* ⇒ `Show`
//! * `o` ⇒ `Octal`
//! * `x` ⇒ `LowerHex`
//! * `X` ⇒ `UpperHex`
//! * `p` ⇒ `Pointer`
//! * `b` ⇒ `Binary`
//! * `e` ⇒ `LowerExp`
//! * `E` ⇒ `UpperExp`
//!
//! What this means is that any type of argument which implements the
//! `std::fmt::Binary` trait can then be formatted with `{:b}`. Implementations are
//! provided for these traits for a number of primitive types by the standard
//! library as well. If no format is specified (as in `{}` or `{:6}`), then the
//! format trait used is the `Show` trait. This is one of the more commonly
//! implemented traits when formatting a custom type.
//!
//! When implementing a format trait for your own type, you will have to implement a
//! method of the signature:
//!
//! ```rust
//! # use std::fmt;
//! # struct Foo; // our custom type
//! # impl fmt::Show for Foo {
//! fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
//! # write!(f, "testing, testing")
//! # } }
//! ```
//!
//! Your type will be passed as `self` by-reference, and then the function should
//! emit output into the `f.buf` stream. It is up to each format trait
//! implementation to correctly adhere to the requested formatting parameters. The
//! values of these parameters will be listed in the fields of the `Formatter`
//! struct. In order to help with this, the `Formatter` struct also provides some
//! helper methods.
//!
//! Additionally, the return value of this function is `fmt::Result` which is a
//! typedef to `Result<(), IoError>` (also known as `IoResult<()>`). Formatting
//! implementations should ensure that they return errors from `write!` correctly
//! (propagating errors upward).
//!
//! An example of implementing the formatting traits would look
//! like:
//!
//! ```rust
//! use std::fmt;
//! use std::f64;
//! use std::num::Float;
//!
//! struct Vector2D {
//! x: int,
//! y: int,
//! }
//!
//! impl fmt::Show for Vector2D {
//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//! // The `f` value implements the `Writer` trait, which is what the
//! // write! macro is expecting. Note that this formatting ignores the
//! // various flags provided to format strings.
//! write!(f, "({}, {})", self.x, self.y)
//! }
//! }
//!
//! // Different traits allow different forms of output of a type. The meaning of
//! // this format is to print the magnitude of a vector.
//! impl fmt::Binary for Vector2D {
//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//! let magnitude = (self.x * self.x + self.y * self.y) as f64;
//! let magnitude = magnitude.sqrt();
//!
//! // Respect the formatting flags by using the helper method
//! // `pad_integral` on the Formatter object. See the method documentation
//! // for details, and the function `pad` can be used to pad strings.
//! let decimals = f.precision().unwrap_or(3);
//! let string = f64::to_str_exact(magnitude, decimals);
//! f.pad_integral(true, "", string.as_bytes())
//! }
//! }
//!
//! fn main() {
//! let myvector = Vector2D { x: 3, y: 4 };
//!
//! println!("{}", myvector); // => "(3, 4)"
//! println!("{:10.3b}", myvector); // => " 5.000"
//! }
//! ```
//!
//! ### Related macros
//!
//! There are a number of related macros in the `format!` family. The ones that are
//! currently implemented are:
//!
//! ```ignore
//! format! // described above
//! write! // first argument is a &mut io::Writer, the destination
//! writeln! // same as write but appends a newline
//! print! // the format string is printed to the standard output
//! println! // same as print but appends a newline
//! format_args! // described below.
//! ```
//!
//! #### `write!`
//!
//! This and `writeln` are two macros which are used to emit the format string to a
//! specified stream. This is used to prevent intermediate allocations of format
//! strings and instead directly write the output. Under the hood, this function is
//! actually invoking the `write` function defined in this module. Example usage is:
//!
//! ```rust
//! # #![allow(unused_must_use)]
//! use std::io;
//!
//! let mut w = Vec::new();
//! write!(&mut w as &mut io::Writer, "Hello {}!", "world");
//! ```
//!
//! #### `print!`
//!
//! This and `println` emit their output to stdout. Similarly to the `write!` macro,
//! the goal of these macros is to avoid intermediate allocations when printing
//! output. Example usage is:
//!
//! ```rust
//! print!("Hello {}!", "world");
//! println!("I have a newline {}", "character at the end");
//! ```
//!
//! #### `format_args!`
//! This is a curious macro which is used to safely pass around
//! an opaque object describing the format string. This object
//! does not require any heap allocations to create, and it only
//! references information on the stack. Under the hood, all of
//! the related macros are implemented in terms of this. First
//! off, some example usage is:
//!
//! ```
//! use std::fmt;
//! use std::io;
//!
//! # #[allow(unused_must_use)]
//! # fn main() {
//! format_args!(fmt::format, "this returns {}", "String");
//!
//! let some_writer: &mut io::Writer = &mut io::stdout();
//! format_args!(|args| { write!(some_writer, "{}", args) }, "print with a {}", "closure");
//!
//! fn my_fmt_fn(args: &fmt::Arguments) {
//! write!(&mut io::stdout(), "{}", args);
//! }
//! format_args!(my_fmt_fn, "or a {} too", "function");
//! # }
//! ```
//!
//! The first argument of the `format_args!` macro is a function (or closure) which
//! takes one argument of type `&fmt::Arguments`. This structure can then be
//! passed to the `write` and `format` functions inside this module in order to
//! process the format string. The goal of this macro is to even further prevent
//! intermediate allocations when dealing formatting strings.
//!
//! For example, a logging library could use the standard formatting syntax, but it
//! would internally pass around this structure until it has been determined where
//! output should go to.
//!
//! It is unsafe to programmatically create an instance of `fmt::Arguments` because
//! the operations performed when executing a format string require the compile-time
//! checks provided by the compiler. The `format_args!` macro is the only method of
//! safely creating these structures, but they can be unsafely created with the
//! constructor provided.
//!
//! ## Syntax
//!
//! The syntax for the formatting language used is drawn from other languages, so it
//! should not be too alien. Arguments are formatted with python-like syntax,
//! meaning that arguments are surrounded by `{}` instead of the C-like `%`. The
//! actual grammar for the formatting syntax is:
//!
//! ```text
//! format_string := <text> [ format <text> ] *
//! format := '{' [ argument ] [ ':' format_spec ] '}'
//! argument := integer | identifier
//!
//! format_spec := [[fill]align][sign]['#'][0][width]['.' precision][type]
//! fill := character
//! align := '<' | '^' | '>'
//! sign := '+' | '-'
//! width := count
//! precision := count | '*'
//! type := identifier | ''
//! count := parameter | integer
//! parameter := integer '$'
//! ```
//!
//! ## Formatting Parameters
//!
//! Each argument being formatted can be transformed by a number of formatting
//! parameters (corresponding to `format_spec` in the syntax above). These
//! parameters affect the string representation of what's being formatted. This
//! syntax draws heavily from Python's, so it may seem a bit familiar.
//!
//! ### Fill/Alignment
//!
//! The fill character is provided normally in conjunction with the `width`
//! parameter. This indicates that if the value being formatted is smaller than
//! `width` some extra characters will be printed around it. The extra characters
//! are specified by `fill`, and the alignment can be one of two options:
//!
//! * `<` - the argument is left-aligned in `width` columns
//! * `^` - the argument is center-aligned in `width` columns
//! * `>` - the argument is right-aligned in `width` columns
//!
//! ### Sign/#/0
//!
//! These can all be interpreted as flags for a particular formatter.
//!
//! * '+' - This is intended for numeric types and indicates that the sign should
//! always be printed. Positive signs are never printed by default, and the
//! negative sign is only printed by default for the `Signed` trait. This
//! flag indicates that the correct sign (+ or -) should always be printed.
//! * '-' - Currently not used
//! * '#' - This flag is indicates that the "alternate" form of printing should be
//! used. By default, this only applies to the integer formatting traits and
//! performs like:
//! * `x` - precedes the argument with a "0x"
//! * `X` - precedes the argument with a "0x"
//! * `t` - precedes the argument with a "0b"
//! * `o` - precedes the argument with a "0o"
//! * '0' - This is used to indicate for integer formats that the padding should
//! both be done with a `0` character as well as be sign-aware. A format
//! like `{:08d}` would yield `00000001` for the integer `1`, while the same
//! format would yield `-0000001` for the integer `-1`. Notice that the
//! negative version has one fewer zero than the positive version.
//!
//! ### Width
//!
//! This is a parameter for the "minimum width" that the format should take up. If
//! the value's string does not fill up this many characters, then the padding
//! specified by fill/alignment will be used to take up the required space.
//!
//! The default fill/alignment for non-numerics is a space and left-aligned. The
//! defaults for numeric formatters is also a space but with right-alignment. If the
//! '0' flag is specified for numerics, then the implicit fill character is '0'.
//!
//! The value for the width can also be provided as a `uint` in the list of
//! parameters by using the `2$` syntax indicating that the second argument is a
//! `uint` specifying the width.
//!
//! ### Precision
//!
//! For non-numeric types, this can be considered a "maximum width". If the
//! resulting string is longer than this width, then it is truncated down to this
//! many characters and only those are emitted.
//!
//! For integral types, this has no meaning currently.
//!
//! For floating-point types, this indicates how many digits after the decimal point
//! should be printed.
//!
//! ## Escaping
//!
//! The literal characters `{` and `}` may be included in a string by preceding them
//! with the same character. For example, the `{` character is escaped with `{{` and
//! the `}` character is escaped with `}}`.
#![experimental]

View File

@ -8,58 +8,56 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Generic hashing support.
*
* This module provides a generic way to compute the hash of a value. The
* simplest way to make a type hashable is to use `#[deriving(Hash)]`:
*
* # Example
*
* ```rust
* use std::hash;
* use std::hash::Hash;
*
* #[deriving(Hash)]
* struct Person {
* id: uint,
* name: String,
* phone: u64,
* }
*
* let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
* let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
*
* assert!(hash::hash(&person1) != hash::hash(&person2));
* ```
*
* If you need more control over how a value is hashed, you need to implement
* the trait `Hash`:
*
* ```rust
* use std::hash;
* use std::hash::Hash;
* use std::hash::sip::SipState;
*
* struct Person {
* id: uint,
* name: String,
* phone: u64,
* }
*
* impl Hash for Person {
* fn hash(&self, state: &mut SipState) {
* self.id.hash(state);
* self.phone.hash(state);
* }
* }
*
* let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
* let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
*
* assert!(hash::hash(&person1) == hash::hash(&person2));
* ```
*/
//! Generic hashing support.
//!
//! This module provides a generic way to compute the hash of a value. The
//! simplest way to make a type hashable is to use `#[deriving(Hash)]`:
//!
//! # Example
//!
//! ```rust
//! use std::hash;
//! use std::hash::Hash;
//!
//! #[deriving(Hash)]
//! struct Person {
//! id: uint,
//! name: String,
//! phone: u64,
//! }
//!
//! let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
//! let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
//!
//! assert!(hash::hash(&person1) != hash::hash(&person2));
//! ```
//!
//! If you need more control over how a value is hashed, you need to implement
//! the trait `Hash`:
//!
//! ```rust
//! use std::hash;
//! use std::hash::Hash;
//! use std::hash::sip::SipState;
//!
//! struct Person {
//! id: uint,
//! name: String,
//! phone: u64,
//! }
//!
//! impl Hash for Person {
//! fn hash(&self, state: &mut SipState) {
//! self.id.hash(state);
//! self.phone.hash(state);
//! }
//! }
//!
//! let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
//! let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
//!
//! assert!(hash::hash(&person1) == hash::hash(&person2));
//! ```
#![experimental]

View File

@ -10,47 +10,45 @@
//
// ignore-lexer-test FIXME #15679
/*! Synchronous File I/O
This module provides a set of functions and traits for working
with regular files & directories on a filesystem.
At the top-level of the module are a set of freestanding functions, associated
with various filesystem operations. They all operate on `Path` objects.
All operations in this module, including those as part of `File` et al
block the task during execution. In the event of failure, all functions/methods
will return an `IoResult` type with an `Err` value.
Also included in this module is an implementation block on the `Path` object
defined in `std::path::Path`. The impl adds useful methods about inspecting the
metadata of a file. This includes getting the `stat` information, reading off
particular bits of it, etc.
# Example
```rust
# #![allow(unused_must_use)]
use std::io::fs::PathExtensions;
use std::io::{File, fs};
let path = Path::new("foo.txt");
// create the file, whether it exists or not
let mut file = File::create(&path);
file.write(b"foobar");
# drop(file);
// open the file in read-only mode
let mut file = File::open(&path);
file.read_to_end();
println!("{}", path.stat().unwrap().size);
# drop(file);
fs::unlink(&path);
```
*/
//! Synchronous File I/O
//!
//! This module provides a set of functions and traits for working
//! with regular files & directories on a filesystem.
//!
//! At the top-level of the module are a set of freestanding functions, associated
//! with various filesystem operations. They all operate on `Path` objects.
//!
//! All operations in this module, including those as part of `File` et al
//! block the task during execution. In the event of failure, all functions/methods
//! will return an `IoResult` type with an `Err` value.
//!
//! Also included in this module is an implementation block on the `Path` object
//! defined in `std::path::Path`. The impl adds useful methods about inspecting the
//! metadata of a file. This includes getting the `stat` information, reading off
//! particular bits of it, etc.
//!
//! # Example
//!
//! ```rust
//! # #![allow(unused_must_use)]
//! use std::io::fs::PathExtensions;
//! use std::io::{File, fs};
//!
//! let path = Path::new("foo.txt");
//!
//! // create the file, whether it exists or not
//! let mut file = File::create(&path);
//! file.write(b"foobar");
//! # drop(file);
//!
//! // open the file in read-only mode
//! let mut file = File::open(&path);
//! file.read_to_end();
//!
//! println!("{}", path.stat().unwrap().size);
//! # drop(file);
//! fs::unlink(&path);
//! ```
use clone::Clone;
use io::standard_error;

View File

@ -16,207 +16,205 @@
// error handling
/*! I/O, including files, networking, timers, and processes
`std::io` provides Rust's basic I/O types,
for reading and writing to files, TCP, UDP,
and other types of sockets and pipes,
manipulating the file system, spawning processes.
# Examples
Some examples of obvious things you might want to do
* Read lines from stdin
```rust
use std::io;
for line in io::stdin().lines() {
print!("{}", line.unwrap());
}
```
* Read a complete file
```rust
use std::io::File;
let contents = File::open(&Path::new("message.txt")).read_to_end();
```
* Write a line to a file
```rust
# #![allow(unused_must_use)]
use std::io::File;
let mut file = File::create(&Path::new("message.txt"));
file.write(b"hello, file!\n");
# drop(file);
# ::std::io::fs::unlink(&Path::new("message.txt"));
```
* Iterate over the lines of a file
```rust,no_run
use std::io::BufferedReader;
use std::io::File;
let path = Path::new("message.txt");
let mut file = BufferedReader::new(File::open(&path));
for line in file.lines() {
print!("{}", line.unwrap());
}
```
* Pull the lines of a file into a vector of strings
```rust,no_run
use std::io::BufferedReader;
use std::io::File;
let path = Path::new("message.txt");
let mut file = BufferedReader::new(File::open(&path));
let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();
```
* Make a simple TCP client connection and request
```rust
# #![allow(unused_must_use)]
use std::io::TcpStream;
# // connection doesn't fail if a server is running on 8080
# // locally, we still want to be type checking this code, so lets
# // just stop it running (#11576)
# if false {
let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap();
socket.write(b"GET / HTTP/1.0\n\n");
let response = socket.read_to_end();
# }
```
* Make a simple TCP server
```rust
# fn main() { }
# fn foo() {
# #![allow(dead_code)]
use std::io::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
let listener = TcpListener::bind("127.0.0.1:80");
// bind the listener to the specified address
let mut acceptor = listener.listen();
fn handle_client(mut stream: TcpStream) {
// ...
# &mut stream; // silence unused mutability/variable warning
}
// accept connections and process them, spawning a new tasks for each one
for stream in acceptor.incoming() {
match stream {
Err(e) => { /* connection failed */ }
Ok(stream) => spawn(proc() {
// connection succeeded
handle_client(stream)
})
}
}
// close the socket server
drop(acceptor);
# }
```
# Error Handling
I/O is an area where nearly every operation can result in unexpected
errors. Errors should be painfully visible when they happen, and handling them
should be easy to work with. It should be convenient to handle specific I/O
errors, and it should also be convenient to not deal with I/O errors.
Rust's I/O employs a combination of techniques to reduce boilerplate
while still providing feedback about errors. The basic strategy:
* All I/O operations return `IoResult<T>` which is equivalent to
`Result<T, IoError>`. The `Result` type is defined in the `std::result`
module.
* If the `Result` type goes unused, then the compiler will by default emit a
warning about the unused result. This is because `Result` has the
`#[must_use]` attribute.
* Common traits are implemented for `IoResult`, e.g.
`impl<R: Reader> Reader for IoResult<R>`, so that error values do not have
to be 'unwrapped' before use.
These features combine in the API to allow for expressions like
`File::create(&Path::new("diary.txt")).write(b"Met a girl.\n")`
without having to worry about whether "diary.txt" exists or whether
the write succeeds. As written, if either `new` or `write_line`
encounters an error then the result of the entire expression will
be an error.
If you wanted to handle the error though you might write:
```rust
# #![allow(unused_must_use)]
use std::io::File;
match File::create(&Path::new("diary.txt")).write(b"Met a girl.\n") {
Ok(()) => (), // succeeded
Err(e) => println!("failed to write to my diary: {}", e),
}
# ::std::io::fs::unlink(&Path::new("diary.txt"));
```
So what actually happens if `create` encounters an error?
It's important to know that what `new` returns is not a `File`
but an `IoResult<File>`. If the file does not open, then `new` will simply
return `Err(..)`. Because there is an implementation of `Writer` (the trait
required ultimately required for types to implement `write_line`) there is no
need to inspect or unwrap the `IoResult<File>` and we simply call `write_line`
on it. If `new` returned an `Err(..)` then the followup call to `write_line`
will also return an error.
## `try!`
Explicit pattern matching on `IoResult`s can get quite verbose, especially
when performing many I/O operations. Some examples (like those above) are
alleviated with extra methods implemented on `IoResult`, but others have more
complex interdependencies among each I/O operation.
The `try!` macro from `std::macros` is provided as a method of early-return
inside `Result`-returning functions. It expands to an early-return on `Err`
and otherwise unwraps the contained `Ok` value.
If you wanted to read several `u32`s from a file and return their product:
```rust
use std::io::{File, IoResult};
fn file_product(p: &Path) -> IoResult<u32> {
let mut f = File::open(p);
let x1 = try!(f.read_le_u32());
let x2 = try!(f.read_le_u32());
Ok(x1 * x2)
}
match file_product(&Path::new("numbers.bin")) {
Ok(x) => println!("{}", x),
Err(e) => println!("Failed to read numbers!")
}
```
With `try!` in `file_product`, each `read_le_u32` need not be directly
concerned with error handling; instead its caller is responsible for
responding to errors that may occur while attempting to read the numbers.
*/
//! I/O, including files, networking, timers, and processes
//!
//! `std::io` provides Rust's basic I/O types,
//! for reading and writing to files, TCP, UDP,
//! and other types of sockets and pipes,
//! manipulating the file system, spawning processes.
//!
//! # Examples
//!
//! Some examples of obvious things you might want to do
//!
//! * Read lines from stdin
//!
//! ```rust
//! use std::io;
//!
//! for line in io::stdin().lines() {
//! print!("{}", line.unwrap());
//! }
//! ```
//!
//! * Read a complete file
//!
//! ```rust
//! use std::io::File;
//!
//! let contents = File::open(&Path::new("message.txt")).read_to_end();
//! ```
//!
//! * Write a line to a file
//!
//! ```rust
//! # #![allow(unused_must_use)]
//! use std::io::File;
//!
//! let mut file = File::create(&Path::new("message.txt"));
//! file.write(b"hello, file!\n");
//! # drop(file);
//! # ::std::io::fs::unlink(&Path::new("message.txt"));
//! ```
//!
//! * Iterate over the lines of a file
//!
//! ```rust,no_run
//! use std::io::BufferedReader;
//! use std::io::File;
//!
//! let path = Path::new("message.txt");
//! let mut file = BufferedReader::new(File::open(&path));
//! for line in file.lines() {
//! print!("{}", line.unwrap());
//! }
//! ```
//!
//! * Pull the lines of a file into a vector of strings
//!
//! ```rust,no_run
//! use std::io::BufferedReader;
//! use std::io::File;
//!
//! let path = Path::new("message.txt");
//! let mut file = BufferedReader::new(File::open(&path));
//! let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();
//! ```
//!
//! * Make a simple TCP client connection and request
//!
//! ```rust
//! # #![allow(unused_must_use)]
//! use std::io::TcpStream;
//!
//! # // connection doesn't fail if a server is running on 8080
//! # // locally, we still want to be type checking this code, so lets
//! # // just stop it running (#11576)
//! # if false {
//! let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap();
//! socket.write(b"GET / HTTP/1.0\n\n");
//! let response = socket.read_to_end();
//! # }
//! ```
//!
//! * Make a simple TCP server
//!
//! ```rust
//! # fn main() { }
//! # fn foo() {
//! # #![allow(dead_code)]
//! use std::io::{TcpListener, TcpStream};
//! use std::io::{Acceptor, Listener};
//!
//! let listener = TcpListener::bind("127.0.0.1:80");
//!
//! // bind the listener to the specified address
//! let mut acceptor = listener.listen();
//!
//! fn handle_client(mut stream: TcpStream) {
//! // ...
//! # &mut stream; // silence unused mutability/variable warning
//! }
//! // accept connections and process them, spawning a new tasks for each one
//! for stream in acceptor.incoming() {
//! match stream {
//! Err(e) => { /* connection failed */ }
//! Ok(stream) => spawn(proc() {
//! // connection succeeded
//! handle_client(stream)
//! })
//! }
//! }
//!
//! // close the socket server
//! drop(acceptor);
//! # }
//! ```
//!
//!
//! # Error Handling
//!
//! I/O is an area where nearly every operation can result in unexpected
//! errors. Errors should be painfully visible when they happen, and handling them
//! should be easy to work with. It should be convenient to handle specific I/O
//! errors, and it should also be convenient to not deal with I/O errors.
//!
//! Rust's I/O employs a combination of techniques to reduce boilerplate
//! while still providing feedback about errors. The basic strategy:
//!
//! * All I/O operations return `IoResult<T>` which is equivalent to
//! `Result<T, IoError>`. The `Result` type is defined in the `std::result`
//! module.
//! * If the `Result` type goes unused, then the compiler will by default emit a
//! warning about the unused result. This is because `Result` has the
//! `#[must_use]` attribute.
//! * Common traits are implemented for `IoResult`, e.g.
//! `impl<R: Reader> Reader for IoResult<R>`, so that error values do not have
//! to be 'unwrapped' before use.
//!
//! These features combine in the API to allow for expressions like
//! `File::create(&Path::new("diary.txt")).write(b"Met a girl.\n")`
//! without having to worry about whether "diary.txt" exists or whether
//! the write succeeds. As written, if either `new` or `write_line`
//! encounters an error then the result of the entire expression will
//! be an error.
//!
//! If you wanted to handle the error though you might write:
//!
//! ```rust
//! # #![allow(unused_must_use)]
//! use std::io::File;
//!
//! match File::create(&Path::new("diary.txt")).write(b"Met a girl.\n") {
//! Ok(()) => (), // succeeded
//! Err(e) => println!("failed to write to my diary: {}", e),
//! }
//!
//! # ::std::io::fs::unlink(&Path::new("diary.txt"));
//! ```
//!
//! So what actually happens if `create` encounters an error?
//! It's important to know that what `new` returns is not a `File`
//! but an `IoResult<File>`. If the file does not open, then `new` will simply
//! return `Err(..)`. Because there is an implementation of `Writer` (the trait
//! required ultimately required for types to implement `write_line`) there is no
//! need to inspect or unwrap the `IoResult<File>` and we simply call `write_line`
//! on it. If `new` returned an `Err(..)` then the followup call to `write_line`
//! will also return an error.
//!
//! ## `try!`
//!
//! Explicit pattern matching on `IoResult`s can get quite verbose, especially
//! when performing many I/O operations. Some examples (like those above) are
//! alleviated with extra methods implemented on `IoResult`, but others have more
//! complex interdependencies among each I/O operation.
//!
//! The `try!` macro from `std::macros` is provided as a method of early-return
//! inside `Result`-returning functions. It expands to an early-return on `Err`
//! and otherwise unwraps the contained `Ok` value.
//!
//! If you wanted to read several `u32`s from a file and return their product:
//!
//! ```rust
//! use std::io::{File, IoResult};
//!
//! fn file_product(p: &Path) -> IoResult<u32> {
//! let mut f = File::open(p);
//! let x1 = try!(f.read_le_u32());
//! let x2 = try!(f.read_le_u32());
//!
//! Ok(x1 * x2)
//! }
//!
//! match file_product(&Path::new("numbers.bin")) {
//! Ok(x) => println!("{}", x),
//! Err(e) => println!("Failed to read numbers!")
//! }
//! ```
//!
//! With `try!` in `file_product`, each `read_le_u32` need not be directly
//! concerned with error handling; instead its caller is responsible for
//! responding to errors that may occur while attempting to read the numbers.
#![experimental]
#![deny(unused_must_use)]

View File

@ -8,14 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Synchronous DNS Resolution
Contains the functionality to perform DNS resolution in a style related to
getaddrinfo()
*/
//! Synchronous DNS Resolution
//!
//! Contains the functionality to perform DNS resolution in a style related to
//! `getaddrinfo()`
#![allow(missing_docs)]

View File

@ -8,19 +8,15 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Named pipes
This module contains the ability to communicate over named pipes with
synchronous I/O. On windows, this corresponds to talking over a Named Pipe,
while on Unix it corresponds to UNIX domain sockets.
These pipes are similar to TCP in the sense that you can have both a stream to a
server and a server itself. The server provided accepts other `UnixStream`
instances as clients.
*/
//! Named pipes
//!
//! This module contains the ability to communicate over named pipes with
//! synchronous I/O. On windows, this corresponds to talking over a Named Pipe,
//! while on Unix it corresponds to UNIX domain sockets.
//!
//! These pipes are similar to TCP in the sense that you can have both a stream to a
//! server and a server itself. The server provided accepts other `UnixStream`
//! instances as clients.
#![allow(missing_docs)]

View File

@ -8,24 +8,22 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! Non-blocking access to stdin, stdout, and stderr.
This module provides bindings to the local event loop's TTY interface, using it
to offer synchronous but non-blocking versions of stdio. These handles can be
inspected for information about terminal dimensions or for related information
about the stream or terminal to which it is attached.
# Example
```rust
# #![allow(unused_must_use)]
use std::io;
let mut out = io::stdout();
out.write(b"Hello, world!");
```
*/
//! Non-blocking access to stdin, stdout, and stderr.
//!
//! This module provides bindings to the local event loop's TTY interface, using it
//! to offer synchronous but non-blocking versions of stdio. These handles can be
//! inspected for information about terminal dimensions or for related information
//! about the stream or terminal to which it is attached.
//!
//! # Example
//!
//! ```rust
//! # #![allow(unused_must_use)]
//! use std::io;
//!
//! let mut out = io::stdout();
//! out.write(b"Hello, world!");
//! ```
use self::StdSource::*;

Some files were not shown because too many files have changed in this diff Show More