Use arena allocation instead of reference counting for `Module`s to fix memory leaks from `Rc` cycles.
A module references its module children and its import resolutions, and an import resolution references the module defining the imported name, so there is a cycle whenever a module imports something from an ancestor module.
For example,
```rust
mod foo { // `foo` references `bar`.
fn baz() {}
mod bar { // `bar` references the import.
use foo::baz; // The import references `foo`.
}
}
```
1. Make sure you have installed the dependencies:
* `g++` 4.7 or `clang++` 3.x
- * `python` 2.6 or later (but not 3.x)
+ * `python` 2.7 or later (but not 3.x)
* GNU `make` 3.81 or later
* `curl`
* `git`
valopt nacl-cross-path "" "NaCl SDK path (Pepper Canary is recommended). Must be absolute!"
valopt release-channel "dev" "the name of the release channel to build"
valopt musl-root "/usr/local" "MUSL root installation directory"
+valopt extra-filename "" "Additional data that is hashed and passed to the -C extra-filename flag"
# Used on systems where "cc" and "ar" are unavailable
valopt default-linker "cc" "the default linker"
CFG_STATIC_LIB_NAME_arm-unknown-linux-gnueabi=lib$(1).a
CFG_LIB_GLOB_arm-unknown-linux-gnueabi=lib$(1)-*.so
CFG_LIB_DSYM_GLOB_arm-unknown-linux-gnueabi=lib$(1)-*.dylib.dSYM
-CFG_JEMALLOC_CFLAGS_arm-unknown-linux-gnueabi := -D__arm__ -mfpu=vfp $(CFLAGS)
-CFG_GCCISH_CFLAGS_arm-unknown-linux-gnueabi := -Wall -g -fPIC -D__arm__ -mfpu=vfp $(CFLAGS)
+CFG_JEMALLOC_CFLAGS_arm-unknown-linux-gnueabi := -D__arm__ -mfloat-abi=soft $(CFLAGS)
+CFG_GCCISH_CFLAGS_arm-unknown-linux-gnueabi := -Wall -g -fPIC -D__arm__ -mfloat-abi=soft $(CFLAGS)
CFG_GCCISH_CXXFLAGS_arm-unknown-linux-gnueabi := -fno-rtti $(CXXFLAGS)
CFG_GCCISH_LINK_FLAGS_arm-unknown-linux-gnueabi := -shared -fPIC -g
CFG_GCCISH_DEF_FLAG_arm-unknown-linux-gnueabi := -Wl,--export-dynamic,--dynamic-list=
$(foreach crate,$(TOOLS),$(eval $(call RUST_TOOL,$(crate))))
-ifdef CFG_DISABLE_ELF_TLS
-RUSTFLAGS_std := --cfg no_elf_tls
-endif
-
CRATEFILE_libc := $(SREL)src/liblibc/src/lib.rs
RUSTFLAGS_libc := --cfg stdbuild
# Append a version-dependent hash to each library, so we can install different
# versions in the same place
-CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE) | $(CFG_HASH_COMMAND))
+CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE)$(CFG_EXTRA_FILENAME) | $(CFG_HASH_COMMAND))
ifeq ($(CFG_RELEASE_CHANNEL),stable)
# This is the normal semver version string, e.g. "0.12.0", "0.12.0-nightly"
```
Our distance calculation works regardless of our `Edge` type, so the `E` stuff in
-this signature is just a distraction.
+this signature is a distraction.
What we really want to say is that a certain `E`dge and `N`ode type come together
to form each kind of `Graph`. We can do that with associated types:
This silly implementation always returns `true` and an empty `Vec<Edge>`, but it
gives you an idea of how to implement this kind of thing. We first need three
`struct`s, one for the graph, one for the node, and one for the edge. If it made
-more sense to use a different type, that would work as well, we’re just going to
+more sense to use a different type, that would work as well, we’re going to
use `struct`s for all three here.
-Next is the `impl` line, which is just like implementing any other trait.
+Next is the `impl` line, which is an implementation like any other trait.
From here, we use `=` to define our associated types. The name the trait uses
goes on the left of the `=`, and the concrete type we’re `impl`ementing this
The `transmute` function is provided by a [compiler intrinsic][intrinsics], and
what it does is very simple, but very scary. It tells Rust to treat a value of
one type as though it were another type. It does this regardless of the
-typechecking system, and just completely trusts you.
+typechecking system, and completely trusts you.
[intrinsics]: intrinsics.html
## `*const T` and `*mut T`
-These are C-like raw pointers with no lifetime or ownership attached to them. They just point to
+These are C-like raw pointers with no lifetime or ownership attached to them. They point to
some location in memory with no other restrictions. The only guarantee that these provide is that
they cannot be dereferenced except in code marked `unsafe`.
## `Arc<T>`
-[`Arc<T>`][arc] is just a version of `Rc<T>` that uses an atomic reference count (hence, "Arc").
+[`Arc<T>`][arc] is a version of `Rc<T>` that uses an atomic reference count (hence, "Arc").
This can be sent freely between threads.
C++'s `shared_ptr` is similar to `Arc`, however in the case of C++ the inner data is always mutable.
# Taking closures as arguments
Now that we know that closures are traits, we already know how to accept and
-return closures: just like any other trait!
+return closures: the same as any other trait!
This also means that we can choose static vs dynamic dispatch as well. First,
let’s write a function which takes something callable, calls it, and returns
assert_eq!(3, answer);
```
-We pass our closure, `|x| x + 2`, to `call_with_one`. It just does what it
+We pass our closure, `|x| x + 2`, to `call_with_one`. It does what it
suggests: it calls the closure, giving it `1` as an argument.
Let’s examine the signature of `call_with_one` in more depth:
we have a `[closure@<anon>:7:9: 7:20]`. Wait, what?
Because each closure generates its own environment `struct` and implementation
-of `Fn` and friends, these types are anonymous. They exist just solely for
+of `Fn` and friends, these types are anonymous. They exist solely for
this closure. So Rust shows them as `closure@<anon>`, rather than some
autogenerated name.
}
```
-We use the `mpsc::channel()` method to construct a new channel. We just `send`
+We use the `mpsc::channel()` method to construct a new channel. We `send`
a simple `()` down the channel, and then wait for ten of them to come back.
-While this channel is just sending a generic signal, we can send any data that
+While this channel is sending a generic signal, we can send any data that
is `Send` over the channel!
```rust
}
```
-Of course, you can copy and paste this from this web page, or just type
+Of course, you can copy and paste this from this web page, or type
something else. It’s not important that you actually put ‘konnichiwa’ to learn
about the module system.
Rust allows you to precisely control which aspects of your interface are
public, and so private is the default. To make things public, you use the `pub`
keyword. Let’s focus on the `english` module first, so let’s reduce our `src/main.rs`
-to just this:
+to only this:
```rust,ignore
extern crate phrases;
## Re-exporting with `pub use`
-You don’t just use `use` to shorten identifiers. You can also use it inside of your crate
+You don’t only use `use` to shorten identifiers. You can also use it inside of your crate
to re-export a function inside another module. This allows you to present an external
interface that may not directly map to your internal code organization.
```
As you can see, the curly brackets compress `use` statements for several items
-under the same path, and in this context `self` just refers back to that path.
+under the same path, and in this context `self` refers back to that path.
Note: The curly brackets cannot be nested or mixed with star globbing.
The compiler currently ships two default allocators: `alloc_system` and
`alloc_jemalloc` (some targets don't have jemalloc, however). These allocators
-are just normal Rust crates and contain an implementation of the routines to
+are normal Rust crates and contain an implementation of the routines to
allocate and deallocate memory. The standard library is not compiled assuming
either one, and the compiler will decide which allocator is in use at
compile-time depending on the type of output artifact being produced.
size
}
-# // just needed to get rustdoc to test this
+# // only needed to get rustdoc to test this
# fn main() {}
# #[lang = "panic_fmt"] fn panic_fmt() {}
# #[lang = "eh_personality"] fn eh_personality() {}
```
This will highlight according to whatever language you're showing off.
-If you're just showing plain text, choose `text`.
+If you're only showing plain text, choose `text`.
It's important to choose the correct annotation here, because `rustdoc` uses it
in an interesting way: It can be used to actually test your examples in a
can use this to your advantage. In this case, documentation comments need
to apply to some kind of function, so if I want to show you just a
documentation comment, I need to add a little function definition below
-it. At the same time, it's just there to satisfy the compiler, so hiding
+it. At the same time, it's only there to satisfy the compiler, so hiding
it makes the example more clear. You can use this technique to explain
longer examples in detail, while still preserving the testability of your
documentation.
# fn foo() {}
```
-is just
+is:
~~~markdown
# Examples
equality yet, but we’ll find out in the [`traits`][traits] section.
[match]: match.html
-[if-let]: if-let.html
[traits]: traits.html
# Constructors as functions
panic is embedded in the calls to `unwrap`.
To “unwrap” something in Rust is to say, “Give me the result of the
-computation, and if there was an error, just panic and stop the program.”
-It would be better if we just showed the code for unwrapping because it is so
+computation, and if there was an error, panic and stop the program.”
+It would be better if we showed the code for unwrapping because it is so
simple, but to do that, we will first need to explore the `Option` and `Result`
types. Both of these types have a method called `unwrap` defined on them.
}
```
-Notice that when this function finds a matching character, it doesn't just
+Notice that when this function finds a matching character, it doesn't only
return the `offset`. Instead, it returns `Some(offset)`. `Some` is a variant or
a *value constructor* for the `Option` type. You can think of it as a function
with the type `fn<T>(value: T) -> Option<T>`. Correspondingly, `None` is also a
not all file names have a `.` in them, so it's possible that the file name has
no extension. This *possibility of absence* is encoded into the types using
`Option<T>`. In other words, the compiler will force us to address the
-possibility that an extension does not exist. In our case, we just print out a
+possibility that an extension does not exist. In our case, we only print out a
message saying as such.
Getting the extension of a file name is a pretty common operation, so it makes
In fact, the case analysis in `extension_explicit` follows a very common
pattern: *map* a function on to the value inside of an `Option<T>`, unless the
-option is `None`, in which case, just return `None`.
+option is `None`, in which case, return `None`.
Rust has parametric polymorphism, so it is very easy to define a combinator
that abstracts this pattern:
}
```
-You might think that we could just use the `map` combinator to reduce the case
+You might think that we could use the `map` combinator to reduce the case
analysis, but its type doesn't quite fit. Namely, `map` takes a function that
does something only with the inner value. The result of that function is then
*always* [rewrapped with `Some`](#code-option-map). Instead, we need something
with both an `Option` and a `Result`, the solution is *usually* to convert the
`Option` to a `Result`. In our case, the absence of a command line parameter
(from `env::args()`) means the user didn't invoke the program correctly. We
-could just use a `String` to describe the error. Let's try:
+could use a `String` to describe the error. Let's try:
<span id="code-error-double-string"></span>
The other new combinator used here is
[`Result::map_err`](../std/result/enum.Result.html#method.map_err).
-This is just like `Result::map`, except it maps a function on to the *error*
+This is like `Result::map`, except it maps a function on to the *error*
portion of a `Result` value. If the `Result` is an `Ok(...)` value, then it is
returned unmodified.
an `i32`) by `2`. If an error had occurred before that point, this operation
would have been skipped because of how `map` is defined.
-`map_err` is the trick that makes all of this work. `map_err` is just like
+`map_err` is the trick that makes all of this work. `map_err` is like
`map`, except it applies a function to the `Err(...)` value of a `Result`. In
this case, we want to convert all of our errors to one type: `String`. Since
both `io::Error` and `num::ParseIntError` implement `ToString`, we can call the
## The `try!` macro
A cornerstone of error handling in Rust is the `try!` macro. The `try!` macro
-abstracts case analysis just like combinators, but unlike combinators, it also
+abstracts case analysis like combinators, but unlike combinators, it also
abstracts *control flow*. Namely, it can abstract the *early return* pattern
seen above.
[`ErrorKind`](../std/io/enum.ErrorKind.html)) or keep it hidden (like
[`ParseIntError`](../std/num/struct.ParseIntError.html)). Regardless
of how you do it, it's usually good practice to at least provide some
-information about the error beyond just its `String`
+information about the error beyond its `String`
representation. But certainly, this will vary depending on use cases.
At a minimum, you should probably implement the
The data we'll be using comes from the [Data Science
Toolkit][11]. I've prepared some data from it for this exercise. You
can either grab the [world population data][12] (41MB gzip compressed,
-145MB uncompressed) or just the [US population data][13] (2.2MB gzip
+145MB uncompressed) or only the [US population data][13] (2.2MB gzip
compressed, 7.2MB uncompressed).
Up until now, we've kept the code limited to Rust's standard library. For a real
[Previously](#the-limits-of-combinators) we started refactoring our code by
changing the type of our function from `T` to `Result<T, OurErrorType>`. In
-this case, `OurErrorType` is just `Box<Error>`. But what's `T`? And can we add
+this case, `OurErrorType` is only `Box<Error>`. But what's `T`? And can we add
a return type to `main`?
The answer to the second question is no, we can't. That means we'll need to
But how can we use the same code over both types? There's actually a
couple ways we could go about this. One way is to write `search` such
that it is generic on some type parameter `R` that satisfies
-`io::Read`. Another way is to just use trait objects:
+`io::Read`. Another way is to use trait objects:
```rust,ignore
fn search<P: AsRef<Path>>
...
```
-Now we just need to implement our “quiet” functionality. This requires us to
+Now we only need to implement our “quiet” functionality. This requires us to
tweak the case analysis in `main`:
```rust,ignore
heuristics!
* If you're writing short example code that would be overburdened by error
- handling, it's probably just fine to use `unwrap` (whether that's
+ handling, it's probably fine to use `unwrap` (whether that's
[`Result::unwrap`](../std/result/enum.Result.html#method.unwrap),
[`Option::unwrap`](../std/option/enum.Option.html#method.unwrap)
or preferably
A few examples of how this model can be used are:
* A native build dependency. Sometimes some C/C++ glue is needed when writing
- some Rust code, but distribution of the C/C++ code in a library format is just
+ some Rust code, but distribution of the C/C++ code in a library format is
a burden. In this case, the code will be archived into `libfoo.a` and then the
Rust crate would declare a dependency via `#[link(name = "foo", kind =
"static")]`.
architecture, this means that the abi used would be `stdcall`. On x86_64,
however, windows uses the `C` calling convention, so `C` would be used. This
means that in our previous example, we could have used `extern "system" { ... }`
-to define a block for all windows systems, not just x86 ones.
+to define a block for all windows systems, not only x86 ones.
# Interoperability with foreign code
Rust: ‘declaration statements’ and ‘expression statements’. Everything else is
an expression. Let’s talk about declaration statements first.
-In some languages, variable bindings can be written as expressions, not just
+In some languages, variable bindings can be written as expressions, not
statements. Like Ruby:
```ruby
expression, although its value is not particularly useful. Unlike other
languages where an assignment evaluates to the assigned value (e.g. `5` in the
previous example), in Rust the value of an assignment is an empty tuple `()`
-because the assigned value can have [just one owner](ownership.html), and any
+because the assigned value can have [only one owner](ownership.html), and any
other returned value would be too surprising:
```rust
// found `core::option::Option<_>` (expected f64 but found integral variable)
```
-That doesn’t mean we can’t make `Option<T>`s that hold an `f64`! They just have
+That doesn’t mean we can’t make `Option<T>`s that hold an `f64`! They have
to match up:
```rust
Similar to functions, the `<T>` is where we declare the generic parameters,
and we then use `x: T` in the type declaration, too.
-When you want to add an implementation for the generic `struct`, you just
+When you want to add an implementation for the generic `struct`, you
declare the type parameter after the `impl`:
```rust
## Uninstalling
-Uninstalling Rust is as easy as installing it. On Linux or Mac, just run
+Uninstalling Rust is as easy as installing it. On Linux or Mac, run
the uninstall script:
```bash
The nice thing about starting with such a simple program is that you can
quickly verify that your compiler is installed, and that it's working properly.
-Printing information to the screen is also just a pretty common thing to do, so
+Printing information to the screen is also a pretty common thing to do, so
practicing it early on is good.
> Note: This book assumes basic familiarity with the command line. Rust itself
Hello, world!
```
-In Windows, just replace `main` with `main.exe`. Regardless of your operating
+In Windows, replace `main` with `main.exe`. Regardless of your operating
system, you should see the string `Hello, world!` print to the terminal. If you
did, then congratulations! You've officially written a Rust program. That makes
you a Rust programmer! Welcome.
The second important part is the `println!()` line. This is calling a Rust
*[macro]*, which is how metaprogramming is done in Rust. If it were calling a
function instead, it would look like this: `println()` (without the !). We'll
-discuss Rust macros in more detail later, but for now you just need to
+discuss Rust macros in more detail later, but for now you only need to
know that when you see a `!` that means that you’re calling a macro instead of
a normal function.
[statically allocated]: the-stack-and-the-heap.html
-The line ends with a semicolon (`;`). Rust is an *[expression oriented]*
-language, which means that most things are expressions, rather than statements.
-The `;` indicates that this expression is over, and the next one is ready to
-begin. Most lines of Rust code end with a `;`.
+The line ends with a semicolon (`;`). Rust is an *[expression-oriented
+language]*, which means that most things are expressions, rather than
+statements. The `;` indicates that this expression is over, and the next one is
+ready to begin. Most lines of Rust code end with a `;`.
[expression-oriented language]: glossary.html#expression-oriented-language
The first line, `[package]`, indicates that the following statements are
configuring a package. As we add more information to this file, we’ll add other
-sections, but for now, we just have the package configuration.
+sections, but for now, we only have the package configuration.
The other three lines set the three bits of configuration that Cargo needs to
know to compile your program: its name, what version it is, and who wrote it.
With simple projects, Cargo doesn't bring a whole lot over just using `rustc`,
but it will become useful in future. With complex projects composed of multiple
crates, it’s much easier to let Cargo coordinate the build. With Cargo, you can
-just run `cargo build`, and it should work the right way.
+run `cargo build`, and it should work the right way.
## Building for Release
```
Great! The `run` command comes in handy when you need to rapidly iterate on a
-project. Our game is just such a project, we need to quickly test each
+project. Our game is such a project, we need to quickly test each
iteration before moving on to the next one.
# Processing a Guess
Rust warns us that we haven’t used the `Result` value. This warning comes from
a special annotation that `io::Result` has. Rust is trying to tell you that
you haven’t handled a possible error. The right way to suppress the error is
-to actually write error handling. Luckily, if we just want to crash if there’s
+to actually write error handling. Luckily, if we want to crash if there’s
a problem, we can use these two little methods. If we can recover from the
error somehow, we’d do something else, but we’ll save that for a future
project.
-There’s just one line of this first example left:
+There’s only one line of this first example left:
```rust,ignore
println!("You guessed: {}", guess);
That’s right, no output! Cargo knows that our project has been built, and that
all of its dependencies are built, and so there’s no reason to do all that
stuff. With nothing to do, it simply exits. If we open up `src/main.rs` again,
-make a trivial change, and then save it again, we’ll just see one line:
+make a trivial change, and then save it again, we’ll only see one line:
```bash
$ cargo build
[concurrency]: concurrency.html
-The second line just prints out the secret number. This is useful while
+The second line prints out the secret number. This is useful while
we’re developing our program, so we can easily test it out. But we’ll be
deleting it for the final version. It’s not much of a game if it prints out
the answer when you start it up!
the beginning and end of our string. This is important, as we had to press the
‘return’ key to satisfy `read_line()`. This means that if we type `5` and hit
return, `guess` looks like this: `5\n`. The `\n` represents ‘newline’, the
-enter key. `trim()` gets rid of this, leaving our string with just the `5`. The
+enter key. `trim()` gets rid of this, leaving our string with only the `5`. The
[`parse()` method on strings][parse] parses a string into some kind of number.
Since it can parse a variety of numbers, we need to give Rust a hint as to the
exact type of number we want. Hence, `let guess: u32`. The colon (`:`) after
By adding the `break` line after the `You win!`, we’ll exit the loop when we
win. Exiting the loop also means exiting the program, since it’s the last
-thing in `main()`. We have just one more tweak to make: when someone inputs a
-non-number, we don’t want to quit, we just want to ignore it. We can do that
+thing in `main()`. We have only one more tweak to make: when someone inputs a
+non-number, we don’t want to quit, we want to ignore it. We can do that
like this:
```rust,ignore
```
This is how you generally move from ‘crash on error’ to ‘actually handle the
-returned by `parse()` is an `enum` just like `Ordering`, but in this case, each
+returned by `parse()` is an `enum` like `Ordering`, but in this case, each
variant has some data associated with it: `Ok` is a success, and `Err` is a
failure. Each contains more information: the successfully parsed integer, or an
error type. In this case, we `match` on `Ok(num)`, which sets the inner value
-of the `Ok` to the name `num`, and then we just return it on the right-hand
-side. In the `Err` case, we don’t care what kind of error it is, so we just
+of the `Ok` to the name `num`, and then we return it on the right-hand
+side. In the `Err` case, we don’t care what kind of error it is, so we
use `_` instead of a name. This ignores the error, and `continue` causes us
to go to the next iteration of the `loop`.
`None`, we `break` out of the loop.
This code sample is basically the same as our `for` loop version. The `for`
-loop is just a handy way to write this `loop`/`match`/`break` construct.
+loop is a handy way to write this `loop`/`match`/`break` construct.
`for` loops aren't the only thing that uses iterators, however. Writing your
own iterator involves implementing the `Iterator` trait. While doing that is
references? Firstly, because we explicitly asked it to with
`&`. Secondly, if it gave us the data itself, we would have to be its
owner, which would involve making a copy of the data and giving us the
-copy. With references, we're just borrowing a reference to the data,
-and so it's just passing a reference, without needing to do the move.
+copy. With references, we're only borrowing a reference to the data,
+and so it's only passing a reference, without needing to do the move.
So, now that we've established that ranges are often not what you want, let's
talk about what you do want instead.
```
If you are trying to execute a closure on an iterator for its side effects,
-just use `for` instead.
+use `for` instead.
There are tons of interesting iterator adaptors. `take(n)` will return an
iterator over the next `n` elements of the original iterator. Let's try it out
discuss the `<>`s after a function’s name. A function can have ‘generic
parameters’ between the `<>`s, of which lifetimes are one kind. We’ll discuss
other kinds of generics [later in the book][generics], but for now, let’s
-just focus on the lifetimes aspect.
+focus on the lifetimes aspect.
[functions]: functions.html
[generics]: generics.html
...(x: &'a mut i32)
```
-If you compare `&mut i32` to `&'a mut i32`, they’re the same, it’s just that
+If you compare `&mut i32` to `&'a mut i32`, they’re the same, it’s that
the lifetime `'a` has snuck in between the `&` and the `mut i32`. We read `&mut
i32` as ‘a mutable reference to an `i32`’ and `&'a mut i32` as ‘a mutable
reference to an `i32` with the lifetime `'a`’.
```
As you can see, we need to declare a lifetime for `Foo` in the `impl` line. We repeat
-`'a` twice, just like on functions: `impl<'a>` defines a lifetime `'a`, and `Foo<'a>`
+`'a` twice, like on functions: `impl<'a>` defines a lifetime `'a`, and `Foo<'a>`
uses it.
## Multiple lifetimes
Methods take a special first parameter, of which there are three variants:
`self`, `&self`, and `&mut self`. You can think of this first parameter as
being the `foo` in `foo.bar()`. The three variants correspond to the three
-kinds of things `foo` could be: `self` if it’s just a value on the stack,
+kinds of things `foo` could be: `self` if it’s a value on the stack,
`&self` if it’s a reference, and `&mut self` if it’s a mutable reference.
-Because we took the `&self` parameter to `area`, we can use it just like any
+Because we took the `&self` parameter to `area`, we can use it like any
other parameter. Because we know it’s a `Circle`, we can access the `radius`
-just like we would with any other `struct`.
+like we would with any other `struct`.
We should default to using `&self`, as you should prefer borrowing over taking
ownership, as well as taking immutable references over mutable ones. Here’s an
# Circle } }
```
-We just say we’re returning a `Circle`. With this method, we can grow a new
+We say we’re returning a `Circle`. With this method, we can grow a new
`Circle` to any arbitrary size.
# Associated functions
$ sudo /usr/local/lib/rustlib/uninstall.sh
```
-If you used the Windows installer, just re-run the `.msi` and it will give you
+If you used the Windows installer, re-run the `.msi` and it will give you
an uninstall option.
Some people, and somewhat rightfully so, get very upset when we tell you to
platform upon release, but if we're honest, the Windows experience isn't as
integrated as the Linux/OS X experience is. We're working on it! If anything
does not work, it is a bug. Please let us know if that happens. Each and every
-commit is tested against Windows just like any other platform.
+commit is tested against Windows like any other platform.
If you've got Rust installed, you can open up a shell, and type this:
The core library has very few dependencies and is much more portable than the
standard library itself. Additionally, the core library has most of the
necessary functionality for writing idiomatic and effective Rust code. When
-using `#![no_std]`, Rust will automatically inject the `core` crate, just like
+using `#![no_std]`, Rust will automatically inject the `core` crate, like
we do for `std` when we’re using it.
As an example, here is a program that will calculate the dot product of two
}
```
-For `HasArea` and `Square`, we just declare a type parameter `T` and replace
+For `HasArea` and `Square`, we declare a type parameter `T` and replace
`f64` with it. The `impl` needs more involved modifications:
```ignore
This prints `x is 0`.
-You can do this kind of match on any member, not just the first:
+You can do this kind of match on any member, not only the first:
```rust
struct Point {
```
In the first arm, we bind the value inside the `Ok` variant to `value`. But
-in the `Err` arm, we use `_` to disregard the specific error, and just print
+in the `Err` arm, we use `_` to disregard the specific error, and print
a general error message.
`_` is valid in any pattern that creates a binding. This can be useful to
```
This prints `no`, because the `if` applies to the whole of `4 | 5`, and not to
-just the `5`. In other words, the precedence of `if` behaves like this:
+only the `5`. In other words, the precedence of `if` behaves like this:
```text
(4 | 5) if y => ...
A ‘slice’ is a reference to (or “view” into) another data structure. They are
useful for allowing safe, efficient access to a portion of an array without
-copying. For example, you might want to reference just one line of a file read
+copying. For example, you might want to reference only one line of a file read
into memory. By nature, a slice is not created directly, but from an existing
variable binding. Slices have a defined length, can be mutable or immutable.
```rust
let a = [0, 1, 2, 3, 4];
let complete = &a[..]; // A slice containing all of the elements in a
-let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3
+let middle = &a[1..4]; // A slice of a: only the elements 1, 2, and 3
```
Slices have type `&[T]`. We’ll talk about that `T` when we cover
let x: (i32, &str) = (1, "hello");
```
-As you can see, the type of a tuple looks just like the tuple, but with each
+As you can see, the type of a tuple looks like the tuple, but with each
position having a type name rather than the value. Careful readers will also
note that tuples are heterogeneous: we have an `i32` and a `&str` in this tuple.
In systems programming languages, strings are a bit more complex than in other
-languages. For now, just read `&str` as a *string slice*, and we’ll learn more
+languages. For now, read `&str` as a *string slice*, and we’ll learn more
soon.
You can assign one tuple into another, if they have the same contained types
```
Remember [before][let] when I said the left-hand side of a `let` statement was more
-powerful than just assigning a binding? Here we are. We can put a pattern on
+powerful than assigning a binding? Here we are. We can put a pattern on
the left-hand side of the `let`, and if it matches up to the right-hand side,
we can assign multiple bindings at once. In this case, `let` “destructures”
or “breaks up” the tuple, and assigns the bits to three bindings.
resource when it goes out of scope. This means that after the call to `foo()`,
we can use our original bindings again.
-References are immutable, just like bindings. This means that inside of `foo()`,
+References are immutable, like bindings. This means that inside of `foo()`,
the vectors can’t be changed at all:
```rust,ignore
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
+Otherwise, `&mut` references are like references. There _is_ a large
difference between the two, and how they interact, though. You can tell
something is fishy in the above example, because we need that extra scope, with
the `{` and `}`. If we remove them, we get an error:
assert_eq!("foobar", s);
```
-Rust has more than just `&str`s though. A `String`, is a heap-allocated string.
+Rust has more than only `&str`s though. A `String`, is a heap-allocated string.
This string is growable, and is also guaranteed to be UTF-8. `String`s are
commonly created by converting from a string slice using the `to_string`
method.
```
As you can see here, you can extract the inner integer type through a
-destructuring `let`, just as with regular tuples. In this case, the
+destructuring `let`, as with regular tuples. In this case, the
`let Inches(integer_length)` assigns `10` to `integer_length`.
# Unit-like structs
marker type), but in combination with other features, it can become
useful. For instance, a library may ask you to create a structure that
implements a certain [trait][trait] to handle events. If you don’t have
-any data you need to store in the structure, you can just create a
+any data you need to store in the structure, you can create a
unit-like `struct`.
[trait]: traits.html
It works!
The current convention is to use the `tests` module to hold your "unit-style"
-tests. Anything that just tests one small bit of functionality makes sense to
+tests. Anything that tests one small bit of functionality makes sense to
go here. But what about "integration-style" tests instead? For that, we have
the `tests` directory.
Well, when a function gets called, some memory gets allocated for all of its
local variables and some other information. This is called a ‘stack frame’, and
for the purpose of this tutorial, we’re going to ignore the extra information
-and just consider the local variables we’re allocating. So in this case, when
+and only consider the local variables we’re allocating. So in this case, when
`main()` is run, we’ll allocate a single 32-bit integer for our stack frame.
This is automatically handled for you, as you can see; we didn’t have to write
any special Rust code or anything.
| 0 | x | 42 |
Whew! Our stack is growing tall.
-After `italic()` is over, its frame is deallocated, leaving just `bold()` and
+After `italic()` is over, its frame is deallocated, leaving only `bold()` and
`main()`:
| Address | Name | Value |
| **1** | **a**| **5** |
| 0 | x | 42 |
-And then `bold()` ends, leaving just `main()`:
+And then `bold()` ends, leaving only `main()`:
| Address | Name | Value |
|---------|------|-------|
We haven’t really talked too much about what it actually means to allocate and
deallocate memory in these contexts. Getting into very deep detail is out of
the scope of this tutorial, but what’s important to point out here is that
-the heap isn’t just a stack that grows from the opposite end. We’ll have an
+the heap isn’t a stack that grows from the opposite end. We’ll have an
example of this later in the book, but because the heap can be allocated and
freed in any order, it can end up with ‘holes’. Here’s a diagram of the memory
layout of a program which has been running for a while now:
| 1 | y | → 0 |
| 0 | x | 5 |
-Stack frames aren’t just for local bindings, they’re for arguments too. So in
+Stack frames aren’t only for local bindings, they’re for arguments too. So in
this case, we need to have both `i`, our argument, and `z`, our local variable
binding. `i` is a copy of the argument, `y`. Since `y`’s value is `0`, so is
`i`’s.
This is one reason why borrowing a variable doesn’t deallocate any memory: the
-value of a reference is just a pointer to a memory location. If we got rid of
+value of a reference is a pointer to a memory location. If we got rid of
the underlying memory, things wouldn’t work very well.
# A complex example
| 0 | h | 3 |
We end up allocating another value on the heap, and so we have to subtract one
-from (2<sup>30</sup>) - 1. It’s easier to just write that than `1,073,741,822`. In any
+from (2<sup>30</sup>) - 1. It’s easier to write that than `1,073,741,822`. In any
case, we set up the variables as usual.
At the end of `bar()`, it calls `baz()`:
## Runtime Efficiency
-Managing the memory for the stack is trivial: The machine just
+Managing the memory for the stack is trivial: The machine
increments or decrements a single value, the so-called “stack pointer”.
Managing memory for the heap is non-trivial: heap-allocated memory is freed at
arbitrary points, and each block of heap-allocated memory can be of arbitrary
Suppose we’ve got some values that implement `Foo`. The explicit form of
construction and use of `Foo` trait objects might look a bit like (ignoring the
-type mismatches: they’re all just pointers anyway):
+type mismatches: they’re all pointers anyway):
```rust,ignore
let a: String = "foo".to_string();
```
As you can see, the `trait` block looks very similar to the `impl` block,
-but we don’t define a body, just a type signature. When we `impl` a trait,
-we use `impl Trait for Item`, rather than just `impl Item`.
+but we don’t define a body, only a type signature. When we `impl` a trait,
+we use `impl Trait for Item`, rather than only `impl Item`.
## Trait bounds on generic functions
That’s it. It’s important that `unsafe` does not, for example, ‘turn off the
borrow checker’. Adding `unsafe` to some random Rust code doesn’t change its
-semantics, it won’t just start accepting anything. But it will let you write
+semantics, it won’t start accepting anything. But it will let you write
things that _do_ break some of the rules.
You will also encounter the `unsafe` keyword when writing bindings to foreign
are three:
1. We can only manipulate an instance of an unsized type via a pointer. An
- `&[T]` works just fine, but a `[T]` does not.
+ `&[T]` works fine, but a `[T]` does not.
2. Variables and arguments cannot have dynamically sized types.
3. Only the last field in a `struct` may have a dynamically sized type; the
other fields must not. Enum variants must not have dynamically sized types as
Virtually every non-'Hello World’ Rust program uses *variable bindings*. They
bind some value to a name, so it can be used later. `let` is
-used to introduce a binding, just like this:
+used to introduce a binding, like this:
```rust
fn main() {
In many languages, a variable binding would be called a *variable*, but Rust’s
variable bindings have a few tricks up their sleeves. For example the
-left-hand side of a `let` expression is a ‘[pattern][pattern]’, not just a
+left-hand side of a `let` expression is a ‘[pattern][pattern]’, not a
variable name. This means we can do things like:
```rust
After this expression is evaluated, `x` will be one, and `y` will be two.
Patterns are really powerful, and have [their own section][pattern] in the
-book. We don’t need those features for now, so we’ll just keep this in the back
+book. We don’t need those features for now, so we’ll keep this in the back
of our minds as we go forward.
[pattern]: patterns.html
want `x` to be the value we’re interpolating. The comma is used to separate
arguments we pass to functions and macros, if you’re passing more than one.
-When you just use the curly braces, Rust will attempt to display the value in a
+When you use the curly braces, Rust will attempt to display the value in a
meaningful way by checking out its type. If you want to specify the format in a
more detailed manner, there are a [wide number of options available][format].
-For now, we'll just stick to the default: integers aren't very complicated to
+For now, we'll stick to the default: integers aren't very complicated to
print.
[format]: ../std/fmt/index.html
compiler. For more comprehensive documentation see [the
website](https://www.rust-lang.org).
-[**The Rust Programming Language**](book/index.html)
+[**The Rust Programming Language**][book]. Also known as "The Book",
+The Rust Programming Language is the most comprehensive resource for
+all topics related to Rust, and is the primary official document of
+the language.
-[**The Rust Reference**](reference.html)
+[**The Rust Reference**][ref]. While Rust does not have a
+specification, the reference tries to describe its working in
+detail. It tends to be out of date.
-[**The Standard Library API Reference**](std/index.html)
+[**Standard Library API Reference**][api]. Documentation for the
+standard library.
-[**The Rustonomicon**](nomicon/index.html)
+[**The Rustonomicon**][nomicon]. An entire book dedicated to
+explaining how to write unsafe Rust code. It is for advanced Rust
+programmers.
+
+[**Compiler Error Index**][err]. Extended explanations of
+the errors produced by the Rust compiler.
+
+[book]: book/index.html
+[ref]: reference.html
+[api]: std/index.html
+[nomicon]: nomicon/index.html
+[err]: error-index.html
-[**The Compiler Error Index**](error-index.html)
"""
from __future__ import print_function
import sys
+from math import ceil, log
from fractions import Fraction
from collections import namedtuple
MIN_SIG = 2 ** (N - 1)
MAX_SIG = (2 ** N) - 1
-
# Hand-rolled fp representation without arithmetic or any other operations.
# The significand is normalized and always N bit, but the exponent is
# unrestricted in range.
ulp_err = abs_err / Fraction(2) ** z.exp
return float(ulp_err)
-LICENSE = """
+HEADER = """
// 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.
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+
+//! Tables of approximations of powers of ten.
+//! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py`
"""
+
def main():
+ print(HEADER.strip())
+ print()
+ print_proper_powers()
+ print()
+ print_short_powers(32, 24)
+ print()
+ print_short_powers(64, 53)
+
+
+def print_proper_powers():
MIN_E = -305
MAX_E = 305
e_range = range(MIN_E, MAX_E+1)
err = error(1, e, z)
assert err < 0.5
powers.append(z)
- typ = "([u64; {0}], [i16; {0}])".format(len(e_range))
- print(LICENSE.strip())
- print("// Table of approximations of powers of ten.")
- print("// DO NOT MODIFY: Generated by a src/etc/dec2flt_table.py")
print("pub const MIN_E: i16 = {};".format(MIN_E))
print("pub const MAX_E: i16 = {};".format(MAX_E))
print()
+ typ = "([u64; {0}], [i16; {0}])".format(len(powers))
print("pub const POWERS: ", typ, " = ([", sep='')
for z in powers:
print(" 0x{:x},".format(z.sig))
print("]);")
+def print_short_powers(num_bits, significand_size):
+ max_sig = 2**significand_size - 1
+ # The fast path bails out for exponents >= ceil(log5(max_sig))
+ max_e = int(ceil(log(max_sig, 5)))
+ e_range = range(max_e)
+ typ = "[f{}; {}]".format(num_bits, len(e_range))
+ print("pub const F", num_bits, "_SHORT_POWERS: ", typ, " = [", sep='')
+ for e in e_range:
+ print(" 1e{},".format(e))
+ print("];")
+
+
if __name__ == '__main__':
main()
return props
-# load all widths of want_widths, except those in except_cats
-def load_east_asian_width(want_widths, except_cats):
- f = "EastAsianWidth.txt"
- fetch(f)
- widths = {}
- re1 = re.compile("^([0-9A-F]+);(\w+) +# (\w+)")
- re2 = re.compile("^([0-9A-F]+)\.\.([0-9A-F]+);(\w+) +# (\w+)")
-
- for line in fileinput.input(f):
- width = None
- d_lo = 0
- d_hi = 0
- cat = None
- m = re1.match(line)
- if m:
- d_lo = m.group(1)
- d_hi = m.group(1)
- width = m.group(2)
- cat = m.group(3)
- else:
- m = re2.match(line)
- if m:
- d_lo = m.group(1)
- d_hi = m.group(2)
- width = m.group(3)
- cat = m.group(4)
- else:
- continue
- if cat in except_cats or width not in want_widths:
- continue
- d_lo = int(d_lo, 16)
- d_hi = int(d_hi, 16)
- if width not in widths:
- widths[width] = []
- widths[width].append((d_lo, d_hi))
- return widths
-
def escape_char(c):
return "'\\u{%x}'" % c if c != 0 else "'\\0'"
fn bsearch_range_table(c: char, r: &'static [(char, char)]) -> bool {
use core::cmp::Ordering::{Equal, Less, Greater};
r.binary_search_by(|&(lo, hi)| {
- if lo <= c && c <= hi {
- Equal
+ if c < lo {
+ Greater
} else if hi < c {
Less
} else {
- Greater
+ Equal
}
})
.is_ok()
def emit_conversions_module(f, to_upper, to_lower, to_title):
f.write("pub mod conversions {")
f.write("""
- use core::cmp::Ordering::{Equal, Less, Greater};
use core::option::Option;
use core::option::Option::{Some, None};
- use core::result::Result::{Ok, Err};
pub fn to_lower(c: char) -> [char; 3] {
match bsearch_case_table(c, to_lowercase_table) {
- None => [c, '\\0', '\\0'],
- Some(index) => to_lowercase_table[index].1
+ None => [c, '\\0', '\\0'],
+ Some(index) => to_lowercase_table[index].1,
}
}
pub fn to_upper(c: char) -> [char; 3] {
match bsearch_case_table(c, to_uppercase_table) {
None => [c, '\\0', '\\0'],
- Some(index) => to_uppercase_table[index].1
+ Some(index) => to_uppercase_table[index].1,
}
}
fn bsearch_case_table(c: char, table: &'static [(char, [char; 3])]) -> Option<usize> {
- match table.binary_search_by(|&(key, _)| {
- if c == key { Equal }
- else if key < c { Less }
- else { Greater }
- }) {
- Ok(i) => Some(i),
- Err(_) => None,
- }
+ table.binary_search_by(|&(key, _)| key.cmp(&c)).ok()
}
""")
is_pub=False, t_type = t_type, pfun=pfun)
f.write("}\n\n")
-def emit_charwidth_module(f, width_table):
- f.write("pub mod charwidth {\n")
- f.write(" use core::option::Option;\n")
- f.write(" use core::option::Option::{Some, None};\n")
- f.write(" use core::result::Result::{Ok, Err};\n")
- f.write("""
- fn bsearch_range_value_table(c: char, is_cjk: bool, r: &'static [(char, char, u8, u8)]) -> u8 {
- use core::cmp::Ordering::{Equal, Less, Greater};
- match r.binary_search_by(|&(lo, hi, _, _)| {
- if lo <= c && c <= hi { Equal }
- else if hi < c { Less }
- else { Greater }
- }) {
- Ok(idx) => {
- let (_, _, r_ncjk, r_cjk) = r[idx];
- if is_cjk { r_cjk } else { r_ncjk }
- }
- Err(_) => 1
- }
- }
-""")
-
- f.write("""
- pub fn width(c: char, is_cjk: bool) -> Option<usize> {
- match c as usize {
- _c @ 0 => Some(0), // null is zero width
- cu if cu < 0x20 => None, // control sequences have no width
- cu if cu < 0x7F => Some(1), // ASCII
- cu if cu < 0xA0 => None, // more control sequences
- _ => Some(bsearch_range_value_table(c, is_cjk, charwidth_table) as usize)
- }
- }
-
-""")
-
- f.write(" // character width table. Based on Markus Kuhn's free wcwidth() implementation,\n")
- f.write(" // http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c\n")
- emit_table(f, "charwidth_table", width_table, "&'static [(char, char, u8, u8)]", is_pub=False,
- pfun=lambda x: "(%s,%s,%s,%s)" % (escape_char(x[0]), escape_char(x[1]), x[2], x[3]))
- f.write("}\n\n")
-
def emit_norm_module(f, canon, compat, combine, norm_props):
canon_keys = canon.keys()
canon_keys.sort()
canon_comp_keys = canon_comp.keys()
canon_comp_keys.sort()
-def remove_from_wtable(wtable, val):
- wtable_out = []
- while wtable:
- if wtable[0][1] < val:
- wtable_out.append(wtable.pop(0))
- elif wtable[0][0] > val:
- break
- else:
- (wt_lo, wt_hi, width, width_cjk) = wtable.pop(0)
- if wt_lo == wt_hi == val:
- continue
- elif wt_lo == val:
- wtable_out.append((wt_lo+1, wt_hi, width, width_cjk))
- elif wt_hi == val:
- wtable_out.append((wt_lo, wt_hi-1, width, width_cjk))
- else:
- wtable_out.append((wt_lo, val-1, width, width_cjk))
- wtable_out.append((val+1, wt_hi, width, width_cjk))
- if wtable:
- wtable_out.extend(wtable)
- return wtable_out
-
-
-
-def optimize_width_table(wtable):
- wtable_out = []
- w_this = wtable.pop(0)
- while wtable:
- if w_this[1] == wtable[0][0] - 1 and w_this[2:3] == wtable[0][2:3]:
- w_tmp = wtable.pop(0)
- w_this = (w_this[0], w_tmp[1], w_tmp[2], w_tmp[3])
- else:
- wtable_out.append(w_this)
- w_this = wtable.pop(0)
- wtable_out.append(w_this)
- return wtable_out
-
if __name__ == "__main__":
r = "tables.rs"
if os.path.exists(r):
#![feature(unsize)]
#![feature(drop_in_place)]
#![feature(fn_traits)]
+#![feature(const_fn)]
#![feature(needs_allocator)]
+// Issue# 30592: Systematically use alloc_system during stage0 since jemalloc
+// might be unavailable or disabled
+#![cfg_attr(stage0, feature(alloc_system))]
+
#![cfg_attr(test, feature(test, rustc_private, box_heap))]
+#[cfg(stage0)]
+extern crate alloc_system;
+
// Allow testing this library
#[cfg(test)]
pub mod arc;
pub mod rc;
pub mod raw_vec;
+pub mod oom;
-/// Common out-of-memory routine
-#[cold]
-#[inline(never)]
-#[unstable(feature = "oom", reason = "not a scrutinized interface",
- issue = "27700")]
-pub fn oom() -> ! {
- // FIXME(#14674): This really needs to do something other than just abort
- // here, but any printing done must be *guaranteed* to not
- // allocate.
- unsafe { core::intrinsics::abort() }
-}
+pub use oom::oom;
--- /dev/null
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use core::sync::atomic::{AtomicPtr, Ordering};
+use core::mem;
+use core::intrinsics;
+
+static OOM_HANDLER: AtomicPtr<()> = AtomicPtr::new(default_oom_handler as *mut ());
+
+fn default_oom_handler() -> ! {
+ // The default handler can't do much more since we can't assume the presence
+ // of libc or any way of printing an error message.
+ unsafe { intrinsics::abort() }
+}
+
+/// Common out-of-memory routine
+#[cold]
+#[inline(never)]
+#[unstable(feature = "oom", reason = "not a scrutinized interface",
+ issue = "27700")]
+pub fn oom() -> ! {
+ let value = OOM_HANDLER.load(Ordering::SeqCst);
+ let handler: fn() -> ! = unsafe { mem::transmute(value) };
+ handler();
+}
+
+/// Set a custom handler for out-of-memory conditions
+///
+/// To avoid recursive OOM failures, it is critical that the OOM handler does
+/// not allocate any memory itself.
+#[unstable(feature = "oom", reason = "not a scrutinized interface",
+ issue = "27700")]
+pub fn set_oom_handler(handler: fn() -> !) {
+ OOM_HANDLER.store(handler as *mut (), Ordering::SeqCst);
+}
}
}
+ /// Attempts to double the size of the type's backing allocation in place. This is common
+ /// enough to want to do that it's easiest to just have a dedicated method. Slightly
+ /// more efficient logic can be provided for this than the general case.
+ ///
+ /// Returns true if the reallocation attempt has succeeded, or false otherwise.
+ ///
+ /// # Panics
+ ///
+ /// * Panics if T is zero-sized on the assumption that you managed to exhaust
+ /// all `usize::MAX` slots in your imaginary buffer.
+ /// * Panics on 32-bit platforms if the requested capacity exceeds
+ /// `isize::MAX` bytes.
+ #[inline(never)]
+ #[cold]
+ pub fn double_in_place(&mut self) -> bool {
+ unsafe {
+ let elem_size = mem::size_of::<T>();
+ let align = mem::align_of::<T>();
+
+ // since we set the capacity to usize::MAX when elem_size is
+ // 0, getting to here necessarily means the RawVec is overfull.
+ assert!(elem_size != 0, "capacity overflow");
+
+ // Since we guarantee that we never allocate more than isize::MAX bytes,
+ // `elem_size * self.cap <= isize::MAX` as a precondition, so this can't overflow
+ let new_cap = 2 * self.cap;
+ let new_alloc_size = new_cap * elem_size;
+
+ alloc_guard(new_alloc_size);
+ let size = heap::reallocate_inplace(self.ptr() as *mut _,
+ self.cap * elem_size,
+ new_alloc_size,
+ align);
+ if size >= new_alloc_size {
+ // We can't directly divide `size`.
+ self.cap = new_cap;
+ }
+ size >= new_alloc_size
+ }
+ }
+
/// Ensures that the buffer contains at least enough space to hold
/// `used_cap + needed_extra_cap` elements. If it doesn't already,
/// will reallocate the minimum possible amount of memory necessary.
}
}
+ /// Calculates the buffer's new size given that it'll hold `used_cap +
+ /// needed_extra_cap` elements. This logic is used in amortized reserve methods.
+ /// Returns `(new_capacity, new_alloc_size)`.
+ fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> (usize, usize) {
+ let elem_size = mem::size_of::<T>();
+ // Nothing we can really do about these checks :(
+ let required_cap = used_cap.checked_add(needed_extra_cap)
+ .expect("capacity overflow");
+ // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
+ let double_cap = self.cap * 2;
+ // `double_cap` guarantees exponential growth.
+ let new_cap = cmp::max(double_cap, required_cap);
+ let new_alloc_size = new_cap.checked_mul(elem_size).expect("capacity overflow");
+ (new_cap, new_alloc_size)
+ }
+
/// Ensures that the buffer contains at least enough space to hold
/// `used_cap + needed_extra_cap` elements. If it doesn't already have
/// enough capacity, will reallocate enough space plus comfortable slack
return;
}
- // Nothing we can really do about these checks :(
- let required_cap = used_cap.checked_add(needed_extra_cap)
- .expect("capacity overflow");
-
- // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
- let double_cap = self.cap * 2;
-
- // `double_cap` guarantees exponential growth.
- let new_cap = cmp::max(double_cap, required_cap);
-
- let new_alloc_size = new_cap.checked_mul(elem_size).expect("capacity overflow");
+ let (new_cap, new_alloc_size) = self.amortized_new_size(used_cap, needed_extra_cap);
// FIXME: may crash and burn on over-reserve
alloc_guard(new_alloc_size);
}
}
+ /// Attempts to ensure that the buffer contains at least enough space to hold
+ /// `used_cap + needed_extra_cap` elements. If it doesn't already have
+ /// enough capacity, will reallocate in place enough space plus comfortable slack
+ /// space to get amortized `O(1)` behaviour. Will limit this behaviour
+ /// if it would needlessly cause itself to panic.
+ ///
+ /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate
+ /// the requested space. This is not really unsafe, but the unsafe
+ /// code *you* write that relies on the behaviour of this function may break.
+ ///
+ /// Returns true if the reallocation attempt has succeeded, or false otherwise.
+ ///
+ /// # Panics
+ ///
+ /// * Panics if the requested capacity exceeds `usize::MAX` bytes.
+ /// * Panics on 32-bit platforms if the requested capacity exceeds
+ /// `isize::MAX` bytes.
+ pub fn reserve_in_place(&mut self, used_cap: usize, needed_extra_cap: usize) -> bool {
+ unsafe {
+ let elem_size = mem::size_of::<T>();
+ let align = mem::align_of::<T>();
+
+ // NOTE: we don't early branch on ZSTs here because we want this
+ // to actually catch "asking for more than usize::MAX" in that case.
+ // If we make it past the first branch then we are guaranteed to
+ // panic.
+
+ // Don't actually need any more capacity. If the current `cap` is 0, we can't
+ // reallocate in place.
+ // Wrapping in case they give a bad `used_cap`
+ if self.cap().wrapping_sub(used_cap) >= needed_extra_cap || self.cap == 0 {
+ return false;
+ }
+
+ let (_, new_alloc_size) = self.amortized_new_size(used_cap, needed_extra_cap);
+ // FIXME: may crash and burn on over-reserve
+ alloc_guard(new_alloc_size);
+
+ let size = heap::reallocate_inplace(self.ptr() as *mut _,
+ self.cap * elem_size,
+ new_alloc_size,
+ align);
+ if size >= new_alloc_size {
+ self.cap = new_alloc_size / elem_size;
+ }
+ size >= new_alloc_size
+ }
+ }
+
/// Shrinks the allocation down to the specified amount. If the given amount
/// is 0, actually completely deallocates.
///
use libc;
use MIN_ALIGN;
- extern "C" {
- // Apparently android doesn't have posix_memalign
- #[cfg(target_os = "android")]
- fn memalign(align: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
-
- #[cfg(not(target_os = "android"))]
- fn posix_memalign(memptr: *mut *mut libc::c_void,
- align: libc::size_t,
- size: libc::size_t)
- -> libc::c_int;
- }
-
pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
if align <= MIN_ALIGN {
libc::malloc(size as libc::size_t) as *mut u8
} else {
- #[cfg(target_os = "android")]
- unsafe fn more_aligned_malloc(size: usize, align: usize) -> *mut u8 {
- memalign(align as libc::size_t, size as libc::size_t) as *mut u8
- }
- #[cfg(not(target_os = "android"))]
- unsafe fn more_aligned_malloc(size: usize, align: usize) -> *mut u8 {
- let mut out = ptr::null_mut();
- let ret = posix_memalign(&mut out, align as libc::size_t, size as libc::size_t);
- if ret != 0 {
- ptr::null_mut()
- } else {
- out as *mut u8
- }
+ let mut out = ptr::null_mut();
+ let ret = libc::posix_memalign(&mut out, align as libc::size_t, size as libc::size_t);
+ if ret != 0 {
+ ptr::null_mut()
+ } else {
+ out as *mut u8
}
- more_aligned_malloc(size, align)
}
}
test(no_crate_inject, attr(deny(warnings))))]
#![feature(alloc)]
-#![feature(box_syntax)]
#![feature(core_intrinsics)]
+#![feature(drop_in_place)]
#![feature(heap_api)]
-#![feature(oom)]
-#![feature(ptr_as_ref)]
#![feature(raw)]
+#![feature(heap_api)]
#![feature(staged_api)]
#![feature(dropck_parametricity)]
#![cfg_attr(test, feature(test))]
+#![allow(deprecated)]
+
extern crate alloc;
use std::cell::{Cell, RefCell};
use std::cmp;
use std::intrinsics;
-use std::marker;
+use std::marker::{PhantomData, Send};
use std::mem;
use std::ptr;
-use std::rc::Rc;
+use std::slice;
-use alloc::heap::{allocate, deallocate};
+use alloc::heap;
+use alloc::raw_vec::RawVec;
-// The way arena uses arrays is really deeply awful. The arrays are
-// allocated, and have capacities reserved, but the fill for the array
-// will always stay at 0.
-#[derive(Clone, PartialEq)]
struct Chunk {
- data: Rc<RefCell<Vec<u8>>>,
+ data: RawVec<u8>,
+ /// Index of the first unused byte.
fill: Cell<usize>,
+ /// Indicates whether objects with destructors are stored in this chunk.
is_copy: Cell<bool>,
}
impl Chunk {
+ fn new(size: usize, is_copy: bool) -> Chunk {
+ Chunk {
+ data: RawVec::with_capacity(size),
+ fill: Cell::new(0),
+ is_copy: Cell::new(is_copy),
+ }
+ }
+
fn capacity(&self) -> usize {
- self.data.borrow().capacity()
+ self.data.cap()
}
unsafe fn as_ptr(&self) -> *const u8 {
- self.data.borrow().as_ptr()
+ self.data.ptr()
+ }
+
+ // Walk down a chunk, running the destructors for any objects stored
+ // in it.
+ unsafe fn destroy(&self) {
+ let mut idx = 0;
+ let buf = self.as_ptr();
+ let fill = self.fill.get();
+
+ while idx < fill {
+ let tydesc_data = buf.offset(idx as isize) as *const usize;
+ let (tydesc, is_done) = un_bitpack_tydesc_ptr(*tydesc_data);
+ let (size, align) = ((*tydesc).size, (*tydesc).align);
+
+ let after_tydesc = idx + mem::size_of::<*const TyDesc>();
+
+ let start = round_up(after_tydesc, align);
+
+ if is_done {
+ ((*tydesc).drop_glue)(buf.offset(start as isize) as *const i8);
+ }
+
+ // Find where the next tydesc lives
+ idx = round_up(start + size, mem::align_of::<*const TyDesc>());
+ }
}
}
/// A slower reflection-based arena that can allocate objects of any type.
///
-/// This arena uses `Vec<u8>` as a backing store to allocate objects from. For
-/// each allocated object, the arena stores a pointer to the type descriptor
+/// This arena uses `RawVec<u8>` as a backing store to allocate objects from.
+/// For each allocated object, the arena stores a pointer to the type descriptor
/// followed by the object (potentially with alignment padding after each
/// element). When the arena is destroyed, it iterates through all of its
/// chunks, and uses the tydesc information to trace through the objects,
/// than objects without destructors. This reduces overhead when initializing
/// plain-old-data (`Copy` types) and means we don't need to waste time running
/// their destructors.
+#[unstable(feature = "rustc_private",
+ reason = "Private to rustc", issue = "0")]
+#[rustc_deprecated(since = "1.6.0-dev", reason =
+"The reflection-based arena is superseded by the any-arena crate")]
pub struct Arena<'longer_than_self> {
- // The head is separated out from the list as a unbenchmarked
- // microoptimization, to avoid needing to case on the list to access the
- // head.
+ // The heads are separated out from the list as a unbenchmarked
+ // microoptimization, to avoid needing to case on the list to access a head.
head: RefCell<Chunk>,
copy_head: RefCell<Chunk>,
chunks: RefCell<Vec<Chunk>>,
- _marker: marker::PhantomData<*mut &'longer_than_self ()>,
+ _marker: PhantomData<*mut &'longer_than_self ()>,
}
impl<'a> Arena<'a> {
/// Allocates a new Arena with `initial_size` bytes preallocated.
pub fn new_with_size(initial_size: usize) -> Arena<'a> {
Arena {
- head: RefCell::new(chunk(initial_size, false)),
- copy_head: RefCell::new(chunk(initial_size, true)),
+ head: RefCell::new(Chunk::new(initial_size, false)),
+ copy_head: RefCell::new(Chunk::new(initial_size, true)),
chunks: RefCell::new(Vec::new()),
- _marker: marker::PhantomData,
+ _marker: PhantomData,
}
}
}
-fn chunk(size: usize, is_copy: bool) -> Chunk {
- Chunk {
- data: Rc::new(RefCell::new(Vec::with_capacity(size))),
- fill: Cell::new(0),
- is_copy: Cell::new(is_copy),
- }
-}
-
impl<'longer_than_self> Drop for Arena<'longer_than_self> {
fn drop(&mut self) {
unsafe {
- destroy_chunk(&*self.head.borrow());
+ self.head.borrow().destroy();
for chunk in self.chunks.borrow().iter() {
if !chunk.is_copy.get() {
- destroy_chunk(chunk);
+ chunk.destroy();
}
}
}
(base.checked_add(align - 1)).unwrap() & !(align - 1)
}
-// Walk down a chunk, running the destructors for any objects stored
-// in it.
-unsafe fn destroy_chunk(chunk: &Chunk) {
- let mut idx = 0;
- let buf = chunk.as_ptr();
- let fill = chunk.fill.get();
-
- while idx < fill {
- let tydesc_data = buf.offset(idx as isize) as *const usize;
- let (tydesc, is_done) = un_bitpack_tydesc_ptr(*tydesc_data);
- let (size, align) = ((*tydesc).size, (*tydesc).align);
-
- let after_tydesc = idx + mem::size_of::<*const TyDesc>();
-
- let start = round_up(after_tydesc, align);
-
- // debug!("freeing object: idx = {}, size = {}, align = {}, done = {}",
- // start, size, align, is_done);
- if is_done {
- ((*tydesc).drop_glue)(buf.offset(start as isize) as *const i8);
- }
-
- // Find where the next tydesc lives
- idx = round_up(start + size, mem::align_of::<*const TyDesc>());
- }
-}
-
// We encode whether the object a tydesc describes has been
// initialized in the arena in the low bit of the tydesc pointer. This
// is necessary in order to properly do cleanup if a panic occurs
// HACK(eddyb) TyDesc replacement using a trait object vtable.
// This could be replaced in the future with a custom DST layout,
// or `&'static (drop_glue, size, align)` created by a `const fn`.
+// Requirements:
+// * rvalue promotion (issue #1056)
+// * mem::{size_of, align_of} must be const fns
struct TyDesc {
drop_glue: fn(*const i8),
size: usize,
unsafe fn get_tydesc<T>() -> *const TyDesc {
use std::raw::TraitObject;
- let ptr = &*(1 as *const T);
+ let ptr = &*(heap::EMPTY as *const T);
// Can use any trait that is implemented for all types.
let obj = mem::transmute::<&AllTypes, TraitObject>(ptr);
}
impl<'longer_than_self> Arena<'longer_than_self> {
- fn chunk_size(&self) -> usize {
- self.copy_head.borrow().capacity()
+ // Grows a given chunk and returns `false`, or replaces it with a bigger
+ // chunk and returns `true`.
+ // This method is shared by both parts of the arena.
+ #[cold]
+ fn alloc_grow(&self, head: &mut Chunk, used_cap: usize, n_bytes: usize) -> bool {
+ if head.data.reserve_in_place(used_cap, n_bytes) {
+ // In-place reallocation succeeded.
+ false
+ } else {
+ // Allocate a new chunk.
+ let new_min_chunk_size = cmp::max(n_bytes, head.capacity());
+ let new_chunk = Chunk::new((new_min_chunk_size + 1).next_power_of_two(), false);
+ let old_chunk = mem::replace(head, new_chunk);
+ if old_chunk.fill.get() != 0 {
+ self.chunks.borrow_mut().push(old_chunk);
+ }
+ true
+ }
}
- // Functions for the POD part of the arena
- fn alloc_copy_grow(&self, n_bytes: usize, align: usize) -> *const u8 {
- // Allocate a new chunk.
- let new_min_chunk_size = cmp::max(n_bytes, self.chunk_size());
- self.chunks.borrow_mut().push(self.copy_head.borrow().clone());
-
- *self.copy_head.borrow_mut() = chunk((new_min_chunk_size + 1).next_power_of_two(), true);
-
- self.alloc_copy_inner(n_bytes, align)
- }
+ // Functions for the copyable part of the arena.
#[inline]
fn alloc_copy_inner(&self, n_bytes: usize, align: usize) -> *const u8 {
- let start = round_up(self.copy_head.borrow().fill.get(), align);
-
- let end = start + n_bytes;
- if end > self.chunk_size() {
- return self.alloc_copy_grow(n_bytes, align);
+ let mut copy_head = self.copy_head.borrow_mut();
+ let fill = copy_head.fill.get();
+ let mut start = round_up(fill, align);
+ let mut end = start + n_bytes;
+
+ if end > copy_head.capacity() {
+ if self.alloc_grow(&mut *copy_head, fill, end - fill) {
+ // Continuing with a newly allocated chunk
+ start = 0;
+ end = n_bytes;
+ copy_head.is_copy.set(true);
+ }
}
- let copy_head = self.copy_head.borrow();
copy_head.fill.set(end);
unsafe { copy_head.as_ptr().offset(start as isize) }
}
}
- // Functions for the non-POD part of the arena
- fn alloc_noncopy_grow(&self, n_bytes: usize, align: usize) -> (*const u8, *const u8) {
- // Allocate a new chunk.
- let new_min_chunk_size = cmp::max(n_bytes, self.chunk_size());
- self.chunks.borrow_mut().push(self.head.borrow().clone());
-
- *self.head.borrow_mut() = chunk((new_min_chunk_size + 1).next_power_of_two(), false);
-
- self.alloc_noncopy_inner(n_bytes, align)
- }
+ // Functions for the non-copyable part of the arena.
#[inline]
fn alloc_noncopy_inner(&self, n_bytes: usize, align: usize) -> (*const u8, *const u8) {
- // Be careful to not maintain any `head` borrows active, because
- // `alloc_noncopy_grow` borrows it mutably.
- let (start, end, tydesc_start, head_capacity) = {
- let head = self.head.borrow();
- let fill = head.fill.get();
-
- let tydesc_start = fill;
- let after_tydesc = fill + mem::size_of::<*const TyDesc>();
- let start = round_up(after_tydesc, align);
- let end = start + n_bytes;
-
- (start, end, tydesc_start, head.capacity())
- };
-
- if end > head_capacity {
- return self.alloc_noncopy_grow(n_bytes, align);
+ let mut head = self.head.borrow_mut();
+ let fill = head.fill.get();
+
+ let mut tydesc_start = fill;
+ let after_tydesc = fill + mem::size_of::<*const TyDesc>();
+ let mut start = round_up(after_tydesc, align);
+ let mut end = round_up(start + n_bytes, mem::align_of::<*const TyDesc>());
+
+ if end > head.capacity() {
+ if self.alloc_grow(&mut *head, tydesc_start, end - tydesc_start) {
+ // Continuing with a newly allocated chunk
+ tydesc_start = 0;
+ start = round_up(mem::size_of::<*const TyDesc>(), align);
+ end = round_up(start + n_bytes, mem::align_of::<*const TyDesc>());
+ }
}
- let head = self.head.borrow();
- head.fill.set(round_up(end, mem::align_of::<*const TyDesc>()));
+ head.fill.set(end);
unsafe {
let buf = head.as_ptr();
}
}
}
-}
-#[test]
-fn test_arena_destructors() {
- let arena = Arena::new();
- for i in 0..10 {
- // Arena allocate something with drop glue to make sure it
- // doesn't leak.
- arena.alloc(|| Rc::new(i));
- // Allocate something with funny size and alignment, to keep
- // things interesting.
- arena.alloc(|| [0u8, 1u8, 2u8]);
+ /// Allocates a slice of bytes of requested length. The bytes are not guaranteed to be zero
+ /// if the arena has previously been cleared.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the requested length is too large and causes overflow.
+ pub fn alloc_bytes(&self, len: usize) -> &mut [u8] {
+ unsafe {
+ // Check for overflow.
+ self.copy_head.borrow().fill.get().checked_add(len).expect("length overflow");
+ let ptr = self.alloc_copy_inner(len, 1);
+ intrinsics::assume(!ptr.is_null());
+ slice::from_raw_parts_mut(ptr as *mut _, len)
+ }
}
-}
-#[test]
-#[should_panic]
-fn test_arena_destructors_fail() {
- let arena = Arena::new();
- // Put some stuff in the arena.
- for i in 0..10 {
- // Arena allocate something with drop glue to make sure it
- // doesn't leak.
- arena.alloc(|| Rc::new(i));
- // Allocate something with funny size and alignment, to keep
- // things interesting.
- arena.alloc(|| [0u8, 1, 2]);
- }
- // Now, panic while allocating
- arena.alloc::<Rc<i32>, _>(|| {
- panic!();
- });
+ /// Clears the arena. Deallocates all but the longest chunk which may be reused.
+ pub fn clear(&mut self) {
+ unsafe {
+ self.head.borrow().destroy();
+ self.head.borrow().fill.set(0);
+ self.copy_head.borrow().fill.set(0);
+ for chunk in self.chunks.borrow().iter() {
+ if !chunk.is_copy.get() {
+ chunk.destroy();
+ }
+ }
+ self.chunks.borrow_mut().clear();
+ }
+ }
}
/// A faster arena that can hold objects of only one type.
pub struct TypedArena<T> {
/// A pointer to the next object to be allocated.
- ptr: Cell<*const T>,
+ ptr: Cell<*mut T>,
/// A pointer to the end of the allocated area. When this pointer is
/// reached, a new chunk is allocated.
- end: Cell<*const T>,
+ end: Cell<*mut T>,
- /// A pointer to the first arena segment.
- first: RefCell<*mut TypedArenaChunk<T>>,
+ /// A vector arena segments.
+ chunks: RefCell<Vec<TypedArenaChunk<T>>>,
/// Marker indicating that dropping the arena causes its owned
/// instances of `T` to be dropped.
- _own: marker::PhantomData<T>,
+ _own: PhantomData<T>,
}
struct TypedArenaChunk<T> {
- marker: marker::PhantomData<T>,
-
/// Pointer to the next arena segment.
- next: *mut TypedArenaChunk<T>,
-
- /// The number of elements that this chunk can hold.
- capacity: usize,
-
- // Objects follow here, suitably aligned.
-}
-
-fn calculate_size<T>(capacity: usize) -> usize {
- let mut size = mem::size_of::<TypedArenaChunk<T>>();
- size = round_up(size, mem::align_of::<T>());
- let elem_size = mem::size_of::<T>();
- let elems_size = elem_size.checked_mul(capacity).unwrap();
- size = size.checked_add(elems_size).unwrap();
- size
+ storage: RawVec<T>,
}
impl<T> TypedArenaChunk<T> {
#[inline]
- unsafe fn new(next: *mut TypedArenaChunk<T>, capacity: usize) -> *mut TypedArenaChunk<T> {
- let size = calculate_size::<T>(capacity);
- let chunk =
- allocate(size, mem::align_of::<TypedArenaChunk<T>>()) as *mut TypedArenaChunk<T>;
- if chunk.is_null() {
- alloc::oom()
- }
- (*chunk).next = next;
- (*chunk).capacity = capacity;
- chunk
+ unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
+ TypedArenaChunk { storage: RawVec::with_capacity(capacity) }
}
- /// Destroys this arena chunk. If the type descriptor is supplied, the
- /// drop glue is called; otherwise, drop glue is not called.
+ /// Destroys this arena chunk.
#[inline]
unsafe fn destroy(&mut self, len: usize) {
- // Destroy all the allocated objects.
+ // The branch on needs_drop() is an -O1 performance optimization.
+ // Without the branch, dropping TypedArena<u8> takes linear time.
if intrinsics::needs_drop::<T>() {
let mut start = self.start();
+ // Destroy all allocated objects.
for _ in 0..len {
- ptr::read(start as *const T); // run the destructor on the pointer
- start = start.offset(mem::size_of::<T>() as isize)
+ ptr::drop_in_place(start);
+ start = start.offset(1);
}
}
-
- // Destroy the next chunk.
- let next = self.next;
- let size = calculate_size::<T>(self.capacity);
- let self_ptr: *mut TypedArenaChunk<T> = self;
- deallocate(self_ptr as *mut u8,
- size,
- mem::align_of::<TypedArenaChunk<T>>());
- if !next.is_null() {
- let capacity = (*next).capacity;
- (*next).destroy(capacity);
- }
}
// Returns a pointer to the first allocated object.
#[inline]
- fn start(&self) -> *const u8 {
- let this: *const TypedArenaChunk<T> = self;
- unsafe { round_up(this.offset(1) as usize, mem::align_of::<T>()) as *const u8 }
+ fn start(&self) -> *mut T {
+ self.storage.ptr()
}
// Returns a pointer to the end of the allocated space.
#[inline]
- fn end(&self) -> *const u8 {
+ fn end(&self) -> *mut T {
unsafe {
- let size = mem::size_of::<T>().checked_mul(self.capacity).unwrap();
- self.start().offset(size as isize)
+ if mem::size_of::<T>() == 0 {
+ // A pointer as large as possible for zero-sized elements.
+ !0 as *mut T
+ } else {
+ self.start().offset(self.storage.cap() as isize)
+ }
}
}
}
+const PAGE: usize = 4096;
+
impl<T> TypedArena<T> {
- /// Creates a new `TypedArena` with preallocated space for eight objects.
+ /// Creates a new `TypedArena` with preallocated space for many objects.
#[inline]
pub fn new() -> TypedArena<T> {
- TypedArena::with_capacity(8)
+ // Reserve at least one page.
+ let elem_size = cmp::max(1, mem::size_of::<T>());
+ TypedArena::with_capacity(PAGE / elem_size)
}
/// Creates a new `TypedArena` with preallocated space for the given number of
#[inline]
pub fn with_capacity(capacity: usize) -> TypedArena<T> {
unsafe {
- let chunk = TypedArenaChunk::<T>::new(ptr::null_mut(), capacity);
+ let chunk = TypedArenaChunk::<T>::new(cmp::max(1, capacity));
TypedArena {
- ptr: Cell::new((*chunk).start() as *const T),
- end: Cell::new((*chunk).end() as *const T),
- first: RefCell::new(chunk),
- _own: marker::PhantomData,
+ ptr: Cell::new(chunk.start()),
+ end: Cell::new(chunk.end()),
+ chunks: RefCell::new(vec![chunk]),
+ _own: PhantomData,
}
}
}
}
unsafe {
- let ptr: &mut T = &mut *(self.ptr.get() as *mut T);
- ptr::write(ptr, object);
- self.ptr.set(self.ptr.get().offset(1));
- ptr
+ if mem::size_of::<T>() == 0 {
+ self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T);
+ let ptr = heap::EMPTY as *mut T;
+ // Don't drop the object. This `write` is equivalent to `forget`.
+ ptr::write(ptr, object);
+ &mut *ptr
+ } else {
+ let ptr = self.ptr.get();
+ // Advance the pointer.
+ self.ptr.set(self.ptr.get().offset(1));
+ // Write into uninitialized memory.
+ ptr::write(ptr, object);
+ &mut *ptr
+ }
}
}
/// Grows the arena.
#[inline(never)]
+ #[cold]
fn grow(&self) {
unsafe {
- let chunk = *self.first.borrow_mut();
- let new_capacity = (*chunk).capacity.checked_mul(2).unwrap();
- let chunk = TypedArenaChunk::<T>::new(chunk, new_capacity);
- self.ptr.set((*chunk).start() as *const T);
- self.end.set((*chunk).end() as *const T);
- *self.first.borrow_mut() = chunk
+ let mut chunks = self.chunks.borrow_mut();
+ let prev_capacity = chunks.last().unwrap().storage.cap();
+ let new_capacity = prev_capacity.checked_mul(2).unwrap();
+ if chunks.last_mut().unwrap().storage.double_in_place() {
+ self.end.set(chunks.last().unwrap().end());
+ } else {
+ let chunk = TypedArenaChunk::<T>::new(new_capacity);
+ self.ptr.set(chunk.start());
+ self.end.set(chunk.end());
+ chunks.push(chunk);
+ }
+ }
+ }
+ /// Clears the arena. Deallocates all but the longest chunk which may be reused.
+ pub fn clear(&mut self) {
+ unsafe {
+ // Clear the last chunk, which is partially filled.
+ let mut chunks_borrow = self.chunks.borrow_mut();
+ let last_idx = chunks_borrow.len() - 1;
+ self.clear_last_chunk(&mut chunks_borrow[last_idx]);
+ // If `T` is ZST, code below has no effect.
+ for mut chunk in chunks_borrow.drain(..last_idx) {
+ let cap = chunk.storage.cap();
+ chunk.destroy(cap);
+ }
+ }
+ }
+
+ // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other
+ // chunks.
+ fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk<T>) {
+ // Determine how much was filled.
+ let start = last_chunk.start() as usize;
+ // We obtain the value of the pointer to the first uninitialized element.
+ let end = self.ptr.get() as usize;
+ // We then calculate the number of elements to be dropped in the last chunk,
+ // which is the filled area's length.
+ let diff = if mem::size_of::<T>() == 0 {
+ // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
+ // the number of zero-sized values in the last and only chunk, just out of caution.
+ // Recall that `end` was incremented for each allocated value.
+ end - start
+ } else {
+ (end - start) / mem::size_of::<T>()
+ };
+ // Pass that to the `destroy` method.
+ unsafe {
+ last_chunk.destroy(diff);
}
+ // Reset the chunk.
+ self.ptr.set(last_chunk.start());
}
}
fn drop(&mut self) {
unsafe {
// Determine how much was filled.
- let start = self.first.borrow().as_ref().unwrap().start() as usize;
- let end = self.ptr.get() as usize;
- let diff = (end - start) / mem::size_of::<T>();
-
- // Pass that to the `destroy` method.
- (**self.first.borrow_mut()).destroy(diff)
+ let mut chunks_borrow = self.chunks.borrow_mut();
+ let mut last_chunk = chunks_borrow.pop().unwrap();
+ // Drop the contents of the last chunk.
+ self.clear_last_chunk(&mut last_chunk);
+ // The last chunk will be dropped. Destroy all other chunks.
+ for chunk in chunks_borrow.iter_mut() {
+ let cap = chunk.storage.cap();
+ chunk.destroy(cap);
+ }
+ // RawVec handles deallocation of `last_chunk` and `self.chunks`.
}
}
}
+unsafe impl<T: Send> Send for TypedArena<T> {}
+
#[cfg(test)]
mod tests {
extern crate test;
use self::test::Bencher;
use super::{Arena, TypedArena};
+ use std::cell::Cell;
+ use std::rc::Rc;
#[allow(dead_code)]
+ #[derive(Debug, Eq, PartialEq)]
struct Point {
x: i32,
y: i32,
#[bench]
pub fn bench_copy_nonarena(b: &mut Bencher) {
b.iter(|| {
- let _: Box<_> = box Point { x: 1, y: 2, z: 3 };
+ let _: Box<_> = Box::new(Point { x: 1, y: 2, z: 3 });
})
}
}
}
+ #[test]
+ pub fn test_typed_arena_zero_sized() {
+ let arena = TypedArena::new();
+ for _ in 0..100000 {
+ arena.alloc(());
+ }
+ }
+
+ #[test]
+ pub fn test_arena_zero_sized() {
+ let arena = Arena::new();
+ let mut points = vec![];
+ for _ in 0..1000 {
+ for _ in 0..100 {
+ arena.alloc(|| ());
+ }
+ let point = arena.alloc(|| Point { x: 1, y: 2, z: 3 });
+ points.push(point);
+ }
+ for point in &points {
+ assert_eq!(**point, Point { x: 1, y: 2, z: 3 });
+ }
+ }
+
+ #[test]
+ pub fn test_typed_arena_clear() {
+ let mut arena = TypedArena::new();
+ for _ in 0..10 {
+ arena.clear();
+ for _ in 0..10000 {
+ arena.alloc(Point { x: 1, y: 2, z: 3 });
+ }
+ }
+ }
+
+ #[test]
+ pub fn test_arena_clear() {
+ let mut arena = Arena::new();
+ for _ in 0..10 {
+ arena.clear();
+ for _ in 0..10000 {
+ arena.alloc(|| Point { x: 1, y: 2, z: 3 });
+ arena.alloc(|| {
+ Noncopy {
+ string: "hello world".to_string(),
+ array: vec![],
+ }
+ });
+ }
+ }
+ }
+
+ #[test]
+ pub fn test_arena_alloc_bytes() {
+ let arena = Arena::new();
+ for i in 0..10000 {
+ arena.alloc(|| Point { x: 1, y: 2, z: 3 });
+ for byte in arena.alloc_bytes(i % 42).iter_mut() {
+ *byte = i as u8;
+ }
+ }
+ }
+
+ #[test]
+ fn test_arena_destructors() {
+ let arena = Arena::new();
+ for i in 0..10 {
+ // Arena allocate something with drop glue to make sure it
+ // doesn't leak.
+ arena.alloc(|| Rc::new(i));
+ // Allocate something with funny size and alignment, to keep
+ // things interesting.
+ arena.alloc(|| [0u8, 1u8, 2u8]);
+ }
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_arena_destructors_fail() {
+ let arena = Arena::new();
+ // Put some stuff in the arena.
+ for i in 0..10 {
+ // Arena allocate something with drop glue to make sure it
+ // doesn't leak.
+ arena.alloc(|| Rc::new(i));
+ // Allocate something with funny size and alignment, to keep
+ // things interesting.
+ arena.alloc(|| [0u8, 1, 2]);
+ }
+ // Now, panic while allocating
+ arena.alloc::<Rc<i32>, _>(|| {
+ panic!();
+ });
+ }
+
+ // Drop tests
+
+ struct DropCounter<'a> {
+ count: &'a Cell<u32>,
+ }
+
+ impl<'a> Drop for DropCounter<'a> {
+ fn drop(&mut self) {
+ self.count.set(self.count.get() + 1);
+ }
+ }
+
+ #[test]
+ fn test_arena_drop_count() {
+ let counter = Cell::new(0);
+ {
+ let arena = Arena::new();
+ for _ in 0..100 {
+ // Allocate something with drop glue to make sure it doesn't leak.
+ arena.alloc(|| DropCounter { count: &counter });
+ // Allocate something with funny size and alignment, to keep
+ // things interesting.
+ arena.alloc(|| [0u8, 1u8, 2u8]);
+ }
+ // dropping
+ };
+ assert_eq!(counter.get(), 100);
+ }
+
+ #[test]
+ fn test_arena_drop_on_clear() {
+ let counter = Cell::new(0);
+ for i in 0..10 {
+ let mut arena = Arena::new();
+ for _ in 0..100 {
+ // Allocate something with drop glue to make sure it doesn't leak.
+ arena.alloc(|| DropCounter { count: &counter });
+ // Allocate something with funny size and alignment, to keep
+ // things interesting.
+ arena.alloc(|| [0u8, 1u8, 2u8]);
+ }
+ arena.clear();
+ assert_eq!(counter.get(), i * 100 + 100);
+ }
+ }
+
+ #[test]
+ fn test_typed_arena_drop_count() {
+ let counter = Cell::new(0);
+ {
+ let arena: TypedArena<DropCounter> = TypedArena::new();
+ for _ in 0..100 {
+ // Allocate something with drop glue to make sure it doesn't leak.
+ arena.alloc(DropCounter { count: &counter });
+ }
+ };
+ assert_eq!(counter.get(), 100);
+ }
+
+ #[test]
+ fn test_typed_arena_drop_on_clear() {
+ let counter = Cell::new(0);
+ let mut arena: TypedArena<DropCounter> = TypedArena::new();
+ for i in 0..10 {
+ for _ in 0..100 {
+ // Allocate something with drop glue to make sure it doesn't leak.
+ arena.alloc(DropCounter { count: &counter });
+ }
+ arena.clear();
+ assert_eq!(counter.get(), i * 100 + 100);
+ }
+ }
+
+ thread_local! {
+ static DROP_COUNTER: Cell<u32> = Cell::new(0)
+ }
+
+ struct SmallDroppable;
+
+ impl Drop for SmallDroppable {
+ fn drop(&mut self) {
+ DROP_COUNTER.with(|c| c.set(c.get() + 1));
+ }
+ }
+
+ #[test]
+ fn test_arena_drop_small_count() {
+ DROP_COUNTER.with(|c| c.set(0));
+ {
+ let arena = Arena::new();
+ for _ in 0..10 {
+ for _ in 0..10 {
+ // Allocate something with drop glue to make sure it doesn't leak.
+ arena.alloc(|| SmallDroppable);
+ }
+ // Allocate something with funny size and alignment, to keep
+ // things interesting.
+ arena.alloc(|| [0u8, 1u8, 2u8]);
+ }
+ // dropping
+ };
+ assert_eq!(DROP_COUNTER.with(|c| c.get()), 100);
+ }
+
+ #[test]
+ fn test_typed_arena_drop_small_count() {
+ DROP_COUNTER.with(|c| c.set(0));
+ {
+ let arena: TypedArena<SmallDroppable> = TypedArena::new();
+ for _ in 0..100 {
+ // Allocate something with drop glue to make sure it doesn't leak.
+ arena.alloc(SmallDroppable);
+ }
+ // dropping
+ };
+ assert_eq!(DROP_COUNTER.with(|c| c.get()), 100);
+ }
+
#[bench]
pub fn bench_noncopy(b: &mut Bencher) {
let arena = TypedArena::new();
#[bench]
pub fn bench_noncopy_nonarena(b: &mut Bencher) {
b.iter(|| {
- let _: Box<_> = box Noncopy {
+ let _: Box<_> = Box::new(Noncopy {
string: "hello world".to_string(),
array: vec![1, 2, 3, 4, 5],
- };
+ });
})
}
* configure.ac: Add --enable-host-shared.
* configure: Regenerate.
-
+\f
Copyright (C) 2013-2014 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
# met:
# (1) Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
+# notice, this list of conditions and the following disclaimer.
# (2) Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
-# distribution.
+# distribution.
# (3) The name of the author may not be used to
# endorse or promote products derived from this software without
# met:
# (1) Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
+# notice, this list of conditions and the following disclaimer.
# (2) Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
-# distribution.
+# distribution.
# (3) The name of the author may not be used to
# endorse or promote products derived from this software without
$(LDFLAGS) -o $@
SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \
$(btest_SOURCES) $(stest_SOURCES)
-MULTISRCTOP =
-MULTIBUILDTOP =
-MULTIDIRS =
-MULTISUBDIR =
+MULTISRCTOP =
+MULTIBUILDTOP =
+MULTIDIRS =
+MULTISUBDIR =
MULTIDO = true
MULTICLEAN = true
am__can_run_installinfo = \
stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
@rm -f stamp-h1
cd $(top_builddir) && $(SHELL) ./config.status config.h
-$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
($(am__cd) $(top_srcdir) && $(AUTOHEADER))
rm -f stamp-h1
touch $@
echo "rm -f \"$${dir}/so_locations\""; \
rm -f "$${dir}/so_locations"; \
done
-libbacktrace.la: $(libbacktrace_la_OBJECTS) $(libbacktrace_la_DEPENDENCIES) $(EXTRA_libbacktrace_la_DEPENDENCIES)
+libbacktrace.la: $(libbacktrace_la_OBJECTS) $(libbacktrace_la_DEPENDENCIES) $(EXTRA_libbacktrace_la_DEPENDENCIES)
$(LINK) $(libbacktrace_la_OBJECTS) $(libbacktrace_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES)
+btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES)
@rm -f btest$(EXEEXT)
$(btest_LINK) $(btest_OBJECTS) $(btest_LDADD) $(LIBS)
-stest$(EXEEXT): $(stest_OBJECTS) $(stest_DEPENDENCIES) $(EXTRA_stest_DEPENDENCIES)
+stest$(EXEEXT): $(stest_OBJECTS) $(stest_DEPENDENCIES) $(EXTRA_stest_DEPENDENCIES)
@rm -f stest$(EXEEXT)
$(LINK) $(stest_OBJECTS) $(stest_LDADD) $(LIBS)
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
(unsigned int) bdata.index, j + 1);
bdata.failed = 1;
}
- }
+ }
check ("test3", 0, all, f3line, "f23", &bdata.failed);
check ("test3", 1, all, f2line, "f22", &bdata.failed);
# met:
# (1) Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
+# notice, this list of conditions and the following disclaimer.
# (2) Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
-# distribution.
-
+# distribution.
+
# (3) The name of the author may not be used to
# endorse or promote products derived from this software without
# specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
static int
find_address_ranges (struct backtrace_state *state, uintptr_t base_address,
- struct dwarf_buf *unit_buf,
+ struct dwarf_buf *unit_buf,
const unsigned char *dwarf_str, size_t dwarf_str_size,
const unsigned char *dwarf_ranges,
size_t dwarf_ranges_size,
if (!advance (line_buf, hdrlen))
return 0;
-
+
hdr->min_insn_len = read_byte (&hdr_buf);
if (hdr->version < 4)
hdr->max_ops_per_insn = 1;
/* We don't care about default_is_stmt. */
read_byte (&hdr_buf);
-
+
hdr->line_base = read_sbyte (&hdr_buf);
hdr->line_range = read_byte (&hdr_buf);
/* This file declares various DWARF-related constants using a set of
macros which can be redefined by the including file.
-
+
The macros are in sections. Each section corresponds to a single
set of DWARF constants and has a corresponding key. The key is
used in all the macro names.
-
+
The sections are TAG (for DW_TAG_ constants), FORM (DW_FORM_), AT
(DW_AT_), OP (DW_OP_), ATE (DW_ATE_), and CFA (DW_CFA_).
-
+
Using TAG as an example, the following macros may be used for each
key:
-
+
DW_FIRST_TAG(name, value) - Introduce the first DW_TAG constant.
-
+
DW_TAG(name, value) - Define a subsequent constant.
-
+
DW_TAG_DUP(name, value) - Define a subsequent constant whose value
is a duplicate of some other constant. Not all keys use the _DUP
macro form. If more than one name shares a value, then the base
(DW_TAG) form will be the preferred name and DW_TAG_DUP will hold
any alternate names.
-
+
DW_END_TAG - Invoked at the end of the DW_TAG constants. */
DW_FIRST_TAG (DW_TAG_padding, 0x00)
DW_MACRO_GNU_lo_user = 0xe0,
DW_MACRO_GNU_hi_user = 0xff
};
-
+\f
/* @@@ For use with GNU frame unwind information. */
#define DW_EH_PE_absptr 0x00
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
met:
(1) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
- distribution.
-
+ distribution.
+
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
self.data.pop().map(|mut item| {
if !self.is_empty() {
swap(&mut item, &mut self.data[0]);
- self.sift_down(0);
+ self.sift_down_to_bottom(0);
}
item
})
self.sift_down_range(pos, len);
}
+ /// Take an element at `pos` and move it all the way down the heap,
+ /// then sift it up to its position.
+ ///
+ /// Note: This is faster when the element is known to be large / should
+ /// be closer to the bottom.
+ fn sift_down_to_bottom(&mut self, mut pos: usize) {
+ let end = self.len();
+ let start = pos;
+ unsafe {
+ let mut hole = Hole::new(&mut self.data, pos);
+ let mut child = 2 * pos + 1;
+ while child < end {
+ let right = child + 1;
+ // compare with the greater of the two children
+ if right < end && !(hole.get(child) > hole.get(right)) {
+ child = right;
+ }
+ hole.move_to(child);
+ child = 2 * hole.pos() + 1;
+ }
+ pos = hole.pos;
+ }
+ self.sift_up(start, pos);
+ }
+
/// Returns the length of the binary heap.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn len(&self) -> usize {
Utf16Units { encoder: Utf16Encoder::new(self[..].chars()) }
}
- /// Returns `true` if the given `&str` is a sub-slice of this string slice.
+ /// Returns `true` if the given pattern matches a sub-slice of
+ /// this string slice.
///
- /// Returns `false` if it's not.
+ /// Returns `false` if it does not.
///
/// # Examples
///
core_str::StrExt::contains(self, pat)
}
- /// Returns `true` if the given `&str` is a prefix of this string slice.
+ /// Returns `true` if the given pattern matches a prefix of this
+ /// string slice.
///
- /// Returns `false` if it's not.
+ /// Returns `false` if it does not.
///
/// # Examples
///
core_str::StrExt::starts_with(self, pat)
}
- /// Returns `true` if the given `&str` is a suffix of this string slice.
+ /// Returns `true` if the given pattern matches a suffix of this
+ /// string slice.
///
- /// Returns `false` if not.
+ /// Returns `false` if it does not.
///
/// # Examples
///
core_str::StrExt::parse(self)
}
- /// Replaces all occurrences of one string with another.
+ /// Replaces all matches of a pattern with another string.
///
/// `replace` creates a new [`String`], and copies the data from this string slice into it.
- /// While doing so, it attempts to find a sub-`&str`. If it finds it, it replaces it with
- /// the replacement string slice.
+ /// While doing so, it attempts to find matches of a pattern. If it finds any, it
+ /// replaces them with the replacement string slice.
///
/// [`String`]: string/struct.String.html
///
/// assert_eq!("this is new", s.replace("old", "new"));
/// ```
///
- /// When a `&str` isn't found:
+ /// When the pattern doesn't match:
///
/// ```
/// let s = "this is old";
/// assert_eq!(s, s.replace("cookie monster", "little lamb"));
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn replace(&self, from: &str, to: &str) -> String {
+ pub fn replace<'a, P: Pattern<'a>>(&'a self, from: P, to: &str) -> String {
let mut result = String::new();
let mut last_end = 0;
for (start, part) in self.match_indices(from) {
//!
//! [`String`]: struct.String.html
//! [`ToString`]: trait.ToString.html
+//!
+//! # Examples
+//!
+//! There are multiple ways to create a new `String` from a string literal:
+//!
+//! ```rust
+//! let s = "Hello".to_string();
+//!
+//! let s = String::from("world");
+//! let s: String = "also this".into();
+//! ```
+//!
+//! You can create a new `String` from an existing one by concatenating with
+//! `+`:
+//!
+//! ```rust
+//! let s = "Hello".to_string();
+//!
+//! let message = s + " world!";
+//! ```
+//!
+//! If you have a vector of valid UTF-8 bytes, you can make a `String` out of
+//! it. You can do the reverse too.
+//!
+//! ```rust
+//! let sparkle_heart = vec![240, 159, 146, 150];
+//!
+//! // We know these bytes are valid, so we'll use `unwrap()`.
+//! let sparkle_heart = String::from_utf8(sparkle_heart).unwrap();
+//!
+//! assert_eq!("💖", sparkle_heart);
+//!
+//! let bytes = sparkle_heart.into_bytes();
+//!
+//! assert_eq!(bytes, [240, 159, 146, 150]);
+//! ```
#![stable(feature = "rust1", since = "1.0.0")]
assert_eq!(data.replace(d, repl), data);
}
+#[test]
+fn test_replace_pattern() {
+ let data = "abcdαβγδabcdαβγδ";
+ assert_eq!(data.replace("dαβ", "😺😺😺"), "abc😺😺😺γδabc😺😺😺γδ");
+ assert_eq!(data.replace('γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ");
+ assert_eq!(data.replace(&['a', 'γ'] as &[_], "😺😺😺"), "😺😺😺bcdαβ😺😺😺δ😺😺😺bcdαβ😺😺😺δ");
+ assert_eq!(data.replace(|c| c == 'γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ");
+}
+
#[test]
fn test_slice() {
assert_eq!("ab", &"abc"[0..2]);
}
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Debug for PhantomData<T> {
+impl<T: ?Sized> Debug for PhantomData<T> {
fn fmt(&self, f: &mut Formatter) -> Result {
f.pad("PhantomData")
}
///
/// [module-level documentation]: index.html
/// [impl]: index.html#implementing-iterator
-#[lang = "iterator"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented = "`{Self}` is not an iterator; maybe try calling \
`.iter()` or a similar method"]
if f > T::max_sig() {
return None;
}
- let e = e as i16; // Can't overflow because e.abs() <= LOG5_OF_EXP_N
// The case e < 0 cannot be folded into the other branch. Negative powers result in
// a repeating fractional part in binary, which are rounded, which causes real
// (and occasioally quite significant!) errors in the final result.
- // The case `e == 0`, however, is unnecessary for correctness. It's just measurably faster.
- if e == 0 {
- Some(T::from_int(f))
- } else if e > 0 {
- Some(T::from_int(f) * fp_to_float(power_of_ten(e)))
+ if e >= 0 {
+ Some(T::from_int(f) * T::short_fast_pow10(e as usize))
} else {
- Some(T::from_int(f) / fp_to_float(power_of_ten(-e)))
+ Some(T::from_int(f) / T::short_fast_pow10(e.abs() as usize))
}
}
use num::FpCategory::{Infinite, Zero, Subnormal, Normal, Nan};
use num::Float;
use num::dec2flt::num::{self, Big};
+use num::dec2flt::table;
#[derive(Copy, Clone, Debug)]
pub struct Unpacked {
/// represented, the other code in this module makes sure to never let that happen.
fn from_int(x: u64) -> Self;
+ /// Get the value 10^e from a pre-computed table. Panics for e >= ceil_log5_of_max_sig().
+ fn short_fast_pow10(e: usize) -> Self;
+
// FIXME Everything that follows should be associated constants, but taking the value of an
// associated constant from a type parameter does not work (yet?)
// A possible workaround is having a `FloatInfo` struct for all the constants, but so far
x as f32
}
+ fn short_fast_pow10(e: usize) -> Self {
+ table::F32_SHORT_POWERS[e]
+ }
+
fn max_normal_digits() -> usize {
35
}
x as f64
}
+ fn short_fast_pow10(e: usize) -> Self {
+ table::F64_SHORT_POWERS[e]
+ }
+
fn max_normal_digits() -> usize {
305
}
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// Table of approximations of powers of ten.
-// DO NOT MODIFY: Generated by a src/etc/dec2flt_table.py
+
+//! Tables of approximations of powers of ten.
+//! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py`
+
pub const MIN_E: i16 = -305;
pub const MAX_E: i16 = 305;
946,
950,
]);
+
+pub const F32_SHORT_POWERS: [f32; 11] = [
+ 1e0,
+ 1e1,
+ 1e2,
+ 1e3,
+ 1e4,
+ 1e5,
+ 1e6,
+ 1e7,
+ 1e8,
+ 1e9,
+ 1e10,
+];
+
+pub const F64_SHORT_POWERS: [f64; 23] = [
+ 1e0,
+ 1e1,
+ 1e2,
+ 1e3,
+ 1e4,
+ 1e5,
+ 1e6,
+ 1e7,
+ 1e8,
+ 1e9,
+ 1e10,
+ 1e11,
+ 1e12,
+ 1e13,
+ 1e14,
+ 1e15,
+ 1e16,
+ 1e17,
+ 1e18,
+ 1e19,
+ 1e20,
+ 1e21,
+ 1e22,
+];
// `Int` + `SignedInt` implemented for signed integers
macro_rules! int_impl {
- ($ActualT:ty, $UnsignedT:ty, $BITS:expr,
+ ($ActualT:ident, $UnsignedT:ty, $BITS:expr,
$add_with_overflow:path,
$sub_with_overflow:path,
$mul_with_overflow:path) => {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn checked_add(self, other: Self) -> Option<Self> {
- checked_op!($ActualT, $add_with_overflow, self, other)
+ let (a, b) = self.overflowing_add(other);
+ if b {None} else {Some(a)}
}
/// Checked integer subtraction. Computes `self - other`, returning
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn checked_sub(self, other: Self) -> Option<Self> {
- checked_op!($ActualT, $sub_with_overflow, self, other)
+ let (a, b) = self.overflowing_sub(other);
+ if b {None} else {Some(a)}
}
/// Checked integer multiplication. Computes `self * other`, returning
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn checked_mul(self, other: Self) -> Option<Self> {
- checked_op!($ActualT, $mul_with_overflow, self, other)
+ let (a, b) = self.overflowing_mul(other);
+ if b {None} else {Some(a)}
}
/// Checked integer division. Computes `self / other`, returning `None`
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn checked_div(self, other: Self) -> Option<Self> {
- match other {
- 0 => None,
- -1 if self == Self::min_value()
- => None,
- other => Some(self / other),
+ if other == 0 {
+ None
+ } else {
+ let (a, b) = self.overflowing_div(other);
+ if b {None} else {Some(a)}
}
}
+ /// Checked integer remainder. Computes `self % other`, returning `None`
+ /// if `other == 0` or the operation results in underflow or overflow.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::i32;
+ ///
+ /// assert_eq!(5i32.checked_rem(2), Some(1));
+ /// assert_eq!(5i32.checked_rem(0), None);
+ /// assert_eq!(i32::MIN.checked_rem(-1), None);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn checked_rem(self, other: Self) -> Option<Self> {
+ if other == 0 {
+ None
+ } else {
+ let (a, b) = self.overflowing_rem(other);
+ if b {None} else {Some(a)}
+ }
+ }
+
+ /// Checked negation. Computes `!self`, returning `None` if `self ==
+ /// MIN`.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::i32;
+ ///
+ /// assert_eq!(5i32.checked_neg(), Some(-5));
+ /// assert_eq!(i32::MIN.checked_neg(), None);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn checked_neg(self) -> Option<Self> {
+ let (a, b) = self.overflowing_neg();
+ if b {None} else {Some(a)}
+ }
+
+ /// Checked shift left. Computes `self << rhs`, returning `None`
+ /// if `rhs` is larger than or equal to the number of bits in `self`.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0x10i32.checked_shl(4), Some(0x100));
+ /// assert_eq!(0x10i32.checked_shl(33), None);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn checked_shl(self, rhs: u32) -> Option<Self> {
+ let (a, b) = self.overflowing_shl(rhs);
+ if b {None} else {Some(a)}
+ }
+
+ /// Checked shift right. Computes `self >> rhs`, returning `None`
+ /// if `rhs` is larger than or equal to the number of bits in `self`.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0x10i32.checked_shr(4), Some(0x1));
+ /// assert_eq!(0x10i32.checked_shr(33), None);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn checked_shr(self, rhs: u32) -> Option<Self> {
+ let (a, b) = self.overflowing_shr(rhs);
+ if b {None} else {Some(a)}
+ }
+
/// Saturating integer addition. Computes `self + other`, saturating at
/// the numeric bounds instead of overflowing.
///
#[inline]
pub fn saturating_add(self, other: Self) -> Self {
match self.checked_add(other) {
- Some(x) => x,
+ Some(x) => x,
None if other >= Self::zero() => Self::max_value(),
None => Self::min_value(),
}
#[inline]
pub fn saturating_sub(self, other: Self) -> Self {
match self.checked_sub(other) {
- Some(x) => x,
+ Some(x) => x,
None if other >= Self::zero() => Self::min_value(),
None => Self::max_value(),
}
}
+ /// Saturating integer multiplication. Computes `self * other`,
+ /// saturating at the numeric bounds instead of overflowing.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::i32;
+ ///
+ /// assert_eq!(100i32.saturating_mul(127), 12700);
+ /// assert_eq!((1i32 << 23).saturating_mul(1 << 23), i32::MAX);
+ /// assert_eq!((-1i32 << 23).saturating_mul(1 << 23), i32::MIN);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn saturating_mul(self, other: Self) -> Self {
+ self.checked_mul(other).unwrap_or_else(|| {
+ if (self < 0 && other < 0) || (self > 0 && other > 0) {
+ Self::max_value()
+ } else {
+ Self::min_value()
+ }
+ })
+ }
+
/// Wrapping (modular) addition. Computes `self + other`,
/// wrapping around at the boundary of the type.
///
/// in the type. In such a case, this function returns `MIN`
/// itself.
///
+ /// # Panics
+ ///
+ /// This function will panic if `rhs` is 0.
+ ///
/// # Examples
///
/// Basic usage:
/// -1` on a signed type (where `MIN` is the negative
/// minimal value). In such a case, this function returns `0`.
///
+ /// # Panics
+ ///
+ /// This function will panic if `rhs` is 0.
+ ///
/// # Examples
///
/// Basic usage:
self.overflowing_shr(rhs).0
}
+ /// Calculates `self` + `rhs`
+ ///
+ /// Returns a tuple of the addition along with a boolean indicating
+ /// whether an arithmetic overflow would occur. If an overflow would
+ /// have occurred then the wrapped value is returned.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::i32;
+ ///
+ /// assert_eq!(5i32.overflowing_add(2), (7, false));
+ /// assert_eq!(i32::MAX.overflowing_add(1), (i32::MIN, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_add(self, rhs: Self) -> (Self, bool) {
+ unsafe {
+ let (a, b) = $add_with_overflow(self as $ActualT,
+ rhs as $ActualT);
+ (a as Self, b)
+ }
+ }
+
+ /// Calculates `self` - `rhs`
+ ///
+ /// Returns a tuple of the subtraction along with a boolean indicating
+ /// whether an arithmetic overflow would occur. If an overflow would
+ /// have occurred then the wrapped value is returned.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::i32;
+ ///
+ /// assert_eq!(5i32.overflowing_sub(2), (3, false));
+ /// assert_eq!(i32::MIN.overflowing_sub(1), (i32::MAX, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
+ unsafe {
+ let (a, b) = $sub_with_overflow(self as $ActualT,
+ rhs as $ActualT);
+ (a as Self, b)
+ }
+ }
+
+ /// Calculates the multiplication of `self` and `rhs`.
+ ///
+ /// Returns a tuple of the multiplication along with a boolean
+ /// indicating whether an arithmetic overflow would occur. If an
+ /// overflow would have occurred then the wrapped value is returned.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(5i32.overflowing_mul(2), (10, false));
+ /// assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
+ unsafe {
+ let (a, b) = $mul_with_overflow(self as $ActualT,
+ rhs as $ActualT);
+ (a as Self, b)
+ }
+ }
+
+ /// Calculates the divisor when `self` is divided by `rhs`.
+ ///
+ /// Returns a tuple of the divisor along with a boolean indicating
+ /// whether an arithmetic overflow would occur. If an overflow would
+ /// occur then self is returned.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if `rhs` is 0.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::i32;
+ ///
+ /// assert_eq!(5i32.overflowing_div(2), (2, false));
+ /// assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_div(self, rhs: Self) -> (Self, bool) {
+ if self == Self::min_value() && rhs == -1 {
+ (self, true)
+ } else {
+ (self / rhs, false)
+ }
+ }
+
+ /// Calculates the remainder when `self` is divided by `rhs`.
+ ///
+ /// Returns a tuple of the remainder after dividing along with a boolean
+ /// indicating whether an arithmetic overflow would occur. If an
+ /// overflow would occur then 0 is returned.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if `rhs` is 0.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::i32;
+ ///
+ /// assert_eq!(5i32.overflowing_rem(2), (1, false));
+ /// assert_eq!(i32::MIN.overflowing_rem(-1), (0, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
+ if self == Self::min_value() && rhs == -1 {
+ (0, true)
+ } else {
+ (self % rhs, false)
+ }
+ }
+
+ /// Negates self, overflowing if this is equal to the minimum value.
+ ///
+ /// Returns a tuple of the negated version of self along with a boolean
+ /// indicating whether an overflow happened. If `self` is the minimum
+ /// value (e.g. `i32::MIN` for values of type `i32`), then the minimum
+ /// value will be returned again and `true` will be returned for an
+ /// overflow happening.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::i32;
+ ///
+ /// assert_eq!(2i32.overflowing_neg(), (-2, false));
+ /// assert_eq!(i32::MIN.overflowing_neg(), (i32::MIN, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_neg(self) -> (Self, bool) {
+ if self == Self::min_value() {
+ (Self::min_value(), true)
+ } else {
+ (-self, false)
+ }
+ }
+
+ /// Shifts self left by `rhs` bits.
+ ///
+ /// Returns a tuple of the shifted version of self along with a boolean
+ /// indicating whether the shift value was larger than or equal to the
+ /// number of bits. If the shift value is too large, then value is
+ /// masked (N-1) where N is the number of bits, and this value is then
+ /// used to perform the shift.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0x10i32.overflowing_shl(4), (0x100, false));
+ /// assert_eq!(0x10i32.overflowing_shl(36), (0x100, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
+ (self << (rhs & ($BITS - 1)), (rhs > ($BITS - 1)))
+ }
+
+ /// Shifts self right by `rhs` bits.
+ ///
+ /// Returns a tuple of the shifted version of self along with a boolean
+ /// indicating whether the shift value was larger than or equal to the
+ /// number of bits. If the shift value is too large, then value is
+ /// masked (N-1) where N is the number of bits, and this value is then
+ /// used to perform the shift.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0x10i32.overflowing_shr(4), (0x1, false));
+ /// assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
+ (self >> (rhs & ($BITS - 1)), (rhs > ($BITS - 1)))
+ }
+
/// Raises self to the power of `exp`, using exponentiation by squaring.
///
/// # Examples
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn checked_add(self, other: Self) -> Option<Self> {
- checked_op!($ActualT, $add_with_overflow, self, other)
+ let (a, b) = self.overflowing_add(other);
+ if b {None} else {Some(a)}
}
/// Checked integer subtraction. Computes `self - other`, returning
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn checked_sub(self, other: Self) -> Option<Self> {
- checked_op!($ActualT, $sub_with_overflow, self, other)
+ let (a, b) = self.overflowing_sub(other);
+ if b {None} else {Some(a)}
}
/// Checked integer multiplication. Computes `self * other`, returning
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn checked_mul(self, other: Self) -> Option<Self> {
- checked_op!($ActualT, $mul_with_overflow, self, other)
+ let (a, b) = self.overflowing_mul(other);
+ if b {None} else {Some(a)}
}
/// Checked integer division. Computes `self / other`, returning `None`
}
}
+ /// Checked integer remainder. Computes `self % other`, returning `None`
+ /// if `other == 0` or the operation results in underflow or overflow.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(5u32.checked_rem(2), Some(1));
+ /// assert_eq!(5u32.checked_rem(0), None);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn checked_rem(self, other: Self) -> Option<Self> {
+ if other == 0 {
+ None
+ } else {
+ Some(self % other)
+ }
+ }
+
+ /// Checked shift left. Computes `self << rhs`, returning `None`
+ /// if `rhs` is larger than or equal to the number of bits in `self`.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0x10u32.checked_shl(4), Some(0x100));
+ /// assert_eq!(0x10u32.checked_shl(33), None);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn checked_shl(self, rhs: u32) -> Option<Self> {
+ let (a, b) = self.overflowing_shl(rhs);
+ if b {None} else {Some(a)}
+ }
+
+ /// Checked shift right. Computes `self >> rhs`, returning `None`
+ /// if `rhs` is larger than or equal to the number of bits in `self`.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0x10u32.checked_shr(4), Some(0x1));
+ /// assert_eq!(0x10u32.checked_shr(33), None);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn checked_shr(self, rhs: u32) -> Option<Self> {
+ let (a, b) = self.overflowing_shr(rhs);
+ if b {None} else {Some(a)}
+ }
+
/// Saturating integer addition. Computes `self + other`, saturating at
/// the numeric bounds instead of overflowing.
///
}
}
+ /// Saturating integer multiplication. Computes `self * other`,
+ /// saturating at the numeric bounds instead of overflowing.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::u32;
+ ///
+ /// assert_eq!(100u32.saturating_mul(127), 12700);
+ /// assert_eq!((1u32 << 23).saturating_mul(1 << 23), u32::MAX);
+ /// ```
+ #[unstable(feature = "wrapping", issue = "27755")]
+ #[inline]
+ pub fn saturating_mul(self, other: Self) -> Self {
+ self.checked_mul(other).unwrap_or(Self::max_value())
+ }
+
/// Wrapping (modular) addition. Computes `self + other`,
/// wrapping around at the boundary of the type.
///
self.overflowing_shr(rhs).0
}
+ /// Calculates `self` + `rhs`
+ ///
+ /// Returns a tuple of the addition along with a boolean indicating
+ /// whether an arithmetic overflow would occur. If an overflow would
+ /// have occurred then the wrapped value is returned.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::u32;
+ ///
+ /// assert_eq!(5u32.overflowing_add(2), (7, false));
+ /// assert_eq!(u32::MAX.overflowing_add(1), (0, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_add(self, rhs: Self) -> (Self, bool) {
+ unsafe {
+ let (a, b) = $add_with_overflow(self as $ActualT,
+ rhs as $ActualT);
+ (a as Self, b)
+ }
+ }
+
+ /// Calculates `self` - `rhs`
+ ///
+ /// Returns a tuple of the subtraction along with a boolean indicating
+ /// whether an arithmetic overflow would occur. If an overflow would
+ /// have occurred then the wrapped value is returned.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// use std::u32;
+ ///
+ /// assert_eq!(5u32.overflowing_sub(2), (3, false));
+ /// assert_eq!(0u32.overflowing_sub(1), (u32::MAX, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
+ unsafe {
+ let (a, b) = $sub_with_overflow(self as $ActualT,
+ rhs as $ActualT);
+ (a as Self, b)
+ }
+ }
+
+ /// Calculates the multiplication of `self` and `rhs`.
+ ///
+ /// Returns a tuple of the multiplication along with a boolean
+ /// indicating whether an arithmetic overflow would occur. If an
+ /// overflow would have occurred then the wrapped value is returned.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(5u32.overflowing_mul(2), (10, false));
+ /// assert_eq!(1_000_000_000u32.overflowing_mul(10), (1410065408, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
+ unsafe {
+ let (a, b) = $mul_with_overflow(self as $ActualT,
+ rhs as $ActualT);
+ (a as Self, b)
+ }
+ }
+
+ /// Calculates the divisor when `self` is divided by `rhs`.
+ ///
+ /// Returns a tuple of the divisor along with a boolean indicating
+ /// whether an arithmetic overflow would occur. Note that for unsigned
+ /// integers overflow never occurs, so the second value is always
+ /// `false`.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if `rhs` is 0.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(5u32.overflowing_div(2), (2, false));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_div(self, rhs: Self) -> (Self, bool) {
+ (self / rhs, false)
+ }
+
+ /// Calculates the remainder when `self` is divided by `rhs`.
+ ///
+ /// Returns a tuple of the remainder after dividing along with a boolean
+ /// indicating whether an arithmetic overflow would occur. Note that for
+ /// unsigned integers overflow never occurs, so the second value is
+ /// always `false`.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if `rhs` is 0.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(5u32.overflowing_rem(2), (1, false));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
+ (self % rhs, false)
+ }
+
+ /// Negates self in an overflowing fashion.
+ ///
+ /// Returns `!self + 1` using wrapping operations to return the value
+ /// that represents the negation of this unsigned value. Note that for
+ /// positive unsigned values overflow always occurs, but negating 0 does
+ /// not overflow.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0u32.overflowing_neg(), (0, false));
+ /// assert_eq!(2u32.overflowing_neg(), (-2i32 as u32, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_neg(self) -> (Self, bool) {
+ ((!self).wrapping_add(1), self != 0)
+ }
+
+ /// Shifts self left by `rhs` bits.
+ ///
+ /// Returns a tuple of the shifted version of self along with a boolean
+ /// indicating whether the shift value was larger than or equal to the
+ /// number of bits. If the shift value is too large, then value is
+ /// masked (N-1) where N is the number of bits, and this value is then
+ /// used to perform the shift.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0x10u32.overflowing_shl(4), (0x100, false));
+ /// assert_eq!(0x10u32.overflowing_shl(36), (0x100, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
+ (self << (rhs & ($BITS - 1)), (rhs > ($BITS - 1)))
+ }
+
+ /// Shifts self right by `rhs` bits.
+ ///
+ /// Returns a tuple of the shifted version of self along with a boolean
+ /// indicating whether the shift value was larger than or equal to the
+ /// number of bits. If the shift value is too large, then value is
+ /// masked (N-1) where N is the number of bits, and this value is then
+ /// used to perform the shift.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage
+ ///
+ /// ```
+ /// #![feature(wrapping)]
+ ///
+ /// assert_eq!(0x10u32.overflowing_shr(4), (0x1, false));
+ /// assert_eq!(0x10u32.overflowing_shr(36), (0x1, true));
+ /// ```
+ #[inline]
+ #[unstable(feature = "wrapping", issue = "27755")]
+ pub fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
+ (self >> (rhs & ($BITS - 1)), (rhs > ($BITS - 1)))
+ }
+
/// Raises self to the power of `exp`, using exponentiation by squaring.
///
/// # Examples
-Subproject commit e0c0bf439add63a6a25a25ba47e4aec9547bf9af
+Subproject commit 95d6a00134f284e6b889d98f4c2cb4b285950327
// |
// type `i32` assigned to variable `x`
```
+
+Another situation in which this occurs is when you attempt to use the `try!`
+macro inside a function that does not return a `Result<T, E>`:
+
+```
+use std::fs::File;
+
+fn main() {
+ let mut f = try!(File::create("foo.txt"));
+}
+```
+
+This code gives an error like this:
+
+```text
+<std macros>:5:8: 6:42 error: mismatched types:
+ expected `()`,
+ found `core::result::Result<_, _>`
+ (expected (),
+ found enum `core::result::Result`) [E0308]
+```
+
+`try!` returns a `Result<T, E>`, and so the function must. But `main()` has
+`()` as its return type, hence the error.
"##,
E0309: r##"
"type parameter default erroneously allowed in invalid location"
}
+declare_lint! {
+ pub MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
+ Warn,
+ "unit struct or enum variant erroneously allowed to match via path::ident(..)"
+}
+
/// Does nothing as a lint pass, but registers some `Lint`s
/// which are used by other parts of the compiler.
#[derive(Copy, Clone)]
TRIVIAL_NUMERIC_CASTS,
PRIVATE_IN_PUBLIC,
INVALID_TYPE_PARAM_DEFAULT,
+ MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
CONST_ERR
)
}
use middle::const_eval::EvalHint::ExprTypeChecked;
use middle::def::*;
use middle::def_id::{DefId};
-use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init};
-use middle::expr_use_visitor::{JustWrite, LoanCause, MutateMode};
-use middle::expr_use_visitor::WriteAndRead;
+use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor};
+use middle::expr_use_visitor::{LoanCause, MutateMode};
use middle::expr_use_visitor as euv;
use middle::infer;
use middle::mem_categorization::{cmt};
fn decl_without_init(&mut self, _: NodeId, _: Span) {}
fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) {
match mode {
- JustWrite | WriteAndRead => {
+ MutateMode::JustWrite | MutateMode::WriteAndRead => {
span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard")
}
- Init => {}
+ MutateMode::Init => {}
}
}
}
//! normal visitor, which just walks the entire body in one shot, the
//! `ExprUseVisitor` determines how expressions are being used.
-pub use self::MutateMode::*;
pub use self::LoanCause::*;
pub use self::ConsumeMode::*;
pub use self::MoveReason::*;
self.consume_expr(&*output.expr);
} else {
self.mutate_expr(expr, &*output.expr,
- if output.is_rw { WriteAndRead } else { JustWrite });
+ if output.is_rw {
+ MutateMode::WriteAndRead
+ } else {
+ MutateMode::JustWrite
+ });
}
}
}
}
hir::ExprAssign(ref lhs, ref rhs) => {
- self.mutate_expr(expr, &**lhs, JustWrite);
+ self.mutate_expr(expr, &**lhs, MutateMode::JustWrite);
self.consume_expr(&**rhs);
}
assert!(::rustc_front::util::is_by_value_binop(op.node));
if !self.walk_overloaded_operator(expr, lhs, vec![rhs], PassArgs::ByValue) {
- self.mutate_expr(expr, &**lhs, WriteAndRead);
+ self.mutate_expr(expr, &**lhs, MutateMode::WriteAndRead);
self.consume_expr(&**rhs);
}
}
let def = def_map.borrow().get(&pat.id).unwrap().full_def();
match mc.cat_def(pat.id, pat.span, pat_ty, def) {
Ok(binding_cmt) => {
- delegate.mutate(pat.id, pat.span, binding_cmt, Init);
+ delegate.mutate(pat.id, pat.span, binding_cmt, MutateMode::Init);
}
Err(_) => { }
}
b_def_id,
util::fresh_type_vars_for_impl);
- debug!("overlap: a_trait_ref={:?}", a_trait_ref);
+ debug!("overlap: a_trait_ref={:?} a_obligations={:?}", a_trait_ref, a_obligations);
- debug!("overlap: b_trait_ref={:?}", b_trait_ref);
+ debug!("overlap: b_trait_ref={:?} b_obligations={:?}", b_trait_ref, b_obligations);
// Do `a` and `b` unify? If not, no overlap.
if let Err(_) = infer::mk_eq_trait_refs(selcx.infcx(),
tt.principal_def_id().is_local()
}
- ty::TyClosure(..) |
ty::TyError => {
+ true
+ }
+
+ ty::TyClosure(..) => {
tcx.sess.bug(
&format!("ty_is_local invoked on unexpected type: {:?}",
ty))
}
}
-/// in various error cases, we just set TyError and return an obligation
-/// that, when fulfilled, will lead to an error.
+/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
+/// hold. In various error cases, we cannot generate a valid
+/// normalized projection. Therefore, we create an inference variable
+/// return an associated obligation that, when fulfilled, will lead to
+/// an error.
///
-/// FIXME: the TyError created here can enter the obligation we create,
-/// leading to error messages involving TyError.
+/// Note that we used to return `TyError` here, but that was quite
+/// dubious -- the premise was that an error would *eventually* be
+/// reported, when the obligation was processed. But in general once
+/// you see a `TyError` you are supposed to be able to assume that an
+/// error *has been* reported, so that you can take whatever heuristic
+/// paths you want to take. To make things worse, it was possible for
+/// cycles to arise, where you basically had a setup like `<MyType<$0>
+/// as Trait>::Foo == $0`. Here, normalizing `<MyType<$0> as
+/// Trait>::Foo> to `[type error]` would lead to an obligation of
+/// `<MyType<[type error]> as Trait>::Foo`. We are supposed to report
+/// an error for this obligation, but we legitimately should not,
+/// because it contains `[type error]`. Yuck! (See issue #29857 for
+/// one case where this arose.)
fn normalize_to_error<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
let trait_obligation = Obligation { cause: cause,
recursion_depth: depth,
predicate: trait_ref.to_predicate() };
+ let new_value = selcx.infcx().next_ty_var();
Normalized {
- value: selcx.tcx().types.err,
+ value: new_value,
obligations: vec!(trait_obligation)
}
}
BuiltinObjectCandidate,
BuiltinUnsizeCandidate,
-
- ErrorCandidate,
}
struct SelectionCandidateSet<'tcx> {
stack: &TraitObligationStack<'o, 'tcx>)
-> SelectionResult<'tcx, SelectionCandidate<'tcx>>
{
- if stack.obligation.predicate.0.self_ty().references_error() {
- return Ok(Some(ErrorCandidate));
+ if stack.obligation.predicate.references_error() {
+ // If we encounter a `TyError`, we generally prefer the
+ // most "optimistic" result in response -- that is, the
+ // one least likely to report downstream errors. But
+ // because this routine is shared by coherence and by
+ // trait selection, there isn't an obvious "right" choice
+ // here in that respect, so we opt to just return
+ // ambiguity and let the upstream clients sort it out.
+ return Ok(None);
}
if !self.is_knowable(stack) {
true
},
&ParamCandidate(..) => false,
- &ErrorCandidate => false // propagate errors
},
_ => false
}
try!(self.confirm_builtin_candidate(obligation, builtin_bound))))
}
- ErrorCandidate => {
- Ok(VtableBuiltin(VtableBuiltinData { nested: vec![] }))
- }
-
ParamCandidate(param) => {
let obligations = self.confirm_param_candidate(obligation, param);
Ok(VtableParam(obligations))
}
}
- pub fn destination(&self) -> Option<Lvalue<'tcx>> {
+ pub fn destination(&self) -> Option<&Lvalue<'tcx>> {
match *self {
CallKind::Converging { ref destination, .. } |
- CallKind::ConvergingCleanup { ref destination, .. } => Some(destination.clone()),
+ CallKind::ConvergingCleanup { ref destination, .. } => Some(destination),
+ CallKind::Diverging |
+ CallKind::DivergingCleanup(_) => None
+ }
+ }
+
+ pub fn destination_mut(&mut self) -> Option<&mut Lvalue<'tcx>> {
+ match *self {
+ CallKind::Converging { ref mut destination, .. } |
+ CallKind::ConvergingCleanup { ref mut destination, .. } => Some(destination),
CallKind::Diverging |
CallKind::DivergingCleanup(_) => None
}
pub prints: Vec<PrintRequest>,
pub cg: CodegenOptions,
pub color: ColorConfig,
- pub show_span: Option<String>,
pub externs: HashMap<String, Vec<String>>,
pub crate_name: Option<String>,
/// An optional name to use as the crate for std during std injection,
prints: Vec::new(),
cg: basic_codegen_options(),
color: ColorConfig::Auto,
- show_span: None,
externs: HashMap::new(),
crate_name: None,
alt_std_name: None,
"don't clear the resolution tables after analysis"),
keep_ast: bool = (false, parse_bool,
"keep the AST after lowering it to HIR"),
+ show_span: Option<String> = (None, parse_opt_string,
+ "show spans for compiler debugging (expr|pat|ty)"),
}
pub fn default_lib_output() -> CrateType {
`hir` (the HIR), `hir,identified`, or
`hir,typed` (HIR with types for each node).",
"TYPE"),
- opt::opt_u("", "show-span", "Show spans for compiler debugging", "expr|pat|ty"),
]);
opts
}
prints: prints,
cg: cg,
color: color,
- show_span: None,
externs: externs,
crate_name: crate_name,
alt_std_name: None,
use target::Target;
pub fn target() -> Target {
- let mut base = super::linux_base::opts();
- base.has_elf_tls = false;
+ let base = super::linux_base::opts();
Target {
llvm_target: "aarch64-unknown-linux-gnu".to_string(),
target_endian: "little".to_string(),
base.pre_link_args.push("-Wl,--allow-multiple-definition".to_string());
base.is_like_android = true;
base.position_independent_executables = true;
+ base.has_elf_tls = false;
base
}
pub fn target() -> Target {
let mut base = super::android_base::opts();
base.features = "+v7".to_string();
- base.has_elf_tls = false;
Target {
llvm_target: "arm-linux-androideabi".to_string(),
use borrowck::*;
use borrowck::InteriorKind::{InteriorElement, InteriorField};
use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::expr_use_visitor::MutateMode;
use rustc::middle::infer;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
match opt_loan_path(&assignee_cmt) {
Some(lp) => {
match mode {
- euv::Init | euv::JustWrite => {
+ MutateMode::Init | MutateMode::JustWrite => {
// In a case like `path = 1`, then path does not
// have to be *FULLY* initialized, but we still
// must be careful lest it contains derefs of
MovedInUse,
&lp);
}
- euv::WriteAndRead => {
+ MutateMode::WriteAndRead => {
// In a case like `path += 1`, then path must be
// fully initialized, since we will read it before
// we write it.
use rustc::middle::dataflow::DataFlowOperator;
use rustc::middle::dataflow::KillFrom;
use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::expr_use_visitor::MutateMode;
use rustc::middle::ty;
use rustc::util::nodemap::{FnvHashMap, NodeSet};
self.fragments.borrow_mut().add_assignment(path_index);
match mode {
- euv::Init | euv::JustWrite => {
+ MutateMode::Init | MutateMode::JustWrite => {
self.assignee_ids.borrow_mut().insert(assignee_id);
}
- euv::WriteAndRead => { }
+ MutateMode::WriteAndRead => { }
}
let assignment = Assignment {
println!("Pre-expansion node count: {}", count_nodes(&krate));
}
- if let Some(ref s) = sess.opts.show_span {
+ if let Some(ref s) = sess.opts.debugging_opts.show_span {
syntax::show_span::run(sess.diagnostic(), s, &krate);
}
};
let cstore = Rc::new(CStore::new(token::get_ident_interner()));
- let mut sess = build_session(sopts, input_file_path, descriptions,
+ let sess = build_session(sopts, input_file_path, descriptions,
cstore.clone());
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
- if sess.unstable_options() {
- sess.opts.show_span = matches.opt_str("show-span");
- }
let mut cfg = config::build_configuration(&sess);
target_features::add_configuration(&mut cfg, &sess);
fn build_controller(&mut self, sess: &Session) -> CompileController<'a> {
let mut control = CompileController::basic();
- if sess.opts.parse_only || sess.opts.show_span.is_some() ||
+ if sess.opts.parse_only || sess.opts.debugging_opts.show_span.is_some() ||
sess.opts.debugging_opts.ast_json_noexpand {
control.after_parse.stop = Compilation::Stop;
}
#![feature(staged_api)]
#![feature(str_char)]
+#[macro_use]
extern crate syntax;
#[macro_use]
extern crate rustc;
UNUSED_UNSAFE, PATH_STATEMENTS, UNUSED_ATTRIBUTES);
add_lint_group!(sess, FUTURE_INCOMPATIBLE,
- PRIVATE_IN_PUBLIC, INVALID_TYPE_PARAM_DEFAULT);
+ PRIVATE_IN_PUBLIC, INVALID_TYPE_PARAM_DEFAULT,
+ MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT);
// We have one lint pass defined specially
store.register_late_pass(sess, false, box lint::GatherNodeLevels);
store.register_renamed("unknown_features", "unused_features");
store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate");
+ store.register_removed("negate_unsigned", "cast a signed value instead");
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![allow(non_snake_case)]
+
use middle::{infer};
use middle::def_id::DefId;
use middle::subst::Substs;
use syntax::{abi, ast};
use syntax::attr::{self, AttrMetaMethods};
use syntax::codemap::{self, Span};
-use syntax::feature_gate::{emit_feature_err, GateIssue};
use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
use rustc_front::hir;
use rustc_front::intravisit::{self, Visitor};
use rustc_front::util::is_shift_binop;
+register_long_diagnostics! {
+E0519: r##"
+It is not allowed to negate an unsigned integer.
+You can negate a signed integer and cast it to an
+unsigned integer or use the `!` operator.
+
+```
+let x: usize = -1isize as usize;
+let y: usize = !0;
+assert_eq!(x, y);
+```
+
+Alternatively you can use the `Wrapping` newtype
+or the `wrapping_neg` operation that all
+integral types support:
+
+```
+use std::num::Wrapping;
+let x: Wrapping<usize> = -Wrapping(1);
+let Wrapping(x) = x;
+let y: usize = 1.wrapping_neg();
+assert_eq!(x, y);
+```
+
+"##
+}
+
declare_lint! {
UNUSED_COMPARISONS,
Warn,
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
match e.node {
hir::ExprUnary(hir::UnNeg, ref expr) => {
- match expr.node {
- hir::ExprLit(ref lit) => {
- match lit.node {
- ast::LitInt(_, ast::UnsignedIntLit(_)) => {
- check_unsigned_negation_feature(cx, e.span);
- },
- ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
- if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
- check_unsigned_negation_feature(cx, e.span);
- }
- },
- _ => ()
- }
- },
- _ => {
- let t = cx.tcx.node_id_to_type(expr.id);
- match t.sty {
- ty::TyUint(_) => {
- check_unsigned_negation_feature(cx, e.span);
- },
- _ => ()
- }
+ if let hir::ExprLit(ref lit) = expr.node {
+ match lit.node {
+ ast::LitInt(_, ast::UnsignedIntLit(_)) => {
+ forbid_unsigned_negation(cx, e.span);
+ },
+ ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
+ if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
+ forbid_unsigned_negation(cx, e.span);
+ }
+ },
+ _ => ()
}
- };
+ } else {
+ let t = cx.tcx.node_id_to_type(expr.id);
+ if let ty::TyUint(_) = t.sty {
+ forbid_unsigned_negation(cx, e.span);
+ }
+ }
// propagate negation, if the negation itself isn't negated
if self.negated_expr_id != e.id {
self.negated_expr_id = expr.id;
}
}
- fn check_unsigned_negation_feature(cx: &LateContext, span: Span) {
- if !cx.sess().features.borrow().negate_unsigned {
- emit_feature_err(
- &cx.sess().parse_sess.span_diagnostic,
- "negate_unsigned",
- span,
- GateIssue::Language,
- "unary negation of unsigned integers may be removed in the future");
- }
+ fn forbid_unsigned_negation(cx: &LateContext, span: Span) {
+ cx.sess()
+ .struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519")
+ .span_help(span, "use a cast or the `!` operator")
+ .emit();
}
}
}
let eii: &mut EncodeInlinedItem = &mut *eii;
eii(ecx, rbml_w, ii);
- encode_mir(ecx, rbml_w, ii);
-}
-
-fn encode_mir(ecx: &EncodeContext, rbml_w: &mut Encoder, ii: InlinedItemRef) {
- let id = match ii {
+ let node_id = match ii {
InlinedItemRef::Item(item) => item.id,
InlinedItemRef::TraitItem(_, trait_item) => trait_item.id,
InlinedItemRef::ImplItem(_, impl_item) => impl_item.id,
InlinedItemRef::Foreign(foreign_item) => foreign_item.id
};
- if let Some(mir) = ecx.mir_map.get(&id) {
+ encode_mir(ecx, rbml_w, node_id);
+}
+
+fn encode_mir(ecx: &EncodeContext, rbml_w: &mut Encoder, node_id: NodeId) {
+ if let Some(mir) = ecx.mir_map.get(&node_id) {
rbml_w.start_tag(tag_mir as usize);
rbml_w.emit_opaque(|opaque_encoder| {
tls::enter_encoding_context(ecx, opaque_encoder, |_, opaque_encoder| {
ecx.tcx.map.with_path(expr.id, |path| encode_path(rbml_w, path));
+ assert!(ecx.mir_map.contains_key(&expr.id));
+ encode_mir(ecx, rbml_w, expr.id);
+
rbml_w.end_tag();
}
_ => { }
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use build::{BlockAnd, Builder};
+use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*;
use rustc::mir::repr::*;
use rustc_front::hir;
mut block: BasicBlock,
ast_block: &'tcx hir::Block)
-> BlockAnd<()> {
- let this = self;
- let Block { extent, span: _, stmts, expr } = this.hir.mirror(ast_block);
- this.in_scope(extent, block, |this| {
+ let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
+ self.in_scope(extent, block, move |this| {
unpack!(block = this.stmts(block, stmts));
- this.into(destination, block, expr)
+ match expr {
+ Some(expr) => this.into(destination, block, expr),
+ None => {
+ this.cfg.push_assign_unit(block, span, destination);
+ block.unit()
+ }
+ }
})
}
}
self.block_data_mut(block).statements.push(statement);
}
- pub fn push_assign_constant(&mut self,
- block: BasicBlock,
- span: Span,
- temp: &Lvalue<'tcx>,
- constant: Constant<'tcx>) {
- self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
- }
-
pub fn push_drop(&mut self, block: BasicBlock, span: Span,
kind: DropKind, lvalue: &Lvalue<'tcx>) {
self.push(block, Statement {
});
}
+ pub fn push_assign_constant(&mut self,
+ block: BasicBlock,
+ span: Span,
+ temp: &Lvalue<'tcx>,
+ constant: Constant<'tcx>) {
+ self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
+ }
+
+ pub fn push_assign_unit(&mut self,
+ block: BasicBlock,
+ span: Span,
+ lvalue: &Lvalue<'tcx>) {
+ self.push_assign(block, span, lvalue, Rvalue::Aggregate(
+ AggregateKind::Tuple, vec![]
+ ));
+ }
+
pub fn terminate(&mut self,
block: BasicBlock,
terminator: Terminator<'tcx>) {
});
unpack!(then_block = this.into(destination, then_block, then_expr));
- unpack!(else_block = this.into(destination, else_block, else_expr));
+ else_block = if let Some(else_expr) = else_expr {
+ unpack!(this.into(destination, else_block, else_expr))
+ } else {
+ // Body of the `if` expression without an `else` clause must return `()`, thus
+ // we implicitly generate a `else {}` if it is not specified.
+ this.cfg.push_assign_unit(else_block, expr_span, &Lvalue::ReturnPointer);
+ else_block
+ };
let join_block = this.cfg.start_new_block();
this.cfg.terminate(then_block, Terminator::Goto { target: join_block });
}
// execute the body, branching back to the test
- let unit_temp = this.unit_temp.clone();
- let body_block_end = unpack!(this.into(&unit_temp, body_block, body));
+ // We write body’s “return value” into the destination of loop. This is fine,
+ // because:
+ //
+ // * In Rust both loop expression and its body are required to have `()`
+ // as the “return value”;
+ // * The destination will be considered uninitialised (given it was
+ // uninitialised before the loop) during the first iteration, thus
+ // disallowing its use inside the body. Alternatively, if it was already
+ // initialised, the `destination` can only possibly have a value of `()`,
+ // therefore, “mutating” the destination during iteration is fine.
+ let body_block_end = unpack!(this.into(destination, body_block, body));
this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
-
- // final point is exit_block
exit_block.unit()
})
}
this.break_or_continue(expr_span, label, block, |loop_scope| loop_scope.break_block)
}
ExprKind::Return { value } => {
- unpack!(block = this.into(&Lvalue::ReturnPointer, block, value));
+ block = match value {
+ Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
+ None => {
+ this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer);
+ block
+ }
+ };
let extent = this.extent_of_outermost_scope();
this.exit_scope(expr_span, extent, block, END_BLOCK);
this.cfg.start_new_block().unit()
//! wrapped up as expressions (e.g. blocks). To make this ergonomic, we use this
//! latter `EvalInto` trait.
-use build::{BlockAnd, BlockAndExtension, Builder};
+use build::{BlockAnd, Builder};
use hair::*;
use rustc::mir::repr::*;
builder.into_expr(destination, block, self)
}
}
-
-impl<'tcx> EvalInto<'tcx> for Option<ExprRef<'tcx>> {
- fn eval_into<'a>(self,
- builder: &mut Builder<'a, 'tcx>,
- destination: &Lvalue<'tcx>,
- block: BasicBlock)
- -> BlockAnd<()> {
- match self {
- Some(expr) => builder.into(destination, block, expr),
- None => block.unit(),
- }
- }
-}
cfg: CFG<'tcx>,
scopes: Vec<scope::Scope<'tcx>>,
loop_scopes: Vec<scope::LoopScope>,
- unit_temp: Lvalue<'tcx>,
var_decls: Vec<VarDecl<'tcx>>,
var_indices: FnvHashMap<ast::NodeId, u32>,
temp_decls: Vec<TempDecl<'tcx>>,
///////////////////////////////////////////////////////////////////////////
// construct() -- the main entry point for building MIR for a function
-pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>,
+pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
_span: Span,
implicit_arguments: Vec<Ty<'tcx>>,
explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
-> Mir<'tcx> {
let cfg = CFG { basic_blocks: vec![] };
- // it's handy to have a temporary of type `()` sometimes, so make
- // one from the start and keep it available
- let temp_decls = vec![TempDecl::<'tcx> { ty: hir.unit_ty() }];
- let unit_temp = Lvalue::Temp(0);
-
let mut builder = Builder {
hir: hir,
cfg: cfg,
scopes: vec![],
loop_scopes: vec![],
- temp_decls: temp_decls,
+ temp_decls: vec![],
var_decls: vec![],
var_indices: FnvHashMap(),
- unit_temp: unit_temp,
};
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
matches, it sometimes happen that certain code (namely guards) gets
executed multiple times. This means that the scope lexical scope may
in fact correspond to multiple, disjoint SEME regions. So in fact our
-mapping os from one scope to a vector of SEME regions.
+mapping is from one scope to a vector of SEME regions.
### Drops
ast.make_mirror(self)
}
- pub fn unit_ty(&mut self) -> Ty<'tcx> {
- self.tcx.mk_nil()
- }
-
pub fn usize_ty(&mut self) -> Ty<'tcx> {
self.tcx.types.usize
}
}
// Terminator at the bottom.
- try!(writeln!(w, "{0}{0}{1:?};", INDENT, data.terminator));
+ try!(writeln!(w, "{0}{0}{1:?};", INDENT, data.terminator()));
writeln!(w, "{}}}", INDENT)
}
*switch_ty = self.tcx.erase_regions(switch_ty);
},
Terminator::Call { ref mut func, ref mut args, ref mut kind } => {
- if let Some(ref mut destination) = kind.destination() {
+ if let Some(destination) = kind.destination_mut() {
self.erase_regions_lvalue(destination);
}
self.erase_regions_operand(func);
let lvalue = Lvalue::new_with_hint(caller_name, bcx, p_id, HintKind::DontZeroJustUse);
let datum = Datum::new(llval, var_ty, lvalue);
+ debug!("mk_binding_alloca cleanup_scope={:?} llval={} var_ty={:?}",
+ cleanup_scope, bcx.ccx().tn().val_to_string(llval), var_ty);
+
// Subtle: be sure that we *populate* the memory *before*
// we schedule the cleanup.
call_lifetime_start(bcx, llval);
use syntax::attr;
use syntax::attr::IntType;
use trans::_match;
+use trans::base::InitAlloca;
use trans::build::*;
use trans::cleanup;
use trans::cleanup::CleanupMethods;
let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
let scratch = unpack_datum!(bcx, datum::lvalue_scratch_datum(
bcx, tcx.dtor_type(), "drop_flag",
- cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| bcx
+ InitAlloca::Uninit("drop flag itself has no dtor"),
+ cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| {
+ debug!("no-op populate call for trans_drop_flag_ptr on dtor_type={:?}",
+ tcx.dtor_type());
+ bcx
+ }
));
bcx = fold_variants(bcx, r, val, |variant_cx, st, value| {
let ptr = struct_field_ptr(variant_cx, st, MaybeSizedValue::sized(value),
next_cx
}
-pub fn call_lifetime_start(cx: Block, ptr: ValueRef) {
- if cx.sess().opts.optimize == config::No {
+enum Lifetime { Start, End }
+
+// If LLVM lifetime intrinsic support is enabled (i.e. optimizations
+// on), and `ptr` is nonzero-sized, then extracts the size of `ptr`
+// and the intrinsic for `lt` and passes them to `emit`, which is in
+// charge of generating code to call the passed intrinsic on whatever
+// block of generated code is targetted for the intrinsic.
+//
+// If LLVM lifetime intrinsic support is disabled (i.e. optimizations
+// off) or `ptr` is zero-sized, then no-op (does not call `emit`).
+fn core_lifetime_emit<'blk, 'tcx, F>(ccx: &'blk CrateContext<'blk, 'tcx>,
+ ptr: ValueRef,
+ lt: Lifetime,
+ emit: F)
+ where F: FnOnce(&'blk CrateContext<'blk, 'tcx>, machine::llsize, ValueRef)
+{
+ if ccx.sess().opts.optimize == config::No {
return;
}
- let _icx = push_ctxt("lifetime_start");
- let ccx = cx.ccx();
+ let _icx = push_ctxt(match lt {
+ Lifetime::Start => "lifetime_start",
+ Lifetime::End => "lifetime_end"
+ });
let size = machine::llsize_of_alloc(ccx, val_ty(ptr).element_type());
if size == 0 {
return;
}
- let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
- let lifetime_start = ccx.get_intrinsic(&"llvm.lifetime.start");
- Call(cx,
- lifetime_start,
- &[C_u64(ccx, size), ptr],
- None,
- DebugLoc::None);
+ let lifetime_intrinsic = ccx.get_intrinsic(match lt {
+ Lifetime::Start => "llvm.lifetime.start",
+ Lifetime::End => "llvm.lifetime.end"
+ });
+ emit(ccx, size, lifetime_intrinsic)
}
-pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
- if cx.sess().opts.optimize == config::No {
- return;
- }
-
- let _icx = push_ctxt("lifetime_end");
- let ccx = cx.ccx();
-
- let size = machine::llsize_of_alloc(ccx, val_ty(ptr).element_type());
- if size == 0 {
- return;
- }
+pub fn call_lifetime_start(cx: Block, ptr: ValueRef) {
+ core_lifetime_emit(cx.ccx(), ptr, Lifetime::Start, |ccx, size, lifetime_start| {
+ let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
+ Call(cx,
+ lifetime_start,
+ &[C_u64(ccx, size), ptr],
+ None,
+ DebugLoc::None);
+ })
+}
- let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
- let lifetime_end = ccx.get_intrinsic(&"llvm.lifetime.end");
- Call(cx,
- lifetime_end,
- &[C_u64(ccx, size), ptr],
- None,
- DebugLoc::None);
+pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
+ core_lifetime_emit(cx.ccx(), ptr, Lifetime::End, |ccx, size, lifetime_end| {
+ let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
+ Call(cx,
+ lifetime_end,
+ &[C_u64(ccx, size), ptr],
+ None,
+ DebugLoc::None);
+ })
}
// Generates code for resumption of unwind at the end of a landing pad.
None);
}
-pub fn alloc_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, name: &str) -> ValueRef {
+/// In general, when we create an scratch value in an alloca, the
+/// creator may not know if the block (that initializes the scratch
+/// with the desired value) actually dominates the cleanup associated
+/// with the scratch value.
+///
+/// To deal with this, when we do an alloca (at the *start* of whole
+/// function body), we optionally can also set the associated
+/// dropped-flag state of the alloca to "dropped."
+#[derive(Copy, Clone, Debug)]
+pub enum InitAlloca {
+ /// Indicates that the state should have its associated drop flag
+ /// set to "dropped" at the point of allocation.
+ Dropped,
+ /// Indicates the value of the associated drop flag is irrelevant.
+ /// The embedded string literal is a programmer provided argument
+ /// for why. This is a safeguard forcing compiler devs to
+ /// document; it might be a good idea to also emit this as a
+ /// comment with the alloca itself when emitting LLVM output.ll.
+ Uninit(&'static str),
+}
+
+
+pub fn alloc_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+ t: Ty<'tcx>,
+ name: &str) -> ValueRef {
+ // pnkfelix: I do not know why alloc_ty meets the assumptions for
+ // passing Uninit, but it was never needed (even back when we had
+ // the original boolean `zero` flag on `lvalue_scratch_datum`).
+ alloc_ty_init(bcx, t, InitAlloca::Uninit("all alloc_ty are uninit"), name)
+}
+
+/// This variant of `fn alloc_ty` does not necessarily assume that the
+/// alloca should be created with no initial value. Instead the caller
+/// controls that assumption via the `init` flag.
+///
+/// Note that if the alloca *is* initialized via `init`, then we will
+/// also inject an `llvm.lifetime.start` before that initialization
+/// occurs, and thus callers should not call_lifetime_start
+/// themselves. But if `init` says "uninitialized", then callers are
+/// in charge of choosing where to call_lifetime_start and
+/// subsequently populate the alloca.
+///
+/// (See related discussion on PR #30823.)
+pub fn alloc_ty_init<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+ t: Ty<'tcx>,
+ init: InitAlloca,
+ name: &str) -> ValueRef {
let _icx = push_ctxt("alloc_ty");
let ccx = bcx.ccx();
let ty = type_of::type_of(ccx, t);
assert!(!t.has_param_types());
- alloca(bcx, ty, name)
+ match init {
+ InitAlloca::Dropped => alloca_dropped(bcx, t, name),
+ InitAlloca::Uninit(_) => alloca(bcx, ty, name),
+ }
+}
+
+pub fn alloca_dropped<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ty: Ty<'tcx>, name: &str) -> ValueRef {
+ let _icx = push_ctxt("alloca_dropped");
+ let llty = type_of::type_of(cx.ccx(), ty);
+ if cx.unreachable.get() {
+ unsafe { return llvm::LLVMGetUndef(llty.ptr_to().to_ref()); }
+ }
+ let p = alloca(cx, llty, name);
+ let b = cx.fcx.ccx.builder();
+ b.position_before(cx.fcx.alloca_insert_pt.get().unwrap());
+
+ // This is just like `call_lifetime_start` (but latter expects a
+ // Block, which we do not have for `alloca_insert_pt`).
+ core_lifetime_emit(cx.ccx(), p, Lifetime::Start, |ccx, size, lifetime_start| {
+ let ptr = b.pointercast(p, Type::i8p(ccx));
+ b.call(lifetime_start, &[C_u64(ccx, size), ptr], None);
+ });
+ memfill(&b, p, ty, adt::DTOR_DONE);
+ p
}
pub fn alloca(cx: Block, ty: Type, name: &str) -> ValueRef {
// Create the drop-flag hints for every unfragmented path in the function.
let tcx = fcx.ccx.tcx();
let fn_did = tcx.map.local_def_id(fcx.id);
+ let tables = tcx.tables.borrow();
let mut hints = fcx.lldropflag_hints.borrow_mut();
let fragment_infos = tcx.fragment_infos.borrow();
let (var, datum) = match info {
ty::FragmentInfo::Moved { var, .. } |
ty::FragmentInfo::Assigned { var, .. } => {
- let datum = seen.get(&var).cloned().unwrap_or_else(|| {
- let datum = make_datum(var);
- seen.insert(var, datum.clone());
- datum
+ let opt_datum = seen.get(&var).cloned().unwrap_or_else(|| {
+ let ty = tables.node_types[&var];
+ if fcx.type_needs_drop(ty) {
+ let datum = make_datum(var);
+ seen.insert(var, Some(datum.clone()));
+ Some(datum)
+ } else {
+ // No drop call needed, so we don't need a dropflag hint
+ None
+ }
});
- (var, datum)
+ if let Some(datum) = opt_datum {
+ (var, datum)
+ } else {
+ continue
+ }
}
};
match info {
let fcx = bcx.fcx;
let arg_scope_id = cleanup::CustomScope(arg_scope);
+ debug!("create_datums_for_fn_args");
+
// Return an array wrapping the ValueRefs that we get from `get_param` for
// each argument into datums.
//
// This alloca should be optimized away by LLVM's mem-to-reg pass in
// the event it's not truly needed.
let mut idx = fcx.arg_offset() as c_uint;
+ let uninit_reason = InitAlloca::Uninit("fn_arg populate dominates dtor");
for (i, &arg_ty) in arg_tys.iter().enumerate() {
let arg_datum = if !has_tupled_arg || i < arg_tys.len() - 1 {
if type_of::arg_is_indirect(bcx.ccx(), arg_ty) &&
let data = get_param(fcx.llfn, idx);
let extra = get_param(fcx.llfn, idx + 1);
idx += 2;
- unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "",
+ unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "", uninit_reason,
arg_scope_id, (data, extra),
|(data, extra), bcx, dst| {
+ debug!("populate call for create_datum_for_fn_args \
+ early fat arg, on arg[{}] ty={:?}", i, arg_ty);
+
Store(bcx, data, expr::get_dataptr(bcx, dst));
Store(bcx, extra, expr::get_meta(bcx, dst));
bcx
datum::lvalue_scratch_datum(bcx,
arg_ty,
"",
+ uninit_reason,
arg_scope_id,
tmp,
- |tmp, bcx, dst| tmp.store_to(bcx, dst)))
+ |tmp, bcx, dst| {
+
+ debug!("populate call for create_datum_for_fn_args \
+ early thin arg, on arg[{}] ty={:?}", i, arg_ty);
+
+ tmp.store_to(bcx, dst)
+ }))
}
} else {
// FIXME(pcwalton): Reduce the amount of code bloat this is responsible for.
datum::lvalue_scratch_datum(bcx,
arg_ty,
"tupled_args",
+ uninit_reason,
arg_scope_id,
(),
|(),
mut bcx,
- llval| {
+ llval| {
+ debug!("populate call for create_datum_for_fn_args \
+ tupled_args, on arg[{}] ty={:?}", i, arg_ty);
for (j, &tupled_arg_ty) in
tupled_arg_tys.iter().enumerate() {
let lldest = StructGEP(bcx, llval, j);
return DatumBlock::new(bcx, immediate_rvalue(val, ty))
}
-
/// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
/// it. The memory will be dropped upon exit from `scope`. The callback `populate` should
/// initialize the memory.
+///
+/// The flag `zero` indicates how the temporary space itself should be
+/// initialized at the outset of the function; the only time that
+/// `InitAlloca::Uninit` is a valid value for `zero` is when the
+/// caller can prove that either (1.) the code injected by `populate`
+/// onto `bcx` always dominates the end of `scope`, or (2.) the data
+/// being allocated has no associated destructor.
pub fn lvalue_scratch_datum<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
ty: Ty<'tcx>,
name: &str,
+ zero: InitAlloca,
scope: cleanup::ScopeId,
arg: A,
populate: F)
-> DatumBlock<'blk, 'tcx, Lvalue> where
F: FnOnce(A, Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>,
{
- let scratch = alloc_ty(bcx, ty, name);
+ // Very subtle: potentially initialize the scratch memory at point where it is alloca'ed.
+ // (See discussion at Issue 30530.)
+ let scratch = alloc_ty_init(bcx, ty, zero, name);
+ debug!("lvalue_scratch_datum scope={:?} scratch={} ty={:?}",
+ scope, bcx.ccx().tn().val_to_string(scratch), ty);
// Subtle. Populate the scratch memory *before* scheduling cleanup.
let bcx = populate(arg, bcx, scratch);
scope: cleanup::ScopeId,
val: ValueRef,
ty: Ty<'tcx>) {
+ debug!("add_rvalue_clean scope={:?} val={} ty={:?}",
+ scope, fcx.ccx.tn().val_to_string(val), ty);
match mode {
ByValue => { fcx.schedule_drop_immediate(scope, val, ty); }
ByRef => {
ByValue => {
lvalue_scratch_datum(
- bcx, self.ty, name, scope, self,
+ bcx, self.ty, name, InitAlloca::Dropped, scope, self,
|this, bcx, llval| {
- call_lifetime_start(bcx, llval);
+ debug!("populate call for Datum::to_lvalue_datum_in_scope \
+ self.ty={:?}", this.ty);
+ // do not call_lifetime_start here; the
+ // `InitAlloc::Dropped` will start scratch
+ // value's lifetime at open of function body.
let bcx = this.store_to(bcx, llval);
bcx.fcx.schedule_lifetime_end(scope, llval);
bcx
}
};
+ debug!("trans_adt");
+
// This scope holds intermediates that must be cleaned should
// panic occur before the ADT as a whole is ready.
let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
let mut llargs = Vec::with_capacity(args.len() + 1);
// Prepare the return value destination
- let (ret_dest_ty, must_copy_dest) = if let Some(ref d) = kind.destination() {
+ let (ret_dest_ty, must_copy_dest) = if let Some(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) {
use rustc::middle::const_eval::ConstVal;
use rustc::mir::repr as mir;
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 trans::consts;
+use trans::expr;
+use trans::type_of;
use super::operand::{OperandRef, OperandValue};
use super::MirContext;
+
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
pub fn trans_constval(&mut self,
bcx: Block<'bcx, 'tcx>,
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::Struct(id) | ConstVal::Tuple(id) |
ConstVal::Array(id, _) | ConstVal::Repeat(id, _) => {
let expr = bcx.tcx().map.expect_expr(id);
expr::trans(bcx, expr).datum.val
// Always create an alloca even if zero-sized, to preserve
// the non-null invariant of the inner slice ptr
- let llfixed = base::alloc_ty(bcx, fixed_ty, "");
- call_lifetime_start(bcx, llfixed);
+ let llfixed;
+ // Issue 30018: ensure state is initialized as dropped if necessary.
+ if fcx.type_needs_drop(vt.unit_ty) {
+ llfixed = base::alloc_ty_init(bcx, fixed_ty, InitAlloca::Dropped, "");
+ } else {
+ let uninit = InitAlloca::Uninit("fcx says vt.unit_ty is non-drop");
+ llfixed = base::alloc_ty_init(bcx, fixed_ty, uninit, "");
+ call_lifetime_start(bcx, llfixed);
+ };
if count > 0 {
// Arrange for the backing array to be cleaned up.
bcx = expr::trans_into(bcx, &**element,
SaveIn(lleltptr));
let scope = cleanup::CustomScope(temp_scope);
- fcx.schedule_lifetime_end(scope, lleltptr);
- fcx.schedule_drop_mem(scope, lleltptr, vt.unit_ty, None);
+ // Issue #30822: mark memory as dropped after running destructor
+ fcx.schedule_drop_and_fill_mem(scope, lleltptr, vt.unit_ty, None);
}
fcx.pop_custom_cleanup_scope(temp_scope);
}
use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
use check::{check_expr_with_lvalue_pref};
use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
+use lint;
use require_same_types;
use util::nodemap::FnvHashMap;
use session::Session;
if pat_is_resolved_const(&tcx.def_map.borrow(), pat) => {
if let hir::PatEnum(ref path, ref subpats) = pat.node {
if !(subpats.is_some() && subpats.as_ref().unwrap().is_empty()) {
- bad_struct_kind_err(tcx.sess, pat.span, path, false);
+ bad_struct_kind_err(tcx.sess, pat, path, false);
return;
}
}
}
// This function exists due to the warning "diagnostic code E0164 already used"
-fn bad_struct_kind_err(sess: &Session, span: Span, path: &hir::Path, is_warning: bool) {
+fn bad_struct_kind_err(sess: &Session, pat: &hir::Pat, path: &hir::Path, lint: bool) {
let name = pprust::path_to_string(path);
- span_err_or_warn!(is_warning, sess, span, E0164,
- "`{}` does not name a tuple variant or a tuple struct", name);
+ let msg = format!("`{}` does not name a tuple variant or a tuple struct", name);
+ if lint {
+ let expanded_msg =
+ format!("{}; RFC 218 disallowed matching of unit variants or unit structs via {}(..)",
+ msg,
+ name);
+ sess.add_lint(lint::builtin::MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
+ pat.id,
+ pat.span,
+ expanded_msg);
+ } else {
+ span_err!(sess, pat.span, E0164, "{}", msg);
+ }
}
pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
opt_ty, def, pat.span, pat.id);
let report_bad_struct_kind = |is_warning| {
- bad_struct_kind_err(tcx.sess, pat.span, path, is_warning);
- if is_warning {
- return;
- }
-
+ bad_struct_kind_err(tcx.sess, pat, path, is_warning);
+ if is_warning { return; }
fcx.write_error(pat.id);
if let Some(subpats) = subpats {
for pat in subpats {
report_bad_struct_kind(is_special_case);
if !is_special_case {
return
- } else {
- // 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.");
}
}
(variant.fields
ty::TyStruct(struct_def, expected_substs) => {
let variant = struct_def.struct_variant();
if is_tuple_struct_pat && variant.kind() != ty::VariantKind::Tuple {
- report_bad_struct_kind(false);
+ // Matching unit structs with tuple variant patterns (`UnitVariant(..)`)
+ // is allowed for backward compatibility.
+ let is_special_case = variant.kind() == ty::VariantKind::Unit;
+ report_bad_struct_kind(is_special_case);
return;
}
(variant.fields
use middle::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer};
use middle::ty::adjustment::{AdjustUnsafeFnPointer};
use middle::ty::{self, LvaluePreference, TypeAndMut, Ty};
+use middle::ty::fold::TypeFoldable;
use middle::ty::error::TypeError;
use middle::ty::relate::RelateResult;
use util::common::indent;
a,
b);
+ let a = self.fcx.infcx().shallow_resolve(a);
+
+ // Just ignore error types.
+ if a.references_error() || b.references_error() {
+ return Ok(None);
+ }
+
// Consider coercing the subtype to a DST
- let unsize = self.unpack_actual_value(a, |a| {
- self.coerce_unsized(a, b)
- });
+ let unsize = self.coerce_unsized(a, b);
if unsize.is_ok() {
return unsize;
}
// See above for details.
match b.sty {
ty::TyRawPtr(mt_b) => {
- return self.unpack_actual_value(a, |a| {
- self.coerce_unsafe_ptr(a, b, mt_b.mutbl)
- });
+ return self.coerce_unsafe_ptr(a, b, mt_b.mutbl);
}
ty::TyRef(_, mt_b) => {
- return self.unpack_actual_value(a, |a| {
- self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl)
- });
+ return self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl);
}
_ => {}
}
- self.unpack_actual_value(a, |a| {
- match a.sty {
- ty::TyBareFn(Some(_), a_f) => {
- // Function items are coercible to any closure
- // type; function pointers are not (that would
- // require double indirection).
- self.coerce_from_fn_item(a, a_f, b)
- }
- ty::TyBareFn(None, a_f) => {
- // We permit coercion of fn pointers to drop the
- // unsafe qualifier.
- self.coerce_from_fn_pointer(a, a_f, b)
- }
- _ => {
- // Otherwise, just use subtyping rules.
- self.subtype(a, b)
- }
+ match a.sty {
+ ty::TyBareFn(Some(_), a_f) => {
+ // Function items are coercible to any closure
+ // type; function pointers are not (that would
+ // require double indirection).
+ self.coerce_from_fn_item(a, a_f, b)
}
- })
+ ty::TyBareFn(None, a_f) => {
+ // We permit coercion of fn pointers to drop the
+ // unsafe qualifier.
+ self.coerce_from_fn_pointer(a, a_f, b)
+ }
+ _ => {
+ // Otherwise, just use subtyping rules.
+ self.subtype(a, b)
+ }
+ }
}
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
t_cast: Ty<'tcx>,
t_expr: Ty<'tcx>,
id: ast::NodeId) {
+ if t_cast.references_error() || t_expr.references_error() {
+ return;
+ }
let tstr = fcx.infcx().ty_to_string(t_cast);
let mut err = fcx.type_error_struct(span, |actual| {
format!("cast to unsized type: `{}` as `{}`", actual, tstr)
let t_cast = structurally_resolved_type(fcx, expr.span, t_cast);
check_expr_with_expectation(fcx, e, ExpectCastableToType(t_cast));
let t_expr = fcx.expr_ty(e);
+ let t_cast = fcx.infcx().resolve_type_vars_if_possible(&t_cast);
// Eagerly check for some obvious errors.
- if t_expr.references_error() {
+ if t_expr.references_error() || t_cast.references_error() {
fcx.write_error(id);
} else if !fcx.type_is_known_to_be_sized(t_cast, expr.span) {
report_cast_to_unsized_type(fcx, expr.span, t.span, e.span, t_cast, t_expr, id);
trait_ref,
item.name);
+ // Skip impls where one of the self type is an error type.
+ // This occurs with e.g. resolve failures (#30589).
+ if trait_ref.references_error() {
+ return;
+ }
+
enforce_trait_manually_implementable(self.crate_context.tcx,
item.span,
trait_ref.def_id);
self.add_trait_impl(trait_ref, impl_did);
} else {
+ // Skip inherent impls where the self type is an error
+ // type. This occurs with e.g. resolve failures (#30589).
+ if self_type.ty.references_error() {
+ return;
+ }
+
// Add the implementation to the mapping from implementation to base
// type def ID, if there is a base type for this implementation and
// the implementation does not have any associated traits.
/// that the unicode parts of `CharExt` and `UnicodeStrPrelude` traits are based on.
pub const UNICODE_VERSION: (u64, u64, u64) = (8, 0, 0);
-fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool {
+fn bsearch_range_table(c: char, r: &'static [(char, char)]) -> bool {
use core::cmp::Ordering::{Equal, Less, Greater};
- r.binary_search_by(|&(lo,hi)| {
- if lo <= c && c <= hi { Equal }
- else if hi < c { Less }
- else { Greater }
- }).is_ok()
+ r.binary_search_by(|&(lo, hi)| {
+ if c < lo {
+ Greater
+ } else if hi < c {
+ Less
+ } else {
+ Equal
+ }
+ })
+ .is_ok()
}
pub mod general_category {
}
pub mod conversions {
- use core::cmp::Ordering::{Equal, Less, Greater};
use core::option::Option;
use core::option::Option::{Some, None};
- use core::result::Result::{Ok, Err};
pub fn to_lower(c: char) -> [char; 3] {
match bsearch_case_table(c, to_lowercase_table) {
- None => [c, '\0', '\0'],
- Some(index) => to_lowercase_table[index].1
+ None => [c, '\0', '\0'],
+ Some(index) => to_lowercase_table[index].1,
}
}
pub fn to_upper(c: char) -> [char; 3] {
match bsearch_case_table(c, to_uppercase_table) {
None => [c, '\0', '\0'],
- Some(index) => to_uppercase_table[index].1
+ Some(index) => to_uppercase_table[index].1,
}
}
fn bsearch_case_table(c: char, table: &'static [(char, [char; 3])]) -> Option<usize> {
- match table.binary_search_by(|&(key, _)| {
- if c == key { Equal }
- else if key < c { Less }
- else { Greater }
- }) {
- Ok(i) => Some(i),
- Err(_) => None,
- }
+ table.binary_search_by(|&(key, _)| key.cmp(&c)).ok()
}
const to_lowercase_table: &'static [(char, [char; 3])] = &[
fill_in(cx, tcx, did, items);
}
cstore::DlDef(def) if item.vis == hir::Public => {
- if !visited.insert(def) { return }
+ if !visited.insert(def) { continue }
match try_inline_def(cx, tcx, def) {
Some(i) => items.extend(i),
None => {}
} else if stab.level == stability::Unstable {
let unstable_extra = if show_reason {
match (!stab.feature.is_empty(), &cx.issue_tracker_base_url, stab.issue) {
- (true, &Some(ref tracker_url), Some(issue_no)) =>
+ (true, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 =>
format!(" (<code>{}</code> <a href=\"{}{}\">#{}</a>)",
Escape(&stab.feature), tracker_url, issue_no, issue_no),
- (false, &Some(ref tracker_url), Some(issue_no)) =>
+ (false, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 =>
format!(" (<a href=\"{}{}\">#{}</a>)", Escape(&tracker_url), issue_no,
issue_no),
(true, _, _) =>
}
}
+#[stable(feature = "string_box_error", since = "1.7.0")]
+impl From<String> for Box<Error> {
+ fn from(str_err: String) -> Box<Error> {
+ let err1: Box<Error + Send + Sync> = From::from(str_err);
+ let err2: Box<Error> = err1;
+ err2
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, 'b> From<&'b str> for Box<Error + Send + Sync + 'a> {
fn from(err: &'b str) -> Box<Error + Send + Sync + 'a> {
}
}
+#[stable(feature = "string_box_error", since = "1.7.0")]
+impl<'a> From<&'a str> for Box<Error> {
+ fn from(err: &'a str) -> Box<Error> {
+ From::from(String::from(err))
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for str::ParseBoolError {
fn description(&self) -> &str { "failed to parse bool" }
}
}
-struct InternalBufWriter<W: Write>(BufWriter<W>);
-
-impl<W: Read + Write> InternalBufWriter<W> {
- fn get_mut(&mut self) -> &mut BufWriter<W> {
- let InternalBufWriter(ref mut w) = *self;
- return w;
- }
-}
-
-impl<W: Read + Write> Read for InternalBufWriter<W> {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- self.get_mut().inner.as_mut().unwrap().read(buf)
- }
-}
-
#[cfg(test)]
mod tests {
use prelude::v1::*;
#![feature(slice_concat_ext)]
#![feature(slice_patterns)]
#![feature(staged_api)]
+#![feature(stmt_expr_attributes)]
#![feature(str_char)]
#![feature(str_internals)]
#![feature(str_utf16)]
mod shims {
use libc::{c_float, c_int};
+ #[inline]
pub unsafe fn acosf(n: c_float) -> c_float {
f64::acos(n as f64) as c_float
}
+ #[inline]
pub unsafe fn asinf(n: c_float) -> c_float {
f64::asin(n as f64) as c_float
}
+ #[inline]
pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float {
f64::atan2(n as f64, b as f64) as c_float
}
+ #[inline]
pub unsafe fn atanf(n: c_float) -> c_float {
f64::atan(n as f64) as c_float
}
+ #[inline]
pub unsafe fn coshf(n: c_float) -> c_float {
f64::cosh(n as f64) as c_float
}
+ #[inline]
pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float {
let (a, b) = f64::frexp(x as f64);
*value = b as c_int;
a as c_float
}
+ #[inline]
pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float {
f64::ldexp(x as f64, n as isize) as c_float
}
+ #[inline]
pub unsafe fn sinhf(n: c_float) -> c_float {
f64::sinh(n as f64) as c_float
}
+ #[inline]
pub unsafe fn tanf(n: c_float) -> c_float {
f64::tan(n as f64) as c_float
}
+ #[inline]
pub unsafe fn tanhf(n: c_float) -> c_float {
f64::tanh(n as f64) as c_float
}
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn floor(self) -> f32 {
- return floorf(self);
-
// On MSVC LLVM will lower many math intrinsics to a call to the
// corresponding function. On MSVC, however, many of these functions
// aren't actually available as symbols to call, but rather they are all
// redirect to this comment, so `floorf` is just one case of a missing
// function on MSVC, but there are many others elsewhere.
#[cfg(target_env = "msvc")]
- fn floorf(f: f32) -> f32 { (f as f64).floor() as f32 }
+ return (self as f64).floor() as f32;
#[cfg(not(target_env = "msvc"))]
- fn floorf(f: f32) -> f32 { unsafe { intrinsics::floorf32(f) } }
+ return unsafe { intrinsics::floorf32(self) };
}
/// Returns the smallest integer greater than or equal to a number.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn ceil(self) -> f32 {
- return ceilf(self);
-
// see notes above in `floor`
#[cfg(target_env = "msvc")]
- fn ceilf(f: f32) -> f32 { (f as f64).ceil() as f32 }
+ return (self as f64).ceil() as f32;
#[cfg(not(target_env = "msvc"))]
- fn ceilf(f: f32) -> f32 { unsafe { intrinsics::ceilf32(f) } }
+ return unsafe { intrinsics::ceilf32(self) };
}
/// Returns the nearest integer to a number. Round half-way cases away from
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn powf(self, n: f32) -> f32 {
- return powf(self, n);
-
// see notes above in `floor`
#[cfg(target_env = "msvc")]
- fn powf(f: f32, n: f32) -> f32 { (f as f64).powf(n as f64) as f32 }
+ return (self as f64).powf(n as f64) as f32;
#[cfg(not(target_env = "msvc"))]
- fn powf(f: f32, n: f32) -> f32 { unsafe { intrinsics::powf32(f, n) } }
+ return unsafe { intrinsics::powf32(self, n) };
}
/// Takes the square root of a number.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn exp(self) -> f32 {
- return expf(self);
-
// see notes above in `floor`
#[cfg(target_env = "msvc")]
- fn expf(f: f32) -> f32 { (f as f64).exp() as f32 }
+ return (self as f64).exp() as f32;
#[cfg(not(target_env = "msvc"))]
- fn expf(f: f32) -> f32 { unsafe { intrinsics::expf32(f) } }
+ return unsafe { intrinsics::expf32(self) };
}
/// Returns `2^(self)`.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn ln(self) -> f32 {
- return logf(self);
-
// see notes above in `floor`
#[cfg(target_env = "msvc")]
- fn logf(f: f32) -> f32 { (f as f64).ln() as f32 }
+ return (self as f64).ln() as f32;
#[cfg(not(target_env = "msvc"))]
- fn logf(f: f32) -> f32 { unsafe { intrinsics::logf32(f) } }
+ return unsafe { intrinsics::logf32(self) };
}
/// Returns the logarithm of the number with respect to an arbitrary base.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn log10(self) -> f32 {
- return log10f(self);
-
// see notes above in `floor`
#[cfg(target_env = "msvc")]
- fn log10f(f: f32) -> f32 { (f as f64).log10() as f32 }
+ return (self as f64).log10() as f32;
#[cfg(not(target_env = "msvc"))]
- fn log10f(f: f32) -> f32 { unsafe { intrinsics::log10f32(f) } }
+ return unsafe { intrinsics::log10f32(self) };
}
/// Converts radians to degrees.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn sin(self) -> f32 {
- return sinf(self);
-
// see notes in `core::f32::Float::floor`
#[cfg(target_env = "msvc")]
- fn sinf(f: f32) -> f32 { (f as f64).sin() as f32 }
+ return (self as f64).sin() as f32;
#[cfg(not(target_env = "msvc"))]
- fn sinf(f: f32) -> f32 { unsafe { intrinsics::sinf32(f) } }
+ return unsafe { intrinsics::sinf32(self) };
}
/// Computes the cosine of a number (in radians).
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn cos(self) -> f32 {
- return cosf(self);
-
// see notes in `core::f32::Float::floor`
#[cfg(target_env = "msvc")]
- fn cosf(f: f32) -> f32 { (f as f64).cos() as f32 }
+ return (self as f64).cos() as f32;
#[cfg(not(target_env = "msvc"))]
- fn cosf(f: f32) -> f32 { unsafe { intrinsics::cosf32(f) } }
+ return unsafe { intrinsics::cosf32(self) };
}
/// Computes the tangent of a number (in radians).
// debugger provides a useable stacktrace.
if panics >= 3 {
util::dumb_print(format_args!("thread panicked while processing \
- panic. aborting."));
+ panic. aborting.\n"));
unsafe { intrinsics::abort() }
}
// just abort. In the future we may consider resuming
// unwinding or otherwise exiting the thread cleanly.
util::dumb_print(format_args!("thread panicked while panicking. \
- aborting."));
+ aborting.\n"));
unsafe { intrinsics::abort() }
}
}
// getentropy(2) permits a maximum buffer size of 256 bytes
for s in v.chunks_mut(256) {
let ret = unsafe {
- libc::syscall(libc::NR_GETENTROPY, s.as_mut_ptr(), s.len())
+ libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
};
if ret == -1 {
panic!("unexpected getentropy error: {}", errno());
}
pub fn abort(args: fmt::Arguments) -> ! {
- dumb_print(format_args!("fatal runtime error: {}", args));
+ dumb_print(format_args!("fatal runtime error: {}\n", args));
unsafe { intrinsics::abort(); }
}
#[allow(dead_code)] // stack overflow detection not enabled on all platforms
pub unsafe fn report_overflow() {
- dumb_print(format_args!("\nthread '{}' has overflowed its stack",
+ dumb_print(format_args!("\nthread '{}' has overflowed its stack\n",
thread::current().name().unwrap_or("<unknown>")));
}
#[cfg(any(target_os = "macos",
target_os = "ios",
- target_os = "netbsd"))]
+ target_os = "netbsd",
+ target_os = "openbsd"))]
fn name_bytes(&self) -> &[u8] {
unsafe {
::slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8,
}
#[cfg(any(target_os = "freebsd",
target_os = "dragonfly",
- target_os = "bitrig",
- target_os = "openbsd"))]
+ target_os = "bitrig"))]
fn name_bytes(&self) -> &[u8] {
unsafe {
::slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8,
use libc;
use num::One;
use ops::Neg;
+use alloc::oom;
#[cfg(target_os = "android")] pub use os::android as platform;
#[cfg(target_os = "bitrig")] pub use os::bitrig as platform;
pub mod time;
pub mod stdio;
+// A nicer handler for out-of-memory situations than the default one. This one
+// prints a message to stderr before aborting. It is critical that this code
+// does not allocate any memory since we are in an OOM situation. Any errors are
+// ignored while printing since there's nothing we can do about them and we are
+// about to exit anyways.
+fn oom_handler() -> ! {
+ use intrinsics;
+ let msg = "fatal runtime error: out of memory\n";
+ unsafe {
+ libc::write(libc::STDERR_FILENO,
+ msg.as_ptr() as *const libc::c_void,
+ msg.len() as libc::size_t);
+ intrinsics::abort();
+ }
+}
+
#[cfg(not(any(target_os = "nacl", test)))]
pub fn init() {
use libc::signal;
unsafe {
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0);
}
+
+ oom::set_oom_handler(oom_handler);
}
#[cfg(all(target_os = "nacl", not(test)))]
-pub fn init() { }
+pub fn init() {
+ oom::set_oom_handler(oom_handler);
+}
pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as libc::c_int {
unsafe { libc::_exit(1) }
}
+ // Make sure that the source descriptors are not an stdio descriptor,
+ // otherwise the order which we set the child's descriptors may blow
+ // away a descriptor which we are hoping to save. For example,
+ // suppose we want the child's stderr to be the parent's stdout, and
+ // the child's stdout to be the parent's stderr. No matter which we
+ // dup first, the second will get overwritten prematurely.
+ let maybe_migrate = |src: Stdio, output: &mut AnonPipe| {
+ match src {
+ Stdio::Raw(fd @ libc::STDIN_FILENO) |
+ Stdio::Raw(fd @ libc::STDOUT_FILENO) |
+ Stdio::Raw(fd @ libc::STDERR_FILENO) => {
+ let fd = match cvt_r(|| libc::dup(fd)) {
+ Ok(fd) => fd,
+ Err(_) => fail(output),
+ };
+ let fd = FileDesc::new(fd);
+ fd.set_cloexec();
+ Stdio::Raw(fd.into_raw())
+ },
+
+ s @ Stdio::None |
+ s @ Stdio::Inherit |
+ s @ Stdio::Raw(_) => s,
+ }
+ };
+
let setup = |src: Stdio, dst: c_int| {
match src {
Stdio::Inherit => true,
}
};
+ // Make sure we migrate all source descriptors before
+ // we start overwriting them
+ let in_fd = maybe_migrate(in_fd, &mut output);
+ let out_fd = maybe_migrate(out_fd, &mut output);
+ let err_fd = maybe_migrate(err_fd, &mut output);
+
if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) }
if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) }
if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) }
static mut PAGE_SIZE: usize = 0;
#[cfg(any(target_os = "linux", target_os = "android"))]
- unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> *mut libc::c_void {
+ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
#[repr(C)]
struct siginfo_t {
a: [libc::c_int; 3], // si_signo, si_code, si_errno,
si_addr: *mut libc::c_void,
}
- (*(info as *const siginfo_t)).si_addr
+ (*(info as *const siginfo_t)).si_addr as usize
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
- unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> *mut libc::c_void {
- (*info).si_addr
+ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
+ (*info).si_addr as usize
}
// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
use sys_common::util::report_overflow;
let guard = thread_info::stack_guard().unwrap_or(0);
- let addr = siginfo_si_addr(info) as usize;
+ let addr = siginfo_si_addr(info);
// If the faulting address is within the guard page, then we print a
// message saying so.
use os::windows::ffi::{OsStrExt, OsStringExt};
use path::PathBuf;
use time::Duration;
+use alloc::oom;
#[macro_use] pub mod compat;
pub mod time;
pub mod stdio;
-pub fn init() {}
+// See comment in sys/unix/mod.rs
+fn oom_handler() -> ! {
+ use intrinsics;
+ use ptr;
+ let msg = "fatal runtime error: out of memory\n";
+ unsafe {
+ // WriteFile silently fails if it is passed an invalid handle, so there
+ // is no need to check the result of GetStdHandle.
+ c::WriteFile(c::GetStdHandle(c::STD_ERROR_HANDLE),
+ msg.as_ptr() as c::LPVOID,
+ msg.len() as c::DWORD,
+ ptr::null_mut(),
+ ptr::null_mut());
+ intrinsics::abort();
+ }
+}
+
+pub fn init() {
+ oom::set_oom_handler(oom_handler);
+}
pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as c::DWORD {
issue = "0")]
#[macro_export]
#[allow_internal_unstable]
-#[cfg(no_elf_tls)]
macro_rules! __scoped_thread_local_inner {
($t:ty) => {{
- static _KEY: $crate::thread::__ScopedKeyInner<$t> =
- $crate::thread::__ScopedKeyInner::new();
- fn _getit() -> &'static $crate::thread::__ScopedKeyInner<$t> { &_KEY }
- $crate::thread::ScopedKey::new(_getit)
- }}
-}
-
-#[doc(hidden)]
-#[unstable(feature = "thread_local_internals",
- reason = "should not be necessary",
- issue = "0")]
-#[macro_export]
-#[allow_internal_unstable]
-#[cfg(not(no_elf_tls))]
-macro_rules! __scoped_thread_local_inner {
- ($t:ty) => {{
- #[cfg_attr(not(any(windows,
- target_os = "android",
- target_os = "ios",
- target_os = "netbsd",
- target_os = "openbsd",
- target_arch = "aarch64")),
- thread_local)]
+ #[cfg_attr(target_thread_local, thread_local)]
static _KEY: $crate::thread::__ScopedKeyInner<$t> =
$crate::thread::__ScopedKeyInner::new();
fn _getit() -> &'static $crate::thread::__ScopedKeyInner<$t> { &_KEY }
}
}
-#[cfg(not(any(windows,
- target_os = "android",
- target_os = "ios",
- target_os = "netbsd",
- target_os = "openbsd",
- target_arch = "aarch64",
- no_elf_tls)))]
+#[cfg(target_thread_local)]
#[doc(hidden)]
mod imp {
use cell::Cell;
}
}
-#[cfg(any(windows,
- target_os = "android",
- target_os = "ios",
- target_os = "netbsd",
- target_os = "openbsd",
- target_arch = "aarch64",
- no_elf_tls))]
+#[cfg(not(target_thread_local))]
#[doc(hidden)]
mod imp {
use cell::Cell;
/// let ten_millis = Duration::from_millis(10);
/// ```
#[stable(feature = "duration", since = "1.3.0")]
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Duration {
secs: u64,
nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC
use util::small_vector::SmallVector;
use std::cell::RefCell;
+use std::collections::{HashMap};
+use std::collections::hash_map::{Entry};
use std::rc::Rc;
-use std::iter::once;
struct ParserAnyMacro<'a> {
parser: RefCell<Parser<'a>>,
NormalTT(exp, Some(def.span), def.allow_internal_unstable)
}
+// why is this here? because of https://github.com/rust-lang/rust/issues/27774
+fn ref_slice<A>(s: &A) -> &[A] { use std::slice::from_raw_parts; unsafe { from_raw_parts(s, 1) } }
+
fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &TokenTree, sp: Span) {
// lhs is going to be like TokenTree::Delimited(...), where the
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
match lhs {
&TokenTree::Delimited(_, ref tts) => {
- check_matcher(cx, tts.tts.iter(), &Eof);
+ check_matcher(cx, &tts.tts);
},
tt @ &TokenTree::Sequence(..) => {
- check_matcher(cx, Some(tt).into_iter(), &Eof);
+ check_matcher(cx, ref_slice(tt));
},
_ => cx.span_err(sp, "invalid macro matcher; matchers must be contained \
in balanced delimiters or a repetition indicator")
false
}
-// returns the last token that was checked, for TokenTree::Sequence. this gets used later on.
-fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
+// Issue 30450: when we are through a warning cycle, we can just error
+// on all failure conditions and remove this struct and enum.
+
+#[derive(Debug)]
+struct OnFail {
+ saw_failure: bool,
+ action: OnFailAction,
+}
+
+#[derive(Copy, Clone, Debug)]
+enum OnFailAction { Warn, Error, DoNothing }
+
+impl OnFail {
+ fn warn() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::Warn } }
+ fn error() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::Error } }
+ fn do_nothing() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::DoNothing } }
+ fn react(&mut self, cx: &mut ExtCtxt, sp: Span, msg: &str) {
+ match self.action {
+ OnFailAction::DoNothing => {}
+ OnFailAction::Error => cx.span_err(sp, msg),
+ OnFailAction::Warn => {
+ cx.struct_span_warn(sp, msg)
+ .span_note(sp, "The above warning will be a hard error in the next release.")
+ .emit();
+ }
+ };
+ self.saw_failure = true;
+ }
+}
+
+fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree]) {
+ // Issue 30450: when we are through a warning cycle, we can just
+ // error on all failure conditions (and remove check_matcher_old).
+
+ // First run the old-pass, but *only* to find out if it would have failed.
+ let mut on_fail = OnFail::do_nothing();
+ check_matcher_old(cx, matcher.iter(), &Eof, &mut on_fail);
+ // Then run the new pass, but merely warn if the old pass accepts and new pass rejects.
+ // (Note this silently accepts code if new pass accepts.)
+ let mut on_fail = if on_fail.saw_failure {
+ OnFail::error()
+ } else {
+ OnFail::warn()
+ };
+ check_matcher_new(cx, matcher, &mut on_fail);
+}
+
+// returns the last token that was checked, for TokenTree::Sequence.
+// return value is used by recursive calls.
+fn check_matcher_old<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token, on_fail: &mut OnFail)
-> Option<(Span, Token)> where I: Iterator<Item=&'a TokenTree> {
use print::pprust::token_to_string;
+ use std::iter::once;
let mut last = None;
// look at the token that follows the
// sequence, which may itself be a sequence,
// and so on).
- cx.span_err(sp,
+ on_fail.react(cx, sp,
&format!("`${0}:{1}` is followed by a \
sequence repetition, which is not \
allowed for `{1}` fragments",
// If T' is in the set FOLLOW(NT), continue. Else, reject.
match (&next_token, is_in_follow(cx, &next_token, &frag_spec.name.as_str())) {
(_, Err(msg)) => {
- cx.span_err(sp, &msg);
+ on_fail.react(cx, sp, &msg);
continue
}
(&Eof, _) => return Some((sp, tok.clone())),
(_, Ok(true)) => continue,
(next, Ok(false)) => {
- cx.span_err(sp, &format!("`${0}:{1}` is followed by `{2}`, which \
+ on_fail.react(cx, sp, &format!("`${0}:{1}` is followed by `{2}`, which \
is not allowed for `{1}` fragments",
name, frag_spec,
token_to_string(next)));
// run the algorithm on the contents with F set to U. If it
// accepts, continue, else, reject.
Some(ref u) => {
- let last = check_matcher(cx, seq.tts.iter(), u);
+ let last = check_matcher_old(cx, seq.tts.iter(), u, on_fail);
match last {
// Since the delimiter isn't required after the last
// repetition, make sure that the *next* token is
Some(&&TokenTree::Delimited(_, ref delim)) =>
delim.close_token(),
Some(_) => {
- cx.span_err(sp, "sequence repetition followed by \
+ on_fail.react(cx, sp, "sequence repetition followed by \
another sequence repetition, which is not allowed");
Eof
},
None => Eof
};
- check_matcher(cx, once(&TokenTree::Token(span, tok.clone())),
- &fol)
+ check_matcher_old(cx, once(&TokenTree::Token(span, tok.clone())),
+ &fol, on_fail)
},
None => last,
}
Some(&&TokenTree::Token(_, ref tok)) => tok.clone(),
Some(&&TokenTree::Delimited(_, ref delim)) => delim.close_token(),
Some(_) => {
- cx.span_err(sp, "sequence repetition followed by another \
+ on_fail.react(cx, sp, "sequence repetition followed by another \
sequence repetition, which is not allowed");
Eof
},
None => Eof
};
- check_matcher(cx, seq.tts.iter(), &fol)
+ check_matcher_old(cx, seq.tts.iter(), &fol, on_fail)
}
}
},
TokenTree::Delimited(_, ref tts) => {
// if we don't pass in that close delimiter, we'll incorrectly consider the matcher
// `{ $foo:ty }` as having a follow that isn't `RBrace`
- check_matcher(cx, tts.tts.iter(), &tts.close_token())
+ check_matcher_old(cx, tts.tts.iter(), &tts.close_token(), on_fail)
}
}
}
last
}
+fn check_matcher_new(cx: &mut ExtCtxt, matcher: &[TokenTree], on_fail: &mut OnFail) {
+ let first_sets = FirstSets::new(matcher);
+ let empty_suffix = TokenSet::empty();
+ check_matcher_core(cx, &first_sets, matcher, &empty_suffix, on_fail);
+}
+
+// The FirstSets for a matcher is a mapping from subsequences in the
+// matcher to the FIRST set for that subsequence.
+//
+// This mapping is partially precomputed via a backwards scan over the
+// token trees of the matcher, which provides a mapping from each
+// repetition sequence to its FIRST set.
+//
+// (Hypothetically sequences should be uniquely identifiable via their
+// spans, though perhaps that is false e.g. for macro-generated macros
+// that do not try to inject artificial span information. My plan is
+// to try to catch such cases ahead of time and not include them in
+// the precomputed mapping.)
+struct FirstSets {
+ // this maps each TokenTree::Sequence `$(tt ...) SEP OP` that is uniquely identified by its
+ // span in the original matcher to the First set for the inner sequence `tt ...`.
+ //
+ // If two sequences have the same span in a matcher, then map that
+ // span to None (invalidating the mapping here and forcing the code to
+ // use a slow path).
+ first: HashMap<Span, Option<TokenSet>>,
+}
+
+impl FirstSets {
+ fn new(tts: &[TokenTree]) -> FirstSets {
+ let mut sets = FirstSets { first: HashMap::new() };
+ build_recur(&mut sets, tts);
+ return sets;
+
+ // walks backward over `tts`, returning the FIRST for `tts`
+ // and updating `sets` at the same time for all sequence
+ // substructure we find within `tts`.
+ fn build_recur(sets: &mut FirstSets, tts: &[TokenTree]) -> TokenSet {
+ let mut first = TokenSet::empty();
+ for tt in tts.iter().rev() {
+ match *tt {
+ TokenTree::Token(sp, ref tok) => {
+ first.replace_with((sp, tok.clone()));
+ }
+ TokenTree::Delimited(_, ref delimited) => {
+ build_recur(sets, &delimited.tts[..]);
+ first.replace_with((delimited.open_span,
+ Token::OpenDelim(delimited.delim)));
+ }
+ TokenTree::Sequence(sp, ref seq_rep) => {
+ let subfirst = build_recur(sets, &seq_rep.tts[..]);
+
+ match sets.first.entry(sp) {
+ Entry::Vacant(vac) => {
+ vac.insert(Some(subfirst.clone()));
+ }
+ Entry::Occupied(mut occ) => {
+ // if there is already an entry, then a span must have collided.
+ // This should not happen with typical macro_rules macros,
+ // but syntax extensions need not maintain distinct spans,
+ // so distinct syntax trees can be assigned the same span.
+ // In such a case, the map cannot be trusted; so mark this
+ // entry as unusable.
+ occ.insert(None);
+ }
+ }
+
+ // If the sequence contents can be empty, then the first
+ // token could be the separator token itself.
+
+ if let (Some(ref sep), true) = (seq_rep.separator.clone(),
+ subfirst.maybe_empty) {
+ first.add_one_maybe((sp, sep.clone()));
+ }
+
+ // Reverse scan: Sequence comes before `first`.
+ if subfirst.maybe_empty || seq_rep.op == ast::KleeneOp::ZeroOrMore {
+ // If sequence is potentially empty, then
+ // union them (preserving first emptiness).
+ first.add_all(&TokenSet { maybe_empty: true, ..subfirst });
+ } else {
+ // Otherwise, sequence guaranteed
+ // non-empty; replace first.
+ first = subfirst;
+ }
+ }
+ }
+ }
+
+ return first;
+ }
+ }
+
+ // walks forward over `tts` until all potential FIRST tokens are
+ // identified.
+ fn first(&self, tts: &[TokenTree]) -> TokenSet {
+ let mut first = TokenSet::empty();
+ for tt in tts.iter() {
+ assert!(first.maybe_empty);
+ match *tt {
+ TokenTree::Token(sp, ref tok) => {
+ first.add_one((sp, tok.clone()));
+ return first;
+ }
+ TokenTree::Delimited(_, ref delimited) => {
+ first.add_one((delimited.open_span,
+ Token::OpenDelim(delimited.delim)));
+ return first;
+ }
+ TokenTree::Sequence(sp, ref seq_rep) => {
+ match self.first.get(&sp) {
+ Some(&Some(ref subfirst)) => {
+
+ // If the sequence contents can be empty, then the first
+ // token could be the separator token itself.
+
+ if let (Some(ref sep), true) = (seq_rep.separator.clone(),
+ subfirst.maybe_empty) {
+ first.add_one_maybe((sp, sep.clone()));
+ }
+
+ assert!(first.maybe_empty);
+ first.add_all(subfirst);
+ if subfirst.maybe_empty || seq_rep.op == ast::KleeneOp::ZeroOrMore {
+ // continue scanning for more first
+ // tokens, but also make sure we
+ // restore empty-tracking state
+ first.maybe_empty = true;
+ continue;
+ } else {
+ return first;
+ }
+ }
+
+ Some(&None) => {
+ panic!("assume all sequences have (unique) spans for now");
+ }
+
+ None => {
+ panic!("We missed a sequence during FirstSets construction");
+ }
+ }
+ }
+ }
+ }
+
+ // we only exit the loop if `tts` was empty or if every
+ // element of `tts` matches the empty sequence.
+ assert!(first.maybe_empty);
+ return first;
+ }
+}
+
+// A set of Tokens, which may include MatchNt tokens (for
+// macro-by-example syntactic variables). It also carries the
+// `maybe_empty` flag; that is true if and only if the matcher can
+// match an empty token sequence.
+//
+// The First set is computed on submatchers like `$($a:expr b),* $(c)* d`,
+// which has corresponding FIRST = {$a:expr, c, d}.
+// Likewise, `$($a:expr b),* $(c)+ d` has FIRST = {$a:expr, c}.
+//
+// (Notably, we must allow for *-op to occur zero times.)
+#[derive(Clone, Debug)]
+struct TokenSet {
+ tokens: Vec<(Span, Token)>,
+ maybe_empty: bool,
+}
+
+impl TokenSet {
+ // Returns a set for the empty sequence.
+ fn empty() -> Self { TokenSet { tokens: Vec::new(), maybe_empty: true } }
+
+ // Returns the set `{ tok }` for the single-token (and thus
+ // non-empty) sequence [tok].
+ fn singleton(tok: (Span, Token)) -> Self {
+ TokenSet { tokens: vec![tok], maybe_empty: false }
+ }
+
+ // Changes self to be the set `{ tok }`.
+ // Since `tok` is always present, marks self as non-empty.
+ fn replace_with(&mut self, tok: (Span, Token)) {
+ self.tokens.clear();
+ self.tokens.push(tok);
+ self.maybe_empty = false;
+ }
+
+ // Changes self to be the empty set `{}`; meant for use when
+ // the particular token does not matter, but we want to
+ // record that it occurs.
+ fn replace_with_irrelevant(&mut self) {
+ self.tokens.clear();
+ self.maybe_empty = false;
+ }
+
+ // Adds `tok` to the set for `self`, marking sequence as non-empy.
+ fn add_one(&mut self, tok: (Span, Token)) {
+ if !self.tokens.contains(&tok) {
+ self.tokens.push(tok);
+ }
+ self.maybe_empty = false;
+ }
+
+ // Adds `tok` to the set for `self`. (Leaves `maybe_empty` flag alone.)
+ fn add_one_maybe(&mut self, tok: (Span, Token)) {
+ if !self.tokens.contains(&tok) {
+ self.tokens.push(tok);
+ }
+ }
+
+ // Adds all elements of `other` to this.
+ //
+ // (Since this is a set, we filter out duplicates.)
+ //
+ // If `other` is potentially empty, then preserves the previous
+ // setting of the empty flag of `self`. If `other` is guaranteed
+ // non-empty, then `self` is marked non-empty.
+ fn add_all(&mut self, other: &Self) {
+ for tok in &other.tokens {
+ if !self.tokens.contains(tok) {
+ self.tokens.push(tok.clone());
+ }
+ }
+ if !other.maybe_empty {
+ self.maybe_empty = false;
+ }
+ }
+}
+
+// Checks that `matcher` is internally consistent and that it
+// can legally by followed by a token N, for all N in `follow`.
+// (If `follow` is empty, then it imposes no constraint on
+// the `matcher`.)
+//
+// Returns the set of NT tokens that could possibly come last in
+// `matcher`. (If `matcher` matches the empty sequence, then
+// `maybe_empty` will be set to true.)
+//
+// Requires that `first_sets` is pre-computed for `matcher`;
+// see `FirstSets::new`.
+fn check_matcher_core(cx: &mut ExtCtxt,
+ first_sets: &FirstSets,
+ matcher: &[TokenTree],
+ follow: &TokenSet,
+ on_fail: &mut OnFail) -> TokenSet {
+ use print::pprust::token_to_string;
+
+ let mut last = TokenSet::empty();
+
+ // 2. For each token and suffix [T, SUFFIX] in M:
+ // ensure that T can be followed by SUFFIX, and if SUFFIX may be empty,
+ // then ensure T can also be followed by any element of FOLLOW.
+ 'each_token: for i in 0..matcher.len() {
+ let token = &matcher[i];
+ let suffix = &matcher[i+1..];
+
+ let build_suffix_first = || {
+ let mut s = first_sets.first(suffix);
+ if s.maybe_empty { s.add_all(follow); }
+ return s;
+ };
+
+ // (we build `suffix_first` on demand below; you can tell
+ // which cases are supposed to fall through by looking for the
+ // initialization of this variable.)
+ let suffix_first;
+
+ // First, update `last` so that it corresponds to the set
+ // of NT tokens that might end the sequence `... token`.
+ match *token {
+ TokenTree::Token(sp, ref tok) => {
+ let can_be_followed_by_any;
+ if let Err(bad_frag) = has_legal_fragment_specifier(tok) {
+ on_fail.react(cx, sp, &format!("invalid fragment specifier `{}`", bad_frag));
+ // (This eliminates false positives and duplicates
+ // from error messages.)
+ can_be_followed_by_any = true;
+ } else {
+ can_be_followed_by_any = token_can_be_followed_by_any(tok);
+ }
+
+ if can_be_followed_by_any {
+ // don't need to track tokens that work with any,
+ last.replace_with_irrelevant();
+ // ... and don't need to check tokens that can be
+ // followed by anything against SUFFIX.
+ continue 'each_token;
+ } else {
+ last.replace_with((sp, tok.clone()));
+ suffix_first = build_suffix_first();
+ }
+ }
+ TokenTree::Delimited(_, ref d) => {
+ let my_suffix = TokenSet::singleton((d.close_span, Token::CloseDelim(d.delim)));
+ check_matcher_core(cx, first_sets, &d.tts, &my_suffix, on_fail);
+ // don't track non NT tokens
+ last.replace_with_irrelevant();
+
+ // also, we don't need to check delimited sequences
+ // against SUFFIX
+ continue 'each_token;
+ }
+ TokenTree::Sequence(sp, ref seq_rep) => {
+ suffix_first = build_suffix_first();
+ // The trick here: when we check the interior, we want
+ // to include the separator (if any) as a potential
+ // (but not guaranteed) element of FOLLOW. So in that
+ // case, we make a temp copy of suffix and stuff
+ // delimiter in there.
+ //
+ // FIXME: Should I first scan suffix_first to see if
+ // delimiter is already in it before I go through the
+ // work of cloning it? But then again, this way I may
+ // get a "tighter" span?
+ let mut new;
+ let my_suffix = if let Some(ref u) = seq_rep.separator {
+ new = suffix_first.clone();
+ new.add_one_maybe((sp, u.clone()));
+ &new
+ } else {
+ &suffix_first
+ };
+
+ // At this point, `suffix_first` is built, and
+ // `my_suffix` is some TokenSet that we can use
+ // for checking the interior of `seq_rep`.
+ let next = check_matcher_core(cx, first_sets, &seq_rep.tts, my_suffix, on_fail);
+ if next.maybe_empty {
+ last.add_all(&next);
+ } else {
+ last = next;
+ }
+
+ // the recursive call to check_matcher_core already ran the 'each_last
+ // check below, so we can just keep going forward here.
+ continue 'each_token;
+ }
+ }
+
+ // (`suffix_first` guaranteed initialized once reaching here.)
+
+ // Now `last` holds the complete set of NT tokens that could
+ // end the sequence before SUFFIX. Check that every one works with `suffix`.
+ 'each_last: for &(_sp, ref t) in &last.tokens {
+ if let MatchNt(ref name, ref frag_spec, _, _) = *t {
+ for &(sp, ref next_token) in &suffix_first.tokens {
+ match is_in_follow(cx, next_token, &frag_spec.name.as_str()) {
+ Err(msg) => {
+ on_fail.react(cx, sp, &msg);
+ // don't bother reporting every source of
+ // conflict for a particular element of `last`.
+ continue 'each_last;
+ }
+ Ok(true) => {}
+ Ok(false) => {
+ let may_be = if last.tokens.len() == 1 &&
+ suffix_first.tokens.len() == 1
+ {
+ "is"
+ } else {
+ "may be"
+ };
+
+ on_fail.react(
+ cx, sp,
+ &format!("`${name}:{frag}` {may_be} followed by `{next}`, which \
+ is not allowed for `{frag}` fragments",
+ name=name,
+ frag=frag_spec,
+ next=token_to_string(next_token),
+ may_be=may_be));
+ }
+ }
+ }
+ }
+ }
+ }
+ last
+}
+
+
+fn token_can_be_followed_by_any(tok: &Token) -> bool {
+ if let &MatchNt(_, ref frag_spec, _, _) = tok {
+ frag_can_be_followed_by_any(&frag_spec.name.as_str())
+ } else {
+ // (Non NT's can always be followed by anthing in matchers.)
+ true
+ }
+}
+
+/// True if a fragment of type `frag` can be followed by any sort of
+/// token. We use this (among other things) as a useful approximation
+/// for when `frag` can be followed by a repetition like `$(...)*` or
+/// `$(...)+`. In general, these can be a bit tricky to reason about,
+/// so we adopt a conservative position that says that any fragment
+/// specifier which consumes at most one token tree can be followed by
+/// a fragment specifier (indeed, these fragments can be followed by
+/// ANYTHING without fear of future compatibility hazards).
+fn frag_can_be_followed_by_any(frag: &str) -> bool {
+ match frag {
+ "item" | // always terminated by `}` or `;`
+ "block" | // exactly one token tree
+ "ident" | // exactly one token tree
+ "meta" | // exactly one token tree
+ "tt" => // exactly one token tree
+ true,
+
+ _ =>
+ false,
+ }
+}
+
/// True if a fragment of type `frag` can be followed by any sort of
/// token. We use this (among other things) as a useful approximation
/// for when `frag` can be followed by a repetition like `$(...)*` or
}
/// True if `frag` can legally be followed by the token `tok`. For
-/// fragments that can consume an unbounded numbe of tokens, `tok`
+/// fragments that can consume an unbounded number of tokens, `tok`
/// must be within a well-defined follow set. This is intended to
/// guarantee future compatibility: for example, without this rule, if
/// we expanded `expr` to include a new binary operator, we might
},
"pat" => {
match *tok {
- FatArrow | Comma | Eq => Ok(true),
- Ident(i, _) if i.name.as_str() == "if" || i.name.as_str() == "in" => Ok(true),
+ FatArrow | Comma | Eq | BinOp(token::Or) => Ok(true),
+ Ident(i, _) if (i.name.as_str() == "if" ||
+ i.name.as_str() == "in") => Ok(true),
_ => Ok(false)
}
},
"path" | "ty" => {
match *tok {
- Comma | FatArrow | Colon | Eq | Gt | Semi => Ok(true),
- Ident(i, _) if i.name.as_str() == "as" => Ok(true),
+ OpenDelim(token::DelimToken::Brace) |
+ Comma | FatArrow | Colon | Eq | Gt | Semi | BinOp(token::Or) => Ok(true),
+ Ident(i, _) if (i.name.as_str() == "as" ||
+ i.name.as_str() == "where") => Ok(true),
_ => Ok(false)
}
},
}
}
}
+
+fn has_legal_fragment_specifier(tok: &Token) -> Result<(), String> {
+ debug!("has_legal_fragment_specifier({:?})", tok);
+ if let &MatchNt(_, ref frag_spec, _, _) = tok {
+ let s = &frag_spec.name.as_str();
+ if !is_legal_fragment_specifier(s) {
+ return Err(s.to_string());
+ }
+ }
+ Ok(())
+}
+
+fn is_legal_fragment_specifier(frag: &str) -> bool {
+ match frag {
+ "item" | "block" | "stmt" | "expr" | "pat" |
+ "path" | "ty" | "ident" | "meta" | "tt" => true,
+ _ => false,
+ }
+}
("slice_patterns", "1.0.0", Some(23121), Active),
// Allows use of unary negate on unsigned integers, e.g. -e for e: u8
- ("negate_unsigned", "1.0.0", Some(29645), Active),
+ ("negate_unsigned", "1.0.0", Some(29645), Removed),
// Allows the definition of associated constants in `trait` or `impl`
// blocks.
pub allow_pushpop_unsafe: bool,
pub simd_ffi: bool,
pub unmarked_api: bool,
- pub negate_unsigned: bool,
/// spans of #![feature] attrs for stable language features. for error reporting
pub declared_stable_lang_features: Vec<Span>,
/// #![feature] attrs for non-language (library) features
allow_pushpop_unsafe: false,
simd_ffi: false,
unmarked_api: false,
- negate_unsigned: false,
declared_stable_lang_features: Vec::new(),
declared_lib_features: Vec::new(),
const_fn: false,
allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
simd_ffi: cx.has_feature("simd_ffi"),
unmarked_api: cx.has_feature("unmarked_api"),
- negate_unsigned: cx.has_feature("negate_unsigned"),
declared_stable_lang_features: accepted_features,
declared_lib_features: unknown_features,
const_fn: cx.has_feature("const_fn"),
}
pub fn is_doc_comment(s: &str) -> bool {
- (s.starts_with("///") && super::is_doc_comment(s)) ||
- s.starts_with("//!") ||
- (s.starts_with("/**") && is_block_doc_comment(s)) ||
- s.starts_with("/*!")
+ (s.starts_with("///") && super::is_doc_comment(s)) || s.starts_with("//!") ||
+ (s.starts_with("/**") && is_block_doc_comment(s)) || s.starts_with("/*!")
}
pub fn doc_comment_style(comment: &str) -> ast::AttrStyle {
let mut i = 0;
let mut j = lines.len();
// first line of all-stars should be omitted
- if !lines.is_empty() &&
- lines[0].chars().all(|c| c == '*') {
+ if !lines.is_empty() && lines[0].chars().all(|c| c == '*') {
i += 1;
}
while i < j && lines[i].trim().is_empty() {
i += 1;
}
// like the first, a last line of all stars should be omitted
- if j > i && lines[j - 1]
- .chars()
- .skip(1)
- .all(|c| c == '*') {
+ if j > i &&
+ lines[j - 1]
+ .chars()
+ .skip(1)
+ .all(|c| c == '*') {
j -= 1;
}
while j > i && lines[j - 1].trim().is_empty() {
}
/// remove a "[ \t]*\*" block from each line, if possible
- fn horizontal_trim(lines: Vec<String> ) -> Vec<String> {
+ fn horizontal_trim(lines: Vec<String>) -> Vec<String> {
let mut i = usize::MAX;
let mut can_trim = true;
let mut first = true;
}
if can_trim {
- lines.iter().map(|line| {
- (&line[i + 1..line.len()]).to_string()
- }).collect()
+ lines.iter()
+ .map(|line| (&line[i + 1..line.len()]).to_string())
+ .collect()
} else {
lines
}
if comment.starts_with("/*") {
let lines = comment[3..comment.len() - 2]
- .lines()
- .map(|s| s.to_string())
- .collect::<Vec<String> >();
+ .lines()
+ .map(|s| s.to_string())
+ .collect::<Vec<String>>();
let lines = vertical_trim(lines);
let lines = horizontal_trim(lines);
});
}
-fn consume_whitespace_counting_blank_lines(rdr: &mut StringReader,
- comments: &mut Vec<Comment>) {
+fn consume_whitespace_counting_blank_lines(rdr: &mut StringReader, comments: &mut Vec<Comment>) {
while is_whitespace(rdr.curr) && !rdr.is_eof() {
if rdr.col == CharPos(0) && rdr.curr_is('\n') {
push_blank_line_comment(rdr, &mut *comments);
}
-fn read_shebang_comment(rdr: &mut StringReader, code_to_the_left: bool,
+fn read_shebang_comment(rdr: &mut StringReader,
+ code_to_the_left: bool,
comments: &mut Vec<Comment>) {
debug!(">>> shebang comment");
let p = rdr.last_pos;
debug!("<<< shebang comment");
comments.push(Comment {
style: if code_to_the_left { Trailing } else { Isolated },
- lines: vec!(rdr.read_one_line_comment()),
- pos: p
+ lines: vec![rdr.read_one_line_comment()],
+ pos: p,
});
}
-fn read_line_comments(rdr: &mut StringReader, code_to_the_left: bool,
+fn read_line_comments(rdr: &mut StringReader,
+ code_to_the_left: bool,
comments: &mut Vec<Comment>) {
debug!(">>> line comments");
let p = rdr.last_pos;
comments.push(Comment {
style: if code_to_the_left { Trailing } else { Isolated },
lines: lines,
- pos: p
+ pos: p,
});
}
}
return Some(cursor);
}
-fn trim_whitespace_prefix_and_push_line(lines: &mut Vec<String> ,
- s: String, col: CharPos) {
+fn trim_whitespace_prefix_and_push_line(lines: &mut Vec<String>, s: String, col: CharPos) {
let len = s.len();
let s1 = match all_whitespace(&s[..], col) {
Some(col) => {
fn read_block_comment(rdr: &mut StringReader,
code_to_the_left: bool,
- comments: &mut Vec<Comment> ) {
+ comments: &mut Vec<Comment>) {
debug!(">>> block comment");
let p = rdr.last_pos;
let mut lines: Vec<String> = Vec::new();
rdr.bump();
}
if is_block_doc_comment(&curr_line[..]) {
- return
+ return;
}
assert!(!curr_line.contains('\n'));
lines.push(curr_line);
panic!(rdr.fatal("unterminated block comment"));
}
if rdr.curr_is('\n') {
- trim_whitespace_prefix_and_push_line(&mut lines,
- curr_line,
- col);
+ trim_whitespace_prefix_and_push_line(&mut lines, curr_line, col);
curr_line = String::new();
rdr.bump();
} else {
rdr.bump();
curr_line.push('/');
level -= 1;
- } else { rdr.bump(); }
+ } else {
+ rdr.bump();
+ }
}
}
}
if !curr_line.is_empty() {
- trim_whitespace_prefix_and_push_line(&mut lines,
- curr_line,
- col);
+ trim_whitespace_prefix_and_push_line(&mut lines, curr_line, col);
}
}
- let mut style = if code_to_the_left { Trailing } else { Isolated };
+ let mut style = if code_to_the_left {
+ Trailing
+ } else {
+ Isolated
+ };
rdr.consume_non_eol_whitespace();
if !rdr.is_eof() && !rdr.curr_is('\n') && lines.len() == 1 {
style = Mixed;
}
debug!("<<< block comment");
- comments.push(Comment {style: style, lines: lines, pos: p});
+ comments.push(Comment {
+ style: style,
+ lines: lines,
+ pos: p,
+ });
}
-fn consume_comment(rdr: &mut StringReader,
- code_to_the_left: bool,
- comments: &mut Vec<Comment> ) {
+fn consume_comment(rdr: &mut StringReader, code_to_the_left: bool, comments: &mut Vec<Comment>) {
debug!(">>> consume comment");
if rdr.curr_is('/') && rdr.nextch_is('/') {
read_line_comments(rdr, code_to_the_left, comments);
read_block_comment(rdr, code_to_the_left, comments);
} else if rdr.curr_is('#') && rdr.nextch_is('!') {
read_shebang_comment(rdr, code_to_the_left, comments);
- } else { panic!(); }
+ } else {
+ panic!();
+ }
debug!("<<< consume comment");
}
pub fn gather_comments_and_literals(span_diagnostic: &errors::Handler,
path: String,
srdr: &mut Read)
- -> (Vec<Comment>, Vec<Literal>) {
+ -> (Vec<Comment>, Vec<Literal>) {
let mut src = Vec::new();
srdr.read_to_end(&mut src).unwrap();
let src = String::from_utf8(src).unwrap();
let bstart = rdr.last_pos;
rdr.next_token();
- //discard, and look ahead; we're working with internal state
+ // discard, and look ahead; we're working with internal state
let TokenAndSpan { tok, sp } = rdr.peek();
if tok.is_lit() {
rdr.with_str_from(bstart, |s| {
debug!("tok lit: {}", s);
- literals.push(Literal {lit: s.to_string(), pos: sp.lo});
+ literals.push(Literal {
+ lit: s.to_string(),
+ pos: sp.lo,
+ });
})
} else {
debug!("tok: {}", pprust::token_to_string(&tok));
mod tests {
use super::*;
- #[test] fn test_block_doc_comment_1() {
+ #[test]
+ fn test_block_doc_comment_1() {
let comment = "/**\n * Test \n ** Test\n * Test\n*/";
let stripped = strip_doc_comment_decoration(comment);
assert_eq!(stripped, " Test \n* Test\n Test");
}
- #[test] fn test_block_doc_comment_2() {
+ #[test]
+ fn test_block_doc_comment_2() {
let comment = "/**\n * Test\n * Test\n*/";
let stripped = strip_doc_comment_decoration(comment);
assert_eq!(stripped, " Test\n Test");
}
- #[test] fn test_block_doc_comment_3() {
+ #[test]
+ fn test_block_doc_comment_3() {
let comment = "/**\n let a: *i32;\n *a = 5;\n*/";
let stripped = strip_doc_comment_decoration(comment);
assert_eq!(stripped, " let a: *i32;\n *a = 5;");
}
- #[test] fn test_block_doc_comment_4() {
+ #[test]
+ fn test_block_doc_comment_4() {
let comment = "/*******************\n test\n *********************/";
let stripped = strip_doc_comment_decoration(comment);
assert_eq!(stripped, " test");
}
- #[test] fn test_line_doc_comment() {
+ #[test]
+ fn test_line_doc_comment() {
let stripped = strip_doc_comment_decoration("/// test");
assert_eq!(stripped, " test");
let stripped = strip_doc_comment_decoration("///! test");
match t.tok {
token::Whitespace | token::Comment | token::Shebang(_) => {
t = self.next_token();
- },
- _ => break
+ }
+ _ => break,
}
}
t
/// The last character to be read
pub curr: Option<char>,
pub filemap: Rc<codemap::FileMap>,
- /* cached: */
+ // cached:
pub peek_tok: token::Token,
pub peek_span: Span,
// cache a direct reference to the source text, so that we don't have to
// retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
- source_text: Rc<String>
+ source_text: Rc<String>,
}
impl<'a> Reader for StringReader<'a> {
- fn is_eof(&self) -> bool { self.curr.is_none() }
+ fn is_eof(&self) -> bool {
+ self.curr.is_none()
+ }
/// Return the next token. EFFECT: advances the string_reader.
fn next_token(&mut self) -> TokenAndSpan {
let ret_val = TokenAndSpan {
impl<'a> StringReader<'a> {
/// For comments.rs, which hackily pokes into pos and curr
pub fn new_raw<'b>(span_diagnostic: &'b Handler,
- filemap: Rc<codemap::FileMap>) -> StringReader<'b> {
+ filemap: Rc<codemap::FileMap>)
+ -> StringReader<'b> {
if filemap.src.is_none() {
- span_diagnostic.bug(&format!("Cannot lex filemap without source: {}",
- filemap.name)[..]);
+ span_diagnostic.bug(&format!("Cannot lex filemap \
+ without source: {}",
+ filemap.name)[..]);
}
let source_text = (*filemap.src.as_ref().unwrap()).clone();
col: CharPos(0),
curr: Some('\n'),
filemap: filemap,
- /* dummy values; not read */
+ // dummy values; not read
peek_tok: token::Eof,
peek_span: codemap::DUMMY_SP,
- source_text: source_text
+ source_text: source_text,
};
sr.bump();
sr
}
pub fn new<'b>(span_diagnostic: &'b Handler,
- filemap: Rc<codemap::FileMap>) -> StringReader<'b> {
+ filemap: Rc<codemap::FileMap>)
+ -> StringReader<'b> {
let mut sr = StringReader::new_raw(span_diagnostic, filemap);
sr.advance_token();
sr
fn fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) -> FatalError {
let mut m = m.to_string();
m.push_str(": ");
- for c in c.escape_default() { m.push(c) }
+ for c in c.escape_default() {
+ m.push(c)
+ }
self.fatal_span_(from_pos, to_pos, &m[..])
}
fn struct_fatal_span_char(&self,
to_pos: BytePos,
m: &str,
c: char)
- -> DiagnosticBuilder<'a> {
+ -> DiagnosticBuilder<'a> {
let mut m = m.to_string();
m.push_str(": ");
- for c in c.escape_default() { m.push(c) }
+ for c in c.escape_default() {
+ m.push(c)
+ }
self.span_diagnostic.struct_span_fatal(codemap::mk_sp(from_pos, to_pos), &m[..])
}
fn err_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) {
let mut m = m.to_string();
m.push_str(": ");
- for c in c.escape_default() { m.push(c) }
+ for c in c.escape_default() {
+ m.push(c)
+ }
self.err_span_(from_pos, to_pos, &m[..]);
}
fn struct_err_span_char(&self,
to_pos: BytePos,
m: &str,
c: char)
- -> DiagnosticBuilder<'a> {
+ -> DiagnosticBuilder<'a> {
let mut m = m.to_string();
m.push_str(": ");
- for c in c.escape_default() { m.push(c) }
+ for c in c.escape_default() {
+ m.push(c)
+ }
self.span_diagnostic.struct_span_err(codemap::mk_sp(from_pos, to_pos), &m[..])
}
Some(comment) => {
self.peek_span = comment.sp;
self.peek_tok = comment.tok;
- },
+ }
None => {
if self.is_eof() {
self.peek_tok = token::Eof;
} else {
let start_bytepos = self.last_pos;
self.peek_tok = self.next_token_inner();
- self.peek_span = codemap::mk_sp(start_bytepos,
- self.last_pos);
+ self.peek_span = codemap::mk_sp(start_bytepos, self.last_pos);
};
}
}
/// Calls `f` with a string slice of the source text spanning from `start`
/// up to but excluding `self.last_pos`, meaning the slice does not include
/// the character `self.curr`.
- pub fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T where
- F: FnOnce(&str) -> T,
+ pub fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T
+ where F: FnOnce(&str) -> T
{
self.with_str_from_to(start, self.last_pos, f)
}
/// Calls `f` with a string slice of the source text spanning from `start`
/// up to but excluding `end`.
- fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T where
- F: FnOnce(&str) -> T,
+ fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T
+ where F: FnOnce(&str) -> T
{
- f(&self.source_text[self.byte_offset(start).to_usize()..
- self.byte_offset(end).to_usize()])
+ f(&self.source_text[self.byte_offset(start).to_usize()..self.byte_offset(end).to_usize()])
}
/// Converts CRLF to LF in the given string, raising an error on bare CR.
- fn translate_crlf<'b>(&self, start: BytePos,
- s: &'b str, errmsg: &'b str) -> Cow<'b, str> {
+ fn translate_crlf<'b>(&self, start: BytePos, s: &'b str, errmsg: &'b str) -> Cow<'b, str> {
let mut i = 0;
while i < s.len() {
let ch = char_at(s, i);
}
return s.into();
- fn translate_crlf_(rdr: &StringReader, start: BytePos,
- s: &str, errmsg: &str, mut i: usize) -> String {
+ fn translate_crlf_(rdr: &StringReader,
+ start: BytePos,
+ s: &str,
+ errmsg: &str,
+ mut i: usize)
+ -> String {
let mut buf = String::with_capacity(s.len());
let mut j = 0;
while i < s.len() {
let ch = char_at(s, i);
let next = i + ch.len_utf8();
if ch == '\r' {
- if j < i { buf.push_str(&s[j..i]); }
+ if j < i {
+ buf.push_str(&s[j..i]);
+ }
j = next;
if next >= s.len() || char_at(s, next) != '\n' {
let pos = start + BytePos(i as u32);
}
i = next;
}
- if j < s.len() { buf.push_str(&s[j..]); }
+ if j < s.len() {
+ buf.push_str(&s[j..]);
+ }
buf
}
}
pub fn nextnextch(&self) -> Option<char> {
let offset = self.byte_offset(self.pos).to_usize();
let s = &self.source_text[..];
- if offset >= s.len() { return None }
+ if offset >= s.len() {
+ return None;
+ }
let next = offset + char_at(s, offset).len_utf8();
if next < s.len() {
Some(char_at(s, next))
/// Eats <XID_start><XID_continue>*, if possible.
fn scan_optional_raw_name(&mut self) -> Option<ast::Name> {
if !ident_start(self.curr) {
- return None
+ return None;
}
let start = self.last_pos;
while ident_continue(self.curr) {
Some(c) => {
if c.is_whitespace() {
self.span_diagnostic.span_err(codemap::mk_sp(self.last_pos, self.last_pos),
- "called consume_any_line_comment, but there was whitespace");
+ "called consume_any_line_comment, but there \
+ was whitespace");
}
- },
- None => { }
+ }
+ None => {}
}
if self.curr_is('/') {
'\r' => {
if self.nextch_is('\n') {
// CRLF
- break
+ break;
} else if doc_comment {
- self.err_span_(self.last_pos, self.pos,
+ self.err_span_(self.last_pos,
+ self.pos,
"bare CR not allowed in doc-comment");
}
}
- _ => ()
+ _ => (),
}
self.bump();
}
Some(TokenAndSpan {
tok: tok,
- sp: codemap::mk_sp(start_bpos, self.last_pos)
+ sp: codemap::mk_sp(start_bpos, self.last_pos),
})
})
} else {
Some(TokenAndSpan {
tok: token::Comment,
- sp: codemap::mk_sp(start_bpos, self.last_pos)
+ sp: codemap::mk_sp(start_bpos, self.last_pos),
})
- }
+ };
}
Some('*') => {
- self.bump(); self.bump();
+ self.bump();
+ self.bump();
self.scan_block_comment()
}
- _ => None
+ _ => None,
}
} else if self.curr_is('#') {
if self.nextch_is('!') {
if loc.line == 1 && loc.col == CharPos(0) {
// FIXME: Add shebang "token", return it
let start = self.last_pos;
- while !self.curr_is('\n') && !self.is_eof() { self.bump(); }
+ while !self.curr_is('\n') && !self.is_eof() {
+ self.bump();
+ }
return Some(TokenAndSpan {
tok: token::Shebang(self.name_from(start)),
- sp: codemap::mk_sp(start, self.last_pos)
+ sp: codemap::mk_sp(start, self.last_pos),
});
}
}
let c = self.scan_comment();
debug!("scanning a comment {:?}", c);
c
- },
+ }
c if is_whitespace(Some(c)) => {
let start_bpos = self.last_pos;
- while is_whitespace(self.curr) { self.bump(); }
+ while is_whitespace(self.curr) {
+ self.bump();
+ }
let c = Some(TokenAndSpan {
tok: token::Whitespace,
- sp: codemap::mk_sp(start_bpos, self.last_pos)
+ sp: codemap::mk_sp(start_bpos, self.last_pos),
});
debug!("scanning whitespace: {:?}", c);
c
- },
- _ => None
+ }
+ _ => None,
}
}
'\r' => {
has_cr = true;
}
- _ => ()
+ _ => (),
}
self.bump();
}
// but comments with only "*"s between two "/"s are not
let tok = if is_block_doc_comment(string) {
let string = if has_cr {
- self.translate_crlf(start_bpos, string,
+ self.translate_crlf(start_bpos,
+ string,
"bare CR not allowed in block doc-comment")
- } else { string.into() };
+ } else {
+ string.into()
+ };
token::DocComment(token::intern(&string[..]))
} else {
token::Comment
};
- Some(TokenAndSpan{
+ Some(TokenAndSpan {
tok: tok,
- sp: codemap::mk_sp(start_bpos, self.last_pos)
+ sp: codemap::mk_sp(start_bpos, self.last_pos),
})
})
}
let mut len = 0;
loop {
let c = self.curr;
- if c == Some('_') { debug!("skipping a _"); self.bump(); continue; }
+ if c == Some('_') {
+ debug!("skipping a _");
+ self.bump();
+ continue;
+ }
match c.and_then(|cc| cc.to_digit(scan_radix)) {
Some(_) => {
debug!("{:?} in scan_digits", c);
// check that the hypothetical digit is actually
// in range for the true radix
if c.unwrap().to_digit(real_radix).is_none() {
- self.err_span_(self.last_pos, self.pos,
- &format!("invalid digit for a base {} literal",
- real_radix));
+ self.err_span_(self.last_pos,
+ self.pos,
+ &format!("invalid digit for a base {} literal", real_radix));
}
len += 1;
self.bump();
}
- _ => return len
+ _ => return len,
}
- };
+ }
}
/// Lex a LIT_INTEGER or a LIT_FLOAT
if c == '0' {
match self.curr.unwrap_or('\0') {
- 'b' => { self.bump(); base = 2; num_digits = self.scan_digits(2, 10); }
- 'o' => { self.bump(); base = 8; num_digits = self.scan_digits(8, 10); }
- 'x' => { self.bump(); base = 16; num_digits = self.scan_digits(16, 16); }
+ 'b' => {
+ self.bump();
+ base = 2;
+ num_digits = self.scan_digits(2, 10);
+ }
+ 'o' => {
+ self.bump();
+ base = 8;
+ num_digits = self.scan_digits(8, 10);
+ }
+ 'x' => {
+ self.bump();
+ base = 16;
+ num_digits = self.scan_digits(16, 16);
+ }
'0'...'9' | '_' | '.' => {
num_digits = self.scan_digits(10, 10) + 1;
}
}
if num_digits == 0 {
- self.err_span_(start_bpos, self.last_pos, "no valid digits found for number");
+ self.err_span_(start_bpos,
+ self.last_pos,
+ "no valid digits found for number");
return token::Integer(token::intern("0"));
}
// might be a float, but don't be greedy if this is actually an
// integer literal followed by field/method access or a range pattern
// (`0..2` and `12.foo()`)
- if self.curr_is('.') && !self.nextch_is('.') && !self.nextch().unwrap_or('\0')
- .is_xid_start() {
+ if self.curr_is('.') && !self.nextch_is('.') &&
+ !self.nextch()
+ .unwrap_or('\0')
+ .is_xid_start() {
// might have stuff after the ., and if it does, it needs to start
// with a number
self.bump();
/// Scan over `n_digits` hex digits, stopping at `delim`, reporting an
/// error if too many or too few digits are encountered.
- fn scan_hex_digits(&mut self,
- n_digits: usize,
- delim: char,
- below_0x7f_only: bool)
- -> bool {
+ fn scan_hex_digits(&mut self, n_digits: usize, delim: char, below_0x7f_only: bool) -> bool {
debug!("scanning {} digits until {:?}", n_digits, delim);
let start_bpos = self.last_pos;
let mut accum_int = 0;
}
if self.curr_is(delim) {
let last_bpos = self.last_pos;
- self.err_span_(start_bpos, last_bpos, "numeric character escape is too short");
+ self.err_span_(start_bpos,
+ last_bpos,
+ "numeric character escape is too short");
valid = false;
break;
}
let c = self.curr.unwrap_or('\x00');
accum_int *= 16;
accum_int += c.to_digit(16).unwrap_or_else(|| {
- self.err_span_char(self.last_pos, self.pos,
- "invalid character in numeric character escape", c);
+ self.err_span_char(self.last_pos,
+ self.pos,
+ "invalid character in numeric character escape",
+ c);
valid = false;
0
if below_0x7f_only && accum_int >= 0x80 {
self.err_span_(start_bpos,
self.last_pos,
- "this form of character escape may only be used \
- with characters in the range [\\x00-\\x7f]");
+ "this form of character escape may only be used with characters in \
+ the range [\\x00-\\x7f]");
valid = false;
}
/// `start` is the position of `first_source_char`, which is already consumed.
///
/// Returns true if there was a valid char/byte, false otherwise.
- fn scan_char_or_byte(&mut self, start: BytePos, first_source_char: char,
- ascii_only: bool, delim: char) -> bool {
+ fn scan_char_or_byte(&mut self,
+ start: BytePos,
+ first_source_char: char,
+ ascii_only: bool,
+ delim: char)
+ -> bool {
match first_source_char {
'\\' => {
// '\X' for some X must be a character constant:
let escaped_pos = self.last_pos;
self.bump();
match escaped {
- None => {}, // EOF here is an error that will be checked later.
+ None => {} // EOF here is an error that will be checked later.
Some(e) => {
return match e {
'n' | 'r' | 't' | '\\' | '\'' | '"' | '0' => true,
self.scan_unicode_escape(delim) && !ascii_only
} else {
let span = codemap::mk_sp(start, self.last_pos);
- self.span_diagnostic.struct_span_err(span,
- "incorrect unicode escape sequence")
+ self.span_diagnostic
+ .struct_span_err(span, "incorrect unicode escape sequence")
.span_help(span,
- "format of unicode escape sequences is `\\u{…}`")
+ "format of unicode escape sequences is \
+ `\\u{…}`")
.emit();
false
};
if ascii_only {
- self.err_span_(start, self.last_pos,
- "unicode escape sequences cannot be used as a byte or in \
- a byte string"
- );
+ self.err_span_(start,
+ self.last_pos,
+ "unicode escape sequences cannot be used as a \
+ byte or in a byte string");
}
valid
'\n' if delim == '"' => {
self.consume_whitespace();
true
- },
+ }
'\r' if delim == '"' && self.curr_is('\n') => {
self.consume_whitespace();
true
}
c => {
let last_pos = self.last_pos;
- let mut err = self.struct_err_span_char(
- escaped_pos, last_pos,
- if ascii_only { "unknown byte escape" }
- else { "unknown character escape" },
- c);
+ 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' {
err.span_help(codemap::mk_sp(escaped_pos, last_pos),
- "this is an isolated carriage return; consider checking \
- your editor and version control settings");
+ "this is an isolated carriage return; consider \
+ checking your editor and version control \
+ settings");
}
if (e == '{' || e == '}') && !ascii_only {
err.span_help(codemap::mk_sp(escaped_pos, last_pos),
- "if used in a formatting string, \
- curly braces are escaped with `{{` and `}}`");
+ "if used in a formatting string, curly braces \
+ are escaped with `{{` and `}}`");
}
err.emit();
false
}
'\t' | '\n' | '\r' | '\'' if delim == '\'' => {
let last_pos = self.last_pos;
- self.err_span_char(
- start, last_pos,
- if ascii_only { "byte constant must be escaped" }
- else { "character constant must be escaped" },
- first_source_char);
+ self.err_span_char(start,
+ last_pos,
+ if ascii_only {
+ "byte constant must be escaped"
+ } else {
+ "character constant must be escaped"
+ },
+ first_source_char);
return false;
}
'\r' => {
self.bump();
return true;
} else {
- self.err_span_(start, self.last_pos,
+ self.err_span_(start,
+ self.last_pos,
"bare CR not allowed in string, use \\r instead");
return false;
}
}
- _ => if ascii_only && first_source_char > '\x7F' {
- let last_pos = self.last_pos;
- self.err_span_char(
- start, last_pos,
- "byte constant must be ASCII. \
- Use a \\xHH escape for a non-ASCII byte", first_source_char);
- return false;
+ _ => {
+ if ascii_only && first_source_char > '\x7F' {
+ let last_pos = self.last_pos;
+ self.err_span_char(start,
+ last_pos,
+ "byte constant must be ASCII. Use a \\xHH escape for a \
+ non-ASCII byte",
+ first_source_char);
+ return false;
+ }
}
}
true
let c = match self.curr {
Some(c) => c,
None => {
- panic!(self.fatal_span_(start_bpos, self.last_pos,
+ panic!(self.fatal_span_(start_bpos,
+ self.last_pos,
"unterminated unicode escape (found EOF)"));
}
};
accum_int *= 16;
accum_int += c.to_digit(16).unwrap_or_else(|| {
if c == delim {
- panic!(self.fatal_span_(self.last_pos, self.pos,
+ panic!(self.fatal_span_(self.last_pos,
+ self.pos,
"unterminated unicode escape (needed a `}`)"));
} else {
- self.err_span_char(self.last_pos, self.pos,
- "invalid character in unicode escape", c);
+ self.err_span_char(self.last_pos,
+ self.pos,
+ "invalid character in unicode escape",
+ c);
}
valid = false;
0
}
if count > 6 {
- self.err_span_(start_bpos, self.last_pos,
- "overlong unicode escape (can have at most 6 hex digits)");
+ self.err_span_(start_bpos,
+ self.last_pos,
+ "overlong unicode escape (can have at most 6 hex digits)");
valid = false;
}
if valid && (char::from_u32(accum_int).is_none() || count == 0) {
- self.err_span_(start_bpos, self.last_pos, "invalid unicode character escape");
+ self.err_span_(start_bpos,
+ self.last_pos,
+ "invalid unicode character escape");
valid = false;
}
self.bump();
}
if self.scan_digits(10, 10) == 0 {
- self.err_span_(self.last_pos, self.pos, "expected at least one digit in exponent")
+ self.err_span_(self.last_pos,
+ self.pos,
+ "expected at least one digit in exponent")
}
}
}
/// error if it isn't.
fn check_float_base(&mut self, start_bpos: BytePos, last_bpos: BytePos, base: usize) {
match base {
- 16 => self.err_span_(start_bpos, last_bpos, "hexadecimal float literal is not \
- supported"),
- 8 => self.err_span_(start_bpos, last_bpos, "octal float literal is not supported"),
- 2 => self.err_span_(start_bpos, last_bpos, "binary float literal is not supported"),
- _ => ()
+ 16 => {
+ self.err_span_(start_bpos,
+ last_bpos,
+ "hexadecimal float literal is not supported")
+ }
+ 8 => {
+ self.err_span_(start_bpos,
+ last_bpos,
+ "octal float literal is not supported")
+ }
+ 2 => {
+ self.err_span_(start_bpos,
+ last_bpos,
+ "binary float literal is not supported")
+ }
+ _ => (),
}
}
/// token, and updates the interner
fn next_token_inner(&mut self) -> token::Token {
let c = self.curr;
- if ident_start(c) && match (c.unwrap(), self.nextch(), self.nextnextch()) {
+ if ident_start(c) &&
+ match (c.unwrap(), self.nextch(), self.nextnextch()) {
// Note: r as in r" or r#" is part of a raw string literal,
// b as in b' is part of a byte literal.
// They are not identifiers, and are handled further down.
- ('r', Some('"'), _) | ('r', Some('#'), _) |
- ('b', Some('"'), _) | ('b', Some('\''), _) |
- ('b', Some('r'), Some('"')) | ('b', Some('r'), Some('#')) => false,
- _ => true
+ ('r', Some('"'), _) |
+ ('r', Some('#'), _) |
+ ('b', Some('"'), _) |
+ ('b', Some('\''), _) |
+ ('b', Some('r'), Some('"')) |
+ ('b', Some('r'), Some('#')) => false,
+ _ => true,
} {
let start = self.last_pos;
while ident_continue(self.curr) {
let num = self.scan_number(c.unwrap());
let suffix = self.scan_optional_raw_name();
debug!("next_token_inner: scanned number {:?}, {:?}", num, suffix);
- return token::Literal(num, suffix)
+ return token::Literal(num, suffix);
}
match c.expect("next_token_inner called at EOF") {
- // One-byte tokens.
- ';' => { self.bump(); return token::Semi; }
- ',' => { self.bump(); return token::Comma; }
- '.' => {
- self.bump();
- return if self.curr_is('.') {
- self.bump();
- if self.curr_is('.') {
- self.bump();
- token::DotDotDot
- } else {
- token::DotDot
- }
- } else {
- token::Dot
- };
- }
- '(' => { self.bump(); return token::OpenDelim(token::Paren); }
- ')' => { self.bump(); return token::CloseDelim(token::Paren); }
- '{' => { self.bump(); return token::OpenDelim(token::Brace); }
- '}' => { self.bump(); return token::CloseDelim(token::Brace); }
- '[' => { self.bump(); return token::OpenDelim(token::Bracket); }
- ']' => { self.bump(); return token::CloseDelim(token::Bracket); }
- '@' => { self.bump(); return token::At; }
- '#' => { self.bump(); return token::Pound; }
- '~' => { self.bump(); return token::Tilde; }
- '?' => { self.bump(); return token::Question; }
- ':' => {
- self.bump();
- if self.curr_is(':') {
+ // One-byte tokens.
+ ';' => {
self.bump();
- return token::ModSep;
- } else {
- return token::Colon;
+ return token::Semi;
+ }
+ ',' => {
+ self.bump();
+ return token::Comma;
+ }
+ '.' => {
+ self.bump();
+ return if self.curr_is('.') {
+ self.bump();
+ if self.curr_is('.') {
+ self.bump();
+ token::DotDotDot
+ } else {
+ token::DotDot
+ }
+ } else {
+ token::Dot
+ };
+ }
+ '(' => {
+ self.bump();
+ return token::OpenDelim(token::Paren);
+ }
+ ')' => {
+ self.bump();
+ return token::CloseDelim(token::Paren);
+ }
+ '{' => {
+ self.bump();
+ return token::OpenDelim(token::Brace);
+ }
+ '}' => {
+ self.bump();
+ return token::CloseDelim(token::Brace);
+ }
+ '[' => {
+ self.bump();
+ return token::OpenDelim(token::Bracket);
+ }
+ ']' => {
+ self.bump();
+ return token::CloseDelim(token::Bracket);
+ }
+ '@' => {
+ self.bump();
+ return token::At;
+ }
+ '#' => {
+ self.bump();
+ return token::Pound;
+ }
+ '~' => {
+ self.bump();
+ return token::Tilde;
+ }
+ '?' => {
+ self.bump();
+ return token::Question;
+ }
+ ':' => {
+ self.bump();
+ if self.curr_is(':') {
+ self.bump();
+ return token::ModSep;
+ } else {
+ return token::Colon;
+ }
}
- }
- '$' => { self.bump(); return token::Dollar; }
+ '$' => {
+ self.bump();
+ return token::Dollar;
+ }
- // Multi-byte tokens.
- '=' => {
- self.bump();
- if self.curr_is('=') {
+ // Multi-byte tokens.
+ '=' => {
self.bump();
- return token::EqEq;
- } else if self.curr_is('>') {
+ if self.curr_is('=') {
+ self.bump();
+ return token::EqEq;
+ } else if self.curr_is('>') {
+ self.bump();
+ return token::FatArrow;
+ } else {
+ return token::Eq;
+ }
+ }
+ '!' => {
self.bump();
- return token::FatArrow;
- } else {
- return token::Eq;
+ if self.curr_is('=') {
+ self.bump();
+ return token::Ne;
+ } else {
+ return token::Not;
+ }
}
- }
- '!' => {
- self.bump();
- if self.curr_is('=') {
+ '<' => {
self.bump();
- return token::Ne;
- } else { return token::Not; }
- }
- '<' => {
- self.bump();
- match self.curr.unwrap_or('\x00') {
- '=' => { self.bump(); return token::Le; }
- '<' => { return self.binop(token::Shl); }
- '-' => {
+ match self.curr.unwrap_or('\x00') {
+ '=' => {
+ self.bump();
+ return token::Le;
+ }
+ '<' => {
+ return self.binop(token::Shl);
+ }
+ '-' => {
+ self.bump();
+ match self.curr.unwrap_or('\x00') {
+ _ => {
+ return token::LArrow;
+ }
+ }
+ }
+ _ => {
+ return token::Lt;
+ }
+ }
+ }
+ '>' => {
self.bump();
match self.curr.unwrap_or('\x00') {
- _ => { return token::LArrow; }
+ '=' => {
+ self.bump();
+ return token::Ge;
+ }
+ '>' => {
+ return self.binop(token::Shr);
+ }
+ _ => {
+ return token::Gt;
+ }
}
- }
- _ => { return token::Lt; }
}
- }
- '>' => {
- self.bump();
- match self.curr.unwrap_or('\x00') {
- '=' => { self.bump(); return token::Ge; }
- '>' => { return self.binop(token::Shr); }
- _ => { return token::Gt; }
- }
- }
- '\'' => {
- // Either a character constant 'a' OR a lifetime name 'abc
- self.bump();
- let start = self.last_pos;
+ '\'' => {
+ // Either a character constant 'a' OR a lifetime name 'abc
+ self.bump();
+ let start = self.last_pos;
- // the eof will be picked up by the final `'` check below
- let c2 = self.curr.unwrap_or('\x00');
- self.bump();
+ // the eof will be picked up by the final `'` check below
+ let c2 = self.curr.unwrap_or('\x00');
+ self.bump();
- // If the character is an ident start not followed by another single
- // quote, then this is a lifetime name:
- if ident_start(Some(c2)) && !self.curr_is('\'') {
- while ident_continue(self.curr) {
- self.bump();
- }
+ // If the character is an ident start not followed by another single
+ // quote, then this is a lifetime name:
+ if ident_start(Some(c2)) && !self.curr_is('\'') {
+ while ident_continue(self.curr) {
+ self.bump();
+ }
- // Include the leading `'` in the real identifier, for macro
- // expansion purposes. See #12512 for the gory details of why
- // this is necessary.
- let ident = self.with_str_from(start, |lifetime_name| {
- str_to_ident(&format!("'{}", lifetime_name))
- });
+ // Include the leading `'` in the real identifier, for macro
+ // expansion purposes. See #12512 for the gory details of why
+ // this is necessary.
+ let ident = self.with_str_from(start, |lifetime_name| {
+ str_to_ident(&format!("'{}", lifetime_name))
+ });
- // Conjure up a "keyword checking ident" to make sure that
- // the lifetime name is not a keyword.
- let keyword_checking_ident =
- self.with_str_from(start, |lifetime_name| {
+ // Conjure up a "keyword checking ident" to make sure that
+ // the lifetime name is not a keyword.
+ let keyword_checking_ident = self.with_str_from(start, |lifetime_name| {
str_to_ident(lifetime_name)
});
- let keyword_checking_token =
- &token::Ident(keyword_checking_ident, token::Plain);
- let last_bpos = self.last_pos;
- if keyword_checking_token.is_keyword(token::keywords::SelfValue) {
- self.err_span_(start,
- last_bpos,
- "invalid lifetime name: 'self \
- is no longer a special lifetime");
- } else if keyword_checking_token.is_any_keyword() &&
- !keyword_checking_token.is_keyword(token::keywords::Static)
- {
- self.err_span_(start,
- last_bpos,
- "invalid lifetime name");
+ let keyword_checking_token = &token::Ident(keyword_checking_ident,
+ token::Plain);
+ let last_bpos = self.last_pos;
+ if keyword_checking_token.is_keyword(token::keywords::SelfValue) {
+ self.err_span_(start,
+ last_bpos,
+ "invalid lifetime name: 'self is no longer a special \
+ lifetime");
+ } else if keyword_checking_token.is_any_keyword() &&
+ !keyword_checking_token.is_keyword(token::keywords::Static) {
+ self.err_span_(start, last_bpos, "invalid lifetime name");
+ }
+ return token::Lifetime(ident);
}
- return token::Lifetime(ident);
- }
- // Otherwise it is a character constant:
- let valid = self.scan_char_or_byte(start, c2, /* ascii_only = */ false, '\'');
- if !self.curr_is('\'') {
- let last_bpos = self.last_pos;
- panic!(self.fatal_span_verbose(
- // Byte offsetting here is okay because the
- // character before position `start` is an
- // ascii single quote.
- start - BytePos(1), last_bpos,
-
- String::from("character literal may only contain one codepoint")));
- }
- let id = if valid { self.name_from(start) } else { token::intern("0") };
- self.bump(); // advance curr past token
- let suffix = self.scan_optional_raw_name();
- return token::Literal(token::Char(id), suffix);
- }
- 'b' => {
- self.bump();
- let lit = match self.curr {
- Some('\'') => self.scan_byte(),
- Some('"') => self.scan_byte_string(),
- Some('r') => self.scan_raw_byte_string(),
- _ => unreachable!() // Should have been a token::Ident above.
- };
- let suffix = self.scan_optional_raw_name();
- return token::Literal(lit, suffix);
- }
- '"' => {
- let start_bpos = self.last_pos;
- let mut valid = true;
- self.bump();
- while !self.curr_is('"') {
- if self.is_eof() {
+ // Otherwise it is a character constant:
+ let valid = self.scan_char_or_byte(start,
+ c2,
+ // ascii_only =
+ false,
+ '\'');
+ if !self.curr_is('\'') {
let last_bpos = self.last_pos;
- panic!(self.fatal_span_(start_bpos,
- last_bpos,
- "unterminated double quote string"));
+ panic!(self.fatal_span_verbose(// Byte offsetting here is okay because the
+ // character before position `start` is an
+ // ascii single quote.
+ start - BytePos(1),
+ last_bpos,
+
+ String::from("character literal may only \
+ contain one codepoint")));
}
-
- let ch_start = self.last_pos;
- let ch = self.curr.unwrap();
- self.bump();
- valid &= self.scan_char_or_byte(ch_start, ch, /* ascii_only = */ false, '"');
+ let id = if valid {
+ self.name_from(start)
+ } else {
+ token::intern("0")
+ };
+ self.bump(); // advance curr past token
+ let suffix = self.scan_optional_raw_name();
+ return token::Literal(token::Char(id), suffix);
}
- // adjust for the ASCII " at the start of the literal
- let id = if valid { self.name_from(start_bpos + BytePos(1)) }
- else { token::intern("??") };
- self.bump();
- let suffix = self.scan_optional_raw_name();
- return token::Literal(token::Str_(id), suffix);
- }
- 'r' => {
- let start_bpos = self.last_pos;
- self.bump();
- let mut hash_count = 0;
- while self.curr_is('#') {
+ 'b' => {
self.bump();
- hash_count += 1;
+ let lit = match self.curr {
+ Some('\'') => self.scan_byte(),
+ Some('"') => self.scan_byte_string(),
+ Some('r') => self.scan_raw_byte_string(),
+ _ => unreachable!(), // Should have been a token::Ident above.
+ };
+ let suffix = self.scan_optional_raw_name();
+ return token::Literal(lit, suffix);
}
+ '"' => {
+ let start_bpos = self.last_pos;
+ let mut valid = true;
+ self.bump();
+ while !self.curr_is('"') {
+ if self.is_eof() {
+ let last_bpos = self.last_pos;
+ panic!(self.fatal_span_(start_bpos,
+ last_bpos,
+ "unterminated double quote string"));
+ }
- if self.is_eof() {
- let last_bpos = self.last_pos;
- panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
- } else if !self.curr_is('"') {
- let last_bpos = self.last_pos;
- let curr_char = self.curr.unwrap();
- panic!(self.fatal_span_char(start_bpos, last_bpos,
- "found invalid character; \
- only `#` is allowed in raw string delimitation",
- curr_char));
+ let ch_start = self.last_pos;
+ let ch = self.curr.unwrap();
+ self.bump();
+ valid &= self.scan_char_or_byte(ch_start,
+ ch,
+ // ascii_only =
+ false,
+ '"');
+ }
+ // adjust for the ASCII " at the start of the literal
+ let id = if valid {
+ self.name_from(start_bpos + BytePos(1))
+ } else {
+ token::intern("??")
+ };
+ self.bump();
+ let suffix = self.scan_optional_raw_name();
+ return token::Literal(token::Str_(id), suffix);
}
- self.bump();
- let content_start_bpos = self.last_pos;
- let mut content_end_bpos;
- let mut valid = true;
- 'outer: loop {
+ 'r' => {
+ let start_bpos = self.last_pos;
+ self.bump();
+ let mut hash_count = 0;
+ while self.curr_is('#') {
+ self.bump();
+ hash_count += 1;
+ }
+
if self.is_eof() {
let last_bpos = self.last_pos;
panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
+ } else if !self.curr_is('"') {
+ let last_bpos = self.last_pos;
+ let curr_char = self.curr.unwrap();
+ panic!(self.fatal_span_char(start_bpos,
+ last_bpos,
+ "found invalid character; only `#` is allowed \
+ in raw string delimitation",
+ curr_char));
}
- //if self.curr_is('"') {
- //content_end_bpos = self.last_pos;
- //for _ in 0..hash_count {
- //self.bump();
- //if !self.curr_is('#') {
- //continue 'outer;
- let c = self.curr.unwrap();
- match c {
- '"' => {
- content_end_bpos = self.last_pos;
- for _ in 0..hash_count {
- self.bump();
- if !self.curr_is('#') {
- continue 'outer;
+ self.bump();
+ let content_start_bpos = self.last_pos;
+ let mut content_end_bpos;
+ let mut valid = true;
+ 'outer: loop {
+ if self.is_eof() {
+ let last_bpos = self.last_pos;
+ panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
+ }
+ // if self.curr_is('"') {
+ // content_end_bpos = self.last_pos;
+ // for _ in 0..hash_count {
+ // self.bump();
+ // if !self.curr_is('#') {
+ // continue 'outer;
+ let c = self.curr.unwrap();
+ match c {
+ '"' => {
+ content_end_bpos = self.last_pos;
+ for _ in 0..hash_count {
+ self.bump();
+ if !self.curr_is('#') {
+ continue 'outer;
+ }
}
+ break;
}
- break;
- },
- '\r' => {
- if !self.nextch_is('\n') {
- let last_bpos = self.last_pos;
- self.err_span_(start_bpos, last_bpos, "bare CR not allowed in raw \
- string, use \\r instead");
- valid = false;
+ '\r' => {
+ if !self.nextch_is('\n') {
+ let last_bpos = self.last_pos;
+ self.err_span_(start_bpos,
+ last_bpos,
+ "bare CR not allowed in raw string, use \\r \
+ instead");
+ valid = false;
+ }
}
+ _ => (),
}
- _ => ()
+ self.bump();
}
self.bump();
+ let id = if valid {
+ self.name_from_to(content_start_bpos, content_end_bpos)
+ } else {
+ token::intern("??")
+ };
+ let suffix = self.scan_optional_raw_name();
+ return token::Literal(token::StrRaw(id, hash_count), suffix);
+ }
+ '-' => {
+ if self.nextch_is('>') {
+ self.bump();
+ self.bump();
+ return token::RArrow;
+ } else {
+ return self.binop(token::Minus);
+ }
+ }
+ '&' => {
+ if self.nextch_is('&') {
+ self.bump();
+ self.bump();
+ return token::AndAnd;
+ } else {
+ return self.binop(token::And);
+ }
+ }
+ '|' => {
+ match self.nextch() {
+ Some('|') => {
+ self.bump();
+ self.bump();
+ return token::OrOr;
+ }
+ _ => {
+ return self.binop(token::Or);
+ }
+ }
+ }
+ '+' => {
+ return self.binop(token::Plus);
+ }
+ '*' => {
+ return self.binop(token::Star);
+ }
+ '/' => {
+ return self.binop(token::Slash);
+ }
+ '^' => {
+ return self.binop(token::Caret);
+ }
+ '%' => {
+ return self.binop(token::Percent);
+ }
+ c => {
+ let last_bpos = self.last_pos;
+ let bpos = self.pos;
+ 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);
}
- self.bump();
- let id = if valid {
- self.name_from_to(content_start_bpos, content_end_bpos)
- } else {
- token::intern("??")
- };
- let suffix = self.scan_optional_raw_name();
- return token::Literal(token::StrRaw(id, hash_count), suffix);
- }
- '-' => {
- if self.nextch_is('>') {
- self.bump();
- self.bump();
- return token::RArrow;
- } else { return self.binop(token::Minus); }
- }
- '&' => {
- if self.nextch_is('&') {
- self.bump();
- self.bump();
- return token::AndAnd;
- } else { return self.binop(token::And); }
- }
- '|' => {
- match self.nextch() {
- Some('|') => { self.bump(); self.bump(); return token::OrOr; }
- _ => { return self.binop(token::Or); }
- }
- }
- '+' => { return self.binop(token::Plus); }
- '*' => { return self.binop(token::Star); }
- '/' => { return self.binop(token::Slash); }
- '^' => { return self.binop(token::Caret); }
- '%' => { return self.binop(token::Percent); }
- c => {
- let last_bpos = self.last_pos;
- let bpos = self.pos;
- 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);
- }
}
}
fn consume_whitespace(&mut self) {
- while is_whitespace(self.curr) && !self.is_eof() { self.bump(); }
+ while is_whitespace(self.curr) && !self.is_eof() {
+ self.bump();
+ }
}
fn read_to_eol(&mut self) -> String {
val.push(self.curr.unwrap());
self.bump();
}
- if self.curr_is('\n') { self.bump(); }
- return val
+ if self.curr_is('\n') {
+ self.bump();
+ }
+ return val;
}
fn read_one_line_comment(&mut self) -> String {
let val = self.read_to_eol();
- assert!((val.as_bytes()[0] == b'/' && val.as_bytes()[1] == b'/')
- || (val.as_bytes()[0] == b'#' && val.as_bytes()[1] == b'!'));
+ assert!((val.as_bytes()[0] == b'/' && val.as_bytes()[1] == b'/') ||
+ (val.as_bytes()[0] == b'#' && val.as_bytes()[1] == b'!'));
return val;
}
}
fn peeking_at_comment(&self) -> bool {
- (self.curr_is('/') && self.nextch_is('/'))
- || (self.curr_is('/') && self.nextch_is('*'))
- // consider shebangs comments, but not inner attributes
- || (self.curr_is('#') && self.nextch_is('!') && !self.nextnextch_is('['))
+ (self.curr_is('/') && self.nextch_is('/')) || (self.curr_is('/') && self.nextch_is('*')) ||
+ // consider shebangs comments, but not inner attributes
+ (self.curr_is('#') && self.nextch_is('!') && !self.nextnextch_is('['))
}
fn scan_byte(&mut self) -> token::Lit {
let c2 = self.curr.unwrap_or('\x00');
self.bump();
- let valid = self.scan_char_or_byte(start, c2, /* ascii_only = */ true, '\'');
+ let valid = self.scan_char_or_byte(start,
+ c2,
+ // ascii_only =
+ true,
+ '\'');
if !self.curr_is('\'') {
// Byte offsetting here is okay because the
// character before position `start` are an
// ascii single quote and ascii 'b'.
let last_pos = self.last_pos;
- panic!(self.fatal_span_verbose(
- start - BytePos(2), last_pos,
- "unterminated byte constant".to_string()));
+ panic!(self.fatal_span_verbose(start - BytePos(2),
+ last_pos,
+ "unterminated byte constant".to_string()));
}
- let id = if valid { self.name_from(start) } else { token::intern("?") };
+ let id = if valid {
+ self.name_from(start)
+ } else {
+ token::intern("?")
+ };
self.bump(); // advance curr past token
return token::Byte(id);
}
let ch_start = self.last_pos;
let ch = self.curr.unwrap();
self.bump();
- valid &= self.scan_char_or_byte(ch_start, ch, /* ascii_only = */ true, '"');
+ valid &= self.scan_char_or_byte(ch_start,
+ ch,
+ // ascii_only =
+ true,
+ '"');
}
- let id = if valid { self.name_from(start) } else { token::intern("??") };
+ let id = if valid {
+ self.name_from(start)
+ } else {
+ token::intern("??")
+ };
self.bump();
return token::ByteStr(id);
}
} else if !self.curr_is('"') {
let last_pos = self.last_pos;
let ch = self.curr.unwrap();
- panic!(self.fatal_span_char(start_bpos, last_pos,
- "found invalid character; \
- only `#` is allowed in raw string delimitation",
- ch));
+ panic!(self.fatal_span_char(start_bpos,
+ last_pos,
+ "found invalid character; only `#` is allowed in raw \
+ string delimitation",
+ ch));
}
self.bump();
let content_start_bpos = self.last_pos;
None => {
let last_pos = self.last_pos;
panic!(self.fatal_span_(start_bpos, last_pos, "unterminated raw string"))
- },
+ }
Some('"') => {
content_end_bpos = self.last_pos;
for _ in 0..hash_count {
}
}
break;
- },
- Some(c) => if c > '\x7F' {
- let last_pos = self.last_pos;
- self.err_span_char(
- last_pos, last_pos, "raw byte string must be ASCII", c);
+ }
+ Some(c) => {
+ if c > '\x7F' {
+ let last_pos = self.last_pos;
+ self.err_span_char(last_pos, last_pos, "raw byte string must be ASCII", c);
+ }
}
}
self.bump();
}
self.bump();
- return token::ByteStrRaw(self.name_from_to(content_start_bpos,
- content_end_bpos),
- hash_count);
+ return token::ByteStrRaw(self.name_from_to(content_start_bpos, content_end_bpos),
+ hash_count);
}
}
pub fn is_whitespace(c: Option<char>) -> bool {
match c.unwrap_or('\x00') { // None can be null for now... it's not whitespace
' ' | '\n' | '\t' | '\r' => true,
- _ => false
+ _ => false,
}
}
fn in_range(c: Option<char>, lo: char, hi: char) -> bool {
match c {
Some(c) => lo <= c && c <= hi,
- _ => false
+ _ => false,
}
}
-fn is_dec_digit(c: Option<char>) -> bool { return in_range(c, '0', '9'); }
+fn is_dec_digit(c: Option<char>) -> bool {
+ return in_range(c, '0', '9');
+}
pub fn is_doc_comment(s: &str) -> bool {
- let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/')
- || s.starts_with("//!");
+ let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/') ||
+ s.starts_with("//!");
debug!("is {:?} a doc comment? {}", s, res);
res
}
pub fn is_block_doc_comment(s: &str) -> bool {
- let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*')
- || s.starts_with("/*!"))
- && s.len() >= 5; // Prevent `/**/` from being parsed as a doc comment
+ // Prevent `/**/` from being parsed as a doc comment
+ let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*') ||
+ s.starts_with("/*!")) && s.len() >= 5;
debug!("is {:?} a doc comment? {}", s, res);
res
}
fn ident_start(c: Option<char>) -> bool {
- let c = match c { Some(c) => c, None => return false };
+ let c = match c {
+ Some(c) => c,
+ None => return false,
+ };
- (c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z')
- || c == '_'
- || (c > '\x7f' && c.is_xid_start())
+ (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c > '\x7f' && c.is_xid_start())
}
fn ident_continue(c: Option<char>) -> bool {
- let c = match c { Some(c) => c, None => return false };
+ let c = match c {
+ Some(c) => c,
+ None => return false,
+ };
- (c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z')
- || (c >= '0' && c <= '9')
- || c == '_'
- || (c > '\x7f' && c.is_xid_continue())
+ (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' ||
+ (c > '\x7f' && c.is_xid_continue())
}
#[cfg(test)]
use codemap::{BytePos, CodeMap, Span, NO_EXPANSION};
use errors;
use parse::token;
- use parse::token::{str_to_ident};
+ use parse::token::str_to_ident;
use std::io;
use std::rc::Rc;
// open a string reader for the given string
fn setup<'a>(cm: &CodeMap,
span_handler: &'a errors::Handler,
- teststr: String) -> StringReader<'a> {
+ teststr: String)
+ -> StringReader<'a> {
let fm = cm.new_filemap("zebra.rs".to_string(), teststr);
StringReader::new(span_handler, fm)
}
- #[test] fn t1 () {
+ #[test]
+ fn t1() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
- let mut string_reader = setup(&cm, &sh,
- "/* my source file */ \
- fn main() { println!(\"zebra\"); }\n".to_string());
+ let mut string_reader = setup(&cm,
+ &sh,
+ "/* my source file */ fn main() { println!(\"zebra\"); }\n"
+ .to_string());
let id = str_to_ident("fn");
assert_eq!(string_reader.next_token().tok, token::Comment);
assert_eq!(string_reader.next_token().tok, token::Whitespace);
let tok1 = string_reader.next_token();
- let tok2 = TokenAndSpan{
- tok:token::Ident(id, token::Plain),
- sp:Span {lo:BytePos(21),hi:BytePos(23),expn_id: NO_EXPANSION}};
- assert_eq!(tok1,tok2);
+ let tok2 = TokenAndSpan {
+ tok: token::Ident(id, token::Plain),
+ sp: Span {
+ lo: BytePos(21),
+ hi: BytePos(23),
+ expn_id: NO_EXPANSION,
+ },
+ };
+ assert_eq!(tok1, tok2);
assert_eq!(string_reader.next_token().tok, token::Whitespace);
// the 'main' id is already read:
assert_eq!(string_reader.last_pos.clone(), BytePos(28));
// read another token:
let tok3 = string_reader.next_token();
- let tok4 = TokenAndSpan{
- tok:token::Ident(str_to_ident("main"), token::Plain),
- sp:Span {lo:BytePos(24),hi:BytePos(28),expn_id: NO_EXPANSION}};
- assert_eq!(tok3,tok4);
+ let tok4 = TokenAndSpan {
+ tok: token::Ident(str_to_ident("main"), token::Plain),
+ sp: Span {
+ lo: BytePos(24),
+ hi: BytePos(28),
+ expn_id: NO_EXPANSION,
+ },
+ };
+ assert_eq!(tok3, tok4);
// the lparen is already read:
assert_eq!(string_reader.last_pos.clone(), BytePos(29))
}
// check that the given reader produces the desired stream
// of tokens (stop checking after exhausting the expected vec)
- fn check_tokenization (mut string_reader: StringReader, expected: Vec<token::Token> ) {
+ fn check_tokenization(mut string_reader: StringReader, expected: Vec<token::Token>) {
for expected_tok in &expected {
assert_eq!(&string_reader.next_token().tok, expected_tok);
}
token::Ident(str_to_ident(id), style)
}
- #[test] fn doublecolonparsing () {
+ #[test]
+ fn doublecolonparsing() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
check_tokenization(setup(&cm, &sh, "a b".to_string()),
mk_ident("b", token::Plain)]);
}
- #[test] fn dcparsing_2 () {
+ #[test]
+ fn dcparsing_2() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
check_tokenization(setup(&cm, &sh, "a::b".to_string()),
- vec![mk_ident("a",token::ModName),
+ vec![mk_ident("a", token::ModName),
token::ModSep,
mk_ident("b", token::Plain)]);
}
- #[test] fn dcparsing_3 () {
+ #[test]
+ fn dcparsing_3() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
check_tokenization(setup(&cm, &sh, "a ::b".to_string()),
mk_ident("b", token::Plain)]);
}
- #[test] fn dcparsing_4 () {
+ #[test]
+ fn dcparsing_4() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
check_tokenization(setup(&cm, &sh, "a:: b".to_string()),
- vec![mk_ident("a",token::ModName),
+ vec![mk_ident("a", token::ModName),
token::ModSep,
token::Whitespace,
mk_ident("b", token::Plain)]);
}
- #[test] fn character_a() {
+ #[test]
+ fn character_a() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
assert_eq!(setup(&cm, &sh, "'a'".to_string()).next_token().tok,
token::Literal(token::Char(token::intern("a")), None));
}
- #[test] fn character_space() {
+ #[test]
+ fn character_space() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
assert_eq!(setup(&cm, &sh, "' '".to_string()).next_token().tok,
token::Literal(token::Char(token::intern(" ")), None));
}
- #[test] fn character_escaped() {
+ #[test]
+ fn character_escaped() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
assert_eq!(setup(&cm, &sh, "'\\n'".to_string()).next_token().tok,
token::Literal(token::Char(token::intern("\\n")), None));
}
- #[test] fn lifetime_name() {
+ #[test]
+ fn lifetime_name() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
assert_eq!(setup(&cm, &sh, "'abc".to_string()).next_token().tok,
token::Lifetime(token::str_to_ident("'abc")));
}
- #[test] fn raw_string() {
+ #[test]
+ fn raw_string() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
- assert_eq!(setup(&cm, &sh,
- "r###\"\"#a\\b\x00c\"\"###".to_string()).next_token()
- .tok,
+ assert_eq!(setup(&cm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string())
+ .next_token()
+ .tok,
token::Literal(token::StrRaw(token::intern("\"#a\\b\x00c\""), 3), None));
}
- #[test] fn literal_suffixes() {
+ #[test]
+ fn literal_suffixes() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
macro_rules! test {
Some(token::intern("suffix"))));
}
- #[test] fn line_doc_comments() {
+ #[test]
+ fn line_doc_comments() {
assert!(is_doc_comment("///"));
assert!(is_doc_comment("/// blah"));
assert!(!is_doc_comment("////"));
}
- #[test] fn nested_block_comments() {
+ #[test]
+ fn nested_block_comments() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
let mut lexer = setup(&cm, &sh, "/* /* */ */'a'".to_string());
match lexer.next_token().tok {
- token::Comment => { },
- _ => panic!("expected a comment!")
+ token::Comment => {}
+ _ => panic!("expected a comment!"),
}
- assert_eq!(lexer.next_token().tok, token::Literal(token::Char(token::intern("a")), None));
+ assert_eq!(lexer.next_token().tok,
+ token::Literal(token::Char(token::intern("a")), None));
}
- #[test] fn crlf_comments() {
+ #[test]
+ fn crlf_comments() {
let cm = Rc::new(CodeMap::new());
let sh = mk_sh(cm.clone());
let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string());
assert_eq!(comment.tok, token::Comment);
assert_eq!(comment.sp, ::codemap::mk_sp(BytePos(0), BytePos(7)));
assert_eq!(lexer.next_token().tok, token::Whitespace);
- assert_eq!(lexer.next_token().tok, token::DocComment(token::intern("/// test")));
+ assert_eq!(lexer.next_token().tok,
+ token::DocComment(token::intern("/// test")));
}
}
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() {
- self.span.hi
- } else {
- span_hi
+ let span_hi = match self.parse_ty() {
+ Ok(..) => self.span.hi,
+ Err(ref mut err) => {
+ err.cancel();
+ span_hi
+ }
};
let msg = format!("did you mean a single argument type &'a Type, \
#[cfg(any(target_os = "freebsd",
target_os = "dragonfly",
target_os = "bitrig",
- target_os = "openbsd",
target_os = "netbsd"))]
fn num_cpus() -> usize {
let mut cpus: libc::c_uint = 0;
}
cpus as usize
}
+
+ #[cfg(target_os = "openbsd")]
+ fn num_cpus() -> usize {
+ let mut cpus: libc::c_uint = 0;
+ let mut cpus_size = std::mem::size_of_val(&cpus);
+ let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
+
+ unsafe {
+ libc::sysctl(mib.as_mut_ptr(), 2,
+ &mut cpus as *mut _ as *mut _,
+ &mut cpus_size as *mut _ as *mut _,
+ 0 as *mut _, 0);
+ }
+ if cpus < 1 {
+ cpus = 1;
+ }
+ cpus as usize
+ }
}
pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+// Here we expect a coherence conflict because, even though `i32` does
+// not implement `Iterator`, we cannot rely on that negative reasoning
+// due to the orphan rules. Therefore, `A::Item` may yet turn out to
+// be `i32`.
+
+pub trait Foo<P> {}
+
+pub trait Bar {
+ type Output: 'static;
+}
+
+impl Foo<i32> for i32 { } //~ ERROR E0119
+
+impl<A:Iterator> Foo<A::Item> for A { }
+
+fn main() {}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Coherence error results because we do not know whether `T: Foo<P>` or not
+// for the second impl.
+
+use std::marker::PhantomData;
+
+pub trait Foo<P> {}
+
+impl <P, T: Foo<P>> Foo<P> for Option<T> {} //~ ERROR E0119
+
+impl<T, U> Foo<T> for Option<U> { }
+
+fn main() {}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::marker::PhantomData;
+
+pub trait Foo<P> {}
+
+pub trait Bar {
+ type Output: 'static;
+}
+
+impl Foo<i32> for i32 { } //~ ERROR E0119
+
+impl<A:Bar> Foo<A::Output> for A { }
+
+impl Bar for i32 {
+ type Output = i32;
+}
+
+fn main() {}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+// Here we do not get a coherence conflict because `Baz: Iterator`
+// does not hold and (due to the orphan rules), we can rely on that.
+
+pub trait Foo<P> {}
+
+pub trait Bar {
+ type Output: 'static;
+}
+
+struct Baz;
+impl Foo<i32> for Baz { }
+
+impl<A:Iterator> Foo<A::Item> for A { }
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+pub trait Foo<P> {}
+
+pub trait Bar {
+ type Output: 'static;
+}
+
+impl Foo<i32> for i32 { }
+
+impl<A:Bar> Foo<A::Output> for A { }
+
+impl Bar for i32 {
+ type Output = u32;
+}
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
const A_I8_T
: [u32; (i8::MAX as i8 + 1u8) as usize]
//~^ ERROR mismatched types
- //~| the trait `core::ops::Add<u8>` is not implemented for the type `i8`
+ //~| ERROR the trait `core::ops::Add<u8>` is not implemented for the type `i8`
= [0; (i8::MAX as usize) + 1];
fn main() {
// except according to those terms.
#![allow(unused_imports)]
-#![feature(negate_unsigned)]
// Note: the relevant lint pass here runs before some of the constant
// evaluation below (e.g. that performed by trans and llvm), so if you
);
const VALS_U8: (u8, u8, u8, u8) =
- (-u8::MIN,
+ (-(u8::MIN as i8) as u8,
u8::MIN - 1,
//~^ ERROR attempted to sub with overflow
u8::MAX + 1,
);
const VALS_U16: (u16, u16, u16, u16) =
- (-u16::MIN,
+ (-(u16::MIN as i16) as u16,
u16::MIN - 1,
//~^ ERROR attempted to sub with overflow
u16::MAX + 1,
);
const VALS_U32: (u32, u32, u32, u32) =
- (-u32::MIN,
+ (-(u32::MIN as i32) as u32,
u32::MIN - 1,
//~^ ERROR attempted to sub with overflow
u32::MAX + 1,
);
const VALS_U64: (u64, u64, u64, u64) =
- (-u64::MIN,
+ (-(u64::MIN as i64) as u64,
u64::MIN - 1,
//~^ ERROR attempted to sub with overflow
u64::MAX + 1,
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused_imports)]
+
+// Note: the relevant lint pass here runs before some of the constant
+// evaluation below (e.g. that performed by trans and llvm), so if you
+// change this warn to a deny, then the compiler will exit before
+// those errors are detected.
+
+use std::fmt;
+use std::{i8, i16, i32, i64, isize};
+use std::{u8, u16, u32, u64, usize};
+
+const VALS_I8: (i8, i8, i8, i8) =
+ (-i8::MIN,
+ i8::MIN - 1,
+ i8::MAX + 1,
+ i8::MIN * 2,
+ );
+
+const VALS_I16: (i16, i16, i16, i16) =
+ (-i16::MIN,
+ i16::MIN - 1,
+ i16::MAX + 1,
+ i16::MIN * 2,
+ );
+
+const VALS_I32: (i32, i32, i32, i32) =
+ (-i32::MIN,
+ i32::MIN - 1,
+ i32::MAX + 1,
+ i32::MIN * 2,
+ );
+
+const VALS_I64: (i64, i64, i64, i64) =
+ (-i64::MIN,
+ i64::MIN - 1,
+ i64::MAX + 1,
+ i64::MAX * 2,
+ );
+
+const VALS_U8: (u8, u8, u8, u8) =
+ (-u8::MIN,
+ //~^ ERROR unary negation of unsigned integer
+ //~| HELP use a cast or the `!` operator
+ u8::MIN - 1,
+ u8::MAX + 1,
+ u8::MAX * 2,
+ );
+
+const VALS_U16: (u16, u16, u16, u16) =
+ (-u16::MIN,
+ //~^ ERROR unary negation of unsigned integer
+ //~| HELP use a cast or the `!` operator
+ u16::MIN - 1,
+ u16::MAX + 1,
+ u16::MAX * 2,
+ );
+
+const VALS_U32: (u32, u32, u32, u32) =
+ (-u32::MIN,
+ //~^ ERROR unary negation of unsigned integer
+ //~| HELP use a cast or the `!` operator
+ u32::MIN - 1,
+ u32::MAX + 1,
+ u32::MAX * 2,
+ );
+
+const VALS_U64: (u64, u64, u64, u64) =
+ (-u64::MIN,
+ //~^ ERROR unary negation of unsigned integer
+ //~| HELP use a cast or the `!` operator
+ u64::MIN - 1,
+ u64::MAX + 1,
+ u64::MAX * 2,
+ );
+
+fn main() {
+ foo(VALS_I8);
+ foo(VALS_I16);
+ foo(VALS_I32);
+ foo(VALS_I64);
+
+ foo(VALS_U8);
+ foo(VALS_U16);
+ foo(VALS_U32);
+ foo(VALS_U64);
+}
+
+fn foo<T:fmt::Debug>(x: T) {
+ println!("{:?}", x);
+}
// Can't use unit struct as enum pattern
+#![feature(rustc_attrs)]
+// remove prior feature after warning cycle and promoting warnings to errors
#![feature(braced_empty_structs)]
struct Empty1;
Empty2
}
-fn main() {
+// remove attribute after warning cycle and promoting warnings to errors
+#[rustc_error]
+fn main() { //~ ERROR: compilation successful
let e1 = Empty1;
let e2 = E::Empty2;
// Empty1() => () // ERROR `Empty1` does not name a tuple variant or a tuple struct
// }
match e1 {
- Empty1(..) => () //~ ERROR `Empty1` does not name a tuple variant or a tuple struct
+ Empty1(..) => () //~ WARN `Empty1` does not name a tuple variant or a tuple struct
}
// Rejected by parser as yet
// match e2 {
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(negate_unsigned)]
#[repr(u8)] //~ NOTE discriminant type specified here
enum Eu8 {
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// Test that negating unsigned integers is gated by `negate_unsigned` feature
-// gate
+// Test that negating unsigned integers doesn't compile
struct S;
impl std::ops::Neg for S {
}
const _MAX: usize = -1;
-//~^ ERROR unary negation of unsigned integers may be removed in the future
+//~^ ERROR unary negation of unsigned integer
+//~| HELP use a cast or the `!` operator
fn main() {
let a = -1;
- //~^ ERROR unary negation of unsigned integers may be removed in the future
+ //~^ ERROR unary negation of unsigned integer
+ //~| HELP use a cast or the `!` operator
let _b : u8 = a; // for infering variable a to u8.
-a;
- //~^ ERROR unary negation of unsigned integers may be removed in the future
+ //~^ ERROR unary negation of unsigned integer
+ //~| HELP use a cast or the `!` operator
let _d = -1u8;
- //~^ ERROR unary negation of unsigned integers may be removed in the future
+ //~^ ERROR unary negation of unsigned integer
+ //~| HELP use a cast or the `!` operator
for _ in -10..10u8 {}
- //~^ ERROR unary negation of unsigned integers may be removed in the future
+ //~^ ERROR unary negation of unsigned integer
+ //~| HELP use a cast or the `!` operator
-S; // should not trigger the gate; issue 26840
}
fn akemi(homura: Homura) {
let Some(ref madoka) = Some(homura.kaname()); //~ ERROR no method named `kaname` found
- madoka.clone(); //~ ERROR the type of this value must be known in this context
+ madoka.clone(); //~ ERROR the type of this value must be known
}
fn main() { }
let x = &10 as
&Add;
//~^ ERROR the type parameter `RHS` must be explicitly specified in an object type because its default value `Self` references the type `Self`
- //~^^ ERROR the value of the associated type `Output` (from the trait `core::ops::Add`) must be specified
+ //~| ERROR the value of the associated type `Output` (from the trait `core::ops::Add`) must be specified
}
// except according to those terms.
fn main() {
- "".chars().fold(|_, _| (), ()); //~ ERROR is not implemented for the type `()`
+ "".chars().fold(|_, _| (), ());
+ //~^ ERROR E0277
+ //~| ERROR E0277
}
fn main() {
1.0f64 - 1.0;
- 1.0f64 - 1 //~ ERROR: is not implemented
+ 1.0f64 - 1 //~ ERROR E0277
}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+#![feature(rustc_attrs)]
+
+use std::marker::PhantomData;
+
+pub trait Foo<P> {}
+
+impl <P, T: Foo<P>> Foo<P> for Option<T> {}
+
+pub struct Qux<T> (PhantomData<*mut T>);
+
+impl<T> Foo<*mut T> for Option<Qux<T>> {}
+
+pub trait Bar {
+ type Output: 'static;
+}
+
+impl<T: 'static, W: Bar<Output = T>> Foo<*mut T> for W {}
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt;
+
+impl fmt::Display for DecoderError { //~ ERROR E0412
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Missing data: {}", self.0)
+ }
+}
+fn main() {
+}
macro_rules! parallel {
(
- for $id:ident in $iter:expr {
+ // If future has `pred`/`moelarry` fragments (where "pred" is
+ // "like expr, but with `{` in its FOLLOW set"), then could
+ // use `pred` instead of future-proof erroring here. See also:
+ //
+ // https://github.com/rust-lang/rfcs/pull/1384#issuecomment-160165525
+ for $id:ident in $iter:expr { //~ WARN `$iter:expr` is followed by `{`
$( $inner:expr; )*
}
) => {};
fn main() {
let p = Point::new(0.0, 0.0);
//~^ ERROR no associated item named `new` found for type `Point` in the current scope
- println!("{}", p.to_string());
- //~^ ERROR the type of this value must be known in this context
+ println!("{}", p.to_string()); //~ ERROR type of this value must be known
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(negate_unsigned)]
#![allow(dead_code)]
-#![feature(negate_unsigned)]
// compile-flags: -D unused-comparisons
fn main() { }
($bl:block < ) => ();
($pa:pat >) => (); //~ ERROR `$pa:pat` is followed by `>`, which is not allowed for `pat`
($pa:pat , ) => ();
- ($pa:pat | ) => (); //~ ERROR `$pa:pat` is followed by `|`
($pa:pat $pb:pat $ty:ty ,) => ();
//~^ ERROR `$pa:pat` is followed by `$pb:pat`, which is not allowed
//~^^ ERROR `$pb:pat` is followed by `$ty:ty`, which is not allowed
($($ty:ty)* -) => (); //~ ERROR `$ty:ty` is followed by `-`
($($a:ty, $b:ty)* -) => (); //~ ERROR `$b:ty` is followed by `-`
($($ty:ty)-+) => (); //~ ERROR `$ty:ty` is followed by `-`, which is not allowed for `ty`
+ ( $($a:expr)* $($b:tt)* ) => { };
+ //~^ ERROR `$a:expr` is followed by `$b:tt`, which is not allowed for `expr` fragments
}
fn main() { }
+++ /dev/null
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Check that we cannot have two sequence repetitions in a row.
-
-macro_rules! foo {
- ( $($a:expr)* $($b:tt)* ) => { }; //~ ERROR sequence repetition followed by another sequence
- ( $($a:tt)* $($b:tt)* ) => { }; //~ ERROR sequence repetition followed by another sequence
-}
-
-fn main() { }
color::cmyk(_, _, _, _) => { }
color::no_color(_) => { }
//~^ ERROR this pattern has 1 field, but the corresponding variant has no fields
- //~^^ WARN `color::no_color` does not name a tuple variant or a tuple struct
}
}
}
match A::B(1, 2) {
A::B(_, _, _) => (), //~ ERROR this pattern has 3 fields, but
A::D(_) => (), //~ ERROR this pattern has 1 field, but
- //~^ WARN `A::D` does not name a tuple variant or a tuple struct
_ => ()
}
match 'c' {
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+struct Foo<'a, 'b> {
+ a: &'a &'b i32
+}
+
+fn foo<'a, 'b>(x: &mut Foo<'a; 'b>) {}
+//~^ ERROR expected `,` or `>` after lifetime name, found `;`
+//~^^ NOTE did you mean a single argument type &'a Type, or did you mean the comma-separated
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(negate_unsigned)]
-
#[cfg(any(target_arch = "x86", target_arch = "arm"))]
fn target() {
- assert_eq!(-1000 as usize >> 3_usize, 536870787_usize);
+ assert_eq!(-1000isize as usize >> 3_usize, 536870787_usize);
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
fn target() {
- assert_eq!(-1000 as usize >> 3_usize, 2305843009213693827_usize);
+ assert_eq!(-1000isize as usize >> 3_usize, 2305843009213693827_usize);
}
fn general() {
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-
-#![feature(negate_unsigned)]
#![feature(intrinsics)]
mod rusti {
assert_eq!(ctpop(100u32), 3); assert_eq!(ctpop(100i32), 3);
assert_eq!(ctpop(100u64), 3); assert_eq!(ctpop(100i64), 3);
- assert_eq!(ctpop(-1u8), 8); assert_eq!(ctpop(-1i8), 8);
- assert_eq!(ctpop(-1u16), 16); assert_eq!(ctpop(-1i16), 16);
- assert_eq!(ctpop(-1u32), 32); assert_eq!(ctpop(-1i32), 32);
- assert_eq!(ctpop(-1u64), 64); assert_eq!(ctpop(-1i64), 64);
+ assert_eq!(ctpop(-1i8 as u8), 8); assert_eq!(ctpop(-1i8), 8);
+ assert_eq!(ctpop(-1i16 as u16), 16); assert_eq!(ctpop(-1i16), 16);
+ assert_eq!(ctpop(-1i32 as u32), 32); assert_eq!(ctpop(-1i32), 32);
+ assert_eq!(ctpop(-1i64 as u64), 64); assert_eq!(ctpop(-1i64), 64);
assert_eq!(ctlz(0u8), 8); assert_eq!(ctlz(0i8), 8);
assert_eq!(ctlz(0u16), 16); assert_eq!(ctlz(0i16), 16);
assert_eq!(ctlz(100u32), 25); assert_eq!(ctlz(100i32), 25);
assert_eq!(ctlz(100u64), 57); assert_eq!(ctlz(100i64), 57);
- assert_eq!(cttz(-1u8), 0); assert_eq!(cttz(-1i8), 0);
- assert_eq!(cttz(-1u16), 0); assert_eq!(cttz(-1i16), 0);
- assert_eq!(cttz(-1u32), 0); assert_eq!(cttz(-1i32), 0);
- assert_eq!(cttz(-1u64), 0); assert_eq!(cttz(-1i64), 0);
+ assert_eq!(cttz(-1i8 as u8), 0); assert_eq!(cttz(-1i8), 0);
+ assert_eq!(cttz(-1i16 as u16), 0); assert_eq!(cttz(-1i16), 0);
+ assert_eq!(cttz(-1i32 as u32), 0); assert_eq!(cttz(-1i32), 0);
+ assert_eq!(cttz(-1i64 as u64), 0); assert_eq!(cttz(-1i64), 0);
assert_eq!(cttz(0u8), 8); assert_eq!(cttz(0i8), 8);
assert_eq!(cttz(0u16), 16); assert_eq!(cttz(0i16), 16);
--- /dev/null
+// Copyright 2012-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for Issue #29092.
+//
+// (Possibly redundant with regression test run-pass/issue-30530.rs)
+
+use self::Term::*;
+
+#[derive(Clone)]
+pub enum Term {
+ Dummy,
+ A(Box<Term>),
+ B(Box<Term>),
+}
+
+// a small-step evaluator
+pub fn small_eval(v: Term) -> Term {
+ match v {
+ A(t) => *t.clone(),
+ B(t) => *t.clone(),
+ _ => Dummy,
+ }
+}
+
+fn main() {
+ small_eval(Dummy);
+}
--- /dev/null
+// Copyright 2012-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// More thorough regression test for Issues #30018 and #30822. This
+// attempts to explore different ways that array element construction
+// (for both scratch arrays and non-scratch ones) interacts with
+// breaks in the control-flow, in terms of the order of evaluation of
+// the destructors (which may change; see RFC Issue 744) and the
+// number of times that the destructor evaluates for each value (which
+// should never exceed 1; this latter case is what #30822 is about).
+
+use std::cell::RefCell;
+
+struct D<'a>(&'a RefCell<Vec<i32>>, i32);
+
+impl<'a> Drop for D<'a> {
+ fn drop(&mut self) {
+ println!("Dropping D({})", self.1);
+ (self.0).borrow_mut().push(self.1);
+ }
+}
+
+fn main() {
+ println!("Start");
+ break_during_elem();
+ break_after_whole();
+ println!("Finis");
+}
+
+fn break_during_elem() {
+ let log = &RefCell::new(Vec::new());
+
+ // CASE 1: Fixed-size array itself is stored in _r slot.
+ loop {
+ let _r = [D(log, 10),
+ D(log, 11),
+ { D(log, 12); break; },
+ D(log, 13)];
+ }
+ assert_eq!(&log.borrow()[..], &[12, 11, 10]);
+ log.borrow_mut().clear();
+
+ // CASE 2: Slice (borrow of array) is stored in _r slot.
+ // This is the case that is actually being reported in #30018.
+ loop {
+ let _r = &[D(log, 20),
+ D(log, 21),
+ { D(log, 22); break; },
+ D(log, 23)];
+ }
+ assert_eq!(&log.borrow()[..], &[22, 21, 20]);
+ log.borrow_mut().clear();
+
+ // CASE 3: (Borrow of) slice-index of array is stored in _r slot.
+ loop {
+ let _r = &[D(log, 30),
+ D(log, 31),
+ { D(log, 32); break; },
+ D(log, 33)][..];
+ }
+ assert_eq!(&log.borrow()[..], &[32, 31, 30]);
+ log.borrow_mut().clear();
+}
+
+// The purpose of these functions is to test what happens when we
+// panic after an array has been constructed in its entirety.
+//
+// It is meant to act as proof that we still need to continue
+// scheduling the destruction of an array even after we've scheduling
+// drop for its elements during construction; the latter is tested by
+// `fn break_during_elem()`.
+fn break_after_whole() {
+ let log = &RefCell::new(Vec::new());
+
+ // CASE 1: Fixed-size array itself is stored in _r slot.
+ loop {
+ let _r = [D(log, 10),
+ D(log, 11),
+ D(log, 12)];
+ break;
+ }
+ assert_eq!(&log.borrow()[..], &[10, 11, 12]);
+ log.borrow_mut().clear();
+
+ // CASE 2: Slice (borrow of array) is stored in _r slot.
+ loop {
+ let _r = &[D(log, 20),
+ D(log, 21),
+ D(log, 22)];
+ break;
+ }
+ assert_eq!(&log.borrow()[..], &[20, 21, 22]);
+ log.borrow_mut().clear();
+
+ // CASE 3: (Borrow of) slice-index of array is stored in _r slot.
+ loop {
+ let _r = &[D(log, 30),
+ D(log, 31),
+ D(log, 32)][..];
+ break;
+ }
+ assert_eq!(&log.borrow()[..], &[30, 31, 32]);
+ log.borrow_mut().clear();
+}
--- /dev/null
+// Copyright 2012-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for Issue #30018. This is very similar to the
+// original reported test, except that the panic is wrapped in a
+// spawned thread to isolate the expected error result from the
+// SIGTRAP injected by the drop-flag consistency checking.
+
+struct Foo;
+
+impl Drop for Foo {
+ fn drop(&mut self) {}
+}
+
+fn foo() -> Foo {
+ panic!();
+}
+
+fn main() {
+ use std::thread;
+ let handle = thread::spawn(|| {
+ let _ = &[foo()];
+ });
+ let _ = handle.join();
+}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Previously libstd would set stdio descriptors of a child process
+// by `dup`ing the requested descriptors to inherit directly into the
+// stdio descriptors. This, however, would incorrectly handle cases
+// where the descriptors to inherit were already stdio descriptors.
+// This test checks to avoid that regression.
+
+#![cfg_attr(unix, feature(libc))]
+#![cfg_attr(windows, allow(unused_imports))]
+
+#[cfg(unix)]
+extern crate libc;
+
+use std::fs::File;
+use std::io::{Read, Write};
+use std::io::{stdout, stderr};
+use std::process::{Command, Stdio};
+
+#[cfg(unix)]
+use std::os::unix::io::FromRawFd;
+
+#[cfg(not(unix))]
+fn main() {
+ // Bug not present in Windows
+}
+
+#[cfg(unix)]
+fn main() {
+ let mut args = std::env::args();
+ let name = args.next().unwrap();
+ let args: Vec<String> = args.collect();
+ if let Some("--child") = args.get(0).map(|s| &**s) {
+ return child();
+ } else if !args.is_empty() {
+ panic!("unknown options");
+ }
+
+ let stdout_backup = unsafe { libc::dup(libc::STDOUT_FILENO) };
+ let stderr_backup = unsafe { libc::dup(libc::STDERR_FILENO) };
+ assert!(stdout_backup > -1);
+ assert!(stderr_backup > -1);
+
+ let (stdout_reader, stdout_writer) = pipe();
+ let (stderr_reader, stderr_writer) = pipe();
+ assert!(unsafe { libc::dup2(stdout_writer, libc::STDOUT_FILENO) } > -1);
+ assert!(unsafe { libc::dup2(stderr_writer, libc::STDERR_FILENO) } > -1);
+
+ // Make sure we close any duplicates of the writer end of the pipe,
+ // otherwise we can get stuck reading from the pipe which has open
+ // writers but no one supplying any input
+ assert_eq!(unsafe { libc::close(stdout_writer) }, 0);
+ assert_eq!(unsafe { libc::close(stderr_writer) }, 0);
+
+ stdout().write_all("parent stdout\n".as_bytes()).expect("failed to write to stdout");
+ stderr().write_all("parent stderr\n".as_bytes()).expect("failed to write to stderr");
+
+ let child = {
+ Command::new(name)
+ .arg("--child")
+ .stdin(Stdio::inherit())
+ .stdout(unsafe { FromRawFd::from_raw_fd(libc::STDERR_FILENO) })
+ .stderr(unsafe { FromRawFd::from_raw_fd(libc::STDOUT_FILENO) })
+ .spawn()
+ };
+
+ // The Stdio passed into the Command took over (and closed) std{out, err}
+ // so we should restore them as they were.
+ assert!(unsafe { libc::dup2(stdout_backup, libc::STDOUT_FILENO) } > -1);
+ assert!(unsafe { libc::dup2(stderr_backup, libc::STDERR_FILENO) } > -1);
+
+ // Using File as a shim around the descriptor
+ let mut read = String::new();
+ let mut f: File = unsafe { FromRawFd::from_raw_fd(stdout_reader) };
+ f.read_to_string(&mut read).expect("failed to read from stdout file");
+ assert_eq!(read, "parent stdout\nchild stderr\n");
+
+ // Using File as a shim around the descriptor
+ read.clear();
+ let mut f: File = unsafe { FromRawFd::from_raw_fd(stderr_reader) };
+ f.read_to_string(&mut read).expect("failed to read from stderr file");
+ assert_eq!(read, "parent stderr\nchild stdout\n");
+
+ assert!(child.expect("failed to execute child process").wait().unwrap().success());
+}
+
+#[cfg(unix)]
+fn child() {
+ stdout().write_all("child stdout\n".as_bytes()).expect("child failed to write to stdout");
+ stderr().write_all("child stderr\n".as_bytes()).expect("child failed to write to stderr");
+}
+
+#[cfg(unix)]
+/// Returns a pipe (reader, writer combo)
+fn pipe() -> (i32, i32) {
+ let mut fds = [0; 2];
+ assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr()) }, 0);
+ (fds[0], fds[1])
+}
--- /dev/null
+// Copyright 2012-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for Issue #30530: alloca's created for storing
+// intermediate scratch values during brace-less match arms need to be
+// initialized with their drop-flag set to "dropped" (or else we end
+// up running the destructors on garbage data at the end of the
+// function).
+
+pub enum Handler {
+ Default,
+ #[allow(dead_code)]
+ Custom(*mut Box<Fn()>),
+}
+
+fn main() {
+ take(Handler::Default, Box::new(main));
+}
+
+#[inline(never)]
+pub fn take(h: Handler, f: Box<Fn()>) -> Box<Fn()> {
+ unsafe {
+ match h {
+ Handler::Custom(ptr) => *Box::from_raw(ptr),
+ Handler::Default => f,
+ }
+ }
+}
}}
}
+macro_rules! pat_bar {
+ ($p:pat | $p2:pat) => {{
+ match Some(1u8) {
+ $p | $p2 => {},
+ _ => {}
+ }
+ }}
+}
+
fn main() {
pat_in!(Some(_) in 0..10);
pat_if!(Some(x) if x > 0);
+ pat_bar!(Some(1u8) | None);
}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test of allowing two sequences repetitions in a row,
+// functionality added as byproduct of RFC amendment #1384
+// https://github.com/rust-lang/rfcs/pull/1384
+
+// Old version of Rust would reject this macro definition, even though
+// there are no local ambiguities (the initial `banana` and `orange`
+// tokens are enough for the expander to distinguish which case is
+// intended).
+macro_rules! foo {
+ ( $(banana $a:ident)* $(orange $b:tt)* ) => { };
+}
+
+fn main() {
+ foo!( banana id1 banana id2
+ orange hi orange (hello world) );
+}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+#![feature(rustc_attrs)]
+
+#[derive(PartialEq, Debug)]
+struct Point {
+ _x: i32,
+ _y: i32,
+}
+const STRUCT: Point = Point { _x: 42, _y: 42 };
+const TUPLE1: (i32, i32) = (42, 42);
+const TUPLE2: (&'static str, &'static str) = ("hello","world");
+
+#[rustc_mir]
+fn mir() -> (Point, (i32, i32), (&'static str, &'static str)){
+ let struct1 = STRUCT;
+ let tuple1 = TUPLE1;
+ let tuple2 = TUPLE2;
+ (struct1, tuple1, tuple2)
+}
+
+fn main(){
+ assert_eq!(mir(), (STRUCT, TUPLE1, TUPLE2));
+}
+
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Ensure that both `Box<Error + Send + Sync>` and `Box<Error>` can be obtained from `String`.
+
+use std::error::Error;
+
+fn main() {
+ let _err1: Box<Error + Send + Sync> = From::from("test".to_string());
+ let _err2: Box<Error> = From::from("test".to_string());
+ let _err3: Box<Error + Send + Sync + 'static> = From::from("test");
+ let _err4: Box<Error> = From::from("test");
+}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(negate_unsigned)]
-
pub fn main() {
let a = 1;
let a_neg: i8 = -a;
let e = 1;
let e_neg: isize = -e;
println!("{}", e_neg);
-
- // intentional overflows
-
- let f = 1;
- let f_neg: u8 = -f;
- println!("{}", f_neg);
-
- let g = 1;
- let g_neg: u16 = -g;
- println!("{}", g_neg);
-
- let h = 1;
- let h_neg: u32 = -h;
- println!("{}", h_neg);
-
- let i = 1;
- let i_neg: u64 = -i;
- println!("{}", i_neg);
-
- let j = 1;
- let j_neg: usize = -j;
- println!("{}", j_neg);
}