Robert Millar <robert.millar@cantab.net>
Robin Gloster <robin@loc-com.de>
Robin Stocker <robin@nibor.org>
-Rohit Joshi <rohit.joshi@capitalone.com>
+Rohit Joshi <rohit.c.joshi@gmail.com>
Roland Tanglao <roland@rolandtanglao.com>
Rolf Timmermans <rolftimmermans@voormedia.com>
Rolf van de Krol <info@rolfvandekrol.nl>
# make check-stage1-std RUST_TEST_TASKS=1
#
# This is hardly all there is to know of The Rust Build System's
-# mysteries. The tale continues on the wiki[1][2].
+# mysteries. The tale continues on the wiki[1].
#
-# [1]: https://github.com/rust-lang/rust/wiki/Note-getting-started-developing-Rust
-# [2]: https://github.com/rust-lang/rust/wiki/Note-testsuite
+# [1]: https://github.com/rust-lang/rust/wiki/Note-testsuite
#
# If you really feel like getting your hands dirty, then:
#
### Building on Windows
-To easily build on windows we can use [MSYS2](http://sourceforge.net/projects/msys2/):
+To easily build on windows we can use [MSYS2](http://msys2.github.io/):
1. Grab the latest MSYS2 installer and go through the installer.
2. Now from the MSYS2 terminal we want to install the mingw64 toolchain and the other
tools we need.
- $ pacman -S mingw-w64-i686-toolchain
- $ pacman -S base-devel
+```bash
+# choose one based on platform
+$ pacman -S mingw-w64-i686-toolchain
+$ pacman -S mingw-w64-x86_64-toolchain
-3. With that now start `mingw32_shell.bat` from where you installed MSYS2 (i.e. `C:\msys`).
+$ pacman -S base-devel
+```
+
+3. With that now start `mingw32_shell.bat` or `mingw64_shell.bat`
+ from where you installed MSYS2 (i.e. `C:\msys`). Which one you
+ choose depends on if you want 32 or 64 bit Rust.
4. From there just navigate to where you have Rust's source code, configure and build it:
$ ./configure
CFG_CPUTYPE=aarch64
;;
+ powerpc)
+ CFG_CPUTYPE=powerpc
+ ;;
+
x86_64 | x86-64 | x64 | amd64)
CFG_CPUTYPE=x86_64
;;
make_dir $t/rt/jemalloc
for i in \
isaac sync test \
- arch/i386 arch/x86_64 arch/arm arch/aarch64 arch/mips
+ arch/i386 arch/x86_64 arch/arm arch/aarch64 arch/mips arch/powerpc
do
make_dir $t/rt/stage$s/$i
done
msg "configuring LLVM for $gnu_t"
- LLVM_TARGETS="--enable-targets=x86,x86_64,arm,aarch64,mips"
+ LLVM_TARGETS="--enable-targets=x86,x86_64,arm,aarch64,mips,powerpc"
LLVM_BUILD="--build=$gnu_t"
LLVM_HOST="--host=$gnu_t"
LLVM_TARGET="--target=$gnu_t"
endif
CFG_LIB_NAME_aarch64-apple-ios = lib$(1).a
CFG_LIB_GLOB_aarch64-apple-ios = lib$(1)-*.a
-CFG_LIB_SKIP_INSTALL_aarch64-apple-ios = 1 #lib$(1)-*.a
+CFG_INSTALL_ONLY_RLIB_aarch64-apple-ios = 1
CFG_STATIC_LIB_NAME_aarch64-apple-ios=lib$(1).a
CFG_LIB_DSYM_GLOB_aarch64-apple-ios = lib$(1)-*.a.dSYM
CFG_CFLAGS_aarch64-apple-ios := $(CFG_IOS_SDK_FLAGS_aarch64-apple-ios)
endif
CFG_LIB_NAME_armv7-apple-ios = lib$(1).a
CFG_LIB_GLOB_armv7-apple-ios = lib$(1)-*.a
-CFG_LIB_SKIP_INSTALL_armv7-apple-ios = 1 #lib$(1)-*.a
+CFG_INSTALL_ONLY_RLIB_armv7-apple-ios = 1
CFG_STATIC_LIB_NAME_armv7-apple-ios=lib$(1).a
CFG_LIB_DSYM_GLOB_armv7-apple-ios = lib$(1)-*.a.dSYM
CFG_JEMALLOC_CFLAGS_armv7-apple-ios := -arch armv7 -mfpu=vfp3 $(CFG_IOS_SDK_FLAGS_armv7-apple-ios)
endif
CFG_LIB_NAME_armv7s-apple-ios = lib$(1).a
CFG_LIB_GLOB_armv7s-apple-ios = lib$(1)-*.a
-CFG_LIB_SKIP_INSTALL_armv7s-apple-ios = 1 #lib$(1)-*.a
+CFG_INSTALL_ONLY_RLIB_armv7s-apple-ios = 1
CFG_STATIC_LIB_NAME_armv7s-apple-ios=lib$(1).a
CFG_LIB_DSYM_GLOB_armv7s-apple-ios = lib$(1)-*.a.dSYM
CFG_JEMALLOC_CFLAGS_armv7s-apple-ios := -arch armv7s -mfpu=vfp4 $(CFG_IOS_SDK_FLAGS_armv7s-apple-ios)
endif
CFG_LIB_NAME_i386-apple-ios = lib$(1).a
CFG_LIB_GLOB_i386-apple-ios = lib$(1)-*.dylib
+CFG_INSTALL_ONLY_RLIB_i386-apple-ios = 1
CFG_STATIC_LIB_NAME_i386-apple-ios=lib$(1).a
CFG_LIB_DSYM_GLOB_i386-apple-ios = lib$(1)-*.dylib.dSYM
CFG_GCCISH_CFLAGS_i386-apple-ios := -Wall -Werror -g -fPIC -m32 $(CFG_IOSSIM_FLAGS_i386-apple-ios)
--- /dev/null
+# powerpc-unknown-linux-gnu configuration
+CROSS_PREFIX_powerpc-unknown-linux-gnu=powerpc-linux-gnu-
+CC_powerpc-unknown-linux-gnu=$(CC)
+CXX_powerpc-unknown-linux-gnu=$(CXX)
+CPP_powerpc-unknown-linux-gnu=$(CPP)
+AR_powerpc-unknown-linux-gnu=$(AR)
+CFG_LIB_NAME_powerpc-unknown-linux-gnu=lib$(1).so
+CFG_STATIC_LIB_NAME_powerpc-unknown-linux-gnu=lib$(1).a
+CFG_LIB_GLOB_powerpc-unknown-linux-gnu=lib$(1)-*.so
+CFG_LIB_DSYM_GLOB_powerpc-unknown-linux-gnu=lib$(1)-*.dylib.dSYM
+CFG_CFLAGS_powerpc-unknown-linux-gnu := -m32 $(CFLAGS)
+CFG_GCCISH_CFLAGS_powerpc-unknown-linux-gnu := -Wall -Werror -g -fPIC -m32 $(CFLAGS)
+CFG_GCCISH_CXXFLAGS_powerpc-unknown-linux-gnu := -fno-rtti $(CXXFLAGS)
+CFG_GCCISH_LINK_FLAGS_powerpc-unknown-linux-gnu := -shared -fPIC -ldl -pthread -lrt -g -m32
+CFG_GCCISH_DEF_FLAG_powerpc-unknown-linux-gnu := -Wl,--export-dynamic,--dynamic-list=
+CFG_GCCISH_PRE_LIB_FLAGS_powerpc-unknown-linux-gnu := -Wl,-whole-archive
+CFG_GCCISH_POST_LIB_FLAGS_powerpc-unknown-linux-gnu := -Wl,-no-whole-archive
+CFG_DEF_SUFFIX_powerpc-unknown-linux-gnu := .linux.def
+CFG_LLC_FLAGS_powerpc-unknown-linux-gnu :=
+CFG_INSTALL_NAME_powerpc-unknown-linux-gnu =
+CFG_EXE_SUFFIX_powerpc-unknown-linux-gnu =
+CFG_WINDOWSY_powerpc-unknown-linux-gnu :=
+CFG_UNIXY_powerpc-unknown-linux-gnu := 1
+CFG_PATH_MUNGE_powerpc-unknown-linux-gnu := true
+CFG_LDPATH_powerpc-unknown-linux-gnu :=
+CFG_RUN_powerpc-unknown-linux-gnu=$(2)
+CFG_RUN_TARG_powerpc-unknown-linux-gnu=$(call CFG_RUN_powerpc-unknown-linux-gnu,,$(2))
+CFG_GNU_TRIPLE_powerpc-unknown-linux-gnu := powerpc-unknown-linux-gnu
endif
CFG_LIB_NAME_x86_64-apple-ios = lib$(1).a
CFG_LIB_GLOB_x86_64-apple-ios = lib$(1)-*.a
-CFG_LIB_SKIP_INSTALL_x86_64-apple-ios = 1 #lib$(1)-*.a
+CFG_INSTALL_ONLY_RLIB_x86_64-apple-ios = 1
CFG_STATIC_LIB_NAME_x86_64-apple-ios=lib$(1).a
CFG_LIB_DSYM_GLOB_x86_64-apple-ios = lib$(1)-*.a.dSYM
CFG_CFLAGS_x86_64-apple-ios := $(CFG_IOSSIM_FLAGS_x86_64-apple-ios)
######################################################################
# FIXME: x86-ism
-LLVM_COMPONENTS=x86 arm aarch64 mips ipo bitreader bitwriter linker asmparser mcjit \
+LLVM_COMPONENTS=x86 arm aarch64 mips powerpc ipo bitreader bitwriter linker asmparser mcjit \
interpreter instrumentation
# Only build these LLVM tools
$$(CFG_GCCISH_DEF_FLAG_$(1))$$(3) $$(2) \
$$(call CFG_INSTALL_NAME_$(1),$$(4))
- ifeq ($$(findstring $(HOST_$(1)),arm aarch64 mips mipsel),)
+ ifeq ($$(findstring $(HOST_$(1)),arm aarch64 mips mipsel powerpc),)
# We're using llvm-mc as our assembler because it supports
# .cfi pseudo-ops on mac
-o=$$(1)
else
- # For the ARM, AARCH64 and MIPS crosses, use the toolchain assembler
+ # For the ARM, AARCH64, MIPS and POWER crosses, use the toolchain assembler
# FIXME: We should be able to use the LLVM assembler
CFG_ASSEMBLE_$(1)=$$(CC_$(1)) $$(CFG_GCCISH_CFLAGS_$(1)) \
$$(CFG_DEPEND_FLAGS) $$(2) -c -o $$(1)
$$(if $$(findstring $(3), $$(PREPARE_HOST)), \
$$(call PREPARE_DIR,$$(PREPARE_WORKING_DEST_LIB_DIR)) \
$$(foreach crate,$$(TARGET_CRATES), \
- $$(if $$(findstring 1, $$(ONLY_RLIB_$$(crate))),, \
+ $$(if $$(or $$(findstring 1, $$(ONLY_RLIB_$$(crate))),$$(findstring 1,$$(CFG_INSTALL_ONLY_RLIB_$(2)))),, \
$$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate)))) \
$$(call PREPARE_LIB,$$(call CFG_RLIB_GLOB,$$(crate)))) \
$$(if $$(findstring $(2),$$(CFG_HOST)), \
This introduction will give you a rough idea of what Rust is like, eliding many
details. It does not require prior experience with systems programming, but you
-may find the syntax easier if you've used a 'curly brace' programming language
+may find the syntax easier if you've used a "curly brace" programming language
before, like C or JavaScript. The concepts are more important than the syntax,
so don't worry if you don't get every last detail: you can read [The
Rust Programming Language](book/index.html) to get a more complete explanation.
homepage](http://rust-lang.org) for explanation.
To show off Rust, let's talk about how easy it is to get started with Rust.
-Then, we'll talk about Rust's most interesting feature, **ownership**, and
+Then, we'll talk about Rust's most interesting feature, *ownership*, and
then discuss how it makes concurrency easier to reason about. Finally,
we'll talk about how Rust breaks down the perceived dichotomy between speed
and safety.
authors = ["Your Name <you@example.com>"]
```
-This is called a **manifest**, and it contains all of the metadata that Cargo
+This is called a *manifest*, and it contains all of the metadata that Cargo
needs to compile your project.
Here's what's in `src/main.rs`:
}
```
-Cargo generated a 'hello world' for us. We'll talk more about the syntax here
+Cargo generated a "Hello World" for us. We'll talk more about the syntax here
later, but that's what Rust code looks like! Let's compile and run it:
```{bash}
# Ownership
-Rust's defining feature is 'memory safety without garbage collection.' Let's
-take a moment to talk about what that means. **Memory safety** means that the
+Rust's defining feature is "memory safety without garbage collection". Let's
+take a moment to talk about what that means. *Memory safety* means that the
programming language eliminates certain kinds of bugs, such as [buffer
overflows](http://en.wikipedia.org/wiki/Buffer_overflow) and [dangling
pointers](http://en.wikipedia.org/wiki/Dangling_pointer). These problems occur
adds an element to the end of an array.
Next, we make a new variable, `x`, that's equal to the first element of
-the array. Simple, but this is where the 'bug' will appear.
+the array. Simple, but this is where the "bug" will appear.
Let's keep going. We then call `push` again, pushing "world" onto the
end of the array. `v` now is `["Hello", "world"]`.
as well, we just don't think about it very often. So why does the C++ version
segfault when we allocate more memory?
-The answer is that in the C++ version, `x` is a **reference** to the memory
+The answer is that in the C++ version, `x` is a *reference* to the memory
location where the first element of the array is stored. But in Ruby, `x` is a
standalone value, not connected to the underyling array at all. Let's dig into
the details for a moment. Your program has access to memory, provided to it by
When we try to mutate the array by `push`ing it the second time, Rust throws
an error. It says that we "cannot borrow v as mutable because it is also
-borrowed as immutable." What's up with "borrowed"?
+borrowed as immutable." What does it mean by "borrowed"?
-In Rust, the type system encodes the notion of **ownership**. The variable `v`
-is an "owner" of the vector. When we make a reference to `v`, we let that
-variable (in this case, `x`) 'borrow' it for a while. Just like if you own a
+In Rust, the type system encodes the notion of *ownership*. The variable `v`
+is an *owner* of the vector. When we make a reference to `v`, we let that
+variable (in this case, `x`) *borrow* it for a while. Just like if you own a
book, and you lend it to me, I'm borrowing the book.
So, when I try to modify the vector with the second call to `push`, I need
use std::thread::Thread;
fn main() {
- for _ in range(0u, 10u) {
- Thread::spawn(move || {
+ let guards: Vec<_> = (0..10).map(|_| {
+ Thread::scoped(|| {
println!("Hello, world!");
- });
- }
+ })
+ }).collect();
}
```
-This program creates ten threads, who all print `Hello, world!`. The
-`spawn` function takes one argument, a closure, indicated by the
-double bars `||`. (The `move` keyword indicates that the closure takes
-ownership of any data it uses; we'll have more on the significance of
-this shortly.) This closure is executed in a new thread created by
-`spawn`.
+This program creates ten threads, which all print `Hello, world!`. The `scoped`
+function takes one argument, a closure, indicated by the double bars `||`. This
+closure is executed in a new thread created by `scoped`. The method is called
+`scoped` because it returns a 'join guard', which will automatically join the
+child thread when it goes out of scope. Because we `collect` these guards into
+a `Vec<T>`, and that vector goes out of scope at the end of our program, our
+program will wait for every thread to finish before finishing.
-One common form of problem in concurrent programs is a 'data race.'
+One common form of problem in concurrent programs is a *data race*.
This occurs when two different threads attempt to access the same
location in memory in a non-synchronized way, where at least one of
them is a write. If one thread is attempting to read, and one thread
Rust disallows it.
What to do here? Rust has two types that helps us: `Arc<T>` and `Mutex<T>`.
-"Arc" stands for "atomically reference counted." In other words, an Arc will
+*Arc* stands for "atomically reference counted". In other words, an Arc will
keep track of the number of references to something, and not free the
-associated resource until the count is zero. The 'atomic' portion refers to an
+associated resource until the count is zero. The *atomic* portion refers to an
Arc's usage of concurrency primitives to atomically update the count, making it
safe across threads. If we use an Arc, we can have our three references. But,
an Arc does not allow mutable borrows of the data it holds, and we want to
with regards to concurrency. In order to share ownership, we were forced to be
explicit and use a mechanism to ensure that it would be properly handled.
-# Safety _and_ speed
+# Safety _and_ Speed
-Safety and speed are always presented as a continuum. On one hand, you have
-maximum speed, but no safety. On the other, you have absolute safety, with no
-speed. Rust seeks to break out of this mode by introducing safety at compile
-time, ensuring that you haven't done anything wrong, while compiling to the
-same low-level code you'd expect without the safety.
+Safety and speed are always presented as a continuum. At one end of the spectrum,
+you have maximum speed, but no safety. On the other end, you have absolute safety
+with no speed. Rust seeks to break out of this paradigm by introducing safety at
+compile time, ensuring that you haven't done anything wrong, while compiling to
+the same low-level code you'd expect without the safety.
As an example, Rust's ownership system is _entirely_ at compile time. The
safety check that makes this an error about moved values:
Some things that might be helpful to you though:
## Search
+
* <form action="https://duckduckgo.com/">
<input type="text" id="site-search" name="q" size="80"></input>
<input type="submit" value="Search DuckDuckGo">
* Rust doc search: <span id="core-search"></span>
## Reference
+
* [The Rust official site](http://rust-lang.org)
-* [The Rust reference](http://doc.rust-lang.org/reference.html) (* [PDF](http://doc.rust-lang.org/reference.pdf))
+* [The Rust reference](http://doc.rust-lang.org/reference.html)
## Docs
+
* [The standard library](http://doc.rust-lang.org/std/)
<script>
trait is in scope) to pointers to the trait name, used as a type.
```
-# use std::boxed::Box;
# trait Shape { }
# impl Shape for int { }
# let mycircle = 0i;
Likewise, supertrait methods may also be called on trait objects.
```{.ignore}
-# use std::boxed::Box;
# trait Shape { fn area(&self) -> f64; }
# trait Circle : Shape { fn radius(&self) -> f64; }
# impl Shape for int { fn area(&self) -> f64 { 0.0 } }
The following configurations must be defined by the implementation:
* `target_arch = "..."`. Target CPU architecture, such as `"x86"`, `"x86_64"`
- `"mips"`, `"arm"`, or `"aarch64"`.
+ `"mips"`, `"powerpc"`, `"arm"`, or `"aarch64"`.
* `target_endian = "..."`. Endianness of the target CPU, either `"little"` or
`"big"`.
* `target_family = "..."`. Operating system family of the target, e. g.
* `#[inline(always)]` asks the compiler to always perform an inline expansion.
* `#[inline(never)]` asks the compiler to never perform an inline expansion.
-### Deriving
+### Derive
-The `deriving` attribute allows certain traits to be automatically implemented
+The `derive` attribute allows certain traits to be automatically implemented
for data structures. For example, the following will create an `impl` for the
`PartialEq` and `Clone` traits for `Foo`, the type parameter `T` will be given
the `PartialEq` or `Clone` constraints for the appropriate `impl`:
```
-#[deriving(PartialEq, Clone)]
+#[derive(PartialEq, Clone)]
struct Foo<T> {
a: int,
b: T
}
```
-Supported traits for `deriving` are:
+Supported traits for `derive` are:
* Comparison traits: `PartialEq`, `Eq`, `PartialOrd`, `Ord`.
* Serialization: `Encodable`, `Decodable`. These require `serialize`.
### Unary operator expressions
-Rust defines six symbolic unary operators. They are all written as prefix
-operators, before the expression they apply to.
+Rust defines three unary operators. They are all written as prefix operators,
+before the expression they apply to.
* `-`
: Negation. May only be applied to numeric types.
: Logical negation. On the boolean type, this flips between `true` and
`false`. On integer types, this inverts the individual bits in the
two's complement representation of the value.
-* `box`
- : [Boxing](#pointer-types) operators. Allocate a box to hold the value they
- are applied to, and store the value in it. `box` creates a box.
-* `&`
- : Borrow operator. Returns a reference, pointing to its operand. The operand
- of a borrow is statically proven to outlive the resulting pointer. If the
- borrow-checker cannot prove this, it is a compilation error.
### Binary operator expressions
An example of a *recursive* type and its use:
```
-# use std::boxed::Box;
enum List<T> {
Nil,
Cons(T, Box<List<T>>)
An example of an object type:
```
-# use std::boxed::Box;
trait Printable {
fn stringify(&self) -> String;
}
An example of a box type and value:
```
-# use std::boxed::Box;
let x: Box<int> = Box::new(10);
```
the source location cannot be used unless it is reinitialized.
```
-# use std::boxed::Box;
let x: Box<int> = Box::new(10);
let y = x;
// attempting to use `x` will result in an error here
pre, code {
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", monospace;
+ word-wrap: break-word;
}
pre {
border-left: 2px solid #eee;
margin: 20px 0;
font-size: 13px;
word-break: break-all;
- word-wrap: break-word;
}
code {
padding: 0 2px;
table {
border-collapse: collapse;
border-spacing: 0;
+ overflow-x: auto;
+ display: block;
}
table tr.odd {
documentation examples. If a line is prefixed with `# `, then the line
will not show up in the HTML documentation, but it will be used when
testing the code block (NB. the space after the `#` is required, so
-that one can still write things like `#[deriving(Eq)]`).
+that one can still write things like `#[derive(Eq)]`).
~~~md
```
The documentation online would look like `spawn(move || { fib(200); })`, but when
testing this code, the `fib` function will be included (so it can compile).
+Rustdoc will automatically add a `main()` wrapper around your code, and in the right
+place. For example:
+
+```
+/// ```
+/// use std::rc::Rc;
+///
+/// let five = Rc::new(5);
+/// ```
+# fn foo() {}
+```
+
+This will end up testing:
+
+```
+fn main() {
+ use std::rc::Rc;
+ let five = Rc::new(5);
+}
+```
+
+Here's the full algorithm:
+
+1. Given a code block, if it does not contain `fn main`, it is wrapped in `fn main() { your_code }`
+2. Given that result, if it contains no `extern crate` directives but it also
+ contains the name of the crate being tested, then `extern crate <name>` is
+ injected at the top.
+3. Some common `allow` attributes are added for documentation examples at the top.
+
## Running tests (advanced)
Running tests often requires some special configuration to filter tests, find
"The Rust Programming Language" is split into three sections, which you can
navigate through the menu on the left.
-## Basics
+<h2 class="section-header"><a href="basic.html">Basics</a></h2>
This section is a linear introduction to the basic syntax and semantics of
Rust. It has individual sections on each part of Rust's syntax, and culminates
After reading "Basics," you will have a good foundation to learn more about
Rust, and can write very simple programs.
-## Intermediate
+<h2 class="section-header"><a href="intermediate.html">Intermediate</a></h2>
This section contains individual chapters, which are self-contained. They focus
on specific topics, and can be read in any order.
After reading "Intermediate," you will have a solid understanding of Rust,
and will be able to understand most Rust code and write more complex programs.
-## Advanced
+<h2 class="section-header"><a href="advanced.html">Advanced</a></h2>
In a similar fashion to "Intermediate," this section is full of individual,
deep-dive chapters, which stand alone and can be read in any order. These
* [Iterators](iterators.md)
* [Generics](generics.md)
* [Traits](traits.md)
- * [Tasks](tasks.md)
+ * [Threads](threads.md)
* [Error Handling](error-handling.md)
* [III: Advanced Topics](advanced.md)
* [FFI](ffi.md)
```{rust}
let a = [0, 1, 2, 3, 4];
-let middle = a.slice(1, 4); // A slice of a: just the elements [1,2,3]
+let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3
for e in middle.iter() {
println!("{}", e); // Prints 1, 2, 3
```{rust,ignore}
fn main() {
- let mut x = 5;
+ let mut x: i32 = 5;
let printer = |&:| { println!("x is: {}", x); };
There _is_ one case when a tuple struct is very useful, though, and that's a
tuple struct with only one element. We call this a *newtype*, because it lets
-you create a new type that's a synonym for another one:
+you create a new type that's similar to another one:
```{rust}
struct Inches(i32);
```
As you can see here, you can extract the inner integer type through a
-destructuring `let`.
+destructuring `let`, as we discussed previously in 'tuples.' In this case, the
+`let Inches(integer_length)` assigns `10` to `integer_length`.
## Enums
# Non-recoverable errors with `panic!`
In the case of an error that is unexpected and not recoverable, the `panic!`
-macro will induce a panic. This will crash the current task, and give an error:
+macro will induce a panic. This will crash the current thread, and give an error:
```{rust,ignore}
panic!("boom");
gives
```text
-task '<main>' panicked at 'boom', hello.rs:2
+thread '<main>' panicked at 'boom', hello.rs:2
```
when you run it.
# Stack management
-Rust tasks by default run on a *large stack*. This is actually implemented as a
+Rust threads by default run on a *large stack*. This is actually implemented as a
reserving a large segment of the address space and then lazily mapping in pages
as they are needed. When calling an external C function, the code is invoked on
the same stack as the rust stack. This means that there is no extra
stack-switching mechanism in place because it is assumed that the large stack
-for the rust task is plenty for the C function to have.
+for the rust thread is plenty for the C function to have.
A planned future improvement (not yet implemented at the time of this writing)
is to have a guard page at the end of every rust stack. No rust function will
need for any extra effort on a user's perspective. The C stack naturally
interleaves with the rust stack, and it's "large enough" for both to
interoperate. If, however, it is determined that a larger stack is necessary,
-there are appropriate functions in the task spawning API to control the size of
-the stack of the task which is spawned.
+there are appropriate functions in the thread spawning API to control the size of
+the stack of the thread which is spawned.
# Destructors
Rust code:
~~~~no_run
-# use std::boxed::Box;
-
#[repr(C)]
struct RustObject {
a: i32,
to a function call to the external C library.
The control over the current thread is switched from Rust to C to Rust for the
execution of the callback, but in the end the callback is executed on the
-same thread (and Rust task) that lead called the function which triggered
-the callback.
+same thread that called the function which triggered the callback.
Things get more complicated when the external library spawns its own threads
and invokes callbacks from there.
especially unsafe and proper synchronization mechanisms must be used.
Besides classical synchronization mechanisms like mutexes, one possibility in
Rust is to use channels (in `std::comm`) to forward data from the C thread
-that invoked the callback into a Rust task.
+that invoked the callback into a Rust thread.
If an asynchronous callback targets a special object in the Rust address space
it is also absolutely necessary that no more callbacks are performed by the
if we wanted to. Convention says that the first generic parameter should be
`T`, for 'type,' and that we use `E` for 'error.' Rust doesn't care, however.
-The `Result<T, E>` type is intended to
-be used to return the result of a computation, and to have the ability to
-return an error if it didn't work out. Here's an example:
+The `Result<T, E>` type is intended to be used to return the result of a
+computation, and to have the ability to return an error if it didn't work out.
+Here's an example:
```{rust}
let x: Result<f64, String> = Ok(2.3f64);
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
src/main.rs:20:15: 20:20 error: mismatched types: expected `i32` but found `collections::string::String` (expected i32 but found struct collections::string::String)
src/main.rs:20 match cmp(input, secret_number) {
^~~~~
-src/main.rs:20:22: 20:35 error: mismatched types: expected `i32` but found `uint` (expected i32 but found uint)
+src/main.rs:20:22: 20:35 error: mismatched types: expected `i32` but found `u32` (expected i32 but found u32)
src/main.rs:20 match cmp(input, secret_number) {
^~~~~~~~~~~~~
error: aborting due to 2 previous errors
strengths. You try out some code, see if it compiles, and Rust tells you that
you've done something wrong. In this case, our `cmp` function works on integers,
but we've given it unsigned integers. In this case, the fix is easy, because
-we wrote the `cmp` function! Let's change it to take `uint`s:
+we wrote the `cmp` function! Let's change it to take `u32`s:
```{rust,ignore}
use std::io;
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
}
}
-fn cmp(a: uint, b: uint) -> Ordering {
+fn cmp(a: u32, b: u32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
```bash
$ cargo build
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
-src/main.rs:20:15: 20:20 error: mismatched types: expected `uint` but found `collections::string::String` (expected uint but found struct collections::string::String)
+src/main.rs:20:15: 20:20 error: mismatched types: expected `u32` but found `collections::string::String` (expected u32 but found struct collections::string::String)
src/main.rs:20 match cmp(input, secret_number) {
^~~~~
error: aborting due to previous error
```
-This error is similar to the last one: we expected to get a `uint`, but we got
+This error is similar to the last one: we expected to get a `u32`, but we got
a `String` instead! That's because our `input` variable is coming from the
standard input, and you can guess anything. Try it:
This works because the older version we did successfully compile was still lying
around. Gotta be careful!
-Anyway, we have a `String`, but we need a `uint`. What to do? Well, there's
+Anyway, we have a `String`, but we need a `u32`. What to do? Well, there's
a function for that:
```{rust,ignore}
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
-let input_num: Option<uint> = input.parse();
+let input_num: Option<u32> = input.parse();
```
The `parse` function takes in a `&str` value and converts it into something.
`random()`? It looked like this:
```{rust,ignore}
-rand::random::<uint>();
+rand::random::<u32>();
```
There's an alternate way of providing a hint too, and that's declaring the type
in a `let`:
```{rust,ignore}
-let x: uint = rand::random();
+let x: u32 = rand::random();
```
-In this case, we say `x` is a `uint` explicitly, so Rust is able to properly
+In this case, we say `x` is a `u32` explicitly, so Rust is able to properly
tell `random()` what to generate. In a similar fashion, both of these work:
```{rust,ignore}
-let input_num = "5".parse::<uint>(); // input_num: Option<uint>
-let input_num: Option<uint> = "5".parse(); // input_num: Option<uint>
+let input_num = "5".parse::<u32>(); // input_num: Option<u32>
+let input_num: Option<u32> = "5".parse(); // input_num: Option<u32>
```
Anyway, with us now converting our input to a number, our code looks like this:
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
- let input_num: Option<uint> = input.parse();
+ let input_num: Option<u32> = input.parse();
println!("You guessed: {}", input_num);
}
}
-fn cmp(a: uint, b: uint) -> Ordering {
+fn cmp(a: u32, b: u32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
```bash
$ cargo build
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
-src/main.rs:22:15: 22:24 error: mismatched types: expected `uint` but found `core::option::Option<uint>` (expected uint but found enum core::option::Option)
+src/main.rs:22:15: 22:24 error: mismatched types: expected `u32` but found `core::option::Option<u32>` (expected u32 but found enum core::option::Option)
src/main.rs:22 match cmp(input_num, secret_number) {
^~~~~~~~~
error: aborting due to previous error
```
-Oh yeah! Our `input_num` has the type `Option<uint>`, rather than `uint`. We
+Oh yeah! Our `input_num` has the type `Option<u32>`, rather than `u32`. We
need to unwrap the Option. If you remember from before, `match` is a great way
to do that. Try this code:
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
- let input_num: Option<uint> = input.parse();
+ let input_num: Option<u32> = input.parse();
let num = match input_num {
Some(num) => num,
}
}
-fn cmp(a: uint, b: uint) -> Ordering {
+fn cmp(a: u32, b: u32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
}
```
-We use a `match` to either give us the `uint` inside of the `Option`, or else
+We use a `match` to either give us the `u32` inside of the `Option`, or else
print an error message and return. Let's give this a shot:
```bash
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
- let input_num: Option<uint> = input.trim().parse();
+ let input_num: Option<u32> = input.trim().parse();
let num = match input_num {
Some(num) => num,
}
}
-fn cmp(a: uint, b: uint) -> Ordering {
+fn cmp(a: u32, b: u32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
- let input_num: Option<uint> = input.trim().parse();
+ let input_num: Option<u32> = input.trim().parse();
let num = match input_num {
Some(num) => num,
}
}
-fn cmp(a: uint, b: uint) -> Ordering {
+fn cmp(a: u32, b: u32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
- let input_num: Option<uint> = input.trim().parse();
+ let input_num: Option<u32> = input.trim().parse();
let num = match input_num {
Some(num) => num,
}
}
-fn cmp(a: uint, b: uint) -> Ordering {
+fn cmp(a: u32, b: u32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
println!("The secret number is: {}", secret_number);
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
- let input_num: Option<uint> = input.trim().parse();
+ let input_num: Option<u32> = input.trim().parse();
let num = match input_num {
Some(num) => num,
}
}
-fn cmp(a: uint, b: uint) -> Ordering {
+fn cmp(a: u32, b: u32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
fn main() {
println!("Guess the number!");
- let secret_number = (rand::random::<uint>() % 100u) + 1u;
+ let secret_number = (rand::random::<u32>() % 100) + 1;
loop {
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
- let input_num: Option<uint> = input.trim().parse();
+ let input_num: Option<u32> = input.trim().parse();
let num = match input_num {
Some(num) => num,
}
}
-fn cmp(a: uint, b: uint) -> Ordering {
+fn cmp(a: u32, b: u32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
it's the beginning of every Rust program. The first line says "I'm declaring a
function named `main`, which takes no arguments and returns nothing." If there
were arguments, they would go inside the parentheses (`(` and `)`), and because
-we aren't returning anything from this function, we've dropped that notation
-entirely. We'll get to it later.
+we aren't returning anything from this function, we can omit the return type
+entirely. We'll get to it later.
You'll also note that the function is wrapped in curly braces (`{` and `}`).
Rust requires these around all function bodies. It is also considered good
# let input_1 = T::SpecialA(0);
# let input_2 = T::SpecialA(0);
macro_rules! early_return {
- ($inp:expr, $sp:path) => ( // invoke it like `(input_5 SpecialE)`
+ ($inp:expr, $sp:path) => ( // invoke it like `(input_5, SpecialE)`
match $inp {
$sp(x) => { return x; }
_ => {}
~~~~
Macros are defined in pattern-matching style: in the above example, the text
-`($inp:expr $sp:ident)` that appears on the left-hand side of the `=>` is the
+`($inp:expr, $sp:path)` that appears on the left-hand side of the `=>` is the
*macro invocation syntax*, a pattern denoting how to write a call to the
macro. The text on the right-hand side of the `=>`, beginning with `match
$inp`, is the *macro transcription syntax*: what the macro expands to.
2. `$` has special meaning (described below).
3. The `()`s, `[]`s, and `{}`s it contains must balance. For example, `([)` is
forbidden.
+4. Some arguments can be followed only by a limited set of separators, to
+avoid ambiguity (described below).
Otherwise, the invocation syntax is free-form.
`foo`.)
* `expr` (an expression. Examples: `2 + 2`; `if true then { 1 } else { 2 }`;
`f(42)`.)
-* `ty` (a type. Examples: `int`, `Vec<(char, String)>`, `&T`.)
+* `ty` (a type. Examples: `i32`, `Vec<(char, String)>`, `&T`.)
+* `path` (a path to struct or enum variant. Example: `T::SpecialA`)
* `pat` (a pattern, usually appearing in a `match` or on the left-hand side of
a declaration. Examples: `Some(t)`; `(17, 'a')`; `_`.)
* `block` (a sequence of actions. Example: `{ log(error, "hi"); return 12; }`)
So `($x:ident -> (($e:expr)))`, though excessively fancy, would designate a macro
that could be invoked like: `my_macro!(i->(( 2+2 )))`.
+To avoid ambiguity, macro invocation syntax must conform to the following rules:
+* `expr` must be followed by `=>`, `,` or `;`.
+* `ty` and `path` must be followed by `=>`, `,`, `:`, `=`, `>` or `as`.
+* `pat` must be followed by `=>`, `,` or `=`.
+* `ident` and `block` can be followed by any token.
+
## Invocation location
A macro invocation may take the place of (and therefore expand to) an
command-line argument to the compiler will show the result of expansion.
If Rust's macro system can't do what you need, you may want to write a
-[compiler plugin](plugin.html) instead. Compared to `macro_rules!`
+[compiler plugin](plugins.html) instead. Compared to `macro_rules!`
macros, this is significantly more work, the interfaces are much less stable,
and the warnings about debugging apply ten-fold. In exchange you get the
flexibility of running arbitrary Rust code within the compiler. Syntax
Rust:
```rust
-# use std::boxed::Box;
{
let x = Box::new(5);
}
to a function? Let's look at some code:
```rust
-# use std::boxed::Box;
fn main() {
let x = Box::new(5);
code, where we print out the value of `x`:
```{rust,ignore}
-# use std::boxed::Box;
fn main() {
let x = Box::new(5);
box:
```rust
-# use std::boxed::Box;
fn main() {
let x = Box::new(5);
This function borrows an `i32` from its caller, and then increments it. When
the function is over, and `num` goes out of scope, the borrow is over.
+We have to change our `main` a bit too:
+
+```rust
+fn main() {
+ let mut x = 5;
+
+ add_one(&mut x);
+
+ println!("{}", x);
+}
+
+fn add_one(num: &mut i32) {
+ *num += 1;
+}
+```
+
+We don't need to assign the result of `add_one()` anymore, because it doesn't
+return anything anymore. This is because we're not passing ownership back,
+since we just borrow, not take ownership.
+
# Lifetimes
Lending out a reference to a resource that someone else owns can be
three. The ownership system in Rust does this through a concept called
*lifetimes*, which describe the scope that a reference is valid for.
-Let's look at that function which borrows an `i32` again:
+Remember the function that borrowed an `i32`? Let's look at it again.
```rust
fn add_one(num: &i32) -> i32 {
a way to define new literal syntax for any data type.
In addition to procedural macros, you can define new
-[`deriving`](../reference.html#deriving)-like attributes and other kinds of
+[`derive`](../reference.html#derive)-like attributes and other kinds of
extensions. See
[`Registry::register_syntax_extension`](../rustc/plugin/registry/struct.Registry.html#method.register_syntax_extension)
and the [`SyntaxExtension`
Note that the caller of your function will have to modify their calls slightly:
```{rust}
-# use std::boxed::Box;
use std::rc::Rc;
fn succ(x: &int) -> int { *x + 1 }
heap allocation in Rust. Creating a box looks like this:
```{rust}
-# use std::boxed::Box;
let x = Box::new(5i);
```
they go out of scope:
```{rust}
-# use std::boxed::Box;
{
let x = Box::new(5i);
boxes, though. As a rough approximation, you can treat this Rust code:
```{rust}
-# use std::boxed::Box;
{
let x = Box::new(5i);
Using boxes and references together is very common. For example:
```{rust}
-# use std::boxed::Box;
fn add_one(x: &int) -> int {
*x + 1
}
We can borrow `x` multiple times, as long as it's not simultaneous:
```{rust}
-# use std::boxed::Box;
fn add_one(x: &int) -> int {
*x + 1
}
Or as long as it's not a mutable borrow. This will error:
```{rust,ignore}
-# use std::boxed::Box;
fn add_one(x: &mut int) -> int {
*x + 1
}
```{rust}
-# use std::boxed::Box;
#[derive(Show)]
enum List<T> {
Cons(T, Box<List<T>>),
so as to avoid copying a large data structure. For example:
```{rust}
-# use std::boxed::Box;
struct BigStruct {
one: int,
two: int,
This is an antipattern in Rust. Instead, write this:
```{rust}
-# use std::boxed::Box;
struct BigStruct {
one: int,
two: int,
This gives you flexibility without sacrificing performance.
You may think that this gives us terrible performance: return a value and then
-immediately box it up ?! Isn't that the worst of both worlds? Rust is smarter
-than that. There is no copy in this code. `main` allocates enough room for the
-`box`, passes a pointer to that memory into `foo` as `x`, and then `foo` writes
-the value straight into that pointer. This writes the return value directly into
-the allocated box.
+immediately box it up ?! Isn't this pattern the worst of both worlds? Rust is
+smarter than that. There is no copy in this code. `main` allocates enough room
+for the `box`, passes a pointer to that memory into `foo` as `x`, and then
+`foo` writes the value straight into the `Box<T>`.
This is important enough that it bears repeating: pointers are not for
optimizing returning values from your code. Allow the caller to choose how they
+++ /dev/null
-% The Rust Threads and Communication Guide
-
-**NOTE** This guide is badly out of date and needs to be rewritten.
-
-# Introduction
-
-Rust provides safe concurrent abstractions through a number of core library
-primitives. This guide will describe the concurrency model in Rust, how it
-relates to the Rust type system, and introduce the fundamental library
-abstractions for constructing concurrent programs.
-
-Threads provide failure isolation and recovery. When a fatal error occurs in Rust
-code as a result of an explicit call to `panic!()`, an assertion failure, or
-another invalid operation, the runtime system destroys the entire thread. Unlike
-in languages such as Java and C++, there is no way to `catch` an exception.
-Instead, threads may monitor each other to see if they panic.
-
-Threads use Rust's type system to provide strong memory safety guarantees. In
-particular, the type system guarantees that threads cannot induce a data race
-from shared mutable state.
-
-# Basics
-
-At its simplest, creating a thread is a matter of calling the `spawn` function
-with a closure argument. `spawn` executes the closure in the new thread.
-
-```{rust,ignore}
-# use std::thread::spawn;
-
-// Print something profound in a different thread using a named function
-fn print_message() { println!("I am running in a different thread!"); }
-spawn(print_message);
-
-// Alternatively, use a `move ||` expression instead of a named function.
-// `||` expressions evaluate to an unnamed closure. The `move` keyword
-// indicates that the closure should take ownership of any variables it
-// touches.
-spawn(move || println!("I am also running in a different thread!"));
-```
-
-In Rust, a thread is not a concept that appears in the language semantics.
-Instead, Rust's type system provides all the tools necessary to implement safe
-concurrency: particularly, ownership. The language leaves the implementation
-details to the standard library.
-
-The `spawn` function has the type signature: `fn
-spawn<F:FnOnce()+Send>(f: F)`. This indicates that it takes as
-argument a closure (of type `F`) that it will run exactly once. This
-closure is limited to capturing `Send`-able data from its environment
-(that is, data which is deeply owned). Limiting the closure to `Send`
-ensures that `spawn` can safely move the entire closure and all its
-associated state into an entirely different thread for execution.
-
-```{rust,ignore}
-# use std::thread::spawn;
-# fn generate_thread_number() -> int { 0 }
-// Generate some state locally
-let child_thread_number = generate_thread_number();
-
-spawn(move || {
- // Capture it in the remote thread. The `move` keyword indicates
- // that this closure should move `child_thread_number` into its
- // environment, rather than capturing a reference into the
- // enclosing stack frame.
- println!("I am child number {}", child_thread_number);
-});
-```
-
-## Communication
-
-Now that we have spawned a new thread, it would be nice if we could communicate
-with it. For this, we use *channels*. A channel is simply a pair of endpoints:
-one for sending messages and another for receiving messages.
-
-The simplest way to create a channel is to use the `channel` function to create a
-`(Sender, Receiver)` pair. In Rust parlance, a *sender* is a sending endpoint
-of a channel, and a *receiver* is the receiving endpoint. Consider the following
-example of calculating two results concurrently:
-
-```{rust,ignore}
-# use std::thread::spawn;
-
-let (tx, rx): (Sender<int>, Receiver<int>) = channel();
-
-spawn(move || {
- let result = some_expensive_computation();
- tx.send(result);
-});
-
-some_other_expensive_computation();
-let result = rx.recv();
-# fn some_expensive_computation() -> int { 42 }
-# fn some_other_expensive_computation() {}
-```
-
-Let's examine this example in detail. First, the `let` statement creates a
-stream for sending and receiving integers (the left-hand side of the `let`,
-`(tx, rx)`, is an example of a destructuring let: the pattern separates a tuple
-into its component parts).
-
-```{rust,ignore}
-let (tx, rx): (Sender<int>, Receiver<int>) = channel();
-```
-
-The child thread will use the sender to send data to the parent thread, which will
-wait to receive the data on the receiver. The next statement spawns the child
-thread.
-
-```{rust,ignore}
-# use std::thread::spawn;
-# fn some_expensive_computation() -> int { 42 }
-# let (tx, rx) = channel();
-spawn(move || {
- let result = some_expensive_computation();
- tx.send(result);
-});
-```
-
-Notice that the creation of the thread closure transfers `tx` to the child thread
-implicitly: the closure captures `tx` in its environment. Both `Sender` and
-`Receiver` are sendable types and may be captured into threads or otherwise
-transferred between them. In the example, the child thread runs an expensive
-computation, then sends the result over the captured channel.
-
-Finally, the parent continues with some other expensive computation, then waits
-for the child's result to arrive on the receiver:
-
-```{rust,ignore}
-# fn some_other_expensive_computation() {}
-# let (tx, rx) = channel::<int>();
-# tx.send(0);
-some_other_expensive_computation();
-let result = rx.recv();
-```
-
-The `Sender` and `Receiver` pair created by `channel` enables efficient
-communication between a single sender and a single receiver, but multiple
-senders cannot use a single `Sender` value, and multiple receivers cannot use a
-single `Receiver` value. What if our example needed to compute multiple
-results across a number of threads? The following program is ill-typed:
-
-```{rust,ignore}
-# fn some_expensive_computation() -> int { 42 }
-let (tx, rx) = channel();
-
-spawn(move || {
- tx.send(some_expensive_computation());
-});
-
-// ERROR! The previous spawn statement already owns the sender,
-// so the compiler will not allow it to be captured again
-spawn(move || {
- tx.send(some_expensive_computation());
-});
-```
-
-Instead we can clone the `tx`, which allows for multiple senders.
-
-```{rust,ignore}
-let (tx, rx) = channel();
-
-for init_val in range(0u, 3) {
- // Create a new channel handle to distribute to the child thread
- let child_tx = tx.clone();
- spawn(move || {
- child_tx.send(some_expensive_computation(init_val));
- });
-}
-
-let result = rx.recv() + rx.recv() + rx.recv();
-# fn some_expensive_computation(_i: uint) -> int { 42 }
-```
-
-Cloning a `Sender` produces a new handle to the same channel, allowing multiple
-threads to send data to a single receiver. It upgrades the channel internally in
-order to allow this functionality, which means that channels that are not
-cloned can avoid the overhead required to handle multiple senders. But this
-fact has no bearing on the channel's usage: the upgrade is transparent.
-
-Note that the above cloning example is somewhat contrived since you could also
-simply use three `Sender` pairs, but it serves to illustrate the point. For
-reference, written with multiple streams, it might look like the example below.
-
-```{rust,ignore}
-# use std::thread::spawn;
-
-// Create a vector of ports, one for each child thread
-let rxs = Vec::from_fn(3, |init_val| {
- let (tx, rx) = channel();
- spawn(move || {
- tx.send(some_expensive_computation(init_val));
- });
- rx
-});
-
-// Wait on each port, accumulating the results
-let result = rxs.iter().fold(0, |accum, rx| accum + rx.recv() );
-# fn some_expensive_computation(_i: uint) -> int { 42 }
-```
-
-## Backgrounding computations: Futures
-
-With `sync::Future`, rust has a mechanism for requesting a computation and
-getting the result later.
-
-The basic example below illustrates this.
-
-```{rust,ignore}
-# #![allow(deprecated)]
-use std::sync::Future;
-
-# fn main() {
-# fn make_a_sandwich() {};
-fn fib(n: u64) -> u64 {
- // lengthy computation returning an uint
- 12586269025
-}
-
-let mut delayed_fib = Future::spawn(move || fib(50));
-make_a_sandwich();
-println!("fib(50) = {}", delayed_fib.get())
-# }
-```
-
-The call to `future::spawn` immediately returns a `future` object regardless of
-how long it takes to run `fib(50)`. You can then make yourself a sandwich while
-the computation of `fib` is running. The result of the execution of the method
-is obtained by calling `get` on the future. This call will block until the
-value is available (*i.e.* the computation is complete). Note that the future
-needs to be mutable so that it can save the result for next time `get` is
-called.
-
-Here is another example showing how futures allow you to background
-computations. The workload will be distributed on the available cores.
-
-```{rust,ignore}
-# #![allow(deprecated)]
-# use std::num::Float;
-# use std::sync::Future;
-fn partial_sum(start: uint) -> f64 {
- let mut local_sum = 0f64;
- for num in range(start*100000, (start+1)*100000) {
- local_sum += (num as f64 + 1.0).powf(-2.0);
- }
- local_sum
-}
-
-fn main() {
- let mut futures = Vec::from_fn(200, |ind| Future::spawn(move || partial_sum(ind)));
-
- let mut final_res = 0f64;
- for ft in futures.iter_mut() {
- final_res += ft.get();
- }
- println!("π^2/6 is not far from : {}", final_res);
-}
-```
-
-## Sharing without copying: Arc
-
-To share data between threads, a first approach would be to only use channel as
-we have seen previously. A copy of the data to share would then be made for
-each thread. In some cases, this would add up to a significant amount of wasted
-memory and would require copying the same data more than necessary.
-
-To tackle this issue, one can use an Atomically Reference Counted wrapper
-(`Arc`) as implemented in the `sync` library of Rust. With an Arc, the data
-will no longer be copied for each thread. The Arc acts as a reference to the
-shared data and only this reference is shared and cloned.
-
-Here is a small example showing how to use Arcs. We wish to run concurrently
-several computations on a single large vector of floats. Each thread needs the
-full vector to perform its duty.
-
-```{rust,ignore}
-use std::num::Float;
-use std::rand;
-use std::sync::Arc;
-
-fn pnorm(nums: &[f64], p: uint) -> f64 {
- nums.iter().fold(0.0, |a, b| a + b.powf(p as f64)).powf(1.0 / (p as f64))
-}
-
-fn main() {
- let numbers = Vec::from_fn(1000000, |_| rand::random::<f64>());
- let numbers_arc = Arc::new(numbers);
-
- for num in range(1u, 10) {
- let thread_numbers = numbers_arc.clone();
-
- spawn(move || {
- println!("{}-norm = {}", num, pnorm(thread_numbers.as_slice(), num));
- });
- }
-}
-```
-
-The function `pnorm` performs a simple computation on the vector (it computes
-the sum of its items at the power given as argument and takes the inverse power
-of this value). The Arc on the vector is created by the line:
-
-```{rust,ignore}
-# use std::rand;
-# use std::sync::Arc;
-# fn main() {
-# let numbers = Vec::from_fn(1000000, |_| rand::random::<f64>());
-let numbers_arc = Arc::new(numbers);
-# }
-```
-
-and a clone is captured for each thread via a procedure. This only copies
-the wrapper and not its contents. Within the thread's procedure, the captured
-Arc reference can be used as a shared reference to the underlying vector as
-if it were local.
-
-```{rust,ignore}
-# use std::rand;
-# use std::sync::Arc;
-# fn pnorm(nums: &[f64], p: uint) -> f64 { 4.0 }
-# fn main() {
-# let numbers=Vec::from_fn(1000000, |_| rand::random::<f64>());
-# let numbers_arc = Arc::new(numbers);
-# let num = 4;
-let thread_numbers = numbers_arc.clone();
-spawn(move || {
- // Capture thread_numbers and use it as if it was the underlying vector
- println!("{}-norm = {}", num, pnorm(thread_numbers.as_slice(), num));
-});
-# }
-```
-
-# Handling thread panics
-
-Rust has a built-in mechanism for raising exceptions. The `panic!()` macro
-(which can also be written with an error string as an argument: `panic!(
-~reason)`) and the `assert!` construct (which effectively calls `panic!()` if a
-boolean expression is false) are both ways to raise exceptions. When a thread
-raises an exception, the thread unwinds its stack—running destructors and
-freeing memory along the way—and then exits. Unlike exceptions in C++,
-exceptions in Rust are unrecoverable within a single thread: once a thread panics,
-there is no way to "catch" the exception.
-
-While it isn't possible for a thread to recover from panicking, threads may notify
-each other if they panic. The simplest way of handling a panic is with the
-`try` function, which is similar to `spawn`, but immediately blocks and waits
-for the child thread to finish. `try` returns a value of type
-`Result<T, Box<Any + Send>>`. `Result` is an `enum` type with two variants:
-`Ok` and `Err`. In this case, because the type arguments to `Result` are `int`
-and `()`, callers can pattern-match on a result to check whether it's an `Ok`
-result with an `int` field (representing a successful result) or an `Err` result
-(representing termination with an error).
-
-```{rust,ignore}
-# use std::thread::Thread;
-# fn some_condition() -> bool { false }
-# fn calculate_result() -> int { 0 }
-let result: Result<int, Box<std::any::Any + Send>> = Thread::spawn(move || {
- if some_condition() {
- calculate_result()
- } else {
- panic!("oops!");
- }
-}).join();
-assert!(result.is_err());
-```
-
-Unlike `spawn`, the function spawned using `try` may return a value, which
-`try` will dutifully propagate back to the caller in a [`Result`] enum. If the
-child thread terminates successfully, `try` will return an `Ok` result; if the
-child thread panics, `try` will return an `Error` result.
-
-[`Result`]: ../std/result/index.html
-
-> *Note:* A panicked thread does not currently produce a useful error
-> value (`try` always returns `Err(())`). In the
-> future, it may be possible for threads to intercept the value passed to
-> `panic!()`.
-
-But not all panics are created equal. In some cases you might need to abort
-the entire program (perhaps you're writing an assert which, if it trips,
-indicates an unrecoverable logic error); in other cases you might want to
-contain the panic at a certain boundary (perhaps a small piece of input from
-the outside world, which you happen to be processing in parallel, is malformed
-such that the processing thread cannot proceed).
failures:
---- it_works stdout ----
- task 'it_works' panicked at 'assertion failed: false', /home/steve/tmp/adder/src/lib.rs:3
+ thread 'it_works' panicked at 'assertion failed: false', /home/steve/tmp/adder/src/lib.rs:3
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
-task '<main>' panicked at 'Some tests failed', /home/steve/src/rust/src/libtest/lib.rs:247
+thread '<main>' panicked at 'Some tests failed', /home/steve/src/rust/src/libtest/lib.rs:247
```
Rust indicates that our test failed:
our `src/lib.rs` to make use of it:
```{rust,ignore}
-#![feature(globs)]
pub fn add_two(a: i32) -> i32 {
a + 2
}
```
-Note the `feature` attribute, as well as the different `use` line. Now we run
-our tests:
+Note the different `use` line. Now we run our tests:
```bash
$ cargo test
//! assert_eq!(4, adder::add_two(2));
//! ```
-#![feature(globs)]
-
/// This function adds two to its argument.
///
/// # Examples
code. Let's make our `src/lib.rs` look like this (comments elided):
```{rust,ignore}
-#![feature(globs)]
-
extern crate test;
pub fn add_two(a: i32) -> i32 {
--- /dev/null
+% The Rust Threads and Communication Guide
+
+**NOTE** This guide is badly out of date and needs to be rewritten.
+
+# Introduction
+
+Rust provides safe concurrent abstractions through a number of core library
+primitives. This guide will describe the concurrency model in Rust, how it
+relates to the Rust type system, and introduce the fundamental library
+abstractions for constructing concurrent programs.
+
+Threads provide failure isolation and recovery. When a fatal error occurs in Rust
+code as a result of an explicit call to `panic!()`, an assertion failure, or
+another invalid operation, the runtime system destroys the entire thread. Unlike
+in languages such as Java and C++, there is no way to `catch` an exception.
+Instead, threads may monitor each other to see if they panic.
+
+Threads use Rust's type system to provide strong memory safety guarantees. In
+particular, the type system guarantees that threads cannot induce a data race
+from shared mutable state.
+
+# Basics
+
+At its simplest, creating a thread is a matter of calling the `spawn` function
+with a closure argument. `spawn` executes the closure in the new thread.
+
+```{rust,ignore}
+# use std::thread::spawn;
+
+// Print something profound in a different thread using a named function
+fn print_message() { println!("I am running in a different thread!"); }
+spawn(print_message);
+
+// Alternatively, use a `move ||` expression instead of a named function.
+// `||` expressions evaluate to an unnamed closure. The `move` keyword
+// indicates that the closure should take ownership of any variables it
+// touches.
+spawn(move || println!("I am also running in a different thread!"));
+```
+
+In Rust, a thread is not a concept that appears in the language semantics.
+Instead, Rust's type system provides all the tools necessary to implement safe
+concurrency: particularly, ownership. The language leaves the implementation
+details to the standard library.
+
+The `spawn` function has the type signature: `fn
+spawn<F:FnOnce()+Send>(f: F)`. This indicates that it takes as
+argument a closure (of type `F`) that it will run exactly once. This
+closure is limited to capturing `Send`-able data from its environment
+(that is, data which is deeply owned). Limiting the closure to `Send`
+ensures that `spawn` can safely move the entire closure and all its
+associated state into an entirely different thread for execution.
+
+```{rust,ignore}
+# use std::thread::spawn;
+# fn generate_thread_number() -> int { 0 }
+// Generate some state locally
+let child_thread_number = generate_thread_number();
+
+spawn(move || {
+ // Capture it in the remote thread. The `move` keyword indicates
+ // that this closure should move `child_thread_number` into its
+ // environment, rather than capturing a reference into the
+ // enclosing stack frame.
+ println!("I am child number {}", child_thread_number);
+});
+```
+
+## Communication
+
+Now that we have spawned a new thread, it would be nice if we could communicate
+with it. For this, we use *channels*. A channel is simply a pair of endpoints:
+one for sending messages and another for receiving messages.
+
+The simplest way to create a channel is to use the `channel` function to create a
+`(Sender, Receiver)` pair. In Rust parlance, a *sender* is a sending endpoint
+of a channel, and a *receiver* is the receiving endpoint. Consider the following
+example of calculating two results concurrently:
+
+```{rust,ignore}
+# use std::thread::spawn;
+
+let (tx, rx): (Sender<int>, Receiver<int>) = channel();
+
+spawn(move || {
+ let result = some_expensive_computation();
+ tx.send(result);
+});
+
+some_other_expensive_computation();
+let result = rx.recv();
+# fn some_expensive_computation() -> int { 42 }
+# fn some_other_expensive_computation() {}
+```
+
+Let's examine this example in detail. First, the `let` statement creates a
+stream for sending and receiving integers (the left-hand side of the `let`,
+`(tx, rx)`, is an example of a destructuring let: the pattern separates a tuple
+into its component parts).
+
+```{rust,ignore}
+let (tx, rx): (Sender<int>, Receiver<int>) = channel();
+```
+
+The child thread will use the sender to send data to the parent thread, which will
+wait to receive the data on the receiver. The next statement spawns the child
+thread.
+
+```{rust,ignore}
+# use std::thread::spawn;
+# fn some_expensive_computation() -> int { 42 }
+# let (tx, rx) = channel();
+spawn(move || {
+ let result = some_expensive_computation();
+ tx.send(result);
+});
+```
+
+Notice that the creation of the thread closure transfers `tx` to the child thread
+implicitly: the closure captures `tx` in its environment. Both `Sender` and
+`Receiver` are sendable types and may be captured into threads or otherwise
+transferred between them. In the example, the child thread runs an expensive
+computation, then sends the result over the captured channel.
+
+Finally, the parent continues with some other expensive computation, then waits
+for the child's result to arrive on the receiver:
+
+```{rust,ignore}
+# fn some_other_expensive_computation() {}
+# let (tx, rx) = channel::<int>();
+# tx.send(0);
+some_other_expensive_computation();
+let result = rx.recv();
+```
+
+The `Sender` and `Receiver` pair created by `channel` enables efficient
+communication between a single sender and a single receiver, but multiple
+senders cannot use a single `Sender` value, and multiple receivers cannot use a
+single `Receiver` value. What if our example needed to compute multiple
+results across a number of threads? The following program is ill-typed:
+
+```{rust,ignore}
+# fn some_expensive_computation() -> int { 42 }
+let (tx, rx) = channel();
+
+spawn(move || {
+ tx.send(some_expensive_computation());
+});
+
+// ERROR! The previous spawn statement already owns the sender,
+// so the compiler will not allow it to be captured again
+spawn(move || {
+ tx.send(some_expensive_computation());
+});
+```
+
+Instead we can clone the `tx`, which allows for multiple senders.
+
+```{rust,ignore}
+let (tx, rx) = channel();
+
+for init_val in range(0u, 3) {
+ // Create a new channel handle to distribute to the child thread
+ let child_tx = tx.clone();
+ spawn(move || {
+ child_tx.send(some_expensive_computation(init_val));
+ });
+}
+
+let result = rx.recv() + rx.recv() + rx.recv();
+# fn some_expensive_computation(_i: uint) -> int { 42 }
+```
+
+Cloning a `Sender` produces a new handle to the same channel, allowing multiple
+threads to send data to a single receiver. It upgrades the channel internally in
+order to allow this functionality, which means that channels that are not
+cloned can avoid the overhead required to handle multiple senders. But this
+fact has no bearing on the channel's usage: the upgrade is transparent.
+
+Note that the above cloning example is somewhat contrived since you could also
+simply use three `Sender` pairs, but it serves to illustrate the point. For
+reference, written with multiple streams, it might look like the example below.
+
+```{rust,ignore}
+# use std::thread::spawn;
+
+// Create a vector of ports, one for each child thread
+let rxs = Vec::from_fn(3, |init_val| {
+ let (tx, rx) = channel();
+ spawn(move || {
+ tx.send(some_expensive_computation(init_val));
+ });
+ rx
+});
+
+// Wait on each port, accumulating the results
+let result = rxs.iter().fold(0, |accum, rx| accum + rx.recv() );
+# fn some_expensive_computation(_i: uint) -> int { 42 }
+```
+
+## Backgrounding computations: Futures
+
+With `sync::Future`, rust has a mechanism for requesting a computation and
+getting the result later.
+
+The basic example below illustrates this.
+
+```{rust,ignore}
+# #![allow(deprecated)]
+use std::sync::Future;
+
+# fn main() {
+# fn make_a_sandwich() {};
+fn fib(n: u64) -> u64 {
+ // lengthy computation returning an uint
+ 12586269025
+}
+
+let mut delayed_fib = Future::spawn(move || fib(50));
+make_a_sandwich();
+println!("fib(50) = {}", delayed_fib.get())
+# }
+```
+
+The call to `future::spawn` immediately returns a `future` object regardless of
+how long it takes to run `fib(50)`. You can then make yourself a sandwich while
+the computation of `fib` is running. The result of the execution of the method
+is obtained by calling `get` on the future. This call will block until the
+value is available (*i.e.* the computation is complete). Note that the future
+needs to be mutable so that it can save the result for next time `get` is
+called.
+
+Here is another example showing how futures allow you to background
+computations. The workload will be distributed on the available cores.
+
+```{rust,ignore}
+# #![allow(deprecated)]
+# use std::num::Float;
+# use std::sync::Future;
+fn partial_sum(start: uint) -> f64 {
+ let mut local_sum = 0f64;
+ for num in range(start*100000, (start+1)*100000) {
+ local_sum += (num as f64 + 1.0).powf(-2.0);
+ }
+ local_sum
+}
+
+fn main() {
+ let mut futures = Vec::from_fn(200, |ind| Future::spawn(move || partial_sum(ind)));
+
+ let mut final_res = 0f64;
+ for ft in futures.iter_mut() {
+ final_res += ft.get();
+ }
+ println!("π^2/6 is not far from : {}", final_res);
+}
+```
+
+## Sharing without copying: Arc
+
+To share data between threads, a first approach would be to only use channel as
+we have seen previously. A copy of the data to share would then be made for
+each thread. In some cases, this would add up to a significant amount of wasted
+memory and would require copying the same data more than necessary.
+
+To tackle this issue, one can use an Atomically Reference Counted wrapper
+(`Arc`) as implemented in the `sync` library of Rust. With an Arc, the data
+will no longer be copied for each thread. The Arc acts as a reference to the
+shared data and only this reference is shared and cloned.
+
+Here is a small example showing how to use Arcs. We wish to run concurrently
+several computations on a single large vector of floats. Each thread needs the
+full vector to perform its duty.
+
+```{rust,ignore}
+use std::num::Float;
+use std::rand;
+use std::sync::Arc;
+
+fn pnorm(nums: &[f64], p: uint) -> f64 {
+ nums.iter().fold(0.0, |a, b| a + b.powf(p as f64)).powf(1.0 / (p as f64))
+}
+
+fn main() {
+ let numbers = Vec::from_fn(1000000, |_| rand::random::<f64>());
+ let numbers_arc = Arc::new(numbers);
+
+ for num in range(1u, 10) {
+ let thread_numbers = numbers_arc.clone();
+
+ spawn(move || {
+ println!("{}-norm = {}", num, pnorm(thread_numbers.as_slice(), num));
+ });
+ }
+}
+```
+
+The function `pnorm` performs a simple computation on the vector (it computes
+the sum of its items at the power given as argument and takes the inverse power
+of this value). The Arc on the vector is created by the line:
+
+```{rust,ignore}
+# use std::rand;
+# use std::sync::Arc;
+# fn main() {
+# let numbers = Vec::from_fn(1000000, |_| rand::random::<f64>());
+let numbers_arc = Arc::new(numbers);
+# }
+```
+
+and a clone is captured for each thread via a procedure. This only copies
+the wrapper and not its contents. Within the thread's procedure, the captured
+Arc reference can be used as a shared reference to the underlying vector as
+if it were local.
+
+```{rust,ignore}
+# use std::rand;
+# use std::sync::Arc;
+# fn pnorm(nums: &[f64], p: uint) -> f64 { 4.0 }
+# fn main() {
+# let numbers=Vec::from_fn(1000000, |_| rand::random::<f64>());
+# let numbers_arc = Arc::new(numbers);
+# let num = 4;
+let thread_numbers = numbers_arc.clone();
+spawn(move || {
+ // Capture thread_numbers and use it as if it was the underlying vector
+ println!("{}-norm = {}", num, pnorm(thread_numbers.as_slice(), num));
+});
+# }
+```
+
+# Handling thread panics
+
+Rust has a built-in mechanism for raising exceptions. The `panic!()` macro
+(which can also be written with an error string as an argument: `panic!(
+~reason)`) and the `assert!` construct (which effectively calls `panic!()` if a
+boolean expression is false) are both ways to raise exceptions. When a thread
+raises an exception, the thread unwinds its stack—running destructors and
+freeing memory along the way—and then exits. Unlike exceptions in C++,
+exceptions in Rust are unrecoverable within a single thread: once a thread panics,
+there is no way to "catch" the exception.
+
+While it isn't possible for a thread to recover from panicking, threads may notify
+each other if they panic. The simplest way of handling a panic is with the
+`try` function, which is similar to `spawn`, but immediately blocks and waits
+for the child thread to finish. `try` returns a value of type
+`Result<T, Box<Any + Send>>`. `Result` is an `enum` type with two variants:
+`Ok` and `Err`. In this case, because the type arguments to `Result` are `int`
+and `()`, callers can pattern-match on a result to check whether it's an `Ok`
+result with an `int` field (representing a successful result) or an `Err` result
+(representing termination with an error).
+
+```{rust,ignore}
+# use std::thread::Thread;
+# fn some_condition() -> bool { false }
+# fn calculate_result() -> int { 0 }
+let result: Result<int, Box<std::any::Any + Send>> = Thread::spawn(move || {
+ if some_condition() {
+ calculate_result()
+ } else {
+ panic!("oops!");
+ }
+}).join();
+assert!(result.is_err());
+```
+
+Unlike `spawn`, the function spawned using `try` may return a value, which
+`try` will dutifully propagate back to the caller in a [`Result`] enum. If the
+child thread terminates successfully, `try` will return an `Ok` result; if the
+child thread panics, `try` will return an `Error` result.
+
+[`Result`]: ../std/result/index.html
+
+> *Note:* A panicked thread does not currently produce a useful error
+> value (`try` always returns `Err(())`). In the
+> future, it may be possible for threads to intercept the value passed to
+> `panic!()`.
+
+But not all panics are created equal. In some cases you might need to abort
+the entire program (perhaps you're writing an assert which, if it trips,
+indicates an unrecoverable logic error); in other cases you might want to
+contain the panic at a certain boundary (perhaps a small piece of input from
+the outside world, which you happen to be processing in parallel, is malformed
+such that the processing thread cannot proceed).
as you can see, there's no overhead of deciding which version to call here,
hence *statically dispatched*. The downside is that we have two copies of
the same function, so our binary is a little bit larger.
+
+## Our `inverse` Example
+
+Back in [Generics](generics.html), we were trying to write code like this:
+
+```{rust,ignore}
+fn inverse<T>(x: T) -> Result<T, String> {
+ if x == 0.0 { return Err("x cannot be zero!".to_string()); }
+
+ Ok(1.0 / x)
+}
+```
+
+If we try to compile it, we get this error:
+
+```text
+error: binary operation `==` cannot be applied to type `T`
+```
+
+This is because `T` is too generic: we don't know if a random `T` can be
+compared. For that, we can use trait bounds. It doesn't quite work, but try
+this:
+
+```{rust,ignore}
+fn inverse<T: PartialEq>(x: T) -> Result<T, String> {
+ if x == 0.0 { return Err("x cannot be zero!".to_string()); }
+
+ Ok(1.0 / x)
+}
+```
+
+You should get this error:
+
+```text
+error: mismatched types:
+ expected `T`,
+ found `_`
+(expected type parameter,
+ found floating-point variable)
+```
+
+So this won't work. While our `T` is `PartialEq`, we expected to have another `T`,
+but instead, we found a floating-point variable. We need a different bound. `Float`
+to the rescue:
+
+```
+use std::num::Float;
+
+fn inverse<T: Float>(x: T) -> Result<T, String> {
+ if x == Float::zero() { return Err("x cannot be zero!".to_string()) }
+
+ let one: T = Float::one();
+ Ok(one / x)
+}
+```
+
+We've had to replace our generic `0.0` and `1.0` with the appropriate methods
+from the `Float` trait. Both `f32` and `f64` implement `Float`, so our function
+works just fine:
+
+```
+# use std::num::Float;
+# fn inverse<T: Float>(x: T) -> Result<T, String> {
+# if x == Float::zero() { return Err("x cannot be zero!".to_string()) }
+# let one: T = Float::one();
+# Ok(one / x)
+# }
+println!("the inverse of {} is {:?}", 2.0f32, inverse(2.0f32));
+println!("the inverse of {} is {:?}", 2.0f64, inverse(2.0f64));
+
+println!("the inverse of {} is {:?}", 0.0f32, inverse(0.0f32));
+println!("the inverse of {} is {:?}", 0.0f64, inverse(0.0f64));
+```
- implement the `Drop` for resource clean-up via a destructor, and use
RAII (Resource Acquisition Is Initialization). This reduces the need
for any manual memory management by users, and automatically ensures
- that clean-up is always run, even when the task panics.
+ that clean-up is always run, even when the thread panics.
- ensure that any data stored behind a raw pointer is destroyed at the
appropriate time.
use libc::{c_void, size_t, malloc, free};
use std::mem;
use std::ptr;
-# use std::boxed::Box;
// Define a wrapper around the handle returned by the foreign code.
// Unique<T> has the same semantics as Box<T>
The first of these three functions, `stack_exhausted`, is invoked whenever stack
overflow is detected. This function has a number of restrictions about how it
can be called and what it must do, but if the stack limit register is not being
-maintained then a task always has an "infinite stack" and this function
+maintained then a thread always has an "infinite stack" and this function
shouldn't get triggered.
The second of these three functions, `eh_personality`, is used by the
```
#![no_std]
-#![feature(globs)]
#![feature(lang_items)]
# extern crate libc;
Let's get back to bindings. Rust variable bindings have one more aspect that
differs from other languages: bindings are required to be initialized with a
-value before you're allowed to use them. If we try...
-
-```{ignore}
-let x;
-```
-
-...we'll get an error:
-
-```text
-src/main.rs:2:9: 2:10 error: unable to infer enough type information about `_`; type annotations required
-src/main.rs:2 let x;
- ^
-```
-
-Giving it a type will compile, though:
-
-```{rust}
-let x: i32;
-```
+value before you're allowed to use them.
Let's try it out. Change your `src/main.rs` file to look like this:
% The Rust Tutorial
-This tutorial has been deprecated in favor of [the Guide](guide.html). Go check that out instead!
+This tutorial has been deprecated in favor of [the Book](book/index.html). Go check that out instead!
: 'r' LIT_STR_RAW_INNER SUFFIX?
;
+
+QUESTION : '?';
+
IDENT : XID_start XID_continue* ;
+fragment QUESTION_IDENTIFIER : QUESTION? IDENT;
+
LIFETIME : '\'' IDENT ;
WHITESPACE : [ \r\n\t]+ ;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(globs, plugin)]
+#![feature(plugin)]
extern crate syntax;
extern crate rustc;
"LE" => token::Le,
"LIT_BINARY" => token::Literal(token::Binary(Name(0)), None),
"LIT_BINARY_RAW" => token::Literal(token::BinaryRaw(Name(0), 0), None),
+ "QUESTION" => token::Question,
_ => continue,
};
res.insert(num.to_string(), tok);
}
- debug!("Token map: {}", res);
+ debug!("Token map: {:?}", res);
res
}
parse::token::intern(lit.slice(1, lit.len() - 1))
}
-fn count(lit: &str) -> uint {
+fn count(lit: &str) -> usize {
lit.chars().take_while(|c| *c == '#').count()
}
let toknum = m.name("toknum").unwrap_or("");
let content = m.name("content").unwrap_or("");
- let proto_tok = tokens.get(toknum).expect(format!("didn't find token {} in the map",
+ let proto_tok = tokens.get(toknum).expect(format!("didn't find token {:?} in the map",
toknum).as_slice());
let nm = parse::token::intern(content);
- debug!("What we got: content (`{}`), proto: {}", content, proto_tok);
+ debug!("What we got: content (`{}`), proto: {:?}", content, proto_tok);
let real_tok = match *proto_tok {
token::BinOp(..) => token::BinOp(str_to_binop(content)),
continue
}
- assert!(rustc_tok.sp == antlr_tok.sp, "{} and {} have different spans", rustc_tok,
+ assert!(rustc_tok.sp == antlr_tok.sp, "{:?} and {:?} have different spans", rustc_tok,
antlr_tok);
macro_rules! matches {
if !tok_cmp(&rustc_tok.tok, &antlr_tok.tok) {
// FIXME #15677: needs more robust escaping in
// antlr
- warn!("Different names for {} and {}", rustc_tok, antlr_tok);
+ warn!("Different names for {:?} and {:?}", rustc_tok, antlr_tok);
}
}
- _ => panic!("{} is not {}", antlr_tok, rustc_tok)
+ _ => panic!("{:?} is not {:?}", antlr_tok, rustc_tok)
},)*
- ref c => assert!(c == &antlr_tok.tok, "{} is not {}", rustc_tok, antlr_tok)
+ ref c => assert!(c == &antlr_tok.tok, "{:?} is not {:?}", rustc_tok, antlr_tok)
}
)
}
not(feature = "external_crate"),
any(target_arch = "arm",
target_arch = "mips",
- target_arch = "mipsel")))]
+ target_arch = "mipsel",
+ target_arch = "powerpc")))]
const MIN_ALIGN: uint = 8;
#[cfg(all(not(feature = "external_funcs"),
not(feature = "external_crate"),
for _ in range(0u, 100) {
bitv |= 1 << ((r.next_u32() as uint) % u32::BITS);
}
- black_box(&bitv)
+ black_box(&bitv);
});
}
for _ in range(0u, 100) {
bitv.set((r.next_u32() as uint) % BENCH_BITS, true);
}
- black_box(&bitv)
+ black_box(&bitv);
});
}
#[cfg(test)]
mod test {
use prelude::*;
- use std::borrow::BorrowFrom;
use super::{BTreeMap, Occupied, Vacant};
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
///
/// # Examples
///
- /// ```rust
+ /// ```
/// use std::collections::DList;
///
/// let mut a = DList::new();
/// Returns `true` if the `DList` is empty.
///
/// This operation should compute in O(1) time.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut dl = DList::new();
+ /// assert!(dl.is_empty());
+ ///
+ /// dl.push_front("foo");
+ /// assert!(!dl.is_empty());
+ /// ```
#[inline]
#[stable]
pub fn is_empty(&self) -> bool {
/// Returns the length of the `DList`.
///
/// This operation should compute in O(1) time.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut dl = DList::new();
+ ///
+ /// dl.push_front(2is);
+ /// assert_eq!(dl.len(), 1);
+ ///
+ /// dl.push_front(1);
+ /// assert_eq!(dl.len(), 2);
+ ///
+ /// dl.push_back(3);
+ /// assert_eq!(dl.len(), 3);
+ ///
+ /// ```
#[inline]
#[stable]
pub fn len(&self) -> uint {
/// Removes all elements from the `DList`.
///
/// This operation should compute in O(n) time.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut dl = DList::new();
+ ///
+ /// dl.push_front(2is);
+ /// dl.push_front(1);
+ /// assert_eq!(dl.len(), 2);
+ /// assert_eq!(dl.front(), Some(&1is));
+ ///
+ /// dl.clear();
+ /// assert_eq!(dl.len(), 0);
+ /// assert_eq!(dl.front(), None);
+ ///
+ /// ```
#[inline]
#[stable]
pub fn clear(&mut self) {
/// Provides a reference to the front element, or `None` if the list is
/// empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut dl = DList::new();
+ /// assert_eq!(dl.front(), None);
+ ///
+ /// dl.push_front(1);
+ /// assert_eq!(dl.front(), Some(&1is));
+ ///
+ /// ```
#[inline]
#[stable]
pub fn front(&self) -> Option<&T> {
/// Provides a mutable reference to the front element, or `None` if the list
/// is empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut dl = DList::new();
+ /// assert_eq!(dl.front(), None);
+ ///
+ /// dl.push_front(1);
+ /// assert_eq!(dl.front(), Some(&1is));
+ ///
+ /// match dl.front_mut() {
+ /// None => {},
+ /// Some(x) => *x = 5is,
+ /// }
+ /// assert_eq!(dl.front(), Some(&5is));
+ ///
+ /// ```
#[inline]
#[stable]
pub fn front_mut(&mut self) -> Option<&mut T> {
/// Provides a reference to the back element, or `None` if the list is
/// empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut dl = DList::new();
+ /// assert_eq!(dl.back(), None);
+ ///
+ /// dl.push_back(1);
+ /// assert_eq!(dl.back(), Some(&1is));
+ ///
+ /// ```
#[inline]
#[stable]
pub fn back(&self) -> Option<&T> {
/// Provides a mutable reference to the back element, or `None` if the list
/// is empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut dl = DList::new();
+ /// assert_eq!(dl.back(), None);
+ ///
+ /// dl.push_back(1);
+ /// assert_eq!(dl.back(), Some(&1is));
+ ///
+ /// match dl.back_mut() {
+ /// None => {},
+ /// Some(x) => *x = 5is,
+ /// }
+ /// assert_eq!(dl.back(), Some(&5is));
+ ///
+ /// ```
#[inline]
#[stable]
pub fn back_mut(&mut self) -> Option<&mut T> {
/// Adds an element first in the list.
///
/// This operation should compute in O(1) time.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut dl = DList::new();
+ ///
+ /// dl.push_front(2is);
+ /// assert_eq!(dl.front().unwrap(), &2is);
+ ///
+ /// dl.push_front(1);
+ /// assert_eq!(dl.front().unwrap(), &1);
+ ///
+ /// ```
#[stable]
pub fn push_front(&mut self, elt: T) {
self.push_front_node(box Node::new(elt))
/// empty.
///
/// This operation should compute in O(1) time.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut d = DList::new();
+ /// assert_eq!(d.pop_front(), None);
+ ///
+ /// d.push_front(1is);
+ /// d.push_front(3);
+ /// assert_eq!(d.pop_front(), Some(3));
+ /// assert_eq!(d.pop_front(), Some(1));
+ /// assert_eq!(d.pop_front(), None);
+ ///
+ /// ```
+ ///
#[stable]
pub fn pop_front(&mut self) -> Option<T> {
self.pop_front_node().map(|box Node{value, ..}| value)
///
/// # Examples
///
- /// ```rust
+ /// ```
/// use std::collections::DList;
///
/// let mut d = DList::new();
///
/// # Examples
///
- /// ```rust
+ /// ```
/// use std::collections::DList;
///
/// let mut d = DList::new();
/// including the index.
///
/// This operation should compute in O(n) time.
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::DList;
+ ///
+ /// let mut d = DList::new();
+ ///
+ /// d.push_front(1is);
+ /// d.push_front(2);
+ /// d.push_front(3);
+ ///
+ /// let mut splitted = d.split_off(2);
+ ///
+ /// assert_eq!(splitted.pop_front(), Some(1));
+ /// assert_eq!(splitted.pop_front(), None);
+ /// ```
#[stable]
pub fn split_off(&mut self, at: uint) -> DList<T> {
let len = self.len();
///
/// # Examples
///
- /// ```rust
+ /// ```
/// use std::collections::DList;
///
/// let mut list: DList<int> = vec![1, 3, 4].into_iter().collect();
///
/// # Examples
///
- /// ```rust
+ /// ```
/// use std::collections::DList;
///
/// let mut list: DList<int> = vec![1, 2, 3].into_iter().collect();
let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
b.iter(|| {
- for ch in s.chars() { black_box(ch) }
+ for ch in s.chars() { black_box(ch); }
});
}
let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
b.iter(|| {
- for ch in s.chars().rev() { black_box(ch) }
+ for ch in s.chars().rev() { black_box(ch); }
});
}
end: *const T
}
+unsafe impl<T: Send> Send for IntoIter<T> { }
+unsafe impl<T: Sync> Sync for IntoIter<T> { }
+
impl<T> IntoIter<T> {
#[inline]
/// Drops all items that have not yet been moved and returns the empty vector.
//! not (`None`).
//!
//! ```
-//! # use std::boxed::Box;
//! let optional: Option<Box<int>> = None;
//! check_optional(&optional);
//!
//! though unsafely, transformed from one type to the other.
//!
//! ```
-//! # use std::boxed::Box;
//! use std::mem;
//!
//! unsafe {
//! drop(file);
//! ```
//!
-//! If you *do* write that in Rust, the compiler will by give you a
+//! If you *do* write that in Rust, the compiler will give you a
//! warning (by default, controlled by the `unused_must_use` lint).
//!
//! You might instead, if you don't want to handle the error, simply
//! fn write_info(info: &Info) -> Result<(), IoError> {
//! let mut file = File::open_mode(&Path::new("my_best_friends.txt"), Open, Write);
//! // Early return on error
-//! match file.write_line(format!("name: {}", info.name).as_slice()) {
-//! Ok(_) => (),
-//! Err(e) => return Err(e)
+//! if let Err(e) = file.write_line(format!("name: {}", info.name).as_slice()) {
+//! return Err(e)
//! }
-//! match file.write_line(format!("age: {}", info.age).as_slice()) {
-//! Ok(_) => (),
-//! Err(e) => return Err(e)
+//! if let Err(e) = file.write_line(format!("age: {}", info.age).as_slice()) {
+//! return Err(e)
//! }
//! return file.write_line(format!("rating: {}", info.rating).as_slice());
//! }
}
#[test]
-#[cfg(any(target_arch = "x86",
- target_arch = "arm",
- target_arch = "mips",
- target_arch = "mipsel"))]
+#[cfg(target_pointer_width = "32")]
fn size_of_32() {
assert_eq!(size_of::<uint>(), 4u);
assert_eq!(size_of::<*const uint>(), 4u);
}
#[test]
-#[cfg(any(target_arch = "x86_64",
- target_arch = "aarch64"))]
+#[cfg(target_pointer_width = "64")]
fn size_of_64() {
assert_eq!(size_of::<uint>(), 8u);
assert_eq!(size_of::<*const uint>(), 8u);
}
#[test]
-#[cfg(any(target_arch = "x86",
- target_arch = "arm",
- target_arch = "mips",
- target_arch = "mipsel"))]
+#[cfg(target_pointer_width = "32")]
fn align_of_32() {
assert_eq!(align_of::<uint>(), 4u);
assert_eq!(align_of::<*const uint>(), 4u);
}
#[test]
-#[cfg(any(target_arch = "x86_64",
- target_arch = "aarch64"))]
+#[cfg(target_pointer_width = "64")]
fn align_of_64() {
assert_eq!(align_of::<uint>(), 8u);
assert_eq!(align_of::<*const uint>(), 8u);
#[cfg(any(target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
- target_arch = "mipsel"))]
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
pub mod arch {
pub mod c95 {
pub type c_char = i8;
}
#[cfg(any(target_arch = "x86",
target_arch = "mips",
- target_arch = "mipsel"))]
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
pub mod posix88 {
pub type off_t = i32;
pub type dev_t = u64;
pub __size: [u32; 9]
}
}
- #[cfg(any(target_arch = "mips", target_arch = "mipsel"))]
+ #[cfg(any(target_arch = "mips",
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
pub mod posix01 {
use types::os::arch::c95::{c_long, c_ulong, time_t};
use types::os::arch::posix88::{gid_t, ino_t};
pub const EHWPOISON: c_int = 133;
}
- #[cfg(any(target_arch = "mips", target_arch = "mipsel"))]
+ #[cfg(any(target_arch = "mips",
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
pub mod posix88 {
use types::os::arch::c95::c_int;
use types::common::c95::c_void;
#[cfg(all(target_os = "linux",
any(target_arch = "mips",
target_arch = "mipsel",
- target_arch = "aarch64")))]
+ target_arch = "aarch64",
+ target_arch = "powerpc")))]
pub const PTHREAD_STACK_MIN: size_t = 131072;
pub const CLOCK_REALTIME: c_int = 0;
pub const SHUT_WR: c_int = 1;
pub const SHUT_RDWR: c_int = 2;
}
- #[cfg(any(target_arch = "mips", target_arch = "mipsel"))]
+ #[cfg(any(target_arch = "mips",
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
pub mod bsd44 {
use types::os::arch::c95::c_int;
pub const MAP_NONBLOCK : c_int = 0x010000;
pub const MAP_STACK : c_int = 0x020000;
}
- #[cfg(any(target_arch = "mips", target_arch = "mipsel"))]
+ #[cfg(any(target_arch = "mips",
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
pub mod extra {
use types::os::arch::c95::c_int;
let mut span = e.span;
let id = match e.node {
- ast::ExprPath(..) | ast::ExprStruct(..) => {
+ ast::ExprPath(..) | ast::ExprQPath(..) | ast::ExprStruct(..) => {
match cx.tcx.def_map.borrow().get(&e.id) {
Some(&def) => def.def_id(),
None => return
ast::ExprMac(..) |
ast::ExprClosure(..) |
ast::ExprLit(..) |
- ast::ExprPath(..) => {
+ ast::ExprPath(..) |
+ ast::ExprQPath(..) => {
self.straightline(expr, pred, None::<ast::Expr>.iter())
}
}
pub ast_map: &'a ast_map::Map<'ast>,
pub cfg: &'a cfg::CFG,
pub name: String,
+ /// `labelled_edges` controls whether we emit labels on the edges
+ pub labelled_edges: bool,
}
fn replace_newline_with_backslash_l(s: String) -> String {
fn edge_label(&self, e: &Edge<'a>) -> dot::LabelText<'a> {
let mut label = String::new();
+ if !self.labelled_edges {
+ return dot::LabelText::EscStr(label.into_cow());
+ }
let mut put_one = false;
for (i, &node_id) in e.data.exiting_scopes.iter().enumerate() {
if put_one {
expression");
}
}
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
match v.tcx.def_map.borrow()[e.id] {
DefStatic(..) | DefConst(..) |
DefFn(..) | DefStaticMethod(..) | DefMethod(..) |
"{} are not allowed to have custom pointers",
self.msg());
}
- ast::ExprPath(..) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
match ty::resolve_expr(self.tcx, e) {
def::DefStatic(..) if self.mode == InConstant => {
let msg = "constants cannot refer to other statics, \
fn visit_expr(&mut self, e: &ast::Expr) {
match e.node {
- ast::ExprPath(..) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
match self.def_map.borrow().get(&e.id) {
Some(&DefStatic(def_id, _)) |
Some(&DefConst(def_id)) if
// FIXME: (#3728) we can probably do something CCI-ish
// surrounding nonlocal constants. But we don't yet.
- ast::ExprPath(_) => self.lookup_constness(e),
+ ast::ExprPath(_) | ast::ExprQPath(_) => self.lookup_constness(e),
ast::ExprRepeat(..) => general_const,
}
}
+ ast::ExprQPath(_) => {
+ match lookup_const(tcx, expr) {
+ Some(actual) => return const_expr_to_pat(tcx, actual),
+ _ => unreachable!()
+ }
+ }
+
_ => ast::PatLit(P(expr.clone()))
};
P(ast::Pat { id: expr.id, node: pat, span: expr.span })
ty::ty_float(ast::TyF64) => (f64, const_float, f64)
}))
}
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
match lookup_const(tcx, e) {
Some(actual_e) => eval_const_expr_partial(tcx, &*actual_e),
None => Err("non-constant path in constant expr".to_string())
ast::ExprInlineAsm(..) => {
self.require_unsafe(expr.span, "use of inline assembly");
}
- ast::ExprPath(..) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
if let def::DefStatic(_, true) = ty::resolve_expr(self.tcx, expr) {
self.require_unsafe(expr.span, "use of mutable static");
}
self.walk_expr(&**subexpr)
}
- ast::ExprPath(..) => { }
+ ast::ExprPath(_) | ast::ExprQPath(_) => { }
ast::ExprUnary(ast::UnDeref, ref base) => { // *base
if !self.walk_overloaded_operator(expr, &**base, Vec::new(), PassArgs::ByRef) {
fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
match expr.node {
// live nodes required for uses or definitions of variables:
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
let def = ir.tcx.def_map.borrow()[expr.id].clone();
debug!("expr {}: path that leads to {:?}", expr.id, def);
if let DefLocal(..) = def {
match expr.node {
// Interesting cases with control flow or which gen/kill
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
self.access_path(expr, succ, ACC_READ | ACC_USE)
}
// just ignore such cases and treat them as reads.
match expr.node {
- ast::ExprPath(_) => succ,
+ ast::ExprPath(_) | ast::ExprQPath(_) => succ,
ast::ExprField(ref e, _) => self.propagate_through_expr(&**e, succ),
ast::ExprTupField(ref e, _) => self.propagate_through_expr(&**e, succ),
_ => self.propagate_through_expr(expr, succ)
fn write_lvalue(&mut self, expr: &Expr, succ: LiveNode, acc: uint)
-> LiveNode {
match expr.node {
- ast::ExprPath(_) => self.access_path(expr, succ, acc),
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
+ self.access_path(expr, succ, acc)
+ }
// We do not track other lvalues, so just propagate through
// to their subcomponents. Also, it may happen that
ast::ExprBlock(..) | ast::ExprMac(..) | ast::ExprAddrOf(..) |
ast::ExprStruct(..) | ast::ExprRepeat(..) | ast::ExprParen(..) |
ast::ExprClosure(..) | ast::ExprPath(..) | ast::ExprBox(..) |
- ast::ExprRange(..) => {
+ ast::ExprRange(..) | ast::ExprQPath(..) => {
visit::walk_expr(this, expr);
}
ast::ExprIfLet(..) => {
fn check_lvalue(&mut self, expr: &Expr) {
match expr.node {
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
if let DefLocal(nid) = self.ir.tcx.def_map.borrow()[expr.id].clone() {
// Assignment to an immutable variable or argument: only legal
// if there is no later assignment. If this local is actually
}
}
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
let def = (*self.tcx().def_map.borrow())[expr.id];
self.cat_def(expr.id, expr.span, expr_ty, def)
}
struct type?!"),
}
}
- ast::ExprPath(..) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
let guard = |&: did: ast::DefId| {
let fields = ty::lookup_struct_fields(self.tcx, did);
let any_priv = fields.iter().any(|f| {
fn visit_expr(&mut self, expr: &ast::Expr) {
match expr.node {
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
let def = match self.tcx.def_map.borrow().get(&expr.id) {
Some(&def) => def,
None => {
//
// There are various parts of the compiler that must impose arbitrary limits
// on how deeply they recurse to prevent stack overflow. Users can override
-// this via an attribute on the crate like `#![recursion_limit(22)]`. This pass
+// this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
// just peeks and looks for that attribute.
use session::Session;
}
sess.span_err(attr.span, "malformed recursion limit attribute, \
- expected #![recursion_limit(\"N\")]");
+ expected #![recursion_limit=\"N\"]");
}
}
use std::fmt;
use std::slice::Iter;
-use std::vec::Vec;
+use std::vec::{Vec, IntoIter};
use syntax::codemap::{Span, DUMMY_SP};
///////////////////////////////////////////////////////////////////////////
self.content.iter()
}
+ pub fn into_iter(self) -> IntoIter<T> {
+ self.content.into_iter()
+ }
+
pub fn iter_enumerated<'a>(&'a self) -> EnumeratedItems<'a,T> {
EnumeratedItems::new(self)
}
note_obligation_cause(infcx, obligation);
}
- SelectionError::Unimplemented => {
- match obligation.predicate {
- ty::Predicate::Trait(ref trait_predicate) => {
- let trait_predicate =
- infcx.resolve_type_vars_if_possible(trait_predicate);
- if !trait_predicate.references_error() {
- let trait_ref = trait_predicate.to_poly_trait_ref();
- infcx.tcx.sess.span_err(
- obligation.cause.span,
- format!(
- "the trait `{}` is not implemented for the type `{}`",
- trait_ref.user_string(infcx.tcx),
- trait_ref.self_ty().user_string(infcx.tcx)).as_slice());
- // Check if it has a custom "#[rustc_on_unimplemented]" error message,
- // report with that message if it does
- let custom_note = report_on_unimplemented(infcx, &*trait_ref.0,
- obligation.cause.span);
- if let Some(s) = custom_note {
- infcx.tcx.sess.span_note(obligation.cause.span,
- s.as_slice());
- }
- }
- }
- ty::Predicate::Equate(ref predicate) => {
- let predicate = infcx.resolve_type_vars_if_possible(predicate);
- let err = infcx.equality_predicate(obligation.cause.span,
- &predicate).unwrap_err();
+ SelectionError::Unimplemented => {
+ match &obligation.cause.code {
+ &ObligationCauseCode::CompareImplMethodObligation => {
infcx.tcx.sess.span_err(
obligation.cause.span,
format!(
- "the requirement `{}` is not satisfied (`{}`)",
- predicate.user_string(infcx.tcx),
- ty::type_err_to_str(infcx.tcx, &err)).as_slice());
+ "the requirement `{}` appears on the impl \
+ method but not on the corresponding trait method",
+ obligation.predicate.user_string(infcx.tcx)).as_slice());
}
+ _ => {
+ match obligation.predicate {
+ ty::Predicate::Trait(ref trait_predicate) => {
+ let trait_predicate =
+ infcx.resolve_type_vars_if_possible(trait_predicate);
- ty::Predicate::RegionOutlives(ref predicate) => {
- let predicate = infcx.resolve_type_vars_if_possible(predicate);
- let err = infcx.region_outlives_predicate(obligation.cause.span,
- &predicate).unwrap_err();
- infcx.tcx.sess.span_err(
- obligation.cause.span,
- format!(
- "the requirement `{}` is not satisfied (`{}`)",
- predicate.user_string(infcx.tcx),
- ty::type_err_to_str(infcx.tcx, &err)).as_slice());
- }
+ if !trait_predicate.references_error() {
+ let trait_ref = trait_predicate.to_poly_trait_ref();
+ infcx.tcx.sess.span_err(
+ obligation.cause.span,
+ format!(
+ "the trait `{}` is not implemented for the type `{}`",
+ trait_ref.user_string(infcx.tcx),
+ trait_ref.self_ty().user_string(infcx.tcx)).as_slice());
+ // Check if it has a custom "#[rustc_on_unimplemented]"
+ // error message, report with that message if it does
+ let custom_note = report_on_unimplemented(infcx, &*trait_ref.0,
+ obligation.cause.span);
+ if let Some(s) = custom_note {
+ infcx.tcx.sess.span_note(obligation.cause.span,
+ s.as_slice());
+ }
+ }
+ }
- ty::Predicate::Projection(..) |
- ty::Predicate::TypeOutlives(..) => {
- let predicate =
- infcx.resolve_type_vars_if_possible(&obligation.predicate);
- infcx.tcx.sess.span_err(
- obligation.cause.span,
- format!(
- "the requirement `{}` is not satisfied",
- predicate.user_string(infcx.tcx)).as_slice());
+ ty::Predicate::Equate(ref predicate) => {
+ let predicate = infcx.resolve_type_vars_if_possible(predicate);
+ let err = infcx.equality_predicate(obligation.cause.span,
+ &predicate).unwrap_err();
+ infcx.tcx.sess.span_err(
+ obligation.cause.span,
+ format!(
+ "the requirement `{}` is not satisfied (`{}`)",
+ predicate.user_string(infcx.tcx),
+ ty::type_err_to_str(infcx.tcx, &err)).as_slice());
+ }
+
+ ty::Predicate::RegionOutlives(ref predicate) => {
+ let predicate = infcx.resolve_type_vars_if_possible(predicate);
+ let err = infcx.region_outlives_predicate(obligation.cause.span,
+ &predicate).unwrap_err();
+ infcx.tcx.sess.span_err(
+ obligation.cause.span,
+ format!(
+ "the requirement `{}` is not satisfied (`{}`)",
+ predicate.user_string(infcx.tcx),
+ ty::type_err_to_str(infcx.tcx, &err)).as_slice());
+ }
+
+ ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => {
+ let predicate =
+ infcx.resolve_type_vars_if_possible(&obligation.predicate);
+ infcx.tcx.sess.span_err(
+ obligation.cause.span,
+ format!(
+ "the requirement `{}` is not satisfied",
+ predicate.user_string(infcx.tcx)).as_slice());
+ }
+ }
}
}
}
+
OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, ref e) => {
let expected_trait_ref = infcx.resolve_type_vars_if_possible(&*expected_trait_ref);
let actual_trait_ref = infcx.resolve_type_vars_if_possible(&*actual_trait_ref);
obligation.cause.span,
format!(
"type mismatch: the type `{}` implements the trait `{}`, \
- but the trait `{}` is required ({})",
+ but the trait `{}` is required ({})",
expected_trait_ref.self_ty().user_string(infcx.tcx),
expected_trait_ref.user_string(infcx.tcx),
actual_trait_ref.user_string(infcx.tcx),
ty::type_err_to_str(infcx.tcx, e)).as_slice());
- note_obligation_cause(infcx, obligation);
+ note_obligation_cause(infcx, obligation);
}
}
}
}
fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
- _predicate: &ty::Predicate<'tcx>,
+ predicate: &ty::Predicate<'tcx>,
cause_span: Span,
cause_code: &ObligationCauseCode<'tcx>)
{
let parent_predicate = parent_trait_ref.as_predicate();
note_obligation_cause_code(infcx, &parent_predicate, cause_span, &*data.parent_code);
}
+ ObligationCauseCode::CompareImplMethodObligation => {
+ span_note!(tcx.sess, cause_span,
+ "the requirement `{}` appears on the impl method\
+ but not on the corresponding trait method",
+ predicate.user_string(infcx.tcx));
+ }
}
}
// static items must have `Sync` type
SharedStatic,
+
BuiltinDerivedObligation(DerivedObligationCause<'tcx>),
ImplDerivedObligation(DerivedObligationCause<'tcx>),
+
+ CompareImplMethodObligation,
}
#[derive(Clone)]
&ty_projection(ref data) => {
self.add_flags(HAS_PROJECTION);
- self.add_substs(data.trait_ref.substs);
+ self.add_projection_ty(data);
}
&ty_trait(box TyTrait { ref principal, ref bounds }) => {
let mut computation = FlagComputation::new();
computation.add_substs(principal.0.substs);
+ for projection_bound in bounds.projection_bounds.iter() {
+ let mut proj_computation = FlagComputation::new();
+ proj_computation.add_projection_predicate(&projection_bound.0);
+ computation.add_bound_computation(&proj_computation);
+ }
self.add_bound_computation(&computation);
self.add_bounds(bounds);
}
}
+ fn add_projection_predicate(&mut self, projection_predicate: &ProjectionPredicate) {
+ self.add_projection_ty(&projection_predicate.projection_ty);
+ self.add_ty(projection_predicate.ty);
+ }
+
+ fn add_projection_ty(&mut self, projection_ty: &ProjectionTy) {
+ self.add_substs(projection_ty.trait_ref.substs);
+ }
+
fn add_substs(&mut self, substs: &Substs) {
self.add_tys(substs.types.as_slice());
match substs.regions {
}
match expr.node {
- ast::ExprPath(..) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
match resolve_expr(tcx, expr) {
def::DefVariant(tid, vid, _) => {
let variant_info = enum_variant_with_id(tcx, tid, vid);
self.mt.repr(tcx))
}
}
+
+impl<'a, 'tcx> Repr<'tcx> for ParameterEnvironment<'a, 'tcx> {
+ fn repr(&self, tcx: &ctxt<'tcx>) -> String {
+ format!("ParameterEnvironment(\
+ free_substs={}, \
+ implicit_region_bound={}, \
+ caller_bounds={})",
+ self.free_substs.repr(tcx),
+ self.implicit_region_bound.repr(tcx),
+ self.caller_bounds.repr(tcx))
+ }
+ }
}
}
+impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where 'tcx: 'a {
+ fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ParameterEnvironment<'a, 'tcx> {
+ ty::ParameterEnvironment {
+ tcx: self.tcx,
+ free_substs: self.free_substs.fold_with(folder),
+ implicit_region_bound: self.implicit_region_bound.fold_with(folder),
+ caller_bounds: self.caller_bounds.fold_with(folder),
+ selection_cache: traits::SelectionCache::new(),
+ }
+ }
+}
+
///////////////////////////////////////////////////////////////////////////
// "super" routines: these are the default implementations for TypeFolder.
//
use session::Session;
use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT};
-use syntax::ext::base::{IdentTT, Decorator, Modifier, MacroRulesTT};
+use syntax::ext::base::{IdentTT, Decorator, Modifier, MultiModifier, MacroRulesTT};
use syntax::ext::base::{MacroExpanderFn};
use syntax::codemap::Span;
use syntax::parse::token;
IdentTT(ext, _) => IdentTT(ext, Some(self.krate_span)),
Decorator(ext) => Decorator(ext),
Modifier(ext) => Modifier(ext),
-
+ MultiModifier(ext) => MultiModifier(ext),
MacroRulesTT => {
self.sess.err("plugin tried to register a new MacroRulesTT");
return;
impl<'tcx> Repr<'tcx> for ty::ProjectionTy<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
format!("<{} as {}>::{}",
- self.trait_ref.self_ty().repr(tcx),
+ self.trait_ref.substs.self_ty().repr(tcx),
self.trait_ref.repr(tcx),
self.item_name.repr(tcx))
}
SawExprIndex,
SawExprRange,
SawExprPath,
+ SawExprQPath,
SawExprAddrOf(ast::Mutability),
SawExprRet,
SawExprInlineAsm(&'a ast::InlineAsm),
ExprIndex(..) => SawExprIndex,
ExprRange(..) => SawExprRange,
ExprPath(..) => SawExprPath,
+ ExprQPath(..) => SawExprQPath,
ExprAddrOf(m, _) => SawExprAddrOf(m),
ExprBreak(id) => SawExprBreak(id.map(content)),
ExprAgain(id) => SawExprAgain(id.map(content)),
mod i686_unknown_linux_gnu;
mod mips_unknown_linux_gnu;
mod mipsel_unknown_linux_gnu;
+mod powerpc_unknown_linux_gnu;
mod x86_64_apple_darwin;
mod x86_64_apple_ios;
mod x86_64_pc_windows_gnu;
/// OS name to use for conditional compilation.
pub target_os: String,
/// Architecture to use for ABI considerations. Valid options: "x86", "x86_64", "arm",
- /// "aarch64", and "mips". "mips" includes "mipsel".
+ /// "aarch64", "mips", and "powerpc". "mips" includes "mipsel".
pub arch: String,
/// Optional settings with defaults.
pub options: TargetOptions,
i686_unknown_linux_gnu,
mips_unknown_linux_gnu,
mipsel_unknown_linux_gnu,
+ powerpc_unknown_linux_gnu,
arm_linux_androideabi,
arm_unknown_linux_gnueabi,
arm_unknown_linux_gnueabihf,
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use target::Target;
+
+pub fn target() -> Target {
+ let mut base = super::linux_base::opts();
+ base.pre_link_args.push("-m32".to_string());
+
+ Target {
+ data_layout: "E-S8-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string(),
+ llvm_target: "powerpc-unknown-linux-gnu".to_string(),
+ target_endian: "big".to_string(),
+ target_pointer_width: "32".to_string(),
+ arch: "powerpc".to_string(),
+ target_os: "linux".to_string(),
+ options: base,
+ }
+}
PpmExpandedHygiene,
}
+
+#[derive(Copy, PartialEq, Show)]
+pub enum PpFlowGraphMode {
+ Default,
+ /// Drops the labels from the edges in the flowgraph output. This
+ /// is mostly for use in the --xpretty flowgraph run-make tests,
+ /// since the labels are largely uninteresting in those cases and
+ /// have become a pain to maintain.
+ UnlabelledEdges,
+}
#[derive(Copy, PartialEq, Show)]
pub enum PpMode {
PpmSource(PpSourceMode),
- PpmFlowGraph,
+ PpmFlowGraph(PpFlowGraphMode),
}
pub fn parse_pretty(sess: &Session,
("expanded,identified", _) => PpmSource(PpmExpandedIdentified),
("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene),
("identified", _) => PpmSource(PpmIdentified),
- ("flowgraph", true) => PpmFlowGraph,
+ ("flowgraph", true) => PpmFlowGraph(PpFlowGraphMode::Default),
+ ("flowgraph,unlabelled", true) => PpmFlowGraph(PpFlowGraphMode::UnlabelledEdges),
_ => {
if extended {
sess.fatal(format!(
"argument to `xpretty` must be one of `normal`, \
- `expanded`, `flowgraph=<nodeid>`, `typed`, `identified`, \
+ `expanded`, `flowgraph[,unlabelled]=<nodeid>`, `typed`, `identified`, \
`expanded,identified`, or `everybody_loops`; got {}", name).as_slice());
} else {
sess.fatal(format!(
PpmSource(PpmExpandedIdentified) |
PpmSource(PpmExpandedHygiene) |
PpmSource(PpmTyped) |
- PpmFlowGraph => true
+ PpmFlowGraph(_) => true
}
}
PpmSource(PpmExpandedIdentified) |
PpmSource(PpmExpandedHygiene) |
PpmSource(PpmTyped) |
- PpmFlowGraph => true
+ PpmFlowGraph(_) => true
}
}
pp::eof(&mut pp_state.s)
}),
- (PpmFlowGraph, opt_uii) => {
+ (PpmFlowGraph(mode), opt_uii) => {
debug!("pretty printing flow graph for {:?}", opt_uii);
let uii = opt_uii.unwrap_or_else(|| {
sess.fatal(&format!("`pretty flowgraph=..` needs NodeId (int) or
&arenas,
id,
resolve::MakeGlobMap::No);
- print_flowgraph(variants, analysis, code, out)
+ print_flowgraph(variants, analysis, code, mode, out)
}
None => {
let message = format!("--pretty=flowgraph needs \
fn print_flowgraph<W:io::Writer>(variants: Vec<borrowck_dot::Variant>,
analysis: ty::CrateAnalysis,
code: blocks::Code,
+ mode: PpFlowGraphMode,
mut out: W) -> io::IoResult<()> {
let ty_cx = &analysis.ty_cx;
let cfg = match code {
blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block),
blocks::FnLikeCode(fn_like) => cfg::CFG::new(ty_cx, &*fn_like.body()),
};
+ let labelled_edges = mode != PpFlowGraphMode::UnlabelledEdges;
+ let lcfg = LabelledCFG {
+ ast_map: &ty_cx.map,
+ cfg: &cfg,
+ name: format!("node_{}", code.id()),
+ labelled_edges: labelled_edges,
+ };
match code {
_ if variants.len() == 0 => {
- let lcfg = LabelledCFG {
- ast_map: &ty_cx.map,
- cfg: &cfg,
- name: format!("node_{}", code.id()),
- };
let r = dot::render(&lcfg, &mut out);
return expand_err_details(r);
}
let (bccx, analysis_data) =
borrowck::build_borrowck_dataflow_data_for_fn(ty_cx, fn_parts);
- let lcfg = LabelledCFG {
- ast_map: &ty_cx.map,
- cfg: &cfg,
- name: format!("node_{}", code.id()),
- };
let lcfg = borrowck_dot::DataflowLabeller {
inner: lcfg,
variants: variants,
pub fn LLVMInitializeMipsTargetMC();
pub fn LLVMInitializeMipsAsmPrinter();
pub fn LLVMInitializeMipsAsmParser();
+ pub fn LLVMInitializePowerPCTargetInfo();
+ pub fn LLVMInitializePowerPCTarget();
+ pub fn LLVMInitializePowerPCTargetMC();
+ pub fn LLVMInitializePowerPCAsmPrinter();
+ pub fn LLVMInitializePowerPCAsmParser();
pub fn LLVMRustAddPass(PM: PassManagerRef, Pass: *const c_char) -> bool;
pub fn LLVMRustCreateTargetMachine(Triple: *const c_char,
LLVMInitializeMipsAsmPrinter();
LLVMInitializeMipsAsmParser();
+ LLVMInitializePowerPCTargetInfo();
+ LLVMInitializePowerPCTarget();
+ LLVMInitializePowerPCTargetMC();
+ LLVMInitializePowerPCAsmPrinter();
+ LLVMInitializePowerPCAsmParser();
+
LLVMRustSetLLVMOptions(0 as c_int,
0 as *const _);
use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block, Crate, CrateNum};
use syntax::ast::{DefId, Expr, ExprAgain, ExprBreak, ExprField};
use syntax::ast::{ExprClosure, ExprForLoop, ExprLoop, ExprWhile, ExprMethodCall};
-use syntax::ast::{ExprPath, ExprStruct, FnDecl};
+use syntax::ast::{ExprPath, ExprQPath, ExprStruct, FnDecl};
use syntax::ast::{ForeignItemFn, ForeignItemStatic, Generics};
use syntax::ast::{Ident, ImplItem, Item, ItemConst, ItemEnum, ItemFn};
use syntax::ast::{ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic};
TraitImplementation => "implement",
TraitDerivation => "derive",
TraitObject => "reference",
- TraitQPath => "extract an associated type from",
+ TraitQPath => "extract an associated item from",
};
let msg = format!("attempt to {} a nonexistent trait `{}`", usage_str, path_str);
}
}
- match result_def {
- None => {
- match self.resolve_path(ty.id, path, TypeNS, true) {
- Some(def) => {
- debug!("(resolving type) resolved `{:?}` to \
- type {:?}",
- token::get_ident(path.segments.last().unwrap() .identifier),
- def);
- result_def = Some(def);
- }
- None => {
- result_def = None;
- }
- }
- }
- Some(_) => {} // Continue.
+ if let None = result_def {
+ result_def = self.resolve_path(ty.id, path, TypeNS, true);
}
match result_def {
Some(def) => {
// Write the result into the def map.
debug!("(resolving type) writing resolution for `{}` \
- (id {})",
+ (id {}) = {:?}",
self.path_names_to_string(path),
- path_id);
+ path_id, def);
self.record_def(path_id, def);
}
None => {
TyQPath(ref qpath) => {
self.resolve_type(&*qpath.self_type);
self.resolve_trait_reference(ty.id, &*qpath.trait_ref, TraitQPath);
+ for ty in qpath.item_path.parameters.types().into_iter() {
+ self.resolve_type(&**ty);
+ }
+ for binding in qpath.item_path.parameters.bindings().into_iter() {
+ self.resolve_type(&*binding.ty);
+ }
}
TyPolyTraitRef(ref bounds) => {
// The interpretation of paths depends on whether the path has
// multiple elements in it or not.
- ExprPath(ref path) => {
+ ExprPath(_) | ExprQPath(_) => {
+ let mut path_from_qpath;
+ let path = match expr.node {
+ ExprPath(ref path) => path,
+ ExprQPath(ref qpath) => {
+ self.resolve_type(&*qpath.self_type);
+ self.resolve_trait_reference(expr.id, &*qpath.trait_ref, TraitQPath);
+ path_from_qpath = qpath.trait_ref.path.clone();
+ path_from_qpath.segments.push(qpath.item_path.clone());
+ &path_from_qpath
+ }
+ _ => unreachable!()
+ };
// This is a local path in the value namespace. Walk through
// scopes looking for it.
-
- let path_name = self.path_names_to_string(path);
-
match self.resolve_path(expr.id, path, ValueNS, true) {
// Check if struct variant
Some((DefVariant(_, _, true), _)) => {
+ let path_name = self.path_names_to_string(path);
self.resolve_error(expr.span,
format!("`{}` is a struct variant name, but \
this expression \
Some(def) => {
// Write the result into the def map.
debug!("(resolving expr) resolved `{}`",
- path_name);
+ self.path_names_to_string(path));
self.record_def(expr.id, def);
}
// (The pattern matching def_tys where the id is in self.structs
// matches on regular structs while excluding tuple- and enum-like
// structs, which wouldn't result in this error.)
+ let path_name = self.path_names_to_string(path);
match self.with_no_errors(|this|
this.resolve_path(expr.id, path, TypeNS, false)) {
Some((DefTy(struct_id, _), _))
llvm::LLVMInitializeMipsAsmPrinter();
llvm::LLVMInitializeMipsAsmParser();
+ llvm::LLVMInitializePowerPCTargetInfo();
+ llvm::LLVMInitializePowerPCTarget();
+ llvm::LLVMInitializePowerPCTargetMC();
+ llvm::LLVMInitializePowerPCAsmPrinter();
+ llvm::LLVMInitializePowerPCAsmParser();
+
llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
llvm_args.as_ptr());
});
span: Span,
path: &ast::Path,
ref_kind: Option<recorder::Row>) {
- if generated_code(path.span) {
+ if generated_code(span) {
return
}
visit::walk_expr(self, ex);
},
ast::ExprPath(ref path) => {
- self.process_path(ex.id, ex.span, path, None);
+ self.process_path(ex.id, path.span, path, None);
visit::walk_path(self, path);
}
+ ast::ExprQPath(ref qpath) => {
+ let mut path = qpath.trait_ref.path.clone();
+ path.segments.push(qpath.item_path.clone());
+ self.process_path(ex.id, ex.span, &path, None);
+ visit::walk_qpath(self, ex.span, &**qpath);
+ }
ast::ExprStruct(ref path, ref fields, ref base) =>
self.process_struct_lit(ex, path, fields, base),
ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args),
"")
}
def::DefVariant(..) => {
- paths_to_process.push((id, p.span, p.clone(), Some(ref_kind)))
+ paths_to_process.push((id, p.clone(), Some(ref_kind)))
}
// FIXME(nrc) what are these doing here?
def::DefStatic(_, _) => {}
*def)
}
}
- for &(id, span, ref path, ref_kind) in paths_to_process.iter() {
- self.process_path(id, span, path, ref_kind);
+ for &(id, ref path, ref_kind) in paths_to_process.iter() {
+ self.process_path(id, path.span, path, ref_kind);
}
self.collecting = false;
self.collected_paths.clear();
use trans::adt;
use trans::base::*;
use trans::build::{AddCase, And, BitCast, Br, CondBr, GEPi, InBoundsGEP, Load};
-use trans::build::{Mul, Not, Store, Sub, add_comment};
+use trans::build::{Not, Store, Sub, add_comment};
use trans::build;
use trans::callee;
use trans::cleanup::{self, CleanupMethods};
let vec_datum = match_datum(val, vec_ty);
let (base, len) = vec_datum.get_vec_base_and_len(bcx);
- let slice_byte_offset = Mul(bcx, vt.llunit_size, C_uint(bcx.ccx(), offset_left));
- let slice_begin = tvec::pointer_add_byte(bcx, base, slice_byte_offset);
+ let slice_begin = InBoundsGEP(bcx, base, &[C_uint(bcx.ccx(), offset_left)]);
let slice_len_offset = C_uint(bcx.ccx(), offset_left + offset_right);
let slice_len = Sub(bcx, len, slice_len_offset);
let slice_ty = ty::mk_slice(bcx.tcx(),
/// Checks whether the binding in `discr` is assigned to anywhere in the expression `body`
fn is_discr_reassigned(bcx: Block, discr: &ast::Expr, body: &ast::Expr) -> bool {
let (vid, field) = match discr.node {
- ast::ExprPath(..) => match bcx.def(discr.id) {
+ ast::ExprPath(_) | ast::ExprQPath(_) => match bcx.def(discr.id) {
def::DefLocal(vid) | def::DefUpvar(vid, _, _) => (vid, None),
_ => return false
},
// Default per-arch clobbers
// Basically what clang does
-#[cfg(any(target_arch = "arm",
- target_arch = "aarch64",
- target_arch = "mips",
- target_arch = "mipsel"))]
+#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
fn get_clobbers() -> String {
"".to_string()
}
}
}
-// Structural comparison: a rather involved form of glue.
-pub fn maybe_name_value(cx: &CrateContext, v: ValueRef, s: &str) {
- if cx.sess().opts.cg.save_temps {
- let buf = CString::from_slice(s.as_bytes());
- unsafe { llvm::LLVMSetValueName(v, buf.as_ptr()) }
- }
-}
-
-
// Used only for creating scalar comparison glue.
#[derive(Copy)]
pub enum scalar_type { nil_type, signed_int, unsigned_int, floating_point, }
use trans::cabi_x86_win64;
use trans::cabi_arm;
use trans::cabi_aarch64;
+use trans::cabi_powerpc;
use trans::cabi_mips;
use trans::type_::Type;
cabi_arm::compute_abi_info(ccx, atys, rty, ret_def, flavor)
},
"mips" => cabi_mips::compute_abi_info(ccx, atys, rty, ret_def),
+ "powerpc" => cabi_powerpc::compute_abi_info(ccx, atys, rty, ret_def),
a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)
[]),
}
--- /dev/null
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use libc::c_uint;
+use llvm;
+use llvm::{Integer, Pointer, Float, Double, Struct, Array};
+use llvm::{StructRetAttribute, ZExtAttribute};
+use trans::cabi::{FnType, ArgType};
+use trans::context::CrateContext;
+use trans::type_::Type;
+
+use std::cmp;
+
+fn align_up_to(off: uint, a: uint) -> uint {
+ return (off + a - 1u) / a * a;
+}
+
+fn align(off: uint, ty: Type) -> uint {
+ let a = ty_align(ty);
+ return align_up_to(off, a);
+}
+
+fn ty_align(ty: Type) -> uint {
+ match ty.kind() {
+ Integer => {
+ unsafe {
+ ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
+ }
+ }
+ Pointer => 4,
+ Float => 4,
+ Double => 8,
+ Struct => {
+ if ty.is_packed() {
+ 1
+ } else {
+ let str_tys = ty.field_types();
+ str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t)))
+ }
+ }
+ Array => {
+ let elt = ty.element_type();
+ ty_align(elt)
+ }
+ _ => panic!("ty_size: unhandled type")
+ }
+}
+
+fn ty_size(ty: Type) -> uint {
+ match ty.kind() {
+ Integer => {
+ unsafe {
+ ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8
+ }
+ }
+ Pointer => 4,
+ Float => 4,
+ Double => 8,
+ Struct => {
+ if ty.is_packed() {
+ let str_tys = ty.field_types();
+ str_tys.iter().fold(0, |s, t| s + ty_size(*t))
+ } else {
+ let str_tys = ty.field_types();
+ let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t));
+ align(size, ty)
+ }
+ }
+ Array => {
+ let len = ty.array_length();
+ let elt = ty.element_type();
+ let eltsz = ty_size(elt);
+ len * eltsz
+ }
+ _ => panic!("ty_size: unhandled type")
+ }
+}
+
+fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
+ if is_reg_ty(ty) {
+ let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None };
+ ArgType::direct(ty, None, None, attr)
+ } else {
+ ArgType::indirect(ty, Some(StructRetAttribute))
+ }
+}
+
+fn classify_arg_ty(ccx: &CrateContext, ty: Type, offset: &mut uint) -> ArgType {
+ let orig_offset = *offset;
+ let size = ty_size(ty) * 8;
+ let mut align = ty_align(ty);
+
+ align = cmp::min(cmp::max(align, 4), 8);
+ *offset = align_up_to(*offset, align);
+ *offset += align_up_to(size, align * 8) / 8;
+
+ if is_reg_ty(ty) {
+ let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None };
+ ArgType::direct(ty, None, None, attr)
+ } else {
+ ArgType::direct(
+ ty,
+ Some(struct_ty(ccx, ty)),
+ padding_ty(ccx, align, orig_offset),
+ None
+ )
+ }
+}
+
+fn is_reg_ty(ty: Type) -> bool {
+ return match ty.kind() {
+ Integer
+ | Pointer
+ | Float
+ | Double => true,
+ _ => false
+ };
+}
+
+fn padding_ty(ccx: &CrateContext, align: uint, offset: uint) -> Option<Type> {
+ if ((align - 1 ) & offset) > 0 {
+ Some(Type::i32(ccx))
+ } else {
+ None
+ }
+}
+
+fn coerce_to_int(ccx: &CrateContext, size: uint) -> Vec<Type> {
+ let int_ty = Type::i32(ccx);
+ let mut args = Vec::new();
+
+ let mut n = size / 32;
+ while n > 0 {
+ args.push(int_ty);
+ n -= 1;
+ }
+
+ let r = size % 32;
+ if r > 0 {
+ unsafe {
+ args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint)));
+ }
+ }
+
+ args
+}
+
+fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
+ let size = ty_size(ty) * 8;
+ Type::struct_(ccx, coerce_to_int(ccx, size).as_slice(), false)
+}
+
+pub fn compute_abi_info(ccx: &CrateContext,
+ atys: &[Type],
+ rty: Type,
+ ret_def: bool) -> FnType {
+ let ret_ty = if ret_def {
+ classify_ret_ty(ccx, rty)
+ } else {
+ ArgType::direct(Type::void(ccx), None, None, None)
+ };
+
+ let sret = ret_ty.is_indirect();
+ let mut arg_tys = Vec::new();
+ let mut offset = if sret { 4 } else { 0 };
+
+ for aty in atys.iter() {
+ let ty = classify_arg_ty(ccx, *aty, &mut offset);
+ arg_tys.push(ty);
+ };
+
+ return FnType {
+ arg_tys: arg_tys,
+ ret_ty: ret_ty,
+ };
+}
debug!("callee::trans(expr={})", expr.repr(bcx.tcx()));
// pick out special kinds of expressions that can be called:
- if let ast::ExprPath(_) = expr.node {
- return trans_def(bcx, bcx.def(expr.id), expr);
+ match expr.node {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
+ return trans_def(bcx, bcx.def(expr.id), expr);
+ }
+ _ => {}
}
// any other expressions are closures:
ty::type_contents(cx, ty).needs_drop(cx)
}
-fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- ty: Ty<'tcx>) -> bool {
+fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
match ty.sty {
ty::ty_struct(def_id, substs) => {
- let fields = ty::struct_fields(ccx.tcx(), def_id, substs);
- fields.len() == 1 && type_is_immediate(ccx, fields[0].mt.ty)
+ let fields = ty::lookup_struct_fields(ccx.tcx(), def_id);
+ fields.len() == 1 && {
+ let ty = ty::lookup_field_type(ccx.tcx(), def_id, fields[0].id, substs);
+ let ty = monomorphize::normalize_associated_type(ccx.tcx(), &ty);
+ type_is_immediate(ccx, ty)
+ }
}
_ => false
}
C_array(llunitty, &vs[])
}
}
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
let def = cx.tcx().def_map.borrow()[e.id];
match def {
def::DefFn(..) | def::DefStaticMethod(..) | def::DefMethod(..) => {
fn get_unique_type_id_of_type<'a>(&mut self, cx: &CrateContext<'a, 'tcx>,
type_: Ty<'tcx>) -> UniqueTypeId {
- // basic type -> {:name of the type:}
- // tuple -> {tuple_(:param-uid:)*}
- // struct -> {struct_:svh: / :node-id:_<(:param-uid:),*> }
- // enum -> {enum_:svh: / :node-id:_<(:param-uid:),*> }
- // enum variant -> {variant_:variant-name:_:enum-uid:}
- // reference (&) -> {& :pointee-uid:}
- // mut reference (&mut) -> {&mut :pointee-uid:}
- // ptr (*) -> {* :pointee-uid:}
- // mut ptr (*mut) -> {*mut :pointee-uid:}
- // unique ptr (~) -> {~ :pointee-uid:}
- // @-ptr (@) -> {@ :pointee-uid:}
- // sized vec ([T; x]) -> {[:size:] :element-uid:}
- // unsized vec ([T]) -> {[] :element-uid:}
- // trait (T) -> {trait_:svh: / :node-id:_<(:param-uid:),*> }
- // closure -> {<unsafe_> <once_> :store-sigil:
- // |(:param-uid:),* <,_...>| -> \
- // :return-type-uid: : (:bounds:)*}
- // function -> {<unsafe_> <abi_> fn( (:param-uid:)* <,_...> ) -> \
- // :return-type-uid:}
- // unique vec box (~[]) -> {HEAP_VEC_BOX<:pointee-uid:>}
- // gc box -> {GC_BOX<:pointee-uid:>}
- // projection (<T as U>::V) -> {<:ty-uid: as :trait-uid:> :: :name-uid: }
+ // basic type -> {:name of the type:}
+ // tuple -> {tuple_(:param-uid:)*}
+ // struct -> {struct_:svh: / :node-id:_<(:param-uid:),*> }
+ // enum -> {enum_:svh: / :node-id:_<(:param-uid:),*> }
+ // enum variant -> {variant_:variant-name:_:enum-uid:}
+ // reference (&) -> {& :pointee-uid:}
+ // mut reference (&mut) -> {&mut :pointee-uid:}
+ // ptr (*) -> {* :pointee-uid:}
+ // mut ptr (*mut) -> {*mut :pointee-uid:}
+ // unique ptr (~) -> {~ :pointee-uid:}
+ // @-ptr (@) -> {@ :pointee-uid:}
+ // sized vec ([T; x]) -> {[:size:] :element-uid:}
+ // unsized vec ([T]) -> {[] :element-uid:}
+ // trait (T) -> {trait_:svh: / :node-id:_<(:param-uid:),*> }
+ // closure -> {<unsafe_> <once_> :store-sigil: |(:param-uid:),* <,_...>| -> \
+ // :return-type-uid: : (:bounds:)*}
+ // function -> {<unsafe_> <abi_> fn( (:param-uid:)* <,_...> ) -> \
+ // :return-type-uid:}
+ // unique vec box (~[]) -> {HEAP_VEC_BOX<:pointee-uid:>}
+ // gc box -> {GC_BOX<:pointee-uid:>}
match self.type_to_unique_id.get(&type_).cloned() {
Some(unique_type_id) => return unique_type_id,
principal.substs,
&mut unique_type_id);
},
- ty::ty_projection(ref projection) => {
- unique_type_id.push_str("<");
-
- let self_ty = projection.trait_ref.self_ty();
- let self_type_id = self.get_unique_type_id_of_type(cx, self_ty);
- let self_type_id = self.get_unique_type_id_as_string(self_type_id);
- unique_type_id.push_str(&self_type_id[]);
-
- unique_type_id.push_str(" as ");
-
- from_def_id_and_substs(self,
- cx,
- projection.trait_ref.def_id,
- projection.trait_ref.substs,
- &mut unique_type_id);
-
- unique_type_id.push_str(">::");
- unique_type_id.push_str(token::get_name(projection.item_name).get());
- },
ty::ty_bare_fn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => {
if unsafety == ast::Unsafety::Unsafe {
unique_type_id.push_str("unsafe ");
closure_ty,
&mut unique_type_id);
},
- ty::ty_err |
- ty::ty_infer(_) |
- ty::ty_open(_) |
- ty::ty_param(_) => {
+ _ => {
cx.sess().bug(&format!("get_unique_type_id_of_type() - unexpected type: {}, {:?}",
&ppaux::ty_to_string(cx.tcx(), type_)[],
type_.sty)[])
ast::ExprLit(_) |
ast::ExprBreak(_) |
ast::ExprAgain(_) |
- ast::ExprPath(_) => {}
+ ast::ExprPath(_) |
+ ast::ExprQPath(_) => {}
ast::ExprCast(ref sub_exp, _) |
ast::ExprAddrOf(_, ref sub_exp) |
ty::ty_unboxed_closure(..) => {
output.push_str("closure");
}
- ty::ty_projection(ref projection) => {
- output.push_str("<");
- let self_ty = projection.trait_ref.self_ty();
- push_debuginfo_type_name(cx, self_ty, true, output);
-
- output.push_str(" as ");
-
- push_item_name(cx, projection.trait_ref.def_id, false, output);
- push_type_params(cx, projection.trait_ref.substs, output);
-
- output.push_str(">::");
- output.push_str(token::get_name(projection.item_name).get());
- }
ty::ty_err |
ty::ty_infer(_) |
ty::ty_open(_) |
+ ty::ty_projection(..) |
ty::ty_param(_) => {
cx.sess().bug(&format!("debuginfo: Trying to create type name for \
unexpected type: {}", ppaux::ty_to_string(cx.tcx(), t))[]);
ast::ExprParen(ref e) => {
trans(bcx, &**e)
}
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
trans_def(bcx, expr, bcx.def(expr.id))
}
ast::ExprField(ref base, ident) => {
tvec::vec_types(bcx,
ty::sequence_element_type(bcx.tcx(),
base_datum.ty));
- base::maybe_name_value(bcx.ccx(), vt.llunit_size, "unit_sz");
let (base, len) = base_datum.get_vec_base_and_len(bcx);
ast::ExprParen(ref e) => {
trans_into(bcx, &**e, dest)
}
- ast::ExprPath(_) => {
+ ast::ExprPath(_) | ast::ExprQPath(_) => {
trans_def_dps_unadjusted(bcx, expr, bcx.def(expr.id), dest)
}
ast::ExprIf(ref cond, ref thn, ref els) => {
return C_uint(cx, llsize_of_alloc(cx, ty));
}
-// Returns the "default" size of t (see above), or 1 if the size would
-// be zero. This is important for things like vectors that expect
-// space to be consumed.
-pub fn nonzero_llsize_of(cx: &CrateContext, ty: Type) -> ValueRef {
- if llbitsize_of_real(cx, ty) == 0 {
- unsafe { llvm::LLVMConstInt(cx.int_type().to_ref(), 1, False) }
- } else {
- llsize_of(cx, ty)
- }
-}
-
// Returns the preferred alignment of the given type for the current target.
// The preferred alignment may be larger than the alignment used when
// packing the type into structs. This will be used for things like
param_substs,
callee_substs)
}
+ traits::VtableObject(ref data) => {
+ let trait_item_def_ids =
+ ty::trait_item_def_ids(ccx.tcx(), trait_id);
+ let method_offset_in_trait =
+ trait_item_def_ids.iter()
+ .position(|item| item.def_id() == method_id)
+ .unwrap();
+ let (llfn, ty) =
+ trans_object_shim(ccx, data.object_ty, trait_id, method_offset_in_trait);
+ immediate_rvalue(llfn, ty)
+ }
_ => {
tcx.sess.bug(&format!("static call to invalid vtable: {}",
vtbl.repr(tcx))[]);
Callee { bcx: bcx, data: Fn(llfn) }
}
traits::VtableObject(ref data) => {
- let llfn = trans_object_shim(bcx.ccx(), data.object_ty, trait_id, n_method);
+ let (llfn, _) = trans_object_shim(bcx.ccx(), data.object_ty, trait_id, n_method);
Callee { bcx: bcx, data: Fn(llfn) }
}
traits::VtableBuiltin(..) |
object_ty: Ty<'tcx>,
trait_id: ast::DefId,
method_offset_in_trait: uint)
- -> ValueRef
+ -> (ValueRef, Ty<'tcx>)
{
let _icx = push_ctxt("trans_object_shim");
let tcx = ccx.tcx();
finish_fn(&fcx, bcx, sig.output);
- llfn
+ (llfn, method_bare_fn_ty)
}
/// Creates a returns a dynamic vtable for the given type and vtable origin.
mod cabi_arm;
mod cabi_aarch64;
mod cabi_mips;
+mod cabi_powerpc;
mod foreign;
mod intrinsic;
mod debuginfo;
use trans::expr;
use trans::glue;
use trans::machine;
-use trans::machine::{nonzero_llsize_of, llsize_of_alloc};
+use trans::machine::llsize_of_alloc;
use trans::type_::Type;
use trans::type_of;
use middle::ty::{self, Ty};
Load(bcx, expr::get_dataptr(bcx, vptr))
}
-pub fn pointer_add_byte(bcx: Block, ptr: ValueRef, bytes: ValueRef) -> ValueRef {
- let _icx = push_ctxt("tvec::pointer_add_byte");
- let old_ty = val_ty(ptr);
- let bptr = PointerCast(bcx, ptr, Type::i8p(bcx.ccx()));
- return PointerCast(bcx, InBoundsGEP(bcx, bptr, &[bytes]), old_ty);
-}
-
pub fn make_drop_glue_unboxed<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
vptr: ValueRef,
unit_ty: Ty<'tcx>,
pub struct VecTypes<'tcx> {
pub unit_ty: Ty<'tcx>,
pub llunit_ty: Type,
- pub llunit_size: ValueRef,
pub llunit_alloc_size: u64
}
impl<'tcx> VecTypes<'tcx> {
pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String {
- format!("VecTypes {{unit_ty={}, llunit_ty={}, \
- llunit_size={}, llunit_alloc_size={}}}",
+ format!("VecTypes {{unit_ty={}, llunit_ty={}, llunit_alloc_size={}}}",
ty_to_string(ccx.tcx(), self.unit_ty),
ccx.tn().type_to_string(self.llunit_ty),
- ccx.tn().val_to_string(self.llunit_size),
self.llunit_alloc_size)
}
}
-> VecTypes<'tcx> {
let ccx = bcx.ccx();
let llunit_ty = type_of::type_of(ccx, unit_ty);
- let llunit_size = nonzero_llsize_of(ccx, llunit_ty);
let llunit_alloc_size = llsize_of_alloc(ccx, llunit_ty);
VecTypes {
unit_ty: unit_ty,
llunit_ty: llunit_ty,
- llunit_size: llunit_size,
llunit_alloc_size: llunit_alloc_size
}
}
let fcx = bcx.fcx;
let vt = vec_types(bcx, unit_ty);
- let fill = Mul(bcx, len, vt.llunit_size);
if vt.llunit_alloc_size == 0 {
// Special-case vectors with elements of size 0 so they don't go out of bounds (#9890)
- iter_vec_loop(bcx, data_ptr, &vt, fill, f)
+ iter_vec_loop(bcx, data_ptr, &vt, len, f)
} else {
// Calculate the last pointer address we want to handle.
- // FIXME (#3729): Optimize this when the size of the unit type is
- // statically known to not use pointer casts, which tend to confuse
- // LLVM.
- let data_end_ptr = pointer_add_byte(bcx, data_ptr, fill);
+ let data_end_ptr = InBoundsGEP(bcx, data_ptr, &[len]);
// Now perform the iteration.
let header_bcx = fcx.new_temp_block("iter_vec_loop_header");
{
let mut projections = Vec::new();
+ // the trait reference introduces a binding level here, so
+ // we need to shift the `rscope`. It'd be nice if we could
+ // do away with this rscope stuff and work this knowledge
+ // into resolve_lifetimes, as we do with non-omitted
+ // lifetimes. Oh well, not there yet.
+ let shifted_rscope = ShiftedRscope::new(rscope);
+
let trait_ref =
- instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref,
+ instantiate_trait_ref(this, &shifted_rscope, &ast_trait_ref.trait_ref,
self_ty, Some(&mut projections));
for projection in projections.into_iter() {
}
}
+fn object_path_to_poly_trait_ref<'a,'tcx>(
+ this: &AstConv<'tcx>,
+ rscope: &RegionScope,
+ trait_def_id: ast::DefId,
+ path: &ast::Path,
+ mut projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
+ -> ty::PolyTraitRef<'tcx>
+{
+ // we are introducing a binder here, so shift the
+ // anonymous regions depth to account for that
+ let shifted_rscope = ShiftedRscope::new(rscope);
+
+ let mut tmp = Vec::new();
+ let trait_ref = ty::Binder(ast_path_to_trait_ref(this,
+ &shifted_rscope,
+ trait_def_id,
+ None,
+ path,
+ Some(&mut tmp)));
+ projections.extend(tmp.into_iter().map(ty::Binder));
+ trait_ref
+}
+
fn ast_path_to_trait_ref<'a,'tcx>(
this: &AstConv<'tcx>,
rscope: &RegionScope,
debug!("ast_path_to_trait_ref {:?}", path);
let trait_def = this.get_trait_def(trait_def_id);
- // the trait reference introduces a binding level here, so
- // we need to shift the `rscope`. It'd be nice if we could
- // do away with this rscope stuff and work this knowledge
- // into resolve_lifetimes, as we do with non-omitted
- // lifetimes. Oh well, not there yet.
- let shifted_rscope = ShiftedRscope::new(rscope);
-
let (regions, types, assoc_bindings) = match path.segments.last().unwrap().parameters {
ast::AngleBracketedParameters(ref data) => {
// For now, require that parenthetical notation be used
the crate attributes to enable");
}
- convert_angle_bracketed_parameters(this, &shifted_rscope, data)
+ convert_angle_bracketed_parameters(this, rscope, data)
}
ast::ParenthesizedParameters(ref data) => {
// For now, require that parenthetical notation be used
};
let substs = create_substs_for_ast_path(this,
- &shifted_rscope,
+ rscope,
path.span,
&trait_def.generics,
self_ty,
match this.tcx().def_map.borrow().get(&id) {
Some(&def::DefTrait(trait_def_id)) => {
let mut projection_bounds = Vec::new();
- let trait_ref = ty::Binder(ast_path_to_trait_ref(this,
- rscope,
- trait_def_id,
- None,
- path,
- Some(&mut projection_bounds)));
- let projection_bounds = projection_bounds.into_iter()
- .map(ty::Binder)
- .collect();
+ let trait_ref = object_path_to_poly_trait_ref(this,
+ rscope,
+ trait_def_id,
+ path,
+ &mut projection_bounds);
Ok((trait_ref, projection_bounds))
}
_ => {
debug!("qpath_to_ty: trait_ref={}", trait_ref.repr(this.tcx()));
+ // `<T as Trait>::U<V>` shouldn't parse right now.
+ assert!(qpath.item_path.parameters.is_empty());
+
return this.projected_ty(ast_ty.span,
trait_ref,
- qpath.item_name.name);
+ qpath.item_path.identifier.name);
}
// Parses the programmer's textual representation of a type into our
// N.B. this case overlaps somewhat with
// TyObjectSum, see that fn for details
let mut projection_bounds = Vec::new();
- let trait_ref = ast_path_to_trait_ref(this,
- rscope,
- trait_def_id,
- None,
- path,
- Some(&mut projection_bounds));
- let trait_ref = ty::Binder(trait_ref);
- let projection_bounds = projection_bounds.into_iter()
- .map(ty::Binder)
- .collect();
+
+ let trait_ref = object_path_to_poly_trait_ref(this,
+ rscope,
+ trait_def_id,
+ path,
+ &mut projection_bounds);
+
trait_ref_to_object_type(this, rscope, path.span,
trait_ref, projection_bounds, &[])
}
};
instantiate_path(pcx.fcx, path, ty::lookup_item_type(tcx, enum_def_id),
- def, pat.span, pat.id);
+ None, def, pat.span, pat.id);
let pat_ty = fcx.node_ty(pat.id);
demand::eqtype(fcx, pat.span, expected, pat_ty);
} else {
ctor_scheme
};
- instantiate_path(pcx.fcx, path, path_scheme, def, pat.span, pat.id);
+ instantiate_path(pcx.fcx, path, path_scheme, None, def, pat.span, pat.id);
let pat_ty = fcx.node_ty(pat.id);
demand::eqtype(fcx, pat.span, expected, pat_ty);
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use middle::infer;
+use middle::traits;
+use middle::ty::{self};
+use middle::subst::{self, Subst, Substs, VecPerParamSpace};
+use util::ppaux::{self, Repr};
+
+use syntax::ast;
+use syntax::codemap::{Span};
+use syntax::parse::token;
+
+use super::assoc;
+
+/// Checks that a method from an impl conforms to the signature of
+/// the same method as declared in the trait.
+///
+/// # Parameters
+///
+/// - impl_m: type of the method we are checking
+/// - impl_m_span: span to use for reporting errors
+/// - impl_m_body_id: id of the method body
+/// - trait_m: the method in the trait
+/// - impl_trait_ref: the TraitRef corresponding to the trait implementation
+
+pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
+ impl_m: &ty::Method<'tcx>,
+ impl_m_span: Span,
+ impl_m_body_id: ast::NodeId,
+ trait_m: &ty::Method<'tcx>,
+ impl_trait_ref: &ty::TraitRef<'tcx>) {
+ debug!("compare_impl_method(impl_trait_ref={})",
+ impl_trait_ref.repr(tcx));
+
+ debug!("compare_impl_method: impl_trait_ref (liberated) = {}",
+ impl_trait_ref.repr(tcx));
+
+ let infcx = infer::new_infer_ctxt(tcx);
+ let mut fulfillment_cx = traits::FulfillmentContext::new();
+
+ let trait_to_impl_substs = &impl_trait_ref.substs;
+
+ // Try to give more informative error messages about self typing
+ // mismatches. Note that any mismatch will also be detected
+ // below, where we construct a canonical function type that
+ // includes the self parameter as a normal parameter. It's just
+ // that the error messages you get out of this code are a bit more
+ // inscrutable, particularly for cases where one method has no
+ // self.
+ match (&trait_m.explicit_self, &impl_m.explicit_self) {
+ (&ty::StaticExplicitSelfCategory,
+ &ty::StaticExplicitSelfCategory) => {}
+ (&ty::StaticExplicitSelfCategory, _) => {
+ tcx.sess.span_err(
+ impl_m_span,
+ format!("method `{}` has a `{}` declaration in the impl, \
+ but not in the trait",
+ token::get_name(trait_m.name),
+ ppaux::explicit_self_category_to_str(
+ &impl_m.explicit_self)).as_slice());
+ return;
+ }
+ (_, &ty::StaticExplicitSelfCategory) => {
+ tcx.sess.span_err(
+ impl_m_span,
+ format!("method `{}` has a `{}` declaration in the trait, \
+ but not in the impl",
+ token::get_name(trait_m.name),
+ ppaux::explicit_self_category_to_str(
+ &trait_m.explicit_self)).as_slice());
+ return;
+ }
+ _ => {
+ // Let the type checker catch other errors below
+ }
+ }
+
+ let num_impl_m_type_params = impl_m.generics.types.len(subst::FnSpace);
+ let num_trait_m_type_params = trait_m.generics.types.len(subst::FnSpace);
+ if num_impl_m_type_params != num_trait_m_type_params {
+ span_err!(tcx.sess, impl_m_span, E0049,
+ "method `{}` has {} type parameter{} \
+ but its trait declaration has {} type parameter{}",
+ token::get_name(trait_m.name),
+ num_impl_m_type_params,
+ if num_impl_m_type_params == 1 {""} else {"s"},
+ num_trait_m_type_params,
+ if num_trait_m_type_params == 1 {""} else {"s"});
+ return;
+ }
+
+ if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() {
+ span_err!(tcx.sess, impl_m_span, E0050,
+ "method `{}` has {} parameter{} \
+ but the declaration in trait `{}` has {}",
+ token::get_name(trait_m.name),
+ impl_m.fty.sig.0.inputs.len(),
+ if impl_m.fty.sig.0.inputs.len() == 1 {""} else {"s"},
+ ty::item_path_str(tcx, trait_m.def_id),
+ trait_m.fty.sig.0.inputs.len());
+ return;
+ }
+
+ // This code is best explained by example. Consider a trait:
+ //
+ // trait Trait<'t,T> {
+ // fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
+ // }
+ //
+ // And an impl:
+ //
+ // impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
+ // fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
+ // }
+ //
+ // We wish to decide if those two method types are compatible.
+ //
+ // We start out with trait_to_impl_substs, that maps the trait
+ // type parameters to impl type parameters. This is taken from the
+ // impl trait reference:
+ //
+ // trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
+ //
+ // We create a mapping `dummy_substs` that maps from the impl type
+ // parameters to fresh types and regions. For type parameters,
+ // this is the identity transform, but we could as well use any
+ // skolemized types. For regions, we convert from bound to free
+ // regions (Note: but only early-bound regions, i.e., those
+ // declared on the impl or used in type parameter bounds).
+ //
+ // impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
+ //
+ // Now we can apply skol_substs to the type of the impl method
+ // to yield a new function type in terms of our fresh, skolemized
+ // types:
+ //
+ // <'b> fn(t: &'i0 U0, m: &'b) -> Foo
+ //
+ // We now want to extract and substitute the type of the *trait*
+ // method and compare it. To do so, we must create a compound
+ // substitution by combining trait_to_impl_substs and
+ // impl_to_skol_substs, and also adding a mapping for the method
+ // type parameters. We extend the mapping to also include
+ // the method parameters.
+ //
+ // trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 }
+ //
+ // Applying this to the trait method type yields:
+ //
+ // <'a> fn(t: &'i0 U0, m: &'a) -> Foo
+ //
+ // This type is also the same but the name of the bound region ('a
+ // vs 'b). However, the normal subtyping rules on fn types handle
+ // this kind of equivalency just fine.
+ //
+ // We now use these subsititions to ensure that all declared bounds are
+ // satisfied by the implementation's method.
+ //
+ // We do this by creating a parameter environment which contains a
+ // substition corresponding to impl_to_skol_substs. We then build
+ // trait_to_skol_substs and use it to convert the predicates contained
+ // in the trait_m.generics to the skolemized form.
+ //
+ // Finally we register each of these predicates as an obligation in
+ // a fresh FulfillmentCtxt, and invoke select_all_or_error.
+
+ // Create a parameter environment that represents the implementation's
+ // method.
+ let impl_param_env =
+ ty::ParameterEnvironment::for_item(tcx, impl_m.def_id.node);
+
+ // Create mapping from impl to skolemized.
+ let impl_to_skol_substs = &impl_param_env.free_substs;
+
+ // Create mapping from trait to skolemized.
+ let trait_to_skol_substs =
+ trait_to_impl_substs
+ .subst(tcx, impl_to_skol_substs)
+ .with_method(impl_to_skol_substs.types.get_slice(subst::FnSpace).to_vec(),
+ impl_to_skol_substs.regions().get_slice(subst::FnSpace).to_vec());
+ debug!("compare_impl_method: trait_to_skol_substs={}",
+ trait_to_skol_substs.repr(tcx));
+
+ // Check region bounds. FIXME(@jroesch) refactor this away when removing
+ // ParamBounds.
+ if !check_region_bounds_on_impl_method(tcx,
+ impl_m_span,
+ impl_m,
+ &trait_m.generics,
+ &impl_m.generics,
+ &trait_to_skol_substs,
+ impl_to_skol_substs) {
+ return;
+ }
+
+ // Create obligations for each predicate declared by the impl
+ // definition in the context of the trait's parameter
+ // environment. We can't just use `impl_env.caller_bounds`,
+ // however, because we want to replace all late-bound regions with
+ // region variables.
+ let impl_bounds =
+ impl_m.generics.to_bounds(tcx, impl_to_skol_substs);
+
+ let (impl_bounds, _) =
+ infcx.replace_late_bound_regions_with_fresh_var(
+ impl_m_span,
+ infer::HigherRankedType,
+ &ty::Binder(impl_bounds));
+ debug!("compare_impl_method: impl_bounds={}",
+ impl_bounds.repr(tcx));
+
+ // // Normalize the associated types in the impl_bounds.
+ // let traits::Normalized { value: impl_bounds, .. } =
+ // traits::normalize(&mut selcx, normalize_cause.clone(), &impl_bounds);
+
+ // Normalize the associated types in the trait_bounds.
+ let trait_bounds = trait_m.generics.to_bounds(tcx, &trait_to_skol_substs);
+ // let traits::Normalized { value: trait_bounds, .. } =
+ // traits::normalize(&mut selcx, normalize_cause, &trait_bounds);
+
+ // Obtain the predicate split predicate sets for each.
+ let trait_pred = trait_bounds.predicates.split();
+ let impl_pred = impl_bounds.predicates.split();
+
+ // This is the only tricky bit of the new way we check implementation methods
+ // We need to build a set of predicates where only the FnSpace bounds
+ // are from the trait and we assume all other bounds from the implementation
+ // to be previously satisfied.
+ //
+ // We then register the obligations from the impl_m and check to see
+ // if all constraints hold.
+ let hybrid_preds = VecPerParamSpace::new(
+ impl_pred.types,
+ impl_pred.selfs,
+ trait_pred.fns
+ );
+
+ // Construct trait parameter environment and then shift it into the skolemized viewpoint.
+ let mut trait_param_env = impl_param_env.clone();
+ // The key step here is to update the caller_bounds's predicates to be
+ // the new hybrid bounds we computed.
+ trait_param_env.caller_bounds.predicates = hybrid_preds;
+
+ debug!("compare_impl_method: trait_bounds={}",
+ trait_param_env.caller_bounds.repr(tcx));
+
+ let mut selcx = traits::SelectionContext::new(&infcx, &trait_param_env);
+
+ let normalize_cause =
+ traits::ObligationCause::misc(impl_m_span, impl_m_body_id);
+
+ for predicate in impl_pred.fns.into_iter() {
+ let traits::Normalized { value: predicate, .. } =
+ traits::normalize(&mut selcx, normalize_cause.clone(), &predicate);
+
+ let cause = traits::ObligationCause {
+ span: impl_m_span,
+ body_id: impl_m_body_id,
+ code: traits::ObligationCauseCode::CompareImplMethodObligation
+ };
+
+ fulfillment_cx.register_predicate_obligation(
+ &infcx,
+ traits::Obligation::new(cause, predicate));
+ }
+
+ // We now need to check that the signature of the impl method is
+ // compatible with that of the trait method. We do this by
+ // checking that `impl_fty <: trait_fty`.
+ //
+ // FIXME. Unfortunately, this doesn't quite work right now because
+ // associated type normalization is not integrated into subtype
+ // checks. For the comparison to be valid, we need to
+ // normalize the associated types in the impl/trait methods
+ // first. However, because function types bind regions, just
+ // calling `normalize_associated_types_in` would have no effect on
+ // any associated types appearing in the fn arguments or return
+ // type.
+
+ // Compute skolemized form of impl and trait method tys.
+ let impl_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_m.fty.clone()));
+ let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
+ let trait_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(trait_m.fty.clone()));
+ let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
+
+ let err = infcx.try(|snapshot| {
+ let origin = infer::MethodCompatCheck(impl_m_span);
+
+ let (impl_sig, _) =
+ infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
+ infer::HigherRankedType,
+ &impl_m.fty.sig);
+ let impl_sig =
+ impl_sig.subst(tcx, impl_to_skol_substs);
+ let impl_sig =
+ assoc::normalize_associated_types_in(&infcx,
+ &impl_param_env,
+ &mut fulfillment_cx,
+ impl_m_span,
+ impl_m_body_id,
+ &impl_sig);
+ let impl_fty =
+ ty::mk_bare_fn(tcx,
+ None,
+ tcx.mk_bare_fn(ty::BareFnTy { unsafety: impl_m.fty.unsafety,
+ abi: impl_m.fty.abi,
+ sig: ty::Binder(impl_sig) }));
+ debug!("compare_impl_method: impl_fty={}",
+ impl_fty.repr(tcx));
+
+ let (trait_sig, skol_map) =
+ infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
+ let trait_sig =
+ trait_sig.subst(tcx, &trait_to_skol_substs);
+ let trait_sig =
+ assoc::normalize_associated_types_in(&infcx,
+ &impl_param_env,
+ &mut fulfillment_cx,
+ impl_m_span,
+ impl_m_body_id,
+ &trait_sig);
+ let trait_fty =
+ ty::mk_bare_fn(tcx,
+ None,
+ tcx.mk_bare_fn(ty::BareFnTy { unsafety: trait_m.fty.unsafety,
+ abi: trait_m.fty.abi,
+ sig: ty::Binder(trait_sig) }));
+
+ debug!("compare_impl_method: trait_fty={}",
+ trait_fty.repr(tcx));
+
+ try!(infer::mk_subty(&infcx, false, origin, impl_fty, trait_fty));
+
+ infcx.leak_check(&skol_map, snapshot)
+ });
+
+ match err {
+ Ok(()) => { }
+ Err(terr) => {
+ debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
+ impl_fty.repr(tcx),
+ trait_fty.repr(tcx));
+ span_err!(tcx.sess, impl_m_span, E0053,
+ "method `{}` has an incompatible type for trait: {}",
+ token::get_name(trait_m.name),
+ ty::type_err_to_str(tcx, &terr));
+ return;
+ }
+ }
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ match fulfillment_cx.select_all_or_error(&infcx, &trait_param_env) {
+ Err(ref errors) => { traits::report_fulfillment_errors(&infcx, errors) }
+ Ok(_) => {}
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of lifetime
+ // parameters.
+ infcx.resolve_regions_and_report_errors(impl_m_body_id);
+
+ fn check_region_bounds_on_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
+ span: Span,
+ impl_m: &ty::Method<'tcx>,
+ trait_generics: &ty::Generics<'tcx>,
+ impl_generics: &ty::Generics<'tcx>,
+ trait_to_skol_substs: &Substs<'tcx>,
+ impl_to_skol_substs: &Substs<'tcx>)
+ -> bool
+ {
+
+ let trait_params = trait_generics.regions.get_slice(subst::FnSpace);
+ let impl_params = impl_generics.regions.get_slice(subst::FnSpace);
+
+ debug!("check_region_bounds_on_impl_method: \
+ trait_generics={} \
+ impl_generics={} \
+ trait_to_skol_substs={} \
+ impl_to_skol_substs={}",
+ trait_generics.repr(tcx),
+ impl_generics.repr(tcx),
+ trait_to_skol_substs.repr(tcx),
+ impl_to_skol_substs.repr(tcx));
+
+ // Must have same number of early-bound lifetime parameters.
+ // Unfortunately, if the user screws up the bounds, then this
+ // will change classification between early and late. E.g.,
+ // if in trait we have `<'a,'b:'a>`, and in impl we just have
+ // `<'a,'b>`, then we have 2 early-bound lifetime parameters
+ // in trait but 0 in the impl. But if we report "expected 2
+ // but found 0" it's confusing, because it looks like there
+ // are zero. Since I don't quite know how to phrase things at
+ // the moment, give a kind of vague error message.
+ if trait_params.len() != impl_params.len() {
+ tcx.sess.span_err(
+ span,
+ &format!("lifetime parameters or bounds on method `{}` do \
+ not match the trait declaration",
+ token::get_name(impl_m.name))[]);
+ return false;
+ }
+
+ return true;
+ }
+}
pub use self::LvaluePreference::*;
pub use self::Expectation::*;
+pub use self::compare_method::compare_impl_method;
use self::IsBinopAssignment::*;
use self::TupleArgumentsFlag::*;
use middle::lang_items::TypeIdLangItem;
use lint;
use util::common::{block_query, indenter, loop_query};
-use util::ppaux::{self, UserString, Repr};
+use util::ppaux::{self, Repr};
use util::nodemap::{DefIdMap, FnvHashMap, NodeMap};
use std::cell::{Cell, Ref, RefCell};
pub mod wf;
mod closure;
mod callee;
+mod compare_method;
-/// Fields that are part of a `FnCtxt` which are inherited by
/// closures defined within the function. For example:
///
/// fn foo() {
}
}
-/// Checks that a method from an impl conforms to the signature of
-/// the same method as declared in the trait.
-///
-/// # Parameters
-///
-/// - impl_generics: the generics declared on the impl itself (not the method!)
-/// - impl_m: type of the method we are checking
-/// - impl_m_span: span to use for reporting errors
-/// - impl_m_body_id: id of the method body
-/// - trait_m: the method in the trait
-/// - trait_to_impl_substs: the substitutions used on the type of the trait
-fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
- impl_m: &ty::Method<'tcx>,
- impl_m_span: Span,
- impl_m_body_id: ast::NodeId,
- trait_m: &ty::Method<'tcx>,
- impl_trait_ref: &ty::TraitRef<'tcx>) {
- debug!("compare_impl_method(impl_trait_ref={})",
- impl_trait_ref.repr(tcx));
-
- debug!("impl_trait_ref (liberated) = {}",
- impl_trait_ref.repr(tcx));
-
- let infcx = infer::new_infer_ctxt(tcx);
- let mut fulfillment_cx = traits::FulfillmentContext::new();
-
- let trait_to_impl_substs = &impl_trait_ref.substs;
-
- // Try to give more informative error messages about self typing
- // mismatches. Note that any mismatch will also be detected
- // below, where we construct a canonical function type that
- // includes the self parameter as a normal parameter. It's just
- // that the error messages you get out of this code are a bit more
- // inscrutable, particularly for cases where one method has no
- // self.
- match (&trait_m.explicit_self, &impl_m.explicit_self) {
- (&ty::StaticExplicitSelfCategory,
- &ty::StaticExplicitSelfCategory) => {}
- (&ty::StaticExplicitSelfCategory, _) => {
- tcx.sess.span_err(
- impl_m_span,
- &format!("method `{}` has a `{}` declaration in the impl, \
- but not in the trait",
- token::get_name(trait_m.name),
- ppaux::explicit_self_category_to_str(
- &impl_m.explicit_self))[]);
- return;
- }
- (_, &ty::StaticExplicitSelfCategory) => {
- tcx.sess.span_err(
- impl_m_span,
- &format!("method `{}` has a `{}` declaration in the trait, \
- but not in the impl",
- token::get_name(trait_m.name),
- ppaux::explicit_self_category_to_str(
- &trait_m.explicit_self))[]);
- return;
- }
- _ => {
- // Let the type checker catch other errors below
- }
- }
-
- let num_impl_m_type_params = impl_m.generics.types.len(subst::FnSpace);
- let num_trait_m_type_params = trait_m.generics.types.len(subst::FnSpace);
- if num_impl_m_type_params != num_trait_m_type_params {
- span_err!(tcx.sess, impl_m_span, E0049,
- "method `{}` has {} type parameter{} \
- but its trait declaration has {} type parameter{}",
- token::get_name(trait_m.name),
- num_impl_m_type_params,
- if num_impl_m_type_params == 1 {""} else {"s"},
- num_trait_m_type_params,
- if num_trait_m_type_params == 1 {""} else {"s"});
- return;
- }
-
- if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() {
- span_err!(tcx.sess, impl_m_span, E0050,
- "method `{}` has {} parameter{} \
- but the declaration in trait `{}` has {}",
- token::get_name(trait_m.name),
- impl_m.fty.sig.0.inputs.len(),
- if impl_m.fty.sig.0.inputs.len() == 1 {""} else {"s"},
- ty::item_path_str(tcx, trait_m.def_id),
- trait_m.fty.sig.0.inputs.len());
- return;
- }
-
- // This code is best explained by example. Consider a trait:
- //
- // trait Trait<'t,T> {
- // fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
- // }
- //
- // And an impl:
- //
- // impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
- // fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
- // }
- //
- // We wish to decide if those two method types are compatible.
- //
- // We start out with trait_to_impl_substs, that maps the trait
- // type parameters to impl type parameters. This is taken from the
- // impl trait reference:
- //
- // trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
- //
- // We create a mapping `dummy_substs` that maps from the impl type
- // parameters to fresh types and regions. For type parameters,
- // this is the identity transform, but we could as well use any
- // skolemized types. For regions, we convert from bound to free
- // regions (Note: but only early-bound regions, i.e., those
- // declared on the impl or used in type parameter bounds).
- //
- // impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
- //
- // Now we can apply skol_substs to the type of the impl method
- // to yield a new function type in terms of our fresh, skolemized
- // types:
- //
- // <'b> fn(t: &'i0 U0, m: &'b) -> Foo
- //
- // We now want to extract and substitute the type of the *trait*
- // method and compare it. To do so, we must create a compound
- // substitution by combining trait_to_impl_substs and
- // impl_to_skol_substs, and also adding a mapping for the method
- // type parameters. We extend the mapping to also include
- // the method parameters.
- //
- // trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 }
- //
- // Applying this to the trait method type yields:
- //
- // <'a> fn(t: &'i0 U0, m: &'a) -> Foo
- //
- // This type is also the same but the name of the bound region ('a
- // vs 'b). However, the normal subtyping rules on fn types handle
- // this kind of equivalency just fine.
-
- // Create mapping from impl to skolemized.
- let impl_param_env = ty::construct_parameter_environment(tcx, &impl_m.generics, impl_m_body_id);
- let impl_to_skol_substs = &impl_param_env.free_substs;
-
- // Create mapping from trait to skolemized.
- let trait_to_skol_substs =
- trait_to_impl_substs
- .subst(tcx, impl_to_skol_substs)
- .with_method(impl_to_skol_substs.types.get_slice(subst::FnSpace).to_vec(),
- impl_to_skol_substs.regions().get_slice(subst::FnSpace).to_vec());
-
- // Check region bounds.
- if !check_region_bounds_on_impl_method(tcx,
- impl_m_span,
- impl_m,
- &trait_m.generics,
- &impl_m.generics,
- &trait_to_skol_substs,
- impl_to_skol_substs) {
- return;
- }
-
- // Check bounds. Note that the bounds from the impl may reference
- // late-bound regions declared on the impl, so liberate those.
- // This requires two artificial binding scopes -- one for the impl,
- // and one for the method.
- //
- // An example would be:
- //
- // trait Foo<T> { fn method<U:Bound<T>>() { ... } }
- //
- // impl<'a> Foo<&'a T> for &'a U {
- // fn method<U:Bound<&'a T>>() { ... }
- // }
- //
- // Here, the region parameter `'a` is late-bound, so in the bound
- // `Bound<&'a T>`, the lifetime `'a` will be late-bound with a
- // depth of 3 (it is nested within 3 binders: the impl, method,
- // and trait-ref itself). So when we do the liberation, we have
- // two introduce two `ty::Binder` scopes, one for the impl and one
- // the method.
- //
- // The only late-bounded regions that can possibly appear here are
- // from the impl, not the method. This is because region
- // parameters declared on the method which appear in a type bound
- // would be early bound. On the trait side, there can be no
- // late-bound lifetimes because trait definitions do not introduce
- // a late region binder.
- let trait_bounds =
- trait_m.generics.types.get_slice(subst::FnSpace).iter()
- .map(|trait_param_def| &trait_param_def.bounds);
- let impl_bounds =
- impl_m.generics.types.get_slice(subst::FnSpace).iter()
- .map(|impl_param_def| &impl_param_def.bounds);
- for (i, (trait_param_bounds, impl_param_bounds)) in
- trait_bounds.zip(impl_bounds).enumerate()
- {
- // Check that the impl does not require any builtin-bounds
- // that the trait does not guarantee:
- let extra_bounds =
- impl_param_bounds.builtin_bounds -
- trait_param_bounds.builtin_bounds;
- if !extra_bounds.is_empty() {
- span_err!(tcx.sess, impl_m_span, E0051,
- "in method `{}`, type parameter {} requires `{}`, \
- which is not required by the corresponding type parameter \
- in the trait declaration",
- token::get_name(trait_m.name),
- i,
- extra_bounds.user_string(tcx));
- return;
- }
-
- // Check that the trait bounds of the trait imply the bounds of its
- // implementation.
- //
- // FIXME(pcwalton): We could be laxer here regarding sub- and super-
- // traits, but I doubt that'll be wanted often, so meh.
- for impl_trait_bound in impl_param_bounds.trait_bounds.iter() {
- debug!("compare_impl_method(): impl-trait-bound subst");
- let impl_trait_bound =
- impl_trait_bound.subst(tcx, impl_to_skol_substs);
-
- // There may be late-bound regions from the impl in the
- // impl's bound, so "liberate" those. Note that the
- // trait_to_skol_substs is derived from the impl's
- // trait-ref, and the late-bound regions appearing there
- // have already been liberated, so the result should match
- // up.
-
- let found_match_in_trait =
- trait_param_bounds.trait_bounds.iter().any(|trait_bound| {
- debug!("compare_impl_method(): trait-bound subst");
- let trait_bound =
- trait_bound.subst(tcx, &trait_to_skol_substs);
- infer::mk_sub_poly_trait_refs(&infcx,
- true,
- infer::Misc(impl_m_span),
- trait_bound,
- impl_trait_bound.clone()).is_ok()
- });
-
- if !found_match_in_trait {
- span_err!(tcx.sess, impl_m_span, E0052,
- "in method `{}`, type parameter {} requires bound `{}`, which is not \
- required by the corresponding type parameter in the trait declaration",
- token::get_name(trait_m.name),
- i,
- impl_trait_bound.user_string(tcx));
- }
- }
- }
-
- // We now need to check that the signature of the impl method is
- // compatible with that of the trait method. We do this by
- // checking that `impl_fty <: trait_fty`.
- //
- // FIXME. Unfortunately, this doesn't quite work right now because
- // associated type normalization is not integrated into subtype
- // checks. For the comparison to be valid, we need to
- // normalize the associated types in the impl/trait methods
- // first. However, because function types bind regions, just
- // calling `normalize_associated_types_in` would have no effect on
- // any associated types appearing in the fn arguments or return
- // type.
-
-
- // Compute skolemized form of impl and trait method tys.
- let impl_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_m.fty.clone()));
- let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
- let trait_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(trait_m.fty.clone()));
- let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
-
- let err = infcx.try(|snapshot| {
- let origin = infer::MethodCompatCheck(impl_m_span);
-
- let (impl_sig, _) =
- infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
- infer::HigherRankedType,
- &impl_m.fty.sig);
- let impl_sig =
- impl_sig.subst(tcx, impl_to_skol_substs);
- let impl_sig =
- assoc::normalize_associated_types_in(&infcx,
- &impl_param_env,
- &mut fulfillment_cx,
- impl_m_span,
- impl_m_body_id,
- &impl_sig);
- let impl_fty =
- ty::mk_bare_fn(tcx,
- None,
- tcx.mk_bare_fn(ty::BareFnTy { unsafety: impl_m.fty.unsafety,
- abi: impl_m.fty.abi,
- sig: ty::Binder(impl_sig) }));
- debug!("compare_impl_method: impl_fty={}",
- impl_fty.repr(tcx));
-
- let (trait_sig, skol_map) =
- infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
- let trait_sig =
- trait_sig.subst(tcx, &trait_to_skol_substs);
- let trait_sig =
- assoc::normalize_associated_types_in(&infcx,
- &impl_param_env,
- &mut fulfillment_cx,
- impl_m_span,
- impl_m_body_id,
- &trait_sig);
- let trait_fty =
- ty::mk_bare_fn(tcx,
- None,
- tcx.mk_bare_fn(ty::BareFnTy { unsafety: trait_m.fty.unsafety,
- abi: trait_m.fty.abi,
- sig: ty::Binder(trait_sig) }));
-
- debug!("compare_impl_method: trait_fty={}",
- trait_fty.repr(tcx));
-
- try!(infer::mk_subty(&infcx, false, origin, impl_fty, trait_fty));
-
- infcx.leak_check(&skol_map, snapshot)
- });
-
- match err {
- Ok(()) => { }
- Err(terr) => {
- debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
- impl_fty.repr(tcx),
- trait_fty.repr(tcx));
- span_err!(tcx.sess, impl_m_span, E0053,
- "method `{}` has an incompatible type for trait: {}",
- token::get_name(trait_m.name),
- ty::type_err_to_str(tcx, &terr));
- return;
- }
- }
-
- // Run the fulfillment context to completion to accommodate any
- // associated type normalizations that may have occurred.
- match fulfillment_cx.select_all_or_error(&infcx, &impl_param_env) {
- Ok(()) => { }
- Err(errors) => {
- traits::report_fulfillment_errors(&infcx, &errors);
- }
- }
-
- // Finally, resolve all regions. This catches wily misuses of lifetime
- // parameters.
- infcx.resolve_regions_and_report_errors(impl_m_body_id);
-
- /// 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>,
- trait_generics: &ty::Generics<'tcx>,
- impl_generics: &ty::Generics<'tcx>,
- trait_to_skol_substs: &Substs<'tcx>,
- impl_to_skol_substs: &Substs<'tcx>)
- -> bool
- {
-
- let trait_params = trait_generics.regions.get_slice(subst::FnSpace);
- let impl_params = impl_generics.regions.get_slice(subst::FnSpace);
-
- debug!("check_region_bounds_on_impl_method: \
- trait_generics={} \
- impl_generics={} \
- trait_to_skol_substs={} \
- impl_to_skol_substs={}",
- trait_generics.repr(tcx),
- impl_generics.repr(tcx),
- trait_to_skol_substs.repr(tcx),
- impl_to_skol_substs.repr(tcx));
-
- // Must have same number of early-bound lifetime parameters.
- // Unfortunately, if the user screws up the bounds, then this
- // will change classification between early and late. E.g.,
- // if in trait we have `<'a,'b:'a>`, and in impl we just have
- // `<'a,'b>`, then we have 2 early-bound lifetime parameters
- // in trait but 0 in the impl. But if we report "expected 2
- // but found 0" it's confusing, because it looks like there
- // are zero. Since I don't quite know how to phrase things at
- // the moment, give a kind of vague error message.
- if trait_params.len() != impl_params.len() {
- tcx.sess.span_err(
- span,
- &format!("lifetime parameters or bounds on method `{}` do \
- not match the trait declaration",
- token::get_name(impl_m.name))[]);
- return false;
- }
-
- // Each parameter `'a:'b+'c+'d` in trait should have the same
- // set of bounds in the impl, after subst.
- for (trait_param, impl_param) in
- trait_params.iter().zip(
- impl_params.iter())
- {
- let trait_bounds =
- trait_param.bounds.subst(tcx, trait_to_skol_substs);
- let impl_bounds =
- impl_param.bounds.subst(tcx, impl_to_skol_substs);
-
- debug!("check_region_bounds_on_impl_method: \
- trait_param={} \
- impl_param={} \
- trait_bounds={} \
- impl_bounds={}",
- trait_param.repr(tcx),
- impl_param.repr(tcx),
- trait_bounds.repr(tcx),
- impl_bounds.repr(tcx));
-
- // Collect the set of bounds present in trait but not in
- // impl.
- let missing: Vec<ty::Region> =
- trait_bounds.iter()
- .filter(|&b| !impl_bounds.contains(b))
- .map(|&b| b)
- .collect();
-
- // Collect set present in impl but not in trait.
- let extra: Vec<ty::Region> =
- impl_bounds.iter()
- .filter(|&b| !trait_bounds.contains(b))
- .map(|&b| b)
- .collect();
-
- debug!("missing={} extra={}",
- missing.repr(tcx), extra.repr(tcx));
-
- let err = if missing.len() != 0 || extra.len() != 0 {
- tcx.sess.span_err(
- span,
- &format!(
- "the lifetime parameter `{}` declared in the impl \
- has a distinct set of bounds \
- from its counterpart `{}` \
- declared in the trait",
- impl_param.name.user_string(tcx),
- trait_param.name.user_string(tcx))[]);
- true
- } else {
- false
- };
-
- if missing.len() != 0 {
- tcx.sess.span_note(
- span,
- &format!("the impl is missing the following bounds: `{}`",
- missing.user_string(tcx))[]);
- }
-
- if extra.len() != 0 {
- tcx.sess.span_note(
- span,
- &format!("the impl has the following extra bounds: `{}`",
- extra.user_string(tcx))[]);
- }
-
- if err {
- return false;
- }
- }
-
- return true;
- }
-}
-
fn check_cast(fcx: &FnCtxt,
cast_expr: &ast::Expr,
e: &ast::Expr,
};
fcx.write_ty(id, oprnd_t);
}
- ast::ExprPath(ref pth) => {
- let defn = lookup_def(fcx, pth.span, id);
+ ast::ExprPath(ref path) => {
+ let defn = lookup_def(fcx, path.span, id);
+ let pty = type_scheme_for_def(fcx, expr.span, defn);
+ instantiate_path(fcx, path, pty, None, defn, expr.span, expr.id);
+
+ // We always require that the type provided as the value for
+ // a type parameter outlives the moment of instantiation.
+ constrain_path_type_parameters(fcx, expr);
+ }
+ ast::ExprQPath(ref qpath) => {
+ // Require explicit type params for the trait.
+ let self_ty = fcx.to_ty(&*qpath.self_type);
+ astconv::instantiate_trait_ref(fcx, fcx, &*qpath.trait_ref, Some(self_ty), None);
+
+ let defn = lookup_def(fcx, expr.span, id);
let pty = type_scheme_for_def(fcx, expr.span, defn);
- instantiate_path(fcx, pth, pty, defn, expr.span, expr.id);
+ let mut path = qpath.trait_ref.path.clone();
+ path.segments.push(qpath.item_path.clone());
+ instantiate_path(fcx, &path, pty, Some(self_ty), defn, expr.span, expr.id);
// We always require that the type provided as the value for
// a type parameter outlives the moment of instantiation.
pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
path: &ast::Path,
type_scheme: TypeScheme<'tcx>,
+ opt_self_ty: Option<Ty<'tcx>>,
def: def::Def,
span: Span,
node_id: ast::NodeId) {
}
}
}
+ if let Some(self_ty) = opt_self_ty {
+ // `<T as Trait>::foo` shouldn't have resolved to a `Self`-less item.
+ assert_eq!(type_defs.len(subst::SelfSpace), 1);
+ substs.types.push(subst::SelfSpace, self_ty);
+ }
// Now we have to compare the types that the user *actually*
// provided against the types that were *expected*. If the user
impl Clean<Type> for ast::QPath {
fn clean(&self, cx: &DocContext) -> Type {
Type::QPath {
- name: self.item_name.clean(cx),
+ name: self.item_path.identifier.clean(cx),
self_type: box self.self_type.clean(cx),
trait_: box self.trait_ref.clean(cx)
}
use libc;
use std::ascii::AsciiExt;
use std::ffi::CString;
-use std::cell::{RefCell, Cell};
+use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::slice;
thread_local!(static USED_HEADER_MAP: RefCell<HashMap<String, uint>> = {
RefCell::new(HashMap::new())
});
-thread_local!(static TEST_IDX: Cell<uint> = Cell::new(0));
thread_local!(pub static PLAYGROUND_KRATE: RefCell<Option<Option<String>>> = {
RefCell::new(None)
if rendered { return }
PLAYGROUND_KRATE.with(|krate| {
let mut s = String::new();
- let id = krate.borrow().as_ref().map(|krate| {
- let idx = TEST_IDX.with(|slot| {
- let i = slot.get();
- slot.set(i + 1);
- i
- });
-
+ krate.borrow().as_ref().map(|krate| {
let test = origtext.lines().map(|l| {
stripped_filtered_line(l).unwrap_or(l)
}).collect::<Vec<&str>>().connect("\n");
let krate = krate.as_ref().map(|s| s.as_slice());
let test = test::maketest(test.as_slice(), krate, false, false);
- s.push_str(format!("<span id='rust-example-raw-{}' \
- class='rusttest'>{}</span>",
- idx, Escape(test.as_slice())).as_slice());
- format!("rust-example-rendered-{}", idx)
+ s.push_str(format!("<span class='rusttest'>{}</span>",
+ Escape(test.as_slice())).as_slice());
});
- let id = id.as_ref().map(|a| a.as_slice());
- s.push_str(highlight::highlight(text.as_slice(), None, id)
- .as_slice());
+ s.push_str(highlight::highlight(text.as_slice(),
+ None,
+ Some("rust-example-rendered"))
+ .as_slice());
let output = CString::from_vec(s.into_bytes());
hoedown_buffer_puts(ob, output.as_ptr());
})
/// previous state (if any).
pub fn reset_headers() {
USED_HEADER_MAP.with(|s| s.borrow_mut().clear());
- TEST_IDX.with(|s| s.set(0));
}
impl<'a> fmt::String for Markdown<'a> {
(function() {
if (window.playgroundUrl) {
$('pre.rust').hover(function() {
- if (!$(this).attr('id')) { return; }
- var id = '#' + $(this).attr('id').replace('rendered', 'raw');
var a = $('<a>').text('⇱').attr('class', 'test-arrow');
- var code = $(id).text();
+ var code = $(this).siblings(".rusttest").text();
a.attr('href', window.playgroundUrl + '?code=' +
encodeURIComponent(code));
a.attr('target', '_blank');
<html lang="en">
<head>
<meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc">
<title>{title}</title>
pub const ARCH: &'static str = "mipsel";
}
+#[cfg(target_arch = "powerpc")]
+mod arch_consts {
+ pub const ARCH: &'static str = "powerpc";
+}
+
#[cfg(test)]
mod tests {
use prelude::v1::*;
/// `random()` can generate various types of random things, and so may require
/// type hinting to generate the specific type you want.
///
+/// This function uses the thread local random number generator. This means
+/// that if you're calling `random()` in a loop, caching the generator can
+/// increase performance. An example is shown below.
+///
/// # Examples
///
-/// ```rust
+/// ```
/// use std::rand;
///
/// let x = rand::random();
/// println!("Better lucky than good!");
/// }
/// ```
+///
+/// Caching the thread local random number generator:
+///
+/// ```
+/// use std::rand;
+/// use std::rand::Rng;
+///
+/// let mut v = vec![1, 2, 3];
+///
+/// for x in v.iter_mut() {
+/// *x = rand::random()
+/// }
+///
+/// // would be faster as
+///
+/// let mut rng = rand::thread_rng();
+///
+/// for x in v.iter_mut() {
+/// *x = rng.gen();
+/// }
+/// ```
#[inline]
pub fn random<T: Rand>() -> T {
thread_rng().gen()
any(target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
- target_arch = "aarch64")))]
+ target_arch = "aarch64",
+ target_arch = "powerpc")))]
fn getrandom(buf: &mut [u8]) -> libc::c_long {
extern "C" {
fn syscall(number: libc::c_long, ...) -> libc::c_long;
const NR_GETRANDOM: libc::c_long = 355;
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
const NR_GETRANDOM: libc::c_long = 384;
+ #[cfg(target_arch = "powerpc")]
+ const NR_GETRANDOM: libc::c_long = 384;
unsafe {
syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
any(target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
- target_arch = "aarch64"))))]
+ target_arch = "aarch64",
+ target_arch = "powerpc"))))]
fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
fn getrandom_fill_bytes(v: &mut [u8]) {
any(target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
- target_arch = "aarch64")))]
+ target_arch = "aarch64",
+ target_arch = "powerpc")))]
fn is_getrandom_available() -> bool {
use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
any(target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
- target_arch = "aarch64"))))]
+ target_arch = "aarch64",
+ target_arch = "powerpc"))))]
fn is_getrandom_available() -> bool { false }
/// A random number generator that retrieves randomness straight from
#[cfg(any(target_arch = "mips", target_arch = "mipsel"))]
pub const unwinder_private_data_size: uint = 2;
+#[cfg(target_arch = "powerpc")]
+pub const unwinder_private_data_size: uint = 2;
+
#[repr(C)]
pub struct _Unwind_Exception {
pub exception_class: _Unwind_Exception_Class,
unsafe fn target_record_sp_limit(_: uint) {
}
+ // powerpc - FIXME(POWERPC): missing...
+ #[cfg(target_arch = "powerpc")]
+ unsafe fn target_record_sp_limit(_: uint) {
+ }
+
+
// iOS segmented stack is disabled for now, see related notes
#[cfg(all(target_arch = "arm", target_os = "ios"))] #[inline(always)]
unsafe fn target_record_sp_limit(_: uint) {
1024
}
+ // powepc - FIXME(POWERPC): missing...
+ #[cfg(target_arch = "powerpc")]
+ unsafe fn target_get_sp_limit() -> uint {
+ 1024
+ }
+
// iOS doesn't support segmented stacks yet. This function might
// be called by runtime though so it is unsafe to mark it as
// unreachable, let's return a fixed constant.
target_os = "android"))]
pub const FIONBIO: libc::c_ulong = 0x5421;
#[cfg(all(target_os = "linux",
- any(target_arch = "mips", target_arch = "mipsel")))]
+ any(target_arch = "mips",
+ target_arch = "mipsel",
+ target_arch = "powerpc")))]
pub const FIONBIO: libc::c_ulong = 0x667e;
#[cfg(any(target_os = "macos",
target_os = "android"))]
pub const FIOCLEX: libc::c_ulong = 0x5451;
#[cfg(all(target_os = "linux",
- any(target_arch = "mips", target_arch = "mipsel")))]
+ any(target_arch = "mips",
+ target_arch = "mipsel",
+ target_arch = "powerpc")))]
pub const FIOCLEX: libc::c_ulong = 0x6601;
#[cfg(any(target_os = "macos",
}
#[cfg(all(target_os = "linux",
- any(target_arch = "mips", target_arch = "mipsel")))]
+ any(target_arch = "mips",
+ target_arch = "mipsel",
+ target_arch = "powerpc")))]
mod signal {
use libc;
all(target_os = "linux", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "mips"), // may not match
all(target_os = "linux", target_arch = "mipsel"), // may not match
+ all(target_os = "linux", target_arch = "powerpc"), // may not match
target_os = "android"))] // may not match
mod signal {
use libc;
#[cfg(any(target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
- target_arch = "mipsel"))]
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8;
#[cfg(target_arch = "aarch64")]
const __SIZEOF_PTHREAD_MUTEX_T: uint = 48 - 8;
target_arch = "arm",
target_arch = "aarch64",
target_arch = "mips",
- target_arch = "mipsel"))]
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8;
#[cfg(any(target_arch = "x86_64",
#[cfg(any(target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
- target_arch = "mipsel"))]
+ target_arch = "mipsel",
+ target_arch = "powerpc"))]
const __SIZEOF_PTHREAD_RWLOCK_T: uint = 32 - 8;
#[repr(C)]
/// Variable reference, possibly containing `::` and/or
/// type parameters, e.g. foo::bar::<baz>
ExprPath(Path),
+ /// A "qualified path", e.g. `<Vec<T> as SomeTrait>::SomeType`
+ ExprQPath(P<QPath>),
ExprAddrOf(Mutability, P<Expr>),
ExprBreak(Option<Ident>),
///
/// <Vec<T> as SomeTrait>::SomeAssociatedItem
/// ^~~~~ ^~~~~~~~~ ^~~~~~~~~~~~~~~~~~
-/// self_type trait_name item_name
+/// self_type trait_name item_path
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show)]
pub struct QPath {
pub self_type: P<Ty>,
pub trait_ref: P<TraitRef>,
- pub item_name: Ident, // FIXME(#20301) -- should use Name
+ pub item_path: PathSegment,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show, Copy)]
pub enum Mac_ {
// NB: the additional ident for a macro_rules-style macro is actually
// stored in the enclosing item. Oog.
- MacInvocTT(Path, Vec<TokenTree> , SyntaxContext), // new macro-invocation
+ MacInvocTT(Path, Vec<TokenTree>, SyntaxContext), // new macro-invocation
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show, Copy)]
'statement: loop {
match state {
Asm => {
+ if asm_str_style.is_some() {
+ // If we already have a string with instructions,
+ // ending up in Asm state again is an error.
+ cx.span_err(sp, "malformed inline assembly");
+ return DummyResult::expr(sp);
+ }
let (s, style) = match expr_to_string(cx, p.parse_expr(),
"inline assembly must be a string literal") {
Some((s, st)) => (s, st),
}
}
+#[derive(Show,Clone)]
+pub enum Annotatable {
+ Item(P<ast::Item>),
+ TraitItem(ast::TraitItem),
+ ImplItem(ast::ImplItem),
+}
+
+impl Annotatable {
+ pub fn attrs(&self) -> &[ast::Attribute] {
+ match *self {
+ Annotatable::Item(ref i) => &i.attrs[],
+ Annotatable::TraitItem(ref i) => match *i {
+ ast::TraitItem::RequiredMethod(ref tm) => &tm.attrs[],
+ ast::TraitItem::ProvidedMethod(ref m) => &m.attrs[],
+ ast::TraitItem::TypeTraitItem(ref at) => &at.attrs[],
+ },
+ Annotatable::ImplItem(ref i) => match *i {
+ ast::ImplItem::MethodImplItem(ref m) => &m.attrs[],
+ ast::ImplItem::TypeImplItem(ref t) => &t.attrs[],
+ }
+ }
+ }
+
+ pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
+ match self {
+ Annotatable::Item(i) => Annotatable::Item(P(ast::Item {
+ attrs: attrs,
+ ..(*i).clone()
+ })),
+ Annotatable::TraitItem(i) => match i {
+ ast::TraitItem::RequiredMethod(tm) => Annotatable::TraitItem(
+ ast::TraitItem::RequiredMethod(
+ ast::TypeMethod { attrs: attrs, ..tm })),
+ ast::TraitItem::ProvidedMethod(m) => Annotatable::TraitItem(
+ ast::TraitItem::ProvidedMethod(P(
+ ast::Method { attrs: attrs, ..(*m).clone() }))),
+ ast::TraitItem::TypeTraitItem(at) => Annotatable::TraitItem(
+ ast::TraitItem::TypeTraitItem(P(
+ ast::AssociatedType { attrs: attrs, ..(*at).clone() }))),
+ },
+ Annotatable::ImplItem(i) => match i {
+ ast::ImplItem::MethodImplItem(m) => Annotatable::ImplItem(
+ ast::ImplItem::MethodImplItem(P(
+ ast::Method { attrs: attrs, ..(*m).clone() }))),
+ ast::ImplItem::TypeImplItem(t) => Annotatable::ImplItem(
+ ast::ImplItem::TypeImplItem(P(
+ ast::Typedef { attrs: attrs, ..(*t).clone() }))),
+ }
+ }
+ }
+
+ pub fn expect_item(self) -> P<ast::Item> {
+ match self {
+ Annotatable::Item(i) => i,
+ _ => panic!("expected Item")
+ }
+ }
+
+ pub fn expect_trait_item(self) -> ast::TraitItem {
+ match self {
+ Annotatable::TraitItem(i) => i,
+ _ => panic!("expected Item")
+ }
+ }
+
+ pub fn expect_impl_item(self) -> ast::ImplItem {
+ match self {
+ Annotatable::ImplItem(i) => i,
+ _ => panic!("expected Item")
+ }
+ }
+}
+
+// A more flexible ItemModifier (ItemModifier should go away, eventually, FIXME).
+// meta_item is the annotation, item is the item being modified, parent_item
+// is the impl or trait item is declared in if item is part of such a thing.
+// FIXME Decorators should follow the same pattern too.
+pub trait MultiItemModifier {
+ fn expand(&self,
+ ecx: &mut ExtCtxt,
+ span: Span,
+ meta_item: &ast::MetaItem,
+ item: Annotatable)
+ -> Annotatable;
+}
+
+impl<F> MultiItemModifier for F
+ where F: Fn(&mut ExtCtxt,
+ Span,
+ &ast::MetaItem,
+ Annotatable) -> Annotatable
+{
+ fn expand(&self,
+ ecx: &mut ExtCtxt,
+ span: Span,
+ meta_item: &ast::MetaItem,
+ item: Annotatable)
+ -> Annotatable {
+ (*self)(ecx, span, meta_item, item)
+ }
+}
+
/// Represents a thing that maps token trees to Macro Results
pub trait TTMacroExpander {
fn expand<'cx>(&self,
/// in-place.
Modifier(Box<ItemModifier + 'static>),
+ /// A syntax extension that is attached to an item and modifies it
+ /// in-place. More flexible version than Modifier.
+ MultiModifier(Box<MultiItemModifier + 'static>),
+
/// A normal, function-like syntax extension.
///
/// `bytes!` is a `NormalTT`.
//! (e.g. `Option<T>`), the parameters are automatically given the
//! current trait as a bound. (This includes separate type parameters
//! and lifetimes for methods.)
-//! - Additional bounds on the type parameters, e.g. the `Ord` instance
-//! requires an explicit `PartialEq` bound at the
-//! moment. (`TraitDef.additional_bounds`)
-//!
-//! Unsupported: FIXME #6257: calling methods on reference fields,
-//! e.g. derive Eq/Ord/Clone don't work on `struct A(&int)`,
-//! because of how the auto-dereferencing happens.
+//! - Additional bounds on the type parameters (`TraitDef.additional_bounds`)
//!
//! The most important thing for implementers is the `Substructure` and
//! `SubstructureFields` objects. The latter groups 5 possibilities of the
//! enums (one for each variant). For empty struct and empty enum
//! variants, it is represented as a count of 0.
//!
+//! # "`cs`" functions
+//!
+//! The `cs_...` functions ("combine substructure) are designed to
+//! make life easier by providing some pre-made recipes for common
+//! tasks; mostly calling the function being derived on all the
+//! arguments and then combining them back together in some way (or
+//! letting the user chose that). They are not meant to be the only
+//! way to handle the structures that this code creates.
+//!
//! # Examples
//!
//! The following simplified `PartialEq` is used for in-code examples:
//! When generating the `expr` for the `A` impl, the `SubstructureFields` is
//!
//! ```{.text}
-//! Struct(~[FieldInfo {
+//! Struct(vec![FieldInfo {
//! span: <span of x>
//! name: Some(<ident of x>),
//! self_: <expr for &self.x>,
-//! other: ~[<expr for &other.x]
+//! other: vec![<expr for &other.x]
//! }])
//! ```
//!
//! For the `B` impl, called with `B(a)` and `B(b)`,
//!
//! ```{.text}
-//! Struct(~[FieldInfo {
+//! Struct(vec![FieldInfo {
//! span: <span of `int`>,
//! name: None,
-//! <expr for &a>
-//! ~[<expr for &b>]
+//! self_: <expr for &a>
+//! other: vec![<expr for &b>]
//! }])
//! ```
//!
//!
//! ```{.text}
//! EnumMatching(0, <ast::Variant for C0>,
-//! ~[FieldInfo {
+//! vec![FieldInfo {
//! span: <span of int>
//! name: None,
//! self_: <expr for &a>,
-//! other: ~[<expr for &b>]
+//! other: vec![<expr for &b>]
//! }])
//! ```
//!
//!
//! ```{.text}
//! EnumMatching(1, <ast::Variant for C1>,
-//! ~[FieldInfo {
+//! vec![FieldInfo {
//! span: <span of x>
//! name: Some(<ident of x>),
//! self_: <expr for &self.x>,
-//! other: ~[<expr for &other.x>]
+//! other: vec![<expr for &other.x>]
//! }])
//! ```
//!
//!
//! ```{.text}
//! EnumNonMatchingCollapsed(
-//! ~[<ident of self>, <ident of __arg_1>],
+//! vec![<ident of self>, <ident of __arg_1>],
//! &[<ast::Variant for C0>, <ast::Variant for C1>],
//! &[<ident for self index value>, <ident of __arg_1 index value>])
//! ```
//!
//! ## Static
//!
-//! A static method on the above would result in,
+//! A static method on the types above would result in,
//!
//! ```{.text}
-//! StaticStruct(<ast::StructDef of A>, Named(~[(<ident of x>, <span of x>)]))
+//! StaticStruct(<ast::StructDef of A>, Named(vec![(<ident of x>, <span of x>)]))
//!
-//! StaticStruct(<ast::StructDef of B>, Unnamed(~[<span of x>]))
+//! StaticStruct(<ast::StructDef of B>, Unnamed(vec![<span of x>]))
//!
-//! StaticEnum(<ast::EnumDef of C>, ~[(<ident of C0>, <span of C0>, Unnamed(~[<span of int>])),
-//! (<ident of C1>, <span of C1>,
-//! Named(~[(<ident of x>, <span of x>)]))])
+//! StaticEnum(<ast::EnumDef of C>,
+//! vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of int>])),
+//! (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))])
//! ```
pub use self::StaticFields::*;
/// process the collected results. i.e.
///
/// ```
-/// f(cx, span, ~[self_1.method(__arg_1_1, __arg_2_1),
-/// self_2.method(__arg_1_2, __arg_2_2)])
+/// f(cx, span, vec![self_1.method(__arg_1_1, __arg_2_1),
+/// self_2.method(__arg_1_2, __arg_2_2)])
/// ```
#[inline]
pub fn cs_same_method<F>(f: F,
-> SmallVector<P<ast::Item>> {
let it = expand_item_modifiers(it, fld);
- let mut decorator_items = SmallVector::zero();
- let mut new_attrs = Vec::new();
- for attr in it.attrs.iter() {
- let mname = attr.name();
-
- match fld.cx.syntax_env.find(&intern(mname.get())) {
- Some(rc) => match *rc {
- Decorator(ref dec) => {
- attr::mark_used(attr);
-
- fld.cx.bt_push(ExpnInfo {
- call_site: attr.span,
- callee: NameAndSpan {
- name: mname.get().to_string(),
- format: MacroAttribute,
- span: None
- }
- });
-
- // we'd ideally decorator_items.push_all(expand_item(item, fld)),
- // but that double-mut-borrows fld
- let mut items: SmallVector<P<ast::Item>> = SmallVector::zero();
- dec.expand(fld.cx, attr.span, &*attr.node.value, &*it,
- box |&mut : item| items.push(item));
- decorator_items.extend(items.into_iter()
- .flat_map(|item| expand_item(item, fld).into_iter()));
-
- fld.cx.bt_pop();
- }
- _ => new_attrs.push((*attr).clone()),
- },
- _ => new_attrs.push((*attr).clone()),
- }
- }
-
- let mut new_items = match it.node {
- ast::ItemMac(..) => expand_item_mac(it, fld),
- ast::ItemMod(_) | ast::ItemForeignMod(_) => {
- let valid_ident =
- it.ident.name != parse::token::special_idents::invalid.name;
-
- if valid_ident {
- fld.cx.mod_push(it.ident);
- }
- let macro_use = contains_macro_use(fld, &new_attrs[]);
- let result = with_exts_frame!(fld.cx.syntax_env,
- macro_use,
- noop_fold_item(it, fld));
- if valid_ident {
- fld.cx.mod_pop();
- }
- result
- },
- _ => {
- let it = P(ast::Item {
- attrs: new_attrs,
- ..(*it).clone()
- });
- noop_fold_item(it, fld)
- }
- };
-
- new_items.push_all(decorator_items);
- new_items
+ expand_annotatable(Annotatable::Item(it), fld)
+ .into_iter().map(|i| i.expect_item()).collect()
}
fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
-> P<ast::Item> {
// partition the attributes into ItemModifiers and others
- let (modifiers, other_attrs): (Vec<_>, _) = it.attrs.iter().cloned().partition(|attr| {
- match fld.cx.syntax_env.find(&intern(attr.name().get())) {
- Some(rc) => match *rc { Modifier(_) => true, _ => false },
- _ => false
- }
- });
+ let (modifiers, other_attrs) = modifiers(&it.attrs, fld);
+
// update the attrs, leave everything else alone. Is this mutation really a good idea?
it = P(ast::Item {
attrs: other_attrs,
});
if modifiers.is_empty() {
- return it;
+ let it = expand_item_multi_modifier(Annotatable::Item(it), fld);
+ return it.expect_item();
}
for attr in modifiers.iter() {
}
}
- // expansion may have added new ItemModifiers
+ // Expansion may have added new ItemModifiers.
+ // It is possible, that an item modifier could expand to a multi-modifier or
+ // vice versa. In this case we will expand all modifiers before multi-modifiers,
+ // which might give an odd ordering. However, I think it is unlikely that the
+ // two kinds will be mixed, and I old-style multi-modifiers should be deprecated
+ // anyway.
expand_item_modifiers(it, fld)
}
}
}
+fn expand_annotatable(a: Annotatable,
+ fld: &mut MacroExpander)
+ -> SmallVector<Annotatable> {
+ let a = expand_item_multi_modifier(a, fld);
+
+ let mut decorator_items = SmallVector::zero();
+ let mut new_attrs = Vec::new();
+ for attr in a.attrs().iter() {
+ let mname = attr.name();
+
+ match fld.cx.syntax_env.find(&intern(mname.get())) {
+ Some(rc) => match *rc {
+ Decorator(ref dec) => {
+ let it = match a {
+ Annotatable::Item(ref it) => it,
+ // ItemDecorators are only implemented for Items.
+ _ => break,
+ };
+
+ attr::mark_used(attr);
+
+ fld.cx.bt_push(ExpnInfo {
+ call_site: attr.span,
+ callee: NameAndSpan {
+ name: mname.get().to_string(),
+ format: MacroAttribute,
+ span: None
+ }
+ });
+
+ // we'd ideally decorator_items.push_all(expand_item(item, fld)),
+ // but that double-mut-borrows fld
+ let mut items: SmallVector<P<ast::Item>> = SmallVector::zero();
+ dec.expand(fld.cx, attr.span, &*attr.node.value, &**it,
+ box |&mut: item| items.push(item));
+ decorator_items.extend(items.into_iter()
+ .flat_map(|item| expand_item(item, fld).into_iter()));
+
+ fld.cx.bt_pop();
+ }
+ _ => new_attrs.push((*attr).clone()),
+ },
+ _ => new_attrs.push((*attr).clone()),
+ }
+ }
+
+ let mut new_items: SmallVector<Annotatable> = match a {
+ Annotatable::Item(it) => match it.node {
+ ast::ItemMac(..) => {
+ expand_item_mac(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
+ }
+ ast::ItemMod(_) | ast::ItemForeignMod(_) => {
+ let valid_ident =
+ it.ident.name != parse::token::special_idents::invalid.name;
+
+ if valid_ident {
+ fld.cx.mod_push(it.ident);
+ }
+ let macro_use = contains_macro_use(fld, &new_attrs[]);
+ let result = with_exts_frame!(fld.cx.syntax_env,
+ macro_use,
+ noop_fold_item(it, fld));
+ if valid_ident {
+ fld.cx.mod_pop();
+ }
+ result.into_iter().map(|i| Annotatable::Item(i)).collect()
+ },
+ _ => {
+ let it = P(ast::Item {
+ attrs: new_attrs,
+ ..(*it).clone()
+ });
+ noop_fold_item(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
+ }
+ },
+ Annotatable::TraitItem(it) => match it {
+ ast::TraitItem::ProvidedMethod(m) => {
+ expand_method(m, fld).into_iter().map(|m|
+ Annotatable::TraitItem(ast::TraitItem::ProvidedMethod(m))).collect()
+ }
+ ast::TraitItem::RequiredMethod(m) => {
+ SmallVector::one(Annotatable::TraitItem(
+ ast::TraitItem::RequiredMethod(fld.fold_type_method(m))))
+ }
+ ast::TraitItem::TypeTraitItem(t) => {
+ SmallVector::one(Annotatable::TraitItem(
+ ast::TraitItem::TypeTraitItem(P(fld.fold_associated_type((*t).clone())))))
+ }
+ },
+ Annotatable::ImplItem(it) => match it {
+ ast::ImplItem::MethodImplItem(m) => {
+ expand_method(m, fld).into_iter().map(|m|
+ Annotatable::ImplItem(ast::ImplItem::MethodImplItem(m))).collect()
+ }
+ ast::ImplItem::TypeImplItem(t) => {
+ SmallVector::one(Annotatable::ImplItem(
+ ast::ImplItem::TypeImplItem(P(fld.fold_typedef((*t).clone())))))
+ }
+ }
+ };
+
+ new_items.push_all(decorator_items.into_iter().map(|i| Annotatable::Item(i)).collect());
+ new_items
+}
+
+fn expand_trait_item(i: ast::TraitItem,
+ fld: &mut MacroExpander)
+ -> SmallVector<ast::TraitItem> {
+ expand_annotatable(Annotatable::TraitItem(i), fld)
+ .into_iter().map(|i| i.expect_trait_item()).collect()
+
+}
+
+fn expand_impl_item(i: ast::ImplItem,
+ fld: &mut MacroExpander)
+ -> SmallVector<ast::ImplItem> {
+ expand_annotatable(Annotatable::ImplItem(i), fld)
+ .into_iter().map(|i| i.expect_impl_item()).collect()
+}
+
+// partition the attributes into ItemModifiers and others
+fn modifiers(attrs: &Vec<ast::Attribute>,
+ fld: &MacroExpander)
+ -> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
+ attrs.iter().cloned().partition(|attr| {
+ match fld.cx.syntax_env.find(&intern(attr.name().get())) {
+ Some(rc) => match *rc {
+ Modifier(_) => true,
+ _ => false
+ },
+ _ => false
+ }
+ })
+}
+
+// partition the attributes into MultiModifiers and others
+fn multi_modifiers(attrs: &[ast::Attribute],
+ fld: &MacroExpander)
+ -> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
+ attrs.iter().cloned().partition(|attr| {
+ match fld.cx.syntax_env.find(&intern(attr.name().get())) {
+ Some(rc) => match *rc {
+ MultiModifier(_) => true,
+ _ => false
+ },
+ _ => false
+ }
+ })
+}
+
+fn expand_item_multi_modifier(mut it: Annotatable,
+ fld: &mut MacroExpander)
+ -> Annotatable {
+ let (modifiers, other_attrs) = multi_modifiers(it.attrs(), fld);
+
+ // Update the attrs, leave everything else alone. Is this mutation really a good idea?
+ it = it.fold_attrs(other_attrs);
+
+ if modifiers.is_empty() {
+ return it
+ }
+
+ for attr in modifiers.iter() {
+ let mname = attr.name();
+
+ match fld.cx.syntax_env.find(&intern(mname.get())) {
+ Some(rc) => match *rc {
+ MultiModifier(ref mac) => {
+ attr::mark_used(attr);
+ fld.cx.bt_push(ExpnInfo {
+ call_site: attr.span,
+ callee: NameAndSpan {
+ name: mname.get().to_string(),
+ format: MacroAttribute,
+ span: None,
+ }
+ });
+ it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
+ fld.cx.bt_pop();
+ }
+ _ => unreachable!()
+ },
+ _ => unreachable!()
+ }
+ }
+
+ // Expansion may have added new ItemModifiers.
+ expand_item_multi_modifier(it, fld)
+}
+
// expand a method
fn expand_method(m: P<ast::Method>, fld: &mut MacroExpander) -> SmallVector<P<ast::Method>> {
m.and_then(|m| match m.node {
vis) => {
let id = fld.new_id(m.id);
let (rewritten_fn_decl, rewritten_body)
- = expand_and_rename_fn_decl_and_block(decl,body,fld);
+ = expand_and_rename_fn_decl_and_block(decl, body, fld);
SmallVector::one(P(ast::Method {
attrs: m.attrs.move_map(|a| fld.fold_attribute(a)),
id: id,
expand_arm(arm, self)
}
+ fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
+ expand_trait_item(i, self)
+ }
+
+ fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
+ expand_impl_item(i, self)
+ }
+
fn fold_method(&mut self, method: P<ast::Method>) -> SmallVector<P<ast::Method>> {
expand_method(method, self)
}
noop_fold_item_underscore(i, self)
}
+ fn fold_trait_item(&mut self, i: TraitItem) -> SmallVector<TraitItem> {
+ noop_fold_trait_item(i, self)
+ }
+
+ fn fold_impl_item(&mut self, i: ImplItem) -> SmallVector<ImplItem> {
+ noop_fold_impl_item(i, self)
+ }
+
fn fold_fn_decl(&mut self, d: P<FnDecl>) -> P<FnDecl> {
noop_fold_fn_decl(d, self)
}
QPath {
self_type: fld.fold_ty(qpath.self_type),
trait_ref: qpath.trait_ref.map(|tr| fld.fold_trait_ref(tr)),
- item_name: fld.fold_ident(qpath.item_name),
+ item_path: PathSegment {
+ identifier: fld.fold_ident(qpath.item_path.identifier),
+ parameters: fld.fold_path_parameters(qpath.item_path.parameters),
+ }
}
})
}
ItemStruct(struct_def, folder.fold_generics(generics))
}
ItemImpl(unsafety, polarity, generics, ifce, ty, impl_items) => {
- let mut new_impl_items = Vec::new();
- for impl_item in impl_items.iter() {
- match *impl_item {
- MethodImplItem(ref x) => {
- for method in folder.fold_method((*x).clone())
- .into_iter() {
- new_impl_items.push(MethodImplItem(method))
- }
- }
- TypeImplItem(ref t) => {
- new_impl_items.push(TypeImplItem(
- P(folder.fold_typedef((**t).clone()))));
- }
- }
- }
+ let new_impl_items = impl_items.into_iter().flat_map(|item| {
+ folder.fold_impl_item(item).into_iter()
+ }).collect();
let ifce = match ifce {
None => None,
Some(ref trait_ref) => {
folder.fold_ty(ty),
new_impl_items)
}
- ItemTrait(unsafety, generics, bounds, methods) => {
+ ItemTrait(unsafety, generics, bounds, items) => {
let bounds = folder.fold_bounds(bounds);
- let methods = methods.into_iter().flat_map(|method| {
- let r = match method {
- RequiredMethod(m) => {
- SmallVector::one(RequiredMethod(
- folder.fold_type_method(m)))
- .into_iter()
- }
- ProvidedMethod(method) => {
- // the awkward collect/iter idiom here is because
- // even though an iter and a map satisfy the same
- // trait bound, they're not actually the same type, so
- // the method arms don't unify.
- let methods: SmallVector<ast::TraitItem> =
- folder.fold_method(method).into_iter()
- .map(|m| ProvidedMethod(m)).collect();
- methods.into_iter()
- }
- TypeTraitItem(at) => {
- SmallVector::one(TypeTraitItem(P(
- folder.fold_associated_type(
- (*at).clone()))))
- .into_iter()
- }
- };
- r
+ let items = items.into_iter().flat_map(|item| {
+ folder.fold_trait_item(item).into_iter()
}).collect();
ItemTrait(unsafety,
folder.fold_generics(generics),
bounds,
- methods)
+ items)
}
ItemMac(m) => ItemMac(folder.fold_mac(m)),
}
}
+pub fn noop_fold_trait_item<T: Folder>(i: TraitItem, folder: &mut T) -> SmallVector<TraitItem> {
+ match i {
+ RequiredMethod(m) => {
+ SmallVector::one(RequiredMethod(
+ folder.fold_type_method(m)))
+ }
+ ProvidedMethod(method) => {
+ folder.fold_method(method).into_iter()
+ .map(|m| ProvidedMethod(m)).collect()
+ }
+ TypeTraitItem(at) => {
+ SmallVector::one(TypeTraitItem(P(
+ folder.fold_associated_type(
+ (*at).clone()))))
+ }
+ }
+}
+
+pub fn noop_fold_impl_item<T: Folder>(i: ImplItem, folder: &mut T) -> SmallVector<ImplItem> {
+ match i {
+ MethodImplItem(ref x) => {
+ folder.fold_method((*x).clone()).into_iter().map(|m| MethodImplItem(m)).collect()
+ }
+ TypeImplItem(ref t) => {
+ SmallVector::one(TypeImplItem(
+ P(folder.fold_typedef((**t).clone()))))
+ }
+ }
+}
+
pub fn noop_fold_type_method<T: Folder>(m: TypeMethod, fld: &mut T) -> TypeMethod {
let TypeMethod {
id,
e2.map(|x| folder.fold_expr(x)))
}
ExprPath(pth) => ExprPath(folder.fold_path(pth)),
+ ExprQPath(qpath) => ExprQPath(folder.fold_qpath(qpath)),
ExprBreak(opt_ident) => ExprBreak(opt_ident.map(|x| folder.fold_ident(x))),
ExprAgain(opt_ident) => ExprAgain(opt_ident.map(|x| folder.fold_ident(x))),
ExprRet(e) => ExprRet(e.map(|x| folder.fold_expr(x))),
pub enum ObsoleteSyntax {
Sized,
ForSized,
- OwnedType,
- OwnedExpr,
- OwnedPattern,
- OwnedVector,
- OwnedSelf,
- ImportRenaming,
- SubsliceMatch,
- ExternCrateRenaming,
ProcType,
ProcExpr,
ClosureType,
"`proc` expression",
"use a `move ||` expression instead",
),
- ObsoleteSyntax::OwnedType => (
- "`~` notation for owned pointers",
- "use `Box<T>` in `std::owned` instead"
- ),
- ObsoleteSyntax::OwnedExpr => (
- "`~` notation for owned pointer allocation",
- "use the `box` operator instead of `~`"
- ),
- ObsoleteSyntax::OwnedPattern => (
- "`~` notation for owned pointer patterns",
- "use the `box` operator instead of `~`"
- ),
- ObsoleteSyntax::OwnedVector => (
- "`~[T]` is no longer a type",
- "use the `Vec` type instead"
- ),
- ObsoleteSyntax::OwnedSelf => (
- "`~self` is no longer supported",
- "write `self: Box<Self>` instead"
- ),
- ObsoleteSyntax::ImportRenaming => (
- "`use foo = bar` syntax",
- "write `use bar as foo` instead"
- ),
- ObsoleteSyntax::SubsliceMatch => (
- "subslice match syntax",
- "instead of `..xs`, write `xs..` in a pattern"
- ),
- ObsoleteSyntax::ExternCrateRenaming => (
- "`extern crate foo = bar` syntax",
- "write `extern crate bar as foo` instead"
- ),
ObsoleteSyntax::ClosureType => (
"`|uint| -> bool` closure type syntax",
"use unboxed closures instead, no type annotation needed"
use ast::{ExprBreak, ExprCall, ExprCast};
use ast::{ExprField, ExprTupField, ExprClosure, ExprIf, ExprIfLet, ExprIndex};
use ast::{ExprLit, ExprLoop, ExprMac, ExprRange};
-use ast::{ExprMethodCall, ExprParen, ExprPath};
+use ast::{ExprMethodCall, ExprParen, ExprPath, ExprQPath};
use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary};
use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl};
use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind};
use parse::classify;
use parse::common::{SeqSep, seq_sep_none, seq_sep_trailing_allowed};
use parse::lexer::{Reader, TokenAndSpan};
-use parse::obsolete::*;
+use parse::obsolete::{ParserObsoleteMethods, ObsoleteSyntax};
use parse::token::{self, MatchNt, SubstNt, SpecialVarNt, InternedString};
use parse::token::{keywords, special_idents, SpecialMacroVar};
use parse::{new_sub_parser_from_file, ParseSess};
MutTy { ty: t, mutbl: mutbl }
}
- /// Parse [mut/const/imm] ID : TY
- /// now used only by obsolete record syntax parser...
- pub fn parse_ty_field(&mut self) -> TypeField {
- let lo = self.span.lo;
- let mutbl = self.parse_mutability();
- let id = self.parse_ident();
- self.expect(&token::Colon);
- let ty = self.parse_ty_sum();
- let hi = ty.span.hi;
- ast::TypeField {
- ident: id,
- mt: MutTy { ty: ty, mutbl: mutbl },
- span: mk_sp(lo, hi),
- }
- }
-
/// Parse optional return type [ -> TY ] in function decl
pub fn parse_ret_ty(&mut self) -> FunctionRetTy {
if self.eat(&token::RArrow) {
} else {
TyTup(ts)
}
- } else if self.token == token::Tilde {
- // OWNED POINTER
- self.bump();
- let last_span = self.last_span;
- match self.token {
- token::OpenDelim(token::Bracket) => {
- self.obsolete(last_span, ObsoleteSyntax::OwnedVector)
- }
- _ => self.obsolete(last_span, ObsoleteSyntax::OwnedType)
- }
- TyTup(vec![self.parse_ty()])
} else if self.check(&token::BinOp(token::Star)) {
// STAR POINTER (bare pointer?)
self.bump();
TyQPath(P(QPath {
self_type: self_type,
trait_ref: P(trait_ref),
- item_name: item_name,
+ item_path: ast::PathSegment {
+ identifier: item_name,
+ parameters: ast::PathParameters::none()
+ }
}))
} else if self.check(&token::ModSep) ||
self.token.is_ident() ||
if !self.eat(&token::ModSep) {
segments.push(ast::PathSegment {
identifier: identifier,
- parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
- lifetimes: Vec::new(),
- types: OwnedSlice::empty(),
- bindings: OwnedSlice::empty(),
- })
+ parameters: ast::PathParameters::none()
});
return segments;
}
hi = self.last_span.hi;
}
_ => {
+ if self.eat_lt() {
+ // QUALIFIED PATH `<TYPE as TRAIT_REF>::item::<'a, T>`
+ let self_type = self.parse_ty_sum();
+ self.expect_keyword(keywords::As);
+ let trait_ref = self.parse_trait_ref();
+ self.expect(&token::Gt);
+ self.expect(&token::ModSep);
+ let item_name = self.parse_ident();
+ let parameters = if self.eat(&token::ModSep) {
+ self.expect_lt();
+ // Consumed `item::<`, go look for types
+ let (lifetimes, types, bindings) =
+ self.parse_generic_values_after_lt();
+ ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
+ lifetimes: lifetimes,
+ types: OwnedSlice::from_vec(types),
+ bindings: OwnedSlice::from_vec(bindings),
+ })
+ } else {
+ ast::PathParameters::none()
+ };
+ let hi = self.span.hi;
+ return self.mk_expr(lo, hi, ExprQPath(P(QPath {
+ self_type: self_type,
+ trait_ref: P(trait_ref),
+ item_path: ast::PathSegment {
+ identifier: item_name,
+ parameters: parameters
+ }
+ })));
+ }
if self.eat_keyword(keywords::Move) {
return self.parse_lambda_expr(CaptureByValue);
}
hi = e.span.hi;
ex = ExprAddrOf(m, e);
}
- token::Tilde => {
- self.bump();
- let last_span = self.last_span;
- match self.token {
- token::OpenDelim(token::Bracket) => {
- self.obsolete(last_span, ObsoleteSyntax::OwnedVector)
- },
- _ => self.obsolete(last_span, ObsoleteSyntax::OwnedExpr)
- }
-
- let e = self.parse_prefix_expr();
- hi = e.span.hi;
- ex = self.mk_unary(UnUniq, e);
- }
token::DotDot if !self.restrictions.contains(RESTRICTION_NO_DOTS) => {
// A range, closed above: `..expr`.
self.bump();
span: self.span,
}));
before_slice = false;
- } else {
- let _ = self.parse_pat();
- let span = self.span;
- self.obsolete(span, ObsoleteSyntax::SubsliceMatch);
}
continue
}
span: mk_sp(lo, hi)
})
}
- token::Tilde => {
- // parse ~pat
- self.bump();
- let sub = self.parse_pat();
- pat = PatBox(sub);
- let last_span = self.last_span;
- hi = last_span.hi;
- self.obsolete(last_span, ObsoleteSyntax::OwnedPattern);
- return P(ast::Pat {
- id: ast::DUMMY_NODE_ID,
- node: pat,
- span: mk_sp(lo, hi)
- })
- }
token::BinOp(token::And) | token::AndAnd => {
// parse &pat and &mut pat
let lo = self.span.lo;
self_ident_hi = self.last_span.hi;
eself
}
- token::Tilde => {
- // We need to make sure it isn't a type
- if self.look_ahead(1, |t| t.is_keyword(keywords::Self)) {
- self.bump();
- drop(self.expect_self_ident());
- let last_span = self.last_span;
- self.obsolete(last_span, ObsoleteSyntax::OwnedSelf)
- }
- SelfStatic
- }
token::BinOp(token::Star) => {
// Possibly "*self" or "*mut self" -- not supported. Try to avoid
// emitting cryptic "unexpected token" errors.
} else {
SelfValue(self_ident)
}
- } else if self.token.is_mutability() &&
- self.look_ahead(1, |t| *t == token::Tilde) &&
- self.look_ahead(2, |t| t.is_keyword(keywords::Self)) {
- mutbl_self = self.parse_mutability();
- self.bump();
- drop(self.expect_self_ident());
- let last_span = self.last_span;
- self.obsolete(last_span, ObsoleteSyntax::OwnedSelf);
- SelfStatic
} else {
SelfStatic
}
let (maybe_path, ident) = match self.token {
token::Ident(..) => {
let the_ident = self.parse_ident();
- let path = if self.token == token::Eq {
- self.bump();
- let path = self.parse_str();
- let span = self.span;
- self.obsolete(span, ObsoleteSyntax::ExternCrateRenaming);
- Some(path)
- } else if self.eat_keyword(keywords::As) {
+ let path = if self.eat_keyword(keywords::As) {
// skip the ident if there is one
if self.token.is_ident() { self.bump(); }
}
// either a view item or an item:
if self.eat_keyword(keywords::Extern) {
- let next_is_mod = self.eat_keyword(keywords::Mod);
-
- if next_is_mod || self.eat_keyword(keywords::Crate) {
- if next_is_mod {
- let last_span = self.last_span;
- self.span_err(mk_sp(lo, last_span.hi),
- &format!("`extern mod` is obsolete, use \
- `extern crate` instead \
- to refer to external \
- crates.")[])
- }
+ if self.eat_keyword(keywords::Crate) {
return self.parse_item_extern_crate(lo, visibility, attrs);
}
let first_ident = self.parse_ident();
let mut path = vec!(first_ident);
- match self.token {
- token::Eq => {
- // x = foo::bar
- self.bump();
- let path_lo = self.span.lo;
- path = vec!(self.parse_ident());
- while self.check(&token::ModSep) {
- self.bump();
- let id = self.parse_ident();
- path.push(id);
- }
- let span = mk_sp(path_lo, self.span.hi);
- self.obsolete(span, ObsoleteSyntax::ImportRenaming);
- let path = ast::Path {
- span: span,
- global: false,
- segments: path.into_iter().map(|identifier| {
- ast::PathSegment {
- identifier: identifier,
- parameters: ast::PathParameters::none(),
- }
- }).collect()
- };
- return P(spanned(lo, self.span.hi,
- ViewPathSimple(first_ident, path,
- ast::DUMMY_NODE_ID)));
- }
-
- token::ModSep => {
+ if let token::ModSep = self.token {
// foo::bar or foo::{a,b,c} or foo::*
while self.check(&token::ModSep) {
self.bump();
_ => break
}
}
- }
- _ => ()
}
let mut rename_to = path[path.len() - 1u];
let path = ast::Path {
//! line (which it can't) and so naturally place the content on its own line to
//! avoid combining it with other lines and making matters even worse.
-pub use self::PrintStackBreak::*;
-pub use self::Breaks::*;
-pub use self::Token::*;
-
use std::io;
use std::string;
use std::iter::repeat;
#[derive(Clone)]
pub enum Token {
- String(string::String, int),
+ String(String, int),
Break(BreakToken),
Begin(BeginToken),
End,
impl Token {
pub fn is_eof(&self) -> bool {
- match *self { Eof => true, _ => false }
+ match *self {
+ Token::Eof => true,
+ _ => false,
+ }
}
pub fn is_hardbreak_tok(&self) -> bool {
match *self {
- Break(BreakToken {
+ Token::Break(BreakToken {
offset: 0,
blank_space: bs
}) if bs == SIZE_INFINITY =>
}
}
-pub fn tok_str(t: Token) -> string::String {
- match t {
- String(s, len) => return format!("STR({},{})", s, len),
- Break(_) => return "BREAK".to_string(),
- Begin(_) => return "BEGIN".to_string(),
- End => return "END".to_string(),
- Eof => return "EOF".to_string()
+pub fn tok_str(token: &Token) -> String {
+ match *token {
+ Token::String(ref s, len) => format!("STR({},{})", s, len),
+ Token::Break(_) => "BREAK".to_string(),
+ Token::Begin(_) => "BEGIN".to_string(),
+ Token::End => "END".to_string(),
+ Token::Eof => "EOF".to_string()
}
}
-pub fn buf_str(toks: Vec<Token>,
- szs: Vec<int>,
+pub fn buf_str(toks: &[Token],
+ szs: &[int],
left: uint,
right: uint,
lim: uint)
- -> string::String {
+ -> String {
let n = toks.len();
assert_eq!(n, szs.len());
let mut i = left;
}
s.push_str(&format!("{}={}",
szs[i],
- tok_str(toks[i].clone()))[]);
+ tok_str(&toks[i]))[]);
i += 1u;
i %= n;
}
// fall behind.
let n: uint = 3 * linewidth;
debug!("mk_printer {}", linewidth);
- let token: Vec<Token> = repeat(Eof).take(n).collect();
+ let token: Vec<Token> = repeat(Token::Eof).take(n).collect();
let size: Vec<int> = repeat(0i).take(n).collect();
let scan_stack: Vec<uint> = repeat(0u).take(n).collect();
Printer {
pub fn replace_last_token(&mut self, t: Token) {
self.token[self.right] = t;
}
- pub fn pretty_print(&mut self, t: Token) -> io::IoResult<()> {
+ pub fn pretty_print(&mut self, token: Token) -> io::IoResult<()> {
debug!("pp ~[{},{}]", self.left, self.right);
- match t {
- Eof => {
+ match token {
+ Token::Eof => {
if !self.scan_stack_empty {
self.check_stack(0);
- let left = self.token[self.left].clone();
- let left_size = self.size[self.left];
- try!(self.advance_left(left, left_size));
+ try!(self.advance_left());
}
self.indent(0);
Ok(())
}
- Begin(b) => {
+ Token::Begin(b) => {
if self.scan_stack_empty {
self.left_total = 1;
self.right_total = 1;
} else { self.advance_right(); }
debug!("pp Begin({})/buffer ~[{},{}]",
b.offset, self.left, self.right);
- self.token[self.right] = t;
+ self.token[self.right] = token;
self.size[self.right] = -self.right_total;
let right = self.right;
self.scan_push(right);
Ok(())
}
- End => {
+ Token::End => {
if self.scan_stack_empty {
debug!("pp End/print ~[{},{}]", self.left, self.right);
- self.print(t, 0)
+ self.print(token, 0)
} else {
debug!("pp End/buffer ~[{},{}]", self.left, self.right);
self.advance_right();
- self.token[self.right] = t;
+ self.token[self.right] = token;
self.size[self.right] = -1;
let right = self.right;
self.scan_push(right);
Ok(())
}
}
- Break(b) => {
+ Token::Break(b) => {
if self.scan_stack_empty {
self.left_total = 1;
self.right_total = 1;
self.check_stack(0);
let right = self.right;
self.scan_push(right);
- self.token[self.right] = t;
+ self.token[self.right] = token;
self.size[self.right] = -self.right_total;
self.right_total += b.blank_space;
Ok(())
}
- String(ref s, len) => {
+ Token::String(s, len) => {
if self.scan_stack_empty {
debug!("pp String('{}')/print ~[{},{}]",
- *s, self.left, self.right);
- self.print(t.clone(), len)
+ s, self.left, self.right);
+ self.print(Token::String(s, len), len)
} else {
debug!("pp String('{}')/buffer ~[{},{}]",
- *s, self.left, self.right);
+ s, self.left, self.right);
self.advance_right();
- self.token[self.right] = t.clone();
+ self.token[self.right] = Token::String(s, len);
self.size[self.right] = len;
self.right_total += len;
self.check_stream()
self.size[scanned] = SIZE_INFINITY;
}
}
- let left = self.token[self.left].clone();
- let left_size = self.size[self.left];
- try!(self.advance_left(left, left_size));
+ try!(self.advance_left());
if self.left != self.right {
try!(self.check_stream());
}
self.right %= self.buf_len;
assert!((self.right != self.left));
}
- pub fn advance_left(&mut self, x: Token, l: int) -> io::IoResult<()> {
+ pub fn advance_left(&mut self) -> io::IoResult<()> {
debug!("advance_left ~[{},{}], sizeof({})={}", self.left, self.right,
- self.left, l);
- if l >= 0 {
- let ret = self.print(x.clone(), l);
- match x {
- Break(b) => self.left_total += b.blank_space,
- String(_, len) => {
- assert_eq!(len, l); self.left_total += len;
- }
- _ => ()
- }
- if self.left != self.right {
- self.left += 1u;
- self.left %= self.buf_len;
- let left = self.token[self.left].clone();
- let left_size = self.size[self.left];
- try!(self.advance_left(left, left_size));
+ self.left, self.size[self.left]);
+
+ let mut left_size = self.size[self.left];
+
+ while left_size >= 0 {
+ let left = self.token[self.left].clone();
+
+ let len = match left {
+ Token::Break(b) => b.blank_space,
+ Token::String(_, len) => {
+ assert_eq!(len, left_size);
+ len
+ }
+ _ => 0
+ };
+
+ try!(self.print(left, left_size));
+
+ self.left_total += len;
+
+ if self.left == self.right {
+ break;
}
- ret
- } else {
- Ok(())
+
+ self.left += 1u;
+ self.left %= self.buf_len;
+
+ left_size = self.size[self.left];
}
+
+ Ok(())
}
pub fn check_stack(&mut self, k: int) {
if !self.scan_stack_empty {
let x = self.scan_top();
match self.token[x] {
- Begin(_) => {
+ Token::Begin(_) => {
if k > 0 {
let popped = self.scan_pop();
self.size[popped] = self.size[x] + self.right_total;
self.check_stack(k - 1);
}
}
- End => {
+ Token::End => {
// paper says + not =, but that makes no sense.
let popped = self.scan_pop();
self.size[popped] = 1;
} else {
PrintStackElem {
offset: 0,
- pbreak: Broken(Inconsistent)
+ pbreak: PrintStackBreak::Broken(Breaks::Inconsistent)
}
}
}
}
write!(self.out, "{}", s)
}
- pub fn print(&mut self, x: Token, l: int) -> io::IoResult<()> {
- debug!("print {} {} (remaining line space={})", tok_str(x.clone()), l,
+ pub fn print(&mut self, token: Token, l: int) -> io::IoResult<()> {
+ debug!("print {} {} (remaining line space={})", tok_str(&token), l,
self.space);
- debug!("{}", buf_str(self.token.clone(),
- self.size.clone(),
+ debug!("{}", buf_str(&self.token[],
+ &self.size[],
self.left,
self.right,
6));
- match x {
- Begin(b) => {
+ match token {
+ Token::Begin(b) => {
if l > self.space {
let col = self.margin - self.space + b.offset;
debug!("print Begin -> push broken block at col {}", col);
self.print_stack.push(PrintStackElem {
offset: col,
- pbreak: Broken(b.breaks)
+ pbreak: PrintStackBreak::Broken(b.breaks)
});
} else {
debug!("print Begin -> push fitting block");
self.print_stack.push(PrintStackElem {
offset: 0,
- pbreak: Fits
+ pbreak: PrintStackBreak::Fits
});
}
Ok(())
}
- End => {
+ Token::End => {
debug!("print End -> pop End");
let print_stack = &mut self.print_stack;
assert!((print_stack.len() != 0u));
print_stack.pop().unwrap();
Ok(())
}
- Break(b) => {
+ Token::Break(b) => {
let top = self.get_top();
match top.pbreak {
- Fits => {
+ PrintStackBreak::Fits => {
debug!("print Break({}) in fitting block", b.blank_space);
self.space -= b.blank_space;
self.indent(b.blank_space);
Ok(())
}
- Broken(Consistent) => {
+ PrintStackBreak::Broken(Breaks::Consistent) => {
debug!("print Break({}+{}) in consistent block",
top.offset, b.offset);
let ret = self.print_newline(top.offset + b.offset);
self.space = self.margin - (top.offset + b.offset);
ret
}
- Broken(Inconsistent) => {
+ PrintStackBreak::Broken(Breaks::Inconsistent) => {
if l > self.space {
debug!("print Break({}+{}) w/ newline in inconsistent",
top.offset, b.offset);
}
}
}
- String(s, len) => {
+ Token::String(s, len) => {
debug!("print String({})", s);
assert_eq!(l, len);
// assert!(l <= space);
self.space -= len;
self.print_str(&s[])
}
- Eof => {
+ Token::Eof => {
// Eof should never get here.
panic!();
}
//
// "raw box"
pub fn rbox(p: &mut Printer, indent: uint, b: Breaks) -> io::IoResult<()> {
- p.pretty_print(Begin(BeginToken {
+ p.pretty_print(Token::Begin(BeginToken {
offset: indent as int,
breaks: b
}))
}
pub fn ibox(p: &mut Printer, indent: uint) -> io::IoResult<()> {
- rbox(p, indent, Inconsistent)
+ rbox(p, indent, Breaks::Inconsistent)
}
pub fn cbox(p: &mut Printer, indent: uint) -> io::IoResult<()> {
- rbox(p, indent, Consistent)
+ rbox(p, indent, Breaks::Consistent)
}
pub fn break_offset(p: &mut Printer, n: uint, off: int) -> io::IoResult<()> {
- p.pretty_print(Break(BreakToken {
+ p.pretty_print(Token::Break(BreakToken {
offset: off,
blank_space: n as int
}))
}
-pub fn end(p: &mut Printer) -> io::IoResult<()> { p.pretty_print(End) }
+pub fn end(p: &mut Printer) -> io::IoResult<()> {
+ p.pretty_print(Token::End)
+}
-pub fn eof(p: &mut Printer) -> io::IoResult<()> { p.pretty_print(Eof) }
+pub fn eof(p: &mut Printer) -> io::IoResult<()> {
+ p.pretty_print(Token::Eof)
+}
pub fn word(p: &mut Printer, wrd: &str) -> io::IoResult<()> {
- p.pretty_print(String(/* bad */ wrd.to_string(), wrd.len() as int))
+ p.pretty_print(Token::String(/* bad */ wrd.to_string(), wrd.len() as int))
}
pub fn huge_word(p: &mut Printer, wrd: &str) -> io::IoResult<()> {
- p.pretty_print(String(/* bad */ wrd.to_string(), SIZE_INFINITY))
+ p.pretty_print(Token::String(/* bad */ wrd.to_string(), SIZE_INFINITY))
}
pub fn zero_word(p: &mut Printer, wrd: &str) -> io::IoResult<()> {
- p.pretty_print(String(/* bad */ wrd.to_string(), 0))
+ p.pretty_print(Token::String(/* bad */ wrd.to_string(), 0))
}
pub fn spaces(p: &mut Printer, n: uint) -> io::IoResult<()> {
}
pub fn hardbreak_tok_offset(off: int) -> Token {
- Break(BreakToken {offset: off, blank_space: SIZE_INFINITY})
+ Token::Break(BreakToken {offset: off, blank_space: SIZE_INFINITY})
}
-pub fn hardbreak_tok() -> Token { return hardbreak_tok_offset(0); }
+pub fn hardbreak_tok() -> Token {
+ hardbreak_tok_offset(0)
+}
use parse::lexer::comments;
use parse;
use print::pp::{self, break_offset, word, space, zerobreak, hardbreak};
-use print::pp::{Breaks, Consistent, Inconsistent, eof};
+use print::pp::{Breaks, eof};
+use print::pp::Breaks::{Consistent, Inconsistent};
use ptr::P;
use std::{ascii, mem};
impl<'a> State<'a> {
pub fn ibox(&mut self, u: uint) -> IoResult<()> {
- self.boxes.push(pp::Inconsistent);
+ self.boxes.push(pp::Breaks::Inconsistent);
pp::ibox(&mut self.s, u)
}
}
pub fn cbox(&mut self, u: uint) -> IoResult<()> {
- self.boxes.push(pp::Consistent);
+ self.boxes.push(pp::Breaks::Consistent);
pp::cbox(&mut self.s, u)
}
}
pub fn is_begin(&mut self) -> bool {
- match self.s.last_token() { pp::Begin(_) => true, _ => false }
+ match self.s.last_token() {
+ pp::Token::Begin(_) => true,
+ _ => false,
+ }
}
pub fn is_end(&mut self) -> bool {
- match self.s.last_token() { pp::End => true, _ => false }
+ match self.s.last_token() {
+ pp::Token::End => true,
+ _ => false,
+ }
}
// is this the beginning of a line?
pub fn in_cbox(&self) -> bool {
match self.boxes.last() {
- Some(&last_box) => last_box == pp::Consistent,
+ Some(&last_box) => last_box == pp::Breaks::Consistent,
None => false
}
}
try!(self.print_bounds("", &bounds[]));
}
ast::TyQPath(ref qpath) => {
- try!(word(&mut self.s, "<"));
- try!(self.print_type(&*qpath.self_type));
- try!(space(&mut self.s));
- try!(self.word_space("as"));
- try!(self.print_trait_ref(&*qpath.trait_ref));
- try!(word(&mut self.s, ">"));
- try!(word(&mut self.s, "::"));
- try!(self.print_ident(qpath.item_name));
+ try!(self.print_qpath(&**qpath, false))
}
ast::TyFixedLengthVec(ref ty, ref v) => {
try!(word(&mut self.s, "["));
Ok(())
}
+ fn print_expr_box(&mut self,
+ place: &Option<P<ast::Expr>>,
+ expr: &ast::Expr) -> IoResult<()> {
+ try!(word(&mut self.s, "box"));
+ try!(word(&mut self.s, "("));
+ try!(place.as_ref().map_or(Ok(()), |e|self.print_expr(&**e)));
+ try!(self.word_space(")"));
+ self.print_expr(expr)
+ }
+
+ fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) -> IoResult<()> {
+ try!(self.ibox(indent_unit));
+ try!(word(&mut self.s, "["));
+ try!(self.commasep_exprs(Inconsistent, &exprs[]));
+ try!(word(&mut self.s, "]"));
+ self.end()
+ }
+
+ fn print_expr_repeat(&mut self,
+ element: &ast::Expr,
+ count: &ast::Expr) -> IoResult<()> {
+ try!(self.ibox(indent_unit));
+ try!(word(&mut self.s, "["));
+ try!(self.print_expr(element));
+ try!(self.word_space(";"));
+ try!(self.print_expr(count));
+ try!(word(&mut self.s, "]"));
+ self.end()
+ }
+
+ fn print_expr_struct(&mut self,
+ path: &ast::Path,
+ fields: &[ast::Field],
+ wth: &Option<P<ast::Expr>>) -> IoResult<()> {
+ try!(self.print_path(path, true));
+ if !(fields.is_empty() && wth.is_none()) {
+ try!(word(&mut self.s, "{"));
+ try!(self.commasep_cmnt(
+ Consistent,
+ &fields[],
+ |s, field| {
+ try!(s.ibox(indent_unit));
+ try!(s.print_ident(field.ident.node));
+ try!(s.word_space(":"));
+ try!(s.print_expr(&*field.expr));
+ s.end()
+ },
+ |f| f.span));
+ match *wth {
+ Some(ref expr) => {
+ try!(self.ibox(indent_unit));
+ if !fields.is_empty() {
+ try!(word(&mut self.s, ","));
+ try!(space(&mut self.s));
+ }
+ try!(word(&mut self.s, ".."));
+ try!(self.print_expr(&**expr));
+ try!(self.end());
+ }
+ _ => try!(word(&mut self.s, ",")),
+ }
+ try!(word(&mut self.s, "}"));
+ }
+ Ok(())
+ }
+
+ fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>]) -> IoResult<()> {
+ try!(self.popen());
+ try!(self.commasep_exprs(Inconsistent, &exprs[]));
+ if exprs.len() == 1 {
+ try!(word(&mut self.s, ","));
+ }
+ self.pclose()
+ }
+
+ fn print_expr_call(&mut self,
+ func: &ast::Expr,
+ args: &[P<ast::Expr>]) -> IoResult<()> {
+ try!(self.print_expr_maybe_paren(func));
+ self.print_call_post(args)
+ }
+
+ fn print_expr_method_call(&mut self,
+ ident: ast::SpannedIdent,
+ tys: &[P<ast::Ty>],
+ args: &[P<ast::Expr>]) -> IoResult<()> {
+ let base_args = args.slice_from(1);
+ try!(self.print_expr(&*args[0]));
+ try!(word(&mut self.s, "."));
+ try!(self.print_ident(ident.node));
+ if tys.len() > 0u {
+ try!(word(&mut self.s, "::<"));
+ try!(self.commasep(Inconsistent, tys,
+ |s, ty| s.print_type(&**ty)));
+ try!(word(&mut self.s, ">"));
+ }
+ self.print_call_post(base_args)
+ }
+
+ fn print_expr_binary(&mut self,
+ op: ast::BinOp,
+ lhs: &ast::Expr,
+ rhs: &ast::Expr) -> IoResult<()> {
+ try!(self.print_expr(lhs));
+ try!(space(&mut self.s));
+ try!(self.word_space(ast_util::binop_to_string(op)));
+ self.print_expr(rhs)
+ }
+
+ fn print_expr_unary(&mut self,
+ op: ast::UnOp,
+ expr: &ast::Expr) -> IoResult<()> {
+ try!(word(&mut self.s, ast_util::unop_to_string(op)));
+ self.print_expr_maybe_paren(expr)
+ }
+
+ fn print_expr_addr_of(&mut self,
+ mutability: ast::Mutability,
+ expr: &ast::Expr) -> IoResult<()> {
+ try!(word(&mut self.s, "&"));
+ try!(self.print_mutability(mutability));
+ self.print_expr_maybe_paren(expr)
+ }
+
pub fn print_expr(&mut self, expr: &ast::Expr) -> IoResult<()> {
try!(self.maybe_print_comment(expr.span.lo));
try!(self.ibox(indent_unit));
try!(self.ann.pre(self, NodeExpr(expr)));
match expr.node {
- ast::ExprBox(ref p, ref e) => {
- try!(word(&mut self.s, "box"));
- try!(word(&mut self.s, "("));
- try!(p.as_ref().map_or(Ok(()), |e|self.print_expr(&**e)));
- try!(self.word_space(")"));
- try!(self.print_expr(&**e));
+ ast::ExprBox(ref place, ref expr) => {
+ try!(self.print_expr_box(place, &**expr));
}
ast::ExprVec(ref exprs) => {
- try!(self.ibox(indent_unit));
- try!(word(&mut self.s, "["));
- try!(self.commasep_exprs(Inconsistent, &exprs[]));
- try!(word(&mut self.s, "]"));
- try!(self.end());
+ try!(self.print_expr_vec(&exprs[]));
}
-
ast::ExprRepeat(ref element, ref count) => {
- try!(self.ibox(indent_unit));
- try!(word(&mut self.s, "["));
- try!(self.print_expr(&**element));
- try!(self.word_space(";"));
- try!(self.print_expr(&**count));
- try!(word(&mut self.s, "]"));
- try!(self.end());
+ try!(self.print_expr_repeat(&**element, &**count));
}
-
ast::ExprStruct(ref path, ref fields, ref wth) => {
- try!(self.print_path(path, true));
- if !(fields.is_empty() && wth.is_none()) {
- try!(word(&mut self.s, "{"));
- try!(self.commasep_cmnt(
- Consistent,
- &fields[],
- |s, field| {
- try!(s.ibox(indent_unit));
- try!(s.print_ident(field.ident.node));
- try!(s.word_space(":"));
- try!(s.print_expr(&*field.expr));
- s.end()
- },
- |f| f.span));
- match *wth {
- Some(ref expr) => {
- try!(self.ibox(indent_unit));
- if !fields.is_empty() {
- try!(word(&mut self.s, ","));
- try!(space(&mut self.s));
- }
- try!(word(&mut self.s, ".."));
- try!(self.print_expr(&**expr));
- try!(self.end());
- }
- _ => try!(word(&mut self.s, ",")),
- }
- try!(word(&mut self.s, "}"));
- }
+ try!(self.print_expr_struct(path, &fields[], wth));
}
ast::ExprTup(ref exprs) => {
- try!(self.popen());
- try!(self.commasep_exprs(Inconsistent, &exprs[]));
- if exprs.len() == 1 {
- try!(word(&mut self.s, ","));
- }
- try!(self.pclose());
+ try!(self.print_expr_tup(&exprs[]));
}
ast::ExprCall(ref func, ref args) => {
- try!(self.print_expr_maybe_paren(&**func));
- try!(self.print_call_post(&args[]));
+ try!(self.print_expr_call(&**func, &args[]));
}
ast::ExprMethodCall(ident, ref tys, ref args) => {
- let base_args = args.slice_from(1);
- try!(self.print_expr(&*args[0]));
- try!(word(&mut self.s, "."));
- try!(self.print_ident(ident.node));
- if tys.len() > 0u {
- try!(word(&mut self.s, "::<"));
- try!(self.commasep(Inconsistent, &tys[],
- |s, ty| s.print_type(&**ty)));
- try!(word(&mut self.s, ">"));
- }
- try!(self.print_call_post(base_args));
+ try!(self.print_expr_method_call(ident, &tys[], &args[]));
}
ast::ExprBinary(op, ref lhs, ref rhs) => {
- try!(self.print_expr(&**lhs));
- try!(space(&mut self.s));
- try!(self.word_space(ast_util::binop_to_string(op)));
- try!(self.print_expr(&**rhs));
+ try!(self.print_expr_binary(op, &**lhs, &**rhs));
}
ast::ExprUnary(op, ref expr) => {
- try!(word(&mut self.s, ast_util::unop_to_string(op)));
- try!(self.print_expr_maybe_paren(&**expr));
+ try!(self.print_expr_unary(op, &**expr));
}
ast::ExprAddrOf(m, ref expr) => {
- try!(word(&mut self.s, "&"));
- try!(self.print_mutability(m));
- try!(self.print_expr_maybe_paren(&**expr));
+ try!(self.print_expr_addr_of(m, &**expr));
+ }
+ ast::ExprLit(ref lit) => {
+ try!(self.print_literal(&**lit));
}
- ast::ExprLit(ref lit) => try!(self.print_literal(&**lit)),
ast::ExprCast(ref expr, ref ty) => {
try!(self.print_expr(&**expr));
try!(space(&mut self.s));
}
}
ast::ExprPath(ref path) => try!(self.print_path(path, true)),
+ ast::ExprQPath(ref qpath) => try!(self.print_qpath(&**qpath, true)),
ast::ExprBreak(opt_ident) => {
try!(word(&mut self.s, "break"));
try!(space(&mut self.s));
Ok(())
}
+ fn print_qpath(&mut self,
+ qpath: &ast::QPath,
+ colons_before_params: bool)
+ -> IoResult<()>
+ {
+ try!(word(&mut self.s, "<"));
+ try!(self.print_type(&*qpath.self_type));
+ try!(space(&mut self.s));
+ try!(self.word_space("as"));
+ try!(self.print_trait_ref(&*qpath.trait_ref));
+ try!(word(&mut self.s, ">"));
+ try!(word(&mut self.s, "::"));
+ try!(self.print_ident(qpath.item_path.identifier));
+ self.print_path_parameters(&qpath.item_path.parameters, colons_before_params)
+ }
+
fn print_path_parameters(&mut self,
parameters: &ast::PathParameters,
colons_before_params: bool)
comments::BlankLine => {
// We need to do at least one, possibly two hardbreaks.
let is_semi = match self.s.last_token() {
- pp::String(s, _) => ";" == s,
+ pp::Token::String(s, _) => ";" == s,
_ => false
};
if is_semi || self.is_begin() || self.is_end() {
fn visit_path(&mut self, path: &'v Path, _id: ast::NodeId) {
walk_path(self, path)
}
+ fn visit_qpath(&mut self, qpath_span: Span, qpath: &'v QPath) {
+ walk_qpath(self, qpath_span, qpath)
+ }
fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment) {
walk_path_segment(self, path_span, path_segment)
}
walk_ty_param_bounds_helper(visitor, bounds);
}
TyQPath(ref qpath) => {
- visitor.visit_ty(&*qpath.self_type);
- visitor.visit_trait_ref(&*qpath.trait_ref);
- visitor.visit_ident(typ.span, qpath.item_name);
+ visitor.visit_qpath(typ.span, &**qpath);
}
TyFixedLengthVec(ref ty, ref expression) => {
visitor.visit_ty(&**ty);
}
}
+pub fn walk_qpath<'v, V: Visitor<'v>>(visitor: &mut V,
+ qpath_span: Span,
+ qpath: &'v QPath) {
+ visitor.visit_ty(&*qpath.self_type);
+ visitor.visit_trait_ref(&*qpath.trait_ref);
+ visitor.visit_path_segment(qpath_span, &qpath.item_path);
+}
+
pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V,
path_span: Span,
segment: &'v PathSegment) {
ExprPath(ref path) => {
visitor.visit_path(path, expression.id)
}
+ ExprQPath(ref qpath) => {
+ visitor.visit_qpath(expression.span, &**qpath)
+ }
ExprBreak(_) | ExprAgain(_) => {}
ExprRet(ref optional_expression) => {
walk_expr_opt(visitor, optional_expression)
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
-pub fn black_box<T>(dummy: T) {
+pub fn black_box<T>(dummy: T) -> T {
// we need to "use" the argument in some way LLVM can't
// introspect.
unsafe {asm!("" : : "r"(&dummy))}
+ dummy
}
--- /dev/null
+// Mark stack as non-executable
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack, "", %progbits
+#endif
+
+/* See i386/morestack.S for the lengthy, general explanation. */
+
+.global rust_stack_exhausted
+
+.hidden __morestack
+
+// FIXME(POWERPC): this might not be perfectly right but works for now
+__morestack:
+ .cfi_startproc
+ bl rust_stack_exhausted
+ // the above function ensures that it never returns
+ .cfi_endproc
+.end __morestack
--- /dev/null
+// Mark stack as non-executable
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack, "", %progbits
+#endif
use book;
use book::{Book, BookItem};
use css;
+use javascript;
use regex::Regex;
Ok(())
}
- try!(writeln!(out, "<div id='toc'>"));
+ try!(writeln!(out, "<div id='toc' class='mobile-hidden'>"));
try!(writeln!(out, "<ul class='chapter'>"));
try!(walk_items(&book.chapters[], "", path_to_root, out));
try!(writeln!(out, "</ul>"));
let prelude = tmp.path().join("prelude.html");
{
let mut toc = BufferedWriter::new(try!(File::create(&prelude)));
+ try!(writeln!(&mut toc, r#"<div id="nav">
+ <button id="toggle-nav">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="bar"></span>
+ <span class="bar"></span>
+ <span class="bar"></span>
+ </button>
+ </div>"#));
let _ = write_toc(book, &item.path_to_root, &mut toc);
try!(writeln!(&mut toc, "<div id='page-wrapper'>"));
try!(writeln!(&mut toc, "<div id='page'>"));
let postlude = tmp.path().join("postlude.html");
{
let mut toc = BufferedWriter::new(try!(File::create(&postlude)));
+ try!(toc.write_str(javascript::JAVASCRIPT));
try!(writeln!(&mut toc, "</div></div>"));
}
#page {
margin-left: auto;
margin-right:auto;
- width: 750px;
+ max-width: 750px;
}
.chapter {
.chapter li a {
color: #000000;
}
+
+@media only screen and (max-width: 1060px) {
+ #toc {
+ width: 100%;
+ margin-right: 0;
+ top: 40px;
+ }
+ #page-wrapper {
+ top: 40px;
+ left: 15px;
+ padding-right: 15px;
+ }
+ .mobile-hidden {
+ display: none;
+ }
+}
+
+
+#toggle-nav {
+ height: 20px;
+ width: 30px;
+ padding: 3px 3px 0 3px;
+}
+
+#toggle-nav {
+ margin-top: 5px;
+ width: 30px;
+ height: 30px;
+ background-color: #FFF;
+ border: 1px solid #666;
+ border-radius: 3px 3px 3px 3px;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
+
+.bar {
+ display: block;
+ background-color: #000;
+ border-radius: 2px;
+ width: 100%;
+ height: 2px;
+ margin: 2px 0 3px;
+ padding: 0;
+}
+
"#;
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// The rust-book JavaScript in string form.
+
+pub static JAVASCRIPT: &'static str = r#"
+<script type="text/javascript">
+document.addEventListener("DOMContentLoaded", function(event) {
+ document.getElementById("toggle-nav").onclick = toggleNav;
+ function toggleNav() {
+ var toc = document.getElementById("toc");
+ var pagewrapper = document.getElementById("page-wrapper");
+ toggleClass(toc, "mobile-hidden");
+ toggleClass(pagewrapper, "mobile-hidden");
+ };
+
+ function toggleClass(el, className) {
+ // from http://youmightnotneedjquery.com/
+ if (el.classList) {
+ el.classList.toggle(className);
+ } else {
+ var classes = el.className.split(' ');
+ var existingIndex = classes.indexOf(className);
+
+ if (existingIndex >= 0) {
+ classes.splice(existingIndex, 1);
+ } else {
+ classes.push(className);
+ }
+
+ el.className = classes.join(' ');
+ }
+ }
+});
+</script>
+"#;
mod test;
mod css;
+mod javascript;
#[cfg(not(test))] // thanks #12327
fn main() {
# If this file is modified, then llvm will be forcibly cleaned and then rebuilt.
# The actual contents of this file do not matter, but to trigger a change on the
# build bots then the contents should be changed so git updates the mtime.
-2015-01-05
+2015-01-13
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
extern crate syntax;
extern crate rustc;
-use syntax::ast::{TokenTree, Item, MetaItem};
+use syntax::ast::{TokenTree, Item, MetaItem, ImplItem, TraitItem, Method};
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::parse::token;
reg.register_syntax_extension(
token::intern("into_foo"),
Modifier(box expand_into_foo));
+ reg.register_syntax_extension(
+ token::intern("into_multi_foo"),
+ MultiModifier(box expand_into_foo_multi));
}
fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
})
}
+fn expand_into_foo_multi(cx: &mut ExtCtxt,
+ sp: Span,
+ attr: &MetaItem,
+ it: Annotatable) -> Annotatable {
+ match it {
+ Annotatable::Item(it) => {
+ Annotatable::Item(P(Item {
+ attrs: it.attrs.clone(),
+ ..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone()
+ }))
+ }
+ Annotatable::ImplItem(it) => {
+ Annotatable::ImplItem(ImplItem::MethodImplItem(
+ quote_method!(cx, fn foo(&self) -> i32 { 42 })
+ ))
+ }
+ Annotatable::TraitItem(it) => {
+ Annotatable::TraitItem(TraitItem::ProvidedMethod(
+ quote_method!(cx, fn foo(&self) -> i32 { 0 })
+ ))
+ }
+ }
+}
+
fn expand_forged_ident(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult+'static> {
use syntax::ext::quote::rt::*;
impl Something for X {
fn yay<T: Str>(_:Option<X>, thing: &[T]) {
-//~^ ERROR in method `yay`, type parameter 0 requires bound `Str`, which is not required
-
+ //~^ ERROR the requirement `T : Str` appears on the impl method
}
}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #20831: debruijn index account was thrown off
+// by the (anonymous) lifetime in `<Self as Publisher>::Output`
+// below. Note that changing to a named lifetime made the problem go
+// away.
+
+use std::ops::{Shl, Shr};
+use std::cell::RefCell;
+
+pub trait Subscriber {
+ type Input;
+}
+
+pub trait Publisher<'a> {
+ type Output;
+ fn subscribe(&mut self, Box<Subscriber<Input=Self::Output> + 'a>);
+}
+
+pub trait Processor<'a> : Subscriber + Publisher<'a> { }
+
+impl<'a, P> Processor<'a> for P where P : Subscriber + Publisher<'a> { }
+
+struct MyStruct<'a> {
+ sub: Box<Subscriber<Input=u64> + 'a>
+}
+
+impl<'a> Publisher<'a> for MyStruct<'a> {
+ type Output = u64;
+ fn subscribe(&mut self, t : Box<Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
+ // Not obvious, but there is an implicit lifetime here -------^
+ //~^^ ERROR cannot infer
+ //
+ // The fact that `Publisher` is using an implicit lifetime is
+ // what was causing the debruijn accounting to be off, so
+ // leave it that way!
+ self.sub = t;
+ }
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+#![feature(asm)]
+
+fn main() {
+ let a;
+ asm!("nop" "nop"); //~ ERROR malformed inline assembly
+ asm!("nop" "nop" : "=r"(a)); //~ ERROR malformed inline assembly
+}
}
impl A for E {
- fn b<F: Sync, G>(_x: F) -> F { panic!() } //~ ERROR type parameter 0 requires `Sync`
+ fn b<F: Sync, G>(_x: F) -> F { panic!() }
+ //~^ ERROR `F : core::marker::Sync` appears on the impl method
}
fn main() {}
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Test that ~ pointers give an obsolescence message.
-
-fn foo(x: ~isize) {} //~ ERROR obsolete syntax: `~` notation for owned pointers
-fn bar(x: ~str) {} //~ ERROR obsolete syntax: `~` notation for owned pointers
-fn baz(x: ~[isize]) {} //~ ERROR obsolete syntax: `~[T]` is no longer a type
-
-fn main() {
- let x = ~4is; //~ ERROR obsolete syntax: `~` notation for owned pointer allocation
- let y = ~"hello"; //~ ERROR obsolete syntax: `~` notation for owned pointer allocation
- let z = ~[1is, 2, 3]; //~ ERROR obsolete syntax: `~[T]` is no longer a type
-}
+++ /dev/null
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-fn main() {
- struct S(isize);
- let s = S(0);
- let x = *s; //~ ERROR single-field tuple-structs can no longer be dereferenced
-}
x: &'a mut &'a isize
}
-pub trait Foo<'a> {
+pub trait Foo<'a, 't> {
fn no_bound<'b>(self, b: Inv<'b>);
fn has_bound<'b:'a>(self, b: Inv<'b>);
fn wrong_bound1<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
- fn wrong_bound2<'b,'c,'d:'a+'b+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
+ fn okay_bound<'b,'c,'d:'a+'b+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
+ fn another_bound<'x: 'a>(self, x: Inv<'x>);
}
-impl<'a> Foo<'a> for &'a isize {
+impl<'a, 't> Foo<'a, 't> for &'a isize {
fn no_bound<'b:'a>(self, b: Inv<'b>) {
//~^ ERROR lifetime parameters or bounds on method `no_bound` do not match
}
// cases.
}
- fn wrong_bound2<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) {
- //~^ ERROR distinct set of bounds from its counterpart
+ fn okay_bound<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) {
}
+
+ fn another_bound<'x: 't>(self, x: Inv<'x>) {}
}
fn main() { }
impl Foo for isize {
// invalid bound for T, was defined as Eq in trait
fn test_error1_fn<T: Ord>(&self) {}
- //~^ ERROR in method `test_error1_fn`, type parameter 0 requires bound `core::cmp::Ord`
+ //~^ ERROR the requirement `T : core::cmp::Ord` appears on the impl
// invalid bound for T, was defined as Eq + Ord in trait
fn test_error2_fn<T: Eq + B>(&self) {}
- //~^ ERROR in method `test_error2_fn`, type parameter 0 requires bound `B`
+ //~^ ERROR the requirement `T : B` appears on the impl
// invalid bound for T, was defined as Eq + Ord in trait
fn test_error3_fn<T: B + Eq>(&self) {}
- //~^ ERROR in method `test_error3_fn`, type parameter 0 requires bound `B`
+ //~^ ERROR the requirement `T : B` appears on the impl
// multiple bounds, same order as in trait
fn test3_fn<T: Ord + Eq>(&self) {}
// parameters in impls must be equal or more general than in the defining trait
fn test_error5_fn<T: B>(&self) {}
- //~^ ERROR in method `test_error5_fn`, type parameter 0 requires bound `B`
+ //~^ ERROR the requirement `T : B` appears on the impl
// bound `std::cmp::Eq` not enforced by this implementation, but this is OK
fn test6_fn<T: A>(&self) {}
fn test_error7_fn<T: A + Eq>(&self) {}
- //~^ ERROR in method `test_error7_fn`, type parameter 0 requires bound `core::cmp::Eq`
+ //~^ ERROR the requirement `T : core::cmp::Eq` appears on the impl
fn test_error8_fn<T: C>(&self) {}
- //~^ ERROR in method `test_error8_fn`, type parameter 0 requires bound `C`
+ //~^ ERROR the requirement `T : C` appears on the impl
}
impl Trait for usize {
fn method<G: Getter<usize>>() {}
- //~^ ERROR in method `method`, type parameter 0 requires bound `Getter<usize>`
+ //~^ G : Getter<usize>` appears on the impl method but not on the corresponding trait method
}
fn main() {}
-
impl<A, T: Iterator<A>> IteratorUtil<A> for T {
fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<T, U> {
- //~^ ERROR in method `zip`, type parameter 1 requires bound `Iterator<B>`
+ //~^ ERROR the requirement `U : Iterator<B>` appears on the impl method
ZipIterator{a: self, b: other}
}
}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::borrow::IntoCow;
+
+fn main() {
+ <String as IntoCow>::into_cow("foo".to_string());
+ //~^ ERROR wrong number of type arguments: expected 2, found 0
+}
+
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::ops::Add;
+
+fn main() {
+ <i32 as Add<u32>>::add(1, 2);
+ //~^ ERROR the trait `core::ops::Add<u32>` is not implemented for the type `i32`
+ <i32 as Add<i32>>::add(1u32, 2);
+ //~^ ERROR mismatched types
+ <i32 as Add<i32>>::add(1, 2u32);
+ //~^ ERROR mismatched types
+}
+
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-fn main() {
- let x = [1is, 2, 3];
- match x {
- [a, b, ..c] => { //~ ERROR obsolete syntax
- assert_eq!(a, 1);
- assert_eq!(b, 2);
- let expected: &[_] = &[3];
- assert_eq!(c, expected);
- }
- }
-}
-
+++ /dev/null
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ignore-android: FIXME(#10381)
-// min-lldb-version: 310
-
-// compile-flags:-g
-
-struct Peekable<I> where I: Iterator {
- _iter: I,
- _next: Option<<I as Iterator>::Item>,
-}
-
-fn main() {
- let mut iter = Vec::<i32>::new().into_iter();
- let next = iter.next();
- let _v = Peekable {
- _iter: iter,
- _next : next,
- };
-}
$(TMPDIR)/%.dot: %.rs
$(eval $(call FIND_LAST_BLOCK,$<))
- $(RUSTC_LIB) -Z unstable-options --xpretty flowgraph=$(LASTBLOCKNUM_$<) $< -o $@.tmp
+ $(RUSTC_LIB) -Z unstable-options --xpretty flowgraph,unlabelled=$(LASTBLOCKNUM_$<) $< -o $@.tmp
cat $@.tmp | sed -e 's@ (id=[0-9]*)@@g' \
-e 's@\[label=""\]@@' \
-e 's@digraph [a-zA-Z0-9_]* @digraph block @' \
N11 -> N12;
N12 -> N13;
N13 -> N14;
- N14 -> N6[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 2is { break ; \"unreachable\"; },\lexiting scope_4 block { x -= 1is; if x == 2is { break ; \"unreachable\"; } }"];
+ N14 -> N6;
N15 -> N16;
N16 -> N17;
N17 -> N18;
N12 -> N13;
N13 -> N14;
N14 -> N15;
- N15 -> N9[label="exiting scope_0 expr break \'outer,\lexiting scope_1 stmt break \'outer ;,\lexiting scope_2 block { break \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1is { break \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1is { break \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { break ; \"unreachable\"; }\l y -= 3is;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { break ; \"unreachable\"; }\l y -= 3is;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { break ; \"unreachable\"; }\l y -= 3is;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { break ; \"unreachable\"; }\l y -= 3is;\l }\l y -= 4is;\l x -= 5is;\l}\l"];
+ N15 -> N9;
N16 -> N17;
N17 -> N18;
N18 -> N19;
N23 -> N24;
N24 -> N25;
N25 -> N26;
- N26 -> N11[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y >= 2is { break ; \"unreachable\"; },\lexiting scope_4 stmt if y >= 2is { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { break ; \"unreachable\"; }\l y -= 3is;\l}\l"];
+ N26 -> N11;
N27 -> N28;
N28 -> N29;
N29 -> N30;
N12 -> N13;
N13 -> N14;
N14 -> N15;
- N15 -> N8[label="exiting scope_0 expr continue \'outer,\lexiting scope_1 stmt continue \'outer ;,\lexiting scope_2 block { continue \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1is { continue \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1is { continue \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 1is { break ; \"unreachable\"; }\l y -= 1is;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 1is { break ; \"unreachable\"; }\l y -= 1is;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 1is { break ; \"unreachable\"; }\l y -= 1is;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 1is { break ; \"unreachable\"; }\l y -= 1is;\l }\l y -= 1is;\l x -= 1is;\l}\l"];
+ N15 -> N8;
N16 -> N17;
N17 -> N18;
N18 -> N19;
N23 -> N24;
N24 -> N25;
N25 -> N26;
- N26 -> N11[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y >= 1is { break ; \"unreachable\"; },\lexiting scope_4 stmt if y >= 1is { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 1is { break ; \"unreachable\"; }\l y -= 1is;\l}\l"];
+ N26 -> N11;
N27 -> N28;
N28 -> N29;
N29 -> N30;
N12 -> N13;
N13 -> N14;
N14 -> N15;
- N15 -> N9[label="exiting scope_0 expr break \'outer,\lexiting scope_1 stmt break \'outer ;,\lexiting scope_2 block { break \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1is { break \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1is { break \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l y -= 3is;\l x -= 5is;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l y -= 3is;\l x -= 5is;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l y -= 3is;\l x -= 5is;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l y -= 3is;\l x -= 5is;\l }\l \"unreachable\";\l}\l"];
+ N15 -> N9;
N16 -> N17;
N17 -> N18;
N18 -> N19;
N23 -> N24;
N24 -> N25;
N25 -> N26;
- N26 -> N1[label="exiting scope_0 expr \'inner:\l loop {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l y -= 3is;\l x -= 5is;\l }\l,\lexiting scope_1 expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1is { break \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l y -= 3is;\l x -= 5is;\l }\l \"unreachable\";\l }\l"];
+ N26 -> N1;
N27 -> N28;
N28 -> N29;
N29 -> N30;
N12 -> N13;
N13 -> N14;
N14 -> N15;
- N15 -> N8[label="exiting scope_0 expr continue \'outer,\lexiting scope_1 stmt continue \'outer ;,\lexiting scope_2 block { continue \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1is { continue \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1is { continue \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l x -= 1is;\l y -= 3is;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l x -= 1is;\l y -= 3is;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l x -= 1is;\l y -= 3is;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l x -= 1is;\l y -= 3is;\l }\l \"unreachable\";\l}\l"];
+ N15 -> N8;
N16 -> N17;
N17 -> N18;
N18 -> N19;
N23 -> N24;
N24 -> N25;
N25 -> N26;
- N26 -> N1[label="exiting scope_0 expr \'inner:\l loop {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l x -= 1is;\l y -= 3is;\l }\l,\lexiting scope_1 expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1is { continue \'outer ; \"unreachable\"; }\l if y >= 2is { return; \"unreachable\"; }\l x -= 1is;\l y -= 3is;\l }\l \"unreachable\";\l }\l"];
+ N26 -> N1;
N27 -> N28;
N28 -> N29;
N29 -> N30;
N40 -> N41;
N41 -> N42;
N42 -> N43;
- N43 -> N1[label="exiting scope_0 expr while y > 0is {\l y -= 1is;\l while z > 0is { z -= 1is; }\l if x > 10is { return; \"unreachable\"; }\l}\l,\lexiting scope_1 expr while x > 0is {\l x -= 1is;\l while y > 0is {\l y -= 1is;\l while z > 0is { z -= 1is; }\l if x > 10is { return; \"unreachable\"; }\l }\l}\l"];
+ N43 -> N1;
N44 -> N45;
N45 -> N46;
N46 -> N47;
N13 -> N14;
N14 -> N15;
N15 -> N16;
- N16 -> N12[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 0is { break ; \"unreachable\"; },\lexiting scope_4 stmt if x == 0is { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 0is { break ; \"unreachable\"; }\l x -= 1is;\l loop {\l if y == 0is { break ; \"unreachable\"; }\l y -= 1is;\l loop { if z == 0is { break ; \"unreachable\"; } z -= 1is; }\l if x > 10is { return; \"unreachable\"; }\l }\l}\l"];
+ N16 -> N12;
N17 -> N18;
N18 -> N19;
N19 -> N20;
N30 -> N31;
N31 -> N32;
N32 -> N33;
- N33 -> N29[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y == 0is { break ; \"unreachable\"; },\lexiting scope_4 stmt if y == 0is { break ; \"unreachable\"; },\lexiting scope_5 block {\l if y == 0is { break ; \"unreachable\"; }\l y -= 1is;\l loop { if z == 0is { break ; \"unreachable\"; } z -= 1is; }\l if x > 10is { return; \"unreachable\"; }\l}\l"];
+ N33 -> N29;
N34 -> N35;
N35 -> N36;
N36 -> N37;
N47 -> N48;
N48 -> N49;
N49 -> N50;
- N50 -> N46[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if z == 0is { break ; \"unreachable\"; },\lexiting scope_4 stmt if z == 0is { break ; \"unreachable\"; },\lexiting scope_5 block { if z == 0is { break ; \"unreachable\"; } z -= 1is; }"];
+ N50 -> N46;
N51 -> N52;
N52 -> N53;
N53 -> N54;
N64 -> N65;
N65 -> N66;
N66 -> N67;
- N67 -> N1[label="exiting scope_0 expr loop {\l if y == 0is { break ; \"unreachable\"; }\l y -= 1is;\l loop { if z == 0is { break ; \"unreachable\"; } z -= 1is; }\l if x > 10is { return; \"unreachable\"; }\l}\l,\lexiting scope_1 expr loop {\l if x == 0is { break ; \"unreachable\"; }\l x -= 1is;\l loop {\l if y == 0is { break ; \"unreachable\"; }\l y -= 1is;\l loop { if z == 0is { break ; \"unreachable\"; } z -= 1is; }\l if x > 10is { return; \"unreachable\"; }\l }\l}\l"];
+ N67 -> N1;
N68 -> N69;
N69 -> N70;
N70 -> N71;
N13 -> N14;
N14 -> N15;
N15 -> N16;
- N16 -> N12[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 0is { break ; \"unreachable\"; },\lexiting scope_4 stmt if x == 0is { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 0is { break ; \"unreachable\"; }\l x -= 1is;\l \'a:\l loop {\l if y == 0is { break ; \"unreachable\"; }\l y -= 1is;\l \'a: loop { if z == 0is { break ; \"unreachable\"; } z -= 1is; }\l if x > 10is { continue \'a ; \"unreachable\"; }\l }\l}\l"];
+ N16 -> N12;
N17 -> N18;
N18 -> N19;
N19 -> N20;
N30 -> N31;
N31 -> N32;
N32 -> N33;
- N33 -> N29[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y == 0is { break ; \"unreachable\"; },\lexiting scope_4 stmt if y == 0is { break ; \"unreachable\"; },\lexiting scope_5 block {\l if y == 0is { break ; \"unreachable\"; }\l y -= 1is;\l \'a: loop { if z == 0is { break ; \"unreachable\"; } z -= 1is; }\l if x > 10is { continue \'a ; \"unreachable\"; }\l}\l"];
+ N33 -> N29;
N34 -> N35;
N35 -> N36;
N36 -> N37;
N47 -> N48;
N48 -> N49;
N49 -> N50;
- N50 -> N46[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if z == 0is { break ; \"unreachable\"; },\lexiting scope_4 stmt if z == 0is { break ; \"unreachable\"; },\lexiting scope_5 block { if z == 0is { break ; \"unreachable\"; } z -= 1is; }"];
+ N50 -> N46;
N51 -> N52;
N52 -> N53;
N53 -> N54;
N64 -> N65;
N65 -> N66;
N66 -> N67;
- N67 -> N28[label="exiting scope_0 expr continue \'a,\lexiting scope_1 stmt continue \'a ;,\lexiting scope_2 block { continue \'a ; \"unreachable\"; },\lexiting scope_3 expr if x > 10is { continue \'a ; \"unreachable\"; },\lexiting scope_4 block {\l if y == 0is { break ; \"unreachable\"; }\l y -= 1is;\l \'a: loop { if z == 0is { break ; \"unreachable\"; } z -= 1is; }\l if x > 10is { continue \'a ; \"unreachable\"; }\l}\l"];
+ N67 -> N28;
N68 -> N69;
N69 -> N70;
N70 -> N71;
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
#[derive(PartialEq, Clone, Show)]
fn foo() -> AFakeTypeThatHadBetterGoAway {}
+#[into_multi_foo]
+#[derive(PartialEq, Clone, Show)]
+fn foo() -> AnotherFakeTypeThatHadBetterGoAway {}
+
+trait Qux {
+ #[into_multi_foo]
+ fn bar();
+}
+
+impl Qux for i32 {
+ #[into_multi_foo]
+ fn bar() {}
+}
+
+impl Qux for u8 {}
+
pub fn main() {
assert_eq!(1, make_a_1!());
assert_eq!(2, exported_macro!());
assert_eq!(Foo::Bar, Foo::Bar);
test(None::<Foo>);
+
+ assert_eq!(Foo2::Bar2, Foo2::Bar2);
+ test(None::<Foo2>);
+
+ let x = 10i32;
+ assert_eq!(x.foo(), 42);
+ let x = 10u8;
+ assert_eq!(x.foo(), 0);
}
fn test<T: PartialEq+Clone>(_: Option<T>) {}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for issue #21010: Normalize associated types in
+// various special paths in the `type_is_immediate` function.
+
+#![allow(unstable)]
+
+pub trait OffsetState: Sized {}
+pub trait Offset { type State: OffsetState; }
+
+#[derive(Copy)] pub struct X;
+impl Offset for X { type State = Y; }
+
+#[derive(Copy)] pub struct Y;
+impl OffsetState for Y {}
+
+pub fn now() -> DateTime<X> { from_utc(Y) }
+
+pub struct DateTime<Off: Offset> { pub offset: Off::State }
+pub fn from_utc<Off: Offset>(offset: Off::State) -> DateTime<Off> { DateTime { offset: offset } }
+
+pub fn main() {
+ let _x = now();
+}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Corrected regression test for #20831. The original did not compile.
+// When fixed, it revealed another problem concerning projections that
+// appear in associated type bindings in object types, which were not
+// being properly flagged.
+
+use std::ops::{Shl, Shr};
+use std::cell::RefCell;
+
+pub trait Subscriber {
+ type Input;
+}
+
+pub trait Publisher<'a> {
+ type Output;
+ fn subscribe(&mut self, Box<Subscriber<Input=Self::Output> + 'a>);
+}
+
+pub trait Processor<'a> : Subscriber + Publisher<'a> { }
+
+impl<'a, P> Processor<'a> for P where P : Subscriber + Publisher<'a> { }
+
+struct MyStruct<'a> {
+ sub: Box<Subscriber<Input=u64> + 'a>
+}
+
+impl<'a> Publisher<'a> for MyStruct<'a> {
+ type Output = u64;
+ fn subscribe(&mut self, t : Box<Subscriber<Input=u64> + 'a>) {
+ self.sub = t;
+ }
+}
+
+fn main() {}
#![feature(macro_rules)]
+use std::borrow::{Cow, IntoCow};
use std::collections::Bitv;
use std::default::Default;
use std::iter::FromIterator;
+use std::ops::Add;
use std::option::IntoIter as OptionIter;
use std::rand::Rand;
use std::rand::XorShiftRng as DummyRng;
fn odd(x: uint) -> bool { x % 2 == 1 }
fn dummy_rng() -> DummyRng { DummyRng::new_unseeded() }
+trait Size: Sized {
+ fn size() -> uint { std::mem::size_of::<Self>() }
+}
+impl<T> Size for T {}
+
macro_rules! tests {
($($expr:expr, $ty:ty, ($($test:expr),*);)+) => (pub fn main() {$({
const C: $ty = $expr;
// , (vec![b'f', b'o', b'o'], u8_as_i8);
// Trait static methods.
- // FIXME qualified path expressions aka UFCS i.e. <T as Trait>::method.
+ <bool as Size>::size, fn() -> uint, ();
Default::default, fn() -> int, ();
+ <int as Default>::default, fn() -> int, ();
Rand::rand, fn(&mut DummyRng) -> int, (&mut dummy_rng());
+ <int as Rand>::rand, fn(&mut DummyRng) -> int, (&mut dummy_rng());
Rand::rand::<DummyRng>, fn(&mut DummyRng) -> int, (&mut dummy_rng());
+ <int as Rand>::rand::<DummyRng>, fn(&mut DummyRng) -> int, (&mut dummy_rng());
// Trait non-static methods.
Clone::clone, fn(&int) -> int, (&5);
+ <int as Clone>::clone, fn(&int) -> int, (&5);
FromIterator::from_iter, fn(OptionIter<int>) -> Vec<int>, (Some(5).into_iter());
- FromIterator::from_iter::<OptionIter<int>>, fn(OptionIter<int>) -> Vec<int>
- , (Some(5).into_iter());
+ <Vec<_> as FromIterator<_>>::from_iter, fn(OptionIter<int>) -> Vec<int>,
+ (Some(5).into_iter());
+ <Vec<int> as FromIterator<_>>::from_iter, fn(OptionIter<int>) -> Vec<int>,
+ (Some(5).into_iter());
+ FromIterator::from_iter::<OptionIter<int>>, fn(OptionIter<int>) -> Vec<int>,
+ (Some(5).into_iter());
+ <Vec<int> as FromIterator<_>>::from_iter::<OptionIter<int>>, fn(OptionIter<int>) -> Vec<int>,
+ (Some(5).into_iter());
+ Add::add, fn(i32, i32) -> i32, (5, 6);
+ <i32 as Add<_>>::add, fn(i32, i32) -> i32, (5, 6);
+ <i32 as Add<i32>>::add, fn(i32, i32) -> i32, (5, 6);
+ <String as IntoCow<_, _>>::into_cow, fn(String) -> Cow<'static, String, str>,
+ ("foo".to_string());
+ <String as IntoCow<'static, _, _>>::into_cow, fn(String) -> Cow<'static, String, str>,
+ ("foo".to_string());
}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that overloaded calls work with zero arity closures
+
+#![feature(box_syntax)]
+
+fn main() {
+ let functions: [Box<Fn() -> Option<()>>; 1] = [box || None];
+
+ let _: Option<Vec<()>> = functions.iter().map(|f| (*f)()).collect();
+}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #20676. Error was that we didn't support
+// UFCS-style calls to a method in `Trait` where `Self` was bound to a
+// trait object of type `Trait`. See also `ufcs-trait-object.rs`.
+
+use std::fmt;
+
+fn main() {
+ let a: &fmt::Show = &1_i32;
+ format!("{:?}", a);
+}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct ctxt<'tcx> {
+ x: &'tcx i32
+}
+
+trait AstConv<'tcx> {
+ fn tcx<'a>(&'a self) -> &'a ctxt<'tcx>;
+}
+
+fn foo(conv: &AstConv) { }
+
+fn bar<'tcx>(conv: &AstConv<'tcx>) {
+ foo(conv)
+}
+
+fn main() { }
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that when you use ufcs form to invoke a trait method (on a
+// trait object) everything works fine.
+
+trait Foo {
+ fn test(&self) -> i32;
+}
+
+impl Foo for i32 {
+ fn test(&self) -> i32 { *self }
+}
+
+fn main() {
+ let a: &Foo = &22_i32;
+ assert_eq!(Foo::test(a), 22);
+}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Bound {}
+
+trait Trait {
+ fn a<T>(&self, T) where T: Bound;
+ fn b<T>(&self, T) where T: Bound;
+ fn c<T: Bound>(&self, T);
+ fn d<T: Bound>(&self, T);
+}
+
+impl Trait for bool {
+ fn a<T: Bound>(&self, _: T) {}
+ //^~ This gets rejected but should be accepted
+ fn b<T>(&self, _: T) where T: Bound {}
+ fn c<T: Bound>(&self, _: T) {}
+ fn d<T>(&self, _: T) where T: Bound {}
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+ let x = [(), ()];
+
+ // The subslice used to go out of bounds for zero-sized array items, check that this doesn't
+ // happen anymore
+ match x {
+ [_, y..] => assert_eq!(&x[1] as *const _, &y[0] as *const _)
+ }
+}