From b66dcb98af7c0785ace170a074640388d24e8536 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 15 Jun 2019 23:51:42 +0200 Subject: [PATCH 01/29] explain better that structural pinning is a per-field choice --- src/libcore/pin.rs | 131 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 29 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index c5247e134c86a..e6fdfb8af5ff3 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -166,43 +166,106 @@ //! implementation as well: if an element of your type could have been pinned, //! you must treat Drop as implicitly taking `Pin<&mut Self>`. //! -//! In particular, if your type is `#[repr(packed)]`, the compiler will automatically +//! For example, you could implement `Drop` as follows: +//! ```rust,ignore +//! impl Drop for Type { +//! fn drop(&mut self) { +//! // `new_unchecked` is okay because we know this value is never used +//! // again after being dropped. +//! inner_drop(unsafe { Pin::new_unchecked(self)}); +//! fn inner_drop(this: Pin<&mut Type>) { +//! // Actual drop code goes here. +//! } +//! } +//! } +//! ``` +//! `inner_drop` has the type that `drop` *should* have, so this makes sure that +//! you do not accidentally use `self`/`this` in a way that is in conflict with pinning. +//! +//! Moreover, if your type is `#[repr(packed)]`, the compiler will automatically //! move fields around to be able to drop them. As a consequence, you cannot use //! pinning with a `#[repr(packed)]` type. //! //! # Projections and Structural Pinning //! -//! One interesting question arises when considering the interaction of pinning -//! and the fields of a struct. When can a struct have a "pinning projection", -//! i.e., an operation with type `fn(Pin<&Struct>) -> Pin<&Field>`? In a -//! similar vein, when can a generic wrapper type (such as `Vec`, `Box`, -//! or `RefCell`) have an operation with type `fn(Pin<&Wrapper>) -> -//! Pin<&T>`? +//! When working with pinned structs, the question arises how one can access the +//! fields of that struct in a method that takes just `Pin<&mut Struct>`. +//! The usual approach is to write helper methods (so called *projections*) +//! that turn `Pin<&mut Struct>` into a reference to the field, but what +//! type should that reference have? Is it `Pin<&mut Field>` or `&mut Field`? +//! The same question arises with the fields of an enum, and also when considering +//! container/wrapper types such as `Vec`, `Box`, or `RefCell`. +//! Also, this question arises for both mutable and shared references, we just +//! use the more common case of mutable references here for illustration. +//! +//! It turns out that it is actually up to the author of the data structure +//! to decide whether the pinned projection for a particular field turns +//! `Pin<&mut Struct>` into `Pin<&mut Field>` or `&mut Field`. There are some +//! constraints though, and the most important constraint is *consistency*: +//! every field can be *either* projected to a pinned reference, *or* have +//! pinning removed as part of the projection. If both are done for the same field, +//! that will likely be unsound! +//! +//! Basically, as the author of a data structure you get to decide for each field whether pinning +//! "propagates" to this field or not. Pinning that propagates is also called "structural", +//! because it follows the structure of the type. +//! In the following, we describe the considerations that have to be made for either choice. +//! +//! ## Pinning *is not* structural for `field` +//! +//! It may seem counter-intuitive that the field of a pinned struct is not pinned, +//! but that is actually the easiest choice: if a `Pin<&mut Field>` is never created, +//! nothing can go wrong! So, if you decide that some field does not have structural pinning, +//! all you have to ensure is that you never create a pinned reference to that field. +//! +//! Then you may add a projection method that turns `Pin<&mut Struct>` into `Pin<&mut Field>`: +//! ```rust,ignore +//! impl Struct { +//! fn get_field<'a>(self: Pin<&'a mut Self>) -> &'a mut Field { +//! // This is okay because `field` is never considered pinned. +//! unsafe { &mut self.get_unchecked_mut().field } +//! } +//! } +//! ``` //! -//! Note: For the entirety of this discussion, the same applies for mutable references as it -//! does for shared references. +//! You may also make make `Struct: Unpin` *even if* the type of `field` +//! is not `Unpin`. What that type thinks about pinning is just not relevant +//! when no `Pin<&mut Field>` is ever created. //! -//! Having a pinning projection for some field means that pinning is "structural": -//! when the wrapper is pinned, the field must be considered pinned, too. -//! After all, the pinning projection lets us get a `Pin<&Field>`. +//! ## Pinning *is* structural for `field` //! -//! However, structural pinning comes with a few extra requirements, so not all -//! wrappers can be structural and hence not all wrappers can offer pinning projections: +//! The other option is to decide that pinning is "structural" for `field`, +//! meaning that if the struct is pinned then so is the field. //! -//! 1. The wrapper must only be [`Unpin`] if all the structural fields are -//! `Unpin`. This is the default, but `Unpin` is a safe trait, so as the author of -//! the wrapper it is your responsibility *not* to add something like -//! `impl Unpin for Wrapper`. (Notice that adding a projection operation +//! This allows writing a projection that creates a `Pin<&mut Field>`, thus +//! witnessing that the field is pinned: +//! ```rust,ignore +//! impl Struct { +//! fn get_field<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut Field> { +//! // This is okay because `field` is pinned when `self` is. +//! unsafe { self.map_unchecked_mut(|s| &mut s.field) } +//! } +//! } +//! ``` +//! +//! However, structural pinning comes with a few extra requirements: +//! +//! 1. The struct must only be [`Unpin`] if all the structural fields are +//! `Unpin`. This is the default, but `Unpin` is a safe trait, so it is your +//! responsibility as the author of the struct *not* to add something like +//! `impl Unpin for Struct`. (Notice that adding a projection operation //! requires unsafe code, so the fact that `Unpin` is a safe trait does not break //! the principle that you only have to worry about any of this if you use `unsafe`.) -//! 2. The destructor of the wrapper must not move structural fields out of its argument. This +//! 2. The destructor of the struct must not move structural fields out of its argument. This //! is the exact point that was raised in the [previous section][drop-impl]: `drop` takes -//! `&mut self`, but the wrapper (and hence its fields) might have been pinned before. +//! `&mut self`, but the struct (and hence its fields) might have been pinned before. //! You have to guarantee that you do not move a field inside your `Drop` implementation. -//! In particular, as explained previously, this means that your wrapper type must *not* +//! In particular, as explained previously, this means that your struct must *not* //! be `#[repr(packed)]`. +//! See that section for how to write `drop` in a way that the compiler can help you +//! not accidentally break pinning. //! 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]: -//! once your wrapper is pinned, the memory that contains the +//! once your struct is pinned, the memory that contains the //! content is not overwritten or deallocated without calling the content's destructors. //! This can be tricky, as witnessed by `VecDeque`: the destructor of `VecDeque` can fail //! to call `drop` on all elements if one of the destructors panics. This violates the @@ -210,11 +273,11 @@ //! their destructor being called. (`VecDeque` has no pinning projections, so this //! does not cause unsoundness.) //! 4. You must not offer any other operations that could lead to data being moved out of -//! the fields when your type is pinned. For example, if the wrapper contains an +//! the structural fields when your type is pinned. For example, if the struct contains an //! `Option` and there is a `take`-like operation with type -//! `fn(Pin<&mut Wrapper>) -> Option`, -//! that operation can be used to move a `T` out of a pinned `Wrapper` -- which means -//! pinning cannot be structural. +//! `fn(Pin<&mut Struct>) -> Option`, +//! that operation can be used to move a `T` out of a pinned `Struct` -- which means +//! pinning cannot be structural for the field holding this data. //! //! For a more complex example of moving data out of a pinned type, imagine if `RefCell` //! had a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`. @@ -231,13 +294,16 @@ //! (using `RefCell::get_pin_mut`) and then move that content using the mutable //! reference we got later. //! -//! For a type like `Vec`, both possibilites (structural pinning or not) make sense, -//! and the choice is up to the author. A `Vec` with structural pinning could -//! have `get_pin`/`get_pin_mut` projections. However, it could *not* allow calling +//! ## Examples +//! +//! For a type like `Vec`, both possibilites (structural pinning or not) make sense. +//! A `Vec` with structural pinning could have `get_pin`/`get_pin_mut` methods to get +//! pinned references to elements. However, it could *not* allow calling //! `pop` on a pinned `Vec` because that would move the (structurally pinned) contents! //! Nor could it allow `push`, which might reallocate and thus also move the contents. //! A `Vec` without structural pinning could `impl Unpin for Vec`, because the contents //! are never pinned and the `Vec` itself is fine with being moved as well. +//! At that point pinning just has no effect on the vector at all. //! //! In the standard library, pointer types generally do not have structural pinning, //! and thus they do not offer pinning projections. This is why `Box: Unpin` holds for all `T`. @@ -249,6 +315,13 @@ //! whether the content is pinned is entirely independent of whether the pointer is //! pinned, meaning pinning is *not* structural. //! +//! When implementing a `Future` combinator, you will usually need structural pinning +//! for the nested futures, as you need to get pinned references to them to call `poll`. +//! But if your combinator contains any other data that does not need to be pinned, +//! you can make those fields not structural and hence freely access them with a +//! mutable reference even when you just have `Pin<&mut Self>` (such as in your own +//! `poll` implementation). +//! //! [`Pin

`]: struct.Pin.html //! [`Unpin`]: ../../std/marker/trait.Unpin.html //! [`Deref`]: ../../std/ops/trait.Deref.html From c489636ce1236f0ff3f9b88d212f8f374e600c19 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 15 Jun 2019 23:56:42 +0200 Subject: [PATCH 02/29] mention that overwrite-without-drop also violates the drop guarantee, and link some more stuff --- src/libcore/pin.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index e6fdfb8af5ff3..5a9f9a42071c6 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -138,10 +138,11 @@ //! To make this work, not just moving the data is restricted; deallocating, repurposing, or //! otherwise invalidating the memory used to store the data is restricted, too. //! Concretely, for pinned data you have to maintain the invariant -//! that *its memory will not get invalidated from the moment it gets pinned until +//! that *its memory will not get invalidated or repurposed from the moment it gets pinned until //! when `drop` is called*. Memory can be invalidated by deallocation, but also by //! replacing a [`Some(v)`] by [`None`], or calling [`Vec::set_len`] to "kill" some elements -//! off of a vector. +//! off of a vector. It can be repurposed by using [`ptr::write`] to overwrite it without +//! calling the destructor first. //! //! This is exactly the kind of guarantee that the intrusive linked list from the previous //! section needs to function correctly. @@ -194,7 +195,7 @@ //! that turn `Pin<&mut Struct>` into a reference to the field, but what //! type should that reference have? Is it `Pin<&mut Field>` or `&mut Field`? //! The same question arises with the fields of an enum, and also when considering -//! container/wrapper types such as `Vec`, `Box`, or `RefCell`. +//! container/wrapper types such as [`Vec`], [`Box`], or [`RefCell`]. //! Also, this question arises for both mutable and shared references, we just //! use the more common case of mutable references here for illustration. //! @@ -267,8 +268,8 @@ //! 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]: //! once your struct is pinned, the memory that contains the //! content is not overwritten or deallocated without calling the content's destructors. -//! This can be tricky, as witnessed by `VecDeque`: the destructor of `VecDeque` can fail -//! to call `drop` on all elements if one of the destructors panics. This violates the +//! This can be tricky, as witnessed by [`VecDeque`]: the destructor of `VecDeque` +//! can fail to call `drop` on all elements if one of the destructors panics. This violates the //! `Drop` guarantee, because it can lead to elements being deallocated without //! their destructor being called. (`VecDeque` has no pinning projections, so this //! does not cause unsoundness.) @@ -279,7 +280,7 @@ //! that operation can be used to move a `T` out of a pinned `Struct` -- which means //! pinning cannot be structural for the field holding this data. //! -//! For a more complex example of moving data out of a pinned type, imagine if `RefCell` +//! For a more complex example of moving data out of a pinned type, imagine if [`RefCell`] //! had a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`. //! Then we could do the following: //! ```compile_fail @@ -296,7 +297,7 @@ //! //! ## Examples //! -//! For a type like `Vec`, both possibilites (structural pinning or not) make sense. +//! For a type like [`Vec`], both possibilites (structural pinning or not) make sense. //! A `Vec` with structural pinning could have `get_pin`/`get_pin_mut` methods to get //! pinned references to elements. However, it could *not* allow calling //! `pop` on a pinned `Vec` because that would move the (structurally pinned) contents! @@ -315,7 +316,7 @@ //! whether the content is pinned is entirely independent of whether the pointer is //! pinned, meaning pinning is *not* structural. //! -//! When implementing a `Future` combinator, you will usually need structural pinning +//! When implementing a [`Future`] combinator, you will usually need structural pinning //! for the nested futures, as you need to get pinned references to them to call `poll`. //! But if your combinator contains any other data that does not need to be pinned, //! you can make those fields not structural and hence freely access them with a @@ -329,9 +330,14 @@ //! [`mem::swap`]: ../../std/mem/fn.swap.html //! [`mem::forget`]: ../../std/mem/fn.forget.html //! [`Box`]: ../../std/boxed/struct.Box.html +//! [`Vec`]: ../../std/vec/struct.Vec.html //! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len +//! [`VecDeque`]: ../../std/collections/struct.VecDeque.html +//! [`RefCell`]: ../../std/cell/struct.RefCell.html //! [`None`]: ../../std/option/enum.Option.html#variant.None //! [`Some(v)`]: ../../std/option/enum.Option.html#variant.Some +//! [`ptr::write`]: ../ptr/fn.write.html +//! [`Future`]: ../../std/future/trait.Future.html //! [drop-impl]: #drop-implementation //! [drop-guarantee]: #drop-guarantee From 665aa3e5bb77d13d641c1663aab78cfcc77707e5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Jun 2019 00:05:17 +0200 Subject: [PATCH 03/29] minor edits --- src/libcore/pin.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index 5a9f9a42071c6..66da7c3fb3203 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -196,8 +196,8 @@ //! type should that reference have? Is it `Pin<&mut Field>` or `&mut Field`? //! The same question arises with the fields of an enum, and also when considering //! container/wrapper types such as [`Vec`], [`Box`], or [`RefCell`]. -//! Also, this question arises for both mutable and shared references, we just -//! use the more common case of mutable references here for illustration. +//! (This question applies to both mutable and shared references, we just +//! use the more common case of mutable references here for illustration.) //! //! It turns out that it is actually up to the author of the data structure //! to decide whether the pinned projection for a particular field turns @@ -214,22 +214,22 @@ //! //! ## Pinning *is not* structural for `field` //! -//! It may seem counter-intuitive that the field of a pinned struct is not pinned, +//! It may seem counter-intuitive that the field of a pinned struct might not be pinned, //! but that is actually the easiest choice: if a `Pin<&mut Field>` is never created, //! nothing can go wrong! So, if you decide that some field does not have structural pinning, //! all you have to ensure is that you never create a pinned reference to that field. //! -//! Then you may add a projection method that turns `Pin<&mut Struct>` into `Pin<&mut Field>`: +//! Then you may add a projection method that turns `Pin<&mut Struct>` into `&mut Field`: //! ```rust,ignore //! impl Struct { -//! fn get_field<'a>(self: Pin<&'a mut Self>) -> &'a mut Field { +//! fn pin_get_field<'a>(self: Pin<&'a mut Self>) -> &'a mut Field { //! // This is okay because `field` is never considered pinned. //! unsafe { &mut self.get_unchecked_mut().field } //! } //! } //! ``` //! -//! You may also make make `Struct: Unpin` *even if* the type of `field` +//! You may also `impl Unpin for Struct` *even if* the type of `field` //! is not `Unpin`. What that type thinks about pinning is just not relevant //! when no `Pin<&mut Field>` is ever created. //! @@ -242,7 +242,7 @@ //! witnessing that the field is pinned: //! ```rust,ignore //! impl Struct { -//! fn get_field<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut Field> { +//! fn pin_get_field<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut Field> { //! // This is okay because `field` is pinned when `self` is. //! unsafe { self.map_unchecked_mut(|s| &mut s.field) } //! } @@ -252,8 +252,8 @@ //! However, structural pinning comes with a few extra requirements: //! //! 1. The struct must only be [`Unpin`] if all the structural fields are -//! `Unpin`. This is the default, but `Unpin` is a safe trait, so it is your -//! responsibility as the author of the struct *not* to add something like +//! `Unpin`. This is the default, but `Unpin` is a safe trait, so as the author of +//! the struct it is your responsibility *not* to add something like //! `impl Unpin for Struct`. (Notice that adding a projection operation //! requires unsafe code, so the fact that `Unpin` is a safe trait does not break //! the principle that you only have to worry about any of this if you use `unsafe`.) From 86e283a1b70005e7d0f31131fe17ffe1fb1dcd79 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Jun 2019 10:15:30 +0200 Subject: [PATCH 04/29] keep links in local crate where possible --- src/libcore/pin.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index 66da7c3fb3203..302e40a954ffa 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -324,20 +324,20 @@ //! `poll` implementation). //! //! [`Pin

