From 6f693e94868b12fcffa9d5b53b2ca02778ff8daa Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 18 Mar 2015 23:36:19 -0700 Subject: [PATCH 01/13] Stabilize Entry types This commit marks as `#[stable]` the `Entry` types for the maps provided by `std`. The main reason these had been left unstable previously was uncertainty about an eventual trait design, but several plausible designs have been proposed that all work fine with the current type definitions. --- src/libcollections/btree/map.rs | 16 ++++++++-------- src/libcollections/vec_map.rs | 16 +++++++++------- src/libstd/collections/hash/map.rs | 16 ++++++++-------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index c7e1e3c91766e..75c0ec486cfa9 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -124,26 +124,26 @@ pub struct RangeMut<'a, K: 'a, V: 'a> { } /// A view into a single entry in a map, which may either be vacant or occupied. -#[unstable(feature = "collections", - reason = "precise API still under development")] +#[stable(feature = "rust1", since = "1.0.0")] pub enum Entry<'a, K:'a, V:'a> { /// A vacant Entry + #[stable(feature = "rust1", since = "1.0.0")] Vacant(VacantEntry<'a, K, V>), + /// An occupied Entry + #[stable(feature = "rust1", since = "1.0.0")] Occupied(OccupiedEntry<'a, K, V>), } /// A vacant Entry. -#[unstable(feature = "collections", - reason = "precise API still under development")] +#[stable(feature = "rust1", since = "1.0.0")] pub struct VacantEntry<'a, K:'a, V:'a> { key: K, stack: stack::SearchStack<'a, K, V, node::handle::Edge, node::handle::Leaf>, } /// An occupied Entry. -#[unstable(feature = "collections", - reason = "precise API still under development")] +#[stable(feature = "rust1", since = "1.0.0")] pub struct OccupiedEntry<'a, K:'a, V:'a> { stack: stack::SearchStack<'a, K, V, node::handle::KV, node::handle::LeafOrInternal>, } @@ -1124,9 +1124,9 @@ impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> { } impl<'a, K: Ord, V> Entry<'a, K, V> { - #[unstable(feature = "collections", - reason = "matches collection reform v2 specification, waiting for dust to settle")] /// Returns a mutable reference to the entry if occupied, or the VacantEntry if vacant + #[unstable(feature = "std_misc", + reason = "will soon be replaced by or_insert")] pub fn get(self) -> Result<&'a mut V, VacantEntry<'a, K, V>> { match self { Occupied(entry) => Ok(entry.into_mut()), diff --git a/src/libcollections/vec_map.rs b/src/libcollections/vec_map.rs index 6e67d8763273d..056be4acaeb80 100644 --- a/src/libcollections/vec_map.rs +++ b/src/libcollections/vec_map.rs @@ -67,26 +67,28 @@ pub struct VecMap { } /// A view into a single entry in a map, which may either be vacant or occupied. -#[unstable(feature = "collections", - reason = "precise API still under development")] + +#[stable(feature = "rust1", since = "1.0.0")] pub enum Entry<'a, V:'a> { /// A vacant Entry + #[stable(feature = "rust1", since = "1.0.0")] Vacant(VacantEntry<'a, V>), + /// An occupied Entry + #[stable(feature = "rust1", since = "1.0.0")] Occupied(OccupiedEntry<'a, V>), } /// A vacant Entry. -#[unstable(feature = "collections", - reason = "precise API still under development")] + +#[stable(feature = "rust1", since = "1.0.0")] pub struct VacantEntry<'a, V:'a> { map: &'a mut VecMap, index: usize, } /// An occupied Entry. -#[unstable(feature = "collections", - reason = "precise API still under development")] +#[stable(feature = "rust1", since = "1.0.0")] pub struct OccupiedEntry<'a, V:'a> { map: &'a mut VecMap, index: usize, @@ -651,7 +653,7 @@ impl VecMap { impl<'a, V> Entry<'a, V> { #[unstable(feature = "collections", - reason = "matches collection reform v2 specification, waiting for dust to settle")] + reason = "will soon be replaced by or_insert")] /// Returns a mutable reference to the entry if occupied, or the VacantEntry if vacant pub fn get(self) -> Result<&'a mut V, VacantEntry<'a, V>> { match self { diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 60b1738d2c989..43e60d4dc4f1c 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1335,15 +1335,13 @@ pub struct Drain<'a, K: 'a, V: 'a> { } /// A view into a single occupied location in a HashMap. -#[unstable(feature = "std_misc", - reason = "precise API still being fleshed out")] +#[stable(feature = "rust1", since = "1.0.0")] pub struct OccupiedEntry<'a, K: 'a, V: 'a> { elem: FullBucket>, } /// A view into a single empty location in a HashMap. -#[unstable(feature = "std_misc", - reason = "precise API still being fleshed out")] +#[stable(feature = "rust1", since = "1.0.0")] pub struct VacantEntry<'a, K: 'a, V: 'a> { hash: SafeHash, key: K, @@ -1351,12 +1349,14 @@ pub struct VacantEntry<'a, K: 'a, V: 'a> { } /// A view into a single location in a map, which may be vacant or occupied. -#[unstable(feature = "std_misc", - reason = "precise API still being fleshed out")] +#[stable(feature = "rust1", since = "1.0.0")] pub enum Entry<'a, K: 'a, V: 'a> { /// An occupied Entry. + #[stable(feature = "rust1", since = "1.0.0")] Occupied(OccupiedEntry<'a, K, V>), + /// A vacant Entry. + #[stable(feature = "rust1", since = "1.0.0")] Vacant(VacantEntry<'a, K, V>), } @@ -1477,10 +1477,10 @@ impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } -#[unstable(feature = "std_misc", - reason = "matches collection reform v2 specification, waiting for dust to settle")] impl<'a, K, V> Entry<'a, K, V> { /// Returns a mutable reference to the entry if occupied, or the VacantEntry if vacant. + #[unstable(feature = "std_misc", + reason = "will soon be replaced by or_insert")] pub fn get(self) -> Result<&'a mut V, VacantEntry<'a, K, V>> { match self { Occupied(entry) => Ok(entry.into_mut()), From 0c040b07f00f8cb1e59c4811c59e2adf81e6e167 Mon Sep 17 00:00:00 2001 From: Ches Martin Date: Sun, 1 Mar 2015 23:45:53 -0500 Subject: [PATCH 02/13] guide: minor copy edits --- src/doc/trpl/compound-data-types.md | 3 ++- src/doc/trpl/concurrency.md | 11 +++++------ src/doc/trpl/crates-and-modules.md | 2 +- src/doc/trpl/standard-input.md | 8 ++++++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/doc/trpl/compound-data-types.md b/src/doc/trpl/compound-data-types.md index e09922fd390a9..868874e3589d9 100644 --- a/src/doc/trpl/compound-data-types.md +++ b/src/doc/trpl/compound-data-types.md @@ -47,7 +47,7 @@ This pattern is very powerful, and we'll see it repeated more later. There are also a few things you can do with a tuple as a whole, without destructuring. You can assign one tuple into another, if they have the same -contained types and arity. Tuples have the same arity when they have the same +contained types and [arity]. Tuples have the same arity when they have the same length. ```rust @@ -357,6 +357,7 @@ tool that will let us deconstruct this sum type (the type theory term for enums) in a very elegant way and avoid all these messy `if`/`else`s. +[arity]: ./glossary.html#arity [match]: ./match.html [game]: ./guessing-game.html#comparing-guesses [generics]: ./generics.html diff --git a/src/doc/trpl/concurrency.md b/src/doc/trpl/concurrency.md index 4a16db63950dd..5d301cc0aef6e 100644 --- a/src/doc/trpl/concurrency.md +++ b/src/doc/trpl/concurrency.md @@ -40,14 +40,14 @@ us enforce that it can't leave the current thread. ### `Sync` -The second of these two trait is called [`Sync`](../std/marker/trait.Sync.html). +The second of these traits is called [`Sync`](../std/marker/trait.Sync.html). When a type `T` implements `Sync`, it indicates to the compiler that something of this type has no possibility of introducing memory unsafety when used from multiple threads concurrently. For example, sharing immutable data with an atomic reference count is threadsafe. Rust provides a type like this, `Arc`, and it implements `Sync`, -so that it could be safely shared between threads. +so it is safe to share between threads. These two traits allow you to use the type system to make strong guarantees about the properties of your code under concurrency. Before we demonstrate @@ -69,7 +69,7 @@ fn main() { } ``` -The `Thread::scoped()` method accepts a closure, which is executed in a new +The `thread::scoped()` method accepts a closure, which is executed in a new thread. It's called `scoped` because this thread returns a join guard: ``` @@ -208,10 +208,10 @@ Here's the error: ```text :11:9: 11:22 error: the trait `core::marker::Send` is not implemented for the type `std::sync::mutex::MutexGuard<'_, collections::vec::Vec>` [E0277] -:11 Thread::spawn(move || { +:11 thread::spawn(move || { ^~~~~~~~~~~~~ :11:9: 11:22 note: `std::sync::mutex::MutexGuard<'_, collections::vec::Vec>` cannot be sent between threads safely -:11 Thread::spawn(move || { +:11 thread::spawn(move || { ^~~~~~~~~~~~~ ``` @@ -322,7 +322,6 @@ While this channel is just sending a generic signal, we can send any data that is `Send` over the channel! ``` -use std::sync::{Arc, Mutex}; use std::thread; use std::sync::mpsc; diff --git a/src/doc/trpl/crates-and-modules.md b/src/doc/trpl/crates-and-modules.md index 65ff42ffdcef4..37785c030e6e5 100644 --- a/src/doc/trpl/crates-and-modules.md +++ b/src/doc/trpl/crates-and-modules.md @@ -430,7 +430,7 @@ fn main() { } ``` -But it is not idiomatic. This is significantly more likely to introducing a +But it is not idiomatic. This is significantly more likely to introduce a naming conflict. In our short program, it's not a big deal, but as it grows, it becomes a problem. If we have conflicting names, Rust will give a compilation error. For example, if we made the `japanese` functions public, and tried to do diff --git a/src/doc/trpl/standard-input.md b/src/doc/trpl/standard-input.md index 794b1df7563de..228891f9f052b 100644 --- a/src/doc/trpl/standard-input.md +++ b/src/doc/trpl/standard-input.md @@ -115,8 +115,9 @@ doesn't work, so we're okay with that. In most cases, we would want to handle the error case explicitly. `expect()` allows us to give an error message if this crash happens. -We will cover the exact details of how all of this works later in the Guide. -For now, this gives you enough of a basic understanding to work with. +We will cover the exact details of how all of this works later in the Guide in +[Error Handling]. For now, this gives you enough of a basic understanding to +work with. Back to the code we were working on! Here's a refresher: @@ -157,3 +158,6 @@ here. That's all you need to get basic input from the standard input! It's not too complicated, but there are a number of small parts. + + +[Error Handling]: ./error-handling.html From 92294e7aedf26c1b276b4a721e18b7a43217848d Mon Sep 17 00:00:00 2001 From: Ches Martin Date: Sun, 1 Mar 2015 23:54:37 -0500 Subject: [PATCH 03/13] guide: Improvements to language covering enums --- src/doc/trpl/compound-data-types.md | 68 +++++++++++++++-------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/doc/trpl/compound-data-types.md b/src/doc/trpl/compound-data-types.md index 868874e3589d9..d531a22d0e0dd 100644 --- a/src/doc/trpl/compound-data-types.md +++ b/src/doc/trpl/compound-data-types.md @@ -196,8 +196,9 @@ Now, we have actual names, rather than positions. Good names are important, and with a struct, we have actual names. There _is_ one case when a tuple struct is very useful, though, and that's a -tuple struct with only one element. We call this a *newtype*, because it lets -you create a new type that's similar to another one: +tuple struct with only one element. We call this the *newtype* pattern, because +it allows you to create a new type, distinct from that of its contained value +and expressing its own semantic meaning: ```{rust} struct Inches(i32); @@ -216,7 +217,7 @@ destructuring `let`, as we discussed previously in 'tuples.' In this case, the Finally, Rust has a "sum type", an *enum*. Enums are an incredibly useful feature of Rust, and are used throughout the standard library. An `enum` is -a type which ties a set of alternates to a specific name. For example, below +a type which relates a set of alternates to a specific name. For example, below we define `Character` to be either a `Digit` or something else. These can be used via their fully scoped names: `Character::Other` (more about `::` below). @@ -228,8 +229,8 @@ enum Character { } ``` -An `enum` variant can be defined as most normal types. Below are some example -types which also would be allowed in an `enum`. +Most normal types are allowed as the variant components of an `enum`. Here are +some examples: ```rust struct Empty; @@ -239,15 +240,15 @@ struct Status { Health: i32, Mana: i32, Attack: i32, Defense: i32 } struct HeightDatabase(Vec); ``` -So you see that depending on the sub-datastructure, the `enum` variant, same as -a struct, may or may not hold data. That is, in `Character`, `Digit` is a name -tied to an `i32` where `Other` is just a name. However, the fact that they are -distinct makes this very useful. +You see that, depending on its type, an `enum` variant may or may not hold data. +In `Character`, for instance, `Digit` gives a meaningful name for an `i32` +value, where `Other` is only a name. However, the fact that they represent +distinct categories of `Character` is a very useful property. -As with structures, enums don't by default have access to operators such as -compare ( `==` and `!=`), binary operations (`*` and `+`), and order -(`<` and `>=`). As such, using the previous `Character` type, the -following code is invalid: +As with structures, the variants of an enum by default are not comparable with +equality operators (`==`, `!=`), have no ordering (`<`, `>=`, etc.), and do not +support other binary operations such as `*` and `+`. As such, the following code +is invalid for the example `Character` type: ```{rust,ignore} // These assignments both succeed @@ -265,9 +266,10 @@ let four_equals_ten = four == ten; ``` This may seem rather limiting, but it's a limitation which we can overcome. -There are two ways: by implementing equality ourselves, or by using the -[`match`][match] keyword. We don't know enough about Rust to implement equality -yet, but we can use the `Ordering` enum from the standard library, which does: +There are two ways: by implementing equality ourselves, or by pattern matching +variants with [`match`][match] expressions, which you'll learn in the next +chapter. We don't know enough about Rust to implement equality yet, but we can +use the `Ordering` enum from the standard library, which does: ``` enum Ordering { @@ -277,9 +279,8 @@ enum Ordering { } ``` -Because we did not define `Ordering`, we must import it (from the std -library) with the `use` keyword. Here's an example of how `Ordering` is -used: +Because `Ordering` has already been defined for us, we will import it with the +`use` keyword. Here's an example of how it is used: ```{rust} use std::cmp::Ordering; @@ -313,17 +314,17 @@ the standard library if you need them. Okay, let's talk about the actual code in the example. `cmp` is a function that compares two things, and returns an `Ordering`. We return either -`Ordering::Less`, `Ordering::Greater`, or `Ordering::Equal`, depending on if -the two values are less, greater, or equal. Note that each variant of the -`enum` is namespaced under the `enum` itself: it's `Ordering::Greater` not -`Greater`. +`Ordering::Less`, `Ordering::Greater`, or `Ordering::Equal`, depending on +whether the first value is less than, greater than, or equal to the second. Note +that each variant of the `enum` is namespaced under the `enum` itself: it's +`Ordering::Greater`, not `Greater`. The `ordering` variable has the type `Ordering`, and so contains one of the three values. We then do a bunch of `if`/`else` comparisons to check which one it is. -This `Ordering::Greater` notation is too long. Let's use `use` to import the -`enum` variants instead. This will avoid full scoping: +This `Ordering::Greater` notation is too long. Let's use another form of `use` +to import the `enum` variants instead. This will avoid full scoping: ```{rust} use std::cmp::Ordering::{self, Equal, Less, Greater}; @@ -347,14 +348,15 @@ fn main() { ``` Importing variants is convenient and compact, but can also cause name conflicts, -so do this with caution. It's considered good style to rarely import variants -for this reason. - -As you can see, `enum`s are quite a powerful tool for data representation, and are -even more useful when they're [generic][generics] across types. Before we -get to generics, though, let's talk about how to use them with pattern matching, a -tool that will let us deconstruct this sum type (the type theory term for enums) -in a very elegant way and avoid all these messy `if`/`else`s. +so do this with caution. For this reason, it's normally considered better style +to `use` an enum rather than its variants directly. + +As you can see, `enum`s are quite a powerful tool for data representation, and +are even more useful when they're [generic][generics] across types. Before we +get to generics, though, let's talk about how to use enums with pattern +matching, a tool that will let us deconstruct sum types (the type theory term +for enums) like `Ordering` in a very elegant way that avoids all these messy +and brittle `if`/`else`s. [arity]: ./glossary.html#arity From c175b35495487da6a62bb5d4d9ed7df64186b106 Mon Sep 17 00:00:00 2001 From: FuGangqiang Date: Sat, 21 Mar 2015 22:56:05 +0800 Subject: [PATCH 04/13] fix the attributes sytax --- src/doc/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 92573d7921773..b23a8d91069ee 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -1982,7 +1982,7 @@ the namespace hierarchy as it normally would. ## Attributes ```{.ebnf .gram} -attribute : "#!" ? '[' meta_item ']' ; +attribute : '#' '!' ? '[' meta_item ']' ; meta_item : ident [ '=' literal | '(' meta_seq ')' ] ? ; meta_seq : meta_item [ ',' meta_seq ] ? ; From bc9d9f20db488dae2b6a80e4b30d1cf3d69058c1 Mon Sep 17 00:00:00 2001 From: FuGangqiang Date: Sat, 21 Mar 2015 23:59:30 +0800 Subject: [PATCH 05/13] add lifetime for `while` and `for` expression --- src/doc/reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index b23a8d91069ee..415ec4e4fbf0a 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -3158,7 +3158,7 @@ ten_times(|j| println!("hello, {}", j)); ### While loops ```{.ebnf .gram} -while_expr : "while" no_struct_literal_expr '{' block '}' ; +while_expr : [ lifetime ':' ] "while" no_struct_literal_expr '{' block '}' ; ``` A `while` loop begins by evaluating the boolean loop conditional expression. @@ -3223,7 +3223,7 @@ A `continue` expression is only permitted in the body of a loop. ### For expressions ```{.ebnf .gram} -for_expr : "for" pat "in" no_struct_literal_expr '{' block '}' ; +for_expr : [ lifetime ':' ] "for" pat "in" no_struct_literal_expr '{' block '}' ; ``` A `for` expression is a syntactic construct for looping over elements provided From 90c8592889187681b9766b7507c090e180f61a78 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 22 Mar 2015 17:16:26 +0200 Subject: [PATCH 06/13] Refine Cursor docstring --- src/libstd/io/cursor.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 87e5a2a448855..1ca424f7c6104 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -17,17 +17,14 @@ use iter::repeat; use num::Int; use slice; -/// A `Cursor` is a type which wraps another I/O object to provide a `Seek` +/// A `Cursor` is a type which wraps a non-I/O object to provide a `Seek` /// implementation. /// -/// Cursors are currently typically used with memory buffer objects in order to -/// allow `Seek` plus `Read` and `Write` implementations. For example, common -/// cursor types include: +/// Cursors are typically used with memory buffer objects in order to allow +/// `Seek`, `Read`, and `Write` implementations. For example, common cursor types +/// include `Cursor>` and `Cursor<&[u8]>`. /// -/// * `Cursor>` -/// * `Cursor<&[u8]>` -/// -/// Implementations of the I/O traits for `Cursor` are not currently generic +/// Implementations of the I/O traits for `Cursor` are currently not generic /// over `T` itself. Instead, specific implementations are provided for various /// in-memory buffer types like `Vec` and `&[u8]`. #[stable(feature = "rust1", since = "1.0.0")] From fbc823d1e337e546b5bf7e6fc53f26b51a8583f2 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sun, 22 Mar 2015 15:26:23 -0400 Subject: [PATCH 07/13] Document how to document macros Fixes #23571 --- src/doc/trpl/documentation.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/doc/trpl/documentation.md b/src/doc/trpl/documentation.md index 8e5b3b6a7f0af..7300753cc66d0 100644 --- a/src/doc/trpl/documentation.md +++ b/src/doc/trpl/documentation.md @@ -333,6 +333,41 @@ By repeating all parts of the example, you can ensure that your example still compiles, while only showing the parts that are relevant to that part of your explanation. +### Documenting macros + +Here’s an example of documenting a macro: + +``` +/// Panic with a given message unless an expression evaluates to true. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate foo; +/// # fn main() { +/// panic_unless!(1 + 1 == 2, “Math is broken.”); +/// # } +/// ``` +/// +/// ```should_fail +/// # #[macro_use] extern crate foo; +/// # fn main() { +/// panic_unless!(true == false, “I’m broken.”); +/// # } +/// ``` +#[macro_export] +macro_rules! panic_unless { + ($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } }); +} +``` + +You’ll note three things: we need to add our own `extern crate` line, so that +we can add the `#[macro_use]` attribute. Second, we’ll need to add our own +`main()` as well. Finally, a judicious use of `#` to comment out those two +things, so they don’t show up in the output. + +### Running documentation tests + To run the tests, either ```bash From 45fae882568d9bf36ade39f210a2721d05e556dd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 6 Mar 2015 10:14:12 -0500 Subject: [PATCH 08/13] When matching against a pattern (either via `match` or `let`) that contains ref-bindings, do not permit any upcasting from the type of the value being matched. Similarly, do not permit coercion in a `let`. This is a [breaking-change] in that it closes a type hole that previously existed, and in that coercion is not performed. You should be able to work around the latter by converting: ```rust let ref mut x: T = expr; ``` into ```rust let x: T = expr; let ref mut x = x; ``` Restricting coercion not to apply in the case of `let ref` or `let ref mut` is sort of unexciting to me, but seems the best solution: 1. Mixing coercion and `let ref` or `let ref mut` is a bit odd, because you are taking the address of a (coerced) temporary, but only sometimes. It's not syntactically evident, in other words, what's going on. When you're doing a coercion, you're kind of 2. Put another way, I would like to preserve the relationship that `equality <= subtyping <= coercion <= as-coercion`, where this is an indication of the number of `(T1,T2)` pairs that are accepted by the various relations. Trying to mix `let ref mut` and coercion would create another kind of relation that is like coercion, but acts differently in the case where a precise match is needed. 3. In any case, this is strictly more conservative than what we had before and we can undo it in the future if we find a way to make coercion mix with type equality. The change to match I feel ok about but similarly unthrilled. There is some subtle text already concerning whether to use eqtype or subtype for identifier bindings. The best fix I think would be to always have match use strict equality but use subtyping on identifier bindings, but the comment `(*)` explains why that's not working at the moment. As above, I think we can change this as we clean up the code there. --- src/librustc/middle/pat_util.rs | 18 +++++++++++++ src/librustc/middle/ty.rs | 9 +++++++ src/librustc_typeck/check/_match.rs | 26 ++++++++++++++----- src/librustc_typeck/check/mod.rs | 24 ++++++++++++++--- .../compile-fail/match-ref-mut-invariance.rs | 24 +++++++++++++++++ .../match-ref-mut-let-invariance.rs | 25 ++++++++++++++++++ 6 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 src/test/compile-fail/match-ref-mut-invariance.rs create mode 100644 src/test/compile-fail/match-ref-mut-let-invariance.rs diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs index c5abff3b96360..4f365beed213f 100644 --- a/src/librustc/middle/pat_util.rs +++ b/src/librustc/middle/pat_util.rs @@ -119,6 +119,24 @@ pub fn pat_contains_bindings(dm: &DefMap, pat: &ast::Pat) -> bool { contains_bindings } +/// Checks if the pattern contains any `ref` or `ref mut` bindings. +pub fn pat_contains_ref_binding(dm: &DefMap, pat: &ast::Pat) -> bool { + let mut result = false; + pat_bindings(dm, pat, |mode, _, _, _| { + match mode { + ast::BindingMode::BindByRef(_) => { result = true; } + ast::BindingMode::BindByValue(_) => { } + } + }); + result +} + +/// Checks if the patterns for this arm contain any `ref` or `ref mut` +/// bindings. +pub fn arm_contains_ref_binding(dm: &DefMap, arm: &ast::Arm) -> bool { + arm.pats.iter().any(|pat| pat_contains_ref_binding(dm, pat)) +} + /// Checks if the pattern contains any patterns that bind something to /// an ident or wildcard, e.g. `foo`, or `Foo(_)`, `foo @ Bar(..)`, pub fn pat_contains_bindings_or_wild(dm: &DefMap, pat: &ast::Pat) -> bool { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 99c35c6e54258..b490de335c5a0 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -52,6 +52,7 @@ use middle::mem_categorization as mc; use middle::region; use middle::resolve_lifetime; use middle::infer; +use middle::pat_util; use middle::stability; use middle::subst::{self, ParamSpace, Subst, Substs, VecPerParamSpace}; use middle::traits; @@ -2684,6 +2685,14 @@ impl<'tcx> ctxt<'tcx> { { self.ty_param_defs.borrow()[node_id].clone() } + + pub fn pat_contains_ref_binding(&self, pat: &ast::Pat) -> bool { + pat_util::pat_contains_ref_binding(&self.def_map, pat) + } + + pub fn arm_contains_ref_binding(&self, arm: &ast::Arm) -> bool { + pat_util::arm_contains_ref_binding(&self.def_map, arm) + } } // Interns a type/name combination, stores the resulting box in cx.interner, diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 8e2f4dcefa022..a66c0f351bf49 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -287,10 +287,11 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, // (nmatsakis) an hour or two debugging to remember, so I thought // I'd write them down this time. // - // 1. Most importantly, there is no loss of expressiveness - // here. What we are saying is that the type of `x` - // becomes *exactly* what is expected. This might seem - // like it will cause errors in a case like this: + // 1. There is no loss of expressiveness here, though it does + // cause some inconvenience. What we are saying is that the type + // of `x` becomes *exactly* what is expected. This can cause unnecessary + // errors in some cases, such as this one: + // it will cause errors in a case like this: // // ``` // fn foo<'x>(x: &'x int) { @@ -361,8 +362,21 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, match_src: ast::MatchSource) { let tcx = fcx.ccx.tcx; - let discrim_ty = fcx.infcx().next_ty_var(); - check_expr_has_type(fcx, discrim, discrim_ty); + // Not entirely obvious: if matches may create ref bindings, we + // want to use the *precise* type of the discriminant, *not* some + // supertype, as the "discriminant type" (issue #23116). + let contains_ref_bindings = arms.iter().any(|a| tcx.arm_contains_ref_binding(a)); + let discrim_ty; + if contains_ref_bindings { + check_expr(fcx, discrim); + discrim_ty = fcx.expr_ty(discrim); + } else { + // ...but otherwise we want to use any supertype of the + // discriminant. This is sort of a workaround, see note (*) in + // `check_pat` for some details. + discrim_ty = fcx.infcx().next_ty_var(); + check_expr_has_type(fcx, discrim, discrim_ty); + }; // Typecheck the patterns first, so that we get types for all the // bindings. diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 45d4a1edc6b24..09309c7bbeb88 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4242,11 +4242,27 @@ impl<'tcx> Repr<'tcx> for Expectation<'tcx> { } pub fn check_decl_initializer<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, - nid: ast::NodeId, + local: &'tcx ast::Local, init: &'tcx ast::Expr) { - let local_ty = fcx.local_ty(init.span, nid); - check_expr_coercable_to_type(fcx, init, local_ty) + let ref_bindings = fcx.tcx().pat_contains_ref_binding(&local.pat); + + let local_ty = fcx.local_ty(init.span, local.id); + if !ref_bindings { + check_expr_coercable_to_type(fcx, init, local_ty) + } else { + // Somewhat subtle: if we have a `ref` binding in the pattern, + // we want to avoid introducing coercions for the RHS. This is + // both because it helps preserve sanity and, in the case of + // ref mut, for soundness (issue #23116). In particular, in + // the latter case, we need to be clear that the type of the + // referent for the reference that results is *equal to* the + // type of the lvalue it is referencing, and not some + // supertype thereof. + check_expr(fcx, init); + let init_ty = fcx.expr_ty(init); + demand::eqtype(fcx, init.span, init_ty, local_ty); + }; } pub fn check_decl_local<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, local: &'tcx ast::Local) { @@ -4256,7 +4272,7 @@ pub fn check_decl_local<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, local: &'tcx ast::Local) fcx.write_ty(local.id, t); if let Some(ref init) = local.init { - check_decl_initializer(fcx, local.id, &**init); + check_decl_initializer(fcx, local, &**init); let init_ty = fcx.expr_ty(&**init); if ty::type_is_error(init_ty) { fcx.write_ty(local.id, init_ty); diff --git a/src/test/compile-fail/match-ref-mut-invariance.rs b/src/test/compile-fail/match-ref-mut-invariance.rs new file mode 100644 index 0000000000000..c2b54a972bdc0 --- /dev/null +++ b/src/test/compile-fail/match-ref-mut-invariance.rs @@ -0,0 +1,24 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that when making a ref mut binding with type `&mut T`, the +// type `T` must match precisely the type `U` of the value being +// matched, and in particular cannot be some supertype of `U`. Issue +// #23116. This test focuses on a `match`. + +#![allow(dead_code)] +struct S<'b>(&'b i32); +impl<'b> S<'b> { + fn bar<'a>(&'a mut self) -> &'a mut &'a i32 { + match self.0 { ref mut x => x } //~ ERROR mismatched types + } +} + +fn main() {} diff --git a/src/test/compile-fail/match-ref-mut-let-invariance.rs b/src/test/compile-fail/match-ref-mut-let-invariance.rs new file mode 100644 index 0000000000000..ea16c61dfd4d1 --- /dev/null +++ b/src/test/compile-fail/match-ref-mut-let-invariance.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that when making a ref mut binding with type `&mut T`, the +// type `T` must match precisely the type `U` of the value being +// matched, and in particular cannot be some supertype of `U`. Issue +// #23116. This test focuses on a `let`. + +#![allow(dead_code)] +struct S<'b>(&'b i32); +impl<'b> S<'b> { + fn bar<'a>(&'a mut self) -> &'a mut &'a i32 { + let ref mut x = self.0; + x //~ ERROR mismatched types + } +} + +fn main() {} From 2750e3c83e7cfbad779877ac213bd815c6aa65bb Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 20 Mar 2015 15:22:57 -0400 Subject: [PATCH 09/13] Add note about pointer state after the call. Fixes #23422 --- src/liballoc/heap.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index aaf6e76237ca5..3733350412e49 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -26,6 +26,9 @@ pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { /// /// On failure, return a null pointer and leave the original allocation intact. /// +/// If the allocation was relocated, the memory at the passed-in pointer is +/// undefined after the call. +/// /// Behavior is undefined if the requested size is 0 or the alignment is not a /// power of 2. The alignment must be no larger than the largest supported page /// size on the platform. From d6fb7e9da8d0c1216a1f60ea71ee91080f7b24ae Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 22 Mar 2015 16:16:04 +0100 Subject: [PATCH 10/13] derive missing trait implementations for cursor --- src/libstd/io/cursor.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 365f5e37b0b30..e6debeb2a9ce7 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -31,6 +31,7 @@ use slice; /// over `T` itself. Instead, specific implementations are provided for various /// in-memory buffer types like `Vec` and `&[u8]`. #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone, Debug)] pub struct Cursor { inner: T, pos: u64, From a5e1cbe1915f9fd31900f25f72533fb296ff9a3a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sun, 22 Mar 2015 18:38:40 -0400 Subject: [PATCH 11/13] Beef up BufRead::consume documentation. Fixes #23196 --- src/libstd/io/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 237435d6dfbfa..39c718c96b38a 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -558,6 +558,12 @@ pub trait BufRead: Read { /// This function does not perform any I/O, it simply informs this object /// that some amount of its buffer, returned from `fill_buf`, has been /// consumed and should no longer be returned. + /// + /// This function is used to tell the buffer how many bytes you've consumed + /// from the return value of `fill_buf`, and so may do odd things if + /// `fill_buf` isn't called before calling this. + /// + /// The `amt` must be `<=` the number of bytes in the buffer returned by `fill_buf`. #[stable(feature = "rust1", since = "1.0.0")] fn consume(&mut self, amt: usize); From d52c36246a2e7e1d263b31709424798cfdaa00e3 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 23 Mar 2015 13:40:41 -0400 Subject: [PATCH 12/13] Clarify that slices don't just point to arrays Fixes #23632 --- src/libstd/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index b055796ba547f..6833b6a0a21b8 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -40,11 +40,11 @@ //! //! ## Vectors, slices and strings //! -//! The common container type, `Vec`, a growable vector backed by an -//! array, lives in the [`vec`](vec/index.html) module. References to -//! arrays, `&[T]`, more commonly called "slices", are built-in types -//! for which the [`slice`](slice/index.html) module defines many -//! methods. +//! The common container type, `Vec`, a growable vector backed by an array, +//! lives in the [`vec`](vec/index.html) module. Contiguous, unsized regions +//! of memory, `[T]`, commonly called "slices", and their borrowed versions, +//! `&[T]`, commonly called "borrowed slices", are built-in types for which the +//! for which the [`slice`](slice/index.html) module defines many methods. //! //! `&str`, a UTF-8 string, is a built-in type, and the standard library //! defines methods for it on a variety of traits in the From 1be8fcb4a955457b9ec6e16e34bdfbb7e4d849c7 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 23 Mar 2015 13:06:25 -0400 Subject: [PATCH 13/13] Make note of str in 'more strings' chapter Fixes #21035 --- src/doc/trpl/more-strings.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/doc/trpl/more-strings.md b/src/doc/trpl/more-strings.md index 6567cd448f998..0f19b9249f549 100644 --- a/src/doc/trpl/more-strings.md +++ b/src/doc/trpl/more-strings.md @@ -12,7 +12,7 @@ Additionally, strings are not null-terminated and can contain null bytes. Rust has two main types of strings: `&str` and `String`. -# &str +# `&str` The first kind is a `&str`. This is pronounced a 'string slice'. String literals are of the type `&str`: @@ -36,7 +36,36 @@ Like vector slices, string slices are simply a pointer plus a length. This means that they're a 'view' into an already-allocated string, such as a string literal or a `String`. -# String +## `str` + +You may occasionally see references to a `str` type, without the `&`. While +this type does exist, it’s not something you want to use yourself. Sometimes, +people confuse `str` for `String`, and write this: + +```rust +struct S { + s: str, +} +``` + +This leads to ugly errors: + +```text +error: the trait `core::marker::Sized` is not implemented for the type `str` [E0277] +note: `str` does not have a constant size known at compile-time +``` + +Instead, this `struct` should be + +```rust +struct S { + s: String, +} +``` + +So let’s talk about `String`s. + +# `String` 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