diff --git a/.gitmodules b/.gitmodules index 83be070b3854a..eb033f5401d3c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,7 +13,7 @@ url = https://github.com/rust-lang/jemalloc.git [submodule "src/rust-installer"] path = src/rust-installer - url = https://github.com/rust-lang/rust-installer + url = https://github.com/rust-lang/rust-installer.git [submodule "src/liblibc"] path = src/liblibc - url = https://github.com/rust-lang/libc + url = https://github.com/rust-lang-nursery/libc.git diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a11e9a7d68053..c3851dcc8f1ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -238,7 +238,7 @@ are: * Don't be afraid to ask! The Rust community is friendly and helpful. [gdfrustc]: http://manishearth.github.io/rust-internals-docs/rustc/ -[gsearchdocs]: https://www.google.de/search?q=site:doc.rust-lang.org+your+query+here +[gsearchdocs]: https://www.google.com/search?q=site:doc.rust-lang.org+your+query+here [rif]: http://internals.rust-lang.org [rr]: https://doc.rust-lang.org/book/README.html [tlgba]: http://tomlee.co/2014/04/03/a-more-detailed-tour-of-the-rust-compiler/ diff --git a/COPYRIGHT b/COPYRIGHT index 0eac7ac9de2c5..5ab70b7120fd9 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -6,7 +6,7 @@ terms. Longer version: -The Rust Project is copyright 2015, The Rust Project +The Rust Project is copyright 2016, The Rust Project Developers (given in the file AUTHORS.txt). Licensed under the Apache License, Version 2.0 diff --git a/LICENSE-MIT b/LICENSE-MIT index e69282e381bc0..40b8817a47beb 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2015 The Rust Project Developers +Copyright (c) 2016 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/README.md b/README.md index 6e5e1ca0c3282..7558065831ace 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,16 @@ Read ["Installing Rust"] from [The Book]. ### Building on Windows +There are two prominent ABIs in use on Windows: the native (MSVC) ABI used by +Visual Studio, and the GNU ABI used by the GCC toolchain. Which version of Rust +you need depends largely on what C/C++ libraries you want to interoperate with: +for interop with software produced by Visual Studio use the MSVC build of Rust; +for interop with GNU software built using the MinGW/MSYS2 toolchain use the GNU +build. + + +#### MinGW + [MSYS2](http://msys2.github.io/) can be used to easily build Rust on Windows: 1. Grab the latest MSYS2 installer and go through the installer. @@ -63,12 +73,15 @@ Read ["Installing Rust"] from [The Book]. ```sh # Update package mirrors (may be needed if you have a fresh install of MSYS2) $ pacman -Sy pacman-mirrors + ``` - # Choose one based on platform: - # *** see the note below *** - $ pacman -S mingw-w64-i686-toolchain - $ pacman -S mingw-w64-x86_64-toolchain +Download [MinGW from +here](http://mingw-w64.org/doku.php/download/mingw-builds), and choose the +`threads=win32,exceptions=dwarf/seh` flavor when installing. After installing, +add its `bin` directory to your `PATH`. This is due to #28260, in the future, +installing from pacman should be just fine. + ``` # Make git available in MSYS2 (if not already available on path) $ pacman -S git @@ -84,16 +97,19 @@ Read ["Installing Rust"] from [The Book]. $ ./configure $ make && make install ``` -> ***Note:*** gcc versions >= 5 currently have issues building LLVM on Windows -> resulting in a segmentation fault when building Rust. In order to avoid this -> it may be necessary to obtain an earlier version of gcc such as 4.9.x. -> Msys's `pacman` will install the latest version, so for the time being it is -> recommended to skip gcc toolchain installation step above and use [Mingw-Builds] -> project's installer instead. Be sure to add gcc `bin` directory to the path -> before running `configure`. -> For more information on this see issue #28260. - -[Mingw-Builds]: http://sourceforge.net/projects/mingw-w64/ + +#### MSVC + +MSVC builds of Rust additionally require an installation of Visual Studio 2013 +(or later) so `rustc` can use its linker. Make sure to check the “C++ tools” +option. In addition, `cmake` needs to be installed to build LLVM. + +With these dependencies installed, the build takes two steps: + +```sh +$ ./configure +$ make && make install +``` ## Building Documentation diff --git a/configure b/configure index c4944f5346350..df76d805f824b 100755 --- a/configure +++ b/configure @@ -521,15 +521,18 @@ then # if configure is running in an interactive bash shell. /usr/bin/env # exists *everywhere*. BIN_TO_PROBE="$SHELL" - if [ -z "$BIN_TO_PROBE" -a -e "/usr/bin/env" ]; then - BIN_TO_PROBE="/usr/bin/env" + if [ ! -r "$BIN_TO_PROBE" ]; then + if [ -r "/usr/bin/env" ]; then + BIN_TO_PROBE="/usr/bin/env" + else + warn "Cannot check if the userland is i686 or x86_64" + fi + fi + file -L "$BIN_TO_PROBE" | grep -q "x86[_-]64" + if [ $? != 0 ]; then + msg "i686 userland on x86_64 Linux kernel" + CFG_CPUTYPE=i686 fi - if [ -n "$BIN_TO_PROBE" ]; then - file -L "$BIN_TO_PROBE" | grep -q "x86[_-]64" - if [ $? != 0 ]; then - CFG_CPUTYPE=i686 - fi - fi fi diff --git a/mk/main.mk b/mk/main.mk index 2de18c9871a8d..4c4722a76b939 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -149,7 +149,7 @@ endif ifdef TRACE CFG_RUSTC_FLAGS += -Z trace endif -ifdef CFG_ENABLE_RPATH +ifndef CFG_DISABLE_RPATH CFG_RUSTC_FLAGS += -C rpath endif diff --git a/mk/platform.mk b/mk/platform.mk index 04cab379de3c7..5239086a6552c 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -64,14 +64,18 @@ define DEF_GOOD_VALGRIND ifeq ($(OSTYPE_$(1)),unknown-linux-gnu) GOOD_VALGRIND_$(1) = 1 endif - ifneq (,$(filter $(OSTYPE_$(1)),darwin freebsd)) - ifeq (HOST_$(1),x86_64) + ifneq (,$(filter $(OSTYPE_$(1)),apple-darwin freebsd)) + ifeq ($(HOST_$(1)),x86_64) GOOD_VALGRIND_$(1) = 1 endif endif + ifdef GOOD_VALGRIND_$(t) + $$(info cfg: have good valgrind for $(t)) + else + $$(info cfg: no good valgrind for $(t)) + endif endef $(foreach t,$(CFG_TARGET),$(eval $(call DEF_GOOD_VALGRIND,$(t)))) -$(foreach t,$(CFG_TARGET),$(info cfg: good valgrind for $(t) is $(GOOD_VALGRIND_$(t)))) ifneq ($(findstring linux,$(CFG_OSTYPE)),) ifdef CFG_PERF diff --git a/src/doc/book/README.md b/src/doc/book/README.md index 565f143a291fb..9f9b6a9b043dd 100644 --- a/src/doc/book/README.md +++ b/src/doc/book/README.md @@ -14,31 +14,25 @@ Even then, Rust still allows precise control like a low-level language would. [rust]: https://www.rust-lang.org -“The Rust Programming Language” is split into eight sections. This introduction +“The Rust Programming Language” is split into chapters. This introduction is the first. After this: * [Getting started][gs] - Set up your computer for Rust development. -* [Learn Rust][lr] - Learn Rust programming through small projects. -* [Effective Rust][er] - Higher-level concepts for writing excellent Rust code. +* [Tutorial: Guessing Game][gg] - Learn some Rust with a small project. * [Syntax and Semantics][ss] - Each bit of Rust, broken down into small chunks. +* [Effective Rust][er] - Higher-level concepts for writing excellent Rust code. * [Nightly Rust][nr] - Cutting-edge features that aren’t in stable builds yet. * [Glossary][gl] - A reference of terms used in the book. * [Bibliography][bi] - Background on Rust's influences, papers about Rust. [gs]: getting-started.html -[lr]: learn-rust.html +[gg]: guessing-game.html [er]: effective-rust.html [ss]: syntax-and-semantics.html [nr]: nightly-rust.html [gl]: glossary.html [bi]: bibliography.html -After reading this introduction, you’ll want to dive into either ‘Learn Rust’ or -‘Syntax and Semantics’, depending on your preference: ‘Learn Rust’ if you want -to dive in with a project, or ‘Syntax and Semantics’ if you prefer to start -small, and learn a single concept thoroughly before moving onto the next. -Copious cross-linking connects these parts together. - ### Contributing The source files from which this book is generated can be found on diff --git a/src/doc/book/SUMMARY.md b/src/doc/book/SUMMARY.md index 876a7399be9e5..3df791fd51bf6 100644 --- a/src/doc/book/SUMMARY.md +++ b/src/doc/book/SUMMARY.md @@ -1,10 +1,7 @@ # Summary * [Getting Started](getting-started.md) -* [Learn Rust](learn-rust.md) - * [Guessing Game](guessing-game.md) - * [Dining Philosophers](dining-philosophers.md) - * [Rust Inside Other Languages](rust-inside-other-languages.md) +* [Tutorial: Guessing Game](guessing-game.md) * [Syntax and Semantics](syntax-and-semantics.md) * [Variable Bindings](variable-bindings.md) * [Functions](functions.md) diff --git a/src/doc/book/choosing-your-guarantees.md b/src/doc/book/choosing-your-guarantees.md index d9e92de8d9abc..edf5e2ff73818 100644 --- a/src/doc/book/choosing-your-guarantees.md +++ b/src/doc/book/choosing-your-guarantees.md @@ -340,11 +340,11 @@ With the former, the `RefCell` is wrapping the `Vec`, so the `Vec` in i mutable. At the same time, there can only be one mutable borrow of the whole `Vec` at a given time. This means that your code cannot simultaneously work on different elements of the vector from different `Rc` handles. However, we are able to push and pop from the `Vec` at will. This is -similar to an `&mut Vec` with the borrow checking done at runtime. +similar to a `&mut Vec` with the borrow checking done at runtime. With the latter, the borrowing is of individual elements, but the overall vector is immutable. Thus, we can independently borrow separate elements, but we cannot push or pop from the vector. This is -similar to an `&mut [T]`[^3], but, again, the borrow checking is at runtime. +similar to a `&mut [T]`[^3], but, again, the borrow checking is at runtime. In concurrent programs, we have a similar situation with `Arc>`, which provides shared mutability and ownership. diff --git a/src/doc/book/closures.md b/src/doc/book/closures.md index 7d4452a4c8470..24993477ede25 100644 --- a/src/doc/book/closures.md +++ b/src/doc/book/closures.md @@ -208,7 +208,7 @@ different. Rust’s implementation of closures is a bit different than other languages. They are effectively syntax sugar for traits. You’ll want to make sure to have read -the [traits chapter][traits] before this one, as well as the chapter on [trait +the [traits][traits] section before this one, as well as the section on [trait objects][trait-objects]. [traits]: traits.html diff --git a/src/doc/book/dining-philosophers.md b/src/doc/book/dining-philosophers.md deleted file mode 100644 index a0f629c32e3fb..0000000000000 --- a/src/doc/book/dining-philosophers.md +++ /dev/null @@ -1,723 +0,0 @@ -% Dining Philosophers - -For our second project, let’s look at a classic concurrency problem. It’s -called ‘the dining philosophers’. It was originally conceived by Dijkstra in -1965, but we’ll use a lightly adapted version from [this paper][paper] by Tony -Hoare in 1985. - -[paper]: http://www.usingcsp.com/cspbook.pdf - -> In ancient times, a wealthy philanthropist endowed a College to accommodate -> five eminent philosophers. Each philosopher had a room in which they could -> engage in their professional activity of thinking; there was also a common -> dining room, furnished with a circular table, surrounded by five chairs, each -> labelled by the name of the philosopher who was to sit in it. They sat -> anticlockwise around the table. To the left of each philosopher there was -> laid a golden fork, and in the center stood a large bowl of spaghetti, which -> was constantly replenished. A philosopher was expected to spend most of -> their time thinking; but when they felt hungry, they went to the dining -> room, sat down in their own chair, picked up their own fork on their left, -> and plunged it into the spaghetti. But such is the tangled nature of -> spaghetti that a second fork is required to carry it to the mouth. The -> philosopher therefore had also to pick up the fork on their right. When -> they were finished they would put down both their forks, get up from their -> chair, and continue thinking. Of course, a fork can be used by only one -> philosopher at a time. If the other philosopher wants it, they just have -> to wait until the fork is available again. - -This classic problem shows off a few different elements of concurrency. The -reason is that it's actually slightly tricky to implement: a simple -implementation can deadlock. For example, let's consider a simple algorithm -that would solve this problem: - -1. A philosopher picks up the fork on their left. -2. They then pick up the fork on their right. -3. They eat. -4. They return the forks. - -Now, let’s imagine this sequence of events: - -1. Philosopher 1 begins the algorithm, picking up the fork on their left. -2. Philosopher 2 begins the algorithm, picking up the fork on their left. -3. Philosopher 3 begins the algorithm, picking up the fork on their left. -4. Philosopher 4 begins the algorithm, picking up the fork on their left. -5. Philosopher 5 begins the algorithm, picking up the fork on their left. -6. ... ? All the forks are taken, but nobody can eat! - -There are different ways to solve this problem. We’ll get to our solution in -the tutorial itself. For now, let’s get started and create a new project with -`cargo`: - -```bash -$ cd ~/projects -$ cargo new dining_philosophers --bin -$ cd dining_philosophers -``` - -Now we can start modeling the problem itself. We’ll start with the philosophers -in `src/main.rs`: - -```rust -struct Philosopher { - name: String, -} - -impl Philosopher { - fn new(name: &str) -> Philosopher { - Philosopher { - name: name.to_string(), - } - } -} - -fn main() { - let p1 = Philosopher::new("Judith Butler"); - let p2 = Philosopher::new("Gilles Deleuze"); - let p3 = Philosopher::new("Karl Marx"); - let p4 = Philosopher::new("Emma Goldman"); - let p5 = Philosopher::new("Michel Foucault"); -} -``` - -Here, we make a [`struct`][struct] to represent a philosopher. For now, -a name is all we need. We choose the [`String`][string] type for the name, -rather than `&str`. Generally speaking, working with a type which owns its -data is easier than working with one that uses references. - -[struct]: structs.html -[string]: strings.html - -Let’s continue: - -```rust -# struct Philosopher { -# name: String, -# } -impl Philosopher { - fn new(name: &str) -> Philosopher { - Philosopher { - name: name.to_string(), - } - } -} -``` - -This `impl` block lets us define things on `Philosopher` structs. In this case, -we define an ‘associated function’ called `new`. The first line looks like this: - -```rust -# struct Philosopher { -# name: String, -# } -# impl Philosopher { -fn new(name: &str) -> Philosopher { -# Philosopher { -# name: name.to_string(), -# } -# } -# } -``` - -We take one argument, a `name`, of type `&str`. This is a reference to another -string. It returns an instance of our `Philosopher` struct. - -```rust -# struct Philosopher { -# name: String, -# } -# impl Philosopher { -# fn new(name: &str) -> Philosopher { -Philosopher { - name: name.to_string(), -} -# } -# } -``` - -This creates a new `Philosopher`, and sets its `name` to our `name` argument. -Not just the argument itself, though, as we call `.to_string()` on it. This -will create a copy of the string that our `&str` points to, and give us a new -`String`, which is the type of the `name` field of `Philosopher`. - -Why not accept a `String` directly? It’s nicer to call. If we took a `String`, -but our caller had a `&str`, they’d have to call this method themselves. The -downside of this flexibility is that we _always_ make a copy. For this small -program, that’s not particularly important, as we know we’ll just be using -short strings anyway. - -One last thing you’ll notice: we just define a `Philosopher`, and seemingly -don’t do anything with it. Rust is an ‘expression based’ language, which means -that almost everything in Rust is an expression which returns a value. This is -true of functions as well — the last expression is automatically returned. Since -we create a new `Philosopher` as the last expression of this function, we end -up returning it. - -This name, `new()`, isn’t anything special to Rust, but it is a convention for -functions that create new instances of structs. Before we talk about why, let’s -look at `main()` again: - -```rust -# struct Philosopher { -# name: String, -# } -# -# impl Philosopher { -# fn new(name: &str) -> Philosopher { -# Philosopher { -# name: name.to_string(), -# } -# } -# } -# -fn main() { - let p1 = Philosopher::new("Judith Butler"); - let p2 = Philosopher::new("Gilles Deleuze"); - let p3 = Philosopher::new("Karl Marx"); - let p4 = Philosopher::new("Emma Goldman"); - let p5 = Philosopher::new("Michel Foucault"); -} -``` - -Here, we create five variable bindings with five new philosophers. -If we _didn’t_ define -that `new()` function, it would look like this: - -```rust -# struct Philosopher { -# name: String, -# } -fn main() { - let p1 = Philosopher { name: "Judith Butler".to_string() }; - let p2 = Philosopher { name: "Gilles Deleuze".to_string() }; - let p3 = Philosopher { name: "Karl Marx".to_string() }; - let p4 = Philosopher { name: "Emma Goldman".to_string() }; - let p5 = Philosopher { name: "Michel Foucault".to_string() }; -} -``` - -That’s much noisier. Using `new` has other advantages too, but even in -this simple case, it ends up being nicer to use. - -Now that we’ve got the basics in place, there’s a number of ways that we can -tackle the broader problem here. I like to start from the end first: let’s -set up a way for each philosopher to finish eating. As a tiny step, let’s make -a method, and then loop through all the philosophers, calling it: - -```rust -struct Philosopher { - name: String, -} - -impl Philosopher { - fn new(name: &str) -> Philosopher { - Philosopher { - name: name.to_string(), - } - } - - fn eat(&self) { - println!("{} is done eating.", self.name); - } -} - -fn main() { - let philosophers = vec![ - Philosopher::new("Judith Butler"), - Philosopher::new("Gilles Deleuze"), - Philosopher::new("Karl Marx"), - Philosopher::new("Emma Goldman"), - Philosopher::new("Michel Foucault"), - ]; - - for p in &philosophers { - p.eat(); - } -} -``` - -Let’s look at `main()` first. Rather than have five individual variable -bindings for our philosophers, we make a `Vec` of them instead. `Vec` is -also called a ‘vector’, and it’s a growable array type. We then use a -[`for`][for] loop to iterate through the vector, getting a reference to each -philosopher in turn. - -[for]: loops.html#for - -In the body of the loop, we call `p.eat()`, which is defined above: - -```rust,ignore -fn eat(&self) { - println!("{} is done eating.", self.name); -} -``` - -In Rust, methods take an explicit `self` parameter. That’s why `eat()` is a -method, but `new` is an associated function: `new()` has no `self`. For our -first version of `eat()`, we just print out the name of the philosopher, and -mention they’re done eating. Running this program should give you the following -output: - -```text -Judith Butler is done eating. -Gilles Deleuze is done eating. -Karl Marx is done eating. -Emma Goldman is done eating. -Michel Foucault is done eating. -``` - -Easy enough, they’re all done! We haven’t actually implemented the real problem -yet, though, so we’re not done yet! - -Next, we want to make our philosophers not just finish eating, but actually -eat. Here’s the next version: - -```rust -use std::thread; -use std::time::Duration; - -struct Philosopher { - name: String, -} - -impl Philosopher { - fn new(name: &str) -> Philosopher { - Philosopher { - name: name.to_string(), - } - } - - fn eat(&self) { - println!("{} is eating.", self.name); - - thread::sleep(Duration::from_millis(1000)); - - println!("{} is done eating.", self.name); - } -} - -fn main() { - let philosophers = vec![ - Philosopher::new("Judith Butler"), - Philosopher::new("Gilles Deleuze"), - Philosopher::new("Karl Marx"), - Philosopher::new("Emma Goldman"), - Philosopher::new("Michel Foucault"), - ]; - - for p in &philosophers { - p.eat(); - } -} -``` - -Just a few changes. Let’s break it down. - -```rust,ignore -use std::thread; -``` - -`use` brings names into scope. We’re going to start using the `thread` module -from the standard library, and so we need to `use` it. - -```rust,ignore - fn eat(&self) { - println!("{} is eating.", self.name); - - thread::sleep(Duration::from_millis(1000)); - - println!("{} is done eating.", self.name); - } -``` - -We now print out two messages, with a `sleep` in the middle. This will -simulate the time it takes a philosopher to eat. - -If you run this program, you should see each philosopher eat in turn: - -```text -Judith Butler is eating. -Judith Butler is done eating. -Gilles Deleuze is eating. -Gilles Deleuze is done eating. -Karl Marx is eating. -Karl Marx is done eating. -Emma Goldman is eating. -Emma Goldman is done eating. -Michel Foucault is eating. -Michel Foucault is done eating. -``` - -Excellent! We’re getting there. There’s just one problem: we aren’t actually -operating in a concurrent fashion, which is a core part of the problem! - -To make our philosophers eat concurrently, we need to make a small change. -Here’s the next iteration: - -```rust -use std::thread; -use std::time::Duration; - -struct Philosopher { - name: String, -} - -impl Philosopher { - fn new(name: &str) -> Philosopher { - Philosopher { - name: name.to_string(), - } - } - - fn eat(&self) { - println!("{} is eating.", self.name); - - thread::sleep(Duration::from_millis(1000)); - - println!("{} is done eating.", self.name); - } -} - -fn main() { - let philosophers = vec![ - Philosopher::new("Judith Butler"), - Philosopher::new("Gilles Deleuze"), - Philosopher::new("Karl Marx"), - Philosopher::new("Emma Goldman"), - Philosopher::new("Michel Foucault"), - ]; - - let handles: Vec<_> = philosophers.into_iter().map(|p| { - thread::spawn(move || { - p.eat(); - }) - }).collect(); - - for h in handles { - h.join().unwrap(); - } -} -``` - -All we’ve done is change the loop in `main()`, and added a second one! Here’s the -first change: - -```rust,ignore -let handles: Vec<_> = philosophers.into_iter().map(|p| { - thread::spawn(move || { - p.eat(); - }) -}).collect(); -``` - -While this is only five lines, they’re a dense five. Let’s break it down. - -```rust,ignore -let handles: Vec<_> = -``` - -We introduce a new binding, called `handles`. We’ve given it this name because -we are going to make some new threads, and that will return some handles to those -threads that let us control their operation. We need to explicitly annotate -the type here, though, due to an issue we’ll talk about later. The `_` is -a type placeholder. We’re saying “`handles` is a vector of something, but you -can figure out what that something is, Rust.” - -```rust,ignore -philosophers.into_iter().map(|p| { -``` - -We take our list of philosophers and call `into_iter()` on it. This creates an -iterator that takes ownership of each philosopher. We need to do this to pass -them to our threads. We take that iterator and call `map` on it, which takes a -closure as an argument and calls that closure on each element in turn. - -```rust,ignore - thread::spawn(move || { - p.eat(); - }) -``` - -Here’s where the concurrency happens. The `thread::spawn` function takes a closure -as an argument and executes that closure in a new thread. This closure needs -an extra annotation, `move`, to indicate that the closure is going to take -ownership of the values it’s capturing. In this case, it's the `p` variable of the -`map` function. - -Inside the thread, all we do is call `eat()` on `p`. Also note that -the call to `thread::spawn` lacks a trailing semicolon, making this an -expression. This distinction is important, yielding the correct return -value. For more details, read [Expressions vs. Statements][es]. - -[es]: functions.html#expressions-vs-statements - -```rust,ignore -}).collect(); -``` - -Finally, we take the result of all those `map` calls and collect them up. -`collect()` will make them into a collection of some kind, which is why we -needed to annotate the return type: we want a `Vec`. The elements are the -return values of the `thread::spawn` calls, which are handles to those threads. -Whew! - -```rust,ignore -for h in handles { - h.join().unwrap(); -} -``` - -At the end of `main()`, we loop through the handles and call `join()` on them, -which blocks execution until the thread has completed execution. This ensures -that the threads complete their work before the program exits. - -If you run this program, you’ll see that the philosophers eat out of order! -We have multi-threading! - -```text -Judith Butler is eating. -Gilles Deleuze is eating. -Karl Marx is eating. -Emma Goldman is eating. -Michel Foucault is eating. -Judith Butler is done eating. -Gilles Deleuze is done eating. -Karl Marx is done eating. -Emma Goldman is done eating. -Michel Foucault is done eating. -``` - -But what about the forks? We haven’t modeled them at all yet. - -To do that, let’s make a new `struct`: - -```rust -use std::sync::Mutex; - -struct Table { - forks: Vec>, -} -``` - -This `Table` has a vector of `Mutex`es. A mutex is a way to control -concurrency: only one thread can access the contents at once. This is exactly -the property we need with our forks. We use an empty tuple, `()`, inside the -mutex, since we’re not actually going to use the value, just hold onto it. - -Let’s modify the program to use the `Table`: - -```rust -use std::thread; -use std::time::Duration; -use std::sync::{Mutex, Arc}; - -struct Philosopher { - name: String, - left: usize, - right: usize, -} - -impl Philosopher { - fn new(name: &str, left: usize, right: usize) -> Philosopher { - Philosopher { - name: name.to_string(), - left: left, - right: right, - } - } - - fn eat(&self, table: &Table) { - let _left = table.forks[self.left].lock().unwrap(); - thread::sleep(Duration::from_millis(150)); - let _right = table.forks[self.right].lock().unwrap(); - - println!("{} is eating.", self.name); - - thread::sleep(Duration::from_millis(1000)); - - println!("{} is done eating.", self.name); - } -} - -struct Table { - forks: Vec>, -} - -fn main() { - let table = Arc::new(Table { forks: vec![ - Mutex::new(()), - Mutex::new(()), - Mutex::new(()), - Mutex::new(()), - Mutex::new(()), - ]}); - - let philosophers = vec![ - Philosopher::new("Judith Butler", 0, 1), - Philosopher::new("Gilles Deleuze", 1, 2), - Philosopher::new("Karl Marx", 2, 3), - Philosopher::new("Emma Goldman", 3, 4), - Philosopher::new("Michel Foucault", 0, 4), - ]; - - let handles: Vec<_> = philosophers.into_iter().map(|p| { - let table = table.clone(); - - thread::spawn(move || { - p.eat(&table); - }) - }).collect(); - - for h in handles { - h.join().unwrap(); - } -} -``` - -Lots of changes! However, with this iteration, we’ve got a working program. -Let’s go over the details: - -```rust,ignore -use std::sync::{Mutex, Arc}; -``` - -We’re going to use another structure from the `std::sync` package: `Arc`. -We’ll talk more about it when we use it. - -```rust,ignore -struct Philosopher { - name: String, - left: usize, - right: usize, -} -``` - -We need to add two more fields to our `Philosopher`. Each philosopher is going -to have two forks: the one on their left, and the one on their right. -We’ll use the `usize` type to indicate them, as it’s the type that you index -vectors with. These two values will be the indexes into the `forks` our `Table` -has. - -```rust,ignore -fn new(name: &str, left: usize, right: usize) -> Philosopher { - Philosopher { - name: name.to_string(), - left: left, - right: right, - } -} -``` - -We now need to construct those `left` and `right` values, so we add them to -`new()`. - -```rust,ignore -fn eat(&self, table: &Table) { - let _left = table.forks[self.left].lock().unwrap(); - thread::sleep(Duration::from_millis(150)); - let _right = table.forks[self.right].lock().unwrap(); - - println!("{} is eating.", self.name); - - thread::sleep(Duration::from_millis(1000)); - - println!("{} is done eating.", self.name); -} -``` - -We have three new lines. We’ve added an argument, `table`. We access the -`Table`’s list of forks, and then use `self.left` and `self.right` to access -the fork at that particular index. That gives us access to the `Mutex` at that -index, and we call `lock()` on it. If the mutex is currently being accessed by -someone else, we’ll block until it becomes available. We have also a call to -`thread::sleep` between the moment the first fork is picked and the moment the -second forked is picked, as the process of picking up the fork is not -immediate. - -The call to `lock()` might fail, and if it does, we want to crash. In this -case, the error that could happen is that the mutex is [‘poisoned’][poison], -which is what happens when the thread panics while the lock is held. Since this -shouldn’t happen, we just use `unwrap()`. - -[poison]: ../std/sync/struct.Mutex.html#poisoning - -One other odd thing about these lines: we’ve named the results `_left` and -`_right`. What’s up with that underscore? Well, we aren’t planning on -_using_ the value inside the lock. We just want to acquire it. As such, -Rust will warn us that we never use the value. By using the underscore, -we tell Rust that this is what we intended, and it won’t throw a warning. - -What about releasing the lock? Well, that will happen when `_left` and -`_right` go out of scope, automatically. - -```rust,ignore - let table = Arc::new(Table { forks: vec![ - Mutex::new(()), - Mutex::new(()), - Mutex::new(()), - Mutex::new(()), - Mutex::new(()), - ]}); -``` - -Next, in `main()`, we make a new `Table` and wrap it in an `Arc`. -‘arc’ stands for ‘atomic reference count’, and we need that to share -our `Table` across multiple threads. As we share it, the reference -count will go up, and when each thread ends, it will go back down. - - -```rust,ignore -let philosophers = vec![ - Philosopher::new("Judith Butler", 0, 1), - Philosopher::new("Gilles Deleuze", 1, 2), - Philosopher::new("Karl Marx", 2, 3), - Philosopher::new("Emma Goldman", 3, 4), - Philosopher::new("Michel Foucault", 0, 4), -]; -``` - -We need to pass in our `left` and `right` values to the constructors for our -`Philosopher`s. But there’s one more detail here, and it’s _very_ important. If -you look at the pattern, it’s all consistent until the very end. Monsieur -Foucault should have `4, 0` as arguments, but instead, has `0, 4`. This is what -prevents deadlock, actually: one of our philosophers is left handed! This is -one way to solve the problem, and in my opinion, it’s the simplest. If you -change the order of the parameters, you will be able to observe the deadlock -taking place. - -```rust,ignore -let handles: Vec<_> = philosophers.into_iter().map(|p| { - let table = table.clone(); - - thread::spawn(move || { - p.eat(&table); - }) -}).collect(); -``` - -Finally, inside of our `map()`/`collect()` loop, we call `table.clone()`. The -`clone()` method on `Arc` is what bumps up the reference count, and when it -goes out of scope, it decrements the count. This is needed so that we know how -many references to `table` exist across our threads. If we didn’t have a count, -we wouldn’t know how to deallocate it. - -You’ll notice we can introduce a new binding to `table` here, and it will -shadow the old one. This is often used so that you don’t need to come up with -two unique names. - -With this, our program works! Only two philosophers can eat at any one time, -and so you’ll get some output like this: - -```text -Gilles Deleuze is eating. -Emma Goldman is eating. -Emma Goldman is done eating. -Gilles Deleuze is done eating. -Judith Butler is eating. -Karl Marx is eating. -Judith Butler is done eating. -Michel Foucault is eating. -Karl Marx is done eating. -Michel Foucault is done eating. -``` - -Congrats! You’ve implemented a classic concurrency problem in Rust. diff --git a/src/doc/book/documentation.md b/src/doc/book/documentation.md index 0d0fd8cf1d0a6..86c07f9cf6c18 100644 --- a/src/doc/book/documentation.md +++ b/src/doc/book/documentation.md @@ -73,7 +73,7 @@ hello.rs:4 } ``` This [unfortunate error](https://github.com/rust-lang/rust/issues/22547) is -correct: documentation comments apply to the thing after them, and there's +correct; documentation comments apply to the thing after them, and there's nothing after that last comment. [rc-new]: https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new @@ -385,7 +385,7 @@ error handling. Lets say you want the following, ```rust,ignore /// use std::io; -/// let mut input = String::new(); +/// let mut input = String::new(); /// try!(io::stdin().read_line(&mut input)); ``` @@ -398,7 +398,7 @@ don't return anything so this will give a mismatched types error. /// ``` /// use std::io; /// # fn foo() -> io::Result<()> { -/// let mut input = String::new(); +/// let mut input = String::new(); /// try!(io::stdin().read_line(&mut input)); /// # Ok(()) /// # } diff --git a/src/doc/book/effective-rust.md b/src/doc/book/effective-rust.md index 582ed3b7e65c5..65873c80e55e4 100644 --- a/src/doc/book/effective-rust.md +++ b/src/doc/book/effective-rust.md @@ -3,6 +3,6 @@ So you’ve learned how to write some Rust code. But there’s a difference between writing *any* Rust code and writing *good* Rust code. -This section consists of relatively independent tutorials which show you how to +This chapter consists of relatively independent tutorials which show you how to take your Rust to the next level. Common patterns and standard library features will be introduced. Read these sections in any order of your choosing. diff --git a/src/doc/book/error-handling.md b/src/doc/book/error-handling.md index 57389bdc493cb..fb720ce9b2983 100644 --- a/src/doc/book/error-handling.md +++ b/src/doc/book/error-handling.md @@ -5,18 +5,18 @@ errors in a particular way. Generally speaking, error handling is divided into two broad categories: exceptions and return values. Rust opts for return values. -In this chapter, we intend to provide a comprehensive treatment of how to deal +In this section, we intend to provide a comprehensive treatment of how to deal with errors in Rust. More than that, we will attempt to introduce error handling one piece at a time so that you'll come away with a solid working knowledge of how everything fits together. When done naïvely, error handling in Rust can be verbose and annoying. This -chapter will explore those stumbling blocks and demonstrate how to use the +section will explore those stumbling blocks and demonstrate how to use the standard library to make error handling concise and ergonomic. # Table of Contents -This chapter is very long, mostly because we start at the very beginning with +This section is very long, mostly because we start at the very beginning with sum types and combinators, and try to motivate the way Rust does error handling incrementally. As such, programmers with experience in other expressive type systems may want to jump around. @@ -182,7 +182,7 @@ analysis is the only way to get at the value stored inside an `Option`. This means that you, as the programmer, must handle the case when an `Option` is `None` instead of `Some(t)`. -But wait, what about `unwrap`,which we used [`previously`](#code-unwrap-double)? +But wait, what about `unwrap`, which we used [previously](#code-unwrap-double)? There was no case analysis there! Instead, the case analysis was put inside the `unwrap` method for you. You could define it yourself if you want: @@ -636,7 +636,7 @@ Thus far, we've looked at error handling where everything was either an `Option` and a `Result`? Or what if you have a `Result` and a `Result`? Handling *composition of distinct error types* is the next challenge in front of us, and it will be the major theme throughout the rest of -this chapter. +this section. ## Composing `Option` and `Result` @@ -648,7 +648,7 @@ Of course, in real code, things aren't always as clean. Sometimes you have a mix of `Option` and `Result` types. Must we resort to explicit case analysis, or can we continue using combinators? -For now, let's revisit one of the first examples in this chapter: +For now, let's revisit one of the first examples in this section: ```rust,should_panic use std::env; @@ -887,7 +887,7 @@ fn main() { } ``` -Reasonable people can disagree over whether this code is better that the code +Reasonable people can disagree over whether this code is better than the code that uses combinators, but if you aren't familiar with the combinator approach, this code looks simpler to read to me. It uses explicit case analysis with `match` and `if let`. If an error occurs, it simply stops executing the @@ -1319,7 +1319,7 @@ and [`cause`](../std/error/trait.Error.html#method.cause), but the limitation remains: `Box` is opaque. (N.B. This isn't entirely true because Rust does have runtime reflection, which is useful in some scenarios that are [beyond the scope of this -chapter](https://crates.io/crates/error).) +section](https://crates.io/crates/error).) It's time to revisit our custom `CliError` type and tie everything together. @@ -1486,7 +1486,7 @@ and [`fmt::Result`](../std/fmt/type.Result.html). # Case study: A program to read population data -This chapter was long, and depending on your background, it might be +This section was long, and depending on your background, it might be rather dense. While there is plenty of example code to go along with the prose, most of it was specifically designed to be pedagogical. So, we're going to do something new: a case study. @@ -1512,7 +1512,7 @@ and [`rustc-serialize`](https://crates.io/crates/rustc-serialize) crates. We're not going to spend a lot of time on setting up a project with Cargo because it is already covered well in [the Cargo -chapter](../book/hello-cargo.html) and [Cargo's documentation][14]. +section](../book/hello-cargo.html) and [Cargo's documentation][14]. To get started from scratch, run `cargo new --bin city-pop` and make sure your `Cargo.toml` looks something like this: @@ -1573,11 +1573,11 @@ fn main() { let matches = match opts.parse(&args[1..]) { Ok(m) => { m } - Err(e) => { panic!(e.to_string()) } + Err(e) => { panic!(e.to_string()) } }; if matches.opt_present("h") { print_usage(&program, opts); - return; + return; } let data_path = args[1].clone(); let city = args[2].clone(); @@ -1613,6 +1613,9 @@ CSV data given to us and print out a field in matching rows. Let's do it. (Make sure to add `extern crate csv;` to the top of your file.) ```rust,ignore +use std::fs::File; +use std::path::Path; + // This struct represents the data in each row of the CSV file. // Type based decoding absolves us of a lot of the nitty gritty error // handling, like parsing strings as integers or floats. @@ -1656,7 +1659,7 @@ fn main() { let data_path = Path::new(&data_file); let city = args[2].clone(); - let file = fs::File::open(data_path).unwrap(); + let file = File::open(data_path).unwrap(); let mut rdr = csv::Reader::from_reader(file); for row in rdr.decode::() { @@ -1674,7 +1677,7 @@ fn main() { Let's outline the errors. We can start with the obvious: the three places that `unwrap` is called: -1. [`fs::File::open`](../std/fs/struct.File.html#method.open) +1. [`File::open`](../std/fs/struct.File.html#method.open) can return an [`io::Error`](../std/io/struct.Error.html). 2. [`csv::Reader::decode`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.decode) @@ -1734,7 +1737,7 @@ fn print_usage(program: &str, opts: Options) { fn search>(file_path: P, city: &str) -> Vec { let mut found = vec![]; - let file = fs::File::open(file_path).unwrap(); + let file = File::open(file_path).unwrap(); let mut rdr = csv::Reader::from_reader(file); for row in rdr.decode::() { let row = row.unwrap(); @@ -1796,7 +1799,7 @@ fn search> (file_path: P, city: &str) -> Result, Box> { let mut found = vec![]; - let file = try!(fs::File::open(file_path)); + let file = try!(File::open(file_path)); let mut rdr = csv::Reader::from_reader(file); for row in rdr.decode::() { let row = try!(row); @@ -1930,7 +1933,7 @@ fn search> let mut found = vec![]; let input: Box = match *file_path { None => Box::new(io::stdin()), - Some(ref file_path) => Box::new(try!(fs::File::open(file_path))), + Some(ref file_path) => Box::new(try!(File::open(file_path))), }; let mut rdr = csv::Reader::from_reader(input); // The rest remains unchanged! @@ -2017,7 +2020,7 @@ fn search> let mut found = vec![]; let input: Box = match *file_path { None => Box::new(io::stdin()), - Some(ref file_path) => Box::new(try!(fs::File::open(file_path))), + Some(ref file_path) => Box::new(try!(File::open(file_path))), }; let mut rdr = csv::Reader::from_reader(input); for row in rdr.decode::() { @@ -2105,7 +2108,7 @@ handling. # The Short Story -Since this chapter is long, it is useful to have a quick summary for error +Since this section is long, it is useful to have a quick summary for error handling in Rust. These are some good “rules of thumb." They are emphatically *not* commandments. There are probably good reasons to break every one of these heuristics! diff --git a/src/doc/book/getting-started.md b/src/doc/book/getting-started.md index 17fc5d9233d19..a4c028e85b0d7 100644 --- a/src/doc/book/getting-started.md +++ b/src/doc/book/getting-started.md @@ -1,13 +1,13 @@ % Getting Started -This first section of the book will get us going with Rust and its tooling. +This first chapter of the book will get us going with Rust and its tooling. First, we’ll install Rust. Then, the classic ‘Hello World’ program. Finally, we’ll talk about Cargo, Rust’s build system and package manager. # Installing Rust The first step to using Rust is to install it. Generally speaking, you’ll need -an Internet connection to run the commands in this chapter, as we’ll be +an Internet connection to run the commands in this section, as we’ll be downloading Rust from the internet. We’ll be showing off a number of commands using a terminal, and those lines all diff --git a/src/doc/book/guessing-game.md b/src/doc/book/guessing-game.md index 8cb00f26ba526..6665d1f87d818 100644 --- a/src/doc/book/guessing-game.md +++ b/src/doc/book/guessing-game.md @@ -1,10 +1,14 @@ % Guessing Game -For our first project, we’ll implement a classic beginner programming problem: -the guessing game. Here’s how it works: Our program will generate a random -integer between one and a hundred. It will then prompt us to enter a guess. -Upon entering our guess, it will tell us if we’re too low or too high. Once we -guess correctly, it will congratulate us. Sounds good? +Let’s learn some Rust! For our first project, we’ll implement a classic +beginner programming problem: the guessing game. Here’s how it works: Our +program will generate a random integer between one and a hundred. It will then +prompt us to enter a guess. Upon entering our guess, it will tell us if we’re +too low or too high. Once we guess correctly, it will congratulate us. Sounds +good? + +Along the way, we’ll learn a little bit about Rust. The next chapter, ‘Syntax +and Semantics’, will dive deeper into each part. # Set up diff --git a/src/doc/book/learn-rust.md b/src/doc/book/learn-rust.md index 1a02bc95e9d9c..7be7fa4f039a7 100644 --- a/src/doc/book/learn-rust.md +++ b/src/doc/book/learn-rust.md @@ -1,6 +1,6 @@ % Learn Rust -Welcome! This section has a few tutorials that teach you Rust through building +Welcome! This chapter has a few tutorials that teach you Rust through building projects. You’ll get a high-level overview, but we’ll skim over the details. If you’d prefer a more ‘from the ground up’-style experience, check diff --git a/src/doc/book/lifetimes.md b/src/doc/book/lifetimes.md index 68bbd0c98993d..2d418786e9a1e 100644 --- a/src/doc/book/lifetimes.md +++ b/src/doc/book/lifetimes.md @@ -103,7 +103,7 @@ Then in our parameter list, we use the lifetimes we’ve named: ...(x: &'a i32) ``` -If we wanted an `&mut` reference, we’d do this: +If we wanted a `&mut` reference, we’d do this: ```rust,ignore ...(x: &'a mut i32) diff --git a/src/doc/book/macros.md b/src/doc/book/macros.md index e4a900a2b1a4f..7c8b74bd6495d 100644 --- a/src/doc/book/macros.md +++ b/src/doc/book/macros.md @@ -611,8 +611,7 @@ to define a single macro that works both inside and outside our library. The function name will expand to either `::increment` or `::mylib::increment`. To keep this system simple and correct, `#[macro_use] extern crate ...` may -only appear at the root of your crate, not inside `mod`. This ensures that -`$crate` is a single identifier. +only appear at the root of your crate, not inside `mod`. # The deep end diff --git a/src/doc/book/match.md b/src/doc/book/match.md index 113e218883b34..acffaf4544b10 100644 --- a/src/doc/book/match.md +++ b/src/doc/book/match.md @@ -23,26 +23,24 @@ match x { `match` takes an expression and then branches based on its value. Each ‘arm’ of the branch is of the form `val => expression`. When the value matches, that arm’s expression will be evaluated. It’s called `match` because of the term ‘pattern -matching’, which `match` is an implementation of. There’s an [entire section on +matching’, which `match` is an implementation of. There’s a [separate section on patterns][patterns] that covers all the patterns that are possible here. [patterns]: patterns.html -So what’s the big advantage? Well, there are a few. First of all, `match` -enforces ‘exhaustiveness checking’. Do you see that last arm, the one with the -underscore (`_`)? If we remove that arm, Rust will give us an error: +One of the many advantages of `match` is it enforces ‘exhaustiveness checking’. +For example if we remove the last arm with the underscore `_`, the compiler will +give us an error: ```text error: non-exhaustive patterns: `_` not covered ``` -In other words, Rust is trying to tell us we forgot a value. Because `x` is an -integer, Rust knows that it can have a number of different values – for -example, `6`. Without the `_`, however, there is no arm that could match, and -so Rust refuses to compile the code. `_` acts like a ‘catch-all arm’. If none -of the other arms match, the arm with `_` will, and since we have this -catch-all arm, we now have an arm for every possible value of `x`, and so our -program will compile successfully. +Rust is telling us that we forgot a value. The compiler infers from `x` that it +can have any positive 32bit value; for example 1 to 2,147,483,647. The `_` acts +as a 'catch-all', and will catch all possible values that *aren't* specified in +an arm of `match`. As you can see with the previous example, we provide `match` +arms for integers 1-5, if `x` is 6 or any other value, then it is caught by `_`. `match` is also an expression, which means we can use it on the right-hand side of a `let` binding or directly where an expression is used: @@ -60,7 +58,8 @@ let number = match x { }; ``` -Sometimes it’s a nice way of converting something from one type to another. +Sometimes it’s a nice way of converting something from one type to another; in +this example the integers are converted to `String`. # Matching on enums @@ -91,7 +90,8 @@ fn process_message(msg: Message) { Again, the Rust compiler checks exhaustiveness, so it demands that you have a match arm for every variant of the enum. If you leave one off, it -will give you a compile-time error unless you use `_`. +will give you a compile-time error unless you use `_` or provide all possible +arms. Unlike the previous uses of `match`, you can’t use the normal `if` statement to do this. You can use the [`if let`][if-let] statement, diff --git a/src/doc/book/no-stdlib.md b/src/doc/book/no-stdlib.md index 3a4d4c306dc72..da84c6a337416 100644 --- a/src/doc/book/no-stdlib.md +++ b/src/doc/book/no-stdlib.md @@ -151,7 +151,7 @@ extern fn panic_fmt(args: &core::fmt::Arguments, # fn main() {} ``` -Note that there is one extra lang item here which differs from the examples +Note that there is one lang item here whose signature differs from the examples above, `panic_fmt`. This must be defined by consumers of libcore because the core library declares panics, but it does not define it. The `panic_fmt` lang item is this crate's definition of panic, and it must be guaranteed to diff --git a/src/doc/book/ownership.md b/src/doc/book/ownership.md index 17b263ef00ab7..a62d31d362b14 100644 --- a/src/doc/book/ownership.md +++ b/src/doc/book/ownership.md @@ -51,15 +51,24 @@ fn foo() { } ``` -When `v` comes into scope, a new [`Vec`][vect] is created. In this case, the -vector also allocates space on [the heap][heap], for the three elements. When -`v` goes out of scope at the end of `foo()`, Rust will clean up everything -related to the vector, even the heap-allocated memory. This happens -deterministically, at the end of the scope. +When `v` comes into scope, a new [vector] is created, and it allocates space on +[the heap][heap] for each of its elements. When `v` goes out of scope at the +end of `foo()`, Rust will clean up everything related to the vector, even the +heap-allocated memory. This happens deterministically, at the end of the scope. -[vect]: ../std/vec/struct.Vec.html +We'll cover [vectors] in detail later in this chapter; we only use them +here as an example of a type that allocates space on the heap at runtime. They +behave like [arrays], except their size may change by `push()`ing more +elements onto them. + +Vectors have a [generic type][generics] `Vec`, so in this example `v` will have type +`Vec`. We'll cover generics in detail later in this chapter. + +[arrays]: primitive-types.html#arrays +[vectors]: vectors.html [heap]: the-stack-and-the-heap.html [bindings]: variable-bindings.html +[generics]: generics.html # Move semantics diff --git a/src/doc/book/primitive-types.md b/src/doc/book/primitive-types.md index a8c7a7d41573e..d6188fa7cdcf8 100644 --- a/src/doc/book/primitive-types.md +++ b/src/doc/book/primitive-types.md @@ -167,8 +167,11 @@ variable binding. Slices have a defined length, can be mutable or immutable. ## Slicing syntax You can use a combo of `&` and `[]` to create a slice from various things. The -`&` indicates that slices are similar to references, and the `[]`s, with a -range, let you define the length of the slice: +`&` indicates that slices are similar to [references], which we will cover in +detail later in this section. The `[]`s, with a range, let you define the +length of the slice: + +[references]: references-and-borrowing.html ```rust let a = [0, 1, 2, 3, 4]; @@ -189,11 +192,13 @@ documentation][slice]. # `str` Rust’s `str` type is the most primitive string type. As an [unsized type][dst], -it’s not very useful by itself, but becomes useful when placed behind a reference, -like [`&str`][strings]. As such, we’ll just leave it at that. +it’s not very useful by itself, but becomes useful when placed behind a +reference, like `&str`. We'll elaborate further when we cover +[Strings][strings] and [references]. [dst]: unsized-types.html [strings]: strings.html +[references]: references-and-borrowing.html You can find more documentation for `str` [in the standard library documentation][str]. diff --git a/src/doc/book/references-and-borrowing.md b/src/doc/book/references-and-borrowing.md index d8758e0c695c2..a172390a02126 100644 --- a/src/doc/book/references-and-borrowing.md +++ b/src/doc/book/references-and-borrowing.md @@ -126,7 +126,7 @@ the thing `y` points at. You’ll notice that `x` had to be marked `mut` as well If it wasn’t, we couldn’t take a mutable borrow to an immutable value. You'll also notice we added an asterisk (`*`) in front of `y`, making it `*y`, -this is because `y` is an `&mut` reference. You'll also need to use them for +this is because `y` is a `&mut` reference. You'll also need to use them for accessing the contents of a reference as well. Otherwise, `&mut` references are just like references. There _is_ a large @@ -263,7 +263,7 @@ for i in &v { } ``` -This prints out one through three. As we iterate through the vectors, we’re +This prints out one through three. As we iterate through the vector, we’re only given references to the elements. And `v` is itself borrowed as immutable, which means we can’t change it while we’re iterating: diff --git a/src/doc/book/rust-inside-other-languages.md b/src/doc/book/rust-inside-other-languages.md deleted file mode 100644 index 61627c0b5a2fe..0000000000000 --- a/src/doc/book/rust-inside-other-languages.md +++ /dev/null @@ -1,344 +0,0 @@ -% Rust Inside Other Languages - -For our third project, we’re going to choose something that shows off one of -Rust’s greatest strengths: a lack of a substantial runtime. - -As organizations grow, they increasingly rely on a multitude of programming -languages. Different programming languages have different strengths and -weaknesses, and a polyglot stack lets you use a particular language where -its strengths make sense and a different one where it’s weak. - -A very common area where many programming languages are weak is in runtime -performance of programs. Often, using a language that is slower, but offers -greater programmer productivity, is a worthwhile trade-off. To help mitigate -this, they provide a way to write some of your system in C and then call -that C code as though it were written in the higher-level language. This is -called a ‘foreign function interface’, often shortened to ‘FFI’. - -Rust has support for FFI in both directions: it can call into C code easily, -but crucially, it can also be called _into_ as easily as C. Combined with -Rust’s lack of a garbage collector and low runtime requirements, this makes -Rust a great candidate to embed inside of other languages when you need -that extra oomph. - -There is a whole [chapter devoted to FFI][ffi] and its specifics elsewhere in -the book, but in this chapter, we’ll examine this particular use-case of FFI, -with examples in Ruby, Python, and JavaScript. - -[ffi]: ffi.html - -# The problem - -There are many different projects we could choose here, but we’re going to -pick an example where Rust has a clear advantage over many other languages: -numeric computing and threading. - -Many languages, for the sake of consistency, place numbers on the heap, rather -than on the stack. Especially in languages that focus on object-oriented -programming and use garbage collection, heap allocation is the default. Sometimes -optimizations can stack allocate particular numbers, but rather than relying -on an optimizer to do its job, we may want to ensure that we’re always using -primitive number types rather than some sort of object type. - -Second, many languages have a ‘global interpreter lock’ (GIL), which limits -concurrency in many situations. This is done in the name of safety, which is -a positive effect, but it limits the amount of work that can be done at the -same time, which is a big negative. - -To emphasize these two aspects, we’re going to create a little project that -uses these two aspects heavily. Since the focus of the example is to embed -Rust into other languages, rather than the problem itself, we’ll just use a -toy example: - -> Start ten threads. Inside each thread, count from one to five million. After -> all ten threads are finished, print out ‘done!’. - -I chose five million based on my particular computer. Here’s an example of this -code in Ruby: - -```ruby -threads = [] - -10.times do - threads << Thread.new do - count = 0 - - 5_000_000.times do - count += 1 - end - - count - end -end - -threads.each do |t| - puts "Thread finished with count=#{t.value}" -end -puts "done!" -``` - -Try running this example, and choose a number that runs for a few seconds. -Depending on your computer’s hardware, you may have to increase or decrease the -number. - -On my system, running this program takes `2.156` seconds. And, if I use some -sort of process monitoring tool, like `top`, I can see that it only uses one -core on my machine. That’s the GIL kicking in. - -While it’s true that this is a synthetic program, one can imagine many problems -that are similar to this in the real world. For our purposes, spinning up a few -busy threads represents some sort of parallel, expensive computation. - -# A Rust library - -Let’s rewrite this problem in Rust. First, let’s make a new project with -Cargo: - -```bash -$ cargo new embed -$ cd embed -``` - -This program is fairly easy to write in Rust: - -```rust -use std::thread; - -fn process() { - let handles: Vec<_> = (0..10).map(|_| { - thread::spawn(|| { - let mut x = 0; - for _ in 0..5_000_000 { - x += 1 - } - x - }) - }).collect(); - - for h in handles { - println!("Thread finished with count={}", - h.join().map_err(|_| "Could not join a thread!").unwrap()); - } -} -``` - -Some of this should look familiar from previous examples. We spin up ten -threads, collecting them into a `handles` vector. Inside of each thread, we -loop five million times, and add one to `x` each time. Finally, we join on -each thread. - -Right now, however, this is a Rust library, and it doesn’t expose anything -that’s callable from C. If we tried to hook this up to another language right -now, it wouldn’t work. We only need to make two small changes to fix this, -though. The first is to modify the beginning of our code: - -```rust,ignore -#[no_mangle] -pub extern fn process() { -``` - -We have to add a new attribute, `no_mangle`. When you create a Rust library, it -changes the name of the function in the compiled output. The reasons for this -are outside the scope of this tutorial, but in order for other languages to -know how to call the function, we can’t do that. This attribute turns -that behavior off. - -The other change is the `pub extern`. The `pub` means that this function should -be callable from outside of this module, and the `extern` says that it should -be able to be called from C. That’s it! Not a whole lot of change. - -The second thing we need to do is to change a setting in our `Cargo.toml`. Add -this at the bottom: - -```toml -[lib] -name = "embed" -crate-type = ["dylib"] -``` - -This tells Rust that we want to compile our library into a standard dynamic -library. By default, Rust compiles an ‘rlib’, a Rust-specific format. - -Let’s build the project now: - -```bash -$ cargo build --release - Compiling embed v0.1.0 (file:///home/steve/src/embed) -``` - -We’ve chosen `cargo build --release`, which builds with optimizations on. We -want this to be as fast as possible! You can find the output of the library in -`target/release`: - -```bash -$ ls target/release/ -build deps examples libembed.so native -``` - -That `libembed.so` is our ‘shared object’ library. We can use this file -just like any shared object library written in C! As an aside, this may be -`embed.dll` (Microsoft Windows) or `libembed.dylib` (Mac OS X), depending on -your operating system. - -Now that we’ve got our Rust library built, let’s use it from our Ruby. - -# Ruby - -Open up an `embed.rb` file inside of our project, and do this: - -```ruby -require 'ffi' - -module Hello - extend FFI::Library - ffi_lib 'target/release/libembed.so' - attach_function :process, [], :void -end - -Hello.process - -puts 'done!' -``` - -Before we can run this, we need to install the `ffi` gem: - -```bash -$ gem install ffi # this may need sudo -Fetching: ffi-1.9.8.gem (100%) -Building native extensions. This could take a while... -Successfully installed ffi-1.9.8 -Parsing documentation for ffi-1.9.8 -Installing ri documentation for ffi-1.9.8 -Done installing documentation for ffi after 0 seconds -1 gem installed -``` - -And finally, we can try running it: - -```bash -$ ruby embed.rb -Thread finished with count=5000000 -Thread finished with count=5000000 -Thread finished with count=5000000 -Thread finished with count=5000000 -Thread finished with count=5000000 -Thread finished with count=5000000 -Thread finished with count=5000000 -Thread finished with count=5000000 -Thread finished with count=5000000 -Thread finished with count=5000000 -done! -done! -$ -``` - -Whoa, that was fast! On my system, this took `0.086` seconds, rather than -the two seconds the pure Ruby version took. Let’s break down this Ruby -code: - -```ruby -require 'ffi' -``` - -We first need to require the `ffi` gem. This lets us interface with our -Rust library like a C library. - -```ruby -module Hello - extend FFI::Library - ffi_lib 'target/release/libembed.so' -``` - -The `Hello` module is used to attach the native functions from the shared -library. Inside, we `extend` the necessary `FFI::Library` module and then call -`ffi_lib` to load up our shared object library. We just pass it the path that -our library is stored, which, as we saw before, is -`target/release/libembed.so`. - -```ruby -attach_function :process, [], :void -``` - -The `attach_function` method is provided by the FFI gem. It’s what -connects our `process()` function in Rust to a Ruby function of the -same name. Since `process()` takes no arguments, the second parameter -is an empty array, and since it returns nothing, we pass `:void` as -the final argument. - -```ruby -Hello.process -``` - -This is the actual call into Rust. The combination of our `module` -and the call to `attach_function` sets this all up. It looks like -a Ruby function but is actually Rust! - -```ruby -puts 'done!' -``` - -Finally, as per our project’s requirements, we print out `done!`. - -That’s it! As we’ve seen, bridging between the two languages is really easy, -and buys us a lot of performance. - -Next, let’s try Python! - -# Python - -Create an `embed.py` file in this directory, and put this in it: - -```python -from ctypes import cdll - -lib = cdll.LoadLibrary("target/release/libembed.so") - -lib.process() - -print("done!") -``` - -Even easier! We use `cdll` from the `ctypes` module. A quick call -to `LoadLibrary` later, and we can call `process()`. - -On my system, this takes `0.017` seconds. Speedy! - -# Node.js - -Node isn’t a language, but it’s currently the dominant implementation of -server-side JavaScript. - -In order to do FFI with Node, we first need to install the library: - -```bash -$ npm install ffi -``` - -After that installs, we can use it: - -```javascript -var ffi = require('ffi'); - -var lib = ffi.Library('target/release/libembed', { - 'process': ['void', []] -}); - -lib.process(); - -console.log("done!"); -``` - -It looks more like the Ruby example than the Python example. We use -the `ffi` module to get access to `ffi.Library()`, which loads up -our shared object. We need to annotate the return type and argument -types of the function, which are `void` for return and an empty -array to signify no arguments. From there, we just call it and -print the result. - -On my system, this takes a quick `0.092` seconds. - -# Conclusion - -As you can see, the basics of doing this are _very_ easy. Of course, -there's a lot more that we could do here. Check out the [FFI][ffi] -chapter for more details. diff --git a/src/doc/book/structs.md b/src/doc/book/structs.md index 1d70ee2786919..75d0093b1476a 100644 --- a/src/doc/book/structs.md +++ b/src/doc/book/structs.md @@ -88,6 +88,35 @@ fn main() { } ``` +Your structure can still contain `&mut` pointers, which will let +you do some kinds of mutation: + +```rust +struct Point { + x: i32, + y: i32, +} + +struct PointRef<'a> { + x: &'a mut i32, + y: &'a mut i32, +} + +fn main() { + let mut point = Point { x: 0, y: 0 }; + + { + let r = PointRef { x: &mut point.x, y: &mut point.y }; + + *r.x = 5; + *r.y = 6; + } + + assert_eq!(5, point.x); + assert_eq!(6, point.y); +} +``` + # Update syntax A `struct` can include `..` to indicate that you want to use a copy of some diff --git a/src/doc/book/syntax-and-semantics.md b/src/doc/book/syntax-and-semantics.md index cce985c9e484c..e9ec26dccdcdc 100644 --- a/src/doc/book/syntax-and-semantics.md +++ b/src/doc/book/syntax-and-semantics.md @@ -1,6 +1,6 @@ % Syntax and Semantics -This section breaks Rust down into small chunks, one for each concept. +This chapter breaks Rust down into small chunks, one for each concept. If you’d like to learn Rust from the bottom up, reading this in order is a great way to do that. diff --git a/src/doc/book/syntax-index.md b/src/doc/book/syntax-index.md index 01f06f718f857..f7e32943c638e 100644 --- a/src/doc/book/syntax-index.md +++ b/src/doc/book/syntax-index.md @@ -41,6 +41,7 @@ * `!` (`ident!(…)`, `ident!{…}`, `ident![…]`): denotes macro expansion. See [Macros]. * `!` (`!expr`): bitwise or logical complement. Overloadable (`Not`). +* `!=` (`var != expr`): nonequality comparison. Overloadable (`PartialEq`). * `%` (`expr % expr`): arithmetic remainder. Overloadable (`Rem`). * `%=` (`var %= expr`): arithmetic remainder & assignment. * `&` (`expr & expr`): bitwise and. Overloadable (`BitAnd`). @@ -75,13 +76,13 @@ * `;` (`[…; len]`): part of fixed-size array syntax. See [Primitive Types (Arrays)]. * `<<` (`expr << expr`): left-shift. Overloadable (`Shl`). * `<<=` (`var <<= expr`): left-shift & assignment. -* `<` (`expr < expr`): less-than comparison. Overloadable (`Cmp`, `PartialCmp`). -* `<=` (`var <= expr`): less-than or equal-to comparison. Overloadable (`Cmp`, `PartialCmp`). +* `<` (`expr < expr`): less-than comparison. Overloadable (`PartialOrd`). +* `<=` (`var <= expr`): less-than or equal-to comparison. Overloadable (`PartialOrd`). * `=` (`var = expr`, `ident = type`): assignment/equivalence. See [Variable Bindings], [`type` Aliases], generic parameter defaults. -* `==` (`var == expr`): comparison. Overloadable (`Eq`, `PartialEq`). +* `==` (`var == expr`): equality comparison. Overloadable (`PartialEq`). * `=>` (`pat => expr`): part of match arm syntax. See [Match]. -* `>` (`expr > expr`): greater-than comparison. Overloadable (`Cmp`, `PartialCmp`). -* `>=` (`var >= expr`): greater-than or equal-to comparison. Overloadable (`Cmp`, `PartialCmp`). +* `>` (`expr > expr`): greater-than comparison. Overloadable (`PartialOrd`). +* `>=` (`var >= expr`): greater-than or equal-to comparison. Overloadable (`PartialOrd`). * `>>` (`expr >> expr`): right-shift. Overloadable (`Shr`). * `>>=` (`var >>= expr`): right-shift & assignment. * `@` (`ident @ pat`): pattern binding. See [Patterns (Bindings)]. diff --git a/src/doc/book/the-stack-and-the-heap.md b/src/doc/book/the-stack-and-the-heap.md index 63b73a7fc31fc..bc40eeb8dccfb 100644 --- a/src/doc/book/the-stack-and-the-heap.md +++ b/src/doc/book/the-stack-and-the-heap.md @@ -539,7 +539,7 @@ instead. # Which to use? So if the stack is faster and easier to manage, why do we need the heap? A big -reason is that Stack-allocation alone means you only have LIFO semantics for +reason is that Stack-allocation alone means you only have 'Last In First Out (LIFO)' semantics for reclaiming storage. Heap-allocation is strictly more general, allowing storage to be taken from and returned to the pool in arbitrary order, but at a complexity cost. diff --git a/src/doc/complement-design-faq.md b/src/doc/complement-design-faq.md index 36e76896dad44..f4898dc676530 100644 --- a/src/doc/complement-design-faq.md +++ b/src/doc/complement-design-faq.md @@ -1,186 +1,3 @@ % The Rust Design FAQ -This document describes decisions that were arrived at after lengthy discussion and -experimenting with alternatives. Please do not propose reversing them unless -you have a new, extremely compelling argument. Note that this document -specifically talks about the *language* and not any library or implementation. - -A few general guidelines define the philosophy: - -- [Memory safety][mem] must never be compromised -- [Abstraction][abs] should be zero-cost, while still maintaining safety -- Practicality is key - -[mem]: http://en.wikipedia.org/wiki/Memory_safety -[abs]: http://en.wikipedia.org/wiki/Abstraction_%28computer_science%29 - -# Semantics - -## Data layout is unspecified - -In the general case, `enum` and `struct` layout is undefined. This allows the -compiler to potentially do optimizations like re-using padding for the -discriminant, compacting variants of nested enums, reordering fields to remove -padding, etc. `enum`s which carry no data ("C-like") are eligible to have a -defined representation. Such `enum`s are easily distinguished in that they are -simply a list of names that carry no data: - -``` -enum CLike { - A, - B = 32, - C = 34, - D -} -``` - -The [repr attribute][repr] can be applied to such `enum`s to give them the same -representation as a primitive. This allows using Rust `enum`s in FFI where C -`enum`s are also used, for most use cases. The attribute can also be applied -to `struct`s to get the same layout as a C struct would. - -[repr]: reference.html#ffi-attributes - -## There is no GC - -A language that requires a GC is a language that opts into a larger, more -complex runtime than Rust cares for. Rust is usable on bare metal with no -extra runtime. Additionally, garbage collection is frequently a source of -non-deterministic behavior. Rust provides the tools to make using a GC -possible and even pleasant, but it should not be a requirement for -implementing the language. - -## Non-`Sync` `static mut` is unsafe - -Types which are [`Sync`][sync] are thread-safe when multiple shared -references to them are used concurrently. Types which are not `Sync` are not -thread-safe, and thus when used in a global require unsafe code to use. - -[sync]: core/marker/trait.Sync.html - -### If mutable static items that implement `Sync` are safe, why is taking &mut SHARABLE unsafe? - -Having multiple aliasing `&mut T`s is never allowed. Due to the nature of -globals, the borrow checker cannot possibly ensure that a static obeys the -borrowing rules, so taking a mutable reference to a static is always unsafe. - -## There is no life before or after main (no static ctors/dtors) - -Globals can not have a non-constant-expression constructor and cannot have a -destructor at all. This is an opinion of the language. Static constructors are -undesirable because they can slow down program startup. Life before main is -often considered a misfeature, never to be used. Rust helps this along by just -not having the feature. - -See [the C++ FQA][fqa] about the "static initialization order fiasco", and -[Eric Lippert's blog][elp] for the challenges in C#, which also has this -feature. - -A nice replacement is [lazy_static][lazy_static]. - -[fqa]: http://yosefk.com/c++fqa/ctors.html#fqa-10.12 -[elp]: http://ericlippert.com/2013/02/06/static-constructors-part-one/ -[lazy_static]: https://crates.io/crates/lazy_static - -## The language does not require a runtime - -See the above entry on GC. Requiring a runtime limits the utility of the -language, and makes it undeserving of the title "systems language". All Rust -code should need to run is a stack. - -## `match` must be exhaustive - -`match` being exhaustive has some useful properties. First, if every -possibility is covered by the `match`, adding further variants to the `enum` -in the future will prompt a compilation failure, rather than runtime panic. -Second, it makes cost explicit. In general, the only safe way to have a -non-exhaustive match would be to panic the thread if nothing is matched, though -it could fall through if the type of the `match` expression is `()`. This sort -of hidden cost and special casing is against the language's philosophy. It's -easy to ignore all unspecified cases by using the `_` wildcard: - -```rust,ignore -match val.do_something() { - Cat(a) => { /* ... */ } - _ => { /* ... */ } -} -``` - -[#3101][iss] is the issue that proposed making this the only behavior, with -rationale and discussion. - -[iss]: https://github.com/rust-lang/rust/issues/3101 - -## No guaranteed tail-call optimization - -In general, tail-call optimization is not guaranteed: see [here][tml] for a -detailed explanation with references. There is a [proposed extension][tce] that -would allow tail-call elimination in certain contexts. The compiler is still -free to optimize tail-calls [when it pleases][sco], however. - -[tml]: https://mail.mozilla.org/pipermail/rust-dev/2013-April/003557.html -[sco]: http://llvm.org/docs/CodeGenerator.html#sibling-call-optimization -[tce]: https://github.com/rust-lang/rfcs/pull/81 - -## No constructors - -Functions can serve the same purpose as constructors without adding any -language complexity. - -## No copy constructors - -Types which implement [`Copy`][copy], will do a standard C-like "shallow copy" -with no extra work (similar to "plain old data" in C++). It is impossible to -implement `Copy` types that require custom copy behavior. Instead, in Rust -"copy constructors" are created by implementing the [`Clone`][clone] trait, -and explicitly calling the `clone` method. Making user-defined copy operators -explicit surfaces the underlying complexity, forcing the developer to opt-in -to potentially expensive operations. - -[copy]: core/marker/trait.Copy.html -[clone]: core/clone/trait.Clone.html - -## No move constructors - -Values of all types are moved via `memcpy`. This makes writing generic unsafe -code much simpler since assignment, passing and returning are known to never -have a side effect like unwinding. - -# Syntax - -## Macros require balanced delimiters - -This is to make the language easier to parse for machines. Since the body of a -macro can contain arbitrary tokens, some restriction is needed to allow simple -non-macro-expanding lexers and parsers. This comes in the form of requiring -that all delimiters be balanced. - -## `->` for function return type - -This is to make the language easier to parse for humans, especially in the face -of higher-order functions. `fn foo(f: fn(i32): i32, fn(T): U): U` is not -particularly easy to read. - -## Why is `let` used to introduce variables? - -Instead of the term "variable", we use "variable bindings". The -simplest way for creating a binding is by using the `let` syntax. -Other ways include `if let`, `while let`, and `match`. Bindings also -exist in function argument positions. - -Bindings always happen in pattern matching positions, and it's also Rust's way -to declare mutability. One can also re-declare mutability of a binding in -pattern matching. This is useful to avoid unnecessary `mut` annotations. An -interesting historical note is that Rust comes, syntactically, most closely -from ML, which also uses `let` to introduce bindings. - -See also [a long thread][alt] on renaming `let mut` to `var`. - -[alt]: https://mail.mozilla.org/pipermail/rust-dev/2014-January/008319.html - -## Why no `--x` or `x++`? - -Preincrement and postincrement, while convenient, are also fairly complex. They -require knowledge of evaluation order, and often lead to subtle bugs and -undefined behavior in C and C++. `x = x + 1` or `x += 1` is only slightly -longer, but unambiguous. +This content has moved to [the website](https://www.rust-lang.org/). diff --git a/src/doc/complement-lang-faq.md b/src/doc/complement-lang-faq.md index 55abebf496dca..920c6edc389fe 100644 --- a/src/doc/complement-lang-faq.md +++ b/src/doc/complement-lang-faq.md @@ -1,177 +1,3 @@ % The Rust Language FAQ -## Are there any big programs written in it yet? I want to read big samples. - -There aren't many large programs yet. The Rust [compiler][rustc], 60,000+ lines at the time of writing, is written in Rust. As the oldest body of Rust code it has gone through many iterations of the language, and some parts are nicer to look at than others. It may not be the best code to learn from, but [borrowck] and [resolve] were written recently. - -[rustc]: https://github.com/rust-lang/rust/tree/master/src/librustc -[resolve]: https://github.com/rust-lang/rust/tree/master/src/librustc_resolve -[borrowck]: https://github.com/rust-lang/rust/tree/master/src/librustc_borrowck/borrowck - -A research browser engine called [Servo][servo], currently 30,000+ lines across more than a dozen crates, will be exercising a lot of Rust's distinctive type-system and concurrency features, and integrating many native libraries. - -[servo]: https://github.com/servo/servo - -Some examples that demonstrate different aspects of the language: - -* [sprocketnes], an NES emulator with no GC, using modern Rust conventions -* The language's general-purpose [hash] function, SipHash-2-4. Bit twiddling, OO, macros -* The standard library's [HashMap], a sendable hash map in an OO style -* The standard library's [json] module. Enums and pattern matching - -[sprocketnes]: https://github.com/pcwalton/sprocketnes -[hash]: https://github.com/rust-lang/rust/tree/master/src/libcore/hash -[HashMap]: https://github.com/rust-lang/rust/tree/master/src/libstd/collections/hash -[json]: https://github.com/rust-lang/rust/blob/master/src/libserialize/json.rs - -You may also be interested in browsing [trending Rust repositories][github-rust] on GitHub. - -[github-rust]: https://github.com/trending?l=rust - -## Is anyone using Rust in production? - -Yes. For example (incomplete): - -* [OpenDNS](http://labs.opendns.com/2013/10/04/zeromq-helping-us-block-malicious-domains/) -* [Skylight](http://skylight.io) -* [wit.ai](https://github.com/wit-ai/witd) -* [Codius](https://codius.org/blog/codius-rust/) -* [MaidSafe](http://maidsafe.net/) -* [Terminal.com](https://terminal.com) - -## Does it run on Windows? - -Yes. All development happens in lockstep on all 3 target platforms (using MinGW, not Cygwin). - -## Is it OO? How do I do this thing I normally do in an OO language? - -It is multi-paradigm. Not everything is shoe-horned into a single abstraction. Many things you can do in OO languages you can do in Rust, but not everything, and not always using the same abstraction you're accustomed to. - -## How do you get away with "no null pointers"? - -Data values in the language can only be constructed through a fixed set of initializer forms. Each of those forms requires that its inputs already be initialized. A liveness analysis ensures that local variables are initialized before use. - -## What is the relationship between a module and a crate? - -* A crate is a top-level compilation unit that corresponds to a single loadable object. -* A module is a (possibly nested) unit of name-management inside a crate. -* A crate contains an implicit, un-named top-level module. -* Recursive definitions can span modules, but not crates. -* Crates do not have global names, only a set of non-unique metadata tags. -* There is no global inter-crate namespace; all name management occurs within a crate. - * Using another crate binds the root of _its_ namespace into the user's namespace. - -## Why is panic unwinding non-recoverable within a thread? Why not try to "catch exceptions"? - -In short, because too few guarantees could be made about the dynamic environment of the catch block, as well as invariants holding in the unwound heap, to be able to safely resume; we believe that other methods of signalling and logging errors are more appropriate, with threads playing the role of a "hard" isolation boundary between separate heaps. - -Rust provides, instead, three predictable and well-defined options for handling any combination of the three main categories of "catch" logic: - -* Failure _logging_ is done by the integrated logging subsystem. -* _Recovery_ after a panic is done by trapping a thread panic from _outside_ - the thread, where other threads are known to be unaffected. -* _Cleanup_ of resources is done by RAII-style objects with destructors. - -Cleanup through RAII-style destructors is more likely to work than in catch blocks anyways, since it will be better tested (part of the non-error control paths, so executed all the time). - -## Why aren't modules type-parametric? - -We want to maintain the option to parameterize at runtime. We may eventually change this limitation, but initially this is how type parameters were implemented. - -## Why aren't values type-parametric? Why only items? - -Doing so would make type inference much more complex, and require the implementation strategy of runtime parameterization. - -## Why are enumerations nominal and closed? - -We don't know if there's an obvious, easy, efficient, stock-textbook way of supporting open or structural disjoint unions. We prefer to stick to language features that have an obvious and well-explored semantics. - -## Why aren't channels synchronous? - -There's a lot of debate on this topic; it's easy to find a proponent of default-sync or default-async communication, and there are good reasons for either. Our choice rests on the following arguments: - -* Part of the point of isolating threads is to decouple threads from one another, such that assumptions in one thread do not cause undue constraints (or bugs, if violated!) in another. Temporal coupling is as real as any other kind; async-by-default relaxes the default case to only _causal_ coupling. -* Default-async supports buffering and batching communication, reducing the frequency and severity of thread-switching and inter-thread / inter-domain synchronization. -* Default-async with transmittable channels is the lowest-level building block on which more-complex synchronization topologies and strategies can be built; it is not clear to us that the majority of cases fit the 2-party full-synchronization pattern rather than some more complex multi-party or multi-stage scenario. We did not want to force all programs to pay for wiring the former assumption into all communications. - -## Why are channels half-duplex (one-way)? - -Similar to the reasoning about default-sync: it wires fewer assumptions into the implementation, that would have to be paid by all use-cases even if they actually require a more complex communication topology. - -## Why are strings UTF-8 by default? Why not UCS2 or UCS4? - -The `str` type is UTF-8 because we observe more text in the wild in this encoding – particularly in network transmissions, which are endian-agnostic – and we think it's best that the default treatment of I/O not involve having to recode codepoints in each direction. - -This does mean that indexed access to a Unicode codepoint inside a `str` value is an O(n) operation. On the one hand, this is clearly undesirable; on the other hand, this problem is full of trade-offs and we'd like to point a few important qualifications: - -* Scanning a `str` for ASCII-range codepoints can still be done safely octet-at-a-time. If you use `.as_bytes()`, pulling out a `u8` costs only O(1) and produces a value that can be cast and compared to an ASCII-range `char`. So if you're (say) line-breaking on `'\n'`, octet-based treatment still works. UTF8 was well-designed this way. -* Most "character oriented" operations on text only work under very restricted language assumptions sets such as "ASCII-range codepoints only". Outside ASCII-range, you tend to have to use a complex (non-constant-time) algorithm for determining linguistic-unit (glyph, word, paragraph) boundaries anyways. We recommend using an "honest" linguistically-aware, Unicode-approved algorithm. -* The `char` type is UCS4. If you honestly need to do a codepoint-at-a-time algorithm, it's trivial to write a `type wstr = [char]`, and unpack a `str` into it in a single pass, then work with the `wstr`. In other words: the fact that the language is not "decoding to UCS4 by default" shouldn't stop you from decoding (or re-encoding any other way) if you need to work with that encoding. - -## Why are `str`s, slices, arrays etc. built-in types rather than (say) special kinds of trait/impl? - -In each case there is one or more operator, literal constructor, overloaded use or integration with a built-in control structure that makes us think it would be awkward to phrase the type in terms of more-general type constructors. Same as, say, with numbers! But this is partly an aesthetic call, and we'd be willing to look at a worked-out proposal for eliminating or rephrasing these special cases. - -## Can Rust code call C code? - -Yes. Calling C code from Rust is simple and exactly as efficient as calling C code from C. - -## Can C code call Rust code? - -Yes. The Rust code has to be exposed via an `extern` declaration, which makes it C-ABI compatible. Such a function can be passed to C code as a function pointer or, if given the `#[no_mangle]` attribute to disable symbol mangling, can be called directly from C code. - -## Why aren't function signatures inferred? Why only local variables? - -* Mechanically, it simplifies the inference algorithm; inference only requires looking at one function at a time. -* The same simplification goes double for human readers. A reader does not need an IDE running an inference algorithm across an entire crate to be able to guess at a function's argument types; it's always explicit and nearby. - -## Why does a type parameter need explicit trait bounds to invoke methods on it, when C++ templates do not? - -* Requiring explicit bounds means that the compiler can type-check the code at the point where the type-parametric item is *defined*, rather than delaying to when its type parameters are instantiated. You know that *any* set of type parameters fulfilling the bounds listed in the API will compile. It's an enforced minimal level of documentation, and results in very clean error messages. - -* Scoping of methods is also a problem. C++ needs [Koenig (argument dependent) lookup](http://en.wikipedia.org/wiki/Argument-dependent_name_lookup), which comes with its own host of problems. Explicit bounds avoid this issue: traits are explicitly imported and then used as bounds on type parameters, so there is a clear mapping from the method to its implementation (via the trait and the instantiated type). - - * Related to the above point: since a parameter explicitly names its trait bounds, a single type is able to implement traits whose sets of method names overlap, cleanly and unambiguously. - -* There is further discussion on [this thread on the Rust mailing list](https://mail.mozilla.org/pipermail/rust-dev/2013-September/005603.html). - -## Will Rust implement automatic semicolon insertion, like in Go? - -For simplicity, we do not plan to do so. Implementing automatic semicolon insertion for Rust would be tricky because the absence of a trailing semicolon means "return a value". - -## How do I get my program to display the output of logging macros? - -**Short Answer**: Set the `RUST_LOG` environment variable to the name of your source file, sans extension. - -```sh -rustc hello.rs -export RUST_LOG=hello -./hello -``` - -**Long Answer**: `RUST_LOG` takes a 'logging spec' that consists of a -comma-separated list of paths, where a path consists of the crate name and -sequence of module names, each separated by double-colons. For standalone `.rs` -files, the crate is implicitly named after the source file, so in the above -example we were setting `RUST_LOG` to the name of the hello crate. Multiple paths -can be combined to control the exact logging you want to see. For example, when -debugging linking in the compiler, you might set the following: - -```sh -RUST_LOG=rustc_metadata::creader,rustc::util::filesearch,rustc::back::rpath -``` - -For a full description, see [the logging crate][1]. - -## How fast is Rust? - -As always, this question is difficult to answer. There's still a lot of work to -do on speed, and depending on what you're benchmarking, Rust has variable -performance. - -That said, it is an explicit goal of Rust to be as fast as C++ for most things. -Language decisions are made with performance in mind, and we want Rust to be as -fast as possible. Given that Rust is built on top of LLVM, any performance -improvements in it also help Rust become faster. - -[1]:log/index.html +This content has moved to [the website](https://www.rust-lang.org/). diff --git a/src/doc/complement-project-faq.md b/src/doc/complement-project-faq.md index 1ed961fd55867..b44de8e2cb32d 100644 --- a/src/doc/complement-project-faq.md +++ b/src/doc/complement-project-faq.md @@ -1,42 +1,3 @@ % The Rust Project FAQ -# What is this project's goal, in one sentence? - -To design and implement a safe, concurrent, practical, static systems language. - -# Why are you doing this? - -Existing languages at this level of abstraction and efficiency are unsatisfactory. In particular: - -* Too little attention paid to safety. -* Poor concurrency support. -* Lack of practical affordances, too dogmatic about paradigm. - -# What are some non-goals? - -* To employ any particularly cutting-edge technologies. Old, established techniques are better. -* To prize expressiveness, minimalism or elegance above other goals. These are desirable but subordinate goals. -* To cover the complete feature-set of C++, or any other language. It should provide majority-case features. -* To be 100% static, 100% safe, 100% reflective, or too dogmatic in any other sense. Trade-offs exist. -* To run on "every possible platform". It must eventually work without unnecessary compromises on widely-used hardware and software platforms. - -# Is any part of this thing production-ready? - -Yes! - -# Is this a completely Mozilla-planned and orchestrated thing? - -No. It started as a Graydon Hoare's part-time side project in 2006 and remained so for over 3 years. Mozilla got involved in 2009 once the language was mature enough to run some basic tests and demonstrate the idea. Though it is sponsored by Mozilla, Rust is developed by a diverse community of enthusiasts. - -# What will Mozilla use Rust for? - -Mozilla intends to use Rust as a platform for prototyping experimental browser architectures. Specifically, the hope is to develop a browser that is more amenable to parallelization than existing ones, while also being less prone to common C++ coding errors that result in security exploits. The name of that project is _[Servo](http://github.com/servo/servo)_. - -# Why a BSD-style permissive license rather than MPL or tri-license? - -* Partly due to preference of the original developer (Graydon). -* Partly due to the fact that languages tend to have a wider audience and more diverse set of possible embeddings and end-uses than focused, coherent products such as web browsers. We'd like to appeal to as many of those potential contributors as possible. - -# Why dual MIT/ASL2 license? - -The Apache license includes important protection against patent aggression, but it is not compatible with the GPL, version 2. To avoid problems using Rust with GPL2, it is alternately MIT licensed. +This content has moved to [the website](https://www.rust-lang.org/). diff --git a/src/doc/index.md b/src/doc/index.md index 5f2ef6107298c..fef9f1f369cee 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -1,72 +1,21 @@ % Rust Documentation -Welcome to the Rust documentation! You can use the section headings above -to jump to any particular section. + -# Getting Started +This is an index of the documentation included with the Rust +compiler. For more comprehensive documentation see [the +website](https://www.rust-lang.org). -If you haven't seen Rust at all yet, the first thing you should read is the -introduction to [The Rust Programming Language](book/index.html). It'll give -you a good idea of what Rust is like. +[**The Rust Programming Language**](book/index.html) -The book provides a lengthy explanation of Rust, its syntax, and its -concepts. Upon completing the book, you'll be an intermediate Rust -developer, and will have a good grasp of the fundamental ideas behind -Rust. +[**The Rust Reference**](reference.html) -[Rust By Example][rbe] teaches you Rust through a series of small -examples. +[**The Standard Library API Reference**](std/index.html) -[rbe]: http://rustbyexample.com/ +[**The Rustonomicon**](nomicon/index.html) -# Language Reference - -Rust does not have an exact specification yet, but an effort to describe as much of -the language in as much detail as possible is in [the reference](reference.html). - -# Standard Library Reference - -We have [API documentation for the entire standard -library](std/index.html). There's a list of crates on the left with more -specific sections, or you can use the search bar at the top to search for -something if you know its name. - -# The Rustonomicon - -[The Rustonomicon] is an entire book dedicated to explaining -how to write `unsafe` Rust code. It is for advanced Rust programmers. - -[The Rustonomicon]: nomicon/index.html - -# Tools - -[Cargo](http://doc.crates.io/index.html) is the Rust package manager providing access to libraries -beyond the standard one, and its website contains lots of good documentation. - -[`rustdoc`](book/documentation.html) is the Rust's documentation generator, a tool converting -annotated source code into HTML docs. - -# FAQs - -There are questions that are asked quite often, so we've made FAQs for them: - -* [Language Design FAQ](complement-design-faq.html) -* [Language FAQ](complement-lang-faq.html) -* [Project FAQ](complement-project-faq.html) -* [How to submit a bug report](https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports) - -# The Error Index - -If you encounter an error while compiling your code you may be able to look it -up in the [Rust Compiler Error Index](error-index.html). - -# Community Translations - -Several projects have been started to translate the documentation into other -languages: - -- [Russian](https://github.com/kgv/rust_book_ru) -- [Korean](https://github.com/rust-kr/doc.rust-kr.org) -- [Chinese](https://github.com/KaiserY/rust-book-chinese) -- [Spanish](https://goyox86.github.io/elpr) -- [German](https://panicbit.github.io/rustbook-de) +[**The Compiler Error Index**](error-index.html) diff --git a/src/doc/nomicon/vec-dealloc.md b/src/doc/nomicon/vec-dealloc.md index b767caa491236..706fe680e0066 100644 --- a/src/doc/nomicon/vec-dealloc.md +++ b/src/doc/nomicon/vec-dealloc.md @@ -21,7 +21,7 @@ impl Drop for Vec { let elem_size = mem::size_of::(); let num_bytes = elem_size * self.cap; unsafe { - heap::deallocate(*self.ptr, num_bytes, align); + heap::deallocate(*self.ptr as *mut _, num_bytes, align); } } } diff --git a/src/doc/nomicon/vec-insert-remove.md b/src/doc/nomicon/vec-insert-remove.md index 0a37170c52ca3..bcecd78a1b75a 100644 --- a/src/doc/nomicon/vec-insert-remove.md +++ b/src/doc/nomicon/vec-insert-remove.md @@ -24,7 +24,7 @@ pub fn insert(&mut self, index: usize, elem: T) { // ptr::copy(src, dest, len): "copy from source to dest len elems" ptr::copy(self.ptr.offset(index as isize), self.ptr.offset(index as isize + 1), - len - index); + self.len - index); } ptr::write(self.ptr.offset(index as isize), elem); self.len += 1; @@ -44,7 +44,7 @@ pub fn remove(&mut self, index: usize) -> T { let result = ptr::read(self.ptr.offset(index as isize)); ptr::copy(self.ptr.offset(index as isize + 1), self.ptr.offset(index as isize), - len - index); + self.len - index); result } } diff --git a/src/doc/reference.md b/src/doc/reference.md index e7cc1436824e4..5f71ee4437958 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -208,10 +208,10 @@ A _string literal_ is a sequence of any Unicode characters enclosed within two which must be _escaped_ by a preceding `U+005C` character (`\`). Line-break characters are allowed in string literals. Normally they represent -themselves (i.e. no translation), but as a special exception, when a `U+005C` -character (`\`) occurs immediately before the newline, the `U+005C` character, -the newline, and all whitespace at the beginning of the next line are ignored. -Thus `a` and `b` are equal: +themselves (i.e. no translation), but as a special exception, when an unescaped +`U+005C` character (`\`) occurs immediately before the newline (`U+000A`), the +`U+005C` character, the newline, and all whitespace at the beginning of the +next line are ignored. Thus `a` and `b` are equal: ```rust let a = "foobar"; diff --git a/src/etc/apple-darwin.supp b/src/etc/apple-darwin.supp index 866578930a3d8..50e30caa2b34b 100644 --- a/src/etc/apple-darwin.supp +++ b/src/etc/apple-darwin.supp @@ -1,17 +1,11 @@ { osx-frameworks.rs-fails-otherwise-1 Memcheck:Leak - match-leak-kinds: possible + match-leak-kinds: definite,possible fun:malloc ... fun:__CFInitialize - fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE - fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader19processInitializersERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader15runInitializersERKNS_11LinkContextERNS_21InitializerTimingListE - fun:_ZN4dyld24initializeMainExecutableEv + ... } { @@ -22,10 +16,6 @@ ... fun:__CFInitialize fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE - fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader19processInitializersERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE } { @@ -33,12 +23,10 @@ Memcheck:Leak match-leak-kinds: possible fun:realloc - fun:_ZL12realizeClassP10objc_class - fun:_ZL12realizeClassP10objc_class - fun:_ZN13list_array_ttIm15protocol_list_tE11attachListsEPKPS0_j + ... fun:_read_images fun:map_images_nolock - fun:map_2_images + ... fun:_ZN4dyldL18notifyBatchPartialE17dyld_image_statesbPFPKcS0_jPK15dyld_image_infoE fun:_ZN4dyld36registerImageStateBatchChangeHandlerE17dyld_image_statesPFPKcS0_jPK15dyld_image_infoE fun:dyld_register_image_state_change_handler @@ -49,7 +37,7 @@ { osx-frameworks.rs-fails-otherwise-4 Memcheck:Leak - match-leak-kinds: possible + match-leak-kinds: definite,possible fun:calloc ... fun:__CFInitialize @@ -61,45 +49,27 @@ { osx-frameworks.rs-fails-otherwise-5 Memcheck:Leak - match-leak-kinds: definite - fun:calloc + match-leak-kinds: definite,possible + fun:malloc_zone_malloc ... fun:__CFInitialize - fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE - fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE -} - -{ - osx-frameworks.rs-fails-otherwise-6 - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - fun:strdup - fun:_CFProcessPath - fun:__CFInitialize - fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE - fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader19processInitializersERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader15runInitializersERKNS_11LinkContextERNS_21InitializerTimingListE - fun:_ZN4dyld24initializeMainExecutableEv - fun:_ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_Pm + ... } { - osx-frameworks.rs-fails-otherwise-7 + fails-since-xcode-7.2 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: possible fun:malloc_zone_malloc - ... - fun:__CFInitialize - fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE - fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader19processInitializersERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE - fun:_ZN11ImageLoader15runInitializersERKNS_11LinkContextERNS_21InitializerTimingListE - fun:_ZN4dyld24initializeMainExecutableEv + fun:_objc_copyClassNamesForImage + fun:_ZL9protocolsv + fun:_Z9readClassP10objc_classbb + fun:gc_init + fun:_ZL33objc_initializeClassPair_internalP10objc_classPKcS0_S0_ + fun:layout_string_create + fun:_ZL12realizeClassP10objc_class + fun:_ZL22copySwiftV1MangledNamePKcb + fun:_ZL22copySwiftV1MangledNamePKcb + fun:_ZL22copySwiftV1MangledNamePKcb + fun:_ZL22copySwiftV1MangledNamePKcb } diff --git a/src/etc/x86.supp b/src/etc/x86.supp index e4368da90331f..6e409af79aecf 100644 --- a/src/etc/x86.supp +++ b/src/etc/x86.supp @@ -12,7 +12,7 @@ fun:tlv_finalize fun:_pthread_tsd_cleanup fun:_pthread_exit - fun:_pthread_body + ... fun:_pthread_start fun:thread_start } @@ -24,7 +24,7 @@ fun:tlv_finalize fun:_pthread_tsd_cleanup fun:_pthread_exit - fun:_pthread_body + ... fun:_pthread_start fun:thread_start } @@ -36,7 +36,7 @@ fun:tlv_finalize fun:_pthread_tsd_cleanup fun:_pthread_exit - fun:_pthread_body + ... fun:_pthread_start fun:thread_start } @@ -48,7 +48,7 @@ fun:tlv_finalize fun:_pthread_tsd_cleanup fun:_pthread_exit - fun:_pthread_body + ... fun:_pthread_start fun:thread_start } diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y index b310b2b3351c5..3aa76d168df07 100644 --- a/src/grammar/parser-lalr.y +++ b/src/grammar/parser-lalr.y @@ -1822,8 +1822,8 @@ unpaired_token | LIT_FLOAT { $$ = mk_atom(yytext); } | LIT_STR { $$ = mk_atom(yytext); } | LIT_STR_RAW { $$ = mk_atom(yytext); } -| LIT_BYTE_STR { $$ = mk_atom(yytext); } -| LIT_BYTE_STR_RAW { $$ = mk_atom(yytext); } +| LIT_BYTE_STR { $$ = mk_atom(yytext); } +| LIT_BYTE_STR_RAW { $$ = mk_atom(yytext); } | IDENT { $$ = mk_atom(yytext); } | UNDERSCORE { $$ = mk_atom(yytext); } | LIFETIME { $$ = mk_atom(yytext); } diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 755e44899fc86..169634a7c8255 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -79,6 +79,7 @@ use core::cmp::Ordering; use core::mem::{align_of_val, size_of_val}; use core::intrinsics::abort; use core::mem; +use core::mem::uninitialized; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; @@ -554,9 +555,9 @@ impl Drop for Arc { // This structure has #[unsafe_no_drop_flag], so this drop glue may run // more than once (but it is guaranteed to be zeroed after the first if // it's run more than once) - let ptr = *self._ptr; - // if ptr.is_null() { return } - if ptr as *mut u8 as usize == 0 || ptr as *mut u8 as usize == mem::POST_DROP_USIZE { + let thin = *self._ptr as *const (); + + if thin as usize == mem::POST_DROP_USIZE { return; } @@ -709,9 +710,10 @@ impl Drop for Weak { /// ``` fn drop(&mut self) { let ptr = *self._ptr; + let thin = ptr as *const (); // see comments above for why this check is here - if ptr as *mut u8 as usize == 0 || ptr as *mut u8 as usize == mem::POST_DROP_USIZE { + if thin as usize == mem::POST_DROP_USIZE { return; } @@ -904,6 +906,35 @@ impl From for Arc { } } +impl Weak { + /// Constructs a new `Weak` without an accompanying instance of T. + /// + /// This allocates memory for T, but does not initialize it. Calling + /// Weak::upgrade() on the return value always gives None. + /// + /// # Examples + /// + /// ``` + /// #![feature(downgraded_weak)] + /// + /// use std::sync::Weak; + /// + /// let empty: Weak = Weak::new(); + /// ``` + #[unstable(feature = "downgraded_weak", + reason = "recently added", + issue = "30425")] + pub fn new() -> Weak { + unsafe { + Weak { _ptr: Shared::new(Box::into_raw(box ArcInner { + strong: atomic::AtomicUsize::new(0), + weak: atomic::AtomicUsize::new(1), + data: uninitialized(), + }))} + } + } +} + #[cfg(test)] mod tests { use std::clone::Clone; @@ -1154,6 +1185,12 @@ mod tests { let foo_arc = Arc::from(foo); assert!(123 == *foo_arc); } + + #[test] + fn test_new_weak() { + let foo: Weak = Weak::new(); + assert!(foo.upgrade().is_none()); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 52f035b67bdc9..2c45e88bb24e8 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -163,7 +163,7 @@ use core::hash::{Hasher, Hash}; use core::intrinsics::{assume, abort}; use core::marker; use core::marker::Unsize; -use core::mem::{self, align_of_val, size_of_val, forget}; +use core::mem::{self, align_of_val, size_of_val, forget, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; @@ -449,8 +449,9 @@ impl Drop for Rc { fn drop(&mut self) { unsafe { let ptr = *self._ptr; - if !(*(&ptr as *const _ as *const *const ())).is_null() && - ptr as *const () as usize != mem::POST_DROP_USIZE { + let thin = ptr as *const (); + + if thin as usize != mem::POST_DROP_USIZE { self.dec_strong(); if self.strong() == 0 { // destroy the contained object @@ -782,8 +783,9 @@ impl Drop for Weak { fn drop(&mut self) { unsafe { let ptr = *self._ptr; - if !(*(&ptr as *const _ as *const *const ())).is_null() && - ptr as *const () as usize != mem::POST_DROP_USIZE { + let thin = ptr as *const (); + + if thin as usize != mem::POST_DROP_USIZE { self.dec_weak(); // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. @@ -824,6 +826,37 @@ impl fmt::Debug for Weak { } } +impl Weak { + /// Constructs a new `Weak` without an accompanying instance of T. + /// + /// This allocates memory for T, but does not initialize it. Calling + /// Weak::upgrade() on the return value always gives None. + /// + /// # Examples + /// + /// ``` + /// #![feature(downgraded_weak)] + /// + /// use std::rc::Weak; + /// + /// let empty: Weak = Weak::new(); + /// ``` + #[unstable(feature = "downgraded_weak", + reason = "recently added", + issue="30425")] + pub fn new() -> Weak { + unsafe { + Weak { + _ptr: Shared::new(Box::into_raw(box RcBox { + strong: Cell::new(0), + weak: Cell::new(1), + value: uninitialized(), + })), + } + } + } +} + // NOTE: We checked_add here to deal with mem::forget safety. In particular // if you mem::forget Rcs (or Weaks), the ref-count can overflow, and then // you can free the allocation while outstanding Rcs (or Weaks) exist. @@ -1116,6 +1149,12 @@ mod tests { let foo_rc = Rc::from(foo); assert!(123 == *foo_rc); } + + #[test] + fn test_new_weak() { + let foo: Weak = Weak::new(); + assert!(foo.upgrade().is_none()); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index de9c8a2feafb8..f87f5e6c2e6b7 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -1591,10 +1591,10 @@ impl BTreeMap { #[unstable(feature = "btree_range", reason = "matches collection reform specification, waiting for dust to settle", issue = "27787")] - pub fn range(&self, - min: Bound<&Min>, - max: Bound<&Max>) - -> Range + pub fn range(&self, + min: Bound<&Min>, + max: Bound<&Max>) + -> Range where K: Borrow + Borrow { range_impl!(&self.root, @@ -1633,10 +1633,10 @@ impl BTreeMap { #[unstable(feature = "btree_range", reason = "matches collection reform specification, waiting for dust to settle", issue = "27787")] - pub fn range_mut(&mut self, - min: Bound<&Min>, - max: Bound<&Max>) - -> RangeMut + pub fn range_mut(&mut self, + min: Bound<&Min>, + max: Bound<&Max>) + -> RangeMut where K: Borrow + Borrow { range_impl!(&mut self.root, diff --git a/src/libcollections/btree/set.rs b/src/libcollections/btree/set.rs index 12d3465e5188d..55e9e3a1c34bb 100644 --- a/src/libcollections/btree/set.rs +++ b/src/libcollections/btree/set.rs @@ -154,10 +154,10 @@ impl BTreeSet { #[unstable(feature = "btree_range", reason = "matches collection reform specification, waiting for dust to settle", issue = "27787")] - pub fn range<'a, Min: ?Sized + Ord = T, Max: ?Sized + Ord = T>(&'a self, - min: Bound<&Min>, - max: Bound<&Max>) - -> Range<'a, T> + pub fn range<'a, Min: ?Sized + Ord, Max: ?Sized + Ord>(&'a self, + min: Bound<&Min>, + max: Bound<&Max>) + -> Range<'a, T> where T: Borrow + Borrow { fn first((a, _): (A, B)) -> A { diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 000ec4f0f6610..370857287d6e6 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -46,7 +46,7 @@ #![feature(num_bits_bytes)] #![feature(oom)] #![feature(pattern)] -#![feature(ptr_as_ref)] +#![feature(shared)] #![feature(slice_bytes)] #![feature(slice_patterns)] #![feature(staged_api)] diff --git a/src/libcollections/linked_list.rs b/src/libcollections/linked_list.rs index 631857f8e3c56..1bd5a83d43708 100644 --- a/src/libcollections/linked_list.rs +++ b/src/libcollections/linked_list.rs @@ -27,7 +27,7 @@ use core::fmt; use core::hash::{Hasher, Hash}; use core::iter::FromIterator; use core::mem; -use core::ptr; +use core::ptr::Shared; /// A doubly-linked list. #[stable(feature = "rust1", since = "1.0.0")] @@ -40,7 +40,7 @@ pub struct LinkedList { type Link = Option>>; struct Rawlink { - p: *mut T, + p: Option>, } impl Copy for Rawlink {} @@ -93,12 +93,12 @@ pub struct IntoIter { impl Rawlink { /// Like Option::None for Rawlink fn none() -> Rawlink { - Rawlink { p: ptr::null_mut() } + Rawlink { p: None } } /// Like Option::Some for Rawlink fn some(n: &mut T) -> Rawlink { - Rawlink { p: n } + unsafe { Rawlink { p: Some(Shared::new(n)) } } } /// Convert the `Rawlink` into an Option value @@ -108,7 +108,7 @@ impl Rawlink { /// - Dereference of raw pointer. /// - Returns reference of arbitrary lifetime. unsafe fn resolve<'a>(&self) -> Option<&'a T> { - self.p.as_ref() + self.p.map(|p| &**p) } /// Convert the `Rawlink` into an Option value @@ -118,7 +118,7 @@ impl Rawlink { /// - Dereference of raw pointer. /// - Returns reference of arbitrary lifetime. unsafe fn resolve_mut<'a>(&mut self) -> Option<&'a mut T> { - self.p.as_mut() + self.p.map(|p| &mut **p) } /// Return the `Rawlink` and replace with `Rawlink::none()` @@ -984,6 +984,14 @@ impl Hash for LinkedList { } } +// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. +#[allow(dead_code)] +fn assert_covariance() { + fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { x } + fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { x } + fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { x } +} + #[cfg(test)] mod tests { use std::clone::Clone; diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index 68669b68d294a..92c23ef2f38b8 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -640,7 +640,7 @@ impl str { /// Value, and may not match your idea of what a 'character' is. Iteration /// over grapheme clusters may be what you actually want. /// - /// [`char`]: ../primitive.char.html + /// [`char`]: primitive.char.html /// /// # Examples /// @@ -1551,7 +1551,7 @@ impl str { /// The pattern can be a `&str`, [`char`], or a closure that determines /// if a character matches. /// - /// [`char`]: primtive.char.html + /// [`char`]: primitive.char.html /// /// # Examples /// @@ -1643,7 +1643,7 @@ impl str { /// /// `parse()` can parse any type that implements the [`FromStr`] trait. /// - /// [`FromStr`]: trait.FromStr.html + /// [`FromStr`]: str/trait.FromStr.html /// /// # Failure /// diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 931092f69242e..d2cbcad875f34 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -1029,8 +1029,8 @@ impl String { /// /// # Panics /// - /// Panics if `idx` is larger than the `String`'s length, or if it does not - /// lie on a [`char`] boundary. + /// Panics if `idx` is larger than or equal to the `String`'s length, + /// or if it does not lie on a [`char`] boundary. /// /// [`char`]: ../primitive.char.html /// @@ -1049,7 +1049,7 @@ impl String { #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(&mut self, idx: usize) -> char { let len = self.len(); - assert!(idx <= len); + assert!(idx < len); let ch = self.char_at(idx); let next = idx + ch.len_utf8(); diff --git a/src/libcore/char.rs b/src/libcore/char.rs index c02704217a8de..0fc154a0cd591 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -69,7 +69,7 @@ const MAX_THREE_B: u32 = 0x10000; /// Point], but only ones within a certain range. `MAX` is the highest valid /// code point that's a valid [Unicode Scalar Value]. /// -/// [`char`]: primitive.char.html +/// [`char`]: ../primitive.char.html /// [Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value /// [Code Point]: http://www.unicode.org/glossary/#code_point #[stable(feature = "rust1", since = "1.0.0")] @@ -91,9 +91,9 @@ pub const MAX: char = '\u{10ffff}'; /// [`char`]s. `from_u32()` will return `None` if the input is not a valid value /// for a [`char`]. /// -/// [`char`]: primitive.char.html -/// [`u32`]: primitive.u32.html -/// [`as`]: ../book/casting-between-types.html#as +/// [`char`]: ../primitive.char.html +/// [`u32`]: ../primitive.u32.html +/// [`as`]: ../../book/casting-between-types.html#as /// /// For an unsafe version of this function which ignores these checks, see /// [`from_u32_unchecked()`]. @@ -148,9 +148,9 @@ pub fn from_u32(i: u32) -> Option { /// [`char`]s. `from_u32_unchecked()` will ignore this, and blindly cast to /// [`char`], possibly creating an invalid one. /// -/// [`char`]: primitive.char.html -/// [`u32`]: primitive.u32.html -/// [`as`]: ../book/casting-between-types.html#as +/// [`char`]: ../primitive.char.html +/// [`u32`]: ../primitive.u32.html +/// [`as`]: ../../book/casting-between-types.html#as /// /// # Safety /// @@ -181,7 +181,7 @@ pub unsafe fn from_u32_unchecked(i: u32) -> char { /// /// A 'radix' here is sometimes also called a 'base'. A radix of two /// indicates a binary number, a radix of ten, decimal, and a radix of -/// sixteen, hexicdecimal, to give some common values. Arbitrary +/// sixteen, hexadecimal, to give some common values. Arbitrary /// radicum are supported. /// /// `from_digit()` will return `None` if the input is not a digit in @@ -414,8 +414,8 @@ pub fn encode_utf16_raw(mut ch: u32, dst: &mut [u16]) -> Option { /// This `struct` is created by the [`escape_unicode()`] method on [`char`]. See /// its documentation for more. /// -/// [`escape_unicode()`]: primitive.char.html#method.escape_unicode -/// [`char`]: primitive.char.html +/// [`escape_unicode()`]: ../primitive.char.html#method.escape_unicode +/// [`char`]: ../primitive.char.html #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct EscapeUnicode { @@ -494,8 +494,8 @@ impl Iterator for EscapeUnicode { /// This `struct` is created by the [`escape_default()`] method on [`char`]. See /// its documentation for more. /// -/// [`escape_default()`]: primitive.char.html#method.escape_default -/// [`char`]: primitive.char.html +/// [`escape_default()`]: ../primitive.char.html#method.escape_default +/// [`char`]: ../primitive.char.html #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct EscapeDefault { diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 04676c0c9c8b4..628bf654873c7 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -356,7 +356,7 @@ impl<'a> Display for Arguments<'a> { /// `Debug` implementations using either `derive` or the debug builder API /// on `Formatter` support pretty printing using the alternate flag: `{:#?}`. /// -/// [debug_struct]: ../std/fmt/struct.Formatter.html#method.debug_struct +/// [debug_struct]: ../../std/fmt/struct.Formatter.html#method.debug_struct /// /// Pretty printing with `#?`: /// diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index f063c6b06767b..a30e5b1372af9 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -604,7 +604,7 @@ pub trait Iterator { /// iterators, returning a tuple where the first element comes from the /// first iterator, and the second element comes from the second iterator. /// - /// In other words, it zips two iterators together, into a single one. 🤐 + /// In other words, it zips two iterators together, into a single one. /// /// When either iterator returns `None`, all further calls to `next()` /// will return `None`. @@ -1113,16 +1113,22 @@ pub trait Iterator { Take{iter: self, n: n} } - /// An iterator similar to `fold()`, with internal state. - /// - /// `scan()` accumulates a final value, similar to [`fold()`], but instead - /// of passing along an accumulator, it maintains the accumulator internally. + /// An iterator adaptor similar to [`fold()`] that holds internal state and + /// produces a new iterator. /// /// [`fold()`]: #method.fold /// - /// On each iteraton of `scan()`, you can assign to the internal state, and - /// a mutable reference to the state is passed as the first argument to the - /// closure, allowing you to modify it on each iteration. + /// `scan()` takes two arguments: an initial value which seeds the internal + /// state, and a closure with two arguments, the first being a mutable + /// reference to the internal state and the second an iterator element. + /// The closure can assign to the internal state to share state between + /// iterations. + /// + /// On iteration, the closure will be applied to each element of the + /// iterator and the return value from the closure, an [`Option`], is + /// yielded by the iterator. + /// + /// [`Option`]: ../option/enum.Option.html /// /// # Examples /// @@ -2126,7 +2132,7 @@ pub trait Iterator { /// ``` #[unstable(feature = "iter_arith", reason = "bounds recently changed", issue = "27739")] - fn sum::Item>(self) -> S where + fn sum(self) -> S where S: Add + Zero, Self: Sized, { @@ -2151,7 +2157,7 @@ pub trait Iterator { /// ``` #[unstable(feature="iter_arith", reason = "bounds recently changed", issue = "27739")] - fn product::Item>(self) -> P where + fn product

(self) -> P where P: Mul + One, Self: Sized, { diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 03d3cb11b3ed3..154ca30c62dd1 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -135,10 +135,10 @@ macro_rules! debug_assert { ($($arg:tt)*) => (if cfg!(debug_assertions) { assert!($($arg)*); }) } -/// Asserts that two expressions are equal to each other, testing equality in -/// both directions. +/// Asserts that two expressions are equal to each other. /// -/// On panic, this macro will print the values of the expressions. +/// On panic, this macro will print the values of the expressions with their +/// debug representations. /// /// Unlike `assert_eq!`, `debug_assert_eq!` statements are only enabled in non /// optimized builds by default. An optimized build will omit all diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index b584e59a825ff..621dce3efc86b 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -24,6 +24,8 @@ use hash::Hash; use hash::Hasher; /// Types that can be transferred across thread boundaries. +/// +/// This trait is automatically derived when the compiler determines it's appropriate. #[stable(feature = "rust1", since = "1.0.0")] #[lang = "send"] #[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"] @@ -219,6 +221,8 @@ pub trait Copy : Clone { /// wrapper around the value(s) which can be mutated when behind a `&` /// reference; not doing this is undefined behavior (for example, /// `transmute`-ing from `&T` to `&mut T` is invalid). +/// +/// This trait is automatically derived when the compiler determines it's appropriate. #[stable(feature = "rust1", since = "1.0.0")] #[lang = "sync"] #[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"] @@ -291,6 +295,10 @@ macro_rules! impls{ /// even though it does not. This allows you to inform the compiler about certain safety properties /// of your code. /// +/// For a more in-depth explanation of how to use `PhantomData`, please see [the Nomicon]. +/// +/// [the Nomicon]: ../../nomicon/phantom-data.html +/// /// # A ghastly note 👻👻👻 /// /// Though they both have scary names, `PhantomData` and 'phantom types' are related, but not diff --git a/src/libcore/num/dec2flt/parse.rs b/src/libcore/num/dec2flt/parse.rs index 414bcc874eac1..fce1c250a022e 100644 --- a/src/libcore/num/dec2flt/parse.rs +++ b/src/libcore/num/dec2flt/parse.rs @@ -56,27 +56,28 @@ pub enum ParseResult<'a> { /// Check if the input string is a valid floating point number and if so, locate the integral /// part, the fractional part, and the exponent in it. Does not handle signs. pub fn parse_decimal(s: &str) -> ParseResult { + if s.is_empty() { + return Invalid; + } + let s = s.as_bytes(); let (integral, s) = eat_digits(s); + match s.first() { - None => { - if integral.is_empty() { - return Invalid; // No digits at all - } - Valid(Decimal::new(integral, b"", 0)) - } + None => Valid(Decimal::new(integral, b"", 0)), Some(&b'e') | Some(&b'E') => { if integral.is_empty() { return Invalid; // No digits before 'e' } + parse_exp(integral, b"", &s[1..]) } Some(&b'.') => { let (fractional, s) = eat_digits(&s[1..]); if integral.is_empty() && fractional.is_empty() && s.is_empty() { - // For historic reasons "." is a valid input. - return Valid(Decimal::new(b"", b"", 0)); + return Invalid; } + match s.first() { None => Valid(Decimal::new(integral, fractional, 0)), Some(&b'e') | Some(&b'E') => parse_exp(integral, fractional, &s[1..]), diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index a7d5fcafd5603..8f9e38bbdf9cf 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs @@ -12,13 +12,13 @@ #![unstable(feature = "wrapping", reason = "may be removed or relocated", issue = "27755")] -pub use intrinsics::{add_with_overflow, sub_with_overflow, mul_with_overflow}; +use intrinsics::{add_with_overflow, sub_with_overflow, mul_with_overflow}; use super::Wrapping; use ops::*; -use ::{i8,i16,i32,i64}; +use ::{i8, i16, i32, i64, isize}; pub trait OverflowingOps { fn overflowing_add(self, rhs: Self) -> (Self, bool); @@ -33,15 +33,71 @@ pub trait OverflowingOps { fn overflowing_shr(self, rhs: u32) -> (Self, bool); } -macro_rules! sh_impl { - ($t:ty, $f:ty) => ( +macro_rules! sh_impl_signed { + ($t:ident, $f:ident) => ( #[stable(feature = "rust1", since = "1.0.0")] impl Shl<$f> for Wrapping<$t> { type Output = Wrapping<$t>; #[inline(always)] fn shl(self, other: $f) -> Wrapping<$t> { - Wrapping(self.0 << other) + if other < 0 { + Wrapping(self.0.wrapping_shr((-other & self::shift_max::$t as $f) as u32)) + } else { + Wrapping(self.0.wrapping_shl((other & self::shift_max::$t as $f) as u32)) + } + } + } + + #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")] + impl ShlAssign<$f> for Wrapping<$t> { + #[inline(always)] + fn shl_assign(&mut self, other: $f) { + *self = *self << other; + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Shr<$f> for Wrapping<$t> { + type Output = Wrapping<$t>; + + #[inline(always)] + fn shr(self, other: $f) -> Wrapping<$t> { + if other < 0 { + Wrapping(self.0.wrapping_shl((-other & self::shift_max::$t as $f) as u32)) + } else { + Wrapping(self.0.wrapping_shr((other & self::shift_max::$t as $f) as u32)) + } + } + } + + #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")] + impl ShrAssign<$f> for Wrapping<$t> { + #[inline(always)] + fn shr_assign(&mut self, other: $f) { + *self = *self >> other; + } + } + ) +} + +macro_rules! sh_impl_unsigned { + ($t:ident, $f:ident) => ( + #[stable(feature = "rust1", since = "1.0.0")] + impl Shl<$f> for Wrapping<$t> { + type Output = Wrapping<$t>; + + #[inline(always)] + fn shl(self, other: $f) -> Wrapping<$t> { + Wrapping(self.0.wrapping_shl((other & self::shift_max::$t as $f) as u32)) + } + } + + #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")] + impl ShlAssign<$f> for Wrapping<$t> { + #[inline(always)] + fn shl_assign(&mut self, other: $f) { + *self = *self << other; } } @@ -51,7 +107,15 @@ macro_rules! sh_impl { #[inline(always)] fn shr(self, other: $f) -> Wrapping<$t> { - Wrapping(self.0 >> other) + Wrapping(self.0.wrapping_shr((other & self::shift_max::$t as $f) as u32)) + } + } + + #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")] + impl ShrAssign<$f> for Wrapping<$t> { + #[inline(always)] + fn shr_assign(&mut self, other: $f) { + *self = *self >> other; } } ) @@ -59,23 +123,24 @@ macro_rules! sh_impl { // FIXME (#23545): uncomment the remaining impls macro_rules! sh_impl_all { - ($($t:ty)*) => ($( - // sh_impl! { $t, u8 } - // sh_impl! { $t, u16 } - // sh_impl! { $t, u32 } - // sh_impl! { $t, u64 } - sh_impl! { $t, usize } - - // sh_impl! { $t, i8 } - // sh_impl! { $t, i16 } - // sh_impl! { $t, i32 } - // sh_impl! { $t, i64 } - // sh_impl! { $t, isize } + ($($t:ident)*) => ($( + //sh_impl_unsigned! { $t, u8 } + //sh_impl_unsigned! { $t, u16 } + //sh_impl_unsigned! { $t, u32 } + //sh_impl_unsigned! { $t, u64 } + sh_impl_unsigned! { $t, usize } + + //sh_impl_signed! { $t, i8 } + //sh_impl_signed! { $t, i16 } + //sh_impl_signed! { $t, i32 } + //sh_impl_signed! { $t, i64 } + //sh_impl_signed! { $t, isize } )*) } sh_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize } +// FIXME(30524): impl Op for Wrapping, impl OpAssign for Wrapping macro_rules! wrapping_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] @@ -88,6 +153,14 @@ macro_rules! wrapping_impl { } } + #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")] + impl AddAssign for Wrapping<$t> { + #[inline(always)] + fn add_assign(&mut self, other: Wrapping<$t>) { + *self = *self + other; + } + } + #[stable(feature = "rust1", since = "1.0.0")] impl Sub for Wrapping<$t> { type Output = Wrapping<$t>; @@ -98,6 +171,14 @@ macro_rules! wrapping_impl { } } + #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")] + impl SubAssign for Wrapping<$t> { + #[inline(always)] + fn sub_assign(&mut self, other: Wrapping<$t>) { + *self = *self - other; + } + } + #[stable(feature = "rust1", since = "1.0.0")] impl Mul for Wrapping<$t> { type Output = Wrapping<$t>; @@ -108,6 +189,14 @@ macro_rules! wrapping_impl { } } + #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")] + impl MulAssign for Wrapping<$t> { + #[inline(always)] + fn mul_assign(&mut self, other: Wrapping<$t>) { + *self = *self * other; + } + } + #[stable(feature = "wrapping_div", since = "1.3.0")] impl Div for Wrapping<$t> { type Output = Wrapping<$t>; @@ -118,6 +207,32 @@ macro_rules! wrapping_impl { } } + #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")] + impl DivAssign for Wrapping<$t> { + #[inline(always)] + fn div_assign(&mut self, other: Wrapping<$t>) { + *self = *self / other; + } + } + + #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")] + impl Rem for Wrapping<$t> { + type Output = Wrapping<$t>; + + #[inline(always)] + fn rem(self, other: Wrapping<$t>) -> Wrapping<$t> { + Wrapping(self.0.wrapping_rem(other.0)) + } + } + + #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")] + impl RemAssign for Wrapping<$t> { + #[inline(always)] + fn rem_assign(&mut self, other: Wrapping<$t>) { + *self = *self % other; + } + } + #[stable(feature = "rust1", since = "1.0.0")] impl Not for Wrapping<$t> { type Output = Wrapping<$t>; @@ -138,6 +253,14 @@ macro_rules! wrapping_impl { } } + #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")] + impl BitXorAssign for Wrapping<$t> { + #[inline(always)] + fn bitxor_assign(&mut self, other: Wrapping<$t>) { + *self = *self ^ other; + } + } + #[stable(feature = "rust1", since = "1.0.0")] impl BitOr for Wrapping<$t> { type Output = Wrapping<$t>; @@ -148,6 +271,14 @@ macro_rules! wrapping_impl { } } + #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")] + impl BitOrAssign for Wrapping<$t> { + #[inline(always)] + fn bitor_assign(&mut self, other: Wrapping<$t>) { + *self = *self | other; + } + } + #[stable(feature = "rust1", since = "1.0.0")] impl BitAnd for Wrapping<$t> { type Output = Wrapping<$t>; @@ -157,6 +288,14 @@ macro_rules! wrapping_impl { Wrapping(self.0 & other.0) } } + + #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")] + impl BitAndAssign for Wrapping<$t> { + #[inline(always)] + fn bitand_assign(&mut self, other: Wrapping<$t>) { + *self = *self & other; + } + } )*) } @@ -165,15 +304,29 @@ wrapping_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 } mod shift_max { #![allow(non_upper_case_globals)] + #[cfg(target_pointer_width = "32")] + mod platform { + pub const usize: u32 = super::u32; + pub const isize: u32 = super::i32; + } + + #[cfg(target_pointer_width = "64")] + mod platform { + pub const usize: u32 = super::u64; + pub const isize: u32 = super::i64; + } + pub const i8: u32 = (1 << 3) - 1; pub const i16: u32 = (1 << 4) - 1; pub const i32: u32 = (1 << 5) - 1; pub const i64: u32 = (1 << 6) - 1; + pub use self::platform::isize; pub const u8: u32 = i8; pub const u16: u32 = i16; pub const u32: u32 = i32; pub const u64: u32 = i64; + pub use self::platform::usize; } macro_rules! signed_overflowing_impl { @@ -288,193 +441,5 @@ macro_rules! unsigned_overflowing_impl { )*) } -signed_overflowing_impl! { i8 i16 i32 i64 } -unsigned_overflowing_impl! { u8 u16 u32 u64 } - -#[cfg(target_pointer_width = "64")] -impl OverflowingOps for usize { - #[inline(always)] - fn overflowing_add(self, rhs: usize) -> (usize, bool) { - unsafe { - add_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_sub(self, rhs: usize) -> (usize, bool) { - unsafe { - sub_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_mul(self, rhs: usize) -> (usize, bool) { - unsafe { - mul_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_div(self, rhs: usize) -> (usize, bool) { - let (r, f) = (self as u64).overflowing_div(rhs as u64); - (r as usize, f) - } - #[inline(always)] - fn overflowing_rem(self, rhs: usize) -> (usize, bool) { - let (r, f) = (self as u64).overflowing_rem(rhs as u64); - (r as usize, f) - } - #[inline(always)] - fn overflowing_neg(self) -> (usize, bool) { - let (r, f) = (self as u64).overflowing_neg(); - (r as usize, f) - } - #[inline(always)] - fn overflowing_shl(self, rhs: u32) -> (usize, bool) { - let (r, f) = (self as u64).overflowing_shl(rhs); - (r as usize, f) - } - #[inline(always)] - fn overflowing_shr(self, rhs: u32) -> (usize, bool) { - let (r, f) = (self as u64).overflowing_shr(rhs); - (r as usize, f) - } -} - -#[cfg(target_pointer_width = "32")] -impl OverflowingOps for usize { - #[inline(always)] - fn overflowing_add(self, rhs: usize) -> (usize, bool) { - unsafe { - add_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_sub(self, rhs: usize) -> (usize, bool) { - unsafe { - sub_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_mul(self, rhs: usize) -> (usize, bool) { - unsafe { - mul_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_div(self, rhs: usize) -> (usize, bool) { - let (r, f) = (self as u32).overflowing_div(rhs as u32); - (r as usize, f) - } - #[inline(always)] - fn overflowing_rem(self, rhs: usize) -> (usize, bool) { - let (r, f) = (self as u32).overflowing_rem(rhs as u32); - (r as usize, f) - } - #[inline(always)] - fn overflowing_neg(self) -> (usize, bool) { - let (r, f) = (self as u32).overflowing_neg(); - (r as usize, f) - } - #[inline(always)] - fn overflowing_shl(self, rhs: u32) -> (usize, bool) { - let (r, f) = (self as u32).overflowing_shl(rhs); - (r as usize, f) - } - #[inline(always)] - fn overflowing_shr(self, rhs: u32) -> (usize, bool) { - let (r, f) = (self as u32).overflowing_shr(rhs); - (r as usize, f) - } -} - -#[cfg(target_pointer_width = "64")] -impl OverflowingOps for isize { - #[inline(always)] - fn overflowing_add(self, rhs: isize) -> (isize, bool) { - unsafe { - add_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_sub(self, rhs: isize) -> (isize, bool) { - unsafe { - sub_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_mul(self, rhs: isize) -> (isize, bool) { - unsafe { - mul_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_div(self, rhs: isize) -> (isize, bool) { - let (r, f) = (self as i64).overflowing_div(rhs as i64); - (r as isize, f) - } - #[inline(always)] - fn overflowing_rem(self, rhs: isize) -> (isize, bool) { - let (r, f) = (self as i64).overflowing_rem(rhs as i64); - (r as isize, f) - } - #[inline(always)] - fn overflowing_neg(self) -> (isize, bool) { - let (r, f) = (self as i64).overflowing_neg(); - (r as isize, f) - } - #[inline(always)] - fn overflowing_shl(self, rhs: u32) -> (isize, bool) { - let (r, f) = (self as i64).overflowing_shl(rhs); - (r as isize, f) - } - #[inline(always)] - fn overflowing_shr(self, rhs: u32) -> (isize, bool) { - let (r, f) = (self as i64).overflowing_shr(rhs); - (r as isize, f) - } -} - -#[cfg(target_pointer_width = "32")] -impl OverflowingOps for isize { - #[inline(always)] - fn overflowing_add(self, rhs: isize) -> (isize, bool) { - unsafe { - add_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_sub(self, rhs: isize) -> (isize, bool) { - unsafe { - sub_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_mul(self, rhs: isize) -> (isize, bool) { - unsafe { - mul_with_overflow(self, rhs) - } - } - #[inline(always)] - fn overflowing_div(self, rhs: isize) -> (isize, bool) { - let (r, f) = (self as i32).overflowing_div(rhs as i32); - (r as isize, f) - } - #[inline(always)] - fn overflowing_rem(self, rhs: isize) -> (isize, bool) { - let (r, f) = (self as i32).overflowing_rem(rhs as i32); - (r as isize, f) - } - #[inline(always)] - fn overflowing_neg(self) -> (isize, bool) { - let (r, f) = (self as i32).overflowing_neg(); - (r as isize, f) - } - #[inline(always)] - fn overflowing_shl(self, rhs: u32) -> (isize, bool) { - let (r, f) = (self as i32).overflowing_shl(rhs); - (r as isize, f) - } - #[inline(always)] - fn overflowing_shr(self, rhs: u32) -> (isize, bool) { - let (r, f) = (self as i32).overflowing_shr(rhs); - (r as isize, f) - } -} +signed_overflowing_impl! { i8 i16 i32 i64 isize } +unsigned_overflowing_impl! { u8 u16 u32 u64 usize } diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index edbe64db08086..6144f97b13080 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -38,7 +38,7 @@ //! #[derive(Debug)] //! struct Point { //! x: i32, -//! y: i32 +//! y: i32, //! } //! //! impl Add for Point { @@ -161,7 +161,6 @@ macro_rules! forward_ref_binop { /// ``` /// use std::ops::Add; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Add for Foo { @@ -215,7 +214,6 @@ add_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// ``` /// use std::ops::Sub; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Sub for Foo { @@ -269,7 +267,6 @@ sub_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// ``` /// use std::ops::Mul; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Mul for Foo { @@ -323,7 +320,6 @@ mul_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// ``` /// use std::ops::Div; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Div for Foo { @@ -395,7 +391,6 @@ div_impl_float! { f32 f64 } /// ``` /// use std::ops::Rem; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Rem for Foo { @@ -467,7 +462,6 @@ rem_impl_float! { f32 f64 } /// ``` /// use std::ops::Neg; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Neg for Foo { @@ -535,7 +529,6 @@ neg_impl_numeric! { isize i8 i16 i32 i64 f32 f64 } /// ``` /// use std::ops::Not; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Not for Foo { @@ -589,7 +582,6 @@ not_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 } /// ``` /// use std::ops::BitAnd; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl BitAnd for Foo { @@ -643,7 +635,6 @@ bitand_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 } /// ``` /// use std::ops::BitOr; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl BitOr for Foo { @@ -697,7 +688,6 @@ bitor_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 } /// ``` /// use std::ops::BitXor; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl BitXor for Foo { @@ -751,7 +741,6 @@ bitxor_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 } /// ``` /// use std::ops::Shl; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Shl for Foo { @@ -823,7 +812,6 @@ shl_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize } /// ``` /// use std::ops::Shr; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl Shr for Foo { @@ -898,7 +886,6 @@ shr_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize } /// /// use std::ops::AddAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl AddAssign for Foo { @@ -945,7 +932,6 @@ add_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// /// use std::ops::SubAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl SubAssign for Foo { @@ -992,7 +978,6 @@ sub_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// /// use std::ops::MulAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl MulAssign for Foo { @@ -1039,7 +1024,6 @@ mul_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// /// use std::ops::DivAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl DivAssign for Foo { @@ -1086,7 +1070,6 @@ div_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// /// use std::ops::RemAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl RemAssign for Foo { @@ -1133,7 +1116,6 @@ rem_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 } /// /// use std::ops::BitAndAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl BitAndAssign for Foo { @@ -1180,7 +1162,6 @@ bitand_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 } /// /// use std::ops::BitOrAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl BitOrAssign for Foo { @@ -1227,7 +1208,6 @@ bitor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 } /// /// use std::ops::BitXorAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl BitXorAssign for Foo { @@ -1274,7 +1254,6 @@ bitxor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 } /// /// use std::ops::ShlAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl ShlAssign for Foo { @@ -1339,7 +1318,6 @@ shl_assign_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize } /// /// use std::ops::ShrAssign; /// -/// #[derive(Copy, Clone)] /// struct Foo; /// /// impl ShrAssign for Foo { diff --git a/src/libcoretest/num/dec2flt/mod.rs b/src/libcoretest/num/dec2flt/mod.rs index 0c92b2fe2a7dc..7b25333e21ed2 100644 --- a/src/libcoretest/num/dec2flt/mod.rs +++ b/src/libcoretest/num/dec2flt/mod.rs @@ -98,7 +98,8 @@ fn fast_path_correct() { #[test] fn lonely_dot() { - assert_eq!(".".parse(), Ok(0.0)); + assert!(".".parse::().is_err()); + assert!(".".parse::().is_err()); } #[test] diff --git a/src/librbml/leb128.rs b/src/librbml/leb128.rs new file mode 100644 index 0000000000000..a5e8522623a2e --- /dev/null +++ b/src/librbml/leb128.rs @@ -0,0 +1,162 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[inline] +pub fn write_to_vec(vec: &mut Vec, position: &mut usize, byte: u8) +{ + if *position == vec.len() { + vec.push(byte); + } else { + vec[*position] = byte; + } + + *position += 1; +} + +pub fn write_unsigned_leb128(out: &mut Vec, + start_position: usize, + mut value: u64) + -> usize { + let mut position = start_position; + loop + { + let mut byte = (value & 0x7F) as u8; + value >>= 7; + if value != 0 { + byte |= 0x80; + } + + write_to_vec(out, &mut position, byte); + + if value == 0 { + break; + } + } + + return position - start_position; +} + +pub fn read_unsigned_leb128(data: &[u8], + start_position: usize) + -> (u64, usize) { + let mut result = 0; + let mut shift = 0; + let mut position = start_position; + loop { + let byte = data[position]; + position += 1; + result |= ((byte & 0x7F) as u64) << shift; + if (byte & 0x80) == 0 { + break; + } + shift += 7; + } + + (result, position - start_position) +} + + +pub fn write_signed_leb128(out: &mut Vec, + start_position: usize, + mut value: i64) -> usize { + let mut position = start_position; + + loop { + let mut byte = (value as u8) & 0x7f; + value >>= 7; + let more = !((((value == 0 ) && ((byte & 0x40) == 0)) || + ((value == -1) && ((byte & 0x40) != 0)))); + if more { + byte |= 0x80; // Mark this byte to show that more bytes will follow. + } + + write_to_vec(out, &mut position, byte); + + if !more { + break; + } + } + + return position - start_position; +} + +pub fn read_signed_leb128(data: &[u8], + start_position: usize) + -> (i64, usize) { + let mut result = 0; + let mut shift = 0; + let mut position = start_position; + let mut byte; + + loop { + byte = data[position]; + position += 1; + result |= ((byte & 0x7F) as i64) << shift; + shift += 7; + + if (byte & 0x80) == 0 { + break; + } + } + + if (shift < 64) && ((byte & 0x40) != 0) { + /* sign extend */ + result |= -(1i64 << shift); + } + + (result, position - start_position) +} + +#[test] +fn test_unsigned_leb128() { + let mut stream = Vec::with_capacity(10000); + + for x in 0..62 { + let pos = stream.len(); + let bytes_written = write_unsigned_leb128(&mut stream, pos, 3 << x); + assert_eq!(stream.len(), pos + bytes_written); + } + + let mut position = 0; + for x in 0..62 { + let expected = 3 << x; + let (actual, bytes_read) = read_unsigned_leb128(&stream, position); + assert_eq!(expected, actual); + position += bytes_read; + } + assert_eq!(stream.len(), position); +} + +#[test] +fn test_signed_leb128() { + let mut values = Vec::new(); + + let mut i = -500; + while i < 500 { + values.push(i * 123457i64); + i += 1; + } + + let mut stream = Vec::new(); + + for &x in &values { + let pos = stream.len(); + let bytes_written = write_signed_leb128(&mut stream, pos, x); + assert_eq!(stream.len(), pos + bytes_written); + } + + let mut pos = 0; + for &x in &values { + let (value, bytes_read) = read_signed_leb128(&mut stream, pos); + pos += bytes_read; + assert_eq!(x, value); + } + assert_eq!(pos, stream.len()); +} diff --git a/src/librbml/lib.rs b/src/librbml/lib.rs index d8d6ea93f7569..f3a1bbd7e26b8 100644 --- a/src/librbml/lib.rs +++ b/src/librbml/lib.rs @@ -128,10 +128,17 @@ #![cfg_attr(test, feature(test))] extern crate serialize; + +#[cfg(test)] +extern crate serialize as rustc_serialize; // Used by RustcEncodable + #[macro_use] extern crate log; #[cfg(test)] extern crate test; +pub mod opaque; +pub mod leb128; + pub use self::EbmlEncoderTag::*; pub use self::Error::*; @@ -241,6 +248,7 @@ pub mod reader { use serialize; + use super::opaque; use super::{ ApplicationError, EsVec, EsMap, EsEnum, EsSub8, EsSub32, EsVecElt, EsMapKey, EsU64, EsU32, EsU16, EsU8, EsI64, EsI32, EsI16, EsI8, EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal, @@ -621,18 +629,16 @@ pub mod reader { } pub fn read_opaque(&mut self, op: F) -> DecodeResult where - F: FnOnce(&mut Decoder, Doc) -> DecodeResult, + F: FnOnce(&mut opaque::Decoder, Doc) -> DecodeResult, { let doc = try!(self.next_doc(EsOpaque)); - let (old_parent, old_pos) = (self.parent, self.pos); - self.parent = doc; - self.pos = doc.start; - - let result = try!(op(self, doc)); + let result = { + let mut opaque_decoder = opaque::Decoder::new(doc.data, + doc.start); + try!(op(&mut opaque_decoder, doc)) + }; - self.parent = old_parent; - self.pos = old_pos; Ok(result) } @@ -877,6 +883,7 @@ pub mod writer { use std::io::prelude::*; use std::io::{self, SeekFrom, Cursor}; + use super::opaque; use super::{ EsVec, EsMap, EsEnum, EsSub8, EsSub32, EsVecElt, EsMapKey, EsU64, EsU32, EsU16, EsU8, EsI64, EsI32, EsI16, EsI8, EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal, @@ -1120,10 +1127,16 @@ pub mod writer { } pub fn emit_opaque(&mut self, f: F) -> EncodeResult where - F: FnOnce(&mut Encoder) -> EncodeResult, + F: FnOnce(&mut opaque::Encoder) -> EncodeResult, { try!(self.start_tag(EsOpaque as usize)); - try!(f(self)); + + { + let mut opaque_encoder = opaque::Encoder::new(self.writer); + try!(f(&mut opaque_encoder)); + } + + self.mark_stable_position(); self.end_tag() } } diff --git a/src/librbml/opaque.rs b/src/librbml/opaque.rs new file mode 100644 index 0000000000000..64756090e8b48 --- /dev/null +++ b/src/librbml/opaque.rs @@ -0,0 +1,811 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use Error as DecodeError; +use writer::EncodeResult; +use leb128::{read_signed_leb128, read_unsigned_leb128, write_signed_leb128, + write_unsigned_leb128}; +use std::io::{self, Write}; +use serialize; + +//=----------------------------------------------------------------------------- +// Encoder +//=----------------------------------------------------------------------------- + +pub struct Encoder<'a> { + pub cursor: &'a mut io::Cursor>, +} + +impl<'a> Encoder<'a> { + pub fn new(cursor: &'a mut io::Cursor>) -> Encoder<'a> { + Encoder { + cursor: cursor + } + } +} + + +macro_rules! write_uleb128 { + ($enc:expr, $value:expr) => {{ + let pos = $enc.cursor.position() as usize; + let bytes_written = write_unsigned_leb128($enc.cursor.get_mut(), pos, $value as u64); + $enc.cursor.set_position((pos + bytes_written) as u64); + Ok(()) + }} +} + +macro_rules! write_sleb128 { + ($enc:expr, $value:expr) => {{ + let pos = $enc.cursor.position() as usize; + let bytes_written = write_signed_leb128($enc.cursor.get_mut(), pos, $value as i64); + $enc.cursor.set_position((pos + bytes_written) as u64); + Ok(()) + }} +} + +impl<'a> serialize::Encoder for Encoder<'a> { + type Error = io::Error; + + fn emit_nil(&mut self) -> EncodeResult { + Ok(()) + } + + fn emit_uint(&mut self, v: usize) -> EncodeResult { + write_uleb128!(self, v) + } + + fn emit_u64(&mut self, v: u64) -> EncodeResult { + write_uleb128!(self, v) + } + + fn emit_u32(&mut self, v: u32) -> EncodeResult { + write_uleb128!(self, v) + } + + fn emit_u16(&mut self, v: u16) -> EncodeResult { + write_uleb128!(self, v) + } + + fn emit_u8(&mut self, v: u8) -> EncodeResult { + let _ = self.cursor.write_all(&[v]); + Ok(()) + } + + fn emit_int(&mut self, v: isize) -> EncodeResult { + write_sleb128!(self, v) + } + + fn emit_i64(&mut self, v: i64) -> EncodeResult { + write_sleb128!(self, v) + } + + fn emit_i32(&mut self, v: i32) -> EncodeResult { + write_sleb128!(self, v) + } + + fn emit_i16(&mut self, v: i16) -> EncodeResult { + write_sleb128!(self, v) + } + + fn emit_i8(&mut self, v: i8) -> EncodeResult { + let as_u8: u8 = unsafe { ::std::mem::transmute(v) }; + let _ = self.cursor.write_all(&[as_u8]); + Ok(()) + } + + fn emit_bool(&mut self, v: bool) -> EncodeResult { + self.emit_u8(if v { 1 } else { 0 }) + } + + fn emit_f64(&mut self, v: f64) -> EncodeResult { + let as_u64: u64 = unsafe { ::std::mem::transmute(v) }; + self.emit_u64(as_u64) + } + + fn emit_f32(&mut self, v: f32) -> EncodeResult { + let as_u32: u32 = unsafe { ::std::mem::transmute(v) }; + self.emit_u32(as_u32) + } + + fn emit_char(&mut self, v: char) -> EncodeResult { + self.emit_u32(v as u32) + } + + fn emit_str(&mut self, v: &str) -> EncodeResult { + try!(self.emit_uint(v.len())); + let _ = self.cursor.write_all(v.as_bytes()); + Ok(()) + } + + fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult + where F: FnOnce(&mut Self) -> EncodeResult { + f(self) + } + + fn emit_enum_variant(&mut self, + _v_name: &str, + v_id: usize, + _len: usize, + f: F) -> EncodeResult + where F: FnOnce(&mut Self) -> EncodeResult + { + try!(self.emit_uint(v_id)); + f(self) + } + + fn emit_enum_variant_arg(&mut self, _: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + f(self) + } + + fn emit_enum_struct_variant(&mut self, + v_name: &str, + v_id: usize, + cnt: usize, + f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + self.emit_enum_variant(v_name, v_id, cnt, f) + } + + fn emit_enum_struct_variant_field(&mut self, + _: &str, + idx: usize, + f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + self.emit_enum_variant_arg(idx, f) + } + + fn emit_struct(&mut self, _: &str, _len: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + f(self) + } + + fn emit_struct_field(&mut self, _name: &str, _: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + f(self) + } + + fn emit_tuple(&mut self, len: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + self.emit_seq(len, f) + } + + fn emit_tuple_arg(&mut self, idx: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + self.emit_seq_elt(idx, f) + } + + fn emit_tuple_struct(&mut self, _: &str, len: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + self.emit_seq(len, f) + } + + fn emit_tuple_struct_arg(&mut self, idx: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + self.emit_seq_elt(idx, f) + } + + fn emit_option(&mut self, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + self.emit_enum("Option", f) + } + + fn emit_option_none(&mut self) -> EncodeResult { + self.emit_enum_variant("None", 0, 0, |_| Ok(())) + } + + fn emit_option_some(&mut self, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + self.emit_enum_variant("Some", 1, 1, f) + } + + fn emit_seq(&mut self, len: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + try!(self.emit_uint(len)); + f(self) + } + + fn emit_seq_elt(&mut self, _idx: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + f(self) + } + + fn emit_map(&mut self, len: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + try!(self.emit_uint(len)); + f(self) + } + + fn emit_map_elt_key(&mut self, _idx: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + f(self) + } + + fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> EncodeResult where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + f(self) + } +} + +impl<'a> Encoder<'a> { + pub fn position(&self) -> usize { + self.cursor.position() as usize + } + + pub fn from_rbml<'b: 'c, 'c>(rbml: &'c mut ::writer::Encoder<'b>) -> Encoder<'c> { + Encoder { + cursor: rbml.writer + } + } +} + +//=----------------------------------------------------------------------------- +// Decoder +//=----------------------------------------------------------------------------- + +pub struct Decoder<'a> { + pub data: &'a [u8], + position: usize, +} + +impl<'a> Decoder<'a> { + pub fn new(data: &'a [u8], position: usize) -> Decoder<'a> { + Decoder { + data: data, + position: position + } + } + + pub fn position(&self) -> usize { + self.position + } + + pub fn advance(&mut self, bytes: usize) { + self.position += bytes; + } +} + +macro_rules! read_uleb128 { + ($dec:expr, $t:ty) => ({ + let (value, bytes_read) = read_unsigned_leb128($dec.data, $dec.position); + $dec.position += bytes_read; + Ok(value as $t) + }) +} + +macro_rules! read_sleb128 { + ($dec:expr, $t:ty) => ({ + let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position); + $dec.position += bytes_read; + Ok(value as $t) + }) +} + + +impl<'a> serialize::Decoder for Decoder<'a> { + type Error = DecodeError; + + fn read_nil(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + fn read_u64(&mut self) -> Result { + read_uleb128!(self, u64) + } + + fn read_u32(&mut self) -> Result { + read_uleb128!(self, u32) + } + + fn read_u16(&mut self) -> Result { + read_uleb128!(self, u16) + } + + fn read_u8(&mut self) -> Result { + let value = self.data[self.position]; + self.position += 1; + Ok(value) + } + + fn read_uint(&mut self) -> Result { + read_uleb128!(self, usize) + } + + fn read_i64(&mut self) -> Result { + read_sleb128!(self, i64) + } + + fn read_i32(&mut self) -> Result { + read_sleb128!(self, i32) + } + + fn read_i16(&mut self) -> Result { + read_sleb128!(self, i16) + } + + fn read_i8(&mut self) -> Result { + let as_u8 = self.data[self.position]; + self.position += 1; + unsafe { + Ok(::std::mem::transmute(as_u8)) + } + } + + fn read_int(&mut self) -> Result { + read_sleb128!(self, isize) + } + + fn read_bool(&mut self) -> Result { + let value = try!(self.read_u8()); + Ok(value != 0) + } + + fn read_f64(&mut self) -> Result { + let bits = try!(self.read_u64()); + Ok(unsafe { ::std::mem::transmute(bits) }) + } + + fn read_f32(&mut self) -> Result { + let bits = try!(self.read_u32()); + Ok(unsafe { ::std::mem::transmute(bits) }) + } + + fn read_char(&mut self) -> Result { + let bits = try!(self.read_u32()); + Ok(::std::char::from_u32(bits).unwrap()) + } + + fn read_str(&mut self) -> Result { + let len = try!(self.read_uint()); + let s = ::std::str::from_utf8(&self.data[self.position .. self.position + len]).unwrap(); + self.position += len; + Ok(s.to_string()) + } + + fn read_enum(&mut self, _name: &str, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + f(self) + } + + fn read_enum_variant(&mut self, + _: &[&str], + mut f: F) + -> Result + where F: FnMut(&mut Decoder<'a>, usize) -> Result, + { + let disr = try!(self.read_uint()); + f(self, disr) + } + + fn read_enum_variant_arg(&mut self, _idx: usize, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + f(self) + } + + fn read_enum_struct_variant(&mut self, + _: &[&str], + mut f: F) -> Result + where F: FnMut(&mut Decoder<'a>, usize) -> Result, + { + let disr = try!(self.read_uint()); + f(self, disr) + } + + fn read_enum_struct_variant_field(&mut self, + _name: &str, + _idx: usize, + f: F) + -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + f(self) + } + + fn read_struct(&mut self, _name: &str, _: usize, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + f(self) + } + + fn read_struct_field(&mut self, + _name: &str, + _idx: usize, f: F) + -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + f(self) + } + + fn read_tuple(&mut self, tuple_len: usize, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + self.read_seq(move |d, len| { + if len == tuple_len { + f(d) + } else { + let err = format!("Invalid tuple length. Expected {}, found {}", + tuple_len, + len); + Err(DecodeError::Expected(err)) + } + }) + } + + fn read_tuple_arg(&mut self, idx: usize, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + self.read_seq_elt(idx, f) + } + + fn read_tuple_struct(&mut self, + _name: &str, + len: usize, f: F) + -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + self.read_tuple(len, f) + } + + fn read_tuple_struct_arg(&mut self, + idx: usize, + f: F) + -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + self.read_tuple_arg(idx, f) + } + + fn read_option(&mut self, mut f: F) -> Result where + F: FnMut(&mut Decoder<'a>, bool) -> Result, + { + self.read_enum("Option", move |this| { + this.read_enum_variant(&["None", "Some"], move |this, idx| { + match idx { + 0 => f(this, false), + 1 => f(this, true), + _ => { + let msg = format!("Invalid Option index: {}", idx); + Err(DecodeError::Expected(msg)) + } + } + }) + }) + } + + fn read_seq(&mut self, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>, usize) -> Result, + { + let len = try!(self.read_uint()); + f(self, len) + } + + fn read_seq_elt(&mut self, _idx: usize, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + f(self) + } + + fn read_map(&mut self, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>, usize) -> Result, + { + let len = try!(self.read_uint()); + f(self, len) + } + + fn read_map_elt_key(&mut self, _idx: usize, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + f(self) + } + + fn read_map_elt_val(&mut self, _idx: usize, f: F) -> Result where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + f(self) + } + + fn error(&mut self, err: &str) -> Self::Error { + DecodeError::ApplicationError(err.to_string()) + } +} + + +#[cfg(test)] +mod tests { + use serialize::{Encodable, Decodable}; + use std::io::{Cursor}; + use std::fmt::Debug; + use super::{Encoder, Decoder}; + + #[derive(PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)] + struct Struct { + a: (), + b: u8, + c: u16, + d: u32, + e: u64, + f: usize, + + g: i8, + h: i16, + i: i32, + j: i64, + k: isize, + + l: char, + m: String, + n: f32, + o: f64, + p: bool, + q: Option, + } + + + fn check_round_trip(values: Vec) { + let mut cursor = Cursor::new(Vec::new()); + + for value in &values { + let mut encoder = Encoder::new(&mut cursor); + Encodable::encode(&value, &mut encoder).unwrap(); + } + + let data = cursor.into_inner(); + let mut decoder = Decoder::new(&data[..], 0); + + for value in values { + let decoded = Decodable::decode(&mut decoder).unwrap(); + assert_eq!(value, decoded); + } + } + + #[test] + fn test_unit() { + check_round_trip(vec![(), (), (), ()]); + } + + #[test] + fn test_u8() { + let mut vec = vec![]; + for i in ::std::u8::MIN .. ::std::u8::MAX { + vec.push(i); + } + check_round_trip(vec); + } + + #[test] + fn test_u16() { + for i in ::std::u16::MIN .. ::std::u16::MAX { + check_round_trip(vec![1, 2, 3, i, i, i]); + } + } + + #[test] + fn test_u32() { + check_round_trip(vec![1, 2, 3, ::std::u32::MIN, 0, 1, ::std::u32::MAX, 2, 1]); + } + + #[test] + fn test_u64() { + check_round_trip(vec![1, 2, 3, ::std::u64::MIN, 0, 1, ::std::u64::MAX, 2, 1]); + } + + #[test] + fn test_usize() { + check_round_trip(vec![1, 2, 3, ::std::usize::MIN, 0, 1, ::std::usize::MAX, 2, 1]); + } + + #[test] + fn test_i8() { + let mut vec = vec![]; + for i in ::std::i8::MIN .. ::std::i8::MAX { + vec.push(i); + } + check_round_trip(vec); + } + + #[test] + fn test_i16() { + for i in ::std::i16::MIN .. ::std::i16::MAX { + check_round_trip(vec![-1, 2, -3, i, i, i, 2]); + } + } + + #[test] + fn test_i32() { + check_round_trip(vec![-1, 2, -3, ::std::i32::MIN, 0, 1, ::std::i32::MAX, 2, 1]); + } + + #[test] + fn test_i64() { + check_round_trip(vec![-1, 2, -3, ::std::i64::MIN, 0, 1, ::std::i64::MAX, 2, 1]); + } + + #[test] + fn test_isize() { + check_round_trip(vec![-1, 2, -3, ::std::isize::MIN, 0, 1, ::std::isize::MAX, 2, 1]); + } + + #[test] + fn test_bool() { + check_round_trip(vec![false, true, true, false, false]); + } + + #[test] + fn test_f32() { + let mut vec = vec![]; + for i in -100 .. 100 { + vec.push( (i as f32) / 3.0 ); + } + check_round_trip(vec); + } + + #[test] + fn test_f64() { + let mut vec = vec![]; + for i in -100 .. 100 { + vec.push( (i as f64) / 3.0 ); + } + check_round_trip(vec); + } + + #[test] + fn test_char() { + let vec = vec!['a', 'b', 'c', 'd', 'A', 'X', ' ', '#', 'Ö', 'Ä', 'µ', '€']; + check_round_trip(vec); + } + + #[test] + fn test_string() { + let vec = vec![ + "abcbuÖeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmeabpnvapeapmaebn".to_string(), + "abcbuÖganeiÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µnvapeapmaebn".to_string()]; + + check_round_trip(vec); + } + + #[test] + fn test_option() { + check_round_trip(vec![Some(-1i8)]); + check_round_trip(vec![Some(-2i16)]); + check_round_trip(vec![Some(-3i32)]); + check_round_trip(vec![Some(-4i64)]); + check_round_trip(vec![Some(-5isize)]); + + let none_i8: Option = None; + check_round_trip(vec![none_i8]); + + let none_i16: Option = None; + check_round_trip(vec![none_i16]); + + let none_i32: Option = None; + check_round_trip(vec![none_i32]); + + let none_i64: Option = None; + check_round_trip(vec![none_i64]); + + let none_isize: Option = None; + check_round_trip(vec![none_isize]); + } + + #[test] + fn test_struct() { + check_round_trip(vec![Struct { + a: (), + b: 10, + c: 11, + d: 12, + e: 13, + f: 14, + + g: 15, + h: 16, + i: 17, + j: 18, + k: 19, + + l: 'x', + m: "abc".to_string(), + n: 20.5, + o: 21.5, + p: false, + q: None, + }]); + + check_round_trip(vec![Struct { + a: (), + b: 101, + c: 111, + d: 121, + e: 131, + f: 141, + + g: -15, + h: -16, + i: -17, + j: -18, + k: -19, + + l: 'y', + m: "def".to_string(), + n: -20.5, + o: -21.5, + p: true, + q: Some(1234567), + }]); + } + + #[derive(PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)] + enum Enum { + Variant1, + Variant2(usize, f32), + Variant3 { a: i32, b: char, c: bool } + } + + #[test] + fn test_enum() { + check_round_trip(vec![Enum::Variant1, + Enum::Variant2(1, 2.5), + Enum::Variant3 { a: 3, b: 'b', c: false }, + Enum::Variant3 { a: -4, b: 'f', c: true }]); + } + + #[test] + fn test_sequence() { + let mut vec = vec![]; + for i in -100i64 .. 100i64 { + vec.push(i*100000); + } + + check_round_trip(vec![vec]); + } + + #[test] + fn test_hash_map() { + use std::collections::HashMap; + let mut map = HashMap::new(); + for i in -100i64 .. 100i64 { + map.insert(i*100000, i*10000); + } + + check_round_trip(vec![map]); + } + + #[test] + fn test_tuples() { + check_round_trip(vec![('x', (), false, 0.5f32)]); + check_round_trip(vec![(9i8, 10u16, 1.5f64)]); + check_round_trip(vec![(-12i16, 11u8, 12usize)]); + check_round_trip(vec![(1234567isize, 100000000000000u64, 99999999999999i64)]); + check_round_trip(vec![(String::new(), "some string".to_string())]); + } +} diff --git a/src/librustc/dep_graph/README.md b/src/librustc/dep_graph/README.md new file mode 100644 index 0000000000000..21742d9935dc2 --- /dev/null +++ b/src/librustc/dep_graph/README.md @@ -0,0 +1,390 @@ +# Dependency graph for incremental compilation + +This module contains the infrastructure for managing the incremental +compilation dependency graph. This README aims to explain how it ought +to be used. In this document, we'll first explain the overall +strategy, and then share some tips for handling specific scenarios. + +The high-level idea is that we want to instrument the compiler to +track which parts of the AST and other IR are read/written by what. +This way, when we come back later, we can look at this graph and +determine what work needs to be redone. + +### The dependency graph + +The nodes of the graph are defined by the enum `DepNode`. They represent +one of three things: + +1. HIR nodes (like `Hir(DefId)`) represent the HIR input itself. +2. Data nodes (like `ItemSignature(DefId)`) represent some computed + information about a particular item. +3. Procedure notes (like `CoherenceCheckImpl(DefId)`) represent some + procedure that is executing. Usually this procedure is + performing some kind of check for errors. You can think of them as + computed values where the value being computed is `()` (and the + value may fail to be computed, if an error results). + +An edge `N1 -> N2` is added between two nodes if either: + +- the value of `N1` is used to compute `N2`; +- `N1` is read by the procedure `N2`; +- the procedure `N1` writes the value `N2`. + +The latter two conditions are equivalent to the first one if you think +of procedures as values. + +### Basic tracking + +There is a very general strategy to ensure that you have a correct, if +sometimes overconservative, dependency graph. The two main things you have +to do are (a) identify shared state and (b) identify the current tasks. + +### Identifying shared state + +Identify "shared state" that will be written by one pass and read by +another. In particular, we need to identify shared state that will be +read "across items" -- that is, anything where changes in one item +could invalidate work done for other items. So, for example: + +1. The signature for a function is "shared state". +2. The computed type of some expression in the body of a function is + not shared state, because if it changes it does not itself + invalidate other functions (though it may be that it causes new + monomorphizations to occur, but that's handled independently). + +Put another way: if the HIR for an item changes, we are going to +recompile that item for sure. But we need the dep tracking map to tell +us what *else* we have to recompile. Shared state is anything that is +used to communicate results from one item to another. + +### Identifying the current task + +The dep graph always tracks a current task: this is basically the +`DepNode` that the compiler is computing right now. Typically it would +be a procedure node, but it can also be a data node (as noted above, +the two are kind of equivalent). + +You set the current task by calling `dep_graph.in_task(node)`. For example: + +```rust +let _task = tcx.dep_graph.in_task(DepNode::Privacy); +``` + +Now all the code until `_task` goes out of scope will be considered +part of the "privacy task". + +The tasks are maintained in a stack, so it is perfectly fine to nest +one task within another. Because pushing a task is considered to be +computing a value, when you nest a task `N2` inside of a task `N1`, we +automatically add an edge `N2 -> N1` (since `N1` presumably needed the +result of `N2` to complete): + +```rust +let _n1 = tcx.dep_graph.in_task(DepNode::N1); +let _n2 = tcx.dep_graph.in_task(DepNode::N2); +// this will result in an edge N1 -> n2 +``` + +### Ignore tasks + +Although it is rarely needed, you can also push a special "ignore" +task: + +```rust +let _ignore = tc.dep_graph.in_ignore(); +``` + +This will cause all read/write edges to be ignored until it goes out +of scope or until something else is pushed. For example, we could +suppress the edge between nested tasks like so: + +```rust +let _n1 = tcx.dep_graph.in_task(DepNode::N1); +let _ignore = tcx.dep_graph.in_ignore(); +let _n2 = tcx.dep_graph.in_task(DepNode::N2); +// now no edge is added +``` + +### Tracking reads and writes + +We need to identify what shared state is read/written by the current +task as it executes. The most fundamental way of doing that is to invoke +the `read` and `write` methods on `DepGraph`: + +```rust +// Adds an edge from DepNode::Hir(some_def_id) to the current task +tcx.dep_graph.read(DepNode::Hir(some_def_id)) + +// Adds an edge from the current task to DepNode::ItemSignature(some_def_id) +tcx.dep_graph.write(DepNode::ItemSignature(some_def_id)) +``` + +However, you should rarely need to invoke those methods directly. +Instead, the idea is to *encapsulate* shared state into some API that +will invoke `read` and `write` automatically. The most common way to +do this is to use a `DepTrackingMap`, described in the next section, +but any sort of abstraction barrier will do. In general, the strategy +is that getting access to information implicitly adds an appropriate +`read`. So, for example, when you use the +`dep_graph::visit_all_items_in_krate` helper method, it will visit +each item `X`, start a task `Foo(X)` for that item, and automatically +add an edge `Hir(X) -> Foo(X)`. This edge is added because the code is +being given access to the HIR node for `X`, and hence it is expected +to read from it. Similarly, reading from the `tcache` map for item `X` +(which is a `DepTrackingMap`, described below) automatically invokes +`dep_graph.read(ItemSignature(X))`. + +To make this strategy work, a certain amount of indirection is +required. For example, modules in the HIR do not have direct pointers +to the items that they contain. Rather, they contain node-ids -- one +can then ask the HIR map for the item with a given node-id. This gives +us an opportunity to add an appropriate read edge. + +#### Explicit calls to read and write when starting a new subtask + +One time when you *may* need to call `read` and `write` directly is +when you push a new task onto the stack, either by calling `in_task` +as shown above or indirectly, such as with the `memoize` pattern +described below. In that case, any data that the task has access to +from the surrounding environment must be explicitly "read". For +example, in `librustc_typeck`, the collection code visits all items +and, among other things, starts a subtask producing its signature +(what follows is simplified pseudocode, of course): + +```rust +fn visit_item(item: &hir::Item) { + // Here, current subtask is "Collect(X)", and an edge Hir(X) -> Collect(X) + // has automatically been added by `visit_all_items_in_krate`. + let sig = signature_of_item(item); +} + +fn signature_of_item(item: &hir::Item) { + let def_id = tcx.map.local_def_id(item.id); + let task = tcx.dep_graph.in_task(DepNode::ItemSignature(def_id)); + tcx.dep_graph.read(DepNode::Hir(def_id)); // <-- the interesting line + ... +} +``` + +Here you can see that, in `signature_of_item`, we started a subtask +corresponding to producing the `ItemSignature`. This subtask will read from +`item` -- but it gained access to `item` implicitly. This means that if it just +reads from `item`, there would be missing edges in the graph: + + Hir(X) --+ // added by the explicit call to `read` + | | + | +---> ItemSignature(X) -> Collect(X) + | ^ + | | + +---------------------------------+ // added by `visit_all_items_in_krate` + +In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only +present because we called `read` ourselves when entering the `ItemSignature(X)` +task. + +So, the rule of thumb: when entering a new task yourself, register +reads on any shared state that you inherit. (This actually comes up +fairly infrequently though: the main place you need caution is around +memoization.) + +#### Dependency tracking map + +`DepTrackingMap` is a particularly convenient way to correctly store +shared state. A `DepTrackingMap` is a special hashmap that will add +edges automatically when `get` and `insert` are called. The idea is +that, when you get/insert a value for the key `K`, we will add an edge +from/to the node `DepNode::Variant(K)` (for some variant specific to +the map). + +Each `DepTrackingMap` is parameterized by a special type `M` that +implements `DepTrackingMapConfig`; this trait defines the key and value +types of the map, and also defines a fn for converting from the key to +a `DepNode` label. You don't usually have to muck about with this by +hand, there is a macro for creating it. You can see the complete set +of `DepTrackingMap` definitions in `librustc/middle/ty/maps.rs`. + +As an example, let's look at the `adt_defs` map. The `adt_defs` map +maps from the def-id of a struct/enum to its `AdtDef`. It is defined +using this macro: + +```rust +dep_map_ty! { AdtDefs: ItemSignature(DefId) -> ty::AdtDefMaster<'tcx> } +// ~~~~~~~ ~~~~~~~~~~~~~ ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ +// | | Key type Value type +// | DepNode variant +// Name of map id type +``` + +this indicates that a map id type `AdtDefs` will be created. The key +of the map will be a `DefId` and value will be +`ty::AdtDefMaster<'tcx>`. The `DepNode` will be created by +`DepNode::ItemSignature(K)` for a given key. + +Once that is done, you can just use the `DepTrackingMap` like any +other map: + +```rust +let mut map: DepTrackingMap = DepTrackingMap::new(dep_graph); +map.insert(key, value); // registers dep_graph.write +map.get(key; // registers dep_graph.read +``` + +#### Memoization + +One particularly interesting case is memoization. If you have some +shared state that you compute in a memoized fashion, the correct thing +to do is to define a `RefCell` for it and use the +`memoize` helper: + +```rust +map.memoize(key, || /* compute value */) +``` + +This will create a graph that looks like + + ... -> MapVariant(key) -> CurrentTask + +where `MapVariant` is the `DepNode` variant that the map is associated with, +and `...` are whatever edges the `/* compute value */` closure creates. + +In particular, using the memoize helper is much better than writing +the obvious code yourself: + +``` +if let Some(result) = map.get(key) { + return result; +} +let value = /* compute value */; +map.insert(key, value); +``` + +If you write that code manually, the dependency graph you get will +include artificial edges that are not necessary. For example, imagine that +two tasks, A and B, both invoke the manual memoization code, but A happens +to go first. The resulting graph will be: + + ... -> A -> MapVariant(key) -> B + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // caused by A writing to MapVariant(key) + ~~~~~~~~~~~~~~~~~~~~ // caused by B reading from MapVariant(key) + +This graph is not *wrong*, but it encodes a path from A to B that +should not exist. In contrast, using the memoized helper, you get: + + ... -> MapVariant(key) -> A + | + +----------> B + +which is much cleaner. + +**Be aware though that the closure is executed with `MapVariant(key)` +pushed onto the stack as the current task!** That means that you must +add explicit `read` calls for any shared state that it accesses +implicitly from its environment. See the section on "explicit calls to +read and write when starting a new subtask" above for more details. + +### How to decide where to introduce a new task + +Certainly, you need at least one task on the stack: any attempt to +`read` or `write` shared state will panic if there is no current +task. But where does it make sense to introduce subtasks? The basic +rule is that a subtask makes sense for any discrete unit of work you +may want to skip in the future. Adding a subtask separates out the +reads/writes from *that particular subtask* versus the larger +context. An example: you might have a 'meta' task for all of borrow +checking, and then subtasks for borrow checking individual fns. (Seen +in this light, memoized computations are just a special case where we +may want to avoid redoing the work even within the context of one +compilation.) + +The other case where you might want a subtask is to help with refining +the reads/writes for some later bit of work that needs to be memoized. +For example, we create a subtask for type-checking the body of each +fn. However, in the initial version of incr. comp. at least, we do +not expect to actually *SKIP* type-checking -- we only expect to skip +trans. However, it's still useful to create subtasks for type-checking +individual items, because, otherwise, if a fn sig changes, we won't +know which callers are affected -- in fact, because the graph would be +so coarse, we'd just have to retrans everything, since we can't +distinguish which fns used which fn sigs. + +### Testing the dependency graph + +There are various ways to write tests against the dependency graph. +The simplest mechanism are the +`#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]` +annotations. These are used in compile-fail tests to test whether the +expected set of paths exist in the dependency graph. As an example, +see `src/test/compile-fail/dep-graph-caller-callee.rs`. + +The idea is that you can annotate a test like: + +```rust +#[rustc_if_this_changed] +fn foo() { } + +#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK +fn bar() { foo(); } + +#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path +fn baz() { } +``` + +This will check whether there is a path in the dependency graph from +`Hir(foo)` to `TypeckItemBody(bar)`. An error is reported for each +`#[rustc_then_this_would_need]` annotation that indicates whether a +path exists. `//~ ERROR` annotations can then be used to test if a +path is found (as demonstrated above). + +### Debugging the dependency graph + +The compiler is also capable of dumping the dependency graph for your +debugging pleasure. To do so, pass the `-Z dump-dep-graph` flag. The +graph will be dumped to `dep_graph.{txt,dot}` in the current +directory. You can override the filename with the `RUST_DEP_GRAPH` +environment variable. + +Frequently, though, the full dep graph is quite overwhelming and not +particularly helpful. Therefore, the compiler also allows you to filter +the graph. You can filter in three ways: + +1. All edges originating in a particular set of nodes (usually a single node). +2. All edges reaching a particular set of nodes. +3. All edges that lie between given start and end nodes. + +To filter, use the `RUST_DEP_GRAPH_FILTER` environment variable, which should +look like one of the following: + +``` +source_filter // nodes originating from source_filter +-> target_filter // nodes that can reach target_filter +source_filter -> target_filter // nodes in between source_filter and target_filter +``` + +`source_filter` and `target_filter` are a `&`-separated list of strings. +A node is considered to match a filter if all of those strings appear in its +label. So, for example: + +``` +RUST_DEP_GRAPH_FILTER='-> TypeckItemBody' +``` + +would select the predecessors of all `TypeckItemBody` nodes. Usually though you +want the `TypeckItemBody` node for some particular fn, so you might write: + +``` +RUST_DEP_GRAPH_FILTER='-> TypeckItemBody & bar' +``` + +This will select only the `TypeckItemBody` nodes for fns with `bar` in their name. + +Perhaps you are finding that when you change `foo` you need to re-type-check `bar`, +but you don't think you should have to. In that case, you might do: + +``` +RUST_DEP_GRAPH_FILTER='Hir&foo -> TypeckItemBody & bar' +``` + +This will dump out all the nodes that lead from `Hir(foo)` to +`TypeckItemBody(bar)`, from which you can (hopefully) see the source +of the erroneous edge. + diff --git a/src/librustc/dep_graph/dep_tracking_map.rs b/src/librustc/dep_graph/dep_tracking_map.rs new file mode 100644 index 0000000000000..c49e64f0f543b --- /dev/null +++ b/src/librustc/dep_graph/dep_tracking_map.rs @@ -0,0 +1,137 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc_data_structures::fnv::FnvHashMap; +use std::cell::RefCell; +use std::ops::Index; +use std::hash::Hash; +use std::marker::PhantomData; +use util::common::MemoizationMap; + +use super::{DepNode, DepGraph}; + +/// A DepTrackingMap offers a subset of the `Map` API and ensures that +/// we make calls to `read` and `write` as appropriate. We key the +/// maps with a unique type for brevity. +pub struct DepTrackingMap { + phantom: PhantomData, + graph: DepGraph, + map: FnvHashMap, +} + +pub trait DepTrackingMapConfig { + type Key: Eq + Hash + Clone; + type Value: Clone; + fn to_dep_node(key: &Self::Key) -> DepNode; +} + +impl DepTrackingMap { + pub fn new(graph: DepGraph) -> DepTrackingMap { + DepTrackingMap { + phantom: PhantomData, + graph: graph, + map: FnvHashMap() + } + } + + /// Registers a (synthetic) read from the key `k`. Usually this + /// is invoked automatically by `get`. + fn read(&self, k: &M::Key) { + let dep_node = M::to_dep_node(k); + self.graph.read(dep_node); + } + + /// Registers a (synthetic) write to the key `k`. Usually this is + /// invoked automatically by `insert`. + fn write(&self, k: &M::Key) { + let dep_node = M::to_dep_node(k); + self.graph.write(dep_node); + } + + pub fn get(&self, k: &M::Key) -> Option<&M::Value> { + self.read(k); + self.map.get(k) + } + + pub fn insert(&mut self, k: M::Key, v: M::Value) -> Option { + self.write(&k); + self.map.insert(k, v) + } + + pub fn contains_key(&self, k: &M::Key) -> bool { + self.read(k); + self.map.contains_key(k) + } +} + +impl MemoizationMap for RefCell> { + type Key = M::Key; + type Value = M::Value; + + /// Memoizes an entry in the dep-tracking-map. If the entry is not + /// already present, then `op` will be executed to compute its value. + /// The resulting dependency graph looks like this: + /// + /// [op] -> Map(key) -> CurrentTask + /// + /// Here, `[op]` represents whatever nodes `op` reads in the + /// course of execution; `Map(key)` represents the node for this + /// map; and `CurrentTask` represents the current task when + /// `memoize` is invoked. + /// + /// **Important:* when `op` is invoked, the current task will be + /// switched to `Map(key)`. Therefore, if `op` makes use of any + /// HIR nodes or shared state accessed through its closure + /// environment, it must explicitly register a read of that + /// state. As an example, see `type_scheme_of_item` in `collect`, + /// which looks something like this: + /// + /// ``` + /// fn type_scheme_of_item(..., item: &hir::Item) -> ty::TypeScheme<'tcx> { + /// let item_def_id = ccx.tcx.map.local_def_id(it.id); + /// ccx.tcx.tcache.memoized(item_def_id, || { + /// ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // (*) + /// compute_type_scheme_of_item(ccx, item) + /// }); + /// } + /// ``` + /// + /// The key is the line marked `(*)`: the closure implicitly + /// accesses the body of the item `item`, so we register a read + /// from `Hir(item_def_id)`. + fn memoize(&self, key: M::Key, op: OP) -> M::Value + where OP: FnOnce() -> M::Value + { + let graph; + { + let this = self.borrow(); + if let Some(result) = this.map.get(&key) { + this.read(&key); + return result.clone(); + } + graph = this.graph.clone(); + } + + let _task = graph.in_task(M::to_dep_node(&key)); + let result = op(); + self.borrow_mut().map.insert(key, result.clone()); + result + } +} + +impl<'k, M: DepTrackingMapConfig> Index<&'k M::Key> for DepTrackingMap { + type Output = M::Value; + + #[inline] + fn index(&self, k: &'k M::Key) -> &M::Value { + self.get(k).unwrap() + } +} + diff --git a/src/librustc/dep_graph/edges.rs b/src/librustc/dep_graph/edges.rs new file mode 100644 index 0000000000000..4b25285c476c4 --- /dev/null +++ b/src/librustc/dep_graph/edges.rs @@ -0,0 +1,162 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc_data_structures::fnv::{FnvHashMap, FnvHashSet}; +use super::{DepGraphQuery, DepNode}; + +pub struct DepGraphEdges { + nodes: Vec, + indices: FnvHashMap, + edges: FnvHashSet<(IdIndex, IdIndex)>, + open_nodes: Vec, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +struct IdIndex { + index: u32 +} + +impl IdIndex { + fn new(v: usize) -> IdIndex { + assert!((v & 0xFFFF_FFFF) == v); + IdIndex { index: v as u32 } + } + + fn index(self) -> usize { + self.index as usize + } +} + +#[derive(Clone, Debug, PartialEq)] +enum OpenNode { + Node(IdIndex), + Ignore, +} + +impl DepGraphEdges { + pub fn new() -> DepGraphEdges { + DepGraphEdges { + nodes: vec![], + indices: FnvHashMap(), + edges: FnvHashSet(), + open_nodes: Vec::new() + } + } + + fn id(&self, index: IdIndex) -> DepNode { + self.nodes[index.index()] + } + + /// Creates a node for `id` in the graph. + fn make_node(&mut self, id: DepNode) -> IdIndex { + if let Some(&i) = self.indices.get(&id) { + return i; + } + + let index = IdIndex::new(self.nodes.len()); + self.nodes.push(id.clone()); + self.indices.insert(id, index); + index + } + + /// Top of the stack of open nodes. + fn current_node(&self) -> Option { + self.open_nodes.last().cloned() + } + + pub fn push_ignore(&mut self) { + self.open_nodes.push(OpenNode::Ignore); + } + + pub fn pop_ignore(&mut self) { + let popped_node = self.open_nodes.pop().unwrap(); + assert_eq!(popped_node, OpenNode::Ignore); + } + + pub fn push_task(&mut self, key: DepNode) { + let top_node = self.current_node(); + + let new_node = self.make_node(key); + self.open_nodes.push(OpenNode::Node(new_node)); + + // if we are in the midst of doing task T, then this new task + // N is a subtask of T, so add an edge N -> T. + if let Some(top_node) = top_node { + self.add_edge_from_open_node(top_node, |t| (new_node, t)); + } + } + + pub fn pop_task(&mut self, key: DepNode) { + let popped_node = self.open_nodes.pop().unwrap(); + assert_eq!(OpenNode::Node(self.indices[&key]), popped_node); + } + + /// Indicates that the current task `C` reads `v` by adding an + /// edge from `v` to `C`. If there is no current task, panics. If + /// you want to suppress this edge, use `ignore`. + pub fn read(&mut self, v: DepNode) { + let source = self.make_node(v); + self.add_edge_from_current_node(|current| (source, current)) + } + + /// Indicates that the current task `C` writes `v` by adding an + /// edge from `C` to `v`. If there is no current task, panics. If + /// you want to suppress this edge, use `ignore`. + pub fn write(&mut self, v: DepNode) { + let target = self.make_node(v); + self.add_edge_from_current_node(|current| (current, target)) + } + + /// Invoke `add_edge_from_open_node` with the top of the stack, or + /// panic if stack is empty. + fn add_edge_from_current_node(&mut self, + op: OP) + where OP: FnOnce(IdIndex) -> (IdIndex, IdIndex) + { + match self.current_node() { + Some(open_node) => self.add_edge_from_open_node(open_node, op), + None => panic!("no current node, cannot add edge into dependency graph") + } + } + + /// Adds an edge to or from the `open_node`, assuming `open_node` + /// is not `Ignore`. The direction of the edge is determined by + /// the closure `op` --- we pass as argument the open node `n`, + /// and the closure returns a (source, target) tuple, which should + /// include `n` in one spot or another. + fn add_edge_from_open_node(&mut self, + open_node: OpenNode, + op: OP) + where OP: FnOnce(IdIndex) -> (IdIndex, IdIndex) + { + let (source, target) = match open_node { + OpenNode::Node(n) => op(n), + OpenNode::Ignore => { return; } + }; + + // ignore trivial self edges, which are not very interesting + if source == target { + return; + } + + if self.edges.insert((source, target)) { + debug!("adding edge from {:?} to {:?}", + self.id(source), + self.id(target)); + } + } + + pub fn query(&self) -> DepGraphQuery { + let edges: Vec<_> = self.edges.iter() + .map(|&(i, j)| (self.id(i), self.id(j))) + .collect(); + DepGraphQuery::new(&self.nodes, &edges) + } +} diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs new file mode 100644 index 0000000000000..9bf0a79115e78 --- /dev/null +++ b/src/librustc/dep_graph/mod.rs @@ -0,0 +1,196 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use self::thread::{DepGraphThreadData, DepMessage}; +use middle::def_id::DefId; +use middle::ty; +use middle::ty::fast_reject::SimplifiedType; +use rustc_front::hir; +use rustc_front::intravisit::Visitor; +use std::rc::Rc; + +mod dep_tracking_map; +mod edges; +mod query; +mod raii; +mod thread; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum DepNode { + // Represents the `Krate` as a whole (the `hir::Krate` value) (as + // distinct from the krate module). This is basically a hash of + // the entire krate, so if you read from `Krate` (e.g., by calling + // `tcx.map.krate()`), we will have to assume that any change + // means that you need to be recompiled. This is because the + // `Krate` value gives you access to all other items. To avoid + // this fate, do not call `tcx.map.krate()`; instead, prefer + // wrappers like `tcx.visit_all_items_in_krate()`. If there is no + // suitable wrapper, you can use `tcx.dep_graph.ignore()` to gain + // access to the krate, but you must remember to add suitable + // edges yourself for the individual items that you read. + Krate, + + // Represents the HIR node with the given node-id + Hir(DefId), + + // Represents different phases in the compiler. + CollectItem(DefId), + Coherence, + CoherenceCheckImpl(DefId), + CoherenceOverlapCheck(DefId), + CoherenceOverlapCheckSpecial(DefId), + CoherenceOrphanCheck(DefId), + Variance, + WfCheck(DefId), + TypeckItemType(DefId), + TypeckItemBody(DefId), + Dropck, + DropckImpl(DefId), + CheckConst(DefId), + Privacy, + IntrinsicCheck(DefId), + MatchCheck(DefId), + MirMapConstruction(DefId), + BorrowCheck(DefId), + RvalueCheck(DefId), + Reachability, + DeadCheck, + StabilityCheck, + LateLintCheck, + IntrinsicUseCheck, + TransCrate, + TransCrateItem(DefId), + TransInlinedItem(DefId), + TransWriteMetadata, + + // Nodes representing bits of computed IR in the tcx. Each shared + // table in the tcx (or elsewhere) maps to one of these + // nodes. Often we map multiple tables to the same node if there + // is no point in distinguishing them (e.g., both the type and + // predicates for an item wind up in `ItemSignature`). Other + // times, such as `ImplItems` vs `TraitItemDefIds`, tables which + // might be mergable are kept distinct because the sets of def-ids + // to which they apply are disjoint, and hence we might as well + // have distinct labels for easier debugging. + ImplOrTraitItems(DefId), + ItemSignature(DefId), + FieldTy(DefId), + TraitItemDefIds(DefId), + InherentImpls(DefId), + ImplItems(DefId), + + // The set of impls for a given trait. Ultimately, it would be + // nice to get more fine-grained here (e.g., to include a + // simplified type), but we can't do that until we restructure the + // HIR to distinguish the *header* of an impl from its body. This + // is because changes to the header may change the self-type of + // the impl and hence would require us to be more conservative + // than changes in the impl body. + TraitImpls(DefId), + + // Nodes representing caches. To properly handle a true cache, we + // don't use a DepTrackingMap, but rather we push a task node. + // Otherwise the write into the map would be incorrectly + // attributed to the first task that happened to fill the cache, + // which would yield an overly conservative dep-graph. + TraitItems(DefId), + ReprHints(DefId), + TraitSelect(DefId, Option), +} + +#[derive(Clone)] +pub struct DepGraph { + data: Rc +} + +impl DepGraph { + pub fn new(enabled: bool) -> DepGraph { + DepGraph { + data: Rc::new(DepGraphThreadData::new(enabled)) + } + } + + pub fn query(&self) -> DepGraphQuery { + self.data.query() + } + + pub fn in_ignore<'graph>(&'graph self) -> raii::IgnoreTask<'graph> { + raii::IgnoreTask::new(&self.data) + } + + pub fn in_task<'graph>(&'graph self, key: DepNode) -> raii::DepTask<'graph> { + raii::DepTask::new(&self.data, key) + } + + pub fn with_ignore(&self, op: OP) -> R + where OP: FnOnce() -> R + { + let _task = self.in_ignore(); + op() + } + + pub fn with_task(&self, key: DepNode, op: OP) -> R + where OP: FnOnce() -> R + { + let _task = self.in_task(key); + op() + } + + pub fn read(&self, v: DepNode) { + self.data.enqueue(DepMessage::Read(v)); + } + + pub fn write(&self, v: DepNode) { + self.data.enqueue(DepMessage::Write(v)); + } +} + +pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig}; + +pub use self::query::DepGraphQuery; + +/// Visit all the items in the krate in some order. When visiting a +/// particular item, first create a dep-node by calling `dep_node_fn` +/// and push that onto the dep-graph stack of tasks, and also create a +/// read edge from the corresponding AST node. This is used in +/// compiler passes to automatically record the item that they are +/// working on. +pub fn visit_all_items_in_krate<'tcx,V,F>(tcx: &ty::ctxt<'tcx>, + mut dep_node_fn: F, + visitor: &mut V) + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> +{ + struct TrackingVisitor<'visit, 'tcx: 'visit, F: 'visit, V: 'visit> { + tcx: &'visit ty::ctxt<'tcx>, + dep_node_fn: &'visit mut F, + visitor: &'visit mut V + } + + impl<'visit, 'tcx, F, V> Visitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V> + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> + { + fn visit_item(&mut self, i: &'tcx hir::Item) { + let item_def_id = self.tcx.map.local_def_id(i.id); + let task_id = (self.dep_node_fn)(item_def_id); + debug!("About to start task {:?}", task_id); + let _task = self.tcx.dep_graph.in_task(task_id); + self.tcx.dep_graph.read(DepNode::Hir(item_def_id)); + self.visitor.visit_item(i) + } + } + + let krate = tcx.dep_graph.with_ignore(|| tcx.map.krate()); + let mut tracking_visitor = TrackingVisitor { + tcx: tcx, + dep_node_fn: &mut dep_node_fn, + visitor: visitor + }; + krate.visit_all_items(&mut tracking_visitor) +} diff --git a/src/librustc/dep_graph/query.rs b/src/librustc/dep_graph/query.rs new file mode 100644 index 0000000000000..74a054acb4fa0 --- /dev/null +++ b/src/librustc/dep_graph/query.rs @@ -0,0 +1,68 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc_data_structures::fnv::FnvHashMap; +use rustc_data_structures::graph::{Graph, NodeIndex}; + +use super::DepNode; + +pub struct DepGraphQuery { + pub graph: Graph, + pub indices: FnvHashMap, +} + +impl DepGraphQuery { + pub fn new(nodes: &[DepNode], edges: &[(DepNode, DepNode)]) -> DepGraphQuery { + let mut graph = Graph::new(); + let mut indices = FnvHashMap(); + for node in nodes { + indices.insert(node.clone(), graph.next_node_index()); + graph.add_node(node.clone()); + } + + for &(ref source, ref target) in edges { + let source = indices[source]; + let target = indices[target]; + graph.add_edge(source, target, ()); + } + + DepGraphQuery { + graph: graph, + indices: indices + } + } + + pub fn nodes(&self) -> Vec { + self.graph.all_nodes() + .iter() + .map(|n| n.data.clone()) + .collect() + } + + pub fn edges(&self) -> Vec<(DepNode,DepNode)> { + self.graph.all_edges() + .iter() + .map(|edge| (edge.source(), edge.target())) + .map(|(s, t)| (self.graph.node_data(s).clone(), self.graph.node_data(t).clone())) + .collect() + } + + /// All nodes reachable from `node`. In other words, things that + /// will have to be recomputed if `node` changes. + pub fn dependents(&self, node: DepNode) -> Vec { + if let Some(&index) = self.indices.get(&node) { + self.graph.depth_traverse(index) + .map(|dependent_node| self.graph.node_data(dependent_node).clone()) + .collect() + } else { + vec![] + } + } +} diff --git a/src/librustc/dep_graph/raii.rs b/src/librustc/dep_graph/raii.rs new file mode 100644 index 0000000000000..dd7ff92f9c360 --- /dev/null +++ b/src/librustc/dep_graph/raii.rs @@ -0,0 +1,47 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::DepNode; +use super::thread::{DepGraphThreadData, DepMessage}; + +pub struct DepTask<'graph> { + data: &'graph DepGraphThreadData, + key: DepNode, +} + +impl<'graph> DepTask<'graph> { + pub fn new(data: &'graph DepGraphThreadData, key: DepNode) -> DepTask<'graph> { + data.enqueue(DepMessage::PushTask(key)); + DepTask { data: data, key: key } + } +} + +impl<'graph> Drop for DepTask<'graph> { + fn drop(&mut self) { + self.data.enqueue(DepMessage::PopTask(self.key)); + } +} + +pub struct IgnoreTask<'graph> { + data: &'graph DepGraphThreadData +} + +impl<'graph> IgnoreTask<'graph> { + pub fn new(data: &'graph DepGraphThreadData) -> IgnoreTask<'graph> { + data.enqueue(DepMessage::PushIgnore); + IgnoreTask { data: data } + } +} + +impl<'graph> Drop for IgnoreTask<'graph> { + fn drop(&mut self) { + self.data.enqueue(DepMessage::PopIgnore); + } +} diff --git a/src/librustc/dep_graph/thread.rs b/src/librustc/dep_graph/thread.rs new file mode 100644 index 0000000000000..dbc57605d71ae --- /dev/null +++ b/src/librustc/dep_graph/thread.rs @@ -0,0 +1,137 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Manages the communication between the compiler's main thread and +//! the thread that constructs the dependency graph. The basic idea is +//! to use double buffering to lower the cost of producing a message. +//! In the compiler thread, we accumulate messages in a vector until +//! the vector is full, or until we want to query the graph, and then +//! we send that vector over to the depgraph thread. At the same time, +//! we receive an empty vector from the depgraph thread that we can use +//! to accumulate more messages. This way we only ever have two vectors +//! allocated (and both have a fairly large capacity). + +use rustc_data_structures::veccell::VecCell; +use std::sync::mpsc::{self, Sender, Receiver}; +use std::thread; + +use super::DepGraphQuery; +use super::DepNode; +use super::edges::DepGraphEdges; + +pub enum DepMessage { + Read(DepNode), + Write(DepNode), + PushTask(DepNode), + PopTask(DepNode), + PushIgnore, + PopIgnore, + Query, +} + +pub struct DepGraphThreadData { + enabled: bool, + + // current buffer, where we accumulate messages + messages: VecCell, + + // whence to receive new buffer when full + swap_in: Receiver>, + + // where to send buffer when full + swap_out: Sender>, + + // where to receive query results + query_in: Receiver, +} + +const INITIAL_CAPACITY: usize = 2048; + +impl DepGraphThreadData { + pub fn new(enabled: bool) -> DepGraphThreadData { + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (txq, rxq) = mpsc::channel(); + if enabled { + thread::spawn(move || main(rx1, tx2, txq)); + } + DepGraphThreadData { + enabled: enabled, + messages: VecCell::with_capacity(INITIAL_CAPACITY), + swap_in: rx2, + swap_out: tx1, + query_in: rxq, + } + } + + /// Sends the current batch of messages to the thread. Installs a + /// new vector of messages. + fn swap(&self) { + assert!(self.enabled, "should never swap if not enabled"); + + // should be a buffer waiting for us (though of course we may + // have to wait for depgraph thread to finish processing the + // old messages) + let new_messages = self.swap_in.recv().unwrap(); + assert!(new_messages.is_empty()); + + // swap in the empty buffer and extract the full one + let old_messages = self.messages.swap(new_messages); + + // send full buffer to depgraph thread to be processed + self.swap_out.send(old_messages).unwrap(); + } + + pub fn query(&self) -> DepGraphQuery { + assert!(self.enabled, "cannot query if dep graph construction not enabled"); + self.enqueue(DepMessage::Query); + self.swap(); + self.query_in.recv().unwrap() + } + + /// Enqueue a message to be sent when things are next swapped. (If + /// the buffer is full, this may swap.) + #[inline] + pub fn enqueue(&self, message: DepMessage) { + if self.enabled { + let len = self.messages.push(message); + if len == INITIAL_CAPACITY { + self.swap(); + } + } + } +} + +/// Definition of the depgraph thread. +pub fn main(swap_in: Receiver>, + swap_out: Sender>, + query_out: Sender) { + let mut edges = DepGraphEdges::new(); + + // the compiler thread always expects a fresh buffer to be + // waiting, so queue one up + swap_out.send(Vec::with_capacity(INITIAL_CAPACITY)).unwrap(); + + // process the buffers from compiler thread as we receive them + for mut messages in swap_in { + for msg in messages.drain(..) { + match msg { + DepMessage::Read(node) => edges.read(node), + DepMessage::Write(node) => edges.write(node), + DepMessage::PushTask(node) => edges.push_task(node), + DepMessage::PopTask(node) => edges.pop_task(node), + DepMessage::PushIgnore => edges.push_ignore(), + DepMessage::PopIgnore => edges.pop_ignore(), + DepMessage::Query => query_out.send(edges.query()).unwrap(), + } + } + swap_out.send(messages).unwrap(); + } +} diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 6a5910074d915..178d8a8721418 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1657,6 +1657,46 @@ This will fail because the compiler does not know which instance of `Foo` to call `bar` on. Change `Foo::bar()` to `Foo::::bar()` to resolve the error. "##, +E0283: r##" +This error occurs when the compiler doesn't have enough information +to unambiguously choose an implementation. + +For example: + +``` +trait Generator { + fn create() -> u32; +} + +struct Impl; +impl Generator for Impl { + fn create() -> u32 { 1 } +} + +struct AnotherImpl; +impl Generator for AnotherImpl { + fn create() -> u32 { 2 } +} + +fn main() { + let cont: u32 = Generator::create(); + // error, impossible to choose one of Generator trait implementation + // Impl or AnotherImpl? Maybe anything else? +} +``` + +To resolve this error use the concrete type: + +``` +fn main() { + let gen1 = AnotherImpl::create(); + + // if there are multiple methods with same name (different traits) + let gen2 = ::create(); +} +``` +"##, + E0296: r##" This error indicates that the given recursion limit could not be parsed. Ensure that the value provided is a positive integer between quotes, like so: @@ -2279,14 +2319,13 @@ register_diagnostics! { E0278, // requirement is not satisfied E0279, // requirement is not satisfied E0280, // requirement is not satisfied - E0283, // cannot resolve type E0284, // cannot resolve type E0285, // overflow evaluation builtin bounds E0298, // mismatched types between arms E0299, // mismatched types between arms - E0300, // unexpanded macro - E0304, // expected signed integer constant - E0305, // expected constant + // E0300, // unexpanded macro + // E0304, // expected signed integer constant + // E0305, // expected constant E0311, // thing may not live long enough E0312, // lifetime of reference outlives lifetime of borrowed content E0313, // lifetime of borrowed pointer outlives lifetime of captured variable diff --git a/src/librustc/front/map/mod.rs b/src/librustc/front/map/mod.rs index cbda1e8880b66..7de6099544525 100644 --- a/src/librustc/front/map/mod.rs +++ b/src/librustc/front/map/mod.rs @@ -839,11 +839,10 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest) -> Map<'ast> { } /// Used for items loaded from external crate that are being inlined into this -/// crate. The `path` should be the path to the item but should not include -/// the item itself. +/// crate. pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>, - path: Vec, - def_path: DefPath, + parent_path: Vec, + parent_def_path: DefPath, ii: InlinedItem, fold_ops: F) -> &'ast InlinedItem { @@ -862,7 +861,7 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>, }; let ii_parent = map.forest.inlined_items.alloc(InlinedParent { - path: path, + path: parent_path, ii: ii }); @@ -872,7 +871,7 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>, map.krate(), ii_parent, ii_parent_id, - def_path, + parent_def_path, mem::replace(&mut *map.map.borrow_mut(), vec![]), mem::replace(&mut *map.definitions.borrow_mut(), Definitions::new())); ii_parent.ii.visit(&mut collector); diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 00d6237b85564..f84d5fbaf81dd 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -87,6 +87,8 @@ pub mod back { pub use rustc_back::svh; } +pub mod dep_graph; + pub mod front { pub mod check_attr; pub mod map; diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 2c5c664566a38..aff925d108272 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -16,6 +16,9 @@ use lint::{LintPass, LateLintPass, LintArray}; +// name of the future-incompatible group +pub const FUTURE_INCOMPATIBLE: &'static str = "future_incompatible"; + declare_lint! { pub CONST_ERR, Warn, @@ -124,6 +127,12 @@ declare_lint! { "detect private items in public interfaces not caught by the old implementation" } +declare_lint! { + pub INVALID_TYPE_PARAM_DEFAULT, + Warn, + "type parameter default erroneously allowed in invalid location" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -149,6 +158,7 @@ impl LintPass for HardwiredLints { TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS, PRIVATE_IN_PUBLIC, + INVALID_TYPE_PARAM_DEFAULT, CONST_ERR ) } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index db22000fd9f5d..08fba2dc56fee 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -25,6 +25,7 @@ //! for all lint attributes. use self::TargetLint::*; +use dep_graph::DepNode; use middle::privacy::AccessLevels; use middle::ty; use session::{early_error, Session}; @@ -40,6 +41,7 @@ use std::mem; use syntax::ast_util::{self, IdVisitingOperation}; use syntax::attr::{self, AttrMetaMethods}; use syntax::codemap::Span; +use syntax::errors::{self, DiagnosticBuilder}; use syntax::parse::token::InternedString; use syntax::ast; use syntax::attr::ThinAttributesExt; @@ -47,7 +49,6 @@ use rustc_front::hir; use rustc_front::util; use rustc_front::intravisit as hir_visit; use syntax::visit as ast_visit; -use syntax::errors; /// Information about the registered lints. /// @@ -363,10 +364,26 @@ pub fn gather_attrs(attrs: &[ast::Attribute]) /// in trans that run after the main lint pass is finished. Most /// lints elsewhere in the compiler should call /// `Session::add_lint()` instead. -pub fn raw_emit_lint(sess: &Session, lint: &'static Lint, - lvlsrc: LevelSource, span: Option, msg: &str) { +pub fn raw_emit_lint(sess: &Session, + lints: &LintStore, + lint: &'static Lint, + lvlsrc: LevelSource, + span: Option, + msg: &str) { + raw_struct_lint(sess, lints, lint, lvlsrc, span, msg).emit(); +} + +pub fn raw_struct_lint<'a>(sess: &'a Session, + lints: &LintStore, + lint: &'static Lint, + lvlsrc: LevelSource, + span: Option, + msg: &str) + -> DiagnosticBuilder<'a> { let (mut level, source) = lvlsrc; - if level == Allow { return } + if level == Allow { + return sess.diagnostic().struct_dummy(); + } let name = lint.name_lower(); let mut def = None; @@ -391,17 +408,31 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint, // For purposes of printing, we can treat forbid as deny. if level == Forbid { level = Deny; } - match (level, span) { - (Warn, Some(sp)) => sess.span_warn(sp, &msg[..]), - (Warn, None) => sess.warn(&msg[..]), - (Deny, Some(sp)) => sess.span_err(sp, &msg[..]), - (Deny, None) => sess.err(&msg[..]), + let mut err = match (level, span) { + (Warn, Some(sp)) => sess.struct_span_warn(sp, &msg[..]), + (Warn, None) => sess.struct_warn(&msg[..]), + (Deny, Some(sp)) => sess.struct_span_err(sp, &msg[..]), + (Deny, None) => sess.struct_err(&msg[..]), _ => sess.bug("impossible level in raw_emit_lint"), + }; + + // Check for future incompatibility lints and issue a stronger warning. + let future_incompat_lints = &lints.lint_groups[builtin::FUTURE_INCOMPATIBLE]; + let this_id = LintId::of(lint); + if future_incompat_lints.0.iter().any(|&id| id == this_id) { + let msg = "this lint will become a HARD ERROR in a future release!"; + if let Some(sp) = span { + err.span_note(sp, msg); + } else { + err.note(msg); + } } if let Some(span) = def { - sess.span_note(span, "lint level defined here"); + err.span_note(span, "lint level defined here"); } + + err } pub trait LintContext: Sized { @@ -418,17 +449,36 @@ pub trait LintContext: Sized { self.lints().levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl) } - fn lookup_and_emit(&self, lint: &'static Lint, span: Option, msg: &str) { - let (level, src) = match self.lints().levels.get(&LintId::of(lint)) { - None => return, - Some(&(Warn, src)) => { + fn level_src(&self, lint: &'static Lint) -> Option { + self.lints().levels.get(&LintId::of(lint)).map(|ls| match ls { + &(Warn, src) => { let lint_id = LintId::of(builtin::WARNINGS); (self.lints().get_level_source(lint_id).0, src) } - Some(&pair) => pair, + _ => *ls + }) + } + + fn lookup_and_emit(&self, lint: &'static Lint, span: Option, msg: &str) { + let (level, src) = match self.level_src(lint) { + None => return, + Some(pair) => pair, }; - raw_emit_lint(&self.sess(), lint, (level, src), span, msg); + raw_emit_lint(&self.sess(), self.lints(), lint, (level, src), span, msg); + } + + fn lookup(&self, + lint: &'static Lint, + span: Option, + msg: &str) + -> DiagnosticBuilder { + let (level, src) = match self.level_src(lint) { + None => return self.sess().diagnostic().struct_dummy(), + Some(pair) => pair, + }; + + raw_struct_lint(&self.sess(), self.lints(), lint, (level, src), span, msg) } /// Emit a lint at the appropriate level, for a particular span. @@ -436,26 +486,37 @@ pub trait LintContext: Sized { self.lookup_and_emit(lint, Some(span), msg); } + fn struct_span_lint(&self, + lint: &'static Lint, + span: Span, + msg: &str) + -> DiagnosticBuilder { + self.lookup(lint, Some(span), msg) + } + /// Emit a lint and note at the appropriate level, for a particular span. fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str, note_span: Span, note: &str) { - self.span_lint(lint, span, msg); + let mut err = self.lookup(lint, Some(span), msg); if self.current_level(lint) != Level::Allow { if note_span == span { - self.sess().fileline_note(note_span, note) + err.fileline_note(note_span, note); } else { - self.sess().span_note(note_span, note) + err.span_note(note_span, note); } } + err.emit(); } /// Emit a lint and help at the appropriate level, for a particular span. fn span_lint_help(&self, lint: &'static Lint, span: Span, msg: &str, help: &str) { + let mut err = self.lookup(lint, Some(span), msg); self.span_lint(lint, span, msg); if self.current_level(lint) != Level::Allow { - self.sess().span_help(span, help) + err.span_help(span, help); } + err.emit(); } /// Emit a lint at the appropriate level, with no associated span. @@ -1025,6 +1086,8 @@ impl LateLintPass for GatherNodeLevels { /// /// Consumes the `lint_store` field of the `Session`. pub fn check_crate(tcx: &ty::ctxt, access_levels: &AccessLevels) { + let _task = tcx.dep_graph.in_task(DepNode::LateLintCheck); + let krate = tcx.map.krate(); let mut cx = LateContext::new(tcx, krate, access_levels); diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 23be6117f190f..9c689daab86a7 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -41,7 +41,7 @@ use rustc_front::hir; pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore, raw_emit_lint, check_crate, check_ast_crate, gather_attrs, - GatherNodeLevels}; + raw_struct_lint, GatherNodeLevels}; /// Specification of a single lint. #[derive(Copy, Clone, Debug)] diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index d6f05ffd8a513..a9b3043e09095 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -24,6 +24,7 @@ // - It's not possible to take the address of a static item with unsafe interior. This is enforced // by borrowck::gather_loans +use dep_graph::DepNode; use middle::ty::cast::{CastKind}; use middle::const_eval::{self, ConstEvalErr}; use middle::const_eval::ErrKind::IndexOpFeatureGated; @@ -224,14 +225,15 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { // this doesn't come from a macro that has #[allow_internal_unstable] !self.tcx.sess.codemap().span_allows_unstable(expr.span) { - self.tcx.sess.span_err( + let mut err = self.tcx.sess.struct_span_err( expr.span, "const fns are an unstable feature"); fileline_help!( - self.tcx.sess, + &mut err, expr.span, "in Nightly builds, add `#![feature(const_fn)]` to the crate \ attributes to enable"); + err.emit(); } let qualif = self.fn_like(fn_like.kind(), @@ -714,27 +716,27 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, if !is_const { v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { - fn span_limited_call_error(tcx: &ty::ctxt, span: Span, s: &str) { - span_err!(tcx.sess, span, E0015, "{}", s); - } - // FIXME(#24111) Remove this check when const fn stabilizes - if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features { - span_limited_call_error(&v.tcx, e.span, - &format!("function calls in {}s are limited to \ - struct and enum constructors", - v.msg())); - v.tcx.sess.span_note(e.span, - "a limited form of compile-time function \ - evaluation is available on a nightly \ - compiler via `const fn`"); + let (msg, note) = + if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features { + (format!("function calls in {}s are limited to \ + struct and enum constructors", + v.msg()), + Some("a limited form of compile-time function \ + evaluation is available on a nightly \ + compiler via `const fn`")) } else { - span_limited_call_error(&v.tcx, e.span, - &format!("function calls in {}s are limited \ - to constant functions, \ - struct and enum constructors", - v.msg())); + (format!("function calls in {}s are limited \ + to constant functions, \ + struct and enum constructors", + v.msg()), + None) + }; + let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg); + if let Some(note) = note { + err.span_note(e.span, note); } + err.emit(); } } } @@ -841,13 +843,12 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp } pub fn check_crate(tcx: &ty::ctxt) { - tcx.map.krate().visit_all_items(&mut CheckCrateVisitor { + tcx.visit_all_items_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor { tcx: tcx, mode: Mode::Var, qualif: ConstQualif::NOT_CONST, rvalue_borrows: NodeMap() }); - tcx.sess.abort_if_errors(); } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index cab7e45ee6283..972f9e2c64d0e 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -12,6 +12,7 @@ pub use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; +use dep_graph::DepNode; use middle::const_eval::{compare_const_vals, ConstVal}; use middle::const_eval::{eval_const_expr, eval_const_expr_partial}; use middle::const_eval::{const_expr_to_pat, lookup_const_by_id}; @@ -155,7 +156,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> { } pub fn check_crate(tcx: &ty::ctxt) { - tcx.map.krate().visit_all_items(&mut MatchCheckCtxt { + tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut MatchCheckCtxt { tcx: tcx, param_env: tcx.empty_parameter_environment(), }); @@ -215,12 +216,13 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) { if inlined_arms.is_empty() { if !pat_ty.is_empty(cx.tcx) { // We know the type is inhabited, so this must be wrong - span_err!(cx.tcx.sess, ex.span, E0002, - "non-exhaustive patterns: type {} is non-empty", - pat_ty); - span_help!(cx.tcx.sess, ex.span, + let mut err = struct_span_err!(cx.tcx.sess, ex.span, E0002, + "non-exhaustive patterns: type {} is non-empty", + pat_ty); + span_help!(&mut err, ex.span, "Please ensure that all possible cases are being handled; \ possibly adding wildcards or more match arms."); + err.emit(); } // If the type *is* empty, it's vacuously exhaustive return; @@ -251,14 +253,15 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat) && variant.kind() == VariantKind::Unit ) { let ty_path = cx.tcx.item_path_str(edef.did); - span_warn!(cx.tcx.sess, p.span, E0170, + let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170, "pattern binding `{}` is named the same as one \ of the variants of the type `{}`", ident.node, ty_path); - fileline_help!(cx.tcx.sess, p.span, + fileline_help!(err, p.span, "if you meant to match on a variant, \ consider making the path in the pattern qualified: `{}::{}`", ty_path, ident.node); + err.emit(); } } } @@ -282,13 +285,13 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) { Ok(_) => {} Err(err) => { - span_err!(cx.tcx.sess, err.span, E0471, - "constant evaluation error: {}", - err.description()); + let mut diag = struct_span_err!(cx.tcx.sess, err.span, E0471, + "constant evaluation error: {}", + err.description()); if !p.span.contains(err.span) { - cx.tcx.sess.span_note(p.span, - "in pattern here") + diag.span_note(p.span, "in pattern here"); } + diag.emit(); } } } @@ -1076,9 +1079,10 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, } else if has_guard { span_err!(cx.tcx.sess, p.span, E0008, "cannot bind by-move into a pattern guard"); } else if by_ref_span.is_some() { - span_err!(cx.tcx.sess, p.span, E0009, - "cannot bind by-move and by-ref in the same pattern"); - span_note!(cx.tcx.sess, by_ref_span.unwrap(), "by-ref binding occurs here"); + let mut err = struct_span_err!(cx.tcx.sess, p.span, E0009, + "cannot bind by-move and by-ref in the same pattern"); + span_note!(&mut err, by_ref_span.unwrap(), "by-ref binding occurs here"); + err.emit(); } }; diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs index 35adeae3e617c..8a3e039ac6e53 100644 --- a/src/librustc/middle/check_rvalues.rs +++ b/src/librustc/middle/check_rvalues.rs @@ -11,21 +11,21 @@ // Checks that all rvalues in a crate have statically known size. check_crate // is the public starting point. +use dep_graph::DepNode; use middle::expr_use_visitor as euv; use middle::infer; use middle::mem_categorization as mc; use middle::ty::ParameterEnvironment; use middle::ty; -use syntax::ast; use rustc_front::hir; -use syntax::codemap::Span; use rustc_front::intravisit; +use syntax::ast; +use syntax::codemap::Span; -pub fn check_crate(tcx: &ty::ctxt, - krate: &hir::Crate) { +pub fn check_crate(tcx: &ty::ctxt) { let mut rvcx = RvalueContext { tcx: tcx }; - krate.visit_all_items(&mut rvcx); + tcx.visit_all_items_in_krate(DepNode::RvalueCheck, &mut rvcx); } struct RvalueContext<'a, 'tcx: 'a> { diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 2c3b89bf2fbb2..380f543f969f0 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -432,8 +432,8 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { /// Note, however, that this only works for RBML-based encoding and decoding at /// the moment. pub mod tls { - use rbml::writer::Encoder as RbmlEncoder; - use rbml::reader::Decoder as RbmlDecoder; + use rbml::opaque::Encoder as OpaqueEncoder; + use rbml::opaque::Decoder as OpaqueDecoder; use serialize; use std::mem; use middle::ty::{self, Ty}; @@ -442,8 +442,8 @@ pub mod tls { pub trait EncodingContext<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; - fn encode_ty(&self, rbml_w: &mut RbmlEncoder, t: Ty<'tcx>); - fn encode_substs(&self, rbml_w: &mut RbmlEncoder, substs: &Substs<'tcx>); + fn encode_ty(&self, encoder: &mut OpaqueEncoder, t: Ty<'tcx>); + fn encode_substs(&self, encoder: &mut OpaqueEncoder, substs: &Substs<'tcx>); } /// Marker type used for the scoped TLS slot. @@ -455,13 +455,13 @@ pub mod tls { /// Execute f after pushing the given EncodingContext onto the TLS stack. pub fn enter_encoding_context<'tcx, F, R>(ecx: &EncodingContext<'tcx>, - rbml_w: &mut RbmlEncoder, + encoder: &mut OpaqueEncoder, f: F) -> R - where F: FnOnce(&EncodingContext<'tcx>, &mut RbmlEncoder) -> R + where F: FnOnce(&EncodingContext<'tcx>, &mut OpaqueEncoder) -> R { - let tls_payload = (ecx as *const _, rbml_w as *mut _); + let tls_payload = (ecx as *const _, encoder as *mut _); let tls_ptr = &tls_payload as *const _ as *const TlsPayload; - TLS_ENCODING.set(unsafe { &*tls_ptr }, || f(ecx, rbml_w)) + TLS_ENCODING.set(unsafe { &*tls_ptr }, || f(ecx, encoder)) } /// Execute f with access to the thread-local encoding context and @@ -473,16 +473,16 @@ pub mod tls { /// possible to construct cases where the EncodingContext is exchanged /// while the same encoder is used, thus working with a wrong context. pub fn with_encoding_context<'tcx, E, F, R>(encoder: &mut E, f: F) -> R - where F: FnOnce(&EncodingContext<'tcx>, &mut RbmlEncoder) -> R, + where F: FnOnce(&EncodingContext<'tcx>, &mut OpaqueEncoder) -> R, E: serialize::Encoder { unsafe { - unsafe_with_encoding_context(|ecx, rbml_w| { - assert!(encoder as *mut _ as usize == rbml_w as *mut _ as usize); + unsafe_with_encoding_context(|ecx, tls_encoder| { + assert!(encoder as *mut _ as usize == tls_encoder as *mut _ as usize); let ecx: &EncodingContext<'tcx> = mem::transmute(ecx); - f(ecx, rbml_w) + f(ecx, tls_encoder) }) } } @@ -490,19 +490,19 @@ pub mod tls { /// Execute f with access to the thread-local encoding context and /// rbml encoder. pub unsafe fn unsafe_with_encoding_context(f: F) -> R - where F: FnOnce(&EncodingContext, &mut RbmlEncoder) -> R + where F: FnOnce(&EncodingContext, &mut OpaqueEncoder) -> R { TLS_ENCODING.with(|tls| { let tls_payload = (tls as *const TlsPayload) - as *mut (&EncodingContext, &mut RbmlEncoder); + as *mut (&EncodingContext, &mut OpaqueEncoder); f((*tls_payload).0, (*tls_payload).1) }) } pub trait DecodingContext<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; - fn decode_ty(&self, rbml_r: &mut RbmlDecoder) -> ty::Ty<'tcx>; - fn decode_substs(&self, rbml_r: &mut RbmlDecoder) -> Substs<'tcx>; + fn decode_ty(&self, decoder: &mut OpaqueDecoder) -> ty::Ty<'tcx>; + fn decode_substs(&self, decoder: &mut OpaqueDecoder) -> Substs<'tcx>; fn translate_def_id(&self, def_id: DefId) -> DefId; } @@ -510,13 +510,13 @@ pub mod tls { /// Execute f after pushing the given DecodingContext onto the TLS stack. pub fn enter_decoding_context<'tcx, F, R>(dcx: &DecodingContext<'tcx>, - rbml_r: &mut RbmlDecoder, + decoder: &mut OpaqueDecoder, f: F) -> R - where F: FnOnce(&DecodingContext<'tcx>, &mut RbmlDecoder) -> R + where F: FnOnce(&DecodingContext<'tcx>, &mut OpaqueDecoder) -> R { - let tls_payload = (dcx as *const _, rbml_r as *mut _); + let tls_payload = (dcx as *const _, decoder as *mut _); let tls_ptr = &tls_payload as *const _ as *const TlsPayload; - TLS_DECODING.set(unsafe { &*tls_ptr }, || f(dcx, rbml_r)) + TLS_DECODING.set(unsafe { &*tls_ptr }, || f(dcx, decoder)) } /// Execute f with access to the thread-local decoding context and @@ -530,16 +530,16 @@ pub mod tls { pub fn with_decoding_context<'decoder, 'tcx, D, F, R>(d: &'decoder mut D, f: F) -> R where D: serialize::Decoder, F: FnOnce(&DecodingContext<'tcx>, - &mut RbmlDecoder) -> R, + &mut OpaqueDecoder) -> R, 'tcx: 'decoder { unsafe { - unsafe_with_decoding_context(|dcx, rbml_r| { - assert!((d as *mut _ as usize) == (rbml_r as *mut _ as usize)); + unsafe_with_decoding_context(|dcx, decoder| { + assert!((d as *mut _ as usize) == (decoder as *mut _ as usize)); let dcx: &DecodingContext<'tcx> = mem::transmute(dcx); - f(dcx, rbml_r) + f(dcx, decoder) }) } } @@ -547,11 +547,11 @@ pub mod tls { /// Execute f with access to the thread-local decoding context and /// rbml decoder. pub unsafe fn unsafe_with_decoding_context(f: F) -> R - where F: FnOnce(&DecodingContext, &mut RbmlDecoder) -> R + where F: FnOnce(&DecodingContext, &mut OpaqueDecoder) -> R { TLS_DECODING.with(|tls| { let tls_payload = (tls as *const TlsPayload) - as *mut (&DecodingContext, &mut RbmlDecoder); + as *mut (&DecodingContext, &mut OpaqueDecoder); f((*tls_payload).0, (*tls_payload).1) }) } diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index ec1b447d7111b..1386ef91c70bf 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -12,6 +12,7 @@ // closely. The idea is that all reachable symbols are live, codes called // from live codes are live, and everything else is dead. +use dep_graph::DepNode; use front::map as ast_map; use rustc_front::hir; use rustc_front::intravisit::{self, Visitor}; @@ -590,6 +591,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> { } pub fn check_crate(tcx: &ty::ctxt, access_levels: &privacy::AccessLevels) { + let _task = tcx.dep_graph.in_task(DepNode::DeadCheck); let krate = tcx.map.krate(); let live_symbols = find_live(tcx, access_levels, krate); let mut visitor = DeadVisitor { tcx: tcx, live_symbols: live_symbols }; diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index ab5153e1a61d4..aac6f1edc051d 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs @@ -243,10 +243,11 @@ fn add_library(sess: &session::Session, // This error is probably a little obscure, but I imagine that it // can be refined over time. if link2 != link || link == RequireStatic { - sess.err(&format!("cannot satisfy dependencies so `{}` only \ - shows up once", sess.cstore.crate_name(cnum))); - sess.help("having upstream crates all available in one format \ - will likely make this go away"); + sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \ + shows up once", sess.cstore.crate_name(cnum))) + .help("having upstream crates all available in one format \ + will likely make this go away") + .emit(); } } None => { m.insert(cnum, link); } diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index ecf16aaed836a..2d096f66e09f6 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -146,17 +146,20 @@ fn configure_main(this: &mut EntryContext) { this.session.entry_type.set(Some(config::EntryMain)); } else { // No main function - this.session.err("main function not found"); + let mut err = this.session.struct_err("main function not found"); if !this.non_main_fns.is_empty() { // There were some functions named 'main' though. Try to give the user a hint. - this.session.note("the main function must be defined at the crate level \ - but you have one or more functions named 'main' that are not \ - defined at the crate level. Either move the definition or \ - attach the `#[main]` attribute to override this behavior."); + err.note("the main function must be defined at the crate level \ + but you have one or more functions named 'main' that are not \ + defined at the crate level. Either move the definition or \ + attach the `#[main]` attribute to override this behavior."); for &(_, span) in &this.non_main_fns { - this.session.span_note(span, "here is a function named 'main'"); + err.span_note(span, "here is a function named 'main'"); } + err.emit(); this.session.abort_if_errors(); + } else { + err.emit(); } } } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 0273d1a76e9a8..23d1617e5c658 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -274,7 +274,7 @@ enum PassArgs { } impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> { - pub fn new(delegate: &'d mut (Delegate<'tcx>), + pub fn new(delegate: &'d mut (Delegate<'tcx>+'d), typer: &'t infer::InferCtxt<'a, 'tcx>) -> ExprUseVisitor<'d,'t,'a,'tcx> where 'tcx:'a+'d { diff --git a/src/librustc/middle/implicator.rs b/src/librustc/middle/implicator.rs index 9f33c4df03d41..d25084bbdffb5 100644 --- a/src/librustc/middle/implicator.rs +++ b/src/librustc/middle/implicator.rs @@ -14,7 +14,7 @@ use middle::def_id::DefId; use middle::infer::{InferCtxt, GenericKind}; use middle::subst::Substs; use middle::traits; -use middle::ty::{self, RegionEscape, ToPredicate, Ty}; +use middle::ty::{self, ToPredicate, Ty}; use middle::ty::fold::{TypeFoldable, TypeFolder}; use syntax::ast; @@ -404,7 +404,7 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> { } fn fully_normalize(&self, value: &T) -> Result - where T : TypeFoldable<'tcx> + ty::HasTypeFlags + where T : TypeFoldable<'tcx> { let value = traits::fully_normalize(self.infcx, diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs index 2a25ed2c6e144..faf1bdb0ce504 100644 --- a/src/librustc/middle/infer/combine.rs +++ b/src/librustc/middle/infer/combine.rs @@ -318,7 +318,7 @@ impl<'cx, 'tcx> ty::fold::TypeFolder<'tcx> for Generalizer<'cx, 'tcx> { } } _ => { - ty::fold::super_fold_ty(self, t) + t.super_fold_with(self) } } } diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 181e0c0fbc86b..f390d85a28d66 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -82,7 +82,7 @@ use middle::def_id::DefId; use middle::infer::{self, TypeOrigin}; use middle::region; use middle::subst; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, TypeFoldable}; use middle::ty::{Region, ReFree}; use middle::ty::error::TypeError; @@ -90,12 +90,14 @@ use std::cell::{Cell, RefCell}; use std::char::from_u32; use std::fmt; use syntax::ast; +use syntax::errors::DiagnosticBuilder; use syntax::codemap::{self, Pos, Span}; use syntax::parse::token; use syntax::ptr::P; impl<'tcx> ty::ctxt<'tcx> { pub fn note_and_explain_region(&self, + err: &mut DiagnosticBuilder, prefix: &str, region: ty::Region, suffix: &str) { @@ -126,7 +128,10 @@ impl<'tcx> ty::ctxt<'tcx> { }; let span = match scope.span(&self.region_maps, &self.map) { Some(s) => s, - None => return self.sess.note(&unknown_scope()) + None => { + err.note(&unknown_scope()); + return; + } }; let tag = match self.map.find(scope.node_id(&self.region_maps)) { Some(ast_map::NodeBlock(_)) => "block", @@ -142,7 +147,8 @@ impl<'tcx> ty::ctxt<'tcx> { Some(ast_map::NodeStmt(_)) => "statement", Some(ast_map::NodeItem(it)) => item_scope_tag(&*it), Some(_) | None => { - return self.sess.span_note(span, &unknown_scope()); + err.span_note(span, &unknown_scope()); + return; } }; let scope_decorated_tag = match self.region_maps.code_extent_data(scope) { @@ -214,9 +220,9 @@ impl<'tcx> ty::ctxt<'tcx> { }; let message = format!("{}{}{}", prefix, description, suffix); if let Some(span) = span { - self.sess.span_note(span, &message); + err.span_note(span, &message); } else { - self.sess.note(&message); + err.note(&message); } } } @@ -228,9 +234,15 @@ pub trait ErrorReporting<'tcx> { fn process_errors(&self, errors: &Vec>) -> Vec>; - fn report_type_error(&self, trace: TypeTrace<'tcx>, terr: &TypeError<'tcx>); + fn report_type_error(&self, + trace: TypeTrace<'tcx>, + terr: &TypeError<'tcx>) + -> DiagnosticBuilder<'tcx>; - fn check_and_note_conflicting_crates(&self, terr: &TypeError<'tcx>, sp: Span); + fn check_and_note_conflicting_crates(&self, + err: &mut DiagnosticBuilder, + terr: &TypeError<'tcx>, + sp: Span); fn report_and_explain_type_error(&self, trace: TypeTrace<'tcx>, @@ -238,7 +250,7 @@ pub trait ErrorReporting<'tcx> { fn values_str(&self, values: &ValuePairs<'tcx>) -> Option; - fn expected_found_str + HasTypeFlags>( + fn expected_found_str + TypeFoldable<'tcx>>( &self, exp_found: &ty::error::ExpectedFound) -> Option; @@ -265,17 +277,20 @@ pub trait ErrorReporting<'tcx> { trace_origin: &[(TypeTrace<'tcx>, TypeError<'tcx>)], same_regions: &[SameRegions]); - fn give_suggestion(&self, same_regions: &[SameRegions]); + fn give_suggestion(&self, err: &mut DiagnosticBuilder, same_regions: &[SameRegions]); } trait ErrorReportingHelpers<'tcx> { fn report_inference_failure(&self, - var_origin: RegionVariableOrigin); + var_origin: RegionVariableOrigin) + -> DiagnosticBuilder<'tcx>; fn note_region_origin(&self, + err: &mut DiagnosticBuilder, origin: &SubregionOrigin<'tcx>); fn give_expl_lifetime_param(&self, + err: &mut DiagnosticBuilder, decl: &hir::FnDecl, unsafety: hir::Unsafety, constness: hir::Constness, @@ -460,35 +475,47 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { } } - fn report_type_error(&self, trace: TypeTrace<'tcx>, terr: &TypeError<'tcx>) { + fn report_type_error(&self, + trace: TypeTrace<'tcx>, + terr: &TypeError<'tcx>) + -> DiagnosticBuilder<'tcx> { let expected_found_str = match self.values_str(&trace.values) { Some(v) => v, None => { - return; /* derived error */ + return self.tcx.sess.diagnostic().struct_dummy(); /* derived error */ } }; - span_err!(self.tcx.sess, trace.origin.span(), E0308, - "{}: {} ({})", - trace.origin, - expected_found_str, - terr); + let mut err = struct_span_err!(self.tcx.sess, + trace.origin.span(), + E0308, + "{}: {} ({})", + trace.origin, + expected_found_str, + terr); - self.check_and_note_conflicting_crates(terr, trace.origin.span()); + self.check_and_note_conflicting_crates(&mut err, terr, trace.origin.span()); match trace.origin { TypeOrigin::MatchExpressionArm(_, arm_span, source) => match source { - hir::MatchSource::IfLetDesugar{..} => - self.tcx.sess.span_note(arm_span, "`if let` arm with an incompatible type"), - _ => self.tcx.sess.span_note(arm_span, "match arm with an incompatible type"), + hir::MatchSource::IfLetDesugar{..} => { + err.span_note(arm_span, "`if let` arm with an incompatible type"); + } + _ => { + err.span_note(arm_span, "match arm with an incompatible type"); + } }, _ => () } + err } /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, terr: &TypeError<'tcx>, sp: Span) { - let report_path_match = |did1: DefId, did2: DefId| { + fn check_and_note_conflicting_crates(&self, + err: &mut DiagnosticBuilder, + terr: &TypeError<'tcx>, + sp: Span) { + let report_path_match = |err: &mut DiagnosticBuilder, did1: DefId, did2: DefId| { // Only external crates, if either is from a local // module we could have false positives if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate { @@ -502,9 +529,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { // for imported and non-imported crates if exp_path == found_path { let crate_name = self.tcx.sess.cstore.crate_name(did1.krate); - self.tcx.sess.span_note(sp, &format!("Perhaps two different versions \ - of crate `{}` are being used?", - crate_name)); + err.span_note(sp, &format!("Perhaps two different versions \ + of crate `{}` are being used?", + crate_name)); } } }; @@ -517,13 +544,13 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { (&ty::TyStruct(ref exp_adt, _), &ty::TyStruct(ref found_adt, _)) | (&ty::TyEnum(ref exp_adt, _), &ty::TyStruct(ref found_adt, _)) | (&ty::TyStruct(ref exp_adt, _), &ty::TyEnum(ref found_adt, _)) => { - report_path_match(exp_adt.did, found_adt.did); + report_path_match(err, exp_adt.did, found_adt.did); }, _ => () } }, TypeError::Traits(ref exp_found) => { - report_path_match(exp_found.expected, exp_found.found); + report_path_match(err, exp_found.expected, exp_found.found); }, _ => () // FIXME(#22750) handle traits and stuff } @@ -533,8 +560,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { trace: TypeTrace<'tcx>, terr: &TypeError<'tcx>) { let span = trace.origin.span(); - self.report_type_error(trace, terr); - self.tcx.note_and_explain_type_err(terr, span); + let mut err = self.report_type_error(trace, terr); + self.tcx.note_and_explain_type_err(&mut err, terr, span); + err.emit(); } /// Returns a string of the form "expected `{}`, found `{}`", or None if this is a derived @@ -547,7 +575,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { } } - fn expected_found_str + HasTypeFlags>( + fn expected_found_str + TypeFoldable<'tcx>>( &self, exp_found: &ty::error::ExpectedFound) -> Option @@ -584,51 +612,56 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { format!("the associated type `{}`", p), }; - match sub { + let mut err = match sub { ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => { // Does the required lifetime have a nice name we can print? - span_err!( - self.tcx.sess, origin.span(), E0309, - "{} may not live long enough", labeled_user_string); - self.tcx.sess.fileline_help( - origin.span(), - &format!( - "consider adding an explicit lifetime bound `{}: {}`...", - bound_kind, - sub)); + let mut err = struct_span_err!(self.tcx.sess, + origin.span(), + E0309, + "{} may not live long enough", + labeled_user_string); + err.fileline_help(origin.span(), + &format!("consider adding an explicit lifetime bound `{}: {}`...", + bound_kind, + sub)); + err } ty::ReStatic => { // Does the required lifetime have a nice name we can print? - span_err!( - self.tcx.sess, origin.span(), E0310, - "{} may not live long enough", labeled_user_string); - self.tcx.sess.fileline_help( - origin.span(), - &format!( - "consider adding an explicit lifetime bound `{}: 'static`...", - bound_kind)); + let mut err = struct_span_err!(self.tcx.sess, + origin.span(), + E0310, + "{} may not live long enough", + labeled_user_string); + err.fileline_help(origin.span(), + &format!("consider adding an explicit lifetime \ + bound `{}: 'static`...", + bound_kind)); + err } _ => { // If not, be less specific. - span_err!( - self.tcx.sess, origin.span(), E0311, - "{} may not live long enough", - labeled_user_string); - self.tcx.sess.fileline_help( - origin.span(), - &format!( - "consider adding an explicit lifetime bound for `{}`", - bound_kind)); + let mut err = struct_span_err!(self.tcx.sess, + origin.span(), + E0311, + "{} may not live long enough", + labeled_user_string); + err.fileline_help(origin.span(), + &format!("consider adding an explicit lifetime bound for `{}`", + bound_kind)); self.tcx.note_and_explain_region( + &mut err, &format!("{} must be valid for ", labeled_user_string), sub, "..."); + err } - } + }; - self.note_region_origin(&origin); + self.note_region_origin(&mut err, &origin); + err.emit(); } fn report_concrete_failure(&self, @@ -641,239 +674,262 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { self.report_and_explain_type_error(trace, &terr); } infer::Reborrow(span) => { - span_err!(self.tcx.sess, span, E0312, + let mut err = struct_span_err!(self.tcx.sess, span, E0312, "lifetime of reference outlines \ lifetime of borrowed content..."); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "...the reference is valid for ", sub, "..."); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "...but the borrowed content is only valid for ", sup, ""); + err.emit(); } infer::ReborrowUpvar(span, ref upvar_id) => { - span_err!(self.tcx.sess, span, E0313, + let mut err = struct_span_err!(self.tcx.sess, span, E0313, "lifetime of borrowed pointer outlives \ lifetime of captured variable `{}`...", self.tcx.local_var_name_str(upvar_id.var_id)); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "...the borrowed pointer is valid for ", sub, "..."); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, &format!("...but `{}` is only valid for ", self.tcx.local_var_name_str(upvar_id.var_id)), sup, ""); + err.emit(); } infer::InfStackClosure(span) => { - span_err!(self.tcx.sess, span, E0314, + let mut err = struct_span_err!(self.tcx.sess, span, E0314, "closure outlives stack frame"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "...the closure must be valid for ", sub, "..."); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "...but the closure's stack frame is only valid for ", sup, ""); + err.emit(); } infer::InvokeClosure(span) => { - span_err!(self.tcx.sess, span, E0315, + let mut err = struct_span_err!(self.tcx.sess, span, E0315, "cannot invoke closure outside of its lifetime"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the closure is only valid for ", sup, ""); + err.emit(); } infer::DerefPointer(span) => { - span_err!(self.tcx.sess, span, E0473, + let mut err = struct_span_err!(self.tcx.sess, span, E0473, "dereference of reference outside its lifetime"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the reference is only valid for ", sup, ""); + err.emit(); } infer::FreeVariable(span, id) => { - span_err!(self.tcx.sess, span, E0474, + let mut err = struct_span_err!(self.tcx.sess, span, E0474, "captured variable `{}` does not outlive the enclosing closure", self.tcx.local_var_name_str(id)); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "captured variable is valid for ", sup, ""); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "closure is valid for ", sub, ""); + err.emit(); } infer::IndexSlice(span) => { - span_err!(self.tcx.sess, span, E0475, + let mut err = struct_span_err!(self.tcx.sess, span, E0475, "index of slice outside its lifetime"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the slice is only valid for ", sup, ""); + err.emit(); } infer::RelateObjectBound(span) => { - span_err!(self.tcx.sess, span, E0476, + let mut err = struct_span_err!(self.tcx.sess, span, E0476, "lifetime of the source pointer does not outlive \ lifetime bound of the object type"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "object type is valid for ", sub, ""); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "source pointer is only valid for ", sup, ""); + err.emit(); } infer::RelateParamBound(span, ty) => { - span_err!(self.tcx.sess, span, E0477, + let mut err = struct_span_err!(self.tcx.sess, span, E0477, "the type `{}` does not fulfill the required lifetime", self.ty_to_string(ty)); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "type must outlive ", sub, ""); + err.emit(); } infer::RelateRegionParamBound(span) => { - span_err!(self.tcx.sess, span, E0478, + let mut err = struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "lifetime parameter instantiated with ", sup, ""); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "but lifetime parameter must outlive ", sub, ""); + err.emit(); } infer::RelateDefaultParamBound(span, ty) => { - span_err!(self.tcx.sess, span, E0479, + let mut err = struct_span_err!(self.tcx.sess, span, E0479, "the type `{}` (provided as the value of \ a type parameter) is not valid at this point", self.ty_to_string(ty)); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "type must outlive ", sub, ""); + err.emit(); } infer::CallRcvr(span) => { - span_err!(self.tcx.sess, span, E0480, + let mut err = struct_span_err!(self.tcx.sess, span, E0480, "lifetime of method receiver does not outlive \ the method call"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the receiver is only valid for ", sup, ""); + err.emit(); } infer::CallArg(span) => { - span_err!(self.tcx.sess, span, E0481, + let mut err = struct_span_err!(self.tcx.sess, span, E0481, "lifetime of function argument does not outlive \ the function call"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the function argument is only valid for ", sup, ""); + err.emit(); } infer::CallReturn(span) => { - span_err!(self.tcx.sess, span, E0482, + let mut err = struct_span_err!(self.tcx.sess, span, E0482, "lifetime of return value does not outlive \ the function call"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the return value is only valid for ", sup, ""); + err.emit(); } infer::Operand(span) => { - span_err!(self.tcx.sess, span, E0483, + let mut err = struct_span_err!(self.tcx.sess, span, E0483, "lifetime of operand does not outlive \ the operation"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the operand is only valid for ", sup, ""); + err.emit(); } infer::AddrOf(span) => { - span_err!(self.tcx.sess, span, E0484, + let mut err = struct_span_err!(self.tcx.sess, span, E0484, "reference is not valid at the time of borrow"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the borrow is only valid for ", sup, ""); + err.emit(); } infer::AutoBorrow(span) => { - span_err!(self.tcx.sess, span, E0485, + let mut err = struct_span_err!(self.tcx.sess, span, E0485, "automatically reference is not valid \ at the time of borrow"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the automatic borrow is only valid for ", sup, ""); + err.emit(); } infer::ExprTypeIsNotInScope(t, span) => { - span_err!(self.tcx.sess, span, E0486, + let mut err = struct_span_err!(self.tcx.sess, span, E0486, "type of expression contains references \ that are not valid during the expression: `{}`", self.ty_to_string(t)); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "type is only valid for ", sup, ""); + err.emit(); } infer::SafeDestructor(span) => { - span_err!(self.tcx.sess, span, E0487, + let mut err = struct_span_err!(self.tcx.sess, span, E0487, "unsafe use of destructor: destructor might be called \ while references are dead"); // FIXME (22171): terms "super/subregion" are suboptimal - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "superregion: ", sup, ""); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "subregion: ", sub, ""); + err.emit(); } infer::BindingTypeIsNotValidAtDecl(span) => { - span_err!(self.tcx.sess, span, E0488, + let mut err = struct_span_err!(self.tcx.sess, span, E0488, "lifetime of variable does not enclose its declaration"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the variable is only valid for ", sup, ""); + err.emit(); } infer::ParameterInScope(_, span) => { - span_err!(self.tcx.sess, span, E0489, + let mut err = struct_span_err!(self.tcx.sess, span, E0489, "type/lifetime parameter not in scope here"); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the parameter is only valid for ", sub, ""); + err.emit(); } infer::DataBorrowed(ty, span) => { - span_err!(self.tcx.sess, span, E0490, + let mut err = struct_span_err!(self.tcx.sess, span, E0490, "a value of type `{}` is borrowed for too long", self.ty_to_string(ty)); - self.tcx.note_and_explain_region("the type is valid for ", sub, ""); - self.tcx.note_and_explain_region("but the borrow lasts for ", sup, ""); + self.tcx.note_and_explain_region(&mut err, "the type is valid for ", sub, ""); + self.tcx.note_and_explain_region(&mut err, "but the borrow lasts for ", sup, ""); + err.emit(); } infer::ReferenceOutlivesReferent(ty, span) => { - span_err!(self.tcx.sess, span, E0491, + let mut err = struct_span_err!(self.tcx.sess, span, E0491, "in type `{}`, reference has a longer lifetime \ than the data it references", self.ty_to_string(ty)); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "the pointer is valid for ", sub, ""); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "but the referenced data is only valid for ", sup, ""); + err.emit(); } } } @@ -884,37 +940,42 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { sub_region: Region, sup_origin: SubregionOrigin<'tcx>, sup_region: Region) { - self.report_inference_failure(var_origin); + let mut err = self.report_inference_failure(var_origin); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "first, the lifetime cannot outlive ", sup_region, "..."); - self.note_region_origin(&sup_origin); + self.note_region_origin(&mut err, &sup_origin); - self.tcx.note_and_explain_region( + self.tcx.note_and_explain_region(&mut err, "but, the lifetime must be valid for ", sub_region, "..."); - self.note_region_origin(&sub_origin); + self.note_region_origin(&mut err, &sub_origin); + err.emit(); } fn report_processed_errors(&self, var_origins: &[RegionVariableOrigin], trace_origins: &[(TypeTrace<'tcx>, TypeError<'tcx>)], same_regions: &[SameRegions]) { - for vo in var_origins { - self.report_inference_failure(vo.clone()); + for (i, vo) in var_origins.iter().enumerate() { + let mut err = self.report_inference_failure(vo.clone()); + if i == var_origins.len() - 1 { + self.give_suggestion(&mut err, same_regions); + } + err.emit(); } - self.give_suggestion(same_regions); + for &(ref trace, ref terr) in trace_origins { self.report_and_explain_type_error(trace.clone(), terr); } } - fn give_suggestion(&self, same_regions: &[SameRegions]) { + fn give_suggestion(&self, err: &mut DiagnosticBuilder, same_regions: &[SameRegions]) { let scope_id = same_regions[0].scope_id; let parent = self.tcx.map.get_parent(scope_id); let parent_node = self.tcx.map.find(parent); @@ -968,7 +1029,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { let rebuilder = Rebuilder::new(self.tcx, fn_decl, expl_self, generics, same_regions, &life_giver); let (fn_decl, expl_self, generics) = rebuilder.rebuild(); - self.give_expl_lifetime_param(&fn_decl, unsafety, constness, name, + self.give_expl_lifetime_param(err, &fn_decl, unsafety, constness, name, expl_self.as_ref(), &generics, span); } } @@ -1520,6 +1581,7 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> { impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { fn give_expl_lifetime_param(&self, + err: &mut DiagnosticBuilder, decl: &hir::FnDecl, unsafety: hir::Unsafety, constness: hir::Constness, @@ -1531,11 +1593,12 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { opt_explicit_self, generics); let msg = format!("consider using an explicit lifetime \ parameter as shown: {}", suggested_fn); - self.tcx.sess.span_help(span, &msg[..]); + err.span_help(span, &msg[..]); } fn report_inference_failure(&self, - var_origin: RegionVariableOrigin) { + var_origin: RegionVariableOrigin) + -> DiagnosticBuilder<'tcx> { let br_string = |br: ty::BoundRegion| { let mut s = br.to_string(); if !s.is_empty() { @@ -1574,13 +1637,13 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { } }; - span_err!(self.tcx.sess, var_origin.span(), E0495, + struct_span_err!(self.tcx.sess, var_origin.span(), E0495, "cannot infer an appropriate lifetime{} \ due to conflicting requirements", - var_description); + var_description) } - fn note_region_origin(&self, origin: &SubregionOrigin<'tcx>) { + fn note_region_origin(&self, err: &mut DiagnosticBuilder, origin: &SubregionOrigin<'tcx>) { match *origin { infer::Subtype(ref trace) => { let desc = match trace.origin { @@ -1622,7 +1685,7 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { match self.values_str(&trace.values) { Some(values_str) => { - self.tcx.sess.span_note( + err.span_note( trace.origin.span(), &format!("...so that {} ({})", desc, values_str)); @@ -1632,130 +1695,130 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { // all, since it is derived, but that would // require more refactoring than I feel like // doing right now. - nmatsakis - self.tcx.sess.span_note( + err.span_note( trace.origin.span(), &format!("...so that {}", desc)); } } } infer::Reborrow(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that reference does not outlive \ borrowed content"); } infer::ReborrowUpvar(span, ref upvar_id) => { - self.tcx.sess.span_note( + err.span_note( span, &format!( "...so that closure can access `{}`", self.tcx.local_var_name_str(upvar_id.var_id) - .to_string())) + .to_string())); } infer::InfStackClosure(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that closure does not outlive its stack frame"); } infer::InvokeClosure(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that closure is not invoked outside its lifetime"); } infer::DerefPointer(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that pointer is not dereferenced \ outside its lifetime"); } infer::FreeVariable(span, id) => { - self.tcx.sess.span_note( + err.span_note( span, &format!("...so that captured variable `{}` \ does not outlive the enclosing closure", self.tcx.local_var_name_str(id))); } infer::IndexSlice(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that slice is not indexed outside the lifetime"); } infer::RelateObjectBound(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that it can be closed over into an object"); } infer::CallRcvr(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that method receiver is valid for the method call"); } infer::CallArg(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that argument is valid for the call"); } infer::CallReturn(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that return value is valid for the call"); } infer::Operand(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that operand is valid for operation"); } infer::AddrOf(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that reference is valid \ at the time of borrow"); } infer::AutoBorrow(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that auto-reference is valid \ at the time of borrow"); } infer::ExprTypeIsNotInScope(t, span) => { - self.tcx.sess.span_note( + err.span_note( span, &format!("...so type `{}` of expression is valid during the \ expression", self.ty_to_string(t))); } infer::BindingTypeIsNotValidAtDecl(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that variable is valid at time of its declaration"); } infer::ParameterInScope(_, span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that a type/lifetime parameter is in scope here"); } infer::DataBorrowed(ty, span) => { - self.tcx.sess.span_note( + err.span_note( span, &format!("...so that the type `{}` is not borrowed for too long", self.ty_to_string(ty))); } infer::ReferenceOutlivesReferent(ty, span) => { - self.tcx.sess.span_note( + err.span_note( span, &format!("...so that the reference type `{}` \ does not outlive the data it points at", self.ty_to_string(ty))); } infer::RelateParamBound(span, t) => { - self.tcx.sess.span_note( + err.span_note( span, &format!("...so that the type `{}` \ will meet its required lifetime bounds", self.ty_to_string(t))); } infer::RelateDefaultParamBound(span, t) => { - self.tcx.sess.span_note( + err.span_note( span, &format!("...so that type parameter \ instantiated with `{}`, \ @@ -1763,16 +1826,16 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { self.ty_to_string(t))); } infer::RelateRegionParamBound(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that the declared lifetime parameter bounds \ are satisfied"); } infer::SafeDestructor(span) => { - self.tcx.sess.span_note( + err.span_note( span, "...so that references are valid when the destructor \ - runs") + runs"); } } } diff --git a/src/librustc/middle/infer/freshen.rs b/src/librustc/middle/infer/freshen.rs index 0bae3cd2d8690..76dd62383f1b1 100644 --- a/src/librustc/middle/infer/freshen.rs +++ b/src/librustc/middle/infer/freshen.rs @@ -30,8 +30,7 @@ //! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type //! inferencer knows "so far". -use middle::ty::{self, Ty, HasTypeFlags}; -use middle::ty::fold::TypeFoldable; +use middle::ty::{self, Ty, TypeFoldable}; use middle::ty::fold::TypeFolder; use std::collections::hash_map::{self, Entry}; @@ -169,7 +168,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { ty::TyTuple(..) | ty::TyProjection(..) | ty::TyParam(..) => { - ty::fold::super_fold_ty(self, t) + t.super_fold_with(self) } } } diff --git a/src/librustc/middle/infer/higher_ranked/mod.rs b/src/librustc/middle/infer/higher_ranked/mod.rs index ef6d9ae41914b..e8f542db933cb 100644 --- a/src/librustc/middle/infer/higher_ranked/mod.rs +++ b/src/librustc/middle/infer/higher_ranked/mod.rs @@ -14,9 +14,8 @@ use super::{CombinedSnapshot, InferCtxt, HigherRankedType, SkolemizationMap}; use super::combine::CombineFields; -use middle::ty::{self, Binder}; +use middle::ty::{self, Binder, TypeFoldable}; use middle::ty::error::TypeError; -use middle::ty::fold::TypeFoldable; use middle::ty::relate::{Relate, RelateResult, TypeRelation}; use syntax::codemap::Span; use util::nodemap::{FnvHashMap, FnvHashSet}; @@ -557,7 +556,7 @@ pub fn plug_leaks<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, snapshot: &CombinedSnapshot, value: &T) -> T - where T : TypeFoldable<'tcx> + ty::HasTypeFlags + where T : TypeFoldable<'tcx> { debug_assert!(leak_check(infcx, &skol_map, snapshot).is_ok()); diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index d0ffed56635f7..922d4c251bb64 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -30,7 +30,7 @@ use middle::subst::Subst; use middle::traits; use middle::ty::adjustment; use middle::ty::{TyVid, IntVid, FloatVid}; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty}; use middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use middle::ty::fold::{TypeFolder, TypeFoldable}; use middle::ty::relate::{Relate, RelateResult, TypeRelation}; @@ -40,6 +40,7 @@ use std::fmt; use syntax::ast; use syntax::codemap; use syntax::codemap::{Span, DUMMY_SP}; +use syntax::errors::DiagnosticBuilder; use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap}; use self::combine::CombineFields; @@ -511,7 +512,7 @@ pub struct CombinedSnapshot { } pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { debug!("normalize_associated_type(t={:?})", value); @@ -545,7 +546,7 @@ pub fn drain_fulfillment_cx_or_panic<'a,'tcx,T>(span: Span, fulfill_cx: &mut traits::FulfillmentContext<'tcx>, result: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { match drain_fulfillment_cx(infcx, fulfill_cx, result) { Ok(v) => v, @@ -569,7 +570,7 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, fulfill_cx: &mut traits::FulfillmentContext<'tcx>, result: &T) -> Result>> - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { debug!("drain_fulfillment_cx(result={:?})", result); @@ -928,7 +929,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { snapshot: &CombinedSnapshot, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { /*! See `higher_ranked::plug_leaks` */ @@ -1200,7 +1201,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn resolve_type_vars_if_possible(&self, value: &T) -> T - where T: TypeFoldable<'tcx> + HasTypeFlags + where T: TypeFoldable<'tcx> { /*! * Where possible, replaces type/int/float variables in @@ -1269,19 +1270,43 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { sp: Span, mk_msg: M, actual_ty: String, - err: Option<&TypeError<'tcx>>) where - M: FnOnce(Option, String) -> String, + err: Option<&TypeError<'tcx>>) + where M: FnOnce(Option, String) -> String, { self.type_error_message_str_with_expected(sp, mk_msg, None, actual_ty, err) } + pub fn type_error_struct_str(&self, + sp: Span, + mk_msg: M, + actual_ty: String, + err: Option<&TypeError<'tcx>>) + -> DiagnosticBuilder<'tcx> + where M: FnOnce(Option, String) -> String, + { + self.type_error_struct_str_with_expected(sp, mk_msg, None, actual_ty, err) + } + pub fn type_error_message_str_with_expected(&self, sp: Span, mk_msg: M, expected_ty: Option>, actual_ty: String, - err: Option<&TypeError<'tcx>>) where - M: FnOnce(Option, String) -> String, + err: Option<&TypeError<'tcx>>) + where M: FnOnce(Option, String) -> String, + { + self.type_error_struct_str_with_expected(sp, mk_msg, expected_ty, actual_ty, err) + .emit(); + } + + pub fn type_error_struct_str_with_expected(&self, + sp: Span, + mk_msg: M, + expected_ty: Option>, + actual_ty: String, + err: Option<&TypeError<'tcx>>) + -> DiagnosticBuilder<'tcx> + where M: FnOnce(Option, String) -> String, { debug!("hi! expected_ty = {:?}, actual_ty = {}", expected_ty, actual_ty); @@ -1292,13 +1317,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { format!(" ({})", t_err) }); - self.tcx.sess.span_err(sp, &format!("{}{}", + let mut db = self.tcx.sess.struct_span_err(sp, &format!("{}{}", mk_msg(resolved_expected.map(|t| self.ty_to_string(t)), actual_ty), error_str)); if let Some(err) = err { - self.tcx.note_and_explain_type_err(err, sp) + self.tcx.note_and_explain_type_err(&mut db, err, sp); } + db + } else { + self.tcx.sess.diagnostic().struct_dummy() } } @@ -1306,19 +1334,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { sp: Span, mk_msg: M, actual_ty: Ty<'tcx>, - err: Option<&TypeError<'tcx>>) where - M: FnOnce(String) -> String, + err: Option<&TypeError<'tcx>>) + where M: FnOnce(String) -> String, + { + self.type_error_struct(sp, mk_msg, actual_ty, err).emit(); + } + + pub fn type_error_struct(&self, + sp: Span, + mk_msg: M, + actual_ty: Ty<'tcx>, + err: Option<&TypeError<'tcx>>) + -> DiagnosticBuilder<'tcx> + where M: FnOnce(String) -> String, { let actual_ty = self.resolve_type_vars_if_possible(&actual_ty); // Don't report an error if actual type is TyError. if actual_ty.references_error() { - return; + return self.tcx.sess.diagnostic().struct_dummy(); } - self.type_error_message_str(sp, + self.type_error_struct_str(sp, move |_e, a| { mk_msg(a) }, - self.ty_to_string(actual_ty), err); + self.ty_to_string(actual_ty), err) } pub fn report_mismatched_types(&self, diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index a63c662de6165..2c2b69ff85b4e 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -489,13 +489,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { origin); match (sub, sup) { - (ReEarlyBound(..), ReEarlyBound(..)) => { - // This case is used only to make sure that explicitly-specified - // `Self` types match the real self type in implementations. - // - // FIXME(NDM) -- we really shouldn't be comparing bound things - self.add_verify(VerifyRegSubReg(origin, sub, sup)); - } (ReEarlyBound(..), _) | (ReLateBound(..), _) | (_, ReEarlyBound(..)) | diff --git a/src/librustc/middle/infer/resolve.rs b/src/librustc/middle/infer/resolve.rs index 5190c658194f7..c68d0a9fa5683 100644 --- a/src/librustc/middle/infer/resolve.rs +++ b/src/librustc/middle/infer/resolve.rs @@ -9,8 +9,7 @@ // except according to those terms. use super::{InferCtxt, FixupError, FixupResult}; -use middle::ty::{self, Ty, HasTypeFlags}; -use middle::ty::fold::{TypeFoldable}; +use middle::ty::{self, Ty, TypeFoldable}; /////////////////////////////////////////////////////////////////////////// // OPPORTUNISTIC TYPE RESOLVER @@ -40,7 +39,7 @@ impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for OpportunisticTypeResolver<'a, 'tcx t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t0 = self.infcx.shallow_resolve(t); - ty::fold::super_fold_ty(self, t0) + t0.super_fold_with(self) } } } @@ -68,7 +67,7 @@ impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for OpportunisticTypeAndRegionResolver t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t0 = self.infcx.shallow_resolve(t); - ty::fold::super_fold_ty(self, t0) + t0.super_fold_with(self) } } @@ -133,7 +132,7 @@ impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { t)); } _ => { - ty::fold::super_fold_ty(self, t) + t.super_fold_with(self) } } } diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index 48d7f44063ec3..69b952ca1f3fc 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -8,11 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use dep_graph::DepNode; use middle::def::DefFn; use middle::def_id::DefId; use middle::subst::{Subst, Substs, EnumeratedItems}; use middle::ty::{TransmuteRestriction, ctxt, TyBareFn}; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, TypeFoldable}; use std::fmt; @@ -29,7 +30,7 @@ pub fn check_crate(tcx: &ctxt) { dummy_sized_ty: tcx.types.isize, dummy_unsized_ty: tcx.mk_slice(tcx.types.isize), }; - tcx.map.krate().visit_all_items(&mut visitor); + tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor); } struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> { diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 540af4ae00165..29299f01ed36f 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1500,7 +1500,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { }, _ => false }; - span_err!(self.ir.tcx.sess, sp, E0269, "not all control paths return a value"); + let mut err = struct_span_err!(self.ir.tcx.sess, + sp, + E0269, + "not all control paths return a value"); if ends_with_stmt { let last_stmt = body.stmts.first().unwrap(); let original_span = original_sp(self.ir.tcx.sess.codemap(), @@ -1510,9 +1513,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hi: original_span.hi, expn_id: original_span.expn_id }; - self.ir.tcx.sess.span_help( - span_semicolon, "consider removing this semicolon:"); + err.span_help(span_semicolon, "consider removing this semicolon:"); } + err.emit(); } } ty::FnDiverging diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index d146ad2d8003b..738440adf416d 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -15,6 +15,7 @@ // makes all other generics or inline functions that it references // reachable as well. +use dep_graph::DepNode; use front::map as ast_map; use middle::def; use middle::def_id::DefId; @@ -349,6 +350,7 @@ impl<'a, 'v> Visitor<'v> for CollectPrivateImplItemsVisitor<'a> { pub fn find_reachable(tcx: &ty::ctxt, access_levels: &privacy::AccessLevels) -> NodeSet { + let _task = tcx.dep_graph.in_task(DepNode::Reachability); let mut reachable_context = ReachableContext::new(tcx); diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 9b133c5401519..2c74f3a82e414 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -345,25 +345,25 @@ impl ShadowKind { } } -fn signal_shadowing_problem( - sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) { - if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) { +fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) { + let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) { // lifetime/lifetime shadowing is an error - span_err!(sess, shadower.span, E0496, - "{} name `{}` shadows a \ - {} name that is already in scope", - shadower.kind.desc(), name, orig.kind.desc()); + struct_span_err!(sess, shadower.span, E0496, + "{} name `{}` shadows a \ + {} name that is already in scope", + shadower.kind.desc(), name, orig.kind.desc()) } else { // shadowing involving a label is only a warning, due to issues with // labels and lifetimes not being macro-hygienic. - sess.span_warn(shadower.span, - &format!("{} name `{}` shadows a \ - {} name that is already in scope", - shadower.kind.desc(), name, orig.kind.desc())); - } - sess.span_note(orig.span, - &format!("shadowed {} `{}` declared here", - orig.kind.desc(), name)); + sess.struct_span_warn(shadower.span, + &format!("{} name `{}` shadows a \ + {} name that is already in scope", + shadower.kind.desc(), name, orig.kind.desc())) + }; + err.span_note(orig.span, + &format!("shadowed {} `{}` declared here", + orig.kind.desc(), name)); + err.emit(); } // Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index a41ee51fb5546..8d5c0c98885b2 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -13,6 +13,7 @@ pub use self::StabilityLevel::*; +use dep_graph::DepNode; use session::Session; use lint; use middle::cstore::{CrateStore, LOCAL_CRATE}; @@ -328,6 +329,7 @@ impl<'tcx> Index<'tcx> { /// features used. pub fn check_unstable_api_usage(tcx: &ty::ctxt) -> FnvHashMap { + let _task = tcx.dep_graph.in_task(DepNode::StabilityCheck); let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features; // Put the active features into a map for quick lookup @@ -341,8 +343,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt) }; intravisit::walk_crate(&mut checker, tcx.map.krate()); - let used_features = checker.used_features; - return used_features; + checker.used_features } struct Checker<'a, 'tcx: 'a> { @@ -394,17 +395,19 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { // This is an 'unmarked' API, which should not exist // in the standard library. if self.tcx.sess.features.borrow().unmarked_api { - self.tcx.sess.span_warn(span, "use of unmarked library feature"); - self.tcx.sess.span_note(span, "this is either a bug in the library you are \ + self.tcx.sess.struct_span_warn(span, "use of unmarked library feature") + .span_note(span, "this is either a bug in the library you are \ using or a bug in the compiler - please \ - report it in both places"); + report it in both places") + .emit() } else { - self.tcx.sess.span_err(span, "use of unmarked library feature"); - self.tcx.sess.span_note(span, "this is either a bug in the library you are \ + self.tcx.sess.struct_span_err(span, "use of unmarked library feature") + .span_note(span, "this is either a bug in the library you are \ using or a bug in the compiler - please \ - report it in both places"); - self.tcx.sess.span_note(span, "use #![feature(unmarked_api)] in the \ - crate attributes to override this"); + report it in both places") + .span_note(span, "use #![feature(unmarked_api)] in the \ + crate attributes to override this") + .emit() } } } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index aa47b32dc3e4d..61f7b2db4c432 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -14,7 +14,7 @@ pub use self::ParamSpace::*; pub use self::RegionSubsts::*; use middle::cstore; -use middle::ty::{self, Ty, HasTypeFlags, RegionEscape}; +use middle::ty::{self, Ty}; use middle::ty::fold::{TypeFoldable, TypeFolder}; use serialize::{Encodable, Encoder, Decodable, Decoder}; @@ -674,7 +674,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { self.ty_for_param(p, t) } _ => { - ty::fold::super_fold_ty(self, t) + t.super_fold_with(self) } }; diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index 9cf8043be3ba1..883c5e7bb40eb 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -26,13 +26,15 @@ use super::{ use fmt_macros::{Parser, Piece, Position}; use middle::def_id::DefId; use middle::infer::InferCtxt; -use middle::ty::{self, ToPredicate, HasTypeFlags, ToPolyTraitRef, TraitRef, Ty}; -use middle::ty::fold::TypeFoldable; +use middle::ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TypeFoldable}; +use middle::ty::fast_reject; use util::nodemap::{FnvHashMap, FnvHashSet}; +use std::cmp; use std::fmt; -use syntax::codemap::Span; use syntax::attr::{AttributeMethods, AttrMetaMethods}; +use syntax::codemap::Span; +use syntax::errors::DiagnosticBuilder; #[derive(Debug, PartialEq, Eq, Hash)] pub struct TraitErrorKey<'tcx> { @@ -94,12 +96,12 @@ pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, // then $X will be unified with TyError, but the error still needs to be // reported. if !infcx.tcx.sess.has_errors() || !predicate.references_error() { - span_err!( - infcx.tcx.sess, obligation.cause.span, E0271, + let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0271, "type mismatch resolving `{}`: {}", predicate, error.err); - note_obligation_cause(infcx, obligation); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } } @@ -182,18 +184,19 @@ fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, obligation: &Obligation<'tcx, T>) -> ! - where T: fmt::Display + TypeFoldable<'tcx> + HasTypeFlags + where T: fmt::Display + TypeFoldable<'tcx> { let predicate = infcx.resolve_type_vars_if_possible(&obligation.predicate); - span_err!(infcx.tcx.sess, obligation.cause.span, E0275, - "overflow evaluating the requirement `{}`", - predicate); + let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0275, + "overflow evaluating the requirement `{}`", + predicate); - suggest_new_overflow_limit(infcx.tcx, obligation.cause.span); + suggest_new_overflow_limit(infcx.tcx, &mut err, obligation.cause.span); - note_obligation_cause(infcx, obligation); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); infcx.tcx.sess.abort_if_errors(); unreachable!(); } @@ -218,7 +221,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, if !infcx.tcx.sess.has_errors() || !trait_predicate.references_error() { let trait_ref = trait_predicate.to_poly_trait_ref(); - span_err!( + let mut err = struct_span_err!( infcx.tcx.sess, obligation.cause.span, E0277, "the trait `{}` is not implemented for the type `{}`", trait_ref, trait_ref.self_ty()); @@ -228,54 +231,100 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, let custom_note = report_on_unimplemented(infcx, &trait_ref.0, obligation.cause.span); if let Some(s) = custom_note { - infcx.tcx.sess.fileline_note(obligation.cause.span, &s); + err.fileline_note(obligation.cause.span, &s); + } else { + let simp = fast_reject::simplify_type(infcx.tcx, + trait_ref.self_ty(), + true); + let mut impl_candidates = Vec::new(); + let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id()); + + match simp { + Some(simp) => trait_def.for_each_impl(infcx.tcx, |def_id| { + let imp = infcx.tcx.impl_trait_ref(def_id).unwrap(); + let imp_simp = fast_reject::simplify_type(infcx.tcx, + imp.self_ty(), + true); + if let Some(imp_simp) = imp_simp { + if simp != imp_simp { + return; + } + } + impl_candidates.push(imp); + }), + None => trait_def.for_each_impl(infcx.tcx, |def_id| { + impl_candidates.push( + infcx.tcx.impl_trait_ref(def_id).unwrap()); + }) + }; + + if impl_candidates.len() > 0 { + err.fileline_help( + obligation.cause.span, + &format!("the following implementations were found:")); + + let end = cmp::min(4, impl_candidates.len()); + for candidate in &impl_candidates[0..end] { + err.fileline_help(obligation.cause.span, + &format!(" {:?}", candidate)); + } + if impl_candidates.len() > 4 { + err.fileline_help(obligation.cause.span, + &format!("and {} others", + impl_candidates.len()-4)); + } + } } - note_obligation_cause(infcx, obligation); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } - } - + }, ty::Predicate::Equate(ref predicate) => { let predicate = infcx.resolve_type_vars_if_possible(predicate); let err = infcx.equality_predicate(obligation.cause.span, &predicate).err().unwrap(); - span_err!( + let mut err = struct_span_err!( infcx.tcx.sess, obligation.cause.span, E0278, "the requirement `{}` is not satisfied (`{}`)", predicate, err); - note_obligation_cause(infcx, obligation); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } ty::Predicate::RegionOutlives(ref predicate) => { let predicate = infcx.resolve_type_vars_if_possible(predicate); let err = infcx.region_outlives_predicate(obligation.cause.span, &predicate).err().unwrap(); - span_err!( + let mut err = struct_span_err!( infcx.tcx.sess, obligation.cause.span, E0279, "the requirement `{}` is not satisfied (`{}`)", predicate, err); - note_obligation_cause(infcx, obligation); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => { let predicate = infcx.resolve_type_vars_if_possible(&obligation.predicate); - span_err!( + let mut err = struct_span_err!( infcx.tcx.sess, obligation.cause.span, E0280, "the requirement `{}` is not satisfied", predicate); - note_obligation_cause(infcx, obligation); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } ty::Predicate::ObjectSafe(trait_def_id) => { let violations = object_safety_violations( infcx.tcx, trait_def_id); - report_object_safety_error(infcx.tcx, - obligation.cause.span, - trait_def_id, - violations); - note_obligation_cause(infcx, obligation); + let mut err = report_object_safety_error(infcx.tcx, + obligation.cause.span, + trait_def_id, + violations); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } ty::Predicate::WellFormed(ty) => { @@ -296,7 +345,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, 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); if !actual_trait_ref.self_ty().references_error() { - span_err!( + let mut err = struct_span_err!( infcx.tcx.sess, obligation.cause.span, E0281, "type mismatch: the type `{}` implements the trait `{}`, \ but the trait `{}` is required ({})", @@ -304,14 +353,17 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, expected_trait_ref, actual_trait_ref, e); - note_obligation_cause(infcx, obligation); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } } TraitNotObjectSafe(did) => { let violations = object_safety_violations(infcx.tcx, did); - report_object_safety_error(infcx.tcx, obligation.cause.span, did, violations); - note_obligation_cause(infcx, obligation); + let mut err = report_object_safety_error(infcx.tcx, obligation.cause.span, did, + violations); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } } } @@ -320,8 +372,9 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, trait_def_id: DefId, violations: Vec) + -> DiagnosticBuilder<'tcx> { - span_err!( + let mut err = struct_span_err!( tcx.sess, span, E0038, "the trait `{}` cannot be made into an object", tcx.item_path_str(trait_def_id)); @@ -333,13 +386,13 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>, } match violation { ObjectSafetyViolation::SizedSelf => { - tcx.sess.fileline_note( + err.fileline_note( span, "the trait cannot require that `Self : Sized`"); } ObjectSafetyViolation::SupertraitSelf => { - tcx.sess.fileline_note( + err.fileline_note( span, "the trait cannot use `Self` as a type parameter \ in the supertrait listing"); @@ -347,7 +400,7 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>, ObjectSafetyViolation::Method(method, MethodViolationCode::StaticMethod) => { - tcx.sess.fileline_note( + err.fileline_note( span, &format!("method `{}` has no receiver", method.name)); @@ -355,7 +408,7 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>, ObjectSafetyViolation::Method(method, MethodViolationCode::ReferencesSelf) => { - tcx.sess.fileline_note( + err.fileline_note( span, &format!("method `{}` references the `Self` type \ in its arguments or return type", @@ -364,13 +417,14 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>, ObjectSafetyViolation::Method(method, MethodViolationCode::Generic) => { - tcx.sess.fileline_note( + err.fileline_note( span, &format!("method `{}` has generic type parameters", method.name)); } } } + err } pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, @@ -424,10 +478,12 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, { need_type_info(infcx, obligation.cause.span, self_ty); } else { - span_err!(infcx.tcx.sess, obligation.cause.span, E0283, - "type annotations required: cannot resolve `{}`", - predicate); - note_obligation_cause(infcx, obligation); + let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0283, + "type annotations required: \ + cannot resolve `{}`", + predicate); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } } } @@ -443,10 +499,11 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, _ => { if !infcx.tcx.sess.has_errors() { - span_err!(infcx.tcx.sess, obligation.cause.span, E0284, - "type annotations required: cannot resolve `{}`", - predicate); - note_obligation_cause(infcx, obligation); + let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0284, + "type annotations required: cannot resolve `{}`", + predicate); + note_obligation_cause(infcx, &mut err, obligation); + err.emit(); } } } @@ -463,16 +520,19 @@ fn need_type_info<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, } fn note_obligation_cause<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, + err: &mut DiagnosticBuilder, obligation: &Obligation<'tcx, T>) where T: fmt::Display { note_obligation_cause_code(infcx, + err, &obligation.predicate, obligation.cause.span, &obligation.cause.code); } fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, + err: &mut DiagnosticBuilder, predicate: &T, cause_span: Span, cause_code: &ObligationCauseCode<'tcx>) @@ -482,59 +542,59 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, match *cause_code { ObligationCauseCode::MiscObligation => { } ObligationCauseCode::SliceOrArrayElem => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, "slice and array elements must have `Sized` type"); } ObligationCauseCode::ProjectionWf(data) => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, &format!("required so that the projection `{}` is well-formed", data)); } ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, &format!("required so that reference `{}` does not outlive its referent", ref_ty)); } ObligationCauseCode::ItemObligation(item_def_id) => { let item_name = tcx.item_path_str(item_def_id); - tcx.sess.fileline_note( + err.fileline_note( cause_span, &format!("required by `{}`", item_name)); } ObligationCauseCode::ObjectCastObligation(object_ty) => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, &format!( "required for the cast to the object type `{}`", infcx.ty_to_string(object_ty))); } ObligationCauseCode::RepeatVec => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, "the `Copy` trait is required because the \ repeated element will be copied"); } ObligationCauseCode::VariableType(_) => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, "all local variables must have a statically known size"); } ObligationCauseCode::ReturnType => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, "the return type of a function must have a \ statically known size"); } ObligationCauseCode::AssignmentLhsSized => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, "the left-hand-side of an assignment must have a statically known size"); } ObligationCauseCode::StructInitializerSized => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, "structs must have a statically known size to be initialized"); } @@ -542,7 +602,7 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, let def_id = tcx.lang_items.from_builtin_kind(builtin_bound).unwrap(); let trait_name = tcx.item_path_str(def_id); let name = tcx.local_var_name_str(var_id); - tcx.sess.fileline_note( + err.fileline_note( cause_span, &format!("the closure that captures `{}` requires that all captured variables \ implement the trait `{}`", @@ -550,37 +610,45 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, trait_name)); } ObligationCauseCode::FieldSized => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, "only the last field of a struct or enum variant \ may have a dynamically sized type"); } ObligationCauseCode::SharedStatic => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, "shared static variables must have a type that implements `Sync`"); } ObligationCauseCode::BuiltinDerivedObligation(ref data) => { let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref); - tcx.sess.fileline_note( + err.fileline_note( cause_span, &format!("required because it appears within the type `{}`", parent_trait_ref.0.self_ty())); let parent_predicate = parent_trait_ref.to_predicate(); - note_obligation_cause_code(infcx, &parent_predicate, cause_span, &*data.parent_code); + note_obligation_cause_code(infcx, + err, + &parent_predicate, + cause_span, + &*data.parent_code); } ObligationCauseCode::ImplDerivedObligation(ref data) => { let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref); - tcx.sess.fileline_note( + err.fileline_note( cause_span, &format!("required because of the requirements on the impl of `{}` for `{}`", parent_trait_ref, parent_trait_ref.0.self_ty())); let parent_predicate = parent_trait_ref.to_predicate(); - note_obligation_cause_code(infcx, &parent_predicate, cause_span, &*data.parent_code); + note_obligation_cause_code(infcx, + err, + &parent_predicate, + cause_span, + &*data.parent_code); } ObligationCauseCode::CompareImplMethodObligation => { - tcx.sess.fileline_note( + err.fileline_note( cause_span, &format!("the requirement `{}` appears on the impl method \ but not on the corresponding trait method", @@ -589,10 +657,10 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, } } -fn suggest_new_overflow_limit(tcx: &ty::ctxt, span: Span) { +fn suggest_new_overflow_limit(tcx: &ty::ctxt, err:&mut DiagnosticBuilder, span: Span) { let current_limit = tcx.sess.recursion_limit.get(); let suggested_limit = current_limit * 2; - tcx.sess.fileline_note( + err.fileline_note( span, &format!( "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index b93961f1aa9d2..4f8f6b846a6f4 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -9,7 +9,7 @@ // except according to those terms. use middle::infer::InferCtxt; -use middle::ty::{self, RegionEscape, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, TypeFoldable}; use syntax::ast; use util::common::ErrorReported; diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 255680465ca7f..6cf841cc47756 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -15,11 +15,12 @@ pub use self::FulfillmentErrorCode::*; pub use self::Vtable::*; pub use self::ObligationCauseCode::*; +use dep_graph::DepNode; use middle::def_id::DefId; use middle::free_region::FreeRegionMap; use middle::subst; -use middle::ty::{self, HasTypeFlags, Ty}; -use middle::ty::fold::TypeFoldable; +use middle::ty::{self, Ty, TypeFoldable}; +use middle::ty::fast_reject; use middle::infer::{self, fixup_err_to_string, InferCtxt}; use std::rc::Rc; @@ -468,7 +469,7 @@ pub fn fully_normalize<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, cause: ObligationCause<'tcx>, value: &T) -> Result>> - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { debug!("normalize_param_env(value={:?})", value); @@ -599,6 +600,18 @@ impl<'tcx> FulfillmentError<'tcx> { } impl<'tcx> TraitObligation<'tcx> { + /// Creates the dep-node for selecting/evaluating this trait reference. + fn dep_node(&self, tcx: &ty::ctxt<'tcx>) -> DepNode { + let simplified_ty = + fast_reject::simplify_type(tcx, + self.predicate.skip_binder().self_ty(), // (*) + true); + + // (*) skip_binder is ok because `simplify_type` doesn't care about regions + + DepNode::TraitSelect(self.predicate.def_id(), simplified_ty) + } + fn self_ty(&self) -> ty::Binder> { ty::Binder(self.predicate.skip_binder().self_ty()) } diff --git a/src/librustc/middle/traits/object_safety.rs b/src/librustc/middle/traits/object_safety.rs index bd60d0a212252..0e4a42bd15134 100644 --- a/src/librustc/middle/traits/object_safety.rs +++ b/src/librustc/middle/traits/object_safety.rs @@ -23,7 +23,7 @@ use super::elaborate_predicates; use middle::def_id::DefId; use middle::subst::{self, SelfSpace, TypeSpace}; use middle::traits; -use middle::ty::{self, HasTypeFlags, ToPolyTraitRef, Ty}; +use middle::ty::{self, ToPolyTraitRef, Ty, TypeFoldable}; use std::rc::Rc; use syntax::ast; @@ -254,13 +254,13 @@ fn virtual_call_violation_for_method<'tcx>(tcx: &ty::ctxt<'tcx>, // autorefs) to `&self`. For now, we only accept `self`, `&self` // and `Box`. match method.explicit_self { - ty::StaticExplicitSelfCategory => { + ty::ExplicitSelfCategory::Static => { return Some(MethodViolationCode::StaticMethod); } - ty::ByValueExplicitSelfCategory | - ty::ByReferenceExplicitSelfCategory(..) | - ty::ByBoxExplicitSelfCategory => { + ty::ExplicitSelfCategory::ByValue | + ty::ExplicitSelfCategory::ByReference(..) | + ty::ExplicitSelfCategory::ByBox => { } } diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 8fbfd513c78d6..ad3524661d326 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -23,7 +23,7 @@ use super::util; use middle::infer::{self, TypeOrigin}; use middle::subst::Subst; -use middle::ty::{self, ToPredicate, RegionEscape, HasTypeFlags, ToPolyTraitRef, Ty}; +use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty}; use middle::ty::fold::{TypeFoldable, TypeFolder}; use syntax::parse::token; use util::common::FN_OUTPUT_NAME; @@ -202,7 +202,7 @@ pub fn normalize<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>, cause: ObligationCause<'tcx>, value: &T) -> Normalized<'tcx, T> - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { normalize_with_depth(selcx, cause, 0, value) } @@ -213,7 +213,7 @@ pub fn normalize_with_depth<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tc depth: usize, value: &T) -> Normalized<'tcx, T> - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth); let result = normalizer.fold(value); @@ -245,7 +245,7 @@ impl<'a,'b,'tcx> AssociatedTypeNormalizer<'a,'b,'tcx> { } } - fn fold + HasTypeFlags>(&mut self, value: &T) -> T { + fn fold>(&mut self, value: &T) -> T { let value = self.selcx.infcx().resolve_type_vars_if_possible(value); if !value.has_projection_types() { @@ -273,7 +273,7 @@ impl<'a,'b,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'b,'tcx> { // normalize it when we instantiate those bound regions (which // should occur eventually). - let ty = ty::fold::super_fold_ty(self, ty); + let ty = ty.super_fold_with(self); match ty.sty { ty::TyProjection(ref data) if !data.has_escaping_regions() => { // (*) diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index b0215675fca81..f544f8ce36234 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -39,9 +39,8 @@ use middle::def_id::DefId; use middle::infer; use middle::infer::{InferCtxt, TypeFreshener, TypeOrigin}; use middle::subst::{Subst, Substs, TypeSpace}; -use middle::ty::{self, ToPredicate, RegionEscape, ToPolyTraitRef, Ty, HasTypeFlags}; +use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TypeFoldable}; use middle::ty::fast_reject; -use middle::ty::fold::TypeFoldable; use middle::ty::relate::TypeRelation; use std::cell::RefCell; @@ -310,6 +309,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("select({:?})", obligation); assert!(!obligation.predicate.has_escaping_regions()); + let dep_node = obligation.dep_node(self.tcx()); + let _task = self.tcx().dep_graph.in_task(dep_node); + let stack = self.push_stack(TraitObligationStackList::empty(), obligation); match try!(self.candidate_from_obligation(&stack)) { None => { @@ -411,7 +413,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// accurate if inference variables are involved. pub fn evaluate_obligation_conservatively(&mut self, obligation: &PredicateObligation<'tcx>) - -> bool + -> bool { debug!("evaluate_obligation_conservatively({:?})", obligation); @@ -962,7 +964,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match *candidate { Ok(Some(_)) | Err(_) => true, Ok(None) => { - cache_fresh_trait_pred.0.input_types().has_infer_types() + cache_fresh_trait_pred.0.trait_ref.substs.types.has_infer_types() } } } diff --git a/src/librustc/middle/traits/structural_impls.rs b/src/librustc/middle/traits/structural_impls.rs index 8ce211120019d..453420e2a54dc 100644 --- a/src/librustc/middle/traits/structural_impls.rs +++ b/src/librustc/middle/traits/structural_impls.rs @@ -10,8 +10,7 @@ use middle::traits; use middle::traits::project::Normalized; -use middle::ty::{HasTypeFlags, TypeFlags, RegionEscape}; -use middle::ty::fold::{TypeFoldable, TypeFolder}; +use middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use std::fmt; @@ -131,130 +130,89 @@ impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> { } } -impl<'tcx, P: RegionEscape> RegionEscape for traits::Obligation<'tcx,P> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.predicate.has_regions_escaping_depth(depth) - } -} - -impl<'tcx, T: HasTypeFlags> HasTypeFlags for traits::Obligation<'tcx, T> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.predicate.has_type_flags(flags) - } -} - -impl<'tcx, T: HasTypeFlags> HasTypeFlags for Normalized<'tcx, T> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.value.has_type_flags(flags) || - self.obligations.has_type_flags(flags) - } -} - -impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::VtableImplData<'tcx, N> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.substs.has_type_flags(flags) || - self.nested.has_type_flags(flags) - } -} - -impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::VtableClosureData<'tcx, N> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.substs.has_type_flags(flags) || - self.nested.has_type_flags(flags) - } -} - -impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::VtableDefaultImplData { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.nested.has_type_flags(flags) - } -} - -impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::VtableBuiltinData { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.nested.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for traits::VtableObjectData<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.upcast_trait_ref.has_type_flags(flags) - } -} - -impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::Vtable<'tcx, N> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - match *self { - traits::VtableImpl(ref v) => v.has_type_flags(flags), - traits::VtableDefaultImpl(ref t) => t.has_type_flags(flags), - traits::VtableClosure(ref d) => d.has_type_flags(flags), - traits::VtableFnPointer(ref d) => d.has_type_flags(flags), - traits::VtableParam(ref n) => n.has_type_flags(flags), - traits::VtableBuiltin(ref d) => d.has_type_flags(flags), - traits::VtableObject(ref d) => d.has_type_flags(flags) - } - } -} - impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx, O> { - fn fold_with>(&self, folder: &mut F) -> traits::Obligation<'tcx, O> { + fn super_fold_with>(&self, folder: &mut F) -> Self { traits::Obligation { cause: self.cause.clone(), recursion_depth: self.recursion_depth, predicate: self.predicate.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.predicate.visit_with(visitor) + } } impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableImplData<'tcx, N> { - fn fold_with>(&self, folder: &mut F) -> traits::VtableImplData<'tcx, N> { + fn super_fold_with>(&self, folder: &mut F) -> Self { traits::VtableImplData { impl_def_id: self.impl_def_id, substs: self.substs.fold_with(folder), nested: self.nested.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.substs.visit_with(visitor) || self.nested.visit_with(visitor) + } } impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> { - fn fold_with>(&self, folder: &mut F) -> traits::VtableClosureData<'tcx, N> { + fn super_fold_with>(&self, folder: &mut F) -> Self { traits::VtableClosureData { closure_def_id: self.closure_def_id, substs: self.substs.fold_with(folder), nested: self.nested.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.substs.visit_with(visitor) || self.nested.visit_with(visitor) + } } impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableDefaultImplData { - fn fold_with>(&self, folder: &mut F) -> traits::VtableDefaultImplData { + fn super_fold_with>(&self, folder: &mut F) -> Self { traits::VtableDefaultImplData { trait_def_id: self.trait_def_id, nested: self.nested.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.nested.visit_with(visitor) + } } impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinData { - fn fold_with>(&self, folder: &mut F) -> traits::VtableBuiltinData { + fn super_fold_with>(&self, folder: &mut F) -> Self { traits::VtableBuiltinData { nested: self.nested.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.nested.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx> { - fn fold_with>(&self, folder: &mut F) -> traits::VtableObjectData<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { traits::VtableObjectData { upcast_trait_ref: self.upcast_trait_ref.fold_with(folder), vtable_base: self.vtable_base } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.upcast_trait_ref.visit_with(visitor) + } } impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> { - fn fold_with>(&self, folder: &mut F) -> traits::Vtable<'tcx, N> { + fn super_fold_with>(&self, folder: &mut F) -> Self { match *self { traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)), traits::VtableDefaultImpl(ref t) => traits::VtableDefaultImpl(t.fold_with(folder)), @@ -269,13 +227,29 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> traits::VtableObject(ref d) => traits::VtableObject(d.fold_with(folder)), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + traits::VtableImpl(ref v) => v.visit_with(visitor), + traits::VtableDefaultImpl(ref t) => t.visit_with(visitor), + traits::VtableClosure(ref d) => d.visit_with(visitor), + traits::VtableFnPointer(ref d) => d.visit_with(visitor), + traits::VtableParam(ref n) => n.visit_with(visitor), + traits::VtableBuiltin(ref d) => d.visit_with(visitor), + traits::VtableObject(ref d) => d.visit_with(visitor), + } + } } impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Normalized<'tcx, T> { - fn fold_with>(&self, folder: &mut F) -> Normalized<'tcx, T> { + fn super_fold_with>(&self, folder: &mut F) -> Self { Normalized { value: self.value.fold_with(folder), obligations: self.obligations.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.value.visit_with(visitor) || self.obligations.visit_with(visitor) + } } diff --git a/src/librustc/middle/ty/adjustment.rs b/src/librustc/middle/ty/adjustment.rs index c44ebcfdb693c..6cab0baa55325 100644 --- a/src/librustc/middle/ty/adjustment.rs +++ b/src/librustc/middle/ty/adjustment.rs @@ -11,8 +11,7 @@ pub use self::AutoAdjustment::*; pub use self::AutoRef::*; -use middle::ty::{self, Ty, TypeAndMut}; -use middle::ty::HasTypeFlags; +use middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; use middle::ty::LvaluePreference::{NoPreference}; use syntax::ast; diff --git a/src/librustc/middle/ty/contents.rs b/src/librustc/middle/ty/contents.rs index afe88f70d9450..619201a4a9feb 100644 --- a/src/librustc/middle/ty/contents.rs +++ b/src/librustc/middle/ty/contents.rs @@ -10,7 +10,7 @@ use middle::def_id::{DefId}; use middle::ty::{self, Ty}; -use util::common::{memoized}; +use util::common::MemoizationMap; use util::nodemap::FnvHashMap; use std::fmt; @@ -141,9 +141,7 @@ impl fmt::Debug for TypeContents { impl<'tcx> ty::TyS<'tcx> { pub fn type_contents(&'tcx self, cx: &ty::ctxt<'tcx>) -> TypeContents { - return memoized(&cx.tc_cache, self, |ty| { - tc_ty(cx, ty, &mut FnvHashMap()) - }); + return cx.tc_cache.memoize(self, || tc_ty(cx, self, &mut FnvHashMap())); fn tc_ty<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>, diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index cee651743ca86..d1504d25288a8 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -13,6 +13,7 @@ // FIXME: (@jroesch) @eddyb should remove this when he renames ctxt #![allow(non_camel_case_types)] +use dep_graph::{DepGraph, DepTrackingMap}; use front::map as ast_map; use session::Session; use lint; @@ -29,10 +30,12 @@ use middle::traits; use middle::ty::{self, TraitRef, Ty, TypeAndMut}; use middle::ty::{TyS, TypeVariants}; use middle::ty::{AdtDef, ClosureSubsts, ExistentialBounds, Region}; -use middle::ty::{FreevarMap, GenericPredicates}; +use middle::ty::{FreevarMap}; use middle::ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, TraitTy}; use middle::ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use middle::ty::TypeVariants::*; +use middle::ty::maps; +use util::common::MemoizationMap; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; use util::nodemap::FnvHashMap; @@ -224,6 +227,8 @@ pub struct ctxt<'tcx> { region_interner: RefCell>, stability_interner: RefCell>, + pub dep_graph: DepGraph, + /// Common types, pre-interned for your convenience. pub types: CommonTypes<'tcx>, @@ -245,21 +250,22 @@ pub struct ctxt<'tcx> { pub tables: RefCell>, /// Maps from a trait item to the trait item "descriptor" - pub impl_or_trait_items: RefCell>>, + pub impl_or_trait_items: RefCell>>, /// Maps from a trait def-id to a list of the def-ids of its trait items - pub trait_item_def_ids: RefCell>>>, + pub trait_item_def_ids: RefCell>>, - /// A cache for the trait_items() routine - pub trait_items_cache: RefCell>>>>, + /// A cache for the trait_items() routine; note that the routine + /// itself pushes the `TraitItems` dependency node. + trait_items_cache: RefCell>>, - pub impl_trait_refs: RefCell>>>, - pub trait_defs: RefCell>>, - pub adt_defs: RefCell>>, + pub impl_trait_refs: RefCell>>, + pub trait_defs: RefCell>>, + pub adt_defs: RefCell>>, /// Maps from the def-id of an item (trait/struct/enum/fn) to its /// associated predicates. - pub predicates: RefCell>>, + pub predicates: RefCell>>, /// Maps from the def-id of a trait to the list of /// super-predicates. This is a subset of the full list of @@ -267,21 +273,40 @@ pub struct ctxt<'tcx> { /// evaluate them even during type conversion, often before the /// full predicates are available (note that supertraits have /// additional acyclicity requirements). - pub super_predicates: RefCell>>, + pub super_predicates: RefCell>>, pub map: ast_map::Map<'tcx>, + + // Records the free variables refrenced by every closure + // expression. Do not track deps for this, just recompute it from + // scratch every time. pub freevars: RefCell, - pub tcache: RefCell>>, + + // Records the type of every item. + pub tcache: RefCell>>, + + // Internal cache for metadata decoding. No need to track deps on this. pub rcache: RefCell>>, + + // Cache for the type-contents routine. FIXME -- track deps? pub tc_cache: RefCell, ty::contents::TypeContents>>, + + // Cache for various types within a method body and so forth. + // + // FIXME this should be made local to typeck, but it is currently used by one lint pub ast_ty_to_ty_cache: RefCell>>, + + // FIXME no dep tracking, but we should be able to remove this pub ty_param_defs: RefCell>>, + + // FIXME dep tracking -- should be harmless enough pub normalized_cache: RefCell, Ty<'tcx>>>, + pub lang_items: middle::lang_items::LanguageItems, /// Maps from def-id of a type or region parameter to its /// (inferred) variance. - pub item_variance_map: RefCell>>, + pub item_variance_map: RefCell>>, /// True if the variance has been computed yet; false otherwise. pub variance_computed: Cell, @@ -289,13 +314,13 @@ pub struct ctxt<'tcx> { /// Maps a DefId of a type to a list of its inherent impls. /// Contains implementations of methods that are inherent to a type. /// Methods in these implementations don't need to be exported. - pub inherent_impls: RefCell>>>, + pub inherent_impls: RefCell>>, /// Maps a DefId of an impl to a list of its items. /// Note that this contains all of the impls that we know about, /// including ones in other crates. It's not clear that this is the best /// way to do it. - pub impl_items: RefCell>>, + pub impl_items: RefCell>>, /// Set of used unsafe nodes (functions or blocks). Unsafe nodes not /// present in this set can be warned about. @@ -309,6 +334,7 @@ pub struct ctxt<'tcx> { /// The set of external nominal types whose implementations have been read. /// This is used for lazy resolution of methods. pub populated_external_types: RefCell, + /// The set of external primitive types whose implementations have been read. /// FIXME(arielb1): why is this separate from populated_external_types? pub populated_external_primitive_impls: RefCell, @@ -344,7 +370,7 @@ pub struct ctxt<'tcx> { pub fulfilled_predicates: RefCell>, /// Caches the representation hints for struct definitions. - pub repr_hint_cache: RefCell>>>, + repr_hint_cache: RefCell>>, /// Maps Expr NodeId's to their constant qualification. pub const_qualif_map: RefCell>, @@ -483,7 +509,7 @@ impl<'tcx> ctxt<'tcx> { { let interner = RefCell::new(FnvHashMap()); let common_types = CommonTypes::new(&arenas.type_, &interner); - + let dep_graph = DepGraph::new(s.opts.incremental_compilation); tls::enter(ctxt { arenas: arenas, interner: interner, @@ -491,35 +517,36 @@ impl<'tcx> ctxt<'tcx> { bare_fn_interner: RefCell::new(FnvHashMap()), region_interner: RefCell::new(FnvHashMap()), stability_interner: RefCell::new(FnvHashMap()), + dep_graph: dep_graph.clone(), types: common_types, named_region_map: named_region_map, region_maps: region_maps, free_region_maps: RefCell::new(FnvHashMap()), - item_variance_map: RefCell::new(DefIdMap()), + item_variance_map: RefCell::new(DepTrackingMap::new(dep_graph.clone())), variance_computed: Cell::new(false), sess: s, def_map: def_map, tables: RefCell::new(Tables::empty()), - impl_trait_refs: RefCell::new(DefIdMap()), - trait_defs: RefCell::new(DefIdMap()), - adt_defs: RefCell::new(DefIdMap()), - predicates: RefCell::new(DefIdMap()), - super_predicates: RefCell::new(DefIdMap()), + impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + adt_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + super_predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())), fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()), map: map, freevars: RefCell::new(freevars), - tcache: RefCell::new(DefIdMap()), + tcache: RefCell::new(DepTrackingMap::new(dep_graph.clone())), rcache: RefCell::new(FnvHashMap()), tc_cache: RefCell::new(FnvHashMap()), ast_ty_to_ty_cache: RefCell::new(NodeMap()), - impl_or_trait_items: RefCell::new(DefIdMap()), - trait_item_def_ids: RefCell::new(DefIdMap()), - trait_items_cache: RefCell::new(DefIdMap()), + impl_or_trait_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + trait_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + trait_items_cache: RefCell::new(DepTrackingMap::new(dep_graph.clone())), ty_param_defs: RefCell::new(NodeMap()), normalized_cache: RefCell::new(FnvHashMap()), lang_items: lang_items, - inherent_impls: RefCell::new(DefIdMap()), - impl_items: RefCell::new(DefIdMap()), + inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + impl_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())), used_unsafe: RefCell::new(NodeSet()), used_mut_nodes: RefCell::new(NodeSet()), populated_external_types: RefCell::new(DefIdSet()), @@ -531,7 +558,7 @@ impl<'tcx> ctxt<'tcx> { stability: RefCell::new(stability), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), - repr_hint_cache: RefCell::new(DefIdMap()), + repr_hint_cache: RefCell::new(DepTrackingMap::new(dep_graph.clone())), const_qualif_map: RefCell::new(NodeMap()), custom_coerce_unsized_kinds: RefCell::new(DefIdMap()), cast_kinds: RefCell::new(NodeMap()), @@ -1000,4 +1027,26 @@ impl<'tcx> ctxt<'tcx> { pub fn mk_param_from_def(&self, def: &ty::TypeParameterDef) -> Ty<'tcx> { self.mk_param(def.space, def.index, def.name) } + + pub fn trait_items(&self, trait_did: DefId) -> Rc>> { + self.trait_items_cache.memoize(trait_did, || { + let def_ids = self.trait_item_def_ids(trait_did); + Rc::new(def_ids.iter() + .map(|d| self.impl_or_trait_item(d.def_id())) + .collect()) + }) + } + + /// Obtain the representation annotation for a struct definition. + pub fn lookup_repr_hints(&self, did: DefId) -> Rc> { + self.repr_hint_cache.memoize(did, || { + Rc::new(if did.is_local() { + self.get_attrs(did).iter().flat_map(|meta| { + attr::find_repr_attrs(self.sess.diagnostic(), meta).into_iter() + }).collect() + } else { + self.sess.cstore.repr_attrs(did) + }) + }) + } } diff --git a/src/librustc/middle/ty/error.rs b/src/librustc/middle/ty/error.rs index d9033eaa29f6b..ab48fd7fb8665 100644 --- a/src/librustc/middle/ty/error.rs +++ b/src/librustc/middle/ty/error.rs @@ -17,6 +17,7 @@ use std::fmt; use syntax::abi; use syntax::ast::{self, Name}; use syntax::codemap::Span; +use syntax::errors::DiagnosticBuilder; use rustc_front::hir; @@ -252,27 +253,30 @@ impl<'tcx> ty::TyS<'tcx> { } impl<'tcx> ty::ctxt<'tcx> { - pub fn note_and_explain_type_err(&self, err: &TypeError<'tcx>, sp: Span) { + pub fn note_and_explain_type_err(&self, + db: &mut DiagnosticBuilder, + err: &TypeError<'tcx>, + sp: Span) { use self::TypeError::*; match err.clone() { RegionsDoesNotOutlive(subregion, superregion) => { - self.note_and_explain_region("", subregion, "..."); - self.note_and_explain_region("...does not necessarily outlive ", + self.note_and_explain_region(db, "", subregion, "..."); + self.note_and_explain_region(db, "...does not necessarily outlive ", superregion, ""); } RegionsNotSame(region1, region2) => { - self.note_and_explain_region("", region1, "..."); - self.note_and_explain_region("...is not the same lifetime as ", + self.note_and_explain_region(db, "", region1, "..."); + self.note_and_explain_region(db, "...is not the same lifetime as ", region2, ""); } RegionsNoOverlap(region1, region2) => { - self.note_and_explain_region("", region1, "..."); - self.note_and_explain_region("...does not overlap ", + self.note_and_explain_region(db, "", region1, "..."); + self.note_and_explain_region(db, "...does not overlap ", region2, ""); } RegionsInsufficientlyPolymorphic(_, conc_region) => { - self.note_and_explain_region("concrete lifetime that was found is ", + self.note_and_explain_region(db, "concrete lifetime that was found is ", conc_region, ""); } RegionsOverlyPolymorphic(_, ty::ReVar(_)) => { @@ -280,42 +284,40 @@ impl<'tcx> ty::ctxt<'tcx> { // inference variables, it's not very illuminating. } RegionsOverlyPolymorphic(_, conc_region) => { - self.note_and_explain_region("expected concrete lifetime is ", + self.note_and_explain_region(db, "expected concrete lifetime is ", conc_region, ""); } Sorts(values) => { let expected_str = values.expected.sort_string(self); let found_str = values.found.sort_string(self); if expected_str == found_str && expected_str == "closure" { - self.sess.span_note(sp, + db.span_note(sp, "no two closures, even if identical, have the same type"); - self.sess.span_help(sp, + db.span_help(sp, "consider boxing your closure and/or using it as a trait object"); } }, TyParamDefaultMismatch(values) => { let expected = values.expected; let found = values.found; - self.sess.span_note(sp, - &format!("conflicting type parameter defaults `{}` and `{}`", - expected.ty, - found.ty)); + db.span_note(sp, &format!("conflicting type parameter defaults `{}` and `{}`", + expected.ty, + found.ty)); match self.map.as_local_node_id(expected.def_id) .and_then(|node_id| self.map.opt_span(node_id)) { Some(span) => { - self.sess.span_note(span, "a default was defined here..."); + db.span_note(span, "a default was defined here..."); } None => { - self.sess.note( - &format!("a default is defined on `{}`", - self.item_path_str(expected.def_id))); + db.note(&format!("a default is defined on `{}`", + self.item_path_str(expected.def_id))); } } - self.sess.span_note( + db.span_note( expected.origin_span, "...that was applied to an unconstrained type variable here"); @@ -324,18 +326,16 @@ impl<'tcx> ty::ctxt<'tcx> { .and_then(|node_id| self.map.opt_span(node_id)) { Some(span) => { - self.sess.span_note(span, "a second default was defined here..."); + db.span_note(span, "a second default was defined here..."); } None => { - self.sess.note( - &format!("a second default is defined on `{}`", - self.item_path_str(found.def_id))); + db.note(&format!("a second default is defined on `{}`", + self.item_path_str(found.def_id))); } } - self.sess.span_note( - found.origin_span, - "...that also applies to the same type variable here"); + db.span_note(found.origin_span, + "...that also applies to the same type variable here"); } _ => {} } diff --git a/src/librustc/middle/ty/fast_reject.rs b/src/librustc/middle/ty/fast_reject.rs index 77608f4012845..a06e8a72c44ee 100644 --- a/src/librustc/middle/ty/fast_reject.rs +++ b/src/librustc/middle/ty/fast_reject.rs @@ -15,7 +15,7 @@ use syntax::ast; use self::SimplifiedType::*; /// See `simplify_type -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum SimplifiedType { BoolSimplifiedType, CharSimplifiedType, diff --git a/src/librustc/middle/ty/flags.rs b/src/librustc/middle/ty/flags.rs index 94a50e3cac7f7..a0b03fe8126dd 100644 --- a/src/librustc/middle/ty/flags.rs +++ b/src/librustc/middle/ty/flags.rs @@ -9,7 +9,7 @@ // except according to those terms. use middle::subst; -use middle::ty::{self, HasTypeFlags, Ty, TypeFlags}; +use middle::ty::{self, Ty, TypeFlags, TypeFoldable}; pub struct FlagComputation { pub flags: TypeFlags, diff --git a/src/librustc/middle/ty/fold.rs b/src/librustc/middle/ty/fold.rs index 605e78e928115..da0245a8d2520 100644 --- a/src/librustc/middle/ty/fold.rs +++ b/src/librustc/middle/ty/fold.rs @@ -14,41 +14,98 @@ //! instance of a "folder" (a type which implements `TypeFolder`). Then //! the setup is intended to be: //! -//! T.fold_with(F) --calls--> F.fold_T(T) --calls--> super_fold_T(F, T) +//! T.fold_with(F) --calls--> F.fold_T(T) --calls--> T.super_fold_with(F) //! //! This way, when you define a new folder F, you can override -//! `fold_T()` to customize the behavior, and invoke `super_fold_T()` +//! `fold_T()` to customize the behavior, and invoke `T.super_fold_with()` //! to get the original behavior. Meanwhile, to actually fold //! something, you can just write `T.fold_with(F)`, which is //! convenient. (Note that `fold_with` will also transparently handle //! things like a `Vec` where T is foldable and so on.) //! //! In this ideal setup, the only function that actually *does* -//! anything is `super_fold_T`, which traverses the type `T`. Moreover, -//! `super_fold_T` should only ever call `T.fold_with()`. +//! anything is `T.super_fold_with()`, which traverses the type `T`. +//! Moreover, `T.super_fold_with()` should only ever call `T.fold_with()`. //! //! In some cases, we follow a degenerate pattern where we do not have -//! a `fold_T` nor `super_fold_T` method. Instead, `T.fold_with` -//! traverses the structure directly. This is suboptimal because the -//! behavior cannot be overridden, but it's much less work to implement. -//! If you ever *do* need an override that doesn't exist, it's not hard -//! to convert the degenerate pattern into the proper thing. +//! a `fold_T` method. Instead, `T.fold_with` traverses the structure directly. +//! This is suboptimal because the behavior cannot be overridden, but it's +//! much less work to implement. If you ever *do* need an override that +//! doesn't exist, it's not hard to convert the degenerate pattern into the +//! proper thing. +//! +//! A `TypeFoldable` T can also be visited by a `TypeVisitor` V using similar setup: +//! T.visit_with(V) --calls--> V.visit_T(T) --calls--> T.super_visit_with(V). +//! These methods return true to indicate that the visitor has found what it is looking for +//! and does not need to visit anything else. use middle::region; use middle::subst; use middle::ty::adjustment; -use middle::ty::{self, Binder, Ty, RegionEscape}; +use middle::ty::{self, Binder, Ty, TypeFlags}; use std::fmt; use util::nodemap::{FnvHashMap, FnvHashSet}; -/////////////////////////////////////////////////////////////////////////// -// Two generic traits - /// The TypeFoldable trait is implemented for every type that can be folded. /// Basically, every type that has a corresponding method in TypeFolder. pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { - fn fold_with>(&self, folder: &mut F) -> Self; + fn super_fold_with>(&self, folder: &mut F) -> Self; + fn fold_with>(&self, folder: &mut F) -> Self { + self.super_fold_with(folder) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool; + fn visit_with>(&self, visitor: &mut V) -> bool { + self.super_visit_with(visitor) + } + + fn has_regions_escaping_depth(&self, depth: u32) -> bool { + self.visit_with(&mut HasEscapingRegionsVisitor { depth: depth }) + } + fn has_escaping_regions(&self) -> bool { + self.has_regions_escaping_depth(0) + } + + fn has_type_flags(&self, flags: TypeFlags) -> bool { + self.visit_with(&mut HasTypeFlagsVisitor { flags: flags }) + } + fn has_projection_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PROJECTION) + } + fn references_error(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_ERR) + } + fn has_param_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PARAMS) + } + fn has_self_ty(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_SELF) + } + fn has_infer_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INFER) + } + fn needs_infer(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER) + } + fn needs_subst(&self) -> bool { + self.has_type_flags(TypeFlags::NEEDS_SUBST) + } + fn has_closure_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_CLOSURE) + } + fn has_erasable_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND | + TypeFlags::HAS_RE_INFER | + TypeFlags::HAS_FREE_REGIONS) + } + /// Indicates whether this value references only 'global' + /// types/lifetimes that are the same regardless of what fn we are + /// in. This is used for caching. Errs on the side of returning + /// false. + fn is_global(&self) -> bool { + !self.has_type_flags(TypeFlags::HAS_LOCAL_NAMES) + } } /// The TypeFolder trait defines the actual *folding*. There is a @@ -74,248 +131,77 @@ pub trait TypeFolder<'tcx> : Sized { where T : TypeFoldable<'tcx> { // FIXME(#20526) this should replace `enter_region_binder`/`exit_region_binder`. - super_fold_binder(self, t) + t.super_fold_with(self) } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - super_fold_ty(self, t) + t.super_fold_with(self) } fn fold_mt(&mut self, t: &ty::TypeAndMut<'tcx>) -> ty::TypeAndMut<'tcx> { - super_fold_mt(self, t) + t.super_fold_with(self) } fn fold_trait_ref(&mut self, t: &ty::TraitRef<'tcx>) -> ty::TraitRef<'tcx> { - super_fold_trait_ref(self, t) + t.super_fold_with(self) } fn fold_substs(&mut self, substs: &subst::Substs<'tcx>) -> subst::Substs<'tcx> { - super_fold_substs(self, substs) + substs.super_fold_with(self) } fn fold_fn_sig(&mut self, sig: &ty::FnSig<'tcx>) -> ty::FnSig<'tcx> { - super_fold_fn_sig(self, sig) + sig.super_fold_with(self) } fn fold_output(&mut self, output: &ty::FnOutput<'tcx>) -> ty::FnOutput<'tcx> { - super_fold_output(self, output) + output.super_fold_with(self) } fn fold_bare_fn_ty(&mut self, fty: &ty::BareFnTy<'tcx>) -> ty::BareFnTy<'tcx> { - super_fold_bare_fn_ty(self, fty) + fty.super_fold_with(self) } fn fold_closure_ty(&mut self, fty: &ty::ClosureTy<'tcx>) -> ty::ClosureTy<'tcx> { - super_fold_closure_ty(self, fty) + fty.super_fold_with(self) } fn fold_region(&mut self, r: ty::Region) -> ty::Region { - r + r.super_fold_with(self) } fn fold_existential_bounds(&mut self, s: &ty::ExistentialBounds<'tcx>) -> ty::ExistentialBounds<'tcx> { - super_fold_existential_bounds(self, s) + s.super_fold_with(self) } fn fold_autoref(&mut self, ar: &adjustment::AutoRef<'tcx>) -> adjustment::AutoRef<'tcx> { - super_fold_autoref(self, ar) + ar.super_fold_with(self) } - - fn fold_item_substs(&mut self, i: ty::ItemSubsts<'tcx>) -> ty::ItemSubsts<'tcx> { - super_fold_item_substs(self, i) - } -} - -/////////////////////////////////////////////////////////////////////////// -// "super" routines: these are the default implementations for TypeFolder. -// -// They should invoke `foo.fold_with()` to do recursive folding. - -pub fn super_fold_binder<'tcx, T, U>(this: &mut T, - binder: &Binder) - -> Binder - where T : TypeFolder<'tcx>, U : TypeFoldable<'tcx> -{ - this.enter_region_binder(); - let result = Binder(binder.0.fold_with(this)); - this.exit_region_binder(); - result } -pub fn super_fold_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - ty: Ty<'tcx>) - -> Ty<'tcx> { - let sty = match ty.sty { - ty::TyBox(typ) => { - ty::TyBox(typ.fold_with(this)) - } - ty::TyRawPtr(ref tm) => { - ty::TyRawPtr(tm.fold_with(this)) - } - ty::TyArray(typ, sz) => { - ty::TyArray(typ.fold_with(this), sz) - } - ty::TySlice(typ) => { - ty::TySlice(typ.fold_with(this)) - } - ty::TyEnum(tid, ref substs) => { - let substs = substs.fold_with(this); - ty::TyEnum(tid, this.tcx().mk_substs(substs)) - } - ty::TyTrait(box ty::TraitTy { ref principal, ref bounds }) => { - ty::TyTrait(box ty::TraitTy { - principal: principal.fold_with(this), - bounds: bounds.fold_with(this), - }) - } - ty::TyTuple(ref ts) => { - ty::TyTuple(ts.fold_with(this)) - } - ty::TyBareFn(opt_def_id, ref f) => { - let bfn = f.fold_with(this); - ty::TyBareFn(opt_def_id, this.tcx().mk_bare_fn(bfn)) - } - ty::TyRef(r, ref tm) => { - let r = r.fold_with(this); - ty::TyRef(this.tcx().mk_region(r), tm.fold_with(this)) - } - ty::TyStruct(did, ref substs) => { - let substs = substs.fold_with(this); - ty::TyStruct(did, this.tcx().mk_substs(substs)) - } - ty::TyClosure(did, ref substs) => { - let s = substs.fold_with(this); - ty::TyClosure(did, s) - } - ty::TyProjection(ref data) => { - ty::TyProjection(data.fold_with(this)) - } - ty::TyBool | ty::TyChar | ty::TyStr | - ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | - ty::TyError | ty::TyInfer(_) | - ty::TyParam(..) => { - ty.sty.clone() - } - }; - this.tcx().mk_ty(sty) -} - -pub fn super_fold_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - substs: &subst::Substs<'tcx>) - -> subst::Substs<'tcx> { - let regions = match substs.regions { - subst::ErasedRegions => { - subst::ErasedRegions - } - subst::NonerasedRegions(ref regions) => { - subst::NonerasedRegions(regions.fold_with(this)) - } - }; - - subst::Substs { regions: regions, - types: substs.types.fold_with(this) } -} - -pub fn super_fold_fn_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - sig: &ty::FnSig<'tcx>) - -> ty::FnSig<'tcx> -{ - ty::FnSig { inputs: sig.inputs.fold_with(this), - output: sig.output.fold_with(this), - variadic: sig.variadic } -} - -pub fn super_fold_output<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - output: &ty::FnOutput<'tcx>) - -> ty::FnOutput<'tcx> { - match *output { - ty::FnConverging(ref ty) => ty::FnConverging(ty.fold_with(this)), - ty::FnDiverging => ty::FnDiverging - } -} - -pub fn super_fold_bare_fn_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - fty: &ty::BareFnTy<'tcx>) - -> ty::BareFnTy<'tcx> -{ - ty::BareFnTy { sig: fty.sig.fold_with(this), - abi: fty.abi, - unsafety: fty.unsafety } -} - -pub fn super_fold_closure_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - fty: &ty::ClosureTy<'tcx>) - -> ty::ClosureTy<'tcx> -{ - ty::ClosureTy { - sig: fty.sig.fold_with(this), - unsafety: fty.unsafety, - abi: fty.abi, - } -} - -pub fn super_fold_trait_ref<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - t: &ty::TraitRef<'tcx>) - -> ty::TraitRef<'tcx> -{ - let substs = t.substs.fold_with(this); - ty::TraitRef { - def_id: t.def_id, - substs: this.tcx().mk_substs(substs), - } -} - -pub fn super_fold_mt<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - mt: &ty::TypeAndMut<'tcx>) - -> ty::TypeAndMut<'tcx> { - ty::TypeAndMut {ty: mt.ty.fold_with(this), - mutbl: mt.mutbl} -} - -pub fn super_fold_existential_bounds<'tcx, T: TypeFolder<'tcx>>( - this: &mut T, - bounds: &ty::ExistentialBounds<'tcx>) - -> ty::ExistentialBounds<'tcx> -{ - ty::ExistentialBounds { - region_bound: bounds.region_bound.fold_with(this), - builtin_bounds: bounds.builtin_bounds, - projection_bounds: bounds.projection_bounds.fold_with(this), - } -} +pub trait TypeVisitor<'tcx> : Sized { + fn enter_region_binder(&mut self) { } + fn exit_region_binder(&mut self) { } -pub fn super_fold_autoref<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - autoref: &adjustment::AutoRef<'tcx>) - -> adjustment::AutoRef<'tcx> -{ - match *autoref { - adjustment::AutoPtr(r, m) => { - let r = r.fold_with(this); - adjustment::AutoPtr(this.tcx().mk_region(r), m) - } - adjustment::AutoUnsafe(m) => adjustment::AutoUnsafe(m) + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + t.super_visit_with(self) } -} -pub fn super_fold_item_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - substs: ty::ItemSubsts<'tcx>) - -> ty::ItemSubsts<'tcx> -{ - ty::ItemSubsts { - substs: substs.substs.fold_with(this), + fn visit_region(&mut self, r: ty::Region) -> bool { + r.super_visit_with(self) } } @@ -333,7 +219,7 @@ impl<'a, 'tcx, F> TypeFolder<'tcx> for BottomUpFolder<'a, 'tcx, F> where fn tcx(&self) -> &ty::ctxt<'tcx> { self.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - let t1 = super_fold_ty(self, ty); + let t1 = ty.super_fold_with(self); (self.fldop)(t1) } } @@ -495,7 +381,7 @@ impl<'tcx> ty::ctxt<'tcx> { } pub fn no_late_bound_regions(&self, value: &Binder) -> Option - where T : TypeFoldable<'tcx> + RegionEscape + where T : TypeFoldable<'tcx> { if value.0.has_escaping_regions() { None @@ -561,7 +447,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx> return t; } - super_fold_ty(self, t) + t.super_fold_with(self) } fn fold_region(&mut self, r: ty::Region) -> ty::Region { @@ -612,7 +498,7 @@ impl<'tcx> ty::ctxt<'tcx> { Some(u) => return u } - let t_norm = ty::fold::super_fold_ty(self, ty); + let t_norm = ty.super_fold_with(self); self.tcx().normalized_cache.borrow_mut().insert(ty, t_norm); return t_norm; } @@ -621,7 +507,7 @@ impl<'tcx> ty::ctxt<'tcx> { where T : TypeFoldable<'tcx> { let u = self.tcx().anonymize_late_bound_regions(t); - ty::fold::super_fold_binder(self, &u) + u.super_fold_with(self) } fn fold_region(&mut self, r: ty::Region) -> ty::Region { @@ -678,3 +564,75 @@ pub fn shift_regions<'tcx, T:TypeFoldable<'tcx>>(tcx: &ty::ctxt<'tcx>, shift_region(region, amount) })) } + +/// An "escaping region" is a bound region whose binder is not part of `t`. +/// +/// So, for example, consider a type like the following, which has two binders: +/// +/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize)) +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope +/// +/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the +/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner +/// fn type*, that type has an escaping region: `'a`. +/// +/// Note that what I'm calling an "escaping region" is often just called a "free region". However, +/// we already use the term "free region". It refers to the regions that we use to represent bound +/// regions on a fn definition while we are typechecking its body. +/// +/// To clarify, conceptually there is no particular difference between an "escaping" region and a +/// "free" region. However, there is a big difference in practice. Basically, when "entering" a +/// binding level, one is generally required to do some sort of processing to a bound region, such +/// as replacing it with a fresh/skolemized region, or making an entry in the environment to +/// represent the scope to which it is attached, etc. An escaping region represents a bound region +/// for which this processing has not yet been done. +struct HasEscapingRegionsVisitor { + depth: u32, +} + +impl<'tcx> TypeVisitor<'tcx> for HasEscapingRegionsVisitor { + fn enter_region_binder(&mut self) { + self.depth += 1; + } + + fn exit_region_binder(&mut self) { + self.depth -= 1; + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + t.region_depth > self.depth + } + + fn visit_region(&mut self, r: ty::Region) -> bool { + r.escapes_depth(self.depth) + } +} + +struct HasTypeFlagsVisitor { + flags: ty::TypeFlags, +} + +impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { + fn visit_ty(&mut self, t: Ty) -> bool { + t.flags.get().intersects(self.flags) + } + + fn visit_region(&mut self, r: ty::Region) -> bool { + if self.flags.intersects(ty::TypeFlags::HAS_LOCAL_NAMES) { + // does this represent a region that cannot be named + // in a global way? used in fulfillment caching. + match r { + ty::ReStatic | ty::ReEmpty => {} + _ => return true, + } + } + if self.flags.intersects(ty::TypeFlags::HAS_RE_INFER) { + match r { + ty::ReVar(_) | ty::ReSkolemized(..) => { return true } + _ => {} + } + } + false + } +} diff --git a/src/librustc/middle/ty/ivar.rs b/src/librustc/middle/ty/ivar.rs index 73d567d0acf40..ffc12aa5aea19 100644 --- a/src/librustc/middle/ty/ivar.rs +++ b/src/librustc/middle/ty/ivar.rs @@ -8,7 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use dep_graph::DepNode; use middle::ty::{Ty, TyS}; +use middle::ty::tls; use rustc_data_structures::ivar; @@ -27,6 +29,10 @@ use core::nonzero::NonZero; /// (B) no aliases to this value with a 'tcx longer than this /// value's 'lt exist /// +/// Dependency tracking: each ivar does not know what node in the +/// dependency graph it is associated with, so when you get/fulfill +/// you must supply a `DepNode` id. This should always be the same id! +/// /// NonZero is used rather than Unique because Unique isn't Copy. pub struct TyIVar<'tcx, 'lt: 'tcx>(ivar::Ivar>>, PhantomData)->TyS<'tcx>>); @@ -40,19 +46,28 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> { } #[inline] - pub fn get(&self) -> Option> { + pub fn get(&self, dep_node: DepNode) -> Option> { + tls::with(|tcx| tcx.dep_graph.read(dep_node)); + self.untracked_get() + } + + #[inline] + fn untracked_get(&self) -> Option> { match self.0.get() { None => None, // valid because of invariant (A) Some(v) => Some(unsafe { &*(*v as *const TyS<'tcx>) }) } } + #[inline] - pub fn unwrap(&self) -> Ty<'tcx> { - self.get().unwrap() + pub fn unwrap(&self, dep_node: DepNode) -> Ty<'tcx> { + self.get(dep_node).unwrap() } - pub fn fulfill(&self, value: Ty<'lt>) { + pub fn fulfill(&self, dep_node: DepNode, value: Ty<'lt>) { + tls::with(|tcx| tcx.dep_graph.write(dep_node)); + // Invariant (A) is fulfilled, because by (B), every alias // of this has a 'tcx longer than 'lt. let value: *const TyS<'lt> = value; @@ -64,7 +79,7 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> { impl<'tcx, 'lt> fmt::Debug for TyIVar<'tcx, 'lt> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.get() { + match self.untracked_get() { Some(val) => write!(f, "TyIVar({:?})", val), None => f.write_str("TyIVar()") } diff --git a/src/librustc/middle/ty/maps.rs b/src/librustc/middle/ty/maps.rs new file mode 100644 index 0000000000000..7d5276f379ffe --- /dev/null +++ b/src/librustc/middle/ty/maps.rs @@ -0,0 +1,44 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dep_graph::{DepNode, DepTrackingMapConfig}; +use middle::def_id::DefId; +use middle::ty; +use std::marker::PhantomData; +use std::rc::Rc; +use syntax::attr; + +macro_rules! dep_map_ty { + ($ty_name:ident : $node_name:ident ($key:ty) -> $value:ty) => { + pub struct $ty_name<'tcx> { + data: PhantomData<&'tcx ()> + } + + impl<'tcx> DepTrackingMapConfig for $ty_name<'tcx> { + type Key = $key; + type Value = $value; + fn to_dep_node(key: &$key) -> DepNode { DepNode::$node_name(*key) } + } + } +} + +dep_map_ty! { ImplOrTraitItems: ImplOrTraitItems(DefId) -> ty::ImplOrTraitItem<'tcx> } +dep_map_ty! { Tcache: ItemSignature(DefId) -> ty::TypeScheme<'tcx> } +dep_map_ty! { Predicates: ItemSignature(DefId) -> ty::GenericPredicates<'tcx> } +dep_map_ty! { SuperPredicates: ItemSignature(DefId) -> ty::GenericPredicates<'tcx> } +dep_map_ty! { TraitItemDefIds: TraitItemDefIds(DefId) -> Rc> } +dep_map_ty! { ImplTraitRefs: ItemSignature(DefId) -> Option> } +dep_map_ty! { TraitDefs: ItemSignature(DefId) -> &'tcx ty::TraitDef<'tcx> } +dep_map_ty! { AdtDefs: ItemSignature(DefId) -> ty::AdtDefMaster<'tcx> } +dep_map_ty! { ItemVariances: ItemSignature(DefId) -> Rc } +dep_map_ty! { InherentImpls: InherentImpls(DefId) -> Rc> } +dep_map_ty! { ImplItems: ImplItems(DefId) -> Vec } +dep_map_ty! { TraitItems: TraitItems(DefId) -> Rc>> } +dep_map_ty! { ReprHints: ReprHints(DefId) -> Rc> } diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 308883cf063df..852d9d80a9148 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -12,13 +12,14 @@ pub use self::ImplOrTraitItemId::*; pub use self::ClosureKind::*; pub use self::Variance::*; pub use self::DtorKind::*; -pub use self::ExplicitSelfCategory::*; pub use self::ImplOrTraitItemContainer::*; pub use self::BorrowKind::*; pub use self::ImplOrTraitItem::*; pub use self::IntVarValue::*; pub use self::LvaluePreference::*; +pub use self::fold::TypeFoldable; +use dep_graph::{self, DepNode}; use front::map as ast_map; use front::map::LinkedPath; use middle; @@ -32,13 +33,13 @@ use middle::traits; use middle::ty; use middle::ty::fold::TypeFolder; use middle::ty::walk::TypeWalker; -use util::common::memoized; -use util::nodemap::{NodeMap, NodeSet, DefIdMap}; +use util::common::MemoizationMap; +use util::nodemap::{NodeMap, NodeSet}; use util::nodemap::FnvHashMap; use serialize::{Encodable, Encoder, Decodable, Decoder}; use std::borrow::{Borrow, Cow}; -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::hash::{Hash, Hasher}; use std::iter; use std::rc::Rc; @@ -47,11 +48,12 @@ use std::vec::IntoIter; use std::collections::{HashMap, HashSet}; use syntax::ast::{self, CrateNum, Name, NodeId}; use syntax::attr::{self, AttrMetaMethods}; -use syntax::codemap::Span; +use syntax::codemap::{DUMMY_SP, Span}; use syntax::parse::token::{InternedString, special_idents}; use rustc_front::hir; use rustc_front::hir::{ItemImpl, ItemTrait}; +use rustc_front::intravisit::Visitor; pub use self::sty::{Binder, DebruijnIndex}; pub use self::sty::{BuiltinBound, BuiltinBounds, ExistentialBounds}; @@ -76,14 +78,18 @@ pub use self::contents::TypeContents; pub use self::context::{ctxt, tls}; pub use self::context::{CtxtArenas, Lift, Tables}; +pub use self::trait_def::{TraitDef, TraitFlags}; + pub mod adjustment; pub mod cast; pub mod error; pub mod fast_reject; pub mod fold; pub mod _match; +pub mod maps; pub mod outlives; pub mod relate; +pub mod trait_def; pub mod walk; pub mod wf; pub mod util; @@ -633,7 +639,6 @@ pub struct RegionParameterDef { impl RegionParameterDef { pub fn to_early_bound_region(&self) -> ty::Region { ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: self.def_id, space: self.space, index: self.index, name: self.name, @@ -1319,161 +1324,6 @@ pub struct TypeScheme<'tcx> { pub ty: Ty<'tcx>, } -bitflags! { - flags TraitFlags: u32 { - const NO_TRAIT_FLAGS = 0, - const HAS_DEFAULT_IMPL = 1 << 0, - const IS_OBJECT_SAFE = 1 << 1, - const OBJECT_SAFETY_VALID = 1 << 2, - const IMPLS_VALID = 1 << 3, - } -} - -/// As `TypeScheme` but for a trait ref. -pub struct TraitDef<'tcx> { - pub unsafety: hir::Unsafety, - - /// If `true`, then this trait had the `#[rustc_paren_sugar]` - /// attribute, indicating that it should be used with `Foo()` - /// sugar. This is a temporary thing -- eventually any trait wil - /// be usable with the sugar (or without it). - pub paren_sugar: bool, - - /// Generic type definitions. Note that `Self` is listed in here - /// as having a single bound, the trait itself (e.g., in the trait - /// `Eq`, there is a single bound `Self : Eq`). This is so that - /// default methods get to assume that the `Self` parameters - /// implements the trait. - pub generics: Generics<'tcx>, - - pub trait_ref: TraitRef<'tcx>, - - /// A list of the associated types defined in this trait. Useful - /// for resolving `X::Foo` type markers. - pub associated_type_names: Vec, - - // Impls of this trait. To allow for quicker lookup, the impls are indexed - // by a simplified version of their Self type: impls with a simplifiable - // Self are stored in nonblanket_impls keyed by it, while all other impls - // are stored in blanket_impls. - - /// Impls of the trait. - pub nonblanket_impls: RefCell< - FnvHashMap> - >, - - /// Blanket impls associated with the trait. - pub blanket_impls: RefCell>, - - /// Various flags - pub flags: Cell -} - -impl<'tcx> TraitDef<'tcx> { - // returns None if not yet calculated - pub fn object_safety(&self) -> Option { - if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) { - Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE)) - } else { - None - } - } - - pub fn set_object_safety(&self, is_safe: bool) { - assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true)); - self.flags.set( - self.flags.get() | if is_safe { - TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE - } else { - TraitFlags::OBJECT_SAFETY_VALID - } - ); - } - - /// Records a trait-to-implementation mapping. - pub fn record_impl(&self, - tcx: &ctxt<'tcx>, - impl_def_id: DefId, - impl_trait_ref: TraitRef<'tcx>) { - debug!("TraitDef::record_impl for {:?}, from {:?}", - self, impl_trait_ref); - - // We don't want to borrow_mut after we already populated all impls, - // so check if an impl is present with an immutable borrow first. - if let Some(sty) = fast_reject::simplify_type(tcx, - impl_trait_ref.self_ty(), false) { - if let Some(is) = self.nonblanket_impls.borrow().get(&sty) { - if is.contains(&impl_def_id) { - return // duplicate - skip - } - } - - self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id) - } else { - if self.blanket_impls.borrow().contains(&impl_def_id) { - return // duplicate - skip - } - self.blanket_impls.borrow_mut().push(impl_def_id) - } - } - - - pub fn for_each_impl(&self, tcx: &ctxt<'tcx>, mut f: F) { - tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); - - for &impl_def_id in self.blanket_impls.borrow().iter() { - f(impl_def_id); - } - - for v in self.nonblanket_impls.borrow().values() { - for &impl_def_id in v { - f(impl_def_id); - } - } - } - - /// Iterate over every impl that could possibly match the - /// self-type `self_ty`. - pub fn for_each_relevant_impl(&self, - tcx: &ctxt<'tcx>, - self_ty: Ty<'tcx>, - mut f: F) - { - tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); - - for &impl_def_id in self.blanket_impls.borrow().iter() { - f(impl_def_id); - } - - // simplify_type(.., false) basically replaces type parameters and - // projections with infer-variables. This is, of course, done on - // the impl trait-ref when it is instantiated, but not on the - // predicate trait-ref which is passed here. - // - // for example, if we match `S: Copy` against an impl like - // `impl Copy for Option`, we replace the type variable - // in `Option` with an infer variable, to `Option<_>` (this - // doesn't actually change fast_reject output), but we don't - // replace `S` with anything - this impl of course can't be - // selected, and as there are hundreds of similar impls, - // considering them would significantly harm performance. - if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) { - if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) { - for &impl_def_id in impls { - f(impl_def_id); - } - } - } else { - for v in self.nonblanket_impls.borrow().values() { - for &impl_def_id in v { - f(impl_def_id); - } - } - } - } - -} - bitflags! { flags AdtFlags: u32 { const NO_ADT_FLAGS = 0, @@ -1515,6 +1365,8 @@ pub struct FieldDefData<'tcx, 'container: 'tcx> { pub vis: hir::Visibility, /// TyIVar is used here to allow for variance (see the doc at /// AdtDefData). + /// + /// Note: direct accesses to `ty` must also add dep edges. ty: ivar::TyIVar<'tcx, 'container> } @@ -1805,11 +1657,11 @@ impl<'tcx, 'container> FieldDefData<'tcx, 'container> { } pub fn unsubst_ty(&self) -> Ty<'tcx> { - self.ty.unwrap() + self.ty.unwrap(DepNode::FieldTy(self.did)) } pub fn fulfill_ty(&self, ty: Ty<'container>) { - self.ty.fulfill(ty); + self.ty.fulfill(DepNode::FieldTy(self.did), ty); } } @@ -1932,24 +1784,20 @@ impl LvaluePreference { /// into the map by the `typeck::collect` phase. If the def-id is external, /// then we have to go consult the crate loading code (and cache the result for /// the future). -fn lookup_locally_or_in_crate_store(descr: &str, +fn lookup_locally_or_in_crate_store(descr: &str, def_id: DefId, - map: &RefCell>, - load_external: F) -> V where - V: Clone, - F: FnOnce() -> V, + map: &M, + load_external: F) + -> M::Value where + M: MemoizationMap, + F: FnOnce() -> M::Value, { - match map.borrow().get(&def_id).cloned() { - Some(v) => { return v; } - None => { } - } - - if def_id.is_local() { - panic!("No def'n found for {:?} in tcx.{}", def_id, descr); - } - let v = load_external(); - map.borrow_mut().insert(def_id, v.clone()); - v + map.memoize(def_id, || { + if def_id.is_local() { + panic!("No def'n found for {:?} in tcx.{}", def_id, descr); + } + load_external() + }) } impl BorrowKind { @@ -2225,22 +2073,6 @@ impl<'tcx> ctxt<'tcx> { } } - pub fn trait_items(&self, trait_did: DefId) -> Rc>> { - let mut trait_items = self.trait_items_cache.borrow_mut(); - match trait_items.get(&trait_did).cloned() { - Some(trait_items) => trait_items, - None => { - let def_ids = self.trait_item_def_ids(trait_did); - let items: Rc> = - Rc::new(def_ids.iter() - .map(|d| self.impl_or_trait_item(d.def_id())) - .collect()); - trait_items.insert(trait_did, items.clone()); - items - } - } - } - pub fn trait_impl_polarity(&self, id: DefId) -> Option { if let Some(id) = self.map.as_local_node_id(id) { match self.map.find(id) { @@ -2258,7 +2090,7 @@ impl<'tcx> ctxt<'tcx> { } pub fn custom_coerce_unsized_kind(&self, did: DefId) -> adjustment::CustomCoerceUnsized { - memoized(&self.custom_coerce_unsized_kinds, did, |did: DefId| { + self.custom_coerce_unsized_kinds.memoize(did, || { let (kind, src) = if did.krate != LOCAL_CRATE { (self.sess.cstore.custom_coerce_unsized_kind(did), "external") } else { @@ -2396,6 +2228,39 @@ impl<'tcx> ctxt<'tcx> { || self.sess.cstore.item_super_predicates(self, did)) } + /// If `type_needs_drop` returns true, then `ty` is definitely + /// non-copy and *might* have a destructor attached; if it returns + /// false, then `ty` definitely has no destructor (i.e. no drop glue). + /// + /// (Note that this implies that if `ty` has a destructor attached, + /// then `type_needs_drop` will definitely return `true` for `ty`.) + pub fn type_needs_drop_given_env<'a>(&self, + ty: Ty<'tcx>, + param_env: &ty::ParameterEnvironment<'a,'tcx>) -> bool { + // Issue #22536: We first query type_moves_by_default. It sees a + // normalized version of the type, and therefore will definitely + // know whether the type implements Copy (and thus needs no + // cleanup/drop/zeroing) ... + let implements_copy = !ty.moves_by_default(param_env, DUMMY_SP); + + if implements_copy { return false; } + + // ... (issue #22536 continued) but as an optimization, still use + // prior logic of asking if the `needs_drop` bit is set; we need + // not zero non-Copy types if they have no destructor. + + // FIXME(#22815): Note that calling `ty::type_contents` is a + // conservative heuristic; it may report that `needs_drop` is set + // when actual type does not actually have a destructor associated + // with it. But since `ty` absolutely did not have the `Copy` + // bound attached (see above), it is sound to treat it as having a + // destructor (e.g. zero its memory on move). + + let contents = ty.type_contents(self); + debug!("type_needs_drop ty={:?} contents={:?}", ty, contents); + contents.needs_drop(self) + } + /// Get the attributes of a definition. pub fn get_attrs(&self, did: DefId) -> Cow<'tcx, [ast::Attribute]> { if let Some(id) = self.map.as_local_node_id(did) { @@ -2421,19 +2286,6 @@ impl<'tcx> ctxt<'tcx> { || self.lookup_repr_hints(did).contains(&attr::ReprSimd) } - /// Obtain the representation annotation for a struct definition. - pub fn lookup_repr_hints(&self, did: DefId) -> Rc> { - memoized(&self.repr_hint_cache, did, |did: DefId| { - Rc::new(if did.is_local() { - self.get_attrs(did).iter().flat_map(|meta| { - attr::find_repr_attrs(self.sess.diagnostic(), meta).into_iter() - }).collect() - } else { - self.sess.cstore.repr_attrs(did) - }) - }) - } - pub fn item_variances(&self, item_id: DefId) -> Rc { lookup_locally_or_in_crate_store( "item_variance_map", item_id, &self.item_variance_map, @@ -2460,6 +2312,10 @@ impl<'tcx> ctxt<'tcx> { return } + // The primitive is not local, hence we are reading this out + // of metadata. + let _ignore = self.dep_graph.in_ignore(); + if self.populated_external_primitive_impls.borrow().contains(&primitive_def_id) { return } @@ -2482,6 +2338,10 @@ impl<'tcx> ctxt<'tcx> { return } + // The type is not local, hence we are reading this out of + // metadata and don't need to track edges. + let _ignore = self.dep_graph.in_ignore(); + if self.populated_external_types.borrow().contains(&type_id) { return } @@ -2507,6 +2367,10 @@ impl<'tcx> ctxt<'tcx> { return } + // The type is not local, hence we are reading this out of + // metadata and don't need to track edges. + let _ignore = self.dep_graph.in_ignore(); + let def = self.lookup_trait_def(trait_id); if def.flags.get().intersects(TraitFlags::IMPLS_VALID) { return; @@ -2729,15 +2593,24 @@ impl<'tcx> ctxt<'tcx> { pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option { Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone()) } + + + pub fn visit_all_items_in_krate(&self, + dep_node_fn: F, + visitor: &mut V) + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> + { + dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor); + } } /// The category of explicit self. #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum ExplicitSelfCategory { - StaticExplicitSelfCategory, - ByValueExplicitSelfCategory, - ByReferenceExplicitSelfCategory(Region, hir::Mutability), - ByBoxExplicitSelfCategory, + Static, + ByValue, + ByReference(Region, hir::Mutability), + ByBox, } /// A free variable referred to in a function. @@ -2795,73 +2668,3 @@ impl<'tcx> ctxt<'tcx> { trait_ref.substs.clone().with_method(meth_tps, meth_regions) } } - -/// An "escaping region" is a bound region whose binder is not part of `t`. -/// -/// So, for example, consider a type like the following, which has two binders: -/// -/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize)) -/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope -/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope -/// -/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the -/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner -/// fn type*, that type has an escaping region: `'a`. -/// -/// Note that what I'm calling an "escaping region" is often just called a "free region". However, -/// we already use the term "free region". It refers to the regions that we use to represent bound -/// regions on a fn definition while we are typechecking its body. -/// -/// To clarify, conceptually there is no particular difference between an "escaping" region and a -/// "free" region. However, there is a big difference in practice. Basically, when "entering" a -/// binding level, one is generally required to do some sort of processing to a bound region, such -/// as replacing it with a fresh/skolemized region, or making an entry in the environment to -/// represent the scope to which it is attached, etc. An escaping region represents a bound region -/// for which this processing has not yet been done. -pub trait RegionEscape { - fn has_escaping_regions(&self) -> bool { - self.has_regions_escaping_depth(0) - } - - fn has_regions_escaping_depth(&self, depth: u32) -> bool; -} - -pub trait HasTypeFlags { - fn has_type_flags(&self, flags: TypeFlags) -> bool; - fn has_projection_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PROJECTION) - } - fn references_error(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_ERR) - } - fn has_param_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PARAMS) - } - fn has_self_ty(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_SELF) - } - fn has_infer_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INFER) - } - fn needs_infer(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER) - } - fn needs_subst(&self) -> bool { - self.has_type_flags(TypeFlags::NEEDS_SUBST) - } - fn has_closure_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_CLOSURE) - } - fn has_erasable_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND | - TypeFlags::HAS_RE_INFER | - TypeFlags::HAS_FREE_REGIONS) - } - /// Indicates whether this value references only 'global' - /// types/lifetimes that are the same regardless of what fn we are - /// in. This is used for caching. Errs on the side of returning - /// false. - fn is_global(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_LOCAL_NAMES) - } -} diff --git a/src/librustc/middle/ty/outlives.rs b/src/librustc/middle/ty/outlives.rs index 7752367febb19..fc20c1bcb85fe 100644 --- a/src/librustc/middle/ty/outlives.rs +++ b/src/librustc/middle/ty/outlives.rs @@ -13,7 +13,7 @@ // RFC for reference. use middle::infer::InferCtxt; -use middle::ty::{self, RegionEscape, Ty}; +use middle::ty::{self, Ty, TypeFoldable}; #[derive(Debug)] pub enum Component<'tcx> { diff --git a/src/librustc/middle/ty/relate.rs b/src/librustc/middle/ty/relate.rs index ff0a9789cf1f8..46bc13bd5988b 100644 --- a/src/librustc/middle/ty/relate.rs +++ b/src/librustc/middle/ty/relate.rs @@ -15,9 +15,8 @@ use middle::def_id::DefId; use middle::subst::{ErasedRegions, NonerasedRegions, ParamSpace, Substs}; -use middle::ty::{self, HasTypeFlags, Ty}; +use middle::ty::{self, Ty, TypeFoldable}; use middle::ty::error::{ExpectedFound, TypeError}; -use middle::ty::fold::TypeFoldable; use std::rc::Rc; use syntax::abi; use rustc_front::hir as ast; @@ -80,7 +79,7 @@ pub trait TypeRelation<'a,'tcx> : Sized { where T: Relate<'a,'tcx>; } -pub trait Relate<'a,'tcx>: TypeFoldable<'tcx> + HasTypeFlags { +pub trait Relate<'a,'tcx>: TypeFoldable<'tcx> { fn relate>(relation: &mut R, a: &Self, b: &Self) diff --git a/src/librustc/middle/ty/structural_impls.rs b/src/librustc/middle/ty/structural_impls.rs index ecb2b85fd7744..01b2bd36b4f07 100644 --- a/src/librustc/middle/ty/structural_impls.rs +++ b/src/librustc/middle/ty/structural_impls.rs @@ -10,9 +10,8 @@ use middle::subst::{self, VecPerParamSpace}; use middle::traits; -use middle::ty::{self, TraitRef, Ty, TypeAndMut}; -use middle::ty::{HasTypeFlags, Lift, TypeFlags, RegionEscape}; -use middle::ty::fold::{TypeFoldable, TypeFolder}; +use middle::ty::{self, Lift, TraitRef, Ty}; +use middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use std::rc::Rc; use syntax::abi; @@ -20,390 +19,6 @@ use syntax::ptr::P; use rustc_front::hir; -// FIXME(#20298) -- all of these traits basically walk various -// structures to test whether types/regions are reachable with various -// properties. It should be possible to express them in terms of one -// common "walker" trait or something. - -impl<'tcx> RegionEscape for Ty<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.region_depth > depth - } -} - -impl<'tcx> RegionEscape for ty::TraitTy<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.principal.has_regions_escaping_depth(depth) || - self.bounds.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for ty::ExistentialBounds<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.region_bound.has_regions_escaping_depth(depth) || - self.projection_bounds.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for ty::InstantiatedPredicates<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.predicates.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for subst::Substs<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.types.has_regions_escaping_depth(depth) || - self.regions.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for ty::ClosureSubsts<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.func_substs.has_regions_escaping_depth(depth) || - self.upvar_tys.iter().any(|t| t.has_regions_escaping_depth(depth)) - } -} - -impl RegionEscape for Vec { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.iter().any(|t| t.has_regions_escaping_depth(depth)) - } -} - -impl<'tcx> RegionEscape for ty::FnSig<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.inputs.has_regions_escaping_depth(depth) || - self.output.has_regions_escaping_depth(depth) - } -} - -impl<'tcx,T:RegionEscape> RegionEscape for VecPerParamSpace { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.iter_enumerated().any(|(space, _, t)| { - if space == subst::FnSpace { - t.has_regions_escaping_depth(depth+1) - } else { - t.has_regions_escaping_depth(depth) - } - }) - } -} - -impl<'tcx> RegionEscape for ty::TypeScheme<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.ty.has_regions_escaping_depth(depth) - } -} - -impl RegionEscape for ty::Region { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.escapes_depth(depth) - } -} - -impl<'tcx> RegionEscape for ty::GenericPredicates<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.predicates.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for ty::Predicate<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - match *self { - ty::Predicate::Trait(ref data) => data.has_regions_escaping_depth(depth), - ty::Predicate::Equate(ref data) => data.has_regions_escaping_depth(depth), - ty::Predicate::RegionOutlives(ref data) => data.has_regions_escaping_depth(depth), - ty::Predicate::TypeOutlives(ref data) => data.has_regions_escaping_depth(depth), - ty::Predicate::Projection(ref data) => data.has_regions_escaping_depth(depth), - ty::Predicate::WellFormed(ty) => ty.has_regions_escaping_depth(depth), - ty::Predicate::ObjectSafe(_trait_def_id) => false, - } - } -} - -impl<'tcx> RegionEscape for TraitRef<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.substs.types.iter().any(|t| t.has_regions_escaping_depth(depth)) || - self.substs.regions.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for subst::RegionSubsts { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - match *self { - subst::ErasedRegions => false, - subst::NonerasedRegions(ref r) => { - r.iter().any(|t| t.has_regions_escaping_depth(depth)) - } - } - } -} - -impl<'tcx,T:RegionEscape> RegionEscape for ty::Binder { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.0.has_regions_escaping_depth(depth + 1) - } -} - -impl<'tcx> RegionEscape for ty::FnOutput<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - match *self { - ty::FnConverging(t) => t.has_regions_escaping_depth(depth), - ty::FnDiverging => false - } - } -} - -impl<'tcx> RegionEscape for ty::EquatePredicate<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.0.has_regions_escaping_depth(depth) || self.1.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for ty::TraitPredicate<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.trait_ref.has_regions_escaping_depth(depth) - } -} - -impl RegionEscape for ty::OutlivesPredicate { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.0.has_regions_escaping_depth(depth) || self.1.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for ty::ProjectionPredicate<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.projection_ty.has_regions_escaping_depth(depth) || - self.ty.has_regions_escaping_depth(depth) - } -} - -impl<'tcx> RegionEscape for ty::ProjectionTy<'tcx> { - fn has_regions_escaping_depth(&self, depth: u32) -> bool { - self.trait_ref.has_regions_escaping_depth(depth) - } -} - -impl HasTypeFlags for () { - fn has_type_flags(&self, _flags: TypeFlags) -> bool { - false - } -} - -impl<'tcx,T:HasTypeFlags> HasTypeFlags for Vec { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self[..].has_type_flags(flags) - } -} - -impl<'tcx,T:HasTypeFlags> HasTypeFlags for [T] { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.iter().any(|p| p.has_type_flags(flags)) - } -} - -impl<'tcx,T:HasTypeFlags> HasTypeFlags for VecPerParamSpace { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.iter().any(|p| p.has_type_flags(flags)) - } -} - -impl HasTypeFlags for abi::Abi { - fn has_type_flags(&self, _flags: TypeFlags) -> bool { - false - } -} - -impl HasTypeFlags for hir::Unsafety { - fn has_type_flags(&self, _flags: TypeFlags) -> bool { - false - } -} - -impl HasTypeFlags for ty::BuiltinBounds { - fn has_type_flags(&self, _flags: TypeFlags) -> bool { - false - } -} - -impl<'tcx> HasTypeFlags for ty::ClosureTy<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.sig.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::ClosureUpvar<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.ty.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::ExistentialBounds<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.projection_bounds.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::InstantiatedPredicates<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.predicates.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::Predicate<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - match *self { - ty::Predicate::Trait(ref data) => data.has_type_flags(flags), - ty::Predicate::Equate(ref data) => data.has_type_flags(flags), - ty::Predicate::RegionOutlives(ref data) => data.has_type_flags(flags), - ty::Predicate::TypeOutlives(ref data) => data.has_type_flags(flags), - ty::Predicate::Projection(ref data) => data.has_type_flags(flags), - ty::Predicate::WellFormed(data) => data.has_type_flags(flags), - ty::Predicate::ObjectSafe(_trait_def_id) => false, - } - } -} - -impl<'tcx> HasTypeFlags for ty::TraitPredicate<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.trait_ref.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::EquatePredicate<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.0.has_type_flags(flags) || self.1.has_type_flags(flags) - } -} - -impl HasTypeFlags for ty::Region { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - if flags.intersects(TypeFlags::HAS_LOCAL_NAMES) { - // does this represent a region that cannot be named in a global - // way? used in fulfillment caching. - match *self { - ty::ReStatic | ty::ReEmpty => {} - _ => return true - } - } - if flags.intersects(TypeFlags::HAS_RE_INFER) { - match *self { - ty::ReVar(_) | ty::ReSkolemized(..) => { return true } - _ => {} - } - } - false - } -} - -impl HasTypeFlags for ty::OutlivesPredicate { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.0.has_type_flags(flags) || self.1.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::ProjectionPredicate<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.projection_ty.has_type_flags(flags) || self.ty.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::ProjectionTy<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.trait_ref.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for Ty<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.flags.get().intersects(flags) - } -} - -impl<'tcx> HasTypeFlags for TypeAndMut<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.ty.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for TraitRef<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.substs.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for subst::Substs<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.types.has_type_flags(flags) || match self.regions { - subst::ErasedRegions => false, - subst::NonerasedRegions(ref r) => r.has_type_flags(flags) - } - } -} - -impl<'tcx,T> HasTypeFlags for Option - where T : HasTypeFlags -{ - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.iter().any(|t| t.has_type_flags(flags)) - } -} - -impl<'tcx,T> HasTypeFlags for Rc - where T : HasTypeFlags -{ - fn has_type_flags(&self, flags: TypeFlags) -> bool { - (**self).has_type_flags(flags) - } -} - -impl<'tcx,T> HasTypeFlags for Box - where T : HasTypeFlags -{ - fn has_type_flags(&self, flags: TypeFlags) -> bool { - (**self).has_type_flags(flags) - } -} - -impl HasTypeFlags for ty::Binder - where T : HasTypeFlags -{ - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.0.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::FnOutput<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - match *self { - ty::FnConverging(t) => t.has_type_flags(flags), - ty::FnDiverging => false, - } - } -} - -impl<'tcx> HasTypeFlags for ty::FnSig<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.inputs.iter().any(|t| t.has_type_flags(flags)) || - self.output.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::BareFnTy<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.sig.has_type_flags(flags) - } -} - -impl<'tcx> HasTypeFlags for ty::ClosureSubsts<'tcx> { - fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.func_substs.has_type_flags(flags) || - self.upvar_tys.iter().any(|t| t.has_type_flags(flags)) - } -} - /////////////////////////////////////////////////////////////////////////// // Lift implementations @@ -508,9 +123,13 @@ macro_rules! CopyImpls { ($($ty:ty),+) => { $( impl<'tcx> TypeFoldable<'tcx> for $ty { - fn fold_with>(&self, _: &mut F) -> $ty { + fn super_fold_with>(&self, _: &mut F) -> $ty { *self } + + fn super_visit_with>(&self, _: &mut F) -> bool { + false + } } )+ } @@ -519,50 +138,88 @@ macro_rules! CopyImpls { CopyImpls! { (), hir::Unsafety, abi::Abi } impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) { - fn fold_with>(&self, folder: &mut F) -> (T, U) { + fn super_fold_with>(&self, folder: &mut F) -> (T, U) { (self.0.fold_with(folder), self.1.fold_with(folder)) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.0.visit_with(visitor) || self.1.visit_with(visitor) + } } impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Option { - fn fold_with>(&self, folder: &mut F) -> Option { + fn super_fold_with>(&self, folder: &mut F) -> Self { self.as_ref().map(|t| t.fold_with(folder)) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } } impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { - fn fold_with>(&self, folder: &mut F) -> Rc { + fn super_fold_with>(&self, folder: &mut F) -> Self { Rc::new((**self).fold_with(folder)) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } } impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box { - fn fold_with>(&self, folder: &mut F) -> Box { + fn super_fold_with>(&self, folder: &mut F) -> Self { let content: T = (**self).fold_with(folder); box content } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } } impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec { - fn fold_with>(&self, folder: &mut F) -> Vec { + fn super_fold_with>(&self, folder: &mut F) -> Self { self.iter().map(|t| t.fold_with(folder)).collect() } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } } impl<'tcx, T:TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { - fn fold_with>(&self, folder: &mut F) -> ty::Binder { + fn super_fold_with>(&self, folder: &mut F) -> Self { + folder.enter_region_binder(); + let result = ty::Binder(self.0.fold_with(folder)); + folder.exit_region_binder(); + result + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_binder(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + visitor.enter_region_binder(); + if self.0.visit_with(visitor) { return true } + visitor.exit_region_binder(); + false + } } impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for P<[T]> { - fn fold_with>(&self, folder: &mut F) -> P<[T]> { + fn super_fold_with>(&self, folder: &mut F) -> Self { self.iter().map(|t| t.fold_with(folder)).collect() } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } } impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for VecPerParamSpace { - fn fold_with>(&self, folder: &mut F) -> VecPerParamSpace { + fn super_fold_with>(&self, folder: &mut F) -> Self { // Things in the Fn space take place under an additional level // of region binding relative to the other spaces. This is @@ -582,100 +239,325 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for VecPerParamSpace { } result } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + let mut entered_region_binder = false; + let result = self.iter_enumerated().any(|(space, index, t)| { + if space == subst::FnSpace && index == 0 { + visitor.enter_region_binder(); + entered_region_binder = true; + } + t.visit_with(visitor) + }); + if entered_region_binder { + visitor.exit_region_binder(); + } + result + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::TraitTy<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + ty::TraitTy { + principal: self.principal.fold_with(folder), + bounds: self.bounds.fold_with(folder), + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.principal.visit_with(visitor) || self.bounds.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Ty<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let sty = match self.sty { + ty::TyBox(typ) => ty::TyBox(typ.fold_with(folder)), + ty::TyRawPtr(ref tm) => ty::TyRawPtr(tm.fold_with(folder)), + ty::TyArray(typ, sz) => ty::TyArray(typ.fold_with(folder), sz), + ty::TySlice(typ) => ty::TySlice(typ.fold_with(folder)), + ty::TyEnum(tid, ref substs) => { + let substs = substs.fold_with(folder); + ty::TyEnum(tid, folder.tcx().mk_substs(substs)) + } + ty::TyTrait(ref trait_ty) => ty::TyTrait(trait_ty.fold_with(folder)), + ty::TyTuple(ref ts) => ty::TyTuple(ts.fold_with(folder)), + ty::TyBareFn(opt_def_id, ref f) => { + let bfn = f.fold_with(folder); + ty::TyBareFn(opt_def_id, folder.tcx().mk_bare_fn(bfn)) + } + ty::TyRef(r, ref tm) => { + let r = r.fold_with(folder); + ty::TyRef(folder.tcx().mk_region(r), tm.fold_with(folder)) + } + ty::TyStruct(did, ref substs) => { + let substs = substs.fold_with(folder); + ty::TyStruct(did, folder.tcx().mk_substs(substs)) + } + ty::TyClosure(did, ref substs) => { + ty::TyClosure(did, substs.fold_with(folder)) + } + ty::TyProjection(ref data) => ty::TyProjection(data.fold_with(folder)), + ty::TyBool | ty::TyChar | ty::TyStr | ty::TyInt(_) | + ty::TyUint(_) | ty::TyFloat(_) | ty::TyError | ty::TyInfer(_) | + ty::TyParam(..) => self.sty.clone(), + }; + folder.tcx().mk_ty(sty) + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_ty(*self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match self.sty { + ty::TyBox(typ) => typ.visit_with(visitor), + ty::TyRawPtr(ref tm) => tm.visit_with(visitor), + ty::TyArray(typ, _sz) => typ.visit_with(visitor), + ty::TySlice(typ) => typ.visit_with(visitor), + ty::TyEnum(_tid, ref substs) => substs.visit_with(visitor), + ty::TyTrait(ref trait_ty) => trait_ty.visit_with(visitor), + ty::TyTuple(ref ts) => ts.visit_with(visitor), + ty::TyBareFn(_opt_def_id, ref f) => f.visit_with(visitor), + ty::TyRef(r, ref tm) => r.visit_with(visitor) || tm.visit_with(visitor), + ty::TyStruct(_did, ref substs) => substs.visit_with(visitor), + ty::TyClosure(_did, ref substs) => substs.visit_with(visitor), + ty::TyProjection(ref data) => data.visit_with(visitor), + ty::TyBool | ty::TyChar | ty::TyStr | ty::TyInt(_) | + ty::TyUint(_) | ty::TyFloat(_) | ty::TyError | ty::TyInfer(_) | + ty::TyParam(..) => false, + } + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_ty(self) + } } impl<'tcx> TypeFoldable<'tcx> for ty::BareFnTy<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::BareFnTy<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + ty::BareFnTy { sig: self.sig.fold_with(folder), + abi: self.abi, + unsafety: self.unsafety } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_bare_fn_ty(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.sig.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::ClosureTy<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::ClosureTy<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + ty::ClosureTy { + sig: self.sig.fold_with(folder), + unsafety: self.unsafety, + abi: self.abi, + } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_closure_ty(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.sig.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::TypeAndMut<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::TypeAndMut<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + ty::TypeAndMut { ty: self.ty.fold_with(folder), mutbl: self.mutbl } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_mt(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.ty.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::FnOutput<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::FnOutput<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + ty::FnConverging(ref ty) => ty::FnConverging(ty.fold_with(folder)), + ty::FnDiverging => ty::FnDiverging + } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_output(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + ty::FnConverging(ref ty) => ty.visit_with(visitor), + ty::FnDiverging => false, + } + } } impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::FnSig<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + ty::FnSig { inputs: self.inputs.fold_with(folder), + output: self.output.fold_with(folder), + variadic: self.variadic } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_fn_sig(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.inputs.visit_with(visitor) || self.output.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::TraitRef<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let substs = self.substs.fold_with(folder); + ty::TraitRef { + def_id: self.def_id, + substs: folder.tcx().mk_substs(substs), + } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_trait_ref(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.substs.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::Region { - fn fold_with>(&self, folder: &mut F) -> ty::Region { + fn super_fold_with>(&self, _folder: &mut F) -> Self { + *self + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_region(*self) } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_region(*self) + } } impl<'tcx> TypeFoldable<'tcx> for subst::Substs<'tcx> { - fn fold_with>(&self, folder: &mut F) -> subst::Substs<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let regions = match self.regions { + subst::ErasedRegions => subst::ErasedRegions, + subst::NonerasedRegions(ref regions) => { + subst::NonerasedRegions(regions.fold_with(folder)) + } + }; + + subst::Substs { regions: regions, + types: self.types.fold_with(folder) } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_substs(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.types.visit_with(visitor) || match self.regions { + subst::ErasedRegions => false, + subst::NonerasedRegions(ref regions) => regions.visit_with(visitor), + } + } } impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::ClosureSubsts<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { let func_substs = self.func_substs.fold_with(folder); ty::ClosureSubsts { func_substs: folder.tcx().mk_substs(func_substs), upvar_tys: self.upvar_tys.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.func_substs.visit_with(visitor) || self.upvar_tys.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::ItemSubsts<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::ItemSubsts<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::ItemSubsts { substs: self.substs.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.substs.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::AutoRef<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::adjustment::AutoRef<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + ty::adjustment::AutoPtr(r, m) => { + let r = r.fold_with(folder); + ty::adjustment::AutoPtr(folder.tcx().mk_region(r), m) + } + ty::adjustment::AutoUnsafe(m) => ty::adjustment::AutoUnsafe(m) + } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_autoref(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + ty::adjustment::AutoPtr(r, _m) => r.visit_with(visitor), + ty::adjustment::AutoUnsafe(_m) => false, + } + } } impl<'tcx> TypeFoldable<'tcx> for ty::BuiltinBounds { - fn fold_with>(&self, _folder: &mut F) -> ty::BuiltinBounds { + fn super_fold_with>(&self, _folder: &mut F) -> Self { *self } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } } impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialBounds<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::ExistentialBounds<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + ty::ExistentialBounds { + region_bound: self.region_bound.fold_with(folder), + builtin_bounds: self.builtin_bounds, + projection_bounds: self.projection_bounds.fold_with(folder), + } + } + + fn fold_with>(&self, folder: &mut F) -> Self { folder.fold_existential_bounds(self) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.region_bound.visit_with(visitor) || self.projection_bounds.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::TypeParameterDef<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::TypeParameterDef { name: self.name, def_id: self.def_id, @@ -686,10 +568,15 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> { object_lifetime_default: self.object_lifetime_default.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.default.visit_with(visitor) || + self.object_lifetime_default.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault { - fn fold_with>(&self, folder: &mut F) -> ty::ObjectLifetimeDefault { + fn super_fold_with>(&self, folder: &mut F) -> Self { match *self { ty::ObjectLifetimeDefault::Ambiguous => ty::ObjectLifetimeDefault::Ambiguous, @@ -701,10 +588,17 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault { ty::ObjectLifetimeDefault::Specific(r.fold_with(folder)), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + ty::ObjectLifetimeDefault::Specific(r) => r.visit_with(visitor), + _ => false, + } + } } impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef { - fn fold_with>(&self, folder: &mut F) -> ty::RegionParameterDef { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::RegionParameterDef { name: self.name, def_id: self.def_id, @@ -713,27 +607,39 @@ impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef { bounds: self.bounds.fold_with(folder) } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.bounds.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::Generics<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::Generics<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::Generics { types: self.types.fold_with(folder), regions: self.regions.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.types.visit_with(visitor) || self.regions.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::GenericPredicates<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::GenericPredicates { predicates: self.predicates.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.predicates.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::Predicate<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { match *self { ty::Predicate::Trait(ref a) => ty::Predicate::Trait(a.fold_with(folder)), @@ -751,71 +657,111 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { ty::Predicate::ObjectSafe(trait_def_id), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + ty::Predicate::Trait(ref a) => a.visit_with(visitor), + ty::Predicate::Equate(ref binder) => binder.visit_with(visitor), + ty::Predicate::RegionOutlives(ref binder) => binder.visit_with(visitor), + ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor), + ty::Predicate::Projection(ref binder) => binder.visit_with(visitor), + ty::Predicate::WellFormed(data) => data.visit_with(visitor), + ty::Predicate::ObjectSafe(_trait_def_id) => false, + } + } } impl<'tcx> TypeFoldable<'tcx> for ty::ProjectionPredicate<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::ProjectionPredicate<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::ProjectionPredicate { projection_ty: self.projection_ty.fold_with(folder), ty: self.ty.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.projection_ty.visit_with(visitor) || self.ty.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::ProjectionTy<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::ProjectionTy<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::ProjectionTy { trait_ref: self.trait_ref.fold_with(folder), item_name: self.item_name, } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.trait_ref.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::InstantiatedPredicates<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::InstantiatedPredicates<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::InstantiatedPredicates { predicates: self.predicates.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.predicates.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::EquatePredicate<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::EquatePredicate<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::EquatePredicate(self.0.fold_with(folder), self.1.fold_with(folder)) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.0.visit_with(visitor) || self.1.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::TraitPredicate<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::TraitPredicate<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::TraitPredicate { trait_ref: self.trait_ref.fold_with(folder) } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.trait_ref.visit_with(visitor) + } } impl<'tcx,T,U> TypeFoldable<'tcx> for ty::OutlivesPredicate where T : TypeFoldable<'tcx>, U : TypeFoldable<'tcx>, { - fn fold_with>(&self, folder: &mut F) -> ty::OutlivesPredicate { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::OutlivesPredicate(self.0.fold_with(folder), self.1.fold_with(folder)) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.0.visit_with(visitor) || self.1.visit_with(visitor) + } } impl<'tcx> TypeFoldable<'tcx> for ty::ClosureUpvar<'tcx> { - fn fold_with>(&self, folder: &mut F) -> ty::ClosureUpvar<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::ClosureUpvar { def: self.def, span: self.span, ty: self.ty.fold_with(folder), } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.ty.visit_with(visitor) + } } impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where 'tcx: 'a { - fn fold_with>(&self, folder: &mut F) -> ty::ParameterEnvironment<'a, 'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { ty::ParameterEnvironment { tcx: self.tcx, free_substs: self.free_substs.fold_with(folder), @@ -826,4 +772,23 @@ impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where ' free_id_outlive: self.free_id_outlive, } } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.free_substs.visit_with(visitor) || + self.implicit_region_bound.visit_with(visitor) || + self.caller_bounds.visit_with(visitor) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::TypeScheme<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + ty::TypeScheme { + generics: self.generics.fold_with(folder), + ty: self.ty.fold_with(folder), + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.generics.visit_with(visitor) || self.ty.visit_with(visitor) + } } diff --git a/src/librustc/middle/ty/sty.rs b/src/librustc/middle/ty/sty.rs index 66b2a9d3ad0bf..7e8d38ae8e797 100644 --- a/src/librustc/middle/ty/sty.rs +++ b/src/librustc/middle/ty/sty.rs @@ -15,8 +15,7 @@ use middle::def_id::DefId; use middle::region; use middle::subst::{self, Substs}; use middle::traits; -use middle::ty::{self, AdtDef, TypeFlags, Ty, TyS}; -use middle::ty::{RegionEscape, ToPredicate}; +use middle::ty::{self, AdtDef, ToPredicate, TypeFlags, Ty, TyS, TypeFoldable}; use util::common::ErrorReported; use collections::enum_set::{self, EnumSet, CLike}; @@ -695,7 +694,6 @@ pub enum Region { #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] pub struct EarlyBoundRegion { - pub def_id: DefId, pub space: subst::ParamSpace, pub index: u32, pub name: Name, diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs new file mode 100644 index 0000000000000..db001ce2c446c --- /dev/null +++ b/src/librustc/middle/ty/trait_def.rs @@ -0,0 +1,226 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dep_graph::DepNode; +use middle::def_id::DefId; +use middle::ty; +use middle::ty::fast_reject; +use middle::ty::Ty; +use std::borrow::{Borrow}; +use std::cell::{Cell, Ref, RefCell}; +use syntax::ast::Name; +use rustc_front::hir; +use util::nodemap::FnvHashMap; + +/// As `TypeScheme` but for a trait ref. +pub struct TraitDef<'tcx> { + pub unsafety: hir::Unsafety, + + /// If `true`, then this trait had the `#[rustc_paren_sugar]` + /// attribute, indicating that it should be used with `Foo()` + /// sugar. This is a temporary thing -- eventually any trait wil + /// be usable with the sugar (or without it). + pub paren_sugar: bool, + + /// Generic type definitions. Note that `Self` is listed in here + /// as having a single bound, the trait itself (e.g., in the trait + /// `Eq`, there is a single bound `Self : Eq`). This is so that + /// default methods get to assume that the `Self` parameters + /// implements the trait. + pub generics: ty::Generics<'tcx>, + + pub trait_ref: ty::TraitRef<'tcx>, + + /// A list of the associated types defined in this trait. Useful + /// for resolving `X::Foo` type markers. + pub associated_type_names: Vec, + + // Impls of this trait. To allow for quicker lookup, the impls are indexed + // by a simplified version of their Self type: impls with a simplifiable + // Self are stored in nonblanket_impls keyed by it, while all other impls + // are stored in blanket_impls. + // + // These lists are tracked by `DepNode::TraitImpls`; we don't use + // a DepTrackingMap but instead have the `TraitDef` insert the + // required reads/writes. + + /// Impls of the trait. + nonblanket_impls: RefCell< + FnvHashMap> + >, + + /// Blanket impls associated with the trait. + blanket_impls: RefCell>, + + /// Various flags + pub flags: Cell +} + +impl<'tcx> TraitDef<'tcx> { + pub fn new(unsafety: hir::Unsafety, + paren_sugar: bool, + generics: ty::Generics<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + associated_type_names: Vec) + -> TraitDef<'tcx> { + TraitDef { + paren_sugar: paren_sugar, + unsafety: unsafety, + generics: generics, + trait_ref: trait_ref, + associated_type_names: associated_type_names, + nonblanket_impls: RefCell::new(FnvHashMap()), + blanket_impls: RefCell::new(vec![]), + flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS) + } + } + + pub fn def_id(&self) -> DefId { + self.trait_ref.def_id + } + + // returns None if not yet calculated + pub fn object_safety(&self) -> Option { + if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) { + Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE)) + } else { + None + } + } + + pub fn set_object_safety(&self, is_safe: bool) { + assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true)); + self.flags.set( + self.flags.get() | if is_safe { + TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE + } else { + TraitFlags::OBJECT_SAFETY_VALID + } + ); + } + + fn write_trait_impls(&self, tcx: &ty::ctxt<'tcx>) { + tcx.dep_graph.write(DepNode::TraitImpls(self.trait_ref.def_id)); + } + + fn read_trait_impls(&self, tcx: &ty::ctxt<'tcx>) { + tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id)); + } + + /// Records a trait-to-implementation mapping. + pub fn record_impl(&self, + tcx: &ty::ctxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: ty::TraitRef<'tcx>) { + debug!("TraitDef::record_impl for {:?}, from {:?}", + self, impl_trait_ref); + + // Record the write into the impl set, but only for local + // impls: external impls are handled differently. + if impl_def_id.is_local() { + self.write_trait_impls(tcx); + } + + // We don't want to borrow_mut after we already populated all impls, + // so check if an impl is present with an immutable borrow first. + if let Some(sty) = fast_reject::simplify_type(tcx, + impl_trait_ref.self_ty(), false) { + if let Some(is) = self.nonblanket_impls.borrow().get(&sty) { + if is.contains(&impl_def_id) { + return // duplicate - skip + } + } + + self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id) + } else { + if self.blanket_impls.borrow().contains(&impl_def_id) { + return // duplicate - skip + } + self.blanket_impls.borrow_mut().push(impl_def_id) + } + } + + pub fn for_each_impl(&self, tcx: &ty::ctxt<'tcx>, mut f: F) { + self.read_trait_impls(tcx); + + tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); + + for &impl_def_id in self.blanket_impls.borrow().iter() { + f(impl_def_id); + } + + for v in self.nonblanket_impls.borrow().values() { + for &impl_def_id in v { + f(impl_def_id); + } + } + } + + /// Iterate over every impl that could possibly match the + /// self-type `self_ty`. + pub fn for_each_relevant_impl(&self, + tcx: &ty::ctxt<'tcx>, + self_ty: Ty<'tcx>, + mut f: F) + { + self.read_trait_impls(tcx); + + tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); + + for &impl_def_id in self.blanket_impls.borrow().iter() { + f(impl_def_id); + } + + // simplify_type(.., false) basically replaces type parameters and + // projections with infer-variables. This is, of course, done on + // the impl trait-ref when it is instantiated, but not on the + // predicate trait-ref which is passed here. + // + // for example, if we match `S: Copy` against an impl like + // `impl Copy for Option`, we replace the type variable + // in `Option` with an infer variable, to `Option<_>` (this + // doesn't actually change fast_reject output), but we don't + // replace `S` with anything - this impl of course can't be + // selected, and as there are hundreds of similar impls, + // considering them would significantly harm performance. + if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) { + if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) { + for &impl_def_id in impls { + f(impl_def_id); + } + } + } else { + for v in self.nonblanket_impls.borrow().values() { + for &impl_def_id in v { + f(impl_def_id); + } + } + } + } + + pub fn borrow_impl_lists<'s>(&'s self, tcx: &ty::ctxt<'tcx>) + -> (Ref<'s, Vec>, + Ref<'s, FnvHashMap>>) { + self.read_trait_impls(tcx); + (self.blanket_impls.borrow(), self.nonblanket_impls.borrow()) + } + +} + +bitflags! { + flags TraitFlags: u32 { + const NO_TRAIT_FLAGS = 0, + const HAS_DEFAULT_IMPL = 1 << 0, + const IS_OBJECT_SAFE = 1 << 1, + const OBJECT_SAFETY_VALID = 1 << 2, + const IMPLS_VALID = 1 << 3, + } +} + diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs index 0517769356f75..af23efe2bf4ba 100644 --- a/src/librustc/middle/ty/util.rs +++ b/src/librustc/middle/ty/util.rs @@ -18,9 +18,8 @@ use middle::subst::{self, Subst, Substs}; use middle::infer; use middle::pat_util; use middle::traits; -use middle::ty::{self, Ty, TypeAndMut, TypeFlags}; +use middle::ty::{self, Ty, TypeAndMut, TypeFlags, TypeFoldable}; use middle::ty::{Disr, ParameterEnvironment}; -use middle::ty::{HasTypeFlags, RegionEscape}; use middle::ty::TypeVariants::*; use util::num::ToPrimitive; diff --git a/src/librustc/middle/ty/wf.rs b/src/librustc/middle/ty/wf.rs index d015711fa64db..5f0fc306c24f8 100644 --- a/src/librustc/middle/ty/wf.rs +++ b/src/librustc/middle/ty/wf.rs @@ -13,7 +13,7 @@ use middle::infer::InferCtxt; use middle::ty::outlives::{self, Component}; use middle::subst::Substs; use middle::traits; -use middle::ty::{self, RegionEscape, ToPredicate, Ty}; +use middle::ty::{self, ToPredicate, Ty, TypeFoldable}; use std::iter::once; use syntax::ast; use syntax::codemap::Span; diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index e83fe60f419a6..6ca39d3ba7a87 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -11,14 +11,17 @@ use middle::const_eval::ConstVal; use middle::def_id::DefId; use middle::subst::Substs; -use middle::ty::{AdtDef, ClosureSubsts, FnOutput, Region, Ty}; +use middle::ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty}; use rustc_back::slice; use rustc_data_structures::tuple_slice::TupleSlice; use rustc_front::hir::InlineAsm; -use syntax::ast::Name; +use syntax::ast::{self, Name}; use syntax::codemap::Span; -use std::fmt::{Debug, Formatter, Error}; -use std::u32; +use std::ascii; +use std::borrow::{Cow, IntoCow}; +use std::fmt::{self, Debug, Formatter, Write}; +use std::{iter, u32}; +use std::ops::{Index, IndexMut}; /// Lowered representation of a single function. #[derive(RustcEncodable, RustcDecodable)] @@ -50,9 +53,6 @@ pub const START_BLOCK: BasicBlock = BasicBlock(0); /// where execution ends, on normal return pub const END_BLOCK: BasicBlock = BasicBlock(1); -/// where execution ends, on panic -pub const DIVERGE_BLOCK: BasicBlock = BasicBlock(2); - impl<'tcx> Mir<'tcx> { pub fn all_basic_blocks(&self) -> Vec { (0..self.basic_blocks.len()) @@ -69,6 +69,22 @@ impl<'tcx> Mir<'tcx> { } } +impl<'tcx> Index for Mir<'tcx> { + type Output = BasicBlockData<'tcx>; + + #[inline] + fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { + self.basic_block_data(index) + } +} + +impl<'tcx> IndexMut for Mir<'tcx> { + #[inline] + fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { + self.basic_block_data_mut(index) + } +} + /////////////////////////////////////////////////////////////////////////// // Mutability and borrow kinds @@ -182,8 +198,8 @@ impl BasicBlock { } impl Debug for BasicBlock { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { - write!(fmt, "BB({})", self.0) + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { + write!(fmt, "bb{}", self.0) } } @@ -193,7 +209,8 @@ impl Debug for BasicBlock { #[derive(Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { pub statements: Vec>, - pub terminator: Terminator<'tcx>, + pub terminator: Option>, + pub is_cleanup: bool, } #[derive(RustcEncodable, RustcDecodable)] @@ -203,12 +220,6 @@ pub enum Terminator<'tcx> { target: BasicBlock, }, - /// block should initiate unwinding; should be one successor - /// that does cleanup and branches to DIVERGE_BLOCK - Panic { - target: BasicBlock, - }, - /// jump to branch 0 if this lvalue evaluates to true If { cond: Operand<'tcx>, @@ -242,26 +253,75 @@ pub enum Terminator<'tcx> { targets: Vec, }, - /// Indicates that the last statement in the block panics, aborts, - /// etc. No successors. This terminator appears on exactly one - /// basic block which we create in advance. However, during - /// construction, we use this value as a sentinel for "terminator - /// not yet assigned", and assert at the end that only the - /// well-known diverging block actually diverges. - Diverge, + /// Indicates that the landing pad is finished and unwinding should + /// continue. Emitted by build::scope::diverge_cleanup. + Resume, /// Indicates a normal return. The ReturnPointer lvalue should /// have been filled in by now. This should only occur in the /// `END_BLOCK`. Return, - /// block ends with a call; it should have two successors. The - /// first successor indicates normal return. The second indicates - /// unwinding. + /// Block ends with a call of a converging function Call { - data: CallData<'tcx>, - targets: (BasicBlock, BasicBlock), + /// The function that’s being called + func: Operand<'tcx>, + /// Arguments the function is called with + args: Vec>, + /// The kind of call with associated information + kind: CallKind<'tcx>, + }, +} + +#[derive(Clone, RustcEncodable, RustcDecodable)] +pub enum CallKind<'tcx> { + /// Diverging function without associated cleanup + Diverging, + /// Diverging function with associated cleanup + DivergingCleanup(BasicBlock), + /// Converging function without associated cleanup + Converging { + /// Destination where the call result is written + destination: Lvalue<'tcx>, + /// Block to branch into on successful return + target: BasicBlock, }, + ConvergingCleanup { + /// Destination where the call result is written + destination: Lvalue<'tcx>, + /// First target is branched to on successful return. + /// Second block contains the cleanups to do on unwind. + targets: (BasicBlock, BasicBlock) + } +} + +impl<'tcx> CallKind<'tcx> { + pub fn successors(&self) -> &[BasicBlock] { + match *self { + CallKind::Diverging => &[], + CallKind::DivergingCleanup(ref b) | + CallKind::Converging { target: ref b, .. } => slice::ref_slice(b), + CallKind::ConvergingCleanup { ref targets, .. } => targets.as_slice(), + } + } + + pub fn successors_mut(&mut self) -> &mut [BasicBlock] { + match *self { + CallKind::Diverging => &mut [], + CallKind::DivergingCleanup(ref mut b) | + CallKind::Converging { target: ref mut b, .. } => slice::mut_ref_slice(b), + CallKind::ConvergingCleanup { ref mut targets, .. } => targets.as_mut_slice(), + } + } + + pub fn destination(&self) -> Option> { + match *self { + CallKind::Converging { ref destination, .. } | + CallKind::ConvergingCleanup { ref destination, .. } => Some(destination.clone()), + CallKind::Diverging | + CallKind::DivergingCleanup(_) => None + } + } } impl<'tcx> Terminator<'tcx> { @@ -269,13 +329,12 @@ impl<'tcx> Terminator<'tcx> { use self::Terminator::*; match *self { Goto { target: ref b } => slice::ref_slice(b), - Panic { target: ref b } => slice::ref_slice(b), - If { cond: _, targets: ref b } => b.as_slice(), + If { targets: ref b, .. } => b.as_slice(), Switch { targets: ref b, .. } => b, SwitchInt { targets: ref b, .. } => b, - Diverge => &[], + Resume => &[], Return => &[], - Call { data: _, targets: ref b } => b.as_slice(), + Call { ref kind, .. } => kind.successors(), } } @@ -283,68 +342,129 @@ impl<'tcx> Terminator<'tcx> { use self::Terminator::*; match *self { Goto { target: ref mut b } => slice::mut_ref_slice(b), - Panic { target: ref mut b } => slice::mut_ref_slice(b), - If { cond: _, targets: ref mut b } => b.as_mut_slice(), + If { targets: ref mut b, .. } => b.as_mut_slice(), Switch { targets: ref mut b, .. } => b, SwitchInt { targets: ref mut b, .. } => b, - Diverge => &mut [], + Resume => &mut [], Return => &mut [], - Call { data: _, targets: ref mut b } => b.as_mut_slice(), + Call { ref mut kind, .. } => kind.successors_mut(), } } } -#[derive(Debug, RustcEncodable, RustcDecodable)] -pub struct CallData<'tcx> { - /// where the return value is written to - pub destination: Lvalue<'tcx>, - - /// the fn being called - pub func: Operand<'tcx>, - - /// the arguments - pub args: Vec>, -} - impl<'tcx> BasicBlockData<'tcx> { - pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> { + pub fn new(terminator: Option>) -> BasicBlockData<'tcx> { BasicBlockData { statements: vec![], terminator: terminator, + is_cleanup: false, } } + + /// Accessor for terminator. + /// + /// Terminator may not be None after construction of the basic block is complete. This accessor + /// provides a convenience way to reach the terminator. + pub fn terminator(&self) -> &Terminator<'tcx> { + self.terminator.as_ref().expect("invalid terminator state") + } + + pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> { + self.terminator.as_mut().expect("invalid terminator state") + } } impl<'tcx> Debug for Terminator<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { + try!(self.fmt_head(fmt)); + let successors = self.successors(); + let labels = self.fmt_successor_labels(); + assert_eq!(successors.len(), labels.len()); + + match successors.len() { + 0 => Ok(()), + + 1 => write!(fmt, " -> {:?}", successors[0]), + + _ => { + try!(write!(fmt, " -> [")); + for (i, target) in successors.iter().enumerate() { + if i > 0 { + try!(write!(fmt, ", ")); + } + try!(write!(fmt, "{}: {:?}", labels[i], target)); + } + write!(fmt, "]") + } + + } + } +} + +impl<'tcx> Terminator<'tcx> { + /// Write the "head" part of the terminator; that is, its name and the data it uses to pick the + /// successor basic block, if any. The only information not inlcuded is the list of possible + /// successors, which may be rendered differently between the text and the graphviz format. + pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { use self::Terminator::*; match *self { - Goto { target } => - write!(fmt, "goto -> {:?}", target), - Panic { target } => - write!(fmt, "panic -> {:?}", target), - If { cond: ref lv, ref targets } => - write!(fmt, "if({:?}) -> {:?}", lv, targets), - Switch { discr: ref lv, adt_def: _, ref targets } => - write!(fmt, "switch({:?}) -> {:?}", lv, targets), - SwitchInt { discr: ref lv, switch_ty: _, ref values, ref targets } => - write!(fmt, "switchInt({:?}, {:?}) -> {:?}", lv, values, targets), - Diverge => - write!(fmt, "diverge"), - Return => - write!(fmt, "return"), - Call { data: ref c, targets } => { - try!(write!(fmt, "{:?} = {:?}(", c.destination, c.func)); - for (index, arg) in c.args.iter().enumerate() { + Goto { .. } => write!(fmt, "goto"), + If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv), + Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv), + SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), + Return => write!(fmt, "return"), + Resume => write!(fmt, "resume"), + Call { ref kind, ref func, ref args } => { + if let Some(destination) = kind.destination() { + try!(write!(fmt, "{:?} = ", destination)); + } + try!(write!(fmt, "{:?}(", func)); + for (index, arg) in args.iter().enumerate() { if index > 0 { try!(write!(fmt, ", ")); } try!(write!(fmt, "{:?}", arg)); } - write!(fmt, ") -> {:?}", targets) + write!(fmt, ")") } } } + + /// Return the list of labels for the edges to the successor basic blocks. + pub fn fmt_successor_labels(&self) -> Vec> { + use self::Terminator::*; + match *self { + Return | Resume => vec![], + Goto { .. } => vec!["".into_cow()], + If { .. } => vec!["true".into_cow(), "false".into_cow()], + Switch { ref adt_def, .. } => { + adt_def.variants + .iter() + .map(|variant| variant.name.to_string().into_cow()) + .collect() + } + SwitchInt { ref values, .. } => { + values.iter() + .map(|const_val| { + let mut buf = String::new(); + fmt_const_val(&mut buf, const_val).unwrap(); + buf.into_cow() + }) + .chain(iter::once(String::from("otherwise").into_cow())) + .collect() + } + Call { ref kind, .. } => match *kind { + CallKind::Diverging => + vec![], + CallKind::DivergingCleanup(..) => + vec!["unwind".into_cow()], + CallKind::Converging { .. } => + vec!["return".into_cow()], + CallKind::ConvergingCleanup { .. } => + vec!["return".into_cow(), "unwind".into_cow()], + }, + } + } } @@ -370,7 +490,7 @@ pub enum DropKind { } impl<'tcx> Debug for Statement<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::StatementKind::*; match self.kind { Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv), @@ -445,13 +565,11 @@ pub enum ProjectionElem<'tcx, V> { /// Alias for projections as they appear in lvalues, where the base is an lvalue /// and the index is an operand. -pub type LvalueProjection<'tcx> = - Projection<'tcx,Lvalue<'tcx>,Operand<'tcx>>; +pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Operand<'tcx>>; /// Alias for projections as they appear in lvalues, where the base is an lvalue /// and the index is an operand. -pub type LvalueElem<'tcx> = - ProjectionElem<'tcx,Operand<'tcx>>; +pub type LvalueElem<'tcx> = ProjectionElem<'tcx, Operand<'tcx>>; /// Index into the list of fields found in a `VariantDef` #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] @@ -490,34 +608,34 @@ impl<'tcx> Lvalue<'tcx> { } impl<'tcx> Debug for Lvalue<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::Lvalue::*; match *self { Var(id) => - write!(fmt,"Var({:?})", id), + write!(fmt, "var{:?}", id), Arg(id) => - write!(fmt,"Arg({:?})", id), + write!(fmt, "arg{:?}", id), Temp(id) => - write!(fmt,"Temp({:?})", id), - Static(id) => - write!(fmt,"Static({:?})", id), + write!(fmt, "tmp{:?}", id), + Static(def_id) => + write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))), ReturnPointer => - write!(fmt,"ReturnPointer"), + write!(fmt, "return"), Projection(ref data) => match data.elem { - ProjectionElem::Downcast(_, variant_index) => - write!(fmt,"({:?} as {:?})", data.base, variant_index), + ProjectionElem::Downcast(ref adt_def, index) => + write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name), ProjectionElem::Deref => - write!(fmt,"(*{:?})", data.base), + write!(fmt, "(*{:?})", data.base), ProjectionElem::Field(field) => - write!(fmt,"{:?}.{:?}", data.base, field.index()), + write!(fmt, "{:?}.{:?}", data.base, field.index()), ProjectionElem::Index(ref index) => - write!(fmt,"{:?}[{:?}]", data.base, index), + write!(fmt, "{:?}[{:?}]", data.base, index), ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => - write!(fmt,"{:?}[{:?} of {:?}]", data.base, offset, min_length), + write!(fmt, "{:?}[{:?} of {:?}]", data.base, offset, min_length), ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => - write!(fmt,"{:?}[-{:?} of {:?}]", data.base, offset, min_length), + write!(fmt, "{:?}[-{:?} of {:?}]", data.base, offset, min_length), }, } } @@ -537,7 +655,7 @@ pub enum Operand<'tcx> { } impl<'tcx> Debug for Operand<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::Operand::*; match *self { Constant(ref a) => write!(fmt, "{:?}", a), @@ -664,22 +782,87 @@ pub enum UnOp { } impl<'tcx> Debug for Rvalue<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::Rvalue::*; match *self { Use(ref lvalue) => write!(fmt, "{:?}", lvalue), Repeat(ref a, ref b) => write!(fmt, "[{:?}; {:?}]", a, b), - Ref(ref a, bk, ref b) => write!(fmt, "&{:?} {:?} {:?}", a, bk, b), - Len(ref a) => write!(fmt, "LEN({:?})", a), - Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?}", lv, ty, kind), - BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?},{:?})", op, a, b), + Len(ref a) => write!(fmt, "Len({:?})", a), + Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?})", lv, ty, kind), + BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), - Box(ref t) => write!(fmt, "Box {:?}", t), - Aggregate(ref kind, ref lvs) => write!(fmt, "Aggregate<{:?}>({:?})", kind, lvs), + Box(ref t) => write!(fmt, "Box({:?})", t), InlineAsm(ref asm) => write!(fmt, "InlineAsm({:?})", asm), Slice { ref input, from_start, from_end } => write!(fmt, "{:?}[{:?}..-{:?}]", input, from_start, from_end), + + Ref(_, borrow_kind, ref lv) => { + let kind_str = match borrow_kind { + BorrowKind::Shared => "", + BorrowKind::Mut | BorrowKind::Unique => "mut ", + }; + write!(fmt, "&{}{:?}", kind_str, lv) + } + + Aggregate(ref kind, ref lvs) => { + use self::AggregateKind::*; + + fn fmt_tuple(fmt: &mut Formatter, name: &str, lvs: &[Operand]) -> fmt::Result { + let mut tuple_fmt = fmt.debug_tuple(name); + for lv in lvs { + tuple_fmt.field(lv); + } + tuple_fmt.finish() + } + + match *kind { + Vec => write!(fmt, "{:?}", lvs), + + Tuple => { + match lvs.len() { + 0 => write!(fmt, "()"), + 1 => write!(fmt, "({:?},)", lvs[0]), + _ => fmt_tuple(fmt, "", lvs), + } + } + + Adt(adt_def, variant, _) => { + let variant_def = &adt_def.variants[variant]; + let name = ty::tls::with(|tcx| tcx.item_path_str(variant_def.did)); + + match variant_def.kind() { + ty::VariantKind::Unit => write!(fmt, "{}", name), + ty::VariantKind::Tuple => fmt_tuple(fmt, &name, lvs), + ty::VariantKind::Struct => { + let mut struct_fmt = fmt.debug_struct(&name); + for (field, lv) in variant_def.fields.iter().zip(lvs) { + struct_fmt.field(&field.name.as_str(), lv); + } + struct_fmt.finish() + } + } + } + + Closure(def_id, _) => ty::tls::with(|tcx| { + if let Some(node_id) = tcx.map.as_local_node_id(def_id) { + let name = format!("[closure@{:?}]", tcx.map.span(node_id)); + let mut struct_fmt = fmt.debug_struct(&name); + + tcx.with_freevars(node_id, |freevars| { + for (freevar, lv) in freevars.iter().zip(lvs) { + let var_name = tcx.local_var_name_str(freevar.def.var_id()); + struct_fmt.field(&var_name, lv); + } + }); + + struct_fmt.finish() + } else { + write!(fmt, "[closure]") + } + }), + } + } } } } @@ -691,7 +874,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { // this does not necessarily mean that they are "==" in Rust -- in // particular one must be wary of `NaN`! -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] pub struct Constant<'tcx> { pub span: Span, pub ty: Ty<'tcx>, @@ -701,13 +884,13 @@ pub struct Constant<'tcx> { #[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, RustcDecodable)] pub enum ItemKind { Constant, + /// This is any sort of callable (usually those that have a type of `fn(…) -> …`). This + /// includes functions, constructors, but not methods which have their own ItemKind. Function, - Struct, - Variant, Method, } -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] pub enum Literal<'tcx> { Item { def_id: DefId, @@ -718,3 +901,53 @@ pub enum Literal<'tcx> { value: ConstVal, }, } + +impl<'tcx> Debug for Constant<'tcx> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { + write!(fmt, "{:?}", self.literal) + } +} + +impl<'tcx> Debug for Literal<'tcx> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { + use self::Literal::*; + match *self { + Item { def_id, .. } => + write!(fmt, "{}", item_path_str(def_id)), + Value { ref value } => { + try!(write!(fmt, "const ")); + fmt_const_val(fmt, value) + } + } + } +} + +/// Write a `ConstVal` in a way closer to the original source code than the `Debug` output. +fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { + use middle::const_eval::ConstVal::*; + match *const_val { + Float(f) => write!(fmt, "{:?}", f), + Int(n) => write!(fmt, "{:?}", n), + Uint(n) => write!(fmt, "{:?}", n), + Str(ref s) => write!(fmt, "{:?}", s), + ByteStr(ref bytes) => { + let escaped: String = bytes + .iter() + .flat_map(|&ch| ascii::escape_default(ch).map(|c| c as char)) + .collect(); + write!(fmt, "b\"{}\"", escaped) + } + Bool(b) => write!(fmt, "{:?}", b), + Function(def_id) => write!(fmt, "{}", item_path_str(def_id)), + Struct(node_id) | Tuple(node_id) | Array(node_id, _) | Repeat(node_id, _) => + write!(fmt, "{}", node_to_string(node_id)), + } +} + +fn node_to_string(node_id: ast::NodeId) -> String { + ty::tls::with(|tcx| tcx.map.node_to_user_string(node_id)) +} + +fn item_path_str(def_id: DefId) -> String { + ty::tls::with(|tcx| tcx.item_path_str(def_id)) +} diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 00d21d3c16e1f..7c8ea22de8e92 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -14,543 +14,353 @@ use mir::repr::*; use rustc_data_structures::tuple_slice::TupleSlice; use syntax::codemap::Span; -pub trait Visitor<'tcx> { - // Override these, and call `self.super_xxx` to revert back to the - // default behavior. +macro_rules! make_mir_visitor { + ($visitor_trait_name:ident, $($mutability:ident)*) => { + pub trait $visitor_trait_name<'tcx> { + // Override these, and call `self.super_xxx` to revert back to the + // default behavior. - fn visit_mir(&mut self, mir: &Mir<'tcx>) { - self.super_mir(mir); - } - - fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { - self.super_basic_block_data(block, data); - } - - fn visit_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { - self.super_statement(block, statement); - } - - fn visit_assign(&mut self, block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { - self.super_assign(block, lvalue, rvalue); - } - - fn visit_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { - self.super_terminator(block, terminator); - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { - self.super_rvalue(rvalue); - } - - fn visit_operand(&mut self, operand: &Operand<'tcx>) { - self.super_operand(operand); - } - - fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) { - self.super_lvalue(lvalue, context); - } - - fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { - self.super_branch(source, target); - } - - fn visit_constant(&mut self, constant: &Constant<'tcx>) { - self.super_constant(constant); - } - - fn visit_literal(&mut self, literal: &Literal<'tcx>) { - self.super_literal(literal); - } - - fn visit_def_id(&mut self, def_id: DefId) { - self.super_def_id(def_id); - } - - fn visit_span(&mut self, span: Span) { - self.super_span(span); - } - - // The `super_xxx` methods comprise the default behavior and are - // not meant to be overidden. - - fn super_mir(&mut self, mir: &Mir<'tcx>) { - for block in mir.all_basic_blocks() { - let data = mir.basic_block_data(block); - self.visit_basic_block_data(block, data); - } - } - - fn super_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { - for statement in &data.statements { - self.visit_statement(block, statement); - } - self.visit_terminator(block, &data.terminator); - } - - fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { - self.visit_span(statement.span); - - match statement.kind { - StatementKind::Assign(ref lvalue, ref rvalue) => { - self.visit_assign(block, lvalue, rvalue); - } - StatementKind::Drop(_, ref lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Drop); - } - } - } - - fn super_assign(&mut self, _block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { - self.visit_lvalue(lvalue, LvalueContext::Store); - self.visit_rvalue(rvalue); - } - - fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { - match *terminator { - Terminator::Goto { target } | - Terminator::Panic { target } => { - self.visit_branch(block, target); + fn visit_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) { + self.super_mir(mir); } - Terminator::If { ref cond, ref targets } => { - self.visit_operand(cond); - for &target in targets.as_slice() { - self.visit_branch(block, target); - } + fn visit_basic_block_data(&mut self, + block: BasicBlock, + data: & $($mutability)* BasicBlockData<'tcx>) { + self.super_basic_block_data(block, data); } - Terminator::Switch { ref discr, adt_def: _, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect); - for &target in targets { - self.visit_branch(block, target); - } + fn visit_statement(&mut self, + block: BasicBlock, + statement: & $($mutability)* Statement<'tcx>) { + self.super_statement(block, statement); } - Terminator::SwitchInt { ref discr, switch_ty: _, values: _, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect); - for &target in targets { - self.visit_branch(block, target); - } + fn visit_assign(&mut self, + block: BasicBlock, + lvalue: & $($mutability)* Lvalue<'tcx>, + rvalue: & $($mutability)* Rvalue<'tcx>) { + self.super_assign(block, lvalue, rvalue); } - Terminator::Diverge | - Terminator::Return => { + fn visit_terminator(&mut self, + block: BasicBlock, + terminator: & $($mutability)* Terminator<'tcx>) { + self.super_terminator(block, terminator); } - Terminator::Call { ref data, ref targets } => { - self.visit_lvalue(&data.destination, LvalueContext::Store); - self.visit_operand(&data.func); - for arg in &data.args { - self.visit_operand(arg); - } - for &target in targets.as_slice() { - self.visit_branch(block, target); - } + fn visit_rvalue(&mut self, + rvalue: & $($mutability)* Rvalue<'tcx>) { + self.super_rvalue(rvalue); } - } - } - fn super_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { - match *rvalue { - Rvalue::Use(ref operand) => { - self.visit_operand(operand); + fn visit_operand(&mut self, + operand: & $($mutability)* Operand<'tcx>) { + self.super_operand(operand); } - Rvalue::Repeat(ref value, ref len) => { - self.visit_operand(value); - self.visit_constant(len); + fn visit_lvalue(&mut self, + lvalue: & $($mutability)* Lvalue<'tcx>, + context: LvalueContext) { + self.super_lvalue(lvalue, context); } - Rvalue::Ref(r, bk, ref path) => { - self.visit_lvalue(path, LvalueContext::Borrow { - region: r, - kind: bk - }); + fn visit_branch(&mut self, + source: BasicBlock, + target: BasicBlock) { + self.super_branch(source, target); } - Rvalue::Len(ref path) => { - self.visit_lvalue(path, LvalueContext::Inspect); + fn visit_constant(&mut self, + constant: & $($mutability)* Constant<'tcx>) { + self.super_constant(constant); } - Rvalue::Cast(_, ref operand, _) => { - self.visit_operand(operand); + fn visit_literal(&mut self, + literal: & $($mutability)* Literal<'tcx>) { + self.super_literal(literal); } - Rvalue::BinaryOp(_, ref lhs, ref rhs) => { - self.visit_operand(lhs); - self.visit_operand(rhs); + fn visit_def_id(&mut self, + def_id: & $($mutability)* DefId) { + self.super_def_id(def_id); } - Rvalue::UnaryOp(_, ref op) => { - self.visit_operand(op); + fn visit_span(&mut self, + span: & $($mutability)* Span) { + self.super_span(span); } - Rvalue::Box(_) => { - } + // The `super_xxx` methods comprise the default behavior and are + // not meant to be overidden. - Rvalue::Aggregate(_, ref operands) => { - for operand in operands { - self.visit_operand(operand); + fn super_mir(&mut self, + mir: & $($mutability)* Mir<'tcx>) { + for block in mir.all_basic_blocks() { + let data = & $($mutability)* mir[block]; + self.visit_basic_block_data(block, data); } } - Rvalue::Slice { ref input, from_start, from_end } => { - self.visit_lvalue(input, LvalueContext::Slice { - from_start: from_start, - from_end: from_end, - }); - } + fn super_basic_block_data(&mut self, + block: BasicBlock, + data: & $($mutability)* BasicBlockData<'tcx>) { + for statement in & $($mutability)* data.statements { + self.visit_statement(block, statement); + } - Rvalue::InlineAsm(_) => { + if let Some(ref $($mutability)* terminator) = data.terminator { + self.visit_terminator(block, terminator); + } } - } - } - fn super_operand(&mut self, operand: &Operand<'tcx>) { - match *operand { - Operand::Consume(ref lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Consume); - } - Operand::Constant(ref constant) => { - self.visit_constant(constant); - } - } - } + fn super_statement(&mut self, + block: BasicBlock, + statement: & $($mutability)* Statement<'tcx>) { + self.visit_span(& $($mutability)* statement.span); - fn super_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: LvalueContext) { - match *lvalue { - Lvalue::Var(_) | - Lvalue::Temp(_) | - Lvalue::Arg(_) | - Lvalue::Static(_) | - Lvalue::ReturnPointer => { - } - Lvalue::Projection(ref proj) => { - self.visit_lvalue(&proj.base, LvalueContext::Projection); + match statement.kind { + StatementKind::Assign(ref $($mutability)* lvalue, + ref $($mutability)* rvalue) => { + self.visit_assign(block, lvalue, rvalue); + } + StatementKind::Drop(_, ref $($mutability)* lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Drop); + } + } } - } - } - - fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { - } - - fn super_constant(&mut self, constant: &Constant<'tcx>) { - self.visit_span(constant.span); - self.visit_literal(&constant.literal); - } - fn super_literal(&mut self, literal: &Literal<'tcx>) { - match *literal { - Literal::Item { def_id, .. } => { - self.visit_def_id(def_id); - }, - Literal::Value { .. } => { - // Nothing to do + fn super_assign(&mut self, + _block: BasicBlock, + lvalue: &$($mutability)* Lvalue<'tcx>, + rvalue: &$($mutability)* Rvalue<'tcx>) { + self.visit_lvalue(lvalue, LvalueContext::Store); + self.visit_rvalue(rvalue); } - } - } - - fn super_def_id(&mut self, _def_id: DefId) { - } - - fn super_span(&mut self, _span: Span) { - } -} - -#[derive(Copy, Clone, Debug)] -pub enum LvalueContext { - // Appears as LHS of an assignment or as dest of a call - Store, - - // Being dropped - Drop, - - // Being inspected in some way, like loading a len - Inspect, - - // Being borrowed - Borrow { region: Region, kind: BorrowKind }, - - // Being sliced -- this should be same as being borrowed, probably - Slice { from_start: usize, from_end: usize }, - - // Used as base for another lvalue, e.g. `x` in `x.y` - Projection, - - // Consumed as part of an operand - Consume, -} - -pub trait MutVisitor<'tcx> { - // Override these, and call `self.super_xxx` to revert back to the - // default behavior. - - fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { - self.super_mir(mir); - } - - fn visit_basic_block_data(&mut self, - block: BasicBlock, - data: &mut BasicBlockData<'tcx>) { - self.super_basic_block_data(block, data); - } - fn visit_statement(&mut self, - block: BasicBlock, - statement: &mut Statement<'tcx>) { - self.super_statement(block, statement); - } - - fn visit_assign(&mut self, - block: BasicBlock, - lvalue: &mut Lvalue<'tcx>, - rvalue: &mut Rvalue<'tcx>) { - self.super_assign(block, lvalue, rvalue); - } - - fn visit_terminator(&mut self, - block: BasicBlock, - terminator: &mut Terminator<'tcx>) { - self.super_terminator(block, terminator); - } - - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>) { - self.super_rvalue(rvalue); - } + fn super_terminator(&mut self, + block: BasicBlock, + terminator: &$($mutability)* Terminator<'tcx>) { + match *terminator { + Terminator::Goto { target } => { + self.visit_branch(block, target); + } - fn visit_operand(&mut self, operand: &mut Operand<'tcx>) { - self.super_operand(operand); - } + Terminator::If { ref $($mutability)* cond, + ref $($mutability)* targets } => { + self.visit_operand(cond); + for &target in targets.as_slice() { + self.visit_branch(block, target); + } + } - fn visit_lvalue(&mut self, - lvalue: &mut Lvalue<'tcx>, - context: LvalueContext) { - self.super_lvalue(lvalue, context); - } + Terminator::Switch { ref $($mutability)* discr, + adt_def: _, + ref targets } => { + self.visit_lvalue(discr, LvalueContext::Inspect); + for &target in targets { + self.visit_branch(block, target); + } + } - fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { - self.super_branch(source, target); - } + Terminator::SwitchInt { ref $($mutability)* discr, + switch_ty: _, + values: _, + ref targets } => { + self.visit_lvalue(discr, LvalueContext::Inspect); + for &target in targets { + self.visit_branch(block, target); + } + } - fn visit_constant(&mut self, constant: &mut Constant<'tcx>) { - self.super_constant(constant); - } + Terminator::Resume | + Terminator::Return => { + } - fn visit_literal(&mut self, literal: &mut Literal<'tcx>) { - self.super_literal(literal); - } + Terminator::Call { ref $($mutability)* func, + ref $($mutability)* args, + ref $($mutability)* kind } => { + self.visit_operand(func); + for arg in args { + self.visit_operand(arg); + } + match *kind { + CallKind::Converging { + ref $($mutability)* destination, + .. + } | + CallKind::ConvergingCleanup { + ref $($mutability)* destination, + .. + } => { + self.visit_lvalue(destination, LvalueContext::Store); + } + CallKind::Diverging | + CallKind::DivergingCleanup(_) => {} + } + for &target in kind.successors() { + self.visit_branch(block, target); + } + } + } + } - fn visit_def_id(&mut self, def_id: &mut DefId) { - self.super_def_id(def_id); - } + fn super_rvalue(&mut self, + rvalue: & $($mutability)* Rvalue<'tcx>) { + match *rvalue { + Rvalue::Use(ref $($mutability)* operand) => { + self.visit_operand(operand); + } - fn visit_span(&mut self, span: &mut Span) { - self.super_span(span); - } + Rvalue::Repeat(ref $($mutability)* value, + ref $($mutability)* len) => { + self.visit_operand(value); + self.visit_constant(len); + } - // The `super_xxx` methods comprise the default behavior and are - // not meant to be overidden. + Rvalue::Ref(r, bk, ref $($mutability)* path) => { + self.visit_lvalue(path, LvalueContext::Borrow { + region: r, + kind: bk + }); + } - fn super_mir(&mut self, mir: &mut Mir<'tcx>) { - for block in mir.all_basic_blocks() { - let data = mir.basic_block_data_mut(block); - self.visit_basic_block_data(block, data); - } - } + Rvalue::Len(ref $($mutability)* path) => { + self.visit_lvalue(path, LvalueContext::Inspect); + } - fn super_basic_block_data(&mut self, - block: BasicBlock, - data: &mut BasicBlockData<'tcx>) { - for statement in &mut data.statements { - self.visit_statement(block, statement); - } - self.visit_terminator(block, &mut data.terminator); - } + Rvalue::Cast(_, ref $($mutability)* operand, _) => { + self.visit_operand(operand); + } - fn super_statement(&mut self, - block: BasicBlock, - statement: &mut Statement<'tcx>) { - self.visit_span(&mut statement.span); + Rvalue::BinaryOp(_, + ref $($mutability)* lhs, + ref $($mutability)* rhs) => { + self.visit_operand(lhs); + self.visit_operand(rhs); + } - match statement.kind { - StatementKind::Assign(ref mut lvalue, ref mut rvalue) => { - self.visit_assign(block, lvalue, rvalue); - } - StatementKind::Drop(_, ref mut lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Drop); - } - } - } + Rvalue::UnaryOp(_, ref $($mutability)* op) => { + self.visit_operand(op); + } - fn super_assign(&mut self, - _block: BasicBlock, - lvalue: &mut Lvalue<'tcx>, - rvalue: &mut Rvalue<'tcx>) { - self.visit_lvalue(lvalue, LvalueContext::Store); - self.visit_rvalue(rvalue); - } + Rvalue::Box(_) => { + } - fn super_terminator(&mut self, - block: BasicBlock, - terminator: &mut Terminator<'tcx>) { - match *terminator { - Terminator::Goto { target } | - Terminator::Panic { target } => { - self.visit_branch(block, target); - } + Rvalue::Aggregate(ref $($mutability)* kind, + ref $($mutability)* operands) => { + match *kind { + AggregateKind::Closure(ref $($mutability)* def_id, _) => { + self.visit_def_id(def_id); + } + _ => { /* nothing to do */ } + } + + for operand in & $($mutability)* operands[..] { + self.visit_operand(operand); + } + } - Terminator::If { ref mut cond, ref mut targets } => { - self.visit_operand(cond); - for &target in targets.as_slice() { - self.visit_branch(block, target); - } - } + Rvalue::Slice { ref $($mutability)* input, + from_start, + from_end } => { + self.visit_lvalue(input, LvalueContext::Slice { + from_start: from_start, + from_end: from_end, + }); + } - Terminator::Switch { ref mut discr, adt_def: _, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect); - for &target in targets { - self.visit_branch(block, target); + Rvalue::InlineAsm(_) => { + } } } - Terminator::SwitchInt { ref mut discr, switch_ty: _, values: _, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect); - for &target in targets { - self.visit_branch(block, target); + fn super_operand(&mut self, + operand: & $($mutability)* Operand<'tcx>) { + match *operand { + Operand::Consume(ref $($mutability)* lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Consume); + } + Operand::Constant(ref $($mutability)* constant) => { + self.visit_constant(constant); + } } } - Terminator::Diverge | - Terminator::Return => { - } - - Terminator::Call { ref mut data, ref mut targets } => { - self.visit_lvalue(&mut data.destination, LvalueContext::Store); - self.visit_operand(&mut data.func); - for arg in &mut data.args { - self.visit_operand(arg); - } - for &target in targets.as_slice() { - self.visit_branch(block, target); + fn super_lvalue(&mut self, + lvalue: & $($mutability)* Lvalue<'tcx>, + _context: LvalueContext) { + match *lvalue { + Lvalue::Var(_) | + Lvalue::Temp(_) | + Lvalue::Arg(_) | + Lvalue::ReturnPointer => { + } + Lvalue::Static(ref $($mutability)* def_id) => { + self.visit_def_id(def_id); + } + Lvalue::Projection(ref $($mutability)* proj) => { + self.visit_lvalue(& $($mutability)* proj.base, + LvalueContext::Projection); + } } } - } - } - - fn super_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>) { - match *rvalue { - Rvalue::Use(ref mut operand) => { - self.visit_operand(operand); - } - - Rvalue::Repeat(ref mut value, ref mut len) => { - self.visit_operand(value); - self.visit_constant(len); - } - - Rvalue::Ref(r, bk, ref mut path) => { - self.visit_lvalue(path, LvalueContext::Borrow { - region: r, - kind: bk - }); - } - - Rvalue::Len(ref mut path) => { - self.visit_lvalue(path, LvalueContext::Inspect); - } - - Rvalue::Cast(_, ref mut operand, _) => { - self.visit_operand(operand); - } - - Rvalue::BinaryOp(_, ref mut lhs, ref mut rhs) => { - self.visit_operand(lhs); - self.visit_operand(rhs); - } - Rvalue::UnaryOp(_, ref mut op) => { - self.visit_operand(op); + fn super_branch(&mut self, + _source: BasicBlock, + _target: BasicBlock) { } - Rvalue::Box(_) => { + fn super_constant(&mut self, + constant: & $($mutability)* Constant<'tcx>) { + self.visit_span(& $($mutability)* constant.span); + self.visit_literal(& $($mutability)* constant.literal); } - Rvalue::Aggregate(ref mut kind, ref mut operands) => { - match *kind { - AggregateKind::Closure(ref mut def_id, _) => { + fn super_literal(&mut self, + literal: & $($mutability)* Literal<'tcx>) { + match *literal { + Literal::Item { ref $($mutability)* def_id, .. } => { self.visit_def_id(def_id); + }, + Literal::Value { .. } => { + // Nothing to do } - _ => { /* nothing to do */ } - } - - for operand in &mut operands[..] { - self.visit_operand(operand); } } - Rvalue::Slice { ref mut input, from_start, from_end } => { - self.visit_lvalue(input, LvalueContext::Slice { - from_start: from_start, - from_end: from_end, - }); + fn super_def_id(&mut self, _def_id: & $($mutability)* DefId) { } - Rvalue::InlineAsm(_) => { + fn super_span(&mut self, _span: & $($mutability)* Span) { } } } +} - fn super_operand(&mut self, operand: &mut Operand<'tcx>) { - match *operand { - Operand::Consume(ref mut lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Consume); - } - Operand::Constant(ref mut constant) => { - self.visit_constant(constant); - } - } - } +make_mir_visitor!(Visitor,); +make_mir_visitor!(MutVisitor,mut); - fn super_lvalue(&mut self, - lvalue: &mut Lvalue<'tcx>, - _context: LvalueContext) { - match *lvalue { - Lvalue::Var(_) | - Lvalue::Temp(_) | - Lvalue::Arg(_) | - Lvalue::ReturnPointer => { - } - Lvalue::Static(ref mut def_id) => { - self.visit_def_id(def_id); - } - Lvalue::Projection(ref mut proj) => { - self.visit_lvalue(&mut proj.base, LvalueContext::Projection); - } - } - } +#[derive(Copy, Clone, Debug)] +pub enum LvalueContext { + // Appears as LHS of an assignment or as dest of a call + Store, - fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { - } + // Being dropped + Drop, - fn super_constant(&mut self, constant: &mut Constant<'tcx>) { - self.visit_span(&mut constant.span); - self.visit_literal(&mut constant.literal); - } + // Being inspected in some way, like loading a len + Inspect, - fn super_literal(&mut self, literal: &mut Literal<'tcx>) { - match *literal { - Literal::Item { ref mut def_id, .. } => { - self.visit_def_id(def_id); - }, - Literal::Value { .. } => { - // Nothing to do - } - } - } + // Being borrowed + Borrow { region: Region, kind: BorrowKind }, - fn super_def_id(&mut self, _def_id: &mut DefId) { - } + // Being sliced -- this should be same as being borrowed, probably + Slice { from_start: usize, from_end: usize }, - fn super_span(&mut self, _span: &mut Span) { - } + // Used as base for another lvalue, e.g. `x` in `x.y` + Projection, + + // Consumed as part of an operand + Consume, } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index b275480a6fc68..0134bcdf1757b 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -125,6 +125,8 @@ pub struct Options { pub parse_only: bool, pub no_trans: bool, pub treat_err_as_bug: bool, + pub incremental_compilation: bool, + pub dump_dep_graph: bool, pub no_analysis: bool, pub debugging_opts: DebuggingOptions, pub prints: Vec, @@ -234,6 +236,8 @@ pub fn basic_options() -> Options { parse_only: false, no_trans: false, treat_err_as_bug: false, + incremental_compilation: false, + dump_dep_graph: false, no_analysis: false, debugging_opts: basic_debugging_options(), prints: Vec::new(), @@ -539,6 +543,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "explicitly enable the cfg(debug_assertions) directive"), inline_threshold: Option = (None, parse_opt_uint, "set the inlining threshold for"), + disable_gold: bool = (false, parse_bool, + "disable use of the ld.gold linker"), } @@ -604,6 +610,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "run all passes except translation; no output"), treat_err_as_bug: bool = (false, parse_bool, "treat all errors that occur as bugs"), + incr_comp: bool = (false, parse_bool, + "enable incremental compilation (experimental)"), + dump_dep_graph: bool = (false, parse_bool, + "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), no_analysis: bool = (false, parse_bool, "parse and expand the source, but run no analysis"), extra_plugins: Vec = (Vec::new(), parse_list, @@ -622,6 +632,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "force nonzeroing move optimization on"), keep_mtwt_tables: bool = (false, parse_bool, "don't clear the resolution tables after analysis"), + keep_ast: bool = (false, parse_bool, + "keep the AST after lowering it to HIR"), } pub fn default_lib_output() -> CrateType { @@ -928,6 +940,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let parse_only = debugging_opts.parse_only; let no_trans = debugging_opts.no_trans; let treat_err_as_bug = debugging_opts.treat_err_as_bug; + let incremental_compilation = debugging_opts.incr_comp; + let dump_dep_graph = debugging_opts.dump_dep_graph; let no_analysis = debugging_opts.no_analysis; if debugging_opts.debug_llvm { @@ -1102,6 +1116,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { parse_only: parse_only, no_trans: no_trans, treat_err_as_bug: treat_err_as_bug, + incremental_compilation: incremental_compilation || dump_dep_graph, + dump_dep_graph: dump_dep_graph, no_analysis: no_analysis, debugging_opts: debugging_opts, prints: prints, diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index a8587e8373681..80b4c1916a816 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -16,7 +16,7 @@ use util::nodemap::{NodeMap, FnvHashMap}; use syntax::ast::{NodeId, NodeIdAssigner, Name}; use syntax::codemap::Span; -use syntax::errors; +use syntax::errors::{self, DiagnosticBuilder}; use syntax::errors::emitter::{Emitter, BasicEmitter}; use syntax::diagnostics; use syntax::feature_gate; @@ -80,6 +80,61 @@ pub struct Session { } impl Session { + pub fn struct_span_warn<'a>(&'a self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + self.diagnostic().struct_span_warn(sp, msg) + } + pub fn struct_span_warn_with_code<'a>(&'a self, + sp: Span, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { + self.diagnostic().struct_span_warn_with_code(sp, msg, code) + } + pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { + self.diagnostic().struct_warn(msg) + } + pub fn struct_span_err<'a>(&'a self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + match split_msg_into_multilines(msg) { + Some(ref msg) => self.diagnostic().struct_span_err(sp, msg), + None => self.diagnostic().struct_span_err(sp, msg), + } + } + pub fn struct_span_err_with_code<'a>(&'a self, + sp: Span, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { + match split_msg_into_multilines(msg) { + Some(ref msg) => self.diagnostic().struct_span_err_with_code(sp, msg, code), + None => self.diagnostic().struct_span_err_with_code(sp, msg, code), + } + } + pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { + self.diagnostic().struct_err(msg) + } + pub fn struct_span_fatal<'a>(&'a self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + self.diagnostic().struct_span_fatal(sp, msg) + } + pub fn struct_span_fatal_with_code<'a>(&'a self, + sp: Span, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { + self.diagnostic().struct_span_fatal_with_code(sp, msg, code) + } + pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { + self.diagnostic().struct_fatal(msg) + } + pub fn span_fatal(&self, sp: Span, msg: &str) -> ! { panic!(self.diagnostic().span_fatal(sp, msg)) } @@ -98,13 +153,13 @@ impl Session { } pub fn span_err(&self, sp: Span, msg: &str) { match split_msg_into_multilines(msg) { - Some(msg) => self.diagnostic().span_err(sp, &msg[..]), + Some(msg) => self.diagnostic().span_err(sp, &msg), None => self.diagnostic().span_err(sp, msg) } } pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) { match split_msg_into_multilines(msg) { - Some(msg) => self.diagnostic().span_err_with_code(sp, &msg[..], code), + Some(msg) => self.diagnostic().span_err_with_code(sp, &msg, code), None => self.diagnostic().span_err_with_code(sp, msg, code) } } @@ -144,34 +199,6 @@ impl Session { None => self.warn(msg), } } - pub fn span_note(&self, sp: Span, msg: &str) { - self.diagnostic().span_note(sp, msg) - } - pub fn span_end_note(&self, sp: Span, msg: &str) { - self.diagnostic().span_end_note(sp, msg) - } - - /// Prints out a message with a suggested edit of the code. - /// - /// See `errors::RenderSpan::Suggestion` for more information. - pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) { - self.diagnostic().span_suggestion(sp, msg, suggestion) - } - pub fn span_help(&self, sp: Span, msg: &str) { - self.diagnostic().span_help(sp, msg) - } - pub fn fileline_note(&self, sp: Span, msg: &str) { - self.diagnostic().fileline_note(sp, msg) - } - pub fn fileline_help(&self, sp: Span, msg: &str) { - self.diagnostic().fileline_help(sp, msg) - } - pub fn note(&self, msg: &str) { - self.diagnostic().note(msg) - } - pub fn help(&self, msg: &str) { - self.diagnostic().help(msg) - } pub fn opt_span_bug(&self, opt_sp: Option, msg: &str) -> ! { match opt_sp { Some(sp) => self.span_bug(sp, msg), @@ -188,6 +215,12 @@ impl Session { pub fn bug(&self, msg: &str) -> ! { self.diagnostic().bug(msg) } + pub fn note_without_error(&self, msg: &str) { + self.diagnostic().note_without_error(msg) + } + pub fn span_note_without_error(&self, sp: Span, msg: &str) { + self.diagnostic().span_note_without_error(sp, msg) + } pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { self.diagnostic().span_unimpl(sp, msg) } diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index b5d259d9ac999..2481cab78b4d6 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -201,46 +201,38 @@ pub fn block_query

(b: &hir::Block, p: P) -> bool where P: FnMut(&hir::Expr) - return v.flag; } -/// Memoizes a one-argument closure using the given RefCell containing -/// a type implementing MutableMap to serve as a cache. -/// -/// In the future the signature of this function is expected to be: -/// ``` -/// pub fn memoized>( -/// cache: &RefCell, -/// f: &|T| -> U -/// ) -> impl |T| -> U { -/// ``` -/// but currently it is not possible. -/// -/// # Examples -/// ``` -/// struct Context { -/// cache: RefCell> -/// } -/// -/// fn factorial(ctxt: &Context, n: usize) -> usize { -/// memoized(&ctxt.cache, n, |n| match n { -/// 0 | 1 => n, -/// _ => factorial(ctxt, n - 2) + factorial(ctxt, n - 1) -/// }) -/// } -/// ``` -#[inline(always)] -pub fn memoized(cache: &RefCell>, arg: T, f: F) -> U - where T: Clone + Hash + Eq, - U: Clone, - S: HashState, - F: FnOnce(T) -> U, +pub trait MemoizationMap { + type Key: Clone; + type Value: Clone; + + /// If `key` is present in the map, return the valuee, + /// otherwise invoke `op` and store the value in the map. + /// + /// NB: if the receiver is a `DepTrackingMap`, special care is + /// needed in the `op` to ensure that the correct edges are + /// added into the dep graph. See the `DepTrackingMap` impl for + /// more details! + fn memoize(&self, key: Self::Key, op: OP) -> Self::Value + where OP: FnOnce() -> Self::Value; +} + +impl MemoizationMap for RefCell> + where K: Hash+Eq+Clone, V: Clone, S: HashState { - let key = arg.clone(); - let result = cache.borrow().get(&key).cloned(); - match result { - Some(result) => result, - None => { - let result = f(arg); - cache.borrow_mut().insert(key, result.clone()); - result + type Key = K; + type Value = V; + + fn memoize(&self, key: K, op: OP) -> V + where OP: FnOnce() -> V + { + let result = self.borrow().get(&key).cloned(); + match result { + Some(result) => result, + None => { + let result = op(); + self.borrow_mut().insert(key, result.clone()); + result + } } } } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 3c0a395f57ccb..77e39bba54afc 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -17,8 +17,7 @@ use middle::ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyBareFn}; use middle::ty::{TyParam, TyRawPtr, TyRef, TyTuple}; use middle::ty::TyClosure; use middle::ty::{TyBox, TyTrait, TyInt, TyUint, TyInfer}; -use middle::ty::{self, Ty, HasTypeFlags}; -use middle::ty::fold::TypeFoldable; +use middle::ty::{self, Ty, TypeFoldable}; use std::fmt; use syntax::{abi}; @@ -252,10 +251,13 @@ fn in_binder<'tcx, T, U>(f: &mut fmt::Formatter, struct TraitAndProjections<'tcx>(ty::TraitRef<'tcx>, Vec>); impl<'tcx> TypeFoldable<'tcx> for TraitAndProjections<'tcx> { - fn fold_with>(&self, folder: &mut F) - -> TraitAndProjections<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { TraitAndProjections(self.0.fold_with(folder), self.1.fold_with(folder)) } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.0.visit_with(visitor) || self.1.visit_with(visitor) + } } impl<'tcx> fmt::Display for TraitAndProjections<'tcx> { @@ -462,8 +464,7 @@ impl fmt::Debug for ty::Region { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ty::ReEarlyBound(ref data) => { - write!(f, "ReEarlyBound({:?}, {:?}, {}, {})", - data.def_id, + write!(f, "ReEarlyBound({:?}, {}, {})", data.space, data.index, data.name) @@ -920,13 +921,13 @@ impl fmt::Display for ty::InferTy { impl fmt::Display for ty::ExplicitSelfCategory { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match *self { - ty::StaticExplicitSelfCategory => "static", - ty::ByValueExplicitSelfCategory => "self", - ty::ByReferenceExplicitSelfCategory(_, hir::MutMutable) => { + ty::ExplicitSelfCategory::Static => "static", + ty::ExplicitSelfCategory::ByValue => "self", + ty::ExplicitSelfCategory::ByReference(_, hir::MutMutable) => { "&mut self" } - ty::ByReferenceExplicitSelfCategory(_, hir::MutImmutable) => "&self", - ty::ByBoxExplicitSelfCategory => "Box", + ty::ExplicitSelfCategory::ByReference(_, hir::MutImmutable) => "&self", + ty::ExplicitSelfCategory::ByBox => "Box", }) } } diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 3f48051029d9d..df181aec4ea4c 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -465,44 +465,44 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { format!("`{}`", ol) }; - match (new_loan.kind, old_loan.kind) { + let mut err = match (new_loan.kind, old_loan.kind) { (ty::MutBorrow, ty::MutBorrow) => { - span_err!(self.bccx, new_loan.span, E0499, - "cannot borrow `{}`{} as mutable \ - more than once at a time", - nl, new_loan_msg); + struct_span_err!(self.bccx, new_loan.span, E0499, + "cannot borrow `{}`{} as mutable \ + more than once at a time", + nl, new_loan_msg) } (ty::UniqueImmBorrow, _) => { - span_err!(self.bccx, new_loan.span, E0500, - "closure requires unique access to `{}` \ - but {} is already borrowed{}", - nl, ol_pronoun, old_loan_msg); + struct_span_err!(self.bccx, new_loan.span, E0500, + "closure requires unique access to `{}` \ + but {} is already borrowed{}", + nl, ol_pronoun, old_loan_msg) } (_, ty::UniqueImmBorrow) => { - span_err!(self.bccx, new_loan.span, E0501, - "cannot borrow `{}`{} as {} because \ - previous closure requires unique access", - nl, new_loan_msg, new_loan.kind.to_user_str()); + struct_span_err!(self.bccx, new_loan.span, E0501, + "cannot borrow `{}`{} as {} because \ + previous closure requires unique access", + nl, new_loan_msg, new_loan.kind.to_user_str()) } (_, _) => { - span_err!(self.bccx, new_loan.span, E0502, - "cannot borrow `{}`{} as {} because \ - {} is also borrowed as {}{}", - nl, - new_loan_msg, - new_loan.kind.to_user_str(), - ol_pronoun, - old_loan.kind.to_user_str(), - old_loan_msg); + struct_span_err!(self.bccx, new_loan.span, E0502, + "cannot borrow `{}`{} as {} because \ + {} is also borrowed as {}{}", + nl, + new_loan_msg, + new_loan.kind.to_user_str(), + ol_pronoun, + old_loan.kind.to_user_str(), + old_loan_msg) } - } + }; match new_loan.cause { euv::ClosureCapture(span) => { - self.bccx.span_note( + err.span_note( span, &format!("borrow occurs due to use of `{}` in closure", nl)); @@ -553,15 +553,15 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { } }; - self.bccx.span_note( + err.span_note( old_loan.span, &format!("{}; {}", borrow_summary, rule_summary)); let old_loan_span = self.tcx().map.span( old_loan.kill_scope.node_id(&self.tcx().region_maps)); - self.bccx.span_end_note(old_loan_span, - "previous borrow ends here"); - + err.span_end_note(old_loan_span, + "previous borrow ends here"); + err.emit(); return false; } @@ -616,14 +616,14 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) { UseOk => { } UseWhileBorrowed(loan_path, loan_span) => { - span_err!(self.bccx, span, E0503, - "cannot use `{}` because it was mutably borrowed", - &self.bccx.loan_path_to_string(copy_path)); - self.bccx.span_note( - loan_span, - &format!("borrow of `{}` occurs here", - &self.bccx.loan_path_to_string(&*loan_path)) - ); + struct_span_err!(self.bccx, span, E0503, + "cannot use `{}` because it was mutably borrowed", + &self.bccx.loan_path_to_string(copy_path)) + .span_note(loan_span, + &format!("borrow of `{}` occurs here", + &self.bccx.loan_path_to_string(&*loan_path)) + ) + .emit(); } } } @@ -639,24 +639,25 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) { UseOk => { } UseWhileBorrowed(loan_path, loan_span) => { - match move_kind { + let mut err = match move_kind { move_data::Captured => - span_err!(self.bccx, span, E0504, - "cannot move `{}` into closure because it is borrowed", - &self.bccx.loan_path_to_string(move_path)), + struct_span_err!(self.bccx, span, E0504, + "cannot move `{}` into closure because it is borrowed", + &self.bccx.loan_path_to_string(move_path)), move_data::Declared | move_data::MoveExpr | move_data::MovePat => - span_err!(self.bccx, span, E0505, - "cannot move out of `{}` because it is borrowed", - &self.bccx.loan_path_to_string(move_path)) + struct_span_err!(self.bccx, span, E0505, + "cannot move out of `{}` because it is borrowed", + &self.bccx.loan_path_to_string(move_path)) }; - self.bccx.span_note( + err.span_note( loan_span, &format!("borrow of `{}` occurs here", &self.bccx.loan_path_to_string(&*loan_path)) ); + err.emit(); } } } @@ -818,12 +819,12 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { span: Span, loan_path: &LoanPath<'tcx>, loan: &Loan) { - span_err!(self.bccx, span, E0506, - "cannot assign to `{}` because it is borrowed", - self.bccx.loan_path_to_string(loan_path)); - self.bccx.span_note( - loan.span, - &format!("borrow of `{}` occurs here", - self.bccx.loan_path_to_string(loan_path))); + struct_span_err!(self.bccx, span, E0506, + "cannot assign to `{}` because it is borrowed", + self.bccx.loan_path_to_string(loan_path)) + .span_note(loan.span, + &format!("borrow of `{}` occurs here", + self.bccx.loan_path_to_string(loan_path))) + .emit(); } } diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index a56a03174f609..4cb9673785ecb 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -15,6 +15,7 @@ use rustc::middle::mem_categorization::InteriorOffsetKind as Kind; use rustc::middle::ty; use syntax::ast; use syntax::codemap; +use syntax::errors::DiagnosticBuilder; use rustc_front::hir; pub struct MoveErrorCollector<'tcx> { @@ -68,13 +69,14 @@ fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &Vec>) { let grouped_errors = group_errors_with_same_origin(errors); for error in &grouped_errors { - report_cannot_move_out_of(bccx, error.move_from.clone()); + let mut err = report_cannot_move_out_of(bccx, error.move_from.clone()); let mut is_first_note = true; for move_to in &error.move_to_places { - note_move_destination(bccx, move_to.span, + note_move_destination(&mut err, move_to.span, move_to.name, is_first_note); is_first_note = false; } + err.emit(); } } @@ -112,24 +114,28 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec>) // (keep in sync with gather_moves::check_and_get_illegal_move_origin ) fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - move_from: mc::cmt<'tcx>) { + move_from: mc::cmt<'tcx>) + -> DiagnosticBuilder<'a> { match move_from.cat { Categorization::Deref(_, _, mc::BorrowedPtr(..)) | Categorization::Deref(_, _, mc::Implicit(..)) | Categorization::Deref(_, _, mc::UnsafePtr(..)) | Categorization::StaticItem => { - span_err!(bccx, move_from.span, E0507, - "cannot move out of {}", - move_from.descriptive_string(bccx.tcx)); + struct_span_err!(bccx, move_from.span, E0507, + "cannot move out of {}", + move_from.descriptive_string(bccx.tcx)) } Categorization::Interior(ref b, mc::InteriorElement(Kind::Index, _)) => { let expr = bccx.tcx.map.expect_expr(move_from.id); if let hir::ExprIndex(..) = expr.node { - span_err!(bccx, move_from.span, E0508, - "cannot move out of type `{}`, \ - a non-copy fixed-size array", - b.ty); + struct_span_err!(bccx, move_from.span, E0508, + "cannot move out of type `{}`, \ + a non-copy fixed-size array", + b.ty) + } else { + bccx.span_bug(move_from.span, "this path should not cause illegal move"); + unreachable!(); } } @@ -138,39 +144,41 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, match b.ty.sty { ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor() => { - span_err!(bccx, move_from.span, E0509, - "cannot move out of type `{}`, \ - which defines the `Drop` trait", - b.ty); + struct_span_err!(bccx, move_from.span, E0509, + "cannot move out of type `{}`, \ + which defines the `Drop` trait", + b.ty) }, _ => { - bccx.span_bug(move_from.span, "this path should not cause illegal move") + bccx.span_bug(move_from.span, "this path should not cause illegal move"); + unreachable!(); } } } _ => { - bccx.span_bug(move_from.span, "this path should not cause illegal move") + bccx.span_bug(move_from.span, "this path should not cause illegal move"); + unreachable!(); } } } -fn note_move_destination(bccx: &BorrowckCtxt, +fn note_move_destination(err: &mut DiagnosticBuilder, move_to_span: codemap::Span, pat_name: ast::Name, is_first_note: bool) { if is_first_note { - bccx.span_note( + err.span_note( move_to_span, "attempting to move value to here"); - bccx.fileline_help( + err.fileline_help( move_to_span, &format!("to prevent the move, \ - use `ref {0}` or `ref mut {0}` to capture value by \ - reference", - pat_name)); + use `ref {0}` or `ref mut {0}` to capture value by \ + reference", + pat_name)); } else { - bccx.span_note(move_to_span, - &format!("and here (use `ref {0}` or `ref mut {0}`)", + err.span_note(move_to_span, + &format!("and here (use `ref {0}` or `ref mut {0}`)", pat_name)); } } diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 38ec728e9c16f..631149e69d77e 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -20,6 +20,7 @@ pub use self::MovedValueUseKind::*; use self::InteriorKind::*; +use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; use rustc::front::map::blocks::FnParts; use rustc::middle::cfg; @@ -40,6 +41,7 @@ use std::mem; use std::rc::Rc; use syntax::ast; use syntax::codemap::Span; +use syntax::errors::DiagnosticBuilder; use rustc_front::hir; use rustc_front::hir::{FnDecl, Block}; @@ -108,7 +110,7 @@ pub fn check_crate(tcx: &ty::ctxt) { } }; - tcx.map.krate().visit_all_items(&mut bccx); + tcx.visit_all_items_in_krate(DepNode::BorrowCheck, &mut bccx); if tcx.sess.borrowck_stats() { println!("--- borrowck stats ---"); @@ -591,10 +593,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } // General fallback. - self.span_err( + let mut db = self.struct_span_err( err.span, &self.bckerr_to_string(&err)); - self.note_and_explain_bckerr(err); + self.note_and_explain_bckerr(&mut db, err); + db.emit(); } pub fn report_use_of_moved_value<'b>(&self, @@ -609,16 +612,17 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { MovedInCapture => "capture", }; - let (ol, moved_lp_msg) = match the_move.kind { + let (ol, moved_lp_msg, mut err) = match the_move.kind { move_data::Declared => { - span_err!( + let err = struct_span_err!( self.tcx.sess, use_span, E0381, "{} of possibly uninitialized variable: `{}`", verb, self.loan_path_to_string(lp)); (self.loan_path_to_string(moved_lp), - String::new()) + String::new(), + err) } _ => { // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would @@ -653,11 +657,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let msg = if !has_fork && partial { "partially " } else if has_fork && !has_common { "collaterally "} else { "" }; - span_err!( + let err = struct_span_err!( self.tcx.sess, use_span, E0382, "{} of {}moved value: `{}`", verb, msg, nl); - (ol, moved_lp_msg) + (ol, moved_lp_msg, err) } }; @@ -684,7 +688,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { // multiple times. Avoid printing the same span and adjust the wording so it makes // more sense that it's from multiple evalutations. if expr_span == use_span { - self.tcx.sess.note( + err.note( &format!("`{}` was previously moved here{} because it has type `{}`, \ which is {}", ol, @@ -692,7 +696,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { expr_ty, suggestion)); } else { - self.tcx.sess.span_note( + err.span_note( expr_span, &format!("`{}` moved here{} because it has type `{}`, which is {}", ol, @@ -705,7 +709,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { move_data::MovePat => { let pat_ty = self.tcx.node_id_to_type(the_move.id); let span = self.tcx.map.span(the_move.id); - self.tcx.sess.span_note(span, + err.span_note(span, &format!("`{}` moved here{} because it has type `{}`, \ which is moved by default", ol, @@ -713,14 +717,14 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { pat_ty)); match self.tcx.sess.codemap().span_to_snippet(span) { Ok(string) => { - self.tcx.sess.span_suggestion( + err.span_suggestion( span, &format!("if you would like to borrow the value instead, \ use a `ref` binding as shown:"), format!("ref {}", string)); }, Err(_) => { - self.tcx.sess.fileline_help(span, + err.fileline_help(span, "use `ref` to override"); }, } @@ -746,7 +750,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { expr_ty, ("moved by default", "make a copy and capture that instead to override")); - self.tcx.sess.span_note( + err.span_note( expr_span, &format!("`{}` moved into closure environment here{} because it \ has type `{}`, which is {}", @@ -754,9 +758,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { moved_lp_msg, moved_lp.ty, suggestion)); - self.tcx.sess.fileline_help(expr_span, help); + err.fileline_help(expr_span, help); } } + err.emit(); fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>, span: Span, @@ -791,35 +796,36 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { lp: &LoanPath<'tcx>, assign: &move_data::Assignment) { - span_err!( + struct_span_err!( self.tcx.sess, span, E0384, "re-assignment of immutable variable `{}`", - self.loan_path_to_string(lp)); - self.tcx.sess.span_note(assign.span, "prior assignment occurs here"); + self.loan_path_to_string(lp)) + .span_note(assign.span, "prior assignment occurs here") + .emit(); } pub fn span_err(&self, s: Span, m: &str) { self.tcx.sess.span_err(s, m); } - pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) { - self.tcx.sess.span_err_with_code(s, msg, code); + pub fn struct_span_err(&self, s: Span, m: &str) -> DiagnosticBuilder<'a> { + self.tcx.sess.struct_span_err(s, m) } - pub fn span_bug(&self, s: Span, m: &str) { - self.tcx.sess.span_bug(s, m); - } - - pub fn span_note(&self, s: Span, m: &str) { - self.tcx.sess.span_note(s, m); + pub fn struct_span_err_with_code(&self, + s: Span, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { + self.tcx.sess.struct_span_err_with_code(s, msg, code) } - pub fn span_end_note(&self, s: Span, m: &str) { - self.tcx.sess.span_end_note(s, m); + pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) { + self.tcx.sess.span_err_with_code(s, msg, code); } - pub fn fileline_help(&self, s: Span, m: &str) { - self.tcx.sess.fileline_help(s, m); + pub fn span_bug(&self, s: Span, m: &str) { + self.tcx.sess.span_bug(s, m); } pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String { @@ -913,19 +919,19 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } }; - match cause { + let mut err = match cause { mc::AliasableOther => { - span_err!( + struct_span_err!( self.tcx.sess, span, E0385, - "{} in an aliasable location", prefix); + "{} in an aliasable location", prefix) } mc::AliasableReason::UnaliasableImmutable => { - span_err!( + struct_span_err!( self.tcx.sess, span, E0386, - "{} in an immutable container", prefix); + "{} in an immutable container", prefix) } mc::AliasableClosure(id) => { - span_err!( + let mut err = struct_span_err!( self.tcx.sess, span, E0387, "{} in a captured outer variable in an `Fn` closure", prefix); if let BorrowViolation(euv::ClosureCapture(_)) = kind { @@ -933,31 +939,32 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { // happen for nested closures, so we know the enclosing // closure incorrectly accepts an `Fn` while it needs to // be `FnMut`. - span_help!(self.tcx.sess, self.tcx.map.span(id), + span_help!(&mut err, self.tcx.map.span(id), "consider changing this to accept closures that implement `FnMut`"); } else { - span_help!(self.tcx.sess, self.tcx.map.span(id), + span_help!(&mut err, self.tcx.map.span(id), "consider changing this closure to take self by mutable reference"); } + err } mc::AliasableStatic | mc::AliasableStaticMut => { - span_err!( + struct_span_err!( self.tcx.sess, span, E0388, - "{} in a static location", prefix); + "{} in a static location", prefix) } mc::AliasableBorrowed => { - span_err!( + struct_span_err!( self.tcx.sess, span, E0389, - "{} in a `&` reference", prefix); + "{} in a `&` reference", prefix) } - } + }; if is_closure { - self.tcx.sess.fileline_help( - span, - "closures behind references must be called via `&mut`"); + err.fileline_help(span, + "closures behind references must be called via `&mut`"); } + err.emit(); } fn report_out_of_scope_escaping_closure_capture(&self, @@ -966,34 +973,30 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { { let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt); - span_err!( - self.tcx.sess, err.span, E0373, - "closure may outlive the current function, \ - but it borrows {}, \ - which is owned by the current function", - cmt_path_or_string); - - self.tcx.sess.span_note( - capture_span, - &format!("{} is borrowed here", - cmt_path_or_string)); - let suggestion = match self.tcx.sess.codemap().span_to_snippet(err.span) { Ok(string) => format!("move {}", string), Err(_) => format!("move || ") }; - self.tcx.sess.span_suggestion( - err.span, - &format!("to force the closure to take ownership of {} \ - (and any other referenced variables), \ - use the `move` keyword, as shown:", - cmt_path_or_string), - suggestion); + struct_span_err!(self.tcx.sess, err.span, E0373, + "closure may outlive the current function, \ + but it borrows {}, \ + which is owned by the current function", + cmt_path_or_string) + .span_note(capture_span, + &format!("{} is borrowed here", + cmt_path_or_string)) + .span_suggestion(err.span, + &format!("to force the closure to take ownership of {} \ + (and any other referenced variables), \ + use the `move` keyword, as shown:", + cmt_path_or_string), + suggestion) + .emit(); } - pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) { + pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) { let code = err.code; match code { err_mutbl => { @@ -1007,7 +1010,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { _ => unreachable!() }; if kind == ty::FnClosureKind { - self.tcx.sess.span_help( + db.span_help( self.tcx.map.span(upvar_id.closure_expr_id), "consider changing this closure to take \ self by mutable reference"); @@ -1017,7 +1020,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { if let Categorization::Local(local_id) = err.cmt.cat { let span = self.tcx.map.span(local_id); if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { - self.tcx.sess.span_suggestion( + db.span_suggestion( span, &format!("to make the {} mutable, use `mut` as shown:", self.cmt_to_string(&err.cmt)), @@ -1030,16 +1033,18 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { err_out_of_scope(super_scope, sub_scope) => { self.tcx.note_and_explain_region( + db, "reference must be valid for ", sub_scope, "..."); self.tcx.note_and_explain_region( + db, "...but borrowed value is only valid for ", super_scope, ""); if let Some(span) = statement_scope_span(self.tcx, super_scope) { - self.tcx.sess.span_help(span, - "consider using a `let` binding to increase its lifetime"); + db.span_help(span, + "consider using a `let` binding to increase its lifetime"); } } @@ -1051,11 +1056,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { None => self.cmt_to_string(&*err.cmt), }; self.tcx.note_and_explain_region( + db, &format!("{} would have to be valid for ", descr), loan_scope, "..."); self.tcx.note_and_explain_region( + db, &format!("...but {} is only valid for ", descr), ptr_scope, ""); diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 4a3810c822b4d..1ea09490aed2f 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -77,16 +77,16 @@ impl Debug for Edge { } } -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct NodeIndex(pub usize); -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct EdgeIndex(pub usize); pub const INVALID_EDGE_INDEX: EdgeIndex = EdgeIndex(usize::MAX); // Use a private field here to guarantee no more instances are created: -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct Direction { repr: usize } pub const OUTGOING: Direction = Direction { repr: 0 }; @@ -410,4 +410,12 @@ impl Edge { pub fn target(&self) -> NodeIndex { self.target } + + pub fn source_or_target(&self, direction: Direction) -> NodeIndex { + if direction == OUTGOING { + self.target + } else { + self.source + } + } } diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 3bba7d651ad71..ef64d7dde091c 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -40,6 +40,7 @@ pub mod transitive_relation; pub mod unify; pub mod fnv; pub mod tuple_slice; +pub mod veccell; // See comments in src/librustc/lib.rs #[doc(hidden)] diff --git a/src/librustc_data_structures/veccell/mod.rs b/src/librustc_data_structures/veccell/mod.rs new file mode 100644 index 0000000000000..008642d9d6567 --- /dev/null +++ b/src/librustc_data_structures/veccell/mod.rs @@ -0,0 +1,47 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cell::UnsafeCell; +use std::mem; + +pub struct VecCell { + data: UnsafeCell> +} + +impl VecCell { + pub fn with_capacity(capacity: usize) -> VecCell{ + VecCell { data: UnsafeCell::new(Vec::with_capacity(capacity)) } + } + + #[inline] + pub fn push(&self, data: T) -> usize { + // The logic here, and in `swap` below, is that the `push` + // method on the vector will not recursively access this + // `VecCell`. Therefore, we can temporarily obtain mutable + // access, secure in the knowledge that even if aliases exist + // -- indeed, even if aliases are reachable from within the + // vector -- they will not be used for the duration of this + // particular fn call. (Note that we also are relying on the + // fact that `VecCell` is not `Sync`.) + unsafe { + let v = self.data.get(); + (*v).push(data); + (*v).len() + } + } + + pub fn swap(&self, mut data: Vec) -> Vec { + unsafe { + let v = self.data.get(); + mem::swap(&mut *v, &mut data); + } + data + } +} diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 89299c0119975..d172bfb441358 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -121,7 +121,7 @@ pub fn compile_input(sess: Session, } let arenas = ty::CtxtArenas::new(); - let ast_map = make_map(&sess, &mut hir_forest); + let hir_map = make_map(&sess, &mut hir_forest); write_out_deps(&sess, &outputs, &id); @@ -130,9 +130,9 @@ pub fn compile_input(sess: Session, CompileState::state_after_write_deps(input, &sess, outdir, - &ast_map, + &hir_map, &expanded_crate, - &ast_map.krate(), + &hir_map.krate(), &id[..], &lcx)); @@ -144,9 +144,17 @@ pub fn compile_input(sess: Session, "early lint checks", || lint::check_ast_crate(&sess, &expanded_crate)); + let opt_crate = if sess.opts.debugging_opts.keep_ast || + sess.opts.debugging_opts.save_analysis { + Some(&expanded_crate) + } else { + drop(expanded_crate); + None + }; + phase_3_run_analysis_passes(&sess, &cstore, - ast_map, + hir_map, &arenas, &id, control.make_glob_map, @@ -157,7 +165,7 @@ pub fn compile_input(sess: Session, CompileState::state_after_analysis(input, &tcx.sess, outdir, - &expanded_crate, + opt_crate, tcx.map.krate(), &analysis, &mir_map, @@ -341,7 +349,7 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> { fn state_after_write_deps(input: &'a Input, session: &'a Session, out_dir: &'a Option, - ast_map: &'a hir_map::Map<'ast>, + hir_map: &'a hir_map::Map<'ast>, krate: &'a ast::Crate, hir_crate: &'a hir::Crate, crate_name: &'a str, @@ -349,7 +357,7 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> { -> CompileState<'a, 'ast, 'tcx> { CompileState { crate_name: Some(crate_name), - ast_map: Some(ast_map), + ast_map: Some(hir_map), krate: Some(krate), hir_crate: Some(hir_crate), lcx: Some(lcx), @@ -360,7 +368,7 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> { fn state_after_analysis(input: &'a Input, session: &'a Session, out_dir: &'a Option, - krate: &'a ast::Crate, + krate: Option<&'a ast::Crate>, hir_crate: &'a hir::Crate, analysis: &'a ty::CrateAnalysis, mir_map: &'a MirMap<'tcx>, @@ -372,7 +380,7 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> { analysis: Some(analysis), mir_map: Some(mir_map), tcx: Some(tcx), - krate: Some(krate), + krate: krate, hir_crate: Some(hir_crate), lcx: Some(lcx), crate_name: Some(crate_name), @@ -670,14 +678,12 @@ pub fn assign_node_ids(sess: &Session, krate: ast::Crate) -> ast::Crate { } pub fn make_map<'ast>(sess: &Session, - forest: &'ast mut front::map::Forest) - -> front::map::Map<'ast> { - // Construct the 'ast'-map - let map = time(sess.time_passes(), - "indexing hir", - move || front::map::map_crate(forest)); - - map + forest: &'ast mut hir_map::Forest) + -> hir_map::Map<'ast> { + // Construct the HIR map + time(sess.time_passes(), + "indexing hir", + move || hir_map::map_crate(forest)) } /// Run the resolution, typechecking, region checking and other @@ -685,7 +691,7 @@ pub fn make_map<'ast>(sess: &Session, /// structures carrying the results of the analysis. pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, cstore: &CStore, - ast_map: front::map::Map<'tcx>, + hir_map: hir_map::Map<'tcx>, arenas: &'tcx ty::CtxtArenas<'tcx>, name: &str, make_glob_map: resolve::MakeGlobMap, @@ -694,15 +700,15 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, where F: for<'a> FnOnce(&'a ty::ctxt<'tcx>, MirMap<'tcx>, ty::CrateAnalysis) -> R { let time_passes = sess.time_passes(); - let krate = ast_map.krate(); + let krate = hir_map.krate(); time(time_passes, "external crate/lib resolution", - || LocalCrateReader::new(sess, cstore, &ast_map).read_crates(krate)); + || LocalCrateReader::new(sess, cstore, &hir_map).read_crates(krate)); let lang_items = time(time_passes, "language item collection", - || middle::lang_items::collect_language_items(&sess, &ast_map)); + || middle::lang_items::collect_language_items(&sess, &hir_map)); let resolve::CrateMap { def_map, @@ -713,7 +719,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, glob_map, } = time(time_passes, "resolution", - || resolve::resolve_crate(sess, &ast_map, make_glob_map)); + || resolve::resolve_crate(sess, &hir_map, make_glob_map)); let named_region_map = time(time_passes, "lifetime resolution", @@ -721,7 +727,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "looking for entry point", - || middle::entry::find_entry_point(sess, &ast_map)); + || middle::entry::find_entry_point(sess, &hir_map)); sess.plugin_registrar_fn.set(time(time_passes, "looking for plugin registrar", || { plugin::build::find_plugin_registrar(sess.diagnostic(), krate) @@ -737,13 +743,13 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "static item recursion checking", - || middle::check_static_recursion::check_crate(sess, krate, &def_map.borrow(), &ast_map)); + || middle::check_static_recursion::check_crate(sess, krate, &def_map.borrow(), &hir_map)); ty::ctxt::create_and_enter(sess, arenas, def_map, named_region_map, - ast_map, + hir_map, freevars, region_map, lang_items, @@ -795,7 +801,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "rvalue checking", - || middle::check_rvalues::check_crate(tcx, krate)); + || middle::check_rvalues::check_crate(tcx)); // Avoid overwhelming user with errors if type checking failed. // I'm not sure how helpful this is, to be honest, but it avoids @@ -995,8 +1001,9 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { - session.span_err(a.span, "`crate_type` requires a value"); - session.note("for example: `#![crate_type=\"lib\"]`"); + session.struct_span_err(a.span, "`crate_type` requires a value") + .note("for example: `#![crate_type=\"lib\"]`") + .emit(); None } } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 210b1a26c21f1..7b8970876f6e3 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -70,6 +70,7 @@ use rustc_metadata::loader; use rustc_metadata::cstore::CStore; use rustc::util::common::time; +use std::cmp::max; use std::cmp::Ordering::Equal; use std::env; use std::io::{self, Read, Write}; @@ -627,11 +628,13 @@ Available lint options: - let max_name_len = plugin_groups.iter() - .chain(&builtin_groups) - .map(|&(s, _)| s.chars().count()) - .max() - .unwrap_or(0); + let max_name_len = max("warnings".len(), + plugin_groups.iter() + .chain(&builtin_groups) + .map(|&(s, _)| s.chars().count()) + .max() + .unwrap_or(0)); + let padded = |x: &str| { let mut s = repeat(" ") .take(max_name_len - x.chars().count()) @@ -643,6 +646,7 @@ Available lint options: println!("Lint groups provided by rustc:\n"); println!(" {} {}", padded("name"), "sub-lints"); println!(" {} {}", padded("----"), "---------"); + println!(" {} {}", padded("warnings"), "all built-in lints"); let print_lint_groups = |lints: Vec<(&'static str, Vec)>| { for (name, to) in lints { diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 47b89e3be5d43..ba5ecc22e7474 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -204,6 +204,7 @@ impl PpSourceMode { let annotation = TypedAnnotation { tcx: tcx, }; + let _ignore = tcx.dep_graph.in_ignore(); f(&annotation, payload, &ast_map.forest.krate) diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index df9294a9d5bfb..8f3366eacb364 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -21,7 +21,7 @@ use rustc_typeck::middle::resolve_lifetime; use rustc_typeck::middle::stability; use rustc_typeck::middle::subst; use rustc_typeck::middle::subst::Subst; -use rustc_typeck::middle::ty::{self, Ty, RegionEscape}; +use rustc_typeck::middle::ty::{self, Ty, TypeFoldable}; use rustc_typeck::middle::ty::relate::TypeRelation; use rustc_typeck::middle::infer::{self, TypeOrigin}; use rustc_typeck::middle::infer::lub::Lub; @@ -60,7 +60,7 @@ struct ExpectErrorEmitter { fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) { match lvl { Level::Bug | Level::Fatal | Level::Error => {} - Level::Warning | Level::Note | Level::Help => { + _ => { return; } } @@ -289,7 +289,6 @@ impl<'a, 'tcx> Env<'a, 'tcx> { -> ty::Region { let name = token::intern(name); ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: self.infcx.tcx.map.local_def_id(ast::DUMMY_NODE_ID), space: space, index: index, name: name, diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index f99cdd39da2a8..81d3367ab00dc 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -319,9 +319,9 @@ pub fn lower_path_parameters(lctx: &LoweringContext, path_parameters: &PathParameters) -> hir::PathParameters { match *path_parameters { - AngleBracketedParameters(ref data) => + PathParameters::AngleBracketed(ref data) => hir::AngleBracketedParameters(lower_angle_bracketed_parameter_data(lctx, data)), - ParenthesizedParameters(ref data) => + PathParameters::Parenthesized(ref data) => hir::ParenthesizedParameters(lower_parenthesized_parameter_data(lctx, data)), } } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index f27cc629791ed..09e6f454fb4f5 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -752,19 +752,19 @@ impl LateLintPass for UnconditionalRecursion { // no break */ }`) shouldn't be linted unless it actually // recurs. if !reached_exit_without_self_call && !self_call_spans.is_empty() { - cx.span_lint(UNCONDITIONAL_RECURSION, sp, - "function cannot return without recurring"); + let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION, sp, + "function cannot return without recurring"); // FIXME #19668: these could be span_lint_note's instead of this manual guard. if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow { - let sess = cx.sess(); // offer some help to the programmer. for call in &self_call_spans { - sess.span_note(*call, "recursive call site") + db.span_note(*call, "recursive call site"); } - sess.fileline_help(sp, "a `loop` may express intention \ - better if this is on purpose") + db.fileline_help(sp, "a `loop` may express intention \ + better if this is on purpose"); } + db.emit(); } // all done diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 80ef334fe189b..f2e75960406f1 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -143,8 +143,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UNUSED_MUT, UNREACHABLE_CODE, UNUSED_MUST_USE, UNUSED_UNSAFE, PATH_STATEMENTS, UNUSED_ATTRIBUTES); - add_lint_group!(sess, "future_incompatible", - PRIVATE_IN_PUBLIC); + add_lint_group!(sess, FUTURE_INCOMPATIBLE, + PRIVATE_IN_PUBLIC, INVALID_TYPE_PARAM_DEFAULT); // We have one lint pass defined specially store.register_late_pass(sess, false, box lint::GatherNodeLevels); diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index 714f0a4350b3d..8c3bd3c4f8a53 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -124,20 +124,20 @@ impl<'a, 'b, 'c, 'tcx> ast_map::FoldOps for &'a DecodeContext<'b, 'c, 'tcx> { /// ast-map. pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata, tcx: &ty::ctxt<'tcx>, - path: Vec, - def_path: ast_map::DefPath, + parent_path: Vec, + parent_def_path: ast_map::DefPath, par_doc: rbml::Doc, orig_did: DefId) -> Result<&'tcx InlinedItem, (Vec, ast_map::DefPath)> { match par_doc.opt_child(c::tag_ast) { - None => Err((path, def_path)), + None => Err((parent_path, parent_def_path)), Some(ast_doc) => { let mut path_as_str = None; debug!("> Decoding inlined fn: {:?}::?", { // Do an Option dance to use the path after it is moved below. - let s = ast_map::path_to_string(path.iter().cloned()); + let s = ast_map::path_to_string(parent_path.iter().cloned()); path_as_str = Some(s); path_as_str.as_ref().map(|x| &x[..]) }); @@ -152,8 +152,11 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata, last_filemap_index: Cell::new(0) }; let raw_ii = decode_ast(ast_doc); - let ii = ast_map::map_decoded_item(&dcx.tcx.map, path, def_path, raw_ii, dcx); - + let ii = ast_map::map_decoded_item(&dcx.tcx.map, + parent_path, + parent_def_path, + raw_ii, + dcx); let name = match *ii { InlinedItem::Item(ref i) => i.name, InlinedItem::Foreign(ref i) => i.name, @@ -289,7 +292,7 @@ impl def_id_decoder_helpers for D fn encode_ast(rbml_w: &mut Encoder, item: &InlinedItem) { rbml_w.start_tag(c::tag_tree as usize); - item.encode(rbml_w); + rbml_w.emit_opaque(|this| item.encode(this)); rbml_w.end_tag(); } @@ -358,8 +361,8 @@ fn simplify_ast(ii: InlinedItemRef) -> InlinedItem { fn decode_ast(par_doc: rbml::Doc) -> InlinedItem { let chi_doc = par_doc.get(c::tag_tree as usize); - let mut d = reader::Decoder::new(chi_doc); - Decodable::decode(&mut d).unwrap() + let mut rbml_r = reader::Decoder::new(chi_doc); + rbml_r.read_opaque(|decoder, _| Decodable::decode(decoder)).unwrap() } // ______________________________________________________________________ @@ -506,21 +509,6 @@ pub fn encode_cast_kind(ebml_w: &mut Encoder, kind: cast::CastKind) { // ______________________________________________________________________ // Encoding and decoding the side tables -trait get_ty_str_ctxt<'tcx> { - fn ty_str_ctxt<'a>(&'a self) -> tyencode::ctxt<'a, 'tcx>; -} - -impl<'a, 'tcx> get_ty_str_ctxt<'tcx> for e::EncodeContext<'a, 'tcx> { - fn ty_str_ctxt<'b>(&'b self) -> tyencode::ctxt<'b, 'tcx> { - tyencode::ctxt { - diag: self.tcx.sess.diagnostic(), - ds: e::def_to_string, - tcx: self.tcx, - abbrevs: &self.type_abbrevs - } - } -} - trait rbml_writer_helpers<'tcx> { fn emit_region(&mut self, ecx: &e::EncodeContext, r: ty::Region); fn emit_ty<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, ty: Ty<'tcx>); @@ -545,11 +533,15 @@ trait rbml_writer_helpers<'tcx> { impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { fn emit_region(&mut self, ecx: &e::EncodeContext, r: ty::Region) { - self.emit_opaque(|this| Ok(e::write_region(ecx, this, r))); + self.emit_opaque(|this| Ok(tyencode::enc_region(&mut this.cursor, + &ecx.ty_str_ctxt(), + r))); } fn emit_ty<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, ty: Ty<'tcx>) { - self.emit_opaque(|this| Ok(e::write_type(ecx, this, ty))); + self.emit_opaque(|this| Ok(tyencode::enc_ty(&mut this.cursor, + &ecx.ty_str_ctxt(), + ty))); } fn emit_tys<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, tys: &[Ty<'tcx>]) { @@ -558,13 +550,15 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { fn emit_trait_ref<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, trait_ref: &ty::TraitRef<'tcx>) { - self.emit_opaque(|this| Ok(e::write_trait_ref(ecx, this, trait_ref))); + self.emit_opaque(|this| Ok(tyencode::enc_trait_ref(&mut this.cursor, + &ecx.ty_str_ctxt(), + *trait_ref))); } fn emit_predicate<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, predicate: &ty::Predicate<'tcx>) { self.emit_opaque(|this| { - Ok(tyencode::enc_predicate(this, + Ok(tyencode::enc_predicate(&mut this.cursor, &ecx.ty_str_ctxt(), predicate)) }); @@ -572,13 +566,13 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { fn emit_existential_bounds<'b>(&mut self, ecx: &e::EncodeContext<'b,'tcx>, bounds: &ty::ExistentialBounds<'tcx>) { - self.emit_opaque(|this| Ok(tyencode::enc_existential_bounds(this, + self.emit_opaque(|this| Ok(tyencode::enc_existential_bounds(&mut this.cursor, &ecx.ty_str_ctxt(), bounds))); } fn emit_builtin_bounds(&mut self, ecx: &e::EncodeContext, bounds: &ty::BuiltinBounds) { - self.emit_opaque(|this| Ok(tyencode::enc_builtin_bounds(this, + self.emit_opaque(|this| Ok(tyencode::enc_builtin_bounds(&mut this.cursor, &ecx.ty_str_ctxt(), bounds))); } @@ -605,9 +599,9 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { fn emit_substs<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, substs: &subst::Substs<'tcx>) { - self.emit_opaque(|this| Ok(tyencode::enc_substs(this, - &ecx.ty_str_ctxt(), - substs))); + self.emit_opaque(|this| Ok(tyencode::enc_substs(&mut this.cursor, + &ecx.ty_str_ctxt(), + substs))); } fn emit_auto_adjustment<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, @@ -875,10 +869,6 @@ trait rbml_decoder_decoder_helpers<'tcx> { -> adjustment::AutoDerefRef<'tcx>; fn read_autoref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> adjustment::AutoRef<'tcx>; - fn convert_def_id(&mut self, - dcx: &DecodeContext, - did: DefId) - -> DefId; // Versions of the type reading functions that don't need the full // DecodeContext. @@ -930,12 +920,12 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { fn read_ty_encoded<'b, 'c, F, R>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>, op: F) -> R where F: for<'x> FnOnce(&mut tydecode::TyDecoder<'x,'tcx>) -> R { - return self.read_opaque(|this, doc| { + return self.read_opaque(|_, doc| { debug!("read_ty_encoded({})", type_string(doc)); Ok(op( &mut tydecode::TyDecoder::with_doc( dcx.tcx, dcx.cdata.cnum, doc, - &mut |a| this.convert_def_id(dcx, a)))) + &mut |d| convert_def_id(dcx, d)))) }).unwrap(); fn type_string(doc: rbml::Doc) -> String { @@ -986,9 +976,9 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { fn read_substs<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>) -> subst::Substs<'tcx> { - self.read_opaque(|this, doc| { + self.read_opaque(|_, doc| { Ok(tydecode::TyDecoder::with_doc(dcx.tcx, dcx.cdata.cnum, doc, - &mut |a| this.convert_def_id(dcx, a)) + &mut |d| convert_def_id(dcx, d)) .parse_substs()) }).unwrap() } @@ -1094,47 +1084,46 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { { Decodable::decode(self).unwrap() } +} - /// Converts a def-id that appears in a type. The correct - /// translation will depend on what kind of def-id this is. - /// This is a subtle point: type definitions are not - /// inlined into the current crate, so if the def-id names - /// a nominal type or type alias, then it should be - /// translated to refer to the source crate. - /// - /// However, *type parameters* are cloned along with the function - /// they are attached to. So we should translate those def-ids - /// to refer to the new, cloned copy of the type parameter. - /// We only see references to free type parameters in the body of - /// an inlined function. In such cases, we need the def-id to - /// be a local id so that the TypeContents code is able to lookup - /// the relevant info in the ty_param_defs table. - /// - /// *Region parameters*, unfortunately, are another kettle of fish. - /// In such cases, def_id's can appear in types to distinguish - /// shadowed bound regions and so forth. It doesn't actually - /// matter so much what we do to these, since regions are erased - /// at trans time, but it's good to keep them consistent just in - /// case. We translate them with `tr_def_id()` which will map - /// the crate numbers back to the original source crate. - /// - /// Scopes will end up as being totally bogus. This can actually - /// be fixed though. - /// - /// Unboxed closures are cloned along with the function being - /// inlined, and all side tables use interned node IDs, so we - /// translate their def IDs accordingly. - /// - /// It'd be really nice to refactor the type repr to not include - /// def-ids so that all these distinctions were unnecessary. - fn convert_def_id(&mut self, - dcx: &DecodeContext, - did: DefId) - -> DefId { - let r = dcx.tr_def_id(did); - debug!("convert_def_id(did={:?})={:?}", did, r); - return r; - } +// Converts a def-id that appears in a type. The correct +// translation will depend on what kind of def-id this is. +// This is a subtle point: type definitions are not +// inlined into the current crate, so if the def-id names +// a nominal type or type alias, then it should be +// translated to refer to the source crate. +// +// However, *type parameters* are cloned along with the function +// they are attached to. So we should translate those def-ids +// to refer to the new, cloned copy of the type parameter. +// We only see references to free type parameters in the body of +// an inlined function. In such cases, we need the def-id to +// be a local id so that the TypeContents code is able to lookup +// the relevant info in the ty_param_defs table. +// +// *Region parameters*, unfortunately, are another kettle of fish. +// In such cases, def_id's can appear in types to distinguish +// shadowed bound regions and so forth. It doesn't actually +// matter so much what we do to these, since regions are erased +// at trans time, but it's good to keep them consistent just in +// case. We translate them with `tr_def_id()` which will map +// the crate numbers back to the original source crate. +// +// Scopes will end up as being totally bogus. This can actually +// be fixed though. +// +// Unboxed closures are cloned along with the function being +// inlined, and all side tables use interned node IDs, so we +// translate their def IDs accordingly. +// +// It'd be really nice to refactor the type repr to not include +// def-ids so that all these distinctions were unnecessary. +fn convert_def_id(dcx: &DecodeContext, + did: DefId) + -> DefId { + let r = dcx.tr_def_id(did); + debug!("convert_def_id(did={:?})={:?}", did, r); + return r; } fn decode_side_tables(dcx: &DecodeContext, diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 4420da5f9b87d..9122148a8cc05 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -35,6 +35,7 @@ use syntax::codemap::{self, Span, mk_sp, Pos}; use syntax::parse; use syntax::attr; use syntax::attr::AttrMetaMethods; +use syntax::errors::FatalError; use syntax::parse::token::InternedString; use syntax::util::small_vector::SmallVector; use rustc_front::intravisit::Visitor; @@ -504,7 +505,10 @@ impl<'a> CrateReader<'a> { let lo = p.span.lo; let body = match p.parse_all_token_trees() { Ok(body) => body, - Err(err) => panic!(err), + Err(mut err) => { + err.emit(); + panic!(FatalError); + } }; let span = mk_sp(lo, p.last_span.hi); p.abort_if_errors(); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 357158c24bacd..b8dfb9f74c6a4 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -33,12 +33,12 @@ use middle::def_id::{DefId, DefIndex}; use middle::lang_items; use middle::subst; use middle::ty::{ImplContainer, TraitContainer}; -use middle::ty::{self, RegionEscape, Ty}; +use middle::ty::{self, Ty, TypeFoldable}; use rustc::mir; use rustc::mir::visit::MutVisitor; -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::io::prelude::*; use std::io; use std::rc::Rc; @@ -353,16 +353,11 @@ pub fn get_trait_def<'tcx>(cdata: Cmd, let associated_type_names = parse_associated_type_names(item_doc); let paren_sugar = parse_paren_sugar(item_doc); - ty::TraitDef { - paren_sugar: paren_sugar, - unsafety: unsafety, - generics: generics, - trait_ref: item_trait_ref(item_doc, tcx, cdata), - associated_type_names: associated_type_names, - nonblanket_impls: RefCell::new(FnvHashMap()), - blanket_impls: RefCell::new(vec![]), - flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS) - } + ty::TraitDef::new(unsafety, + paren_sugar, + generics, + item_trait_ref(item_doc, tcx, cdata), + associated_type_names) } pub fn get_adt_def<'tcx>(intr: &IdentInterner, @@ -763,29 +758,54 @@ pub fn get_item_name(intr: &IdentInterner, cdata: Cmd, id: DefIndex) -> ast::Nam pub type DecodeInlinedItem<'a> = Box FnMut(Cmd, &ty::ctxt<'tcx>, - Vec, - hir_map::DefPath, + Vec, // parent_path + hir_map::DefPath, // parent_def_path rbml::Doc, DefId) -> Result<&'tcx InlinedItem, (Vec, hir_map::DefPath)> + 'a>; -pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, tcx: &ty::ctxt<'tcx>, id: DefIndex, +pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, + tcx: &ty::ctxt<'tcx>, + id: DefIndex, mut decode_inlined_item: DecodeInlinedItem) -> FoundAst<'tcx> { debug!("Looking up item: {:?}", id); let item_doc = cdata.lookup_item(id); let item_did = item_def_id(item_doc, cdata); - let path = item_path(item_doc).split_last().unwrap().1.to_vec(); - let def_path = def_path(cdata, id); - match decode_inlined_item(cdata, tcx, path, def_path, item_doc, item_did) { + let parent_path = { + let mut path = item_path(item_doc); + path.pop(); + path + }; + let parent_def_path = { + let mut def_path = def_path(cdata, id); + def_path.pop(); + def_path + }; + match decode_inlined_item(cdata, + tcx, + parent_path, + parent_def_path, + item_doc, + item_did) { Ok(ii) => FoundAst::Found(ii), - Err((path, def_path)) => { + Err((mut parent_path, mut parent_def_path)) => { match item_parent_item(cdata, item_doc) { - Some(did) => { - let parent_item = cdata.lookup_item(did.index); - match decode_inlined_item(cdata, tcx, path, def_path, parent_item, did) { - Ok(ii) => FoundAst::FoundParent(did, ii), + Some(parent_did) => { + // Remove the last element from the paths, since we are now + // trying to inline the parent. + parent_path.pop(); + parent_def_path.pop(); + + let parent_item = cdata.lookup_item(parent_did.index); + match decode_inlined_item(cdata, + tcx, + parent_path, + parent_def_path, + parent_item, + parent_did) { + Ok(ii) => FoundAst::FoundParent(parent_did, ii), Err(_) => FoundAst::NotFound } } @@ -808,9 +828,11 @@ pub fn maybe_get_item_mir<'tcx>(cdata: Cmd, }; let mut decoder = reader::Decoder::new(mir_doc); - let mut mir = tls::enter_decoding_context(&dcx, &mut decoder, |_, decoder| { - Decodable::decode(decoder).unwrap() - }); + let mut mir = decoder.read_opaque(|opaque_decoder, _| { + tls::enter_decoding_context(&dcx, opaque_decoder, |_, opaque_decoder| { + Decodable::decode(opaque_decoder) + }) + }).unwrap(); let mut def_id_and_span_translator = MirDefIdAndSpanTranslator { crate_metadata: cdata, @@ -859,12 +881,12 @@ fn get_explicit_self(item: rbml::Doc) -> ty::ExplicitSelfCategory { let explicit_self_kind = string.as_bytes()[0]; match explicit_self_kind as char { - 's' => ty::StaticExplicitSelfCategory, - 'v' => ty::ByValueExplicitSelfCategory, - '~' => ty::ByBoxExplicitSelfCategory, + 's' => ty::ExplicitSelfCategory::Static, + 'v' => ty::ExplicitSelfCategory::ByValue, + '~' => ty::ExplicitSelfCategory::ByBox, // FIXME(#4846) expl. region '&' => { - ty::ByReferenceExplicitSelfCategory( + ty::ExplicitSelfCategory::ByReference( ty::ReEmpty, get_mutability(string.as_bytes()[1])) } @@ -898,7 +920,7 @@ pub fn is_static_method(cdata: Cmd, id: DefIndex) -> bool { let doc = cdata.lookup_item(id); match item_sort(doc) { Some('r') | Some('p') => { - get_explicit_self(doc) == ty::StaticExplicitSelfCategory + get_explicit_self(doc) == ty::ExplicitSelfCategory::Static } _ => false } @@ -1618,7 +1640,9 @@ pub fn get_imported_filemaps(metadata: &[u8]) -> Vec { reader::tagged_docs(cm_doc, tag_codemap_filemap).map(|filemap_doc| { let mut decoder = reader::Decoder::new(filemap_doc); - Decodable::decode(&mut decoder).unwrap() + decoder.read_opaque(|opaque_decoder, _| { + Decodable::decode(opaque_decoder) + }).unwrap() }).collect() } diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs index 2340efd2cae7a..50b9ea5755086 100644 --- a/src/librustc_metadata/diagnostics.rs +++ b/src/librustc_metadata/diagnostics.rs @@ -56,6 +56,20 @@ you want. Example: ``` "##, +E0463: r##" +A plugin/crate was declared but cannot be found. Erroneous code example: + +``` +#![feature(plugin)] +#![plugin(cookie_monster)] // error: can't find crate for `cookie_monster` +extern crate cake_is_a_lie; // error: can't find crate for `cake_is_a_lie` +``` + +You need to link your code to the relevant crate in order to be able to use it +(through Cargo or the `-L` option of rustc example). Plugins are crates as +well, and you link to them the same way. +"##, + } register_diagnostics! { @@ -66,7 +80,6 @@ register_diagnostics! { E0460, // found possibly newer version of crate `..` E0461, // couldn't find crate `..` with expected target triple .. E0462, // found staticlib `..` instead of rlib or dylib - E0463, // can't find crate for `..` E0464, // multiple matching crates for `..` E0465, // multiple .. candidates for `..` found E0466, // bad macro import diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index cd70172e8fa21..26f32e207647f 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -140,15 +140,9 @@ fn encode_trait_ref<'a, 'tcx>(rbml_w: &mut Encoder, ecx: &EncodeContext<'a, 'tcx>, trait_ref: ty::TraitRef<'tcx>, tag: usize) { - let ty_str_ctxt = &tyencode::ctxt { - diag: ecx.diag, - ds: def_to_string, - tcx: ecx.tcx, - abbrevs: &ecx.type_abbrevs - }; - rbml_w.start_tag(tag); - tyencode::enc_trait_ref(rbml_w, ty_str_ctxt, trait_ref); + tyencode::enc_trait_ref(rbml_w.writer, &ecx.ty_str_ctxt(), trait_ref); + rbml_w.mark_stable_position(); rbml_w.end_tag(); } @@ -202,59 +196,19 @@ fn encode_variant_id(rbml_w: &mut Encoder, vid: DefId) { rbml_w.wr_tagged_u64(tag_mod_child, id); } -pub fn write_closure_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, - rbml_w: &mut Encoder, - closure_type: &ty::ClosureTy<'tcx>) { - let ty_str_ctxt = &tyencode::ctxt { - diag: ecx.diag, - ds: def_to_string, - tcx: ecx.tcx, - abbrevs: &ecx.type_abbrevs - }; - tyencode::enc_closure_ty(rbml_w, ty_str_ctxt, closure_type); -} - -pub fn write_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, +fn write_closure_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, - typ: Ty<'tcx>) { - let ty_str_ctxt = &tyencode::ctxt { - diag: ecx.diag, - ds: def_to_string, - tcx: ecx.tcx, - abbrevs: &ecx.type_abbrevs - }; - tyencode::enc_ty(rbml_w, ty_str_ctxt, typ); -} - -pub fn write_trait_ref<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, - rbml_w: &mut Encoder, - trait_ref: &ty::TraitRef<'tcx>) { - let ty_str_ctxt = &tyencode::ctxt { - diag: ecx.diag, - ds: def_to_string, - tcx: ecx.tcx, - abbrevs: &ecx.type_abbrevs - }; - tyencode::enc_trait_ref(rbml_w, ty_str_ctxt, *trait_ref); -} - -pub fn write_region(ecx: &EncodeContext, - rbml_w: &mut Encoder, - r: ty::Region) { - let ty_str_ctxt = &tyencode::ctxt { - diag: ecx.diag, - ds: def_to_string, - tcx: ecx.tcx, - abbrevs: &ecx.type_abbrevs - }; - tyencode::enc_region(rbml_w, ty_str_ctxt, r); + closure_type: &ty::ClosureTy<'tcx>) { + tyencode::enc_closure_ty(rbml_w.writer, &ecx.ty_str_ctxt(), closure_type); + rbml_w.mark_stable_position(); } fn encode_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, typ: Ty<'tcx>) { rbml_w.start_tag(tag_items_data_item_type); - write_type(ecx, rbml_w, typ); + tyencode::enc_ty(rbml_w.writer, &ecx.ty_str_ctxt(), typ); + rbml_w.mark_stable_position(); rbml_w.end_tag(); } @@ -262,7 +216,8 @@ fn encode_region(ecx: &EncodeContext, rbml_w: &mut Encoder, r: ty::Region) { rbml_w.start_tag(tag_items_data_region); - write_region(ecx, rbml_w, r); + tyencode::enc_region(rbml_w.writer, &ecx.ty_str_ctxt(), r); + rbml_w.mark_stable_position(); rbml_w.end_tag(); } @@ -498,16 +453,16 @@ fn encode_explicit_self(rbml_w: &mut Encoder, // Encode the base self type. match *explicit_self { - ty::StaticExplicitSelfCategory => { + ty::ExplicitSelfCategory::Static => { rbml_w.wr_tagged_bytes(tag, &['s' as u8]); } - ty::ByValueExplicitSelfCategory => { + ty::ExplicitSelfCategory::ByValue => { rbml_w.wr_tagged_bytes(tag, &['v' as u8]); } - ty::ByBoxExplicitSelfCategory => { + ty::ExplicitSelfCategory::ByBox => { rbml_w.wr_tagged_bytes(tag, &['~' as u8]); } - ty::ByReferenceExplicitSelfCategory(_, m) => { + ty::ExplicitSelfCategory::ByReference(_, m) => { // FIXME(#4846) encode custom lifetime let ch = encode_mutability(m); rbml_w.wr_tagged_bytes(tag, &['&' as u8, ch]); @@ -592,17 +547,10 @@ fn encode_generics<'a, 'tcx>(rbml_w: &mut Encoder, { rbml_w.start_tag(tag); - // Type parameters - let ty_str_ctxt = &tyencode::ctxt { - diag: ecx.diag, - ds: def_to_string, - tcx: ecx.tcx, - abbrevs: &ecx.type_abbrevs - }; - for param in &generics.types { rbml_w.start_tag(tag_type_param_def); - tyencode::enc_type_param_def(rbml_w, ty_str_ctxt, param); + tyencode::enc_type_param_def(rbml_w.writer, &ecx.ty_str_ctxt(), param); + rbml_w.mark_stable_position(); rbml_w.end_tag(); } @@ -675,7 +623,7 @@ fn encode_method_ty_fields<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_visibility(rbml_w, method_ty.vis); encode_explicit_self(rbml_w, &method_ty.explicit_self); match method_ty.explicit_self { - ty::StaticExplicitSelfCategory => { + ty::ExplicitSelfCategory::Static => { encode_family(rbml_w, STATIC_METHOD_FAMILY); } _ => encode_family(rbml_w, METHOD_FAMILY) @@ -871,7 +819,11 @@ fn encode_mir(ecx: &EncodeContext, rbml_w: &mut Encoder, ii: InlinedItemRef) { if let Some(mir) = ecx.mir_map.get(&id) { rbml_w.start_tag(tag_mir as usize); - Encodable::encode(mir, rbml_w).unwrap(); + rbml_w.emit_opaque(|opaque_encoder| { + tls::enter_encoding_context(ecx, opaque_encoder, |_, opaque_encoder| { + Encodable::encode(mir, opaque_encoder) + }) + }).unwrap(); rbml_w.end_tag(); } } @@ -916,23 +868,17 @@ fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, xrefs: FnvHashMap, u32>) { - let ty_str_ctxt = &tyencode::ctxt { - diag: ecx.diag, - ds: def_to_string, - tcx: ecx.tcx, - abbrevs: &ecx.type_abbrevs - }; - let mut xref_positions = vec![0; xrefs.len()]; rbml_w.start_tag(tag_xref_data); for (xref, id) in xrefs.into_iter() { xref_positions[id as usize] = rbml_w.mark_stable_position() as u32; match xref { XRef::Predicate(p) => { - tyencode::enc_predicate(rbml_w, ty_str_ctxt, &p) + tyencode::enc_predicate(rbml_w.writer, &ecx.ty_str_ctxt(), &p) } } } + rbml_w.mark_stable_position(); rbml_w.end_tag(); rbml_w.start_tag(tag_xref_index); @@ -1340,7 +1286,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, path.clone().chain(Some(elem))); match method_ty.explicit_self { - ty::StaticExplicitSelfCategory => { + ty::ExplicitSelfCategory::Static => { encode_family(rbml_w, STATIC_METHOD_FAMILY); } @@ -1353,7 +1299,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, ecx.local_id(method_def_id)); is_nonstatic_method = method_ty.explicit_self != - ty::StaticExplicitSelfCategory; + ty::ExplicitSelfCategory::Static; } ty::TypeTraitItem(associated_type) => { encode_name(rbml_w, associated_type.name); @@ -1750,7 +1696,9 @@ fn encode_codemap(ecx: &EncodeContext, rbml_w: &mut Encoder) { } rbml_w.start_tag(tag_codemap_filemap); - filemap.encode(rbml_w); + rbml_w.emit_opaque(|opaque_encoder| { + filemap.encode(opaque_encoder) + }).unwrap(); rbml_w.end_tag(); } @@ -1961,9 +1909,7 @@ pub fn encode_metadata(parms: EncodeParams, krate: &hir::Crate) -> Vec { { let mut rbml_w = Encoder::new(&mut wr); - tls::enter_encoding_context(&ecx, &mut rbml_w, |_, rbml_w| { - encode_metadata_inner(rbml_w, &ecx, krate) - }); + encode_metadata_inner(&mut rbml_w, &ecx, krate) } // RBML compacts the encoded bytes whenever appropriate, @@ -2132,7 +2078,7 @@ fn encode_metadata_inner(rbml_w: &mut Encoder, // Get the encoded string for a type pub fn encoded_ty<'tcx>(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) -> Vec { let mut wr = Cursor::new(Vec::new()); - tyencode::enc_ty(&mut Encoder::new(&mut wr), &tyencode::ctxt { + tyencode::enc_ty(&mut wr, &tyencode::ctxt { diag: tcx.sess.diagnostic(), ds: def_to_string, tcx: tcx, diff --git a/src/librustc_metadata/loader.rs b/src/librustc_metadata/loader.rs index 81788e08c7ef2..40665beaa5ac2 100644 --- a/src/librustc_metadata/loader.rs +++ b/src/librustc_metadata/loader.rs @@ -226,7 +226,7 @@ use rustc_llvm as llvm; use rustc_llvm::{False, ObjectFile, mk_section_iter}; use rustc_llvm::archive_ro::ArchiveRO; use syntax::codemap::Span; -use syntax::errors::Handler; +use syntax::errors::DiagnosticBuilder; use rustc_back::target::Target; use std::cmp; @@ -315,38 +315,38 @@ impl<'a> Context<'a> { &Some(ref r) => format!(" which `{}` depends on", r.ident) }; - if !self.rejected_via_hash.is_empty() { - span_err!(self.sess, self.span, E0460, - "found possibly newer version of crate `{}`{}", - self.ident, add); + let mut err = if !self.rejected_via_hash.is_empty() { + struct_span_err!(self.sess, self.span, E0460, + "found possibly newer version of crate `{}`{}", + self.ident, add) } else if !self.rejected_via_triple.is_empty() { - span_err!(self.sess, self.span, E0461, - "couldn't find crate `{}` with expected target triple {}{}", - self.ident, self.triple, add); + struct_span_err!(self.sess, self.span, E0461, + "couldn't find crate `{}` with expected target triple {}{}", + self.ident, self.triple, add) } else if !self.rejected_via_kind.is_empty() { - span_err!(self.sess, self.span, E0462, - "found staticlib `{}` instead of rlib or dylib{}", - self.ident, add); + struct_span_err!(self.sess, self.span, E0462, + "found staticlib `{}` instead of rlib or dylib{}", + self.ident, add) } else { - span_err!(self.sess, self.span, E0463, - "can't find crate for `{}`{}", - self.ident, add); - } + struct_span_err!(self.sess, self.span, E0463, + "can't find crate for `{}`{}", + self.ident, add) + }; if !self.rejected_via_triple.is_empty() { let mismatches = self.rejected_via_triple.iter(); for (i, &CrateMismatch{ ref path, ref got }) in mismatches.enumerate() { - self.sess.fileline_note(self.span, + err.fileline_note(self.span, &format!("crate `{}`, path #{}, triple {}: {}", self.ident, i+1, got, path.display())); } } if !self.rejected_via_hash.is_empty() { - self.sess.span_note(self.span, "perhaps this crate needs \ + err.span_note(self.span, "perhaps this crate needs \ to be recompiled?"); let mismatches = self.rejected_via_hash.iter(); for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() { - self.sess.fileline_note(self.span, + err.fileline_note(self.span, &format!("crate `{}` path #{}: {}", self.ident, i+1, path.display())); } @@ -354,7 +354,7 @@ impl<'a> Context<'a> { &None => {} &Some(ref r) => { for (i, path) in r.paths().iter().enumerate() { - self.sess.fileline_note(self.span, + err.fileline_note(self.span, &format!("crate `{}` path #{}: {}", r.ident, i+1, path.display())); } @@ -362,15 +362,17 @@ impl<'a> Context<'a> { } } if !self.rejected_via_kind.is_empty() { - self.sess.fileline_help(self.span, "please recompile this crate using \ - --crate-type lib"); + err.fileline_help(self.span, "please recompile this crate using \ + --crate-type lib"); let mismatches = self.rejected_via_kind.iter(); for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() { - self.sess.fileline_note(self.span, - &format!("crate `{}` path #{}: {}", - self.ident, i+1, path.display())); + err.fileline_note(self.span, + &format!("crate `{}` path #{}: {}", + self.ident, i+1, path.display())); } } + + err.emit(); self.sess.abort_if_errors(); } @@ -480,29 +482,30 @@ impl<'a> Context<'a> { 0 => None, 1 => Some(libraries.into_iter().next().unwrap()), _ => { - span_err!(self.sess, self.span, E0464, - "multiple matching crates for `{}`", - self.crate_name); - self.sess.note("candidates:"); + let mut err = struct_span_err!(self.sess, self.span, E0464, + "multiple matching crates for `{}`", + self.crate_name); + err.note("candidates:"); for lib in &libraries { match lib.dylib { Some((ref p, _)) => { - self.sess.note(&format!("path: {}", - p.display())); + err.note(&format!("path: {}", + p.display())); } None => {} } match lib.rlib { Some((ref p, _)) => { - self.sess.note(&format!("path: {}", - p.display())); + err.note(&format!("path: {}", + p.display())); } None => {} } let data = lib.metadata.as_slice(); let name = decoder::get_crate_name(data); - note_crate_name(self.sess.diagnostic(), &name); + note_crate_name(&mut err, &name); } + err.emit(); None } } @@ -533,6 +536,7 @@ impl<'a> Context<'a> { } } + let mut err: Option = None; for (lib, kind) in m { info!("{} reading metadata from: {}", flavor, lib.display()); let metadata = match get_metadata_section(self.target, &lib) { @@ -555,27 +559,37 @@ impl<'a> Context<'a> { // candidates have the same hash, so they're not actually // duplicates that we should warn about. if ret.is_some() && self.hash.is_none() { - span_err!(self.sess, self.span, E0465, - "multiple {} candidates for `{}` found", - flavor, self.crate_name); - self.sess.span_note(self.span, - &format!(r"candidate #1: {}", - ret.as_ref().unwrap().0 - .display())); + let mut e = struct_span_err!(self.sess, self.span, E0465, + "multiple {} candidates for `{}` found", + flavor, self.crate_name); + e.span_note(self.span, + &format!(r"candidate #1: {}", + ret.as_ref().unwrap().0 + .display())); + if let Some(ref mut e) = err { + e.emit(); + } + err = Some(e); error = 1; ret = None; } if error > 0 { error += 1; - self.sess.span_note(self.span, - &format!(r"candidate #{}: {}", error, - lib.display())); + err.as_mut().unwrap().span_note(self.span, + &format!(r"candidate #{}: {}", error, + lib.display())); continue } *slot = Some(metadata); ret = Some((lib, kind)); } - return if error > 0 {None} else {ret} + + if error > 0 { + err.unwrap().emit(); + None + } else { + ret + } } fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> bool { @@ -662,8 +676,11 @@ impl<'a> Context<'a> { return true } } - sess.err(&format!("extern location for {} is of an unknown type: {}", - self.crate_name, loc.display())); + sess.struct_err(&format!("extern location for {} is of an unknown type: {}", + self.crate_name, loc.display())) + .help(&format!("file name should be lib*.rlib or {}*.{}", + dylibname.0, dylibname.1)) + .emit(); false }); @@ -697,8 +714,8 @@ impl<'a> Context<'a> { } } -pub fn note_crate_name(diag: &Handler, name: &str) { - diag.note(&format!("crate name: {}", name)); +pub fn note_crate_name(err: &mut DiagnosticBuilder, name: &str) { + err.note(&format!("crate name: {}", name)); } impl ArchiveMetadata { diff --git a/src/librustc_metadata/tls_context.rs b/src/librustc_metadata/tls_context.rs index eb82d2df94d15..37e661c21e15a 100644 --- a/src/librustc_metadata/tls_context.rs +++ b/src/librustc_metadata/tls_context.rs @@ -11,8 +11,8 @@ // This module provides implementations for the thread-local encoding and // decoding context traits in rustc::middle::cstore::tls. -use rbml::writer::Encoder as RbmlEncoder; -use rbml::reader::Decoder as RbmlDecoder; +use rbml::opaque::Encoder as OpaqueEncoder; +use rbml::opaque::Decoder as OpaqueDecoder; use rustc::middle::cstore::tls; use rustc::middle::def_id::DefId; use rustc::middle::subst::Substs; @@ -23,25 +23,18 @@ use encoder; use tydecode::TyDecoder; use tyencode; - impl<'a, 'tcx: 'a> tls::EncodingContext<'tcx> for encoder::EncodeContext<'a, 'tcx> { fn tcx<'s>(&'s self) -> &'s ty::ctxt<'tcx> { &self.tcx } - fn encode_ty(&self, rbml_w: &mut RbmlEncoder, t: ty::Ty<'tcx>) { - encoder::write_type(self, rbml_w, t); + fn encode_ty(&self, encoder: &mut OpaqueEncoder, t: ty::Ty<'tcx>) { + tyencode::enc_ty(encoder.cursor, &self.ty_str_ctxt(), t); } - fn encode_substs(&self, rbml_w: &mut RbmlEncoder, substs: &Substs<'tcx>) { - let ty_str_ctxt = &tyencode::ctxt { - diag: self.diag, - ds: encoder::def_to_string, - tcx: self.tcx, - abbrevs: &self.type_abbrevs - }; - tyencode::enc_substs(rbml_w, ty_str_ctxt, substs); + fn encode_substs(&self, encoder: &mut OpaqueEncoder, substs: &Substs<'tcx>) { + tyencode::enc_substs(encoder.cursor, &self.ty_str_ctxt(), substs); } } @@ -56,12 +49,12 @@ impl<'a, 'tcx: 'a> tls::DecodingContext<'tcx> for DecodingContext<'a, 'tcx> { &self.tcx } - fn decode_ty(&self, rbml_r: &mut RbmlDecoder) -> ty::Ty<'tcx> { + fn decode_ty(&self, decoder: &mut OpaqueDecoder) -> ty::Ty<'tcx> { let def_id_convert = &mut |did| { decoder::translate_def_id(self.crate_metadata, did) }; - let starting_position = rbml_r.position(); + let starting_position = decoder.position(); let mut ty_decoder = TyDecoder::new( self.crate_metadata.data.as_slice(), @@ -77,16 +70,16 @@ impl<'a, 'tcx: 'a> tls::DecodingContext<'tcx> for DecodingContext<'a, 'tcx> { // We can just reuse the tydecode implementation for parsing types, but // we have to make sure to leave the rbml reader at the position just // after the type. - rbml_r.advance(end_position - starting_position); + decoder.advance(end_position - starting_position); ty } - fn decode_substs(&self, rbml_r: &mut RbmlDecoder) -> Substs<'tcx> { + fn decode_substs(&self, decoder: &mut OpaqueDecoder) -> Substs<'tcx> { let def_id_convert = &mut |did| { decoder::translate_def_id(self.crate_metadata, did) }; - let starting_position = rbml_r.position(); + let starting_position = decoder.position(); let mut ty_decoder = TyDecoder::new( self.crate_metadata.data.as_slice(), @@ -99,7 +92,7 @@ impl<'a, 'tcx: 'a> tls::DecodingContext<'tcx> for DecodingContext<'a, 'tcx> { let end_position = ty_decoder.position(); - rbml_r.advance(end_position - starting_position); + decoder.advance(end_position - starting_position); substs } diff --git a/src/librustc_metadata/tydecode.rs b/src/librustc_metadata/tydecode.rs index 4d4db062329e0..5a48d6019d699 100644 --- a/src/librustc_metadata/tydecode.rs +++ b/src/librustc_metadata/tydecode.rs @@ -22,9 +22,10 @@ use middle::def_id::{DefId, DefIndex}; use middle::region; use middle::subst; use middle::subst::VecPerParamSpace; -use middle::ty::{self, ToPredicate, Ty, HasTypeFlags}; +use middle::ty::{self, ToPredicate, Ty, TypeFoldable}; use rbml; +use rbml::leb128; use std::str; use syntax::abi; use syntax::ast; @@ -103,9 +104,10 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> { } fn parse_vuint(&mut self) -> usize { - let res = rbml::reader::vuint_at(self.data, self.pos).unwrap(); - self.pos = res.next; - res.val + let (value, bytes_read) = leb128::read_unsigned_leb128(self.data, + self.pos); + self.pos += bytes_read; + value as usize } fn parse_name(&mut self, last: char) -> ast::Name { @@ -192,14 +194,12 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> { } 'B' => { assert_eq!(self.next(), '['); - let def_id = self.parse_def(); let space = self.parse_param_space(); assert_eq!(self.next(), '|'); let index = self.parse_u32(); assert_eq!(self.next(), '|'); let name = token::intern(&self.parse_str(']')); ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: def_id, space: space, index: index, name: name diff --git a/src/librustc_metadata/tyencode.rs b/src/librustc_metadata/tyencode.rs index 0119f1d5cc109..f03c25d698feb 100644 --- a/src/librustc_metadata/tyencode.rs +++ b/src/librustc_metadata/tyencode.rs @@ -31,9 +31,8 @@ use syntax::abi::Abi; use syntax::ast; use syntax::errors::Handler; -use rbml::writer::{self, Encoder}; - -macro_rules! mywrite { ($w:expr, $($arg:tt)*) => ({ write!($w.writer, $($arg)*); }) } +use rbml::leb128; +use encoder; pub struct ctxt<'a, 'tcx: 'a> { pub diag: &'a Handler, @@ -44,6 +43,17 @@ pub struct ctxt<'a, 'tcx: 'a> { pub abbrevs: &'a abbrev_map<'tcx> } +impl<'a, 'tcx> encoder::EncodeContext<'a, 'tcx> { + pub fn ty_str_ctxt<'b>(&'b self) -> ctxt<'b, 'tcx> { + ctxt { + diag: self.tcx.sess.diagnostic(), + ds: encoder::def_to_string, + tcx: self.tcx, + abbrevs: &self.type_abbrevs + } + } +} + // Compact string representation for Ty values. API TyStr & parse_from_str. // Extra parameters are for converting to/from def_ids in the string rep. // Whatever format you choose should not contain pipe characters. @@ -53,125 +63,129 @@ pub struct ty_abbrev { pub type abbrev_map<'tcx> = RefCell, ty_abbrev>>; -pub fn enc_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) { +pub fn enc_ty<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) { match cx.abbrevs.borrow_mut().get(&t) { - Some(a) => { w.writer.write_all(&a.s); return; } + Some(a) => { w.write_all(&a.s); return; } None => {} } - // type abbreviations needs a stable position - let pos = w.mark_stable_position(); + let pos = w.position(); match t.sty { - ty::TyBool => mywrite!(w, "b"), - ty::TyChar => mywrite!(w, "c"), + ty::TyBool => { write!(w, "b"); } + ty::TyChar => { write!(w, "c"); } ty::TyInt(t) => { match t { - ast::TyIs => mywrite!(w, "is"), - ast::TyI8 => mywrite!(w, "MB"), - ast::TyI16 => mywrite!(w, "MW"), - ast::TyI32 => mywrite!(w, "ML"), - ast::TyI64 => mywrite!(w, "MD") - } + ast::TyIs => write!(w, "is"), + ast::TyI8 => write!(w, "MB"), + ast::TyI16 => write!(w, "MW"), + ast::TyI32 => write!(w, "ML"), + ast::TyI64 => write!(w, "MD") + }; } ty::TyUint(t) => { match t { - ast::TyUs => mywrite!(w, "us"), - ast::TyU8 => mywrite!(w, "Mb"), - ast::TyU16 => mywrite!(w, "Mw"), - ast::TyU32 => mywrite!(w, "Ml"), - ast::TyU64 => mywrite!(w, "Md") - } + ast::TyUs => write!(w, "us"), + ast::TyU8 => write!(w, "Mb"), + ast::TyU16 => write!(w, "Mw"), + ast::TyU32 => write!(w, "Ml"), + ast::TyU64 => write!(w, "Md") + }; } ty::TyFloat(t) => { match t { - ast::TyF32 => mywrite!(w, "Mf"), - ast::TyF64 => mywrite!(w, "MF"), - } + ast::TyF32 => write!(w, "Mf"), + ast::TyF64 => write!(w, "MF"), + }; } ty::TyEnum(def, substs) => { - mywrite!(w, "t[{}|", (cx.ds)(def.did)); + write!(w, "t[{}|", (cx.ds)(def.did)); enc_substs(w, cx, substs); - mywrite!(w, "]"); + write!(w, "]"); } ty::TyTrait(box ty::TraitTy { ref principal, ref bounds }) => { - mywrite!(w, "x["); + write!(w, "x["); enc_trait_ref(w, cx, principal.0); enc_existential_bounds(w, cx, bounds); - mywrite!(w, "]"); + write!(w, "]"); } ty::TyTuple(ref ts) => { - mywrite!(w, "T["); + write!(w, "T["); for t in ts { enc_ty(w, cx, *t); } - mywrite!(w, "]"); + write!(w, "]"); } - ty::TyBox(typ) => { mywrite!(w, "~"); enc_ty(w, cx, typ); } - ty::TyRawPtr(mt) => { mywrite!(w, "*"); enc_mt(w, cx, mt); } + ty::TyBox(typ) => { write!(w, "~"); enc_ty(w, cx, typ); } + ty::TyRawPtr(mt) => { write!(w, "*"); enc_mt(w, cx, mt); } ty::TyRef(r, mt) => { - mywrite!(w, "&"); + write!(w, "&"); enc_region(w, cx, *r); enc_mt(w, cx, mt); } ty::TyArray(t, sz) => { - mywrite!(w, "V"); + write!(w, "V"); enc_ty(w, cx, t); - mywrite!(w, "/{}|", sz); + write!(w, "/{}|", sz); } ty::TySlice(t) => { - mywrite!(w, "V"); + write!(w, "V"); enc_ty(w, cx, t); - mywrite!(w, "/|"); + write!(w, "/|"); } ty::TyStr => { - mywrite!(w, "v"); + write!(w, "v"); } ty::TyBareFn(Some(def_id), f) => { - mywrite!(w, "F"); - mywrite!(w, "{}|", (cx.ds)(def_id)); + write!(w, "F"); + write!(w, "{}|", (cx.ds)(def_id)); enc_bare_fn_ty(w, cx, f); } ty::TyBareFn(None, f) => { - mywrite!(w, "G"); + write!(w, "G"); enc_bare_fn_ty(w, cx, f); } ty::TyInfer(_) => { cx.diag.bug("cannot encode inference variable types"); } ty::TyParam(ParamTy {space, idx, name}) => { - mywrite!(w, "p[{}|{}|{}]", idx, space.to_uint(), name) + write!(w, "p[{}|{}|{}]", idx, space.to_uint(), name); } ty::TyStruct(def, substs) => { - mywrite!(w, "a[{}|", (cx.ds)(def.did)); + write!(w, "a[{}|", (cx.ds)(def.did)); enc_substs(w, cx, substs); - mywrite!(w, "]"); + write!(w, "]"); } ty::TyClosure(def, ref substs) => { - mywrite!(w, "k[{}|", (cx.ds)(def)); + write!(w, "k[{}|", (cx.ds)(def)); enc_substs(w, cx, &substs.func_substs); for ty in &substs.upvar_tys { enc_ty(w, cx, ty); } - mywrite!(w, "."); - mywrite!(w, "]"); + write!(w, "."); + write!(w, "]"); } ty::TyProjection(ref data) => { - mywrite!(w, "P["); + write!(w, "P["); enc_trait_ref(w, cx, data.trait_ref); - mywrite!(w, "{}]", data.item_name); + write!(w, "{}]", data.item_name); } ty::TyError => { - mywrite!(w, "e"); + write!(w, "e"); } } - let end = w.mark_stable_position(); + let end = w.position(); let len = end - pos; - let buf: &mut [u8] = &mut [0; 16]; // vuint < 15 bytes - let mut abbrev = Cursor::new(buf); + let mut abbrev = Cursor::new(Vec::with_capacity(16)); abbrev.write_all(b"#"); - writer::write_vuint(&mut abbrev, pos as usize); + { + let start_position = abbrev.position() as usize; + let bytes_written = leb128::write_unsigned_leb128(abbrev.get_mut(), + start_position, + pos); + abbrev.set_position((start_position + bytes_written) as u64); + } cx.abbrevs.borrow_mut().insert(t, ty_abbrev { s: if abbrev.position() < len { @@ -180,102 +194,105 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) { // if the abbreviation is longer than the real type, // don't use #-notation. However, insert it here so // other won't have to `mark_stable_position` - w.writer.get_ref()[pos as usize..end as usize].to_owned() + w.get_ref()[pos as usize .. end as usize].to_owned() } }); } -fn enc_mutability(w: &mut Encoder, mt: hir::Mutability) { +fn enc_mutability(w: &mut Cursor>, mt: hir::Mutability) { match mt { hir::MutImmutable => (), - hir::MutMutable => mywrite!(w, "m"), - } + hir::MutMutable => { + write!(w, "m"); + } + }; } -fn enc_mt<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, +fn enc_mt<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, mt: ty::TypeAndMut<'tcx>) { enc_mutability(w, mt.mutbl); enc_ty(w, cx, mt.ty); } -fn enc_opt(w: &mut Encoder, t: Option, enc_f: F) where - F: FnOnce(&mut Encoder, T), +fn enc_opt(w: &mut Cursor>, t: Option, enc_f: F) where + F: FnOnce(&mut Cursor>, T), { match t { - None => mywrite!(w, "n"), + None => { + write!(w, "n"); + } Some(v) => { - mywrite!(w, "s"); + write!(w, "s"); enc_f(w, v); } } } -fn enc_vec_per_param_space<'a, 'tcx, T, F>(w: &mut Encoder, +fn enc_vec_per_param_space<'a, 'tcx, T, F>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, v: &VecPerParamSpace, mut op: F) where - F: FnMut(&mut Encoder, &ctxt<'a, 'tcx>, &T), + F: FnMut(&mut Cursor>, &ctxt<'a, 'tcx>, &T), { for &space in &subst::ParamSpace::all() { - mywrite!(w, "["); + write!(w, "["); for t in v.get_slice(space) { op(w, cx, t); } - mywrite!(w, "]"); + write!(w, "]"); } } -pub fn enc_substs<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, +pub fn enc_substs<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, substs: &subst::Substs<'tcx>) { enc_region_substs(w, cx, &substs.regions); enc_vec_per_param_space(w, cx, &substs.types, |w, cx, &ty| enc_ty(w, cx, ty)); } -fn enc_region_substs(w: &mut Encoder, cx: &ctxt, substs: &subst::RegionSubsts) { +fn enc_region_substs(w: &mut Cursor>, cx: &ctxt, substs: &subst::RegionSubsts) { match *substs { subst::ErasedRegions => { - mywrite!(w, "e"); + write!(w, "e"); } subst::NonerasedRegions(ref regions) => { - mywrite!(w, "n"); + write!(w, "n"); enc_vec_per_param_space(w, cx, regions, |w, cx, &r| enc_region(w, cx, r)); } } } -pub fn enc_region(w: &mut Encoder, cx: &ctxt, r: ty::Region) { +pub fn enc_region(w: &mut Cursor>, cx: &ctxt, r: ty::Region) { match r { ty::ReLateBound(id, br) => { - mywrite!(w, "b[{}|", id.depth); + write!(w, "b[{}|", id.depth); enc_bound_region(w, cx, br); - mywrite!(w, "]"); + write!(w, "]"); } ty::ReEarlyBound(ref data) => { - mywrite!(w, "B[{}|{}|{}|{}]", - (cx.ds)(data.def_id), - data.space.to_uint(), - data.index, - data.name); + write!(w, "B[{}|{}|{}]", + data.space.to_uint(), + data.index, + data.name); } ty::ReFree(ref fr) => { - mywrite!(w, "f["); + write!(w, "f["); enc_scope(w, cx, fr.scope); - mywrite!(w, "|"); + write!(w, "|"); enc_bound_region(w, cx, fr.bound_region); - mywrite!(w, "]"); + write!(w, "]"); } ty::ReScope(scope) => { - mywrite!(w, "s"); + write!(w, "s"); enc_scope(w, cx, scope); - mywrite!(w, "|"); + write!(w, "|"); } ty::ReStatic => { - mywrite!(w, "t"); + write!(w, "t"); } ty::ReEmpty => { - mywrite!(w, "e"); + write!(w, "e"); } ty::ReVar(_) | ty::ReSkolemized(..) => { // these should not crop up after typeck @@ -284,107 +301,107 @@ pub fn enc_region(w: &mut Encoder, cx: &ctxt, r: ty::Region) { } } -fn enc_scope(w: &mut Encoder, cx: &ctxt, scope: region::CodeExtent) { +fn enc_scope(w: &mut Cursor>, cx: &ctxt, scope: region::CodeExtent) { match cx.tcx.region_maps.code_extent_data(scope) { region::CodeExtentData::CallSiteScope { - fn_id, body_id } => mywrite!(w, "C[{}|{}]", fn_id, body_id), + fn_id, body_id } => write!(w, "C[{}|{}]", fn_id, body_id), region::CodeExtentData::ParameterScope { - fn_id, body_id } => mywrite!(w, "P[{}|{}]", fn_id, body_id), - region::CodeExtentData::Misc(node_id) => mywrite!(w, "M{}", node_id), + fn_id, body_id } => write!(w, "P[{}|{}]", fn_id, body_id), + region::CodeExtentData::Misc(node_id) => write!(w, "M{}", node_id), region::CodeExtentData::Remainder(region::BlockRemainder { - block: b, first_statement_index: i }) => mywrite!(w, "B[{}|{}]", b, i), - region::CodeExtentData::DestructionScope(node_id) => mywrite!(w, "D{}", node_id), - } + block: b, first_statement_index: i }) => write!(w, "B[{}|{}]", b, i), + region::CodeExtentData::DestructionScope(node_id) => write!(w, "D{}", node_id), + }; } -fn enc_bound_region(w: &mut Encoder, cx: &ctxt, br: ty::BoundRegion) { +fn enc_bound_region(w: &mut Cursor>, cx: &ctxt, br: ty::BoundRegion) { match br { ty::BrAnon(idx) => { - mywrite!(w, "a{}|", idx); + write!(w, "a{}|", idx); } ty::BrNamed(d, name) => { - mywrite!(w, "[{}|{}]", + write!(w, "[{}|{}]", (cx.ds)(d), name); } ty::BrFresh(id) => { - mywrite!(w, "f{}|", id); + write!(w, "f{}|", id); } ty::BrEnv => { - mywrite!(w, "e|"); + write!(w, "e|"); } } } -pub fn enc_trait_ref<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, +pub fn enc_trait_ref<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, s: ty::TraitRef<'tcx>) { - mywrite!(w, "{}|", (cx.ds)(s.def_id)); + write!(w, "{}|", (cx.ds)(s.def_id)); enc_substs(w, cx, s.substs); } -fn enc_unsafety(w: &mut Encoder, p: hir::Unsafety) { +fn enc_unsafety(w: &mut Cursor>, p: hir::Unsafety) { match p { - hir::Unsafety::Normal => mywrite!(w, "n"), - hir::Unsafety::Unsafe => mywrite!(w, "u"), - } + hir::Unsafety::Normal => write!(w, "n"), + hir::Unsafety::Unsafe => write!(w, "u"), + }; } -fn enc_abi(w: &mut Encoder, abi: Abi) { - mywrite!(w, "["); - mywrite!(w, "{}", abi.name()); - mywrite!(w, "]") +fn enc_abi(w: &mut Cursor>, abi: Abi) { + write!(w, "["); + write!(w, "{}", abi.name()); + write!(w, "]"); } -pub fn enc_bare_fn_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, +pub fn enc_bare_fn_ty<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, ft: &ty::BareFnTy<'tcx>) { enc_unsafety(w, ft.unsafety); enc_abi(w, ft.abi); enc_fn_sig(w, cx, &ft.sig); } -pub fn enc_closure_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, +pub fn enc_closure_ty<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, ft: &ty::ClosureTy<'tcx>) { enc_unsafety(w, ft.unsafety); enc_fn_sig(w, cx, &ft.sig); enc_abi(w, ft.abi); } -fn enc_fn_sig<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, +fn enc_fn_sig<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, fsig: &ty::PolyFnSig<'tcx>) { - mywrite!(w, "["); + write!(w, "["); for ty in &fsig.0.inputs { enc_ty(w, cx, *ty); } - mywrite!(w, "]"); + write!(w, "]"); if fsig.0.variadic { - mywrite!(w, "V"); + write!(w, "V"); } else { - mywrite!(w, "N"); + write!(w, "N"); } match fsig.0.output { ty::FnConverging(result_type) => { enc_ty(w, cx, result_type); } ty::FnDiverging => { - mywrite!(w, "z"); + write!(w, "z"); } } } -pub fn enc_builtin_bounds(w: &mut Encoder, _cx: &ctxt, bs: &ty::BuiltinBounds) { +pub fn enc_builtin_bounds(w: &mut Cursor>, _cx: &ctxt, bs: &ty::BuiltinBounds) { for bound in bs { match bound { - ty::BoundSend => mywrite!(w, "S"), - ty::BoundSized => mywrite!(w, "Z"), - ty::BoundCopy => mywrite!(w, "P"), - ty::BoundSync => mywrite!(w, "T"), - } + ty::BoundSend => write!(w, "S"), + ty::BoundSized => write!(w, "Z"), + ty::BoundCopy => write!(w, "P"), + ty::BoundSync => write!(w, "T"), + }; } - mywrite!(w, "."); + write!(w, "."); } -pub fn enc_existential_bounds<'a,'tcx>(w: &mut Encoder, +pub fn enc_existential_bounds<'a,'tcx>(w: &mut Cursor>, cx: &ctxt<'a,'tcx>, bs: &ty::ExistentialBounds<'tcx>) { enc_builtin_bounds(w, cx, &bs.builtin_bounds); @@ -392,90 +409,94 @@ pub fn enc_existential_bounds<'a,'tcx>(w: &mut Encoder, enc_region(w, cx, bs.region_bound); for tp in &bs.projection_bounds { - mywrite!(w, "P"); + write!(w, "P"); enc_projection_predicate(w, cx, &tp.0); } - mywrite!(w, "."); + write!(w, "."); } -pub fn enc_type_param_def<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, +pub fn enc_type_param_def<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, v: &ty::TypeParameterDef<'tcx>) { - mywrite!(w, "{}:{}|{}|{}|{}|", + write!(w, "{}:{}|{}|{}|{}|", v.name, (cx.ds)(v.def_id), v.space.to_uint(), v.index, (cx.ds)(v.default_def_id)); enc_opt(w, v.default, |w, t| enc_ty(w, cx, t)); enc_object_lifetime_default(w, cx, v.object_lifetime_default); } -pub fn enc_region_param_def(w: &mut Encoder, cx: &ctxt, +pub fn enc_region_param_def(w: &mut Cursor>, cx: &ctxt, v: &ty::RegionParameterDef) { - mywrite!(w, "{}:{}|{}|{}|", + write!(w, "{}:{}|{}|{}|", v.name, (cx.ds)(v.def_id), v.space.to_uint(), v.index); for &r in &v.bounds { - mywrite!(w, "R"); + write!(w, "R"); enc_region(w, cx, r); } - mywrite!(w, "."); + write!(w, "."); } -fn enc_object_lifetime_default<'a, 'tcx>(w: &mut Encoder, +fn enc_object_lifetime_default<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, default: ty::ObjectLifetimeDefault) { match default { - ty::ObjectLifetimeDefault::Ambiguous => mywrite!(w, "a"), - ty::ObjectLifetimeDefault::BaseDefault => mywrite!(w, "b"), + ty::ObjectLifetimeDefault::Ambiguous => { + write!(w, "a"); + } + ty::ObjectLifetimeDefault::BaseDefault => { + write!(w, "b"); + } ty::ObjectLifetimeDefault::Specific(r) => { - mywrite!(w, "s"); + write!(w, "s"); enc_region(w, cx, r); } } } -pub fn enc_predicate<'a, 'tcx>(w: &mut Encoder, +pub fn enc_predicate<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, p: &ty::Predicate<'tcx>) { match *p { ty::Predicate::Trait(ref trait_ref) => { - mywrite!(w, "t"); + write!(w, "t"); enc_trait_ref(w, cx, trait_ref.0.trait_ref); } ty::Predicate::Equate(ty::Binder(ty::EquatePredicate(a, b))) => { - mywrite!(w, "e"); + write!(w, "e"); enc_ty(w, cx, a); enc_ty(w, cx, b); } ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(a, b))) => { - mywrite!(w, "r"); + write!(w, "r"); enc_region(w, cx, a); enc_region(w, cx, b); } ty::Predicate::TypeOutlives(ty::Binder(ty::OutlivesPredicate(a, b))) => { - mywrite!(w, "o"); + write!(w, "o"); enc_ty(w, cx, a); enc_region(w, cx, b); } ty::Predicate::Projection(ty::Binder(ref data)) => { - mywrite!(w, "p"); - enc_projection_predicate(w, cx, data) + write!(w, "p"); + enc_projection_predicate(w, cx, data); } ty::Predicate::WellFormed(data) => { - mywrite!(w, "w"); + write!(w, "w"); enc_ty(w, cx, data); } ty::Predicate::ObjectSafe(trait_def_id) => { - mywrite!(w, "O{}|", (cx.ds)(trait_def_id)); + write!(w, "O{}|", (cx.ds)(trait_def_id)); } } } -fn enc_projection_predicate<'a, 'tcx>(w: &mut Encoder, +fn enc_projection_predicate<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, data: &ty::ProjectionPredicate<'tcx>) { enc_trait_ref(w, cx, data.projection_ty.trait_ref); - mywrite!(w, "{}|", data.projection_ty.item_name); + write!(w, "{}|", data.projection_ty.item_name); enc_ty(w, cx, data.ty); } diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index d28724c30aaa0..2e70e6bb5ae04 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -28,7 +28,7 @@ impl<'tcx> CFG<'tcx> { pub fn start_new_block(&mut self) -> BasicBlock { let node_index = self.basic_blocks.len(); - self.basic_blocks.push(BasicBlockData::new(Terminator::Diverge)); + self.basic_blocks.push(BasicBlockData::new(None)); BasicBlock::new(node_index) } @@ -67,15 +67,9 @@ impl<'tcx> CFG<'tcx> { pub fn terminate(&mut self, block: BasicBlock, terminator: Terminator<'tcx>) { - // Check whether this block has already been terminated. For - // this, we rely on the fact that the initial state is to have - // a Diverge terminator and an empty list of targets (which - // is not a valid state). - debug_assert!(match self.block_data(block).terminator { Terminator::Diverge => true, - _ => false }, + debug_assert!(self.block_data(block).terminator.is_none(), "terminate: block {:?} already has a terminator set", block); - - self.block_data_mut(block).terminator = terminator; + self.block_data_mut(block).terminator = Some(terminator); } } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index 1c96addcea0d9..4e03ed489eb9f 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -63,7 +63,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { this.cfg.push_assign(block, expr_span, // lt = idx < len <, Rvalue::BinaryOp(BinOp::Lt, idx.clone(), - Operand::Consume(len))); + Operand::Consume(len.clone()))); let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block()); this.cfg.terminate(block, @@ -71,7 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { cond: Operand::Consume(lt), targets: (success, failure), }); - this.panic(failure); + this.panic_bounds_check(failure, idx.clone(), Operand::Consume(len), expr_span); success.and(slice.index(idx)) } ExprKind::SelfRef => { diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 802c55ce7647a..44d1d52a06a34 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -15,6 +15,7 @@ use build::expr::category::{Category, RvalueFunc}; use build::scope::LoopScope; use hair::*; use rustc::middle::region::CodeExtent; +use rustc::middle::ty; use rustc::mir::repr::*; use syntax::codemap::Span; @@ -210,23 +211,35 @@ impl<'a,'tcx> Builder<'a,'tcx> { this.exit_scope(expr_span, extent, block, END_BLOCK); this.cfg.start_new_block().unit() } - ExprKind::Call { fun, args } => { + ExprKind::Call { ty, fun, args } => { + let diverges = match ty.sty { + ty::TyBareFn(_, ref f) => f.sig.0.output.diverges(), + _ => false + }; let fun = unpack!(block = this.as_operand(block, fun)); let args: Vec<_> = args.into_iter() .map(|arg| unpack!(block = this.as_operand(block, arg))) .collect(); + let success = this.cfg.start_new_block(); - let panic = this.diverge_cleanup(); - this.cfg.terminate(block, - Terminator::Call { - data: CallData { - destination: destination.clone(), - func: fun, - args: args, - }, - targets: (success, panic), - }); + let cleanup = this.diverge_cleanup(); + this.cfg.terminate(block, Terminator::Call { + func: fun, + args: args, + kind: match (cleanup, diverges) { + (None, true) => CallKind::Diverging, + (Some(c), true) => CallKind::DivergingCleanup(c), + (None, false) => CallKind::Converging { + destination: destination.clone(), + target: success + }, + (Some(c), false) => CallKind::ConvergingCleanup { + destination: destination.clone(), + targets: (success, c) + } + } + }); success.unit() } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index a02ed06ad099e..c2c87fcbd20da 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -61,16 +61,13 @@ impl<'a,'tcx> Builder<'a,'tcx> { // assemble a list of candidates: there is one candidate per // pattern, which means there may be more than one candidate // *per arm*. These candidates are kept sorted such that the - // highest priority candidate comes last in the list. This the - // reverse of the order in which candidates are written in the - // source. + // highest priority candidate comes first in the list. + // (i.e. same order as in source) let candidates: Vec<_> = arms.iter() .enumerate() - .rev() // highest priority comes last .flat_map(|(arm_index, arm)| { arm.patterns.iter() - .rev() .map(move |pat| (arm_index, pat, arm.guard.clone())) }) .map(|(arm_index, pattern, guard)| { @@ -92,7 +89,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // not entirely precise if !otherwise.is_empty() { let join_block = self.join_otherwise_blocks(otherwise); - self.panic(join_block); + self.panic(join_block, "something about matches algorithm not being precise", span); } // all the arm blocks will rejoin here @@ -259,7 +256,7 @@ enum TestKind<'tcx> { // test for equality Eq { - value: Literal<'tcx>, + value: ConstVal, ty: Ty<'tcx>, }, @@ -290,9 +287,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// The main match algorithm. It begins with a set of candidates /// `candidates` and has the job of generating code to determine /// which of these candidates, if any, is the correct one. The - /// candidates are sorted in inverse priority -- so the last item - /// in the list has highest priority. When a candidate is found to - /// match the value, we will generate a branch to the appropriate + /// candidates are sorted such that the first item in the list + /// has the highest priority. When a candidate is found to match + /// the value, we will generate a branch to the appropriate /// block found in `arm_blocks`. /// /// The return value is a list of "otherwise" blocks. These are @@ -324,17 +321,17 @@ impl<'a,'tcx> Builder<'a,'tcx> { unpack!(block = self.simplify_candidate(block, candidate)); } - // The candidates are inversely sorted by priority. Check to - // see whether the candidates in the front of the queue (and - // hence back of the vec) have satisfied all their match + // The candidates are sorted by priority. Check to see + // whether the higher priority candidates (and hence at + // the front of the vec) have satisfied all their match // pairs. let fully_matched = - candidates.iter().rev().take_while(|c| c.match_pairs.is_empty()).count(); + candidates.iter().take_while(|c| c.match_pairs.is_empty()).count(); debug!("match_candidates: {:?} candidates fully matched", fully_matched); - for _ in 0..fully_matched { + let mut unmatched_candidates = candidates.split_off(fully_matched); + for candidate in candidates { // If so, apply any bindings, test the guard (if any), and // branch to the arm. - let candidate = candidates.pop().unwrap(); if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks, candidate) { block = b; } else { @@ -346,13 +343,13 @@ impl<'a,'tcx> Builder<'a,'tcx> { // If there are no candidates that still need testing, we're done. // Since all matches are exhaustive, execution should never reach this point. - if candidates.is_empty() { + if unmatched_candidates.is_empty() { return vec![block]; } // Test candidates where possible. let (otherwise, tested_candidates) = - self.test_candidates(span, arm_blocks, &candidates, block); + self.test_candidates(span, arm_blocks, &unmatched_candidates, block); // If the target candidates were exhaustive, then we are done. if otherwise.is_empty() { @@ -361,15 +358,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { // If all candidates were sorted into `target_candidates` somewhere, then // the initial set was inexhaustive. - let untested_candidates = candidates.len() - tested_candidates; - if untested_candidates == 0 { + let untested_candidates = unmatched_candidates.split_off(tested_candidates); + if untested_candidates.len() == 0 { return otherwise; } // Otherwise, let's process those remaining candidates. let join_block = self.join_otherwise_blocks(otherwise); - candidates.truncate(untested_candidates); - self.match_candidates(span, arm_blocks, candidates, join_block) + self.match_candidates(span, arm_blocks, untested_candidates, join_block) } fn join_otherwise_blocks(&mut self, @@ -461,7 +457,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { -> (Vec, usize) { // extract the match-pair from the highest priority candidate - let match_pair = &candidates.last().unwrap().match_pairs[0]; + let match_pair = &candidates.first().unwrap().match_pairs[0]; let mut test = self.test(match_pair); // most of the time, the test to perform is simply a function @@ -470,7 +466,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // available match test.kind { TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => { - for candidate in candidates.iter().rev() { + for candidate in candidates.iter() { if !self.add_cases_to_switch(&match_pair.lvalue, candidate, switch_ty, @@ -497,7 +493,6 @@ impl<'a,'tcx> Builder<'a,'tcx> { // that point, we stop sorting. let tested_candidates = candidates.iter() - .rev() .take_while(|c| self.sort_candidate(&match_pair.lvalue, &test, c, diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 7b329fc4d52c3..ec67429379f95 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -37,7 +37,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { } } - PatternKind::Constant { value: Literal::Value { .. } } + PatternKind::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => { // for integers, we use a SwitchInt match, which allows // us to handle more cases @@ -55,12 +55,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { } PatternKind::Constant { ref value } => { - // for other types, we use an equality comparison Test { span: match_pair.pattern.span, kind: TestKind::Eq { value: value.clone(), - ty: match_pair.pattern.ty.clone(), + ty: match_pair.pattern.ty.clone() } } } @@ -113,7 +112,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }; match *match_pair.pattern.kind { - PatternKind::Constant { value: Literal::Value { ref value } } => { + PatternKind::Constant { ref value } => { // if the lvalues match, the type should match assert_eq!(match_pair.pattern.ty, switch_ty); @@ -126,7 +125,6 @@ impl<'a,'tcx> Builder<'a,'tcx> { } PatternKind::Range { .. } | - PatternKind::Constant { .. } | PatternKind::Variant { .. } | PatternKind::Slice { .. } | PatternKind::Array { .. } | @@ -177,36 +175,26 @@ impl<'a,'tcx> Builder<'a,'tcx> { } TestKind::Eq { ref value, ty } => { - // call PartialEq::eq(discrim, constant) - let constant = self.literal_operand(test.span, ty.clone(), value.clone()); - let item_ref = self.hir.partial_eq(ty); - self.call_comparison_fn(block, test.span, item_ref, - Operand::Consume(lvalue.clone()), constant) + let expect = self.literal_operand(test.span, ty.clone(), Literal::Value { + value: value.clone() + }); + let val = Operand::Consume(lvalue.clone()); + let fail = self.cfg.start_new_block(); + let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val.clone()); + vec![block, fail] } TestKind::Range { ref lo, ref hi, ty } => { - // Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`. + // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. let lo = self.literal_operand(test.span, ty.clone(), lo.clone()); let hi = self.literal_operand(test.span, ty.clone(), hi.clone()); - let item_ref = self.hir.partial_le(ty); - - let lo_blocks = self.call_comparison_fn(block, - test.span, - item_ref.clone(), - lo, - Operand::Consume(lvalue.clone())); - - let hi_blocks = self.call_comparison_fn(lo_blocks[0], - test.span, - item_ref, - Operand::Consume(lvalue.clone()), - hi); + let val = Operand::Consume(lvalue.clone()); - let failure = self.cfg.start_new_block(); - self.cfg.terminate(lo_blocks[1], Terminator::Goto { target: failure }); - self.cfg.terminate(hi_blocks[1], Terminator::Goto { target: failure }); + let fail = self.cfg.start_new_block(); + let block = self.compare(block, fail, test.span, BinOp::Le, lo, val.clone()); + let block = self.compare(block, fail, test.span, BinOp::Le, val, hi); - vec![hi_blocks[0], failure] + vec![block, fail] } TestKind::Len { len, op } => { @@ -240,37 +228,27 @@ impl<'a,'tcx> Builder<'a,'tcx> { } } - fn call_comparison_fn(&mut self, - block: BasicBlock, - span: Span, - item_ref: ItemRef<'tcx>, - lvalue1: Operand<'tcx>, - lvalue2: Operand<'tcx>) - -> Vec { - let target_blocks = vec![self.cfg.start_new_block(), self.cfg.start_new_block()]; - + fn compare(&mut self, + block: BasicBlock, + fail_block: BasicBlock, + span: Span, + op: BinOp, + left: Operand<'tcx>, + right: Operand<'tcx>) -> BasicBlock { let bool_ty = self.hir.bool_ty(); - let eq_result = self.temp(bool_ty); - let func = self.item_ref_operand(span, item_ref); - let call_blocks = (self.cfg.start_new_block(), self.diverge_cleanup()); - self.cfg.terminate(block, - Terminator::Call { - data: CallData { - destination: eq_result.clone(), - func: func, - args: vec![lvalue1, lvalue2], - }, - targets: call_blocks, - }); - - // check the result - self.cfg.terminate(call_blocks.0, - Terminator::If { - cond: Operand::Consume(eq_result), - targets: (target_blocks[0], target_blocks[1]), - }); - - target_blocks + let result = self.temp(bool_ty); + + // result = op(left, right) + self.cfg.push_assign(block, span, &result, Rvalue::BinaryOp(op, left, right)); + + // branch based on result + let target_block = self.cfg.start_new_block(); + self.cfg.terminate(block, Terminator::If { + cond: Operand::Consume(result), + targets: (target_block, fail_block) + }); + + target_block } /// Given that we are performing `test` against `test_lvalue`, @@ -357,7 +335,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // things out here, in some cases. TestKind::SwitchInt { switch_ty: _, options: _, ref indices } => { match *match_pair.pattern.kind { - PatternKind::Constant { value: Literal::Value { ref value } } + PatternKind::Constant { ref value } if is_switch_ty(match_pair.pattern.ty) => { let index = indices[value]; let new_candidate = self.candidate_without_match_pair(match_pair_index, diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index bd94f4e5bf257..e6e5b8380b241 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -107,7 +107,6 @@ pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>, assert_eq!(builder.cfg.start_new_block(), START_BLOCK); assert_eq!(builder.cfg.start_new_block(), END_BLOCK); - assert_eq!(builder.cfg.start_new_block(), DIVERGE_BLOCK); let mut block = START_BLOCK; let arg_decls = unpack!(block = builder.args_and_body(block, @@ -138,28 +137,25 @@ impl<'a,'tcx> Builder<'a,'tcx> { -> BlockAnd>> { self.in_scope(argument_extent, block, |this| { - let arg_decls = { - let implicit_arg_decls = implicit_arguments.into_iter() - .map(|ty| ArgDecl { ty: ty }); - - // to start, translate the argument patterns and collect the - // argument types. - let explicit_arg_decls = - explicit_arguments - .into_iter() - .enumerate() - .map(|(index, (ty, pattern))| { + // to start, translate the argument patterns and collect the argument types. + let implicits = implicit_arguments.into_iter().map(|ty| (ty, None)); + let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat))); + let arg_decls = + implicits + .chain(explicits) + .enumerate() + .map(|(index, (ty, pattern))| { + if let Some(pattern) = pattern { let lvalue = Lvalue::Arg(index as u32); let pattern = this.hir.irrefutable_pat(pattern); unpack!(block = this.lvalue_into_pattern(block, argument_extent, pattern, &lvalue)); - ArgDecl { ty: ty } - }); - - implicit_arg_decls.chain(explicit_arg_decls).collect() - }; + } + ArgDecl { ty: ty } + }) + .collect(); // start the first basic block and translate the body unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block)); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 4d136d265e5f4..e912e933bd81f 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -86,11 +86,14 @@ should go to. */ -use build::{BlockAnd, BlockAndExtension, Builder, CFG}; +use build::{BlockAnd, BlockAndExtension, Builder}; use rustc::middle::region::CodeExtent; -use rustc::middle::ty::Ty; +use rustc::middle::lang_items; +use rustc::middle::subst::Substs; +use rustc::middle::ty::{self, Ty}; use rustc::mir::repr::*; -use syntax::codemap::Span; +use syntax::codemap::{Span, DUMMY_SP}; +use syntax::parse::token::intern_and_get_ident; pub struct Scope<'tcx> { extent: CodeExtent, @@ -227,17 +230,39 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.terminate(block, Terminator::Goto { target: target }); } - /// Creates a path that performs all required cleanup for - /// unwinding. This path terminates in DIVERGE. Returns the start - /// of the path. See module comment for more details. - pub fn diverge_cleanup(&mut self) -> BasicBlock { - diverge_cleanup_helper(&mut self.cfg, &mut self.scopes) - } + /// Creates a path that performs all required cleanup for unwinding. + /// + /// This path terminates in Resume. Returns the start of the path. + /// See module comment for more details. None indicates there’s no + /// cleanup to do at this point. + pub fn diverge_cleanup(&mut self) -> Option { + if self.scopes.is_empty() { + return None; + } - /// Create diverge cleanup and branch to it from `block`. - pub fn panic(&mut self, block: BasicBlock) { - let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Panic { target: cleanup }); + let mut terminator = Terminator::Resume; + // Given an array of scopes, we generate these from the outermost scope to the innermost + // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will + // generate B0 <- B1 <- B2 in left-to-right order. The outermost scope (B0) will always + // terminate with a Resume terminator. + for scope in self.scopes.iter_mut().filter(|s| !s.drops.is_empty()) { + if let Some(b) = scope.cached_block { + terminator = Terminator::Goto { target: b }; + continue; + } else { + let new_block = self.cfg.start_new_block(); + self.cfg.block_data_mut(new_block).is_cleanup = true; + self.cfg.terminate(new_block, terminator); + terminator = Terminator::Goto { target: new_block }; + for &(kind, span, ref lvalue) in scope.drops.iter().rev() { + self.cfg.push_drop(new_block, span, kind, lvalue); + } + scope.cached_block = Some(new_block); + } + } + // Return the innermost cached block, most likely the one we just generated. + // Note that if there are no cleanups in scope we return None. + self.scopes.iter().rev().flat_map(|b| b.cached_block).next() } /// Indicates that `lvalue` should be dropped on exit from @@ -248,15 +273,19 @@ impl<'a,'tcx> Builder<'a,'tcx> { kind: DropKind, lvalue: &Lvalue<'tcx>, lvalue_ty: Ty<'tcx>) { - if self.hir.needs_drop(lvalue_ty, span) { - match self.scopes.iter_mut().rev().find(|s| s.extent == extent) { - Some(scope) => { + if self.hir.needs_drop(lvalue_ty) { + for scope in self.scopes.iter_mut().rev() { + // We must invalidate all the cached_blocks leading up to the scope we’re looking + // for, because otherwise some/most of the blocks in the chain might become + // incorrect (i.e. they still are pointing at old cached_block). + scope.cached_block = None; + if scope.extent == extent { scope.drops.push((kind, span, lvalue.clone())); - scope.cached_block = None; + return; } - None => self.hir.span_bug(span, &format!("extent {:?} not in scope to drop {:?}", - extent, lvalue)), } + self.hir.span_bug(span, + &format!("extent {:?} not in scope to drop {:?}", extent, lvalue)); } } @@ -267,29 +296,111 @@ impl<'a,'tcx> Builder<'a,'tcx> { pub fn extent_of_outermost_scope(&self) -> CodeExtent { self.scopes.first().map(|scope| scope.extent).unwrap() } -} - -fn diverge_cleanup_helper<'tcx>(cfg: &mut CFG<'tcx>, scopes: &mut [Scope<'tcx>]) -> BasicBlock { - let len = scopes.len(); - if len == 0 { - return DIVERGE_BLOCK; + pub fn panic_bounds_check(&mut self, + block: BasicBlock, + index: Operand<'tcx>, + len: Operand<'tcx>, + span: Span) { + // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> ! + let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem); + let args = func.ty.fn_args(); + let ref_ty = args.skip_binder()[0]; + let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty { + (region, tyandmut.ty) + } else { + self.hir.span_bug(span, &format!("unexpected panic_bound_check type: {:?}", func.ty)); + }; + let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); + let (file, line) = self.span_to_fileline_args(span); + let elems = vec![Operand::Constant(file), Operand::Constant(line)]; + // FIXME: We should have this as a constant, rather than a stack variable (to not pollute + // icache with cold branch code), however to achieve that we either have to rely on rvalue + // promotion or have some way, in MIR, to create constants. + self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (file_arg, line_arg); + Rvalue::Aggregate(AggregateKind::Tuple, elems)); + // FIXME: is this region really correct here? + self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; + Rvalue::Ref(*region, BorrowKind::Unique, tuple)); + let cleanup = self.diverge_cleanup(); + self.cfg.terminate(block, Terminator::Call { + func: Operand::Constant(func), + args: vec![Operand::Consume(tuple_ref), index, len], + kind: match cleanup { + None => CallKind::Diverging, + Some(c) => CallKind::DivergingCleanup(c) + } + }); } - let (remaining, scope) = scopes.split_at_mut(len - 1); - let scope = &mut scope[0]; - - if let Some(b) = scope.cached_block { - return b; + /// Create diverge cleanup and branch to it from `block`. + pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) { + // fn(&(msg: &'static str filename: &'static str, line: u32)) -> ! + let func = self.lang_function(lang_items::PanicFnLangItem); + let args = func.ty.fn_args(); + let ref_ty = args.skip_binder()[0]; + let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty { + (region, tyandmut.ty) + } else { + self.hir.span_bug(span, &format!("unexpected panic type: {:?}", func.ty)); + }; + let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); + let (file, line) = self.span_to_fileline_args(span); + let message = Constant { + span: DUMMY_SP, + ty: self.hir.tcx().mk_static_str(), + literal: self.hir.str_literal(intern_and_get_ident(msg)) + }; + let elems = vec![Operand::Constant(message), + Operand::Constant(file), + Operand::Constant(line)]; + // FIXME: We should have this as a constant, rather than a stack variable (to not pollute + // icache with cold branch code), however to achieve that we either have to rely on rvalue + // promotion or have some way, in MIR, to create constants. + self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg); + Rvalue::Aggregate(AggregateKind::Tuple, elems)); + // FIXME: is this region really correct here? + self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; + Rvalue::Ref(*region, BorrowKind::Unique, tuple)); + let cleanup = self.diverge_cleanup(); + self.cfg.terminate(block, Terminator::Call { + func: Operand::Constant(func), + args: vec![Operand::Consume(tuple_ref)], + kind: match cleanup { + None => CallKind::Diverging, + Some(c) => CallKind::DivergingCleanup(c) + } + }); } - let block = cfg.start_new_block(); - for &(kind, span, ref lvalue) in &scope.drops { - cfg.push_drop(block, span, kind, lvalue); + fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> { + let funcdid = match self.hir.tcx().lang_items.require(lang_item) { + Ok(d) => d, + Err(m) => { + self.hir.tcx().sess.fatal(&*m) + } + }; + Constant { + span: DUMMY_SP, + ty: self.hir.tcx().lookup_item_type(funcdid).ty, + literal: Literal::Item { + def_id: funcdid, + kind: ItemKind::Function, + substs: self.hir.tcx().mk_substs(Substs::empty()) + } + } } - scope.cached_block = Some(block); - let remaining_cleanup_block = diverge_cleanup_helper(cfg, remaining); - cfg.terminate(block, Terminator::Goto { target: remaining_cleanup_block }); - block + fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) { + let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo); + (Constant { + span: DUMMY_SP, + ty: self.hir.tcx().mk_static_str(), + literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name)) + }, Constant { + span: DUMMY_SP, + ty: self.hir.tcx().types.u32, + literal: self.hir.usize_literal(span_lines.line) + }) + } } diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs new file mode 100644 index 0000000000000..1b8fe6505583c --- /dev/null +++ b/src/librustc_mir/graphviz.rs @@ -0,0 +1,133 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dot; +use rustc::mir::repr::*; +use rustc::middle::ty; +use std::fmt::Debug; +use std::io::{self, Write}; + +/// Write a graphviz DOT graph for the given MIR. +pub fn write_mir_graphviz(mir: &Mir, w: &mut W) -> io::Result<()> { + try!(writeln!(w, "digraph Mir {{")); + + // Global graph properties + try!(writeln!(w, r#" graph [fontname="monospace"];"#)); + try!(writeln!(w, r#" node [fontname="monospace"];"#)); + try!(writeln!(w, r#" edge [fontname="monospace"];"#)); + + // Graph label + try!(write_graph_label(mir, w)); + + // Nodes + for block in mir.all_basic_blocks() { + try!(write_node(block, mir, w)); + } + + // Edges + for source in mir.all_basic_blocks() { + try!(write_edges(source, mir, w)); + } + + writeln!(w, "}}") +} + +/// Write a graphviz DOT node for the given basic block. +fn write_node(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { + let data = mir.basic_block_data(block); + + // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. + try!(write!(w, r#" {} [shape="none", label=<"#, node(block))); + try!(write!(w, r#""#)); + + // Basic block number at the top. + try!(write!(w, r#""#, block.index())); + + // List of statements in the middle. + if !data.statements.is_empty() { + try!(write!(w, r#"")); + } + + // Terminator head at the bottom, not including the list of successor blocks. Those will be + // displayed as labels on the edges between blocks. + let mut terminator_head = String::new(); + data.terminator().fmt_head(&mut terminator_head).unwrap(); + try!(write!(w, r#""#, dot::escape_html(&terminator_head))); + + // Close the table, node label, and the node itself. + writeln!(w, "
{}
"#)); + for statement in &data.statements { + try!(write!(w, "{}
", escape(statement))); + } + try!(write!(w, "
{}
>];") +} + +/// Write graphviz DOT edges with labels between the given basic block and all of its successors. +fn write_edges(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { + let terminator = &mir.basic_block_data(source).terminator(); + let labels = terminator.fmt_successor_labels(); + + for (&target, label) in terminator.successors().iter().zip(labels) { + try!(writeln!(w, r#" {} -> {} [label="{}"];"#, node(source), node(target), label)); + } + + Ok(()) +} + +/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that +/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of +/// all the variables and temporaries. +fn write_graph_label(mir: &Mir, w: &mut W) -> io::Result<()> { + try!(write!(w, " label= 0 { + try!(write!(w, ", ")); + } + try!(write!(w, "{:?}: {}", Lvalue::Arg(i as u32), escape(&arg.ty))); + } + + try!(write!(w, ") -> ")); + + // fn return type. + match mir.return_ty { + ty::FnOutput::FnConverging(ty) => try!(write!(w, "{}", escape(ty))), + ty::FnOutput::FnDiverging => try!(write!(w, "!")), + } + + try!(write!(w, r#"
"#)); + + // User variable types (including the user's name in a comment). + for (i, var) in mir.var_decls.iter().enumerate() { + try!(write!(w, "let ")); + if var.mutability == Mutability::Mut { + try!(write!(w, "mut ")); + } + try!(write!(w, r#"{:?}: {}; // {}
"#, + Lvalue::Var(i as u32), escape(&var.ty), var.name)); + } + + // Compiler-introduced temporary types. + for (i, temp) in mir.temp_decls.iter().enumerate() { + try!(write!(w, r#"let mut {:?}: {};
"#, + Lvalue::Temp(i as u32), escape(&temp.ty))); + } + + writeln!(w, ">;") +} + +fn node(block: BasicBlock) -> String { + format!("bb{}", block.index()) +} + +fn escape(t: &T) -> String { + dot::escape_html(&format!("{:?}", t)) +} diff --git a/src/librustc_mir/graphviz/mod.rs b/src/librustc_mir/graphviz/mod.rs deleted file mode 100644 index d5da606d76e80..0000000000000 --- a/src/librustc_mir/graphviz/mod.rs +++ /dev/null @@ -1,166 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use dot; -use rustc::mir::repr::*; -use std::borrow::IntoCow; - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct EdgeIndex { - source: BasicBlock, - target: BasicBlock, - index: usize, -} - -impl<'a,'tcx> dot::Labeller<'a, BasicBlock, EdgeIndex> for Mir<'tcx> { - fn graph_id(&'a self) -> dot::Id<'a> { - dot::Id::new("Mir").unwrap() - } - - fn node_id(&'a self, n: &BasicBlock) -> dot::Id<'a> { - dot::Id::new(format!("BB{}", n.index())).unwrap() - } - - fn node_shape(&'a self, _: &BasicBlock) -> Option> { - Some(dot::LabelText::label("none")) - } - - fn node_label(&'a self, &n: &BasicBlock) -> dot::LabelText<'a> { - let mut buffer = String::new(); - buffer.push_str(""); - - buffer.push_str(""); - - let data = self.basic_block_data(n); - for statement in &data.statements { - buffer.push_str(""); - } - - buffer.push_str(""); - - buffer.push_str("
"); - buffer.push_str(&escape(format!("{:?}", n))); - buffer.push_str("
"); - buffer.push_str(&escape(format!("{:?}", statement))); - buffer.push_str("
"); - buffer.push_str(&escape(format!("{:?}", &data.terminator))); - buffer.push_str("
"); - - dot::LabelText::html(buffer) - } - - fn edge_label(&'a self, edge: &EdgeIndex) -> dot::LabelText<'a> { - dot::LabelText::label(format!("{}", edge.index)) - } -} - -impl<'a,'tcx> dot::GraphWalk<'a, BasicBlock, EdgeIndex> for Mir<'tcx> { - fn nodes(&'a self) -> dot::Nodes<'a, BasicBlock> { - self.all_basic_blocks().into_cow() - } - - fn edges(&'a self) -> dot::Edges<'a, EdgeIndex> { - self.all_basic_blocks() - .into_iter() - .flat_map(|source| { - self.basic_block_data(source) - .terminator - .successors() - .iter() - .enumerate() - .map(move |(index, &target)| { - EdgeIndex { - source: source, - target: target, - index: index, - } - }) - }) - .collect::>() - .into_cow() - } - - fn source(&'a self, edge: &EdgeIndex) -> BasicBlock { - edge.source - } - - fn target(&'a self, edge: &EdgeIndex) -> BasicBlock { - edge.target - } -} - -fn escape(text: String) -> String { - let text = dot::escape_html(&text); - let text = all_to_subscript("Temp", text); - let text = all_to_subscript("Var", text); - let text = all_to_subscript("Arg", text); - let text = all_to_subscript("BB", text); - text -} - -/// A call like `all_to_subscript("Temp", "Temp(123)")` will convert -/// to `Temp₁₂₃`. -fn all_to_subscript(header: &str, mut text: String) -> String { - let mut offset = 0; - while offset < text.len() { - if let Some(text1) = to_subscript1(header, &text, &mut offset) { - text = text1; - } - } - return text; - - /// Looks for `Foo(\d*)` where `header=="Foo"` and replaces the `\d` with subscripts. - /// Updates `offset` to point to the next location where we might want to search. - /// Returns an updated string if changes were made, else None. - fn to_subscript1(header: &str, text: &str, offset: &mut usize) -> Option { - let a = match text[*offset..].find(header) { - None => { - *offset = text.len(); - return None; - } - Some(a) => a + *offset, - }; - - // Example: - // - // header: "Foo" - // text: ....Foo(123)... - // ^ ^ - // a b - - let b = a + header.len(); - *offset = b; - - let mut chars = text[b..].chars(); - if Some('(') != chars.next() { - return None; - } - - let mut result = String::new(); - result.push_str(&text[..b]); - - while let Some(c) = chars.next() { - if c == ')' { - break; - } - if !c.is_digit(10) { - return None; - } - - // 0x208 is _0 in unicode, 0x209 is _1, etc - const SUBSCRIPTS: &'static str = "₀₁₂₃₄₅₆₇₈₉"; - let n = (c as usize) - ('0' as usize); - result.extend(SUBSCRIPTS.chars().skip(n).take(1)); - } - - result.extend(chars); - return Some(result); - } -} diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 5cb12627d6ba8..012bd9691be5f 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -41,11 +41,38 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { .map(|e| e.to_ref()) .collect(); ExprKind::Call { + ty: expr.ty, fun: expr.to_ref(), args: args, } } + hir::ExprCall(ref fun, ref args) => { + if cx.tcx.is_method_call(self.id) { + // The callee is something implementing Fn, FnMut, or FnOnce. + // Find the actual method implementation being called and + // build the appropriate UFCS call expression with the + // callee-object as self parameter. + + let method = method_callee(cx, self, ty::MethodCall::expr(self.id)); + let mut argrefs = vec![fun.to_ref()]; + argrefs.extend(args.iter().map(|a| a.to_ref())); + + ExprKind::Call { + ty: method.ty, + fun: method.to_ref(), + args: argrefs, + } + } else { + ExprKind::Call { + ty: &cx.tcx.node_id_to_type(fun.id), + fun: fun.to_ref(), + args: args.to_ref(), + } + + } + } + hir::ExprAddrOf(mutbl, ref expr) => { let region = match expr_ty.sty { ty::TyRef(r, _) => r, @@ -328,8 +355,6 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { ExprKind::Vec { fields: fields.to_ref() }, hir::ExprTup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() }, - hir::ExprCall(ref fun, ref args) => - ExprKind::Call { fun: fun.to_ref(), args: args.to_ref() }, }; let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id); @@ -519,51 +544,64 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(expr.id).substs); // Otherwise there may be def_map borrow conflicts let def = cx.tcx.def_map.borrow()[&expr.id].full_def(); - match def { - def::DefVariant(_, def_id, false) | - def::DefStruct(def_id) | - def::DefFn(def_id, _) | - def::DefMethod(def_id) => { - let kind = match def { - def::DefVariant(..) => ItemKind::Variant, - def::DefStruct(..) => ItemKind::Struct, - def::DefFn(..) => ItemKind::Function, - def::DefMethod(..) => ItemKind::Method, - _ => panic!() - }; - ExprKind::Literal { - literal: Literal::Item { def_id: def_id, kind: kind, substs: substs } - } + let (def_id, kind) = match def { + // A regular function. + def::DefFn(def_id, _) => (def_id, ItemKind::Function), + def::DefMethod(def_id) => (def_id, ItemKind::Method), + def::DefStruct(def_id) => match cx.tcx.node_id_to_type(expr.id).sty { + // A tuple-struct constructor. + ty::TyBareFn(..) => (def_id, ItemKind::Function), + // This is a special case: a unit struct which is used as a value. We return a + // completely different ExprKind here to account for this special case. + ty::TyStruct(adt_def, substs) => return ExprKind::Adt { + adt_def: adt_def, + variant_index: 0, + substs: substs, + fields: vec![], + base: None + }, + ref sty => panic!("unexpected sty: {:?}", sty) + }, + def::DefVariant(enum_id, variant_id, false) => match cx.tcx.node_id_to_type(expr.id).sty { + // A variant constructor. + ty::TyBareFn(..) => (variant_id, ItemKind::Function), + // A unit variant, similar special case to the struct case above. + ty::TyEnum(adt_def, substs) => { + debug_assert!(adt_def.did == enum_id); + let index = adt_def.variant_index_with_id(variant_id); + return ExprKind::Adt { + adt_def: adt_def, + substs: substs, + variant_index: index, + fields: vec![], + base: None + }; + }, + ref sty => panic!("unexpected sty: {:?}", sty) }, def::DefConst(def_id) | def::DefAssociatedConst(def_id) => { if let Some(v) = cx.try_const_eval_literal(expr) { - ExprKind::Literal { literal: v } + return ExprKind::Literal { literal: v }; } else { - ExprKind::Literal { - literal: Literal::Item { - def_id: def_id, - kind: ItemKind::Constant, - substs: substs - } - } + (def_id, ItemKind::Constant) } } - - def::DefStatic(node_id, _) => - ExprKind::StaticRef { - id: node_id, - }, + def::DefStatic(node_id, _) => return ExprKind::StaticRef { + id: node_id, + }, def @ def::DefLocal(..) | - def @ def::DefUpvar(..) => - convert_var(cx, expr, def), + def @ def::DefUpvar(..) => return convert_var(cx, expr, def), def => cx.tcx.sess.span_bug( expr.span, &format!("def `{:?}` not yet implemented", def)), + }; + ExprKind::Literal { + literal: Literal::Item { def_id: def_id, kind: kind, substs: substs } } } @@ -771,6 +809,7 @@ fn overloaded_operator<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, // now create the call itself let fun = method_callee(cx, expr, method_call); ExprKind::Call { + ty: fun.ty, fun: fun.to_ref(), args: argrefs, } diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index d6cfd1a2c6ecf..f9dd40defdf70 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -19,9 +19,7 @@ use hair::*; use rustc::mir::repr::*; use rustc::middle::const_eval::{self, ConstVal}; -use rustc::middle::def_id::DefId; use rustc::middle::infer::InferCtxt; -use rustc::middle::subst::{Subst, Substs}; use rustc::middle::ty::{self, Ty}; use syntax::codemap::Span; use syntax::parse::token; @@ -64,6 +62,10 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { self.tcx.types.bool } + pub fn str_literal(&mut self, value: token::InternedString) -> Literal<'tcx> { + Literal::Value { value: ConstVal::Str(value) } + } + pub fn true_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: ConstVal::Bool(true) } } @@ -83,16 +85,6 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { .map(|v| Literal::Value { value: v }) } - pub fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> { - let eq_def_id = self.tcx.lang_items.eq_trait().unwrap(); - self.cmp_method_ref(eq_def_id, "eq", ty) - } - - pub fn partial_le(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> { - let ord_def_id = self.tcx.lang_items.ord_trait().unwrap(); - self.cmp_method_ref(ord_def_id, "le", ty) - } - pub fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize { adt_def.variants.len() } @@ -103,17 +95,8 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { .collect() } - pub fn needs_drop(&mut self, ty: Ty<'tcx>, span: Span) -> bool { - if self.infcx.type_moves_by_default(ty, span) { - // FIXME(#21859) we should do an add'l check here to determine if - // any dtor will execute, but the relevant fn - // (`type_needs_drop`) is currently factored into - // `librustc_trans`, so we can't easily do so. - true - } else { - // if type implements Copy, cannot require drop - false - } + pub fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool { + self.tcx.type_needs_drop_given_env(ty, &self.infcx.parameter_environment) } pub fn span_bug(&mut self, span: Span, message: &str) -> ! { @@ -123,35 +106,6 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.tcx } - - fn cmp_method_ref(&mut self, - trait_def_id: DefId, - method_name: &str, - arg_ty: Ty<'tcx>) - -> ItemRef<'tcx> { - let method_name = token::intern(method_name); - let substs = Substs::new_trait(vec![arg_ty], vec![], arg_ty); - for trait_item in self.tcx.trait_items(trait_def_id).iter() { - match *trait_item { - ty::ImplOrTraitItem::MethodTraitItem(ref method) => { - if method.name == method_name { - let method_ty = self.tcx.lookup_item_type(method.def_id); - let method_ty = method_ty.ty.subst(self.tcx, &substs); - return ItemRef { - ty: method_ty, - kind: ItemKind::Method, - def_id: method.def_id, - substs: self.tcx.mk_substs(substs), - }; - } - } - ty::ImplOrTraitItem::ConstTraitItem(..) | - ty::ImplOrTraitItem::TypeTraitItem(..) => {} - } - } - - self.tcx.sess.bug(&format!("found no method `{}` in `{:?}`", method_name, trait_def_id)); - } } mod block; diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs index 8f3a1c17440fc..1f425aafa256a 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -14,11 +14,11 @@ use rustc_data_structures::fnv::FnvHashMap; use rustc::middle::const_eval; use rustc::middle::def; use rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding}; -use rustc::middle::subst::Substs; use rustc::middle::ty::{self, Ty}; use rustc::mir::repr::*; use rustc_front::hir; use syntax::ast; +use syntax::codemap::Span; use syntax::ptr::P; /// When there are multiple patterns in a single arm, each one has its @@ -40,15 +40,15 @@ struct PatCx<'patcx, 'cx: 'patcx, 'tcx: 'cx> { } impl<'cx, 'tcx> Cx<'cx, 'tcx> { - pub fn irrefutable_pat(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { - PatCx::new(self, None).to_pat(pat) + pub fn irrefutable_pat(&mut self, pat: &hir::Pat) -> Pattern<'tcx> { + PatCx::new(self, None).to_pattern(pat) } pub fn refutable_pat(&mut self, binding_map: Option<&FnvHashMap>, - pat: &'tcx hir::Pat) + pat: &hir::Pat) -> Pattern<'tcx> { - PatCx::new(self, binding_map).to_pat(pat) + PatCx::new(self, binding_map).to_pattern(pat) } } @@ -62,13 +62,12 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { } } - fn to_pat(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { + fn to_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> { let kind = match pat.node { hir::PatWild => PatternKind::Wild, hir::PatLit(ref value) => { let value = const_eval::eval_const_expr(self.cx.tcx, value); - let value = Literal::Value { value: value }; PatternKind::Constant { value: value } } @@ -88,22 +87,9 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { def::DefConst(def_id) | def::DefAssociatedConst(def_id) => match const_eval::lookup_const_by_id(self.cx.tcx, def_id, Some(pat.id)) { Some(const_expr) => { - let opt_value = - const_eval::eval_const_expr_partial( - self.cx.tcx, const_expr, - const_eval::EvalHint::ExprTypeChecked, - None); - let literal = if let Ok(value) = opt_value { - Literal::Value { value: value } - } else { - let substs = self.cx.tcx.mk_substs(Substs::empty()); - Literal::Item { - def_id: def_id, - kind: ItemKind::Constant, - substs: substs - } - }; - PatternKind::Constant { value: literal } + let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr, + pat.span); + return self.to_pattern(&*pat); } None => { self.cx.tcx.sess.span_bug( @@ -120,7 +106,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { hir::PatRegion(ref subpattern, _) | hir::PatBox(ref subpattern) => { - PatternKind::Deref { subpattern: self.to_pat(subpattern) } + PatternKind::Deref { subpattern: self.to_pattern(subpattern) } } hir::PatVec(ref prefix, ref slice, ref suffix) => { @@ -131,14 +117,14 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { subpattern: Pattern { ty: mt.ty, span: pat.span, - kind: Box::new(self.slice_or_array_pattern(pat, mt.ty, prefix, + kind: Box::new(self.slice_or_array_pattern(pat.span, mt.ty, prefix, slice, suffix)), }, }, ty::TySlice(..) | ty::TyArray(..) => - self.slice_or_array_pattern(pat, ty, prefix, slice, suffix), + self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix), ref sty => self.cx.tcx.sess.span_bug( @@ -153,7 +139,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { .enumerate() .map(|(i, subpattern)| FieldPattern { field: Field::new(i), - pattern: self.to_pat(subpattern), + pattern: self.to_pattern(subpattern), }) .collect(); @@ -188,7 +174,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { name: ident.node.name, var: id, ty: var_ty, - subpattern: self.to_opt_pat(sub), + subpattern: self.to_opt_pattern(sub), } } @@ -203,7 +189,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { .enumerate() .map(|(i, field)| FieldPattern { field: Field::new(i), - pattern: self.to_pat(field), + pattern: self.to_pattern(field), }) .collect(); self.variant_or_leaf(pat, subpatterns) @@ -234,7 +220,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { }); FieldPattern { field: Field::new(index), - pattern: self.to_pat(&field.node.pat), + pattern: self.to_pattern(&field.node.pat), } }) .collect(); @@ -256,28 +242,28 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { } } - fn to_pats(&mut self, pats: &'tcx [P]) -> Vec> { - pats.iter().map(|p| self.to_pat(p)).collect() + fn to_patterns(&mut self, pats: &[P]) -> Vec> { + pats.iter().map(|p| self.to_pattern(p)).collect() } - fn to_opt_pat(&mut self, pat: &'tcx Option>) -> Option> { - pat.as_ref().map(|p| self.to_pat(p)) + fn to_opt_pattern(&mut self, pat: &Option>) -> Option> { + pat.as_ref().map(|p| self.to_pattern(p)) } fn slice_or_array_pattern(&mut self, - pat: &'tcx hir::Pat, + span: Span, ty: Ty<'tcx>, - prefix: &'tcx [P], - slice: &'tcx Option>, - suffix: &'tcx [P]) + prefix: &[P], + slice: &Option>, + suffix: &[P]) -> PatternKind<'tcx> { match ty.sty { ty::TySlice(..) => { // matching a slice or fixed-length array PatternKind::Slice { - prefix: self.to_pats(prefix), - slice: self.to_opt_pat(slice), - suffix: self.to_pats(suffix), + prefix: self.to_patterns(prefix), + slice: self.to_opt_pattern(slice), + suffix: self.to_patterns(suffix), } } @@ -285,20 +271,20 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { // fixed-length array assert!(len >= prefix.len() + suffix.len()); PatternKind::Array { - prefix: self.to_pats(prefix), - slice: self.to_opt_pat(slice), - suffix: self.to_pats(suffix), + prefix: self.to_patterns(prefix), + slice: self.to_opt_pattern(slice), + suffix: self.to_patterns(suffix), } } _ => { - self.cx.tcx.sess.span_bug(pat.span, "unexpanded macro or bad constant etc"); + self.cx.tcx.sess.span_bug(span, "unexpanded macro or bad constant etc"); } } } fn variant_or_leaf(&mut self, - pat: &'tcx hir::Pat, + pat: &hir::Pat, subpatterns: Vec>) -> PatternKind<'tcx> { let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def(); diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 99e6d6633f288..fb81cc7e6d97a 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -15,10 +15,11 @@ //! structures. use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp, ItemKind}; +use rustc::middle::const_eval::ConstVal; use rustc::middle::def_id::DefId; use rustc::middle::region::CodeExtent; use rustc::middle::subst::Substs; -use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty}; +use rustc::middle::ty::{self, AdtDef, ClosureSubsts, Region, Ty}; use rustc_front::hir; use syntax::ast; use syntax::codemap::Span; @@ -123,6 +124,7 @@ pub enum ExprKind<'tcx> { value: ExprRef<'tcx>, }, Call { + ty: ty::Ty<'tcx>, fun: ExprRef<'tcx>, args: Vec>, }, @@ -305,7 +307,7 @@ pub enum PatternKind<'tcx> { }, // box P, &P, &mut P, etc Constant { - value: Literal<'tcx>, + value: ConstVal, }, Range { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 710d5ba4b4368..9cc40bbc3838a 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -19,7 +19,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![crate_type = "dylib"] #![feature(rustc_private)] -#![feature(into_cow)] #[macro_use] extern crate log; extern crate graphviz as dot; @@ -30,8 +29,8 @@ extern crate rustc_back; extern crate syntax; pub mod build; -pub mod mir_map; +pub mod graphviz; mod hair; -mod graphviz; +pub mod mir_map; +pub mod pretty; pub mod transform; - diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 39a315f3c41c7..ac15878dc5136 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -21,8 +21,10 @@ extern crate rustc; extern crate rustc_front; use build; -use dot; +use graphviz; +use pretty; use transform::*; +use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; use std::fs::File; @@ -47,7 +49,7 @@ pub fn build_mir_for_crate<'tcx>(tcx: &ty::ctxt<'tcx>) -> MirMap<'tcx> { tcx: tcx, map: &mut map, }; - tcx.map.krate().visit_all_items(&mut dump); + tcx.visit_all_items_in_krate(DepNode::MirMapConstruction, &mut dump); } map } @@ -152,27 +154,29 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> { .flat_map(|a| a.meta_item_list()) .flat_map(|l| l.iter()); for item in meta_item_list { - if item.check_name("graphviz") { + if item.check_name("graphviz") || item.check_name("pretty") { match item.value_str() { Some(s) => { - match - File::create(format!("{}{}", prefix, s)) - .and_then(|ref mut output| dot::render(&mir, output)) - { - Ok(()) => { } - Err(e) => { - self.tcx.sess.span_fatal( - item.span, - &format!("Error writing graphviz \ - results to `{}`: {}", - s, e)); + let filename = format!("{}{}", prefix, s); + let result = File::create(&filename).and_then(|ref mut output| { + if item.check_name("graphviz") { + graphviz::write_mir_graphviz(&mir, output) + } else { + pretty::write_mir_pretty(&mir, output) } + }); + + if let Err(e) = result { + self.tcx.sess.span_fatal( + item.span, + &format!("Error writing MIR {} results to `{}`: {}", + item.name(), filename, e)); } } None => { self.tcx.sess.span_err( item.span, - "graphviz attribute requires a path"); + &format!("{} attribute requires a path", item.name())); } } } diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs new file mode 100644 index 0000000000000..163559f279281 --- /dev/null +++ b/src/librustc_mir/pretty.rs @@ -0,0 +1,85 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::mir::repr::*; +use rustc::middle::ty; +use std::io::{self, Write}; + +const INDENT: &'static str = " "; + +/// Write out a human-readable textual representation for the given MIR. +pub fn write_mir_pretty(mir: &Mir, w: &mut W) -> io::Result<()> { + try!(write_mir_intro(mir, w)); + + // Nodes + for block in mir.all_basic_blocks() { + try!(write_basic_block(block, mir, w)); + } + + writeln!(w, "}}") +} + +/// Write out a human-readable textual representation for the given basic block. +fn write_basic_block(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { + let data = mir.basic_block_data(block); + + // Basic block label at the top. + try!(writeln!(w, "\n{}{:?}: {{", INDENT, block)); + + // List of statements in the middle. + for statement in &data.statements { + try!(writeln!(w, "{0}{0}{1:?};", INDENT, statement)); + } + + // Terminator at the bottom. + try!(writeln!(w, "{0}{0}{1:?};", INDENT, data.terminator)); + + writeln!(w, "{}}}", INDENT) +} + +/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its +/// local variables (both user-defined bindings and compiler temporaries). +fn write_mir_intro(mir: &Mir, w: &mut W) -> io::Result<()> { + try!(write!(w, "fn(")); + + // fn argument types. + for (i, arg) in mir.arg_decls.iter().enumerate() { + if i > 0 { + try!(write!(w, ", ")); + } + try!(write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty)); + } + + try!(write!(w, ") -> ")); + + // fn return type. + match mir.return_ty { + ty::FnOutput::FnConverging(ty) => try!(write!(w, "{}", ty)), + ty::FnOutput::FnDiverging => try!(write!(w, "!")), + } + + try!(writeln!(w, " {{")); + + // User variable types (including the user's name in a comment). + for (i, var) in mir.var_decls.iter().enumerate() { + try!(write!(w, "{}let ", INDENT)); + if var.mutability == Mutability::Mut { + try!(write!(w, "mut ")); + } + try!(writeln!(w, "{:?}: {}; // {}", Lvalue::Var(i as u32), var.ty, var.name)); + } + + // Compiler-introduced temporary types. + for (i, temp) in mir.temp_decls.iter().enumerate() { + try!(writeln!(w, "{}let mut {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty)); + } + + Ok(()) +} diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 1eb3bfd7e0229..20a14cf415404 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -59,7 +59,7 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { self.erase_regions_statement(statement); } - self.erase_regions_terminator(&mut basic_block.terminator); + self.erase_regions_terminator(basic_block.terminator_mut()); } fn erase_regions_statement(&mut self, @@ -79,9 +79,8 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { terminator: &mut Terminator<'tcx>) { match *terminator { Terminator::Goto { .. } | - Terminator::Diverge | - Terminator::Return | - Terminator::Panic { .. } => { + Terminator::Resume | + Terminator::Return => { /* nothing to do */ } Terminator::If { ref mut cond, .. } => { @@ -90,23 +89,14 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { Terminator::Switch { ref mut discr, .. } => { self.erase_regions_lvalue(discr); } - Terminator::SwitchInt { - ref mut discr, - ref mut switch_ty, - .. - } => { + Terminator::SwitchInt { ref mut discr, ref mut switch_ty, .. } => { self.erase_regions_lvalue(discr); *switch_ty = self.tcx.erase_regions(switch_ty); }, - Terminator::Call { - data: CallData { - ref mut destination, - ref mut func, - ref mut args - }, - .. - } => { - self.erase_regions_lvalue(destination); + Terminator::Call { ref mut func, ref mut args, ref mut kind } => { + if let Some(ref mut destination) = kind.destination() { + self.erase_regions_lvalue(destination); + } self.erase_regions_operand(func); for arg in &mut *args { self.erase_regions_operand(arg); diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index d0c0afc80a657..7a5a00a8d560b 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -10,7 +10,6 @@ use rustc::middle::const_eval::ConstVal; use rustc::mir::repr::*; -use std::mem; use transform::util; use transform::MirPass; @@ -27,11 +26,10 @@ impl SimplifyCfg { // These blocks are always required. seen[START_BLOCK.index()] = true; seen[END_BLOCK.index()] = true; - seen[DIVERGE_BLOCK.index()] = true; let mut worklist = vec![START_BLOCK]; while let Some(bb) = worklist.pop() { - for succ in mir.basic_block_data(bb).terminator.successors() { + for succ in mir.basic_block_data(bb).terminator().successors() { if !seen[succ.index()] { seen[succ.index()] = true; worklist.push(*succ); @@ -51,7 +49,7 @@ impl SimplifyCfg { while mir.basic_block_data(target).statements.is_empty() { match mir.basic_block_data(target).terminator { - Terminator::Goto { target: next } => { + Some(Terminator::Goto { target: next }) => { if seen.contains(&next) { return None; } @@ -67,9 +65,9 @@ impl SimplifyCfg { let mut changed = false; for bb in mir.all_basic_blocks() { - // Temporarily swap out the terminator we're modifying to keep borrowck happy - let mut terminator = Terminator::Diverge; - mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator); + // Temporarily take ownership of the terminator we're modifying to keep borrowck happy + let mut terminator = mir.basic_block_data_mut(bb).terminator.take() + .expect("invalid terminator state"); for target in terminator.successors_mut() { let new_target = match final_target(mir, *target) { @@ -80,10 +78,8 @@ impl SimplifyCfg { changed |= *target != new_target; *target = new_target; } - - mir.basic_block_data_mut(bb).terminator = terminator; + mir.basic_block_data_mut(bb).terminator = Some(terminator); } - changed } @@ -91,11 +87,10 @@ impl SimplifyCfg { let mut changed = false; for bb in mir.all_basic_blocks() { - // Temporarily swap out the terminator we're modifying to keep borrowck happy - let mut terminator = Terminator::Diverge; - mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator); + let basic_block = mir.basic_block_data_mut(bb); + let mut terminator = basic_block.terminator_mut(); - mir.basic_block_data_mut(bb).terminator = match terminator { + *terminator = match *terminator { Terminator::If { ref targets, .. } if targets.0 == targets.1 => { changed = true; Terminator::Goto { target: targets.0 } @@ -115,7 +110,7 @@ impl SimplifyCfg { Terminator::SwitchInt { ref targets, .. } if targets.len() == 1 => { Terminator::Goto { target: targets[0] } } - _ => terminator + _ => continue } } @@ -131,7 +126,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { changed |= self.remove_goto_chains(mir); self.remove_dead_blocks(mir); } - // FIXME: Should probably be moved into some kind of pass manager mir.basic_blocks.shrink_to_fit(); } diff --git a/src/librustc_mir/transform/util.rs b/src/librustc_mir/transform/util.rs index 9510269454485..7e44beb18a2e9 100644 --- a/src/librustc_mir/transform/util.rs +++ b/src/librustc_mir/transform/util.rs @@ -15,7 +15,7 @@ use rustc::mir::repr::*; /// in a single pass pub fn update_basic_block_ids(mir: &mut Mir, replacements: &[BasicBlock]) { for bb in mir.all_basic_blocks() { - for target in mir.basic_block_data_mut(bb).terminator.successors_mut() { + for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() { *target = replacements[target.index()]; } } diff --git a/src/librustc_plugin/build.rs b/src/librustc_plugin/build.rs index 476425a75c22d..5adde4304f57c 100644 --- a/src/librustc_plugin/build.rs +++ b/src/librustc_plugin/build.rs @@ -46,10 +46,11 @@ pub fn find_plugin_registrar(diagnostic: &errors::Handler, Some(node_id) }, _ => { - diagnostic.err("multiple plugin registration functions found"); + let mut e = diagnostic.struct_err("multiple plugin registration functions found"); for &(_, span) in &finder.registrars { - diagnostic.span_note(span, "one is here"); + e.span_note(span, "one is here"); } + e.emit(); diagnostic.abort_if_errors(); unreachable!(); } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index d1a894c61f7e2..955e68be0b006 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -35,6 +35,7 @@ use std::mem::replace; use rustc_front::hir; use rustc_front::intravisit::{self, Visitor}; +use rustc::dep_graph::DepNode; use rustc::lint; use rustc::middle::def; use rustc::middle::def_id::DefId; @@ -597,13 +598,11 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { match result { None => true, Some((span, msg, note)) => { - self.tcx.sess.span_err(span, &msg[..]); - match note { - Some((span, msg)) => { - self.tcx.sess.span_note(span, &msg[..]) - } - None => {}, + let mut err = self.tcx.sess.struct_span_err(span, &msg[..]); + if let Some((span, msg)) = note { + err.span_note(span, &msg[..]); } + err.emit(); false }, } @@ -1028,10 +1027,12 @@ impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> { fn check_sane_privacy(&self, item: &hir::Item) { let check_inherited = |sp, vis, note: &str| { if vis != hir::Inherited { - span_err!(self.tcx.sess, sp, E0449, "unnecessary visibility qualifier"); + let mut err = struct_span_err!(self.tcx.sess, sp, E0449, + "unnecessary visibility qualifier"); if !note.is_empty() { - self.tcx.sess.span_note(sp, note); + err.span_note(sp, note); } + err.emit(); } }; @@ -1674,6 +1675,8 @@ pub fn check_crate(tcx: &ty::ctxt, export_map: &def::ExportMap, external_exports: ExternalExports) -> AccessLevels { + let _task = tcx.dep_graph.in_task(DepNode::Privacy); + let krate = tcx.map.krate(); // Sanity check to make sure that all privacy usage and controls are diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 11d09fa3e9a6b..1d3f2b79844ea 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -24,7 +24,7 @@ use {names_to_string, module_to_string}; use ParentLink::{self, ModuleParentLink, BlockParentLink}; use Resolver; use resolve_imports::Shadowable; -use {resolve_error, ResolutionError}; +use {resolve_error, resolve_struct_error, ResolutionError}; use self::DuplicateCheckingMode::*; @@ -137,12 +137,16 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { // Record an error here by looking up the namespace that had the duplicate let ns_str = match ns { TypeNS => "type or module", ValueNS => "value" }; - resolve_error(self, sp, ResolutionError::DuplicateDefinition(ns_str, name)); + let mut err = resolve_struct_error(self, + sp, + ResolutionError::DuplicateDefinition(ns_str, + name)); if let Some(sp) = child[ns].span() { let note = format!("first definition of {} `{}` here", ns_str, name); - self.session.span_note(sp, ¬e); + err.span_note(sp, ¬e); } + err.emit(); child } } @@ -253,13 +257,13 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { }) .collect::>(); if mod_spans.len() > 1 { - resolve_error(self, + let mut e = resolve_struct_error(self, mod_spans[0], ResolutionError::SelfImportCanOnlyAppearOnceInTheList); for other_span in mod_spans.iter().skip(1) { - self.session - .span_note(*other_span, "another `self` import appears here"); + e.span_note(*other_span, "another `self` import appears here"); } + e.emit(); } for source_item in source_items { diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 85fb5d9ccf9e5..04ab3fe70e9fa 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -274,7 +274,7 @@ https://doc.rust-lang.org/reference.html#use-declarations "##, E0401: r##" -Inner functions do not inherit type parameters from the functions they are +Inner items do not inherit type parameters from the functions they are embedded in. For example, this will not compile: ``` @@ -286,12 +286,32 @@ fn foo(x: T) { } ``` -Functions inside functions are basically just like top-level functions, except -that they can only be called from the function they are in. +nor will this: + +``` +fn foo(x: T) { + type MaybeT = Option; + // ... +} +``` + +or this: + +``` +fn foo(x: T) { + struct Foo { + x: T, + } + // ... +} +``` + +Items inside functions are basically just like top-level items, except +that they can only be used from the function they are in. There are a couple of solutions for this. -You can use a closure: +If the item is a function, you may use a closure: ``` fn foo(x: T) { @@ -302,7 +322,7 @@ fn foo(x: T) { } ``` -or copy over the parameters: +For a generic item, you can copy over the parameters: ``` fn foo(x: T) { @@ -313,6 +333,12 @@ fn foo(x: T) { } ``` +``` +fn foo(x: T) { + type MaybeT = Option; +} +``` + Be sure to copy over any bounds as well: ``` @@ -324,10 +350,18 @@ fn foo(x: T) { } ``` +``` +fn foo(x: T) { + struct Foo { + x: T, + } +} +``` + This may require additional type hints in the function body. -In case the function is in an `impl`, defining a private helper function might -be easier: +In case the item is a function inside an `impl`, defining a private helper +function might be easier: ``` impl Foo { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 201012073b1a7..a5a22fd7f3904 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -30,7 +30,6 @@ extern crate syntax; #[no_link] extern crate rustc_bitflags; extern crate rustc_front; - extern crate rustc; use self::PatternBindingMode::*; @@ -64,9 +63,10 @@ use syntax::ast; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, TyIs, TyI8, TyI16, TyI32, TyI64}; use syntax::ast::{TyUs, TyU8, TyU16, TyU32, TyU64, TyF64, TyF32}; use syntax::attr::AttrMetaMethods; -use syntax::parse::token::{self, special_names, special_idents}; use syntax::codemap::{self, Span, Pos}; -use syntax::util::lev_distance::{lev_distance, max_suggestion_distance}; +use syntax::errors::DiagnosticBuilder; +use syntax::parse::token::{self, special_names, special_idents}; +use syntax::util::lev_distance::find_best_match_for_name; use rustc_front::intravisit::{self, FnKind, Visitor}; use rustc_front::hir; @@ -91,7 +91,6 @@ use std::cell::{Cell, RefCell}; use std::fmt; use std::mem::replace; use std::rc::{Rc, Weak}; -use std::usize; use resolve_imports::{Target, ImportDirective, ImportResolutionPerNamespace}; use resolve_imports::Shadowable; @@ -118,7 +117,7 @@ macro_rules! execute_callback { enum SuggestionType { Macro(String), - Function(String), + Function(token::InternedString), NotFound, } @@ -217,210 +216,221 @@ pub enum UnresolvedNameContext { fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>, span: syntax::codemap::Span, resolution_error: ResolutionError<'b>) { + resolve_struct_error(resolver, span, resolution_error).emit(); +} + +fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>, + span: syntax::codemap::Span, + resolution_error: ResolutionError<'b>) + -> DiagnosticBuilder<'a> { if !resolver.emit_errors { - return; + return resolver.session.diagnostic().struct_dummy(); } + match resolution_error { ResolutionError::TypeParametersFromOuterFunction => { - span_err!(resolver.session, - span, - E0401, - "can't use type parameters from outer function; try using a local type \ - parameter instead"); + struct_span_err!(resolver.session, + span, + E0401, + "can't use type parameters from outer function; try using a local \ + type parameter instead") } ResolutionError::OuterTypeParameterContext => { - span_err!(resolver.session, - span, - E0402, - "cannot use an outer type parameter in this context"); + struct_span_err!(resolver.session, + span, + E0402, + "cannot use an outer type parameter in this context") } ResolutionError::NameAlreadyUsedInTypeParameterList(name) => { - span_err!(resolver.session, - span, - E0403, - "the name `{}` is already used for a type parameter in this type parameter \ - list", - name); + struct_span_err!(resolver.session, + span, + E0403, + "the name `{}` is already used for a type parameter in this type \ + parameter list", + name) } ResolutionError::IsNotATrait(name) => { - span_err!(resolver.session, span, E0404, "`{}` is not a trait", name); + struct_span_err!(resolver.session, span, E0404, "`{}` is not a trait", name) } ResolutionError::UndeclaredTraitName(name) => { - span_err!(resolver.session, - span, - E0405, - "use of undeclared trait name `{}`", - name); + struct_span_err!(resolver.session, + span, + E0405, + "use of undeclared trait name `{}`", + name) } ResolutionError::UndeclaredAssociatedType => { - span_err!(resolver.session, span, E0406, "undeclared associated type"); + struct_span_err!(resolver.session, span, E0406, "undeclared associated type") } ResolutionError::MethodNotMemberOfTrait(method, trait_) => { - span_err!(resolver.session, - span, - E0407, - "method `{}` is not a member of trait `{}`", - method, - trait_); + struct_span_err!(resolver.session, + span, + E0407, + "method `{}` is not a member of trait `{}`", + method, + trait_) } ResolutionError::TypeNotMemberOfTrait(type_, trait_) => { - span_err!(resolver.session, - span, - E0437, - "type `{}` is not a member of trait `{}`", - type_, - trait_); + struct_span_err!(resolver.session, + span, + E0437, + "type `{}` is not a member of trait `{}`", + type_, + trait_) } ResolutionError::ConstNotMemberOfTrait(const_, trait_) => { - span_err!(resolver.session, - span, - E0438, - "const `{}` is not a member of trait `{}`", - const_, - trait_); + struct_span_err!(resolver.session, + span, + E0438, + "const `{}` is not a member of trait `{}`", + const_, + trait_) } ResolutionError::VariableNotBoundInPattern(variable_name, pattern_number) => { - span_err!(resolver.session, - span, - E0408, - "variable `{}` from pattern #1 is not bound in pattern #{}", - variable_name, - pattern_number); + struct_span_err!(resolver.session, + span, + E0408, + "variable `{}` from pattern #1 is not bound in pattern #{}", + variable_name, + pattern_number) } ResolutionError::VariableBoundWithDifferentMode(variable_name, pattern_number) => { - span_err!(resolver.session, - span, - E0409, - "variable `{}` is bound with different mode in pattern #{} than in pattern \ - #1", - variable_name, - pattern_number); + struct_span_err!(resolver.session, + span, + E0409, + "variable `{}` is bound with different mode in pattern #{} than in \ + pattern #1", + variable_name, + pattern_number) } ResolutionError::VariableNotBoundInParentPattern(variable_name, pattern_number) => { - span_err!(resolver.session, - span, - E0410, - "variable `{}` from pattern #{} is not bound in pattern #1", - variable_name, - pattern_number); + struct_span_err!(resolver.session, + span, + E0410, + "variable `{}` from pattern #{} is not bound in pattern #1", + variable_name, + pattern_number) } ResolutionError::SelfUsedOutsideImplOrTrait => { - span_err!(resolver.session, - span, - E0411, - "use of `Self` outside of an impl or trait"); + struct_span_err!(resolver.session, + span, + E0411, + "use of `Self` outside of an impl or trait") } ResolutionError::UseOfUndeclared(kind, name) => { - span_err!(resolver.session, - span, - E0412, - "use of undeclared {} `{}`", - kind, - name); + struct_span_err!(resolver.session, + span, + E0412, + "use of undeclared {} `{}`", + kind, + name) } ResolutionError::DeclarationShadowsEnumVariantOrUnitLikeStruct(name) => { - span_err!(resolver.session, - span, - E0413, - "declaration of `{}` shadows an enum variant or unit-like struct in scope", - name); + struct_span_err!(resolver.session, + span, + E0413, + "declaration of `{}` shadows an enum variant \ + or unit-like struct in scope", + name) } ResolutionError::OnlyIrrefutablePatternsAllowedHere(did, name) => { - span_err!(resolver.session, - span, - E0414, - "only irrefutable patterns allowed here"); - resolver.session.span_note(span, - "there already is a constant in scope sharing the same \ - name as this pattern"); + let mut err = struct_span_err!(resolver.session, + span, + E0414, + "only irrefutable patterns allowed here"); + err.span_note(span, + "there already is a constant in scope sharing the same \ + name as this pattern"); if let Some(sp) = resolver.ast_map.span_if_local(did) { - resolver.session.span_note(sp, "constant defined here"); + err.span_note(sp, "constant defined here"); } if let Some(directive) = resolver.current_module .import_resolutions .borrow() .get(&name) { let item = resolver.ast_map.expect_item(directive.value_ns.id); - resolver.session.span_note(item.span, "constant imported here"); + err.span_note(item.span, "constant imported here"); } + err } ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { - span_err!(resolver.session, - span, - E0415, - "identifier `{}` is bound more than once in this parameter list", - identifier); + struct_span_err!(resolver.session, + span, + E0415, + "identifier `{}` is bound more than once in this parameter list", + identifier) } ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { - span_err!(resolver.session, - span, - E0416, - "identifier `{}` is bound more than once in the same pattern", - identifier); + struct_span_err!(resolver.session, + span, + E0416, + "identifier `{}` is bound more than once in the same pattern", + identifier) } ResolutionError::StaticVariableReference => { - span_err!(resolver.session, - span, - E0417, - "static variables cannot be referenced in a pattern, use a `const` instead"); + struct_span_err!(resolver.session, + span, + E0417, + "static variables cannot be referenced in a pattern, use a \ + `const` instead") } ResolutionError::NotAnEnumVariantStructOrConst(name) => { - span_err!(resolver.session, - span, - E0418, - "`{}` is not an enum variant, struct or const", - name); + struct_span_err!(resolver.session, + span, + E0418, + "`{}` is not an enum variant, struct or const", + name) } ResolutionError::UnresolvedEnumVariantStructOrConst(name) => { - span_err!(resolver.session, - span, - E0419, - "unresolved enum variant, struct or const `{}`", - name); + struct_span_err!(resolver.session, + span, + E0419, + "unresolved enum variant, struct or const `{}`", + name) } ResolutionError::NotAnAssociatedConst(name) => { - span_err!(resolver.session, - span, - E0420, - "`{}` is not an associated const", - name); + struct_span_err!(resolver.session, + span, + E0420, + "`{}` is not an associated const", + name) } ResolutionError::UnresolvedAssociatedConst(name) => { - span_err!(resolver.session, - span, - E0421, - "unresolved associated const `{}`", - name); + struct_span_err!(resolver.session, + span, + E0421, + "unresolved associated const `{}`", + name) } ResolutionError::DoesNotNameAStruct(name) => { - span_err!(resolver.session, - span, - E0422, - "`{}` does not name a structure", - name); + struct_span_err!(resolver.session, + span, + E0422, + "`{}` does not name a structure", + name) } ResolutionError::StructVariantUsedAsFunction(path_name) => { - span_err!(resolver.session, - span, - E0423, - "`{}` is the name of a struct or struct variant, but this expression uses \ - it like a function name", - path_name); + struct_span_err!(resolver.session, + span, + E0423, + "`{}` is the name of a struct or struct variant, but this expression \ + uses it like a function name", + path_name) } ResolutionError::SelfNotAvailableInStaticMethod => { - span_err!(resolver.session, - span, - E0424, - "`self` is not available in a static method. Maybe a `self` argument is \ - missing?"); + struct_span_err!(resolver.session, + span, + E0424, + "`self` is not available in a static method. Maybe a `self` \ + argument is missing?") } ResolutionError::UnresolvedName(path, msg, context) => { - span_err!(resolver.session, - span, - E0425, - "unresolved name `{}`{}", - path, - msg); + let mut err = struct_span_err!(resolver.session, + span, + E0425, + "unresolved name `{}`{}", + path, + msg); match context { UnresolvedNameContext::Other => { } // no help available @@ -455,75 +465,77 @@ fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>, } if !help_msg.is_empty() { - resolver.session.fileline_help(span, &help_msg); + err.fileline_help(span, &help_msg); } } } + err } ResolutionError::UndeclaredLabel(name) => { - span_err!(resolver.session, - span, - E0426, - "use of undeclared label `{}`", - name); + struct_span_err!(resolver.session, + span, + E0426, + "use of undeclared label `{}`", + name) } ResolutionError::CannotUseRefBindingModeWith(descr) => { - span_err!(resolver.session, - span, - E0427, - "cannot use `ref` binding mode with {}", - descr); + struct_span_err!(resolver.session, + span, + E0427, + "cannot use `ref` binding mode with {}", + descr) } ResolutionError::DuplicateDefinition(namespace, name) => { - span_err!(resolver.session, - span, - E0428, - "duplicate definition of {} `{}`", - namespace, - name); + struct_span_err!(resolver.session, + span, + E0428, + "duplicate definition of {} `{}`", + namespace, + name) } ResolutionError::SelfImportsOnlyAllowedWithin => { - span_err!(resolver.session, - span, - E0429, - "{}", - "`self` imports are only allowed within a { } list"); + struct_span_err!(resolver.session, + span, + E0429, + "{}", + "`self` imports are only allowed within a { } list") } ResolutionError::SelfImportCanOnlyAppearOnceInTheList => { - span_err!(resolver.session, - span, - E0430, - "`self` import can only appear once in the list"); + struct_span_err!(resolver.session, + span, + E0430, + "`self` import can only appear once in the list") } ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => { - span_err!(resolver.session, - span, - E0431, - "`self` import can only appear in an import list with a non-empty prefix"); + struct_span_err!(resolver.session, + span, + E0431, + "`self` import can only appear in an import list with a \ + non-empty prefix") } ResolutionError::UnresolvedImport(name) => { let msg = match name { Some((n, p)) => format!("unresolved import `{}`{}", n, p), None => "unresolved import".to_owned(), }; - span_err!(resolver.session, span, E0432, "{}", msg); + struct_span_err!(resolver.session, span, E0432, "{}", msg) } ResolutionError::FailedToResolve(msg) => { - span_err!(resolver.session, span, E0433, "failed to resolve. {}", msg); + struct_span_err!(resolver.session, span, E0433, "failed to resolve. {}", msg) } ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { - span_err!(resolver.session, - span, - E0434, - "{}", - "can't capture dynamic environment in a fn item; use the || { ... } \ - closure form instead"); + struct_span_err!(resolver.session, + span, + E0434, + "{}", + "can't capture dynamic environment in a fn item; use the || { ... } \ + closure form instead") } ResolutionError::AttemptToUseNonConstantValueInConstant => { - span_err!(resolver.session, - span, - E0435, - "attempt to use a non-constant value in a constant"); + struct_span_err!(resolver.session, + span, + E0435, + "attempt to use a non-constant value in a constant") } } } @@ -2187,16 +2199,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { debug!("(resolving trait) found trait def: {:?}", path_res); Ok(path_res) } else { - resolve_error(self, - trait_path.span, - ResolutionError::IsNotATrait(&*path_names_to_string(trait_path, - path_depth))); + let mut err = + resolve_struct_error(self, + trait_path.span, + ResolutionError::IsNotATrait(&*path_names_to_string(trait_path, + path_depth))); // If it's a typedef, give a note if let DefTy(..) = path_res.base_def { - self.session - .span_note(trait_path.span, "`type` aliases cannot be used for traits"); + err.span_note(trait_path.span, + "`type` aliases cannot be used for traits"); } + err.emit(); Err(()) } } else { @@ -3427,39 +3441,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { NoSuggestion } - fn find_best_match_for_name(&mut self, name: &str) -> SuggestionType { - let mut maybes: Vec = Vec::new(); - let mut values: Vec = Vec::new(); - + fn find_best_match(&mut self, name: &str) -> SuggestionType { if let Some(macro_name) = self.session.available_macros - .borrow().iter().find(|n| n.as_str() == name) { + .borrow().iter().find(|n| n.as_str() == name) { return SuggestionType::Macro(format!("{}!", macro_name)); } - for rib in self.value_ribs.iter().rev() { - for (&k, _) in &rib.bindings { - maybes.push(k.as_str()); - values.push(usize::MAX); - } - } - - let mut smallest = 0; - for (i, other) in maybes.iter().enumerate() { - values[i] = lev_distance(name, &other); + let names = self.value_ribs + .iter() + .rev() + .flat_map(|rib| rib.bindings.keys()); - if values[i] <= values[smallest] { - smallest = i; + if let Some(found) = find_best_match_for_name(names, name, None) { + if name != &*found { + return SuggestionType::Function(found); } - } - - let max_distance = max_suggestion_distance(name); - if !values.is_empty() && values[smallest] <= max_distance && name != &maybes[smallest][..] { - - SuggestionType::Function(maybes[smallest].to_string()) - - } else { - SuggestionType::NotFound - } + } SuggestionType::NotFound } fn resolve_expr(&mut self, expr: &Expr) { @@ -3494,17 +3491,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if let DefVariant(_, _, true) = path_res.base_def { let path_name = path_names_to_string(path, 0); - resolve_error(self, - expr.span, - ResolutionError::StructVariantUsedAsFunction(&*path_name)); + let mut err = resolve_struct_error(self, + expr.span, + ResolutionError::StructVariantUsedAsFunction(&*path_name)); let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?", path_name); if self.emit_errors { - self.session.fileline_help(expr.span, &msg); + err.fileline_help(expr.span, &msg); } else { - self.session.span_help(expr.span, &msg); + err.span_help(expr.span, &msg); } + err.emit(); self.record_def(expr.id, err_path_resolution()); } else { // Write the result into the def map. @@ -3534,20 +3532,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.record_def(expr.id, err_path_resolution()); match type_res.map(|r| r.base_def) { Some(DefTy(struct_id, _)) if self.structs.contains_key(&struct_id) => { - resolve_error( - self, - expr.span, - ResolutionError::StructVariantUsedAsFunction( - &*path_name) - ); + let mut err = resolve_struct_error(self, + expr.span, + ResolutionError::StructVariantUsedAsFunction(&*path_name)); let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?", path_name); if self.emit_errors { - self.session.fileline_help(expr.span, &msg); + err.fileline_help(expr.span, &msg); } else { - self.session.span_help(expr.span, &msg); + err.span_help(expr.span, &msg); } + err.emit(); } _ => { // Keep reporting some errors even if they're ignored above. @@ -3573,7 +3569,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { NoSuggestion => { // limit search to 5 to reduce the number // of stupid suggestions - match self.find_best_match_for_name(&path_name) { + match self.find_best_match(&path_name) { SuggestionType::Macro(s) => { format!("the macro `{}`", s) } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 69d5621ce73d9..40bf55efde645 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -33,11 +33,11 @@ use rustc::middle::privacy::*; use syntax::ast::{NodeId, Name}; use syntax::attr::AttrMetaMethods; use syntax::codemap::Span; +use syntax::util::lev_distance::find_best_match_for_name; use std::mem::replace; use std::rc::Rc; - /// Contains data for specific types of import directives. #[derive(Copy, Clone,Debug)] pub enum ImportDirectiveSubclass { @@ -425,17 +425,22 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { }; // We need to resolve both namespaces for this to succeed. - // let mut value_result = UnknownResult; let mut type_result = UnknownResult; + let mut lev_suggestion = "".to_owned(); // Search for direct children of the containing module. build_reduced_graph::populate_module_if_necessary(self.resolver, &target_module); match target_module.children.borrow().get(&source) { None => { - // Continue. + let names = target_module.children.borrow(); + if let Some(name) = find_best_match_for_name(names.keys(), + &source.as_str(), + None) { + lev_suggestion = format!(". Did you mean to use `{}`?", name); + } } Some(ref child_name_bindings) => { // pub_err makes sure we don't give the same error twice. @@ -449,8 +454,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { let note_msg = format!("Consider marking `{}` as `pub` in the imported \ module", source); - span_err!(self.resolver.session, directive.span, E0364, "{}", &msg); - self.resolver.session.span_note(directive.span, ¬e_msg); + struct_span_err!(self.resolver.session, directive.span, E0364, "{}", &msg) + .span_note(directive.span, ¬e_msg) + .emit(); pub_err = true; } if directive.is_public && child_name_bindings.value_ns. @@ -474,8 +480,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { let msg = format!("`{}` is private, and cannot be reexported", source); let note_msg = format!("Consider declaring module `{}` as a `pub mod`", source); - span_err!(self.resolver.session, directive.span, E0365, "{}", &msg); - self.resolver.session.span_note(directive.span, ¬e_msg); + struct_span_err!(self.resolver.session, directive.span, E0365, "{}", &msg) + .span_note(directive.span, ¬e_msg) + .emit(); } if !pub_err && directive.is_public && child_name_bindings.type_ns. defined_with(DefModifiers::PRIVATE_VARIANT) { @@ -516,6 +523,17 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { // therefore accurately report that the names are // unbound. + if lev_suggestion.is_empty() { // skip if we already have a suggestion + let names = target_module.import_resolutions.borrow(); + if let Some(name) = find_best_match_for_name(names.keys(), + &source.as_str(), + None) { + lev_suggestion = + format!(". Did you mean to use the re-exported import `{}`?", + name); + } + } + if value_result.is_unknown() { value_result = UnboundResult; } @@ -693,9 +711,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { target); if value_result.is_unbound() && type_result.is_unbound() { - let msg = format!("There is no `{}` in `{}`", + let msg = format!("There is no `{}` in `{}`{}", source, - module_to_string(&target_module)); + module_to_string(&target_module), lev_suggestion); return ResolveResult::Failed(Some((directive.span, msg))); } let value_used_public = value_used_reexport || value_used_public; @@ -943,19 +961,20 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { } ValueNS => "value", }; - span_err!(self.resolver.session, - import_span, - E0252, - "a {} named `{}` has already been imported in this module", - ns_word, - name); let use_id = import_resolution[namespace].id; let item = self.resolver.ast_map.expect_item(use_id); - // item is syntax::ast::Item; - span_note!(self.resolver.session, + let mut err = struct_span_err!(self.resolver.session, + import_span, + E0252, + "a {} named `{}` has already been imported \ + in this module", + ns_word, + name); + span_note!(&mut err, item.span, "previous import of `{}` here", name); + err.emit(); } Some(_) | None => {} } @@ -1006,14 +1025,16 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { match import.value_ns.target { Some(ref target) if target.shadowable != Shadowable::Always => { if let Some(ref value) = *name_bindings.value_ns.borrow() { - span_err!(self.resolver.session, - import_span, - E0255, - "import `{}` conflicts with value in this module", - name); + let mut err = struct_span_err!(self.resolver.session, + import_span, + E0255, + "import `{}` conflicts with \ + value in this module", + name); if let Some(span) = value.span { - self.resolver.session.span_note(span, "conflicting value here"); + err.span_note(span, "conflicting value here"); } + err.emit(); } } Some(_) | None => {} @@ -1029,15 +1050,16 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { ("trait in this module", "note conflicting trait here"), _ => ("type in this module", "note conflicting type here"), }; - span_err!(self.resolver.session, - import_span, - E0256, - "import `{}` conflicts with {}", - name, - what); + let mut err = struct_span_err!(self.resolver.session, + import_span, + E0256, + "import `{}` conflicts with {}", + name, + what); if let Some(span) = ty.span { - self.resolver.session.span_note(span, note); + err.span_note(span, note); } + err.emit(); } } Some(_) | None => {} diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs index f5431554a7564..850608588234c 100644 --- a/src/librustc_trans/back/archive.rs +++ b/src/librustc_trans/back/archive.rs @@ -406,11 +406,12 @@ impl<'a> ArchiveBuilder<'a> { Ok(prog) => { let o = prog.wait_with_output().unwrap(); if !o.status.success() { - sess.err(&format!("{:?} failed with: {}", cmd, o.status)); - sess.note(&format!("stdout ---\n{}", - str::from_utf8(&o.stdout).unwrap())); - sess.note(&format!("stderr ---\n{}", - str::from_utf8(&o.stderr).unwrap())); + sess.struct_err(&format!("{:?} failed with: {}", cmd, o.status)) + .note(&format!("stdout ---\n{}", + str::from_utf8(&o.stdout).unwrap())) + .note(&format!("stderr ---\n{}", + str::from_utf8(&o.stderr).unwrap())) + .emit(); sess.abort_if_errors(); } o diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index fb79f804932f8..e1edbf4a1276d 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -182,8 +182,10 @@ pub fn find_crate_name(sess: Option<&Session>, "rust_out".to_string() } -pub fn build_link_meta(sess: &Session, krate: &hir::Crate, - name: &str) -> LinkMeta { +pub fn build_link_meta(sess: &Session, + krate: &hir::Crate, + name: &str) + -> LinkMeta { let r = LinkMeta { crate_name: name.to_owned(), crate_hash: Svh::calculate(&sess.opts.cg.metadata, krate), @@ -817,10 +819,10 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path, ab.build(); if !all_native_libs.is_empty() { - sess.note("link against the following native artifacts when linking against \ - this static library"); - sess.note("the order and any duplication can be significant on some platforms, \ - and so may need to be preserved"); + sess.note_without_error("link against the following native artifacts when linking against \ + this static library"); + sess.note_without_error("the order and any duplication can be significant on some \ + platforms, and so may need to be preserved"); } for &(kind, ref lib) in &all_native_libs { @@ -829,7 +831,7 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path, NativeLibraryKind::NativeUnknown => "library", NativeLibraryKind::NativeFramework => "framework", }; - sess.note(&format!("{}: {}", name, *lib)); + sess.note_without_error(&format!("{}: {}", name, *lib)); } } @@ -902,13 +904,14 @@ fn link_natively(sess: &Session, dylib: bool, }) } if !prog.status.success() { - sess.err(&format!("linking with `{}` failed: {}", - pname, - prog.status)); - sess.note(&format!("{:?}", &cmd)); let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); - sess.note(&*escape_string(&output[..])); + sess.struct_err(&format!("linking with `{}` failed: {}", + pname, + prog.status)) + .note(&format!("{:?}", &cmd)) + .note(&*escape_string(&output[..])) + .emit(); sess.abort_if_errors(); } info!("linker stderr:\n{}", escape_string(&prog.stderr[..])); @@ -1060,6 +1063,10 @@ fn link_args(cmd: &mut Linker, cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); } + // Use the gold linker if possible instead of ld. It is much + // faster. + cmd.try_gold_linker(); + // Finally add all the linker arguments provided on the command line along // with any #[link_args] attributes found inside the crate if let Some(ref args) = sess.opts.cg.link_args { diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 1ee1c9f1912c7..90ebf364367a0 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::env; use std::ffi::OsString; use std::fs::{self, File}; use std::io::{self, BufWriter}; @@ -56,6 +57,7 @@ pub trait Linker { fn no_whole_archives(&mut self); fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation, tmpdir: &Path); + fn try_gold_linker(&mut self); } pub struct GnuLinker<'a> { @@ -199,6 +201,53 @@ impl<'a> Linker for GnuLinker<'a> { fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) { // noop, visibility in object files takes care of this } + + fn try_gold_linker(&mut self) { + // Only use gold under specific conditions that we know work + + let gold_exists = match env::var_os("PATH") { + Some(ref env_path) => { + env::split_paths(env_path).any(|mut p| { + p.push("ld.gold"); + p.exists() + }) + } + None => false + }; + let host_is_linux = cfg!(target_os = "linux"); + // Defensively prevent trying to use gold for bogus cross-targets. + let target_is_host_compatible = { + let host_os_is_target_os = self.sess.target.target.target_os == env::consts::OS; + let host_arch_is_target_arch = self.sess.target.target.arch == env::consts::ARCH; + // Support x86_64->i686 and reverse + let host_and_target_are_x86ish = + (self.sess.target.target.arch == "x86" || + self.sess.target.target.arch == "x86_64") && + (env::consts::ARCH == "x86" || + env::consts::ARCH == "x86_64"); + host_os_is_target_os && (host_arch_is_target_arch || host_and_target_are_x86ish) + }; + // We have strong confidence that x86 works, but not much + // visibility into other architectures. + let target_works_with_gold = + self.sess.target.target.arch == "x86" || + self.sess.target.target.arch == "x86_64"; + let opt_out = self.sess.opts.cg.disable_gold; + + let can_use_gold = + gold_exists && + host_is_linux && + target_is_host_compatible && + target_works_with_gold && + !opt_out; + + if can_use_gold { + info!("linking with ld.gold"); + self.cmd.arg("-fuse-ld=gold"); + } else { + info!("linking with ld"); + } + } } pub struct MsvcLinker<'a> { @@ -358,4 +407,6 @@ impl<'a> Linker for MsvcLinker<'a> { arg.push(path); self.cmd.arg(&arg); } + + fn try_gold_linker(&mut self) {} } diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index 8505c3968ee15..85419a072503a 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -29,8 +29,9 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, name_extra: &str, output_names: &config::OutputFilenames) { if sess.opts.cg.prefer_dynamic { - sess.err("cannot prefer dynamic linking when performing LTO"); - sess.note("only 'staticlib' and 'bin' outputs are supported with LTO"); + sess.struct_err("cannot prefer dynamic linking when performing LTO") + .note("only 'staticlib' and 'bin' outputs are supported with LTO") + .emit(); sess.abort_if_errors(); } diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 67eff1ca19fbc..9d0a83fe36350 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -359,8 +359,9 @@ unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>, } None => { - cgcx.handler.err(msg); - cgcx.handler.note("build without -C codegen-units for more exact errors"); + cgcx.handler.struct_err(msg) + .note("build without -C codegen-units for more exact errors") + .emit(); } } } @@ -397,11 +398,11 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo if enabled { let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc); - cgcx.handler.note(&format!("optimization {} for {} at {}: {}", - opt.kind.describe(), - pass_name, - if loc.is_empty() { "[unknown]" } else { &*loc }, - llvm::twine_to_string(opt.message))); + cgcx.handler.note_without_error(&format!("optimization {} for {} at {}: {}", + opt.kind.describe(), + pass_name, + if loc.is_empty() { "[unknown]" } else { &*loc }, + llvm::twine_to_string(opt.message))); } } @@ -931,13 +932,15 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { match cmd.output() { Ok(prog) => { if !prog.status.success() { - sess.err(&format!("linking with `{}` failed: {}", - pname, - prog.status)); - sess.note(&format!("{:?}", &cmd)); let mut note = prog.stderr.clone(); note.extend_from_slice(&prog.stdout); - sess.note(str::from_utf8(¬e[..]).unwrap()); + + sess.struct_err(&format!("linking with `{}` failed: {}", + pname, + prog.status)) + .note(&format!("{:?}", &cmd)) + .note(str::from_utf8(¬e[..]).unwrap()) + .emit(); sess.abort_if_errors(); } }, diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index a17e0a4ccd78d..0892cf1b5d33e 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -27,6 +27,7 @@ #![feature(const_fn)] #![feature(custom_attribute)] #![allow(unused_attributes)] +#![feature(into_cow)] #![feature(iter_arith)] #![feature(libc)] #![feature(path_relative_from)] diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index 9c6b54e13796d..c34013a7bbbb1 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -682,7 +682,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { def::DefMethod(did) => { let ti = self.tcx.impl_or_trait_item(did); if let ty::MethodTraitItem(m) = ti { - if m.explicit_self == ty::StaticExplicitSelfCategory { + if m.explicit_self == ty::ExplicitSelfCategory::Static { self.write_sub_path_trait_truncated(path); } } diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 501ab566f1c5a..e1343c73acfa9 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -716,6 +716,8 @@ pub fn process_crate<'l, 'tcx>(tcx: &'l ty::ctxt<'tcx>, analysis: &ty::CrateAnalysis, cratename: &str, odir: Option<&Path>) { + let _ignore = tcx.dep_graph.in_ignore(); + if generated_code(krate.span) { return; } diff --git a/src/librustc_trans/trans/assert_dep_graph.rs b/src/librustc_trans/trans/assert_dep_graph.rs new file mode 100644 index 0000000000000..924700f0ae591 --- /dev/null +++ b/src/librustc_trans/trans/assert_dep_graph.rs @@ -0,0 +1,430 @@ +// 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. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This pass is only used for the UNIT TESTS and DEBUGGING NEEDS +//! around dependency graph construction. It serves two purposes; it +//! will dump graphs in graphviz form to disk, and it searches for +//! `#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]` +//! annotations. These annotations can be used to test whether paths +//! exist in the graph. We report errors on each +//! `rustc_if_this_changed` annotation. If a path exists in all +//! cases, then we would report "all path(s) exist". Otherwise, we +//! report: "no path to `foo`" for each case where no path exists. +//! `compile-fail` tests can then be used to check when paths exist or +//! do not. +//! +//! The full form of the `rustc_if_this_changed` annotation is +//! `#[rustc_if_this_changed(id)]`. The `"id"` is optional and +//! defaults to `"id"` if omitted. +//! +//! Example: +//! +//! ``` +//! #[rustc_if_this_changed] +//! fn foo() { } +//! +//! #[rustc_then_this_would_need("trans")] //~ ERROR no path from `foo` +//! fn bar() { } +//! +//! #[rustc_then_this_would_need("trans")] //~ ERROR OK +//! fn baz() { foo(); } +//! ``` + +use graphviz as dot; +use rustc::dep_graph::{DepGraphQuery, DepNode}; +use rustc::middle::def_id::DefId; +use rustc::middle::ty; +use rustc_data_structures::fnv::{FnvHashMap, FnvHashSet}; +use rustc_data_structures::graph::{Direction, INCOMING, OUTGOING, NodeIndex}; +use rustc_front::hir; +use rustc_front::intravisit::Visitor; +use std::borrow::IntoCow; +use std::env; +use std::fs::File; +use std::io::Write; +use syntax::ast; +use syntax::attr::AttrMetaMethods; +use syntax::codemap::Span; +use syntax::parse::token::InternedString; + +const IF_THIS_CHANGED: &'static str = "rustc_if_this_changed"; +const THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need"; +const ID: &'static str = "id"; + +pub fn assert_dep_graph(tcx: &ty::ctxt) { + let _ignore = tcx.dep_graph.in_ignore(); + + if tcx.sess.opts.dump_dep_graph { + dump_graph(tcx); + } + + // Find annotations supplied by user (if any). + let (if_this_changed, then_this_would_need) = { + let mut visitor = IfThisChanged { tcx: tcx, + if_this_changed: FnvHashMap(), + then_this_would_need: FnvHashMap() }; + tcx.map.krate().visit_all_items(&mut visitor); + (visitor.if_this_changed, visitor.then_this_would_need) + }; + + // Check paths. + check_paths(tcx, &if_this_changed, &then_this_would_need); +} + +type SourceHashMap = FnvHashMap>; +type TargetHashMap = FnvHashMap>; + +struct IfThisChanged<'a, 'tcx:'a> { + tcx: &'a ty::ctxt<'tcx>, + if_this_changed: SourceHashMap, + then_this_would_need: TargetHashMap, +} + +impl<'a, 'tcx> IfThisChanged<'a, 'tcx> { + fn process_attrs(&mut self, node_id: ast::NodeId, def_id: DefId) { + for attr in self.tcx.get_attrs(def_id).iter() { + if attr.check_name(IF_THIS_CHANGED) { + let mut id = None; + for meta_item in attr.meta_item_list().unwrap_or_default() { + match meta_item.node { + ast::MetaWord(ref s) if id.is_none() => id = Some(s.clone()), + _ => { + self.tcx.sess.span_err( + meta_item.span, + &format!("unexpected meta-item {:?}", meta_item.node)); + } + } + } + let id = id.unwrap_or(InternedString::new(ID)); + self.if_this_changed.entry(id) + .or_insert(FnvHashSet()) + .insert((attr.span, def_id, DepNode::Hir(def_id))); + } else if attr.check_name(THEN_THIS_WOULD_NEED) { + let mut dep_node_interned = None; + let mut id = None; + for meta_item in attr.meta_item_list().unwrap_or_default() { + match meta_item.node { + ast::MetaWord(ref s) if dep_node_interned.is_none() => + dep_node_interned = Some(s.clone()), + ast::MetaWord(ref s) if id.is_none() => + id = Some(s.clone()), + _ => { + self.tcx.sess.span_err( + meta_item.span, + &format!("unexpected meta-item {:?}", meta_item.node)); + } + } + } + let dep_node_str = dep_node_interned.as_ref().map(|s| &**s); + macro_rules! match_depnode_name { + ($input:expr, $def_id:expr, match { $($variant:ident,)* } else $y:expr) => { + match $input { + $(Some(stringify!($variant)) => DepNode::$variant($def_id),)* + _ => $y + } + } + } + let dep_node = match_depnode_name! { + dep_node_str, def_id, match { + CollectItem, + BorrowCheck, + TransCrateItem, + TypeckItemType, + TypeckItemBody, + ImplOrTraitItems, + ItemSignature, + FieldTy, + TraitItemDefIds, + InherentImpls, + ImplItems, + TraitImpls, + ReprHints, + } else { + self.tcx.sess.span_fatal( + attr.span, + &format!("unrecognized DepNode variant {:?}", dep_node_str)); + } + }; + let id = id.unwrap_or(InternedString::new(ID)); + self.then_this_would_need + .entry(id) + .or_insert(FnvHashSet()) + .insert((attr.span, dep_node_interned.clone().unwrap(), node_id, dep_node)); + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for IfThisChanged<'a, 'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item) { + let def_id = self.tcx.map.local_def_id(item.id); + self.process_attrs(item.id, def_id); + } +} + +fn check_paths(tcx: &ty::ctxt, + if_this_changed: &SourceHashMap, + then_this_would_need: &TargetHashMap) +{ + // Return early here so as not to construct the query, which is not cheap. + if if_this_changed.is_empty() { + return; + } + let query = tcx.dep_graph.query(); + for (id, sources) in if_this_changed { + let targets = match then_this_would_need.get(id) { + Some(targets) => targets, + None => { + for &(source_span, _, _) in sources.iter().take(1) { + tcx.sess.span_err( + source_span, + &format!("no targets for id `{}`", id)); + } + continue; + } + }; + + for &(_, source_def_id, source_dep_node) in sources { + let dependents = query.dependents(source_dep_node); + for &(target_span, ref target_pass, _, ref target_dep_node) in targets { + if !dependents.contains(&target_dep_node) { + tcx.sess.span_err( + target_span, + &format!("no path from `{}` to `{}`", + tcx.item_path_str(source_def_id), + target_pass)); + } else { + tcx.sess.span_err( + target_span, + &format!("OK")); + } + } + } + } +} + +fn dump_graph(tcx: &ty::ctxt) { + let path: String = env::var("RUST_DEP_GRAPH").unwrap_or_else(|_| format!("dep_graph")); + let query = tcx.dep_graph.query(); + + let nodes = match env::var("RUST_DEP_GRAPH_FILTER") { + Ok(string) => { + // Expect one of: "-> target", "source -> target", or "source ->". + let parts: Vec<_> = string.split("->").collect(); + if parts.len() > 2 { + panic!("Invalid RUST_DEP_GRAPH_FILTER: expected '[source] -> [target]'"); + } + let sources = node_set(&query, &parts[0]); + let targets = node_set(&query, &parts[1]); + filter_nodes(&query, &sources, &targets) + } + Err(_) => { + query.nodes() + .into_iter() + .collect() + } + }; + let edges = filter_edges(&query, &nodes); + + { // dump a .txt file with just the edges: + let txt_path = format!("{}.txt", path); + let mut file = File::create(&txt_path).unwrap(); + for &(source, target) in &edges { + write!(file, "{:?} -> {:?}\n", source, target).unwrap(); + } + } + + { // dump a .dot file in graphviz format: + let dot_path = format!("{}.dot", path); + let mut v = Vec::new(); + dot::render(&GraphvizDepGraph(nodes, edges), &mut v).unwrap(); + File::create(&dot_path).and_then(|mut f| f.write_all(&v)).unwrap(); + } +} + +pub struct GraphvizDepGraph(FnvHashSet, Vec<(DepNode, DepNode)>); + +impl<'a, 'tcx> dot::GraphWalk<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph { + fn nodes(&self) -> dot::Nodes { + let nodes: Vec<_> = self.0.iter().cloned().collect(); + nodes.into_cow() + } + fn edges(&self) -> dot::Edges<(DepNode, DepNode)> { + self.1[..].into_cow() + } + fn source(&self, edge: &(DepNode, DepNode)) -> DepNode { + edge.0 + } + fn target(&self, edge: &(DepNode, DepNode)) -> DepNode { + edge.1 + } +} + +impl<'a, 'tcx> dot::Labeller<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph { + fn graph_id(&self) -> dot::Id { + dot::Id::new("DependencyGraph").unwrap() + } + fn node_id(&self, n: &DepNode) -> dot::Id { + let s: String = + format!("{:?}", n).chars() + .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' }) + .collect(); + debug!("n={:?} s={:?}", n, s); + dot::Id::new(s).unwrap() + } + fn node_label(&self, n: &DepNode) -> dot::LabelText { + dot::LabelText::label(format!("{:?}", n)) + } +} + +// Given an optional filter like `"x,y,z"`, returns either `None` (no +// filter) or the set of nodes whose labels contain all of those +// substrings. +fn node_set(query: &DepGraphQuery, filter: &str) -> Option> { + debug!("node_set(filter={:?})", filter); + + if filter.trim().is_empty() { + return None; + } + + let filters: Vec<&str> = filter.split("&").map(|s| s.trim()).collect(); + + debug!("node_set: filters={:?}", filters); + + Some(query.nodes() + .into_iter() + .filter(|n| { + let s = format!("{:?}", n); + filters.iter().all(|f| s.contains(f)) + }) + .collect()) +} + +fn filter_nodes(query: &DepGraphQuery, + sources: &Option>, + targets: &Option>) + -> FnvHashSet +{ + if let &Some(ref sources) = sources { + if let &Some(ref targets) = targets { + walk_between(query, sources, targets) + } else { + walk_nodes(query, sources, OUTGOING) + } + } else if let &Some(ref targets) = targets { + walk_nodes(query, targets, INCOMING) + } else { + query.nodes().into_iter().collect() + } +} + +fn walk_nodes(query: &DepGraphQuery, + starts: &FnvHashSet, + direction: Direction) + -> FnvHashSet +{ + let mut set = FnvHashSet(); + for start in starts { + debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING); + if set.insert(*start) { + let mut stack = vec![query.indices[start]]; + while let Some(index) = stack.pop() { + for (_, edge) in query.graph.adjacent_edges(index, direction) { + let neighbor_index = edge.source_or_target(direction); + let neighbor = query.graph.node_data(neighbor_index); + if set.insert(*neighbor) { + stack.push(neighbor_index); + } + } + } + } + } + set +} + +fn walk_between(query: &DepGraphQuery, + sources: &FnvHashSet, + targets: &FnvHashSet) + -> FnvHashSet +{ + // This is a bit tricky. We want to include a node only if it is: + // (a) reachable from a source and (b) will reach a target. And we + // have to be careful about cycles etc. Luckily efficiency is not + // a big concern! + + #[derive(Copy, Clone, PartialEq)] + enum State { Undecided, Deciding, Included, Excluded } + + let mut node_states = vec![State::Undecided; query.graph.len_nodes()]; + + for &target in targets { + node_states[query.indices[&target].0] = State::Included; + } + + for source in sources.iter().map(|n| query.indices[n]) { + recurse(query, &mut node_states, source); + } + + return query.nodes() + .into_iter() + .filter(|n| { + let index = query.indices[n]; + node_states[index.0] == State::Included + }) + .collect(); + + fn recurse(query: &DepGraphQuery, + node_states: &mut [State], + node: NodeIndex) + -> bool + { + match node_states[node.0] { + // known to reach a target + State::Included => return true, + + // known not to reach a target + State::Excluded => return false, + + // backedge, not yet known, say false + State::Deciding => return false, + + State::Undecided => { } + } + + node_states[node.0] = State::Deciding; + + for neighbor_index in query.graph.successor_nodes(node) { + if recurse(query, node_states, neighbor_index) { + node_states[node.0] = State::Included; + } + } + + // if we didn't find a path to target, then set to excluded + if node_states[node.0] == State::Deciding { + node_states[node.0] = State::Excluded; + false + } else { + assert!(node_states[node.0] == State::Included); + true + } + } +} + +fn filter_edges(query: &DepGraphQuery, + nodes: &FnvHashSet) + -> Vec<(DepNode, DepNode)> +{ + query.edges() + .into_iter() + .filter(|&(source, target)| nodes.contains(&source) && nodes.contains(&target)) + .collect() +} diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 838a5435d4fee..d694374acc927 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -42,13 +42,16 @@ use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; use middle::weak_lang_items; use middle::pat_util::simple_name; use middle::subst::Substs; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, TypeFoldable}; +use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; +use rustc::util::common::time; use rustc_mir::mir_map::MirMap; use session::config::{self, NoDebugInfo, FullDebugInfo}; use session::Session; use trans::_match; use trans::adt; +use trans::assert_dep_graph; use trans::attributes; use trans::build::*; use trans::builder::{Builder, noname}; @@ -957,22 +960,28 @@ pub fn wants_msvc_seh(sess: &Session) -> bool { sess.target.target.options.is_like_msvc && sess.target.target.arch == "x86" } -pub fn need_invoke(bcx: Block) -> bool { +pub fn avoid_invoke(bcx: Block) -> bool { // FIXME(#25869) currently SEH-based unwinding is pretty buggy in LLVM and // is being overhauled as this is being written. Until that // time such that upstream LLVM's implementation is more solid // and we start binding it we need to skip invokes for any // target which wants SEH-based unwinding. if bcx.sess().no_landing_pads() || wants_msvc_seh(bcx.sess()) { - return false; + true + } else if bcx.is_lpad { + // Avoid using invoke if we are already inside a landing pad. + true + } else { + false } +} - // Avoid using invoke if we are already inside a landing pad. - if bcx.is_lpad { - return false; +pub fn need_invoke(bcx: Block) -> bool { + if avoid_invoke(bcx) { + false + } else { + bcx.fcx.needs_invoke() } - - bcx.fcx.needs_invoke() } pub fn load_if_immediate<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, t: Ty<'tcx>) -> ValueRef { @@ -2181,17 +2190,20 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &hir::EnumDef, sp: Span, } ); + // FIXME(#30505) Should use logging for this. if print_info { let llty = type_of::sizing_type_of(ccx, ty); let sess = &ccx.tcx().sess; - sess.span_note(sp, &*format!("total size: {} bytes", llsize_of_real(ccx, llty))); + sess.span_note_without_error(sp, + &*format!("total size: {} bytes", llsize_of_real(ccx, llty))); match *avar { adt::General(..) => { for (i, var) in enum_def.variants.iter().enumerate() { ccx.tcx() .sess - .span_note(var.span, &*format!("variant data: {} bytes", sizes[i])); + .span_note_without_error(var.span, + &*format!("variant data: {} bytes", sizes[i])); } } _ => {} @@ -2203,16 +2215,17 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &hir::EnumDef, sp: Span, if !is_allow && largest > slargest * 3 && slargest > 0 { // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing // pass for the latter already ran. - lint::raw_emit_lint(&ccx.tcx().sess, - lint::builtin::VARIANT_SIZE_DIFFERENCES, - *lvlsrc.unwrap(), - Some(sp), - &format!("enum variant is more than three times larger ({} bytes) \ - than the next largest (ignoring padding)", - largest)); - - ccx.sess().span_note(enum_def.variants[largest_index].span, - "this variant is the largest"); + lint::raw_struct_lint(&ccx.tcx().sess, + &ccx.tcx().sess.lint_store.borrow(), + lint::builtin::VARIANT_SIZE_DIFFERENCES, + *lvlsrc.unwrap(), + Some(sp), + &format!("enum variant is more than three times larger ({} bytes) \ + than the next largest (ignoring padding)", + largest)) + .span_note(enum_def.variants[largest_index].span, + "this variant is the largest") + .emit(); } } @@ -2489,9 +2502,10 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) { let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()], &ccx.int_type()); let llfn = declare::define_cfn(ccx, "main", llfty, ccx.tcx().mk_nil()).unwrap_or_else(|| { - ccx.sess().span_err(sp, "entry symbol `main` defined multiple times"); // FIXME: We should be smart and show a better diagnostic here. - ccx.sess().help("did you use #[no_mangle] on `fn main`? Use #[start] instead"); + ccx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times") + .help("did you use #[no_mangle] on `fn main`? Use #[start] instead") + .emit(); ccx.sess().abort_if_errors(); panic!(); }); @@ -2973,9 +2987,16 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx>, analysis: ty::CrateAnalysis) -> CrateTranslation { - let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis; + let _task = tcx.dep_graph.in_task(DepNode::TransCrate); + + // Be careful with this krate: obviously it gives access to the + // entire contents of the krate. So if you push any subtasks of + // `TransCrate`, you need to be careful to register "reads" of the + // particular items that will be processed. let krate = tcx.map.krate(); + let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis; + let check_overflow = if let Some(v) = tcx.sess.opts.debugging_opts.force_overflow_checks { v } else { @@ -3053,7 +3074,9 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, let reachable_symbol_ids = filter_reachable_ids(&shared_ccx); // Translate the metadata. - let metadata = write_metadata(&shared_ccx, krate, &reachable_symbol_ids, mir_map); + let metadata = time(tcx.sess.time_passes(), "write metadata", || { + write_metadata(&shared_ccx, krate, &reachable_symbol_ids, mir_map) + }); if shared_ccx.sess().trans_stats() { let stats = shared_ccx.stats(); @@ -3127,6 +3150,8 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, }; let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); + assert_dep_graph::assert_dep_graph(tcx); + CrateTranslation { modules: modules, metadata_module: metadata_module, @@ -3179,7 +3204,16 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TransItemsWithinModVisitor<'a, 'tcx> { // skip modules, they will be uncovered by the TransModVisitor } _ => { - trans_item(self.ccx, i); + let def_id = self.ccx.tcx().map.local_def_id(i.id); + let tcx = self.ccx.tcx(); + + // Create a subtask for trans'ing a particular item. We are + // giving `trans_item` access to this item, so also record a read. + tcx.dep_graph.with_task(DepNode::TransCrateItem(def_id), || { + tcx.dep_graph.read(DepNode::Hir(def_id)); + trans_item(self.ccx, i); + }); + intravisit::walk_item(self, i); } } diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index d33beab9313d0..e3b7502b69c0e 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -50,7 +50,7 @@ use trans::meth; use trans::monomorphize; use trans::type_::Type; use trans::type_of; -use middle::ty::{self, Ty, HasTypeFlags, RegionEscape}; +use middle::ty::{self, Ty, TypeFoldable}; use middle::ty::MethodCall; use rustc_front::hir; diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index e089a6e059ccd..ea9ca8f37b173 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -31,6 +31,7 @@ use session::config::FullDebugInfo; use syntax::abi::RustCall; use syntax::ast; +use syntax::attr::{ThinAttributes, ThinAttributesExt}; use rustc_front::hir; @@ -176,7 +177,8 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, body: &hir::Block, id: ast::NodeId, closure_def_id: DefId, // (*) - closure_substs: &'tcx ty::ClosureSubsts<'tcx>) + closure_substs: &'tcx ty::ClosureSubsts<'tcx>, + closure_expr_attrs: &ThinAttributes) -> Option> { // (*) Note that in the case of inlined functions, the `closure_def_id` will be the @@ -218,7 +220,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, llfn, param_substs, id, - &[], + closure_expr_attrs.as_attr_slice(), sig.output, function_type.abi, ClosureEnv::Closure(closure_def_id, &freevars)); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index fa500ab93552c..5046c2e29207c 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -37,7 +37,7 @@ use trans::monomorphize; use trans::type_::Type; use trans::type_of; use middle::traits; -use middle::ty::{self, HasTypeFlags, Ty}; +use middle::ty::{self, Ty}; use middle::ty::fold::{TypeFolder, TypeFoldable}; use rustc_front::hir; use rustc::mir::repr::Mir; @@ -73,45 +73,6 @@ pub fn type_is_fat_ptr<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { } } -/// If `type_needs_drop` returns true, then `ty` is definitely -/// non-copy and *might* have a destructor attached; if it returns -/// false, then `ty` definitely has no destructor (i.e. no drop glue). -/// -/// (Note that this implies that if `ty` has a destructor attached, -/// then `type_needs_drop` will definitely return `true` for `ty`.) -pub fn type_needs_drop<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { - type_needs_drop_given_env(cx, ty, &cx.empty_parameter_environment()) -} - -/// Core implementation of type_needs_drop, potentially making use of -/// and/or updating caches held in the `param_env`. -fn type_needs_drop_given_env<'a,'tcx>(cx: &ty::ctxt<'tcx>, - ty: Ty<'tcx>, - param_env: &ty::ParameterEnvironment<'a,'tcx>) -> bool { - // Issue #22536: We first query type_moves_by_default. It sees a - // normalized version of the type, and therefore will definitely - // know whether the type implements Copy (and thus needs no - // cleanup/drop/zeroing) ... - let implements_copy = !ty.moves_by_default(param_env, DUMMY_SP); - - if implements_copy { return false; } - - // ... (issue #22536 continued) but as an optimization, still use - // prior logic of asking if the `needs_drop` bit is set; we need - // not zero non-Copy types if they have no destructor. - - // FIXME(#22815): Note that calling `ty::type_contents` is a - // conservative heuristic; it may report that `needs_drop` is set - // when actual type does not actually have a destructor associated - // with it. But since `ty` absolutely did not have the `Copy` - // bound attached (see above), it is sound to treat it as having a - // destructor (e.g. zero its memory on move). - - let contents = ty.type_contents(cx); - debug!("type_needs_drop ty={:?} contents={:?}", ty, contents); - contents.needs_drop(cx) -} - fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.sty { ty::TyStruct(def, substs) => { @@ -508,7 +469,7 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { } pub fn monomorphize(&self, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { monomorphize::apply_param_substs(self.ccx.tcx(), self.param_substs, @@ -518,7 +479,7 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { /// This is the same as `common::type_needs_drop`, except that it /// may use or update caches within this `FunctionContext`. pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - type_needs_drop_given_env(self.ccx.tcx(), ty, &self.param_env) + self.ccx.tcx().type_needs_drop_given_env(ty, &self.param_env) } pub fn eh_personality(&self) -> ValueRef { @@ -689,7 +650,7 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> { } pub fn monomorphize(&self, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { monomorphize::apply_param_substs(self.tcx(), self.fcx.param_substs, diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 0fafe08178a27..3ea358a8f4cd3 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -31,7 +31,6 @@ use trans::{adt, closure, debuginfo, expr, inline, machine}; use trans::base::{self, push_ctxt}; use trans::common::{self, type_is_sized, ExprOrMethodCall, node_id_substs, C_nil, const_get_elt}; use trans::common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty}; -use trans::common::C_floating_f64; use trans::common::{C_struct, C_undef, const_to_opt_int, const_to_opt_uint, VariantInfo, C_uint}; use trans::common::{type_is_fat_ptr, Field, C_vector, C_array, C_null, ExprId, MethodCallKey}; use trans::declare; @@ -108,38 +107,6 @@ pub fn const_lit(cx: &CrateContext, e: &hir::Expr, lit: &ast::Lit) } } -pub fn trans_constval<'blk, 'tcx>(bcx: common::Block<'blk, 'tcx>, - cv: &ConstVal, - ty: Ty<'tcx>, - param_substs: &'tcx Substs<'tcx>) - -> ValueRef -{ - let ccx = bcx.ccx(); - let llty = type_of::type_of(ccx, ty); - match *cv { - ConstVal::Float(v) => C_floating_f64(v, llty), - ConstVal::Bool(v) => C_bool(ccx, v), - ConstVal::Int(v) => C_integral(llty, v as u64, true), - ConstVal::Uint(v) => C_integral(llty, v, false), - ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), - ConstVal::ByteStr(ref v) => addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"), - ConstVal::Struct(id) | ConstVal::Tuple(id) => { - let expr = bcx.tcx().map.expect_expr(id); - match const_expr(ccx, expr, param_substs, None, TrueConst::Yes) { - Ok((val, _)) => val, - Err(e) => panic!("const eval failure: {}", e.description()), - } - }, - ConstVal::Array(id, _) | ConstVal::Repeat(id, _) => { - let expr = bcx.tcx().map.expect_expr(id); - expr::trans(bcx, expr).datum.val - }, - ConstVal::Function(_) => { - unimplemented!() - }, - } -} - pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef { unsafe { llvm::LLVMConstPointerCast(val, ty.to_ref()) @@ -1019,8 +986,13 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, hir::ExprClosure(_, ref decl, ref body) => { match ety.sty { ty::TyClosure(def_id, ref substs) => { - closure::trans_closure_expr(closure::Dest::Ignore(cx), decl, - body, e.id, def_id, substs); + closure::trans_closure_expr(closure::Dest::Ignore(cx), + decl, + body, + e.id, + def_id, + substs, + &e.attrs); } _ => cx.sess().span_bug( diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index ecf54cde9f63a..fb6f2190207ee 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1196,7 +1196,13 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, &format!("closure expr without closure type: {:?}", t)), }; - closure::trans_closure_expr(dest, decl, body, expr.id, def_id, substs).unwrap_or(bcx) + closure::trans_closure_expr(dest, + decl, + body, + expr.id, + def_id, + substs, + &expr.attrs).unwrap_or(bcx) } hir::ExprCall(ref f, ref args) => { if bcx.tcx().is_method_call(expr.id) { @@ -2187,15 +2193,19 @@ fn auto_ref<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let referent_ty = lv_datum.ty; let ptr_ty = bcx.tcx().mk_imm_ref(bcx.tcx().mk_region(ty::ReStatic), referent_ty); + // Construct the resulting datum. The right datum to return here would be an Lvalue datum, + // because there is cleanup scheduled and the datum doesn't own the data, but for thin pointers + // we microoptimize it to be an Rvalue datum to avoid the extra alloca and level of + // indirection and for thin pointers, this has no ill effects. + let kind = if type_is_sized(bcx.tcx(), referent_ty) { + RvalueExpr(Rvalue::new(ByValue)) + } else { + LvalueExpr(lv_datum.kind) + }; + // Get the pointer. let llref = lv_datum.to_llref(); - - // Construct the resulting datum, using what was the "by ref" - // ValueRef of type `referent_ty` to be the "by value" ValueRef - // of type `&referent_ty`. - // Pointers to DST types are non-immediate, and therefore still use ByRef. - let kind = if type_is_sized(bcx.tcx(), referent_ty) { ByValue } else { ByRef }; - DatumBlock::new(bcx, Datum::new(llref, ptr_ty, RvalueExpr(Rvalue::new(kind)))) + DatumBlock::new(bcx, Datum::new(llref, ptr_ty, kind)) } fn deref_multiple<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 9012ecaa2134f..01fd36867fffb 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -460,12 +460,13 @@ fn gate_simd_ffi(tcx: &ty::ctxt, decl: &hir::FnDecl, ty: &ty::BareFnTy) { if !tcx.sess.features.borrow().simd_ffi { let check = |ast_ty: &hir::Ty, ty: ty::Ty| { if ty.is_simd() { - tcx.sess.span_err(ast_ty.span, + tcx.sess.struct_span_err(ast_ty.span, &format!("use of SIMD type `{}` in FFI is highly experimental and \ may result in invalid code", - pprust::ty_to_string(ast_ty))); - tcx.sess.fileline_help(ast_ty.span, - "add #![feature(simd_ffi)] to the crate attributes to enable"); + pprust::ty_to_string(ast_ty))) + .fileline_help(ast_ty.span, + "add #![feature(simd_ffi)] to the crate attributes to enable") + .emit(); } }; let sig = &ty.sig.0; @@ -639,7 +640,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let llfn = declare::define_internal_rust_fn(ccx, &ps, t); attributes::from_fn_attrs(ccx, attrs, llfn); - base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]); + base::trans_fn(ccx, decl, body, llfn, param_substs, id, attrs); llfn } diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 3fc9bc0d66d3d..a1165ffe171d0 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -88,6 +88,10 @@ pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } +fn type_needs_drop<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { + tcx.type_needs_drop_given_env(ty, &tcx.empty_parameter_environment()) +} + pub fn get_drop_glue_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Ty<'tcx> { let tcx = ccx.tcx(); @@ -106,11 +110,11 @@ pub fn get_drop_glue_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // returned `tcx.types.i8` does not appear unsound. The impact on // code quality is unknown at this time.) - if !type_needs_drop(tcx, t) { + if !type_needs_drop(&tcx, t) { return tcx.types.i8; } match t.sty { - ty::TyBox(typ) if !type_needs_drop(tcx, typ) + ty::TyBox(typ) if !type_needs_drop(&tcx, typ) && type_is_sized(tcx, typ) => { let llty = sizing_type_of(ccx, typ); // `Box` does not allocate. diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs index 29965755eac76..baf244c2e7960 100644 --- a/src/librustc_trans/trans/inline.rs +++ b/src/librustc_trans/trans/inline.rs @@ -15,12 +15,13 @@ use middle::subst::Substs; use trans::base::{push_ctxt, trans_item, get_item_val, trans_fn}; use trans::common::*; +use rustc::dep_graph::DepNode; use rustc_front::hir; -fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) - -> Option { +fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option { debug!("instantiate_inline({:?})", fn_id); let _icx = push_ctxt("instantiate_inline"); + let _task = ccx.tcx().dep_graph.in_task(DepNode::TransInlinedItem(fn_id)); match ccx.external().borrow().get(&fn_id) { Some(&Some(node_id)) => { @@ -165,7 +166,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) llfn, empty_substs, impl_item.id, - &[]); + &impl_item.attrs); // See linkage comments on items. if ccx.sess().opts.cg.codegen_units == 1 { SetLinkage(llfn, InternalLinkage); diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 66f53deffa9f6..07cc6ec8fbd11 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -35,8 +35,9 @@ use trans::glue; use trans::type_of; use trans::machine; use trans::type_::Type; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, TypeFoldable}; use middle::subst::Substs; +use rustc::dep_graph::DepNode; use rustc_front::hir; use syntax::abi::{self, RustIntrinsic}; use syntax::ast; @@ -101,6 +102,7 @@ pub fn span_transmute_size_error(a: &Session, b: Span, msg: &str) { /// Performs late verification that intrinsics are used correctly. At present, /// the only intrinsic that needs such verification is `transmute`. pub fn check_intrinsics(ccx: &CrateContext) { + let _task = ccx.tcx().dep_graph.in_task(DepNode::IntrinsicUseCheck); let mut last_failing_id = None; for transmute_restriction in ccx.tcx().transmute_restrictions.borrow().iter() { // Sometimes, a single call to transmute will push multiple diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index d29ab2ee6fbf6..bd12dd8c3effc 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -34,7 +34,7 @@ use trans::machine; use trans::monomorphize; use trans::type_::Type; use trans::type_of::*; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, TypeFoldable}; use middle::ty::MethodCall; use syntax::ast; @@ -74,8 +74,13 @@ pub fn trans_impl(ccx: &CrateContext, for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) { let llfn = get_item_val(ccx, impl_item.id); let empty_substs = tcx.mk_substs(Substs::trans_empty()); - trans_fn(ccx, &sig.decl, body, llfn, - empty_substs, impl_item.id, &[]); + trans_fn(ccx, + &sig.decl, + body, + llfn, + empty_substs, + impl_item.id, + &impl_item.attrs); update_linkage(ccx, llfn, Some(impl_item.id), diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 265969c52b39d..aa0b3a25ebb0c 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -8,16 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::BasicBlockRef; -use middle::infer; -use middle::ty; +use llvm::{BasicBlockRef, ValueRef}; use rustc::mir::repr as mir; use trans::adt; use trans::base; use trans::build; +use trans::attributes; use trans::common::{self, Block}; use trans::debuginfo::DebugLoc; use trans::type_of; +use trans::type_::Type; use super::MirContext; use super::operand::OperandValue::{FatPtr, Immediate, Ref}; @@ -33,17 +33,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx = self.trans_statement(bcx, statement); } - debug!("trans_block: terminator: {:?}", data.terminator); + debug!("trans_block: terminator: {:?}", data.terminator()); - match data.terminator { + match *data.terminator() { mir::Terminator::Goto { target } => { build::Br(bcx, self.llblock(target), DebugLoc::None) } - mir::Terminator::Panic { .. } => { - unimplemented!() - } - mir::Terminator::If { ref cond, targets: (true_bb, false_bb) } => { let cond = self.trans_operand(bcx, cond); let lltrue = self.llblock(true_bb); @@ -60,10 +56,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // The else branch of the Switch can't be hit, so branch to an unreachable // instruction so LLVM knows that - // FIXME it might be nice to have just one such block (created lazilly), we could - // store it in the "MIR trans" state. - let unreachable_blk = bcx.fcx.new_temp_block("enum-variant-unreachable"); - build::Unreachable(unreachable_blk); + let unreachable_blk = self.unreachable_block(); let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len()); assert_eq!(adt_def.variants.len(), targets.len()); @@ -86,18 +79,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Diverge => { - if let Some(llpersonalityslot) = self.llpersonalityslot { - let lp = build::Load(bcx, llpersonalityslot); - // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality); - build::Resume(bcx, lp); - } else { - // This fn never encountered anything fallible, so - // a Diverge cannot actually happen. Note that we - // do a total hack to ensure that we visit the - // DIVERGE block last. - build::Unreachable(bcx); - } + mir::Terminator::Resume => { + let ps = self.get_personality_slot(bcx); + let lp = build::Load(bcx, ps); + base::call_lifetime_end(bcx, ps); + base::trans_unwind_resume(bcx, lp); } mir::Terminator::Return => { @@ -105,65 +91,163 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None); } - mir::Terminator::Call { ref data, targets } => { - // The location we'll write the result of the call into. - let call_dest = self.trans_lvalue(bcx, &data.destination); - - // Create the callee. This will always be a fn - // ptr and hence a kind of scalar. - let callee = self.trans_operand(bcx, &data.func); - let ret_ty = if let ty::TyBareFn(_, ref f) = callee.ty.sty { - let sig = bcx.tcx().erase_late_bound_regions(&f.sig); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - sig.output - } else { - panic!("trans_block: expected TyBareFn as callee"); - }; + mir::Terminator::Call { ref func, ref args, ref kind } => { + // Create the callee. This will always be a fn ptr and hence a kind of scalar. + let callee = self.trans_operand(bcx, func); + let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty); + let debugloc = DebugLoc::None; + // The arguments we'll be passing. Plus one to account for outptr, if used. + let mut llargs = Vec::with_capacity(args.len() + 1); - // The arguments we'll be passing - let mut llargs = vec![]; - - // Does the fn use an outptr? If so, that's the first arg. - if let ty::FnConverging(ret_ty) = ret_ty { + // Prepare the return value destination + let (ret_dest_ty, must_copy_dest) = if let Some(ref d) = kind.destination() { + let dest = self.trans_lvalue(bcx, d); + let ret_ty = dest.ty.to_ty(bcx.tcx()); if type_of::return_uses_outptr(bcx.ccx(), ret_ty) { - llargs.push(call_dest.llval); + llargs.push(dest.llval); + (Some((dest, ret_ty)), false) + } else { + (Some((dest, ret_ty)), !common::type_is_zero_size(bcx.ccx(), ret_ty)) } - } + } else { + (None, false) + }; // Process the rest of the args. - for arg in &data.args { - let arg_op = self.trans_operand(bcx, arg); - match arg_op.val { + for arg in args { + match self.trans_operand(bcx, arg).val { Ref(llval) | Immediate(llval) => llargs.push(llval), - FatPtr(base, extra) => { - // The two words in a fat ptr are passed separately - llargs.push(base); - llargs.push(extra); + FatPtr(b, e) => { + llargs.push(b); + llargs.push(e); } } } - // FIXME: Handle panics - //let panic_bb = self.llblock(targets.1); - //self.make_landing_pad(panic_bb); - - // Do the actual call. - let (llret, b) = base::invoke(bcx, - callee.immediate(), - &llargs[..], - callee.ty, - DebugLoc::None); - bcx = b; - - // Copy the return value into the destination. - if let ty::FnConverging(ret_ty) = ret_ty { - if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && - !common::type_is_zero_size(bcx.ccx(), ret_ty) { - base::store_ty(bcx, llret, call_dest.llval, ret_ty); + // Many different ways to call a function handled here + match (base::avoid_invoke(bcx), kind) { + // The two cases below are the only ones to use LLVM’s `invoke`. + (false, &mir::CallKind::DivergingCleanup(cleanup)) => { + let cleanup = self.bcx(cleanup); + let landingpad = self.make_landing_pad(cleanup); + build::Invoke(bcx, + callee.immediate(), + &llargs[..], + self.unreachable_block().llbb, + landingpad.llbb, + Some(attrs), + debugloc); + }, + (false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => { + let cleanup = self.bcx(targets.1); + let landingpad = self.make_landing_pad(cleanup); + let (target, postinvoke) = if must_copy_dest { + (bcx.fcx.new_block(false, "", None), Some(self.bcx(targets.0))) + } else { + (self.bcx(targets.0), None) + }; + let invokeret = build::Invoke(bcx, + callee.immediate(), + &llargs[..], + target.llbb, + landingpad.llbb, + Some(attrs), + debugloc); + if let Some(postinvoketarget) = postinvoke { + // We translate the copy into a temoprary block. The temporary block is + // necessary because the current block has already been terminated (by + // `invoke`) and we cannot really translate into the target block + // because: + // * The target block may have more than a single precedesor; + // * Some LLVM insns cannot have a preceeding store insn (phi, + // cleanuppad), and adding/prepending the store now may render + // those other instructions invalid. + // + // NB: This approach still may break some LLVM code. For example if the + // target block starts with a `phi` (which may only match on immediate + // precedesors), it cannot know about this temporary block thus + // resulting in an invalid code: + // + // this: + // … + // %0 = … + // %1 = invoke to label %temp … + // temp: + // store ty %1, ty* %dest + // br label %actualtargetblock + // actualtargetblock: ; preds: %temp, … + // phi … [%this, …], [%0, …] ; ERROR: phi requires to match only on + // ; immediate precedesors + let (ret_dest, ret_ty) = ret_dest_ty + .expect("return destination and type not set"); + base::store_ty(target, invokeret, ret_dest.llval, ret_ty); + build::Br(target, postinvoketarget.llbb, debugloc); + } + }, + (_, &mir::CallKind::DivergingCleanup(_)) | + (_, &mir::CallKind::Diverging) => { + build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc); + build::Unreachable(bcx); + } + (_, k@&mir::CallKind::ConvergingCleanup { .. }) | + (_, k@&mir::CallKind::Converging { .. }) => { + // Bug #20046 + let target = match *k { + mir::CallKind::ConvergingCleanup { targets, .. } => targets.0, + mir::CallKind::Converging { target, .. } => target, + _ => unreachable!() + }; + let llret = build::Call(bcx, + callee.immediate(), + &llargs[..], + Some(attrs), + debugloc); + if must_copy_dest { + let (ret_dest, ret_ty) = ret_dest_ty + .expect("return destination and type not set"); + base::store_ty(bcx, llret, ret_dest.llval, ret_ty); + } + build::Br(bcx, self.llblock(target), debugloc); } } + } + } + } + + fn get_personality_slot(&mut self, bcx: Block<'bcx, 'tcx>) -> ValueRef { + let ccx = bcx.ccx(); + if let Some(slot) = self.llpersonalityslot { + slot + } else { + let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); + let slot = base::alloca(bcx, llretty, "personalityslot"); + self.llpersonalityslot = Some(slot); + base::call_lifetime_start(bcx, slot); + slot + } + } + + fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> { + let bcx = cleanup.fcx.new_block(true, "cleanup", None); + let ccx = bcx.ccx(); + let llpersonality = bcx.fcx.eh_personality(); + let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); + let llretval = build::LandingPad(bcx, llretty, llpersonality, 1); + build::SetCleanup(bcx, llretval); + let slot = self.get_personality_slot(bcx); + build::Store(bcx, llretval, slot); + build::Br(bcx, cleanup.llbb, DebugLoc::None); + bcx + } - build::Br(bcx, self.llblock(targets.0), DebugLoc::None) + fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> { + match self.unreachable_block { + Some(b) => b, + None => { + let bl = self.fcx.new_block(false, "unreachable", None); + build::Unreachable(bl); + self.unreachable_block = Some(bl); + bl } } } diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs index 70a56990efd03..e461a1c05bcf3 100644 --- a/src/librustc_trans/trans/mir/constant.rs +++ b/src/librustc_trans/trans/mir/constant.rs @@ -9,11 +9,14 @@ // except according to those terms. use back::abi; -use middle::ty::{Ty, HasTypeFlags}; +use llvm::ValueRef; +use middle::subst::Substs; +use middle::ty::{Ty, TypeFoldable}; use rustc::middle::const_eval::ConstVal; use rustc::mir::repr as mir; -use trans::consts; -use trans::common::{self, Block}; +use trans::common::{self, Block, C_bool, C_bytes, C_floating_f64, C_integral, C_str_slice}; +use trans::consts::{self, TrueConst}; +use trans::{type_of, expr}; use super::operand::{OperandRef, OperandValue}; @@ -27,7 +30,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { -> OperandRef<'tcx> { let ccx = bcx.ccx(); - let val = consts::trans_constval(bcx, cv, ty, bcx.fcx.param_substs); + let val = self.trans_constval_inner(bcx, cv, ty, bcx.fcx.param_substs); let val = if common::type_is_immediate(ccx, ty) { OperandValue::Immediate(val) } else if common::type_is_fat_ptr(bcx.tcx(), ty) { @@ -46,6 +49,39 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } + /// Translate ConstVal into a bare LLVM ValueRef. + fn trans_constval_inner(&mut self, + bcx: common::Block<'bcx, 'tcx>, + cv: &ConstVal, + ty: Ty<'tcx>, + param_substs: &'tcx Substs<'tcx>) + -> ValueRef + { + let ccx = bcx.ccx(); + let llty = type_of::type_of(ccx, ty); + match *cv { + ConstVal::Float(v) => C_floating_f64(v, llty), + ConstVal::Bool(v) => C_bool(ccx, v), + ConstVal::Int(v) => C_integral(llty, v as u64, true), + ConstVal::Uint(v) => C_integral(llty, v, false), + ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), + ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"), + ConstVal::Struct(id) | ConstVal::Tuple(id) => { + let expr = bcx.tcx().map.expect_expr(id); + match consts::const_expr(ccx, expr, param_substs, None, TrueConst::Yes) { + Ok((val, _)) => val, + Err(e) => panic!("const eval failure: {}", e.description()), + } + }, + ConstVal::Array(id, _) | ConstVal::Repeat(id, _) => { + let expr = bcx.tcx().map.expect_expr(id); + expr::trans(bcx, expr).datum.val + }, + ConstVal::Function(did) => + self.trans_fn_ref(bcx, ty, param_substs, did).immediate() + } + } + pub fn trans_constant(&mut self, bcx: Block<'bcx, 'tcx>, constant: &mir::Constant<'tcx>) @@ -53,8 +89,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { { let ty = bcx.monomorphize(&constant.ty); match constant.literal { - mir::Literal::Item { def_id, kind, substs } => - self.trans_item_ref(bcx, ty, kind, substs, def_id), + mir::Literal::Item { def_id, kind, substs } => { + let substs = bcx.tcx().mk_substs(bcx.monomorphize(&substs)); + self.trans_item_ref(bcx, ty, kind, substs, def_id) + } mir::Literal::Value { ref value } => { self.trans_constval(bcx, value, ty) } diff --git a/src/librustc_trans/trans/mir/did.rs b/src/librustc_trans/trans/mir/did.rs index 368708d470bef..0e2387e3e349e 100644 --- a/src/librustc_trans/trans/mir/did.rs +++ b/src/librustc_trans/trans/mir/did.rs @@ -12,7 +12,7 @@ use syntax::codemap::DUMMY_SP; use rustc::front::map; -use rustc::middle::ty::{self, Ty, HasTypeFlags}; +use rustc::middle::ty::{self, Ty, TypeFoldable}; use rustc::middle::subst::Substs; use rustc::middle::const_eval; use rustc::middle::def_id::DefId; @@ -21,6 +21,7 @@ use rustc::middle::traits; use rustc::mir::repr::ItemKind; use trans::common::{Block, fulfill_obligation}; use trans::base; +use trans::closure; use trans::expr; use trans::monomorphize; use trans::meth; @@ -38,10 +39,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { substs: &'tcx Substs<'tcx>, did: DefId) -> OperandRef<'tcx> { + debug!("trans_item_ref(ty={:?}, kind={:?}, substs={:?}, did={})", + ty, kind, substs, bcx.tcx().item_path_str(did)); + match kind { - ItemKind::Function | - ItemKind::Struct | - ItemKind::Variant => self.trans_fn_ref(bcx, ty, substs, did), + ItemKind::Function => self.trans_fn_ref(bcx, ty, substs, did), ItemKind::Method => match bcx.tcx().impl_or_trait_item(did).container() { ty::ImplContainer(_) => self.trans_fn_ref(bcx, ty, substs, did), ty::TraitContainer(tdid) => self.trans_static_method(bcx, ty, did, tdid, substs) @@ -70,6 +72,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { substs: &'tcx Substs<'tcx>, did: DefId) -> OperandRef<'tcx> { + debug!("trans_fn_ref(ty={:?}, substs={:?}, did={})", + ty, substs, bcx.tcx().item_path_str(did)); + let did = inline::maybe_instantiate_inline(bcx.ccx(), did); if !substs.types.is_empty() || is_named_tuple_constructor(bcx.tcx(), did) { @@ -103,9 +108,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { trait_id: DefId, substs: &'tcx Substs<'tcx>) -> OperandRef<'tcx> { + debug!("trans_static_method(ty={:?}, method={}, trait={}, substs={:?})", + ty, + bcx.tcx().item_path_str(method_id), + bcx.tcx().item_path_str(trait_id), + substs); + let ccx = bcx.ccx(); let tcx = bcx.tcx(); - let mname = tcx.item_name(method_id); let subst::SeparateVecsPerParamSpace { types: rcvr_type, selfs: rcvr_self, @@ -120,6 +130,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match vtbl { traits::VtableImpl(traits::VtableImplData { impl_def_id, substs: imp_substs, .. }) => { assert!(!imp_substs.types.needs_infer()); + + let mname = tcx.item_name(method_id); + let subst::SeparateVecsPerParamSpace { types: impl_type, selfs: impl_self, @@ -132,6 +145,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let mthsubsts = tcx.mk_substs(mth.substs); self.trans_fn_ref(bcx, ty, mthsubsts, mth.method.def_id) }, + traits::VtableClosure(data) => { + let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap(); + let llfn = closure::trans_closure_method(bcx.ccx(), + data.closure_def_id, + data.substs, + trait_closure_kind); + OperandRef { + ty: ty, + val: OperandValue::Immediate(llfn) + } + }, traits::VtableObject(ref data) => { let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id); OperandRef::from_rvalue_datum( diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index b167633909a4a..ba0427ec3d686 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -9,7 +9,7 @@ // except according to those terms. use llvm::ValueRef; -use rustc::middle::ty::{self, Ty, HasTypeFlags}; +use rustc::middle::ty::{self, Ty, TypeFoldable}; use rustc::mir::repr as mir; use rustc::mir::tcx::LvalueTy; use trans::adt; @@ -18,6 +18,8 @@ use trans::build; use trans::common::{self, Block}; use trans::debuginfo::DebugLoc; use trans::machine; +use trans::type_of; +use llvm; use std::ptr; @@ -91,10 +93,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { const_ty) }, mir::Lvalue::ReturnPointer => { - let return_ty = bcx.monomorphize(&self.mir.return_ty); - let llval = fcx.get_ret_slot(bcx, return_ty, "return"); - LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty.unwrap())) - } + let fn_return_ty = bcx.monomorphize(&self.mir.return_ty); + let return_ty = fn_return_ty.unwrap(); + let llval = if !common::return_type_is_void(bcx.ccx(), return_ty) { + fcx.get_ret_slot(bcx, fn_return_ty, "") + } else { + // This is a void return; that is, there’s no place to store the value and + // there cannot really be one (or storing into it doesn’t make sense, anyway). + // Ergo, we return an undef ValueRef, so we do not have to special-case every + // place using lvalues, and could use it the same way you use a regular + // ReturnPointer LValue (i.e. store into it, load from it etc). + let llty = type_of::type_of(bcx.ccx(), return_ty).ptr_to(); + unsafe { + llvm::LLVMGetUndef(llty.to_ref()) + } + }; + LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty)) + }, mir::Lvalue::Projection(ref projection) => { let tr_base = self.trans_lvalue(bcx, &projection.base); let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 0ed76ebeb4362..75ce33da2c9b9 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -28,6 +28,9 @@ use self::operand::OperandRef; pub struct MirContext<'bcx, 'tcx:'bcx> { mir: &'bcx mir::Mir<'tcx>, + /// Function context + fcx: &'bcx common::FunctionContext<'bcx, 'tcx>, + /// When unwinding is initiated, we have to store this personality /// value somewhere so that we can load it and re-use it in the /// resume instruction. The personality is (afaik) some kind of @@ -40,6 +43,9 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// A `Block` for each MIR `BasicBlock` blocks: Vec>, + /// Cached unreachable block + unreachable_block: Option>, + /// An LLVM alloca for each MIR `VarDecl` vars: Vec>, @@ -107,7 +113,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { // Allocate a `Block` for every basic block let block_bcxs: Vec> = mir_blocks.iter() - .map(|&bb| fcx.new_block(false, &format!("{:?}", bb), None)) + .map(|&bb|{ + let is_cleanup = mir.basic_block_data(bb).is_cleanup; + fcx.new_block(is_cleanup, &format!("{:?}", bb), None) + }) .collect(); // Branch to the START block @@ -116,8 +125,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { let mut mircx = MirContext { mir: mir, + fcx: fcx, llpersonalityslot: None, blocks: block_bcxs, + unreachable_block: None, vars: vars, temps: temps, args: args, @@ -125,16 +136,8 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { // Translate the body of each block for &bb in &mir_blocks { - if bb != mir::DIVERGE_BLOCK { - mircx.trans_block(bb); - } + mircx.trans_block(bb); } - - // Total hack: translate DIVERGE_BLOCK last. This is so that any - // panics which the fn may do can initialize the - // `llpersonalityslot` cell. We don't do this up front because the - // LLVM type of it is (frankly) annoying to compute. - mircx.trans_block(mir::DIVERGE_BLOCK); } /// Produce, for each argument, a `ValueRef` pointing at the diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs index 3a3087b478852..6240473b78ec3 100644 --- a/src/librustc_trans/trans/mir/operand.rs +++ b/src/librustc_trans/trans/mir/operand.rs @@ -9,7 +9,7 @@ // except according to those terms. use llvm::ValueRef; -use rustc::middle::ty::{Ty, HasTypeFlags}; +use rustc::middle::ty::{Ty, TypeFoldable}; use rustc::mir::repr as mir; use trans::base; use trans::common::{self, Block}; diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 529e65dace04c..b57d3b107fb45 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -10,6 +10,7 @@ use llvm::ValueRef; use rustc::middle::ty::{self, Ty}; +use middle::ty::cast::{CastTy, IntTy}; use rustc::mir::repr as mir; use trans::asm; @@ -19,6 +20,7 @@ use trans::common::{self, Block, Result}; use trans::debuginfo::DebugLoc; use trans::declare; use trans::expr; +use trans::adt; use trans::machine; use trans::type_::Type; use trans::type_of; @@ -26,21 +28,22 @@ use trans::tvec; use super::MirContext; use super::operand::{OperandRef, OperandValue}; +use super::lvalue::LvalueRef; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_rvalue(&mut self, bcx: Block<'bcx, 'tcx>, - lldest: ValueRef, + dest: LvalueRef<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> Block<'bcx, 'tcx> { - debug!("trans_rvalue(lldest={}, rvalue={:?})", - bcx.val_to_string(lldest), + debug!("trans_rvalue(dest.llval={}, rvalue={:?})", + bcx.val_to_string(dest.llval), rvalue); match *rvalue { mir::Rvalue::Use(ref operand) => { - self.trans_operand_into(bcx, lldest, operand); + self.trans_operand_into(bcx, dest.llval, operand); bcx } @@ -49,7 +52,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // into-coerce of a thin pointer to a fat pointer - just // use the operand path. let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(bcx, lldest, temp); + self.store_operand(bcx, dest.llval, temp); return bcx; } @@ -72,12 +75,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { base::store_ty(bcx, llval, lltemp, operand.ty); base::coerce_unsized_into(bcx, lltemp, operand.ty, - lldest, cast_ty); + dest.llval, cast_ty); } OperandValue::Ref(llref) => { base::coerce_unsized_into(bcx, llref, operand.ty, - lldest, cast_ty); + dest.llval, cast_ty); } } bcx @@ -86,20 +89,32 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Repeat(ref elem, ref count) => { let elem = self.trans_operand(bcx, elem); let size = self.trans_constant(bcx, count).immediate(); - let base = expr::get_dataptr(bcx, lldest); + let base = expr::get_dataptr(bcx, dest.llval); tvec::iter_vec_raw(bcx, base, elem.ty, size, |bcx, llslot, _| { self.store_operand(bcx, llslot, elem); bcx }) } - mir::Rvalue::Aggregate(_, ref operands) => { - for (i, operand) in operands.iter().enumerate() { - // Note: perhaps this should be StructGep, but - // note that in some cases the values here will - // not be structs but arrays. - let lldest_i = build::GEPi(bcx, lldest, &[0, i]); - self.trans_operand_into(bcx, lldest_i, operand); + mir::Rvalue::Aggregate(ref kind, ref operands) => { + match *kind { + // Unit struct or variant; both are translated very differently compared to any + // other aggregate + mir::AggregateKind::Adt(adt_def, index, _) + if adt_def.variants[index].kind() == ty::VariantKind::Unit => { + let repr = adt::represent_type(bcx.ccx(), dest.ty.to_ty(bcx.tcx())); + let disr = adt_def.variants[index].disr_val; + adt::trans_set_discr(bcx, &*repr, dest.llval, disr); + }, + _ => { + for (i, operand) in operands.iter().enumerate() { + // Note: perhaps this should be StructGep, but + // note that in some cases the values here will + // not be structs but arrays. + let lldest_i = build::GEPi(bcx, dest.llval, &[0, i]); + self.trans_operand_into(bcx, lldest_i, operand); + } + } } bcx } @@ -113,9 +128,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let llbase1 = build::GEPi(bcx, llbase, &[from_start]); let adj = common::C_uint(ccx, from_start + from_end); let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None); - let lladdrdest = expr::get_dataptr(bcx, lldest); + let lladdrdest = expr::get_dataptr(bcx, dest.llval); build::Store(bcx, llbase1, lladdrdest); - let llmetadest = expr::get_meta(bcx, lldest); + let llmetadest = expr::get_meta(bcx, dest.llval); build::Store(bcx, lllen1, llmetadest); bcx } @@ -127,7 +142,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { _ => { assert!(rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(bcx, lldest, temp); + self.store_operand(bcx, dest.llval, temp); bcx } } @@ -185,7 +200,89 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } } - mir::CastKind::Misc => unimplemented!() + mir::CastKind::Misc if common::type_is_immediate(bcx.ccx(), operand.ty) => { + debug_assert!(common::type_is_immediate(bcx.ccx(), cast_ty)); + let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast"); + let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); + let ll_t_in = type_of::arg_type_of(bcx.ccx(), operand.ty); + let ll_t_out = type_of::arg_type_of(bcx.ccx(), cast_ty); + let (llval, ll_t_in, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in { + let repr = adt::represent_type(bcx.ccx(), operand.ty); + let llval = operand.immediate(); + let discr = adt::trans_get_discr(bcx, &*repr, llval, None); + (discr, common::val_ty(discr), adt::is_discr_signed(&*repr)) + } else { + (operand.immediate(), ll_t_in, operand.ty.is_signed()) + }; + + let newval = match (r_t_in, r_t_out) { + (CastTy::Int(_), CastTy::Int(_)) => { + let srcsz = ll_t_in.int_width(); + let dstsz = ll_t_out.int_width(); + if srcsz == dstsz { + build::BitCast(bcx, llval, ll_t_out) + } else if srcsz > dstsz { + build::Trunc(bcx, llval, ll_t_out) + } else if signed { + build::SExt(bcx, llval, ll_t_out) + } else { + build::ZExt(bcx, llval, ll_t_out) + } + } + (CastTy::Float, CastTy::Float) => { + let srcsz = ll_t_in.float_width(); + let dstsz = ll_t_out.float_width(); + if dstsz > srcsz { + build::FPExt(bcx, llval, ll_t_out) + } else if srcsz > dstsz { + build::FPTrunc(bcx, llval, ll_t_out) + } else { + llval + } + } + (CastTy::Ptr(_), CastTy::Ptr(_)) | + (CastTy::FnPtr, CastTy::Ptr(_)) | + (CastTy::RPtr(_), CastTy::Ptr(_)) => + build::PointerCast(bcx, llval, ll_t_out), + (CastTy::Ptr(_), CastTy::Int(_)) | + (CastTy::FnPtr, CastTy::Int(_)) => + build::PtrToInt(bcx, llval, ll_t_out), + (CastTy::Int(_), CastTy::Ptr(_)) => + build::IntToPtr(bcx, llval, ll_t_out), + (CastTy::Int(_), CastTy::Float) if signed => + build::SIToFP(bcx, llval, ll_t_out), + (CastTy::Int(_), CastTy::Float) => + build::UIToFP(bcx, llval, ll_t_out), + (CastTy::Float, CastTy::Int(IntTy::I)) => + build::FPToSI(bcx, llval, ll_t_out), + (CastTy::Float, CastTy::Int(_)) => + build::FPToUI(bcx, llval, ll_t_out), + _ => bcx.ccx().sess().bug( + &format!("unsupported cast: {:?} to {:?}", operand.ty, cast_ty) + ) + }; + OperandValue::Immediate(newval) + } + mir::CastKind::Misc => { // Casts from a fat-ptr. + let ll_cast_ty = type_of::arg_type_of(bcx.ccx(), cast_ty); + let ll_from_ty = type_of::arg_type_of(bcx.ccx(), operand.ty); + if let OperandValue::FatPtr(data_ptr, meta_ptr) = operand.val { + if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { + let ll_cft = ll_cast_ty.field_types(); + let ll_fft = ll_from_ty.field_types(); + let data_cast = build::PointerCast(bcx, data_ptr, ll_cft[0]); + assert_eq!(ll_cft[1].kind(), ll_fft[1].kind()); + OperandValue::FatPtr(data_cast, meta_ptr) + } else { // cast to thin-ptr + // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and + // pointer-cast of that pointer to desired pointer type. + let llval = build::PointerCast(bcx, data_ptr, ll_cast_ty); + OperandValue::Immediate(llval) + } + } else { + panic!("Unexpected non-FatPtr operand") + } + } }; (bcx, OperandRef { val: val, diff --git a/src/librustc_trans/trans/mir/statement.rs b/src/librustc_trans/trans/mir/statement.rs index 9894626e284a2..dae0d3b55c0ba 100644 --- a/src/librustc_trans/trans/mir/statement.rs +++ b/src/librustc_trans/trans/mir/statement.rs @@ -31,7 +31,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let index = index as usize; match self.temps[index as usize] { TempRef::Lvalue(tr_dest) => { - self.trans_rvalue(bcx, tr_dest.llval, rvalue) + self.trans_rvalue(bcx, tr_dest, rvalue) } TempRef::Operand(None) => { let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue); @@ -47,7 +47,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } _ => { let tr_dest = self.trans_lvalue(bcx, lvalue); - self.trans_rvalue(bcx, tr_dest.llval, rvalue) + self.trans_rvalue(bcx, tr_dest, rvalue) } } } diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index b102e96af20e2..1fbc0d5c01529 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -20,6 +20,7 @@ mod macros; mod adt; mod asm; +mod assert_dep_graph; mod attributes; mod base; mod basic_block; diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 4b6a0d1a50965..416c1908c973e 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -23,7 +23,7 @@ use trans::base; use trans::common::*; use trans::declare; use trans::foreign; -use middle::ty::{self, HasTypeFlags, Ty}; +use middle::ty::{self, Ty}; use rustc::front::map as hir_map; use rustc_front::hir; @@ -185,7 +185,13 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx, &**decl, &**body, &[], d, psubsts, fn_node_id, Some(&hash[..])); } else { - trans_fn(ccx, &**decl, &**body, d, psubsts, fn_node_id, &[]); + trans_fn(ccx, + &**decl, + &**body, + d, + psubsts, + fn_node_id, + &i.attrs); } } @@ -216,7 +222,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, d, psubsts, impl_item.id, - &[]); + &impl_item.attrs); } d } @@ -232,8 +238,13 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let d = mk_lldecl(abi::Rust); let needs_body = setup_lldecl(d, &trait_item.attrs); if needs_body { - trans_fn(ccx, &sig.decl, body, d, - psubsts, trait_item.id, &[]); + trans_fn(ccx, + &sig.decl, + body, + d, + psubsts, + trait_item.id, + &trait_item.attrs); } d } @@ -288,7 +299,7 @@ pub fn apply_param_substs<'tcx,T>(tcx: &ty::ctxt<'tcx>, param_substs: &Substs<'tcx>, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { let substituted = value.subst(tcx, param_substs); normalize_associated_type(tcx, &substituted) diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs index e65a212e41b13..8696bdd60e291 100644 --- a/src/librustc_trans/trans/type_of.rs +++ b/src/librustc_trans/trans/type_of.rs @@ -17,7 +17,7 @@ use trans::adt; use trans::common::*; use trans::foreign; use trans::machine; -use middle::ty::{self, RegionEscape, Ty}; +use middle::ty::{self, Ty, TypeFoldable}; use trans::type_::Type; diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 98ae3071b87d2..98effeefad2a7 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -57,7 +57,7 @@ use middle::resolve_lifetime as rl; use middle::privacy::{AllPublic, LastMod}; use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace}; use middle::traits; -use middle::ty::{self, RegionEscape, Ty, ToPredicate, HasTypeFlags}; +use middle::ty::{self, Ty, ToPredicate, TypeFoldable}; use middle::ty::wf::object_region_bounds; use require_c_abi_if_variadic; use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope, @@ -68,6 +68,7 @@ use util::nodemap::FnvHashSet; use syntax::{abi, ast}; use syntax::codemap::{Span, Pos}; +use syntax::errors::DiagnosticBuilder; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::parse::token; @@ -169,10 +170,8 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &hir::Lifetime) ty::ReLateBound(debruijn, ty::BrNamed(tcx.map.local_def_id(id), lifetime.name)) } - Some(&rl::DefEarlyBoundRegion(space, index, id)) => { - let def_id = tcx.map.local_def_id(id); + Some(&rl::DefEarlyBoundRegion(space, index, _)) => { ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: def_id, space: space, index: index, name: lifetime.name @@ -197,7 +196,7 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &hir::Lifetime) } fn report_elision_failure( - tcx: &ty::ctxt, + db: &mut DiagnosticBuilder, default_span: Span, params: Vec) { @@ -235,26 +234,26 @@ fn report_elision_failure( } if len == 0 { - fileline_help!(tcx.sess, default_span, + fileline_help!(db, default_span, "this function's return type contains a borrowed value, but \ there is no value for it to be borrowed from"); - fileline_help!(tcx.sess, default_span, + fileline_help!(db, default_span, "consider giving it a 'static lifetime"); } else if !any_lifetimes { - fileline_help!(tcx.sess, default_span, + fileline_help!(db, default_span, "this function's return type contains a borrowed value with \ an elided lifetime, but the lifetime cannot be derived from \ the arguments"); - fileline_help!(tcx.sess, default_span, + fileline_help!(db, default_span, "consider giving it an explicit bounded or 'static \ lifetime"); } else if len == 1 { - fileline_help!(tcx.sess, default_span, + fileline_help!(db, default_span, "this function's return type contains a borrowed value, but \ the signature does not say which {} it is borrowed from", m); } else { - fileline_help!(tcx.sess, default_span, + fileline_help!(db, default_span, "this function's return type contains a borrowed value, but \ the signature does not say whether it is borrowed from {}", m); @@ -275,11 +274,12 @@ pub fn opt_ast_region_to_region<'tcx>( None => match rscope.anon_regions(default_span, 1) { Ok(rs) => rs[0], Err(params) => { - span_err!(this.tcx().sess, default_span, E0106, - "missing lifetime specifier"); + let mut err = struct_span_err!(this.tcx().sess, default_span, E0106, + "missing lifetime specifier"); if let Some(params) = params { - report_elision_failure(this.tcx(), default_span, params); + report_elision_failure(&mut err, default_span, params); } + err.emit(); ty::ReStatic } } @@ -1046,9 +1046,9 @@ fn ast_ty_to_trait_ref<'tcx>(this: &AstConv<'tcx>, } } _ => { - span_err!(this.tcx().sess, ty.span, E0178, - "expected a path on the left-hand side of `+`, not `{}`", - pprust::ty_to_string(ty)); + let mut err = struct_span_err!(this.tcx().sess, ty.span, E0178, + "expected a path on the left-hand side of `+`, not `{}`", + pprust::ty_to_string(ty)); let hi = bounds.iter().map(|x| match *x { hir::TraitTyParamBound(ref tr, _) => tr.span.hi, hir::RegionTyParamBound(ref r) => r.span.hi, @@ -1061,29 +1061,28 @@ fn ast_ty_to_trait_ref<'tcx>(this: &AstConv<'tcx>, match (&ty.node, full_span) { (&hir::TyRptr(None, ref mut_ty), Some(full_span)) => { let mutbl_str = if mut_ty.mutbl == hir::MutMutable { "mut " } else { "" }; - this.tcx().sess - .span_suggestion(full_span, "try adding parentheses (per RFC 438):", - format!("&{}({} +{})", - mutbl_str, - pprust::ty_to_string(&*mut_ty.ty), - pprust::bounds_to_string(bounds))); + err.span_suggestion(full_span, "try adding parentheses (per RFC 438):", + format!("&{}({} +{})", + mutbl_str, + pprust::ty_to_string(&*mut_ty.ty), + pprust::bounds_to_string(bounds))); } (&hir::TyRptr(Some(ref lt), ref mut_ty), Some(full_span)) => { let mutbl_str = if mut_ty.mutbl == hir::MutMutable { "mut " } else { "" }; - this.tcx().sess - .span_suggestion(full_span, "try adding parentheses (per RFC 438):", - format!("&{} {}({} +{})", - pprust::lifetime_to_string(lt), - mutbl_str, - pprust::ty_to_string(&*mut_ty.ty), - pprust::bounds_to_string(bounds))); + err.span_suggestion(full_span, "try adding parentheses (per RFC 438):", + format!("&{} {}({} +{})", + pprust::lifetime_to_string(lt), + mutbl_str, + pprust::ty_to_string(&*mut_ty.ty), + pprust::bounds_to_string(bounds))); } _ => { - fileline_help!(this.tcx().sess, ty.span, + fileline_help!(&mut err, ty.span, "perhaps you forgot parentheses? (per RFC 438)"); } } + err.emit(); Err(ErrorReported) } } @@ -1136,7 +1135,8 @@ fn make_object_type<'tcx>(this: &AstConv<'tcx>, traits::astconv_object_safety_violations(tcx, principal.def_id()); if !object_safety_violations.is_empty() { traits::report_object_safety_error( - tcx, span, principal.def_id(), object_safety_violations); + tcx, span, principal.def_id(), object_safety_violations) + .emit(); return tcx.types.err; } @@ -1237,17 +1237,18 @@ fn one_bound_for_assoc_type<'tcx>(tcx: &ty::ctxt<'tcx>, } if bounds.len() > 1 { - span_err!(tcx.sess, span, E0221, - "ambiguous associated type `{}` in bounds of `{}`", - assoc_name, - ty_param_name); + let mut err = struct_span_err!(tcx.sess, span, E0221, + "ambiguous associated type `{}` in bounds of `{}`", + assoc_name, + ty_param_name); for bound in &bounds { - span_note!(tcx.sess, span, + span_note!(&mut err, span, "associated type `{}` could derive from `{}`", ty_param_name, bound); } + err.emit(); } Ok(bounds[0].clone()) @@ -1709,12 +1710,13 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>, } } Err(ref r) => { - span_err!(tcx.sess, r.span, E0250, - "array length constant evaluation error: {}", - r.description()); + let mut err = struct_span_err!(tcx.sess, r.span, E0250, + "array length constant evaluation error: {}", + r.description()); if !ast_ty.span.contains(r.span) { - span_note!(tcx.sess, ast_ty.span, "for array length here") + span_note!(&mut err, ast_ty.span, "for array length here") } + err.emit(); this.tcx().types.err } } @@ -1797,75 +1799,31 @@ fn ty_of_method_or_bare_fn<'a, 'tcx>(this: &AstConv<'tcx>, // lifetime elision, we can determine it in two ways. First (determined // here), if self is by-reference, then the implied output region is the // region of the self parameter. - let mut explicit_self_category_result = None; - let (self_ty, implied_output_region) = match opt_self_info { + let (self_ty, explicit_self_category) = match opt_self_info { None => (None, None), - Some(self_info) => { - // This type comes from an impl or trait; no late-bound - // regions should be present. - assert!(!self_info.untransformed_self_ty.has_escaping_regions()); - - // Figure out and record the explicit self category. - let explicit_self_category = - determine_explicit_self_category(this, &rb, &self_info); - explicit_self_category_result = Some(explicit_self_category); - match explicit_self_category { - ty::StaticExplicitSelfCategory => { - (None, None) - } - ty::ByValueExplicitSelfCategory => { - (Some(self_info.untransformed_self_ty), None) - } - ty::ByReferenceExplicitSelfCategory(region, mutability) => { - (Some(this.tcx().mk_ref( - this.tcx().mk_region(region), - ty::TypeAndMut { - ty: self_info.untransformed_self_ty, - mutbl: mutability - })), - Some(region)) - } - ty::ByBoxExplicitSelfCategory => { - (Some(this.tcx().mk_box(self_info.untransformed_self_ty)), None) - } - } - } + Some(self_info) => determine_self_type(this, &rb, self_info) }; // HACK(eddyb) replace the fake self type in the AST with the actual type. - let input_params = if self_ty.is_some() { + let arg_params = if self_ty.is_some() { &decl.inputs[1..] } else { &decl.inputs[..] }; - let input_tys = input_params.iter().map(|a| ty_of_arg(this, &rb, a, None)); - let input_pats: Vec = input_params.iter() - .map(|a| pprust::pat_to_string(&*a.pat)) - .collect(); - let self_and_input_tys: Vec = - self_ty.into_iter().chain(input_tys).collect(); - + let arg_tys: Vec = + arg_params.iter().map(|a| ty_of_arg(this, &rb, a, None)).collect(); + let arg_pats: Vec = + arg_params.iter().map(|a| pprust::pat_to_string(&*a.pat)).collect(); // Second, if there was exactly one lifetime (either a substitution or a // reference) in the arguments, then any anonymous regions in the output // have that lifetime. - let implied_output_region = match implied_output_region { - Some(r) => Ok(r), - None => { - let input_tys = if self_ty.is_some() { - // Skip the first argument if `self` is present. - &self_and_input_tys[1..] - } else { - &self_and_input_tys[..] - }; - - find_implied_output_region(this.tcx(), input_tys, input_pats) - } + let implied_output_region = match explicit_self_category { + Some(ty::ExplicitSelfCategory::ByReference(region, _)) => Ok(region), + _ => find_implied_output_region(this.tcx(), &arg_tys, arg_pats) }; let output_ty = match decl.output { - hir::Return(ref output) if output.node == hir::TyInfer => - ty::FnConverging(this.ty_infer(None, None, None, output.span)), hir::Return(ref output) => ty::FnConverging(convert_ty_with_lifetime_elision(this, implied_output_region, @@ -1878,28 +1836,37 @@ fn ty_of_method_or_bare_fn<'a, 'tcx>(this: &AstConv<'tcx>, unsafety: unsafety, abi: abi, sig: ty::Binder(ty::FnSig { - inputs: self_and_input_tys, + inputs: self_ty.into_iter().chain(arg_tys).collect(), output: output_ty, variadic: decl.variadic }), - }, explicit_self_category_result) + }, explicit_self_category) } -fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>, - rscope: &RegionScope, - self_info: &SelfInfo<'a, 'tcx>) - -> ty::ExplicitSelfCategory +fn determine_self_type<'a, 'tcx>(this: &AstConv<'tcx>, + rscope: &RegionScope, + self_info: SelfInfo<'a, 'tcx>) + -> (Option>, Option) { + let self_ty = self_info.untransformed_self_ty; return match self_info.explicit_self.node { - hir::SelfStatic => ty::StaticExplicitSelfCategory, - hir::SelfValue(_) => ty::ByValueExplicitSelfCategory, + hir::SelfStatic => (None, Some(ty::ExplicitSelfCategory::Static)), + hir::SelfValue(_) => { + (Some(self_ty), Some(ty::ExplicitSelfCategory::ByValue)) + } hir::SelfRegion(ref lifetime, mutability, _) => { let region = opt_ast_region_to_region(this, rscope, self_info.explicit_self.span, lifetime); - ty::ByReferenceExplicitSelfCategory(region, mutability) + (Some(this.tcx().mk_ref( + this.tcx().mk_region(region), + ty::TypeAndMut { + ty: self_ty, + mutbl: mutability + })), + Some(ty::ExplicitSelfCategory::ByReference(region, mutability))) } hir::SelfExplicit(ref ast_type, _) => { let explicit_type = ast_ty_to_ty(this, rscope, &**ast_type); @@ -1915,12 +1882,12 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>, // ``` // impl Foo for &T { // // Legal declarations: - // fn method1(self: &&T); // ByReferenceExplicitSelfCategory - // fn method2(self: &T); // ByValueExplicitSelfCategory - // fn method3(self: Box<&T>); // ByBoxExplicitSelfCategory + // fn method1(self: &&T); // ExplicitSelfCategory::ByReference + // fn method2(self: &T); // ExplicitSelfCategory::ByValue + // fn method3(self: Box<&T>); // ExplicitSelfCategory::ByBox // // // Invalid cases will be caught later by `check_method_self_type`: - // fn method_err1(self: &mut T); // ByReferenceExplicitSelfCategory + // fn method_err1(self: &mut T); // ExplicitSelfCategory::ByReference // } // ``` // @@ -1931,7 +1898,7 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>, // call it by-ref, by-box as appropriate. For method1, for // example, the impl type has one modifier, but the method // type has two, so we end up with - // ByReferenceExplicitSelfCategory. + // ExplicitSelfCategory::ByReference. let impl_modifiers = count_modifiers(self_info.untransformed_self_ty); let method_modifiers = count_modifiers(explicit_type); @@ -1944,15 +1911,17 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>, impl_modifiers, method_modifiers); - if impl_modifiers >= method_modifiers { - ty::ByValueExplicitSelfCategory + let category = if impl_modifiers >= method_modifiers { + ty::ExplicitSelfCategory::ByValue } else { match explicit_type.sty { - ty::TyRef(r, mt) => ty::ByReferenceExplicitSelfCategory(*r, mt.mutbl), - ty::TyBox(_) => ty::ByBoxExplicitSelfCategory, - _ => ty::ByValueExplicitSelfCategory, + ty::TyRef(r, mt) => ty::ExplicitSelfCategory::ByReference(*r, mt.mutbl), + ty::TyBox(_) => ty::ExplicitSelfCategory::ByBox, + _ => ty::ExplicitSelfCategory::ByValue, } - } + }; + + (Some(explicit_type), Some(category)) } }; diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index efcc08c69f824..926d7fd6e25eb 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -14,7 +14,7 @@ use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding}; use middle::pat_util::pat_is_resolved_const; use middle::privacy::{AllPublic, LastMod}; use middle::subst::Substs; -use middle::ty::{self, Ty, HasTypeFlags, LvaluePreference}; +use middle::ty::{self, Ty, TypeFoldable, LvaluePreference}; use check::{check_expr, check_expr_has_type, check_expr_with_expectation}; use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation}; use check::{check_expr_with_lvalue_pref}; @@ -700,7 +700,9 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, if !is_special_case { return } else { - span_note!(tcx.sess, pat.span, + // Boo! Too painful to attach this to the actual warning, + // it should go away at some point though. + tcx.sess.span_note_without_error(pat.span, "this warning will become a HARD ERROR in a future release. \ See RFC 218 for details."); } @@ -786,12 +788,13 @@ pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, for &Spanned { node: ref field, span } in fields { let field_ty = match used_fields.entry(field.name) { Occupied(occupied) => { - span_err!(tcx.sess, span, E0025, - "field `{}` bound multiple times in the pattern", - field.name); - span_note!(tcx.sess, *occupied.get(), - "field `{}` previously bound here", - field.name); + let mut err = struct_span_err!(tcx.sess, span, E0025, + "field `{}` bound multiple times in the pattern", + field.name); + span_note!(&mut err, *occupied.get(), + "field `{}` previously bound here", + field.name); + err.emit(); tcx.types.err } Vacant(vacant) => { diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs index 8dc95562e44af..91916efa88277 100644 --- a/src/librustc_typeck/check/assoc.rs +++ b/src/librustc_typeck/check/assoc.rs @@ -11,7 +11,6 @@ use middle::infer::InferCtxt; use middle::traits::{self, FulfillmentContext, Normalized, MiscObligation, SelectionContext, ObligationCause}; -use middle::ty::HasTypeFlags; use middle::ty::fold::TypeFoldable; use syntax::ast; use syntax::codemap::Span; @@ -23,7 +22,7 @@ pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, body_id: ast::NodeId, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { debug!("normalize_associated_types_in(value={:?})", value); let mut selcx = SelectionContext::new(infcx); diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 1e20cd3985467..a1b378d84d001 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -61,11 +61,12 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id: return // not a closure method, everything is OK. }; - span_err!(tcx.sess, span, E0174, - "explicit use of unboxed closure method `{}` is experimental", - method); - fileline_help!(tcx.sess, span, - "add `#![feature(unboxed_closures)]` to the crate attributes to enable"); + struct_span_err!(tcx.sess, span, E0174, + "explicit use of unboxed closure method `{}` is experimental", + method) + .fileline_help(span, "add `#![feature(unboxed_closures)]` to the crate \ + attributes to enable") + .emit(); } } @@ -228,7 +229,7 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, sig } _ => { - fcx.type_error_message(call_expr.span, |actual| { + let mut err = fcx.type_error_struct(call_expr.span, |actual| { format!("expected function, found `{}`", actual) }, callee_ty, None); @@ -237,12 +238,14 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, if let Some(pr) = tcx.def_map.borrow().get(&expr.id) { if pr.depth == 0 && pr.base_def != def::DefErr { if let Some(span) = tcx.map.span_if_local(pr.def_id()) { - tcx.sess.span_note(span, "defined here") + err.span_note(span, "defined here"); } } } } + err.emit(); + // This is the "default" function signature, used in case of error. // In that case, we check each argument against "error" in order to // set up all the node type bindings. diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 13e5e46ed2700..fd6c4f44ba428 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -45,7 +45,7 @@ use super::structurally_resolved_type; use lint; use middle::def_id::DefId; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, TypeFoldable}; use middle::ty::cast::{CastKind, CastTy}; use syntax::codemap::Span; use rustc_front::hir; @@ -127,23 +127,25 @@ impl<'tcx> CastCheck<'tcx> { CastError::NeedViaThinPtr | CastError::NeedViaInt | CastError::NeedViaUsize => { - fcx.type_error_message(self.span, |actual| { + fcx.type_error_struct(self.span, |actual| { format!("casting `{}` as `{}` is invalid", actual, fcx.infcx().ty_to_string(self.cast_ty)) - }, self.expr_ty, None); - fcx.ccx.tcx.sess.fileline_help(self.span, - &format!("cast through {} first", match e { - CastError::NeedViaPtr => "a raw pointer", - CastError::NeedViaThinPtr => "a thin pointer", - CastError::NeedViaInt => "an integer", - CastError::NeedViaUsize => "a usize", - _ => unreachable!() - })); + }, self.expr_ty, None) + .fileline_help(self.span, + &format!("cast through {} first", match e { + CastError::NeedViaPtr => "a raw pointer", + CastError::NeedViaThinPtr => "a thin pointer", + CastError::NeedViaInt => "an integer", + CastError::NeedViaUsize => "a usize", + _ => unreachable!() + })) + .emit(); } CastError::CastToBool => { - span_err!(fcx.tcx().sess, self.span, E0054, "cannot cast as `bool`"); - fcx.ccx.tcx.sess.fileline_help(self.span, "compare with zero instead"); + struct_span_err!(fcx.tcx().sess, self.span, E0054, "cannot cast as `bool`") + .fileline_help(self.span, "compare with zero instead") + .emit(); } CastError::CastToChar => { fcx.type_error_message(self.span, |actual| { @@ -165,12 +167,13 @@ impl<'tcx> CastCheck<'tcx> { }, self.expr_ty, None); } CastError::DifferingKinds => { - fcx.type_error_message(self.span, |actual| { + fcx.type_error_struct(self.span, |actual| { format!("casting `{}` as `{}` is invalid", actual, fcx.infcx().ty_to_string(self.cast_ty)) - }, self.expr_ty, None); - fcx.ccx.tcx.sess.fileline_note(self.span, "vtable kinds may not match"); + }, self.expr_ty, None) + .fileline_note(self.span, "vtable kinds may not match") + .emit(); } } } diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index d28a673f748c0..554424a36b19a 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -55,9 +55,9 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, // 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, _) => { + (&ty::ExplicitSelfCategory::Static, + &ty::ExplicitSelfCategory::Static) => {} + (&ty::ExplicitSelfCategory::Static, _) => { span_err!(tcx.sess, impl_m_span, E0185, "method `{}` has a `{}` declaration in the impl, \ but not in the trait", @@ -65,7 +65,7 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, impl_m.explicit_self); return; } - (_, &ty::StaticExplicitSelfCategory) => { + (_, &ty::ExplicitSelfCategory::Static) => { span_err!(tcx.sess, impl_m_span, E0186, "method `{}` has a `{}` declaration in the trait, \ but not in the impl", diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 59025346ce3bd..0cf552b6efecb 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -95,12 +95,13 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( if let Err(_) = infer::mk_eqty(&infcx, true, infer::TypeOrigin::Misc(drop_impl_span), named_type, fresh_impl_self_ty) { - span_err!(tcx.sess, drop_impl_span, E0366, - "Implementations of Drop cannot be specialized"); let item_span = tcx.map.span(self_type_node_id); - tcx.sess.span_note(item_span, - "Use same sequence of generic type and region \ - parameters that is on the struct/enum definition"); + struct_span_err!(tcx.sess, drop_impl_span, E0366, + "Implementations of Drop cannot be specialized") + .span_note(item_span, + "Use same sequence of generic type and region \ + parameters that is on the struct/enum definition") + .emit(); return Err(()); } @@ -197,11 +198,12 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( if !assumptions_in_impl_context.contains(&predicate) { let item_span = tcx.map.span(self_type_node_id); - span_err!(tcx.sess, drop_impl_span, E0367, - "The requirement `{}` is added only by the Drop impl.", predicate); - tcx.sess.span_note(item_span, - "The same requirement must be part of \ - the struct/enum definition"); + struct_span_err!(tcx.sess, drop_impl_span, E0367, + "The requirement `{}` is added only by the Drop impl.", predicate) + .span_note(item_span, + "The same requirement must be part of \ + the struct/enum definition") + .emit(); } } @@ -289,8 +291,8 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx> Ok(()) => {} Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => { let tcx = rcx.tcx(); - span_err!(tcx.sess, span, E0320, - "overflow while adding drop-check rules for {}", typ); + let mut err = struct_span_err!(tcx.sess, span, E0320, + "overflow while adding drop-check rules for {}", typ); match *ctxt { TypeContext::Root => { // no need for an additional note if the overflow @@ -311,7 +313,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx> format!("`{}`", field) }; span_note!( - rcx.tcx().sess, + &mut err, span, "overflowed on {} field {} type: {}", variant_name, @@ -319,6 +321,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx> detected_on_typ); } } + err.emit(); } } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index e0ad51b4ea1b5..44b36294cb480 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -17,7 +17,7 @@ use middle::def_id::DefId; use middle::privacy::{AllPublic, DependsOn, LastPrivate, LastMod}; use middle::subst; use middle::traits; -use middle::ty::{self, RegionEscape, ToPredicate, ToPolyTraitRef, TraitRef}; +use middle::ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable}; use middle::ty::adjustment::{AdjustDerefRef, AutoDerefRef, AutoPtr}; use middle::infer; @@ -274,13 +274,13 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, method_ty.explicit_self); match method_ty.explicit_self { - ty::ByValueExplicitSelfCategory => { + ty::ExplicitSelfCategory::ByValue => { // Trait method is fn(self), no transformation needed. assert!(!unsize); fcx.write_autoderef_adjustment(self_expr.id, autoderefs); } - ty::ByReferenceExplicitSelfCategory(..) => { + ty::ExplicitSelfCategory::ByReference(..) => { // Trait method is fn(&self) or fn(&mut self), need an // autoref. Pull the region etc out of the type of first argument. match transformed_self_ty.sty { diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index ed819d46041e6..44dd0ef7b17d8 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -19,9 +19,7 @@ use middle::def_id::DefId; use middle::subst; use middle::subst::Subst; use middle::traits; -use middle::ty::{self, NoPreference, RegionEscape, Ty, ToPolyTraitRef, TraitRef}; -use middle::ty::HasTypeFlags; -use middle::ty::fold::TypeFoldable; +use middle::ty::{self, NoPreference, Ty, ToPolyTraitRef, TraitRef, TypeFoldable}; use middle::infer; use middle::infer::{InferCtxt, TypeOrigin}; use syntax::ast; @@ -1144,10 +1142,10 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { match *item { ty::ImplOrTraitItem::MethodTraitItem(ref method) => match method.explicit_self { - ty::StaticExplicitSelfCategory => self.mode == Mode::Path, - ty::ByValueExplicitSelfCategory | - ty::ByReferenceExplicitSelfCategory(..) | - ty::ByBoxExplicitSelfCategory => true, + ty::ExplicitSelfCategory::Static => self.mode == Mode::Path, + ty::ExplicitSelfCategory::ByValue | + ty::ExplicitSelfCategory::ByReference(..) | + ty::ExplicitSelfCategory::ByBox => true, }, ty::ImplOrTraitItem::ConstTraitItem(..) => self.mode == Mode::Path, _ => false, diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 955bc92a8f31e..560e84b52d1d6 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -16,7 +16,7 @@ use CrateCtxt; use astconv::AstConv; use check::{self, FnCtxt}; use front::map as hir_map; -use middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, HasTypeFlags}; +use middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable}; use middle::cstore::{self, CrateStore, DefLike}; use middle::def; use middle::def_id::DefId; @@ -27,6 +27,7 @@ use util::nodemap::{FnvHashSet}; use syntax::ast; use syntax::codemap::Span; +use syntax::errors::DiagnosticBuilder; use rustc_front::print::pprust; use rustc_front::hir; @@ -55,7 +56,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, mode }) => { let cx = fcx.tcx(); - fcx.type_error_message( + let mut err = fcx.type_error_struct( span, |actual| { format!("no {} named `{}` found for type `{}` \ @@ -78,17 +79,21 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // snippet }; - let span_stored_function = || { - cx.sess.span_note(span, + macro_rules! span_stored_function { + () => { + err.span_note(span, &format!("use `({0}.{1})(...)` if you meant to call \ the function stored in the `{1}` field", expr_string, item_name)); - }; + } + } - let span_did_you_mean = || { - cx.sess.span_note(span, &format!("did you mean to write `{0}.{1}`?", + macro_rules! span_did_you_mean { + () => { + err.span_note(span, &format!("did you mean to write `{0}.{1}`?", expr_string, item_name)); - }; + } + } // Determine if the field can be used as a function in some way let field_ty = field.ty(cx, substs); @@ -96,19 +101,22 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, match field_ty.sty { // Not all of these (e.g. unsafe fns) implement FnOnce // so we look for these beforehand - ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(), + ty::TyClosure(..) | ty::TyBareFn(..) => { + span_stored_function!(); + } // If it's not a simple function, look for things which implement FnOnce _ => { if let Ok(fn_once_trait_did) = cx.lang_items.require(FnOnceTraitLangItem) { let infcx = fcx.infcx(); infcx.probe(|_| { - let fn_once_substs = Substs::new_trait(vec![ - infcx.next_ty_var()], - Vec::new(), - field_ty); - let trait_ref = ty::TraitRef::new(fn_once_trait_did, - cx.mk_substs(fn_once_substs)); + let fn_once_substs = + Substs::new_trait(vec![infcx.next_ty_var()], + Vec::new(), + field_ty); + let trait_ref = + ty::TraitRef::new(fn_once_trait_did, + cx.mk_substs(fn_once_substs)); let poly_trait_ref = trait_ref.to_poly_trait_ref(); let obligation = Obligation::misc(span, fcx.body_id, @@ -117,13 +125,13 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, let mut selcx = SelectionContext::new(infcx); if selcx.evaluate_obligation(&obligation) { - span_stored_function(); + span_stored_function!(); } else { - span_did_you_mean(); + span_did_you_mean!(); } }); } else { - span_did_you_mean() + span_did_you_mean!(); } } } @@ -131,11 +139,11 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } if !static_sources.is_empty() { - cx.sess.fileline_note( + err.fileline_note( span, "found defined static methods, maybe a `self` is missing?"); - report_candidates(fcx, span, item_name, static_sources); + report_candidates(fcx, &mut err, span, item_name, static_sources); } if !unsatisfied_predicates.is_empty() { @@ -145,7 +153,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, p)) .collect::>() .join(", "); - cx.sess.fileline_note( + err.fileline_note( span, &format!("the method `{}` exists but the \ following trait bounds were not satisfied: {}", @@ -153,15 +161,17 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, bound_list)); } - suggest_traits_to_import(fcx, span, rcvr_ty, item_name, - rcvr_expr, out_of_scope_traits) + suggest_traits_to_import(fcx, &mut err, span, rcvr_ty, item_name, + rcvr_expr, out_of_scope_traits); + err.emit(); } MethodError::Ambiguity(sources) => { - span_err!(fcx.sess(), span, E0034, - "multiple applicable items in scope"); + let mut err = struct_span_err!(fcx.sess(), span, E0034, + "multiple applicable items in scope"); - report_candidates(fcx, span, item_name, sources); + report_candidates(fcx, &mut err, span, item_name, sources); + err.emit(); } MethodError::ClosureAmbiguity(trait_def_id) => { @@ -181,6 +191,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } fn report_candidates(fcx: &FnCtxt, + err: &mut DiagnosticBuilder, span: Span, item_name: ast::Name, mut sources: Vec) { @@ -213,7 +224,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } }; - span_note!(fcx.sess(), item_span, + span_note!(err, item_span, "candidate #{} is defined in an impl{} for the type `{}`", idx + 1, insertion, @@ -222,7 +233,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, CandidateSource::TraitSource(trait_did) => { let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap(); let item_span = fcx.tcx().map.def_id_span(item.def_id(), span); - span_note!(fcx.sess(), item_span, + span_note!(err, item_span, "candidate #{} is defined in the trait `{}`", idx + 1, fcx.tcx().item_path_str(trait_did)); @@ -236,6 +247,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, pub type AllTraitsVec = Vec; fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + err: &mut DiagnosticBuilder, span: Span, rcvr_ty: Ty<'tcx>, item_name: ast::Name, @@ -255,14 +267,13 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"}, one_of_them = if candidates.len() == 1 {"it"} else {"one of them"}); - fcx.sess().fileline_help(span, &msg[..]); + err.fileline_help(span, &msg[..]); for (i, trait_did) in candidates.iter().enumerate() { - fcx.sess().fileline_help(span, - &*format!("candidate #{}: use `{}`", - i + 1, - fcx.tcx().item_path_str(*trait_did))) - + err.fileline_help(span, + &*format!("candidate #{}: use `{}`", + i + 1, + fcx.tcx().item_path_str(*trait_did))); } return } @@ -301,13 +312,13 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, one_of_them = if candidates.len() == 1 {"it"} else {"one of them"}, name = item_name); - fcx.sess().fileline_help(span, &msg[..]); + err.fileline_help(span, &msg[..]); for (i, trait_info) in candidates.iter().enumerate() { - fcx.sess().fileline_help(span, - &*format!("candidate #{}: `{}`", - i + 1, - fcx.tcx().item_path_str(trait_info.def_id))) + err.fileline_help(span, + &*format!("candidate #{}: `{}`", + i + 1, + fcx.tcx().item_path_str(trait_info.def_id))); } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e8eb479a1c36f..3cf75483fea0f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -82,6 +82,7 @@ use self::TupleArgumentsFlag::*; use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode}; use check::_match::pat_ctxt; +use dep_graph::DepNode; use fmt_macros::{Parser, Piece, Position}; use middle::astconv_util::prohibit_type_params; use middle::cstore::LOCAL_CRATE; @@ -96,7 +97,7 @@ use middle::traits::{self, report_fulfillment_errors}; use middle::ty::{GenericPredicates, TypeScheme}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; use middle::ty::{LvaluePreference, NoPreference, PreferMutLvalue}; -use middle::ty::{self, HasTypeFlags, RegionEscape, ToPolyTraitRef, Ty}; +use middle::ty::{self, ToPolyTraitRef, Ty}; use middle::ty::{MethodCall, MethodCallee}; use middle::ty::adjustment; use middle::ty::error::TypeError; @@ -119,9 +120,10 @@ use syntax::ast; use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::codemap::{self, Span, Spanned}; +use syntax::errors::DiagnosticBuilder; use syntax::parse::token::{self, InternedString}; use syntax::ptr::P; -use syntax::util::lev_distance::lev_distance; +use syntax::util::lev_distance::find_best_match_for_name; use rustc_front::intravisit::{self, Visitor}; use rustc_front::hir; @@ -316,7 +318,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { body_id: ast::NodeId, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { let mut fulfillment_cx = self.infcx.fulfillment_cx.borrow_mut(); assoc::normalize_associated_types_in(&self.infcx, @@ -383,34 +385,33 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> { pub fn check_wf_new(ccx: &CrateCtxt) { ccx.tcx.sess.abort_if_new_errors(|| { - let krate = ccx.tcx.map.krate(); let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(ccx); - krate.visit_all_items(&mut visit); + ccx.tcx.visit_all_items_in_krate(DepNode::WfCheck, &mut visit); }); } pub fn check_item_types(ccx: &CrateCtxt) { ccx.tcx.sess.abort_if_new_errors(|| { - let krate = ccx.tcx.map.krate(); let mut visit = CheckItemTypesVisitor { ccx: ccx }; - krate.visit_all_items(&mut visit); + ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemType, &mut visit); }); } pub fn check_item_bodies(ccx: &CrateCtxt) { ccx.tcx.sess.abort_if_new_errors(|| { - let krate = ccx.tcx.map.krate(); let mut visit = CheckItemBodiesVisitor { ccx: ccx }; - krate.visit_all_items(&mut visit); + ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemBody, &mut visit); }); } pub fn check_drop_impls(ccx: &CrateCtxt) { ccx.tcx.sess.abort_if_new_errors(|| { + let _task = ccx.tcx.dep_graph.in_task(DepNode::Dropck); let drop_trait = match ccx.tcx.lang_items.drop_trait() { Some(id) => ccx.tcx.lookup_trait_def(id), None => { return } }; drop_trait.for_each_impl(ccx.tcx, |drop_impl_did| { + let _task = ccx.tcx.dep_graph.in_task(DepNode::DropckImpl(drop_impl_did)); if drop_impl_did.is_local() { match dropck::check_drop_impl(ccx.tcx, drop_impl_did) { Ok(()) => {} @@ -702,11 +703,12 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) { for item in &m.items { let pty = ccx.tcx.lookup_item_type(ccx.tcx.map.local_def_id(item.id)); if !pty.generics.types.is_empty() { - span_err!(ccx.tcx.sess, item.span, E0044, + let mut err = struct_span_err!(ccx.tcx.sess, item.span, E0044, "foreign items may not have type parameters"); - span_help!(ccx.tcx.sess, item.span, + span_help!(&mut err, item.span, "consider using specialization instead of \ type parameters"); + err.emit(); } if let hir::ForeignItemFn(ref fn_decl, _) = item.node { @@ -1037,7 +1039,7 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, t_expr: Ty<'tcx>, id: ast::NodeId) { let tstr = fcx.infcx().ty_to_string(t_cast); - fcx.type_error_message(span, |actual| { + let mut err = fcx.type_error_struct(span, |actual| { format!("cast to unsized type: `{}` as `{}`", actual, tstr) }, t_expr, None); match t_expr.sty { @@ -1049,16 +1051,16 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, if t_cast.is_trait() { match fcx.tcx().sess.codemap().span_to_snippet(t_span) { Ok(s) => { - fcx.tcx().sess.span_suggestion(t_span, - "try casting to a reference instead:", - format!("&{}{}", mtstr, s)); + err.span_suggestion(t_span, + "try casting to a reference instead:", + format!("&{}{}", mtstr, s)); }, Err(_) => - span_help!(fcx.tcx().sess, t_span, + span_help!(err, t_span, "did you mean `&{}{}`?", mtstr, tstr), } } else { - span_help!(fcx.tcx().sess, span, + span_help!(err, span, "consider using an implicit coercion to `&{}{}` instead", mtstr, tstr); } @@ -1066,19 +1068,20 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, ty::TyBox(..) => { match fcx.tcx().sess.codemap().span_to_snippet(t_span) { Ok(s) => { - fcx.tcx().sess.span_suggestion(t_span, - "try casting to a `Box` instead:", - format!("Box<{}>", s)); + err.span_suggestion(t_span, + "try casting to a `Box` instead:", + format!("Box<{}>", s)); }, Err(_) => - span_help!(fcx.tcx().sess, t_span, "did you mean `Box<{}>`?", tstr), + span_help!(err, t_span, "did you mean `Box<{}>`?", tstr), } } _ => { - span_help!(fcx.tcx().sess, e_span, + span_help!(err, e_span, "consider using a box or reference as appropriate"); } } + err.emit(); fcx.write_error(id); } @@ -1331,7 +1334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { substs: &Substs<'tcx>, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { let value = value.subst(self.tcx(), substs); let result = self.normalize_associated_types_in(span, &value); @@ -1357,7 +1360,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn normalize_associated_types_in(&self, span: Span, value: &T) -> T - where T : TypeFoldable<'tcx> + HasTypeFlags + where T : TypeFoldable<'tcx> { self.inh.normalize_associated_types_in(span, self.body_id, value) } @@ -1443,10 +1446,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((adt, variant)) } else if var_kind == ty::VariantKind::Unit { if !self.tcx().sess.features.borrow().braced_empty_structs { - self.tcx().sess.span_err(span, "empty structs and enum variants \ - with braces are unstable"); - fileline_help!(self.tcx().sess, span, "add #![feature(braced_empty_structs)] to \ - the crate features to enable"); + let mut err = self.tcx().sess.struct_span_err(span, + "empty structs and enum variants \ + with braces are unstable"); + fileline_help!(&mut err, span, "add #![feature(braced_empty_structs)] to \ + the crate features to enable"); + err.emit(); } Some((adt, variant)) @@ -1614,12 +1619,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sp: Span, mk_msg: M, actual_ty: Ty<'tcx>, - err: Option<&TypeError<'tcx>>) where - M: FnOnce(String) -> String, + err: Option<&TypeError<'tcx>>) + where M: FnOnce(String) -> String, { self.infcx().type_error_message(sp, mk_msg, actual_ty, err); } + pub fn type_error_struct(&self, + sp: Span, + mk_msg: M, + actual_ty: Ty<'tcx>, + err: Option<&TypeError<'tcx>>) + -> DiagnosticBuilder<'tcx> + where M: FnOnce(String) -> String, + { + self.infcx().type_error_struct(sp, mk_msg, actual_ty, err) + } + pub fn report_mismatched_types(&self, sp: Span, e: Ty<'tcx>, @@ -2913,7 +2929,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, lvalue_pref: LvaluePreference, base: &'tcx hir::Expr, field: &Spanned) { - let tcx = fcx.ccx.tcx; check_expr_with_lvalue_pref(fcx, base, lvalue_pref); let expr_t = structurally_resolved_type(fcx, expr.span, fcx.expr_ty(base)); @@ -2945,19 +2960,18 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } if method::exists(fcx, field.span, field.node, expr_t, expr.id) { - fcx.type_error_message( - field.span, - |actual| { - format!("attempted to take value of method `{}` on type \ - `{}`", field.node, actual) - }, - expr_t, None); - - tcx.sess.fileline_help(field.span, + fcx.type_error_struct(field.span, + |actual| { + format!("attempted to take value of method `{}` on type \ + `{}`", field.node, actual) + }, + expr_t, None) + .fileline_help(field.span, "maybe a `()` to call it is missing? \ - If not, try an anonymous function"); + If not, try an anonymous function") + .emit(); } else { - fcx.type_error_message( + let mut err = fcx.type_error_struct( expr.span, |actual| { format!("attempted access of field `{}` on \ @@ -2968,41 +2982,36 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, }, expr_t, None); if let ty::TyStruct(def, _) = expr_t.sty { - suggest_field_names(def.struct_variant(), field, tcx, vec![]); + suggest_field_names(&mut err, def.struct_variant(), field, vec![]); } + err.emit(); } fcx.write_error(expr.id); } // displays hints about the closest matches in field names - fn suggest_field_names<'tcx>(variant: ty::VariantDef<'tcx>, + fn suggest_field_names<'tcx>(err: &mut DiagnosticBuilder, + variant: ty::VariantDef<'tcx>, field: &Spanned, - tcx: &ty::ctxt<'tcx>, skip : Vec) { let name = field.node.as_str(); + let names = variant.fields + .iter() + .filter_map(|ref field| { + // ignore already set fields and private fields from non-local crates + if skip.iter().any(|x| *x == field.name.as_str()) || + (variant.did.krate != LOCAL_CRATE && field.vis != Visibility::Public) { + None + } else { + Some(&field.name) + } + }); + // only find fits with at least one matching letter - let mut best_dist = name.len(); - let mut best = None; - for elem in &variant.fields { - let n = elem.name.as_str(); - // ignore already set fields - if skip.iter().any(|x| *x == n) { - continue; - } - // ignore private fields from non-local crates - if variant.did.krate != LOCAL_CRATE && elem.vis != Visibility::Public { - continue; - } - let dist = lev_distance(&n, &name); - if dist < best_dist { - best = Some(n); - best_dist = dist; - } - } - if let Some(n) = best { - tcx.sess.span_help(field.span, - &format!("did you mean `{}`?", n)); + if let Some(name) = find_best_match_for_name(names, &name, Some(name.len())) { + err.span_help(field.span, + &format!("did you mean `{}`?", name)); } } @@ -3077,7 +3086,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, variant: ty::VariantDef<'tcx>, field: &hir::Field, skip_fields: &[hir::Field]) { - fcx.type_error_message( + let mut err = fcx.type_error_struct( field.name.span, |actual| if let ty::TyEnum(..) = ty.sty { format!("struct variant `{}::{}` has no field named `{}`", @@ -3090,7 +3099,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, None); // prevent all specified fields from being suggested let skip_fields = skip_fields.iter().map(|ref x| x.name.node.as_str()); - suggest_field_names(variant, &field.name, fcx.tcx(), skip_fields.collect()); + suggest_field_names(&mut err, variant, &field.name, skip_fields.collect()); + err.emit(); } fn check_expr_struct_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, @@ -4152,9 +4162,9 @@ pub fn check_representable(tcx: &ty::ctxt, // caught by case 1. match rty.is_representable(tcx, sp) { Representability::SelfRecursive => { - span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation); - tcx.sess.fileline_help( - sp, "wrap the inner value in a box to make it representable"); + struct_span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation) + .fileline_help(sp, "wrap the inner value in a box to make it representable") + .emit(); return false } Representability::Representable | Representability::ContainsRecursive => (), @@ -4251,11 +4261,12 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, // Check for duplicate discriminant values match disr_vals.iter().position(|&x| x == current_disr_val) { Some(i) => { - span_err!(ccx.tcx.sess, v.span, E0081, + let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0081, "discriminant value `{}` already exists", disr_vals[i]); let variant_i_node_id = ccx.tcx.map.as_local_node_id(variants[i].did).unwrap(); - span_note!(ccx.tcx.sess, ccx.tcx.map.span(variant_i_node_id), - "conflicting discriminant here") + span_note!(&mut err, ccx.tcx.map.span(variant_i_node_id), + "conflicting discriminant here"); + err.emit(); } None => {} } @@ -4264,10 +4275,11 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, attr::ReprAny | attr::ReprExtern => (), attr::ReprInt(sp, ity) => { if !disr_in_range(ccx, ity, current_disr_val) { - span_err!(ccx.tcx.sess, v.span, E0082, + let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0082, "discriminant value outside specified type"); - span_note!(ccx.tcx.sess, sp, + span_note!(&mut err, sp, "discriminant type specified here"); + err.emit(); } } attr::ReprSimd => { diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 0c65f68f02e3e..f4841b75d13d5 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -19,7 +19,7 @@ use super::{ FnCtxt, }; use middle::def_id::DefId; -use middle::ty::{Ty, HasTypeFlags, PreferMutLvalue}; +use middle::ty::{Ty, TypeFoldable, PreferMutLvalue}; use syntax::ast; use syntax::parse::token; use rustc_front::hir; @@ -187,10 +187,10 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, hir_util::binop_to_string(op.node), lhs_ty); } else { - span_err!(fcx.tcx().sess, lhs_expr.span, E0369, - "binary operation `{}` cannot be applied to type `{}`", - hir_util::binop_to_string(op.node), - lhs_ty); + let mut err = struct_span_err!(fcx.tcx().sess, lhs_expr.span, E0369, + "binary operation `{}` cannot be applied to type `{}`", + hir_util::binop_to_string(op.node), + lhs_ty); let missing_trait = match op.node { hir::BiAdd => Some("std::ops::Add"), hir::BiSub => Some("std::ops::Sub"), @@ -208,10 +208,11 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, }; if let Some(missing_trait) = missing_trait { - span_note!(fcx.tcx().sess, lhs_expr.span, + span_note!(&mut err, lhs_expr.span, "an implementation of `{}` might be missing for `{}`", missing_trait, lhs_ty); } + err.emit(); } } fcx.tcx().types.err diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 759d561a961b7..47cd31d9898d1 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -61,7 +61,7 @@ //! //! struct Foo { i: i32 } //! struct Bar { foo: Foo } -//! fn get_i(x: &'a Bar) -> &'a i32 { +//! fn get_i<'a>(x: &'a Bar) -> &'a i32 { //! let foo = &x.foo; // Lifetime L1 //! &foo.i // Lifetime L2 //! } @@ -92,7 +92,7 @@ use middle::mem_categorization::Categorization; use middle::region::{self, CodeExtent}; use middle::subst::Substs; use middle::traits; -use middle::ty::{self, RegionEscape, Ty, MethodCall, HasTypeFlags}; +use middle::ty::{self, Ty, MethodCall, TypeFoldable}; use middle::infer::{self, GenericKind, InferCtxt, SubregionOrigin, TypeOrigin, VerifyBound}; use middle::pat_util; use middle::ty::adjustment; diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 5d0e713012f2a..4f3f716c20d61 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -23,6 +23,7 @@ use std::cell::RefCell; use std::collections::HashSet; use syntax::ast; use syntax::codemap::{Span}; +use syntax::errors::DiagnosticBuilder; use syntax::parse::token::{special_idents}; use rustc_front::intravisit::{self, Visitor}; use rustc_front::hir; @@ -136,9 +137,10 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { let item = fcx.tcx().impl_or_trait_item(fcx.tcx().map.local_def_id(item_id)); - let mut implied_bounds = match item.container() { - ty::TraitContainer(_) => vec![], - ty::ImplContainer(def_id) => impl_implied_bounds(fcx, def_id, span) + let (mut implied_bounds, self_ty) = match item.container() { + ty::TraitContainer(_) => (vec![], fcx.tcx().mk_self_type()), + ty::ImplContainer(def_id) => (impl_implied_bounds(fcx, def_id, span), + fcx.tcx().lookup_item_type(def_id).ty) }; match item { @@ -152,6 +154,8 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { let predicates = fcx.instantiate_bounds(span, free_substs, &method.predicates); this.check_fn_or_method(fcx, span, &method_ty, &predicates, free_id_outlive, &mut implied_bounds); + this.check_method_receiver(fcx, span, &method, + free_id_outlive, self_ty); } ty::TypeTraitItem(assoc_type) => { if let Some(ref ty) = assoc_type.ty { @@ -377,6 +381,47 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { self.check_where_clauses(fcx, span, predicates); } + fn check_method_receiver<'fcx>(&mut self, + fcx: &FnCtxt<'fcx,'tcx>, + span: Span, + method: &ty::Method<'tcx>, + free_id_outlive: CodeExtent, + self_ty: ty::Ty<'tcx>) + { + // check that the type of the method's receiver matches the + // method's first parameter. + + let free_substs = &fcx.inh.infcx.parameter_environment.free_substs; + let fty = fcx.instantiate_type_scheme(span, free_substs, &method.fty); + let sig = fcx.tcx().liberate_late_bound_regions(free_id_outlive, &fty.sig); + + debug!("check_method_receiver({:?},cat={:?},self_ty={:?},sig={:?})", + method.name, method.explicit_self, self_ty, sig); + + let rcvr_ty = match method.explicit_self { + ty::ExplicitSelfCategory::Static => return, + ty::ExplicitSelfCategory::ByValue => self_ty, + ty::ExplicitSelfCategory::ByReference(region, mutability) => { + fcx.tcx().mk_ref(fcx.tcx().mk_region(region), ty::TypeAndMut { + ty: self_ty, + mutbl: mutability + }) + } + ty::ExplicitSelfCategory::ByBox => fcx.tcx().mk_box(self_ty) + }; + let rcvr_ty = fcx.instantiate_type_scheme(span, free_substs, &rcvr_ty); + let rcvr_ty = fcx.tcx().liberate_late_bound_regions(free_id_outlive, + &ty::Binder(rcvr_ty)); + + debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty); + + let _ = ::require_same_types( + fcx.tcx(), Some(fcx.infcx()), false, span, + sig.inputs[0], rcvr_ty, + || "mismatched method receiver".to_owned() + ); + } + fn check_variances_for_type_defn(&self, item: &hir::Item, ast_generics: &hir::Generics) @@ -452,12 +497,12 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { span: Span, param_name: ast::Name) { - error_392(self.tcx(), span, param_name); + let mut err = error_392(self.tcx(), span, param_name); let suggested_marker_id = self.tcx().lang_items.phantom_data(); match suggested_marker_id { Some(def_id) => { - self.tcx().sess.fileline_help( + err.fileline_help( span, &format!("consider removing `{}` or using a marker such as `{}`", param_name, @@ -467,6 +512,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { // no lang items, no help! } } + err.emit(); } } @@ -577,9 +623,10 @@ pub fn error_380<'ccx,'tcx>(ccx: &'ccx CrateCtxt<'ccx, 'tcx>, span: Span) { Trait for ..`) must have no methods or associated items") } -pub fn error_392<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, param_name: ast::Name) { - span_err!(tcx.sess, span, E0392, - "parameter `{}` is never used", param_name); +pub fn error_392<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, param_name: ast::Name) + -> DiagnosticBuilder<'tcx> { + struct_span_err!(tcx.sess, span, E0392, + "parameter `{}` is never used", param_name) } pub fn error_194<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, name: ast::Name) { diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index c24a416a0109c..c2abb074efa13 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -17,7 +17,7 @@ use astconv::AstConv; use check::FnCtxt; use middle::def_id::DefId; use middle::pat_util; -use middle::ty::{self, Ty, MethodCall, MethodCallee, HasTypeFlags}; +use middle::ty::{self, Ty, MethodCall, MethodCallee}; use middle::ty::adjustment; use middle::ty::fold::{TypeFolder,TypeFoldable}; use middle::infer; @@ -129,13 +129,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { !self.fcx.expr_ty(lhs).references_error() && !self.fcx.expr_ty(rhs).references_error() { - tcx.sess.span_err( - e.span, - "overloaded augmented assignments are not stable"); - fileline_help!( - tcx.sess, e.span, - "add #![feature(augmented_assignments)] to the crate root \ - to enable"); + tcx.sess.struct_span_err(e.span, + "overloaded augmented assignments \ + are not stable") + .fileline_help(e.span, + "add #![feature(augmented_assignments)] to the \ + crate root to enable") + .emit() } } } diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 37bbfb4e967f7..2c8fedb46a7b7 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -20,8 +20,7 @@ use middle::def_id::DefId; use middle::lang_items::UnsizeTraitLangItem; use middle::subst::{self, Subst}; use middle::traits; -use middle::ty; -use middle::ty::RegionEscape; +use middle::ty::{self, TypeFoldable}; use middle::ty::{ImplOrTraitItemId, ConstTraitItemId}; use middle::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment}; use middle::ty::{Ty, TyBool, TyChar, TyEnum, TyError}; @@ -39,9 +38,10 @@ use std::rc::Rc; use syntax::codemap::Span; use syntax::parse::token; use util::nodemap::{DefIdMap, FnvHashMap}; +use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; use rustc_front::intravisit; -use rustc_front::hir::{Item, ItemImpl,Crate}; +use rustc_front::hir::{Item, ItemImpl}; use rustc_front::hir; mod orphan; @@ -104,11 +104,13 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CoherenceCheckVisitor<'a, 'tcx> { } impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { - fn check(&self, krate: &Crate) { + fn check(&self) { // Check implementations and traits. This populates the tables // containing the inherent methods and extension methods. It also // builds up the trait inheritance table. - krate.visit_all_items(&mut CoherenceCheckVisitor { cc: self }); + self.crate_context.tcx.visit_all_items_in_krate( + DepNode::CoherenceCheckImpl, + &mut CoherenceCheckVisitor { cc: self }); // Copy over the inherent impls we gathered up during the walk into // the tcx. @@ -502,17 +504,24 @@ fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id: } else { return // everything OK }; - span_err!(tcx.sess, sp, E0183, "manual implementations of `{}` are experimental", trait_name); - fileline_help!(tcx.sess, sp, - "add `#![feature(unboxed_closures)]` to the crate attributes to enable"); + let mut err = struct_span_err!(tcx.sess, + sp, + E0183, + "manual implementations of `{}` are experimental", + trait_name); + fileline_help!(&mut err, sp, + "add `#![feature(unboxed_closures)]` to the crate attributes to enable"); + err.emit(); } pub fn check_coherence(crate_context: &CrateCtxt) { + let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence); + let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true); CoherenceChecker { crate_context: crate_context, - inference_context: new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true), + inference_context: infcx, inherent_impls: RefCell::new(FnvHashMap()), - }.check(crate_context.tcx.map.krate()); + }.check(); unsafety::check(crate_context.tcx); orphan::check(crate_context.tcx); overlap::check(crate_context.tcx); diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs index 411e1a4e480fc..69eb7f51f3785 100644 --- a/src/librustc_typeck/coherence/orphan.rs +++ b/src/librustc_typeck/coherence/orphan.rs @@ -17,12 +17,13 @@ use middle::traits; use middle::ty; use syntax::ast; use syntax::codemap::Span; +use rustc::dep_graph::DepNode; use rustc_front::intravisit; use rustc_front::hir; pub fn check(tcx: &ty::ctxt) { let mut orphan = OrphanChecker { tcx: tcx }; - tcx.map.krate().visit_all_items(&mut orphan); + tcx.visit_all_items_in_krate(DepNode::CoherenceOrphanCheck, &mut orphan); } struct OrphanChecker<'cx, 'tcx:'cx> { @@ -48,11 +49,11 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> { match lang_def_id { Some(lang_def_id) if lang_def_id == impl_def_id => { /* OK */ }, _ => { - span_err!(self.tcx.sess, span, E0390, + struct_span_err!(self.tcx.sess, span, E0390, "only a single inherent implementation marked with `#[lang = \"{}\"]` \ - is allowed for the `{}` primitive", lang, ty); - span_help!(self.tcx.sess, span, - "consider using a trait to implement these methods"); + is allowed for the `{}` primitive", lang, ty) + .span_help(span, "consider using a trait to implement these methods") + .emit(); } } } @@ -234,10 +235,10 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> { } Err(traits::OrphanCheckErr::UncoveredTy(param_ty)) => { span_err!(self.tcx.sess, item.span, E0210, - "type parameter `{}` must be used as the type parameter for \ - some local type (e.g. `MyStruct`); only traits defined in \ - the current crate can be implemented for a type parameter", - param_ty); + "type parameter `{}` must be used as the type parameter for \ + some local type (e.g. `MyStruct`); only traits defined in \ + the current crate can be implemented for a type parameter", + param_ty); return; } } diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 7d1f71967a2d5..71c6fc1fd08ec 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -18,53 +18,53 @@ use middle::ty; use middle::infer; use syntax::ast; use syntax::codemap::Span; +use rustc::dep_graph::DepNode; use rustc_front::hir; use rustc_front::intravisit; -use util::nodemap::DefIdMap; +use util::nodemap::{DefIdMap, DefIdSet}; pub fn check(tcx: &ty::ctxt) { - let mut overlap = OverlapChecker { tcx: tcx, default_impls: DefIdMap() }; - overlap.check_for_overlapping_impls(); + let mut overlap = OverlapChecker { tcx: tcx, + traits_checked: DefIdSet(), + default_impls: DefIdMap() }; // this secondary walk specifically checks for some other cases, // like defaulted traits, for which additional overlap rules exist - tcx.map.krate().visit_all_items(&mut overlap); + tcx.visit_all_items_in_krate(DepNode::CoherenceOverlapCheckSpecial, &mut overlap); } struct OverlapChecker<'cx, 'tcx:'cx> { tcx: &'cx ty::ctxt<'tcx>, + // The set of traits where we have checked for overlap. This is + // used to avoid checking the same trait twice. + // + // NB. It's ok to skip tracking this set because we fully + // encapsulate it, and we always create a task + // (`CoherenceOverlapCheck`) corresponding to each entry. + traits_checked: DefIdSet, + // maps from a trait def-id to an impl id default_impls: DefIdMap, } impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { - fn check_for_overlapping_impls(&self) { - debug!("check_for_overlapping_impls"); - - // Collect this into a vector to avoid holding the - // refcell-lock during the - // check_for_overlapping_impls_of_trait() check, since that - // check can populate this table further with impls from other - // crates. - let trait_defs: Vec<_> = self.tcx.trait_defs.borrow().values().cloned().collect(); - - for trait_def in trait_defs { - self.tcx.populate_implementations_for_trait_if_necessary(trait_def.trait_ref.def_id); - self.check_for_overlapping_impls_of_trait(trait_def); + fn check_for_overlapping_impls_of_trait(&mut self, trait_def_id: DefId) { + debug!("check_for_overlapping_impls_of_trait(trait_def_id={:?})", + trait_def_id); + + let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id)); + if !self.traits_checked.insert(trait_def_id) { + return; } - } - fn check_for_overlapping_impls_of_trait(&self, - trait_def: &'tcx ty::TraitDef<'tcx>) - { - debug!("check_for_overlapping_impls_of_trait(trait_def={:?})", - trait_def); + let trait_def = self.tcx.lookup_trait_def(trait_def_id); + self.tcx.populate_implementations_for_trait_if_necessary( + trait_def.trait_ref.def_id); // We should already know all impls of this trait, so these // borrows are safe. - let blanket_impls = trait_def.blanket_impls.borrow(); - let nonblanket_impls = trait_def.nonblanket_impls.borrow(); + let (blanket_impls, nonblanket_impls) = trait_def.borrow_impl_lists(self.tcx); // Conflicts can only occur between a blanket impl and another impl, // or between 2 non-blanket impls of the same kind. @@ -150,18 +150,19 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { }).unwrap_or(String::new()) }; - span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119, - "conflicting implementations of trait `{}`{}:", - trait_ref, - self_type); + let mut err = struct_span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119, + "conflicting implementations of trait `{}`{}:", + trait_ref, + self_type); if impl2.is_local() { - span_note!(self.tcx.sess, self.span_of_impl(impl2), + span_note!(&mut err, self.span_of_impl(impl2), "conflicting implementation is here:"); } else { let cname = self.tcx.sess.cstore.crate_name(impl2.krate); - self.tcx.sess.note(&format!("conflicting implementation in crate `{}`", cname)); + err.note(&format!("conflicting implementation in crate `{}`", cname)); } + err.emit(); } fn span_of_impl(&self, impl_did: DefId) -> Span { @@ -174,12 +175,20 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { fn visit_item(&mut self, item: &'v hir::Item) { match item.node { - hir::ItemDefaultImpl(_, _) => { + hir::ItemTrait(..) => { + let trait_def_id = self.tcx.map.local_def_id(item.id); + self.check_for_overlapping_impls_of_trait(trait_def_id); + } + + hir::ItemDefaultImpl(..) => { // look for another default impl; note that due to the // general orphan/coherence rules, it must always be // in this crate. let impl_def_id = self.tcx.map.local_def_id(item.id); let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); + + self.check_for_overlapping_impls_of_trait(trait_ref.def_id); + let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id); match prev_default_impl { Some(prev_id) => { @@ -194,6 +203,7 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { let impl_def_id = self.tcx.map.local_def_id(item.id); let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; + self.check_for_overlapping_impls_of_trait(trait_def_id); match trait_ref.self_ty().sty { ty::TyTrait(ref data) => { // This is something like impl Trait1 for Trait2. Illegal diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index bfe4151da5c91..fbbd2f02908f1 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -34,13 +34,12 @@ lazilly and on demand, and include logic that checks for cycles. Demand is driven by calls to `AstConv::get_item_type_scheme` or `AstConv::lookup_trait_def`. -Currently, we "convert" types and traits in three phases (note that +Currently, we "convert" types and traits in two phases (note that conversion only affects the types of items / enum variants / methods; it does not e.g. compute the types of individual expressions): 0. Intrinsics -1. Trait definitions -2. Type definitions +1. Trait/Type definitions Conversion itself is done by simply walking each of the items in turn and invoking an appropriate function (e.g., `trait_def_of_item` or @@ -56,38 +55,32 @@ There are some shortcomings in this design: - Because the type scheme includes defaults, cycles through type parameter defaults are illegal even if those defaults are never employed. This is not necessarily a bug. -- The phasing of trait definitions before type definitions does not - seem to be necessary, sufficient, or particularly helpful, given that - processing a trait definition can trigger processing a type def and - vice versa. However, if I remove it, I get ICEs, so some more work is - needed in that area. -nmatsakis */ use astconv::{self, AstConv, ty_of_arg, ast_ty_to_ty, ast_region_to_region}; +use lint; use middle::def; use middle::def_id::DefId; use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; -use middle::free_region::FreeRegionMap; -use middle::region; use middle::resolve_lifetime; use middle::const_eval::{self, ConstVal}; use middle::const_eval::EvalHint::UncheckedExprHint; use middle::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace}; use middle::ty::{ToPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer}; -use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty, TypeScheme}; +use middle::ty::{self, ToPolyTraitRef, Ty, TypeScheme}; use middle::ty::{VariantKind}; -use middle::ty::fold::{TypeFolder, TypeFoldable}; +use middle::ty::fold::{TypeFolder}; use middle::ty::util::IntTypeExt; -use middle::infer; use rscope::*; +use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; -use util::common::{ErrorReported, memoized}; +use util::common::{ErrorReported, MemoizationMap}; use util::nodemap::{FnvHashMap, FnvHashSet}; use write_ty_to_tcx; -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; use std::collections::HashSet; use std::rc::Rc; @@ -107,9 +100,6 @@ use rustc_front::print::pprust; pub fn collect_item_types(tcx: &ty::ctxt) { let ccx = &CrateCtxt { tcx: tcx, stack: RefCell::new(Vec::new()) }; - let mut visitor = CollectTraitDefVisitor{ ccx: ccx }; - ccx.tcx.map.krate().visit_all_items(&mut visitor); - let mut visitor = CollectItemTypesVisitor{ ccx: ccx }; ccx.tcx.map.krate().visit_all_items(&mut visitor); } @@ -149,41 +139,17 @@ enum AstConvRequest { } /////////////////////////////////////////////////////////////////////////// -// First phase: just collect *trait definitions* -- basically, the set -// of type parameters and supertraits. This is information we need to -// know later when parsing field defs. - -struct CollectTraitDefVisitor<'a, 'tcx: 'a> { - ccx: &'a CrateCtxt<'a, 'tcx> -} - -impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CollectTraitDefVisitor<'a, 'tcx> { - fn visit_item(&mut self, i: &hir::Item) { - match i.node { - hir::ItemTrait(..) => { - // computing the trait def also fills in the table - let _ = trait_def_of_item(self.ccx, i); - } - _ => { } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Second phase: collection proper. struct CollectItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> } impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CollectItemTypesVisitor<'a, 'tcx> { - fn visit_item(&mut self, i: &hir::Item) { - convert_item(self.ccx, i); - intravisit::walk_item(self, i); - } - fn visit_foreign_item(&mut self, i: &hir::ForeignItem) { - convert_foreign_item(self.ccx, i); - intravisit::walk_foreign_item(self, i); + fn visit_item(&mut self, item: &hir::Item) { + let tcx = self.ccx.tcx; + let item_def_id = tcx.map.local_def_id(item.id); + let _task = tcx.dep_graph.in_task(DepNode::CollectItem(item_def_id)); + convert_item(self.ccx, item); } } @@ -195,16 +161,6 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> { ItemCtxt { ccx: self, param_bounds: param_bounds } } - fn method_ty(&self, method_id: ast::NodeId) -> Rc> { - let def_id = self.tcx.map.local_def_id(method_id); - match *self.tcx.impl_or_trait_items.borrow().get(&def_id).unwrap() { - ty::MethodTraitItem(ref mty) => mty.clone(), - _ => { - self.tcx.sess.bug(&format!("method with id {} has the wrong type", method_id)); - } - } - } - fn cycle_check(&self, span: Span, request: AstConvRequest, @@ -238,24 +194,24 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> { assert!(!cycle.is_empty()); let tcx = self.tcx; - span_err!(tcx.sess, span, E0391, + let mut err = struct_span_err!(tcx.sess, span, E0391, "unsupported cyclic reference between types/traits detected"); match cycle[0] { AstConvRequest::GetItemTypeScheme(def_id) | AstConvRequest::GetTraitDef(def_id) => { - tcx.sess.note( + err.note( &format!("the cycle begins when processing `{}`...", tcx.item_path_str(def_id))); } AstConvRequest::EnsureSuperPredicates(def_id) => { - tcx.sess.note( + err.note( &format!("the cycle begins when computing the supertraits of `{}`...", tcx.item_path_str(def_id))); } AstConvRequest::GetTypeParameterBounds(id) => { let def = tcx.type_parameter_def(id); - tcx.sess.note( + err.note( &format!("the cycle begins when computing the bounds \ for type parameter `{}`...", def.name)); @@ -266,18 +222,18 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> { match *request { AstConvRequest::GetItemTypeScheme(def_id) | AstConvRequest::GetTraitDef(def_id) => { - tcx.sess.note( + err.note( &format!("...which then requires processing `{}`...", tcx.item_path_str(def_id))); } AstConvRequest::EnsureSuperPredicates(def_id) => { - tcx.sess.note( + err.note( &format!("...which then requires computing the supertraits of `{}`...", tcx.item_path_str(def_id))); } AstConvRequest::GetTypeParameterBounds(id) => { let def = tcx.type_parameter_def(id); - tcx.sess.note( + err.note( &format!("...which then requires computing the bounds \ for type parameter `{}`...", def.name)); @@ -288,24 +244,25 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> { match cycle[0] { AstConvRequest::GetItemTypeScheme(def_id) | AstConvRequest::GetTraitDef(def_id) => { - tcx.sess.note( + err.note( &format!("...which then again requires processing `{}`, completing the cycle.", tcx.item_path_str(def_id))); } AstConvRequest::EnsureSuperPredicates(def_id) => { - tcx.sess.note( + err.note( &format!("...which then again requires computing the supertraits of `{}`, \ completing the cycle.", tcx.item_path_str(def_id))); } AstConvRequest::GetTypeParameterBounds(id) => { let def = tcx.type_parameter_def(id); - tcx.sess.note( + err.note( &format!("...which then again requires computing the bounds \ for type parameter `{}`, completing the cycle.", def.name)); } } + err.emit(); } /// Loads the trait def for a given trait, returning ErrorReported if a cycle arises. @@ -573,10 +530,10 @@ fn is_param<'tcx>(tcx: &ty::ctxt<'tcx>, fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, container: ImplOrTraitItemContainer, - sig: &hir::MethodSig, - id: ast::NodeId, name: ast::Name, + id: ast::NodeId, vis: hir::Visibility, + sig: &hir::MethodSig, untransformed_rcvr_ty: Ty<'tcx>, rcvr_ty_generics: &ty::Generics<'tcx>, rcvr_ty_predicates: &ty::GenericPredicates<'tcx>) { @@ -681,33 +638,6 @@ fn convert_associated_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, .insert(ccx.tcx.map.local_def_id(id), ty::TypeTraitItem(associated_type)); } -fn convert_methods<'a,'tcx,'i,I>(ccx: &CrateCtxt<'a, 'tcx>, - container: ImplOrTraitItemContainer, - methods: I, - untransformed_rcvr_ty: Ty<'tcx>, - rcvr_ty_generics: &ty::Generics<'tcx>, - rcvr_ty_predicates: &ty::GenericPredicates<'tcx>) - where I: Iterator -{ - debug!("convert_methods(untransformed_rcvr_ty={:?}, rcvr_ty_generics={:?}, \ - rcvr_ty_predicates={:?})", - untransformed_rcvr_ty, - rcvr_ty_generics, - rcvr_ty_predicates); - - for (sig, id, name, vis, _span) in methods { - convert_method(ccx, - container, - sig, - id, - name, - vis, - untransformed_rcvr_ty, - rcvr_ty_generics, - rcvr_ty_predicates); - } -} - fn ensure_no_ty_param_bounds(ccx: &CrateCtxt, span: Span, generics: &hir::Generics, @@ -742,8 +672,12 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { debug!("convert: item {} with id {}", it.name, it.id); match it.node { // These don't define types. - hir::ItemExternCrate(_) | hir::ItemUse(_) | - hir::ItemForeignMod(_) | hir::ItemMod(_) => { + hir::ItemExternCrate(_) | hir::ItemUse(_) | hir::ItemMod(_) => { + } + hir::ItemForeignMod(ref foreign_mod) => { + for item in &foreign_mod.items { + convert_foreign_item(ccx, item); + } } hir::ItemEnum(ref enum_definition, _) => { let (scheme, predicates) = convert_typed_item(ccx, it); @@ -867,36 +801,17 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { } } - let methods = impl_items.iter().filter_map(|ii| { - if let hir::ImplItemKind::Method(ref sig, _) = ii.node { + for impl_item in impl_items { + if let hir::ImplItemKind::Method(ref sig, _) = impl_item.node { // if the method specifies a visibility, use that, otherwise // inherit the visibility from the impl (so `foo` in `pub impl // { fn foo(); }` is public, but private in `impl { fn // foo(); }`). - let method_vis = ii.vis.inherit_from(parent_visibility); - Some((sig, ii.id, ii.name, method_vis, ii.span)) - } else { - None - } - }); - convert_methods(ccx, - ImplContainer(def_id), - methods, - selfty, - &ty_generics, - &ty_predicates); + let method_vis = impl_item.vis.inherit_from(parent_visibility); - for impl_item in impl_items { - if let hir::ImplItemKind::Method(ref sig, ref body) = impl_item.node { - let body_id = body.id; - let body_scope = ccx.tcx.region_maps.call_site_extent(impl_item.id, body_id); - check_method_self_type(ccx, - &BindingRscope::new(), - ccx.method_ty(impl_item.id), - selfty, - &sig.explicit_self, - body_scope, - body_id); + convert_method(ccx, ImplContainer(def_id), + impl_item.name, impl_item.id, method_vis, + sig, selfty, &ty_generics, &ty_predicates); } } @@ -904,112 +819,80 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { }, hir::ItemTrait(_, _, _, ref trait_items) => { let trait_def = trait_def_of_item(ccx, it); + let def_id = trait_def.trait_ref.def_id; let _: Result<(), ErrorReported> = // any error is already reported, can ignore - ccx.ensure_super_predicates(it.span, ccx.tcx.map.local_def_id(it.id)); + ccx.ensure_super_predicates(it.span, def_id); convert_trait_predicates(ccx, it); - let trait_predicates = tcx.lookup_predicates(ccx.tcx.map.local_def_id(it.id)); + let trait_predicates = tcx.lookup_predicates(def_id); debug!("convert: trait_bounds={:?}", trait_predicates); - // Convert all the associated types. + // FIXME: is the ordering here important? I think it is. + let container = TraitContainer(def_id); + + // Convert all the associated constants. for trait_item in trait_items { - match trait_item.node { - hir::ConstTraitItem(ref ty, ref default) => { - let ty = ccx.icx(&trait_predicates) - .to_ty(&ExplicitRscope, ty); - tcx.register_item_type(ccx.tcx.map.local_def_id(trait_item.id), - TypeScheme { - generics: trait_def.generics.clone(), - ty: ty, - }); - convert_associated_const(ccx, - TraitContainer(ccx.tcx.map.local_def_id(it.id)), - trait_item.name, - trait_item.id, - hir::Public, - ty, - default.is_some()) - } - _ => {} + if let hir::ConstTraitItem(ref ty, ref default) = trait_item.node { + let ty = ccx.icx(&trait_predicates) + .to_ty(&ExplicitRscope, ty); + tcx.register_item_type(ccx.tcx.map.local_def_id(trait_item.id), + TypeScheme { + generics: trait_def.generics.clone(), + ty: ty, + }); + convert_associated_const(ccx, + container, + trait_item.name, + trait_item.id, + hir::Public, + ty, + default.is_some()) } - }; + } // Convert all the associated types. for trait_item in trait_items { - match trait_item.node { - hir::TypeTraitItem(_, ref opt_ty) => { - let typ = opt_ty.as_ref().map({ - |ty| ccx.icx(&trait_predicates).to_ty(&ExplicitRscope, &ty) - }); - - convert_associated_type(ccx, - TraitContainer(ccx.tcx.map.local_def_id(it.id)), - trait_item.name, - trait_item.id, - hir::Public, - typ); - } - _ => {} + if let hir::TypeTraitItem(_, ref opt_ty) = trait_item.node { + let typ = opt_ty.as_ref().map({ + |ty| ccx.icx(&trait_predicates).to_ty(&ExplicitRscope, &ty) + }); + + convert_associated_type(ccx, + container, + trait_item.name, + trait_item.id, + hir::Public, + typ); } - }; + } - let methods = trait_items.iter().filter_map(|ti| { - let sig = match ti.node { - hir::MethodTraitItem(ref sig, _) => sig, - _ => return None, - }; - Some((sig, ti.id, ti.name, hir::Inherited, ti.span)) - }); + // Convert all the methods + for trait_item in trait_items { + if let hir::MethodTraitItem(ref sig, _) = trait_item.node { + convert_method(ccx, + container, + trait_item.name, + trait_item.id, + hir::Inherited, + sig, + tcx.mk_self_type(), + &trait_def.generics, + &trait_predicates); - // Run convert_methods on the trait methods. - convert_methods(ccx, - TraitContainer(ccx.tcx.map.local_def_id(it.id)), - methods, - tcx.mk_self_type(), - &trait_def.generics, - &trait_predicates); + } + } // Add an entry mapping let trait_item_def_ids = Rc::new(trait_items.iter().map(|trait_item| { let def_id = ccx.tcx.map.local_def_id(trait_item.id); match trait_item.node { - hir::ConstTraitItem(..) => { - ty::ConstTraitItemId(def_id) - } - hir::MethodTraitItem(..) => { - ty::MethodTraitItemId(def_id) - } - hir::TypeTraitItem(..) => { - ty::TypeTraitItemId(def_id) - } + hir::ConstTraitItem(..) => ty::ConstTraitItemId(def_id), + hir::MethodTraitItem(..) => ty::MethodTraitItemId(def_id), + hir::TypeTraitItem(..) => ty::TypeTraitItemId(def_id) } }).collect()); tcx.trait_item_def_ids.borrow_mut().insert(ccx.tcx.map.local_def_id(it.id), trait_item_def_ids); - - // This must be done after `collect_trait_methods` so that - // we have a method type stored for every method. - for trait_item in trait_items { - let (sig, the_scope, the_id) = match trait_item.node { - hir::MethodTraitItem(ref sig, Some(ref body)) => { - let body_scope = - ccx.tcx.region_maps.call_site_extent(trait_item.id, body.id); - (sig, body_scope, body.id) - } - hir::MethodTraitItem(ref sig, None) => { - let item_scope = ccx.tcx.region_maps.item_extent(trait_item.id); - (sig, item_scope, it.id) - } - _ => continue - }; - check_method_self_type(ccx, - &BindingRscope::new(), - ccx.method_ty(trait_item.id), - tcx.mk_self_type(), - &sig.explicit_self, - the_scope, - the_id) - } }, hir::ItemStruct(ref struct_def, _) => { let (scheme, predicates) = convert_typed_item(ccx, it); @@ -1103,10 +986,11 @@ fn convert_struct_variant<'tcx>(tcx: &ty::ctxt<'tcx>, hir::NamedField(name, vis) => { let dup_span = seen_fields.get(&name).cloned(); if let Some(prev_span) = dup_span { - span_err!(tcx.sess, f.span, E0124, - "field `{}` is already declared", - name); - span_note!(tcx.sess, prev_span, "previously declared here"); + let mut err = struct_span_err!(tcx.sess, f.span, E0124, + "field `{}` is already declared", + name); + span_note!(&mut err, prev_span, "previously declared here"); + err.emit(); } else { seen_fields.insert(name, f.span); } @@ -1171,12 +1055,13 @@ fn convert_enum_def<'tcx>(tcx: &ty::ctxt<'tcx>, None }, Err(err) => { - span_err!(tcx.sess, err.span, E0080, - "constant evaluation error: {}", - err.description()); + let mut diag = struct_span_err!(tcx.sess, err.span, E0080, + "constant evaluation error: {}", + err.description()); if !e.span.contains(err.span) { - tcx.sess.span_note(e.span, "for enum discriminant here"); + diag.span_note(e.span, "for enum discriminant here"); } + diag.emit(); None } } @@ -1345,13 +1230,14 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let paren_sugar = tcx.has_attr(def_id, "rustc_paren_sugar"); if paren_sugar && !ccx.tcx.sess.features.borrow().unboxed_closures { - ccx.tcx.sess.span_err( + let mut err = ccx.tcx.sess.struct_span_err( it.span, "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \ which traits can use parenthetical notation"); - fileline_help!(ccx.tcx.sess, it.span, + fileline_help!(&mut err, it.span, "add `#![feature(unboxed_closures)]` to \ the crate attributes to use it"); + err.emit(); } let substs = ccx.tcx.mk_substs(mk_trait_substs(ccx, generics)); @@ -1370,16 +1256,11 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, substs: substs, }; - let trait_def = ty::TraitDef { - paren_sugar: paren_sugar, - unsafety: unsafety, - generics: ty_generics, - trait_ref: trait_ref, - associated_type_names: associated_type_names, - nonblanket_impls: RefCell::new(FnvHashMap()), - blanket_impls: RefCell::new(vec![]), - flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS) - }; + let trait_def = ty::TraitDef::new(unsafety, + paren_sugar, + ty_generics, + trait_ref, + associated_type_names); return tcx.intern_trait_def(trait_def); @@ -1395,7 +1276,6 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, .iter() .enumerate() .map(|(i, def)| ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: tcx.map.local_def_id(def.lifetime.id), space: TypeSpace, index: i as u32, name: def.lifetime.name @@ -1540,12 +1420,17 @@ fn type_scheme_of_def_id<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } fn type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, - it: &hir::Item) + item: &hir::Item) -> ty::TypeScheme<'tcx> { - memoized(&ccx.tcx.tcache, - ccx.tcx.map.local_def_id(it.id), - |_| compute_type_scheme_of_item(ccx, it)) + let item_def_id = ccx.tcx.map.local_def_id(item.id); + ccx.tcx.tcache.memoize(item_def_id, || { + // NB. Since the `memoized` function enters a new task, and we + // are giving this task access to the item `item`, we must + // register a read. + ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); + compute_type_scheme_of_item(ccx, item) + }) } fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, @@ -1659,13 +1544,18 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fn type_scheme_of_foreign_item<'a, 'tcx>( ccx: &CrateCtxt<'a, 'tcx>, - it: &hir::ForeignItem, + item: &hir::ForeignItem, abi: abi::Abi) -> ty::TypeScheme<'tcx> { - memoized(&ccx.tcx.tcache, - ccx.tcx.map.local_def_id(it.id), - |_| compute_type_scheme_of_foreign_item(ccx, it, abi)) + let item_def_id = ccx.tcx.map.local_def_id(item.id); + ccx.tcx.tcache.memoize(item_def_id, || { + // NB. Since the `memoized` function enters a new task, and we + // are giving this task access to the item `item`, we must + // register a read. + ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); + compute_type_scheme_of_foreign_item(ccx, item, abi) + }) } fn compute_type_scheme_of_foreign_item<'a, 'tcx>( @@ -1870,10 +1760,8 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, let early_lifetimes = early_bound_lifetimes_from_generics(space, ast_generics); for (index, param) in early_lifetimes.iter().enumerate() { let index = index as u32; - let def_id = tcx.map.local_def_id(param.lifetime.id); let region = ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: def_id, space: space, index: index, name: param.lifetime.name @@ -2023,6 +1911,17 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, let parent = tcx.map.get_parent(param.id); + if space != TypeSpace && default.is_some() { + if !tcx.sess.features.borrow().default_type_parameter_fallback { + tcx.sess.add_lint( + lint::builtin::INVALID_TYPE_PARAM_DEFAULT, + param.id, + param.span, + format!("defaults for type parameters are only allowed \ + on `struct` or `enum` definitions (see issue #27336)")); + } + } + let def = ty::TypeParameterDef { space: space, index: index, @@ -2281,106 +2180,6 @@ fn mk_item_substs<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, Substs::new(types, regions) } -/// Verifies that the explicit self type of a method matches the impl -/// or trait. This is a bit weird but basically because right now we -/// don't handle the general case, but instead map it to one of -/// several pre-defined options using various heuristics, this method -/// comes back to check after the fact that explicit type the user -/// wrote actually matches what the pre-defined option said. -fn check_method_self_type<'a, 'tcx, RS:RegionScope>( - ccx: &CrateCtxt<'a, 'tcx>, - rs: &RS, - method_type: Rc>, - required_type: Ty<'tcx>, - explicit_self: &hir::ExplicitSelf, - body_scope: region::CodeExtent, - body_id: ast::NodeId) -{ - let tcx = ccx.tcx; - if let hir::SelfExplicit(ref ast_type, _) = explicit_self.node { - let typ = ccx.icx(&method_type.predicates).to_ty(rs, &**ast_type); - let base_type = match typ.sty { - ty::TyRef(_, tm) => tm.ty, - ty::TyBox(typ) => typ, - _ => typ, - }; - - // "Required type" comes from the trait definition. It may - // contain late-bound regions from the method, but not the - // trait (since traits only have early-bound region - // parameters). - assert!(!base_type.has_regions_escaping_depth(1)); - let required_type_free = - liberate_early_bound_regions( - tcx, body_scope, - &tcx.liberate_late_bound_regions(body_scope, &ty::Binder(required_type))); - - // The "base type" comes from the impl. It too may have late-bound - // regions from the method. - assert!(!base_type.has_regions_escaping_depth(1)); - let base_type_free = - liberate_early_bound_regions( - tcx, body_scope, - &tcx.liberate_late_bound_regions(body_scope, &ty::Binder(base_type))); - - debug!("required_type={:?} required_type_free={:?} \ - base_type={:?} base_type_free={:?}", - required_type, - required_type_free, - base_type, - base_type_free); - - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); - drop(::require_same_types(tcx, - Some(&infcx), - false, - explicit_self.span, - base_type_free, - required_type_free, - || { - format!("mismatched self type: expected `{}`", - required_type) - })); - - // We could conceviably add more free-region relations here, - // but since this code is just concerned with checking that - // the `&Self` types etc match up, it's not really necessary. - // It would just allow people to be more approximate in some - // cases. In any case, we can do it later as we feel the need; - // I'd like this function to go away eventually. - let free_regions = FreeRegionMap::new(); - - infcx.resolve_regions_and_report_errors(&free_regions, body_id); - } - - fn liberate_early_bound_regions<'tcx,T>( - tcx: &ty::ctxt<'tcx>, - scope: region::CodeExtent, - value: &T) - -> T - where T : TypeFoldable<'tcx> - { - /*! - * Convert early-bound regions into free regions; normally this is done by - * applying the `free_substs` from the `ParameterEnvironment`, but this particular - * method-self-type check is kind of hacky and done very early in the process, - * before we really have a `ParameterEnvironment` to check. - */ - - tcx.fold_regions(value, &mut false, |region, _| { - match region { - ty::ReEarlyBound(data) => { - ty::ReFree(ty::FreeRegion { - scope: scope, - bound_region: ty::BrNamed(data.def_id, data.name) - }) - } - _ => region - } - }) - } -} - /// Checks that all the type parameters on an impl fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>, ast_generics: &hir::Generics, @@ -2450,9 +2249,7 @@ fn enforce_impl_lifetimes_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>, .collect(); for (index, lifetime_def) in ast_generics.lifetimes.iter().enumerate() { - let def_id = tcx.map.local_def_id(lifetime_def.lifetime.id); - let region = ty::EarlyBoundRegion { def_id: def_id, - space: TypeSpace, + let region = ty::EarlyBoundRegion { space: TypeSpace, index: index as u32, name: lifetime_def.lifetime.name }; if diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 70c6d6f05c66a..867d12a1def92 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -92,6 +92,7 @@ extern crate rustc_platform_intrinsics as intrinsics; extern crate rustc_front; extern crate rustc_back; +pub use rustc::dep_graph; pub use rustc::front; pub use rustc::lint; pub use rustc::middle; @@ -102,7 +103,7 @@ use front::map as hir_map; use middle::def; use middle::infer::{self, TypeOrigin}; use middle::subst; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, TypeFoldable}; use session::config; use util::common::time; use rustc_front::hir; @@ -203,8 +204,9 @@ fn require_same_types<'a, 'tcx, M>(tcx: &ty::ctxt<'tcx>, match result { Ok(_) => true, Err(ref terr) => { - span_err!(tcx.sess, span, E0211, "{}: {}", msg(), terr); - tcx.note_and_explain_type_err(terr, span); + let mut err = struct_span_err!(tcx.sess, span, E0211, "{}: {}", msg(), terr); + tcx.note_and_explain_type_err(&mut err, terr, span); + err.emit(); false } } diff --git a/src/librustc_typeck/variance.rs b/src/librustc_typeck/variance.rs index 1fafe3484f0f2..ce0e9e14035f5 100644 --- a/src/librustc_typeck/variance.rs +++ b/src/librustc_typeck/variance.rs @@ -266,6 +266,7 @@ use self::ParamKind::*; use arena; use arena::TypedArena; +use dep_graph::DepNode; use middle::def_id::DefId; use middle::resolve_lifetime as rl; use middle::subst; @@ -280,6 +281,7 @@ use rustc_front::intravisit::Visitor; use util::nodemap::NodeMap; pub fn infer_variance(tcx: &ty::ctxt) { + let _task = tcx.dep_graph.in_task(DepNode::Variance); let krate = tcx.map.krate(); let mut arena = arena::TypedArena::new(); let terms_cx = determine_parameters_to_be_inferred(tcx, &mut arena, krate); @@ -1002,12 +1004,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { /// Adds constraints appropriate for a region appearing in a /// context with ambient variance `variance` fn add_constraints_from_region(&mut self, - _generics: &ty::Generics<'tcx>, + generics: &ty::Generics<'tcx>, region: ty::Region, variance: VarianceTermPtr<'a>) { match region { ty::ReEarlyBound(ref data) => { - let node_id = self.tcx().map.as_local_node_id(data.def_id).unwrap(); + let def_id = + generics.regions.get(data.space, data.index as usize).def_id; + let node_id = self.tcx().map.as_local_node_id(def_id).unwrap(); if self.is_to_be_inferred(node_id) { let index = self.inferred_index(node_id); self.add_constraint(index, variance); diff --git a/src/librustc_unicode/char.rs b/src/librustc_unicode/char.rs index 455e2feee4c2f..66f8068eae657 100644 --- a/src/librustc_unicode/char.rs +++ b/src/librustc_unicode/char.rs @@ -126,7 +126,7 @@ impl char { /// /// A 'radix' here is sometimes also called a 'base'. A radix of two /// indicates a binary number, a radix of ten, decimal, and a radix of - /// sixteen, hexicdecimal, to give some common values. Arbitrary + /// sixteen, hexadecimal, to give some common values. Arbitrary /// radicum are supported. /// /// Compared to `is_numeric()`, this function only recognizes the characters @@ -185,7 +185,7 @@ impl char { /// /// A 'radix' here is sometimes also called a 'base'. A radix of two /// indicates a binary number, a radix of ten, decimal, and a radix of - /// sixteen, hexicdecimal, to give some common values. Arbitrary + /// sixteen, hexadecimal, to give some common values. Arbitrary /// radicum are supported. /// /// 'Digit' is defined to be only the following characters: diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 675eef637b10b..d2a5fd457d2fb 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1306,16 +1306,16 @@ impl Clean for hir::ImplItem { impl<'tcx> Clean for ty::Method<'tcx> { fn clean(&self, cx: &DocContext) -> Item { let (self_, sig) = match self.explicit_self { - ty::StaticExplicitSelfCategory => (hir::SelfStatic.clean(cx), - self.fty.sig.clone()), + ty::ExplicitSelfCategory::Static => (hir::SelfStatic.clean(cx), + self.fty.sig.clone()), s => { let sig = ty::Binder(ty::FnSig { inputs: self.fty.sig.0.inputs[1..].to_vec(), ..self.fty.sig.0.clone() }); let s = match s { - ty::ByValueExplicitSelfCategory => SelfValue, - ty::ByReferenceExplicitSelfCategory(..) => { + ty::ExplicitSelfCategory::ByValue => SelfValue, + ty::ExplicitSelfCategory::ByReference(..) => { match self.fty.sig.0.inputs[0].sty { ty::TyRef(r, mt) => { SelfBorrowed(r.clean(cx), mt.mutbl.clean(cx)) @@ -1323,10 +1323,10 @@ impl<'tcx> Clean for ty::Method<'tcx> { _ => unreachable!(), } } - ty::ByBoxExplicitSelfCategory => { + ty::ExplicitSelfCategory::ByBox => { SelfExplicit(self.fty.sig.0.inputs[0].clean(cx)) } - ty::StaticExplicitSelfCategory => unreachable!(), + ty::ExplicitSelfCategory::Static => unreachable!(), }; (s, sig) } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index d7190a4bea974..d57d1bcd92da5 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -154,6 +154,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, &name, resolve::MakeGlobMap::No, |tcx, _, analysis| { + let _ignore = tcx.dep_graph.in_ignore(); let ty::CrateAnalysis { access_levels, .. } = analysis; // Convert from a NodeId set to a DefId set since we don't always have easy access diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 9b1db51bba50f..0bde582c19f28 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -143,6 +143,10 @@ pre { padding: 20px; } +img { + max-width: 100%; +} + .content.source { margin-top: 50px; max-width: none; diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index 62ec23ccb2000..41001153c3cc4 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -16,7 +16,7 @@ reason = "API has not been scrutinized and is highly likely to \ either disappear or change", issue = "27810")] -#![rustc_deprecated(since = "1.5.0", reason = "replaced with crates.io crates")] +#![rustc_deprecated(since = "1.5.0", reason = "replaced with 'dylib' on crates.io")] #![allow(missing_docs)] #![allow(deprecated)] @@ -132,6 +132,7 @@ mod tests { #[cfg_attr(any(windows, target_os = "android", // FIXME #10379 target_env = "musl"), ignore)] + #[allow(deprecated)] fn test_loading_cosine() { // The math library does not need to be loaded since it is already // statically linked in @@ -164,6 +165,7 @@ mod tests { target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"))] + #[allow(deprecated)] fn test_errors_do_not_crash() { // Open /dev/null as a library to get an error, and make sure // that only causes an error, and not a crash. diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 3f3913471b895..9a41272299e94 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -20,7 +20,7 @@ use iter::Iterator; use libc; use mem; use memchr; -use ops::Deref; +use ops; use option::Option::{self, Some, None}; use os::raw::c_char; use result::Result::{self, Ok, Err}; @@ -282,7 +282,7 @@ impl CString { } #[stable(feature = "rust1", since = "1.0.0")] -impl Deref for CString { +impl ops::Deref for CString { type Target = CStr; fn deref(&self) -> &CStr { @@ -522,6 +522,37 @@ impl ToOwned for CStr { } } +#[stable(feature = "cstring_asref", since = "1.7.0")] +impl<'a> From<&'a CStr> for CString { + fn from(s: &'a CStr) -> CString { + s.to_owned() + } +} + +#[stable(feature = "cstring_asref", since = "1.7.0")] +impl ops::Index for CString { + type Output = CStr; + + #[inline] + fn index(&self, _index: ops::RangeFull) -> &CStr { + self + } +} + +#[stable(feature = "cstring_asref", since = "1.7.0")] +impl AsRef for CStr { + fn as_ref(&self) -> &CStr { + self + } +} + +#[stable(feature = "cstring_asref", since = "1.7.0")] +impl AsRef for CString { + fn as_ref(&self) -> &CStr { + self + } +} + #[cfg(test)] mod tests { use prelude::v1::*; diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 90b108e677072..eb5ddecbd054d 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -8,27 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A type that can represent all platform-native strings, but is cheaply -//! interconvertable with Rust strings. -//! -//! The need for this type arises from the fact that: -//! -//! * On Unix systems, strings are often arbitrary sequences of non-zero -//! bytes, in many cases interpreted as UTF-8. -//! -//! * On Windows, strings are often arbitrary sequences of non-zero 16-bit -//! values, interpreted as UTF-16 when it is valid to do so. -//! -//! * In Rust, strings are always valid UTF-8, but may contain zeros. -//! -//! The types in this module bridge this gap by simultaneously representing Rust -//! and platform-native string values, and in particular allowing a Rust string -//! to be converted into an "OS" string with no cost. -//! -//! **Note**: At the moment, these types are extremely bare-bones, usable only -//! for conversion to/from various other string types. Eventually these types -//! will offer a full-fledged string API. - use borrow::{Borrow, Cow, ToOwned}; use ffi::CString; use fmt::{self, Debug}; @@ -42,14 +21,29 @@ use vec::Vec; use sys::os_str::{Buf, Slice}; use sys_common::{AsInner, IntoInner, FromInner}; -/// Owned, mutable OS strings. +/// A type that can represent owned, mutable platform-native strings, but is +/// cheaply interconvertable with Rust strings. +/// +/// The need for this type arises from the fact that: +/// +/// * On Unix systems, strings are often arbitrary sequences of non-zero +/// bytes, in many cases interpreted as UTF-8. +/// +/// * On Windows, strings are often arbitrary sequences of non-zero 16-bit +/// values, interpreted as UTF-16 when it is valid to do so. +/// +/// * In Rust, strings are always valid UTF-8, but may contain zeros. +/// +/// `OsString` and `OsStr` bridge this gap by simultaneously representing Rust +/// and platform-native string values, and in particular allowing a Rust string +/// to be converted into an "OS" string with no cost. #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct OsString { inner: Buf } -/// Slices into OS strings. +/// Slices into OS strings (see `OsString`). #[stable(feature = "rust1", since = "1.0.0")] pub struct OsStr { inner: Slice diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 715749f50d4e4..635ed91f35da4 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -715,7 +715,7 @@ impl DirEntry { /// This function will not traverse symlinks if this entry points at a /// symlink. /// - /// # Platform behavior + /// # Platform-specific behavior /// /// On Windows this function is cheap to call (no extra system calls /// needed), but on Unix platforms this function is the equivalent of @@ -730,7 +730,7 @@ impl DirEntry { /// This function will not traverse symlinks if this entry points at a /// symlink. /// - /// # Platform behavior + /// # Platform-specific behavior /// /// On Windows and most Unix platforms this function is free (no extra /// system calls needed), but some Unix platforms may require the equivalent @@ -758,11 +758,20 @@ impl AsInner for DirEntry { /// guarantee that the file is immediately deleted (e.g. depending on /// platform, other open file descriptors may prevent immediate removal). /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `unlink` function on Unix +/// and the `DeleteFile` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// /// # Errors /// -/// This function will return an error if `path` points to a directory, if the -/// user lacks permissions to remove the file, or if some other filesystem-level -/// error occurs. +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` points to a directory. +/// * The user lacks permissions to remove the file. /// /// # Examples /// @@ -785,6 +794,21 @@ pub fn remove_file>(path: P) -> io::Result<()> { /// This function will traverse symbolic links to query information about the /// destination file. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `stat` function on Unix +/// and the `GetFileAttributesEx` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The user lacks permissions to perform `metadata` call on `path`. +/// * `path` does not exist. +/// /// # Examples /// /// ```rust @@ -796,12 +820,6 @@ pub fn remove_file>(path: P) -> io::Result<()> { /// # Ok(()) /// # } /// ``` -/// -/// # Errors -/// -/// This function will return an error if the user lacks the requisite -/// permissions to perform a `metadata` call on the given `path` or if there -/// is no entry in the filesystem at the provided path. #[stable(feature = "rust1", since = "1.0.0")] pub fn metadata>(path: P) -> io::Result { fs_imp::stat(path.as_ref()).map(Metadata) @@ -809,6 +827,21 @@ pub fn metadata>(path: P) -> io::Result { /// Query the metadata about a file without following symlinks. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `lstat` function on Unix +/// and the `GetFileAttributesEx` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The user lacks permissions to perform `metadata` call on `path`. +/// * `path` does not exist. +/// /// # Examples /// /// ```rust @@ -829,12 +862,21 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// /// This will not work if the new name is on a different mount point. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `rename` function on Unix +/// and the `MoveFileEx` function with the `MOVEFILE_REPLACE_EXISTING` flag on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// /// # Errors /// -/// This function will return an error if the provided `from` doesn't exist, if -/// the process lacks permissions to view the contents, if `from` and `to` -/// reside on separate filesystems, or if some other intermittent I/O error -/// occurs. +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `from` does not exist. +/// * The user lacks permissions to view contents. +/// * `from` and `to` are on separate filesystems. /// /// # Examples /// @@ -842,7 +884,7 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::rename("a.txt", "b.txt")); +/// try!(fs::rename("a.txt", "b.txt")); // Rename a.txt to b.txt /// # Ok(()) /// # } /// ``` @@ -861,15 +903,24 @@ pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// /// On success, the total number of bytes copied is returned. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `open` function in Unix +/// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`. +/// `O_CLOEXEC` is set for returned file descriptors. +/// On Windows, this function currently corresponds to `CopyFileEx`. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// /// # Errors /// /// This function will return an error in the following situations, but is not /// limited to just these cases: /// -/// * The `from` path is not a file -/// * The `from` file does not exist +/// * The `from` path is not a file. +/// * The `from` file does not exist. /// * The current process does not have the permission rights to access -/// `from` or write `to` +/// `from` or write `to`. /// /// # Examples /// @@ -877,7 +928,7 @@ pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::copy("foo.txt", "bar.txt")); +/// try!(fs::copy("foo.txt", "bar.txt")); // Copy foo.txt to bar.txt /// # Ok(()) } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -890,13 +941,27 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// The `dst` path will be a link pointing to the `src` path. Note that systems /// often require these two paths to both be located on the same filesystem. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `link` function on Unix +/// and the `CreateHardLink` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The `src` path is not a file or doesn't exist. +/// /// # Examples /// /// ``` /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::hard_link("a.txt", "b.txt")); +/// try!(fs::hard_link("a.txt", "b.txt")); // Hard link a.txt to b.txt /// # Ok(()) /// # } /// ``` @@ -933,11 +998,21 @@ pub fn soft_link, Q: AsRef>(src: P, dst: Q) -> io::Result<( /// Reads a symbolic link, returning the file that the link points to. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `readlink` function on Unix +/// and the `CreateFile` function with `FILE_FLAG_OPEN_REPARSE_POINT` and +/// `FILE_FLAG_BACKUP_SEMANTICS` flags on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// /// # Errors /// -/// This function will return an error on failure. Failure conditions include -/// reading a file that does not exist or reading a file that is not a symbolic -/// link. +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` is not a symbolic link. +/// * `path` does not exist. /// /// # Examples /// @@ -957,8 +1032,20 @@ pub fn read_link>(path: P) -> io::Result { /// Returns the canonical form of a path with all intermediate components /// normalized and symbolic links resolved. /// -/// This function may return an error in situations like where the path does not -/// exist, a component in the path is not a directory, or an I/O error happens. +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `realpath` function on Unix +/// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` does not exist. +/// * A component in path is not a directory. /// /// # Examples /// @@ -977,10 +1064,20 @@ pub fn canonicalize>(path: P) -> io::Result { /// Creates a new, empty directory at the provided path /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectory` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// /// # Errors /// -/// This function will return an error if the user lacks permissions to make a -/// new directory at the provided `path`, or if the directory already exists. +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * User lacks permissions to create directory at `path`. +/// * `path` already exists. /// /// # Examples /// @@ -1000,9 +1097,19 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// Recursively create a directory and all of its parent components if they /// are missing. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectory` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// /// # Errors /// -/// This function will fail if any directory in the path specified by `path` +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * If any directory in the path specified by `path` /// does not already exist and it could not be created otherwise. The specific /// error conditions for when a directory is being created (after it is /// determined to not exist) are outlined by `fs::create_dir`. @@ -1024,10 +1131,20 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// Removes an existing, empty directory. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `rmdir` function on Unix +/// and the `RemoveDirectory` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// /// # Errors /// -/// This function will return an error if the user lacks permissions to remove -/// the directory at the provided `path`, or if the directory isn't empty. +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The user lacks permissions to remove the directory at the provided `path`. +/// * The directory isn't empty. /// /// # Examples /// @@ -1050,6 +1167,14 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// This function does **not** follow symbolic links and it will simply remove the /// symbolic link itself. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to `opendir`, `lstat`, `rm` and `rmdir` functions on Unix +/// and the `FindFirstFile`, `GetFileAttributesEx`, `DeleteFile`, and `RemoveDirectory` functions +/// on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// /// # Errors /// /// See `file::remove_file` and `fs::remove_dir`. @@ -1087,6 +1212,22 @@ fn _remove_dir_all(path: &Path) -> io::Result<()> { /// The iterator will yield instances of `io::Result`. New errors may /// be encountered after an iterator is initially constructed. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `opendir` function on Unix +/// and the `FindFirstFile` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The provided `path` doesn't exist. +/// * The process lacks permissions to view the contents. +/// * The `path` points at a non-directory file. +/// /// # Examples /// /// ``` @@ -1109,12 +1250,6 @@ fn _remove_dir_all(path: &Path) -> io::Result<()> { /// Ok(()) /// } /// ``` -/// -/// # Errors -/// -/// This function will return an error if the provided `path` doesn't exist, if -/// the process lacks permissions to view the contents or if the `path` points -/// at a non-directory file #[stable(feature = "rust1", since = "1.0.0")] pub fn read_dir>(path: P) -> io::Result { fs_imp::readdir(path.as_ref()).map(ReadDir) @@ -1180,6 +1315,21 @@ impl Iterator for WalkDir { /// Changes the permissions found on a file or a directory. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `chmod` function on Unix +/// and the `SetFileAttributes` function on Windows. +/// Note that, this [may change in the future][changes]. +/// [changes]: ../io/index.html#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` does not exist. +/// * The user lacks the permission to change attributes of the file. +/// /// # Examples /// /// ``` @@ -1192,12 +1342,6 @@ impl Iterator for WalkDir { /// # Ok(()) /// # } /// ``` -/// -/// # Errors -/// -/// This function will return an error if the provided `path` doesn't exist, if -/// the process lacks permissions to change the attributes of the file, or if -/// some other I/O error is encountered. #[stable(feature = "set_permissions", since = "1.1.0")] pub fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { @@ -1281,7 +1425,6 @@ mod tests { use io::{ErrorKind, SeekFrom}; use path::PathBuf; use path::Path as Path2; - use os; use rand::{self, StdRng, Rng}; use str; @@ -1410,8 +1553,8 @@ mod tests { let message = "ten-four"; let mut read_mem = [0; 4]; let set_cursor = 4 as u64; - let mut tell_pos_pre_read; - let mut tell_pos_post_read; + let tell_pos_pre_read; + let tell_pos_post_read; let tmpdir = tmpdir(); let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); { diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 79eedbeda2c72..16ce05ef2dcaf 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -791,7 +791,7 @@ impl Read for InternalBufWriter { mod tests { use prelude::v1::*; use io::prelude::*; - use io::{self, BufReader, BufWriter, Cursor, LineWriter, SeekFrom}; + use io::{self, BufReader, BufWriter, LineWriter, SeekFrom}; use test; /// A dummy reader intended at testing short-reads propagation. diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index 1dd96f5ddfc01..e3f17c839f135 100644 --- a/src/libstd/io/error.rs +++ b/src/libstd/io/error.rs @@ -377,7 +377,7 @@ mod test { struct TestError; impl fmt::Display for TestError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) } } diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index cc3f8097a88e9..b5ba6ff54c0e3 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -237,7 +237,16 @@ //! to read the line and print it, so we use `()`. //! //! [result]: type.Result.html -//! [try]: macro.try!.html +//! [try]: ../macro.try!.html +//! +//! ## Platform-specific behavior +//! +//! Many I/O functions throughout the standard library are documented to indicate +//! what various library or syscalls they are delegated to. This is done to help +//! applications both understand what's happening under the hood as well as investigate +//! any possibly unclear semantics. Note, however, that this is informative, not a binding +//! contract. The implementation of many of these functions are subject to change over +//! time and may call fewer or more syscalls/library functions. #![stable(feature = "rust1", since = "1.0.0")] @@ -992,8 +1001,8 @@ pub trait Write { /// explicitly be called. The [`write!`][write] macro should be favored to /// invoke this method instead. /// - /// [formatargs]: ../std/macro.format_args!.html - /// [write]: ../std/macro.write!.html + /// [formatargs]: ../macro.format_args!.html + /// [write]: ../macro.write!.html /// /// This function internally uses the [`write_all`][writeall] method on /// this trait and hence will continuously write data so long as no errors @@ -1126,7 +1135,7 @@ pub trait Write { /// /// [`File`][file]s implement `Seek`: /// -/// [file]: ../std/fs/struct.File.html +/// [file]: ../fs/struct.File.html /// /// ``` /// use std::io; @@ -1171,7 +1180,7 @@ pub enum SeekFrom { /// Set the offset to the size of this object plus the specified number of /// bytes. /// - /// It is possible to seek beyond the end of an object, but is an error to + /// It is possible to seek beyond the end of an object, but it's an error to /// seek before byte 0. #[stable(feature = "rust1", since = "1.0.0")] End(i64), @@ -1179,7 +1188,7 @@ pub enum SeekFrom { /// Set the offset to the current position plus the specified number of /// bytes. /// - /// It is possible to seek beyond the end of an object, but is an error to + /// It is possible to seek beyond the end of an object, but it's an error to /// seek before byte 0. #[stable(feature = "rust1", since = "1.0.0")] Current(i64), @@ -1983,7 +1992,7 @@ mod tests { b.iter(|| { let mut lr = repeat(1).take(10000000); let mut vec = Vec::with_capacity(1024); - super::read_to_end(&mut lr, &mut vec); + super::read_to_end(&mut lr, &mut vec) }); } } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 985dbdd895f84..79091fd3d6b9d 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -133,14 +133,17 @@ fn handle_ebadf(r: io::Result, default: T) -> io::Result { /// A handle to the standard input stream of a process. /// /// Each handle is a shared reference to a global buffer of input data to this -/// process. A handle can be `lock`'d to gain full access to `BufRead` methods +/// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods /// (e.g. `.lines()`). Writes to this handle are otherwise locked with respect /// to other writes. /// /// This handle implements the `Read` trait, but beware that concurrent reads /// of `Stdin` must be executed with care. /// -/// Created by the function `io::stdin()`. +/// Created by the [`io::stdin`] method. +/// +/// [`io::stdin`]: fn.stdin.html +/// [`BufRead`]: trait.BufRead.html #[stable(feature = "rust1", since = "1.0.0")] pub struct Stdin { inner: Arc>>>, @@ -148,8 +151,12 @@ pub struct Stdin { /// A locked reference to the `Stdin` handle. /// -/// This handle implements both the `Read` and `BufRead` traits and is -/// constructed via the `lock` method on `Stdin`. +/// This handle implements both the [`Read`] and [`BufRead`] traits, and +/// is constructed via the [`Stdin::lock`] method. +/// +/// [`Read`]: trait.Read.html +/// [`BufRead`]: trait.BufRead.html +/// [`Stdin::lock`]: struct.Stdin.html#method.lock #[stable(feature = "rust1", since = "1.0.0")] pub struct StdinLock<'a> { inner: MutexGuard<'a, BufReader>>, @@ -159,7 +166,7 @@ pub struct StdinLock<'a> { /// /// Each handle returned is a reference to a shared global buffer whose access /// is synchronized via a mutex. If you need more explicit control over -/// locking, see the [lock() method][lock]. +/// locking, see the [`lock() method`][lock]. /// /// [lock]: struct.Stdin.html#method.lock /// @@ -221,8 +228,11 @@ impl Stdin { /// guard. /// /// The lock is released when the returned lock goes out of scope. The - /// returned guard also implements the `Read` and `BufRead` traits for + /// returned guard also implements the [`Read`] and [`BufRead`] traits for /// accessing the underlying data. + /// + /// [`Read`]: trait.Read.html + /// [`BufRead`]: trait.BufRead.html #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> StdinLock { StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } @@ -231,7 +241,9 @@ impl Stdin { /// Locks this handle and reads a line of input into the specified buffer. /// /// For detailed semantics of this method, see the documentation on - /// `BufRead::read_line`. + /// [`BufRead::read_line`]. + /// + /// [`BufRead::read_line`]: trait.BufRead.html#method.read_line /// /// # Examples /// @@ -252,7 +264,7 @@ impl Stdin { /// /// - Pipe some text to it, e.g. `printf foo | path/to/executable` /// - Give it text interactively by running the executable directly, - // in which case it will wait for the Enter key to be pressed before + /// in which case it will wait for the Enter key to be pressed before /// continuing #[stable(feature = "rust1", since = "1.0.0")] pub fn read_line(&self, buf: &mut String) -> io::Result { @@ -314,7 +326,9 @@ const OUT_MAX: usize = ::usize::MAX; /// output stream. Access is also synchronized via a lock and explicit control /// over locking is available via the `lock` method. /// -/// Created by the function `io::stdout()`. +/// Created by the [`io::stdout`] method. +/// +/// [`io::stdout`]: fn.stdout.html #[stable(feature = "rust1", since = "1.0.0")] pub struct Stdout { // FIXME: this should be LineWriter or BufWriter depending on the state of @@ -325,8 +339,11 @@ pub struct Stdout { /// A locked reference to the `Stdout` handle. /// -/// This handle implements the `Write` trait and is constructed via the `lock` -/// method on `Stdout`. +/// This handle implements the [`Write`] trait, and is constructed via +/// the [`Stdout::lock`] method. +/// +/// [`Write`]: trait.Write.html +/// [`Stdout::lock`]: struct.Stdout.html#method.lock #[stable(feature = "rust1", since = "1.0.0")] pub struct StdoutLock<'a> { inner: ReentrantMutexGuard<'a, RefCell>>>, @@ -336,9 +353,9 @@ pub struct StdoutLock<'a> { /// /// Each handle returned is a reference to a shared global buffer whose access /// is synchronized via a mutex. If you need more explicit control over -/// locking, see the [lock() method][lock]. +/// locking, see the [Stdout::lock] method. /// -/// [lock]: struct.Stdout.html#method.lock +/// [Stdout::lock]: struct.Stdout.html#method.lock /// /// # Examples /// @@ -424,7 +441,9 @@ impl<'a> Write for StdoutLock<'a> { /// A handle to the standard error stream of a process. /// -/// For more information, see `stderr` +/// For more information, see the [`io::stderr`] method. +/// +/// [`io::stderr`]: fn.stderr.html #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { inner: Arc>>>, @@ -432,8 +451,10 @@ pub struct Stderr { /// A locked reference to the `Stderr` handle. /// -/// This handle implements the `Write` trait and is constructed via the `lock` -/// method on `Stderr`. +/// This handle implements the `Write` trait and is constructed via +/// the [`Stderr::lock`] method. +/// +/// [`Stderr::lock`]: struct.Stderr.html#method.lock #[stable(feature = "rust1", since = "1.0.0")] pub struct StderrLock<'a> { inner: ReentrantMutexGuard<'a, RefCell>>, diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index 07a9548224aca..e05a0d577ff89 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -199,6 +199,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn tee() { let mut buf = [0; 10]; { @@ -209,6 +210,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn broadcast() { let mut buf1 = [0; 10]; let mut buf2 = [0; 10]; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e66cc49290adf..9c20a48d42518 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -76,7 +76,7 @@ //! `i32`](primitive.i32.html) that lists all the methods that can be called on //! 32-bit integers (very useful), and there is a [page for the module //! `std::i32`](i32/index.html) that documents the constant values [`MIN`] and -//! [`MAX`] (rarely useful). +//! [`MAX`](i32/constant.MAX.html) (rarely useful). //! //! Note the documentation for the primitives [`str`] and [`[T]`][slice] (also //! called 'slice'). Many method calls on [`String`] and [`Vec`] are actually @@ -153,7 +153,6 @@ //! //! [I/O]: io/index.html //! [`MIN`]: i32/constant.MIN.html -//! [`MAX`]: i32/constant.MAX.html //! [TCP]: net/struct.TcpStream.html //! [The Rust Prelude]: prelude/index.html //! [UDP]: net/struct.UdpSocket.html diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index 9c4e2b1a54c30..74d308dbf0cf5 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -468,9 +468,7 @@ impl<'a, T: ToSocketAddrs + ?Sized> ToSocketAddrs for &'a T { #[cfg(test)] mod tests { use prelude::v1::*; - use io; use net::*; - use net::Ipv6MulticastScope::*; use net::test::{tsa, sa6, sa4}; #[test] diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 00be17f738356..808cf5cc031be 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -527,7 +527,6 @@ impl FromInner for Ipv6Addr { #[cfg(test)] mod tests { use prelude::v1::*; - use io; use net::*; use net::Ipv6MulticastScope::*; use net::test::{tsa, sa6, sa4}; diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index f54d4f804806d..f9c38c3845847 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -319,7 +319,7 @@ mod tests { use net::test::{next_test_ip4, next_test_ip6}; use sync::mpsc::channel; use sys_common::AsInner; - use time::Duration; + use time::{Instant, Duration}; use thread; fn each_ip(f: &mut FnMut(SocketAddr)) { @@ -929,6 +929,7 @@ mod tests { t!(stream.set_write_timeout(None)); assert_eq!(None, t!(stream.write_timeout())); + drop(listener); } #[test] @@ -940,11 +941,11 @@ mod tests { t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); let mut buf = [0; 10]; - let wait = Duration::span(|| { - let kind = stream.read(&mut buf).err().expect("expected error").kind(); - assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); - }); - assert!(wait > Duration::from_millis(400)); + let start = Instant::now(); + let kind = stream.read(&mut buf).err().expect("expected error").kind(); + assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); + assert!(start.elapsed() > Duration::from_millis(400)); + drop(listener); } #[test] @@ -962,10 +963,10 @@ mod tests { t!(stream.read(&mut buf)); assert_eq!(b"hello world", &buf[..]); - let wait = Duration::span(|| { - let kind = stream.read(&mut buf).err().expect("expected error").kind(); - assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); - }); - assert!(wait > Duration::from_millis(400)); + let start = Instant::now(); + let kind = stream.read(&mut buf).err().expect("expected error").kind(); + assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); + assert!(start.elapsed() > Duration::from_millis(400)); + drop(listener); } } diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index b212d4d32aadf..29ac991a547dc 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -27,18 +27,19 @@ use time::Duration; /// use std::net::UdpSocket; /// /// # fn foo() -> std::io::Result<()> { -/// let mut socket = try!(UdpSocket::bind("127.0.0.1:34254")); +/// { +/// let mut socket = try!(UdpSocket::bind("127.0.0.1:34254")); /// -/// let mut buf = [0; 10]; -/// let (amt, src) = try!(socket.recv_from(&mut buf)); +/// // read from the socket +/// let mut buf = [0; 10]; +/// let (amt, src) = try!(socket.recv_from(&mut buf)); /// -/// // Send a reply to the socket we received data from -/// let buf = &mut buf[..amt]; -/// buf.reverse(); -/// try!(socket.send_to(buf, &src)); -/// -/// drop(socket); // close the socket -/// # Ok(()) +/// // send a reply to the socket we received data from +/// let buf = &mut buf[..amt]; +/// buf.reverse(); +/// try!(socket.send_to(buf, &src)); +/// # Ok(()) +/// } // the socket is closed here /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -169,7 +170,7 @@ mod tests { use net::test::{next_test_ip4, next_test_ip6}; use sync::mpsc::channel; use sys_common::AsInner; - use time::Duration; + use time::{Instant, Duration}; use thread; fn each_ip(f: &mut FnMut(SocketAddr, SocketAddr)) { @@ -370,22 +371,22 @@ mod tests { fn test_read_timeout() { let addr = next_test_ip4(); - let mut stream = t!(UdpSocket::bind(&addr)); + let stream = t!(UdpSocket::bind(&addr)); t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); let mut buf = [0; 10]; - let wait = Duration::span(|| { - let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); - assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); - }); - assert!(wait > Duration::from_millis(400)); + + let start = Instant::now(); + let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); + assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); + assert!(start.elapsed() > Duration::from_millis(400)); } #[test] fn test_read_with_timeout() { let addr = next_test_ip4(); - let mut stream = t!(UdpSocket::bind(&addr)); + let stream = t!(UdpSocket::bind(&addr)); t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); t!(stream.send_to(b"hello world", &addr)); @@ -394,10 +395,9 @@ mod tests { t!(stream.recv_from(&mut buf)); assert_eq!(b"hello world", &buf[..]); - let wait = Duration::span(|| { - let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); - assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); - }); - assert!(wait > Duration::from_millis(400)); + let start = Instant::now(); + let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); + assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); + assert!(start.elapsed() > Duration::from_millis(400)); } } diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 30bee80fbf658..ce8ce097f54cb 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -15,11 +15,16 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] +#[cfg(not(test))] use core::num; +#[cfg(not(test))] use intrinsics; +#[cfg(not(test))] use libc::c_int; +#[cfg(not(test))] use num::FpCategory; + #[stable(feature = "rust1", since = "1.0.0")] pub use core::f32::{RADIX, MANTISSA_DIGITS, DIGITS, EPSILON}; #[stable(feature = "rust1", since = "1.0.0")] @@ -671,8 +676,6 @@ impl f32 { /// Converts radians to degrees. /// /// ``` - /// #![feature(float_extras)] - /// /// use std::f32::{self, consts}; /// /// let angle = consts::PI; @@ -681,16 +684,13 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` - #[unstable(feature = "float_extras", reason = "desirability is unclear", - issue = "27752")] + #[stable(feature = "f32_deg_rad_conversions", since="1.7.0")] #[inline] pub fn to_degrees(self) -> f32 { num::Float::to_degrees(self) } /// Converts degrees to radians. /// /// ``` - /// #![feature(float_extras)] - /// /// use std::f32::{self, consts}; /// /// let angle = 180.0f32; @@ -699,8 +699,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` - #[unstable(feature = "float_extras", reason = "desirability is unclear", - issue = "27752")] + #[stable(feature = "f32_deg_rad_conversions", since="1.7.0")] #[inline] pub fn to_radians(self) -> f32 { num::Float::to_radians(self) } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index d444b2594453c..b6a85ee0e9f58 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -15,9 +15,13 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] +#[cfg(not(test))] use core::num; +#[cfg(not(test))] use intrinsics; +#[cfg(not(test))] use libc::c_int; +#[cfg(not(test))] use num::FpCategory; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/num/mod.rs b/src/libstd/num/mod.rs index 8e207f5e6cfc5..faaff494cab51 100644 --- a/src/libstd/num/mod.rs +++ b/src/libstd/num/mod.rs @@ -47,11 +47,6 @@ pub fn test_num(ten: T, two: T) where #[cfg(test)] mod tests { use super::*; - use i8; - use i16; - use i32; - use i64; - use isize; use u8; use u16; use u32; diff --git a/src/libstd/os/raw.rs b/src/libstd/os/raw.rs index 17d6b2605c628..3bc063f426920 100644 --- a/src/libstd/os/raw.rs +++ b/src/libstd/os/raw.rs @@ -88,7 +88,8 @@ mod tests { c_longlong c_ulonglong c_float c_double); } - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "android")))] + #[test] fn unix() { { use os::unix::raw; @@ -101,6 +102,7 @@ mod tests { } #[cfg(windows)] + #[test] fn windows() { use os::windows::raw; } diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 6e4ba337b08ee..8c0a785a10256 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -13,6 +13,8 @@ #![unstable(feature = "std_panic", reason = "awaiting feedback", issue = "27719")] +use any::Any; +use boxed::Box; use cell::UnsafeCell; use ops::{Deref, DerefMut}; use ptr::{Unique, Shared}; @@ -21,6 +23,8 @@ use sync::{Arc, Mutex, RwLock}; use sys_common::unwind; use thread::Result; +pub use panicking::{take_handler, set_handler, PanicInfo, Location}; + /// A marker trait which represents "panic safe" types in Rust. /// /// This trait is implemented by default for many types and behaves similarly in @@ -39,15 +43,15 @@ use thread::Result; /// panics. /// 2. This broken invariant is then later observed. /// -/// Typically in Rust it is difficult to perform step (2) because catching a +/// Typically in Rust, it is difficult to perform step (2) because catching a /// panic involves either spawning a thread (which in turns makes it difficult /// to later witness broken invariants) or using the `recover` function in this -/// module. Additionally, even if an invariant is witness, it typically isn't a +/// module. Additionally, even if an invariant is witnessed, it typically isn't a /// problem in Rust because there's no uninitialized values (like in C or C++). /// /// It is possible, however, for **logical** invariants to be broken in Rust, /// which can end up causing behavioral bugs. Another key aspect of panic safety -/// in Rust is that in the absence of `unsafe` code, a panic cannot lead to +/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to /// memory unsafety. /// /// That was a bit of a whirlwind tour of panic safety, but for more information @@ -58,12 +62,12 @@ use thread::Result; /// ## What is `RecoverSafe`? /// /// Now that we've got an idea of what panic safety is in Rust, it's also -/// important to understand that this trait represents. As mentioned above, one +/// important to understand what this trait represents. As mentioned above, one /// way to witness broken invariants is through the `recover` function in this /// module as it allows catching a panic and then re-using the environment of /// the closure. /// -/// Simply but, a type `T` implements `RecoverSafe` if it cannot easily allow +/// Simply put, a type `T` implements `RecoverSafe` if it cannot easily allow /// witnessing a broken invariant through the use of `recover` (catching a /// panic). This trait is a marker trait, so it is automatically implemented for /// many types, and it is also structurally composed (e.g. a struct is recover @@ -99,8 +103,11 @@ use thread::Result; across a recover boundary"] pub trait RecoverSafe {} -/// A marker trait representing types which do not contain an `UnsafeCell` by -/// value internally. +/// A marker trait representing types where a shared reference is considered +/// recover safe. +/// +/// This trait is namely not implemented by `UnsafeCell`, the root of all +/// interior mutability. /// /// This is a "helper marker trait" used to provide impl blocks for the /// `RecoverSafe` trait, for more information see that documentation. @@ -108,7 +115,7 @@ pub trait RecoverSafe {} #[rustc_on_unimplemented = "the type {Self} contains interior mutability \ and a reference may not be safely transferrable \ across a recover boundary"] -pub trait NoUnsafeCell {} +pub trait RefRecoverSafe {} /// A simple wrapper around a type to assert that it is panic safe. /// @@ -157,11 +164,11 @@ pub struct AssertRecoverSafe(T); // * Our custom AssertRecoverSafe wrapper is indeed recover safe impl RecoverSafe for .. {} impl<'a, T: ?Sized> !RecoverSafe for &'a mut T {} -impl<'a, T: NoUnsafeCell + ?Sized> RecoverSafe for &'a T {} -impl RecoverSafe for *const T {} -impl RecoverSafe for *mut T {} +impl<'a, T: RefRecoverSafe + ?Sized> RecoverSafe for &'a T {} +impl RecoverSafe for *const T {} +impl RecoverSafe for *mut T {} impl RecoverSafe for Unique {} -impl RecoverSafe for Shared {} +impl RecoverSafe for Shared {} impl RecoverSafe for Mutex {} impl RecoverSafe for RwLock {} impl RecoverSafe for AssertRecoverSafe {} @@ -169,15 +176,16 @@ impl RecoverSafe for AssertRecoverSafe {} // not covered via the Shared impl above b/c the inner contents use // Cell/AtomicUsize, but the usage here is recover safe so we can lift the // impl up one level to Arc/Rc itself -impl RecoverSafe for Rc {} -impl RecoverSafe for Arc {} +impl RecoverSafe for Rc {} +impl RecoverSafe for Arc {} -// Pretty simple implementations for the `NoUnsafeCell` marker trait, basically -// just saying that this is a marker trait and `UnsafeCell` is the only thing -// which doesn't implement it (which then transitively applies to everything -// else. -impl NoUnsafeCell for .. {} -impl !NoUnsafeCell for UnsafeCell {} +// Pretty simple implementations for the `RefRecoverSafe` marker trait, +// basically just saying that this is a marker trait and `UnsafeCell` is the +// only thing which doesn't implement it (which then transitively applies to +// everything else). +impl RefRecoverSafe for .. {} +impl !RefRecoverSafe for UnsafeCell {} +impl RefRecoverSafe for AssertRecoverSafe {} impl AssertRecoverSafe { /// Creates a new `AssertRecoverSafe` wrapper around the provided type. @@ -253,3 +261,28 @@ pub fn recover R + RecoverSafe, R>(f: F) -> Result { } Ok(result.unwrap()) } + +/// Triggers a panic without invoking the panic handler. +/// +/// This is designed to be used in conjunction with `recover` to, for example, +/// carry a panic across a layer of C code. +/// +/// # Examples +/// +/// ```should_panic +/// #![feature(std_panic, recover, panic_propagate)] +/// +/// use std::panic; +/// +/// let result = panic::recover(|| { +/// panic!("oh no!"); +/// }); +/// +/// if let Err(err) = result { +/// panic::propagate(err); +/// } +/// ``` +#[unstable(feature = "panic_propagate", reason = "awaiting feedback", issue = "30752")] +pub fn propagate(payload: Box) -> ! { + unwind::rust_panic(payload) +} diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 2b2af350c992c..3f9a1c30ef493 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -15,10 +15,12 @@ use any::Any; use cell::Cell; use cell::RefCell; use intrinsics; +use sync::StaticRwLock; use sys::stdio::Stderr; use sys_common::backtrace; use sys_common::thread_info; use sys_common::util; +use thread; thread_local! { pub static PANIC_COUNT: Cell = Cell::new(0) } @@ -28,11 +30,138 @@ thread_local! { } } -fn log_panic(obj: &(Any+Send), file: &'static str, line: u32, - log_backtrace: bool) { - let msg = match obj.downcast_ref::<&'static str>() { +#[derive(Copy, Clone)] +enum Handler { + Default, + Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)), +} + +static HANDLER_LOCK: StaticRwLock = StaticRwLock::new(); +static mut HANDLER: Handler = Handler::Default; + +/// Registers a custom panic handler, replacing any that was previously +/// registered. +/// +/// The panic handler is invoked when a thread panics, but before it begins +/// unwinding the stack. The default handler prints a message to standard error +/// and generates a backtrace if requested, but this behavior can be customized +/// with the `set_handler` and `take_handler` functions. +/// +/// The handler is provided with a `PanicInfo` struct which contains information +/// about the origin of the panic, including the payload passed to `panic!` and +/// the source code location from which the panic originated. +/// +/// The panic handler is a global resource. +/// +/// # Panics +/// +/// Panics if called from a panicking thread. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub fn set_handler(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send { + if thread::panicking() { + panic!("cannot modify the panic handler from a panicking thread"); + } + + let handler = Box::new(handler); + unsafe { + let lock = HANDLER_LOCK.write(); + let old_handler = HANDLER; + HANDLER = Handler::Custom(Box::into_raw(handler)); + drop(lock); + + if let Handler::Custom(ptr) = old_handler { + Box::from_raw(ptr); + } + } +} + +/// Unregisters the current panic handler, returning it. +/// +/// If no custom handler is registered, the default handler will be returned. +/// +/// # Panics +/// +/// Panics if called from a panicking thread. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub fn take_handler() -> Box { + if thread::panicking() { + panic!("cannot modify the panic handler from a panicking thread"); + } + + unsafe { + let lock = HANDLER_LOCK.write(); + let handler = HANDLER; + HANDLER = Handler::Default; + drop(lock); + + match handler { + Handler::Default => Box::new(default_handler), + Handler::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530 + } + } +} + +/// A struct providing information about a panic. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub struct PanicInfo<'a> { + payload: &'a (Any + Send), + location: Location<'a>, +} + +impl<'a> PanicInfo<'a> { + /// Returns the payload associated with the panic. + /// + /// This will commonly, but not always, be a `&'static str` or `String`. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn payload(&self) -> &(Any + Send) { + self.payload + } + + /// Returns information about the location from which the panic originated, + /// if available. + /// + /// This method will currently always return `Some`, but this may change + /// in future versions. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn location(&self) -> Option<&Location> { + Some(&self.location) + } +} + +/// A struct containing information about the location of a panic. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub struct Location<'a> { + file: &'a str, + line: u32, +} + +impl<'a> Location<'a> { + /// Returns the name of the source file from which the panic originated. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn file(&self) -> &str { + self.file + } + + /// Returns the line number from which the panic originated. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn line(&self) -> u32 { + self.line + } +} + +fn default_handler(info: &PanicInfo) { + let panics = PANIC_COUNT.with(|s| s.get()); + + // If this is a double panic, make sure that we print a backtrace + // for this panic. Otherwise only print it if logging is enabled. + let log_backtrace = panics >= 2 || backtrace::log_enabled(); + + let file = info.location.file; + let line = info.location.line; + + let msg = match info.payload.downcast_ref::<&'static str>() { Some(s) => *s, - None => match obj.downcast_ref::() { + None => match info.payload.downcast_ref::() { Some(s) => &s[..], None => "Box", } @@ -81,10 +210,21 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { unsafe { intrinsics::abort() } } - // If this is a double panic, make sure that we print a backtrace - // for this panic. Otherwise only print it if logging is enabled. - let log_backtrace = panics >= 2 || backtrace::log_enabled(); - log_panic(obj, file, line, log_backtrace); + let info = PanicInfo { + payload: obj, + location: Location { + file: file, + line: line, + }, + }; + + unsafe { + let _lock = HANDLER_LOCK.read(); + match HANDLER { + Handler::Default => default_handler(&info), + Handler::Custom(ptr) => (*ptr)(&info), + } + } if panics >= 2 { // If a thread panics while it's already unwinding then we diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index c7cb10a470748..0ba0e01ce2910 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -32,10 +32,6 @@ mod imp { 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; - } - #[cfg(target_arch = "x86_64")] const NR_GETRANDOM: libc::c_long = 318; #[cfg(target_arch = "x86")] @@ -46,7 +42,7 @@ mod imp { const NR_GETRANDOM: libc::c_long = 278; unsafe { - syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) + libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) } } @@ -381,8 +377,6 @@ mod imp { #[cfg(test)] mod tests { - use prelude::v1::*; - use sync::mpsc::channel; use rand::Rng; use super::OsRng; diff --git a/src/libstd/rand/reader.rs b/src/libstd/rand/reader.rs index 36adf44cd3a3e..08bc809ed4d46 100644 --- a/src/libstd/rand/reader.rs +++ b/src/libstd/rand/reader.rs @@ -63,8 +63,6 @@ impl Rng for ReaderRng { #[cfg(test)] mod tests { - use prelude::v1::*; - use super::ReaderRng; use rand::Rng; diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs index 63fb9b561ff32..fcd827e2a8b72 100644 --- a/src/libstd/rt.rs +++ b/src/libstd/rt.rs @@ -22,13 +22,7 @@ issue = "0")] #![doc(hidden)] -use borrow::ToOwned; -use mem; -use panic; -use sys; -use sys_common::thread_info::{self, NewThread}; -use sys_common; -use thread::Thread; + // Reexport some of our utilities which are expected by other crates. pub use sys_common::unwind::{begin_unwind, begin_unwind_fmt}; @@ -41,6 +35,14 @@ pub use sys_common::unwind::imp::eh_frame_registry::*; #[cfg(not(test))] #[lang = "start"] fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { + use borrow::ToOwned; + use mem; + use panic; + use sys; + use sys_common; + use sys_common::thread_info::{self, NewThread}; + use thread::Thread; + sys::init(); let failed = unsafe { diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index d817a261f7c94..1f7fe820bf86a 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -510,15 +510,15 @@ mod tests { static M: StaticMutex = StaticMutex::new(); let g = M.lock().unwrap(); - let (g, _no_timeout) = C.wait_timeout_ms(g, 1).unwrap(); + let (g, _no_timeout) = C.wait_timeout(g, Duration::from_millis(1)).unwrap(); // spurious wakeups mean this isn't necessarily true // assert!(!no_timeout); let _t = thread::spawn(move || { let _g = M.lock().unwrap(); C.notify_one(); }); - let (g, no_timeout) = C.wait_timeout_ms(g, u32::MAX).unwrap(); - assert!(no_timeout); + let (g, timeout_res) = C.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap(); + assert!(!timeout_res.timed_out()); drop(g); unsafe { C.destroy(); M.destroy(); } } diff --git a/src/libstd/sys/common/io.rs b/src/libstd/sys/common/io.rs index 151d853fc9f7e..9f2f0df3a6470 100644 --- a/src/libstd/sys/common/io.rs +++ b/src/libstd/sys/common/io.rs @@ -133,7 +133,7 @@ mod tests { b.iter(|| { let mut lr = repeat(1).take(10000000); let mut vec = Vec::with_capacity(1024); - unsafe { read_to_end_uninitialized(&mut lr, &mut vec) }; + unsafe { read_to_end_uninitialized(&mut lr, &mut vec) } }); } } diff --git a/src/libstd/sys/common/remutex.rs b/src/libstd/sys/common/remutex.rs index f3f21e47a1406..31caa68c4b7ea 100644 --- a/src/libstd/sys/common/remutex.rs +++ b/src/libstd/sys/common/remutex.rs @@ -167,7 +167,6 @@ mod tests { use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use cell::RefCell; use sync::Arc; - use boxed; use thread; #[test] @@ -208,13 +207,13 @@ mod tests { fn trylock_works() { let m = Arc::new(ReentrantMutex::new(())); let m2 = m.clone(); - let lock = m.try_lock().unwrap(); - let lock2 = m.try_lock().unwrap(); + let _lock = m.try_lock().unwrap(); + let _lock2 = m.try_lock().unwrap(); thread::spawn(move || { let lock = m2.try_lock(); assert!(lock.is_err()); }).join().unwrap(); - let lock3 = m.try_lock().unwrap(); + let _lock3 = m.try_lock().unwrap(); } pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell>); @@ -233,9 +232,8 @@ mod tests { *lock.borrow_mut() = 1; let lock2 = mc.lock().unwrap(); *lock.borrow_mut() = 2; - let answer = Answer(lock2); + let _answer = Answer(lock2); panic!("What the answer to my lifetimes dilemma is?"); - drop(answer); }).join(); assert!(result.is_err()); let r = m.lock().err().unwrap().into_inner(); diff --git a/src/libstd/sys/common/unwind/mod.rs b/src/libstd/sys/common/unwind/mod.rs index 0f10e7274614c..666b2ed72adce 100644 --- a/src/libstd/sys/common/unwind/mod.rs +++ b/src/libstd/sys/common/unwind/mod.rs @@ -172,7 +172,7 @@ pub fn panicking() -> bool { #[inline(never)] #[no_mangle] #[allow(private_no_mangle_fns)] -fn rust_panic(cause: Box) -> ! { +pub fn rust_panic(cause: Box) -> ! { unsafe { imp::panic(cause) } diff --git a/src/libstd/sys/common/wtf8.rs b/src/libstd/sys/common/wtf8.rs index 2e092d5f770c5..bc997af3a27e4 100644 --- a/src/libstd/sys/common/wtf8.rs +++ b/src/libstd/sys/common/wtf8.rs @@ -1067,7 +1067,7 @@ mod tests { #[test] fn wtf8buf_show_str() { let text = "a\té 💩\r"; - let mut string = Wtf8Buf::from_str(text); + let string = Wtf8Buf::from_str(text); assert_eq!(format!("{:?}", text), format!("{:?}", string)); } diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs index 52ac37c6e334d..4163ede46afed 100644 --- a/src/libstd/sys/unix/ext/io.rs +++ b/src/libstd/sys/unix/ext/io.rs @@ -33,7 +33,7 @@ pub trait AsRawFd { /// Extracts the raw file descriptor. /// /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guarantee to be valid while + /// to the caller. The descriptor is only guaranteed to be valid while /// the original object has not yet been destroyed. #[stable(feature = "rust1", since = "1.0.0")] fn as_raw_fd(&self) -> RawFd; diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 68b0c3d6b0ee7..929fd2fb0c38b 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -45,7 +45,7 @@ pub mod thread_local; pub mod time; pub mod stdio; -#[cfg(not(target_os = "nacl"))] +#[cfg(not(any(target_os = "nacl", test)))] pub fn init() { use libc::signal; // By default, some platforms will send a *signal* when an EPIPE error @@ -59,7 +59,8 @@ pub fn init() { assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0); } } -#[cfg(target_os = "nacl")] + +#[cfg(all(target_os = "nacl", not(test)))] pub fn init() { } pub fn decode_error_kind(errno: i32) -> ErrorKind { diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 407fcb0a1b8f1..495f45f5c7e22 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -462,8 +462,7 @@ mod tests { use mem; use ptr; use libc; - use slice; - use sys::{self, cvt, pipe}; + use sys::{self, cvt}; macro_rules! t { ($e:expr) => { @@ -482,6 +481,8 @@ mod tests { #[cfg(target_os = "android")] unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int { + use slice; + let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::()); let bit = (signum - 1) as usize; raw[bit / 8] |= 1 << (bit % 8); diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index 9a7f98d24cd5d..776acd20b069b 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -7,11 +7,13 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +#![cfg_attr(test, allow(dead_code))] use libc; use self::imp::{make_handler, drop_handler}; -pub use self::imp::{init, cleanup}; +pub use self::imp::cleanup; +pub use self::imp::init; pub struct Handler { _data: *mut libc::c_void @@ -40,12 +42,11 @@ impl Drop for Handler { target_os = "openbsd"))] mod imp { use super::Handler; - use sys_common::util::report_overflow; use mem; use ptr; + use libc::{sigaltstack, SIGSTKSZ}; use libc::{sigaction, SIGBUS, SIG_DFL, - SA_SIGINFO, SA_ONSTACK, sigaltstack, - SIGSTKSZ, sighandler_t}; + SA_SIGINFO, SA_ONSTACK, sighandler_t}; use libc; use libc::{mmap, munmap}; use libc::{SIGSEGV, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON}; @@ -94,6 +95,8 @@ mod imp { unsafe extern fn signal_handler(signum: libc::c_int, info: *mut libc::siginfo_t, _data: *mut libc::c_void) { + use sys_common::util::report_overflow; + let guard = thread_info::stack_guard().unwrap_or(0); let addr = siginfo_si_addr(info) as usize; diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 0e525f394216c..116cd5da2ce7b 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -830,14 +830,14 @@ mod tests { fn test_park_timeout_unpark_before() { for _ in 0..10 { thread::current().unpark(); - thread::park_timeout_ms(u32::MAX); + thread::park_timeout(Duration::from_millis(u32::MAX as u64)); } } #[test] fn test_park_timeout_unpark_not_called() { for _ in 0..10 { - thread::park_timeout_ms(10); + thread::park_timeout(Duration::from_millis(10)); } } @@ -847,17 +847,17 @@ mod tests { let th = thread::current(); let _guard = thread::spawn(move || { - super::sleep_ms(50); + super::sleep(Duration::from_millis(50)); th.unpark(); }); - thread::park_timeout_ms(u32::MAX); + thread::park_timeout(Duration::from_millis(u32::MAX as u64)); } } #[test] fn sleep_ms_smoke() { - thread::sleep_ms(2); + thread::sleep(Duration::from_millis(2)); } // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 0981a8c4a5bf3..ad1be82d6d842 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -173,7 +173,6 @@ impl Div for Duration { #[cfg(test)] mod tests { - use prelude::v1::*; use super::Duration; #[test] diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0ad655ce00bb9..e327adfaf892c 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -250,14 +250,14 @@ pub struct PathSegment { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum PathParameters { /// The `<'a, A,B,C>` in `foo::bar::baz::<'a, A,B,C>` - AngleBracketedParameters(AngleBracketedParameterData), + AngleBracketed(AngleBracketedParameterData), /// The `(A,B)` and `C` in `Foo(A,B) -> C` - ParenthesizedParameters(ParenthesizedParameterData), + Parenthesized(ParenthesizedParameterData), } impl PathParameters { pub fn none() -> PathParameters { - AngleBracketedParameters(AngleBracketedParameterData { + PathParameters::AngleBracketed(AngleBracketedParameterData { lifetimes: Vec::new(), types: P::empty(), bindings: P::empty(), @@ -266,25 +266,25 @@ impl PathParameters { pub fn is_empty(&self) -> bool { match *self { - AngleBracketedParameters(ref data) => data.is_empty(), + PathParameters::AngleBracketed(ref data) => data.is_empty(), // Even if the user supplied no types, something like // `X()` is equivalent to `X<(),()>`. - ParenthesizedParameters(..) => false, + PathParameters::Parenthesized(..) => false, } } pub fn has_lifetimes(&self) -> bool { match *self { - AngleBracketedParameters(ref data) => !data.lifetimes.is_empty(), - ParenthesizedParameters(_) => false, + PathParameters::AngleBracketed(ref data) => !data.lifetimes.is_empty(), + PathParameters::Parenthesized(_) => false, } } pub fn has_types(&self) -> bool { match *self { - AngleBracketedParameters(ref data) => !data.types.is_empty(), - ParenthesizedParameters(..) => true, + PathParameters::AngleBracketed(ref data) => !data.types.is_empty(), + PathParameters::Parenthesized(..) => true, } } @@ -292,10 +292,10 @@ impl PathParameters { /// parameters in the parenthesized case. pub fn types(&self) -> Vec<&P> { match *self { - AngleBracketedParameters(ref data) => { + PathParameters::AngleBracketed(ref data) => { data.types.iter().collect() } - ParenthesizedParameters(ref data) => { + PathParameters::Parenthesized(ref data) => { data.inputs.iter() .chain(data.output.iter()) .collect() @@ -305,10 +305,10 @@ impl PathParameters { pub fn lifetimes(&self) -> Vec<&Lifetime> { match *self { - AngleBracketedParameters(ref data) => { + PathParameters::AngleBracketed(ref data) => { data.lifetimes.iter().collect() } - ParenthesizedParameters(_) => { + PathParameters::Parenthesized(_) => { Vec::new() } } @@ -316,10 +316,10 @@ impl PathParameters { pub fn bindings(&self) -> Vec<&P> { match *self { - AngleBracketedParameters(ref data) => { + PathParameters::AngleBracketed(ref data) => { data.bindings.iter().collect() } - ParenthesizedParameters(_) => { + PathParameters::Parenthesized(_) => { Vec::new() } } @@ -334,7 +334,7 @@ pub struct AngleBracketedParameterData { /// The type parameters for this path segment, if present. pub types: P<[P]>, /// Bindings (equality constraints) on associated types, if present. - /// E.g., `Foo`. + /// e.g., `Foo`. pub bindings: P<[P]>, } @@ -447,7 +447,7 @@ pub struct WhereClause { /// A single predicate in a `where` clause #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum WherePredicate { - /// A type binding, eg `for<'c> Foo: Send+Clone+'c` + /// A type binding, e.g. `for<'c> Foo: Send+Clone+'c` BoundPredicate(WhereBoundPredicate), /// A lifetime predicate, e.g. `'a: 'b+'c` RegionPredicate(WhereRegionPredicate), @@ -455,7 +455,7 @@ pub enum WherePredicate { EqPredicate(WhereEqPredicate), } -/// A type bound, eg `for<'c> Foo: Send+Clone+'c` +/// A type bound, e.g. `for<'c> Foo: Send+Clone+'c` #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct WhereBoundPredicate { pub span: Span, @@ -1095,7 +1095,7 @@ impl Delimited { } } -/// A sequence of token treesee +/// A sequence of token trees #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct SequenceRepetition { /// The sequence of token trees @@ -1346,7 +1346,7 @@ pub struct MethodSig { } /// Represents a method declaration in a trait declaration, possibly including -/// a default implementation A trait method is either required (meaning it +/// a default implementation. A trait method is either required (meaning it /// doesn't have an implementation, just a signature) or provided (meaning it /// has a default implementation). #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 70561b1eb551d..ba4d1e2193e5f 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -40,7 +40,7 @@ pub fn ident_to_path(s: Span, identifier: Ident) -> Path { segments: vec!( ast::PathSegment { identifier: identifier, - parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData { + parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { lifetimes: Vec::new(), types: P::empty(), bindings: P::empty(), diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 26662605ba87f..96d0052cf1801 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -305,8 +305,10 @@ pub fn find_export_name_attr(diag: &Handler, attrs: &[Attribute]) -> Option(&self, s: &mut S) -> Result<(), S::Error> { - // Encode spans as a single u64 in order to cut down on tagging overhead - // added by the RBML metadata encoding. The should be solved differently - // altogether some time (FIXME #21482) - s.emit_u64( (self.lo.0 as u64) | ((self.hi.0 as u64) << 32) ) + try!(s.emit_u32(self.lo.0)); + s.emit_u32(self.hi.0) } } impl Decodable for Span { fn decode(d: &mut D) -> Result { - let lo_hi: u64 = try! { d.read_u64() }; - let lo = BytePos(lo_hi as u32); - let hi = BytePos((lo_hi >> 32) as u32); + let lo = BytePos(try! { d.read_u32() }); + let hi = BytePos(try! { d.read_u32() }); Ok(mk_sp(lo, hi)) } } diff --git a/src/libsyntax/diagnostics/macros.rs b/src/libsyntax/diagnostics/macros.rs index 3c8347f8a8e0e..95a74d875545f 100644 --- a/src/libsyntax/diagnostics/macros.rs +++ b/src/libsyntax/diagnostics/macros.rs @@ -30,6 +30,14 @@ macro_rules! span_err { }) } +#[macro_export] +macro_rules! span_warn { + ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ + __diagnostic_used!($code); + $session.span_warn_with_code($span, &format!($($message)*), stringify!($code)) + }) +} + #[macro_export] macro_rules! span_err_or_warn { ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ @@ -43,31 +51,59 @@ macro_rules! span_err_or_warn { } #[macro_export] -macro_rules! span_warn { +macro_rules! struct_span_fatal { ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); - $session.span_warn_with_code($span, &format!($($message)*), stringify!($code)) + $session.struct_span_fatal_with_code($span, &format!($($message)*), stringify!($code)) + }) +} + +#[macro_export] +macro_rules! struct_span_err { + ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ + __diagnostic_used!($code); + $session.struct_span_err_with_code($span, &format!($($message)*), stringify!($code)) + }) +} + +#[macro_export] +macro_rules! struct_span_warn { + ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ + __diagnostic_used!($code); + $session.struct_span_warn_with_code($span, &format!($($message)*), stringify!($code)) + }) +} + +#[macro_export] +macro_rules! struct_span_err_or_warn { + ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ + __diagnostic_used!($code); + if $is_warning { + $session.struct_span_warn_with_code($span, &format!($($message)*), stringify!($code)) + } else { + $session.struct_span_err_with_code($span, &format!($($message)*), stringify!($code)) + } }) } #[macro_export] macro_rules! span_note { - ($session:expr, $span:expr, $($message:tt)*) => ({ - ($session).span_note($span, &format!($($message)*)) + ($err:expr, $span:expr, $($message:tt)*) => ({ + ($err).span_note($span, &format!($($message)*)); }) } #[macro_export] macro_rules! span_help { - ($session:expr, $span:expr, $($message:tt)*) => ({ - ($session).span_help($span, &format!($($message)*)) + ($err:expr, $span:expr, $($message:tt)*) => ({ + ($err).span_help($span, &format!($($message)*)); }) } #[macro_export] macro_rules! fileline_help { - ($session:expr, $span:expr, $($message:tt)*) => ({ - ($session).fileline_help($span, &format!($($message)*)) + ($err:expr, $span:expr, $($message:tt)*) => ({ + ($err).fileline_help($span, &format!($($message)*)); }) } diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index be0d5729c7009..d17ca3892dc68 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -62,10 +62,10 @@ pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt, match diagnostics.get_mut(&code.name) { // Previously used errors. Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }) => { - ecx.span_warn(span, &format!( + ecx.struct_span_warn(span, &format!( "diagnostic code {} already used", code - )); - ecx.span_note(previous_span, "previous invocation"); + )).span_note(previous_span, "previous invocation") + .emit(); } // Newly used errors. Some(ref mut info) => { diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index 7fef85a833e25..a7bfdedf71813 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -13,7 +13,7 @@ use self::Destination::*; use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, Pos, Span}; use diagnostics; -use errors::{Level, RenderSpan}; +use errors::{Level, RenderSpan, DiagnosticBuilder}; use errors::RenderSpan::*; use errors::Level::*; @@ -27,6 +27,17 @@ use term; pub trait Emitter { fn emit(&mut self, span: Option, msg: &str, code: Option<&str>, lvl: Level); fn custom_emit(&mut self, sp: RenderSpan, msg: &str, lvl: Level); + + /// Emit a structured diagnostic. + fn emit_struct(&mut self, db: &DiagnosticBuilder) { + self.emit(db.span, &db.message, db.code.as_ref().map(|s| &**s), db.level); + for child in &db.children { + match child.render_span { + Some(ref sp) => self.custom_emit(sp.clone(), &child.message, child.level), + None => self.emit(child.span, &child.message, None, child.level), + } + } + } } /// maximum number of lines we will print for each error; arbitrary. @@ -49,8 +60,8 @@ impl ColorConfig { } } -// A basic emitter for when we don't have access to a codemap or registry. Used -// for reporting very early errors, etc. +/// A basic emitter for when we don't have access to a codemap or registry. Used +/// for reporting very early errors, etc. pub struct BasicEmitter { dst: Destination, } @@ -111,9 +122,8 @@ impl Emitter for EmitterWriter { sp: RenderSpan, msg: &str, lvl: Level) { - match self.emit_(sp, msg, None, lvl) { - Ok(()) => {} - Err(e) => panic!("failed to print diagnostics: {:?}", e), + if let Err(e) = self.emit_(sp, msg, None, lvl) { + panic!("failed to print diagnostics: {:?}", e); } } } diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs index f2e61090ba23c..a2fae975148f9 100644 --- a/src/libsyntax/errors/mod.rs +++ b/src/libsyntax/errors/mod.rs @@ -98,6 +98,171 @@ impl error::Error for ExplicitBug { } } +/// Used for emitting structured error messages and other diagnostic information. +#[must_use] +pub struct DiagnosticBuilder<'a> { + emitter: &'a RefCell>, + level: Level, + message: String, + code: Option, + span: Option, + children: Vec, +} + +/// For example a note attached to an error. +struct SubDiagnostic { + level: Level, + message: String, + span: Option, + render_span: Option, +} + +impl<'a> DiagnosticBuilder<'a> { + /// Emit the diagnostic. + pub fn emit(&mut self) { + if self.cancelled() { + return; + } + + self.emitter.borrow_mut().emit_struct(&self); + self.cancel(); + + // if self.is_fatal() { + // panic!(FatalError); + // } + } + + /// Cancel the diagnostic (a structured diagnostic must either be emitted or + /// cancelled or it will panic when dropped). + /// BEWARE: if this DiagnosticBuilder is an error, then creating it will + /// bump the error count on the Handler and cancelling it won't undo that. + /// If you want to decrement the error count you should use `Handler::cancel`. + pub fn cancel(&mut self) { + self.level = Level::Cancelled; + } + + pub fn cancelled(&self) -> bool { + self.level == Level::Cancelled + } + + pub fn is_fatal(&self) -> bool { + self.level == Level::Fatal + } + + pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Note, msg, None, None); + self + } + pub fn span_note(&mut self , + sp: Span, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Note, msg, Some(sp), None); + self + } + pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Help, msg, None, None); + self + } + pub fn span_help(&mut self , + sp: Span, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Help, msg, Some(sp), None); + self + } + /// Prints out a message with a suggested edit of the code. + /// + /// See `diagnostic::RenderSpan::Suggestion` for more information. + pub fn span_suggestion(&mut self , + sp: Span, + msg: &str, + suggestion: String) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Help, msg, Some(sp), Some(Suggestion(sp, suggestion))); + self + } + pub fn span_end_note(&mut self , + sp: Span, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Note, msg, Some(sp), Some(EndSpan(sp))); + self + } + pub fn fileline_note(&mut self , + sp: Span, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Note, msg, Some(sp), Some(FileLine(sp))); + self + } + pub fn fileline_help(&mut self , + sp: Span, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Help, msg, Some(sp), Some(FileLine(sp))); + self + } + + pub fn span(&mut self, sp: Span) -> &mut Self { + self.span = Some(sp); + self + } + + pub fn code(&mut self, s: String) -> &mut Self { + self.code = Some(s); + self + } + + /// Convenience function for internal use, clients should use one of the + /// struct_* methods on Handler. + fn new(emitter: &'a RefCell>, + level: Level, + message: &str) -> DiagnosticBuilder<'a> { + DiagnosticBuilder { + emitter: emitter, + level: level, + message: message.to_owned(), + code: None, + span: None, + children: vec![], + } + } + + /// Convenience function for internal use, clients should use one of the + /// public methods above. + fn sub(&mut self, + level: Level, + message: &str, + span: Option, + render_span: Option) { + let sub = SubDiagnostic { + level: level, + message: message.to_owned(), + span: span, + render_span: render_span, + }; + self.children.push(sub); + } +} + +impl<'a> fmt::Debug for DiagnosticBuilder<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.message.fmt(f) + } +} + +/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or +/// we emit a bug. +impl<'a> Drop for DiagnosticBuilder<'a> { + fn drop(&mut self) { + if !self.cancelled() { + self.emitter.borrow_mut().emit(None, "Error constructed but not emitted", None, Bug); + panic!(); + } + } +} + /// A handler deals with errors; certain errors /// (fatal, bug, unimpl) may cause immediate exit, /// others log errors for later reporting. @@ -132,11 +297,104 @@ impl Handler { } } + pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> { + DiagnosticBuilder::new(&self.emit, Level::Cancelled, "") + } + + pub fn struct_span_warn<'a>(&'a self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); + result.span(sp); + if !self.can_emit_warnings { + result.cancel(); + } + result + } + pub fn struct_span_warn_with_code<'a>(&'a self, + sp: Span, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { + let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); + result.span(sp); + result.code(code.to_owned()); + if !self.can_emit_warnings { + result.cancel(); + } + result + } + pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { + let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); + if !self.can_emit_warnings { + result.cancel(); + } + result + } + pub fn struct_span_err<'a>(&'a self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + self.bump_err_count(); + let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg); + result.span(sp); + result + } + pub fn struct_span_err_with_code<'a>(&'a self, + sp: Span, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { + self.bump_err_count(); + let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg); + result.span(sp); + result.code(code.to_owned()); + result + } + pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { + self.bump_err_count(); + DiagnosticBuilder::new(&self.emit, Level::Error, msg) + } + pub fn struct_span_fatal<'a>(&'a self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + self.bump_err_count(); + let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg); + result.span(sp); + result + } + pub fn struct_span_fatal_with_code<'a>(&'a self, + sp: Span, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { + self.bump_err_count(); + let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg); + result.span(sp); + result.code(code.to_owned()); + result + } + pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { + self.bump_err_count(); + DiagnosticBuilder::new(&self.emit, Level::Fatal, msg) + } + + pub fn cancel(&mut self, err: &mut DiagnosticBuilder) { + if err.level == Level::Error || err.level == Level::Fatal { + assert!(self.has_errors()); + self.err_count.set(self.err_count.get() + 1); + } + err.cancel(); + } + pub fn span_fatal(&self, sp: Span, msg: &str) -> FatalError { if self.treat_err_as_bug { self.span_bug(sp, msg); } self.emit(Some(sp), msg, Fatal); + self.bump_err_count(); return FatalError; } pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError { @@ -144,6 +402,7 @@ impl Handler { self.span_bug(sp, msg); } self.emit_with_code(Some(sp), msg, code, Fatal); + self.bump_err_count(); return FatalError; } pub fn span_err(&self, sp: Span, msg: &str) { @@ -166,27 +425,6 @@ impl Handler { pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) { self.emit_with_code(Some(sp), msg, code, Warning); } - pub fn span_note(&self, sp: Span, msg: &str) { - self.emit(Some(sp), msg, Note); - } - pub fn span_end_note(&self, sp: Span, msg: &str) { - self.custom_emit(EndSpan(sp), msg, Note); - } - pub fn span_help(&self, sp: Span, msg: &str) { - self.emit(Some(sp), msg, Help); - } - /// Prints out a message with a suggested edit of the code. - /// - /// See `diagnostic::RenderSpan::Suggestion` for more information. - pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) { - self.custom_emit(Suggestion(sp, suggestion), msg, Help); - } - pub fn fileline_note(&self, sp: Span, msg: &str) { - self.custom_emit(FileLine(sp), msg, Note); - } - pub fn fileline_help(&self, sp: Span, msg: &str) { - self.custom_emit(FileLine(sp), msg, Help); - } pub fn span_bug(&self, sp: Span, msg: &str) -> ! { self.emit(Some(sp), msg, Bug); panic!(ExplicitBug); @@ -199,6 +437,9 @@ impl Handler { self.emit(Some(sp), msg, Bug); self.bump_err_count(); } + pub fn span_note_without_error(&self, sp: Span, msg: &str) { + self.emit.borrow_mut().emit(Some(sp), msg, None, Note); + } pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { self.span_bug(sp, &format!("unimplemented {}", msg)); } @@ -207,6 +448,7 @@ impl Handler { self.bug(msg); } self.emit.borrow_mut().emit(None, msg, None, Fatal); + self.bump_err_count(); FatalError } pub fn err(&self, msg: &str) { @@ -219,12 +461,9 @@ impl Handler { pub fn warn(&self, msg: &str) { self.emit.borrow_mut().emit(None, msg, None, Warning); } - pub fn note(&self, msg: &str) { + pub fn note_without_error(&self, msg: &str) { self.emit.borrow_mut().emit(None, msg, None, Note); } - pub fn help(&self, msg: &str) { - self.emit.borrow_mut().emit(None, msg, None, Help); - } pub fn bug(&self, msg: &str) -> ! { self.emit.borrow_mut().emit(None, msg, None, Bug); panic!(ExplicitBug); @@ -266,7 +505,7 @@ impl Handler { } } - panic!(self.fatal(&s[..])); + panic!(self.fatal(&s)); } pub fn emit(&self, @@ -301,6 +540,7 @@ pub enum Level { Warning, Note, Help, + Cancelled, } impl fmt::Display for Level { @@ -313,6 +553,7 @@ impl fmt::Display for Level { Warning => "warning".fmt(f), Note => "note".fmt(f), Help => "help".fmt(f), + Cancelled => unreachable!(), } } } @@ -324,6 +565,7 @@ impl Level { Warning => term::color::BRIGHT_YELLOW, Note => term::color::BRIGHT_GREEN, Help => term::color::BRIGHT_CYAN, + Cancelled => unreachable!(), } } } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index f198460137d5f..107626c54cc9d 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -14,6 +14,7 @@ use ast; use ast::Name; use codemap; use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION}; +use errors::DiagnosticBuilder; use ext; use ext::expand; use ext::tt::macro_rules; @@ -24,7 +25,7 @@ use parse::token; use parse::token::{InternedString, intern, str_to_ident}; use ptr::P; use util::small_vector::SmallVector; -use util::lev_distance::{lev_distance, max_suggestion_distance}; +use util::lev_distance::find_best_match_for_name; use ext::mtwt; use fold::Folder; @@ -678,6 +679,25 @@ impl<'a> ExtCtxt<'a> { } } + pub fn struct_span_warn(&self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + self.parse_sess.span_diagnostic.struct_span_warn(sp, msg) + } + pub fn struct_span_err(&self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + self.parse_sess.span_diagnostic.struct_span_err(sp, msg) + } + pub fn struct_span_fatal(&self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + self.parse_sess.span_diagnostic.struct_span_fatal(sp, msg) + } + /// Emit `msg` attached to `sp`, and stop compilation immediately. /// /// `span_err` should be strongly preferred where-ever possible: @@ -710,15 +730,6 @@ impl<'a> ExtCtxt<'a> { pub fn span_bug(&self, sp: Span, msg: &str) -> ! { self.parse_sess.span_diagnostic.span_bug(sp, msg); } - pub fn span_note(&self, sp: Span, msg: &str) { - self.parse_sess.span_diagnostic.span_note(sp, msg); - } - pub fn span_help(&self, sp: Span, msg: &str) { - self.parse_sess.span_diagnostic.span_help(sp, msg); - } - pub fn fileline_help(&self, sp: Span, msg: &str) { - self.parse_sess.span_diagnostic.fileline_help(sp, msg); - } pub fn bug(&self, msg: &str) -> ! { self.parse_sess.span_diagnostic.bug(msg); } @@ -743,17 +754,13 @@ impl<'a> ExtCtxt<'a> { token::intern(st) } - pub fn suggest_macro_name(&mut self, name: &str, span: Span) { - let mut min: Option<(Name, usize)> = None; - let max_dist = max_suggestion_distance(name); - for macro_name in self.syntax_env.names.iter() { - let dist = lev_distance(name, ¯o_name.as_str()); - if dist <= max_dist && (min.is_none() || min.unwrap().1 > dist) { - min = Some((*macro_name, dist)); - } - } - if let Some((suggestion, _)) = min { - self.fileline_help(span, &format!("did you mean `{}!`?", suggestion)); + pub fn suggest_macro_name(&mut self, + name: &str, + span: Span, + err: &mut DiagnosticBuilder<'a>) { + let names = &self.syntax_env.names; + if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) { + err.fileline_help(span, &format!("did you mean `{}!`?", suggestion)); } } } @@ -819,7 +826,7 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt, let mut es = Vec::new(); while p.token != token::Eof { es.push(cx.expander().fold_expr(panictry!(p.parse_expr()))); - if panictry!(p.eat(&token::Comma)){ + if p.eat(&token::Comma) { continue; } if p.token != token::Eof { diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 8c19a10b4b147..a74c2340cecb8 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -328,7 +328,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { }).collect(); segments.push(ast::PathSegment { identifier: last_identifier, - parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData { + parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { lifetimes: lifetimes, types: P::from_vec(types), bindings: P::from_vec(bindings), @@ -366,7 +366,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { let mut path = trait_path; path.segments.push(ast::PathSegment { identifier: ident, - parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData { + parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { lifetimes: lifetimes, types: P::from_vec(types), bindings: P::from_vec(bindings), diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 743bcda18def4..5f27bdfc98a41 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -201,11 +201,12 @@ fn expand_mac_invoc(mac: ast::Mac, let extname = pth.segments[0].identifier.name; match fld.cx.syntax_env.find(extname) { None => { - fld.cx.span_err( + let mut err = fld.cx.struct_span_err( pth.span, &format!("macro undefined: '{}!'", &extname)); - fld.cx.suggest_macro_name(&extname.as_str(), pth.span); + fld.cx.suggest_macro_name(&extname.as_str(), pth.span, &mut err); + err.emit(); // let compilation continue None @@ -334,11 +335,15 @@ fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool for attr in attrs { let mut is_use = attr.check_name("macro_use"); if attr.check_name("macro_escape") { - fld.cx.span_warn(attr.span, "macro_escape is a deprecated synonym for macro_use"); + let mut err = + fld.cx.struct_span_warn(attr.span, + "macro_escape is a deprecated synonym for macro_use"); is_use = true; if let ast::AttrStyle::Inner = attr.node.style { - fld.cx.fileline_help(attr.span, "consider an outer attribute, \ - #[macro_use] mod ..."); + err.fileline_help(attr.span, "consider an outer attribute, \ + #[macro_use] mod ...").emit(); + } else { + err.emit(); } }; diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 0c3a8b05fba1e..bc7dc67e1bae5 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -801,8 +801,8 @@ fn parse_arguments_to_quote(cx: &ExtCtxt, tts: &[TokenTree]) p.quote_depth += 1; let cx_expr = panictry!(p.parse_expr()); - if !panictry!(p.eat(&token::Comma)) { - panic!(p.fatal("expected token `,`")); + if !p.eat(&token::Comma) { + let _ = p.diagnostic().fatal("expected token `,`"); } let tts = panictry!(p.parse_all_token_trees()); diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 7899e170ecf19..f00224bacdd2d 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -117,11 +117,9 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree while self.p.token != token::Eof { match panictry!(self.p.parse_item()) { Some(item) => ret.push(item), - None => panic!(self.p.span_fatal( - self.p.span, - &format!("expected item, found `{}`", - self.p.this_token_to_string()) - )) + None => panic!(self.p.diagnostic().span_fatal(self.p.span, + &format!("expected item, found `{}`", + self.p.this_token_to_string()))) } } Some(ret) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index fb09118035c7e..ae8ab0541050c 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -82,6 +82,7 @@ use ast; use ast::{TokenTree, Name, Ident}; use codemap::{BytePos, mk_sp, Span, Spanned}; use codemap; +use errors::FatalError; use parse::lexer::*; //resolve bug? use parse::ParseSess; use parse::parser::{LifetimeAndTypesWithoutColons, Parser}; @@ -499,27 +500,34 @@ pub fn parse(sess: &ParseSess, } } -pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal { +pub fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal { match name { "tt" => { p.quote_depth += 1; //but in theory, non-quoted tts might be useful - let res = token::NtTT(P(panictry!(p.parse_token_tree()))); + let res: ::parse::PResult<'a, _> = p.parse_token_tree(); + let res = token::NtTT(P(panictry!(res))); p.quote_depth -= 1; return res; } _ => {} } // check at the beginning and the parser checks after each bump - panictry!(p.check_unknown_macro_variable()); + p.check_unknown_macro_variable(); match name { "item" => match panictry!(p.parse_item()) { Some(i) => token::NtItem(i), - None => panic!(p.fatal("expected an item keyword")) + None => { + p.fatal("expected an item keyword").emit(); + panic!(FatalError); + } }, "block" => token::NtBlock(panictry!(p.parse_block())), "stmt" => match panictry!(p.parse_stmt()) { Some(s) => token::NtStmt(s), - None => panic!(p.fatal("expected a statement")) + None => { + p.fatal("expected a statement").emit(); + panic!(FatalError); + } }, "pat" => token::NtPat(panictry!(p.parse_pat())), "expr" => token::NtExpr(panictry!(p.parse_expr())), @@ -527,13 +535,14 @@ pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal { // this could be handled like a token, since it is one "ident" => match p.token { token::Ident(sn,b) => { - panictry!(p.bump()); + p.bump(); token::NtIdent(Box::new(Spanned::{node: sn, span: p.span}),b) } _ => { let token_str = pprust::token_to_string(&p.token); - panic!(p.fatal(&format!("expected ident, found {}", - &token_str[..]))) + p.fatal(&format!("expected ident, found {}", + &token_str[..])).emit(); + panic!(FatalError) } }, "path" => { @@ -541,11 +550,12 @@ pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal { }, "meta" => token::NtMeta(panictry!(p.parse_meta_item())), _ => { - panic!(p.span_fatal_help(sp, - &format!("invalid fragment specifier `{}`", name), - "valid fragment specifiers are `ident`, `block`, \ - `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \ - and `item`")) + p.span_fatal_help(sp, + &format!("invalid fragment specifier `{}`", name), + "valid fragment specifiers are `ident`, `block`, \ + `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \ + and `item`").emit(); + panic!(FatalError); } } } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index fae60d2803293..c61b91df09249 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -47,7 +47,7 @@ impl<'a> ParserAnyMacro<'a> { fn ensure_complete_parse(&self, allow_semi: bool, context: &str) { let mut parser = self.parser.borrow_mut(); if allow_semi && parser.token == token::Semi { - panictry!(parser.bump()) + parser.bump(); } if parser.token != token::Eof { let token_str = parser.this_token_to_string(); @@ -55,12 +55,12 @@ impl<'a> ParserAnyMacro<'a> { following", token_str); let span = parser.span; - parser.span_err(span, &msg[..]); - + let mut err = parser.diagnostic().struct_span_err(span, &msg[..]); let msg = format!("caused by the macro expansion here; the usage \ of `{}!` is likely invalid in {} context", self.macro_ident, context); - parser.span_note(self.site_span, &msg[..]); + err.span_note(self.site_span, &msg[..]) + .emit(); } } } @@ -111,7 +111,10 @@ impl<'a> MacResult for ParserAnyMacro<'a> { Some(stmt) => ret.push(stmt), None => (), }, - Err(_) => break, + Err(mut e) => { + e.emit(); + break; + } } } } @@ -175,7 +178,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers let lhs_tt = match *lhs { TokenTree::Delimited(_, ref delim) => &delim.tts[..], - _ => cx.span_bug(sp, "malformed macro lhs") + _ => cx.span_fatal(sp, "malformed macro lhs") }; match TokenTree::parse(cx, lhs_tt, arg) { @@ -183,7 +186,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, let rhs = match rhses[i] { // ignore delimiters TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(), - _ => cx.span_bug(sp, "malformed macro rhs"), + _ => cx.span_fatal(sp, "malformed macro rhs"), }; // rhs has holes ( `$id` and `$(...)` that need filled) let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic, @@ -191,7 +194,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, imported_from, rhs); let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr)); - panictry!(p.check_unknown_macro_variable()); + p.check_unknown_macro_variable(); // Let the context choose how to interpret the result. // Weird, but useful for X-macros. return Box::new(ParserAnyMacro { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 4ea0fd76fea4a..c281571305b8b 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -326,6 +326,14 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat "the `#[rustc_error]` attribute \ is just used for rustc unit tests \ and will never be stable")), + ("rustc_if_this_changed", Whitelisted, Gated("rustc_attrs", + "the `#[rustc_if_this_changed]` attribute \ + is just used for rustc unit tests \ + and will never be stable")), + ("rustc_then_this_would_need", Whitelisted, Gated("rustc_attrs", + "the `#[rustc_if_this_changed]` attribute \ + is just used for rustc unit tests \ + and will never be stable")), ("rustc_move_fragments", Normal, Gated("rustc_attrs", "the `#[rustc_move_fragments]` attribute \ is just used for rustc unit tests \ @@ -725,17 +733,21 @@ pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIs GateIssue::Library(lib) => lib, }; - if let Some(n) = issue { - diag.span_err(span, &format!("{} (see issue #{})", explain, n)); + let mut err = if let Some(n) = issue { + diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n)) } else { - diag.span_err(span, explain); - } + diag.struct_span_err(span, explain) + }; // #23973: do not suggest `#![feature(...)]` if we are in beta/stable - if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { return; } - diag.fileline_help(span, &format!("add #![feature({})] to the \ - crate attributes to enable", - feature)); + if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { + err.emit(); + return; + } + err.fileline_help(span, &format!("add #![feature({})] to the \ + crate attributes to enable", + feature)); + err.emit(); } pub const EXPLAIN_ASM: &'static str = @@ -942,11 +954,13 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { self.gate_feature("braced_empty_structs", span, "empty structs and enum variants with braces are unstable"); } else if s.is_tuple() { - self.context.span_handler.span_err(span, "empty tuple structs and enum variants \ - are not allowed, use unit structs and \ - enum variants instead"); - self.context.span_handler.span_help(span, "remove trailing `()` to make a unit \ - struct or unit enum variant"); + self.context.span_handler.struct_span_err(span, "empty tuple structs and enum \ + variants are not allowed, use \ + unit structs and enum variants \ + instead") + .span_help(span, "remove trailing `()` to make a unit \ + struct or unit enum variant") + .emit(); } } visit::walk_struct_def(self, s) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index cb467f9016b6f..1de6d6c002f06 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -469,10 +469,10 @@ pub fn noop_fold_path_parameters(path_parameters: PathParameters, fld -> PathParameters { match path_parameters { - AngleBracketedParameters(data) => - AngleBracketedParameters(fld.fold_angle_bracketed_parameter_data(data)), - ParenthesizedParameters(data) => - ParenthesizedParameters(fld.fold_parenthesized_parameter_data(data)), + PathParameters::AngleBracketed(data) => + PathParameters::AngleBracketed(fld.fold_angle_bracketed_parameter_data(data)), + PathParameters::Parenthesized(data) => + PathParameters::Parenthesized(fld.fold_parenthesized_parameter_data(data)), } } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index c09e35f1077a5..795f4044f6eb1 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -40,17 +40,22 @@ extern crate libc; extern crate serialize as rustc_serialize; // used by deriving -// A variant of 'try!' that panics on Err(FatalError). This is used as a -// crutch on the way towards a non-panic!-prone parser. It should be used -// for fatal parsing errors; eventually we plan to convert all code using -// panictry to just use normal try +// A variant of 'try!' that panics on an Err. This is used as a crutch on the +// way towards a non-panic!-prone parser. It should be used for fatal parsing +// errors; eventually we plan to convert all code using panictry to just use +// normal try. +// Exported for syntax_ext, not meant for general use. +#[macro_export] macro_rules! panictry { ($e:expr) => ({ use std::result::Result::{Ok, Err}; - use errors::FatalError; + use $crate::errors::FatalError; match $e { Ok(e) => e, - Err(FatalError) => panic!(FatalError) + Err(mut e) => { + e.emit(); + panic!(FatalError); + } } }) } diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 5df2478d48758..cad9b45694b28 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -19,7 +19,7 @@ use ptr::P; impl<'a> Parser<'a> { /// Parse attributes that appear before an item - pub fn parse_outer_attributes(&mut self) -> PResult> { + pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec> { let mut attrs: Vec = Vec::new(); loop { debug!("parse_outer_attributes: self.token={:?}", @@ -39,7 +39,7 @@ impl<'a> Parser<'a> { return Err(self.fatal("expected outer comment")); } attrs.push(attr); - try!(self.bump()); + self.bump(); } _ => break } @@ -51,24 +51,26 @@ impl<'a> Parser<'a> { /// /// If permit_inner is true, then a leading `!` indicates an inner /// attribute - pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult { + pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> { debug!("parse_attributes: permit_inner={:?} self.token={:?}", permit_inner, self.token); let (span, value, mut style) = match self.token { token::Pound => { let lo = self.span.lo; - try!(self.bump()); + self.bump(); if permit_inner { self.expected_tokens.push(TokenType::Token(token::Not)); } let style = if self.token == token::Not { - try!(self.bump()); + self.bump(); if !permit_inner { let span = self.span; - self.span_err(span, - "an inner attribute is not permitted in \ - this context"); - self.fileline_help(span, - "place inner attribute at the top of the module or block"); + self.diagnostic().struct_span_err(span, + "an inner attribute is not permitted in \ + this context") + .fileline_help(span, + "place inner attribute at the top of \ + the module or block") + .emit() } ast::AttrStyle::Inner } else { @@ -89,7 +91,7 @@ impl<'a> Parser<'a> { }; if permit_inner && self.token == token::Semi { - try!(self.bump()); + self.bump(); self.span_warn(span, "this inner attribute syntax is deprecated. \ The new syntax is `#![foo]`, with a bang and no semicolon"); style = ast::AttrStyle::Inner; @@ -111,7 +113,7 @@ impl<'a> Parser<'a> { /// terminated by a semicolon. /// matches inner_attrs* - pub fn parse_inner_attributes(&mut self) -> PResult> { + pub fn parse_inner_attributes(&mut self) -> PResult<'a, Vec> { let mut attrs: Vec = vec![]; loop { match self.token { @@ -132,7 +134,7 @@ impl<'a> Parser<'a> { let attr = attr::mk_sugared_doc_attr(attr::mk_attr_id(), str, lo, hi); if attr.node.style == ast::AttrStyle::Inner { attrs.push(attr); - try!(self.bump()); + self.bump(); } else { break; } @@ -146,7 +148,7 @@ impl<'a> Parser<'a> { /// matches meta_item = IDENT /// | IDENT = lit /// | IDENT meta_seq - pub fn parse_meta_item(&mut self) -> PResult> { + pub fn parse_meta_item(&mut self) -> PResult<'a, P> { let nt_meta = match self.token { token::Interpolated(token::NtMeta(ref e)) => { Some(e.clone()) @@ -156,7 +158,7 @@ impl<'a> Parser<'a> { match nt_meta { Some(meta) => { - try!(self.bump()); + self.bump(); return Ok(meta); } None => {} @@ -167,7 +169,7 @@ impl<'a> Parser<'a> { let name = self.id_to_interned_str(ident); match self.token { token::Eq => { - try!(self.bump()); + self.bump(); let lit = try!(self.parse_lit()); // FIXME #623 Non-string meta items are not serialized correctly; // just forbid them for now @@ -195,10 +197,10 @@ impl<'a> Parser<'a> { } /// matches meta_seq = ( COMMASEP(meta_item) ) - fn parse_meta_seq(&mut self) -> PResult>> { + fn parse_meta_seq(&mut self) -> PResult<'a, Vec>> { self.parse_unspanned_seq(&token::OpenDelim(token::Paren), &token::CloseDelim(token::Paren), seq_sep_trailing_allowed(token::Comma), - |p| p.parse_meta_item()) + |p: &mut Parser<'a>| p.parse_meta_item()) } } diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 4619410ada727..3d8f3bcd5268f 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -11,7 +11,7 @@ use ast; use codemap::{BytePos, CharPos, CodeMap, Pos, Span}; use codemap; -use errors::{FatalError, Handler}; +use errors::{FatalError, Handler, DiagnosticBuilder}; use ext::tt::transcribe::tt_next_token; use parse::token::str_to_ident; use parse::token; @@ -173,10 +173,6 @@ impl<'a> StringReader<'a> { self.span_diagnostic.span_err(sp, m) } - /// Suggest some help with a given span. - pub fn help_span(&self, sp: Span, m: &str) { - self.span_diagnostic.span_help(sp, m) - } /// Report a fatal error spanning [`from_pos`, `to_pos`). fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError { @@ -188,11 +184,6 @@ impl<'a> StringReader<'a> { self.err_span(codemap::mk_sp(from_pos, to_pos), m) } - /// Suggest some help spanning [`from_pos`, `to_pos`). - fn help_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) { - self.help_span(codemap::mk_sp(from_pos, to_pos), m) - } - /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an /// escaped character to the error message fn fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) -> FatalError { @@ -201,6 +192,17 @@ impl<'a> StringReader<'a> { for c in c.escape_default() { m.push(c) } self.fatal_span_(from_pos, to_pos, &m[..]) } + fn struct_fatal_span_char(&self, + from_pos: BytePos, + to_pos: BytePos, + m: &str, + c: char) + -> DiagnosticBuilder<'a> { + let mut m = m.to_string(); + m.push_str(": "); + for c in c.escape_default() { m.push(c) } + self.span_diagnostic.struct_span_fatal(codemap::mk_sp(from_pos, to_pos), &m[..]) + } /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an /// escaped character to the error message @@ -210,6 +212,17 @@ impl<'a> StringReader<'a> { for c in c.escape_default() { m.push(c) } self.err_span_(from_pos, to_pos, &m[..]); } + fn struct_err_span_char(&self, + from_pos: BytePos, + to_pos: BytePos, + m: &str, + c: char) + -> DiagnosticBuilder<'a> { + let mut m = m.to_string(); + m.push_str(": "); + for c in c.escape_default() { m.push(c) } + self.span_diagnostic.struct_span_err(codemap::mk_sp(from_pos, to_pos), &m[..]) + } /// Report a lexical error spanning [`from_pos`, `to_pos`), appending the /// offending string to the error message @@ -746,10 +759,12 @@ impl<'a> StringReader<'a> { let valid = if self.curr_is('{') { self.scan_unicode_escape(delim) && !ascii_only } else { - self.err_span_(start, self.last_pos, - "incorrect unicode escape sequence"); - self.help_span_(start, self.last_pos, - "format of unicode escape sequences is `\\u{…}`"); + let span = codemap::mk_sp(start, self.last_pos); + self.span_diagnostic.struct_span_err(span, + "incorrect unicode escape sequence") + .span_help(span, + "format of unicode escape sequences is `\\u{…}`") + .emit(); false }; if ascii_only { @@ -771,21 +786,22 @@ impl<'a> StringReader<'a> { } c => { let last_pos = self.last_pos; - self.err_span_char( + let mut err = self.struct_err_span_char( escaped_pos, last_pos, if ascii_only { "unknown byte escape" } else { "unknown character escape" }, c); if e == '\r' { - self.help_span_(escaped_pos, last_pos, + err.span_help(codemap::mk_sp(escaped_pos, last_pos), "this is an isolated carriage return; consider checking \ - your editor and version control settings") + your editor and version control settings"); } if (e == '{' || e == '}') && !ascii_only { - self.help_span_(escaped_pos, last_pos, + err.span_help(codemap::mk_sp(escaped_pos, last_pos), "if used in a formatting string, \ - curly braces are escaped with `{{` and `}}`") + curly braces are escaped with `{{` and `}}`"); } + err.emit(); false } } @@ -1224,8 +1240,13 @@ impl<'a> StringReader<'a> { c => { let last_bpos = self.last_pos; let bpos = self.pos; - unicode_chars::check_for_substitution(&self, c); - panic!(self.fatal_span_char(last_bpos, bpos, "unknown start of token", c)) + let mut err = self.struct_fatal_span_char(last_bpos, + bpos, + "unknown start of token", + c); + unicode_chars::check_for_substitution(&self, c, &mut err); + err.emit(); + panic!(FatalError); } } } diff --git a/src/libsyntax/parse/lexer/unicode_chars.rs b/src/libsyntax/parse/lexer/unicode_chars.rs index a2f37426210e9..1d32dd4973127 100644 --- a/src/libsyntax/parse/lexer/unicode_chars.rs +++ b/src/libsyntax/parse/lexer/unicode_chars.rs @@ -12,6 +12,7 @@ // http://www.unicode.org/Public/security/revision-06/confusables.txt use codemap::mk_sp as make_span; +use errors::DiagnosticBuilder; use super::StringReader; const UNICODE_ARRAY: &'static [(char, &'static str, char)] = &[ @@ -179,7 +180,9 @@ const ASCII_ARRAY: &'static [(char, &'static str)] = &[ ('=', "Equals Sign"), ('>', "Greater-Than Sign"), ]; -pub fn check_for_substitution(reader: &StringReader, ch: char) { +pub fn check_for_substitution<'a>(reader: &StringReader<'a>, + ch: char, + err: &mut DiagnosticBuilder<'a>) { UNICODE_ARRAY .iter() .find(|&&(c, _, _)| c == ch) @@ -190,7 +193,7 @@ pub fn check_for_substitution(reader: &StringReader, ch: char) { let msg = format!("unicode character '{}' ({}) looks much like '{}' ({}), but it's not", ch, u_name, ascii_char, ascii_name); - reader.help_span(span, &msg); + err.span_help(span, &msg); }, None => { reader diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 6951d6319acaf..a122456550116 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -12,7 +12,7 @@ use ast; use codemap::{self, Span, CodeMap, FileMap}; -use errors::{Handler, ColorConfig, FatalError}; +use errors::{Handler, ColorConfig, DiagnosticBuilder}; use parse::parser::Parser; use parse::token::InternedString; use ptr::P; @@ -25,7 +25,7 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use std::str; -pub type PResult = Result; +pub type PResult<'a, T> = Result>; #[macro_use] pub mod parser; @@ -76,8 +76,8 @@ pub fn parse_crate_from_file( cfg: ast::CrateConfig, sess: &ParseSess ) -> ast::Crate { - panictry!(new_parser_from_file(sess, cfg, input).parse_crate_mod()) - // why is there no p.abort_if_errors here? + let mut parser = new_parser_from_file(sess, cfg, input); + abort_if_errors(parser.parse_crate_mod(), &parser) } pub fn parse_crate_attrs_from_file( @@ -85,8 +85,8 @@ pub fn parse_crate_attrs_from_file( cfg: ast::CrateConfig, sess: &ParseSess ) -> Vec { - // FIXME: maybe_aborted? - panictry!(new_parser_from_file(sess, cfg, input).parse_inner_attributes()) + let mut parser = new_parser_from_file(sess, cfg, input); + abort_if_errors(parser.parse_inner_attributes(), &parser) } pub fn parse_crate_from_source_str(name: String, @@ -261,7 +261,7 @@ pub fn tts_to_parser<'a>(sess: &'a ParseSess, cfg: ast::CrateConfig) -> Parser<'a> { let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, None, tts); let mut p = Parser::new(sess, cfg, Box::new(trdr)); - panictry!(p.check_unknown_macro_variable()); + p.check_unknown_macro_variable(); p } @@ -271,6 +271,20 @@ pub fn maybe_aborted(result: T, p: Parser) -> T { result } +fn abort_if_errors<'a, T>(result: PResult<'a, T>, p: &Parser) -> T { + match result { + Ok(c) => { + p.abort_if_errors(); + c + } + Err(mut e) => { + e.emit(); + p.abort_if_errors(); + unreachable!(); + } + } +} + /// Parse a string representing a character literal into its final form. /// Rather than just accepting/rejecting a given literal, unescapes it as /// well. Can take any slice prefixed by a character escape. Returns the @@ -449,11 +463,13 @@ fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>, Some(suf) => { if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) { // if it looks like a width, lets try to be helpful. - sd.span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..])); - sd.fileline_help(sp, "valid widths are 32 and 64"); + sd.struct_span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..])) + .fileline_help(sp, "valid widths are 32 and 64") + .emit(); } else { - sd.span_err(sp, &format!("invalid suffix `{}` for float literal", suf)); - sd.fileline_help(sp, "valid suffixes are `f32` and `f64`"); + sd.struct_span_err(sp, &format!("invalid suffix `{}` for float literal", suf)) + .fileline_help(sp, "valid suffixes are `f32` and `f64`") + .emit(); } ast::LitFloatUnsuffixed(data) @@ -622,13 +638,15 @@ pub fn integer_lit(s: &str, // i and u look like widths, so lets // give an error message along those lines if looks_like_width_suffix(&['i', 'u'], suf) { - sd.span_err(sp, &format!("invalid width `{}` for integer literal", - &suf[1..])); - sd.fileline_help(sp, "valid widths are 8, 16, 32 and 64"); + sd.struct_span_err(sp, &format!("invalid width `{}` for integer literal", + &suf[1..])) + .fileline_help(sp, "valid widths are 8, 16, 32 and 64") + .emit(); } else { - sd.span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf)); - sd.fileline_help(sp, "the suffix must be one of the integral types \ - (`u32`, `isize`, etc)"); + sd.struct_span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf)) + .fileline_help(sp, "the suffix must be one of the integral types \ + (`u32`, `isize`, etc)") + .emit(); } ty diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs index 5dba1e189ab15..75f1ac49c9acc 100644 --- a/src/libsyntax/parse/obsolete.rs +++ b/src/libsyntax/parse/obsolete.rs @@ -59,18 +59,17 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> { kind_str: &str, desc: &str, error: bool) { - if error { - self.span_err(sp, &format!("obsolete syntax: {}", kind_str)); + let mut err = if error { + self.diagnostic().struct_span_err(sp, &format!("obsolete syntax: {}", kind_str)) } else { - self.span_warn(sp, &format!("obsolete syntax: {}", kind_str)); - } + self.diagnostic().struct_span_warn(sp, &format!("obsolete syntax: {}", kind_str)) + }; if !self.obsolete_set.contains(&kind) && (error || self.sess.span_diagnostic.can_emit_warnings) { - self.sess - .span_diagnostic - .note(&format!("{}", desc)); + err.note(&format!("{}", desc)); self.obsolete_set.insert(kind); } + err.emit(); } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index dbd34b49f7d9e..e089e630e5760 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -60,14 +60,14 @@ use attr::{ThinAttributes, ThinAttributesExt, AttributesExt}; use ast; use ast_util::{self, ident_to_path}; use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap}; -use errors::{self, FatalError}; +use errors::{self, DiagnosticBuilder}; use ext::tt::macro_parser; use parse; use parse::classify; use parse::common::{SeqSep, seq_sep_none, seq_sep_trailing_allowed}; use parse::lexer::{Reader, TokenAndSpan}; use parse::obsolete::{ParserObsoleteMethods, ObsoleteSyntax}; -use parse::token::{self, MatchNt, SubstNt, SpecialVarNt, InternedString}; +use parse::token::{self, intern, MatchNt, SubstNt, SpecialVarNt, InternedString}; use parse::token::{keywords, special_idents, SpecialMacroVar}; use parse::{new_sub_parser_from_file, ParseSess}; use util::parser::{AssocOp, Fixity}; @@ -155,7 +155,7 @@ macro_rules! maybe_whole_expr { }; match found { Some(e) => { - try!($p.bump()); + $p.bump(); return Ok(e); } None => () @@ -170,7 +170,7 @@ macro_rules! maybe_whole { { let found = match ($p).token { token::Interpolated(token::$constructor(_)) => { - Some(try!(($p).bump_and_get())) + Some(($p).bump_and_get()) } _ => None }; @@ -183,7 +183,7 @@ macro_rules! maybe_whole { { let found = match ($p).token { token::Interpolated(token::$constructor(_)) => { - Some(try!(($p).bump_and_get())) + Some(($p).bump_and_get()) } _ => None }; @@ -196,7 +196,7 @@ macro_rules! maybe_whole { { let found = match ($p).token { token::Interpolated(token::$constructor(_)) => { - Some(try!(($p).bump_and_get())) + Some(($p).bump_and_get()) } _ => None }; @@ -209,7 +209,7 @@ macro_rules! maybe_whole { { let found = match ($p).token { token::Interpolated(token::$constructor(_)) => { - Some(try!(($p).bump_and_get())) + Some(($p).bump_and_get()) } _ => None }; @@ -222,7 +222,7 @@ macro_rules! maybe_whole { { let found = match ($p).token { token::Interpolated(token::$constructor(_)) => { - Some(try!(($p).bump_and_get())) + Some(($p).bump_and_get()) } _ => None }; @@ -392,26 +392,26 @@ impl<'a> Parser<'a> { Parser::token_to_string(&self.token) } - pub fn unexpected_last(&self, t: &token::Token) -> FatalError { + pub fn unexpected_last(&self, t: &token::Token) -> PResult<'a, T> { let token_str = Parser::token_to_string(t); let last_span = self.last_span; - self.span_fatal(last_span, &format!("unexpected token: `{}`", - token_str)) + Err(self.span_fatal(last_span, &format!("unexpected token: `{}`", token_str))) } - pub fn unexpected(&mut self) -> FatalError { + pub fn unexpected(&mut self) -> PResult<'a, T> { match self.expect_one_of(&[], &[]) { - Err(e) => e, - Ok(_) => unreachable!() + Err(e) => Err(e), + Ok(_) => unreachable!(), } } /// Expect and consume the token t. Signal an error if /// the next token is not t. - pub fn expect(&mut self, t: &token::Token) -> PResult<()> { + pub fn expect(&mut self, t: &token::Token) -> PResult<'a, ()> { if self.expected_tokens.is_empty() { if self.token == *t { - self.bump() + self.bump(); + Ok(()) } else { let token_str = Parser::token_to_string(t); let this_token_str = self.this_token_to_string(); @@ -429,7 +429,7 @@ impl<'a> Parser<'a> { /// anything. Signal a fatal error if next token is unexpected. pub fn expect_one_of(&mut self, edible: &[token::Token], - inedible: &[token::Token]) -> PResult<()>{ + inedible: &[token::Token]) -> PResult<'a, ()>{ fn tokens_to_string(tokens: &[TokenType]) -> String { let mut i = tokens.iter(); // This might be a sign we need a connect method on Iterator. @@ -448,7 +448,8 @@ impl<'a> Parser<'a> { }) } if edible.contains(&self.token) { - self.bump() + self.bump(); + Ok(()) } else if inedible.contains(&self.token) { // leave it in the input Ok(()) @@ -484,19 +485,18 @@ impl<'a> Parser<'a> { /// true if and only if input was consumed for recovery. pub fn check_for_erroneous_unit_struct_expecting(&mut self, expected: &[token::Token]) - -> PResult { + -> bool { if self.token == token::OpenDelim(token::Brace) && expected.iter().all(|t| *t != token::OpenDelim(token::Brace)) && self.look_ahead(1, |t| *t == token::CloseDelim(token::Brace)) { // matched; signal non-fatal error and recover. let span = self.span; - self.span_err(span, - "unit-like struct construction is written with no trailing `{ }`"); - try!(self.eat(&token::OpenDelim(token::Brace))); - try!(self.eat(&token::CloseDelim(token::Brace))); - Ok(true) + self.span_err(span, "unit-like struct construction is written with no trailing `{ }`"); + self.eat(&token::OpenDelim(token::Brace)); + self.eat(&token::CloseDelim(token::Brace)); + true } else { - Ok(false) + false } } @@ -504,7 +504,7 @@ impl<'a> Parser<'a> { /// followed by some token from the set edible + inedible. Recover /// from anticipated input errors, discarding erroneous characters. pub fn commit_expr(&mut self, e: &Expr, edible: &[token::Token], - inedible: &[token::Token]) -> PResult<()> { + inedible: &[token::Token]) -> PResult<'a, ()> { debug!("commit_expr {:?}", e); if let ExprPath(..) = e.node { // might be unit-struct construction; check for recoverableinput error. @@ -512,12 +512,12 @@ impl<'a> Parser<'a> { .cloned() .chain(inedible.iter().cloned()) .collect::>(); - try!(self.check_for_erroneous_unit_struct_expecting(&expected[..])); + self.check_for_erroneous_unit_struct_expecting(&expected[..]); } self.expect_one_of(edible, inedible) } - pub fn commit_expr_expecting(&mut self, e: &Expr, edible: token::Token) -> PResult<()> { + pub fn commit_expr_expecting(&mut self, e: &Expr, edible: token::Token) -> PResult<'a, ()> { self.commit_expr(e, &[edible], &[]) } @@ -525,7 +525,7 @@ impl<'a> Parser<'a> { /// followed by some token from the set edible + inedible. Check /// for recoverable input errors, discarding erroneous characters. pub fn commit_stmt(&mut self, edible: &[token::Token], - inedible: &[token::Token]) -> PResult<()> { + inedible: &[token::Token]) -> PResult<'a, ()> { if self.last_token .as_ref() .map_or(false, |t| t.is_ident() || t.is_path()) { @@ -533,21 +533,21 @@ impl<'a> Parser<'a> { .cloned() .chain(inedible.iter().cloned()) .collect::>(); - try!(self.check_for_erroneous_unit_struct_expecting(&expected)); + self.check_for_erroneous_unit_struct_expecting(&expected); } self.expect_one_of(edible, inedible) } - pub fn commit_stmt_expecting(&mut self, edible: token::Token) -> PResult<()> { + pub fn commit_stmt_expecting(&mut self, edible: token::Token) -> PResult<'a, ()> { self.commit_stmt(&[edible], &[]) } - pub fn parse_ident(&mut self) -> PResult { + pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> { self.check_strict_keywords(); - try!(self.check_reserved_keywords()); + self.check_reserved_keywords(); match self.token { token::Ident(i, _) => { - try!(self.bump()); + self.bump(); Ok(i) } token::Interpolated(token::NtIdent(..)) => { @@ -561,7 +561,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_ident_or_self_type(&mut self) -> PResult { + pub fn parse_ident_or_self_type(&mut self) -> PResult<'a, ast::Ident> { if self.is_self_type_ident() { self.expect_self_type_ident() } else { @@ -569,9 +569,9 @@ impl<'a> Parser<'a> { } } - pub fn parse_path_list_item(&mut self) -> PResult { + pub fn parse_path_list_item(&mut self) -> PResult<'a, ast::PathListItem> { let lo = self.span.lo; - let node = if try!(self.eat_keyword(keywords::SelfValue)) { + let node = if self.eat_keyword(keywords::SelfValue) { let rename = try!(self.parse_rename()); ast::PathListMod { id: ast::DUMMY_NODE_ID, rename: rename } } else { @@ -595,10 +595,10 @@ impl<'a> Parser<'a> { /// Consume token 'tok' if it exists. Returns true if the given /// token was present, false otherwise. - pub fn eat(&mut self, tok: &token::Token) -> PResult { + pub fn eat(&mut self, tok: &token::Token) -> bool { let is_present = self.check(tok); - if is_present { try!(self.bump())} - Ok(is_present) + if is_present { self.bump() } + is_present } pub fn check_keyword(&mut self, kw: keywords::Keyword) -> bool { @@ -608,30 +608,30 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, eat it and return /// true. Otherwise, return false. - pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> PResult { + pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> bool { if self.check_keyword(kw) { - try!(self.bump()); - Ok(true) + self.bump(); + true } else { - Ok(false) + false } } - pub fn eat_keyword_noexpect(&mut self, kw: keywords::Keyword) -> PResult { + pub fn eat_keyword_noexpect(&mut self, kw: keywords::Keyword) -> bool { if self.token.is_keyword(kw) { - try!(self.bump()); - Ok(true) + self.bump(); + true } else { - Ok(false) + false } } /// If the given word is not a keyword, signal an error. /// If the next token is not the given word, signal an error. /// Otherwise, eat it. - pub fn expect_keyword(&mut self, kw: keywords::Keyword) -> PResult<()> { - if !try!(self.eat_keyword(kw) ){ - self.expect_one_of(&[], &[]) + pub fn expect_keyword(&mut self, kw: keywords::Keyword) -> PResult<'a, ()> { + if !self.eat_keyword(kw) { + self.unexpected() } else { Ok(()) } @@ -649,28 +649,28 @@ impl<'a> Parser<'a> { } /// Signal an error if the current token is a reserved keyword - pub fn check_reserved_keywords(&mut self) -> PResult<()>{ + pub fn check_reserved_keywords(&mut self) { if self.token.is_reserved_keyword() { let token_str = self.this_token_to_string(); - Err(self.fatal(&format!("`{}` is a reserved keyword", - token_str))) - } else { - Ok(()) + self.fatal(&format!("`{}` is a reserved keyword", token_str)).emit() } } /// Expect and consume an `&`. If `&&` is seen, replace it with a single /// `&` and continue. If an `&` is not seen, signal an error. - fn expect_and(&mut self) -> PResult<()> { + fn expect_and(&mut self) -> PResult<'a, ()> { self.expected_tokens.push(TokenType::Token(token::BinOp(token::And))); match self.token { - token::BinOp(token::And) => self.bump(), + token::BinOp(token::And) => { + self.bump(); + Ok(()) + } token::AndAnd => { let span = self.span; let lo = span.lo + BytePos(1); Ok(self.replace_token(token::BinOp(token::And), lo, span.hi)) } - _ => self.expect_one_of(&[], &[]) + _ => self.unexpected() } } @@ -693,23 +693,26 @@ impl<'a> Parser<'a> { /// /// This is meant to be used when parsing generics on a path to get the /// starting token. - fn eat_lt(&mut self) -> PResult { + fn eat_lt(&mut self) -> bool { self.expected_tokens.push(TokenType::Token(token::Lt)); match self.token { - token::Lt => { try!(self.bump()); Ok(true)} + token::Lt => { + self.bump(); + true + } token::BinOp(token::Shl) => { let span = self.span; let lo = span.lo + BytePos(1); self.replace_token(token::Lt, lo, span.hi); - Ok(true) + true } - _ => Ok(false), + _ => false, } } - fn expect_lt(&mut self) -> PResult<()> { - if !try!(self.eat_lt()) { - self.expect_one_of(&[], &[]) + fn expect_lt(&mut self) -> PResult<'a, ()> { + if !self.eat_lt() { + self.unexpected() } else { Ok(()) } @@ -718,10 +721,13 @@ impl<'a> Parser<'a> { /// Expect and consume a GT. if a >> is seen, replace it /// with a single > and continue. If a GT is not seen, /// signal an error. - pub fn expect_gt(&mut self) -> PResult<()> { + pub fn expect_gt(&mut self) -> PResult<'a, ()> { self.expected_tokens.push(TokenType::Token(token::Gt)); match self.token { - token::Gt => self.bump(), + token::Gt => { + self.bump(); + Ok(()) + } token::BinOp(token::Shr) => { let span = self.span; let lo = span.lo + BytePos(1); @@ -750,8 +756,8 @@ impl<'a> Parser<'a> { pub fn parse_seq_to_before_gt_or_return(&mut self, sep: Option, mut f: F) - -> PResult<(P<[T]>, bool)> where - F: FnMut(&mut Parser) -> PResult>, + -> PResult<'a, (P<[T]>, bool)> + where F: FnMut(&mut Parser<'a>) -> PResult<'a, Option>, { let mut v = Vec::new(); // This loop works by alternating back and forth between parsing types @@ -788,8 +794,8 @@ impl<'a> Parser<'a> { pub fn parse_seq_to_before_gt(&mut self, sep: Option, mut f: F) - -> PResult> where - F: FnMut(&mut Parser) -> PResult, + -> PResult<'a, P<[T]>> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { let (result, returned) = try!(self.parse_seq_to_before_gt_or_return(sep, |p| Ok(Some(try!(f(p)))))); @@ -800,8 +806,8 @@ impl<'a> Parser<'a> { pub fn parse_seq_to_gt(&mut self, sep: Option, f: F) - -> PResult> where - F: FnMut(&mut Parser) -> PResult, + -> PResult<'a, P<[T]>> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { let v = try!(self.parse_seq_to_before_gt(sep, f)); try!(self.expect_gt()); @@ -811,8 +817,8 @@ impl<'a> Parser<'a> { pub fn parse_seq_to_gt_or_return(&mut self, sep: Option, f: F) - -> PResult<(P<[T]>, bool)> where - F: FnMut(&mut Parser) -> PResult>, + -> PResult<'a, (P<[T]>, bool)> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, Option>, { let (v, returned) = try!(self.parse_seq_to_before_gt_or_return(sep, f)); if !returned { @@ -828,11 +834,11 @@ impl<'a> Parser<'a> { ket: &token::Token, sep: SeqSep, f: F) - -> PResult> where - F: FnMut(&mut Parser) -> PResult, + -> PResult<'a, Vec> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { let val = try!(self.parse_seq_to_before_end(ket, sep, f)); - try!(self.bump()); + self.bump(); Ok(val) } @@ -843,8 +849,8 @@ impl<'a> Parser<'a> { ket: &token::Token, sep: SeqSep, mut f: F) - -> PResult> where - F: FnMut(&mut Parser) -> PResult, + -> PResult<'a, Vec> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { let mut first: bool = true; let mut v = vec!(); @@ -870,12 +876,12 @@ impl<'a> Parser<'a> { ket: &token::Token, sep: SeqSep, f: F) - -> PResult> where - F: FnMut(&mut Parser) -> PResult, + -> PResult<'a, Vec> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { try!(self.expect(bra)); let result = try!(self.parse_seq_to_before_end(ket, sep, f)); - try!(self.bump()); + self.bump(); Ok(result) } @@ -886,8 +892,8 @@ impl<'a> Parser<'a> { ket: &token::Token, sep: SeqSep, f: F) - -> PResult> where - F: FnMut(&mut Parser) -> PResult, + -> PResult<'a, Vec> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { let result = try!(self.parse_unspanned_seq(bra, ket, sep, f)); if result.is_empty() { @@ -905,19 +911,19 @@ impl<'a> Parser<'a> { ket: &token::Token, sep: SeqSep, f: F) - -> PResult>> where - F: FnMut(&mut Parser) -> PResult, + -> PResult<'a, Spanned>> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { let lo = self.span.lo; try!(self.expect(bra)); let result = try!(self.parse_seq_to_before_end(ket, sep, f)); let hi = self.span.hi; - try!(self.bump()); + self.bump(); Ok(spanned(lo, hi, result)) } /// Advance the parser by one token - pub fn bump(&mut self) -> PResult<()> { + pub fn bump(&mut self) { self.last_span = self.span; // Stash token for error recovery (sometimes; clone is not necessarily cheap). self.last_token = if self.token.is_ident() || @@ -946,14 +952,14 @@ impl<'a> Parser<'a> { self.tokens_consumed += 1; self.expected_tokens.clear(); // check after each token - self.check_unknown_macro_variable() + self.check_unknown_macro_variable(); } /// Advance the parser by one token and return the bumped token. - pub fn bump_and_get(&mut self) -> PResult { + pub fn bump_and_get(&mut self) -> token::Token { let old_token = mem::replace(&mut self.token, token::Underscore); - try!(self.bump()); - Ok(old_token) + self.bump(); + old_token } /// EFFECT: replace the current token and span with the given one @@ -981,28 +987,16 @@ impl<'a> Parser<'a> { } f(&self.buffer[((self.buffer_start + dist - 1) & 3) as usize].tok) } - pub fn fatal(&self, m: &str) -> errors::FatalError { - self.sess.span_diagnostic.span_fatal(self.span, m) - } - pub fn span_fatal(&self, sp: Span, m: &str) -> errors::FatalError { - self.sess.span_diagnostic.span_fatal(sp, m) - } - pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> errors::FatalError { - self.span_err(sp, m); - self.fileline_help(sp, help); - errors::FatalError - } - pub fn span_note(&self, sp: Span, m: &str) { - self.sess.span_diagnostic.span_note(sp, m) + pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> { + self.sess.span_diagnostic.struct_span_fatal(self.span, m) } - pub fn span_help(&self, sp: Span, m: &str) { - self.sess.span_diagnostic.span_help(sp, m) + pub fn span_fatal(&self, sp: Span, m: &str) -> DiagnosticBuilder<'a> { + self.sess.span_diagnostic.struct_span_fatal(sp, m) } - pub fn span_suggestion(&self, sp: Span, m: &str, n: String) { - self.sess.span_diagnostic.span_suggestion(sp, m, n) - } - pub fn fileline_help(&self, sp: Span, m: &str) { - self.sess.span_diagnostic.fileline_help(sp, m) + pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> DiagnosticBuilder<'a> { + let mut err = self.sess.span_diagnostic.struct_span_fatal(sp, m); + err.fileline_help(sp, help); + err } pub fn bug(&self, m: &str) -> ! { self.sess.span_diagnostic.span_bug(self.span, m) @@ -1023,6 +1017,10 @@ impl<'a> Parser<'a> { self.sess.span_diagnostic.abort_if_errors(); } + pub fn diagnostic(&self) -> &'a errors::Handler { + &self.sess.span_diagnostic + } + pub fn id_to_interned_str(&mut self, id: Ident) -> InternedString { id.name.as_str() } @@ -1042,7 +1040,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_for_in_type(&mut self) -> PResult { + pub fn parse_for_in_type(&mut self) -> PResult<'a, Ty_> { /* Parses whatever can come after a `for` keyword in a type. The `for` has already been consumed. @@ -1072,7 +1070,7 @@ impl<'a> Parser<'a> { let poly_trait_ref = ast::PolyTraitRef { bound_lifetimes: lifetime_defs, trait_ref: trait_ref, span: mk_sp(lo, hi)}; - let other_bounds = if try!(self.eat(&token::BinOp(token::Plus)) ){ + let other_bounds = if self.eat(&token::BinOp(token::Plus)) { try!(self.parse_ty_param_bounds(BoundParsingMode::Bare)) } else { P::empty() @@ -1085,12 +1083,12 @@ impl<'a> Parser<'a> { } } - pub fn parse_ty_path(&mut self) -> PResult { + pub fn parse_ty_path(&mut self) -> PResult<'a, Ty_> { Ok(TyPath(None, try!(self.parse_path(LifetimeAndTypesWithoutColons)))) } /// parse a TyBareFn type: - pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec) -> PResult { + pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec) -> PResult<'a, Ty_> { /* [unsafe] [extern "ABI"] fn <'lt> (S) -> T @@ -1104,7 +1102,7 @@ impl<'a> Parser<'a> { */ let unsafety = try!(self.parse_unsafety()); - let abi = if try!(self.eat_keyword(keywords::Extern) ){ + let abi = if self.eat_keyword(keywords::Extern) { try!(self.parse_opt_abi()).unwrap_or(abi::C) } else { abi::Rust @@ -1127,24 +1125,24 @@ impl<'a> Parser<'a> { } /// Parses an obsolete closure kind (`&:`, `&mut:`, or `:`). - pub fn parse_obsolete_closure_kind(&mut self) -> PResult<()> { + pub fn parse_obsolete_closure_kind(&mut self) -> PResult<'a, ()> { let lo = self.span.lo; if self.check(&token::BinOp(token::And)) && self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) && self.look_ahead(2, |t| *t == token::Colon) { - try!(self.bump()); - try!(self.bump()); - try!(self.bump()); + self.bump(); + self.bump(); + self.bump(); } else if self.token == token::BinOp(token::And) && self.look_ahead(1, |t| *t == token::Colon) { - try!(self.bump()); - try!(self.bump()); + self.bump(); + self.bump(); } else if - try!(self.eat(&token::Colon)) + self.eat(&token::Colon) { /* nothing */ } else { @@ -1156,8 +1154,8 @@ impl<'a> Parser<'a> { Ok(()) } - pub fn parse_unsafety(&mut self) -> PResult { - if try!(self.eat_keyword(keywords::Unsafe)) { + pub fn parse_unsafety(&mut self) -> PResult<'a, Unsafety> { + if self.eat_keyword(keywords::Unsafe) { return Ok(Unsafety::Unsafe); } else { return Ok(Unsafety::Normal); @@ -1165,17 +1163,17 @@ impl<'a> Parser<'a> { } /// Parse the items in a trait declaration - pub fn parse_trait_items(&mut self) -> PResult>> { + pub fn parse_trait_items(&mut self) -> PResult<'a, Vec>> { self.parse_unspanned_seq( &token::OpenDelim(token::Brace), &token::CloseDelim(token::Brace), seq_sep_none(), - |p| -> PResult> { + |p| -> PResult<'a, P> { maybe_whole!(no_clone p, NtTraitItem); let mut attrs = try!(p.parse_outer_attributes()); let lo = p.span.lo; - let (name, node) = if try!(p.eat_keyword(keywords::Type)) { + let (name, node) = if p.eat_keyword(keywords::Type) { let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param()); try!(p.expect(&token::Semi)); (ident, TypeTraitItem(bounds, default)) @@ -1185,7 +1183,7 @@ impl<'a> Parser<'a> { try!(p.expect(&token::Colon)); let ty = try!(p.parse_ty_sum()); let default = if p.check(&token::Eq) { - try!(p.bump()); + p.bump(); let expr = try!(p.parse_expr()); try!(p.commit_expr_expecting(&expr, token::Semi)); Some(expr) @@ -1200,7 +1198,7 @@ impl<'a> Parser<'a> { let ident = try!(p.parse_ident()); let mut generics = try!(p.parse_generics()); - let (explicit_self, d) = try!(p.parse_fn_decl_with_self(|p|{ + let (explicit_self, d) = try!(p.parse_fn_decl_with_self(|p: &mut Parser<'a>|{ // This is somewhat dubious; We don't want to allow // argument names to be left off if there is a // definition... @@ -1219,7 +1217,7 @@ impl<'a> Parser<'a> { let body = match p.token { token::Semi => { - try!(p.bump()); + p.bump(); debug!("parse_trait_methods(): parsing required method"); None } @@ -1251,16 +1249,16 @@ impl<'a> Parser<'a> { } /// Parse a possibly mutable type - pub fn parse_mt(&mut self) -> PResult { + pub fn parse_mt(&mut self) -> PResult<'a, MutTy> { let mutbl = try!(self.parse_mutability()); let t = try!(self.parse_ty()); Ok(MutTy { ty: t, mutbl: mutbl }) } /// Parse optional return type [ -> TY ] in function decl - pub fn parse_ret_ty(&mut self) -> PResult { - if try!(self.eat(&token::RArrow) ){ - if try!(self.eat(&token::Not) ){ + pub fn parse_ret_ty(&mut self) -> PResult<'a, FunctionRetTy> { + if self.eat(&token::RArrow) { + if self.eat(&token::Not) { Ok(NoReturn(self.last_span)) } else { Ok(Return(try!(self.parse_ty()))) @@ -1272,11 +1270,11 @@ impl<'a> Parser<'a> { } /// Parse a type in a context where `T1+T2` is allowed. - pub fn parse_ty_sum(&mut self) -> PResult> { + pub fn parse_ty_sum(&mut self) -> PResult<'a, P> { let lo = self.span.lo; let lhs = try!(self.parse_ty()); - if !try!(self.eat(&token::BinOp(token::Plus)) ){ + if !self.eat(&token::BinOp(token::Plus)) { return Ok(lhs); } @@ -1297,13 +1295,13 @@ impl<'a> Parser<'a> { } /// Parse a type. - pub fn parse_ty(&mut self) -> PResult> { + pub fn parse_ty(&mut self) -> PResult<'a, P> { maybe_whole!(no_clone self, NtTy); let lo = self.span.lo; let t = if self.check(&token::OpenDelim(token::Paren)) { - try!(self.bump()); + self.bump(); // (t) is a parenthesized ty // (t,) is the type of a tuple with only one field, @@ -1314,7 +1312,7 @@ impl<'a> Parser<'a> { ts.push(try!(self.parse_ty_sum())); if self.check(&token::Comma) { last_comma = true; - try!(self.bump()); + self.bump(); } else { last_comma = false; break; @@ -1329,7 +1327,7 @@ impl<'a> Parser<'a> { } } else if self.check(&token::BinOp(token::Star)) { // STAR POINTER (bare pointer?) - try!(self.bump()); + self.bump(); TyPtr(try!(self.parse_ptr())) } else if self.check(&token::OpenDelim(token::Bracket)) { // VECTOR @@ -1354,14 +1352,14 @@ impl<'a> Parser<'a> { } else if self.token_is_bare_fn_keyword() { // BARE FUNCTION try!(self.parse_ty_bare_fn(Vec::new())) - } else if try!(self.eat_keyword_noexpect(keywords::Typeof)) { + } else if self.eat_keyword_noexpect(keywords::Typeof) { // TYPEOF // In order to not be ambiguous, the type must be surrounded by parens. try!(self.expect(&token::OpenDelim(token::Paren))); let e = try!(self.parse_expr()); try!(self.expect(&token::CloseDelim(token::Paren))); TyTypeof(e) - } else if try!(self.eat_lt()) { + } else if self.eat_lt() { let (qself, path) = try!(self.parse_qualified_path(NoTypesAllowed)); @@ -1373,7 +1371,7 @@ impl<'a> Parser<'a> { let path = try!(self.parse_path(LifetimeAndTypesWithoutColons)); if self.check(&token::Not) { // MACRO INVOCATION - try!(self.bump()); + self.bump(); let delim = try!(self.expect_open_delim()); let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim), seq_sep_none(), @@ -1384,7 +1382,7 @@ impl<'a> Parser<'a> { // NAMED TYPE TyPath(None, path) } - } else if try!(self.eat(&token::Underscore) ){ + } else if self.eat(&token::Underscore) { // TYPE TO BE INFERRED TyInfer } else { @@ -1397,7 +1395,7 @@ impl<'a> Parser<'a> { Ok(P(Ty {id: ast::DUMMY_NODE_ID, node: t, span: sp})) } - pub fn parse_borrowed_pointee(&mut self) -> PResult { + pub fn parse_borrowed_pointee(&mut self) -> PResult<'a, Ty_> { // look for `&'lt` or `&'foo ` and interpret `foo` as the region name: let opt_lifetime = try!(self.parse_opt_lifetime()); @@ -1405,10 +1403,10 @@ impl<'a> Parser<'a> { return Ok(TyRptr(opt_lifetime, mt)); } - pub fn parse_ptr(&mut self) -> PResult { - let mutbl = if try!(self.eat_keyword(keywords::Mut) ){ + pub fn parse_ptr(&mut self) -> PResult<'a, MutTy> { + let mutbl = if self.eat_keyword(keywords::Mut) { MutMutable - } else if try!(self.eat_keyword(keywords::Const) ){ + } else if self.eat_keyword(keywords::Const) { MutImmutable } else { let span = self.last_span; @@ -1443,7 +1441,7 @@ impl<'a> Parser<'a> { /// This version of parse arg doesn't necessarily require /// identifier names. - pub fn parse_arg_general(&mut self, require_name: bool) -> PResult { + pub fn parse_arg_general(&mut self, require_name: bool) -> PResult<'a, Arg> { maybe_whole!(no_clone self, NtArg); let pat = if require_name || self.is_named_argument() { @@ -1470,14 +1468,14 @@ impl<'a> Parser<'a> { } /// Parse a single function argument - pub fn parse_arg(&mut self) -> PResult { + pub fn parse_arg(&mut self) -> PResult<'a, Arg> { self.parse_arg_general(true) } /// Parse an argument in a lambda header e.g. |arg, arg| - pub fn parse_fn_block_arg(&mut self) -> PResult { + pub fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> { let pat = try!(self.parse_pat()); - let t = if try!(self.eat(&token::Colon) ){ + let t = if self.eat(&token::Colon) { try!(self.parse_ty_sum()) } else { P(Ty { @@ -1493,9 +1491,9 @@ impl<'a> Parser<'a> { }) } - pub fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult>> { + pub fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<'a, Option>> { if self.check(&token::Semi) { - try!(self.bump()); + self.bump(); Ok(Some(try!(self.parse_expr()))) } else { Ok(None) @@ -1503,12 +1501,12 @@ impl<'a> Parser<'a> { } /// Matches token_lit = LIT_INTEGER | ... - pub fn lit_from_token(&self, tok: &token::Token) -> PResult { + pub fn lit_from_token(&self, tok: &token::Token) -> PResult<'a, Lit_> { match *tok { token::Interpolated(token::NtExpr(ref v)) => { match v.node { ExprLit(ref lit) => { Ok(lit.node.clone()) } - _ => { return Err(self.unexpected_last(tok)); } + _ => { return self.unexpected_last(tok); } } } token::Literal(lit, suf) => { @@ -1557,19 +1555,19 @@ impl<'a> Parser<'a> { Ok(out) } - _ => { return Err(self.unexpected_last(tok)); } + _ => { return self.unexpected_last(tok); } } } /// Matches lit = true | false | token_lit - pub fn parse_lit(&mut self) -> PResult { + pub fn parse_lit(&mut self) -> PResult<'a, Lit> { let lo = self.span.lo; - let lit = if try!(self.eat_keyword(keywords::True) ){ + let lit = if self.eat_keyword(keywords::True) { LitBool(true) - } else if try!(self.eat_keyword(keywords::False) ){ + } else if self.eat_keyword(keywords::False) { LitBool(false) } else { - let token = try!(self.bump_and_get()); + let token = self.bump_and_get(); let lit = try!(self.lit_from_token(&token)); lit }; @@ -1577,9 +1575,9 @@ impl<'a> Parser<'a> { } /// matches '-' lit | lit - pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult> { + pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<'a, P> { let minus_lo = self.span.lo; - let minus_present = try!(self.eat(&token::BinOp(token::Minus))); + let minus_present = self.eat(&token::BinOp(token::Minus)); let lo = self.span.lo; let literal = P(try!(self.parse_lit())); let hi = self.last_span.hi; @@ -1610,10 +1608,10 @@ impl<'a> Parser<'a> { /// `::a` /// `::F::a::` pub fn parse_qualified_path(&mut self, mode: PathParsingMode) - -> PResult<(QSelf, ast::Path)> { + -> PResult<'a, (QSelf, ast::Path)> { let span = self.last_span; let self_type = try!(self.parse_ty_sum()); - let mut path = if try!(self.eat_keyword(keywords::As)) { + let mut path = if self.eat_keyword(keywords::As) { try!(self.parse_path(LifetimeAndTypesWithoutColons)) } else { ast::Path { @@ -1653,10 +1651,10 @@ impl<'a> Parser<'a> { /// mode. The `mode` parameter determines whether lifetimes, types, and/or /// bounds are permitted and whether `::` must precede type parameter /// groups. - pub fn parse_path(&mut self, mode: PathParsingMode) -> PResult { + pub fn parse_path(&mut self, mode: PathParsingMode) -> PResult<'a, ast::Path> { // Check for a whole path... let found = match self.token { - token::Interpolated(token::NtPath(_)) => Some(try!(self.bump_and_get())), + token::Interpolated(token::NtPath(_)) => Some(self.bump_and_get()), _ => None, }; if let Some(token::Interpolated(token::NtPath(path))) = found { @@ -1664,7 +1662,7 @@ impl<'a> Parser<'a> { } let lo = self.span.lo; - let is_global = try!(self.eat(&token::ModSep)); + let is_global = self.eat(&token::ModSep); // Parse any number of segments and bound sets. A segment is an // identifier followed by an optional lifetime and a set of types. @@ -1696,22 +1694,22 @@ impl<'a> Parser<'a> { /// - `a::b::c` /// - `a::b::c(V) -> W` /// - `a::b::c(V)` - pub fn parse_path_segments_without_colons(&mut self) -> PResult> { + pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec> { let mut segments = Vec::new(); loop { // First, parse an identifier. let identifier = try!(self.parse_ident_or_self_type()); // Parse types, optionally. - let parameters = if try!(self.eat_lt() ){ + let parameters = if self.eat_lt() { let (lifetimes, types, bindings) = try!(self.parse_generic_values_after_lt()); - ast::AngleBracketedParameters(ast::AngleBracketedParameterData { + ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { lifetimes: lifetimes, types: P::from_vec(types), bindings: P::from_vec(bindings), }) - } else if try!(self.eat(&token::OpenDelim(token::Paren)) ){ + } else if self.eat(&token::OpenDelim(token::Paren)) { let lo = self.last_span.lo; let inputs = try!(self.parse_seq_to_end( @@ -1719,7 +1717,7 @@ impl<'a> Parser<'a> { seq_sep_trailing_allowed(token::Comma), |p| p.parse_ty_sum())); - let output_ty = if try!(self.eat(&token::RArrow) ){ + let output_ty = if self.eat(&token::RArrow) { Some(try!(self.parse_ty())) } else { None @@ -1727,7 +1725,7 @@ impl<'a> Parser<'a> { let hi = self.last_span.hi; - ast::ParenthesizedParameters(ast::ParenthesizedParameterData { + ast::PathParameters::Parenthesized(ast::ParenthesizedParameterData { span: mk_sp(lo, hi), inputs: inputs, output: output_ty, @@ -1741,7 +1739,7 @@ impl<'a> Parser<'a> { parameters: parameters }); // Continue only if we see a `::` - if !try!(self.eat(&token::ModSep) ){ + if !self.eat(&token::ModSep) { return Ok(segments); } } @@ -1749,14 +1747,14 @@ impl<'a> Parser<'a> { /// Examples: /// - `a::b::::c` - pub fn parse_path_segments_with_colons(&mut self) -> PResult> { + pub fn parse_path_segments_with_colons(&mut self) -> PResult<'a, Vec> { let mut segments = Vec::new(); loop { // First, parse an identifier. let identifier = try!(self.parse_ident_or_self_type()); // If we do not see a `::`, stop. - if !try!(self.eat(&token::ModSep) ){ + if !self.eat(&token::ModSep) { segments.push(ast::PathSegment { identifier: identifier, parameters: ast::PathParameters::none() @@ -1765,20 +1763,21 @@ impl<'a> Parser<'a> { } // Check for a type segment. - if try!(self.eat_lt() ){ + if self.eat_lt() { // Consumed `a::b::<`, go look for types let (lifetimes, types, bindings) = try!(self.parse_generic_values_after_lt()); + let parameters = ast::AngleBracketedParameterData { + lifetimes: lifetimes, + types: P::from_vec(types), + bindings: P::from_vec(bindings), + }; segments.push(ast::PathSegment { identifier: identifier, - parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData { - lifetimes: lifetimes, - types: P::from_vec(types), - bindings: P::from_vec(bindings), - }), + parameters: ast::PathParameters::AngleBracketed(parameters), }); // Consumed `a::b::`, check for `::` before proceeding - if !try!(self.eat(&token::ModSep) ){ + if !self.eat(&token::ModSep) { return Ok(segments); } } else { @@ -1794,7 +1793,7 @@ impl<'a> Parser<'a> { /// Examples: /// - `a::b::c` - pub fn parse_path_segments_without_types(&mut self) -> PResult> { + pub fn parse_path_segments_without_types(&mut self) -> PResult<'a, Vec> { let mut segments = Vec::new(); loop { // First, parse an identifier. @@ -1807,14 +1806,14 @@ impl<'a> Parser<'a> { }); // If we do not see a `::`, stop. - if !try!(self.eat(&token::ModSep) ){ + if !self.eat(&token::ModSep) { return Ok(segments); } } } /// parses 0 or 1 lifetime - pub fn parse_opt_lifetime(&mut self) -> PResult> { + pub fn parse_opt_lifetime(&mut self) -> PResult<'a, Option> { match self.token { token::Lifetime(..) => { Ok(Some(try!(self.parse_lifetime()))) @@ -1827,11 +1826,11 @@ impl<'a> Parser<'a> { /// Parses a single lifetime /// Matches lifetime = LIFETIME - pub fn parse_lifetime(&mut self) -> PResult { + pub fn parse_lifetime(&mut self) -> PResult<'a, ast::Lifetime> { match self.token { token::Lifetime(i) => { let span = self.span; - try!(self.bump()); + self.bump(); return Ok(ast::Lifetime { id: ast::DUMMY_NODE_ID, span: span, @@ -1846,7 +1845,7 @@ impl<'a> Parser<'a> { /// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def = /// lifetime [':' lifetimes]` - pub fn parse_lifetime_defs(&mut self) -> PResult> { + pub fn parse_lifetime_defs(&mut self) -> PResult<'a, Vec> { let mut res = Vec::new(); loop { @@ -1854,7 +1853,7 @@ impl<'a> Parser<'a> { token::Lifetime(_) => { let lifetime = try!(self.parse_lifetime()); let bounds = - if try!(self.eat(&token::Colon) ){ + if self.eat(&token::Colon) { try!(self.parse_lifetimes(token::BinOp(token::Plus))) } else { Vec::new() @@ -1869,7 +1868,7 @@ impl<'a> Parser<'a> { } match self.token { - token::Comma => { try!(self.bump());} + token::Comma => { self.bump();} token::Gt => { return Ok(res); } token::BinOp(token::Shr) => { return Ok(res); } _ => { @@ -1889,7 +1888,7 @@ impl<'a> Parser<'a> { /// Parses zero or more comma separated lifetimes. Expects each lifetime to be followed by /// either a comma or `>`. Used when parsing type parameter lists, where we expect something /// like `<'a, 'b, T>`. - pub fn parse_lifetimes(&mut self, sep: token::Token) -> PResult> { + pub fn parse_lifetimes(&mut self, sep: token::Token) -> PResult<'a, Vec> { let mut res = Vec::new(); loop { @@ -1906,13 +1905,13 @@ impl<'a> Parser<'a> { return Ok(res); } - try!(self.bump()); + self.bump(); } } /// Parse mutability declaration (mut/const/imm) - pub fn parse_mutability(&mut self) -> PResult { - if try!(self.eat_keyword(keywords::Mut) ){ + pub fn parse_mutability(&mut self) -> PResult<'a, Mutability> { + if self.eat_keyword(keywords::Mut) { Ok(MutMutable) } else { Ok(MutImmutable) @@ -1920,7 +1919,7 @@ impl<'a> Parser<'a> { } /// Parse ident COLON expr - pub fn parse_field(&mut self) -> PResult { + pub fn parse_field(&mut self) -> PResult<'a, Field> { let lo = self.span.lo; let i = try!(self.parse_ident()); let hi = self.last_span.hi; @@ -2012,11 +2011,11 @@ impl<'a> Parser<'a> { }) } - fn expect_open_delim(&mut self) -> PResult { + fn expect_open_delim(&mut self) -> PResult<'a, token::DelimToken> { self.expected_tokens.push(TokenType::Token(token::Gt)); match self.token { token::OpenDelim(delim) => { - try!(self.bump()); + self.bump(); Ok(delim) }, _ => Err(self.fatal("expected open delimiter")), @@ -2030,7 +2029,7 @@ impl<'a> Parser<'a> { /// NB: This does not parse outer attributes, /// and is private because it only works /// correctly if called from parse_dot_or_call_expr(). - fn parse_bottom_expr(&mut self) -> PResult> { + fn parse_bottom_expr(&mut self) -> PResult<'a, P> { maybe_whole_expr!(self); // Outer attributes are already parsed and will be @@ -2048,7 +2047,7 @@ impl<'a> Parser<'a> { // Note: when adding new syntax here, don't forget to adjust Token::can_begin_expr(). match self.token { token::OpenDelim(token::Paren) => { - try!(self.bump()); + self.bump(); let attrs = try!(self.parse_inner_attributes()) .into_thin_attrs() @@ -2065,13 +2064,13 @@ impl<'a> Parser<'a> { if self.check(&token::Comma) { trailing_comma = true; - try!(self.bump()); + self.bump(); } else { trailing_comma = false; break; } } - try!(self.bump()); + self.bump(); hi = self.last_span.hi; return if es.len() == 1 && !trailing_comma { @@ -2091,13 +2090,13 @@ impl<'a> Parser<'a> { name: token::SELF_KEYWORD_NAME, ctxt: _ }, token::Plain) => { - try!(self.bump()); + self.bump(); let path = ast_util::ident_to_path(mk_sp(lo, hi), id); ex = ExprPath(None, path); hi = self.last_span.hi; } token::OpenDelim(token::Bracket) => { - try!(self.bump()); + self.bump(); let inner_attrs = try!(self.parse_inner_attributes()) .into_thin_attrs(); @@ -2105,20 +2104,20 @@ impl<'a> Parser<'a> { if self.check(&token::CloseDelim(token::Bracket)) { // Empty vector. - try!(self.bump()); + self.bump(); ex = ExprVec(Vec::new()); } else { // Nonempty vector. let first_expr = try!(self.parse_expr()); if self.check(&token::Semi) { // Repeating array syntax: [ 0; 512 ] - try!(self.bump()); + self.bump(); let count = try!(self.parse_expr()); try!(self.expect(&token::CloseDelim(token::Bracket))); ex = ExprRepeat(first_expr, count); } else if self.check(&token::Comma) { // Vector with two or more elements. - try!(self.bump()); + self.bump(); let remaining_exprs = try!(self.parse_seq_to_end( &token::CloseDelim(token::Bracket), seq_sep_trailing_allowed(token::Comma), @@ -2136,54 +2135,54 @@ impl<'a> Parser<'a> { hi = self.last_span.hi; } _ => { - if try!(self.eat_lt()){ + if self.eat_lt() { let (qself, path) = try!(self.parse_qualified_path(LifetimeAndTypesWithColons)); hi = path.span.hi; return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path), attrs)); } - if try!(self.eat_keyword(keywords::Move) ){ + if self.eat_keyword(keywords::Move) { let lo = self.last_span.lo; return self.parse_lambda_expr(lo, CaptureByValue, attrs); } - if try!(self.eat_keyword(keywords::If)) { + if self.eat_keyword(keywords::If) { return self.parse_if_expr(attrs); } - if try!(self.eat_keyword(keywords::For) ){ + if self.eat_keyword(keywords::For) { let lo = self.last_span.lo; return self.parse_for_expr(None, lo, attrs); } - if try!(self.eat_keyword(keywords::While) ){ + if self.eat_keyword(keywords::While) { let lo = self.last_span.lo; return self.parse_while_expr(None, lo, attrs); } if self.token.is_lifetime() { let lifetime = self.get_lifetime(); let lo = self.span.lo; - try!(self.bump()); + self.bump(); try!(self.expect(&token::Colon)); - if try!(self.eat_keyword(keywords::While) ){ + if self.eat_keyword(keywords::While) { return self.parse_while_expr(Some(lifetime), lo, attrs) } - if try!(self.eat_keyword(keywords::For) ){ + if self.eat_keyword(keywords::For) { return self.parse_for_expr(Some(lifetime), lo, attrs) } - if try!(self.eat_keyword(keywords::Loop) ){ + if self.eat_keyword(keywords::Loop) { return self.parse_loop_expr(Some(lifetime), lo, attrs) } return Err(self.fatal("expected `while`, `for`, or `loop` after a label")) } - if try!(self.eat_keyword(keywords::Loop) ){ + if self.eat_keyword(keywords::Loop) { let lo = self.last_span.lo; return self.parse_loop_expr(None, lo, attrs); } - if try!(self.eat_keyword(keywords::Continue) ){ + if self.eat_keyword(keywords::Continue) { let ex = if self.token.is_lifetime() { let ex = ExprAgain(Some(Spanned{ node: self.get_lifetime(), span: self.span })); - try!(self.bump()); + self.bump(); ex } else { ExprAgain(None) @@ -2191,16 +2190,16 @@ impl<'a> Parser<'a> { let hi = self.last_span.hi; return Ok(self.mk_expr(lo, hi, ex, attrs)); } - if try!(self.eat_keyword(keywords::Match) ){ + if self.eat_keyword(keywords::Match) { return self.parse_match_expr(attrs); } - if try!(self.eat_keyword(keywords::Unsafe) ){ + if self.eat_keyword(keywords::Unsafe) { return self.parse_block_expr( lo, UnsafeBlock(ast::UserProvided), attrs); } - if try!(self.eat_keyword(keywords::Return) ){ + if self.eat_keyword(keywords::Return) { if self.token.can_begin_expr() { let e = try!(self.parse_expr()); hi = e.span.hi; @@ -2208,13 +2207,13 @@ impl<'a> Parser<'a> { } else { ex = ExprRet(None); } - } else if try!(self.eat_keyword(keywords::Break) ){ + } else if self.eat_keyword(keywords::Break) { if self.token.is_lifetime() { ex = ExprBreak(Some(Spanned { node: self.get_lifetime(), span: self.span })); - try!(self.bump()); + self.bump(); } else { ex = ExprBreak(None); } @@ -2229,7 +2228,7 @@ impl<'a> Parser<'a> { // `!`, as an operator, is prefix, so we know this isn't that if self.check(&token::Not) { // MACRO INVOCATION expression - try!(self.bump()); + self.bump(); let delim = try!(self.expect_open_delim()); let tts = try!(self.parse_seq_to_end( @@ -2251,7 +2250,7 @@ impl<'a> Parser<'a> { ); if !prohibited { // It's a struct literal. - try!(self.bump()); + self.bump(); let mut fields = Vec::new(); let mut base = None; @@ -2260,7 +2259,7 @@ impl<'a> Parser<'a> { .into_thin_attrs()); while self.token != token::CloseDelim(token::Brace) { - if try!(self.eat(&token::DotDot) ){ + if self.eat(&token::DotDot) { base = Some(try!(self.parse_expr())); break; } @@ -2294,7 +2293,7 @@ impl<'a> Parser<'a> { fn parse_or_use_outer_attributes(&mut self, already_parsed_attrs: Option) - -> PResult { + -> PResult<'a, ThinAttributes> { if let Some(attrs) = already_parsed_attrs { Ok(attrs) } else { @@ -2305,7 +2304,7 @@ impl<'a> Parser<'a> { /// Parse a block or unsafe block pub fn parse_block_expr(&mut self, lo: BytePos, blk_mode: BlockCheckMode, attrs: ThinAttributes) - -> PResult> { + -> PResult<'a, P> { let outer_attrs = attrs; try!(self.expect(&token::OpenDelim(token::Brace))); @@ -2320,7 +2319,7 @@ impl<'a> Parser<'a> { /// parse a.b or a(13) or a[4] or just a pub fn parse_dot_or_call_expr(&mut self, already_parsed_attrs: Option) - -> PResult> { + -> PResult<'a, P> { let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs)); let b = try!(self.parse_bottom_expr()); @@ -2330,7 +2329,7 @@ impl<'a> Parser<'a> { pub fn parse_dot_or_call_expr_with(&mut self, e0: P, attrs: ThinAttributes) - -> PResult> { + -> PResult<'a, P> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure @@ -2356,19 +2355,19 @@ impl<'a> Parser<'a> { ) } - fn parse_dot_or_call_expr_with_(&mut self, e0: P) -> PResult> { + fn parse_dot_or_call_expr_with_(&mut self, e0: P) -> PResult<'a, P> { let mut e = e0; let lo = e.span.lo; let mut hi; loop { // expr.f - if try!(self.eat(&token::Dot) ){ + if self.eat(&token::Dot) { match self.token { token::Ident(i, _) => { let dot = self.last_span.hi; hi = self.span.hi; - try!(self.bump()); - let (_, tys, bindings) = if try!(self.eat(&token::ModSep) ){ + self.bump(); + let (_, tys, bindings) = if self.eat(&token::ModSep) { try!(self.expect_lt()); try!(self.parse_generic_values_after_lt()) } else { @@ -2418,7 +2417,7 @@ impl<'a> Parser<'a> { let dot = self.last_span.hi; hi = self.span.hi; - try!(self.bump()); + self.bump(); let index = n.as_str().parse::().ok(); match index { @@ -2434,25 +2433,26 @@ impl<'a> Parser<'a> { } } token::Literal(token::Float(n), _suf) => { - try!(self.bump()); + self.bump(); let last_span = self.last_span; let fstr = n.as_str(); - self.span_err(last_span, - &format!("unexpected token: `{}`", n.as_str())); + let mut err = self.diagnostic().struct_span_err(last_span, + &format!("unexpected token: `{}`", n.as_str())); if fstr.chars().all(|x| "0123456789.".contains(x)) { let float = match fstr.parse::().ok() { Some(f) => f, None => continue, }; - self.fileline_help(last_span, + err.fileline_help(last_span, &format!("try parenthesizing the first index; e.g., `(foo.{}){}`", float.trunc() as usize, format!(".{}", fstr.splitn(2, ".").last().unwrap()))); } + err.emit(); self.abort_if_errors(); } - _ => return Err(self.unexpected()) + _ => return self.unexpected() } continue; } @@ -2475,7 +2475,7 @@ impl<'a> Parser<'a> { // expr[...] // Could be either an index expression or a slicing expression. token::OpenDelim(token::Bracket) => { - try!(self.bump()); + self.bump(); let ix = try!(self.parse_expr()); hi = self.span.hi; try!(self.commit_expr_expecting(&*ix, token::CloseDelim(token::Bracket))); @@ -2489,11 +2489,11 @@ impl<'a> Parser<'a> { } // Parse unquoted tokens after a `$` in a token tree - fn parse_unquoted(&mut self) -> PResult { + fn parse_unquoted(&mut self) -> PResult<'a, TokenTree> { let mut sp = self.span; let (name, namep) = match self.token { token::Dollar => { - try!(self.bump()); + self.bump(); if self.token == token::OpenDelim(token::Paren) { let Spanned { node: seq, span: seq_span } = try!(self.parse_seq( @@ -2512,7 +2512,7 @@ impl<'a> Parser<'a> { num_captures: name_num }))); } else if self.token.is_keyword_allow_following_colon(keywords::Crate) { - try!(self.bump()); + self.bump(); return Ok(TokenTree::Token(sp, SpecialVarNt(SpecialMacroVar::CrateMacroVar))); } else { sp = mk_sp(sp.lo, self.span.hi); @@ -2522,7 +2522,7 @@ impl<'a> Parser<'a> { } } token::SubstNt(name, namep) => { - try!(self.bump()); + self.bump(); (name, namep) } _ => unreachable!() @@ -2531,7 +2531,7 @@ impl<'a> Parser<'a> { if self.token == token::Colon && self.look_ahead(1, |t| t.is_ident() && !t.is_strict_keyword() && !t.is_reserved_keyword()) { - try!(self.bump()); + self.bump(); sp = mk_sp(sp.lo, self.span.hi); let kindp = match self.token { token::Ident(_, p) => p, _ => token::Plain }; let nt_kind = try!(self.parse_ident()); @@ -2541,29 +2541,28 @@ impl<'a> Parser<'a> { } } - pub fn check_unknown_macro_variable(&mut self) -> PResult<()> { + pub fn check_unknown_macro_variable(&mut self) { if self.quote_depth == 0 { match self.token { token::SubstNt(name, _) => - return Err(self.fatal(&format!("unknown macro variable `{}`", - name))), + self.fatal(&format!("unknown macro variable `{}`", name)).emit(), _ => {} } } - Ok(()) } /// Parse an optional separator followed by a Kleene-style /// repetition token (+ or *). - pub fn parse_sep_and_kleene_op(&mut self) -> PResult<(Option, ast::KleeneOp)> { - fn parse_kleene_op(parser: &mut Parser) -> PResult> { + pub fn parse_sep_and_kleene_op(&mut self) + -> PResult<'a, (Option, ast::KleeneOp)> { + fn parse_kleene_op<'a>(parser: &mut Parser<'a>) -> PResult<'a, Option> { match parser.token { token::BinOp(token::Star) => { - try!(parser.bump()); + parser.bump(); Ok(Some(ast::ZeroOrMore)) }, token::BinOp(token::Plus) => { - try!(parser.bump()); + parser.bump(); Ok(Some(ast::OneOrMore)) }, _ => Ok(None) @@ -2575,7 +2574,7 @@ impl<'a> Parser<'a> { None => {} } - let separator = try!(self.bump_and_get()); + let separator = self.bump_and_get(); match try!(parse_kleene_op(self)) { Some(zerok) => Ok((Some(separator), zerok)), None => return Err(self.fatal("expected `*` or `+`")) @@ -2583,7 +2582,7 @@ impl<'a> Parser<'a> { } /// parse a single token tree from the input. - pub fn parse_token_tree(&mut self) -> PResult { + pub fn parse_token_tree(&mut self) -> PResult<'a, TokenTree> { // FIXME #6994: currently, this is too eager. It // parses token trees but also identifies TokenType::Sequence's // and token::SubstNt's; it's too early to know yet @@ -2596,27 +2595,27 @@ impl<'a> Parser<'a> { // not an EOF, and not the desired right-delimiter (if // it were, parse_seq_to_before_end would have prevented // reaching this point. - fn parse_non_delim_tt_tok(p: &mut Parser) -> PResult { + fn parse_non_delim_tt_tok<'b>(p: &mut Parser<'b>) -> PResult<'b, TokenTree> { maybe_whole!(deref p, NtTT); match p.token { token::CloseDelim(_) => { + let token_str = p.this_token_to_string(); + let mut err = p.fatal( + &format!("incorrect close delimiter: `{}`", token_str)); // This is a conservative error: only report the last unclosed delimiter. The // previous unclosed delimiters could actually be closed! The parser just hasn't // gotten to them yet. - match p.open_braces.last() { - None => {} - Some(&sp) => p.span_note(sp, "unclosed delimiter"), + if let Some(&sp) = p.open_braces.last() { + err.span_note(sp, "unclosed delimiter"); }; - let token_str = p.this_token_to_string(); - Err(p.fatal(&format!("incorrect close delimiter: `{}`", - token_str))) + Err(err) }, /* we ought to allow different depths of unquotation */ token::Dollar | token::SubstNt(..) if p.quote_depth > 0 => { p.parse_unquoted() } _ => { - Ok(TokenTree::Token(p.span, try!(p.bump_and_get()))) + Ok(TokenTree::Token(p.span, p.bump_and_get())) } } } @@ -2624,12 +2623,12 @@ impl<'a> Parser<'a> { match self.token { token::Eof => { let open_braces = self.open_braces.clone(); + let mut err: DiagnosticBuilder<'a> = + self.fatal("this file contains an un-closed delimiter"); for sp in &open_braces { - self.span_help(*sp, "did you mean to close this delimiter?"); + err.span_help(*sp, "did you mean to close this delimiter?"); } - // There shouldn't really be a span, but it's easier for the test runner - // if we give it one - return Err(self.fatal("this file contains an un-closed delimiter ")); + return Err(err); }, token::OpenDelim(delim) => { // The span for beginning of the delimited section @@ -2638,7 +2637,7 @@ impl<'a> Parser<'a> { // Parse the open delimiter. self.open_braces.push(self.span); let open_span = self.span; - try!(self.bump()); + self.bump(); // Parse the token trees within the delimiters let tts = try!(self.parse_seq_to_before_end( @@ -2649,7 +2648,7 @@ impl<'a> Parser<'a> { // Parse the close delimiter. let close_span = self.span; - try!(self.bump()); + self.bump(); self.open_braces.pop().unwrap(); // Expand to cover the entire delimited token tree @@ -2668,7 +2667,7 @@ impl<'a> Parser<'a> { // parse a stream of tokens into a list of TokenTree's, // up to EOF. - pub fn parse_all_token_trees(&mut self) -> PResult> { + pub fn parse_all_token_trees(&mut self) -> PResult<'a, Vec> { let mut tts = Vec::new(); while self.token != token::Eof { tts.push(try!(self.parse_token_tree())); @@ -2679,26 +2678,26 @@ impl<'a> Parser<'a> { /// Parse a prefix-unary-operator expr pub fn parse_prefix_expr(&mut self, already_parsed_attrs: Option) - -> PResult> { + -> PResult<'a, P> { let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs)); let lo = self.span.lo; let hi; // Note: when adding new unary operators, don't forget to adjust Token::can_begin_expr() let ex = match self.token { token::Not => { - try!(self.bump()); + self.bump(); let e = try!(self.parse_prefix_expr(None)); hi = e.span.hi; self.mk_unary(UnNot, e) } token::BinOp(token::Minus) => { - try!(self.bump()); + self.bump(); let e = try!(self.parse_prefix_expr(None)); hi = e.span.hi; self.mk_unary(UnNeg, e) } token::BinOp(token::Star) => { - try!(self.bump()); + self.bump(); let e = try!(self.parse_prefix_expr(None)); hi = e.span.hi; self.mk_unary(UnDeref, e) @@ -2711,7 +2710,7 @@ impl<'a> Parser<'a> { ExprAddrOf(m, e) } token::Ident(..) if self.token.is_keyword(keywords::In) => { - try!(self.bump()); + self.bump(); let place = try!(self.parse_expr_res( Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None, @@ -2724,7 +2723,7 @@ impl<'a> Parser<'a> { ExprInPlace(place, blk_expr) } token::Ident(..) if self.token.is_keyword(keywords::Box) => { - try!(self.bump()); + self.bump(); let subexpression = try!(self.parse_prefix_expr(None)); hi = subexpression.span.hi; ExprBox(subexpression) @@ -2740,7 +2739,7 @@ impl<'a> Parser<'a> { /// the expression. pub fn parse_assoc_expr(&mut self, already_parsed_attrs: Option) - -> PResult> { + -> PResult<'a, P> { self.parse_assoc_expr_with(0, already_parsed_attrs.into()) } @@ -2748,7 +2747,7 @@ impl<'a> Parser<'a> { pub fn parse_assoc_expr_with(&mut self, min_prec: usize, lhs: LhsExpr) - -> PResult> { + -> PResult<'a, P> { let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs { expr } else { @@ -2777,7 +2776,7 @@ impl<'a> Parser<'a> { if op.precedence() < min_prec { break; } - try!(self.bump()); + self.bump(); if op.is_comparison() { self.check_no_chained_comparison(&*lhs, &op); } @@ -2799,8 +2798,15 @@ impl<'a> Parser<'a> { // We have 2 alternatives here: `x..y` and `x..` The other two variants are // handled with `parse_prefix_range_expr` call above. let rhs = if self.is_at_start_of_range_notation_rhs() { - self.parse_assoc_expr_with(op.precedence() + 1, - LhsExpr::NotYetParsed).ok() + let rhs = self.parse_assoc_expr_with(op.precedence() + 1, + LhsExpr::NotYetParsed); + match rhs { + Ok(e) => Some(e), + Err(mut e) => { + e.cancel(); + None + } + } } else { None }; @@ -2815,16 +2821,25 @@ impl<'a> Parser<'a> { } let rhs = try!(match op.fixity() { - Fixity::Right => self.with_res(restrictions, |this|{ - this.parse_assoc_expr_with(op.precedence(), LhsExpr::NotYetParsed) + Fixity::Right => self.with_res( + restrictions - Restrictions::RESTRICTION_STMT_EXPR, + |this| { + this.parse_assoc_expr_with(op.precedence(), + LhsExpr::NotYetParsed) }), - Fixity::Left => self.with_res(restrictions, |this|{ - this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed) + Fixity::Left => self.with_res( + restrictions - Restrictions::RESTRICTION_STMT_EXPR, + |this| { + this.parse_assoc_expr_with(op.precedence() + 1, + LhsExpr::NotYetParsed) }), // We currently have no non-associative operators that are not handled above by // the special cases. The code is here only for future convenience. - Fixity::None => self.with_res(restrictions, |this|{ - this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed) + Fixity::None => self.with_res( + restrictions - Restrictions::RESTRICTION_STMT_EXPR, + |this| { + this.parse_assoc_expr_with(op.precedence() + 1, + LhsExpr::NotYetParsed) }), }); @@ -2879,12 +2894,13 @@ impl<'a> Parser<'a> { ExprBinary(op, _, _) if op.node.is_comparison() => { // respan to include both operators let op_span = mk_sp(op.span.lo, self.span.hi); - self.span_err(op_span, + let mut err = self.diagnostic().struct_span_err(op_span, "chained comparison operators require parentheses"); if op.node == BiLt && *outer_op == AssocOp::Greater { - self.fileline_help(op_span, + err.fileline_help(op_span, "use `::<...>` instead of `<...>` if you meant to specify type arguments"); } + err.emit(); } _ => {} } @@ -2893,12 +2909,12 @@ impl<'a> Parser<'a> { /// Parse prefix-forms of range notation: `..expr` and `..` fn parse_prefix_range_expr(&mut self, already_parsed_attrs: Option) - -> PResult> { + -> PResult<'a, P> { debug_assert!(self.token == token::DotDot); let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs)); let lo = self.span.lo; let mut hi = self.span.hi; - try!(self.bump()); + self.bump(); let opt_end = if self.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than DotDot. let next_prec = AssocOp::from_token(&token::DotDot).unwrap().precedence() + 1; @@ -2928,7 +2944,7 @@ impl<'a> Parser<'a> { } /// Parse an 'if' or 'if let' expression ('if' token already eaten) - pub fn parse_if_expr(&mut self, attrs: ThinAttributes) -> PResult> { + pub fn parse_if_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P> { if self.check_keyword(keywords::Let) { return self.parse_if_let_expr(attrs); } @@ -2937,7 +2953,7 @@ impl<'a> Parser<'a> { let thn = try!(self.parse_block()); let mut els: Option> = None; let mut hi = thn.span.hi; - if try!(self.eat_keyword(keywords::Else) ){ + if self.eat_keyword(keywords::Else) { let elexpr = try!(self.parse_else_expr()); hi = elexpr.span.hi; els = Some(elexpr); @@ -2947,14 +2963,14 @@ impl<'a> Parser<'a> { /// Parse an 'if let' expression ('if' token already eaten) pub fn parse_if_let_expr(&mut self, attrs: ThinAttributes) - -> PResult> { + -> PResult<'a, P> { let lo = self.last_span.lo; try!(self.expect_keyword(keywords::Let)); let pat = try!(self.parse_pat()); try!(self.expect(&token::Eq)); let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None)); let thn = try!(self.parse_block()); - let (hi, els) = if try!(self.eat_keyword(keywords::Else) ){ + let (hi, els) = if self.eat_keyword(keywords::Else) { let expr = try!(self.parse_else_expr()); (expr.span.hi, Some(expr)) } else { @@ -2967,7 +2983,7 @@ impl<'a> Parser<'a> { pub fn parse_lambda_expr(&mut self, lo: BytePos, capture_clause: CaptureClause, attrs: ThinAttributes) - -> PResult> + -> PResult<'a, P> { let decl = try!(self.parse_fn_block_decl()); let body = match decl.output { @@ -2997,8 +3013,8 @@ impl<'a> Parser<'a> { } // `else` token already eaten - pub fn parse_else_expr(&mut self) -> PResult> { - if try!(self.eat_keyword(keywords::If) ){ + pub fn parse_else_expr(&mut self) -> PResult<'a, P> { + if self.eat_keyword(keywords::If) { return self.parse_if_expr(None); } else { let blk = try!(self.parse_block()); @@ -3009,7 +3025,7 @@ impl<'a> Parser<'a> { /// Parse a 'for' .. 'in' expression ('for' token already eaten) pub fn parse_for_expr(&mut self, opt_ident: Option, span_lo: BytePos, - attrs: ThinAttributes) -> PResult> { + attrs: ThinAttributes) -> PResult<'a, P> { // Parse: `for in ` let pat = try!(self.parse_pat()); @@ -3028,7 +3044,7 @@ impl<'a> Parser<'a> { /// Parse a 'while' or 'while let' expression ('while' token already eaten) pub fn parse_while_expr(&mut self, opt_ident: Option, span_lo: BytePos, - attrs: ThinAttributes) -> PResult> { + attrs: ThinAttributes) -> PResult<'a, P> { if self.token.is_keyword(keywords::Let) { return self.parse_while_let_expr(opt_ident, span_lo, attrs); } @@ -3043,7 +3059,7 @@ impl<'a> Parser<'a> { /// Parse a 'while let' expression ('while' token already eaten) pub fn parse_while_let_expr(&mut self, opt_ident: Option, span_lo: BytePos, - attrs: ThinAttributes) -> PResult> { + attrs: ThinAttributes) -> PResult<'a, P> { try!(self.expect_keyword(keywords::Let)); let pat = try!(self.parse_pat()); try!(self.expect(&token::Eq)); @@ -3057,7 +3073,7 @@ impl<'a> Parser<'a> { // parse `loop {...}`, `loop` token already eaten pub fn parse_loop_expr(&mut self, opt_ident: Option, span_lo: BytePos, - attrs: ThinAttributes) -> PResult> { + attrs: ThinAttributes) -> PResult<'a, P> { let (iattrs, body) = try!(self.parse_inner_attrs_and_block()); let attrs = attrs.append(iattrs.into_thin_attrs()); let hi = body.span.hi; @@ -3065,14 +3081,15 @@ impl<'a> Parser<'a> { } // `match` token already eaten - fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult> { + fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P> { let match_span = self.last_span; let lo = self.last_span.lo; let discriminant = try!(self.parse_expr_res( Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None)); - if let Err(e) = self.commit_expr_expecting(&*discriminant, token::OpenDelim(token::Brace)) { + if let Err(mut e) = self.commit_expr_expecting(&*discriminant, + token::OpenDelim(token::Brace)) { if self.token == token::Token::Semi { - self.span_note(match_span, "did you mean to remove this `match` keyword?"); + e.span_note(match_span, "did you mean to remove this `match` keyword?"); } return Err(e) } @@ -3083,17 +3100,17 @@ impl<'a> Parser<'a> { arms.push(try!(self.parse_arm())); } let hi = self.span.hi; - try!(self.bump()); + self.bump(); return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms), attrs)); } - pub fn parse_arm(&mut self) -> PResult { + pub fn parse_arm(&mut self) -> PResult<'a, Arm> { maybe_whole!(no_clone self, NtArm); let attrs = try!(self.parse_outer_attributes()); let pats = try!(self.parse_pats()); let mut guard = None; - if try!(self.eat_keyword(keywords::If) ){ + if self.eat_keyword(keywords::If) { guard = Some(try!(self.parse_expr())); } try!(self.expect(&token::FatArrow)); @@ -3106,7 +3123,7 @@ impl<'a> Parser<'a> { if require_comma { try!(self.commit_expr(&*expr, &[token::Comma], &[token::CloseDelim(token::Brace)])); } else { - try!(self.eat(&token::Comma)); + self.eat(&token::Comma); } Ok(ast::Arm { @@ -3118,15 +3135,16 @@ impl<'a> Parser<'a> { } /// Parse an expression - pub fn parse_expr(&mut self) -> PResult> { + pub fn parse_expr(&mut self) -> PResult<'a, P> { self.parse_expr_res(Restrictions::empty(), None) } /// Evaluate the closure with restrictions in place. /// /// After the closure is evaluated, restrictions are reset. - pub fn with_res(&mut self, r: Restrictions, f: F) -> PResult> - where F: FnOnce(&mut Self) -> PResult> { + pub fn with_res(&mut self, r: Restrictions, f: F) -> PResult<'a, P> + where F: FnOnce(&mut Self) -> PResult<'a, P> + { let old = self.restrictions; self.restrictions = r; let r = f(self); @@ -3138,14 +3156,14 @@ impl<'a> Parser<'a> { /// Parse an expression, subject to the given restrictions pub fn parse_expr_res(&mut self, r: Restrictions, already_parsed_attrs: Option) - -> PResult> { + -> PResult<'a, P> { self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs)) } /// Parse the RHS of a local variable declaration (e.g. '= 14;') - fn parse_initializer(&mut self) -> PResult>> { + fn parse_initializer(&mut self) -> PResult<'a, Option>> { if self.check(&token::Eq) { - try!(self.bump()); + self.bump(); Ok(Some(try!(self.parse_expr()))) } else { Ok(None) @@ -3153,21 +3171,21 @@ impl<'a> Parser<'a> { } /// Parse patterns, separated by '|' s - fn parse_pats(&mut self) -> PResult>> { + fn parse_pats(&mut self) -> PResult<'a, Vec>> { let mut pats = Vec::new(); loop { pats.push(try!(self.parse_pat())); - if self.check(&token::BinOp(token::Or)) { try!(self.bump());} + if self.check(&token::BinOp(token::Or)) { self.bump();} else { return Ok(pats); } }; } - fn parse_pat_tuple_elements(&mut self) -> PResult>> { + fn parse_pat_tuple_elements(&mut self) -> PResult<'a, Vec>> { let mut fields = vec![]; if !self.check(&token::CloseDelim(token::Paren)) { fields.push(try!(self.parse_pat())); if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) { - while try!(self.eat(&token::Comma)) && + while self.eat(&token::Comma) && !self.check(&token::CloseDelim(token::Paren)) { fields.push(try!(self.parse_pat())); } @@ -3181,7 +3199,7 @@ impl<'a> Parser<'a> { fn parse_pat_vec_elements( &mut self, - ) -> PResult<(Vec>, Option>, Vec>)> { + ) -> PResult<'a, (Vec>, Option>, Vec>)> { let mut before = Vec::new(); let mut slice = None; let mut after = Vec::new(); @@ -3202,7 +3220,7 @@ impl<'a> Parser<'a> { if before_slice { if self.check(&token::DotDot) { - try!(self.bump()); + self.bump(); if self.check(&token::Comma) || self.check(&token::CloseDelim(token::Bracket)) { @@ -3219,7 +3237,7 @@ impl<'a> Parser<'a> { let subpat = try!(self.parse_pat()); if before_slice && self.check(&token::DotDot) { - try!(self.bump()); + self.bump(); slice = Some(subpat); before_slice = false; } else if before_slice { @@ -3233,7 +3251,7 @@ impl<'a> Parser<'a> { } /// Parse the fields of a struct-like pattern - fn parse_pat_fields(&mut self) -> PResult<(Vec> , bool)> { + fn parse_pat_fields(&mut self) -> PResult<'a, (Vec> , bool)> { let mut fields = Vec::new(); let mut etc = false; let mut first = true; @@ -3250,7 +3268,7 @@ impl<'a> Parser<'a> { let hi; if self.check(&token::DotDot) { - try!(self.bump()); + self.bump(); if self.token != token::CloseDelim(token::Brace) { let token_str = self.this_token_to_string(); return Err(self.fatal(&format!("expected `{}`, found `{}`", "}", @@ -3264,16 +3282,16 @@ impl<'a> Parser<'a> { let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { // Parsing a pattern of the form "fieldname: pat" let fieldname = try!(self.parse_ident()); - try!(self.bump()); + self.bump(); let pat = try!(self.parse_pat()); hi = pat.span.hi; (pat, fieldname, false) } else { // Parsing a pattern of the form "(box) (ref) (mut) fieldname" - let is_box = try!(self.eat_keyword(keywords::Box)); + let is_box = self.eat_keyword(keywords::Box); let boxed_span_lo = self.span.lo; - let is_ref = try!(self.eat_keyword(keywords::Ref)); - let is_mut = try!(self.eat_keyword(keywords::Mut)); + let is_ref = self.eat_keyword(keywords::Ref); + let is_mut = self.eat_keyword(keywords::Mut); let fieldname = try!(self.parse_ident()); hi = self.last_span.hi; @@ -3310,10 +3328,10 @@ impl<'a> Parser<'a> { return Ok((fields, etc)); } - fn parse_pat_range_end(&mut self) -> PResult> { + fn parse_pat_range_end(&mut self) -> PResult<'a, P> { if self.is_path_start() { let lo = self.span.lo; - let (qself, path) = if try!(self.eat_lt()) { + let (qself, path) = if self.eat_lt() { // Parse a qualified path let (qself, path) = try!(self.parse_qualified_path(NoTypesAllowed)); @@ -3336,7 +3354,7 @@ impl<'a> Parser<'a> { } /// Parse a pattern. - pub fn parse_pat(&mut self) -> PResult> { + pub fn parse_pat(&mut self) -> PResult<'a, P> { maybe_whole!(self, NtPat); let lo = self.span.lo; @@ -3344,7 +3362,7 @@ impl<'a> Parser<'a> { match self.token { token::Underscore => { // Parse _ - try!(self.bump()); + self.bump(); pat = PatWild; } token::BinOp(token::And) | token::AndAnd => { @@ -3360,28 +3378,28 @@ impl<'a> Parser<'a> { } token::OpenDelim(token::Paren) => { // Parse (pat,pat,pat,...) as tuple pattern - try!(self.bump()); + self.bump(); let fields = try!(self.parse_pat_tuple_elements()); try!(self.expect(&token::CloseDelim(token::Paren))); pat = PatTup(fields); } token::OpenDelim(token::Bracket) => { // Parse [pat,pat,...] as slice pattern - try!(self.bump()); + self.bump(); let (before, slice, after) = try!(self.parse_pat_vec_elements()); try!(self.expect(&token::CloseDelim(token::Bracket))); pat = PatVec(before, slice, after); } _ => { // At this point, token != _, &, &&, (, [ - if try!(self.eat_keyword(keywords::Mut)) { + if self.eat_keyword(keywords::Mut) { // Parse mut ident @ pat pat = try!(self.parse_pat_ident(BindingMode::ByValue(MutMutable))); - } else if try!(self.eat_keyword(keywords::Ref)) { + } else if self.eat_keyword(keywords::Ref) { // Parse ref ident @ pat / ref mut ident @ pat let mutbl = try!(self.parse_mutability()); pat = try!(self.parse_pat_ident(BindingMode::ByRef(mutbl))); - } else if try!(self.eat_keyword(keywords::Box)) { + } else if self.eat_keyword(keywords::Box) { // Parse box pat let subpat = try!(self.parse_pat()); pat = PatBox(subpat); @@ -3398,7 +3416,7 @@ impl<'a> Parser<'a> { let ident = try!(self.parse_ident()); let ident_span = self.last_span; let path = ident_to_path(ident_span, ident); - try!(self.bump()); + self.bump(); let delim = try!(self.expect_open_delim()); let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim), seq_sep_none(), |p| p.parse_token_tree())); @@ -3412,7 +3430,7 @@ impl<'a> Parser<'a> { pat = try!(self.parse_pat_ident(BindingMode::ByValue(MutImmutable))); } } else { - let (qself, path) = if try!(self.eat_lt()) { + let (qself, path) = if self.eat_lt() { // Parse a qualified path let (qself, path) = try!(self.parse_qualified_path(NoTypesAllowed)); @@ -3426,7 +3444,7 @@ impl<'a> Parser<'a> { // Parse range let hi = self.last_span.hi; let begin = self.mk_expr(lo, hi, ExprPath(qself, path), None); - try!(self.bump()); + self.bump(); let end = try!(self.parse_pat_range_end()); pat = PatRange(begin, end); } @@ -3435,9 +3453,9 @@ impl<'a> Parser<'a> { return Err(self.fatal("unexpected `{` after qualified path")); } // Parse struct pattern - try!(self.bump()); + self.bump(); let (fields, etc) = try!(self.parse_pat_fields()); - try!(self.bump()); + self.bump(); pat = PatStruct(path, fields, etc); } token::OpenDelim(token::Paren) => { @@ -3447,8 +3465,8 @@ impl<'a> Parser<'a> { // Parse tuple struct or enum pattern if self.look_ahead(1, |t| *t == token::DotDot) { // This is a "top constructor only" pat - try!(self.bump()); - try!(self.bump()); + self.bump(); + self.bump(); try!(self.expect(&token::CloseDelim(token::Paren))); pat = PatEnum(path, None); } else { @@ -3473,7 +3491,7 @@ impl<'a> Parser<'a> { } else { // Try to parse everything else as literal with optional minus let begin = try!(self.parse_pat_literal_maybe_minus()); - if try!(self.eat(&token::DotDotDot)) { + if self.eat(&token::DotDotDot) { let end = try!(self.parse_pat_range_end()); pat = PatRange(begin, end); } else { @@ -3496,7 +3514,7 @@ impl<'a> Parser<'a> { /// error message when parsing mistakes like ref foo(a,b) fn parse_pat_ident(&mut self, binding_mode: ast::BindingMode) - -> PResult { + -> PResult<'a, ast::Pat_> { if !self.token.is_plain_ident() { let span = self.span; let tok_str = self.this_token_to_string(); @@ -3506,7 +3524,7 @@ impl<'a> Parser<'a> { let ident = try!(self.parse_ident()); let last_span = self.last_span; let name = codemap::Spanned{span: last_span, node: ident}; - let sub = if try!(self.eat(&token::At) ){ + let sub = if self.eat(&token::At) { Some(try!(self.parse_pat())) } else { None @@ -3529,12 +3547,12 @@ impl<'a> Parser<'a> { } /// Parse a local variable declaration - fn parse_local(&mut self, attrs: ThinAttributes) -> PResult> { + fn parse_local(&mut self, attrs: ThinAttributes) -> PResult<'a, P> { let lo = self.span.lo; let pat = try!(self.parse_pat()); let mut ty = None; - if try!(self.eat(&token::Colon) ){ + if self.eat(&token::Colon) { ty = Some(try!(self.parse_ty_sum())); } let init = try!(self.parse_initializer()); @@ -3549,7 +3567,7 @@ impl<'a> Parser<'a> { } /// Parse a "let" stmt - fn parse_let(&mut self, attrs: ThinAttributes) -> PResult> { + fn parse_let(&mut self, attrs: ThinAttributes) -> PResult<'a, P> { let lo = self.span.lo; let local = try!(self.parse_local(attrs)); Ok(P(spanned(lo, self.last_span.hi, DeclLocal(local)))) @@ -3557,7 +3575,7 @@ impl<'a> Parser<'a> { /// Parse a structure field fn parse_name_and_ty(&mut self, pr: Visibility, - attrs: Vec ) -> PResult { + attrs: Vec ) -> PResult<'a, StructField> { let lo = match pr { Inherited => self.span.lo, Public => self.last_span.lo, @@ -3589,11 +3607,11 @@ impl<'a> Parser<'a> { } /// Parse a statement. may include decl. - pub fn parse_stmt(&mut self) -> PResult>> { + pub fn parse_stmt(&mut self) -> PResult<'a, Option>> { Ok(try!(self.parse_stmt_()).map(P)) } - fn parse_stmt_(&mut self) -> PResult> { + fn parse_stmt_(&mut self) -> PResult<'a, Option> { maybe_whole!(Some deref self, NtStmt); let attrs = try!(self.parse_outer_attributes()); @@ -3613,7 +3631,7 @@ impl<'a> Parser<'a> { // Potential trouble: if we allow macros with paths instead of // idents, we'd need to look ahead past the whole path here... let pth = try!(self.parse_path(NoTypesAllowed)); - try!(self.bump()); + self.bump(); let id = match self.token { token::OpenDelim(_) => token::special_idents::invalid, // no special identifier @@ -3666,7 +3684,7 @@ impl<'a> Parser<'a> { // // Require a semicolon or braces. if style != MacStmtWithBraces { - if !try!(self.eat(&token::Semi) ){ + if !self.eat(&token::Semi) { let last_span = self.last_span; self.span_err(last_span, "macros that expand to items must \ @@ -3702,7 +3720,7 @@ impl<'a> Parser<'a> { // Do not attempt to parse an expression if we're done here. if self.token == token::Semi { unused_attrs(&attrs, self); - try!(self.bump()); + self.bump(); return Ok(None); } @@ -3729,12 +3747,12 @@ impl<'a> Parser<'a> { } /// Parse a block. No inner attrs are allowed. - pub fn parse_block(&mut self) -> PResult> { + pub fn parse_block(&mut self) -> PResult<'a, P> { maybe_whole!(no_clone self, NtBlock); let lo = self.span.lo; - if !try!(self.eat(&token::OpenDelim(token::Brace)) ){ + if !self.eat(&token::OpenDelim(token::Brace)) { let sp = self.span; let tok = self.this_token_to_string(); return Err(self.span_fatal_help(sp, @@ -3746,7 +3764,7 @@ impl<'a> Parser<'a> { } /// Parse a block. Inner attrs are allowed. - fn parse_inner_attrs_and_block(&mut self) -> PResult<(Vec, P)> { + fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec, P)> { maybe_whole!(pair_empty self, NtBlock); let lo = self.span.lo; @@ -3757,11 +3775,11 @@ impl<'a> Parser<'a> { /// Parse the rest of a block expression or function body /// Precondition: already parsed the '{'. - fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult> { + fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P> { let mut stmts = vec![]; let mut expr = None; - while !try!(self.eat(&token::CloseDelim(token::Brace))) { + while !self.eat(&token::CloseDelim(token::Brace)) { let Spanned {node, span} = if let Some(s) = try!(self.parse_stmt_()) { s } else { @@ -3781,7 +3799,7 @@ impl<'a> Parser<'a> { node: StmtMac(mac, MacStmtWithSemicolon, attrs), span: mk_sp(span.lo, self.span.hi), })); - try!(self.bump()); + self.bump(); } _ => { let e = self.mk_mac_expr(span.lo, span.hi, @@ -3805,7 +3823,7 @@ impl<'a> Parser<'a> { node: StmtMac(m, MacStmtWithSemicolon, attrs), span: mk_sp(span.lo, self.span.hi), })); - try!(self.bump()); + self.bump(); } token::CloseDelim(token::Brace) => { // if a block ends in `m!(arg)` without @@ -3851,7 +3869,7 @@ impl<'a> Parser<'a> { e: P, span: Span, stmts: &mut Vec>, - last_block_expr: &mut Option>) -> PResult<()> { + last_block_expr: &mut Option>) -> PResult<'a, ()> { // expression without semicolon if classify::expr_requires_semi_to_be_stmt(&*e) { // Just check for errors and recover; do not eat semicolon yet. @@ -3861,7 +3879,7 @@ impl<'a> Parser<'a> { match self.token { token::Semi => { - try!(self.bump()); + self.bump(); let span_with_semi = Span { lo: span.lo, hi: self.last_span.hi, @@ -3887,9 +3905,9 @@ impl<'a> Parser<'a> { // otherwise returns empty list. fn parse_colon_then_ty_param_bounds(&mut self, mode: BoundParsingMode) - -> PResult + -> PResult<'a, TyParamBounds> { - if !try!(self.eat(&token::Colon) ){ + if !self.eat(&token::Colon) { Ok(P::empty()) } else { self.parse_ty_param_bounds(mode) @@ -3902,12 +3920,12 @@ impl<'a> Parser<'a> { // and bound = 'region | trait_ref fn parse_ty_param_bounds(&mut self, mode: BoundParsingMode) - -> PResult + -> PResult<'a, TyParamBounds> { let mut result = vec!(); loop { let question_span = self.span; - let ate_question = try!(self.eat(&token::Question)); + let ate_question = self.eat(&token::Question); match self.token { token::Lifetime(lifetime) => { if ate_question { @@ -3919,7 +3937,7 @@ impl<'a> Parser<'a> { span: self.span, name: lifetime.name })); - try!(self.bump()); + self.bump(); } token::ModSep | token::Ident(..) => { let poly_trait_ref = try!(self.parse_poly_trait_ref()); @@ -3939,7 +3957,7 @@ impl<'a> Parser<'a> { _ => break, } - if !try!(self.eat(&token::BinOp(token::Plus)) ){ + if !self.eat(&token::BinOp(token::Plus)) { break; } } @@ -3948,14 +3966,14 @@ impl<'a> Parser<'a> { } /// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )? - fn parse_ty_param(&mut self) -> PResult { + fn parse_ty_param(&mut self) -> PResult<'a, TyParam> { let span = self.span; let ident = try!(self.parse_ident()); let bounds = try!(self.parse_colon_then_ty_param_bounds(BoundParsingMode::Modified)); let default = if self.check(&token::Eq) { - try!(self.bump()); + self.bump(); Some(try!(self.parse_ty_sum())) } else { None @@ -3977,10 +3995,10 @@ impl<'a> Parser<'a> { /// matches generics = ( ) | ( < > ) | ( < typaramseq ( , )? > ) | ( < lifetimes ( , )? > ) /// | ( < lifetimes , typaramseq ( , )? > ) /// where typaramseq = ( typaram ) | ( typaram , typaramseq ) - pub fn parse_generics(&mut self) -> PResult { + pub fn parse_generics(&mut self) -> PResult<'a, ast::Generics> { maybe_whole!(self, NtGenerics); - if try!(self.eat(&token::Lt) ){ + if self.eat(&token::Lt) { let lifetime_defs = try!(self.parse_lifetime_defs()); let mut seen_default = false; let ty_params = try!(self.parse_seq_to_gt(Some(token::Comma), |p| { @@ -4008,7 +4026,7 @@ impl<'a> Parser<'a> { } } - fn parse_generic_values_after_lt(&mut self) -> PResult<(Vec, + fn parse_generic_values_after_lt(&mut self) -> PResult<'a, (Vec, Vec>, Vec>)> { let span_lo = self.span.lo; @@ -4025,7 +4043,7 @@ impl<'a> Parser<'a> { let msg = format!("expected `,` or `>` after lifetime \ name, found `{}`", self.this_token_to_string()); - self.span_err(self.span, &msg); + let mut err = self.diagnostic().struct_span_err(self.span, &msg); let span_hi = self.span.hi; let span_hi = if self.parse_ty().is_ok() { @@ -4037,7 +4055,8 @@ impl<'a> Parser<'a> { let msg = format!("did you mean a single argument type &'a Type, \ or did you mean the comma-separated arguments \ 'a, Type?"); - self.span_note(mk_sp(span_lo, span_hi), &msg); + err.span_note(mk_sp(span_lo, span_hi), &msg); + err.emit(); self.abort_if_errors() } @@ -4067,7 +4086,7 @@ impl<'a> Parser<'a> { try!(p.forbid_lifetime()); let lo = p.span.lo; let ident = try!(p.parse_ident()); - let found_eq = try!(p.eat(&token::Eq)); + let found_eq = p.eat(&token::Eq); if !found_eq { let span = p.span; p.span_warn(span, "whoops, no =?"); @@ -4085,7 +4104,7 @@ impl<'a> Parser<'a> { Ok((lifetimes, types.into_vec(), bindings.into_vec())) } - fn forbid_lifetime(&mut self) -> PResult<()> { + fn forbid_lifetime(&mut self) -> PResult<'a, ()> { if self.token.is_lifetime() { let span = self.span; return Err(self.span_fatal(span, "lifetime parameters must be declared \ @@ -4099,7 +4118,7 @@ impl<'a> Parser<'a> { /// ```ignore /// where T : Trait + 'b, 'a : 'b /// ``` - pub fn parse_where_clause(&mut self) -> PResult { + pub fn parse_where_clause(&mut self) -> PResult<'a, ast::WhereClause> { maybe_whole!(self, NtWhereClause); let mut where_clause = WhereClause { @@ -4107,7 +4126,7 @@ impl<'a> Parser<'a> { predicates: Vec::new(), }; - if !try!(self.eat_keyword(keywords::Where)) { + if !self.eat_keyword(keywords::Where) { return Ok(where_clause); } @@ -4123,7 +4142,7 @@ impl<'a> Parser<'a> { let bounded_lifetime = try!(self.parse_lifetime()); - try!(self.eat(&token::Colon)); + self.eat(&token::Colon); let bounds = try!(self.parse_lifetimes(token::BinOp(token::Plus))); @@ -4143,7 +4162,7 @@ impl<'a> Parser<'a> { } _ => { - let bound_lifetimes = if try!(self.eat_keyword(keywords::For) ){ + let bound_lifetimes = if self.eat_keyword(keywords::For) { // Higher ranked constraint. try!(self.expect(&token::Lt)); let lifetime_defs = try!(self.parse_lifetime_defs()); @@ -4155,7 +4174,7 @@ impl<'a> Parser<'a> { let bounded_ty = try!(self.parse_ty()); - if try!(self.eat(&token::Colon) ){ + if self.eat(&token::Colon) { let bounds = try!(self.parse_ty_param_bounds(BoundParsingMode::Bare)); let hi = self.last_span.hi; let span = mk_sp(lo, hi); @@ -4175,7 +4194,7 @@ impl<'a> Parser<'a> { })); parsed_something = true; - } else if try!(self.eat(&token::Eq) ){ + } else if self.eat(&token::Eq) { // let ty = try!(self.parse_ty()); let hi = self.last_span.hi; let span = mk_sp(lo, hi); @@ -4199,7 +4218,7 @@ impl<'a> Parser<'a> { } }; - if !try!(self.eat(&token::Comma) ){ + if !self.eat(&token::Comma) { break } } @@ -4215,7 +4234,7 @@ impl<'a> Parser<'a> { } fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool) - -> PResult<(Vec , bool)> { + -> PResult<'a, (Vec , bool)> { let sp = self.span; let mut args: Vec> = try!(self.parse_unspanned_seq( @@ -4224,7 +4243,7 @@ impl<'a> Parser<'a> { seq_sep_trailing_allowed(token::Comma), |p| { if p.token == token::DotDotDot { - try!(p.bump()); + p.bump(); if allow_variadic { if p.token != token::CloseDelim(token::Paren) { let span = p.span; @@ -4264,7 +4283,7 @@ impl<'a> Parser<'a> { } /// Parse the argument list and result type of a function declaration - pub fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult> { + pub fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<'a, P> { let (args, variadic) = try!(self.parse_fn_args(true, allow_variadic)); let ret_ty = try!(self.parse_ret_ty()); @@ -4283,10 +4302,10 @@ impl<'a> Parser<'a> { } } - fn expect_self_ident(&mut self) -> PResult { + fn expect_self_ident(&mut self) -> PResult<'a, ast::Ident> { match self.token { token::Ident(id, token::Plain) if id.name == special_idents::self_.name => { - try!(self.bump()); + self.bump(); Ok(id) }, _ => { @@ -4304,10 +4323,10 @@ impl<'a> Parser<'a> { } } - fn expect_self_type_ident(&mut self) -> PResult { + fn expect_self_type_ident(&mut self) -> PResult<'a, ast::Ident> { match self.token { token::Ident(id, token::Plain) if id.name == special_idents::type_self.name => { - try!(self.bump()); + self.bump(); Ok(id) }, _ => { @@ -4321,11 +4340,11 @@ impl<'a> Parser<'a> { /// Parse the argument list and result type of a function /// that may have a self type. fn parse_fn_decl_with_self(&mut self, - parse_arg_fn: F) -> PResult<(ExplicitSelf, P)> where - F: FnMut(&mut Parser) -> PResult, + parse_arg_fn: F) -> PResult<'a, (ExplicitSelf, P)> where + F: FnMut(&mut Parser<'a>) -> PResult<'a, Arg>, { - fn maybe_parse_borrowed_explicit_self(this: &mut Parser) - -> PResult { + fn maybe_parse_borrowed_explicit_self<'b>(this: &mut Parser<'b>) + -> PResult<'b, ast::ExplicitSelf_> { // The following things are possible to see here: // // fn(&mut self) @@ -4336,22 +4355,22 @@ impl<'a> Parser<'a> { // We already know that the current token is `&`. if this.look_ahead(1, |t| t.is_keyword(keywords::SelfValue)) { - try!(this.bump()); + this.bump(); Ok(SelfRegion(None, MutImmutable, try!(this.expect_self_ident()))) } else if this.look_ahead(1, |t| t.is_mutability()) && this.look_ahead(2, |t| t.is_keyword(keywords::SelfValue)) { - try!(this.bump()); + this.bump(); let mutability = try!(this.parse_mutability()); Ok(SelfRegion(None, mutability, try!(this.expect_self_ident()))) } else if this.look_ahead(1, |t| t.is_lifetime()) && this.look_ahead(2, |t| t.is_keyword(keywords::SelfValue)) { - try!(this.bump()); + this.bump(); let lifetime = try!(this.parse_lifetime()); Ok(SelfRegion(Some(lifetime), MutImmutable, try!(this.expect_self_ident()))) } else if this.look_ahead(1, |t| t.is_lifetime()) && this.look_ahead(2, |t| t.is_mutability()) && this.look_ahead(3, |t| t.is_keyword(keywords::SelfValue)) { - try!(this.bump()); + this.bump(); let lifetime = try!(this.parse_lifetime()); let mutability = try!(this.parse_mutability()); Ok(SelfRegion(Some(lifetime), mutability, try!(this.expect_self_ident()))) @@ -4379,7 +4398,7 @@ impl<'a> Parser<'a> { token::BinOp(token::Star) => { // Possibly "*self" or "*mut self" -- not supported. Try to avoid // emitting cryptic "unexpected token" errors. - try!(self.bump()); + self.bump(); let _mutability = if self.token.is_mutability() { try!(self.parse_mutability()) } else { @@ -4388,7 +4407,7 @@ impl<'a> Parser<'a> { if self.is_self_ident() { let span = self.span; self.span_err(span, "cannot pass self by raw pointer"); - try!(self.bump()); + self.bump(); } // error case, making bogus self ident: SelfValue(special_idents::self_) @@ -4399,7 +4418,7 @@ impl<'a> Parser<'a> { // Determine whether this is the fully explicit form, `self: // TYPE`. - if try!(self.eat(&token::Colon) ){ + if self.eat(&token::Colon) { SelfExplicit(try!(self.parse_ty_sum()), self_ident) } else { SelfValue(self_ident) @@ -4411,7 +4430,7 @@ impl<'a> Parser<'a> { // Determine whether this is the fully explicit form, // `self: TYPE`. - if try!(self.eat(&token::Colon) ){ + if self.eat(&token::Colon) { SelfExplicit(try!(self.parse_ty_sum()), self_ident) } else { SelfValue(self_ident) @@ -4433,7 +4452,7 @@ impl<'a> Parser<'a> { // If we parsed a self type, expect a comma before the argument list. match self.token { token::Comma => { - try!(self.bump()); + self.bump(); let sep = seq_sep_trailing_allowed(token::Comma); let mut fn_inputs = try!(self.parse_seq_to_before_end( &token::CloseDelim(token::Paren), @@ -4483,9 +4502,9 @@ impl<'a> Parser<'a> { } // parse the |arg, arg| header on a lambda - fn parse_fn_block_decl(&mut self) -> PResult> { + fn parse_fn_block_decl(&mut self) -> PResult<'a, P> { let inputs_captures = { - if try!(self.eat(&token::OrOr) ){ + if self.eat(&token::OrOr) { Vec::new() } else { try!(self.expect(&token::BinOp(token::Or))); @@ -4495,7 +4514,7 @@ impl<'a> Parser<'a> { seq_sep_trailing_allowed(token::Comma), |p| p.parse_fn_block_arg() )); - try!(self.bump()); + self.bump(); args } }; @@ -4509,7 +4528,7 @@ impl<'a> Parser<'a> { } /// Parse the name and optional generic types of a function header. - fn parse_fn_header(&mut self) -> PResult<(Ident, ast::Generics)> { + fn parse_fn_header(&mut self) -> PResult<'a, (Ident, ast::Generics)> { let id = try!(self.parse_ident()); let generics = try!(self.parse_generics()); Ok((id, generics)) @@ -4533,7 +4552,7 @@ impl<'a> Parser<'a> { unsafety: Unsafety, constness: Constness, abi: abi::Abi) - -> PResult { + -> PResult<'a, ItemInfo> { let (ident, mut generics) = try!(self.parse_fn_header()); let decl = try!(self.parse_fn_decl(false)); generics.where_clause = try!(self.parse_where_clause()); @@ -4556,13 +4575,14 @@ impl<'a> Parser<'a> { /// - `const unsafe fn` /// - `extern fn` /// - etc - pub fn parse_fn_front_matter(&mut self) -> PResult<(ast::Constness, ast::Unsafety, abi::Abi)> { - let is_const_fn = try!(self.eat_keyword(keywords::Const)); + pub fn parse_fn_front_matter(&mut self) + -> PResult<'a, (ast::Constness, ast::Unsafety, abi::Abi)> { + let is_const_fn = self.eat_keyword(keywords::Const); let unsafety = try!(self.parse_unsafety()); let (constness, unsafety, abi) = if is_const_fn { (Constness::Const, unsafety, abi::Rust) } else { - let abi = if try!(self.eat_keyword(keywords::Extern)) { + let abi = if self.eat_keyword(keywords::Extern) { try!(self.parse_opt_abi()).unwrap_or(abi::C) } else { abi::Rust @@ -4574,13 +4594,13 @@ impl<'a> Parser<'a> { } /// Parse an impl item. - pub fn parse_impl_item(&mut self) -> PResult> { + pub fn parse_impl_item(&mut self) -> PResult<'a, P> { maybe_whole!(no_clone self, NtImplItem); let mut attrs = try!(self.parse_outer_attributes()); let lo = self.span.lo; let vis = try!(self.parse_visibility()); - let (name, node) = if try!(self.eat_keyword(keywords::Type)) { + let (name, node) = if self.eat_keyword(keywords::Type) { let name = try!(self.parse_ident()); try!(self.expect(&token::Eq)); let typ = try!(self.parse_ty_sum()); @@ -4614,9 +4634,22 @@ impl<'a> Parser<'a> { fn complain_if_pub_macro(&mut self, visa: Visibility, span: Span) { match visa { Public => { - self.span_err(span, "can't qualify macro invocation with `pub`"); - self.fileline_help(span, "try adjusting the macro to put `pub` inside \ - the invocation"); + let is_macro_rules: bool = match self.token { + token::Ident(sid, _) => sid.name == intern("macro_rules"), + _ => false, + }; + if is_macro_rules { + self.diagnostic().struct_span_err(span, "can't qualify macro_rules \ + invocation with `pub`") + .fileline_help(span, "did you mean #[macro_export]?") + .emit(); + } else { + self.diagnostic().struct_span_err(span, "can't qualify macro \ + invocation with `pub`") + .fileline_help(span, "try adjusting the macro to put `pub` \ + inside the invocation") + .emit(); + } } Inherited => (), } @@ -4624,7 +4657,7 @@ impl<'a> Parser<'a> { /// Parse a method or a macro invocation in a trait impl. fn parse_impl_method(&mut self, vis: Visibility) - -> PResult<(Ident, Vec, ast::ImplItemKind)> { + -> PResult<'a, (Ident, Vec, ast::ImplItemKind)> { // code copied from parse_macro_use_or_failure... abstraction! if !self.token.is_any_keyword() && self.look_ahead(1, |t| *t == token::Not) @@ -4673,7 +4706,7 @@ impl<'a> Parser<'a> { } /// Parse trait Foo { ... } - fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult { + fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<'a, ItemInfo> { let ident = try!(self.parse_ident()); let mut tps = try!(self.parse_generics()); @@ -4691,7 +4724,7 @@ impl<'a> Parser<'a> { /// impl Foo { ... } /// impl ToString for &'static T { ... } /// impl Send for .. {} - fn parse_item_impl(&mut self, unsafety: ast::Unsafety) -> PResult { + fn parse_item_impl(&mut self, unsafety: ast::Unsafety) -> PResult<'a, ItemInfo> { let impl_span = self.span; // First, parse type parameters if necessary. @@ -4702,7 +4735,7 @@ impl<'a> Parser<'a> { let could_be_trait = self.token != token::OpenDelim(token::Paren); let neg_span = self.span; - let polarity = if try!(self.eat(&token::Not) ){ + let polarity = if self.eat(&token::Not) { ast::ImplPolarity::Negative } else { ast::ImplPolarity::Positive @@ -4712,7 +4745,7 @@ impl<'a> Parser<'a> { let mut ty = try!(self.parse_ty_sum()); // Parse traits, if necessary. - let opt_trait = if could_be_trait && try!(self.eat_keyword(keywords::For) ){ + let opt_trait = if could_be_trait && self.eat_keyword(keywords::For) { // New-style trait. Reinterpret the type as a trait. match ty.node { TyPath(None, ref path) => { @@ -4738,7 +4771,7 @@ impl<'a> Parser<'a> { None }; - if opt_trait.is_some() && try!(self.eat(&token::DotDot) ){ + if opt_trait.is_some() && self.eat(&token::DotDot) { if generics.is_parameterized() { self.span_err(impl_span, "default trait implementations are not \ allowed to have generics"); @@ -4758,7 +4791,7 @@ impl<'a> Parser<'a> { let attrs = try!(self.parse_inner_attributes()); let mut impl_items = vec![]; - while !try!(self.eat(&token::CloseDelim(token::Brace))) { + while !self.eat(&token::CloseDelim(token::Brace)) { impl_items.push(try!(self.parse_impl_item())); } @@ -4769,15 +4802,15 @@ impl<'a> Parser<'a> { } /// Parse a::B - fn parse_trait_ref(&mut self) -> PResult { + fn parse_trait_ref(&mut self) -> PResult<'a, TraitRef> { Ok(ast::TraitRef { path: try!(self.parse_path(LifetimeAndTypesWithoutColons)), ref_id: ast::DUMMY_NODE_ID, }) } - fn parse_late_bound_lifetime_defs(&mut self) -> PResult> { - if try!(self.eat_keyword(keywords::For) ){ + fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec> { + if self.eat_keyword(keywords::For) { try!(self.expect(&token::Lt)); let lifetime_defs = try!(self.parse_lifetime_defs()); try!(self.expect_gt()); @@ -4788,7 +4821,7 @@ impl<'a> Parser<'a> { } /// Parse for<'l> a::B - fn parse_poly_trait_ref(&mut self) -> PResult { + fn parse_poly_trait_ref(&mut self) -> PResult<'a, PolyTraitRef> { let lo = self.span.lo; let lifetime_defs = try!(self.parse_late_bound_lifetime_defs()); @@ -4800,7 +4833,7 @@ impl<'a> Parser<'a> { } /// Parse struct Foo { ... } - fn parse_item_struct(&mut self) -> PResult { + fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> { let class_name = try!(self.parse_ident()); let mut generics = try!(self.parse_generics()); @@ -4820,7 +4853,7 @@ impl<'a> Parser<'a> { let vdata = if self.token.is_keyword(keywords::Where) { generics.where_clause = try!(self.parse_where_clause()); - if try!(self.eat(&token::Semi)) { + if self.eat(&token::Semi) { // If we see a: `struct Foo where T: Copy;` style decl. VariantData::Unit(ast::DUMMY_NODE_ID) } else { @@ -4829,7 +4862,7 @@ impl<'a> Parser<'a> { ast::DUMMY_NODE_ID) } // No `where` so: `struct Foo;` - } else if try!(self.eat(&token::Semi) ){ + } else if self.eat(&token::Semi) { VariantData::Unit(ast::DUMMY_NODE_ID) // Record-style struct definition } else if self.token == token::OpenDelim(token::Brace) { @@ -4851,14 +4884,16 @@ impl<'a> Parser<'a> { Ok((class_name, ItemStruct(vdata, generics), None)) } - pub fn parse_record_struct_body(&mut self, parse_pub: ParsePub) -> PResult> { + pub fn parse_record_struct_body(&mut self, + parse_pub: ParsePub) + -> PResult<'a, Vec> { let mut fields = Vec::new(); - if try!(self.eat(&token::OpenDelim(token::Brace)) ){ + if self.eat(&token::OpenDelim(token::Brace)) { while self.token != token::CloseDelim(token::Brace) { fields.push(try!(self.parse_struct_decl_field(parse_pub))); } - try!(self.bump()); + self.bump(); } else { let token_str = self.this_token_to_string(); return Err(self.fatal(&format!("expected `where`, or `{{` after struct \ @@ -4869,7 +4904,9 @@ impl<'a> Parser<'a> { Ok(fields) } - pub fn parse_tuple_struct_body(&mut self, parse_pub: ParsePub) -> PResult> { + pub fn parse_tuple_struct_body(&mut self, + parse_pub: ParsePub) + -> PResult<'a, Vec> { // This is the case where we find `struct Foo(T) where T: Copy;` // Unit like structs are handled in parse_item_struct function let fields = try!(self.parse_unspanned_seq( @@ -4901,11 +4938,11 @@ impl<'a> Parser<'a> { pub fn parse_single_struct_field(&mut self, vis: Visibility, attrs: Vec ) - -> PResult { + -> PResult<'a, StructField> { let a_var = try!(self.parse_name_and_ty(vis, attrs)); match self.token { token::Comma => { - try!(self.bump()); + self.bump(); } token::CloseDelim(token::Brace) => {} _ => { @@ -4921,11 +4958,11 @@ impl<'a> Parser<'a> { } /// Parse an element of a struct definition - fn parse_struct_decl_field(&mut self, parse_pub: ParsePub) -> PResult { + fn parse_struct_decl_field(&mut self, parse_pub: ParsePub) -> PResult<'a, StructField> { let attrs = try!(self.parse_outer_attributes()); - if try!(self.eat_keyword(keywords::Pub) ){ + if self.eat_keyword(keywords::Pub) { if parse_pub == ParsePub::No { let span = self.last_span; self.span_err(span, "`pub` is not allowed here"); @@ -4937,19 +4974,19 @@ impl<'a> Parser<'a> { } /// Parse visibility: PUB or nothing - fn parse_visibility(&mut self) -> PResult { - if try!(self.eat_keyword(keywords::Pub)) { Ok(Public) } + fn parse_visibility(&mut self) -> PResult<'a, Visibility> { + if self.eat_keyword(keywords::Pub) { Ok(Public) } else { Ok(Inherited) } } /// Given a termination token, parse all of the items in a module - fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult { + fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<'a, Mod> { let mut items = vec![]; while let Some(item) = try!(self.parse_item()) { items.push(item); } - if !try!(self.eat(term)) { + if !self.eat(term) { let token_str = self.this_token_to_string(); return Err(self.fatal(&format!("expected item, found `{}`", token_str))); } @@ -4966,7 +5003,7 @@ impl<'a> Parser<'a> { }) } - fn parse_item_const(&mut self, m: Option) -> PResult { + fn parse_item_const(&mut self, m: Option) -> PResult<'a, ItemInfo> { let id = try!(self.parse_ident()); try!(self.expect(&token::Colon)); let ty = try!(self.parse_ty_sum()); @@ -4981,11 +5018,11 @@ impl<'a> Parser<'a> { } /// Parse a `mod { ... }` or `mod ;` item - fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult { + fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<'a, ItemInfo> { let id_span = self.span; let id = try!(self.parse_ident()); if self.check(&token::Semi) { - try!(self.bump()); + self.bump(); // This mod is in an external file. Let's go get it! let (m, attrs) = try!(self.eval_src_mod(id, outer_attrs, id_span)); Ok((id, m, Some(attrs))) @@ -5060,7 +5097,7 @@ impl<'a> Parser<'a> { fn submod_path(&mut self, id: ast::Ident, outer_attrs: &[ast::Attribute], - id_sp: Span) -> PResult { + id_sp: Span) -> PResult<'a, ModulePathSuccess> { let mut prefix = PathBuf::from(&self.sess.codemap().span_to_filename(self.span)); prefix.pop(); let mut dir_path = prefix; @@ -5075,21 +5112,23 @@ impl<'a> Parser<'a> { let paths = Parser::default_submod_path(id, &dir_path, self.sess.codemap()); if !self.owns_directory { - self.span_err(id_sp, "cannot declare a new module at this location"); + let mut err = self.diagnostic().struct_span_err(id_sp, + "cannot declare a new module at this location"); let this_module = match self.mod_path_stack.last() { Some(name) => name.to_string(), None => self.root_module_name.as_ref().unwrap().clone(), }; - self.span_note(id_sp, - &format!("maybe move this module `{0}` to its own directory \ + err.span_note(id_sp, + &format!("maybe move this module `{0}` to its own directory \ via `{0}/mod.rs`", this_module)); if paths.path_exists { - self.span_note(id_sp, - &format!("... or maybe `use` the module `{}` instead \ - of possibly redeclaring it", - paths.name)); + err.span_note(id_sp, + &format!("... or maybe `use` the module `{}` instead \ + of possibly redeclaring it", + paths.name)); } + err.emit(); self.abort_if_errors(); } @@ -5104,7 +5143,7 @@ impl<'a> Parser<'a> { id: ast::Ident, outer_attrs: &[ast::Attribute], id_sp: Span) - -> PResult<(ast::Item_, Vec )> { + -> PResult<'a, (ast::Item_, Vec )> { let ModulePathSuccess { path, owns_directory } = try!(self.submod_path(id, outer_attrs, id_sp)); @@ -5119,7 +5158,7 @@ impl<'a> Parser<'a> { path: PathBuf, owns_directory: bool, name: String, - id_sp: Span) -> PResult<(ast::Item_, Vec )> { + id_sp: Span) -> PResult<'a, (ast::Item_, Vec )> { let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut(); match included_mod_stack.iter().position(|p| *p == path) { Some(i) => { @@ -5152,7 +5191,7 @@ impl<'a> Parser<'a> { /// Parse a function declaration from a foreign module fn parse_item_foreign_fn(&mut self, vis: ast::Visibility, lo: BytePos, - attrs: Vec) -> PResult> { + attrs: Vec) -> PResult<'a, P> { try!(self.expect_keyword(keywords::Fn)); let (ident, mut generics) = try!(self.parse_fn_header()); @@ -5172,9 +5211,9 @@ impl<'a> Parser<'a> { /// Parse a static item from a foreign module fn parse_item_foreign_static(&mut self, vis: ast::Visibility, lo: BytePos, - attrs: Vec) -> PResult> { + attrs: Vec) -> PResult<'a, P> { try!(self.expect_keyword(keywords::Static)); - let mutbl = try!(self.eat_keyword(keywords::Mut)); + let mutbl = self.eat_keyword(keywords::Mut); let ident = try!(self.parse_ident()); try!(self.expect(&token::Colon)); @@ -5201,7 +5240,7 @@ impl<'a> Parser<'a> { lo: BytePos, visibility: Visibility, attrs: Vec) - -> PResult> { + -> PResult<'a, P> { let crate_name = try!(self.parse_ident()); let (maybe_path, ident) = if let Some(ident) = try!(self.parse_rename()) { @@ -5242,7 +5281,7 @@ impl<'a> Parser<'a> { opt_abi: Option, visibility: Visibility, mut attrs: Vec) - -> PResult> { + -> PResult<'a, P> { try!(self.expect(&token::OpenDelim(token::Brace))); let abi = opt_abi.unwrap_or(abi::C); @@ -5269,7 +5308,7 @@ impl<'a> Parser<'a> { } /// Parse type Foo = Bar; - fn parse_item_type(&mut self) -> PResult { + fn parse_item_type(&mut self) -> PResult<'a, ItemInfo> { let ident = try!(self.parse_ident()); let mut tps = try!(self.parse_generics()); tps.where_clause = try!(self.parse_where_clause()); @@ -5280,7 +5319,7 @@ impl<'a> Parser<'a> { } /// Parse the part of an "enum" decl following the '{' - fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult { + fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<'a, EnumDef> { let mut variants = Vec::new(); let mut all_nullary = true; let mut any_disr = None; @@ -5300,7 +5339,7 @@ impl<'a> Parser<'a> { all_nullary = false; struct_def = VariantData::Tuple(try!(self.parse_tuple_struct_body(ParsePub::No)), ast::DUMMY_NODE_ID); - } else if try!(self.eat(&token::Eq) ){ + } else if self.eat(&token::Eq) { disr_expr = Some(try!(self.parse_expr())); any_disr = disr_expr.as_ref().map(|expr| expr.span); struct_def = VariantData::Unit(ast::DUMMY_NODE_ID); @@ -5316,7 +5355,7 @@ impl<'a> Parser<'a> { }; variants.push(P(spanned(vlo, self.last_span.hi, vr))); - if !try!(self.eat(&token::Comma)) { break; } + if !self.eat(&token::Comma) { break; } } try!(self.expect(&token::CloseDelim(token::Brace))); match any_disr { @@ -5330,7 +5369,7 @@ impl<'a> Parser<'a> { } /// Parse an "enum" declaration - fn parse_item_enum(&mut self) -> PResult { + fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> { let id = try!(self.parse_ident()); let mut generics = try!(self.parse_generics()); generics.where_clause = try!(self.parse_where_clause()); @@ -5342,12 +5381,12 @@ impl<'a> Parser<'a> { /// Parses a string as an ABI spec on an extern type or module. Consumes /// the `extern` keyword, if one is found. - fn parse_opt_abi(&mut self) -> PResult> { + fn parse_opt_abi(&mut self) -> PResult<'a, Option> { match self.token { token::Literal(token::Str_(s), suf) | token::Literal(token::StrRaw(s, _), suf) => { let sp = self.span; self.expect_no_suffix(sp, "ABI spec", suf); - try!(self.bump()); + self.bump(); match abi::lookup(&s.as_str()) { Some(abi) => Ok(Some(abi)), None => { @@ -5371,7 +5410,7 @@ impl<'a> Parser<'a> { /// NB: this function no longer parses the items inside an /// extern crate. fn parse_item_(&mut self, attrs: Vec, - macros_allowed: bool, attributes_allowed: bool) -> PResult>> { + macros_allowed: bool, attributes_allowed: bool) -> PResult<'a, Option>> { let nt_item = match self.token { token::Interpolated(token::NtItem(ref item)) => { Some((**item).clone()) @@ -5380,7 +5419,7 @@ impl<'a> Parser<'a> { }; match nt_item { Some(mut item) => { - try!(self.bump()); + self.bump(); let mut attrs = attrs; mem::swap(&mut item.attrs, &mut attrs); item.attrs.extend(attrs); @@ -5393,7 +5432,7 @@ impl<'a> Parser<'a> { let visibility = try!(self.parse_visibility()); - if try!(self.eat_keyword(keywords::Use) ){ + if self.eat_keyword(keywords::Use) { // USE ITEM let item_ = ItemUse(try!(self.parse_view_path())); try!(self.expect(&token::Semi)); @@ -5408,14 +5447,14 @@ impl<'a> Parser<'a> { return Ok(Some(item)); } - if try!(self.eat_keyword(keywords::Extern)) { - if try!(self.eat_keyword(keywords::Crate)) { + if self.eat_keyword(keywords::Extern) { + if self.eat_keyword(keywords::Crate) { return Ok(Some(try!(self.parse_item_extern_crate(lo, visibility, attrs)))); } let opt_abi = try!(self.parse_opt_abi()); - if try!(self.eat_keyword(keywords::Fn) ){ + if self.eat_keyword(keywords::Fn) { // EXTERN FUNCTION ITEM let abi = opt_abi.unwrap_or(abi::C); let (ident, item_, extra_attrs) = @@ -5432,12 +5471,12 @@ impl<'a> Parser<'a> { return Ok(Some(try!(self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs)))); } - try!(self.expect_one_of(&[], &[])); + try!(self.unexpected()); } - if try!(self.eat_keyword(keywords::Static) ){ + if self.eat_keyword(keywords::Static) { // STATIC ITEM - let m = if try!(self.eat_keyword(keywords::Mut)) {MutMutable} else {MutImmutable}; + let m = if self.eat_keyword(keywords::Mut) {MutMutable} else {MutImmutable}; let (ident, item_, extra_attrs) = try!(self.parse_item_const(Some(m))); let last_span = self.last_span; let item = self.mk_item(lo, @@ -5448,17 +5487,17 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if try!(self.eat_keyword(keywords::Const) ){ + if self.eat_keyword(keywords::Const) { if self.check_keyword(keywords::Fn) || (self.check_keyword(keywords::Unsafe) && self.look_ahead(1, |t| t.is_keyword(keywords::Fn))) { // CONST FUNCTION ITEM - let unsafety = if try!(self.eat_keyword(keywords::Unsafe) ){ + let unsafety = if self.eat_keyword(keywords::Unsafe) { Unsafety::Unsafe } else { Unsafety::Normal }; - try!(self.bump()); + self.bump(); let (ident, item_, extra_attrs) = try!(self.parse_item_fn(unsafety, Constness::Const, abi::Rust)); let last_span = self.last_span; @@ -5472,10 +5511,11 @@ impl<'a> Parser<'a> { } // CONST ITEM - if try!(self.eat_keyword(keywords::Mut) ){ + if self.eat_keyword(keywords::Mut) { let last_span = self.last_span; - self.span_err(last_span, "const globals cannot be mutable"); - self.fileline_help(last_span, "did you mean to declare a static?"); + self.diagnostic().struct_span_err(last_span, "const globals cannot be mutable") + .fileline_help(last_span, "did you mean to declare a static?") + .emit(); } let (ident, item_, extra_attrs) = try!(self.parse_item_const(None)); let last_span = self.last_span; @@ -5522,7 +5562,7 @@ impl<'a> Parser<'a> { } if self.check_keyword(keywords::Fn) { // FUNCTION ITEM - try!(self.bump()); + self.bump(); let (ident, item_, extra_attrs) = try!(self.parse_item_fn(Unsafety::Normal, Constness::NotConst, abi::Rust)); let last_span = self.last_span; @@ -5537,8 +5577,8 @@ impl<'a> Parser<'a> { if self.check_keyword(keywords::Unsafe) && self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace)) { // UNSAFE FUNCTION ITEM - try!(self.bump()); - let abi = if try!(self.eat_keyword(keywords::Extern) ){ + self.bump(); + let abi = if self.eat_keyword(keywords::Extern) { try!(self.parse_opt_abi()).unwrap_or(abi::C) } else { abi::Rust @@ -5555,7 +5595,7 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if try!(self.eat_keyword(keywords::Mod) ){ + if self.eat_keyword(keywords::Mod) { // MODULE ITEM let (ident, item_, extra_attrs) = try!(self.parse_item_mod(&attrs[..])); @@ -5568,7 +5608,7 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if try!(self.eat_keyword(keywords::Type) ){ + if self.eat_keyword(keywords::Type) { // TYPE ITEM let (ident, item_, extra_attrs) = try!(self.parse_item_type()); let last_span = self.last_span; @@ -5580,7 +5620,7 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if try!(self.eat_keyword(keywords::Enum) ){ + if self.eat_keyword(keywords::Enum) { // ENUM ITEM let (ident, item_, extra_attrs) = try!(self.parse_item_enum()); let last_span = self.last_span; @@ -5592,7 +5632,7 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if try!(self.eat_keyword(keywords::Trait) ){ + if self.eat_keyword(keywords::Trait) { // TRAIT ITEM let (ident, item_, extra_attrs) = try!(self.parse_item_trait(ast::Unsafety::Normal)); @@ -5605,7 +5645,7 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if try!(self.eat_keyword(keywords::Impl) ){ + if self.eat_keyword(keywords::Impl) { // IMPL ITEM let (ident, item_, extra_attrs) = try!(self.parse_item_impl(ast::Unsafety::Normal)); let last_span = self.last_span; @@ -5617,7 +5657,7 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if try!(self.eat_keyword(keywords::Struct) ){ + if self.eat_keyword(keywords::Struct) { // STRUCT ITEM let (ident, item_, extra_attrs) = try!(self.parse_item_struct()); let last_span = self.last_span; @@ -5633,7 +5673,7 @@ impl<'a> Parser<'a> { } /// Parse a foreign item. - fn parse_foreign_item(&mut self) -> PResult>> { + fn parse_foreign_item(&mut self) -> PResult<'a, Option>> { let attrs = try!(self.parse_outer_attributes()); let lo = self.span.lo; let visibility = try!(self.parse_visibility()); @@ -5664,7 +5704,7 @@ impl<'a> Parser<'a> { attributes_allowed: bool, lo: BytePos, visibility: Visibility - ) -> PResult>> { + ) -> PResult<'a, Option>> { if macros_allowed && !self.token.is_any_keyword() && self.look_ahead(1, |t| *t == token::Not) && (self.look_ahead(2, |t| t.is_plain_ident()) @@ -5701,7 +5741,7 @@ impl<'a> Parser<'a> { self.last_span.hi) }; if delim != token::Brace { - if !try!(self.eat(&token::Semi) ){ + if !self.eat(&token::Semi) { let last_span = self.last_span; self.span_err(last_span, "macros that expand to items must either \ @@ -5736,7 +5776,7 @@ impl<'a> Parser<'a> { Ok(None) } - pub fn parse_item(&mut self) -> PResult>> { + pub fn parse_item(&mut self) -> PResult<'a, Option>> { let attrs = try!(self.parse_outer_attributes()); self.parse_item_(attrs, true, false) } @@ -5747,12 +5787,12 @@ impl<'a> Parser<'a> { /// | MOD? non_global_path MOD_SEP LBRACE ident_seq RBRACE /// | MOD? non_global_path MOD_SEP STAR /// | MOD? non_global_path - fn parse_view_path(&mut self) -> PResult> { + fn parse_view_path(&mut self) -> PResult<'a, P> { let lo = self.span.lo; // Allow a leading :: because the paths are absolute either way. // This occurs with "use $crate::..." in macros. - try!(self.eat(&token::ModSep)); + self.eat(&token::ModSep); if self.check(&token::OpenDelim(token::Brace)) { // use {foo,bar} @@ -5774,7 +5814,7 @@ impl<'a> Parser<'a> { if let token::ModSep = self.token { // foo::bar or foo::{a,b,c} or foo::* while self.check(&token::ModSep) { - try!(self.bump()); + self.bump(); match self.token { token::Ident(..) => { @@ -5805,7 +5845,7 @@ impl<'a> Parser<'a> { // foo::bar::* token::BinOp(token::Star) => { - try!(self.bump()); + self.bump(); let path = ast::Path { span: mk_sp(lo, self.span.hi), global: false, @@ -5843,8 +5883,8 @@ impl<'a> Parser<'a> { Ok(P(spanned(lo, self.last_span.hi, ViewPathSimple(rename_to, path)))) } - fn parse_rename(&mut self) -> PResult> { - if try!(self.eat_keyword(keywords::As)) { + fn parse_rename(&mut self) -> PResult<'a, Option> { + if self.eat_keyword(keywords::As) { self.parse_ident().map(Some) } else { Ok(None) @@ -5853,7 +5893,7 @@ impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main /// entry point for the parser. - pub fn parse_crate_mod(&mut self) -> PResult { + pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> { let lo = self.span.lo; Ok(ast::Crate { attrs: try!(self.parse_inner_attributes()), @@ -5865,9 +5905,9 @@ impl<'a> Parser<'a> { } pub fn parse_optional_str(&mut self) - -> PResult)>> { + -> Option<(InternedString, + ast::StrStyle, + Option)> { let ret = match self.token { token::Literal(token::Str_(s), suf) => { (self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)), ast::CookedStr, suf) @@ -5875,14 +5915,14 @@ impl<'a> Parser<'a> { token::Literal(token::StrRaw(s, n), suf) => { (self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)), ast::RawStr(n), suf) } - _ => return Ok(None) + _ => return None }; - try!(self.bump()); - Ok(Some(ret)) + self.bump(); + Some(ret) } - pub fn parse_str(&mut self) -> PResult<(InternedString, StrStyle)> { - match try!(self.parse_optional_str()) { + pub fn parse_str(&mut self) -> PResult<'a, (InternedString, StrStyle)> { + match self.parse_optional_str() { Some((s, style, suf)) => { let sp = self.last_span; self.expect_no_suffix(sp, "string literal", suf); diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index b942954c1874a..242626154fc8c 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -495,9 +495,6 @@ macro_rules! declare_special_idents_and_keywords {( } fn mk_fresh_ident_interner() -> IdentInterner { - // The indices here must correspond to the numbers in - // special_idents, in Keyword to_name(), and in static - // constants below. let mut init_vec = Vec::new(); $(init_vec.push($si_str);)* $(init_vec.push($sk_str);)* diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 347b9f4574782..67817ee0740e6 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -856,8 +856,8 @@ impl<'a> State<'a> { indented: usize) -> io::Result<()> { self.bclose_maybe_open(span, indented, true) } - pub fn bclose_maybe_open (&mut self, span: codemap::Span, - indented: usize, close_box: bool) -> io::Result<()> { + pub fn bclose_maybe_open(&mut self, span: codemap::Span, + indented: usize, close_box: bool) -> io::Result<()> { try!(self.maybe_print_comment(span.hi)); try!(self.break_offset_if_not_bol(1, -(indented as isize))); try!(word(&mut self.s, "}")); @@ -2403,7 +2403,7 @@ impl<'a> State<'a> { } match *parameters { - ast::AngleBracketedParameters(ref data) => { + ast::PathParameters::AngleBracketed(ref data) => { try!(word(&mut self.s, "<")); let mut comma = false; @@ -2440,7 +2440,7 @@ impl<'a> State<'a> { try!(word(&mut self.s, ">")) } - ast::ParenthesizedParameters(ref data) => { + ast::PathParameters::Parenthesized(ref data) => { try!(word(&mut self.s, "(")); try!(self.commasep( Inconsistent, diff --git a/src/libsyntax/show_span.rs b/src/libsyntax/show_span.rs index 014c7b2a68f4f..5e3cd0773aa45 100644 --- a/src/libsyntax/show_span.rs +++ b/src/libsyntax/show_span.rs @@ -47,21 +47,21 @@ struct ShowSpanVisitor<'a> { impl<'a, 'v> Visitor<'v> for ShowSpanVisitor<'a> { fn visit_expr(&mut self, e: &ast::Expr) { if let Mode::Expression = self.mode { - self.span_diagnostic.span_note(e.span, "expression"); + self.span_diagnostic.span_warn(e.span, "expression"); } visit::walk_expr(self, e); } fn visit_pat(&mut self, p: &ast::Pat) { if let Mode::Pattern = self.mode { - self.span_diagnostic.span_note(p.span, "pattern"); + self.span_diagnostic.span_warn(p.span, "pattern"); } visit::walk_pat(self, p); } fn visit_ty(&mut self, t: &ast::Ty) { if let Mode::Type = self.mode { - self.span_diagnostic.span_note(t.span, "type"); + self.span_diagnostic.span_warn(t.span, "type"); } visit::walk_ty(self, t); } diff --git a/src/libsyntax/util/lev_distance.rs b/src/libsyntax/util/lev_distance.rs index 9bf96311122e0..e0796c34e57ef 100644 --- a/src/libsyntax/util/lev_distance.rs +++ b/src/libsyntax/util/lev_distance.rs @@ -8,50 +8,64 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use ast::Name; use std::cmp; +use parse::token::InternedString; -pub fn lev_distance(me: &str, t: &str) -> usize { - if me.is_empty() { return t.chars().count(); } - if t.is_empty() { return me.chars().count(); } +/// To find the Levenshtein distance between two strings +pub fn lev_distance(a: &str, b: &str) -> usize { + // cases which don't require further computation + if a.is_empty() { + return b.chars().count(); + } else if b.is_empty() { + return a.chars().count(); + } - let mut dcol: Vec<_> = (0..t.len() + 1).collect(); + let mut dcol: Vec<_> = (0..b.len() + 1).collect(); let mut t_last = 0; - for (i, sc) in me.chars().enumerate() { - + for (i, sc) in a.chars().enumerate() { let mut current = i; dcol[0] = current + 1; - for (j, tc) in t.chars().enumerate() { - + for (j, tc) in b.chars().enumerate() { let next = dcol[j + 1]; - if sc == tc { dcol[j + 1] = current; } else { dcol[j + 1] = cmp::min(current, next); dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1; } - current = next; t_last = j; } - } - - dcol[t_last + 1] + } dcol[t_last + 1] } -pub fn max_suggestion_distance(name: &str) -> usize { - use std::cmp::max; - // As a loose rule to avoid obviously incorrect suggestions, clamp the - // maximum edit distance we will accept for a suggestion to one third of - // the typo'd name's length. - max(name.len(), 3) / 3 +/// To find the best match for a given string from an iterator of names +/// As a loose rule to avoid the obviously incorrect suggestions, it takes +/// an optional limit for the maximum allowable edit distance, which defaults +/// to one-third of the given word +pub fn find_best_match_for_name<'a, T>(iter_names: T, + lookup: &str, + dist: Option) -> Option + where T: Iterator { + let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d); + iter_names + .filter_map(|name| { + let dist = lev_distance(lookup, &name.as_str()); + match dist <= max_dist { // filter the unwanted cases + true => Some((name.as_str(), dist)), + false => None, + } + }) + .min_by_key(|&(_, val)| val) // extract the tuple containing the minimum edit distance + .map(|(s, _)| s) // and return only the string } #[test] fn test_lev_distance() { - use std::char::{ from_u32, MAX }; + use std::char::{from_u32, MAX}; // Test bytelength agnosticity for c in (0..MAX as u32) .filter_map(|i| from_u32(i)) diff --git a/src/libsyntax/util/parser_testing.rs b/src/libsyntax/util/parser_testing.rs index a462dbeb6e4ed..454b925a4945e 100644 --- a/src/libsyntax/util/parser_testing.rs +++ b/src/libsyntax/util/parser_testing.rs @@ -30,10 +30,9 @@ pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> source_str) } -fn with_error_checking_parse(s: String, f: F) -> T where - F: FnOnce(&mut Parser) -> PResult, +fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T where + F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, { - let ps = ParseSess::new(); let mut p = string_to_parser(&ps, s); let x = panictry!(f(&mut p)); p.abort_if_errors(); @@ -42,28 +41,32 @@ fn with_error_checking_parse(s: String, f: F) -> T where /// Parse a string, return a crate. pub fn string_to_crate (source_str : String) -> ast::Crate { - with_error_checking_parse(source_str, |p| { + let ps = ParseSess::new(); + with_error_checking_parse(source_str, &ps, |p| { p.parse_crate_mod() }) } /// Parse a string, return an expr pub fn string_to_expr (source_str : String) -> P { - with_error_checking_parse(source_str, |p| { + let ps = ParseSess::new(); + with_error_checking_parse(source_str, &ps, |p| { p.parse_expr() }) } /// Parse a string, return an item pub fn string_to_item (source_str : String) -> Option> { - with_error_checking_parse(source_str, |p| { + let ps = ParseSess::new(); + with_error_checking_parse(source_str, &ps, |p| { p.parse_item() }) } /// Parse a string, return a stmt pub fn string_to_stmt(source_str : String) -> Option> { - with_error_checking_parse(source_str, |p| { + let ps = ParseSess::new(); + with_error_checking_parse(source_str, &ps, |p| { p.parse_stmt() }) } @@ -71,7 +74,8 @@ pub fn string_to_stmt(source_str : String) -> Option> { /// Parse a string, return a pat. Uses "irrefutable"... which doesn't /// (currently) affect parsing. pub fn string_to_pat(source_str: String) -> P { - with_error_checking_parse(source_str, |p| { + let ps = ParseSess::new(); + with_error_checking_parse(source_str, &ps, |p| { p.parse_pat() }) } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index b8dd54790ce51..9b102cd99f305 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -399,12 +399,12 @@ pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V, _path_span: Span, path_parameters: &'v PathParameters) { match *path_parameters { - AngleBracketedParameters(ref data) => { + PathParameters::AngleBracketed(ref data) => { walk_list!(visitor, visit_ty, &data.types); walk_list!(visitor, visit_lifetime, &data.lifetimes); walk_list!(visitor, visit_assoc_type_binding, &data.bindings); } - ParenthesizedParameters(ref data) => { + PathParameters::Parenthesized(ref data) => { walk_list!(visitor, visit_ty, &data.inputs); walk_list!(visitor, visit_ty, &data.output); } diff --git a/src/libsyntax_ext/asm.rs b/src/libsyntax_ext/asm.rs index e5c6a17c78ba2..2f50f610d2be4 100644 --- a/src/libsyntax_ext/asm.rs +++ b/src/libsyntax_ext/asm.rs @@ -113,7 +113,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) p.token != token::ModSep { if !outputs.is_empty() { - panictry!(p.eat(&token::Comma)); + p.eat(&token::Comma); } let (constraint, _str_style) = panictry!(p.parse_str()); @@ -159,7 +159,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) p.token != token::ModSep { if !inputs.is_empty() { - panictry!(p.eat(&token::Comma)); + p.eat(&token::Comma); } let (constraint, _str_style) = panictry!(p.parse_str()); @@ -183,7 +183,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) p.token != token::ModSep { if !clobs.is_empty() { - panictry!(p.eat(&token::Comma)); + p.eat(&token::Comma); } let (s, _str_style) = panictry!(p.parse_str()); @@ -210,7 +210,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } if p.token == token::Comma { - panictry!(p.eat(&token::Comma)); + p.eat(&token::Comma); } } StateNone => () @@ -222,12 +222,12 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) match (&p.token, state.next(), state.next().next()) { (&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => { - panictry!(p.bump()); + p.bump(); break 'statement; } (&token::Colon, st, _) | (&token::ModSep, _, st) => { - panictry!(p.bump()); + p.bump(); state = st; } (&token::Eof, _, _) => break 'statement, diff --git a/src/libsyntax_ext/cfg.rs b/src/libsyntax_ext/cfg.rs index ff0f139d82551..bae0462b8d330 100644 --- a/src/libsyntax_ext/cfg.rs +++ b/src/libsyntax_ext/cfg.rs @@ -28,7 +28,7 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt, let mut p = cx.new_parser_from_tts(tts); let cfg = panictry!(p.parse_meta_item()); - if !panictry!(p.eat(&token::Eof)){ + if !p.eat(&token::Eof) { cx.span_err(sp, "expected 1 cfg-pattern"); return DummyResult::expr(sp); } diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 24094f797e6c2..1fb2b55215ded 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -98,7 +98,7 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) let fmtstr = panictry!(p.parse_expr()); let mut named = false; while p.token != token::Eof { - if !panictry!(p.eat(&token::Comma)) { + if !p.eat(&token::Comma) { ecx.span_err(sp, "expected token: `,`"); return None; } @@ -107,7 +107,7 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) named = true; let ident = match p.token { token::Ident(i, _) => { - panictry!(p.bump()); + p.bump(); i } _ if named => { @@ -130,10 +130,11 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) match names.get(name) { None => {} Some(prev) => { - ecx.span_err(e.span, - &format!("duplicate argument named `{}`", - name)); - ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here"); + ecx.struct_span_err(e.span, + &format!("duplicate argument named `{}`", + name)) + .span_note(prev.span, "previously here") + .emit(); continue } } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index a032666595ebe..0f049fa979255 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -24,26 +24,13 @@ #![feature(str_char)] extern crate fmt_macros; +#[macro_use] extern crate syntax; use syntax::ext::base::{MacroExpanderFn, NormalTT}; use syntax::ext::base::{SyntaxEnv, SyntaxExtension}; use syntax::parse::token::intern; -// A variant of 'try!' that panics on Err(FatalError). This is used as a -// crutch on the way towards a non-panic!-prone parser. It should be used -// for fatal parsing errors; eventually we plan to convert all code using -// panictry to just use normal try -macro_rules! panictry { - ($e:expr) => ({ - use std::result::Result::{Ok, Err}; - use syntax::errors::FatalError; - match $e { - Ok(e) => e, - Err(FatalError) => panic!(FatalError) - } - }) -} mod asm; mod cfg; diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index a844bdf1351d9..2945d20ceb1b8 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -923,21 +923,21 @@ fn get_concurrency() -> usize { target_os = "netbsd"))] fn num_cpus() -> usize { let mut cpus: libc::c_uint = 0; - let mut CPUS_SIZE = std::mem::size_of_val(&cpus); + let mut cpus_size = std::mem::size_of_val(&cpus); let mut mib = [libc::CTL_HW, libc::HW_AVAILCPU, 0, 0]; unsafe { libc::sysctl(mib.as_mut_ptr(), 2, &mut cpus as *mut _ as *mut _, - &mut CPUS_SIZE as *mut _ as *mut _, + &mut cpus_size as *mut _ as *mut _, 0 as *mut _, 0); } if cpus < 1 { - mib[1] = HW_NCPU; + mib[1] = libc::HW_NCPU; unsafe { libc::sysctl(mib.as_mut_ptr(), 2, &mut cpus as *mut _ as *mut _, - &mut CPUS_SIZE as *mut _ as *mut _, + &mut cpus_size as *mut _ as *mut _, 0 as *mut _, 0); } if cpus < 1 { diff --git a/src/rt/rust_android_dummy.h b/src/rt/rust_android_dummy.h deleted file mode 100644 index d2329a46c831a..0000000000000 --- a/src/rt/rust_android_dummy.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef _RUST_ANDROID_DUMMY_H -#define _RUST_ANDROID_DUMMY_H - -int backtrace (void **__array, int __size); - -char **backtrace_symbols (void *__const *__array, int __size); - -void backtrace_symbols_fd (void *__const *__array, int __size, int __fd); - -#include - -struct stat; -typedef struct { - size_t gl_pathc; /* Count of total paths so far. */ - size_t gl_matchc; /* Count of paths matching pattern. */ - size_t gl_offs; /* Reserved at beginning of gl_pathv. */ - int gl_flags; /* Copy of flags parameter to glob. */ - char **gl_pathv; /* List of paths matching pattern. */ - /* Copy of errfunc parameter to glob. */ - int (*gl_errfunc)(const char *, int); - - /* - * Alternate filesystem access methods for glob; replacement - * versions of closedir(3), readdir(3), opendir(3), stat(2) - * and lstat(2). - */ - void (*gl_closedir)(void *); - struct dirent *(*gl_readdir)(void *); - void *(*gl_opendir)(const char *); - int (*gl_lstat)(const char *, struct stat *); -} glob_t; - -#endif diff --git a/src/snapshots.txt b/src/snapshots.txt index 81ff9b051721d..2fa540012816e 100644 --- a/src/snapshots.txt +++ b/src/snapshots.txt @@ -1,8 +1,12 @@ S 2015-12-18 3391630 + bitrig-x86_64 6476e1562df02389b55553b4c88b1f4fd121cd40 + freebsd-i386 7e624c50494402e1feb14c743d659fbd71b448f5 + freebsd-x86_64 91724d4e655807a2a2e940ac50992ebeaac16ea9 linux-i386 a09c4a4036151d0cb28e265101669731600e01f2 linux-x86_64 97e2a5eb8904962df8596e95d6e5d9b574d73bf4 macos-i386 ca52d2d3ba6497ed007705ee3401cf7efc136ca1 macos-x86_64 3c44ffa18f89567c2b81f8d695e711c86d81ffc7 + openbsd-x86_64 6c8aab2c8a169274942f9a15e460069a3ff64be9 winnt-i386 f9056ebd3db9611d31c2dc6dc5f96c7208d5d227 winnt-x86_64 a85a40e535d828016181d3aa40afe34c3e36ab8c diff --git a/src/test/auxiliary/default_ty_param_cross_crate_crate.rs b/src/test/auxiliary/default_ty_param_cross_crate_crate.rs index 270cfdcb7f651..4bd8ecacb96b3 100644 --- a/src/test/auxiliary/default_ty_param_cross_crate_crate.rs +++ b/src/test/auxiliary/default_ty_param_cross_crate_crate.rs @@ -10,6 +10,7 @@ #![crate_type = "lib"] #![crate_name = "default_param_test"] +#![feature(default_type_parameter_fallback)] use std::marker::PhantomData; diff --git a/src/test/compile-fail/dep-graph-caller-callee.rs b/src/test/compile-fail/dep-graph-caller-callee.rs new file mode 100644 index 0000000000000..acd6091cbdd48 --- /dev/null +++ b/src/test/compile-fail/dep-graph-caller-callee.rs @@ -0,0 +1,47 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that immediate callers have to change when callee changes, but +// not callers' callers. + +// compile-flags: -Z incr-comp + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +fn main() { } + +mod x { + #[rustc_if_this_changed] + pub fn x() { } +} + +mod y { + use x; + + // These dependencies SHOULD exist: + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK + #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK + pub fn y() { + x::x(); + } +} + +mod z { + use y; + + // These are expected to yield errors, because changes to `x` + // affect the BODY of `y`, but not its signature. + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path + #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path + pub fn z() { + y::y(); + } +} diff --git a/src/test/compile-fail/dep-graph-struct-signature.rs b/src/test/compile-fail/dep-graph-struct-signature.rs new file mode 100644 index 0000000000000..5cfb748b6f459 --- /dev/null +++ b/src/test/compile-fail/dep-graph-struct-signature.rs @@ -0,0 +1,100 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test cases where a changing struct appears in the signature of fns +// and methods. + +// compile-flags: -Z incr-comp + +#![feature(rustc_attrs)] +#![allow(dead_code)] +#![allow(unused_variables)] + +fn main() { } + +#[rustc_if_this_changed] +struct WillChange { + x: u32, + y: u32 +} + +struct WontChange { + x: u32, + y: u32 +} + +// these are valid dependencies +mod signatures { + use WillChange; + + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + trait Bar { + fn do_something(x: WillChange); + } + + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + fn some_fn(x: WillChange) { } + + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + fn new_foo(x: u32, y: u32) -> WillChange { + WillChange { x: x, y: y } + } + + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + impl WillChange { + fn new(x: u32, y: u32) -> WillChange { loop { } } + } + + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + impl WillChange { + fn method(&self, x: u32) { } + } + + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + struct WillChanges { + x: WillChange, + y: WillChange + } + + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + fn indirect(x: WillChanges) { } +} + +// these are invalid dependencies, though sometimes we create edges +// anyway. +mod invalid_signatures { + use WontChange; + + // FIXME due to the variance pass having overly conservative edges, + // we incorrectly think changes are needed here + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + trait A { + fn do_something_else_twice(x: WontChange); + } + + // FIXME due to the variance pass having overly conservative edges, + // we incorrectly think changes are needed here + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK + #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK + fn b(x: WontChange) { } + + #[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path from `WillChange` + #[rustc_then_this_would_need(CollectItem)] //~ ERROR no path from `WillChange` + fn c(x: u32) { } +} + diff --git a/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs b/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs new file mode 100644 index 0000000000000..57e83586d8d37 --- /dev/null +++ b/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs @@ -0,0 +1,54 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that adding an impl to a trait `Foo` DOES affect functions +// that only use `Bar` if they have methods in common. + +// compile-flags: -Z incr-comp + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +fn main() { } + +pub trait Foo: Sized { + fn method(self) { } +} + +pub trait Bar: Sized { + fn method(self) { } +} + +mod x { + use {Foo, Bar}; + + #[rustc_if_this_changed] + impl Foo for u32 { } + + impl Bar for char { } +} + +mod y { + use {Foo, Bar}; + + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK + pub fn with_char() { + char::method('a'); + } +} + +mod z { + use y; + + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path + pub fn z() { + y::with_char(); + } +} diff --git a/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs b/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs new file mode 100644 index 0000000000000..ba54a05620945 --- /dev/null +++ b/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs @@ -0,0 +1,54 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that adding an impl to a trait `Foo` does not affect functions +// that only use `Bar`, so long as they do not have methods in common. + +// compile-flags: -Z incr-comp + +#![feature(rustc_attrs)] +#![allow(warnings)] + +fn main() { } + +pub trait Foo: Sized { + fn foo(self) { } +} + +pub trait Bar: Sized { + fn bar(self) { } +} + +mod x { + use {Foo, Bar}; + + #[rustc_if_this_changed] + impl Foo for char { } + + impl Bar for char { } +} + +mod y { + use {Foo, Bar}; + + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path + pub fn call_bar() { + char::bar('a'); + } +} + +mod z { + use y; + + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path + pub fn z() { + y::call_bar(); + } +} diff --git a/src/test/compile-fail/dep-graph-trait-impl.rs b/src/test/compile-fail/dep-graph-trait-impl.rs new file mode 100644 index 0000000000000..83e924fe06d7d --- /dev/null +++ b/src/test/compile-fail/dep-graph-trait-impl.rs @@ -0,0 +1,77 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that when a trait impl changes, fns whose body uses that trait +// must also be recompiled. + +// compile-flags: -Z incr-comp + +#![feature(rustc_attrs)] +#![allow(warnings)] + +fn main() { } + +pub trait Foo: Sized { + fn method(self) { } +} + +mod x { + use Foo; + + #[rustc_if_this_changed] + impl Foo for char { } + + impl Foo for u32 { } +} + +mod y { + use Foo; + + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK + #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK + pub fn with_char() { + char::method('a'); + } + + // FIXME(#30741) tcx fulfillment cache not tracked + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path + #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path + pub fn take_foo_with_char() { + take_foo::('a'); + } + + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK + #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK + pub fn with_u32() { + u32::method(22); + } + + // FIXME(#30741) tcx fulfillment cache not tracked + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path + #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path + pub fn take_foo_with_u32() { + take_foo::(22); + } + + pub fn take_foo(t: T) { } +} + +mod z { + use y; + + // These are expected to yield errors, because changes to `x` + // affect the BODY of `y`, but not its signature. + #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path + #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path + pub fn z() { + y::with_char(); + y::with_u32(); + } +} diff --git a/src/test/compile-fail/dep-graph-unrelated.rs b/src/test/compile-fail/dep-graph-unrelated.rs new file mode 100644 index 0000000000000..8feec12a2f76c --- /dev/null +++ b/src/test/compile-fail/dep-graph-unrelated.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that two unrelated functions have no trans dependency. + +// compile-flags: -Z incr-comp + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +#[rustc_if_this_changed] +fn main() { } + +#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path from `main` +fn bar() { } diff --git a/src/test/compile-fail/issue-17740.rs b/src/test/compile-fail/issue-17740.rs index b4791eba76e26..4381bf22e2ace 100644 --- a/src/test/compile-fail/issue-17740.rs +++ b/src/test/compile-fail/issue-17740.rs @@ -15,12 +15,12 @@ struct Foo<'a> { impl <'a> Foo<'a>{ fn bar(self: &mut Foo) { //~^ mismatched types - //~| expected `Foo<'a>` - //~| found `Foo<'_>` + //~| expected `&mut Foo<'a>` + //~| found `&mut Foo<'_>` //~| lifetime mismatch //~| mismatched types - //~| expected `Foo<'a>` - //~| found `Foo<'_>` + //~| expected `&mut Foo<'a>` + //~| found `&mut Foo<'_>` //~| lifetime mismatch } } diff --git a/src/test/compile-fail/issue-17905.rs b/src/test/compile-fail/issue-17905.rs index eabdb36a7efc6..f11d482ea16db 100644 --- a/src/test/compile-fail/issue-17905.rs +++ b/src/test/compile-fail/issue-17905.rs @@ -16,10 +16,6 @@ impl Pair< isize > { fn say(self: &Pair<&str, isize>) { -//~^ ERROR mismatched types -//~| expected `Pair<&'static str, isize>` -//~| found `Pair<&str, isize>` -//~| lifetime mismatch println!("{}", self); } } diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs new file mode 100644 index 0000000000000..416eef4ad258f --- /dev/null +++ b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs @@ -0,0 +1,39 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo
{ + fn foo(&self, a: A) -> A { + a + } +} + +trait NotRelevant { + fn nr(&self, a: A) -> A { + a + } +} + +struct Bar; + +impl Foo for Bar {} + +impl Foo for Bar {} + +impl NotRelevant for Bar {} + +fn main() { + let f1 = Bar; + + f1.foo(1usize); + //~^ error: the trait `Foo` is not implemented for the type `Bar` + // | help: the following implementations were found: + // | help: implementation 1: `Foo` + // | help: implementation 2: `Foo` +} diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs new file mode 100644 index 0000000000000..07a7c98dd7ff6 --- /dev/null +++ b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs @@ -0,0 +1,46 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + fn foo(&self, a: A) -> A { + a + } +} + +trait NotRelevant { + fn nr(&self, a: A) -> A { + a + } +} + +struct Bar; + +impl Foo for Bar {} +impl Foo for Bar {} +impl Foo for Bar {} + +impl Foo for Bar {} +impl Foo for Bar {} +impl Foo for Bar {} + +impl NotRelevant for Bar {} + +fn main() { + let f1 = Bar; + + f1.foo(1usize); + //~^ error: the trait `Foo` is not implemented for the type `Bar` + // | help: the following implementations were found: + // | help: Foo + // | help: Foo + // | help: Foo + // | help: Foo + // | help: and 2 others +} diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs new file mode 100644 index 0000000000000..0bb944edb9d84 --- /dev/null +++ b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs @@ -0,0 +1,34 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + fn foo(&self, a: A) -> A { + a + } +} + +trait NotRelevant { + fn nr(&self, a: A) -> A { + a + } +} + +struct Bar; + +impl NotRelevant for Bar {} + +fn main() { + let f1 = Bar; + + f1.foo(1usize); + //~^ error: method named `foo` found for type `Bar` in the current scope + //~| help: items from traits can only be used if the trait is implemented and in scope + //~| help: candidate #1: `Foo` +} diff --git a/src/test/compile-fail/issue-26194.rs b/src/test/compile-fail/issue-26194.rs index b5c875cc4cbd2..ef91188c5d166 100644 --- a/src/test/compile-fail/issue-26194.rs +++ b/src/test/compile-fail/issue-26194.rs @@ -12,7 +12,7 @@ struct S(String); impl S { fn f(self: *mut S) -> String { self.0 } - //~^ ERROR mismatched self type + //~^ ERROR mismatched method receiver } fn main() { S("".to_owned()).f(); } diff --git a/src/test/compile-fail/issue-26812.rs b/src/test/compile-fail/issue-26812.rs index c1ccfe269cdd3..060a66846d36f 100644 --- a/src/test/compile-fail/issue-26812.rs +++ b/src/test/compile-fail/issue-26812.rs @@ -8,5 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(default_type_parameter_fallback)] + fn avg(_: T) {} //~ ERROR associated type `Item` not found for `T` fn main() {} diff --git a/src/test/compile-fail/issue-30715.rs b/src/test/compile-fail/issue-30715.rs new file mode 100644 index 0000000000000..16761905cb983 --- /dev/null +++ b/src/test/compile-fail/issue-30715.rs @@ -0,0 +1,26 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! parallel { + ( + for $id:ident in $iter:expr { + $( $inner:expr; )* + } + ) => {}; +} + + +fn main() { + parallel! { + for i in 0..n { + x += i; //~ ERROR no rules expected the token `+=` + } + } +} diff --git a/src/test/compile-fail/macro-context.rs b/src/test/compile-fail/macro-context.rs index af33dbb972ec1..8fa5e0a70890e 100644 --- a/src/test/compile-fail/macro-context.rs +++ b/src/test/compile-fail/macro-context.rs @@ -13,7 +13,6 @@ // (typeof used because it's surprisingly hard to find an unparsed token after a stmt) macro_rules! m { () => ( i ; typeof ); //~ ERROR `typeof` is a reserved keyword - //~| ERROR macro expansion ignores token `typeof` //~| ERROR macro expansion ignores token `typeof` //~| ERROR macro expansion ignores token `;` //~| ERROR macro expansion ignores token `;` @@ -29,5 +28,5 @@ fn main() { m!() => {} //~ NOTE the usage of `m!` is likely invalid in pattern context } - m!(); //~ NOTE the usage of `m!` is likely invalid in statement context + m!(); } diff --git a/src/test/parse-fail/assoc-oddities-3.rs b/src/test/compile-fail/malformed_macro_lhs.rs similarity index 74% rename from src/test/parse-fail/assoc-oddities-3.rs rename to src/test/compile-fail/malformed_macro_lhs.rs index 0d4f21f0dca26..88e19af2eea07 100644 --- a/src/test/parse-fail/assoc-oddities-3.rs +++ b/src/test/compile-fail/malformed_macro_lhs.rs @@ -8,9 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +macro_rules! my_precioooous { + $($t:tt)* => (1); +} -fn that_odd_parse() { - // see assoc-oddities-1 for explanation - x + if c { a } else { b }[n]; //~ ERROR expected one of +fn main() { + my_precioooous!(); //~ ERROR malformed macro lhs } diff --git a/src/test/compile-fail/type-parameter-invalid-lint.rs b/src/test/compile-fail/type-parameter-invalid-lint.rs new file mode 100644 index 0000000000000..a3ecbfa84f0a8 --- /dev/null +++ b/src/test/compile-fail/type-parameter-invalid-lint.rs @@ -0,0 +1,17 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(future_incompatible)] +#![allow(dead_code)] + +fn avg(_: T) {} +//~^ ERROR defaults for type parameters are only allowed +//~| NOTE HARD ERROR +fn main() {} diff --git a/src/test/compile-fail/ufcs-explicit-self-bad.rs b/src/test/compile-fail/ufcs-explicit-self-bad.rs index 5d2b5fa52db41..e54a7623cb0c3 100644 --- a/src/test/compile-fail/ufcs-explicit-self-bad.rs +++ b/src/test/compile-fail/ufcs-explicit-self-bad.rs @@ -15,7 +15,7 @@ struct Foo { } impl Foo { - fn foo(self: isize, x: isize) -> isize { //~ ERROR mismatched self type + fn foo(self: isize, x: isize) -> isize { //~ ERROR mismatched method receiver self.f + x } } @@ -25,10 +25,10 @@ struct Bar { } impl Bar { - fn foo(self: Bar, x: isize) -> isize { //~ ERROR mismatched self type + fn foo(self: Bar, x: isize) -> isize { //~ ERROR mismatched method receiver x } - fn bar(self: &Bar, x: isize) -> isize { //~ ERROR mismatched self type + fn bar(self: &Bar, x: isize) -> isize { //~ ERROR mismatched method receiver x } } @@ -41,15 +41,16 @@ trait SomeTrait { impl<'a, T> SomeTrait for &'a Bar { fn dummy1(self: &&'a Bar) { } - fn dummy2(self: &Bar) {} //~ ERROR mismatched self type + fn dummy2(self: &Bar) {} //~ ERROR mismatched types + //~^ ERROR mismatched types fn dummy3(self: &&Bar) {} //~^ ERROR mismatched types - //~| expected `&'a Bar` - //~| found `&Bar` + //~| expected `&&'a Bar` + //~| found `&&Bar` //~| lifetime mismatch //~| ERROR mismatched types - //~| expected `&'a Bar` - //~| found `&Bar` + //~| expected `&&'a Bar` + //~| found `&&Bar` //~| lifetime mismatch } diff --git a/src/test/compile-fail/unresolved-import.rs b/src/test/compile-fail/unresolved-import.rs index 5edcf1d99a02b..b6207450d9835 100644 --- a/src/test/compile-fail/unresolved-import.rs +++ b/src/test/compile-fail/unresolved-import.rs @@ -8,24 +8,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-tidy-linelength + use foo::bar; //~ ERROR unresolved import `foo::bar`. Maybe a missing `extern crate foo`? -use bar::baz as x; //~ ERROR unresolved import `bar::baz`. There is no `baz` in `bar` +use bar::Baz as x; //~ ERROR unresolved import `bar::Baz`. There is no `Baz` in `bar`. Did you mean to use `Bar`? -use food::baz; //~ ERROR unresolved import `food::baz`. There is no `baz` in `food` +use food::baz; //~ ERROR unresolved import `food::baz`. There is no `baz` in `food`. Did you mean to use the re-exported import `bag`? -use food::{quux as beans}; //~ ERROR unresolved import `food::quux`. There is no `quux` in `food` +use food::{beens as Foo}; //~ ERROR unresolved import `food::beens`. There is no `beens` in `food`. Did you mean to use the re-exported import `beans`? mod bar { - struct bar; + pub struct Bar; } mod food { - pub use self::zug::baz::{self as bag, quux as beans}; + pub use self::zug::baz::{self as bag, foobar as beans}; mod zug { pub mod baz { - pub struct quux; + pub struct foobar; } } } diff --git a/src/test/parse-fail/obsolete-proc.rs b/src/test/parse-fail/obsolete-proc.rs index 987eae66f3e63..1ef8cd2714d7f 100644 --- a/src/test/parse-fail/obsolete-proc.rs +++ b/src/test/parse-fail/obsolete-proc.rs @@ -14,6 +14,7 @@ fn foo(p: proc()) { } //~ ERROR `proc` is a reserved keyword -fn bar() { proc() 1; } +fn bar() { proc() 1; } //~ ERROR `proc` is a reserved keyword + //~^ ERROR expected fn main() { } diff --git a/src/test/compile-fail/pub-item-macro.rs b/src/test/parse-fail/pub-item-macro.rs similarity index 100% rename from src/test/compile-fail/pub-item-macro.rs rename to src/test/parse-fail/pub-item-macro.rs diff --git a/src/test/parse-fail/pub-macro-rules.rs b/src/test/parse-fail/pub-macro-rules.rs new file mode 100644 index 0000000000000..93b992f2f8af2 --- /dev/null +++ b/src/test/parse-fail/pub-macro-rules.rs @@ -0,0 +1,27 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] mod bleh { + pub macro_rules! foo { //~ ERROR can't qualify macro_rules invocation with `pub` + //~^ HELP did you mean #[macro_export]? + ($n:ident) => ( + fn $n () -> i32 { + 1 + } + ) + } + +} + +foo!(meh); + +fn main() { + println!("{}", meh()); +} diff --git a/src/test/compile-fail/pub-method-macro.rs b/src/test/parse-fail/pub-method-macro.rs similarity index 100% rename from src/test/compile-fail/pub-method-macro.rs rename to src/test/parse-fail/pub-method-macro.rs diff --git a/src/test/run-fail/mir_indexing_oob_1.rs b/src/test/run-fail/mir_indexing_oob_1.rs new file mode 100644 index 0000000000000..e0d20a20577a8 --- /dev/null +++ b/src/test/run-fail/mir_indexing_oob_1.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:index out of bounds: the len is 5 but the index is 10 +#![feature(rustc_attrs)] + +const C: [u32; 5] = [0; 5]; + +#[rustc_mir] +fn test() -> u32 { + C[10] +} + +fn main() { + test(); +} diff --git a/src/test/run-fail/mir_indexing_oob_2.rs b/src/test/run-fail/mir_indexing_oob_2.rs new file mode 100644 index 0000000000000..6c65be5769f2d --- /dev/null +++ b/src/test/run-fail/mir_indexing_oob_2.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:index out of bounds: the len is 5 but the index is 10 +#![feature(rustc_attrs)] + +const C: &'static [u8; 5] = b"hello"; + +#[rustc_mir] +fn test() -> u8 { + C[10] +} + +fn main() { + test(); +} diff --git a/src/test/run-fail/mir_indexing_oob_3.rs b/src/test/run-fail/mir_indexing_oob_3.rs new file mode 100644 index 0000000000000..5f3fc9376b0d3 --- /dev/null +++ b/src/test/run-fail/mir_indexing_oob_3.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:index out of bounds: the len is 5 but the index is 10 +#![feature(rustc_attrs)] + +const C: &'static [u8; 5] = b"hello"; + +#[rustc_mir] +fn mir() -> u8 { + C[10] +} + +fn main() { + mir(); +} diff --git a/src/test/run-fail/mir_trans_calls_converging_drops.rs b/src/test/run-fail/mir_trans_calls_converging_drops.rs new file mode 100644 index 0000000000000..754f616cfd5a0 --- /dev/null +++ b/src/test/run-fail/mir_trans_calls_converging_drops.rs @@ -0,0 +1,37 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:converging_fn called +// error-pattern:0 dropped +// error-pattern:exit + +use std::io::{self, Write}; + +struct Droppable(u8); +impl Drop for Droppable { + fn drop(&mut self) { + write!(io::stderr(), "{} dropped\n", self.0); + } +} + +fn converging_fn() { + write!(io::stderr(), "converging_fn called\n"); +} + +#[rustc_mir] +fn mir(d: Droppable) { + converging_fn(); +} + +fn main() { + let d = Droppable(0); + mir(d); + panic!("exit"); +} diff --git a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs new file mode 100644 index 0000000000000..5e870be3bc6ec --- /dev/null +++ b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs @@ -0,0 +1,41 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:complex called +// error-pattern:dropped +// error-pattern:exit + +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + write!(io::stderr(), "dropped\n"); + } +} + +// return value of this function is copied into the return slot +fn complex() -> u64 { + write!(io::stderr(), "complex called\n"); + 42 +} + + +#[rustc_mir] +fn mir() -> u64 { + let x = Droppable; + return complex(); + drop(x); +} + +pub fn main() { + assert_eq!(mir(), 42); + panic!("exit"); +} diff --git a/src/test/run-fail/mir_trans_calls_diverging.rs b/src/test/run-fail/mir_trans_calls_diverging.rs new file mode 100644 index 0000000000000..fcd8ab26a0a88 --- /dev/null +++ b/src/test/run-fail/mir_trans_calls_diverging.rs @@ -0,0 +1,24 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:diverging_fn called + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +#[rustc_mir] +fn mir() { + diverging_fn(); +} + +fn main() { + mir(); +} diff --git a/src/test/run-fail/mir_trans_calls_diverging_drops.rs b/src/test/run-fail/mir_trans_calls_diverging_drops.rs new file mode 100644 index 0000000000000..ffa1ff0827775 --- /dev/null +++ b/src/test/run-fail/mir_trans_calls_diverging_drops.rs @@ -0,0 +1,34 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:diverging_fn called +// error-pattern:0 dropped +use std::io::{self, Write}; + +struct Droppable(u8); +impl Drop for Droppable { + fn drop(&mut self) { + write!(io::stderr(), "{} dropped", self.0); + } +} + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +#[rustc_mir] +fn mir(d: Droppable) { + diverging_fn(); +} + +fn main() { + let d = Droppable(0); + mir(d); +} diff --git a/src/test/run-fail/mir_trans_no_landing_pads.rs b/src/test/run-fail/mir_trans_no_landing_pads.rs new file mode 100644 index 0000000000000..bc913fdab1c07 --- /dev/null +++ b/src/test/run-fail/mir_trans_no_landing_pads.rs @@ -0,0 +1,36 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// compile-flags: -Z no-landing-pads +// error-pattern:converging_fn called +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + ::std::process::exit(1) + } +} + +fn converging_fn() { + panic!("converging_fn called") +} + +#[rustc_mir] +fn mir(d: Droppable) { + let x = Droppable; + converging_fn(); + drop(x); + drop(d); +} + +fn main() { + mir(Droppable); +} diff --git a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs new file mode 100644 index 0000000000000..d97eb8c89e3e0 --- /dev/null +++ b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs @@ -0,0 +1,36 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// compile-flags: -Z no-landing-pads +// error-pattern:diverging_fn called +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + ::std::process::exit(1) + } +} + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +#[rustc_mir] +fn mir(d: Droppable) { + let x = Droppable; + diverging_fn(); + drop(x); + drop(d); +} + +fn main() { + mir(Droppable); +} diff --git a/src/test/run-fail/panic-set-handler.rs b/src/test/run-fail/panic-set-handler.rs new file mode 100644 index 0000000000000..bfeb407dd25a4 --- /dev/null +++ b/src/test/run-fail/panic-set-handler.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:greetings from the panic handler + +#![feature(std_panic, panic_handler)] +use std::panic; +use std::io::{self, Write}; + +fn main() { + panic::set_handler(|i| { + write!(io::stderr(), "greetings from the panic handler"); + }); + panic!("foobar"); +} diff --git a/src/test/run-fail/panic-set-unset-handler.rs b/src/test/run-fail/panic-set-unset-handler.rs new file mode 100644 index 0000000000000..6999aa715e791 --- /dev/null +++ b/src/test/run-fail/panic-set-unset-handler.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '

' panicked at 'foobar' + +#![feature(std_panic, panic_handler)] +use std::panic; +use std::io::{self, Write}; + +fn main() { + panic::set_handler(|i| { + write!(io::stderr(), "greetings from the panic handler"); + }); + panic::take_handler(); + panic!("foobar"); +} diff --git a/src/test/run-fail/panic-take-handler-nop.rs b/src/test/run-fail/panic-take-handler-nop.rs new file mode 100644 index 0000000000000..fec1db24adf09 --- /dev/null +++ b/src/test/run-fail/panic-take-handler-nop.rs @@ -0,0 +1,19 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'foobar' + +#![feature(std_panic, panic_handler)] +use std::panic; + +fn main() { + panic::take_handler(); + panic!("foobar"); +} diff --git a/src/test/run-make/simd-ffi/simd.rs b/src/test/run-make/simd-ffi/simd.rs index c0c4b1e7f3f31..49fec6f3619e4 100644 --- a/src/test/run-make/simd-ffi/simd.rs +++ b/src/test/run-make/simd-ffi/simd.rs @@ -47,11 +47,11 @@ extern { fn integer(a: i32x4, b: i32x4) -> i32x4; // vmaxq_s32 - #[cfg(any(target_arch = "arm"))] + #[cfg(target_arch = "arm")] #[link_name = "llvm.arm.neon.vmaxs.v4i32"] fn integer(a: i32x4, b: i32x4) -> i32x4; // vmaxq_s32 - #[cfg(any(target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] #[link_name = "llvm.aarch64.neon.maxs.v4i32"] fn integer(a: i32x4, b: i32x4) -> i32x4; diff --git a/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs index 7c1a45d020b22..b64e5778d9033 100644 --- a/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs +++ b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs @@ -37,34 +37,36 @@ pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> source_str) } -fn with_error_checking_parse(s: String, f: F) -> PResult where - F: FnOnce(&mut Parser) -> PResult, +fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> PResult<'a, T> where + F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, { - let ps = ParseSess::new(); let mut p = string_to_parser(&ps, s); let x = f(&mut p); if ps.span_diagnostic.has_errors() || p.token != token::Eof { + if let Err(mut e) = x { + e.cancel(); + } return Err(p.fatal("parse error")); } x } -fn expr(s: &str) -> PResult> { - with_error_checking_parse(s.to_string(), |p| { +fn expr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P> { + with_error_checking_parse(s.to_string(), ps, |p| { p.parse_expr() }) } -fn stmt(s: &str) -> PResult> { - with_error_checking_parse(s.to_string(), |p| { +fn stmt<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P> { + with_error_checking_parse(s.to_string(), ps, |p| { p.parse_stmt().map(|s| s.unwrap()) }) } -fn attr(s: &str) -> PResult { - with_error_checking_parse(s.to_string(), |p| { +fn attr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Attribute> { + with_error_checking_parse(s.to_string(), ps, |p| { p.parse_attribute(true) }) } @@ -79,29 +81,39 @@ fn str_compare String>(e: &str, expected: &[T], actual: &[T], f: } fn check_expr_attrs(es: &str, expected: &[&str]) { - let e = expr(es).expect("parse error"); + let ps = ParseSess::new(); + let e = expr(es, &ps).expect("parse error"); let actual = &e.attrs; str_compare(es, - &expected.iter().map(|r| attr(r).unwrap()).collect::>(), + &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::>(), actual.as_attr_slice(), pprust::attribute_to_string); } fn check_stmt_attrs(es: &str, expected: &[&str]) { - let e = stmt(es).expect("parse error"); + let ps = ParseSess::new(); + let e = stmt(es, &ps).expect("parse error"); let actual = e.node.attrs(); str_compare(es, - &expected.iter().map(|r| attr(r).unwrap()).collect::>(), + &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::>(), actual, pprust::attribute_to_string); } fn reject_expr_parse(es: &str) { - assert!(expr(es).is_err(), "parser did not reject `{}`", es); + let ps = ParseSess::new(); + match expr(es, &ps) { + Ok(_) => panic!("parser did not reject `{}`", es), + Err(mut e) => e.cancel(), + }; } fn reject_stmt_parse(es: &str) { - assert!(stmt(es).is_err(), "parser did not reject `{}`", es); + let ps = ParseSess::new(); + match stmt(es, &ps) { + Ok(_) => panic!("parser did not reject `{}`", es), + Err(mut e) => e.cancel(), + }; } fn main() { diff --git a/src/test/run-pass-valgrind/exit-flushes.rs b/src/test/run-pass-valgrind/exit-flushes.rs index 76ecbfd2f223d..632693dd728c3 100644 --- a/src/test/run-pass-valgrind/exit-flushes.rs +++ b/src/test/run-pass-valgrind/exit-flushes.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-macos this needs valgrind 3.11 or higher; see +// https://github.com/rust-lang/rust/pull/30365#issuecomment-165763679 + use std::env; use std::process::{exit, Command}; diff --git a/src/test/run-pass/assoc-oddities-3.rs b/src/test/run-pass/assoc-oddities-3.rs new file mode 100644 index 0000000000000..4913816ffe679 --- /dev/null +++ b/src/test/run-pass/assoc-oddities-3.rs @@ -0,0 +1,21 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn that_odd_parse(c: bool, n: usize) -> u32 { + let x = 2; + let a = [1, 2, 3, 4]; + let b = [5, 6, 7, 7]; + x + if c { a } else { b }[n] +} + +fn main() { + assert_eq!(4, that_odd_parse(true, 1)); + assert_eq!(8, that_odd_parse(false, 1)); +} diff --git a/src/test/run-pass/issue-28777.rs b/src/test/run-pass/issue-28777.rs new file mode 100644 index 0000000000000..ea5d4e4f3c27b --- /dev/null +++ b/src/test/run-pass/issue-28777.rs @@ -0,0 +1,30 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let v1 = { 1 + {2} * {3} }; + let v2 = 1 + {2} * {3} ; + + assert_eq!(7, v1); + assert_eq!(7, v2); + + let v3; + v3 = { 1 + {2} * {3} }; + let v4; + v4 = 1 + {2} * {3}; + assert_eq!(7, v3); + assert_eq!(7, v4); + + let v5 = { 1 + {2} * 3 }; + assert_eq!(7, v5); + + let v9 = { 1 + if 1 > 2 {1} else {2} * {3} }; + assert_eq!(7, v9); +} diff --git a/src/test/run-pass/mir_build_match_comparisons.rs b/src/test/run-pass/mir_build_match_comparisons.rs new file mode 100644 index 0000000000000..19266191d445d --- /dev/null +++ b/src/test/run-pass/mir_build_match_comparisons.rs @@ -0,0 +1,55 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +#[rustc_mir] +pub fn test1(x: i8) -> i32 { + match x { + 1...10 => 0, + _ => 1, + } +} + +const U: Option = Some(10); +const S: &'static str = "hello"; + +#[rustc_mir] +pub fn test2(x: i8) -> i32 { + match Some(x) { + U => 0, + _ => 1, + } +} + +#[rustc_mir] +pub fn test3(x: &'static str) -> i32 { + match x { + S => 0, + _ => 1, + } +} + +fn main() { + assert_eq!(test1(0), 1); + assert_eq!(test1(1), 0); + assert_eq!(test1(2), 0); + assert_eq!(test1(5), 0); + assert_eq!(test1(9), 0); + assert_eq!(test1(10), 0); + assert_eq!(test1(11), 1); + assert_eq!(test1(20), 1); + assert_eq!(test2(10), 0); + assert_eq!(test2(0), 1); + assert_eq!(test2(20), 1); + assert_eq!(test3("hello"), 0); + assert_eq!(test3(""), 1); + assert_eq!(test3("world"), 1); +} diff --git a/src/test/run-pass/mir_match_arm_guard.rs b/src/test/run-pass/mir_match_arm_guard.rs new file mode 100644 index 0000000000000..fb177ba7b2bb0 --- /dev/null +++ b/src/test/run-pass/mir_match_arm_guard.rs @@ -0,0 +1,28 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// #30527 - We were not generating arms with guards in certain cases. + +#![feature(rustc_attrs)] + +#[rustc_mir] +fn match_with_guard(x: Option) -> i8 { + match x { + Some(xyz) if xyz > 100 => 0, + Some(_) => -1, + None => -2 + } +} + +fn main() { + assert_eq!(match_with_guard(Some(111)), 0); + assert_eq!(match_with_guard(Some(2)), -1); + assert_eq!(match_with_guard(None), -2); +} diff --git a/src/test/run-pass/mir_misc_casts.rs b/src/test/run-pass/mir_misc_casts.rs new file mode 100644 index 0000000000000..0799ffebe69e5 --- /dev/null +++ b/src/test/run-pass/mir_misc_casts.rs @@ -0,0 +1,349 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(libc, rustc_attrs)] + +extern crate libc; + +fn func(){} + +const STR: &'static str = "hello"; +const BSTR: &'static [u8; 5] = b"hello"; + +#[rustc_mir] +fn from_ptr() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, *const ()) { + let f = 1_usize as *const libc::FILE; + let c1 = f as isize; + let c2 = f as usize; + let c3 = f as i8; + let c4 = f as i16; + let c5 = f as i32; + let c6 = f as i64; + let c7 = f as u8; + let c8 = f as u16; + let c9 = f as u32; + let c10 = f as u64; + let c11 = f as *const (); + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) +} + +#[rustc_mir] +fn from_1() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1 as isize; + let c2 = 1 as usize; + let c3 = 1 as i8; + let c4 = 1 as i16; + let c5 = 1 as i32; + let c6 = 1 as i64; + let c7 = 1 as u8; + let c8 = 1 as u16; + let c9 = 1 as u32; + let c10 = 1 as u64; + let c11 = 1 as f32; + let c12 = 1 as f64; + let c13 = 1 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1usize() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_usize as isize; + let c2 = 1_usize as usize; + let c3 = 1_usize as i8; + let c4 = 1_usize as i16; + let c5 = 1_usize as i32; + let c6 = 1_usize as i64; + let c7 = 1_usize as u8; + let c8 = 1_usize as u16; + let c9 = 1_usize as u32; + let c10 = 1_usize as u64; + let c11 = 1_usize as f32; + let c12 = 1_usize as f64; + let c13 = 1_usize as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1isize() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_isize as isize; + let c2 = 1_isize as usize; + let c3 = 1_isize as i8; + let c4 = 1_isize as i16; + let c5 = 1_isize as i32; + let c6 = 1_isize as i64; + let c7 = 1_isize as u8; + let c8 = 1_isize as u16; + let c9 = 1_isize as u32; + let c10 = 1_isize as u64; + let c11 = 1_isize as f32; + let c12 = 1_isize as f64; + let c13 = 1_isize as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1u8() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_u8 as isize; + let c2 = 1_u8 as usize; + let c3 = 1_u8 as i8; + let c4 = 1_u8 as i16; + let c5 = 1_u8 as i32; + let c6 = 1_u8 as i64; + let c7 = 1_u8 as u8; + let c8 = 1_u8 as u16; + let c9 = 1_u8 as u32; + let c10 = 1_u8 as u64; + let c11 = 1_u8 as f32; + let c12 = 1_u8 as f64; + let c13 = 1_u8 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1i8() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_i8 as isize; + let c2 = 1_i8 as usize; + let c3 = 1_i8 as i8; + let c4 = 1_i8 as i16; + let c5 = 1_i8 as i32; + let c6 = 1_i8 as i64; + let c7 = 1_i8 as u8; + let c8 = 1_i8 as u16; + let c9 = 1_i8 as u32; + let c10 = 1_i8 as u64; + let c11 = 1_i8 as f32; + let c12 = 1_i8 as f64; + let c13 = 1_i8 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1u16() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_u16 as isize; + let c2 = 1_u16 as usize; + let c3 = 1_u16 as i8; + let c4 = 1_u16 as i16; + let c5 = 1_u16 as i32; + let c6 = 1_u16 as i64; + let c7 = 1_u16 as u8; + let c8 = 1_u16 as u16; + let c9 = 1_u16 as u32; + let c10 = 1_u16 as u64; + let c11 = 1_u16 as f32; + let c12 = 1_u16 as f64; + let c13 = 1_u16 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1i16() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_i16 as isize; + let c2 = 1_i16 as usize; + let c3 = 1_i16 as i8; + let c4 = 1_i16 as i16; + let c5 = 1_i16 as i32; + let c6 = 1_i16 as i64; + let c7 = 1_i16 as u8; + let c8 = 1_i16 as u16; + let c9 = 1_i16 as u32; + let c10 = 1_i16 as u64; + let c11 = 1_i16 as f32; + let c12 = 1_i16 as f64; + let c13 = 1_i16 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1u32() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_u32 as isize; + let c2 = 1_u32 as usize; + let c3 = 1_u32 as i8; + let c4 = 1_u32 as i16; + let c5 = 1_u32 as i32; + let c6 = 1_u32 as i64; + let c7 = 1_u32 as u8; + let c8 = 1_u32 as u16; + let c9 = 1_u32 as u32; + let c10 = 1_u32 as u64; + let c11 = 1_u32 as f32; + let c12 = 1_u32 as f64; + let c13 = 1_u32 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1i32() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_i32 as isize; + let c2 = 1_i32 as usize; + let c3 = 1_i32 as i8; + let c4 = 1_i32 as i16; + let c5 = 1_i32 as i32; + let c6 = 1_i32 as i64; + let c7 = 1_i32 as u8; + let c8 = 1_i32 as u16; + let c9 = 1_i32 as u32; + let c10 = 1_i32 as u64; + let c11 = 1_i32 as f32; + let c12 = 1_i32 as f64; + let c13 = 1_i32 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1u64() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_u64 as isize; + let c2 = 1_u64 as usize; + let c3 = 1_u64 as i8; + let c4 = 1_u64 as i16; + let c5 = 1_u64 as i32; + let c6 = 1_u64 as i64; + let c7 = 1_u64 as u8; + let c8 = 1_u64 as u16; + let c9 = 1_u64 as u32; + let c10 = 1_u64 as u64; + let c11 = 1_u64 as f32; + let c12 = 1_u64 as f64; + let c13 = 1_u64 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_1i64() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { + let c1 = 1_i64 as isize; + let c2 = 1_i64 as usize; + let c3 = 1_i64 as i8; + let c4 = 1_i64 as i16; + let c5 = 1_i64 as i32; + let c6 = 1_i64 as i64; + let c7 = 1_i64 as u8; + let c8 = 1_i64 as u16; + let c9 = 1_i64 as u32; + let c10 = 1_i64 as u64; + let c11 = 1_i64 as f32; + let c12 = 1_i64 as f64; + let c13 = 1_i64 as *const libc::FILE; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) +} + +#[rustc_mir] +fn from_bool() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64) { + let c1 = true as isize; + let c2 = true as usize; + let c3 = true as i8; + let c4 = true as i16; + let c5 = true as i32; + let c6 = true as i64; + let c7 = true as u8; + let c8 = true as u16; + let c9 = true as u32; + let c10 = true as u64; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) +} + +#[rustc_mir] +fn from_1f32() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64) { + let c1 = 1.0_f32 as isize; + let c2 = 1.0_f32 as usize; + let c3 = 1.0_f32 as i8; + let c4 = 1.0_f32 as i16; + let c5 = 1.0_f32 as i32; + let c6 = 1.0_f32 as i64; + let c7 = 1.0_f32 as u8; + let c8 = 1.0_f32 as u16; + let c9 = 1.0_f32 as u32; + let c10 = 1.0_f32 as u64; + let c11 = 1.0_f32 as f32; + let c12 = 1.0_f32 as f64; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) +} + +#[rustc_mir] +fn from_1f64() +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64) { + let c1 = 1.0f64 as isize; + let c2 = 1.0f64 as usize; + let c3 = 1.0f64 as i8; + let c4 = 1.0f64 as i16; + let c5 = 1.0f64 as i32; + let c6 = 1.0f64 as i64; + let c7 = 1.0f64 as u8; + let c8 = 1.0f64 as u16; + let c9 = 1.0f64 as u32; + let c10 = 1.0f64 as u64; + let c11 = 1.0f64 as f32; + let c12 = 1.0f64 as f64; + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) +} + +#[rustc_mir] +fn other_casts() +-> (*const u8, *const isize, *const u8, *const u8) { + let c1 = func as *const u8; + let c2 = c1 as *const isize; + + let r = &42u32; + let _ = r as *const u32; + + // fat-ptr -> fat-ptr -> fat-raw-ptr -> thin-ptr + let c3 = STR as &str as *const str as *const u8; + + let c4 = BSTR as *const [u8] as *const [u16] as *const u8; + (c1, c2, c3, c4) +} + +pub fn assert_eq_13(l: (isize, usize, i8, i16, i32, i64, u8, + u16, u32, u64, f32, f64, *const libc::FILE), + r: (isize, usize, i8, i16, i32, i64, u8, + u16, u32, u64, f32, f64, *const libc::FILE)) -> bool { + let (l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13) = l; + let (r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13) = r; + l1 == r1 && l2 == r2 && l3 == r3 && l4 == r4 && l5 == r5 && l6 == r6 && l7 == r7 && + l8 == r8 && l9 == r9 && l10 == r10 && l11 == r11 && l12 == r12 && l13 == r13 +} + + +pub fn main() { + let f = 1_usize as *const libc::FILE; + let t13 = (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0, f); + let t12 = (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0); + assert_eq_13(from_1(), t13); + assert_eq_13(from_1usize(), t13); + assert_eq_13(from_1isize(), t13); + assert_eq_13(from_1u8(), t13); + assert_eq_13(from_1i8(), t13); + assert_eq_13(from_1u16(), t13); + assert_eq_13(from_1i16(), t13); + assert_eq_13(from_1u32(), t13); + assert_eq_13(from_1i32(), t13); + assert_eq_13(from_1u64(), t13); + assert_eq_13(from_1i64(), t13); + assert_eq!(from_1f32(), t12); + assert_eq!(from_1f64(), t12); + + assert_eq!(from_ptr(), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 as *const ())); + assert_eq!(from_bool(), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)); + + assert_eq!(other_casts(), (func as *const u8, func as *const isize, + STR as *const str as *const u8, BSTR as *const [u8] as *const u8)); +} diff --git a/src/test/run-pass/mir_refs_correct.rs b/src/test/run-pass/mir_refs_correct.rs index 2da1a75870964..93953e3f58ae0 100644 --- a/src/test/run-pass/mir_refs_correct.rs +++ b/src/test/run-pass/mir_refs_correct.rs @@ -14,6 +14,8 @@ extern crate mir_external_refs as ext; struct S(u8); +#[derive(Debug, PartialEq, Eq)] +struct Unit; impl S { fn hey() -> u8 { 42 } @@ -57,9 +59,16 @@ enum E { U(u8) } +#[derive(PartialEq, Debug, Eq)] +enum CEnum { + A = 0x321, + B = 0x123 +} + const C: u8 = 84; const C2: [u8; 5] = [42; 5]; const C3: [u8; 3] = [42, 41, 40]; +const C4: fn(u8) -> S = S; fn regular() -> u8 { 21 @@ -175,6 +184,26 @@ fn t20() -> fn(u64, u32)->(u64, u32) { >::staticmeth } +#[rustc_mir] +fn t21() -> Unit { + Unit +} + +#[rustc_mir] +fn t22() -> Option { + None +} + +#[rustc_mir] +fn t23() -> (CEnum, CEnum) { + (CEnum::A, CEnum::B) +} + +#[rustc_mir] +fn t24() -> fn(u8) -> S { + C4 +} + fn main(){ unsafe { assert_eq!(t1()(), regular()); @@ -214,5 +243,9 @@ fn main(){ assert_eq!(t18()(50u64, 5u64), F::f(50u64, 5u64)); assert_eq!(t19()(322u64, 2u32), F::f(322u64, 2u32)); assert_eq!(t20()(123u64, 38u32), >::staticmeth(123, 38)); + assert_eq!(t21(), Unit); + assert_eq!(t22(), None); + assert_eq!(t23(), (CEnum::A, CEnum::B)); + assert_eq!(t24(), C4); } } diff --git a/src/test/run-pass/mir_trans_call_converging.rs b/src/test/run-pass/mir_trans_call_converging.rs new file mode 100644 index 0000000000000..d8acfec25c4b5 --- /dev/null +++ b/src/test/run-pass/mir_trans_call_converging.rs @@ -0,0 +1,28 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] + +fn converging_fn() -> u64 { + 43 +} + +#[rustc_mir] +fn mir() -> u64 { + let x; + loop { + x = converging_fn(); + break; + } + x +} + +fn main() { + assert_eq!(mir(), 43); +} diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs index 2335a3c3348eb..bca72330c85ad 100644 --- a/src/test/run-pass/mir_trans_calls.rs +++ b/src/test/run-pass/mir_trans_calls.rs @@ -88,6 +88,26 @@ fn test8() -> isize { Two::two() } +#[rustc_mir] +fn test_closure(f: &F, x: i32, y: i32) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f(x, y) +} + +#[rustc_mir] +fn test_fn_object(f: &Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { + f(x, y) +} + +#[rustc_mir] +fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { + // This call goes through the Fn implementation for &Fn provided in + // core::ops::impls. It expands to a static Fn::call() that calls the + // Fn::call() implemenation of the object shim underneath. + f(x, y) +} + fn main() { assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..])); assert_eq!(test2(98), 98); @@ -97,4 +117,10 @@ fn main() { assert_eq!(test6(&Foo, 12367), 12367); assert_eq!(test7(), 1); assert_eq!(test8(), 2); + + let closure = |x: i32, y: i32| { x + y }; + assert_eq!(test_closure(&closure, 100, 1), 101); + let function_object = &closure as &Fn(i32, i32) -> i32; + assert_eq!(test_fn_object(function_object, 100, 2), 102); + assert_eq!(test_fn_impl(&function_object, 100, 3), 103); } diff --git a/src/test/run-pass/mir_void_return.rs b/src/test/run-pass/mir_void_return.rs new file mode 100644 index 0000000000000..8b07449b8fafd --- /dev/null +++ b/src/test/run-pass/mir_void_return.rs @@ -0,0 +1,24 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +#[rustc_mir] +fn mir() -> (){ + let x = 1; + let mut y = 0; + while y < x { + y += 1 + } +} + +pub fn main() { + mir(); +} diff --git a/src/test/run-pass/mir_void_return_2.rs b/src/test/run-pass/mir_void_return_2.rs new file mode 100644 index 0000000000000..a3ad343240918 --- /dev/null +++ b/src/test/run-pass/mir_void_return_2.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +fn nil() {} + +#[rustc_mir] +fn mir(){ + nil() +} + +pub fn main() { + mir(); +} diff --git a/src/test/run-pass/num-wrapping.rs b/src/test/run-pass/num-wrapping.rs new file mode 100644 index 0000000000000..33f7b97ef9679 --- /dev/null +++ b/src/test/run-pass/num-wrapping.rs @@ -0,0 +1,414 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// compile-flags: -C debug-assertions +// +// Test std::num::Wrapping for {uN, iN, usize, isize} + +#![feature(op_assign_traits, num_bits_bytes, test)] + +extern crate test; + +use std::num::Wrapping; +use std::ops::{ + Add, Sub, Mul, Div, Rem, BitXor, BitOr, BitAnd, + AddAssign, SubAssign, MulAssign, DivAssign, RemAssign, BitXorAssign, BitOrAssign, BitAndAssign, + Shl, Shr, ShlAssign, ShrAssign +}; +use std::{i8, i16, i32, i64, isize, u8, u16, u32, u64, usize}; +use test::black_box; + +fn main() { + test_ops(); + test_op_assigns(); + test_sh_ops(); + test_sh_op_assigns(); +} + +fn test_ops() { + macro_rules! op_test { + ($op:ident ($lhs:expr, $rhs:expr) == $ans:expr) => { + assert_eq!(black_box(Wrapping($lhs).$op(Wrapping($rhs))), Wrapping($ans)); + // FIXME(30524): uncomment this test when it's implemented + // assert_eq!(black_box(Wrapping($lhs).$op($rhs)), Wrapping($ans)); + } + } + + op_test!(add(i8::MAX, 1) == i8::MIN); + op_test!(add(i16::MAX, 1) == i16::MIN); + op_test!(add(i32::MAX, 1) == i32::MIN); + op_test!(add(i64::MAX, 1) == i64::MIN); + op_test!(add(isize::MAX, 1) == isize::MIN); + + op_test!(add(u8::MAX, 1) == 0); + op_test!(add(u16::MAX, 1) == 0); + op_test!(add(u32::MAX, 1) == 0); + op_test!(add(u64::MAX, 1) == 0); + op_test!(add(usize::MAX, 1) == 0); + + + op_test!(sub(i8::MIN, 1) == i8::MAX); + op_test!(sub(i16::MIN, 1) == i16::MAX); + op_test!(sub(i32::MIN, 1) == i32::MAX); + op_test!(sub(i64::MIN, 1) == i64::MAX); + op_test!(sub(isize::MIN, 1) == isize::MAX); + + op_test!(sub(0u8, 1) == u8::MAX); + op_test!(sub(0u16, 1) == u16::MAX); + op_test!(sub(0u32, 1) == u32::MAX); + op_test!(sub(0u64, 1) == u64::MAX); + op_test!(sub(0usize, 1) == usize::MAX); + + + op_test!(mul(i8::MAX, 2) == -2); + op_test!(mul(i16::MAX, 2) == -2); + op_test!(mul(i32::MAX, 2) == -2); + op_test!(mul(i64::MAX, 2) == -2); + op_test!(mul(isize::MAX, 2) == -2); + + op_test!(mul(u8::MAX, 2) == u8::MAX - 1); + op_test!(mul(u16::MAX, 2) == u16::MAX - 1); + op_test!(mul(u32::MAX, 2) == u32::MAX - 1); + op_test!(mul(u64::MAX, 2) == u64::MAX - 1); + op_test!(mul(usize::MAX, 2) == usize::MAX - 1); + + + op_test!(div(i8::MIN, -1) == i8::MIN); + op_test!(div(i16::MIN, -1) == i16::MIN); + op_test!(div(i32::MIN, -1) == i32::MIN); + op_test!(div(i64::MIN, -1) == i64::MIN); + op_test!(div(isize::MIN, -1) == isize::MIN); + + + op_test!(rem(i8::MIN, -1) == 0); + op_test!(rem(i16::MIN, -1) == 0); + op_test!(rem(i32::MIN, -1) == 0); + op_test!(rem(i64::MIN, -1) == 0); + op_test!(rem(isize::MIN, -1) == 0); + + // these are not that interesting, just testing to make sure they are implemented correctly + op_test!(bitxor(0b101010i8, 0b100110) == 0b001100); + op_test!(bitxor(0b101010i16, 0b100110) == 0b001100); + op_test!(bitxor(0b101010i32, 0b100110) == 0b001100); + op_test!(bitxor(0b101010i64, 0b100110) == 0b001100); + op_test!(bitxor(0b101010isize, 0b100110) == 0b001100); + + op_test!(bitxor(0b101010u8, 0b100110) == 0b001100); + op_test!(bitxor(0b101010u16, 0b100110) == 0b001100); + op_test!(bitxor(0b101010u32, 0b100110) == 0b001100); + op_test!(bitxor(0b101010u64, 0b100110) == 0b001100); + op_test!(bitxor(0b101010usize, 0b100110) == 0b001100); + + + op_test!(bitor(0b101010i8, 0b100110) == 0b101110); + op_test!(bitor(0b101010i16, 0b100110) == 0b101110); + op_test!(bitor(0b101010i32, 0b100110) == 0b101110); + op_test!(bitor(0b101010i64, 0b100110) == 0b101110); + op_test!(bitor(0b101010isize, 0b100110) == 0b101110); + + op_test!(bitor(0b101010u8, 0b100110) == 0b101110); + op_test!(bitor(0b101010u16, 0b100110) == 0b101110); + op_test!(bitor(0b101010u32, 0b100110) == 0b101110); + op_test!(bitor(0b101010u64, 0b100110) == 0b101110); + op_test!(bitor(0b101010usize, 0b100110) == 0b101110); + + + op_test!(bitand(0b101010i8, 0b100110) == 0b100010); + op_test!(bitand(0b101010i16, 0b100110) == 0b100010); + op_test!(bitand(0b101010i32, 0b100110) == 0b100010); + op_test!(bitand(0b101010i64, 0b100110) == 0b100010); + op_test!(bitand(0b101010isize, 0b100110) == 0b100010); + + op_test!(bitand(0b101010u8, 0b100110) == 0b100010); + op_test!(bitand(0b101010u16, 0b100110) == 0b100010); + op_test!(bitand(0b101010u32, 0b100110) == 0b100010); + op_test!(bitand(0b101010u64, 0b100110) == 0b100010); + op_test!(bitand(0b101010usize, 0b100110) == 0b100010); +} + +fn test_op_assigns() { + macro_rules! op_assign_test { + ($op:ident ($initial:expr, $rhs:expr) == $ans:expr) => { + { + let mut tmp = Wrapping($initial); + tmp = black_box(tmp); + tmp.$op(Wrapping($rhs)); + assert_eq!(black_box(tmp), Wrapping($ans)); + } + // FIXME(30524): Uncomment this test + /* + { + let mut tmp = Wrapping($initial); + tmp = black_box(tmp); + tmp.$op($rhs); + assert_eq!(black_box(tmp), Wrapping($ans)); + } + */ + } + } + op_assign_test!(add_assign(i8::MAX, 1) == i8::MIN); + op_assign_test!(add_assign(i16::MAX, 1) == i16::MIN); + op_assign_test!(add_assign(i32::MAX, 1) == i32::MIN); + op_assign_test!(add_assign(i64::MAX, 1) == i64::MIN); + op_assign_test!(add_assign(isize::MAX, 1) == isize::MIN); + + op_assign_test!(add_assign(u8::MAX, 1) == u8::MIN); + op_assign_test!(add_assign(u16::MAX, 1) == u16::MIN); + op_assign_test!(add_assign(u32::MAX, 1) == u32::MIN); + op_assign_test!(add_assign(u64::MAX, 1) == u64::MIN); + op_assign_test!(add_assign(usize::MAX, 1) == usize::MIN); + + + op_assign_test!(sub_assign(i8::MIN, 1) == i8::MAX); + op_assign_test!(sub_assign(i16::MIN, 1) == i16::MAX); + op_assign_test!(sub_assign(i32::MIN, 1) == i32::MAX); + op_assign_test!(sub_assign(i64::MIN, 1) == i64::MAX); + op_assign_test!(sub_assign(isize::MIN, 1) == isize::MAX); + + op_assign_test!(sub_assign(u8::MIN, 1) == u8::MAX); + op_assign_test!(sub_assign(u16::MIN, 1) == u16::MAX); + op_assign_test!(sub_assign(u32::MIN, 1) == u32::MAX); + op_assign_test!(sub_assign(u64::MIN, 1) == u64::MAX); + op_assign_test!(sub_assign(usize::MIN, 1) == usize::MAX); + + + op_assign_test!(mul_assign(i8::MAX, 2) == -2); + op_assign_test!(mul_assign(i16::MAX, 2) == -2); + op_assign_test!(mul_assign(i32::MAX, 2) == -2); + op_assign_test!(mul_assign(i64::MAX, 2) == -2); + op_assign_test!(mul_assign(isize::MAX, 2) == -2); + + op_assign_test!(mul_assign(u8::MAX, 2) == u8::MAX - 1); + op_assign_test!(mul_assign(u16::MAX, 2) == u16::MAX - 1); + op_assign_test!(mul_assign(u32::MAX, 2) == u32::MAX - 1); + op_assign_test!(mul_assign(u64::MAX, 2) == u64::MAX - 1); + op_assign_test!(mul_assign(usize::MAX, 2) == usize::MAX - 1); + + + op_assign_test!(div_assign(i8::MIN, -1) == i8::MIN); + op_assign_test!(div_assign(i16::MIN, -1) == i16::MIN); + op_assign_test!(div_assign(i32::MIN, -1) == i32::MIN); + op_assign_test!(div_assign(i64::MIN, -1) == i64::MIN); + op_assign_test!(div_assign(isize::MIN, -1) == isize::MIN); + + + op_assign_test!(rem_assign(i8::MIN, -1) == 0); + op_assign_test!(rem_assign(i16::MIN, -1) == 0); + op_assign_test!(rem_assign(i32::MIN, -1) == 0); + op_assign_test!(rem_assign(i64::MIN, -1) == 0); + op_assign_test!(rem_assign(isize::MIN, -1) == 0); + + + // these are not that interesting, just testing to make sure they are implemented correctly + op_assign_test!(bitxor_assign(0b101010i8, 0b100110) == 0b001100); + op_assign_test!(bitxor_assign(0b101010i16, 0b100110) == 0b001100); + op_assign_test!(bitxor_assign(0b101010i32, 0b100110) == 0b001100); + op_assign_test!(bitxor_assign(0b101010i64, 0b100110) == 0b001100); + op_assign_test!(bitxor_assign(0b101010isize, 0b100110) == 0b001100); + + op_assign_test!(bitxor_assign(0b101010u8, 0b100110) == 0b001100); + op_assign_test!(bitxor_assign(0b101010u16, 0b100110) == 0b001100); + op_assign_test!(bitxor_assign(0b101010u32, 0b100110) == 0b001100); + op_assign_test!(bitxor_assign(0b101010u64, 0b100110) == 0b001100); + op_assign_test!(bitxor_assign(0b101010usize, 0b100110) == 0b001100); + + + op_assign_test!(bitor_assign(0b101010i8, 0b100110) == 0b101110); + op_assign_test!(bitor_assign(0b101010i16, 0b100110) == 0b101110); + op_assign_test!(bitor_assign(0b101010i32, 0b100110) == 0b101110); + op_assign_test!(bitor_assign(0b101010i64, 0b100110) == 0b101110); + op_assign_test!(bitor_assign(0b101010isize, 0b100110) == 0b101110); + + op_assign_test!(bitor_assign(0b101010u8, 0b100110) == 0b101110); + op_assign_test!(bitor_assign(0b101010u16, 0b100110) == 0b101110); + op_assign_test!(bitor_assign(0b101010u32, 0b100110) == 0b101110); + op_assign_test!(bitor_assign(0b101010u64, 0b100110) == 0b101110); + op_assign_test!(bitor_assign(0b101010usize, 0b100110) == 0b101110); + + + op_assign_test!(bitand_assign(0b101010i8, 0b100110) == 0b100010); + op_assign_test!(bitand_assign(0b101010i16, 0b100110) == 0b100010); + op_assign_test!(bitand_assign(0b101010i32, 0b100110) == 0b100010); + op_assign_test!(bitand_assign(0b101010i64, 0b100110) == 0b100010); + op_assign_test!(bitand_assign(0b101010isize, 0b100110) == 0b100010); + + op_assign_test!(bitand_assign(0b101010u8, 0b100110) == 0b100010); + op_assign_test!(bitand_assign(0b101010u16, 0b100110) == 0b100010); + op_assign_test!(bitand_assign(0b101010u32, 0b100110) == 0b100010); + op_assign_test!(bitand_assign(0b101010u64, 0b100110) == 0b100010); + op_assign_test!(bitand_assign(0b101010usize, 0b100110) == 0b100010); +} + +fn test_sh_ops() { + macro_rules! sh_test { + ($op:ident ($lhs:expr, $rhs:expr) == $ans:expr) => { + assert_eq!(black_box(Wrapping($lhs).$op($rhs)), Wrapping($ans)); + } + } + // NOTE: This will break for i8 if we ever get i/u128 + macro_rules! sh_test_all { + ($t:ty) => { + sh_test!(shl(i8::MAX, (i8::BITS + 1) as $t) == -2); + sh_test!(shl(i16::MAX, (i16::BITS + 1) as $t) == -2); + sh_test!(shl(i32::MAX, (i32::BITS + 1) as $t) == -2); + sh_test!(shl(i64::MAX, (i64::BITS + 1) as $t) == -2); + sh_test!(shl(isize::MAX, (isize::BITS + 1) as $t) == -2); + + sh_test!(shl(u8::MAX, (u8::BITS + 1) as $t) == u8::MAX - 1); + sh_test!(shl(u16::MAX, (u16::BITS + 1) as $t) == u16::MAX - 1); + sh_test!(shl(u32::MAX, (u32::BITS + 1) as $t) == u32::MAX - 1); + sh_test!(shl(u64::MAX, (u64::BITS + 1) as $t) == u64::MAX - 1); + sh_test!(shl(usize::MAX, (usize::BITS + 1) as $t) == usize::MAX - 1); + + + sh_test!(shr(i8::MAX, (i8::BITS + 1) as $t) == i8::MAX / 2); + sh_test!(shr(i16::MAX, (i16::BITS + 1) as $t) == i16::MAX / 2); + sh_test!(shr(i32::MAX, (i32::BITS + 1) as $t) == i32::MAX / 2); + sh_test!(shr(i64::MAX, (i64::BITS + 1) as $t) == i64::MAX / 2); + sh_test!(shr(isize::MAX, (isize::BITS + 1) as $t) == isize::MAX / 2); + + sh_test!(shr(u8::MAX, (u8::BITS + 1) as $t) == u8::MAX / 2); + sh_test!(shr(u16::MAX, (u16::BITS + 1) as $t) == u16::MAX / 2); + sh_test!(shr(u32::MAX, (u32::BITS + 1) as $t) == u32::MAX / 2); + sh_test!(shr(u64::MAX, (u64::BITS + 1) as $t) == u64::MAX / 2); + sh_test!(shr(usize::MAX, (usize::BITS + 1) as $t) == usize::MAX / 2); + } + } + macro_rules! sh_test_negative_all { + ($t:ty) => { + sh_test!(shr(i8::MAX, -((i8::BITS + 1) as $t)) == -2); + sh_test!(shr(i16::MAX, -((i16::BITS + 1) as $t)) == -2); + sh_test!(shr(i32::MAX, -((i32::BITS + 1) as $t)) == -2); + sh_test!(shr(i64::MAX, -((i64::BITS + 1) as $t)) == -2); + sh_test!(shr(isize::MAX, -((isize::BITS + 1) as $t)) == -2); + + sh_test!(shr(u8::MAX, -((u8::BITS + 1) as $t)) == u8::MAX - 1); + sh_test!(shr(u16::MAX, -((u16::BITS + 1) as $t)) == u16::MAX - 1); + sh_test!(shr(u32::MAX, -((u32::BITS + 1) as $t)) == u32::MAX - 1); + sh_test!(shr(u64::MAX, -((u64::BITS + 1) as $t)) == u64::MAX - 1); + sh_test!(shr(usize::MAX, -((usize::BITS + 1) as $t)) == usize::MAX - 1); + + + sh_test!(shl(i8::MAX, -((i8::BITS + 1) as $t)) == i8::MAX / 2); + sh_test!(shl(i16::MAX, -((i16::BITS + 1) as $t)) == i16::MAX / 2); + sh_test!(shl(i32::MAX, -((i32::BITS + 1) as $t)) == i32::MAX / 2); + sh_test!(shl(i64::MAX, -((i64::BITS + 1) as $t)) == i64::MAX / 2); + sh_test!(shl(isize::MAX, -((isize::BITS + 1) as $t)) == isize::MAX / 2); + + sh_test!(shl(u8::MAX, -((u8::BITS + 1) as $t)) == u8::MAX / 2); + sh_test!(shl(u16::MAX, -((u16::BITS + 1) as $t)) == u16::MAX / 2); + sh_test!(shl(u32::MAX, -((u32::BITS + 1) as $t)) == u32::MAX / 2); + sh_test!(shl(u64::MAX, -((u64::BITS + 1) as $t)) == u64::MAX / 2); + sh_test!(shl(usize::MAX, -((usize::BITS + 1) as $t)) == usize::MAX / 2); + } + } + // FIXME(#23545): Uncomment the remaining tests + //sh_test_all!(i8); + //sh_test_all!(u8); + //sh_test_all!(i16); + //sh_test_all!(u16); + //sh_test_all!(i32); + //sh_test_all!(u32); + //sh_test_all!(i64); + //sh_test_all!(u64); + //sh_test_all!(isize); + sh_test_all!(usize); + + //sh_test_negative_all!(i8); + //sh_test_negative_all!(i16); + //sh_test_negative_all!(i32); + //sh_test_negative_all!(i64); + //sh_test_negative_all!(isize); +} + +fn test_sh_op_assigns() { + macro_rules! sh_assign_test { + ($op:ident ($initial:expr, $rhs:expr) == $ans:expr) => {{ + let mut tmp = Wrapping($initial); + tmp = black_box(tmp); + tmp.$op($rhs); + assert_eq!(black_box(tmp), Wrapping($ans)); + }} + } + macro_rules! sh_assign_test_all { + ($t:ty) => { + sh_assign_test!(shl_assign(i8::MAX, (i8::BITS + 1) as $t) == -2); + sh_assign_test!(shl_assign(i16::MAX, (i16::BITS + 1) as $t) == -2); + sh_assign_test!(shl_assign(i32::MAX, (i32::BITS + 1) as $t) == -2); + sh_assign_test!(shl_assign(i64::MAX, (i64::BITS + 1) as $t) == -2); + sh_assign_test!(shl_assign(isize::MAX, (isize::BITS + 1) as $t) == -2); + + sh_assign_test!(shl_assign(u8::MAX, (u8::BITS + 1) as $t) == u8::MAX - 1); + sh_assign_test!(shl_assign(u16::MAX, (u16::BITS + 1) as $t) == u16::MAX - 1); + sh_assign_test!(shl_assign(u32::MAX, (u32::BITS + 1) as $t) == u32::MAX - 1); + sh_assign_test!(shl_assign(u64::MAX, (u64::BITS + 1) as $t) == u64::MAX - 1); + sh_assign_test!(shl_assign(usize::MAX, (usize::BITS + 1) as $t) == usize::MAX - 1); + + + sh_assign_test!(shr_assign(i8::MAX, (i8::BITS + 1) as $t) == i8::MAX / 2); + sh_assign_test!(shr_assign(i16::MAX, (i16::BITS + 1) as $t) == i16::MAX / 2); + sh_assign_test!(shr_assign(i32::MAX, (i32::BITS + 1) as $t) == i32::MAX / 2); + sh_assign_test!(shr_assign(i64::MAX, (i64::BITS + 1) as $t) == i64::MAX / 2); + sh_assign_test!(shr_assign(isize::MAX, (isize::BITS + 1) as $t) == isize::MAX / 2); + + sh_assign_test!(shr_assign(u8::MAX, (u8::BITS + 1) as $t) == u8::MAX / 2); + sh_assign_test!(shr_assign(u16::MAX, (u16::BITS + 1) as $t) == u16::MAX / 2); + sh_assign_test!(shr_assign(u32::MAX, (u32::BITS + 1) as $t) == u32::MAX / 2); + sh_assign_test!(shr_assign(u64::MAX, (u64::BITS + 1) as $t) == u64::MAX / 2); + sh_assign_test!(shr_assign(usize::MAX, (usize::BITS + 1) as $t) == usize::MAX / 2); + } + } + macro_rules! sh_assign_test_negative_all { + ($t:ty) => { + sh_assign_test!(shr_assign(i8::MAX, -((i8::BITS + 1) as $t)) == -2); + sh_assign_test!(shr_assign(i16::MAX, -((i16::BITS + 1) as $t)) == -2); + sh_assign_test!(shr_assign(i32::MAX, -((i32::BITS + 1) as $t)) == -2); + sh_assign_test!(shr_assign(i64::MAX, -((i64::BITS + 1) as $t)) == -2); + sh_assign_test!(shr_assign(isize::MAX, -((isize::BITS + 1) as $t)) == -2); + + sh_assign_test!(shr_assign(u8::MAX, -((u8::BITS + 1) as $t)) == u8::MAX - 1); + sh_assign_test!(shr_assign(u16::MAX, -((u16::BITS + 1) as $t)) == u16::MAX - 1); + sh_assign_test!(shr_assign(u32::MAX, -((u32::BITS + 1) as $t)) == u32::MAX - 1); + sh_assign_test!(shr_assign(u64::MAX, -((u64::BITS + 1) as $t)) == u64::MAX - 1); + sh_assign_test!(shr_assign(usize::MAX, -((usize::BITS + 1) as $t)) == usize::MAX - 1); + + + sh_assign_test!(shl_assign(i8::MAX, -((i8::BITS + 1) as $t)) == i8::MAX / 2); + sh_assign_test!(shl_assign(i16::MAX, -((i16::BITS + 1) as $t)) == i16::MAX / 2); + sh_assign_test!(shl_assign(i32::MAX, -((i32::BITS + 1) as $t)) == i32::MAX / 2); + sh_assign_test!(shl_assign(i64::MAX, -((i64::BITS + 1) as $t)) == i64::MAX / 2); + sh_assign_test!(shl_assign(isize::MAX, -((isize::BITS + 1) as $t)) == isize::MAX / 2); + + sh_assign_test!(shl_assign(u8::MAX, -((u8::BITS + 1) as $t)) == u8::MAX / 2); + sh_assign_test!(shl_assign(u16::MAX, -((u16::BITS + 1) as $t)) == u16::MAX / 2); + sh_assign_test!(shl_assign(u32::MAX, -((u32::BITS + 1) as $t)) == u32::MAX / 2); + sh_assign_test!(shl_assign(u64::MAX, -((u64::BITS + 1) as $t)) == u64::MAX / 2); + sh_assign_test!(shl_assign(usize::MAX, -((usize::BITS + 1) as $t)) == usize::MAX / 2); + } + } + + // FIXME(#23545): Uncomment the remaining tests + //sh_assign_test_all!(i8); + //sh_assign_test_all!(u8); + //sh_assign_test_all!(i16); + //sh_assign_test_all!(u16); + //sh_assign_test_all!(i32); + //sh_assign_test_all!(u32); + //sh_assign_test_all!(i64); + //sh_assign_test_all!(u64); + //sh_assign_test_all!(isize); + sh_assign_test_all!(usize); + + //sh_assign_test_negative_all!(i8); + //sh_assign_test_negative_all!(i16); + //sh_assign_test_negative_all!(i32); + //sh_assign_test_negative_all!(i64); + //sh_assign_test_negative_all!(isize); +} diff --git a/src/test/run-pass/panic-handler-chain.rs b/src/test/run-pass/panic-handler-chain.rs new file mode 100644 index 0000000000000..1ed592d3d6b92 --- /dev/null +++ b/src/test/run-pass/panic-handler-chain.rs @@ -0,0 +1,33 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(panic_handler, const_fn, std_panic)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::panic; +use std::thread; + +static A: AtomicUsize = AtomicUsize::new(0); +static B: AtomicUsize = AtomicUsize::new(0); + +fn main() { + panic::set_handler(|_| { A.fetch_add(1, Ordering::SeqCst); }); + let handler = panic::take_handler(); + panic::set_handler(move |info| { + B.fetch_add(1, Ordering::SeqCst); + handler(info); + }); + + let _ = thread::spawn(|| { + panic!(); + }).join(); + + assert_eq!(1, A.load(Ordering::SeqCst)); + assert_eq!(1, B.load(Ordering::SeqCst)); +} diff --git a/src/test/run-pass/panic-handler-flail-wildly.rs b/src/test/run-pass/panic-handler-flail-wildly.rs new file mode 100644 index 0000000000000..783a44beaf36a --- /dev/null +++ b/src/test/run-pass/panic-handler-flail-wildly.rs @@ -0,0 +1,57 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(panic_handler, std_panic)] + +use std::panic; +use std::thread; + +fn a() { + panic::set_handler(|_| println!("hello yes this is a")); + panic::take_handler(); + panic::set_handler(|_| println!("hello yes this is a part 2")); + panic::take_handler(); +} + +fn b() { + panic::take_handler(); + panic::take_handler(); + panic::take_handler(); + panic::take_handler(); + panic::take_handler(); + panic!(); +} + +fn c() { + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic!(); +} + +fn main() { + for _ in 0..10 { + let mut handles = vec![]; + for _ in 0..10 { + handles.push(thread::spawn(a)); + } + for _ in 0..10 { + handles.push(thread::spawn(b)); + } + for _ in 0..10 { + handles.push(thread::spawn(c)); + } + for handle in handles { + let _ = handle.join(); + } + } +} diff --git a/src/test/run-pass/panic-handler-set-twice.rs b/src/test/run-pass/panic-handler-set-twice.rs new file mode 100644 index 0000000000000..edf65e8e2aa69 --- /dev/null +++ b/src/test/run-pass/panic-handler-set-twice.rs @@ -0,0 +1,27 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(panic_handler, const_fn, std_panic)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::panic; +use std::thread; + +static A: AtomicUsize = AtomicUsize::new(0); + +fn main() { + panic::set_handler(|_| ()); + panic::set_handler(|info| { A.fetch_add(1, Ordering::SeqCst); }); + + let _ = thread::spawn(|| { + panic!(); + }).join(); + + assert_eq!(1, A.load(Ordering::SeqCst)); +} diff --git a/src/test/run-pass/panic-recover-propagate.rs b/src/test/run-pass/panic-recover-propagate.rs new file mode 100644 index 0000000000000..c0b2f25d99c07 --- /dev/null +++ b/src/test/run-pass/panic-recover-propagate.rs @@ -0,0 +1,35 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(std_panic, recover, panic_propagate, panic_handler, const_fn)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::panic; +use std::thread; + +static A: AtomicUsize = AtomicUsize::new(0); + +fn main() { + panic::set_handler(|_| { + A.fetch_add(1, Ordering::SeqCst); + }); + + let result = thread::spawn(|| { + let result = panic::recover(|| { + panic!("hi there"); + }); + + panic::propagate(result.err().unwrap()); + }).join(); + + let msg = *result.err().unwrap().downcast::<&'static str>().unwrap(); + assert_eq!("hi there", msg); + assert_eq!(1, A.load(Ordering::SeqCst)); +} diff --git a/src/test/run-pass/panic-safe.rs b/src/test/run-pass/panic-safe.rs index cd2457e8a52f7..9949b79278c11 100644 --- a/src/test/run-pass/panic-safe.rs +++ b/src/test/run-pass/panic-safe.rs @@ -11,7 +11,7 @@ #![allow(dead_code)] #![feature(recover)] -use std::panic::RecoverSafe; +use std::panic::{RecoverSafe, AssertRecoverSafe}; use std::cell::RefCell; use std::sync::{Mutex, RwLock, Arc}; use std::rc::Rc; @@ -47,5 +47,9 @@ fn main() { assert::>(); assert::>(); assert::>(); + assert::>(); + assert::<&AssertRecoverSafe>(); + assert::>>(); + assert::>>(); } }