`]: struct.Pin.html -//! [`Unpin`]: ../../std/marker/trait.Unpin.html -//! [`Deref`]: ../../std/ops/trait.Deref.html -//! [`DerefMut`]: ../../std/ops/trait.DerefMut.html -//! [`mem::swap`]: ../../std/mem/fn.swap.html -//! [`mem::forget`]: ../../std/mem/fn.forget.html +//! [`Unpin`]: ../marker/trait.Unpin.html +//! [`Deref`]: ../ops/trait.Deref.html +//! [`DerefMut`]: ../ops/trait.DerefMut.html +//! [`mem::swap`]: ../mem/fn.swap.html +//! [`mem::forget`]: ../mem/fn.forget.html //! [`Box`]: ../../std/boxed/struct.Box.html //! [`Vec`]: ../../std/vec/struct.Vec.html //! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len //! [`VecDeque`]: ../../std/collections/struct.VecDeque.html -//! [`RefCell`]: ../../std/cell/struct.RefCell.html -//! [`None`]: ../../std/option/enum.Option.html#variant.None -//! [`Some(v)`]: ../../std/option/enum.Option.html#variant.Some +//! [`RefCell`]: ../cell/struct.RefCell.html +//! [`None`]: ../option/enum.Option.html#variant.None +//! [`Some(v)`]: ../option/enum.Option.html#variant.Some //! [`ptr::write`]: ../ptr/fn.write.html -//! [`Future`]: ../../std/future/trait.Future.html +//! [`Future`]: ../future/trait.Future.html //! [drop-impl]: #drop-implementation //! [drop-guarantee]: #drop-guarantee From 2eb074dff180743336b022144fae7e88ea849c4b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Jun 2019 10:19:22 +0200 Subject: [PATCH 05/29] make example code typecheck at least --- src/libcore/pin.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index 302e40a954ffa..ffea5cbf3318c 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -168,7 +168,9 @@ //! you must treat Drop as implicitly taking `Pin<&mut Self>`. //! //! For example, you could implement `Drop` as follows: -//! ```rust,ignore +//! ```rust,no_run +//! # use std::pin::Pin; +//! # struct Type { } //! impl Drop for Type { //! fn drop(&mut self) { //! // `new_unchecked` is okay because we know this value is never used @@ -220,7 +222,10 @@ //! all you have to ensure is that you never create a pinned reference to that field. //! //! Then you may add a projection method that turns `Pin<&mut Struct>` into `&mut Field`: -//! ```rust,ignore +//! ```rust,no_run +//! # use std::pin::Pin; +//! # type Field = i32; +//! # struct Struct { field: Field } //! impl Struct { //! fn pin_get_field<'a>(self: Pin<&'a mut Self>) -> &'a mut Field { //! // This is okay because `field` is never considered pinned. @@ -240,7 +245,10 @@ //! //! This allows writing a projection that creates a `Pin<&mut Field>`, thus //! witnessing that the field is pinned: -//! ```rust,ignore +//! ```rust,no_run +//! # use std::pin::Pin; +//! # type Field = i32; +//! # struct Struct { field: Field } //! impl Struct { //! fn pin_get_field<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut Field> { //! // This is okay because `field` is pinned when `self` is. From f25095170251c102c32f6bb0705d1feabbd6fe04 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 19 Jun 2019 15:02:50 +0200 Subject: [PATCH 06/29] Apply suggestions from code review Co-Authored-By: Mazdak Farrokhzad --- src/libcore/pin.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index ffea5cbf3318c..15e28f4f9ae94 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -182,7 +182,7 @@ //! } //! } //! ``` -//! `inner_drop` has the type that `drop` *should* have, so this makes sure that +//! The function `inner_drop` has the type that `drop` *should* have, so this makes sure that //! you do not accidentally use `self`/`this` in a way that is in conflict with pinning. //! //! Moreover, if your type is `#[repr(packed)]`, the compiler will automatically @@ -196,7 +196,7 @@ //! The usual approach is to write helper methods (so called *projections*) //! that turn `Pin<&mut Struct>` into a reference to the field, but what //! type should that reference have? Is it `Pin<&mut Field>` or `&mut Field`? -//! The same question arises with the fields of an enum, and also when considering +//! The same question arises with the fields of an `enum`, and also when considering //! container/wrapper types such as [`Vec`], [`Box`], or [`RefCell`]. //! (This question applies to both mutable and shared references, we just //! use the more common case of mutable references here for illustration.) @@ -209,7 +209,7 @@ //! pinning removed as part of the projection. If both are done for the same field, //! that will likely be unsound! //! -//! Basically, as the author of a data structure you get to decide for each field whether pinning +//! As the author of a data structure you get to decide for each field whether pinning //! "propagates" to this field or not. Pinning that propagates is also called "structural", //! because it follows the structure of the type. //! In the following, we describe the considerations that have to be made for either choice. @@ -235,7 +235,7 @@ //! ``` //! //! You may also `impl Unpin for Struct` *even if* the type of `field` -//! is not `Unpin`. What that type thinks about pinning is just not relevant +//! is not `Unpin`. What that type thinks about pinning is not relevant //! when no `Pin<&mut Field>` is ever created. //! //! ## Pinning *is* structural for `field` From bf03a3c539c30f518ca66dcd8ad3890a8b414d15 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 19 Jun 2019 15:11:54 +0200 Subject: [PATCH 07/29] nits --- src/libcore/pin.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index 15e28f4f9ae94..c063cee52270e 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -212,7 +212,8 @@ //! As the author of a data structure you get to decide for each field whether pinning //! "propagates" to this field or not. Pinning that propagates is also called "structural", //! because it follows the structure of the type. -//! In the following, we describe the considerations that have to be made for either choice. +//! In the following subsections, we describe the considerations that have to be made +//! for either choice. //! //! ## Pinning *is not* structural for `field` //! @@ -221,7 +222,8 @@ //! nothing can go wrong! So, if you decide that some field does not have structural pinning, //! all you have to ensure is that you never create a pinned reference to that field. //! -//! Then you may add a projection method that turns `Pin<&mut Struct>` into `&mut Field`: +//! Fields without structural pinning may have a projection method that turns +//! `Pin<&mut Struct>` into `&mut Field`: //! ```rust,no_run //! # use std::pin::Pin; //! # type Field = i32; From a99a7b7f35e3b30862058cc28ed4b0cf51638cf4 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 22 Jun 2019 06:59:27 +0200 Subject: [PATCH 08/29] Remove FnBox. --- .../src/language-features/unsized-locals.md | 4 +- .../src/library-features/fnbox.md | 32 -------- src/liballoc/boxed.rs | 79 ------------------- src/libstd/lib.rs | 1 - .../run-pass/unsized-locals/fnbox-compat.rs | 13 --- 5 files changed, 1 insertion(+), 128 deletions(-) delete mode 100644 src/doc/unstable-book/src/library-features/fnbox.md delete mode 100644 src/test/run-pass/unsized-locals/fnbox-compat.rs diff --git a/src/doc/unstable-book/src/language-features/unsized-locals.md b/src/doc/unstable-book/src/language-features/unsized-locals.md index edc039f896b2c..343084b7db501 100644 --- a/src/doc/unstable-book/src/language-features/unsized-locals.md +++ b/src/doc/unstable-book/src/language-features/unsized-locals.md @@ -117,9 +117,7 @@ fn main () { } ``` -One of the objectives of this feature is to allow `Box`, instead of `Box` in the future. See [#28796] for details. - -[#28796]: https://github.com/rust-lang/rust/issues/28796 +One of the objectives of this feature is to allow `Box`. ## Variable length arrays diff --git a/src/doc/unstable-book/src/library-features/fnbox.md b/src/doc/unstable-book/src/library-features/fnbox.md deleted file mode 100644 index 97e32cc0acb12..0000000000000 --- a/src/doc/unstable-book/src/library-features/fnbox.md +++ /dev/null @@ -1,32 +0,0 @@ -# `fnbox` - -The tracking issue for this feature is [#28796] - -[#28796]: https://github.com/rust-lang/rust/issues/28796 - ------------------------- - -This had been a temporary alternative to the following impls: - -```rust,ignore -impl FnOnce for Box where F: FnOnce + ?Sized {} -impl FnMut for Box where F: FnMut + ?Sized {} -impl Fn for Box where F: Fn + ?Sized {} -``` - -The impls are parallel to these (relatively old) impls: - -```rust,ignore -impl FnOnce for &mut F where F: FnMut + ?Sized {} -impl FnMut for &mut F where F: FnMut + ?Sized {} -impl Fn for &mut F where F: Fn + ?Sized {} -impl FnOnce for &F where F: Fn + ?Sized {} -impl FnMut for &F where F: Fn + ?Sized {} -impl Fn for &F where F: Fn + ?Sized {} -``` - -Before the introduction of [`unsized_locals`][unsized_locals], we had been unable to provide the former impls. That means, unlike `&dyn Fn()` or `&mut dyn FnMut()` we could not use `Box` at that time. - -[unsized_locals]: ../language-features/unsized-locals.md - -`FnBox()` is an alternative approach to `Box` is delegated to `FnBox::call_box` which doesn't need unsized locals. As we now have `Box` working, the `fnbox` feature is going to be removed. diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 76b660fba685c..9109a730cce2d 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -761,85 +761,6 @@ impl + ?Sized> Fn for Box { } } -/// `FnBox` is deprecated and will be removed. -/// `Box` can be called directly, since Rust 1.35.0. -/// -/// `FnBox` is a version of the `FnOnce` intended for use with boxed -/// closure objects. The idea was that where one would normally store a -/// `Box` in a data structure, you whould use -/// `Box`. The two traits behave essentially the same, except -/// that a `FnBox` closure can only be called if it is boxed. -/// -/// # Examples -/// -/// Here is a snippet of code which creates a hashmap full of boxed -/// once closures and then removes them one by one, calling each -/// closure as it is removed. Note that the type of the closures -/// stored in the map is `Box i32>` and not `Box i32>`. -/// -/// ``` -/// #![feature(fnbox)] -/// #![allow(deprecated)] -/// -/// use std::boxed::FnBox; -/// use std::collections::HashMap; -/// -/// fn make_map() -> HashMap i32>> { -/// let mut map: HashMap i32>> = HashMap::new(); -/// map.insert(1, Box::new(|| 22)); -/// map.insert(2, Box::new(|| 44)); -/// map -/// } -/// -/// fn main() { -/// let mut map = make_map(); -/// for i in &[1, 2] { -/// let f = map.remove(&i).unwrap(); -/// assert_eq!(f(), i * 22); -/// } -/// } -/// ``` -/// -/// In Rust 1.35.0 or later, use `FnOnce`, `FnMut`, or `Fn` instead: -/// -/// ``` -/// use std::collections::HashMap; -/// -/// fn make_map() -> HashMap i32>> { -/// let mut map: HashMap i32>> = HashMap::new(); -/// map.insert(1, Box::new(|| 22)); -/// map.insert(2, Box::new(|| 44)); -/// map -/// } -/// -/// fn main() { -/// let mut map = make_map(); -/// for i in &[1, 2] { -/// let f = map.remove(&i).unwrap(); -/// assert_eq!(f(), i * 22); -/// } -/// } -/// ``` -#[rustc_paren_sugar] -#[unstable(feature = "fnbox", issue = "28796")] -#[rustc_deprecated(reason = "use `FnOnce`, `FnMut`, or `Fn` instead", since = "1.37.0")] -pub trait FnBox: FnOnce { - /// Performs the call operation. - fn call_box(self: Box, args: A) -> Self::Output; -} - -#[unstable(feature = "fnbox", issue = "28796")] -#[rustc_deprecated(reason = "use `FnOnce`, `FnMut`, or `Fn` instead", since = "1.37.0")] -#[allow(deprecated, deprecated_in_future)] -impl FnBox for F - where F: FnOnce -{ - fn call_box(self: Box, args: A) -> F::Output { - self.call_once(args) - } -} - #[unstable(feature = "coerce_unsized", issue = "27732")] impl, U: ?Sized> CoerceUnsized> for Box {} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e0ffc9ba92f11..60e06139eba99 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -262,7 +262,6 @@ #![feature(exhaustive_patterns)] #![feature(external_doc)] #![feature(fn_traits)] -#![feature(fnbox)] #![feature(generator_trait)] #![feature(hash_raw_entry)] #![feature(hashmap_internals)] diff --git a/src/test/run-pass/unsized-locals/fnbox-compat.rs b/src/test/run-pass/unsized-locals/fnbox-compat.rs deleted file mode 100644 index 74a4dd5d8515b..0000000000000 --- a/src/test/run-pass/unsized-locals/fnbox-compat.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(fnbox)] -#![allow(deprecated, deprecated_in_future)] - -use std::boxed::FnBox; - -fn call_it(f: Box T>) -> T { - f() -} - -fn main() { - let s = "hello".to_owned(); - assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); -} From 1c12b1be330dd9c3de0b4fe599686d7c0c45e720 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 Jun 2019 22:58:53 +0200 Subject: [PATCH 09/29] call out explicitly that general read needs to be called with an initialized buffer --- src/libstd/io/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 917199f8ea8d0..7fba844897ffe 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -506,9 +506,18 @@ pub trait Read { /// /// No guarantees are provided about the contents of `buf` when this /// function is called, implementations cannot rely on any property of the - /// contents of `buf` being true. It is recommended that implementations + /// contents of `buf` being true. It is recommended that *implementations* /// only write data to `buf` instead of reading its contents. /// + /// Correspondingly, however, *users* of this trait may not assume any guarantees + /// about how the implementation uses `buf`. The trait is safe to implement, + /// so it is perfectly possible that the implementation might inspect that data. + /// As a caller, it is your responsibility to make sure that `buf` is initialized + /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one + /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. + /// + /// [`MaybeUninit`]: ../mem/union.MaybeUninit.html + /// /// # Errors /// /// If this function encounters any form of I/O or other error, an error From b75021b31e43d59eb2b91fe45cfffcb7d25fb141 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 25 Jun 2019 21:02:19 +0300 Subject: [PATCH 10/29] refactor lexer to use idiomatic borrowing --- src/libsyntax/parse/lexer/mod.rs | 225 ++++++++++++++----------------- 1 file changed, 104 insertions(+), 121 deletions(-) diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index ead5d543bec7d..7b9e3bc4d5102 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -321,33 +321,29 @@ impl<'a> StringReader<'a> { (pos - self.source_file.start_pos).to_usize() } - /// Calls `f` with a string slice of the source text spanning from `start` - /// up to but excluding `self.pos`, meaning the slice does not include - /// the character `self.ch`. - fn with_str_from(&self, start: BytePos, f: F) -> T - where F: FnOnce(&str) -> T + /// Slice of the source text from `start` up to but excluding `self.pos`, + /// meaning the slice does not include the character `self.ch`. + fn str_from(&self, start: BytePos) -> &str { - self.with_str_from_to(start, self.pos, f) + self.str_from_to(start, self.pos) } /// Creates a Name from a given offset to the current offset. fn name_from(&self, start: BytePos) -> ast::Name { debug!("taking an ident from {:?} to {:?}", start, self.pos); - self.with_str_from(start, Symbol::intern) + Symbol::intern(self.str_from(start)) } /// As name_from, with an explicit endpoint. fn name_from_to(&self, start: BytePos, end: BytePos) -> ast::Name { debug!("taking an ident from {:?} to {:?}", start, end); - self.with_str_from_to(start, end, Symbol::intern) + Symbol::intern(self.str_from_to(start, end)) } - /// Calls `f` with a string slice of the source text spanning from `start` - /// up to but excluding `end`. - fn with_str_from_to(&self, start: BytePos, end: BytePos, f: F) -> T - where F: FnOnce(&str) -> T + /// Slice of the source text spanning from `start` up to but excluding `end`. + fn str_from_to(&self, start: BytePos, end: BytePos) -> &str { - f(&self.src[self.src_index(start)..self.src_index(end)]) + &self.src[self.src_index(start)..self.src_index(end)] } /// Converts CRLF to LF in the given string, raising an error on bare CR. @@ -456,8 +452,8 @@ impl<'a> StringReader<'a> { self.bump(); } - self.with_str_from(start, |string| { - if string == "_" { + match self.str_from(start) { + "_" => { self.sess.span_diagnostic .struct_span_warn(self.mk_sp(start, self.pos), "underscore literal suffix is not allowed") @@ -468,10 +464,9 @@ impl<'a> StringReader<'a> { ") .emit(); None - } else { - Some(Symbol::intern(string)) } - }) + name => Some(Symbol::intern(name)) + } } /// PRECONDITION: self.ch is not whitespace @@ -513,9 +508,7 @@ impl<'a> StringReader<'a> { } let kind = if doc_comment { - self.with_str_from(start_bpos, |string| { - token::DocComment(Symbol::intern(string)) - }) + token::DocComment(self.name_from(start_bpos)) } else { token::Comment }; @@ -615,23 +608,22 @@ impl<'a> StringReader<'a> { self.bump(); } - self.with_str_from(start_bpos, |string| { - // but comments with only "*"s between two "/"s are not - let kind = if is_block_doc_comment(string) { - let string = if has_cr { - self.translate_crlf(start_bpos, - string, - "bare CR not allowed in block doc-comment") - } else { - string.into() - }; - token::DocComment(Symbol::intern(&string[..])) + let string = self.str_from(start_bpos); + // but comments with only "*"s between two "/"s are not + let kind = if is_block_doc_comment(string) { + let string = if has_cr { + self.translate_crlf(start_bpos, + string, + "bare CR not allowed in block doc-comment") } else { - token::Comment + string.into() }; + token::DocComment(Symbol::intern(&string[..])) + } else { + token::Comment + }; - Some(Token::new(kind, self.mk_sp(start_bpos, self.pos))) - }) + Some(Token::new(kind, self.mk_sp(start_bpos, self.pos))) } /// Scan through any digits (base `scan_radix`) or underscores, @@ -838,20 +830,17 @@ impl<'a> StringReader<'a> { self.bump(); } - return Ok(self.with_str_from(start, |string| { - // FIXME: perform NFKC normalization here. (Issue #2253) - let name = ast::Name::intern(string); - - if is_raw_ident { - let span = self.mk_sp(raw_start, self.pos); - if !name.can_be_raw() { - self.err_span(span, &format!("`{}` cannot be a raw identifier", name)); - } - self.sess.raw_identifier_spans.borrow_mut().push(span); + // FIXME: perform NFKC normalization here. (Issue #2253) + let name = self.name_from(start); + if is_raw_ident { + let span = self.mk_sp(raw_start, self.pos); + if !name.can_be_raw() { + self.err_span(span, &format!("`{}` cannot be a raw identifier", name)); } + self.sess.raw_identifier_spans.borrow_mut().push(span); + } - token::Ident(name, is_raw_ident) - })); + return Ok(token::Ident(name, is_raw_ident)); } } @@ -1300,101 +1289,95 @@ impl<'a> StringReader<'a> { } fn validate_char_escape(&self, start_with_quote: BytePos) { - self.with_str_from_to(start_with_quote + BytePos(1), self.pos - BytePos(1), |lit| { - if let Err((off, err)) = unescape::unescape_char(lit) { + let lit = self.str_from_to(start_with_quote + BytePos(1), self.pos - BytePos(1)); + if let Err((off, err)) = unescape::unescape_char(lit) { + emit_unescape_error( + &self.sess.span_diagnostic, + lit, + self.mk_sp(start_with_quote, self.pos), + unescape::Mode::Char, + 0..off, + err, + ) + } + } + + fn validate_byte_escape(&self, start_with_quote: BytePos) { + let lit = self.str_from_to(start_with_quote + BytePos(1), self.pos - BytePos(1)); + if let Err((off, err)) = unescape::unescape_byte(lit) { + emit_unescape_error( + &self.sess.span_diagnostic, + lit, + self.mk_sp(start_with_quote, self.pos), + unescape::Mode::Byte, + 0..off, + err, + ) + } + } + + fn validate_str_escape(&self, start_with_quote: BytePos) { + let lit = self.str_from_to(start_with_quote + BytePos(1), self.pos - BytePos(1)); + unescape::unescape_str(lit, &mut |range, c| { + if let Err(err) = c { emit_unescape_error( &self.sess.span_diagnostic, lit, self.mk_sp(start_with_quote, self.pos), - unescape::Mode::Char, - 0..off, + unescape::Mode::Str, + range, err, ) } - }); + }) } - fn validate_byte_escape(&self, start_with_quote: BytePos) { - self.with_str_from_to(start_with_quote + BytePos(1), self.pos - BytePos(1), |lit| { - if let Err((off, err)) = unescape::unescape_byte(lit) { + fn validate_raw_str_escape(&self, content_start: BytePos, content_end: BytePos) { + let lit = self.str_from_to(content_start, content_end); + unescape::unescape_raw_str(lit, &mut |range, c| { + if let Err(err) = c { emit_unescape_error( &self.sess.span_diagnostic, lit, - self.mk_sp(start_with_quote, self.pos), - unescape::Mode::Byte, - 0..off, + self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), + unescape::Mode::Str, + range, err, ) } - }); - } - - fn validate_str_escape(&self, start_with_quote: BytePos) { - self.with_str_from_to(start_with_quote + BytePos(1), self.pos - BytePos(1), |lit| { - unescape::unescape_str(lit, &mut |range, c| { - if let Err(err) = c { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(start_with_quote, self.pos), - unescape::Mode::Str, - range, - err, - ) - } - }) - }); - } - - fn validate_raw_str_escape(&self, content_start: BytePos, content_end: BytePos) { - self.with_str_from_to(content_start, content_end, |lit: &str| { - unescape::unescape_raw_str(lit, &mut |range, c| { - if let Err(err) = c { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), - unescape::Mode::Str, - range, - err, - ) - } - }) - }); + }) } fn validate_raw_byte_str_escape(&self, content_start: BytePos, content_end: BytePos) { - self.with_str_from_to(content_start, content_end, |lit: &str| { - unescape::unescape_raw_byte_str(lit, &mut |range, c| { - if let Err(err) = c { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), - unescape::Mode::ByteStr, - range, - err, - ) - } - }) - }); + let lit = self.str_from_to(content_start, content_end); + unescape::unescape_raw_byte_str(lit, &mut |range, c| { + if let Err(err) = c { + emit_unescape_error( + &self.sess.span_diagnostic, + lit, + self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)), + unescape::Mode::ByteStr, + range, + err, + ) + } + }) } fn validate_byte_str_escape(&self, start_with_quote: BytePos) { - self.with_str_from_to(start_with_quote + BytePos(1), self.pos - BytePos(1), |lit| { - unescape::unescape_byte_str(lit, &mut |range, c| { - if let Err(err) = c { - emit_unescape_error( - &self.sess.span_diagnostic, - lit, - self.mk_sp(start_with_quote, self.pos), - unescape::Mode::ByteStr, - range, - err, - ) - } - }) - }); + let lit = self.str_from_to(start_with_quote + BytePos(1), self.pos - BytePos(1)); + unescape::unescape_byte_str(lit, &mut |range, c| { + if let Err(err) = c { + emit_unescape_error( + &self.sess.span_diagnostic, + lit, + self.mk_sp(start_with_quote, self.pos), + unescape::Mode::ByteStr, + range, + err, + ) + } + }) } } From 0f34d7a7e5f068683097e0e664249bd6a417032f Mon Sep 17 00:00:00 2001 From: Erin Power Date: Sun, 23 Jun 2019 11:45:06 +0200 Subject: [PATCH 11/29] Updated RELEASES.md for 1.36.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Taiki Endo Co-Authored-By: Jonas Schievink Co-Authored-By: Torbjørn Birch Moltu --- RELEASES.md | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 91e3c5f721952..b6e2171f6eef9 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,104 @@ +Version 1.36.0 (2019-07-04) +========================== + +Language +-------- +- [Non-Lexical Lifetimes are now enabled on the 2015 edition.][59114] +- [The order of traits in trait objects no longer affects the semantics of that + object.][59445] e.g. `dyn Send + fmt::Debug` is now equivalent to + `dyn fmt::Debug + Send`, where this was previously not the case. + +Libraries +--------- +- [`HashMap`'s implementation has been replaced with `hashbrown::HashMap` implementation.][58623] +- [`TryFromSliceError` now implements `From`.][60318] +- [`mem::needs_drop` is now available as a const fn.][60364] +- [`alloc::Layout::from_size_align_unchecked` is now available as a const fn.][60370] +- [`String` now implements `BorrowMut`.][60404] +- [`io::Cursor` now implements `Default`.][60234] +- [Both `NonNull::{dangling, cast}` are now const fns.][60244] +- [The `alloc` crate is now stable.][59675] `alloc` allows you to use a subset + of `std` (e.g. `Vec`, `Box`, `Arc`) in `#![no_std]` environments if the + environment has access to heap memory allocation. +- [`String` now implements `From<&String>`.][59825] +- [You can now pass multiple arguments to the `dbg!` macro.][59826] `dbg!` will + return a tuple of each argument when there is multiple arguments. +- [`Result::{is_err, is_ok}` are now `#[must_use]` and will produce a warning if + not used.][59648] + +Stabilized APIs +--------------- +- [`VecDeque::rotate_left`] +- [`VecDeque::rotate_right`] +- [`Iterator::copied`] +- [`io::IoSlice`] +- [`io::IoSliceMut`] +- [`Read::read_vectored`] +- [`Write::write_vectored`] +- [`str::as_mut_ptr`] +- [`mem::MaybeUninit`] +- [`pointer::align_offset`] +- [`future::Future`] +- [`task::Context`] +- [`task::RawWaker`] +- [`task::RawWakerVTable`] +- [`task::Waker`] +- [`task::Poll`] + +Cargo +----- +- [Cargo will now produce an error if you attempt to use the name of a required dependency as a feature.][cargo/6860] +- [You can now pass the `--offline` flag to run cargo without accessing the network.][cargo/6934] + +You can find further change's in [Cargo's 1.36.0 release notes][cargo-1-36-0]. + +Clippy +------ +There have been numerous additions and fixes to clippy, see [Clippy's 1.36.0 release notes][clippy-1-36-0] for more details. + +Misc +---- + +Compatibility Notes +------------------- +- With the stabilisation of `mem::MaybeUninit`, `mem::uninitialized` use is no + longer recommended, and will be deprecated in 1.38.0. + +[60318]: https://github.com/rust-lang/rust/pull/60318/ +[60364]: https://github.com/rust-lang/rust/pull/60364/ +[60370]: https://github.com/rust-lang/rust/pull/60370/ +[60404]: https://github.com/rust-lang/rust/pull/60404/ +[60234]: https://github.com/rust-lang/rust/pull/60234/ +[60244]: https://github.com/rust-lang/rust/pull/60244/ +[58623]: https://github.com/rust-lang/rust/pull/58623/ +[59648]: https://github.com/rust-lang/rust/pull/59648/ +[59675]: https://github.com/rust-lang/rust/pull/59675/ +[59825]: https://github.com/rust-lang/rust/pull/59825/ +[59826]: https://github.com/rust-lang/rust/pull/59826/ +[59445]: https://github.com/rust-lang/rust/pull/59445/ +[59114]: https://github.com/rust-lang/rust/pull/59114/ +[cargo/6860]: https://github.com/rust-lang/cargo/pull/6860/ +[cargo/6934]: https://github.com/rust-lang/cargo/pull/6934/ +[`VecDeque::rotate_left`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.rotate_left +[`VecDeque::rotate_right`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.rotate_right +[`Iterator::copied`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.copied +[`io::IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html +[`io::IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html +[`Read::read_vectored`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_vectored +[`Write::write_vectored`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_vectored +[`str::as_mut_ptr`]: https://doc.rust-lang.org/std/primitive.str.html#method.as_mut_ptr +[`mem::MaybeUninit`]: https://doc.rust-lang.org/std/mem/union.MaybeUninit.html +[`pointer::align_offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.align_offset +[`future::Future`]: https://doc.rust-lang.org/std/future/trait.Future.html +[`task::Context`]: https://doc.rust-lang.org/beta/std/task/struct.Context.html +[`task::RawWaker`]: https://doc.rust-lang.org/beta/std/task/struct.RawWaker.html +[`task::RawWakerVTable`]: https://doc.rust-lang.org/beta/std/task/struct.RawWakerVTable.html +[`task::Waker`]: https://doc.rust-lang.org/beta/std/task/struct.Waker.html +[`task::Poll`]: https://doc.rust-lang.org/beta/std/task/enum.Poll.html +[clippy-1-36-0]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#rust-136 +[cargo-1-36-0]: https://github.com/rust-lang/cargo/blob/master/CHANGELOG.md#cargo-136-2019-07-04 + + Version 1.35.0 (2019-05-23) ========================== @@ -62,7 +163,7 @@ Cargo - [You can now set `cargo:rustc-cdylib-link-arg` at build time to pass custom linker arguments when building a `cdylib`.][cargo/6298] Its usage is highly platform specific. - + Misc ---- - [The Rust toolchain is now available natively for musl based distros.][58575] From ba12e7862c58df2155011bb165a8ae1186828bc4 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 24 Jun 2019 17:48:21 -0700 Subject: [PATCH 12/29] Add more tests for async/await --- src/test/ui/async-await/async-fn-nonsend.rs | 59 ++++++++++++ .../ui/async-await/async-fn-nonsend.stderr | 87 ++++++++++++++++++ .../async-await/async-fn-send-uses-nonsend.rs | 59 ++++++++++++ .../ui/async-await/generics-and-bounds.rs | 90 +++++++++++++++++++ src/test/ui/async-await/no-async-const.rs | 8 ++ src/test/ui/async-await/no-async-const.stderr | 8 ++ src/test/ui/async-await/no-const-async.rs | 9 ++ src/test/ui/async-await/no-const-async.stderr | 18 ++++ 8 files changed, 338 insertions(+) create mode 100644 src/test/ui/async-await/async-fn-nonsend.rs create mode 100644 src/test/ui/async-await/async-fn-nonsend.stderr create mode 100644 src/test/ui/async-await/async-fn-send-uses-nonsend.rs create mode 100644 src/test/ui/async-await/generics-and-bounds.rs create mode 100644 src/test/ui/async-await/no-async-const.rs create mode 100644 src/test/ui/async-await/no-async-const.stderr create mode 100644 src/test/ui/async-await/no-const-async.rs create mode 100644 src/test/ui/async-await/no-const-async.stderr diff --git a/src/test/ui/async-await/async-fn-nonsend.rs b/src/test/ui/async-await/async-fn-nonsend.rs new file mode 100644 index 0000000000000..612c1e29d82bd --- /dev/null +++ b/src/test/ui/async-await/async-fn-nonsend.rs @@ -0,0 +1,59 @@ +// compile-fail +// edition:2018 +// compile-flags: --crate-type lib + +#![feature(async_await)] + +use std::{ + cell::RefCell, + fmt::Debug, + rc::Rc, +}; + +fn non_sync() -> impl Debug { RefCell::new(()) } + +fn non_send() -> impl Debug { Rc::new(()) } + +fn take_ref(_: &T) {} + +async fn fut() {} + +async fn fut_arg(_: T) {} + +async fn local_dropped_before_await() { + // FIXME: it'd be nice for this to be allowed in a `Send` `async fn` + let x = non_send(); + drop(x); + fut().await; +} + +async fn non_send_temporary_in_match() { + // We could theoretically make this work as well (produce a `Send` future) + // for scrutinees / temporaries that can or will + // be dropped prior to the match body + // (e.g. `Copy` types). + match Some(non_send()) { + Some(_) => fut().await, + None => {} + } +} + +async fn non_sync_with_method_call() { + // FIXME: it'd be nice for this to work. + let f: &mut std::fmt::Formatter = panic!(); + if non_sync().fmt(f).unwrap() == () { + fut().await; + } +} + +fn assert_send(_: impl Send) {} + +pub fn pass_assert() { + assert_send(local_dropped_before_await()); + //~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely + assert_send(non_send_temporary_in_match()); + //~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely + assert_send(non_sync_with_method_call()); + //~^ ERROR `dyn std::fmt::Write` cannot be sent between threads safely + //~^^ ERROR `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely +} diff --git a/src/test/ui/async-await/async-fn-nonsend.stderr b/src/test/ui/async-await/async-fn-nonsend.stderr new file mode 100644 index 0000000000000..7776a36a28f2b --- /dev/null +++ b/src/test/ui/async-await/async-fn-nonsend.stderr @@ -0,0 +1,87 @@ +error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely + --> $DIR/async-fn-nonsend.rs:52:5 + | +LL | assert_send(local_dropped_before_await()); + | ^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely + | + = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` + = note: required because it appears within the type `impl std::fmt::Debug` + = note: required because it appears within the type `{impl std::fmt::Debug, impl std::future::Future, ()}` + = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:23:39: 28:2 {impl std::fmt::Debug, impl std::future::Future, ()}]` + = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:23:39: 28:2 {impl std::fmt::Debug, impl std::future::Future, ()}]>` + = note: required because it appears within the type `impl std::future::Future` + = note: required because it appears within the type `impl std::future::Future` +note: required by `assert_send` + --> $DIR/async-fn-nonsend.rs:49:1 + | +LL | fn assert_send(_: impl Send) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely + --> $DIR/async-fn-nonsend.rs:54:5 + | +LL | assert_send(non_send_temporary_in_match()); + | ^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely + | + = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` + = note: required because it appears within the type `impl std::fmt::Debug` + = note: required because it appears within the type `{fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, impl std::future::Future, ()}` + = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:30:40: 39:2 {fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, impl std::future::Future, ()}]` + = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:30:40: 39:2 {fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, impl std::future::Future, ()}]>` + = note: required because it appears within the type `impl std::future::Future` + = note: required because it appears within the type `impl std::future::Future` +note: required by `assert_send` + --> $DIR/async-fn-nonsend.rs:49:1 + | +LL | fn assert_send(_: impl Send) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `dyn std::fmt::Write` cannot be sent between threads safely + --> $DIR/async-fn-nonsend.rs:56:5 + | +LL | assert_send(non_sync_with_method_call()); + | ^^^^^^^^^^^ `dyn std::fmt::Write` cannot be sent between threads safely + | + = help: the trait `std::marker::Send` is not implemented for `dyn std::fmt::Write` + = note: required because of the requirements on the impl of `std::marker::Send` for `&mut dyn std::fmt::Write` + = note: required because it appears within the type `std::fmt::Formatter<'_>` + = note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>` + = note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}` + = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:41:38: 47:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]` + = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:41:38: 47:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]>` + = note: required because it appears within the type `impl std::future::Future` + = note: required because it appears within the type `impl std::future::Future` +note: required by `assert_send` + --> $DIR/async-fn-nonsend.rs:49:1 + | +LL | fn assert_send(_: impl Send) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely + --> $DIR/async-fn-nonsend.rs:56:5 + | +LL | assert_send(non_sync_with_method_call()); + | ^^^^^^^^^^^ `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely + | + = help: within `std::fmt::ArgumentV1<'_>`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)` + = note: required because it appears within the type `std::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>` + = note: required because it appears within the type `core::fmt::Void` + = note: required because it appears within the type `&core::fmt::Void` + = note: required because it appears within the type `std::fmt::ArgumentV1<'_>` + = note: required because of the requirements on the impl of `std::marker::Send` for `std::slice::Iter<'_, std::fmt::ArgumentV1<'_>>` + = note: required because it appears within the type `std::fmt::Formatter<'_>` + = note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>` + = note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}` + = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:41:38: 47:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]` + = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:41:38: 47:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]>` + = note: required because it appears within the type `impl std::future::Future` + = note: required because it appears within the type `impl std::future::Future` +note: required by `assert_send` + --> $DIR/async-fn-nonsend.rs:49:1 + | +LL | fn assert_send(_: impl Send) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/async-fn-send-uses-nonsend.rs b/src/test/ui/async-await/async-fn-send-uses-nonsend.rs new file mode 100644 index 0000000000000..f07fc2fceb5b6 --- /dev/null +++ b/src/test/ui/async-await/async-fn-send-uses-nonsend.rs @@ -0,0 +1,59 @@ +// compile-pass +// edition:2018 +// compile-flags: --crate-type lib + +#![feature(async_await)] + +use std::{ + cell::RefCell, + fmt::Debug, + rc::Rc, +}; + +fn non_sync() -> impl Debug { RefCell::new(()) } + +fn non_send() -> impl Debug { Rc::new(()) } + +fn take_ref(_: &T) {} + +async fn fut() {} + +async fn fut_arg(_: T) {} + +async fn still_send() { + fut().await; + println!("{:?} {:?}", non_send(), non_sync()); + fut().await; + drop(non_send()); + drop(non_sync()); + fut().await; + fut_arg(non_sync()).await; + + // Note: all temporaries in `if let` and `match` scrutinee + // are dropped at the *end* of the blocks, so using `non_send()` + // in either of those positions with an await in the middle will + // cause a `!Send` future. It might be nice in the future to allow + // this for `Copy` types, since they can be "dropped" early without + // affecting the end user. + if let Some(_) = Some(non_sync()) { + fut().await; + } + match Some(non_sync()) { + Some(_) => fut().await, + None => fut().await, + } + + let _ = non_send(); + fut().await; + + { + let _x = non_send(); + } + fut().await; +} + +fn assert_send(_: impl Send) {} + +pub fn pass_assert() { + assert_send(still_send()); +} diff --git a/src/test/ui/async-await/generics-and-bounds.rs b/src/test/ui/async-await/generics-and-bounds.rs new file mode 100644 index 0000000000000..913f1435c6adf --- /dev/null +++ b/src/test/ui/async-await/generics-and-bounds.rs @@ -0,0 +1,90 @@ +// compile-pass +// edition:2018 +// compile-flags: --crate-type lib + +#![feature(async_await)] + +use std::future::Future; + +pub async fn simple_generic() {} + +pub trait Foo { + fn foo(&self) {} +} + +struct FooType; +impl Foo for FooType {} + +pub async fn call_generic_bound(f: F) { + f.foo() +} + +pub async fn call_where_clause(f: F) +where + F: Foo, +{ + f.foo() +} + +pub async fn call_impl_trait(f: impl Foo) { + f.foo() +} + +pub async fn call_with_ref(f: &impl Foo) { + f.foo() +} + +pub fn async_fn_with_same_generic_params_unifies() { + let mut a = call_generic_bound(FooType); + a = call_generic_bound(FooType); + + let mut b = call_where_clause(FooType); + b = call_where_clause(FooType); + + let mut c = call_impl_trait(FooType); + c = call_impl_trait(FooType); + + let f_one = FooType; + let f_two = FooType; + let mut d = call_with_ref(&f_one); + d = call_with_ref(&f_two); +} + +pub fn simple_generic_block() -> impl Future { + async move {} +} + +pub fn call_generic_bound_block(f: F) -> impl Future { + async move { f.foo() } +} + +pub fn call_where_clause_block(f: F) -> impl Future +where + F: Foo, +{ + async move { f.foo() } +} + +pub fn call_impl_trait_block(f: impl Foo) -> impl Future { + async move { f.foo() } +} + +pub fn call_with_ref_block<'a>(f: &'a (impl Foo + 'a)) -> impl Future + 'a { + async move { f.foo() } +} + +pub fn async_block_with_same_generic_params_unifies() { + let mut a = call_generic_bound_block(FooType); + a = call_generic_bound_block(FooType); + + let mut b = call_where_clause_block(FooType); + b = call_where_clause_block(FooType); + + let mut c = call_impl_trait_block(FooType); + c = call_impl_trait_block(FooType); + + let f_one = FooType; + let f_two = FooType; + let mut d = call_with_ref_block(&f_one); + d = call_with_ref_block(&f_two); +} diff --git a/src/test/ui/async-await/no-async-const.rs b/src/test/ui/async-await/no-async-const.rs new file mode 100644 index 0000000000000..1db314a5aa208 --- /dev/null +++ b/src/test/ui/async-await/no-async-const.rs @@ -0,0 +1,8 @@ +// compile-fail +// edition:2018 +// compile-flags: --crate-type lib + +#![feature(async_await)] + +pub async const fn x() {} +//~^ ERROR expected one of `fn` or `unsafe`, found `const` diff --git a/src/test/ui/async-await/no-async-const.stderr b/src/test/ui/async-await/no-async-const.stderr new file mode 100644 index 0000000000000..cdb1c6e2d7bd9 --- /dev/null +++ b/src/test/ui/async-await/no-async-const.stderr @@ -0,0 +1,8 @@ +error: expected one of `fn` or `unsafe`, found `const` + --> $DIR/no-async-const.rs:7:11 + | +LL | pub async const fn x() {} + | ^^^^^ expected one of `fn` or `unsafe` here + +error: aborting due to previous error + diff --git a/src/test/ui/async-await/no-const-async.rs b/src/test/ui/async-await/no-const-async.rs new file mode 100644 index 0000000000000..9f09d2188c7c0 --- /dev/null +++ b/src/test/ui/async-await/no-const-async.rs @@ -0,0 +1,9 @@ +// compile-fail +// edition:2018 +// compile-flags: --crate-type lib + +#![feature(async_await)] + +pub const async fn x() {} +//~^ ERROR expected identifier, found reserved keyword `async` +//~^^ expected `:`, found keyword `fn` diff --git a/src/test/ui/async-await/no-const-async.stderr b/src/test/ui/async-await/no-const-async.stderr new file mode 100644 index 0000000000000..693fbf186f913 --- /dev/null +++ b/src/test/ui/async-await/no-const-async.stderr @@ -0,0 +1,18 @@ +error: expected identifier, found reserved keyword `async` + --> $DIR/no-const-async.rs:7:11 + | +LL | pub const async fn x() {} + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | pub const r#async fn x() {} + | ^^^^^^^ + +error: expected `:`, found keyword `fn` + --> $DIR/no-const-async.rs:7:17 + | +LL | pub const async fn x() {} + | ^^ expected `:` + +error: aborting due to 2 previous errors + From 57db25e614dfc3ea3c407374a562501471eb1c5d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 25 Jun 2019 21:37:25 +0300 Subject: [PATCH 13/29] cleanup: rename name_from to symbol_from Lexer uses Symbols for a lot of stuff, not only for identifiers, so the "name" terminology is just confusing. --- src/libsyntax/parse/lexer/mod.rs | 39 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 7b9e3bc4d5102..4e4fe4256c9b0 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1,4 +1,3 @@ -use crate::ast; use crate::parse::ParseSess; use crate::parse::token::{self, Token, TokenKind}; use crate::symbol::{sym, Symbol}; @@ -328,14 +327,14 @@ impl<'a> StringReader<'a> { self.str_from_to(start, self.pos) } - /// Creates a Name from a given offset to the current offset. - fn name_from(&self, start: BytePos) -> ast::Name { + /// Creates a Symbol from a given offset to the current offset. + fn symbol_from(&self, start: BytePos) -> Symbol { debug!("taking an ident from {:?} to {:?}", start, self.pos); Symbol::intern(self.str_from(start)) } - /// As name_from, with an explicit endpoint. - fn name_from_to(&self, start: BytePos, end: BytePos) -> ast::Name { + /// As symbol_from, with an explicit endpoint. + fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol { debug!("taking an ident from {:?} to {:?}", start, end); Symbol::intern(self.str_from_to(start, end)) } @@ -440,7 +439,7 @@ impl<'a> StringReader<'a> { } /// Eats *, if possible. - fn scan_optional_raw_name(&mut self) -> Option { + fn scan_optional_raw_name(&mut self) -> Option { if !ident_start(self.ch) { return None; } @@ -508,7 +507,7 @@ impl<'a> StringReader<'a> { } let kind = if doc_comment { - token::DocComment(self.name_from(start_bpos)) + token::DocComment(self.symbol_from(start_bpos)) } else { token::Comment }; @@ -537,7 +536,7 @@ impl<'a> StringReader<'a> { self.bump(); } return Some(Token::new( - token::Shebang(self.name_from(start)), + token::Shebang(self.symbol_from(start)), self.mk_sp(start, self.pos), )); } @@ -719,17 +718,17 @@ impl<'a> StringReader<'a> { let pos = self.pos; self.check_float_base(start_bpos, pos, base); - (token::Float, self.name_from(start_bpos)) + (token::Float, self.symbol_from(start_bpos)) } else { // it might be a float if it has an exponent if self.ch_is('e') || self.ch_is('E') { self.scan_float_exponent(); let pos = self.pos; self.check_float_base(start_bpos, pos, base); - return (token::Float, self.name_from(start_bpos)); + return (token::Float, self.symbol_from(start_bpos)); } // but we certainly have an integer! - (token::Integer, self.name_from(start_bpos)) + (token::Integer, self.symbol_from(start_bpos)) } } @@ -831,7 +830,7 @@ impl<'a> StringReader<'a> { } // FIXME: perform NFKC normalization here. (Issue #2253) - let name = self.name_from(start); + let name = self.symbol_from(start); if is_raw_ident { let span = self.mk_sp(raw_start, self.pos); if !name.can_be_raw() { @@ -1006,7 +1005,7 @@ impl<'a> StringReader<'a> { // lifetimes shouldn't end with a single quote // if we find one, then this is an invalid character literal if self.ch_is('\'') { - let symbol = self.name_from(start); + let symbol = self.symbol_from(start); self.bump(); self.validate_char_escape(start_with_quote); return Ok(TokenKind::lit(token::Char, symbol, None)); @@ -1024,7 +1023,7 @@ impl<'a> StringReader<'a> { // Include the leading `'` in the real identifier, for macro // expansion purposes. See #12512 for the gory details of why // this is necessary. - return Ok(token::Lifetime(self.name_from(start_with_quote))); + return Ok(token::Lifetime(self.symbol_from(start_with_quote))); } let msg = "unterminated character literal"; let symbol = self.scan_single_quoted_string(start_with_quote, msg); @@ -1052,7 +1051,7 @@ impl<'a> StringReader<'a> { }, Some('r') => { let (start, end, hash_count) = self.scan_raw_string(); - let symbol = self.name_from_to(start, end); + let symbol = self.symbol_from_to(start, end); self.validate_raw_byte_str_escape(start, end); (token::ByteStrRaw(hash_count), symbol) @@ -1073,7 +1072,7 @@ impl<'a> StringReader<'a> { } 'r' => { let (start, end, hash_count) = self.scan_raw_string(); - let symbol = self.name_from_to(start, end); + let symbol = self.symbol_from_to(start, end); self.validate_raw_str_escape(start, end); let suffix = self.scan_optional_raw_name(); @@ -1174,7 +1173,7 @@ impl<'a> StringReader<'a> { fn scan_single_quoted_string(&mut self, start_with_quote: BytePos, - unterminated_msg: &str) -> ast::Name { + unterminated_msg: &str) -> Symbol { // assumes that first `'` is consumed let start = self.pos; // lex `'''` as a single char, for recovery @@ -1206,12 +1205,12 @@ impl<'a> StringReader<'a> { } } - let id = self.name_from(start); + let id = self.symbol_from(start); self.bump(); id } - fn scan_double_quoted_string(&mut self, unterminated_msg: &str) -> ast::Name { + fn scan_double_quoted_string(&mut self, unterminated_msg: &str) -> Symbol { debug_assert!(self.ch_is('\"')); let start_with_quote = self.pos; self.bump(); @@ -1226,7 +1225,7 @@ impl<'a> StringReader<'a> { } self.bump(); } - let id = self.name_from(start); + let id = self.symbol_from(start); self.bump(); id } From 390f717a0af5851271792da9ff235c95f3db2556 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 25 Jun 2019 22:59:00 +0200 Subject: [PATCH 14/29] tweak wording --- src/libstd/io/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 7fba844897ffe..3d0568c16cdf6 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -509,10 +509,10 @@ pub trait Read { /// contents of `buf` being true. It is recommended that *implementations* /// only write data to `buf` instead of reading its contents. /// - /// Correspondingly, however, *users* of this trait may not assume any guarantees + /// Correspondingly, however, *callers* of this method may not assume any guarantees /// about how the implementation uses `buf`. The trait is safe to implement, - /// so it is perfectly possible that the implementation might inspect that data. - /// As a caller, it is your responsibility to make sure that `buf` is initialized + // so it is possible that the code that's supposed to write to the buffer might also read + // from it. It is your responsibility to make sure that `buf` is initialized /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. /// From dedcd97e92f190ee99aa271c6cfd8978f123d731 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 25 Jun 2019 23:08:10 +0200 Subject: [PATCH 15/29] Use f{32,64}::from_bits --- src/libserialize/opaque.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libserialize/opaque.rs b/src/libserialize/opaque.rs index a6a5c318079f1..75988198eb9b5 100644 --- a/src/libserialize/opaque.rs +++ b/src/libserialize/opaque.rs @@ -296,13 +296,13 @@ impl<'a> serialize::Decoder for Decoder<'a> { #[inline] fn read_f64(&mut self) -> Result { let bits = self.read_u64()?; - Ok(unsafe { ::std::mem::transmute(bits) }) + Ok(f64::from_bits(bits)) } #[inline] fn read_f32(&mut self) -> Result { let bits = self.read_u32()?; - Ok(unsafe { ::std::mem::transmute(bits) }) + Ok(f32::from_bits(bits)) } #[inline] From 12806b70506508c4ec187f0223e1e86f89167448 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 25 Jun 2019 23:22:45 +0200 Subject: [PATCH 16/29] Fix clippy::redundant_field_names --- .../vec_linked_list.rs | 2 +- .../annotate_snippet_emitter_writer.rs | 2 +- src/librustc_errors/diagnostic.rs | 2 +- src/librustc_target/spec/fuchsia_base.rs | 2 +- src/libserialize/json.rs | 2 +- src/libsyntax/ast.rs | 4 ++-- src/libsyntax/ext/build.rs | 2 +- src/libsyntax/ext/expand.rs | 6 +++--- src/libsyntax/ext/tt/quoted.rs | 2 +- src/libsyntax/ext/tt/transcribe.rs | 6 +++--- src/libsyntax/feature_gate.rs | 2 +- src/libsyntax/parse/parser.rs | 18 +++++++++--------- src/libsyntax/source_map.rs | 8 ++++---- 13 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/librustc_data_structures/vec_linked_list.rs b/src/librustc_data_structures/vec_linked_list.rs index c00c707a43542..0fb8060031843 100644 --- a/src/librustc_data_structures/vec_linked_list.rs +++ b/src/librustc_data_structures/vec_linked_list.rs @@ -8,7 +8,7 @@ where Ls: Links, { VecLinkedListIterator { - links: links, + links, current: first, } } diff --git a/src/librustc_errors/annotate_snippet_emitter_writer.rs b/src/librustc_errors/annotate_snippet_emitter_writer.rs index 7ed2fddf72d23..3641d355ef19c 100644 --- a/src/librustc_errors/annotate_snippet_emitter_writer.rs +++ b/src/librustc_errors/annotate_snippet_emitter_writer.rs @@ -94,7 +94,7 @@ impl<'a> DiagnosticConverter<'a> { annotation_type: Self::annotation_type_for_level(self.level), }), footer: vec![], - slices: slices, + slices, }) } else { // FIXME(#59346): Is it ok to return None if there's no source_map? diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index fc1fd960c4ace..424d7c0038389 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -388,7 +388,7 @@ impl Diagnostic { }], msg: msg.to_owned(), style: SuggestionStyle::CompletelyHidden, - applicability: applicability, + applicability, }); self } diff --git a/src/librustc_target/spec/fuchsia_base.rs b/src/librustc_target/spec/fuchsia_base.rs index 4e4f2fa0cf34c..48749dff941ac 100644 --- a/src/librustc_target/spec/fuchsia_base.rs +++ b/src/librustc_target/spec/fuchsia_base.rs @@ -19,7 +19,7 @@ pub fn opts() -> TargetOptions { is_like_fuchsia: true, linker_is_gnu: true, has_rpath: false, - pre_link_args: pre_link_args, + pre_link_args, pre_link_objects_exe: vec![ "Scrt1.o".to_string() ], diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index 2c3bea80e349b..826954ce2168c 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -461,7 +461,7 @@ impl<'a> Encoder<'a> { /// Creates a new JSON encoder whose output will be written to the writer /// specified. pub fn new(writer: &'a mut dyn fmt::Write) -> Encoder<'a> { - Encoder { writer: writer, is_emitting_map_key: false, } + Encoder { writer, is_emitting_map_key: false, } } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index f2fac16db01d2..c627596bbdf20 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1832,7 +1832,7 @@ impl Arg { lt, MutTy { ty: infer_ty, - mutbl: mutbl, + mutbl, }, ), span, @@ -2120,7 +2120,7 @@ impl PolyTraitRef { PolyTraitRef { bound_generic_params: generic_params, trait_ref: TraitRef { - path: path, + path, ref_id: DUMMY_NODE_ID, }, span, diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 9d4bf7d518d75..baf1031de1e7c 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -815,7 +815,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn pat(&self, span: Span, pat: PatKind) -> P { - P(ast::Pat { id: ast::DUMMY_NODE_ID, node: pat, span: span }) + P(ast::Pat { id: ast::DUMMY_NODE_ID, node: pat, span }) } fn pat_wild(&self, span: Span) -> P { self.pat(span, PatKind::Wild) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 945cf36af46fe..5473f55aa3370 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -231,7 +231,7 @@ pub struct MacroExpander<'a, 'b> { impl<'a, 'b> MacroExpander<'a, 'b> { pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self { - MacroExpander { cx: cx, monotonic: monotonic } + MacroExpander { cx, monotonic } } pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { @@ -377,7 +377,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { _ => item.clone(), }; invocations.push(Invocation { - kind: InvocationKind::Derive { path: path.clone(), item: item }, + kind: InvocationKind::Derive { path: path.clone(), item }, fragment_kind: invoc.fragment_kind, expansion_data: ExpansionData { mark, @@ -944,7 +944,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } fn collect_bang(&mut self, mac: ast::Mac, span: Span, kind: AstFragmentKind) -> AstFragment { - self.collect(kind, InvocationKind::Bang { mac: mac, ident: None, span: span }) + self.collect(kind, InvocationKind::Bang { mac, ident: None, span }) } fn collect_attr(&mut self, diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 6f5ce89bc315a..ccf9db842ab6e 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -319,7 +319,7 @@ fn parse_tree( tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( span, Lrc::new(Delimited { - delim: delim, + delim, tts: parse( tts.into(), expect_matchers, diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index c51f4b20c31c0..ea7f8e356aa63 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -23,8 +23,8 @@ enum Frame { impl Frame { /// Construct a new frame around the delimited set of tokens. fn new(tts: Vec) -> Frame { - let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts: tts }); - Frame::Delimited { forest: forest, idx: 0, span: DelimSpan::dummy() } + let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts }); + Frame::Delimited { forest, idx: 0, span: DelimSpan::dummy() } } } @@ -248,7 +248,7 @@ pub fn transcribe( // the previous results (from outside the Delimited). quoted::TokenTree::Delimited(mut span, delimited) => { span = span.apply_mark(cx.current_expansion.mark); - stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span }); + stack.push(Frame::Delimited { forest: delimited, idx: 0, span }); result_stack.push(mem::replace(&mut result, Vec::new())); } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c405acd8ee3f6..a6e8441a915e0 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1665,7 +1665,7 @@ impl<'a> Context<'a> { } pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) { - let cx = Context { features: features, parse_sess: parse_sess, plugin_attributes: &[] }; + let cx = Context { features, parse_sess, plugin_attributes: &[] }; cx.check_attribute( attr, attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name).map(|a| *a)), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a1440f2eba47e..fc206580e3811 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -290,10 +290,10 @@ crate enum LastToken { } impl TokenCursorFrame { - fn new(sp: DelimSpan, delim: DelimToken, tts: &TokenStream) -> Self { + fn new(span: DelimSpan, delim: DelimToken, tts: &TokenStream) -> Self { TokenCursorFrame { - delim: delim, - span: sp, + delim, + span, open_delim: delim == token::NoDelim, tree_cursor: tts.clone().into_trees(), close_delim: delim == token::NoDelim, @@ -1449,7 +1449,7 @@ impl<'a> Parser<'a> { let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None }; let mutbl = self.parse_mutability(); let ty = self.parse_ty_no_plus()?; - return Ok(TyKind::Rptr(opt_lifetime, MutTy { ty: ty, mutbl: mutbl })); + return Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl })); } fn parse_ptr(&mut self) -> PResult<'a, MutTy> { @@ -1467,7 +1467,7 @@ impl<'a> Parser<'a> { Mutability::Immutable }; let t = self.parse_ty_no_plus()?; - Ok(MutTy { ty: t, mutbl: mutbl }) + Ok(MutTy { ty: t, mutbl }) } fn is_named_argument(&self) -> bool { @@ -4366,7 +4366,7 @@ impl<'a> Parser<'a> { self.report_invalid_macro_expansion_item(); } - (ident, ast::MacroDef { tokens: tokens, legacy: true }) + (ident, ast::MacroDef { tokens, legacy: true }) } _ => return Ok(None), }; @@ -6789,12 +6789,12 @@ impl<'a> Parser<'a> { let hi = self.token.span; self.expect(&token::Semi)?; Ok(ast::ForeignItem { - ident: ident, - attrs: attrs, + ident, + attrs, node: ForeignItemKind::Ty, id: ast::DUMMY_NODE_ID, span: lo.to(hi), - vis: vis + vis }) } diff --git a/src/libsyntax/source_map.rs b/src/libsyntax/source_map.rs index c0307263387ec..ac30cbb471aea 100644 --- a/src/libsyntax/source_map.rs +++ b/src/libsyntax/source_map.rs @@ -150,7 +150,7 @@ impl SourceMap { -> SourceMap { SourceMap { files: Default::default(), - file_loader: file_loader, + file_loader, path_mapping, } } @@ -396,7 +396,7 @@ impl SourceMap { let f = (*self.files.borrow().source_files)[idx].clone(); match f.lookup_line(pos) { - Some(line) => Ok(SourceFileAndLine { sf: f, line: line }), + Some(line) => Ok(SourceFileAndLine { sf: f, line }), None => Err(f) } } @@ -511,7 +511,7 @@ impl SourceMap { start_col, end_col: hi.col }); - Ok(FileLines {file: lo.file, lines: lines}) + Ok(FileLines {file: lo.file, lines}) } /// Extracts the source surrounding the given `Span` using the `extract_source` function. The @@ -820,7 +820,7 @@ impl SourceMap { let idx = self.lookup_source_file_idx(bpos); let sf = (*self.files.borrow().source_files)[idx].clone(); let offset = bpos - sf.start_pos; - SourceFileAndBytePos {sf: sf, pos: offset} + SourceFileAndBytePos {sf, pos: offset} } /// Converts an absolute BytePos to a CharPos relative to the source_file. From 6c93b47c010f09d6f1ec8303898984944fdbd6f0 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 26 Jun 2019 14:04:37 +0200 Subject: [PATCH 17/29] Fix clippy::cast_losless --- src/librustc_data_structures/fingerprint.rs | 4 ++-- src/librustc_data_structures/sip128.rs | 6 +++--- src/librustc_data_structures/stable_hasher.rs | 2 +- src/libserialize/json.rs | 12 ++++++------ src/libserialize/leb128.rs | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/librustc_data_structures/fingerprint.rs b/src/librustc_data_structures/fingerprint.rs index c4c0db5801209..7975c62b90fb6 100644 --- a/src/librustc_data_structures/fingerprint.rs +++ b/src/librustc_data_structures/fingerprint.rs @@ -39,8 +39,8 @@ impl Fingerprint { // you want. #[inline] pub fn combine_commutative(self, other: Fingerprint) -> Fingerprint { - let a = (self.1 as u128) << 64 | self.0 as u128; - let b = (other.1 as u128) << 64 | other.0 as u128; + let a = u128::from(self.1) << 64 | u128::from(self.0); + let b = u128::from(other.1) << 64 | u128::from(other.0); let c = a.wrapping_add(b); diff --git a/src/librustc_data_structures/sip128.rs b/src/librustc_data_structures/sip128.rs index 06f157f972932..7838a90089024 100644 --- a/src/librustc_data_structures/sip128.rs +++ b/src/librustc_data_structures/sip128.rs @@ -70,15 +70,15 @@ unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { let mut i = 0; // current byte index (from LSB) in the output u64 let mut out = 0; if i + 3 < len { - out = load_int_le!(buf, start + i, u32) as u64; + out = u64::from(load_int_le!(buf, start + i, u32)); i += 4; } if i + 1 < len { - out |= (load_int_le!(buf, start + i, u16) as u64) << (i * 8); + out |= u64::from(load_int_le!(buf, start + i, u16)) << (i * 8); i += 2 } if i < len { - out |= (*buf.get_unchecked(start + i) as u64) << (i * 8); + out |= u64::from(*buf.get_unchecked(start + i)) << (i * 8); i += 1; } debug_assert_eq!(i, len); diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index 0c81c27a96ee5..47dfc1d1688d0 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -44,7 +44,7 @@ impl StableHasher { impl StableHasherResult for u128 { fn finish(hasher: StableHasher) -> Self { let (_0, _1) = hasher.finalize(); - (_0 as u128) | ((_1 as u128) << 64) + u128::from(_0) | (u128::from(_1) << 64) } } diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index 826954ce2168c..a7e7c09f9ae44 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -513,7 +513,7 @@ impl<'a> crate::Encoder for Encoder<'a> { emit_enquoted_if_mapkey!(self, fmt_number_or_null(v)) } fn emit_f32(&mut self, v: f32) -> EncodeResult { - self.emit_f64(v as f64) + self.emit_f64(f64::from(v)) } fn emit_char(&mut self, v: char) -> EncodeResult { @@ -763,7 +763,7 @@ impl<'a> crate::Encoder for PrettyEncoder<'a> { emit_enquoted_if_mapkey!(self, fmt_number_or_null(v)) } fn emit_f32(&mut self, v: f32) -> EncodeResult { - self.emit_f64(v as f64) + self.emit_f64(f64::from(v)) } fn emit_char(&mut self, v: char) -> EncodeResult { @@ -1698,12 +1698,12 @@ impl> Parser { if n2 < 0xDC00 || n2 > 0xDFFF { return self.error(LoneLeadingSurrogateInHexEscape) } - let c = (((n1 - 0xD800) as u32) << 10 | - (n2 - 0xDC00) as u32) + 0x1_0000; + let c = (u32::from(n1 - 0xD800) << 10 | + u32::from(n2 - 0xDC00)) + 0x1_0000; res.push(char::from_u32(c).unwrap()); } - n => match char::from_u32(n as u32) { + n => match char::from_u32(u32::from(n)) { Some(c) => res.push(c), None => return self.error(InvalidUnicodeCodePoint), }, @@ -2405,7 +2405,7 @@ impl ToJson for Json { } impl ToJson for f32 { - fn to_json(&self) -> Json { (*self as f64).to_json() } + fn to_json(&self) -> Json { f64::from(*self).to_json() } } impl ToJson for f64 { diff --git a/src/libserialize/leb128.rs b/src/libserialize/leb128.rs index 16ff59489e718..f9d80842d7558 100644 --- a/src/libserialize/leb128.rs +++ b/src/libserialize/leb128.rs @@ -123,7 +123,7 @@ pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) { loop { byte = data[position]; position += 1; - result |= ((byte & 0x7F) as i128) << shift; + result |= i128::from(byte & 0x7F) << shift; shift += 7; if (byte & 0x80) == 0 { From 33f58baf6eb6beea0ec5c93b1c81714b05eae378 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 26 Jun 2019 14:07:08 +0200 Subject: [PATCH 18/29] Fix clippy::redundant_closure --- src/librustc_data_structures/obligation_forest/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index 4490e5f86d2bd..557e5e2186f11 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -263,7 +263,7 @@ impl ObligationForest { done_cache: Default::default(), waiting_cache: Default::default(), scratch: Some(vec![]), - obligation_tree_id_generator: (0..).map(|i| ObligationTreeId(i)), + obligation_tree_id_generator: (0..).map(ObligationTreeId), error_cache: Default::default(), } } From 1af1f6277e78cb3e1d9c3aafc1e5a65c58916023 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 26 Jun 2019 14:11:40 +0200 Subject: [PATCH 19/29] Fix clippy::print_with_newline --- src/librustc_errors/emitter.rs | 4 ++-- src/libsyntax/print/pp.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index fca8298409a61..a2717ab7ad8a9 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -1339,7 +1339,7 @@ impl EmitterWriter { } let mut dst = self.dst.writable(); - match write!(dst, "\n") { + match writeln!(dst) { Err(e) => panic!("failed to emit error: {}", e), _ => { match dst.flush() { @@ -1598,7 +1598,7 @@ fn emit_to_destination(rendered_buffer: &[Vec], dst.reset()?; } if !short_message && (!lvl.is_failure_note() || pos != rendered_buffer.len() - 1) { - write!(dst, "\n")?; + writeln!(dst)?; } } dst.flush()?; diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index 45eb6995a7699..f5412f3e21602 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -497,7 +497,7 @@ impl<'a> Printer<'a> { pub fn print_newline(&mut self, amount: isize) -> io::Result<()> { debug!("NEWLINE {}", amount); - let ret = write!(self.out, "\n"); + let ret = writeln!(self.out); self.pending_indentation = 0; self.indent(amount); ret From ad62b4203ce3f0bd4c7c348aeabca4f49d5ce075 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 26 Jun 2019 14:14:27 +0200 Subject: [PATCH 20/29] Fix clippy::precedence --- src/librustc_data_structures/sip128.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_data_structures/sip128.rs b/src/librustc_data_structures/sip128.rs index 7838a90089024..e5de359e4759e 100644 --- a/src/librustc_data_structures/sip128.rs +++ b/src/librustc_data_structures/sip128.rs @@ -237,7 +237,7 @@ impl Hasher for SipHasher128 { if self.ntail != 0 { needed = 8 - self.ntail; - self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << 8 * self.ntail; + self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail); if length < needed { self.ntail += length; return From 5cb841d72e70f92fc4318833db4824d07ab4c911 Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Wed, 26 Jun 2019 13:48:41 +0100 Subject: [PATCH 21/29] Don't ICE on item in `.await` expression --- src/librustc/hir/lowering.rs | 1 - src/librustc/hir/mod.rs | 2 +- src/librustc_typeck/check/expr.rs | 20 ++++++-- src/test/ui/async-await/issues/issue-51719.rs | 3 +- .../ui/async-await/issues/issue-51719.stderr | 4 +- src/test/ui/async-await/issues/issue-62009.rs | 19 +++++++ .../ui/async-await/issues/issue-62009.stderr | 49 +++++++++++++++++++ 7 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/async-await/issues/issue-62009.rs create mode 100644 src/test/ui/async-await/issues/issue-62009.stderr diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 1b8e2999afe6a..9c4a208f0f9fc 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -5795,7 +5795,6 @@ impl<'a> LoweringContext<'a> { err.span_label(item_sp, "this is not `async`"); } err.emit(); - return hir::ExprKind::Err; } } let span = self.mark_span_with_reason( diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 2b46170a6d232..6df1c2d8c77e5 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1713,7 +1713,7 @@ pub enum GeneratorMovability { } /// The yield kind that caused an `ExprKind::Yield`. -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable)] pub enum YieldSource { /// An `.await`. Await, diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 85da325197143..21fa219a1cab2 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -295,8 +295,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Index(ref base, ref idx) => { self.check_expr_index(base, idx, needs, expr) } - ExprKind::Yield(ref value, _) => { - self.check_expr_yield(value, expr) + ExprKind::Yield(ref value, ref src) => { + self.check_expr_yield(value, expr, src) } hir::ExprKind::Err => { tcx.types.err @@ -1541,12 +1541,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_expr_yield(&self, value: &'tcx hir::Expr, expr: &'tcx hir::Expr) -> Ty<'tcx> { + fn check_expr_yield( + &self, + value: &'tcx hir::Expr, + expr: &'tcx hir::Expr, + src: &'tcx hir::YieldSource + ) -> Ty<'tcx> { match self.yield_ty { Some(ty) => { self.check_expr_coercable_to_type(&value, ty); } - None => { + // Given that this `yield` expression was generated as a result of lowering a `.await`, + // we know that the yield type must be `()`; however, the context won't contain this + // information. Hence, we check the source of the yield expression here and check its + // value's type against `()` (this check should always hold). + None if src == &hir::YieldSource::Await => { + self.check_expr_coercable_to_type(&value, self.tcx.mk_unit()); + } + _ => { struct_span_err!(self.tcx.sess, expr.span, E0627, "yield statement outside of generator literal").emit(); } diff --git a/src/test/ui/async-await/issues/issue-51719.rs b/src/test/ui/async-await/issues/issue-51719.rs index 5966edd0bf098..361a49c2774ec 100644 --- a/src/test/ui/async-await/issues/issue-51719.rs +++ b/src/test/ui/async-await/issues/issue-51719.rs @@ -7,7 +7,8 @@ async fn foo() {} fn make_generator() { - let _gen = || foo.await; //~ ERROR `await` is only allowed inside `async` functions and blocks + let _gen = || foo().await; + //~^ ERROR `await` is only allowed inside `async` functions and blocks } fn main() {} diff --git a/src/test/ui/async-await/issues/issue-51719.stderr b/src/test/ui/async-await/issues/issue-51719.stderr index c06165b24468f..2a9fb6cf0df6e 100644 --- a/src/test/ui/async-await/issues/issue-51719.stderr +++ b/src/test/ui/async-await/issues/issue-51719.stderr @@ -1,8 +1,8 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-51719.rs:10:19 | -LL | let _gen = || foo.await; - | -- ^^^^^^^^^ only allowed inside `async` functions and blocks +LL | let _gen = || foo().await; + | -- ^^^^^^^^^^^ only allowed inside `async` functions and blocks | | | this is not `async` diff --git a/src/test/ui/async-await/issues/issue-62009.rs b/src/test/ui/async-await/issues/issue-62009.rs new file mode 100644 index 0000000000000..e2d58cac24d94 --- /dev/null +++ b/src/test/ui/async-await/issues/issue-62009.rs @@ -0,0 +1,19 @@ +// edition:2018 + +#![feature(async_await)] + +async fn print_dur() {} + +fn main() { + async { let (); }.await; + //~^ ERROR `await` is only allowed inside `async` functions and blocks + async { + //~^ ERROR `await` is only allowed inside `async` functions and blocks + let task1 = print_dur().await; + }.await; + (async || 2333)().await; + //~^ ERROR `await` is only allowed inside `async` functions and blocks + (|_| 2333).await; + //~^ ERROR `await` is only allowed inside `async` functions and blocks + //~^^ ERROR +} diff --git a/src/test/ui/async-await/issues/issue-62009.stderr b/src/test/ui/async-await/issues/issue-62009.stderr new file mode 100644 index 0000000000000..53d1f34fe4f9b --- /dev/null +++ b/src/test/ui/async-await/issues/issue-62009.stderr @@ -0,0 +1,49 @@ +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/issue-62009.rs:8:5 + | +LL | fn main() { + | ---- this is not `async` +LL | async { let (); }.await; + | ^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/issue-62009.rs:10:5 + | +LL | fn main() { + | ---- this is not `async` +... +LL | / async { +LL | | +LL | | let task1 = print_dur().await; +LL | | }.await; + | |___________^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/issue-62009.rs:14:5 + | +LL | fn main() { + | ---- this is not `async` +... +LL | (async || 2333)().await; + | ^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/issue-62009.rs:16:5 + | +LL | fn main() { + | ---- this is not `async` +... +LL | (|_| 2333).await; + | ^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0277]: the trait bound `[closure@$DIR/issue-62009.rs:16:5: 16:15]: std::future::Future` is not satisfied + --> $DIR/issue-62009.rs:16:5 + | +LL | (|_| 2333).await; + | ^^^^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `[closure@$DIR/issue-62009.rs:16:5: 16:15]` + | + = note: required by `std::future::poll_with_tls_context` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. From 182ce772efee7d3951e807113f069f9bceb87ef6 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Wed, 26 Jun 2019 11:21:59 -0500 Subject: [PATCH 22/29] remove old fixme --- src/libsyntax/parse/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index 07fe521edb037..0ea0b2a694d7d 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -942,7 +942,7 @@ impl<'a> Parser<'a> { // {foo(bar {}} // - ^ // | | - // | help: `)` may belong here (FIXME: #58270) + // | help: `)` may belong here // | // unclosed delimiter if let Some(sp) = unmatched.unclosed_span { From 72ca844ca7d9debbb849eb3aa16c5c4df14d1293 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 26 Jun 2019 10:23:27 -0700 Subject: [PATCH 23/29] Add regression test for MIR drop generation in async loops Fixes #61986. --- src/test/ui/async-await/issues/issue-61986.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/test/ui/async-await/issues/issue-61986.rs diff --git a/src/test/ui/async-await/issues/issue-61986.rs b/src/test/ui/async-await/issues/issue-61986.rs new file mode 100644 index 0000000000000..da8b22bc104bf --- /dev/null +++ b/src/test/ui/async-await/issues/issue-61986.rs @@ -0,0 +1,21 @@ +// compile-pass +// edition:2018 +// +// Tests that we properly handle StorageDead/StorageLives for temporaries +// created in async loop bodies. + +#![feature(async_await)] + +async fn bar() -> Option<()> { + Some(()) +} + +async fn listen() { + while let Some(_) = bar().await { + String::new(); + } +} + +fn main() { + listen(); +} From 7e62050b2381e732808d236d2bb2056ba2edcee8 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 26 Jun 2019 10:57:00 -0700 Subject: [PATCH 24/29] Update books --- src/doc/book | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- src/doc/rustc-guide | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/book b/src/doc/book index 9aacfcc4c5b10..6c0d83499c8e7 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 9aacfcc4c5b102c8cda195932addefd32fe955d2 +Subproject commit 6c0d83499c8e77e06a71d28c5e1adccec278d4f3 diff --git a/src/doc/nomicon b/src/doc/nomicon index c656171b749b7..341c221116a8b 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit c656171b749b7307f21371dd0d3278efee5573b8 +Subproject commit 341c221116a8b9f1010cf1eece952b80c5ec7f54 diff --git a/src/doc/reference b/src/doc/reference index 08ae27a4921ca..7a5aab5fd50d6 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 08ae27a4921ca53967656a7391c82f6c0ddd1ccc +Subproject commit 7a5aab5fd50d6290679beb4cf42fa5f46ed22aec diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index b27472962986e..62b3ff2cb44dd 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit b27472962986e85c94f4183b1a6d2207660d3ed6 +Subproject commit 62b3ff2cb44dd8b648c3ef2a9347c3706d148014 diff --git a/src/doc/rustc-guide b/src/doc/rustc-guide index f55e97c145cf3..abf512fc9cc96 160000 --- a/src/doc/rustc-guide +++ b/src/doc/rustc-guide @@ -1 +1 @@ -Subproject commit f55e97c145cf37fd664db2e0e2f2d05df328bf4f +Subproject commit abf512fc9cc969dcbea69aa15b44586bbeb13c2d From e053eede872b1992c55fbdaeec104b7c84a23f1a Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Wed, 26 Jun 2019 20:34:12 +0200 Subject: [PATCH 25/29] Remove outdated question_mark_macro_sep lint --- src/librustc/lint/builtin.rs | 7 ------- src/librustc/lint/mod.rs | 3 +-- src/librustc_lint/lib.rs | 6 ------ src/libsyntax/early_buffered_lints.rs | 2 -- 4 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index f7af51e47526c..45e598531b969 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -348,12 +348,6 @@ declare_lint! { /// Some lints that are buffered from `libsyntax`. See `syntax::early_buffered_lints`. pub mod parser { - declare_lint! { - pub QUESTION_MARK_MACRO_SEP, - Allow, - "detects the use of `?` as a macro separator" - } - declare_lint! { pub ILL_FORMED_ATTRIBUTE_INPUT, Warn, @@ -444,7 +438,6 @@ declare_lint_pass! { PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, MACRO_USE_EXTERN_CRATE, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, - parser::QUESTION_MARK_MACRO_SEP, parser::ILL_FORMED_ATTRIBUTE_INPUT, DEPRECATED_IN_FUTURE, AMBIGUOUS_ASSOCIATED_ITEMS, diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 041944d887bd9..309af4b72c127 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -27,7 +27,7 @@ use crate::hir::def_id::{CrateNum, LOCAL_CRATE}; use crate::hir::intravisit; use crate::hir; use crate::lint::builtin::BuiltinLintDiagnostics; -use crate::lint::builtin::parser::{QUESTION_MARK_MACRO_SEP, ILL_FORMED_ATTRIBUTE_INPUT}; +use crate::lint::builtin::parser::ILL_FORMED_ATTRIBUTE_INPUT; use crate::session::{Session, DiagnosticMessageId}; use crate::ty::TyCtxt; use crate::ty::query::Providers; @@ -80,7 +80,6 @@ impl Lint { /// Returns the `rust::lint::Lint` for a `syntax::early_buffered_lints::BufferedEarlyLintId`. pub fn from_parser_lint_id(lint_id: BufferedEarlyLintId) -> &'static Self { match lint_id { - BufferedEarlyLintId::QuestionMarkMacroSep => QUESTION_MARK_MACRO_SEP, BufferedEarlyLintId::IllFormedAttributeInput => ILL_FORMED_ATTRIBUTE_INPUT, } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index ec8a9c6fbb2a8..d808a15982e37 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -42,7 +42,6 @@ use rustc::lint::builtin::{ INTRA_DOC_LINK_RESOLUTION_FAILURE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, - parser::QUESTION_MARK_MACRO_SEP, parser::ILL_FORMED_ATTRIBUTE_INPUT, }; use rustc::session; @@ -404,11 +403,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { reference: "issue #50504 ", edition: None, }, - FutureIncompatibleInfo { - id: LintId::of(QUESTION_MARK_MACRO_SEP), - reference: "issue #48075 ", - edition: Some(Edition::Edition2018), - }, FutureIncompatibleInfo { id: LintId::of(MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS), reference: "issue #52234 ", diff --git a/src/libsyntax/early_buffered_lints.rs b/src/libsyntax/early_buffered_lints.rs index 598c8459d1590..b26a1165fed1d 100644 --- a/src/libsyntax/early_buffered_lints.rs +++ b/src/libsyntax/early_buffered_lints.rs @@ -9,8 +9,6 @@ use syntax_pos::MultiSpan; /// Since we cannot import `LintId`s from `rustc::lint`, we define some Ids here which can later be /// passed to `rustc::lint::Lint::from_parser_lint_id` to get a `rustc::lint::Lint`. pub enum BufferedEarlyLintId { - /// Usage of `?` as a macro separator is deprecated. - QuestionMarkMacroSep, IllFormedAttributeInput, } From 768d5001575091dd298fbfce3b7b03a3c3faf117 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 26 Jun 2019 17:22:39 -0700 Subject: [PATCH 26/29] save-analysis: use buffered writes Otherwise it ends up writing the file byte at a time, which can be very slow for large outputs. --- src/librustc_save_analysis/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 19ed9e214073c..ab82f75f74f4b 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -29,6 +29,7 @@ use std::cell::Cell; use std::default::Default; use std::env; use std::fs::File; +use std::io::BufWriter; use std::path::{Path, PathBuf}; use syntax::ast::{self, Attribute, DUMMY_NODE_ID, NodeId, PatKind}; @@ -1025,7 +1026,7 @@ impl<'a> DumpHandler<'a> { } } - fn output_file(&self, ctx: &SaveContext<'_, '_>) -> (File, PathBuf) { + fn output_file(&self, ctx: &SaveContext<'_, '_>) -> (BufWriter, PathBuf) { let sess = &ctx.tcx.sess; let file_name = match ctx.config.output_file { Some(ref s) => PathBuf::from(s), @@ -1059,9 +1060,9 @@ impl<'a> DumpHandler<'a> { info!("Writing output to {}", file_name.display()); - let output_file = File::create(&file_name).unwrap_or_else( + let output_file = BufWriter::new(File::create(&file_name).unwrap_or_else( |e| sess.fatal(&format!("Could not open {}: {}", file_name.display(), e)), - ); + )); (output_file, file_name) } From b6087492ed7b78096adbdba1e086cc987f46b32d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 27 Jun 2019 02:14:57 -0700 Subject: [PATCH 27/29] rustc: Retry SIGILL linker invocations We've seen quite a few issues with spurious illegal instructions getting executed on OSX on CI recently. For whatever reason `cc` itself is executing an illegal instruction and we're not really getting any other information about what's happening. Since we're already retrying the linker when it segfaults, let's just continue to retry everything and automatically reinvoke the linker when it fails with an illegal instruction. --- src/librustc_codegen_ssa/back/link.rs | 71 ++++++++++++++++++--------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 0ba5451bd72f5..618e8b8699fcc 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -29,7 +29,7 @@ use std::fmt; use std::fs; use std::io; use std::path::{Path, PathBuf}; -use std::process::{Output, Stdio}; +use std::process::{Output, Stdio, ExitStatus}; use std::str; use std::env; @@ -510,21 +510,6 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, sess.abort_if_errors(); // Invoke the system linker - // - // Note that there's a terribly awful hack that really shouldn't be present - // in any compiler. Here an environment variable is supported to - // automatically retry the linker invocation if the linker looks like it - // segfaulted. - // - // Gee that seems odd, normally segfaults are things we want to know about! - // Unfortunately though in rust-lang/rust#38878 we're experiencing the - // linker segfaulting on Travis quite a bit which is causing quite a bit of - // pain to land PRs when they spuriously fail due to a segfault. - // - // The issue #38878 has some more debugging information on it as well, but - // this unfortunately looks like it's just a race condition in macOS's linker - // with some thread pool working in the background. It seems that no one - // currently knows a fix for this so in the meantime we're left with this... info!("{:?}", &cmd); let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); let mut prog; @@ -567,21 +552,59 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session, info!("{:?}", &cmd); continue; } + + // Here's a terribly awful hack that really shouldn't be present in any + // compiler. Here an environment variable is supported to automatically + // retry the linker invocation if the linker looks like it segfaulted. + // + // Gee that seems odd, normally segfaults are things we want to know + // about! Unfortunately though in rust-lang/rust#38878 we're + // experiencing the linker segfaulting on Travis quite a bit which is + // causing quite a bit of pain to land PRs when they spuriously fail + // due to a segfault. + // + // The issue #38878 has some more debugging information on it as well, + // but this unfortunately looks like it's just a race condition in + // macOS's linker with some thread pool working in the background. It + // seems that no one currently knows a fix for this so in the meantime + // we're left with this... if !retry_on_segfault || i > 3 { break } let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; let msg_bus = "clang: error: unable to execute command: Bus error: 10"; - if !(out.contains(msg_segv) || out.contains(msg_bus)) { - break + if out.contains(msg_segv) || out.contains(msg_bus) { + warn!( + "looks like the linker segfaulted when we tried to call it, \ + automatically retrying again. cmd = {:?}, out = {}.", + cmd, + out, + ); + continue; } - warn!( - "looks like the linker segfaulted when we tried to call it, \ - automatically retrying again. cmd = {:?}, out = {}.", - cmd, - out, - ); + if is_illegal_instruction(&output.status) { + warn!( + "looks like the linker hit an illegal instruction when we \ + tried to call it, automatically retrying again. cmd = {:?}, ]\ + out = {}, status = {}.", + cmd, + out, + output.status, + ); + continue; + } + + #[cfg(unix)] + fn is_illegal_instruction(status: &ExitStatus) -> bool { + use std::os::unix::prelude::*; + status.signal() == Some(libc::SIGILL) + } + + #[cfg(windows)] + fn is_illegal_instruction(_status: &ExitStatus) -> bool { + false + } } match prog { From d79104bda1a90bb46ddcce84aa963e618fea8d1c Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Thu, 27 Jun 2019 13:41:00 +0200 Subject: [PATCH 28/29] Update RLS --- src/tools/rls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rls b/src/tools/rls index 3e519650cea91..597c9be8c75be 160000 --- a/src/tools/rls +++ b/src/tools/rls @@ -1 +1 @@ -Subproject commit 3e519650cea91a4b785cd773a3e5965553f74249 +Subproject commit 597c9be8c75be3e664f189c4325c96cf9b464dc3 From 88194200e57c90ba0fa7b725d63ff4de28e71bbb Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Wed, 19 Jun 2019 21:55:18 +0100 Subject: [PATCH 29/29] Add suggestion for missing `.await` keyword --- src/libcore/future/future.rs | 1 + src/librustc/middle/lang_items.rs | 1 + src/librustc_typeck/check/demand.rs | 1 + src/librustc_typeck/check/mod.rs | 66 +++++++++++++++++++ .../async-await/dont-suggest-missing-await.rs | 21 ++++++ .../dont-suggest-missing-await.stderr | 12 ++++ .../async-await/suggest-missing-await.fixed | 32 +++++++++ .../ui/async-await/suggest-missing-await.rs | 32 +++++++++ .../async-await/suggest-missing-await.stderr | 27 ++++++++ 9 files changed, 193 insertions(+) create mode 100644 src/test/ui/async-await/dont-suggest-missing-await.rs create mode 100644 src/test/ui/async-await/dont-suggest-missing-await.stderr create mode 100644 src/test/ui/async-await/suggest-missing-await.fixed create mode 100644 src/test/ui/async-await/suggest-missing-await.rs create mode 100644 src/test/ui/async-await/suggest-missing-await.stderr diff --git a/src/libcore/future/future.rs b/src/libcore/future/future.rs index 0492fd709b8dc..acca8d7ba1533 100644 --- a/src/libcore/future/future.rs +++ b/src/libcore/future/future.rs @@ -25,6 +25,7 @@ use crate::task::{Context, Poll}; #[doc(spotlight)] #[must_use = "futures do nothing unless you `.await` or poll them"] #[stable(feature = "futures_api", since = "1.36.0")] +#[cfg_attr(not(bootstrap), lang = "future_trait")] pub trait Future { /// The type of value produced on completion. #[stable(feature = "futures_api", since = "1.36.0")] diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index d7abdb8ecbe10..a6e5bd275ae19 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -320,6 +320,7 @@ language_item_table! { FnMutTraitLangItem, "fn_mut", fn_mut_trait, Target::Trait; FnOnceTraitLangItem, "fn_once", fn_once_trait, Target::Trait; + FutureTraitLangItem, "future_trait", future_trait, Target::Trait; GeneratorStateLangItem, "generator_state", gen_state, Target::Enum; GeneratorTraitLangItem, "generator", gen_trait, Target::Trait; UnpinTraitLangItem, "unpin", unpin_trait, Target::Trait; diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index c469d3516e2d4..14c38ae053d23 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -127,6 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_compatible_variants(&mut err, expr, expected, expr_ty); self.suggest_ref_or_into(&mut err, expr, expected, expr_ty); + self.suggest_missing_await(&mut err, expr, expected, expr_ty); (expected, Some(err)) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5ae26c4118f07..37866bab9009d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3932,6 +3932,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// A possible error is to forget to add `.await` when using futures: + /// + /// ``` + /// #![feature(async_await)] + /// + /// async fn make_u32() -> u32 { + /// 22 + /// } + /// + /// fn take_u32(x: u32) {} + /// + /// async fn foo() { + /// let x = make_u32(); + /// take_u32(x); + /// } + /// ``` + /// + /// This routine checks if the found type `T` implements `Future` where `U` is the + /// expected type. If this is the case, and we are inside of an async body, it suggests adding + /// `.await` to the tail of the expression. + fn suggest_missing_await( + &self, + err: &mut DiagnosticBuilder<'tcx>, + expr: &hir::Expr, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the + // body isn't `async`. + let item_id = self.tcx().hir().get_parent_node(self.body_id); + if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) { + let body = self.tcx().hir().body(body_id); + if let Some(hir::GeneratorKind::Async) = body.generator_kind { + let sp = expr.span; + // Check for `Future` implementations by constructing a predicate to + // prove: `::Output == U` + let future_trait = self.tcx.lang_items().future_trait().unwrap(); + let item_def_id = self.tcx.associated_items(future_trait).next().unwrap().def_id; + let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate { + // `::Output` + projection_ty: ty::ProjectionTy { + // `T` + substs: self.tcx.mk_substs_trait( + found, + self.fresh_substs_for_item(sp, item_def_id) + ), + // `Future::Output` + item_def_id, + }, + ty: expected, + })); + let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); + if self.infcx.predicate_may_hold(&obligation) { + if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { + err.span_suggestion( + sp, + "consider using `.await` here", + format!("{}.await", code), + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + /// A common error is to add an extra semicolon: /// /// ``` diff --git a/src/test/ui/async-await/dont-suggest-missing-await.rs b/src/test/ui/async-await/dont-suggest-missing-await.rs new file mode 100644 index 0000000000000..d551ef57985a1 --- /dev/null +++ b/src/test/ui/async-await/dont-suggest-missing-await.rs @@ -0,0 +1,21 @@ +// edition:2018 + +// This test ensures we don't make the suggestion in bodies that aren't `async`. + +#![feature(async_await)] + +fn take_u32(x: u32) {} + +async fn make_u32() -> u32 { + 22 +} + +async fn dont_suggest_await_in_closure() { + || { + let x = make_u32(); + take_u32(x) + //~^ ERROR mismatched types [E0308] + }; +} + +fn main() {} diff --git a/src/test/ui/async-await/dont-suggest-missing-await.stderr b/src/test/ui/async-await/dont-suggest-missing-await.stderr new file mode 100644 index 0000000000000..c60b0d1f30e80 --- /dev/null +++ b/src/test/ui/async-await/dont-suggest-missing-await.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/dont-suggest-missing-await.rs:16:18 + | +LL | take_u32(x) + | ^ expected u32, found opaque type + | + = note: expected type `u32` + found type `impl std::future::Future` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/async-await/suggest-missing-await.fixed b/src/test/ui/async-await/suggest-missing-await.fixed new file mode 100644 index 0000000000000..282be368c69cd --- /dev/null +++ b/src/test/ui/async-await/suggest-missing-await.fixed @@ -0,0 +1,32 @@ +// edition:2018 +// run-rustfix + +#![feature(async_await)] + +fn take_u32(_x: u32) {} + +async fn make_u32() -> u32 { + 22 +} + +#[allow(unused)] +async fn suggest_await_in_async_fn() { + let x = make_u32(); + take_u32(x.await) + //~^ ERROR mismatched types [E0308] + //~| HELP consider using `.await` here + //~| SUGGESTION x.await +} + +#[allow(unused)] +async fn suggest_await_in_async_closure() { + async || { + let x = make_u32(); + take_u32(x.await) + //~^ ERROR mismatched types [E0308] + //~| HELP consider using `.await` here + //~| SUGGESTION x.await + }; +} + +fn main() {} diff --git a/src/test/ui/async-await/suggest-missing-await.rs b/src/test/ui/async-await/suggest-missing-await.rs new file mode 100644 index 0000000000000..36103f050c147 --- /dev/null +++ b/src/test/ui/async-await/suggest-missing-await.rs @@ -0,0 +1,32 @@ +// edition:2018 +// run-rustfix + +#![feature(async_await)] + +fn take_u32(_x: u32) {} + +async fn make_u32() -> u32 { + 22 +} + +#[allow(unused)] +async fn suggest_await_in_async_fn() { + let x = make_u32(); + take_u32(x) + //~^ ERROR mismatched types [E0308] + //~| HELP consider using `.await` here + //~| SUGGESTION x.await +} + +#[allow(unused)] +async fn suggest_await_in_async_closure() { + async || { + let x = make_u32(); + take_u32(x) + //~^ ERROR mismatched types [E0308] + //~| HELP consider using `.await` here + //~| SUGGESTION x.await + }; +} + +fn main() {} diff --git a/src/test/ui/async-await/suggest-missing-await.stderr b/src/test/ui/async-await/suggest-missing-await.stderr new file mode 100644 index 0000000000000..59c20dcfbc947 --- /dev/null +++ b/src/test/ui/async-await/suggest-missing-await.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/suggest-missing-await.rs:15:14 + | +LL | take_u32(x) + | ^ + | | + | expected u32, found opaque type + | help: consider using `.await` here: `x.await` + | + = note: expected type `u32` + found type `impl std::future::Future` + +error[E0308]: mismatched types + --> $DIR/suggest-missing-await.rs:25:18 + | +LL | take_u32(x) + | ^ + | | + | expected u32, found opaque type + | help: consider using `.await` here: `x.await` + | + = note: expected type `u32` + found type `impl std::future::Future` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`.