From bde6336976f9a660fb9b14b89316ade85837fe44 Mon Sep 17 00:00:00 2001 From: Michael Gattozzi Date: Fri, 24 Mar 2017 23:29:23 -0400 Subject: [PATCH 01/33] Update `Child` docs to not have a note section In #29370 it's noted that for "the Note shouldn't be one, and should come before the examples." This commit changes the positioning of the section and removes wording that said take note in order for it to flow better with the surrounding text and it's new position. --- src/libstd/process.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 7a85e58866236..293c8501400a4 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -73,6 +73,15 @@ use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; /// spawning process and can itself be constructed using a builder-style /// interface. /// +/// There is no implementation of [`Drop`] for child processes, +/// so if you do not ensure the `Child` has exited then it will continue to +/// run, even after the `Child` handle to the child process has gone out of +/// scope. +/// +/// Calling [`wait`][`wait`] (or other functions that wrap around it) will make +/// the parent process wait until the child has actually exited before +/// continuing. +/// /// # Examples /// /// ```should_panic @@ -89,17 +98,6 @@ use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; /// assert!(ecode.success()); /// ``` /// -/// # Note -/// -/// Take note that there is no implementation of [`Drop`] for child processes, -/// so if you do not ensure the `Child` has exited then it will continue to -/// run, even after the `Child` handle to the child process has gone out of -/// scope. -/// -/// Calling [`wait`][`wait`] (or other functions that wrap around it) will make -/// the parent process wait until the child has actually exited before -/// continuing. -/// /// [`Command`]: struct.Command.html /// [`Drop`]: ../../core/ops/trait.Drop.html /// [`wait`]: #method.wait From 7ef1b39b2525dc5475f86b6dc27aa60fe5b3fdca Mon Sep 17 00:00:00 2001 From: Michael Gattozzi Date: Sat, 25 Mar 2017 16:16:50 -0400 Subject: [PATCH 02/33] Remove extra wait from Child docs --- src/libstd/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 293c8501400a4..edcf206501a08 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -78,7 +78,7 @@ use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; /// run, even after the `Child` handle to the child process has gone out of /// scope. /// -/// Calling [`wait`][`wait`] (or other functions that wrap around it) will make +/// Calling [`wait`] (or other functions that wrap around it) will make /// the parent process wait until the child has actually exited before /// continuing. /// From 7897e168bdd9708f83514a9c3e88d83278de4334 Mon Sep 17 00:00:00 2001 From: Dylan Maccora Date: Sat, 1 Apr 2017 15:35:03 +1100 Subject: [PATCH 03/33] Convert docs clean up. --- src/libcore/convert.rs | 145 ++++++++++++++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 31 deletions(-) diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 0b0f831f093b0..5546d2a13c478 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -17,8 +17,8 @@ //! Like many traits, these are often used as bounds for generic functions, to //! support arguments of multiple types. //! -//! - Impl the `As*` traits for reference-to-reference conversions -//! - Impl the [`Into`] trait when you want to consume the value in the conversion +//! - Implement the `As*` traits for reference-to-reference conversions +//! - Implement the [`Into`] trait when you want to consume the value in the conversion //! - The [`From`] trait is the most flexible, useful for value _and_ reference conversions //! - The [`TryFrom`] and [`TryInto`] traits behave like [`From`] and [`Into`], but allow for the //! conversion to fail @@ -29,7 +29,7 @@ //! equivalent [`Into`] or [`TryInto`] implementations for free, thanks to a blanket implementation //! in the standard library. //! -//! # Generic impl +//! # Generic Implementations //! //! - [`AsRef`] and [`AsMut`] auto-dereference if the inner type is a reference //! - [`From`]` for T` implies [`Into`]` for U` @@ -50,10 +50,20 @@ use str::FromStr; -/// A cheap, reference-to-reference conversion. +/// A cheap reference-to-reference conversion. Used to convert a value to a reference value +/// within generic code. /// -/// `AsRef` is very similar to, but different than, [`Borrow`]. See -/// [the book][book] for more. +/// `AsRef` is very similar to, but serves a slightly different purpose than, [`Borrow`]. +/// +/// `AsRef` is to be used when wishing to convert to a reference of another type. +/// `Borrow` is more related to the notion of taking the reference. It is useful when wishing to abstract +/// over the type of reference (`&T`, `&mut T`) or allow both the referenced and owned type to be treated in the same manner. +/// The key difference between the two traits is the intention: +/// +/// - Use `AsRef` when goal is to simply convert into a reference +/// - Use `Borrow` when goal is related to writing code that is agnostic to the type of borrow and if is reference or value +/// +/// See [the book][book] for a more detailed comparison. /// /// [book]: ../../book/borrow-and-asref.html /// [`Borrow`]: ../../std/borrow/trait.Borrow.html @@ -64,7 +74,23 @@ use str::FromStr; /// [`Option`]: ../../std/option/enum.Option.html /// [`Result`]: ../../std/result/enum.Result.html /// +/// # Generic Implementations +/// +/// - `AsRef` auto-dereferences if the inner type is a reference or a mutable +/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`) +/// /// # Examples +/// An example implementation of the trait is [`Path`]. +/// +/// [`Path`]: ../../std/struct.Path.html +/// +/// ``` +/// impl AsRef for str { +/// fn as_ref(&self) -> &Path { +/// Path::new(self) +/// } +/// } +/// ``` /// /// Both [`String`] and `&str` implement `AsRef`: /// @@ -82,11 +108,6 @@ use str::FromStr; /// is_hello(s); /// ``` /// -/// # Generic Impls -/// -/// - `AsRef` auto-dereferences if the inner type is a reference or a mutable -/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`) -/// #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRef { /// Performs the conversion. @@ -96,12 +117,19 @@ pub trait AsRef { /// A cheap, mutable reference-to-mutable reference conversion. /// +/// This trait is similar to `AsRef` but used for converting mutable references. +/// /// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which /// returns an [`Option`] or a [`Result`]. /// /// [`Option`]: ../../std/option/enum.Option.html /// [`Result`]: ../../std/result/enum.Result.html /// +/// # Generic Implementations +/// +/// - `AsMut` auto-dereferences if the inner type is a reference or a mutable +/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`) +/// /// # Examples /// /// [`Box`] implements `AsMut`: @@ -118,10 +146,13 @@ pub trait AsRef { /// assert_eq!(*boxed_num, 1); /// ``` /// -/// # Generic Impls +/// Implementing `AsMut`: /// -/// - `AsMut` auto-dereferences if the inner type is a reference or a mutable -/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`) +/// ``` +/// impl Type { +/// let a = 1; +/// } +/// ``` /// #[stable(feature = "rust1", since = "1.0.0")] pub trait AsMut { @@ -130,7 +161,7 @@ pub trait AsMut { fn as_mut(&mut self) -> &mut T; } -/// A conversion that consumes `self`, which may or may not be expensive. +/// A conversion that consumes `self`, which may or may not be expensive. The reciprocal of [`From`][From]. /// /// **Note: this trait must not fail**. If the conversion can fail, use [`TryInto`] or a dedicated /// method which returns an [`Option`] or a [`Result`]. @@ -139,6 +170,11 @@ pub trait AsMut { /// the [`From`][From] trait, which offers greater flexibility and provides an equivalent `Into` /// implementation for free, thanks to a blanket implementation in the standard library. /// +/// # Generic Implementations +/// +/// - [`From`][From]` for U` implies `Into for T` +/// - [`into`] is reflexive, which means that `Into for T` is implemented +/// /// # Examples /// /// [`String`] implements `Into>`: @@ -153,11 +189,6 @@ pub trait AsMut { /// is_hello(s); /// ``` /// -/// # Generic Impls -/// -/// - [`From`][From]` for U` implies `Into for T` -/// - [`into`] is reflexive, which means that `Into for T` is implemented -/// /// [`TryInto`]: trait.TryInto.html /// [`Option`]: ../../std/option/enum.Option.html /// [`Result`]: ../../std/result/enum.Result.html @@ -171,11 +202,24 @@ pub trait Into: Sized { fn into(self) -> T; } -/// Construct `Self` via a conversion. +/// Simple and safe type conversions in to `Self`. It is the reciprocal of `Into`. +/// +/// This trait is useful when performing error handling as described by [the book][book] and is closely related to the `?` operator. +/// +/// When constructing a function that is capable of failing the return type will generally be of the form `Result`. +/// The `From` trait allows for simplification of error handling by providing a means of returning a single error type that encapsulates +/// numerous possible erroneous situations. +/// This trait is not limited to error handling, rather the general case for this trait would be in any type conversions to have an +/// explicit definition of how they are performed. /// /// **Note: this trait must not fail**. If the conversion can fail, use [`TryFrom`] or a dedicated /// method which returns an [`Option`] or a [`Result`]. /// +/// # Generic Implementations +/// +/// - `From for U` implies [`Into`]` for T` +/// - [`from`] is reflexive, which means that `From for T` is implemented +/// /// # Examples /// /// [`String`] implements `From<&str>`: @@ -186,10 +230,34 @@ pub trait Into: Sized { /// /// assert_eq!(string, other_string); /// ``` -/// # Generic impls /// -/// - `From for U` implies [`Into`]` for T` -/// - [`from`] is reflexive, which means that `From for T` is implemented +/// An example usage for error handling: +/// +/// ``` +/// enum CliError { +/// IoError(io::Error), +/// ParseError(num::ParseIntError), +/// } +/// +/// impl From for MyError { +/// fn from(error: io::Error) -> Self { +/// CliError::IoError(error) +/// } +/// } +/// +/// impl From for MyError { +/// fn from(error: io::Error) -> Self { +/// CliError::ParseError(error) +/// } +/// } +/// +/// fn open_and_parse_file(file_name: &str) -> Result { +/// let file = std::fs::File::open("test")?; +/// let mut contents = String::new(); +/// file.read_to_string(&mut contents)?; +/// let num: i32 = contents.trim().parse()?; +/// } +/// ``` /// /// [`TryFrom`]: trait.TryFrom.html /// [`Option`]: ../../std/option/enum.Option.html @@ -197,6 +265,7 @@ pub trait Into: Sized { /// [`String`]: ../../std/string/struct.String.html /// [`Into`]: trait.Into.html /// [`from`]: trait.From.html#tymethod.from +/// [book]: ../../book/error-handling.html#the-from-trait #[stable(feature = "rust1", since = "1.0.0")] pub trait From: Sized { /// Performs the conversion. @@ -236,7 +305,9 @@ pub trait TryFrom: Sized { // As lifts over & #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a T where T: AsRef { +impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a T + where T: AsRef +{ fn as_ref(&self) -> &U { >::as_ref(*self) } @@ -244,7 +315,9 @@ impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a T where T: AsRef { // As lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a mut T where T: AsRef { +impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a mut T + where T: AsRef +{ fn as_ref(&self) -> &U { >::as_ref(*self) } @@ -260,7 +333,9 @@ impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a mut T where T: AsRef { // AsMut lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: ?Sized, U: ?Sized> AsMut for &'a mut T where T: AsMut { +impl<'a, T: ?Sized, U: ?Sized> AsMut for &'a mut T + where T: AsMut +{ fn as_mut(&mut self) -> &mut U { (*self).as_mut() } @@ -276,7 +351,9 @@ impl<'a, T: ?Sized, U: ?Sized> AsMut for &'a mut T where T: AsMut { // From implies Into #[stable(feature = "rust1", since = "1.0.0")] -impl Into for T where U: From { +impl Into for T + where U: From +{ fn into(self) -> U { U::from(self) } @@ -285,13 +362,17 @@ impl Into for T where U: From { // From (and thus Into) is reflexive #[stable(feature = "rust1", since = "1.0.0")] impl From for T { - fn from(t: T) -> T { t } + fn from(t: T) -> T { + t + } } // TryFrom implies TryInto #[unstable(feature = "try_from", issue = "33417")] -impl TryInto for T where U: TryFrom { +impl TryInto for T + where U: TryFrom +{ type Error = U::Error; fn try_into(self) -> Result { @@ -327,7 +408,9 @@ impl AsRef for str { // FromStr implies TryFrom<&str> #[unstable(feature = "try_from", issue = "33417")] -impl<'a, T> TryFrom<&'a str> for T where T: FromStr { +impl<'a, T> TryFrom<&'a str> for T + where T: FromStr +{ type Error = ::Err; fn try_from(s: &'a str) -> Result { From 79efca10934b3ea0a172db0ab514fdd72ddeacfb Mon Sep 17 00:00:00 2001 From: Dylan Maccora Date: Mon, 3 Apr 2017 07:57:20 +1000 Subject: [PATCH 04/33] Wrapped to 80 characters. Fix links. --- src/libcore/convert.rs | 98 +++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 5546d2a13c478..19cd2d3398f39 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -26,16 +26,16 @@ //! As a library author, you should prefer implementing [`From`][`From`] or //! [`TryFrom`][`TryFrom`] rather than [`Into`][`Into`] or [`TryInto`][`TryInto`], //! as [`From`] and [`TryFrom`] provide greater flexibility and offer -//! equivalent [`Into`] or [`TryInto`] implementations for free, thanks to a blanket implementation -//! in the standard library. +//! equivalent [`Into`] or [`TryInto`] implementations for free, thanks to a +//! blanket implementation in the standard library. //! //! # Generic Implementations //! //! - [`AsRef`] and [`AsMut`] auto-dereference if the inner type is a reference //! - [`From`]` for T` implies [`Into`]` for U` //! - [`TryFrom`]` for T` implies [`TryInto`]` for U` -//! - [`From`] and [`Into`] are reflexive, which means that all types can `into()` -//! themselves and `from()` themselves +//! - [`From`] and [`Into`] are reflexive, which means that all types can +//! `into()` themselves and `from()` themselves //! //! See each trait for usage examples. //! @@ -50,26 +50,31 @@ use str::FromStr; -/// A cheap reference-to-reference conversion. Used to convert a value to a reference value -/// within generic code. -/// -/// `AsRef` is very similar to, but serves a slightly different purpose than, [`Borrow`]. -/// -/// `AsRef` is to be used when wishing to convert to a reference of another type. -/// `Borrow` is more related to the notion of taking the reference. It is useful when wishing to abstract -/// over the type of reference (`&T`, `&mut T`) or allow both the referenced and owned type to be treated in the same manner. +/// A cheap reference-to-reference conversion. Used to convert a value to a +/// reference value within generic code. +/// +/// `AsRef` is very similar to, but serves a slightly different purpose than, +/// [`Borrow`]. +/// +/// `AsRef` is to be used when wishing to convert to a reference of another +/// type. +/// `Borrow` is more related to the notion of taking the reference. It is +/// useful when wishing to abstract +/// over the type of reference (`&T`, `&mut T`) or allow both the referenced +/// and owned type to be treated in the same manner. /// The key difference between the two traits is the intention: /// /// - Use `AsRef` when goal is to simply convert into a reference -/// - Use `Borrow` when goal is related to writing code that is agnostic to the type of borrow and if is reference or value +/// - Use `Borrow` when goal is related to writing code that is agnostic to the +/// type of borrow and if is reference or value /// /// See [the book][book] for a more detailed comparison. /// /// [book]: ../../book/borrow-and-asref.html /// [`Borrow`]: ../../std/borrow/trait.Borrow.html /// -/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which -/// returns an [`Option`] or a [`Result`]. +/// **Note: this trait must not fail**. If the conversion can fail, use a +/// dedicated method which returns an [`Option`] or a [`Result`]. /// /// [`Option`]: ../../std/option/enum.Option.html /// [`Result`]: ../../std/result/enum.Result.html @@ -77,12 +82,13 @@ use str::FromStr; /// # Generic Implementations /// /// - `AsRef` auto-dereferences if the inner type is a reference or a mutable -/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`) +/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type +/// `&mut Foo` or `&&mut Foo`) /// /// # Examples /// An example implementation of the trait is [`Path`]. /// -/// [`Path`]: ../../std/struct.Path.html +/// [`Path`]: ../../std/path/struct.Path.html /// /// ``` /// impl AsRef for str { @@ -119,8 +125,8 @@ pub trait AsRef { /// /// This trait is similar to `AsRef` but used for converting mutable references. /// -/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which -/// returns an [`Option`] or a [`Result`]. +/// **Note: this trait must not fail**. If the conversion can fail, use a +/// dedicated method which returns an [`Option`] or a [`Result`]. /// /// [`Option`]: ../../std/option/enum.Option.html /// [`Result`]: ../../std/result/enum.Result.html @@ -128,7 +134,8 @@ pub trait AsRef { /// # Generic Implementations /// /// - `AsMut` auto-dereferences if the inner type is a reference or a mutable -/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`) +/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type +/// `&mut Foo` or `&&mut Foo`) /// /// # Examples /// @@ -161,14 +168,17 @@ pub trait AsMut { fn as_mut(&mut self) -> &mut T; } -/// A conversion that consumes `self`, which may or may not be expensive. The reciprocal of [`From`][From]. +/// A conversion that consumes `self`, which may or may not be expensive. The +/// reciprocal of [`From`][From]. /// -/// **Note: this trait must not fail**. If the conversion can fail, use [`TryInto`] or a dedicated -/// method which returns an [`Option`] or a [`Result`]. +/// **Note: this trait must not fail**. If the conversion can fail, use +/// [`TryInto`] or a dedicated method which returns an [`Option`] or a +/// [`Result`]. /// -/// Library authors should not directly implement this trait, but should prefer implementing -/// the [`From`][From] trait, which offers greater flexibility and provides an equivalent `Into` -/// implementation for free, thanks to a blanket implementation in the standard library. +/// Library authors should not directly implement this trait, but should prefer +/// implementing the [`From`][From] trait, which offers greater flexibility and +/// provides an equivalent `Into` implementation for free, thanks to a blanket +/// implementation in the standard library. /// /// # Generic Implementations /// @@ -202,18 +212,24 @@ pub trait Into: Sized { fn into(self) -> T; } -/// Simple and safe type conversions in to `Self`. It is the reciprocal of `Into`. +/// Simple and safe type conversions in to `Self`. It is the reciprocal of +/// `Into`. /// -/// This trait is useful when performing error handling as described by [the book][book] and is closely related to the `?` operator. +/// This trait is useful when performing error handling as described by +/// [the book][book] and is closely related to the `?` operator. /// -/// When constructing a function that is capable of failing the return type will generally be of the form `Result`. -/// The `From` trait allows for simplification of error handling by providing a means of returning a single error type that encapsulates -/// numerous possible erroneous situations. -/// This trait is not limited to error handling, rather the general case for this trait would be in any type conversions to have an -/// explicit definition of how they are performed. +/// When constructing a function that is capable of failing the return type +/// will generally be of the form `Result`. +/// The `From` trait allows for simplification of error handling by providing a +/// means of returning a single error type that encapsulates numerous possible +/// erroneous situations. +/// This trait is not limited to error handling, rather the general case for +/// this trait would be in any type conversions to have an explicit definition +/// of how they are performed. /// -/// **Note: this trait must not fail**. If the conversion can fail, use [`TryFrom`] or a dedicated -/// method which returns an [`Option`] or a [`Result`]. +/// **Note: this trait must not fail**. If the conversion can fail, use +/// [`TryFrom`] or a dedicated method which returns an [`Option`] or a +/// [`Result`]. /// /// # Generic Implementations /// @@ -265,7 +281,7 @@ pub trait Into: Sized { /// [`String`]: ../../std/string/struct.String.html /// [`Into`]: trait.Into.html /// [`from`]: trait.From.html#tymethod.from -/// [book]: ../../book/error-handling.html#the-from-trait +/// [book]: ../../book/error-handling.html #[stable(feature = "rust1", since = "1.0.0")] pub trait From: Sized { /// Performs the conversion. @@ -273,11 +289,13 @@ pub trait From: Sized { fn from(T) -> Self; } -/// An attempted conversion that consumes `self`, which may or may not be expensive. +/// An attempted conversion that consumes `self`, which may or may not be +/// expensive. /// -/// Library authors should not directly implement this trait, but should prefer implementing -/// the [`TryFrom`] trait, which offers greater flexibility and provides an equivalent `TryInto` -/// implementation for free, thanks to a blanket implementation in the standard library. +/// Library authors should not directly implement this trait, but should prefer +/// implementing the [`TryFrom`] trait, which offers greater flexibility and +/// provides an equivalent `TryInto` implementation for free, thanks to a +/// blanket implementation in the standard library. /// /// [`TryFrom`]: trait.TryFrom.html #[unstable(feature = "try_from", issue = "33417")] From bb8474682366592fa9d52cb11723a5fd5cd9421e Mon Sep 17 00:00:00 2001 From: Dylan Maccora Date: Tue, 4 Apr 2017 08:27:20 +1000 Subject: [PATCH 05/33] AsMut example --- src/libcore/convert.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 19cd2d3398f39..0bc0550641363 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -91,6 +91,8 @@ use str::FromStr; /// [`Path`]: ../../std/path/struct.Path.html /// /// ``` +/// use std::path::Path; +/// /// impl AsRef for str { /// fn as_ref(&self) -> &Path { /// Path::new(self) @@ -123,7 +125,8 @@ pub trait AsRef { /// A cheap, mutable reference-to-mutable reference conversion. /// -/// This trait is similar to `AsRef` but used for converting mutable references. +/// This trait is similar to `AsRef` but used for converting between mutable +/// references. /// /// **Note: this trait must not fail**. If the conversion can fail, use a /// dedicated method which returns an [`Option`] or a [`Result`]. @@ -153,11 +156,13 @@ pub trait AsRef { /// assert_eq!(*boxed_num, 1); /// ``` /// -/// Implementing `AsMut`: +/// `Vec` implements `AsMut` for converting between itself and a primitive array: /// /// ``` -/// impl Type { -/// let a = 1; +/// impl AsMut<[T]> for Vec { +/// fn as_mut(&mut self) -> &mut [T] { +/// self +/// } /// } /// ``` /// @@ -250,19 +255,22 @@ pub trait Into: Sized { /// An example usage for error handling: /// /// ``` +/// use std::io::{self, Read}; +/// use std::num; +/// /// enum CliError { /// IoError(io::Error), /// ParseError(num::ParseIntError), /// } /// -/// impl From for MyError { +/// impl From for CliError { /// fn from(error: io::Error) -> Self { /// CliError::IoError(error) /// } /// } /// -/// impl From for MyError { -/// fn from(error: io::Error) -> Self { +/// impl From for CliError { +/// fn from(error: num::ParseIntError) -> Self { /// CliError::ParseError(error) /// } /// } From d35fa1e98b244773129f18b0e8bcc81fa099ae68 Mon Sep 17 00:00:00 2001 From: Dylan Maccora Date: Fri, 7 Apr 2017 18:26:36 +1000 Subject: [PATCH 06/33] Removing broken examples --- src/libcore/convert.rs | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 0bc0550641363..abab4bb0f63b4 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -86,19 +86,6 @@ use str::FromStr; /// `&mut Foo` or `&&mut Foo`) /// /// # Examples -/// An example implementation of the trait is [`Path`]. -/// -/// [`Path`]: ../../std/path/struct.Path.html -/// -/// ``` -/// use std::path::Path; -/// -/// impl AsRef for str { -/// fn as_ref(&self) -> &Path { -/// Path::new(self) -/// } -/// } -/// ``` /// /// Both [`String`] and `&str` implement `AsRef`: /// @@ -156,15 +143,6 @@ pub trait AsRef { /// assert_eq!(*boxed_num, 1); /// ``` /// -/// `Vec` implements `AsMut` for converting between itself and a primitive array: -/// -/// ``` -/// impl AsMut<[T]> for Vec { -/// fn as_mut(&mut self) -> &mut [T] { -/// self -/// } -/// } -/// ``` /// #[stable(feature = "rust1", since = "1.0.0")] pub trait AsMut { @@ -275,11 +253,12 @@ pub trait Into: Sized { /// } /// } /// -/// fn open_and_parse_file(file_name: &str) -> Result { -/// let file = std::fs::File::open("test")?; +/// fn open_and_parse_file(file_name: &str) -> Result { +/// let mut file = std::fs::File::open("test")?; /// let mut contents = String::new(); /// file.read_to_string(&mut contents)?; /// let num: i32 = contents.trim().parse()?; +/// Ok(num) /// } /// ``` /// From 2877a01febe7a0667051463ed1bb431fe17c4d18 Mon Sep 17 00:00:00 2001 From: Dylan Maccora Date: Tue, 18 Apr 2017 08:29:05 +1000 Subject: [PATCH 07/33] Address review comments --- src/libcore/convert.rs | 43 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index abab4bb0f63b4..084736685e3a7 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -35,7 +35,7 @@ //! - [`From`]` for T` implies [`Into`]` for U` //! - [`TryFrom`]` for T` implies [`TryInto`]` for U` //! - [`From`] and [`Into`] are reflexive, which means that all types can -//! `into()` themselves and `from()` themselves +//! `into` themselves and `from` themselves //! //! See each trait for usage examples. //! @@ -59,14 +59,15 @@ use str::FromStr; /// `AsRef` is to be used when wishing to convert to a reference of another /// type. /// `Borrow` is more related to the notion of taking the reference. It is -/// useful when wishing to abstract -/// over the type of reference (`&T`, `&mut T`) or allow both the referenced -/// and owned type to be treated in the same manner. +/// useful when wishing to abstract over the type of reference +/// (`&T`, `&mut T`) or allow both the referenced and owned type to be treated +/// in the same manner. +/// /// The key difference between the two traits is the intention: /// /// - Use `AsRef` when goal is to simply convert into a reference /// - Use `Borrow` when goal is related to writing code that is agnostic to the -/// type of borrow and if is reference or value +/// type of borrow and if is reference or value /// /// See [the book][book] for a more detailed comparison. /// @@ -82,8 +83,8 @@ use str::FromStr; /// # Generic Implementations /// /// - `AsRef` auto-dereferences if the inner type is a reference or a mutable -/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type -/// `&mut Foo` or `&&mut Foo`) +/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type +/// `&mut Foo` or `&&mut Foo`) /// /// # Examples /// @@ -124,8 +125,8 @@ pub trait AsRef { /// # Generic Implementations /// /// - `AsMut` auto-dereferences if the inner type is a reference or a mutable -/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type -/// `&mut Foo` or `&&mut Foo`) +/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type +/// `&mut Foo` or `&&mut Foo`) /// /// # Examples /// @@ -203,9 +204,11 @@ pub trait Into: Sized { /// /// When constructing a function that is capable of failing the return type /// will generally be of the form `Result`. +/// /// The `From` trait allows for simplification of error handling by providing a /// means of returning a single error type that encapsulates numerous possible /// erroneous situations. +/// /// This trait is not limited to error handling, rather the general case for /// this trait would be in any type conversions to have an explicit definition /// of how they are performed. @@ -310,8 +313,7 @@ pub trait TryFrom: Sized { // As lifts over & #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a T - where T: AsRef +impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a T where T: AsRef { fn as_ref(&self) -> &U { >::as_ref(*self) @@ -320,8 +322,7 @@ impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a T // As lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a mut T - where T: AsRef +impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a mut T where T: AsRef { fn as_ref(&self) -> &U { >::as_ref(*self) @@ -338,8 +339,7 @@ impl<'a, T: ?Sized, U: ?Sized> AsRef for &'a mut T // AsMut lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: ?Sized, U: ?Sized> AsMut for &'a mut T - where T: AsMut +impl<'a, T: ?Sized, U: ?Sized> AsMut for &'a mut T where T: AsMut { fn as_mut(&mut self) -> &mut U { (*self).as_mut() @@ -356,8 +356,7 @@ impl<'a, T: ?Sized, U: ?Sized> AsMut for &'a mut T // From implies Into #[stable(feature = "rust1", since = "1.0.0")] -impl Into for T - where U: From +impl Into for T where U: From { fn into(self) -> U { U::from(self) @@ -367,16 +366,13 @@ impl Into for T // From (and thus Into) is reflexive #[stable(feature = "rust1", since = "1.0.0")] impl From for T { - fn from(t: T) -> T { - t - } + fn from(t: T) -> T { t } } // TryFrom implies TryInto #[unstable(feature = "try_from", issue = "33417")] -impl TryInto for T - where U: TryFrom +impl TryInto for T where U: TryFrom { type Error = U::Error; @@ -413,8 +409,7 @@ impl AsRef for str { // FromStr implies TryFrom<&str> #[unstable(feature = "try_from", issue = "33417")] -impl<'a, T> TryFrom<&'a str> for T - where T: FromStr +impl<'a, T> TryFrom<&'a str> for T where T: FromStr { type Error = ::Err; From 82ed7830ad5d8d30751ec608ef07c8d42792c878 Mon Sep 17 00:00:00 2001 From: Nicolas Bigaouette Date: Tue, 18 Apr 2017 14:00:08 -0400 Subject: [PATCH 08/33] Use an (over-writable) environment variable for the `gdb` command Instead of hard-coding the command to run, using the environment variable `GDB_CMD` (that defaults to `gdb`) allows using a different debugger than the default `gdb` executable. This gives the possibility to use `cgdb` as the debugger, which provides a nicer user interface. Note that one has to use `GDB_CMD="cgdb --"` to use cgdb (note the trailing `--`) to let cgdb pass the proper arguments to `gdb`. --- src/etc/rust-gdb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/etc/rust-gdb b/src/etc/rust-gdb index 520a108da914c..ef1689886cb88 100755 --- a/src/etc/rust-gdb +++ b/src/etc/rust-gdb @@ -17,7 +17,10 @@ RUSTC_SYSROOT=`rustc --print=sysroot` GDB_PYTHON_MODULE_DIRECTORY="$RUSTC_SYSROOT/lib/rustlib/etc" # Run GDB with the additional arguments that load the pretty printers -PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" gdb \ +# Set the environment variable `GDB_CMD` to overwrite the call to a +# different/specific command (defaults to `gdb`). +GDB_CMD="${GDB_CMD:-gdb}" +PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" ${GDB_CMD} \ -d "$GDB_PYTHON_MODULE_DIRECTORY" \ -iex "add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY" \ "$@" From a850cdcb7ec82cc5eaec76ee90b6ee1794a6da9b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 17 Apr 2017 18:06:43 +0200 Subject: [PATCH 09/33] Fix debug infinite loop --- src/libcollections/btree/set.rs | 12 ++++++++---- src/libcollections/enum_set.rs | 3 ++- src/libcollections/linked_list.rs | 11 ++++++----- src/libcollections/vec_deque.rs | 14 ++++++++++---- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/libcollections/btree/set.rs b/src/libcollections/btree/set.rs index ffca6964c5fdf..7b4079207d2ea 100644 --- a/src/libcollections/btree/set.rs +++ b/src/libcollections/btree/set.rs @@ -138,7 +138,8 @@ pub struct Difference<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for Difference<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("Difference") - .field(&self.clone()) + .field(&self.a) + .field(&self.b) .finish() } } @@ -160,7 +161,8 @@ pub struct SymmetricDifference<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for SymmetricDifference<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("SymmetricDifference") - .field(&self.clone()) + .field(&self.a) + .field(&self.b) .finish() } } @@ -182,7 +184,8 @@ pub struct Intersection<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for Intersection<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("Intersection") - .field(&self.clone()) + .field(&self.a) + .field(&self.b) .finish() } } @@ -204,7 +207,8 @@ pub struct Union<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for Union<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("Union") - .field(&self.clone()) + .field(&self.a) + .field(&self.b) .finish() } } diff --git a/src/libcollections/enum_set.rs b/src/libcollections/enum_set.rs index ebee75d1a1a64..aaee567bf1dbf 100644 --- a/src/libcollections/enum_set.rs +++ b/src/libcollections/enum_set.rs @@ -225,7 +225,8 @@ pub struct Iter { impl fmt::Debug for Iter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("Iter") - .field(&self.clone()) + .field(&self.index) + .field(&self.bits) .finish() } } diff --git a/src/libcollections/linked_list.rs b/src/libcollections/linked_list.rs index bfb03a5b23f1d..1cc5e10418f25 100644 --- a/src/libcollections/linked_list.rs +++ b/src/libcollections/linked_list.rs @@ -75,7 +75,7 @@ pub struct Iter<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("Iter") - .field(&self.clone()) + .field(&self.len) .finish() } } @@ -107,7 +107,8 @@ pub struct IterMut<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for IterMut<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("IterMut") - .field(self.clone()) + .field(&self.list) + .field(&self.len) .finish() } } @@ -129,7 +130,7 @@ pub struct IntoIter { impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("IntoIter") - .field(self.clone()) + .field(&self.list) .finish() } } @@ -1128,7 +1129,7 @@ pub struct FrontPlace<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for FrontPlace<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("FrontPlace") - .field(self.clone()) + .field(&self.list) .finish() } } @@ -1183,7 +1184,7 @@ pub struct BackPlace<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for BackPlace<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("BackPlace") - .field(self.clone()) + .field(&self.list) .finish() } } diff --git a/src/libcollections/vec_deque.rs b/src/libcollections/vec_deque.rs index 2ce3b92843bd7..f0e61ebc47bfc 100644 --- a/src/libcollections/vec_deque.rs +++ b/src/libcollections/vec_deque.rs @@ -1913,7 +1913,9 @@ pub struct Iter<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("Iter") - .field(&self.clone()) + .field(&self.ring) + .field(&self.tail) + .field(&self.head) .finish() } } @@ -2000,7 +2002,9 @@ pub struct IterMut<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for IterMut<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("IterMut") - .field(&self.clone()) + .field(&self.ring) + .field(&self.tail) + .field(&self.head) .finish() } } @@ -2081,7 +2085,7 @@ pub struct IntoIter { impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("IntoIter") - .field(&self.clone()) + .field(&self.inner) .finish() } } @@ -2139,7 +2143,9 @@ pub struct Drain<'a, T: 'a> { impl<'a, T: 'a + fmt::Debug> fmt::Debug for Drain<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("Drain") - .field(&self.clone()) + .field(&self.after_tail) + .field(&self.after_head) + .field(&self.iter) .finish() } } From df383b95476d59c26e822bcda69d3255da61622c Mon Sep 17 00:00:00 2001 From: Michael Gattozzi Date: Wed, 19 Apr 2017 14:00:50 -0400 Subject: [PATCH 10/33] Fix link for wait --- src/libstd/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/process.rs b/src/libstd/process.rs index edcf206501a08..9d7f87f06c31c 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -78,7 +78,7 @@ use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; /// run, even after the `Child` handle to the child process has gone out of /// scope. /// -/// Calling [`wait`] (or other functions that wrap around it) will make +/// Calling [`wait`](#method.wait) (or other functions that wrap around it) will make /// the parent process wait until the child has actually exited before /// continuing. /// From f85a5337ab0f8b492cb8df56a7c2af103010037e Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 19 Apr 2017 11:57:29 -0700 Subject: [PATCH 11/33] specialize Extend for Vec with IntoIter --- src/libcollections/tests/vec.rs | 22 ++++++++++++++++++++++ src/libcollections/vec.rs | 27 +++++++++++++++++++-------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/libcollections/tests/vec.rs b/src/libcollections/tests/vec.rs index 63df0eb730509..64c76142b59d6 100644 --- a/src/libcollections/tests/vec.rs +++ b/src/libcollections/tests/vec.rs @@ -84,6 +84,9 @@ fn test_extend() { let mut v = Vec::new(); let mut w = Vec::new(); + v.extend(w.clone()); + assert_eq!(v, &[]); + v.extend(0..3); for i in 0..3 { w.push(i) @@ -100,6 +103,25 @@ fn test_extend() { v.extend(w.clone()); // specializes to `append` assert!(v.iter().eq(w.iter().chain(w.iter()))); + + // Zero sized types + #[derive(PartialEq, Debug)] + struct Foo; + + let mut a = Vec::new(); + let b = vec![Foo, Foo]; + + a.extend(b); + assert_eq!(a, &[Foo, Foo]); + + // Double drop + let mut count_x = 0; + { + let mut x = Vec::new(); + let y = vec![DropCounter { count: &mut count_x }]; + x.extend(y); + } + assert_eq!(count_x, 1); } #[test] diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 35ecf411db4e0..bf54359226212 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1039,18 +1039,22 @@ impl Vec { #[inline] #[stable(feature = "append", since = "1.4.0")] pub fn append(&mut self, other: &mut Self) { - self.reserve(other.len()); - let len = self.len(); - unsafe { - ptr::copy_nonoverlapping(other.as_ptr(), self.get_unchecked_mut(len), other.len()); - } - - self.len += other.len(); unsafe { + self.append_elements(other.as_slice() as _); other.set_len(0); } } + /// Appends elements to `Self` from other buffer. + #[inline] + unsafe fn append_elements(&mut self, other: *const [T]) { + let count = (*other).len(); + self.reserve(count); + let len = self.len(); + ptr::copy_nonoverlapping(other as *const T, self.get_unchecked_mut(len), count); + self.len += count; + } + /// Create a draining iterator that removes the specified range in the vector /// and yields the removed items. /// @@ -1681,7 +1685,7 @@ impl SpecExtend for Vec vector } - fn spec_extend(&mut self, iterator: I) { + default fn spec_extend(&mut self, iterator: I) { // This is the case for a TrustedLen iterator. let (low, high) = iterator.size_hint(); if let Some(high_value) = high { @@ -1726,6 +1730,13 @@ impl SpecExtend> for Vec { vector } } + + fn spec_extend(&mut self, mut iterator: IntoIter) { + unsafe { + self.append_elements(iterator.as_slice() as _); + } + iterator.ptr = iterator.end; + } } impl<'a, T: 'a, I> SpecExtend<&'a T, I> for Vec From 3f5c311dc1dcb5bfe65d6eecd0dfda04d77c7594 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 17 Apr 2017 18:58:01 +0300 Subject: [PATCH 12/33] rustc: simplify TypeContents drastically. --- src/librustc/ty/contents.rs | 188 ++++++++---------------------------- src/librustc/ty/mod.rs | 2 +- 2 files changed, 42 insertions(+), 148 deletions(-) diff --git a/src/librustc/ty/contents.rs b/src/librustc/ty/contents.rs index e14295982916f..c690795b3421d 100644 --- a/src/librustc/ty/contents.rs +++ b/src/librustc/ty/contents.rs @@ -8,118 +8,45 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use hir::def_id::{DefId}; use ty::{self, Ty, TyCtxt}; use util::common::MemoizationMap; use util::nodemap::FxHashMap; -use std::fmt; -use std::ops; - -use syntax::ast; - -/// Type contents is how the type checker reasons about kinds. -/// They track what kinds of things are found within a type. You can -/// think of them as kind of an "anti-kind". They track the kinds of values -/// and thinks that are contained in types. Having a larger contents for -/// a type tends to rule that type *out* from various kinds. For example, -/// a type that contains a reference is not sendable. -/// -/// The reason we compute type contents and not kinds is that it is -/// easier for me (nmatsakis) to think about what is contained within -/// a type than to think about what is *not* contained within a type. -#[derive(Clone, Copy)] -pub struct TypeContents { - pub bits: u64 -} - -macro_rules! def_type_content_sets { - (mod $mname:ident { $($name:ident = $bits:expr),+ }) => { - #[allow(non_snake_case)] - mod $mname { - use super::TypeContents; - $( - #[allow(non_upper_case_globals)] - pub const $name: TypeContents = TypeContents { bits: $bits }; - )+ - } - } -} - -def_type_content_sets! { - mod TC { - None = 0b0000_0000__0000_0000__0000, - - // Things that are interior to the value (first nibble): - InteriorUnsafe = 0b0000_0000__0000_0000__0010, - InteriorParam = 0b0000_0000__0000_0000__0100, - // InteriorAll = 0b00000000__00000000__1111, - - // Things that are owned by the value (second and third nibbles): - OwnsDtor = 0b0000_0000__0000_0010__0000, - // OwnsAll = 0b0000_0000__1111_1111__0000, - - // All bits - All = 0b1111_1111__1111_1111__1111 +bitflags! { + /// Type contents is how the type checker reasons about kinds. + /// They track what kinds of things are found within a type. You can + /// think of them as kind of an "anti-kind". They track the kinds of values + /// and thinks that are contained in types. Having a larger contents for + /// a type tends to rule that type *out* from various kinds. For example, + /// a type that contains a reference is not sendable. + /// + /// The reason we compute type contents and not kinds is that it is + /// easier for me (nmatsakis) to think about what is contained within + /// a type than to think about what is *not* contained within a type. + flags TypeContents: u8 { + const INTERIOR_UNSAFE = 0b01, + const OWNS_DTOR = 0b10, } } impl TypeContents { pub fn when(&self, cond: bool) -> TypeContents { - if cond {*self} else {TC::None} - } - - pub fn intersects(&self, tc: TypeContents) -> bool { - (self.bits & tc.bits) != 0 - } - - pub fn interior_param(&self) -> bool { - self.intersects(TC::InteriorParam) + if cond {*self} else {TypeContents::empty()} } pub fn interior_unsafe(&self) -> bool { - self.intersects(TC::InteriorUnsafe) + self.intersects(TypeContents::INTERIOR_UNSAFE) } pub fn needs_drop(&self, _: TyCtxt) -> bool { - self.intersects(TC::OwnsDtor) + self.intersects(TypeContents::OWNS_DTOR) } pub fn union(v: I, mut f: F) -> TypeContents where I: IntoIterator, F: FnMut(T) -> TypeContents, { - v.into_iter().fold(TC::None, |tc, ty| tc | f(ty)) - } -} - -impl ops::BitOr for TypeContents { - type Output = TypeContents; - - fn bitor(self, other: TypeContents) -> TypeContents { - TypeContents {bits: self.bits | other.bits} - } -} - -impl ops::BitAnd for TypeContents { - type Output = TypeContents; - - fn bitand(self, other: TypeContents) -> TypeContents { - TypeContents {bits: self.bits & other.bits} - } -} - -impl ops::Sub for TypeContents { - type Output = TypeContents; - - fn sub(self, other: TypeContents) -> TypeContents { - TypeContents {bits: self.bits & !other.bits} - } -} - -impl fmt::Debug for TypeContents { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TypeContents({:b})", self.bits) + v.into_iter().fold(TypeContents::empty(), |tc, ty| tc | f(ty)) } } @@ -139,19 +66,19 @@ impl<'a, 'tcx> ty::TyS<'tcx> { // // When computing the type contents of such a type, we wind up deeply // recursing as we go. So when we encounter the recursive reference - // to List, we temporarily use TC::None as its contents. Later we'll + // to List, we temporarily use TypeContents::empty() as its contents. Later we'll // patch up the cache with the correct value, once we've computed it // (this is basically a co-inductive process, if that helps). So in - // the end we'll compute TC::OwnsOwned, in this case. + // the end we'll compute TypeContents::OwnsOwned, in this case. // // The problem is, as we are doing the computation, we will also // compute an *intermediate* contents for, e.g., Option of - // TC::None. This is ok during the computation of List itself, but if + // TypeContents::empty(). This is ok during the computation of List itself, but if // we stored this intermediate value into tcx.tc_cache, then later - // requests for the contents of Option would also yield TC::None + // requests for the contents of Option would also yield TypeContents::empty() // which is incorrect. This value was computed based on the crutch // value for the type contents of list. The correct value is - // TC::OwnsOwned. This manifested as issue #4821. + // TypeContents::OwnsOwned. This manifested as issue #4821. if let Some(tc) = cache.get(&ty) { return *tc; } @@ -159,32 +86,14 @@ impl<'a, 'tcx> ty::TyS<'tcx> { if let Some(tc) = tcx.tc_cache.borrow().get(&ty) { return *tc; } - cache.insert(ty, TC::None); + cache.insert(ty, TypeContents::empty()); let result = match ty.sty { - // usize and isize are ffi-unsafe - ty::TyUint(ast::UintTy::Us) | ty::TyInt(ast::IntTy::Is) => { - TC::None - } - - // Scalar and unique types are sendable, and durable ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) | ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyNever | - ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar => { - TC::None - } - - ty::TyDynamic(..) => { - TC::All - TC::InteriorParam - } - - ty::TyRawPtr(_) => { - TC::None - } - - ty::TyRef(..) => { - TC::None - } + ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | + ty::TyRawPtr(_) | ty::TyRef(..) | + ty::TyStr => TypeContents::empty(), ty::TyArray(ty, _) => { tc_ty(tcx, ty, cache) @@ -193,7 +102,6 @@ impl<'a, 'tcx> ty::TyS<'tcx> { ty::TySlice(ty) => { tc_ty(tcx, ty, cache) } - ty::TyStr => TC::None, ty::TyClosure(def_id, ref substs) => { TypeContents::union( @@ -207,29 +115,25 @@ impl<'a, 'tcx> ty::TyS<'tcx> { } ty::TyAdt(def, substs) => { - let mut res = - TypeContents::union(&def.variants, |v| { - TypeContents::union(&v.fields, |f| { - tc_ty(tcx, f.ty(tcx, substs), cache) - }) - }); - - if def.is_union() { - // unions don't have destructors regardless of the child types - res = res - TC::OwnsDtor; - } - - if def.has_dtor(tcx) { - res = res | TC::OwnsDtor; - } - - apply_lang_items(tcx, def.did, res) + TypeContents::union(&def.variants, |v| { + TypeContents::union(&v.fields, |f| { + tc_ty(tcx, f.ty(tcx, substs), cache) + }) + }) + + // unions don't have destructors regardless of the child types + - TypeContents::OWNS_DTOR.when(def.is_union()) + | TypeContents::OWNS_DTOR.when(def.has_dtor(tcx)) + | TypeContents::INTERIOR_UNSAFE.when( + Some(def.did) == tcx.lang_items.unsafe_cell_type()) } + + ty::TyDynamic(..) | ty::TyProjection(..) | ty::TyParam(_) | ty::TyAnon(..) => { - TC::All + TypeContents::INTERIOR_UNSAFE | TypeContents::OWNS_DTOR } ty::TyInfer(_) | @@ -241,15 +145,5 @@ impl<'a, 'tcx> ty::TyS<'tcx> { cache.insert(ty, result); result } - - fn apply_lang_items<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - did: DefId, tc: TypeContents) - -> TypeContents { - if Some(did) == tcx.lang_items.unsafe_cell_type() { - tc | TC::InteriorUnsafe - } else { - tc - } - } } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index ab1a06aeacd18..78d3885bb988a 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2405,7 +2405,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // destructor (e.g. zero its memory on move). let contents = ty.type_contents(tcx); - debug!("type_needs_drop ty={:?} contents={:?}", ty, contents); + debug!("type_needs_drop ty={:?} contents={:?}", ty, contents.bits()); contents.needs_drop(tcx) } From 6563374ed2e518ea5fec2b7411dc4331772c46d2 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 17 Apr 2017 21:18:56 +0300 Subject: [PATCH 13/33] rustc: replace interior_unsafe with a Freeze trait. --- src/libcore/marker.rs | 17 ++++++++ src/librustc/middle/lang_items.rs | 1 + src/librustc/ty/contents.rs | 14 +------ src/librustc/ty/mod.rs | 8 ++++ src/librustc/ty/util.rs | 44 ++++++++++++++++++++ src/librustc_mir/transform/qualify_consts.rs | 2 +- src/librustc_passes/consts.rs | 4 +- src/librustc_trans/abi.rs | 6 +-- src/librustc_trans/consts.rs | 3 +- src/librustc_trans/context.rs | 4 ++ 10 files changed, 83 insertions(+), 20 deletions(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 393c01b0105c5..c0aa650a1e854 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -16,6 +16,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use cell::UnsafeCell; use cmp; use hash::Hash; use hash::Hasher; @@ -553,3 +554,19 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<'a, T: Send + ?Sized> Send for &'a mut T {} } + +/// Compiler-internal trait used to determine whether a type contains +/// any `UnsafeCell` internally, but not through an indirection. +/// This affects, for example, whether a `static` of that type is +/// placed in read-only static memory or writable static memory. +#[cfg_attr(not(stage0), lang = "freeze")] +unsafe trait Freeze {} + +unsafe impl Freeze for .. {} + +impl !Freeze for UnsafeCell {} +unsafe impl Freeze for PhantomData {} +unsafe impl Freeze for *const T {} +unsafe impl Freeze for *mut T {} +unsafe impl<'a, T: ?Sized> Freeze for &'a T {} +unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 5989fa9007c44..32dfb63d6150a 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -274,6 +274,7 @@ language_item_table! { UnsizeTraitLangItem, "unsize", unsize_trait; CopyTraitLangItem, "copy", copy_trait; SyncTraitLangItem, "sync", sync_trait; + FreezeTraitLangItem, "freeze", freeze_trait; DropTraitLangItem, "drop", drop_trait; diff --git a/src/librustc/ty/contents.rs b/src/librustc/ty/contents.rs index c690795b3421d..a1cb848213a8f 100644 --- a/src/librustc/ty/contents.rs +++ b/src/librustc/ty/contents.rs @@ -24,8 +24,7 @@ bitflags! { /// easier for me (nmatsakis) to think about what is contained within /// a type than to think about what is *not* contained within a type. flags TypeContents: u8 { - const INTERIOR_UNSAFE = 0b01, - const OWNS_DTOR = 0b10, + const OWNS_DTOR = 0b1, } } @@ -34,10 +33,6 @@ impl TypeContents { if cond {*self} else {TypeContents::empty()} } - pub fn interior_unsafe(&self) -> bool { - self.intersects(TypeContents::INTERIOR_UNSAFE) - } - pub fn needs_drop(&self, _: TyCtxt) -> bool { self.intersects(TypeContents::OWNS_DTOR) } @@ -124,17 +119,12 @@ impl<'a, 'tcx> ty::TyS<'tcx> { // unions don't have destructors regardless of the child types - TypeContents::OWNS_DTOR.when(def.is_union()) | TypeContents::OWNS_DTOR.when(def.has_dtor(tcx)) - | TypeContents::INTERIOR_UNSAFE.when( - Some(def.did) == tcx.lang_items.unsafe_cell_type()) } - ty::TyDynamic(..) | ty::TyProjection(..) | ty::TyParam(_) | - ty::TyAnon(..) => { - TypeContents::INTERIOR_UNSAFE | TypeContents::OWNS_DTOR - } + ty::TyAnon(..) => TypeContents::OWNS_DTOR, ty::TyInfer(_) | ty::TyError => { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 78d3885bb988a..7325235ca7b76 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -425,6 +425,8 @@ bitflags! { const IS_SIZED = 1 << 17, const MOVENESS_CACHED = 1 << 18, const MOVES_BY_DEFAULT = 1 << 19, + const FREEZENESS_CACHED = 1 << 20, + const IS_FREEZE = 1 << 21, } } @@ -1181,6 +1183,9 @@ pub struct ParameterEnvironment<'tcx> { /// A cache for `type_is_sized` pub is_sized_cache: RefCell, bool>>, + + /// A cache for `type_is_freeze` + pub is_freeze_cache: RefCell, bool>>, } impl<'a, 'tcx> ParameterEnvironment<'tcx> { @@ -1195,6 +1200,7 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> { free_id_outlive: self.free_id_outlive, is_copy_cache: RefCell::new(FxHashMap()), is_sized_cache: RefCell::new(FxHashMap()), + is_freeze_cache: RefCell::new(FxHashMap()), } } @@ -2531,6 +2537,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { free_id_outlive: free_id_outlive, is_copy_cache: RefCell::new(FxHashMap()), is_sized_cache: RefCell::new(FxHashMap()), + is_freeze_cache: RefCell::new(FxHashMap()), } } @@ -2603,6 +2610,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { free_id_outlive: free_id_outlive, is_copy_cache: RefCell::new(FxHashMap()), is_sized_cache: RefCell::new(FxHashMap()), + is_freeze_cache: RefCell::new(FxHashMap()), }; let cause = traits::ObligationCause::misc(span, free_id_outlive.node_id(&self.region_maps)); diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 5334ee2835db2..d43d570397b44 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -655,6 +655,50 @@ impl<'a, 'tcx> ty::TyS<'tcx> { result } + /// Returns `true` if and only if there are no `UnsafeCell`s + /// nested within the type (ignoring `PhantomData` or pointers). + #[inline] + pub fn is_freeze(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ParameterEnvironment<'tcx>, + span: Span) -> bool + { + if self.flags.get().intersects(TypeFlags::FREEZENESS_CACHED) { + return self.flags.get().intersects(TypeFlags::IS_FREEZE); + } + + self.is_freeze_uncached(tcx, param_env, span) + } + + fn is_freeze_uncached(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ParameterEnvironment<'tcx>, + span: Span) -> bool { + assert!(!self.needs_infer()); + + // Fast-path for primitive types + let result = match self.sty { + TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) | + TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) | + TyStr | TyNever => Some(true), + + TyArray(..) | TySlice(_) | + TyTuple(..) | TyClosure(..) | TyAdt(..) | + TyDynamic(..) | TyProjection(..) | TyParam(..) | + TyInfer(..) | TyAnon(..) | TyError => None + }.unwrap_or_else(|| { + self.impls_bound(tcx, param_env, tcx.require_lang_item(lang_items::FreezeTraitLangItem), + ¶m_env.is_freeze_cache, span) }); + + if !self.has_param_types() && !self.has_self_ty() { + self.flags.set(self.flags.get() | if result { + TypeFlags::FREEZENESS_CACHED | TypeFlags::IS_FREEZE + } else { + TypeFlags::FREEZENESS_CACHED + }); + } + + result + } + #[inline] pub fn layout<'lcx>(&'tcx self, infcx: &InferCtxt<'a, 'tcx, 'lcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 1313b24fa74f5..cc7d25628432e 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -80,7 +80,7 @@ impl<'a, 'tcx> Qualif { fn restrict(&mut self, ty: Ty<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: &ty::ParameterEnvironment<'tcx>) { - if !ty.type_contents(tcx).interior_unsafe() { + if ty.is_freeze(tcx, param_env, DUMMY_SP) { *self = *self - Qualif::MUTABLE_INTERIOR; } if !tcx.type_needs_drop_given_env(ty, param_env) { diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 2c4439f80a239..535c6a7ab9e12 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -46,7 +46,7 @@ use rustc::lint::builtin::CONST_ERR; use rustc::hir::{self, PatKind, RangeEnd}; use syntax::ast; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use std::collections::hash_map::Entry; @@ -85,7 +85,7 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { // Adds the worst effect out of all the values of one type. fn add_type(&mut self, ty: Ty<'gcx>) { - if ty.type_contents(self.tcx).interior_unsafe() { + if !ty.is_freeze(self.tcx, &self.param_env, DUMMY_SP) { self.promotable = false; } diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index c4fdc46d030c9..e0a75f3caa7b3 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -746,13 +746,13 @@ impl<'a, 'tcx> FnType<'tcx> { // `&T` where `T` contains no `UnsafeCell` is immutable, and can be marked as // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely // on memory dependencies rather than pointer equality - let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe(); + let is_freeze = ccx.shared().type_is_freeze(mt.ty); - if mt.mutbl != hir::MutMutable && !interior_unsafe { + if mt.mutbl != hir::MutMutable && is_freeze { arg.attrs.set(ArgAttribute::NoAlias); } - if mt.mutbl == hir::MutImmutable && !interior_unsafe { + if mt.mutbl == hir::MutImmutable && is_freeze { arg.attrs.set(ArgAttribute::ReadOnly); } diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index 7a53a03344fcb..eb3ac309be16d 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -261,8 +261,7 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // As an optimization, all shared statics which do not have interior // mutability are placed into read-only memory. if m != hir::MutMutable { - let tcontents = ty.type_contents(ccx.tcx()); - if !tcontents.interior_unsafe() { + if ccx.shared().type_is_freeze(ty) { llvm::LLVMSetGlobalConstant(g, llvm::True); } } diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index c3770470bfd05..fd9ff17cc6489 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -399,6 +399,10 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { ty.is_sized(self.tcx, &self.empty_param_env, DUMMY_SP) } + pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { + ty.is_freeze(self.tcx, &self.empty_param_env, DUMMY_SP) + } + pub fn exported_symbols<'a>(&'a self) -> &'a NodeSet { &self.exported_symbols } From 9ad3b94847db8cbee27947d7d62a6add28a9c0e9 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 17 Apr 2017 23:26:48 +0300 Subject: [PATCH 14/33] rustc: replace TypeContents::needs_drop with Ty::may_drop. --- src/librustc/ty/contents.rs | 139 ------------------------------------ src/librustc/ty/context.rs | 4 -- src/librustc/ty/mod.rs | 19 +++-- src/librustc/ty/util.rs | 79 +++++++++++++++++++- 4 files changed, 87 insertions(+), 154 deletions(-) delete mode 100644 src/librustc/ty/contents.rs diff --git a/src/librustc/ty/contents.rs b/src/librustc/ty/contents.rs deleted file mode 100644 index a1cb848213a8f..0000000000000 --- a/src/librustc/ty/contents.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use ty::{self, Ty, TyCtxt}; -use util::common::MemoizationMap; -use util::nodemap::FxHashMap; - -bitflags! { - /// Type contents is how the type checker reasons about kinds. - /// They track what kinds of things are found within a type. You can - /// think of them as kind of an "anti-kind". They track the kinds of values - /// and thinks that are contained in types. Having a larger contents for - /// a type tends to rule that type *out* from various kinds. For example, - /// a type that contains a reference is not sendable. - /// - /// The reason we compute type contents and not kinds is that it is - /// easier for me (nmatsakis) to think about what is contained within - /// a type than to think about what is *not* contained within a type. - flags TypeContents: u8 { - const OWNS_DTOR = 0b1, - } -} - -impl TypeContents { - pub fn when(&self, cond: bool) -> TypeContents { - if cond {*self} else {TypeContents::empty()} - } - - pub fn needs_drop(&self, _: TyCtxt) -> bool { - self.intersects(TypeContents::OWNS_DTOR) - } - - pub fn union(v: I, mut f: F) -> TypeContents where - I: IntoIterator, - F: FnMut(T) -> TypeContents, - { - v.into_iter().fold(TypeContents::empty(), |tc, ty| tc | f(ty)) - } -} - -impl<'a, 'tcx> ty::TyS<'tcx> { - pub fn type_contents(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> TypeContents { - return tcx.tc_cache.memoize(self, || tc_ty(tcx, self, &mut FxHashMap())); - - fn tc_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, - cache: &mut FxHashMap, TypeContents>) -> TypeContents - { - // Subtle: Note that we are *not* using tcx.tc_cache here but rather a - // private cache for this walk. This is needed in the case of cyclic - // types like: - // - // struct List { next: Box>, ... } - // - // When computing the type contents of such a type, we wind up deeply - // recursing as we go. So when we encounter the recursive reference - // to List, we temporarily use TypeContents::empty() as its contents. Later we'll - // patch up the cache with the correct value, once we've computed it - // (this is basically a co-inductive process, if that helps). So in - // the end we'll compute TypeContents::OwnsOwned, in this case. - // - // The problem is, as we are doing the computation, we will also - // compute an *intermediate* contents for, e.g., Option of - // TypeContents::empty(). This is ok during the computation of List itself, but if - // we stored this intermediate value into tcx.tc_cache, then later - // requests for the contents of Option would also yield TypeContents::empty() - // which is incorrect. This value was computed based on the crutch - // value for the type contents of list. The correct value is - // TypeContents::OwnsOwned. This manifested as issue #4821. - if let Some(tc) = cache.get(&ty) { - return *tc; - } - // Must check both caches! - if let Some(tc) = tcx.tc_cache.borrow().get(&ty) { - return *tc; - } - cache.insert(ty, TypeContents::empty()); - - let result = match ty.sty { - ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) | - ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyNever | - ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | - ty::TyRawPtr(_) | ty::TyRef(..) | - ty::TyStr => TypeContents::empty(), - - ty::TyArray(ty, _) => { - tc_ty(tcx, ty, cache) - } - - ty::TySlice(ty) => { - tc_ty(tcx, ty, cache) - } - - ty::TyClosure(def_id, ref substs) => { - TypeContents::union( - substs.upvar_tys(def_id, tcx), - |ty| tc_ty(tcx, &ty, cache)) - } - - ty::TyTuple(ref tys, _) => { - TypeContents::union(&tys[..], - |ty| tc_ty(tcx, *ty, cache)) - } - - ty::TyAdt(def, substs) => { - TypeContents::union(&def.variants, |v| { - TypeContents::union(&v.fields, |f| { - tc_ty(tcx, f.ty(tcx, substs), cache) - }) - }) - - // unions don't have destructors regardless of the child types - - TypeContents::OWNS_DTOR.when(def.is_union()) - | TypeContents::OWNS_DTOR.when(def.has_dtor(tcx)) - } - - ty::TyDynamic(..) | - ty::TyProjection(..) | - ty::TyParam(_) | - ty::TyAnon(..) => TypeContents::OWNS_DTOR, - - ty::TyInfer(_) | - ty::TyError => { - bug!("asked to compute contents of error type"); - } - }; - - cache.insert(ty, result); - result - } - } -} diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 8b7438c0bfad2..a41629258716d 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -436,9 +436,6 @@ pub struct GlobalCtxt<'tcx> { // Internal cache for metadata decoding. No need to track deps on this. pub rcache: RefCell>>, - // Cache for the type-contents routine. FIXME -- track deps? - pub tc_cache: RefCell, ty::contents::TypeContents>>, - // FIXME dep tracking -- should be harmless enough pub normalized_cache: RefCell, Ty<'tcx>>>, @@ -708,7 +705,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { freevars: RefCell::new(resolutions.freevars), maybe_unused_trait_imports: resolutions.maybe_unused_trait_imports, rcache: RefCell::new(FxHashMap()), - tc_cache: RefCell::new(FxHashMap()), normalized_cache: RefCell::new(FxHashMap()), inhabitedness_cache: RefCell::new(FxHashMap()), lang_items: lang_items, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 7325235ca7b76..b6e47568ff4db 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -71,7 +71,6 @@ pub use self::sty::InferTy::*; pub use self::sty::Region::*; pub use self::sty::TypeVariants::*; -pub use self::contents::TypeContents; pub use self::context::{TyCtxt, GlobalArenas, tls}; pub use self::context::{Lift, TypeckTables}; @@ -99,7 +98,6 @@ pub mod walk; pub mod wf; pub mod util; -mod contents; mod context; mod flags; mod instance; @@ -427,6 +425,8 @@ bitflags! { const MOVES_BY_DEFAULT = 1 << 19, const FREEZENESS_CACHED = 1 << 20, const IS_FREEZE = 1 << 21, + const MAY_DROP_CACHED = 1 << 22, + const MAY_DROP = 1 << 23, } } @@ -2400,19 +2400,18 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { if implements_copy { return false; } // ... (issue #22536 continued) but as an optimization, still use - // prior logic of asking if the `needs_drop` bit is set; we need - // not zero non-Copy types if they have no destructor. + // prior logic of asking for the structural `may_drop`. - // FIXME(#22815): Note that calling `ty::type_contents` is a - // conservative heuristic; it may report that `needs_drop` is set + // FIXME(#22815): Note that calling `ty::may_drop` is a + // conservative heuristic; it may report `true` ("may drop") // when actual type does not actually have a destructor associated // with it. But since `ty` absolutely did not have the `Copy` // bound attached (see above), it is sound to treat it as having a - // destructor (e.g. zero its memory on move). + // destructor. - let contents = ty.type_contents(tcx); - debug!("type_needs_drop ty={:?} contents={:?}", ty, contents.bits()); - contents.needs_drop(tcx) + let may_drop = ty.may_drop(tcx); + debug!("type_needs_drop ty={:?} may_drop={:?}", ty, may_drop); + may_drop } /// Get the attributes of a definition. diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index d43d570397b44..a3d2518909d74 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -21,7 +21,7 @@ use ty::fold::TypeVisitor; use ty::layout::{Layout, LayoutError}; use ty::TypeVariants::*; use util::common::ErrorReported; -use util::nodemap::FxHashMap; +use util::nodemap::{FxHashMap, FxHashSet}; use middle::lang_items; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; @@ -699,6 +699,83 @@ impl<'a, 'tcx> ty::TyS<'tcx> { result } + #[inline] + pub fn may_drop(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool { + if self.flags.get().intersects(TypeFlags::MAY_DROP_CACHED) { + return self.flags.get().intersects(TypeFlags::MAY_DROP); + } + + self.may_drop_inner(tcx, &mut FxHashSet()) + } + + fn may_drop_inner(&'tcx self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + visited: &mut FxHashSet>) + -> bool { + if self.flags.get().intersects(TypeFlags::MAY_DROP_CACHED) { + return self.flags.get().intersects(TypeFlags::MAY_DROP); + } + + // This should be reported as an error by `check_representable`. + // + // Consider the type as not needing drop in the meanwhile to avoid + // further errors. + if visited.replace(self).is_some() { + return false; + } + + assert!(!self.needs_infer()); + + let result = match self.sty { + // Fast-path for primitive types + ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) | + ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyNever | + ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | + ty::TyRawPtr(_) | ty::TyRef(..) | ty::TyStr => false, + + // User destructors are the only way to have concrete drop types. + ty::TyAdt(def, _) if def.has_dtor(tcx) => true, + + // Can refer to a type which may drop. + // FIXME(eddyb) check this against a ParameterEnvironment. + ty::TyDynamic(..) | ty::TyProjection(..) | ty::TyParam(_) | + ty::TyAnon(..) | ty::TyInfer(_) | ty::TyError => true, + + // Structural recursion. + ty::TyArray(ty, _) | ty::TySlice(ty) => { + ty.may_drop_inner(tcx, visited) + } + + ty::TyClosure(def_id, ref substs) => { + substs.upvar_tys(def_id, tcx) + .any(|ty| ty.may_drop_inner(tcx, visited)) + } + + ty::TyTuple(ref tys, _) => { + tys.iter().any(|ty| ty.may_drop_inner(tcx, visited)) + } + + // unions don't have destructors regardless of the child types + ty::TyAdt(def, _) if def.is_union() => false, + + ty::TyAdt(def, substs) => { + def.variants.iter().any(|v| { + v.fields.iter().any(|f| { + f.ty(tcx, substs).may_drop_inner(tcx, visited) + }) + }) + } + }; + + self.flags.set(self.flags.get() | if result { + TypeFlags::MAY_DROP_CACHED | TypeFlags::MAY_DROP + } else { + TypeFlags::MAY_DROP_CACHED + }); + + result + } + #[inline] pub fn layout<'lcx>(&'tcx self, infcx: &InferCtxt<'a, 'tcx, 'lcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { From 0adfd810f8aa16ce9e63f6feae182db0ba4833ef Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 18 Apr 2017 00:22:55 +0300 Subject: [PATCH 15/33] rustc: combine type_needs_drop_given_env and may_drop into needs_drop. --- src/librustc/ty/mod.rs | 37 +--------- src/librustc/ty/util.rs | 78 +++++++++++++++----- src/librustc_borrowck/borrowck/mir/mod.rs | 2 +- src/librustc_lint/builtin.rs | 2 +- src/librustc_mir/hair/cx/mod.rs | 2 +- src/librustc_mir/transform/inline.rs | 2 +- src/librustc_mir/transform/qualify_consts.rs | 2 +- src/librustc_mir/util/elaborate_drops.rs | 3 +- src/librustc_passes/consts.rs | 2 +- src/librustc_trans/context.rs | 2 +- 10 files changed, 68 insertions(+), 64 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index b6e47568ff4db..5c0889976c21a 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -425,8 +425,8 @@ bitflags! { const MOVES_BY_DEFAULT = 1 << 19, const FREEZENESS_CACHED = 1 << 20, const IS_FREEZE = 1 << 21, - const MAY_DROP_CACHED = 1 << 22, - const MAY_DROP = 1 << 23, + const NEEDS_DROP_CACHED = 1 << 22, + const NEEDS_DROP = 1 << 23, } } @@ -2381,39 +2381,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Some(self.item_mir(did)) } - /// If `type_needs_drop` returns true, then `ty` is definitely - /// non-copy and *might* have a destructor attached; if it returns - /// false, then `ty` definitely has no destructor (i.e. no drop glue). - /// - /// (Note that this implies that if `ty` has a destructor attached, - /// then `type_needs_drop` will definitely return `true` for `ty`.) - pub fn type_needs_drop_given_env(self, - ty: Ty<'gcx>, - param_env: &ty::ParameterEnvironment<'gcx>) -> bool { - // Issue #22536: We first query type_moves_by_default. It sees a - // normalized version of the type, and therefore will definitely - // know whether the type implements Copy (and thus needs no - // cleanup/drop/zeroing) ... - let tcx = self.global_tcx(); - let implements_copy = !ty.moves_by_default(tcx, param_env, DUMMY_SP); - - if implements_copy { return false; } - - // ... (issue #22536 continued) but as an optimization, still use - // prior logic of asking for the structural `may_drop`. - - // FIXME(#22815): Note that calling `ty::may_drop` is a - // conservative heuristic; it may report `true` ("may drop") - // when actual type does not actually have a destructor associated - // with it. But since `ty` absolutely did not have the `Copy` - // bound attached (see above), it is sound to treat it as having a - // destructor. - - let may_drop = ty.may_drop(tcx); - debug!("type_needs_drop ty={:?} may_drop={:?}", ty, may_drop); - may_drop - } - /// Get the attributes of a definition. pub fn get_attrs(self, did: DefId) -> Cow<'gcx, [ast::Attribute]> { if let Some(id) = self.hir.as_local_node_id(did) { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index a3d2518909d74..49d79f6545e2d 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -699,31 +699,52 @@ impl<'a, 'tcx> ty::TyS<'tcx> { result } + /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely + /// non-copy and *might* have a destructor attached; if it returns + /// `false`, then `ty` definitely has no destructor (i.e. no drop glue). + /// + /// (Note that this implies that if `ty` has a destructor attached, + /// then `needs_drop` will definitely return `true` for `ty`.) #[inline] - pub fn may_drop(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool { - if self.flags.get().intersects(TypeFlags::MAY_DROP_CACHED) { - return self.flags.get().intersects(TypeFlags::MAY_DROP); + pub fn needs_drop(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>) -> bool { + if self.flags.get().intersects(TypeFlags::NEEDS_DROP_CACHED) { + return self.flags.get().intersects(TypeFlags::NEEDS_DROP); } - self.may_drop_inner(tcx, &mut FxHashSet()) + self.needs_drop_uncached(tcx, param_env, &mut FxHashSet()) } - fn may_drop_inner(&'tcx self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - visited: &mut FxHashSet>) - -> bool { - if self.flags.get().intersects(TypeFlags::MAY_DROP_CACHED) { - return self.flags.get().intersects(TypeFlags::MAY_DROP); + fn needs_drop_inner(&'tcx self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + stack: &mut FxHashSet>) + -> bool { + if self.flags.get().intersects(TypeFlags::NEEDS_DROP_CACHED) { + return self.flags.get().intersects(TypeFlags::NEEDS_DROP); } // This should be reported as an error by `check_representable`. // // Consider the type as not needing drop in the meanwhile to avoid // further errors. - if visited.replace(self).is_some() { + if let Some(_) = stack.replace(self) { return false; } + let needs_drop = self.needs_drop_uncached(tcx, param_env, stack); + + // "Pop" the cycle detection "stack". + stack.remove(self); + + needs_drop + } + + fn needs_drop_uncached(&'tcx self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + stack: &mut FxHashSet>) + -> bool { assert!(!self.needs_infer()); let result = match self.sty { @@ -733,6 +754,21 @@ impl<'a, 'tcx> ty::TyS<'tcx> { ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | ty::TyRawPtr(_) | ty::TyRef(..) | ty::TyStr => false, + // Issue #22536: We first query type_moves_by_default. It sees a + // normalized version of the type, and therefore will definitely + // know whether the type implements Copy (and thus needs no + // cleanup/drop/zeroing) ... + _ if !self.moves_by_default(tcx, param_env, DUMMY_SP) => false, + + // ... (issue #22536 continued) but as an optimization, still use + // prior logic of asking for the structural "may drop". + + // FIXME(#22815): Note that this is a conservative heuristic; + // it may report that the type "may drop" when actual type does + // not actually have a destructor associated with it. But since + // the type absolutely did not have the `Copy` bound attached + // (see above), it is sound to treat it as having a destructor. + // User destructors are the only way to have concrete drop types. ty::TyAdt(def, _) if def.has_dtor(tcx) => true, @@ -743,16 +779,16 @@ impl<'a, 'tcx> ty::TyS<'tcx> { // Structural recursion. ty::TyArray(ty, _) | ty::TySlice(ty) => { - ty.may_drop_inner(tcx, visited) + ty.needs_drop_inner(tcx, param_env, stack) } ty::TyClosure(def_id, ref substs) => { substs.upvar_tys(def_id, tcx) - .any(|ty| ty.may_drop_inner(tcx, visited)) + .any(|ty| ty.needs_drop_inner(tcx, param_env, stack)) } ty::TyTuple(ref tys, _) => { - tys.iter().any(|ty| ty.may_drop_inner(tcx, visited)) + tys.iter().any(|ty| ty.needs_drop_inner(tcx, param_env, stack)) } // unions don't have destructors regardless of the child types @@ -761,17 +797,19 @@ impl<'a, 'tcx> ty::TyS<'tcx> { ty::TyAdt(def, substs) => { def.variants.iter().any(|v| { v.fields.iter().any(|f| { - f.ty(tcx, substs).may_drop_inner(tcx, visited) + f.ty(tcx, substs).needs_drop_inner(tcx, param_env, stack) }) }) } }; - self.flags.set(self.flags.get() | if result { - TypeFlags::MAY_DROP_CACHED | TypeFlags::MAY_DROP - } else { - TypeFlags::MAY_DROP_CACHED - }); + if !self.has_param_types() && !self.has_self_ty() { + self.flags.set(self.flags.get() | if result { + TypeFlags::NEEDS_DROP_CACHED | TypeFlags::NEEDS_DROP + } else { + TypeFlags::NEEDS_DROP_CACHED + }); + } result } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index dc01cbe5e7605..de5613dbfaa38 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -322,7 +322,7 @@ fn on_all_drop_children_bits<'a, 'tcx, F>( let ty = lvalue.ty(mir, tcx).to_ty(tcx); debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty); - if tcx.type_needs_drop_given_env(ty, &ctxt.param_env) { + if ty.needs_drop(tcx, &ctxt.param_env) { each_child(child); } else { debug!("on_all_drop_children_bits - skipping") diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 0ee9d4a42c7f8..1c69f3cff172a 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1152,7 +1152,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields { let param_env = &ty::ParameterEnvironment::for_item(ctx.tcx, item.id); for field in vdata.fields() { let field_ty = ctx.tcx.item_type(ctx.tcx.hir.local_def_id(field.id)); - if ctx.tcx.type_needs_drop_given_env(field_ty, param_env) { + if field_ty.needs_drop(ctx.tcx, param_env) { ctx.span_lint(UNIONS_WITH_DROP_FIELDS, field.span, "union contains a field with possibly non-trivial drop code, \ diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 5f9fb8e1b120f..db9da2a280b94 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -168,7 +168,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { type with inference types/regions", ty); }); - self.tcx.type_needs_drop_given_env(ty, &self.infcx.parameter_environment) + ty.needs_drop(self.tcx.global_tcx(), &self.infcx.parameter_environment) } pub fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index ac2bdaad24f76..892d67ac23725 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -357,7 +357,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // a regular goto. let ty = location.ty(&callee_mir, tcx).subst(tcx, callsite.substs); let ty = ty.to_ty(tcx); - if tcx.type_needs_drop_given_env(ty, ¶m_env) { + if ty.needs_drop(tcx, ¶m_env) { cost += CALL_PENALTY; if let Some(unwind) = unwind { work_list.push(unwind); diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index cc7d25628432e..526c1488ab480 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -83,7 +83,7 @@ impl<'a, 'tcx> Qualif { if ty.is_freeze(tcx, param_env, DUMMY_SP) { *self = *self - Qualif::MUTABLE_INTERIOR; } - if !tcx.type_needs_drop_given_env(ty, param_env) { + if !ty.needs_drop(tcx, param_env) { *self = *self - Qualif::NEEDS_DROP; } } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 04a1fc891cf1e..07025fcfdb944 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -277,8 +277,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let mut fields = fields; fields.retain(|&(ref lvalue, _)| { - self.tcx().type_needs_drop_given_env( - self.lvalue_ty(lvalue), self.elaborator.param_env()) + self.lvalue_ty(lvalue).needs_drop(self.tcx(), self.elaborator.param_env()) }); debug!("drop_ladder - fields needing drop: {:?}", fields); diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 535c6a7ab9e12..fdb6752213378 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -89,7 +89,7 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { self.promotable = false; } - if self.tcx.type_needs_drop_given_env(ty, &self.param_env) { + if ty.needs_drop(self.tcx, &self.param_env) { self.promotable = false; } } diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index fd9ff17cc6489..1d1921bf7b96d 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -392,7 +392,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { } pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - self.tcx.type_needs_drop_given_env(ty, &self.empty_param_env) + ty.needs_drop(self.tcx, &self.empty_param_env) } pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { From 61b7ebe7d3fbe19c7aaf7b8fa5b9661eccca6061 Mon Sep 17 00:00:00 2001 From: Nicolas Bigaouette Date: Thu, 20 Apr 2017 11:20:33 -0400 Subject: [PATCH 16/33] Rename environment variable `GDB_CMD` to `RUST_GDB` to prevent ambiguity --- src/etc/rust-gdb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/etc/rust-gdb b/src/etc/rust-gdb index ef1689886cb88..52601cd96f80d 100755 --- a/src/etc/rust-gdb +++ b/src/etc/rust-gdb @@ -17,10 +17,10 @@ RUSTC_SYSROOT=`rustc --print=sysroot` GDB_PYTHON_MODULE_DIRECTORY="$RUSTC_SYSROOT/lib/rustlib/etc" # Run GDB with the additional arguments that load the pretty printers -# Set the environment variable `GDB_CMD` to overwrite the call to a +# Set the environment variable `RUST_GDB` to overwrite the call to a # different/specific command (defaults to `gdb`). -GDB_CMD="${GDB_CMD:-gdb}" -PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" ${GDB_CMD} \ +RUST_GDB="${RUST_GDB:-gdb}" +PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" ${RUST_GDB} \ -d "$GDB_PYTHON_MODULE_DIRECTORY" \ -iex "add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY" \ "$@" From 416d6b73dfb583e2b81369b046f24194773146bf Mon Sep 17 00:00:00 2001 From: Marco A L Barbosa Date: Thu, 20 Apr 2017 13:59:41 -0300 Subject: [PATCH 17/33] Update libc to include x86_64-linux-android support --- src/liblibc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/liblibc b/src/liblibc index 05a2d197356ef..c34a802d1eb03 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit 05a2d197356ef253dfd985166576619ac9b6947f +Subproject commit c34a802d1eb037b44c5252078c7270b5472e0f65 From a65461005a496f0713e5422647b46f53eee34feb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 20 Apr 2017 00:31:34 +0200 Subject: [PATCH 18/33] Fix line display for hoedown --- src/librustdoc/html/markdown.rs | 10 +++++++--- src/librustdoc/lib.rs | 1 + src/librustdoc/test.rs | 35 +++++++++++++++++++++++++++------ 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index b02b60531d108..4bf856240f66a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -492,7 +492,7 @@ pub fn old_find_testable_code(doc: &str, tests: &mut ::test::Collector, position text: *const hoedown_buffer, lang: *const hoedown_buffer, data: *const hoedown_renderer_data, - line: libc::size_t) { + _line: libc::size_t) { unsafe { if text.is_null() { return } let block_info = if lang.is_null() { @@ -503,11 +503,15 @@ pub fn old_find_testable_code(doc: &str, tests: &mut ::test::Collector, position LangString::parse(s) }; if !block_info.rust { return } + let text = (*text).as_bytes(); let opaque = (*data).opaque as *mut hoedown_html_renderer_state; let tests = &mut *((*opaque).opaque as *mut ::test::Collector); - let line = tests.get_line() + line; + let text = str::from_utf8(text).unwrap(); + let lines = text.lines().map(|l| { + stripped_filtered_line(l).unwrap_or(l) + }); let filename = tests.get_filename(); - tests.add_old_test(line, filename); + tests.add_old_test(lines.collect::>().join("\n"), filename); } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d5b997001bb9d..0ca267bb82d2e 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -27,6 +27,7 @@ #![feature(staged_api)] #![feature(test)] #![feature(unicode)] +#![feature(vec_remove_item)] extern crate arena; extern crate getopts; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index fb681b20065fa..3206b5021075d 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::io::prelude::*; @@ -381,7 +382,7 @@ fn partition_source(s: &str) -> (String, String) { pub struct Collector { pub tests: Vec, // to be removed when hoedown will be definitely gone - pub old_tests: Vec, + pub old_tests: HashMap>, names: Vec, cfgs: Vec, libs: SearchPaths, @@ -403,7 +404,7 @@ impl Collector { codemap: Option>, filename: Option) -> Collector { Collector { tests: Vec::new(), - old_tests: Vec::new(), + old_tests: HashMap::new(), names: Vec::new(), cfgs: cfgs, libs: libs, @@ -432,9 +433,24 @@ impl Collector { } } - pub fn add_old_test(&mut self, line: usize, filename: String) { - let name = self.generate_name(line, &filename); - self.old_tests.push(name); + // to be removed once hoedown is gone + fn generate_name_beginning(&self, filename: &str) -> String { + if self.use_headers { + if let Some(ref header) = self.current_header { + format!("{} - {} (line", filename, header) + } else { + format!("{} - (line", filename) + } + } else { + format!("{} - {} (line", filename, self.names.join("::")) + } + } + + pub fn add_old_test(&mut self, test: String, filename: String) { + let name_beg = self.generate_name_beginning(&filename); + let entry = self.old_tests.entry(name_beg) + .or_insert(Vec::new()); + entry.push(test.trim().to_owned()); } pub fn add_test(&mut self, test: String, @@ -442,7 +458,14 @@ impl Collector { as_test_harness: bool, compile_fail: bool, error_codes: Vec, line: usize, filename: String) { let name = self.generate_name(line, &filename); - if self.old_tests.iter().find(|&x| x == &name).is_none() { + let name_beg = self.generate_name_beginning(&filename); + let mut found = false; + // to be removed when hoedown is removed + let test = test.trim().to_owned(); + if let Some(entry) = self.old_tests.get_mut(&name_beg) { + found = entry.remove_item(&test).is_some(); + } + if !found { let _ = writeln!(&mut io::stderr(), "WARNING: {} Code block is not currently run as a test, but will in \ future versions of rustdoc. Please ensure this code block is a \ From 3b4d34d4faa10336847335e68417f49eb8882693 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 18 Apr 2017 12:20:02 -0700 Subject: [PATCH 19/33] Expanded docs and examples for PathBuf::file_name and friends --- src/libstd/path.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 812b65b61e7f0..15bc74a834010 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1189,8 +1189,13 @@ impl PathBuf { /// If [`self.file_name`] was [`None`], this is equivalent to pushing /// `file_name`. /// + /// Otherwise it is equivalent to calling [`pop`] and then pushing + /// `file_name`. The new path will be a sibling of the original path. + /// (That is, it will have the same parent.) + /// /// [`self.file_name`]: struct.PathBuf.html#method.file_name /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`pop`]: struct.PathBuf.html#method.pop /// /// # Examples /// @@ -1725,7 +1730,10 @@ impl Path { }) } - /// Returns the final component of the `Path`, if it is a normal file. + /// Returns the final component of the `Path`, if there is one. + /// + /// If the path is a normal file, this is the file name. If it's the path of a directory, this + /// is the directory name. /// /// Returns [`None`] If the path terminates in `..`. /// @@ -1737,10 +1745,12 @@ impl Path { /// use std::path::Path; /// use std::ffi::OsStr; /// - /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt").file_name()); + /// assert_eq!(Some(OsStr::new("bin")), Path::new("/usr/bin/").file_name()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("tmp/foo.txt").file_name()); /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name()); /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name()); /// assert_eq!(None, Path::new("foo.txt/..").file_name()); + /// assert_eq!(None, Path::new("/").file_name()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn file_name(&self) -> Option<&OsStr> { @@ -1926,6 +1936,9 @@ impl Path { /// /// let path = Path::new("/tmp/foo.txt"); /// assert_eq!(path.with_file_name("bar.txt"), PathBuf::from("/tmp/bar.txt")); + /// + /// let path = Path::new("/tmp"); + /// assert_eq!(path.with_file_name("var"), PathBuf::from("/var")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn with_file_name>(&self, file_name: S) -> PathBuf { From 51cc0e38e34d5ded08403bae351ecf4ee317573d Mon Sep 17 00:00:00 2001 From: Marco A L Barbosa Date: Thu, 20 Apr 2017 14:02:42 -0300 Subject: [PATCH 20/33] Add x86_64-linux-android target --- configure | 2 + src/bootstrap/config.rs | 6 ++ src/ci/docker/dist-android/Dockerfile | 6 +- src/ci/docker/dist-android/install-ndk.sh | 8 ++- src/librustc_back/target/mod.rs | 1 + .../target/x86_64_linux_android.rs | 34 ++++++++++ src/libstd/os/android/raw.rs | 63 +++++++++++++++++++ src/tools/build-manifest/src/main.rs | 1 + 8 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 src/librustc_back/target/x86_64_linux_android.rs diff --git a/configure b/configure index 35b376d5f27b8..c5ecc2236894c 100755 --- a/configure +++ b/configure @@ -479,6 +479,7 @@ valopt i686-linux-android-ndk "" "i686-linux-android NDK standalone path" valopt arm-linux-androideabi-ndk "" "arm-linux-androideabi NDK standalone path" valopt armv7-linux-androideabi-ndk "" "armv7-linux-androideabi NDK standalone path" valopt aarch64-linux-android-ndk "" "aarch64-linux-android NDK standalone path" +valopt x86_64-linux-android-ndk "" "x86_64-linux-android NDK standalone path" valopt nacl-cross-path "" "NaCl SDK path (Pepper Canary is recommended). Must be absolute!" valopt musl-root "/usr/local" "MUSL root installation directory (deprecated)" valopt musl-root-x86_64 "" "x86_64-unknown-linux-musl install directory" @@ -746,6 +747,7 @@ putvar CFG_AARCH64_LINUX_ANDROID_NDK putvar CFG_ARM_LINUX_ANDROIDEABI_NDK putvar CFG_ARMV7_LINUX_ANDROIDEABI_NDK putvar CFG_I686_LINUX_ANDROID_NDK +putvar CFG_X86_64_LINUX_ANDROID_NDK putvar CFG_NACL_CROSS_PATH putvar CFG_MANDIR putvar CFG_DOCDIR diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 693114d01ad9c..34fbc33d981af 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -570,6 +570,12 @@ impl Config { .or_insert(Target::default()); target.ndk = Some(parse_configure_path(value)); } + "CFG_X86_64_LINUX_ANDROID_NDK" if value.len() > 0 => { + let target = "x86_64-linux-android".to_string(); + let target = self.target_config.entry(target) + .or_insert(Target::default()); + target.ndk = Some(parse_configure_path(value)); + } "CFG_LOCAL_RUST_ROOT" if value.len() > 0 => { let path = parse_configure_path(value); self.rustc = Some(push_exe_path(path.clone(), &["bin", "rustc"])); diff --git a/src/ci/docker/dist-android/Dockerfile b/src/ci/docker/dist-android/Dockerfile index 99c176aa820c7..28cc885ed30da 100644 --- a/src/ci/docker/dist-android/Dockerfile +++ b/src/ci/docker/dist-android/Dockerfile @@ -36,9 +36,10 @@ RUN curl -o /usr/local/bin/sccache \ chmod +x /usr/local/bin/sccache ENV TARGETS=arm-linux-androideabi +ENV TARGETS=$TARGETS,armv7-linux-androideabi ENV TARGETS=$TARGETS,i686-linux-android ENV TARGETS=$TARGETS,aarch64-linux-android -ENV TARGETS=$TARGETS,armv7-linux-androideabi +ENV TARGETS=$TARGETS,x86_64-linux-android ENV RUST_CONFIGURE_ARGS \ --target=$TARGETS \ @@ -46,6 +47,7 @@ ENV RUST_CONFIGURE_ARGS \ --arm-linux-androideabi-ndk=/android/ndk-arm-9 \ --armv7-linux-androideabi-ndk=/android/ndk-arm-9 \ --i686-linux-android-ndk=/android/ndk-x86-9 \ - --aarch64-linux-android-ndk=/android/ndk-aarch64 + --aarch64-linux-android-ndk=/android/ndk-arm64-21 \ + --x86_64-linux-android-ndk=/android/ndk-x86_64-21 ENV SCRIPT python2.7 ../x.py dist --target $TARGETS diff --git a/src/ci/docker/dist-android/install-ndk.sh b/src/ci/docker/dist-android/install-ndk.sh index 19c1b94e784c8..d3a2d31754543 100644 --- a/src/ci/docker/dist-android/install-ndk.sh +++ b/src/ci/docker/dist-android/install-ndk.sh @@ -25,7 +25,7 @@ bash android-ndk-r11c/build/tools/make-standalone-toolchain.sh \ bash android-ndk-r11c/build/tools/make-standalone-toolchain.sh \ --platform=android-21 \ --toolchain=aarch64-linux-android-4.9 \ - --install-dir=/android/ndk-aarch64 \ + --install-dir=/android/ndk-arm64-21 \ --ndk-dir=/android/android-ndk-r11c \ --arch=arm64 bash android-ndk-r11c/build/tools/make-standalone-toolchain.sh \ @@ -34,5 +34,11 @@ bash android-ndk-r11c/build/tools/make-standalone-toolchain.sh \ --install-dir=/android/ndk-x86-9 \ --ndk-dir=/android/android-ndk-r11c \ --arch=x86 +bash android-ndk-r11c/build/tools/make-standalone-toolchain.sh \ + --platform=android-21 \ + --toolchain=x86_64-4.9 \ + --install-dir=/android/ndk-x86_64-21 \ + --ndk-dir=/android/android-ndk-r11c \ + --arch=x86_64 rm -rf ./android-ndk-r11c-linux-x86_64.zip ./android-ndk-r11c diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index ca6894a7b7041..e60fdc386ce6e 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -162,6 +162,7 @@ supported_targets! { ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu), ("i686-linux-android", i686_linux_android), + ("x86_64-linux-android", x86_64_linux_android), ("arm-linux-androideabi", arm_linux_androideabi), ("armv7-linux-androideabi", armv7_linux_androideabi), ("aarch64-linux-android", aarch64_linux_android), diff --git a/src/librustc_back/target/x86_64_linux_android.rs b/src/librustc_back/target/x86_64_linux_android.rs new file mode 100644 index 0000000000000..75cf3e1243841 --- /dev/null +++ b/src/librustc_back/target/x86_64_linux_android.rs @@ -0,0 +1,34 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use LinkerFlavor; +use target::{Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::android_base::opts(); + base.cpu = "x86-64".to_string(); + // https://developer.android.com/ndk/guides/abis.html#86-64 + base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + + Ok(Target { + llvm_target: "x86_64-linux-android".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), + arch: "x86_64".to_string(), + target_os: "android".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/src/libstd/os/android/raw.rs b/src/libstd/os/android/raw.rs index 5e473a933a676..60ad8fcc54ccd 100644 --- a/src/libstd/os/android/raw.rs +++ b/src/libstd/os/android/raw.rs @@ -165,3 +165,66 @@ mod arch { } } +#[cfg(target_arch = "x86_64")] +mod arch { + use os::raw::{c_uint, c_long, c_ulong}; + use os::unix::raw::{uid_t, gid_t}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type dev_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type mode_t = u32; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u32; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: ino_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: uid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: gid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_ulong, + __unused: [c_long; 3], + } +} + diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 28c8d22707325..a8cb30da43513 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -81,6 +81,7 @@ static TARGETS: &'static [&'static str] = &[ "s390x-unknown-linux-gnu", "sparc64-unknown-linux-gnu", "wasm32-unknown-emscripten", + "x86_64-linux-android", "x86_64-apple-darwin", "x86_64-apple-ios", "x86_64-pc-windows-gnu", From 865f5d099c6912a3f539e6c1db207d67accdd31f Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 21 Apr 2017 09:20:04 +1200 Subject: [PATCH 21/33] Bump the rls submodule revision --- rls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rls b/rls index 016cbc514cf44..6ecff95fdc3ee 160000 --- a/rls +++ b/rls @@ -1 +1 @@ -Subproject commit 016cbc514cf44a2bd3fe806e8afa6b9c50287373 +Subproject commit 6ecff95fdc3ee7ceed2b9b0cc1a3a64876860bce From 4358e35fda66ab7a00215c7f9d50e7c6dc9b801b Mon Sep 17 00:00:00 2001 From: Cameron Hart Date: Sun, 15 Jan 2017 09:49:29 +1100 Subject: [PATCH 22/33] Implementation of repr struct alignment RFC 1358. The main changes around rustc::ty::Layout::struct and rustc_trans:adt: * Added primitive_align field which stores alignment before repr align * Always emit field padding when generating the LLVM struct fields * Added methods for adjusting field indexes from the layout index to the LLVM struct field index The main user of this information is rustc_trans::adt::struct_llfields which determines the LLVM fields to be used by LLVM, including padding fields. --- src/librustc/diagnostics.rs | 3 +- src/librustc/hir/check_attr.rs | 17 ++ src/librustc/ty/layout.rs | 89 +++++++- src/librustc/ty/mod.rs | 11 +- src/librustc_trans/abi.rs | 2 +- src/librustc_trans/adt.rs | 82 ++++++-- src/librustc_trans/builder.rs | 12 +- src/librustc_trans/intrinsic.rs | 2 +- src/librustc_trans/mir/block.rs | 12 +- src/librustc_trans/mir/lvalue.rs | 17 +- src/librustc_trans/mir/mod.rs | 2 +- src/librustc_trans/mir/operand.rs | 36 ++-- src/librustc_trans/mir/rvalue.rs | 16 +- src/librustc_trans/type_of.rs | 10 + src/librustc_typeck/check/mod.rs | 47 +++++ src/librustc_typeck/diagnostics.rs | 1 + src/libsyntax/attr.rs | 50 ++++- src/libsyntax/diagnostic_list.rs | 2 +- src/libsyntax_ext/deriving/generic/mod.rs | 2 +- src/test/compile-fail/attr-usage-repr.rs | 4 + .../compile-fail/conflicting-repr-hints.rs | 8 +- src/test/compile-fail/repr-align.rs | 22 ++ .../repr-packed-contains-align.rs | 24 +++ src/test/run-pass/align-struct.rs | 195 ++++++++++++++++++ src/test/ui/print_type_sizes/repr-align.rs | 42 ++++ .../ui/print_type_sizes/repr-align.stdout | 16 ++ 26 files changed, 646 insertions(+), 78 deletions(-) create mode 100644 src/test/compile-fail/repr-align.rs create mode 100644 src/test/compile-fail/repr-packed-contains-align.rs create mode 100644 src/test/run-pass/align-struct.rs create mode 100644 src/test/ui/print_type_sizes/repr-align.rs create mode 100644 src/test/ui/print_type_sizes/repr-align.stdout diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 618561f3b0257..2851191dc141a 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1847,5 +1847,6 @@ register_diagnostics! { E0489, // type/lifetime parameter not in scope here E0490, // a value of type `..` is borrowed for too long E0495, // cannot infer an appropriate lifetime due to conflicting requirements - E0566 // conflicting representation hints + E0566, // conflicting representation hints + E0587, // conflicting packed and align representation hints } diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 54ae947214091..bf292ccb8d86d 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -57,6 +57,9 @@ impl<'a> CheckAttrVisitor<'a> { }; let mut conflicting_reprs = 0; + let mut found_packed = false; + let mut found_align = false; + for word in words { let name = match word.name() { @@ -84,6 +87,7 @@ impl<'a> CheckAttrVisitor<'a> { ("attribute should be applied to struct or union", "a struct or union") } else { + found_packed = true; continue } } @@ -96,6 +100,15 @@ impl<'a> CheckAttrVisitor<'a> { continue } } + "align" => { + found_align = true; + if target != Target::Struct { + ("attribute should be applied to struct", + "a struct") + } else { + continue + } + } "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "isize" | "usize" => { @@ -117,6 +130,10 @@ impl<'a> CheckAttrVisitor<'a> { span_warn!(self.sess, attr.span, E0566, "conflicting representation hints"); } + if found_align && found_packed { + struct_span_err!(self.sess, attr.span, E0587, + "conflicting packed and align representation hints").emit(); + } } fn check_attribute(&self, attr: &ast::Attribute, target: Target) { diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index df60eee8c0243..6a206640b3baa 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -548,8 +548,12 @@ pub type FieldPath = Vec; /// A structure, a product type in ADT terms. #[derive(PartialEq, Eq, Hash, Debug)] pub struct Struct { + /// Maximum alignment of fields and repr alignment. pub align: Align, + /// Primitive alignment of fields without repr alignment. + pub primitive_align: Align, + /// If true, no alignment padding is used. pub packed: bool, @@ -583,10 +587,20 @@ impl<'a, 'gcx, 'tcx> Struct { fn new(dl: &TargetDataLayout, fields: &Vec<&'a Layout>, repr: &ReprOptions, kind: StructKind, scapegoat: Ty<'gcx>) -> Result> { - let packed = repr.packed(); + if repr.packed() && repr.align > 0 { + bug!("Struct cannot be packed and aligned"); + } + + let align = if repr.packed() { + dl.i8_align + } else { + dl.aggregate_align + }; + let mut ret = Struct { - align: if packed { dl.i8_align } else { dl.aggregate_align }, - packed: packed, + align: align, + primitive_align: align, + packed: repr.packed(), sized: true, offsets: vec![], memory_index: vec![], @@ -660,7 +674,9 @@ impl<'a, 'gcx, 'tcx> Struct { // Invariant: offset < dl.obj_size_bound() <= 1<<61 if !ret.packed { let align = field.align(dl); + let primitive_align = field.primitive_align(dl); ret.align = ret.align.max(align); + ret.primitive_align = ret.primitive_align.max(primitive_align); offset = offset.abi_align(align); } @@ -671,6 +687,11 @@ impl<'a, 'gcx, 'tcx> Struct { .map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?; } + if repr.align > 0 { + let repr_align = repr.align as u64; + ret.align = ret.align.max(Align::from_bytes(repr_align, repr_align).unwrap()); + debug!("Struct::new repr_align: {:?}", repr_align); + } debug!("Struct::new min_size: {:?}", offset); ret.min_size = offset; @@ -836,12 +857,23 @@ impl<'a, 'gcx, 'tcx> Struct { } Ok(None) } + + pub fn over_align(&self) -> Option { + let align = self.align.abi(); + let primitive_align = self.primitive_align.abi(); + if align > primitive_align { + Some(align as u32) + } else { + None + } + } } /// An untagged union. #[derive(PartialEq, Eq, Hash, Debug)] pub struct Union { pub align: Align, + pub primitive_align: Align, pub min_size: Size, @@ -851,8 +883,10 @@ pub struct Union { impl<'a, 'gcx, 'tcx> Union { fn new(dl: &TargetDataLayout, packed: bool) -> Union { + let align = if packed { dl.i8_align } else { dl.aggregate_align }; Union { - align: if packed { dl.i8_align } else { dl.aggregate_align }, + align: align, + primitive_align: align, min_size: Size::from_bytes(0), packed: packed, } @@ -875,6 +909,7 @@ impl<'a, 'gcx, 'tcx> Union { if !self.packed { self.align = self.align.max(field.align(dl)); + self.primitive_align = self.primitive_align.max(field.primitive_align(dl)); } self.min_size = cmp::max(self.min_size, field.size(dl)); } @@ -888,6 +923,16 @@ impl<'a, 'gcx, 'tcx> Union { pub fn stride(&self) -> Size { self.min_size.abi_align(self.align) } + + pub fn over_align(&self) -> Option { + let align = self.align.abi(); + let primitive_align = self.primitive_align.abi(); + if align > primitive_align { + Some(align as u32) + } else { + None + } + } } /// The first half of a fat pointer. @@ -924,6 +969,7 @@ pub enum Layout { /// If true, the size is exact, otherwise it's only a lower bound. sized: bool, align: Align, + primitive_align: Align, element_size: Size, count: u64 }, @@ -970,7 +1016,8 @@ pub enum Layout { discr: Integer, variants: Vec, size: Size, - align: Align + align: Align, + primitive_align: Align, }, /// Two cases distinguished by a nullable pointer: the case with discriminant @@ -1118,6 +1165,7 @@ impl<'a, 'gcx, 'tcx> Layout { Array { sized: true, align: element.align(dl), + primitive_align: element.primitive_align(dl), element_size: element_size, count: count } @@ -1127,6 +1175,7 @@ impl<'a, 'gcx, 'tcx> Layout { Array { sized: false, align: element.align(dl), + primitive_align: element.primitive_align(dl), element_size: element.size(dl), count: 0 } @@ -1135,6 +1184,7 @@ impl<'a, 'gcx, 'tcx> Layout { Array { sized: false, align: dl.i8_align, + primitive_align: dl.i8_align, element_size: Size::from_bytes(1), count: 0 } @@ -1340,6 +1390,7 @@ impl<'a, 'gcx, 'tcx> Layout { assert!(discr_max >= 0); let (min_ity, _) = Integer::repr_discr(tcx, ty, &def.repr, 0, discr_max); let mut align = dl.aggregate_align; + let mut primitive_align = dl.aggregate_align; let mut size = Size::from_bytes(0); // We're interested in the smallest alignment, so start large. @@ -1369,6 +1420,7 @@ impl<'a, 'gcx, 'tcx> Layout { } size = cmp::max(size, st.min_size); align = align.max(st.align); + primitive_align = primitive_align.max(st.primitive_align); Ok(st) }).collect::, _>>()?; @@ -1435,7 +1487,8 @@ impl<'a, 'gcx, 'tcx> Layout { discr: ity, variants: variants, size: size, - align: align + align: align, + primitive_align: primitive_align } } @@ -1557,6 +1610,30 @@ impl<'a, 'gcx, 'tcx> Layout { } } + /// Returns alignment before repr alignment is applied + pub fn primitive_align(&self, dl: &TargetDataLayout) -> Align { + match *self { + Array { primitive_align, .. } | General { primitive_align, .. } => primitive_align, + Univariant { ref variant, .. } | + StructWrappedNullablePointer { nonnull: ref variant, .. } => { + variant.primitive_align + }, + + _ => self.align(dl) + } + } + + /// Returns repr alignment if it is greater than the primitive alignment. + pub fn over_align(&self, dl: &TargetDataLayout) -> Option { + let align = self.align(dl); + let primitive_align = self.primitive_align(dl); + if align.abi() > primitive_align.abi() { + Some(align.abi() as u32) + } else { + None + } + } + pub fn field_offset(&self, cx: C, i: usize, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index ab1a06aeacd18..db8b093e369e0 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -37,6 +37,7 @@ use serialize::{self, Encodable, Encoder}; use std::borrow::Cow; use std::cell::{Cell, RefCell, Ref}; use std::collections::BTreeMap; +use std::cmp; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::rc::Rc; @@ -1464,10 +1465,12 @@ impl_stable_hash_for!(struct ReprFlags { #[derive(Copy, Clone, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)] pub struct ReprOptions { pub int: Option, + pub align: u16, pub flags: ReprFlags, } impl_stable_hash_for!(struct ReprOptions { + align, int, flags }); @@ -1476,7 +1479,7 @@ impl ReprOptions { pub fn new(tcx: TyCtxt, did: DefId) -> ReprOptions { let mut flags = ReprFlags::empty(); let mut size = None; - + let mut max_align = 0; for attr in tcx.get_attrs(did).iter() { for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) { flags.insert(match r { @@ -1487,6 +1490,10 @@ impl ReprOptions { size = Some(i); ReprFlags::empty() }, + attr::ReprAlign(align) => { + max_align = cmp::max(align, max_align); + ReprFlags::empty() + }, }); } } @@ -1500,7 +1507,7 @@ impl ReprOptions { if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) { flags.insert(ReprFlags::IS_LINEAR); } - ReprOptions { int: size, flags: flags } + ReprOptions { int: size, align: max_align, flags: flags } } #[inline] diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index c4fdc46d030c9..efe4acfa9e072 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -553,7 +553,7 @@ impl<'a, 'tcx> ArgType<'tcx> { // bitcasting to the struct type yields invalid cast errors. // We instead thus allocate some scratch space... - let llscratch = bcx.alloca(ty, "abi_cast"); + let llscratch = bcx.alloca(ty, "abi_cast", None); base::Lifetime::Start.call(bcx, llscratch); // ...where we first store the value... diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 87ca410dece0d..3c73aba7cd7ed 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -90,12 +90,12 @@ pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, /// and fill in the actual contents in a second pass to prevent /// unbounded recursion; see also the comments in `trans::type_of`. pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { - generic_type_of(cx, t, None, false, false) + generic_type_of(cx, t, None, false) } pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, name: &str) -> Type { - generic_type_of(cx, t, Some(name), false, false) + generic_type_of(cx, t, Some(name), false) } pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, @@ -114,7 +114,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, _ => unreachable!() }; let fields = compute_fields(cx, t, nonnull_variant_index as usize, true); - llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant, false, false), + llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant, false), packed) }, _ => bug!("This function cannot handle {} with layout {:#?}", t, l) @@ -124,11 +124,9 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, name: Option<&str>, - sizing: bool, - dst: bool) -> Type { + sizing: bool) -> Type { let l = cx.layout_of(t); - debug!("adt::generic_type_of t: {:?} name: {:?} sizing: {} dst: {}", - t, name, sizing, dst); + debug!("adt::generic_type_of t: {:?} name: {:?} sizing: {}", t, name, sizing); match *l { layout::CEnum { discr, .. } => Type::from_integer(cx, discr), layout::RawNullablePointer { nndiscr, .. } => { @@ -148,7 +146,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fields = compute_fields(cx, t, nndiscr as usize, false); match name { None => { - Type::struct_(cx, &struct_llfields(cx, &fields, nonnull, sizing, dst), + Type::struct_(cx, &struct_llfields(cx, &fields, nonnull, sizing), nonnull.packed) } Some(name) => { @@ -163,7 +161,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fields = compute_fields(cx, t, 0, true); match name { None => { - let fields = struct_llfields(cx, &fields, &variant, sizing, dst); + let fields = struct_llfields(cx, &fields, &variant, sizing); Type::struct_(cx, &fields, variant.packed) } Some(name) => { @@ -245,15 +243,65 @@ fn union_fill(cx: &CrateContext, size: u64, align: u64) -> Type { } -fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fields: &Vec>, +// Double index to account for padding (FieldPath already uses `Struct::memory_index`) +fn struct_llfields_path(discrfield: &layout::FieldPath) -> Vec { + discrfield.iter().map(|&i| (i as usize) << 1).collect::>() +} + + +// Lookup `Struct::memory_index` and double it to account for padding +pub fn struct_llfields_index(variant: &layout::Struct, index: usize) -> usize { + (variant.memory_index[index] as usize) << 1 +} + + +pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, field_tys: &Vec>, variant: &layout::Struct, - sizing: bool, _dst: bool) -> Vec { - let fields = variant.field_index_by_increasing_offset().map(|i| fields[i as usize]); + sizing: bool) -> Vec { if sizing { - bug!() + bug!(); + } + debug!("struct_llfields: variant: {:?}", variant); + let mut first_field = true; + let mut min_offset = 0; + let mut result: Vec = Vec::with_capacity(field_tys.len() * 2); + let field_iter = variant.field_index_by_increasing_offset().map(|i| { + (i, field_tys[i as usize], variant.offsets[i as usize].bytes()) }); + for (index, ty, target_offset) in field_iter.filter( + |&(_, ty, _)| !sizing || cx.shared().type_is_sized(ty)) { + if first_field { + debug!("struct_llfields: {} ty: {} min_offset: {} target_offset: {}", + index, ty, min_offset, target_offset); + first_field = false; + } else { + assert!(target_offset >= min_offset); + let padding_bytes = if variant.packed { 0 } else { target_offset - min_offset }; + result.push(Type::array(&Type::i8(cx), padding_bytes)); + debug!("struct_llfields: {} ty: {} pad_bytes: {} min_offset: {} target_offset: {}", + index, ty, padding_bytes, min_offset, target_offset); + } + let llty = type_of::in_memory_type_of(cx, ty); + result.push(llty); + let layout = cx.layout_of(ty); + let target_size = layout.size(&cx.tcx().data_layout).bytes(); + min_offset = target_offset + target_size; + } + if variant.sized && !field_tys.is_empty() { + if variant.stride().bytes() < min_offset { + bug!("variant: {:?} stride: {} min_offset: {}", variant, variant.stride().bytes(), + min_offset); + } + let padding_bytes = variant.stride().bytes() - min_offset; + debug!("struct_llfields: pad_bytes: {} min_offset: {} min_size: {} stride: {}\n", + padding_bytes, min_offset, variant.min_size.bytes(), variant.stride().bytes()); + result.push(Type::array(&Type::i8(cx), padding_bytes)); + assert!(result.len() == (field_tys.len() * 2)); } else { - fields.map(|ty| type_of::in_memory_type_of(cx, ty)).collect() + debug!("struct_llfields: min_offset: {} min_size: {} stride: {}\n", + min_offset, variant.min_size.bytes(), variant.stride().bytes()); } + + result } pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool { @@ -309,8 +357,8 @@ fn struct_wrapped_nullable_bitdiscr( scrutinee: ValueRef, alignment: Alignment, ) -> ValueRef { - let llptrptr = bcx.gepi(scrutinee, - &discrfield.iter().map(|f| *f as usize).collect::>()); + let path = struct_llfields_path(discrfield); + let llptrptr = bcx.gepi(scrutinee, &path); let llptr = bcx.load(llptrptr, alignment.to_align()); let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; bcx.icmp(cmp, llptr, C_null(val_ty(llptr))) @@ -380,7 +428,7 @@ pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: Valu let align = C_i32(bcx.ccx, nonnull.align.abi() as i32); base::call_memset(bcx, llptr, fill_byte, size, align, false); } else { - let path = discrfield.iter().map(|&i| i as usize).collect::>(); + let path = struct_llfields_path(discrfield); let llptrptr = bcx.gepi(val, &path); let llptrty = val_ty(llptrptr).element_type(); bcx.store(C_null(llptrty), llptrptr, None); diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index 8b1010d89fd9f..5103ca5c5e109 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -477,24 +477,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { + pub fn alloca(&self, ty: Type, name: &str, align: Option) -> ValueRef { let builder = Builder::with_ccx(self.ccx); builder.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) }); - builder.dynamic_alloca(ty, name) + builder.dynamic_alloca(ty, name, align) } - pub fn dynamic_alloca(&self, ty: Type, name: &str) -> ValueRef { + pub fn dynamic_alloca(&self, ty: Type, name: &str, align: Option) -> ValueRef { self.count_insn("alloca"); unsafe { - if name.is_empty() { + let alloca = if name.is_empty() { llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) } else { let name = CString::new(name).unwrap(); llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), name.as_ptr()) + }; + if let Some(align) = align { + llvm::LLVMSetAlignment(alloca, align as c_uint); } + alloca } } diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 7077eade61182..54e20f590c671 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -778,7 +778,7 @@ fn trans_msvc_try<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // // More information can be found in libstd's seh.rs implementation. let i64p = Type::i64(ccx).ptr_to(); - let slot = bcx.alloca(i64p, "slot"); + let slot = bcx.alloca(i64p, "slot", None); bcx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 0f5a38ac7f6b8..d94d7f4430bf0 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -15,6 +15,7 @@ use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::{self, LayoutTyper}; use rustc::mir; use abi::{Abi, FnType, ArgType}; +use adt; use base::{self, Lifetime}; use callee; use builder::Builder; @@ -177,7 +178,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; let llslot = match op.val { Immediate(_) | Pair(..) => { - let llscratch = bcx.alloca(ret.memory_ty(bcx.ccx), "ret"); + let llscratch = bcx.alloca(ret.memory_ty(bcx.ccx), "ret", None); self.store_operand(&bcx, llscratch, None, op); llscratch } @@ -630,7 +631,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (mut llval, align, by_ref) = match op.val { Immediate(_) | Pair(..) => { if arg.is_indirect() || arg.cast.is_some() { - let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg"); + let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg", None); self.store_operand(bcx, llscratch, None, op); (llscratch, Alignment::AbiAligned, true) } else { @@ -642,7 +643,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't // have scary latent bugs around. - let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg"); + let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg", None); base::memcpy_ty(bcx, llscratch, llval, op.ty, Some(1)); (llscratch, Alignment::AbiAligned, true) } @@ -711,7 +712,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bug!("Not a tuple."); }; for (n, &ty) in arg_types.iter().enumerate() { - let mut elem = bcx.extract_value(llval, v.memory_index[n] as usize); + let mut elem = bcx.extract_value( + llval, adt::struct_llfields_index(v, n)); // Truncate bools to i1, if needed if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx) { elem = bcx.trunc(elem, Type::i1(bcx.ccx)); @@ -750,7 +752,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { slot } else { let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - let slot = bcx.alloca(llretty, "personalityslot"); + let slot = bcx.alloca(llretty, "personalityslot", None); self.llpersonalityslot = Some(slot); slot } diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index fc889604ab88e..9a461e4aafc23 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -97,7 +97,8 @@ impl<'a, 'tcx> LvalueRef<'tcx> { pub fn alloca(bcx: &Builder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> LvalueRef<'tcx> { debug!("alloca({:?}: {:?})", name, ty); - let tmp = bcx.alloca(type_of::type_of(bcx.ccx, ty), name); + let tmp = bcx.alloca( + type_of::type_of(bcx.ccx, ty), name, bcx.ccx.over_align_of(ty)); assert!(!ty.has_param_types()); Self::new_sized_ty(tmp, ty, Alignment::AbiAligned) } @@ -131,11 +132,9 @@ impl<'a, 'tcx> LvalueRef<'tcx> { let alignment = self.alignment | Alignment::from_packed(st.packed); + let llfields = adt::struct_llfields(ccx, fields, st, false); let ptr_val = if needs_cast { - let fields = st.field_index_by_increasing_offset().map(|i| { - type_of::in_memory_type_of(ccx, fields[i]) - }).collect::>(); - let real_ty = Type::struct_(ccx, &fields[..], st.packed); + let real_ty = Type::struct_(ccx, &llfields[..], st.packed); bcx.pointercast(self.llval, real_ty.ptr_to()) } else { self.llval @@ -147,14 +146,16 @@ impl<'a, 'tcx> LvalueRef<'tcx> { // * Field is sized - pointer is properly aligned already if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed || bcx.ccx.shared().type_is_sized(fty) { - return (bcx.struct_gep(ptr_val, st.memory_index[ix] as usize), alignment); + return (bcx.struct_gep( + ptr_val, adt::struct_llfields_index(st, ix)), alignment); } // If the type of the last field is [T] or str, then we don't need to do // any adjusments match fty.sty { ty::TySlice(..) | ty::TyStr => { - return (bcx.struct_gep(ptr_val, st.memory_index[ix] as usize), alignment); + return (bcx.struct_gep( + ptr_val, adt::struct_llfields_index(st, ix)), alignment); } _ => () } @@ -163,7 +164,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> { if !self.has_extra() { debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", ix, Value(ptr_val)); - return (bcx.struct_gep(ptr_val, ix), alignment); + return (bcx.struct_gep(ptr_val, adt::struct_llfields_index(st, ix)), alignment); } // We need to get the pointer manually now. diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 3d8c5085462a8..d39a91405c185 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -524,7 +524,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // doesn't actually strip the offset when splitting the closure // environment into its components so it ends up out of bounds. let env_ptr = if !env_ref { - let alloc = bcx.alloca(common::val_ty(llval), "__debuginfo_env_ptr"); + let alloc = bcx.alloca(common::val_ty(llval), "__debuginfo_env_ptr", None); bcx.store(llval, alloc, None); alloc } else { diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index c31142323c85f..6889b5064b649 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -15,6 +15,7 @@ use rustc::mir; use rustc::mir::tcx::LvalueTy; use rustc_data_structures::indexed_vec::Idx; +use adt; use base; use common::{self, CrateContext, C_null}; use builder::Builder; @@ -134,6 +135,12 @@ impl<'a, 'tcx> OperandRef<'tcx> { if common::val_ty(elem) == Type::i1(bcx.ccx) { elem = bcx.zext(elem, Type::i8(bcx.ccx)); } + let layout = bcx.ccx.layout_of(self.ty); + let i = if let Layout::Univariant { ref variant, .. } = *layout { + adt::struct_llfields_index(variant, i) + } else { + i + }; llpair = bcx.insert_value(llpair, elem, i); } self.val = OperandValue::Immediate(llpair); @@ -183,14 +190,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (lldata, llextra) = base::load_fat_ptr(bcx, llval, align, ty); OperandValue::Pair(lldata, llextra) } else if common::type_is_imm_pair(bcx.ccx, ty) { - let f_align = match *bcx.ccx.layout_of(ty) { - Layout::Univariant { ref variant, .. } => - Alignment::from_packed(variant.packed) | align, - _ => align + let (ix0, ix1, f_align) = match *bcx.ccx.layout_of(ty) { + Layout::Univariant { ref variant, .. } => { + (adt::struct_llfields_index(variant, 0), + adt::struct_llfields_index(variant, 1), + Alignment::from_packed(variant.packed) | align) + }, + _ => (0, 1, align) }; let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx, ty).unwrap(); - let a_ptr = bcx.struct_gep(llval, 0); - let b_ptr = bcx.struct_gep(llval, 1); + let a_ptr = bcx.struct_gep(llval, ix0); + let b_ptr = bcx.struct_gep(llval, ix1); OperandValue::Pair( base::load_ty(bcx, a_ptr, f_align, a_ty), @@ -302,17 +312,19 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx.store(base::from_immediate(bcx, s), lldest, align); } OperandValue::Pair(a, b) => { - let f_align = match *bcx.ccx.layout_of(operand.ty) { - Layout::Univariant { ref variant, .. } if variant.packed => { - Some(1) + let (ix0, ix1, f_align) = match *bcx.ccx.layout_of(operand.ty) { + Layout::Univariant { ref variant, .. } => { + (adt::struct_llfields_index(variant, 0), + adt::struct_llfields_index(variant, 1), + if variant.packed { Some(1) } else { None }) } - _ => align + _ => (0, 1, align) }; let a = base::from_immediate(bcx, a); let b = base::from_immediate(bcx, b); - bcx.store(a, bcx.struct_gep(lldest, 0), f_align); - bcx.store(b, bcx.struct_gep(lldest, 1), f_align); + bcx.store(a, bcx.struct_gep(lldest, ix0), f_align); + bcx.store(b, bcx.struct_gep(lldest, ix1), f_align); } } } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 98e9008f829f6..de1c1e492f39b 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -130,10 +130,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { _ => { // If this is a tuple or closure, we need to translate GEP indices. let layout = bcx.ccx.layout_of(dest.ty.to_ty(bcx.tcx())); - let translation = if let Layout::Univariant { ref variant, .. } = *layout { - Some(&variant.memory_index) - } else { - None + let get_memory_index = |i| { + if let Layout::Univariant { ref variant, .. } = *layout { + adt::struct_llfields_index(variant, i) + } else { + i + } }; let alignment = dest.alignment; for (i, operand) in operands.iter().enumerate() { @@ -143,11 +145,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Note: perhaps this should be StructGep, but // note that in some cases the values here will // not be structs but arrays. - let i = if let Some(ref t) = translation { - t[i] as usize - } else { - i - }; + let i = get_memory_index(i); let dest = bcx.gepi(dest.llval, &[0, i]); self.store_operand(&bcx, dest, alignment.to_align(), op); } diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index d4ab6b0782855..9f9126ba83a8f 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -214,6 +214,16 @@ impl<'a, 'tcx> CrateContext<'a, 'tcx> { pub fn size_of(&self, ty: Ty<'tcx>) -> machine::llsize { self.layout_of(ty).size(self).bytes() as machine::llsize } + + pub fn over_align_of(&self, t: Ty<'tcx>) + -> Option { + let layout = self.layout_of(t); + if let Some(align) = layout.over_align(&self.tcx().data_layout) { + Some(align as machine::llalign) + } else { + None + } + } } fn llvm_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> String { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 902cfb889f8c2..839af0fa6706c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -953,6 +953,12 @@ fn check_struct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if def.repr.simd() { check_simd(tcx, span, def_id); } + + // if struct is packed and not aligned, check fields for alignment. + // Checks for combining packed and align attrs on single struct are done elsewhere. + if tcx.lookup_adt_def(def_id).repr.packed() && tcx.lookup_adt_def(def_id).repr.align == 0 { + check_packed(tcx, span, def_id); + } } fn check_union<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -1371,6 +1377,47 @@ pub fn check_simd<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, def_id: DefId } } +fn check_packed<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, def_id: DefId) { + if check_packed_inner(tcx, def_id, &mut Vec::new()) { + struct_span_err!(tcx.sess, sp, E0588, + "packed struct cannot transitively contain a `[repr(align)]` struct").emit(); + } +} + +fn check_packed_inner<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + stack: &mut Vec) -> bool { + let t = tcx.item_type(def_id); + if stack.contains(&def_id) { + debug!("check_packed_inner: {:?} is recursive", t); + return false; + } + match t.sty { + ty::TyAdt(def, substs) if def.is_struct() => { + if tcx.lookup_adt_def(def.did).repr.align > 0 { + return true; + } + // push struct def_id before checking fields + stack.push(def_id); + for field in &def.struct_variant().fields { + let f = field.ty(tcx, substs); + match f.sty { + ty::TyAdt(def, _) => { + if check_packed_inner(tcx, def.did, stack) { + return true; + } + } + _ => () + } + } + // only need to pop if not early out + stack.pop(); + } + _ => () + } + false +} + #[allow(trivial_numeric_casts)] pub fn check_enum<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 68afcae2b6746..c50156fa5f271 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4168,5 +4168,6 @@ register_diagnostics! { // but `{}` was found in the type `{}` E0567, // auto traits can not have type parameters E0568, // auto-traits can not have predicates, + E0588, // packed struct cannot transitively contain a `[repr(align)]` struct E0592, // duplicate definitions with name `{}` } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 6f5f52ff1e953..442345ceb3c18 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -147,6 +147,24 @@ impl NestedMetaItem { self.meta_item().and_then(|meta_item| meta_item.value_str()) } + /// Returns a name and single literal value tuple of the MetaItem. + pub fn name_value_literal(&self) -> Option<(Name, &Lit)> { + self.meta_item().and_then( + |meta_item| meta_item.meta_item_list().and_then( + |meta_item_list| { + if meta_item_list.len() == 1 { + let nested_item = &meta_item_list[0]; + if nested_item.is_literal() { + Some((meta_item.name(), nested_item.literal().unwrap())) + } else { + None + } + } + else { + None + }})) + } + /// Returns a MetaItem if self is a MetaItem with Kind Word. pub fn word(&self) -> Option<&MetaItem> { self.meta_item().and_then(|meta_item| if meta_item.is_word() { @@ -931,6 +949,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec continue } + let mut recognised = false; if let Some(mi) = item.word() { let word = &*mi.name().as_str(); let hint = match word { @@ -941,20 +960,38 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec _ => match int_type_of_word(word) { Some(ity) => Some(ReprInt(ity)), None => { - // Not a word we recognize - span_err!(diagnostic, item.span, E0552, - "unrecognized representation hint"); None } } }; if let Some(h) = hint { + recognised = true; acc.push(h); } - } else { - span_err!(diagnostic, item.span, E0553, - "unrecognized enum representation hint"); + } else if let Some((name, value)) = item.name_value_literal() { + if name == "align" { + recognised = true; + let mut valid_align = false; + if let ast::LitKind::Int(align, ast::LitIntType::Unsuffixed) = value.node { + if align.is_power_of_two() { + // rustc::ty::layout::Align restricts align to <= 32768 + if align <= 32768 { + acc.push(ReprAlign(align as u16)); + valid_align = true; + } + } + } + if !valid_align { + span_err!(diagnostic, item.span, E0589, + "align representation must be a u16 power of two"); + } + } + } + if !recognised { + // Not a word we recognize + span_err!(diagnostic, item.span, E0552, + "unrecognized representation hint"); } } } @@ -986,6 +1023,7 @@ pub enum ReprAttr { ReprExtern, ReprPacked, ReprSimd, + ReprAlign(u16), } #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index 2d59051ec4a53..775775875a4d1 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -287,10 +287,10 @@ register_diagnostics! { E0550, // multiple deprecated attributes E0551, // incorrect meta item E0552, // unrecognized representation hint - E0553, // unrecognized enum representation hint E0554, // #[feature] may not be used on the [] release channel E0555, // malformed feature attribute, expected #![feature(...)] E0556, // malformed feature, expected just one word E0557, // feature has been removed E0584, // file for module `..` found at both .. and .. + E0589, // align representation must be a u16 power of two } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 1ff0fec1c96a6..e96883c26f33a 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -773,7 +773,7 @@ fn find_repr_type_name(diagnostic: &Handler, type_attrs: &[ast::Attribute]) -> & for a in type_attrs { for r in &attr::find_repr_attrs(diagnostic, a) { repr_type_name = match *r { - attr::ReprPacked | attr::ReprSimd => continue, + attr::ReprPacked | attr::ReprSimd | attr::ReprAlign(_) => continue, attr::ReprExtern => "i32", attr::ReprInt(attr::SignedInt(ast::IntTy::Is)) => "isize", diff --git a/src/test/compile-fail/attr-usage-repr.rs b/src/test/compile-fail/attr-usage-repr.rs index b07d3e2f90675..c0bfd3690c859 100644 --- a/src/test/compile-fail/attr-usage-repr.rs +++ b/src/test/compile-fail/attr-usage-repr.rs @@ -9,6 +9,7 @@ // except according to those terms. #![allow(dead_code)] +#![feature(attr_literals)] #![feature(repr_simd)] #[repr(C)] //~ ERROR: attribute should be applied to struct, enum or union @@ -29,6 +30,9 @@ struct SInt(f64, f64); #[repr(C)] enum EExtern { A, B } +#[repr(align(8))] //~ ERROR: attribute should be applied to struct +enum EAlign { A, B } + #[repr(packed)] //~ ERROR: attribute should be applied to struct enum EPacked { A, B } diff --git a/src/test/compile-fail/conflicting-repr-hints.rs b/src/test/compile-fail/conflicting-repr-hints.rs index 9e0c0d845ca24..af38673d3650e 100644 --- a/src/test/compile-fail/conflicting-repr-hints.rs +++ b/src/test/compile-fail/conflicting-repr-hints.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_attrs)] #![allow(dead_code)] +#![feature(attr_literals)] #[repr(C)] enum A { A } @@ -26,5 +26,7 @@ enum D { D } #[repr(C, packed)] struct E(i32); -#[rustc_error] -fn main() {} //~ ERROR compilation successful +#[repr(packed, align(8))] //~ ERROR conflicting packed and align representation hints +struct F(i32); + +fn main() {} diff --git a/src/test/compile-fail/repr-align.rs b/src/test/compile-fail/repr-align.rs new file mode 100644 index 0000000000000..96dfe1c05f9a7 --- /dev/null +++ b/src/test/compile-fail/repr-align.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(dead_code)] +#![feature(attr_literals)] + +#[repr(align(16.0))] //~ ERROR: align representation must be a u16 power of two +struct A(i32); + +#[repr(align(15))] //~ ERROR: align representation must be a u16 power of two +struct B(i32); + +#[repr(align(65536))] //~ ERROR: align representation must be a u16 power of tw +struct C(i32); + +fn main() {} diff --git a/src/test/compile-fail/repr-packed-contains-align.rs b/src/test/compile-fail/repr-packed-contains-align.rs new file mode 100644 index 0000000000000..11829dfae8cbe --- /dev/null +++ b/src/test/compile-fail/repr-packed-contains-align.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(attr_literals)] +#![allow(dead_code)] + +#[repr(align(16))] +struct A(i32); + +struct B(A); + +#[repr(packed)] +struct C(A); //~ ERROR: packed struct cannot transitively contain a `[repr(align)]` struct + +#[repr(packed)] +struct D(B); //~ ERROR: packed struct cannot transitively contain a `[repr(align)]` struct + +fn main() {} diff --git a/src/test/run-pass/align-struct.rs b/src/test/run-pass/align-struct.rs new file mode 100644 index 0000000000000..4793189d366df --- /dev/null +++ b/src/test/run-pass/align-struct.rs @@ -0,0 +1,195 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(attr_literals)] + +use std::mem; + +// Raising alignment +#[repr(align(16))] +struct Align16(i32); + +// Lowering has no effect +#[repr(align(1))] +struct Align1(i32); + +// Multiple attributes take the max +#[repr(align(4))] +#[repr(align(16))] +#[repr(align(8))] +struct AlignMany(i32); + +// Raising alignment may not alter size. +#[repr(align(8))] +#[allow(dead_code)] +struct Align8Many { + a: i32, + b: i32, + c: i32, + d: u8, +} + +enum Enum { + #[allow(dead_code)] + A(i32), + B(Align16) +} + +// Nested alignment - use `#[repr(C)]` to suppress field reordering for sizeof test +#[repr(C)] +struct Nested { + a: i32, + b: i32, + c: Align16, + d: i8, +} + +#[repr(packed)] +struct Packed(i32); + +#[repr(align(16))] +struct AlignContainsPacked { + a: Packed, + b: Packed, +} + +impl Align16 { + // return aligned type + pub fn new(i: i32) -> Align16 { + Align16(i) + } + // pass aligned type + pub fn consume(a: Align16) -> i32 { + a.0 + } +} + +const CONST_ALIGN16: Align16 = Align16(7); +static STATIC_ALIGN16: Align16 = Align16(8); + +// Check the actual address is aligned +fn is_aligned_to(p: &T, align: usize) -> bool { + let addr = p as *const T as usize; + (addr & (align - 1)) == 0 +} + +pub fn main() { + // check alignment and size by type and value + assert_eq!(mem::align_of::(), 16); + assert_eq!(mem::size_of::(), 16); + + let a = Align16(7); + assert_eq!(a.0, 7); + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::size_of_val(&a), 16); + + assert!(is_aligned_to(&a, 16)); + + // lowering should have no effect + assert_eq!(mem::align_of::(), 4); + assert_eq!(mem::size_of::(), 4); + let a = Align1(7); + assert_eq!(a.0, 7); + assert_eq!(mem::align_of_val(&a), 4); + assert_eq!(mem::size_of_val(&a), 4); + assert!(is_aligned_to(&a, 4)); + + // when multiple attributes are specified the max should be used + assert_eq!(mem::align_of::(), 16); + assert_eq!(mem::size_of::(), 16); + let a = AlignMany(7); + assert_eq!(a.0, 7); + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::size_of_val(&a), 16); + assert!(is_aligned_to(&a, 16)); + + // raising alignment should not reduce size + assert_eq!(mem::align_of::(), 8); + assert_eq!(mem::size_of::(), 16); + let a = Align8Many { a: 1, b: 2, c: 3, d: 4 }; + assert_eq!(a.a, 1); + assert_eq!(mem::align_of_val(&a), 8); + assert_eq!(mem::size_of_val(&a), 16); + assert!(is_aligned_to(&a, 8)); + + // return type + let a = Align16::new(1); + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::size_of_val(&a), 16); + assert_eq!(a.0, 1); + assert!(is_aligned_to(&a, 16)); + assert_eq!(Align16::consume(a), 1); + + // check const alignment, size and value + assert_eq!(mem::align_of_val(&CONST_ALIGN16), 16); + assert_eq!(mem::size_of_val(&CONST_ALIGN16), 16); + assert_eq!(CONST_ALIGN16.0, 7); + assert!(is_aligned_to(&CONST_ALIGN16, 16)); + + // check global static alignment, size and value + assert_eq!(mem::align_of_val(&STATIC_ALIGN16), 16); + assert_eq!(mem::size_of_val(&STATIC_ALIGN16), 16); + assert_eq!(STATIC_ALIGN16.0, 8); + assert!(is_aligned_to(&STATIC_ALIGN16, 16)); + + // Note that the size of Nested may change if struct field re-ordering is enabled + assert_eq!(mem::align_of::(), 16); + assert_eq!(mem::size_of::(), 48); + let a = Nested{ a: 1, b: 2, c: Align16(3), d: 4}; + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::align_of_val(&a.b), 4); + assert_eq!(mem::align_of_val(&a.c), 16); + assert_eq!(mem::size_of_val(&a), 48); + assert!(is_aligned_to(&a, 16)); + // check the correct fields are indexed + assert_eq!(a.a, 1); + assert_eq!(a.b, 2); + assert_eq!(a.c.0, 3); + assert_eq!(a.d, 4); + + // enum should be aligned to max alignment + assert_eq!(mem::align_of::(), 16); + assert_eq!(mem::align_of_val(&Enum::B(Align16(0))), 16); + let e = Enum::B(Align16(15)); + match e { + Enum::B(ref a) => { + assert_eq!(a.0, 15); + assert_eq!(mem::align_of_val(a), 16); + assert_eq!(mem::size_of_val(a), 16); + }, + _ => () + } + assert!(is_aligned_to(&e, 16)); + + // arrays of aligned elements should also be aligned + assert_eq!(mem::align_of::<[Align16;2]>(), 16); + assert_eq!(mem::size_of::<[Align16;2]>(), 32); + + let a = [Align16(0), Align16(1)]; + assert_eq!(mem::align_of_val(&a[0]), 16); + assert_eq!(mem::align_of_val(&a[1]), 16); + assert!(is_aligned_to(&a, 16)); + + // check heap value is aligned + assert_eq!(mem::align_of_val(Box::new(Align16(0)).as_ref()), 16); + + // check heap array is aligned + let a = vec!(Align16(0), Align16(1)); + assert_eq!(mem::align_of_val(&a[0]), 16); + assert_eq!(mem::align_of_val(&a[1]), 16); + + assert_eq!(mem::align_of::(), 16); + assert_eq!(mem::size_of::(), 16); + let a = AlignContainsPacked { a: Packed(1), b: Packed(2) }; + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::align_of_val(&a.a), 1); + assert_eq!(mem::align_of_val(&a.b), 1); + assert_eq!(mem::size_of_val(&a), 16); + assert!(is_aligned_to(&a, 16)); +} diff --git a/src/test/ui/print_type_sizes/repr-align.rs b/src/test/ui/print_type_sizes/repr-align.rs new file mode 100644 index 0000000000000..ea2910f86c458 --- /dev/null +++ b/src/test/ui/print_type_sizes/repr-align.rs @@ -0,0 +1,42 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// This file illustrates how padding is handled: alignment +// requirements can lead to the introduction of padding, either before +// fields or at the end of the structure as a whole. +// +// It avoids using u64/i64 because on some targets that is only 4-byte +// aligned (while on most it is 8-byte aligned) and so the resulting +// padding and overall computed sizes can be quite different. +#![feature(attr_literals)] +#![allow(dead_code)] + +#[repr(align(16))] +#[derive(Default)] +struct A(i32); + +enum E { + A(i32), + B(A) +} + +#[derive(Default)] +struct S { + a: i32, + b: i32, + c: A, + d: i8, +} + +fn main() { + let _s: S = Default::default(); +} diff --git a/src/test/ui/print_type_sizes/repr-align.stdout b/src/test/ui/print_type_sizes/repr-align.stdout new file mode 100644 index 0000000000000..7df12f040b15d --- /dev/null +++ b/src/test/ui/print_type_sizes/repr-align.stdout @@ -0,0 +1,16 @@ +print-type-size type: `E`: 32 bytes, alignment: 16 bytes +print-type-size discriminant: 4 bytes +print-type-size variant `A`: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size variant `B`: 28 bytes +print-type-size padding: 12 bytes +print-type-size field `.0`: 16 bytes, alignment: 16 bytes +print-type-size type: `S`: 32 bytes, alignment: 16 bytes +print-type-size field `.c`: 16 bytes +print-type-size field `.a`: 4 bytes +print-type-size field `.b`: 4 bytes +print-type-size field `.d`: 1 bytes +print-type-size end padding: 7 bytes +print-type-size type: `A`: 16 bytes, alignment: 16 bytes +print-type-size field `.0`: 4 bytes +print-type-size end padding: 12 bytes From 7971a47eff4b178ffe7c38c67b1926dacea4c0dd Mon Sep 17 00:00:00 2001 From: Cameron Hart Date: Sat, 25 Mar 2017 19:00:49 +1100 Subject: [PATCH 23/33] Added feature gate, updated error messages and tests. --- src/doc/unstable-book/src/SUMMARY.md | 1 + .../src/language-features/repr-align.md | 11 +++++++++++ src/libsyntax/attr.rs | 13 +++++++++---- src/libsyntax/diagnostic_list.rs | 2 +- src/libsyntax/feature_gate.rs | 8 ++++++++ src/test/compile-fail/conflicting-repr-hints.rs | 1 + src/test/compile-fail/feature-gate-repr_align.rs | 15 +++++++++++++++ src/test/compile-fail/repr-align.rs | 7 ++++--- .../compile-fail/repr-packed-contains-align.rs | 1 + src/test/run-pass/align-struct.rs | 1 + src/test/ui/print_type_sizes/repr-align.rs | 1 + 11 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/repr-align.md create mode 100644 src/test/compile-fail/feature-gate-repr_align.rs diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md index 3e0415439774c..ee4e568a5ca9a 100644 --- a/src/doc/unstable-book/src/SUMMARY.md +++ b/src/doc/unstable-book/src/SUMMARY.md @@ -72,6 +72,7 @@ - [proc_macro](language-features/proc-macro.md) - [quote](language-features/quote.md) - [relaxed_adts](language-features/relaxed-adts.md) + - [repr_align](language-features/repr-align.md) - [repr_simd](language-features/repr-simd.md) - [rustc_attrs](language-features/rustc-attrs.md) - [rustc_diagnostic_macros](language-features/rustc-diagnostic-macros.md) diff --git a/src/doc/unstable-book/src/language-features/repr-align.md b/src/doc/unstable-book/src/language-features/repr-align.md new file mode 100644 index 0000000000000..deea04f4c51cc --- /dev/null +++ b/src/doc/unstable-book/src/language-features/repr-align.md @@ -0,0 +1,11 @@ +# `repr_align` + +The tracking issue for this feature is: [#33626] + +[#33626]: https://github.com/rust-lang/rust/issues/33626 + +------------------------ + + + + diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 442345ceb3c18..82492d976276e 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -972,19 +972,24 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec } else if let Some((name, value)) = item.name_value_literal() { if name == "align" { recognised = true; - let mut valid_align = false; + let mut align_error = None; if let ast::LitKind::Int(align, ast::LitIntType::Unsuffixed) = value.node { if align.is_power_of_two() { // rustc::ty::layout::Align restricts align to <= 32768 if align <= 32768 { acc.push(ReprAlign(align as u16)); - valid_align = true; + } else { + align_error = Some("larger than 32768"); } + } else { + align_error = Some("not a power of two"); } + } else { + align_error = Some("not an unsuffixed integer"); } - if !valid_align { + if let Some(align_error) = align_error { span_err!(diagnostic, item.span, E0589, - "align representation must be a u16 power of two"); + "invalid `repr(align)` attribute: {}", align_error); } } } diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index 775775875a4d1..01d1277ea6265 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -292,5 +292,5 @@ register_diagnostics! { E0556, // malformed feature, expected just one word E0557, // feature has been removed E0584, // file for module `..` found at both .. and .. - E0589, // align representation must be a u16 power of two + E0589, // invalid `repr(align)` attribute } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 175447e111270..9b55a860b3595 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -338,6 +338,9 @@ declare_features! ( // Allows the `catch {...}` expression (active, catch_expr, "1.17.0", Some(31436)), + // Allows `repr(align(u16))` struct attribute (RFC 1358) + (active, repr_align, "1.17.0", Some(33626)), + // See rust-lang/rfcs#1414. Allows code like `let x: &'static u32 = &42` to work. (active, rvalue_static_promotion, "1.15.1", Some(38865)), @@ -1189,6 +1192,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { and possibly buggy"); } + if item.check_name("align") { + gate_feature_post!(&self, repr_align, i.span, + "the struct `#[repr(align(u16))]` attribute \ + is experimental"); + } } } } diff --git a/src/test/compile-fail/conflicting-repr-hints.rs b/src/test/compile-fail/conflicting-repr-hints.rs index af38673d3650e..01fa3ffbaa6ae 100644 --- a/src/test/compile-fail/conflicting-repr-hints.rs +++ b/src/test/compile-fail/conflicting-repr-hints.rs @@ -10,6 +10,7 @@ #![allow(dead_code)] #![feature(attr_literals)] +#![feature(repr_align)] #[repr(C)] enum A { A } diff --git a/src/test/compile-fail/feature-gate-repr_align.rs b/src/test/compile-fail/feature-gate-repr_align.rs new file mode 100644 index 0000000000000..8e986e197f269 --- /dev/null +++ b/src/test/compile-fail/feature-gate-repr_align.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(attr_literals)] + +#[repr(align(64))] +struct Foo(u64, u64); //~ error: the struct `#[repr(align(u16))]` attribute is experimental + +fn main() {} diff --git a/src/test/compile-fail/repr-align.rs b/src/test/compile-fail/repr-align.rs index 96dfe1c05f9a7..eb0b27fe9c07e 100644 --- a/src/test/compile-fail/repr-align.rs +++ b/src/test/compile-fail/repr-align.rs @@ -9,14 +9,15 @@ // except according to those terms. #![allow(dead_code)] #![feature(attr_literals)] +#![feature(repr_align)] -#[repr(align(16.0))] //~ ERROR: align representation must be a u16 power of two +#[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer struct A(i32); -#[repr(align(15))] //~ ERROR: align representation must be a u16 power of two +#[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two struct B(i32); -#[repr(align(65536))] //~ ERROR: align representation must be a u16 power of tw +#[repr(align(65536))] //~ ERROR: invalid `repr(align)` attribute: larger than 32768 struct C(i32); fn main() {} diff --git a/src/test/compile-fail/repr-packed-contains-align.rs b/src/test/compile-fail/repr-packed-contains-align.rs index 11829dfae8cbe..c584dcf3e5993 100644 --- a/src/test/compile-fail/repr-packed-contains-align.rs +++ b/src/test/compile-fail/repr-packed-contains-align.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![feature(attr_literals)] +#![feature(repr_align)] #![allow(dead_code)] #[repr(align(16))] diff --git a/src/test/run-pass/align-struct.rs b/src/test/run-pass/align-struct.rs index 4793189d366df..0b9a3594502b0 100644 --- a/src/test/run-pass/align-struct.rs +++ b/src/test/run-pass/align-struct.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![feature(attr_literals)] +#![feature(repr_align)] use std::mem; diff --git a/src/test/ui/print_type_sizes/repr-align.rs b/src/test/ui/print_type_sizes/repr-align.rs index ea2910f86c458..e9b43145de469 100644 --- a/src/test/ui/print_type_sizes/repr-align.rs +++ b/src/test/ui/print_type_sizes/repr-align.rs @@ -18,6 +18,7 @@ // aligned (while on most it is 8-byte aligned) and so the resulting // padding and overall computed sizes can be quite different. #![feature(attr_literals)] +#![feature(repr_align)] #![allow(dead_code)] #[repr(align(16))] From c219cdfa11b0bb23f1dabbef9415ffb94f10386a Mon Sep 17 00:00:00 2001 From: Cameron Hart Date: Mon, 10 Apr 2017 21:40:24 +1200 Subject: [PATCH 24/33] Removed sizing parameter from struct_llfields. --- src/librustc_trans/adt.rs | 26 +++++++++----------------- src/librustc_trans/mir/lvalue.rs | 2 +- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 3c73aba7cd7ed..5326e9344c89a 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -90,12 +90,12 @@ pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, /// and fill in the actual contents in a second pass to prevent /// unbounded recursion; see also the comments in `trans::type_of`. pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { - generic_type_of(cx, t, None, false) + generic_type_of(cx, t, None) } pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, name: &str) -> Type { - generic_type_of(cx, t, Some(name), false) + generic_type_of(cx, t, Some(name)) } pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, @@ -114,7 +114,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, _ => unreachable!() }; let fields = compute_fields(cx, t, nonnull_variant_index as usize, true); - llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant, false), + llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant), packed) }, _ => bug!("This function cannot handle {} with layout {:#?}", t, l) @@ -123,10 +123,9 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, - name: Option<&str>, - sizing: bool) -> Type { + name: Option<&str>) -> Type { let l = cx.layout_of(t); - debug!("adt::generic_type_of t: {:?} name: {:?} sizing: {}", t, name, sizing); + debug!("adt::generic_type_of t: {:?} name: {:?}", t, name); match *l { layout::CEnum { discr, .. } => Type::from_integer(cx, discr), layout::RawNullablePointer { nndiscr, .. } => { @@ -146,11 +145,10 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fields = compute_fields(cx, t, nndiscr as usize, false); match name { None => { - Type::struct_(cx, &struct_llfields(cx, &fields, nonnull, sizing), + Type::struct_(cx, &struct_llfields(cx, &fields, nonnull), nonnull.packed) } Some(name) => { - assert_eq!(sizing, false); Type::named_struct(cx, name) } } @@ -161,13 +159,12 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fields = compute_fields(cx, t, 0, true); match name { None => { - let fields = struct_llfields(cx, &fields, &variant, sizing); + let fields = struct_llfields(cx, &fields, &variant); Type::struct_(cx, &fields, variant.packed) } Some(name) => { // Hypothesis: named_struct's can never need a // drop flag. (... needs validation.) - assert_eq!(sizing, false); Type::named_struct(cx, name) } } @@ -256,19 +253,14 @@ pub fn struct_llfields_index(variant: &layout::Struct, index: usize) -> usize { pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, field_tys: &Vec>, - variant: &layout::Struct, - sizing: bool) -> Vec { - if sizing { - bug!(); - } + variant: &layout::Struct) -> Vec { debug!("struct_llfields: variant: {:?}", variant); let mut first_field = true; let mut min_offset = 0; let mut result: Vec = Vec::with_capacity(field_tys.len() * 2); let field_iter = variant.field_index_by_increasing_offset().map(|i| { (i, field_tys[i as usize], variant.offsets[i as usize].bytes()) }); - for (index, ty, target_offset) in field_iter.filter( - |&(_, ty, _)| !sizing || cx.shared().type_is_sized(ty)) { + for (index, ty, target_offset) in field_iter { if first_field { debug!("struct_llfields: {} ty: {} min_offset: {} target_offset: {}", index, ty, min_offset, target_offset); diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index 9a461e4aafc23..88e46b5c99a44 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -132,7 +132,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> { let alignment = self.alignment | Alignment::from_packed(st.packed); - let llfields = adt::struct_llfields(ccx, fields, st, false); + let llfields = adt::struct_llfields(ccx, fields, st); let ptr_val = if needs_cast { let real_ty = Type::struct_(ccx, &llfields[..], st.packed); bcx.pointercast(self.llval, real_ty.ptr_to()) From 910532ea4559684e16d4acbd058c643599fe9536 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Thu, 20 Apr 2017 16:13:13 -0700 Subject: [PATCH 25/33] Don't panic if an attribute macro fails to resolve at crate root Adds temporary regression test; this ideally should work as-is (#41430) Closes #41211 --- src/libsyntax/ext/expand.rs | 14 +++++++++-- .../proc-macro/auxiliary/issue-41211.rs | 23 +++++++++++++++++++ .../proc-macro/issue-41211.rs | 22 ++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue-41211.rs create mode 100644 src/test/compile-fail-fulldeps/proc-macro/issue-41211.rs diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 680bd7599ace0..842398ea02b9e 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -205,6 +205,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { module.directory.pop(); self.cx.current_expansion.module = Rc::new(module); + let orig_mod_span = krate.module.inner; + let krate_item = Expansion::Items(SmallVector::one(P(ast::Item { attrs: krate.attrs, span: krate.span, @@ -214,11 +216,19 @@ impl<'a, 'b> MacroExpander<'a, 'b> { vis: ast::Visibility::Public, }))); - match self.expand(krate_item).make_items().pop().unwrap().unwrap() { - ast::Item { attrs, node: ast::ItemKind::Mod(module), .. } => { + match self.expand(krate_item).make_items().pop().map(P::unwrap) { + Some(ast::Item { attrs, node: ast::ItemKind::Mod(module), .. }) => { krate.attrs = attrs; krate.module = module; }, + None => { + // Resolution failed so we return an empty expansion + krate.attrs = vec![]; + krate.module = ast::Mod { + inner: orig_mod_span, + items: vec![], + }; + }, _ => unreachable!(), }; diff --git a/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue-41211.rs b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue-41211.rs new file mode 100644 index 0000000000000..99400bd147c20 --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue-41211.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro)] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn emit_unchanged(_args: TokenStream, input: TokenStream) -> TokenStream { + input +} diff --git a/src/test/compile-fail-fulldeps/proc-macro/issue-41211.rs b/src/test/compile-fail-fulldeps/proc-macro/issue-41211.rs new file mode 100644 index 0000000000000..17237912be49b --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/issue-41211.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:issue-41211.rs + +// FIXME: https://github.com/rust-lang/rust/issues/41430 +// This is a temporary regression test for the ICE reported in #41211 + +#![feature(proc_macro)] +#![emit_unchanged] +//~^ ERROR: cannot find attribute macro `emit_unchanged` in this scope +extern crate issue_41211; +use issue_41211::emit_unchanged; + +fn main() {} From 903bdfccd0ecc5bf1aa4207df365ea088de2c3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 20 Apr 2017 17:06:39 -0700 Subject: [PATCH 26/33] Add test for issue 33884 Fix #33884 --- src/test/ui/span/issue-33884.rs | 27 +++++++++++++++++++++++++++ src/test/ui/span/issue-33884.stderr | 12 ++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/test/ui/span/issue-33884.rs create mode 100644 src/test/ui/span/issue-33884.stderr diff --git a/src/test/ui/span/issue-33884.rs b/src/test/ui/span/issue-33884.rs new file mode 100644 index 0000000000000..93aa502ee1531 --- /dev/null +++ b/src/test/ui/span/issue-33884.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::net::TcpListener; +use std::net::TcpStream; +use std::io::{self, Read, Write}; + +fn handle_client(stream: TcpStream) -> io::Result<()> { + stream.write_fmt(format!("message received")) +} + +fn main() { + if let Ok(listener) = TcpListener::bind("127.0.0.1:8080") { + for incoming in listener.incoming() { + if let Ok(stream) = incoming { + handle_client(stream); + } + } + } +} diff --git a/src/test/ui/span/issue-33884.stderr b/src/test/ui/span/issue-33884.stderr new file mode 100644 index 0000000000000..2a874181c7ad9 --- /dev/null +++ b/src/test/ui/span/issue-33884.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/issue-33884.rs:16:22 + | +16 | stream.write_fmt(format!("message received")) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::fmt::Arguments`, found struct `std::string::String` + | + = note: expected type `std::fmt::Arguments<'_>` + found type `std::string::String` + = note: this error originates in a macro outside of the current crate + +error: aborting due to previous error + From cc07c357e421eebff58eb6948c8e1412ae7d8069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 14 Apr 2017 16:38:10 -0700 Subject: [PATCH 27/33] Reduce visual clutter of multiline start when possible When a span starts on a line with nothing but whitespace to the left, and there are no other annotations in that line, simplify the visual representation of the span. Go from: ```rust error[E0072]: recursive type `A` has infinite size --> file2.rs:1:1 | 1 | struct A { | _^ starting here... 2 | | a: A, 3 | | } | |_^ ...ending here: recursive type has infinite size | ``` To: ```rust error[E0072]: recursive type `A` has infinite size --> file2.rs:1:1 | 1 | / struct A { 2 | | a: A, 3 | | } | |_^ recursive type has infinite size ``` Remove `starting here...`/`...ending here` labels from all multiline diagnostics. --- src/librustc_errors/emitter.rs | 67 ++++++++++--- src/librustc_errors/snippet.rs | 22 +++-- src/libsyntax/test_snippet.rs | 93 +++++++++---------- .../ui/compare-method/region-extra-2.stderr | 5 +- .../traits-misc-mismatch-2.stderr | 5 +- src/test/ui/did_you_mean/issue-40006.stderr | 16 ++-- ...dropck-eyepatch-implies-unsafe-impl.stderr | 10 +- .../issue-37311.stderr | 5 +- ...x1-return-one-existing-name-if-else.stderr | 8 +- .../ex2a-push-one-existing-name.stderr | 8 +- .../ex2b-push-no-existing-names.stderr | 8 +- .../ex2c-push-inference-variable.stderr | 8 +- .../ex2d-push-inference-variable-2.stderr | 8 +- .../ex2e-push-inference-variable-3.stderr | 8 +- src/test/ui/mismatched_types/abridged.stderr | 10 +- src/test/ui/mismatched_types/main.stderr | 4 +- src/test/ui/missing-items/m2.stderr | 5 +- .../ui/span/impl-wrong-item-for-trait.stderr | 20 ++-- src/test/ui/span/issue-23729.stderr | 5 +- src/test/ui/span/issue-23827.stderr | 5 +- src/test/ui/span/issue-24356.stderr | 5 +- src/test/ui/span/issue-7575.stderr | 10 +- src/test/ui/span/lint-unused-unsafe.stderr | 30 +++--- src/test/ui/span/multiline-span-E0072.stderr | 5 +- src/test/ui/span/multiline-span-simple.stderr | 4 +- src/test/ui/type-check/issue-40294.stderr | 5 +- 26 files changed, 200 insertions(+), 179 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index a52628ceb47ab..64652bb308bdc 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -263,6 +263,41 @@ impl EmitterWriter { draw_col_separator(buffer, line_offset, width_offset - 2); + // Special case when there's only one annotation involved, it is the start of a multiline + // span and there's no text at the beginning of the code line. Instead of doing the whole + // graph: + // + // 2 | fn foo() { + // | _^ + // 3 | | + // 4 | | } + // | |_^ test + // + // we simplify the output to: + // + // 2 | / fn foo() { + // 3 | | + // 4 | | } + // | |_^ test + if line.annotations.len() == 1 { + if let Some(ref ann) = line.annotations.get(0) { + if let AnnotationType::MultilineStart(depth) = ann.annotation_type { + if source_string[0..ann.start_col].trim() == "" { + let style = if ann.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }; + buffer.putc(line_offset, + width_offset + depth - 1, + '/', + style); + return vec![(depth, style)]; + } + } + } + } + // We want to display like this: // // vec.push(vec.pop().unwrap()); @@ -355,10 +390,8 @@ impl EmitterWriter { for (i, annotation) in annotations.iter().enumerate() { for (j, next) in annotations.iter().enumerate() { if overlaps(next, annotation, 0) // This label overlaps with another one and both - && !annotation.is_line() // take space (they have text and are not - && !next.is_line() // multiline lines). - && annotation.has_label() - && j > i + && annotation.has_label() // take space (they have text and are not + && j > i // multiline lines). && p == 0 // We're currently on the first line, move the label one line down { // This annotation needs a new line in the output. @@ -374,7 +407,7 @@ impl EmitterWriter { } else { 0 }; - if overlaps(next, annotation, l) // Do not allow two labels to be in the same + if (overlaps(next, annotation, l) // Do not allow two labels to be in the same // line if they overlap including padding, to // avoid situations like: // @@ -383,11 +416,18 @@ impl EmitterWriter { // | | // fn_spanx_span // - && !annotation.is_line() // Do not add a new line if this annotation - && !next.is_line() // or the next are vertical line placeholders. && annotation.has_label() // Both labels must have some text, otherwise - && next.has_label() // they are not overlapping. + && next.has_label()) // they are not overlapping. + // Do not add a new line if this annotation + // or the next are vertical line placeholders. + || (annotation.takes_space() // If either this or the next annotation is + && next.has_label()) // multiline start/end, move it to a new line + || (annotation.has_label() // so as not to overlap the orizontal lines. + && next.takes_space()) + || (annotation.takes_space() + && next.takes_space()) { + // This annotation needs a new line in the output. p += 1; break; } @@ -397,6 +437,7 @@ impl EmitterWriter { line_len = p; } } + if line_len != 0 { line_len += 1; } @@ -480,7 +521,7 @@ impl EmitterWriter { }; let pos = pos + 1; - if pos > 1 && annotation.has_label() { + if pos > 1 && (annotation.has_label() || annotation.takes_space()) { for p in line_offset + 1..line_offset + pos + 1 { buffer.putc(p, code_offset + annotation.start_col, @@ -514,12 +555,12 @@ impl EmitterWriter { // After this we will have: // // 2 | fn foo() { - // | __________ starting here... + // | __________ // | | // | something about `foo` // 3 | // 4 | } - // | _ ...ending here: test + // | _ test for &(pos, annotation) in &annotations_position { let style = if annotation.is_primary { Style::LabelPrimary @@ -557,12 +598,12 @@ impl EmitterWriter { // After this we will have: // // 2 | fn foo() { - // | ____-_____^ starting here... + // | ____-_____^ // | | // | something about `foo` // 3 | // 4 | } - // | _^ ...ending here: test + // | _^ test for &(_, annotation) in &annotations_position { let (underline, style) = if annotation.is_primary { ('^', Style::UnderlinePrimary) diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs index 9aa4682e1afcb..7401ead22089b 100644 --- a/src/librustc_errors/snippet.rs +++ b/src/librustc_errors/snippet.rs @@ -63,7 +63,7 @@ impl MultilineAnnotation { start_col: self.start_col, end_col: self.start_col + 1, is_primary: self.is_primary, - label: Some("starting here...".to_owned()), + label: None, annotation_type: AnnotationType::MultilineStart(self.depth) } } @@ -73,10 +73,7 @@ impl MultilineAnnotation { start_col: self.end_col - 1, end_col: self.end_col, is_primary: self.is_primary, - label: match self.label { - Some(ref label) => Some(format!("...ending here: {}", label)), - None => Some("...ending here".to_owned()), - }, + label: self.label.clone(), annotation_type: AnnotationType::MultilineEnd(self.depth) } } @@ -106,9 +103,9 @@ pub enum AnnotationType { // Each of these corresponds to one part of the following diagram: // // x | foo(1 + bar(x, - // | _________^ starting here... < MultilineStart - // x | | y), < MultilineLine - // | |______________^ ...ending here: label < MultilineEnd + // | _________^ < MultilineStart + // x | | y), < MultilineLine + // | |______________^ label < MultilineEnd // x | z); /// Annotation marking the first character of a fully shown multiline span MultilineStart(usize), @@ -189,6 +186,15 @@ impl Annotation { false } } + + pub fn takes_space(&self) -> bool { + // Multiline annotations always have to keep vertical space. + match self.annotation_type { + AnnotationType::MultilineStart(_) | + AnnotationType::MultilineEnd(_) => true, + _ => false, + } + } } #[derive(Debug)] diff --git a/src/libsyntax/test_snippet.rs b/src/libsyntax/test_snippet.rs index a74f59b004bb7..dc9b22c37e28b 100644 --- a/src/libsyntax/test_snippet.rs +++ b/src/libsyntax/test_snippet.rs @@ -128,9 +128,9 @@ error: foo --> test.rs:2:10 | 2 | fn foo() { - | __________^ starting here... + | __________^ 3 | | } - | |_^ ...ending here: test + | |_^ test "#); } @@ -161,11 +161,11 @@ error: foo --> test.rs:2:10 | 2 | fn foo() { - | __________^ starting here... + | __________^ 3 | | 4 | | 5 | | } - | |___^ ...ending here: test + | |___^ test "#); } @@ -207,14 +207,14 @@ error: foo --> test.rs:3:3 | 3 | X0 Y0 - | ____^__- starting here... + | ____^__- | | ___| - | || starting here... + | || 4 | || X1 Y1 5 | || X2 Y2 - | ||____^__- ...ending here: `Y` is a good letter too + | ||____^__- `Y` is a good letter too | |____| - | ...ending here: `X` is a good letter + | `X` is a good letter "#); } @@ -256,13 +256,13 @@ error: foo --> test.rs:3:3 | 3 | X0 Y0 - | ____^__- starting here... + | ____^__- | | ___| - | || starting here... + | || 4 | || Y1 X1 - | ||____-__^ ...ending here: `X` is a good letter + | ||____-__^ `X` is a good letter | |_____| - | ...ending here: `Y` is a good letter too + | `Y` is a good letter too "#); } @@ -306,13 +306,13 @@ error: foo --> test.rs:3:6 | 3 | X0 Y0 Z0 - | ______^ starting here... + | ______^ 4 | | X1 Y1 Z1 - | |_________- starting here... + | |_________- 5 | || X2 Y2 Z2 - | ||____^ ...ending here: `X` is a good letter + | ||____^ `X` is a good letter 6 | | X3 Y3 Z3 - | |_____- ...ending here: `Y` is a good letter too + | |_____- `Y` is a good letter too "#); } @@ -366,16 +366,16 @@ error: foo --> test.rs:3:3 | 3 | X0 Y0 Z0 - | _____^__-__- starting here... + | _____^__-__- | | ____|__| - | || ___| starting here... - | ||| starting here... + | || ___| + | ||| 4 | ||| X1 Y1 Z1 5 | ||| X2 Y2 Z2 - | |||____^__-__- ...ending here: `Z` label + | |||____^__-__- `Z` label | ||____|__| - | |____| ...ending here: `Y` is a good letter too - | ...ending here: `X` is a good letter + | |____| `Y` is a good letter too + | `X` is a good letter "#); } @@ -430,17 +430,17 @@ error: foo --> test.rs:3:6 | 3 | X0 Y0 Z0 - | ______^ starting here... + | ______^ 4 | | X1 Y1 Z1 - | |____^_- starting here... + | |____^_- | ||____| - | | ...ending here: `X` is a good letter + | | `X` is a good letter 5 | | X2 Y2 Z2 - | |____-______- ...ending here: `Y` is a good letter too + | |____-______- `Y` is a good letter too | ____| - | | starting here... + | | 6 | | X3 Y3 Z3 - | |________- ...ending here: `Z` + | |________- `Z` "#); } @@ -458,7 +458,7 @@ fn foo() { vec![ SpanLabel { start: Position { - string: "Y0", + string: "X0", count: 1, }, end: Position { @@ -481,16 +481,15 @@ fn foo() { ], r#" error: foo - --> test.rs:3:6 + --> test.rs:3:3 | -3 | X0 Y0 Z0 - | ______^ starting here... +3 | / X0 Y0 Z0 4 | | X1 Y1 Z1 - | |____^ ...ending here: `X` is a good letter + | |____^ `X` is a good letter 5 | X2 Y2 Z2 - | ______- starting here... + | ______- 6 | | X3 Y3 Z3 - | |__________- ...ending here: `Y` is a good letter too + | |__________- `Y` is a good letter too "#); } @@ -534,14 +533,14 @@ error: foo --> test.rs:3:6 | 3 | X0 Y0 Z0 - | ______^ starting here... + | ______^ 4 | | X1 Y1 Z1 - | |____^____- starting here... + | |____^____- | ||____| - | | ...ending here: `X` is a good letter + | | `X` is a good letter 5 | | X2 Y2 Z2 6 | | X3 Y3 Z3 - | |___________- ...ending here: `Y` is a good letter too + | |___________- `Y` is a good letter too "#); } @@ -982,18 +981,18 @@ error: foo --> test.rs:3:6 | 3 | X0 Y0 Z0 - | ______^ starting here... + | ______^ 4 | | X1 Y1 Z1 - | |____^____- starting here... + | |____^____- | ||____| - | | ...ending here: `X` is a good letter + | | `X` is a good letter 5 | | 1 6 | | 2 7 | | 3 ... | 15 | | X2 Y2 Z2 16 | | X3 Y3 Z3 - | |___________- ...ending here: `Y` is a good letter too + | |___________- `Y` is a good letter too "#); } @@ -1047,21 +1046,21 @@ error: foo --> test.rs:3:6 | 3 | X0 Y0 Z0 - | ______^ starting here... + | ______^ 4 | | 1 5 | | 2 6 | | 3 7 | | X1 Y1 Z1 - | |_________- starting here... + | |_________- 8 | || 4 9 | || 5 10 | || 6 11 | || X2 Y2 Z2 - | ||__________- ...ending here: `Z` is a good letter too + | ||__________- `Z` is a good letter too ... | 15 | | 10 16 | | X3 Y3 Z3 - | |_______^ ...ending here: `Y` is a good letter + | |_______^ `Y` is a good letter "#); } diff --git a/src/test/ui/compare-method/region-extra-2.stderr b/src/test/ui/compare-method/region-extra-2.stderr index 12b0ecabcc720..af974d501839b 100644 --- a/src/test/ui/compare-method/region-extra-2.stderr +++ b/src/test/ui/compare-method/region-extra-2.stderr @@ -4,12 +4,11 @@ error[E0276]: impl has stricter requirements than trait 15 | fn renew<'b: 'a>(self) -> &'b mut [T]; | -------------------------------------- definition of `renew` from trait ... -19 | fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { - | _____^ starting here... +19 | / fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { 20 | | //~^ ERROR E0276 21 | | &mut self[..] 22 | | } - | |_____^ ...ending here: impl has extra requirement `'a: 'b` + | |_____^ impl has extra requirement `'a: 'b` error: aborting due to previous error diff --git a/src/test/ui/compare-method/traits-misc-mismatch-2.stderr b/src/test/ui/compare-method/traits-misc-mismatch-2.stderr index 77b056f697892..622e144c53a04 100644 --- a/src/test/ui/compare-method/traits-misc-mismatch-2.stderr +++ b/src/test/ui/compare-method/traits-misc-mismatch-2.stderr @@ -4,12 +4,11 @@ error[E0276]: impl has stricter requirements than trait 19 | fn zip>(self, other: U) -> ZipIterator; | ------------------------------------------------------------------ definition of `zip` from trait ... -23 | fn zip>(self, other: U) -> ZipIterator { - | _____^ starting here... +23 | / fn zip>(self, other: U) -> ZipIterator { 24 | | //~^ ERROR E0276 25 | | ZipIterator{a: self, b: other} 26 | | } - | |_____^ ...ending here: impl has extra requirement `U: Iterator` + | |_____^ impl has extra requirement `U: Iterator` error: aborting due to previous error diff --git a/src/test/ui/did_you_mean/issue-40006.stderr b/src/test/ui/did_you_mean/issue-40006.stderr index 29ff0cee3af5c..8e8773eba3e22 100644 --- a/src/test/ui/did_you_mean/issue-40006.stderr +++ b/src/test/ui/did_you_mean/issue-40006.stderr @@ -2,17 +2,17 @@ error: missing `fn`, `type`, or `const` for impl-item declaration --> $DIR/issue-40006.rs:11:9 | 11 | impl X { - | _________^ starting here... + | _________^ 12 | | Y - | |____^ ...ending here: missing `fn`, `type`, or `const` + | |____^ missing `fn`, `type`, or `const` error: missing `fn`, `type`, or `const` for trait-item declaration --> $DIR/issue-40006.rs:17:10 | 17 | trait X { - | __________^ starting here... + | __________^ 18 | | X() {} - | |____^ ...ending here: missing `fn`, `type`, or `const` + | |____^ missing `fn`, `type`, or `const` error: expected `[`, found `#` --> $DIR/issue-40006.rs:19:17 @@ -24,17 +24,17 @@ error: missing `fn`, `type`, or `const` for trait-item declaration --> $DIR/issue-40006.rs:19:21 | 19 | fn xxx() { ### } - | _____________________^ starting here... + | _____________________^ 20 | | L = M; - | |____^ ...ending here: missing `fn`, `type`, or `const` + | |____^ missing `fn`, `type`, or `const` error: missing `fn`, `type`, or `const` for trait-item declaration --> $DIR/issue-40006.rs:20:11 | 20 | L = M; - | ___________^ starting here... + | ___________^ 21 | | Z = { 2 + 3 }; - | |____^ ...ending here: missing `fn`, `type`, or `const` + | |____^ missing `fn`, `type`, or `const` error: expected one of `const`, `extern`, `fn`, `type`, `unsafe`, or `}`, found `;` --> $DIR/issue-40006.rs:21:18 diff --git a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr index 92e2fe8e9367e..2c788e952edbf 100644 --- a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr +++ b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr @@ -1,26 +1,24 @@ error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:32:1 | -32 | impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt { - | _^ starting here... +32 | / impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt { 33 | | //~^ ERROR requires an `unsafe impl` declaration due to `#[may_dangle]` attribute 34 | | 35 | | // (unsafe to access self.1 due to #[may_dangle] on A) 36 | | fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); } 37 | | } - | |_^ ...ending here + | |_^ error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:38:1 | -38 | impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> { - | _^ starting here... +38 | / impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> { 39 | | //~^ ERROR requires an `unsafe impl` declaration due to `#[may_dangle]` attribute 40 | | 41 | | // (unsafe to access self.1 due to #[may_dangle] on 'a) 42 | | fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); } 43 | | } - | |_^ ...ending here + | |_^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr b/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr index 5a63d235a7f07..b51b683a1ac3a 100644 --- a/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr +++ b/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr @@ -1,11 +1,10 @@ error: reached the type-length limit while instantiating `<(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(), &()), &(&()...` --> $DIR/issue-37311.rs:23:5 | -23 | fn recurse(&self) { - | _____^ starting here... +23 | / fn recurse(&self) { 24 | | (self, self).recurse(); 25 | | } - | |_____^ ...ending here + | |_____^ | = note: consider adding a `#![type_length_limit="2097152"]` attribute to your crate diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr index 85e05422ab3b2..cf272b63128e8 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr @@ -8,18 +8,18 @@ note: ...the reference is valid for the lifetime 'a as defined on the body at 11 --> $DIR/ex1-return-one-existing-name-if-else.rs:11:44 | 11 | fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { - | ____________________________________________^ starting here... + | ____________________________________________^ 12 | | if x > y { x } else { y } 13 | | } - | |_^ ...ending here + | |_^ note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the body at 11:43 --> $DIR/ex1-return-one-existing-name-if-else.rs:11:44 | 11 | fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { - | ____________________________________________^ starting here... + | ____________________________________________^ 12 | | if x > y { x } else { y } 13 | | } - | |_^ ...ending here + | |_^ error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr index 6956a043cc694..6e03e66dd258f 100644 --- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr @@ -10,18 +10,18 @@ note: the anonymous lifetime #2 defined on the body at 15:51... --> $DIR/ex2a-push-one-existing-name.rs:15:52 | 15 | fn foo<'a>(x: &mut Vec>, y: Ref) { - | ____________________________________________________^ starting here... + | ____________________________________________________^ 16 | | x.push(y); 17 | | } - | |_^ ...ending here + | |_^ note: ...does not necessarily outlive the lifetime 'a as defined on the body at 15:51 --> $DIR/ex2a-push-one-existing-name.rs:15:52 | 15 | fn foo<'a>(x: &mut Vec>, y: Ref) { - | ____________________________________________________^ starting here... + | ____________________________________________________^ 16 | | x.push(y); 17 | | } - | |_^ ...ending here + | |_^ error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr index 990ae65ba9854..028f54ce97871 100644 --- a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr +++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr @@ -10,18 +10,18 @@ note: the anonymous lifetime #3 defined on the body at 15:43... --> $DIR/ex2b-push-no-existing-names.rs:15:44 | 15 | fn foo(x: &mut Vec>, y: Ref) { - | ____________________________________________^ starting here... + | ____________________________________________^ 16 | | x.push(y); 17 | | } - | |_^ ...ending here + | |_^ note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 15:43 --> $DIR/ex2b-push-no-existing-names.rs:15:44 | 15 | fn foo(x: &mut Vec>, y: Ref) { - | ____________________________________________^ starting here... + | ____________________________________________^ 16 | | x.push(y); 17 | | } - | |_^ ...ending here + | |_^ error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr index 82f6c71ec1c2e..4621214419e42 100644 --- a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr +++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr @@ -8,11 +8,11 @@ note: first, the lifetime cannot outlive the lifetime 'c as defined on the body --> $DIR/ex2c-push-inference-variable.rs:15:67 | 15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { - | ___________________________________________________________________^ starting here... + | ___________________________________________________________________^ 16 | | let z = Ref { data: y.data }; 17 | | x.push(z); 18 | | } - | |_^ ...ending here + | |_^ note: ...so that reference does not outlive borrowed content --> $DIR/ex2c-push-inference-variable.rs:16:25 | @@ -22,11 +22,11 @@ note: but, the lifetime must be valid for the lifetime 'b as defined on the body --> $DIR/ex2c-push-inference-variable.rs:15:67 | 15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { - | ___________________________________________________________________^ starting here... + | ___________________________________________________________________^ 16 | | let z = Ref { data: y.data }; 17 | | x.push(z); 18 | | } - | |_^ ...ending here + | |_^ note: ...so that expression is assignable (expected Ref<'b, _>, found Ref<'_, _>) --> $DIR/ex2c-push-inference-variable.rs:17:12 | diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr index daa6ea2d91aa3..a69694fdc2e5b 100644 --- a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr +++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr @@ -8,12 +8,12 @@ note: first, the lifetime cannot outlive the lifetime 'c as defined on the body --> $DIR/ex2d-push-inference-variable-2.rs:15:67 | 15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { - | ___________________________________________________________________^ starting here... + | ___________________________________________________________________^ 16 | | let a: &mut Vec> = x; 17 | | let b = Ref { data: y.data }; 18 | | a.push(b); 19 | | } - | |_^ ...ending here + | |_^ note: ...so that reference does not outlive borrowed content --> $DIR/ex2d-push-inference-variable-2.rs:17:25 | @@ -23,12 +23,12 @@ note: but, the lifetime must be valid for the lifetime 'b as defined on the body --> $DIR/ex2d-push-inference-variable-2.rs:15:67 | 15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { - | ___________________________________________________________________^ starting here... + | ___________________________________________________________________^ 16 | | let a: &mut Vec> = x; 17 | | let b = Ref { data: y.data }; 18 | | a.push(b); 19 | | } - | |_^ ...ending here + | |_^ note: ...so that expression is assignable (expected &mut std::vec::Vec>, found &mut std::vec::Vec>) --> $DIR/ex2d-push-inference-variable-2.rs:16:33 | diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr index b679532a4d910..eff15bb794b76 100644 --- a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr +++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr @@ -8,12 +8,12 @@ note: first, the lifetime cannot outlive the lifetime 'c as defined on the body --> $DIR/ex2e-push-inference-variable-3.rs:15:67 | 15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { - | ___________________________________________________________________^ starting here... + | ___________________________________________________________________^ 16 | | let a: &mut Vec> = x; 17 | | let b = Ref { data: y.data }; 18 | | Vec::push(a, b); 19 | | } - | |_^ ...ending here + | |_^ note: ...so that reference does not outlive borrowed content --> $DIR/ex2e-push-inference-variable-3.rs:17:25 | @@ -23,12 +23,12 @@ note: but, the lifetime must be valid for the lifetime 'b as defined on the body --> $DIR/ex2e-push-inference-variable-3.rs:15:67 | 15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { - | ___________________________________________________________________^ starting here... + | ___________________________________________________________________^ 16 | | let a: &mut Vec> = x; 17 | | let b = Ref { data: y.data }; 18 | | Vec::push(a, b); 19 | | } - | |_^ ...ending here + | |_^ note: ...so that expression is assignable (expected &mut std::vec::Vec>, found &mut std::vec::Vec>) --> $DIR/ex2e-push-inference-variable-3.rs:16:33 | diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr index c67c6113d17c5..36bdec8d43afe 100644 --- a/src/test/ui/mismatched_types/abridged.stderr +++ b/src/test/ui/mismatched_types/abridged.stderr @@ -37,15 +37,14 @@ error[E0308]: mismatched types error[E0308]: mismatched types --> $DIR/abridged.rs:42:5 | -42 | X { - | _____^ starting here... +42 | / X { 43 | | x: X { 44 | | x: "".to_string(), 45 | | y: 2, 46 | | }, 47 | | y: 3, 48 | | } - | |_____^ ...ending here: expected struct `std::string::String`, found integral variable + | |_____^ expected struct `std::string::String`, found integral variable | = note: expected type `X, std::string::String>` found type `X, {integer}>` @@ -53,15 +52,14 @@ error[E0308]: mismatched types error[E0308]: mismatched types --> $DIR/abridged.rs:52:5 | -52 | X { - | _____^ starting here... +52 | / X { 53 | | x: X { 54 | | x: "".to_string(), 55 | | y: 2, 56 | | }, 57 | | y: "".to_string(), 58 | | } - | |_____^ ...ending here: expected struct `std::string::String`, found integral variable + | |_____^ expected struct `std::string::String`, found integral variable | = note: expected type `X, _>` found type `X, _>` diff --git a/src/test/ui/mismatched_types/main.stderr b/src/test/ui/mismatched_types/main.stderr index 5dd124ebcdff6..c8941fbf95073 100644 --- a/src/test/ui/mismatched_types/main.stderr +++ b/src/test/ui/mismatched_types/main.stderr @@ -2,9 +2,9 @@ error[E0308]: mismatched types --> $DIR/main.rs:12:18 | 12 | let x: u32 = ( - | __________________^ starting here... + | __________________^ 13 | | ); - | |_____^ ...ending here: expected u32, found () + | |_____^ expected u32, found () | = note: expected type `u32` found type `()` diff --git a/src/test/ui/missing-items/m2.stderr b/src/test/ui/missing-items/m2.stderr index 3313543454469..503ce5618d486 100644 --- a/src/test/ui/missing-items/m2.stderr +++ b/src/test/ui/missing-items/m2.stderr @@ -3,10 +3,9 @@ error: main function not found error[E0046]: not all trait items implemented, missing: `CONSTANT`, `Type`, `method` --> $DIR/m2.rs:20:1 | -20 | impl m1::X for X { - | _^ starting here... +20 | / impl m1::X for X { 21 | | } - | |_^ ...ending here: missing `CONSTANT`, `Type`, `method` in implementation + | |_^ missing `CONSTANT`, `Type`, `method` in implementation | = note: `CONSTANT` from trait: `const CONSTANT: u32;` = note: `Type` from trait: `type Type;` diff --git a/src/test/ui/span/impl-wrong-item-for-trait.stderr b/src/test/ui/span/impl-wrong-item-for-trait.stderr index 367af12bb6b1c..ae290b3b11aa7 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.stderr +++ b/src/test/ui/span/impl-wrong-item-for-trait.stderr @@ -19,15 +19,14 @@ error[E0046]: not all trait items implemented, missing: `bar` 16 | fn bar(&self); | -------------- `bar` from trait ... -22 | impl Foo for FooConstForMethod { - | _^ starting here... +22 | / impl Foo for FooConstForMethod { 23 | | //~^ ERROR E0046 24 | | //~| NOTE missing `bar` in implementation 25 | | const bar: u64 = 1; ... | 28 | | const MY_CONST: u32 = 1; 29 | | } - | |_^ ...ending here: missing `bar` in implementation + | |_^ missing `bar` in implementation error[E0324]: item `MY_CONST` is an associated method, which doesn't match its trait `Foo` --> $DIR/impl-wrong-item-for-trait.rs:37:5 @@ -44,15 +43,14 @@ error[E0046]: not all trait items implemented, missing: `MY_CONST` 17 | const MY_CONST: u32; | -------------------- `MY_CONST` from trait ... -33 | impl Foo for FooMethodForConst { - | _^ starting here... +33 | / impl Foo for FooMethodForConst { 34 | | //~^ ERROR E0046 35 | | //~| NOTE missing `MY_CONST` in implementation 36 | | fn bar(&self) {} ... | 39 | | //~| NOTE does not match trait 40 | | } - | |_^ ...ending here: missing `MY_CONST` in implementation + | |_^ missing `MY_CONST` in implementation error[E0325]: item `bar` is an associated type, which doesn't match its trait `Foo` --> $DIR/impl-wrong-item-for-trait.rs:47:5 @@ -69,23 +67,21 @@ error[E0046]: not all trait items implemented, missing: `bar` 16 | fn bar(&self); | -------------- `bar` from trait ... -44 | impl Foo for FooTypeForMethod { - | _^ starting here... +44 | / impl Foo for FooTypeForMethod { 45 | | //~^ ERROR E0046 46 | | //~| NOTE missing `bar` in implementation 47 | | type bar = u64; ... | 50 | | const MY_CONST: u32 = 1; 51 | | } - | |_^ ...ending here: missing `bar` in implementation + | |_^ missing `bar` in implementation error[E0046]: not all trait items implemented, missing: `fmt` --> $DIR/impl-wrong-item-for-trait.rs:53:1 | -53 | impl Debug for FooTypeForMethod { - | _^ starting here... +53 | / impl Debug for FooTypeForMethod { 54 | | } - | |_^ ...ending here: missing `fmt` in implementation + | |_^ missing `fmt` in implementation | = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` diff --git a/src/test/ui/span/issue-23729.stderr b/src/test/ui/span/issue-23729.stderr index 701576ff6f475..d9f4bacce35ae 100644 --- a/src/test/ui/span/issue-23729.stderr +++ b/src/test/ui/span/issue-23729.stderr @@ -1,15 +1,14 @@ error[E0046]: not all trait items implemented, missing: `Item` --> $DIR/issue-23729.rs:20:9 | -20 | impl Iterator for Recurrence { - | _________^ starting here... +20 | / impl Iterator for Recurrence { 21 | | //~^ ERROR E0046 22 | | //~| NOTE missing `Item` in implementation 23 | | //~| NOTE `Item` from trait: `type Item;` ... | 36 | | } 37 | | } - | |_________^ ...ending here: missing `Item` in implementation + | |_________^ missing `Item` in implementation | = note: `Item` from trait: `type Item;` diff --git a/src/test/ui/span/issue-23827.stderr b/src/test/ui/span/issue-23827.stderr index 457fed34ff1ad..3127af157a62b 100644 --- a/src/test/ui/span/issue-23827.stderr +++ b/src/test/ui/span/issue-23827.stderr @@ -1,15 +1,14 @@ error[E0046]: not all trait items implemented, missing: `Output` --> $DIR/issue-23827.rs:36:1 | -36 | impl FnOnce<(C,)> for Prototype { - | _^ starting here... +36 | / impl FnOnce<(C,)> for Prototype { 37 | | //~^ ERROR E0046 38 | | //~| NOTE missing `Output` in implementation 39 | | //~| NOTE `Output` from trait: `type Output;` ... | 42 | | } 43 | | } - | |_^ ...ending here: missing `Output` in implementation + | |_^ missing `Output` in implementation | = note: `Output` from trait: `type Output;` diff --git a/src/test/ui/span/issue-24356.stderr b/src/test/ui/span/issue-24356.stderr index 963f4bd9bbcd8..71ab82d98b809 100644 --- a/src/test/ui/span/issue-24356.stderr +++ b/src/test/ui/span/issue-24356.stderr @@ -1,14 +1,13 @@ error[E0046]: not all trait items implemented, missing: `Target` --> $DIR/issue-24356.rs:30:9 | -30 | impl Deref for Thing { - | _________^ starting here... +30 | / impl Deref for Thing { 31 | | //~^ ERROR E0046 32 | | //~| NOTE missing `Target` in implementation 33 | | //~| NOTE `Target` from trait: `type Target;` 34 | | fn deref(&self) -> i8 { self.0 } 35 | | } - | |_________^ ...ending here: missing `Target` in implementation + | |_________^ missing `Target` in implementation | = note: `Target` from trait: `type Target;` diff --git a/src/test/ui/span/issue-7575.stderr b/src/test/ui/span/issue-7575.stderr index 765aceffe655b..8b813220d789d 100644 --- a/src/test/ui/span/issue-7575.stderr +++ b/src/test/ui/span/issue-7575.stderr @@ -38,11 +38,10 @@ error: no method named `fff` found for type `Myisize` in the current scope note: candidate #1 is defined in an impl for the type `Myisize` --> $DIR/issue-7575.rs:51:5 | -51 | fn fff(i: isize) -> isize { //~ NOTE candidate - | _____^ starting here... +51 | / fn fff(i: isize) -> isize { //~ NOTE candidate 52 | | i 53 | | } - | |_____^ ...ending here + | |_____^ error: no method named `is_str` found for type `T` in the current scope --> $DIR/issue-7575.rs:85:7 @@ -54,11 +53,10 @@ error: no method named `is_str` found for type `T` in the current scope note: candidate #1 is defined in the trait `ManyImplTrait` --> $DIR/issue-7575.rs:57:5 | -57 | fn is_str() -> bool { //~ NOTE candidate - | _____^ starting here... +57 | / fn is_str() -> bool { //~ NOTE candidate 58 | | false 59 | | } - | |_____^ ...ending here + | |_____^ = help: to disambiguate the method call, write `ManyImplTrait::is_str(t)` instead = help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `is_str`, perhaps you need to implement it: = help: candidate #1: `ManyImplTrait` diff --git a/src/test/ui/span/lint-unused-unsafe.stderr b/src/test/ui/span/lint-unused-unsafe.stderr index 0df3fa43022a4..f4998e08907a3 100644 --- a/src/test/ui/span/lint-unused-unsafe.stderr +++ b/src/test/ui/span/lint-unused-unsafe.stderr @@ -49,68 +49,62 @@ note: because it's nested under this `unsafe` fn error: unnecessary `unsafe` block --> $DIR/lint-unused-unsafe.rs:33:9 | -33 | unsafe { //~ ERROR: unnecessary `unsafe` block - | _________^ starting here... +33 | / unsafe { //~ ERROR: unnecessary `unsafe` block 34 | | unsf() 35 | | } - | |_________^ ...ending here: unnecessary `unsafe` block + | |_________^ unnecessary `unsafe` block | note: because it's nested under this `unsafe` block --> $DIR/lint-unused-unsafe.rs:32:5 | -32 | unsafe { // don't put the warning here - | _____^ starting here... +32 | / unsafe { // don't put the warning here 33 | | unsafe { //~ ERROR: unnecessary `unsafe` block 34 | | unsf() 35 | | } 36 | | } - | |_____^ ...ending here + | |_____^ error: unnecessary `unsafe` block --> $DIR/lint-unused-unsafe.rs:39:5 | -39 | unsafe { //~ ERROR: unnecessary `unsafe` block - | _____^ starting here... +39 | / unsafe { //~ ERROR: unnecessary `unsafe` block 40 | | unsafe { //~ ERROR: unnecessary `unsafe` block 41 | | unsf() 42 | | } 43 | | } - | |_____^ ...ending here: unnecessary `unsafe` block + | |_____^ unnecessary `unsafe` block | note: because it's nested under this `unsafe` fn --> $DIR/lint-unused-unsafe.rs:38:1 | -38 | unsafe fn bad7() { - | _^ starting here... +38 | / unsafe fn bad7() { 39 | | unsafe { //~ ERROR: unnecessary `unsafe` block 40 | | unsafe { //~ ERROR: unnecessary `unsafe` block 41 | | unsf() 42 | | } 43 | | } 44 | | } - | |_^ ...ending here + | |_^ error: unnecessary `unsafe` block --> $DIR/lint-unused-unsafe.rs:40:9 | -40 | unsafe { //~ ERROR: unnecessary `unsafe` block - | _________^ starting here... +40 | / unsafe { //~ ERROR: unnecessary `unsafe` block 41 | | unsf() 42 | | } - | |_________^ ...ending here: unnecessary `unsafe` block + | |_________^ unnecessary `unsafe` block | note: because it's nested under this `unsafe` fn --> $DIR/lint-unused-unsafe.rs:38:1 | -38 | unsafe fn bad7() { - | _^ starting here... +38 | / unsafe fn bad7() { 39 | | unsafe { //~ ERROR: unnecessary `unsafe` block 40 | | unsafe { //~ ERROR: unnecessary `unsafe` block 41 | | unsf() 42 | | } 43 | | } 44 | | } - | |_^ ...ending here + | |_^ error: aborting due to 8 previous errors diff --git a/src/test/ui/span/multiline-span-E0072.stderr b/src/test/ui/span/multiline-span-E0072.stderr index 58cdc50230063..9c6816e736313 100644 --- a/src/test/ui/span/multiline-span-E0072.stderr +++ b/src/test/ui/span/multiline-span-E0072.stderr @@ -1,14 +1,13 @@ error[E0072]: recursive type `ListNode` has infinite size --> $DIR/multiline-span-E0072.rs:12:1 | -12 | struct - | _^ starting here... +12 | / struct 13 | | ListNode 14 | | { 15 | | head: u8, 16 | | tail: Option, 17 | | } - | |_^ ...ending here: recursive type has infinite size + | |_^ recursive type has infinite size | = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `ListNode` representable diff --git a/src/test/ui/span/multiline-span-simple.stderr b/src/test/ui/span/multiline-span-simple.stderr index 161b6ca48b282..843c1e811d578 100644 --- a/src/test/ui/span/multiline-span-simple.stderr +++ b/src/test/ui/span/multiline-span-simple.stderr @@ -2,12 +2,12 @@ error[E0277]: the trait bound `u32: std::ops::Add<()>` is not satisfied --> $DIR/multiline-span-simple.rs:23:9 | 23 | foo(1 as u32 + - | _________^ starting here... + | _________^ 24 | | 25 | | bar(x, 26 | | 27 | | y), - | |______________^ ...ending here: the trait `std::ops::Add<()>` is not implemented for `u32` + | |______________^ the trait `std::ops::Add<()>` is not implemented for `u32` | = note: no implementation for `u32 + ()` diff --git a/src/test/ui/type-check/issue-40294.stderr b/src/test/ui/type-check/issue-40294.stderr index 5c388c9d602ea..7a76799889b97 100644 --- a/src/test/ui/type-check/issue-40294.stderr +++ b/src/test/ui/type-check/issue-40294.stderr @@ -1,15 +1,14 @@ error[E0282]: type annotations needed --> $DIR/issue-40294.rs:15:1 | -15 | fn foo<'a,'b,T>(x: &'a T, y: &'b T) - | _^ starting here... +15 | / fn foo<'a,'b,T>(x: &'a T, y: &'b T) 16 | | where &'a T : Foo, 17 | | &'b T : Foo 18 | | { 19 | | x.foo(); 20 | | y.foo(); 21 | | } - | |_^ ...ending here: cannot infer type for `&'a T` + | |_^ cannot infer type for `&'a T` error: aborting due to previous error From d360091e799b14a81ced54a67bc67ae0b0bc3afe Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 8 Nov 2016 22:54:31 +0100 Subject: [PATCH 28/33] Add suggestion for & coercions --- src/librustc_typeck/check/demand.rs | 77 ++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index e922c7447ff85..2933f35abfb6a 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -11,8 +11,9 @@ use check::FnCtxt; use rustc::ty::Ty; -use rustc::infer::{InferOk}; +use rustc::infer::{InferOk, TypeOrigin}; use rustc::traits::ObligationCause; +use rustc::ty; use syntax::ast; use syntax_pos::{self, Span}; @@ -80,12 +81,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - let mode = probe::Mode::MethodCall; - let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID); + let suggestions = if let Some(suggestions) = self.check_ref(expr, + checked_ty, + expected) { + suggestions + } else { + let mode = probe::Mode::MethodCall; + self.probe_for_return_type(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID) + } let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); if suggestions.len() > 0 { err.help(&format!("here are some functions which \ @@ -140,4 +147,60 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => false, } } + + /// This function is used to determine potential "simple" improvements or users' errors and + /// provide them useful help. For example: + /// + /// ``` + /// fn some_fn(s: &str) {} + /// + /// let x = "hey!".to_owned(); + /// some_fn(x); // error + /// ``` + /// + /// No need to find every potential function which could make a coercion to transform a + /// `String` into a `&str` since a `&` would do the trick! + /// + /// In addition of this check, it also checks between references mutability state. If the + /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with + /// `&mut`!". + fn check_ref(&self, + expr: &hir::Expr, + checked_ty: Ty<'tcx>, + expected: Ty<'tcx>) + -> Option { + match (&expected.sty, &checked_ty.sty) { + (&ty::TyRef(_, _), &ty::TyRef(_, _)) => None, + (&ty::TyRef(_, mutability), _) => { + // Check if it can work when put into a ref. For example: + // + // ``` + // fn bar(x: &mut i32) {} + // + // let x = 0u32; + // bar(&x); // error, expected &mut + // ``` + let ref_ty = match mutability.mutbl { + hir::Mutability::MutMutable => self.tcx.mk_mut_ref( + self.tcx.mk_region(ty::ReStatic), + checked_ty), + hir::Mutability::MutImmutable => self.tcx.mk_imm_ref( + self.tcx.mk_region(ty::ReStatic), + checked_ty), + }; + if self.try_coerce(expr, ref_ty, expected).is_ok() { + if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) { + return Some(format!("try with `{}{}`", + match mutability.mutbl { + hir::Mutability::MutMutable => "&mut ", + hir::Mutability::MutImmutable => "&", + }, + &src)); + } + } + None + } + _ => None, + } + } } From 8fe3a9a8f123fa759d35000bfbfb623167a76409 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 8 Nov 2016 22:55:58 +0100 Subject: [PATCH 29/33] Update tests --- src/test/compile-fail/coercion-slice.rs | 1 - src/test/compile-fail/cross-borrow-trait.rs | 2 ++ src/test/compile-fail/dst-bad-coercions.rs | 2 ++ src/test/compile-fail/issue-13058.rs | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/coercion-slice.rs b/src/test/compile-fail/coercion-slice.rs index 6b468ff96620d..7c5a4e0c3c6f6 100644 --- a/src/test/compile-fail/coercion-slice.rs +++ b/src/test/compile-fail/coercion-slice.rs @@ -14,6 +14,5 @@ fn main() { let _: &[i32] = [0]; //~^ ERROR mismatched types //~| expected type `&[i32]` - //~| found type `[{integer}; 1]` //~| expected &[i32], found array of 1 elements } diff --git a/src/test/compile-fail/cross-borrow-trait.rs b/src/test/compile-fail/cross-borrow-trait.rs index e5afccb9cf394..ee67a30fa1c37 100644 --- a/src/test/compile-fail/cross-borrow-trait.rs +++ b/src/test/compile-fail/cross-borrow-trait.rs @@ -20,4 +20,6 @@ pub fn main() { let _y: &Trait = x; //~ ERROR mismatched types //~| expected type `&Trait` //~| found type `std::boxed::Box` + //~| expected &Trait, found box + //~| ERROR the trait bound `Box: Trait` is not satisfied } diff --git a/src/test/compile-fail/dst-bad-coercions.rs b/src/test/compile-fail/dst-bad-coercions.rs index 883c16b089581..ff2e2d619a5ec 100644 --- a/src/test/compile-fail/dst-bad-coercions.rs +++ b/src/test/compile-fail/dst-bad-coercions.rs @@ -23,11 +23,13 @@ pub fn main() { let x: *const S = &S; let y: &S = x; //~ ERROR mismatched types let y: &T = x; //~ ERROR mismatched types + //~^ ERROR the trait bound `*const S: T` is not satisfied // Test that we cannot convert from *-ptr to &S and &T (mut version) let x: *mut S = &mut S; let y: &S = x; //~ ERROR mismatched types let y: &T = x; //~ ERROR mismatched types + //~^ ERROR the trait bound `*mut S: T` is not satisfied // Test that we cannot convert an immutable ptr to a mutable one using *-ptrs let x: &mut T = &S; //~ ERROR mismatched types diff --git a/src/test/compile-fail/issue-13058.rs b/src/test/compile-fail/issue-13058.rs index 408c6d411de90..ed1634441498b 100644 --- a/src/test/compile-fail/issue-13058.rs +++ b/src/test/compile-fail/issue-13058.rs @@ -35,4 +35,5 @@ fn check<'r, I: Iterator, T: Itble<'r, usize, I>>(cont: &T) -> bool fn main() { check((3, 5)); //~^ ERROR mismatched types +//~| HELP try with `&(3, 5)` } From 89bd3f39cadbbe0b361303ddbda2796ea7f39bb9 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 18 Apr 2017 11:02:21 +0300 Subject: [PATCH 30/33] Update #[no_core] users with the "freeze" lang item. --- src/bootstrap/compile.rs | 1 + src/rtstartup/rsbegin.rs | 7 +++++-- src/test/run-make/simd-ffi/simd.rs | 6 +++++- src/test/run-make/target-specs/foo.rs | 6 +++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index bddd570a13d26..cd87b27d4f1aa 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -151,6 +151,7 @@ pub fn build_startup_objects(build: &Build, for_compiler: &Compiler, target: &st if !up_to_date(src_file, dst_file) { let mut cmd = Command::new(&compiler_path); build.run(cmd.env("RUSTC_BOOTSTRAP", "1") + .arg("--cfg").arg(format!("stage{}", compiler.stage)) .arg("--target").arg(target) .arg("--emit=obj") .arg("--out-dir").arg(dst_dir) diff --git a/src/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs index 65c85697ce720..e8b92aab1da1e 100644 --- a/src/rtstartup/rsbegin.rs +++ b/src/rtstartup/rsbegin.rs @@ -22,7 +22,7 @@ // object (usually called `crtX.o), which then invokes initialization callbacks // of other runtime components (registered via yet another special image section). -#![feature(no_core, lang_items)] +#![feature(no_core, lang_items, optin_builtin_traits)] #![crate_type="rlib"] #![no_core] #![allow(non_camel_case_types)] @@ -31,9 +31,12 @@ trait Sized {} #[lang = "sync"] trait Sync {} +impl Sync for .. {} #[lang = "copy"] trait Copy {} -impl Sync for T {} +#[cfg_attr(not(stage0), lang = "freeze")] +trait Freeze {} +impl Freeze for .. {} #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] pub mod eh_frames { diff --git a/src/test/run-make/simd-ffi/simd.rs b/src/test/run-make/simd-ffi/simd.rs index 49fec6f3619e4..8ab8f4715755d 100644 --- a/src/test/run-make/simd-ffi/simd.rs +++ b/src/test/run-make/simd-ffi/simd.rs @@ -12,7 +12,7 @@ #![crate_type = "lib"] // we can compile to a variety of platforms, because we don't need // cross-compiled standard libraries. -#![feature(no_core)] +#![feature(no_core, optin_builtin_traits)] #![no_core] #![feature(repr_simd, simd_ffi, link_llvm_intrinsics, lang_items)] @@ -78,3 +78,7 @@ pub trait Copy { } pub mod marker { pub use Copy; } + +#[lang = "freeze"] +trait Freeze {} +impl Freeze for .. {} diff --git a/src/test/run-make/target-specs/foo.rs b/src/test/run-make/target-specs/foo.rs index 15b5697723216..af24c3b460b2e 100644 --- a/src/test/run-make/target-specs/foo.rs +++ b/src/test/run-make/target-specs/foo.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(lang_items, no_core)] +#![feature(lang_items, no_core, optin_builtin_traits)] #![no_core] #[lang="copy"] @@ -17,6 +17,10 @@ trait Copy { } #[lang="sized"] trait Sized { } +#[lang = "freeze"] +trait Freeze {} +impl Freeze for .. {} + #[lang="start"] fn start(_main: *const u8, _argc: isize, _argv: *const *const u8) -> isize { 0 } From 7d3284ebc18f3d4087d71352421726b2240a7c20 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 10 Nov 2016 00:25:45 +0100 Subject: [PATCH 31/33] Create a new method to run coercion inside probe --- src/librustc_typeck/check/coercion.rs | 30 ++++++++++++++-- src/librustc_typeck/check/demand.rs | 38 ++++++++++----------- src/test/compile-fail/cross-borrow-trait.rs | 4 +-- src/test/compile-fail/dst-bad-coercions.rs | 2 -- src/test/compile-fail/issue-11374.rs | 2 +- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c6a1f6cfc0d7f..a769b55c520ba 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -65,8 +65,8 @@ use check::{Diverges, FnCtxt}; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::infer::{Coercion, InferResult, InferOk, TypeTrace}; -use rustc::infer::type_variable::TypeVariableOrigin; -use rustc::traits::{self, ObligationCause, ObligationCauseCode}; +use rustc::infer::type_variable::{TypeVariableOrigin}; +use rustc::traits::{self, /*FulfillmentContext,*/ ObligationCause, ObligationCauseCode}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::{self, LvaluePreference, TypeAndMut, Ty, ClosureSubsts}; @@ -78,6 +78,7 @@ use errors::DiagnosticBuilder; use syntax::abi; use syntax::feature_gate; use syntax::ptr::P; +use syntax_pos; use std::collections::VecDeque; use std::ops::Deref; @@ -722,6 +723,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Ok(target) } + /// Same as `try_coerce()`, but without side-effects. + pub fn can_coerce(&self, + expr_ty: Ty<'tcx>, + target: Ty<'tcx>) + -> bool { + // FIXME: This is a hack, but coercion wasn't made to be run + // in a probe. It leaks obligations and bounds and things out + // into the environment. For now we just save-and-restore the + // fulfillment context. + /*let saved_fulfillment_cx = + mem::replace( + &mut *self.inh.fulfillment_cx.borrow_mut(), + FulfillmentContext::new());*/ + let source = self.resolve_type_vars_with_obligations(expr_ty); + debug!("coercion::can({:?} -> {:?})", source, target); + + let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable); + let coerce = Coerce::new(self, cause); + let result = self.probe(|_| coerce.coerce::(&[], source, target)).is_ok(); + + //*self.inh.fulfillment_cx.borrow_mut() = saved_fulfillment_cx; + + result + } + /// Given some expressions, their known unified type and another expression, /// tries to unify the types, potentially inserting coercions on any of the /// provided expressions and returns their LUB (aka "common supertype"). diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 2933f35abfb6a..0fd98232becfa 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -10,16 +10,14 @@ use check::FnCtxt; -use rustc::ty::Ty; -use rustc::infer::{InferOk, TypeOrigin}; +use rustc::infer::InferOk; use rustc::traits::ObligationCause; -use rustc::ty; use syntax::ast; use syntax_pos::{self, Span}; use rustc::hir; use rustc::hir::def::Def; -use rustc::ty::{self, AssociatedItem}; +use rustc::ty::{self, Ty, AssociatedItem}; use errors::DiagnosticBuilder; use super::method::probe; @@ -81,24 +79,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - let suggestions = if let Some(suggestions) = self.check_ref(expr, - checked_ty, - expected) { - suggestions + let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); + if let Some(suggestion) = self.check_ref(expr, + checked_ty, + expected) { + err.help(&suggestion); } else { let mode = probe::Mode::MethodCall; - self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID) + let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID); + if suggestions.len() > 0 { + err.help(&format!("here are some functions which \ + might fulfill your needs:\n - {}", + self.get_best_match(&suggestions).join("\n"))); + } } - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); - if suggestions.len() > 0 { - err.help(&format!("here are some functions which \ - might fulfill your needs:\n{}", - self.get_best_match(&suggestions).join("\n"))); - }; err.emit(); } } @@ -188,7 +186,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.tcx.mk_region(ty::ReStatic), checked_ty), }; - if self.try_coerce(expr, ref_ty, expected).is_ok() { + if self.can_coerce(ref_ty, expected) { if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) { return Some(format!("try with `{}{}`", match mutability.mutbl { diff --git a/src/test/compile-fail/cross-borrow-trait.rs b/src/test/compile-fail/cross-borrow-trait.rs index ee67a30fa1c37..847a82c082651 100644 --- a/src/test/compile-fail/cross-borrow-trait.rs +++ b/src/test/compile-fail/cross-borrow-trait.rs @@ -17,9 +17,7 @@ impl Trait for Foo {} pub fn main() { let x: Box = Box::new(Foo); - let _y: &Trait = x; //~ ERROR mismatched types + let _y: &Trait = x; //~ ERROR E0308 //~| expected type `&Trait` //~| found type `std::boxed::Box` - //~| expected &Trait, found box - //~| ERROR the trait bound `Box: Trait` is not satisfied } diff --git a/src/test/compile-fail/dst-bad-coercions.rs b/src/test/compile-fail/dst-bad-coercions.rs index ff2e2d619a5ec..883c16b089581 100644 --- a/src/test/compile-fail/dst-bad-coercions.rs +++ b/src/test/compile-fail/dst-bad-coercions.rs @@ -23,13 +23,11 @@ pub fn main() { let x: *const S = &S; let y: &S = x; //~ ERROR mismatched types let y: &T = x; //~ ERROR mismatched types - //~^ ERROR the trait bound `*const S: T` is not satisfied // Test that we cannot convert from *-ptr to &S and &T (mut version) let x: *mut S = &mut S; let y: &S = x; //~ ERROR mismatched types let y: &T = x; //~ ERROR mismatched types - //~^ ERROR the trait bound `*mut S: T` is not satisfied // Test that we cannot convert an immutable ptr to a mutable one using *-ptrs let x: &mut T = &S; //~ ERROR mismatched types diff --git a/src/test/compile-fail/issue-11374.rs b/src/test/compile-fail/issue-11374.rs index f78786a2889da..1e444a6bebf9b 100644 --- a/src/test/compile-fail/issue-11374.rs +++ b/src/test/compile-fail/issue-11374.rs @@ -33,5 +33,5 @@ pub fn for_stdin<'a>() -> Container<'a> { fn main() { let mut c = for_stdin(); let mut v = Vec::new(); - c.read_to(v); //~ ERROR mismatched types + c.read_to(v); //~ ERROR E0308 } From 7ce1eb77c77ce0a56efc7d11ac8003e6a95208d0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 21 Apr 2017 13:28:31 +0200 Subject: [PATCH 32/33] Update ui test --- src/librustc_typeck/check/coercion.rs | 23 ++++------------------ src/librustc_typeck/check/demand.rs | 2 +- src/test/ui/span/coerce-suggestions.rs | 1 - src/test/ui/span/coerce-suggestions.stderr | 14 +++++-------- 4 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index a769b55c520ba..d21b5f739bd7b 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -65,8 +65,8 @@ use check::{Diverges, FnCtxt}; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::infer::{Coercion, InferResult, InferOk, TypeTrace}; -use rustc::infer::type_variable::{TypeVariableOrigin}; -use rustc::traits::{self, /*FulfillmentContext,*/ ObligationCause, ObligationCauseCode}; +use rustc::infer::type_variable::TypeVariableOrigin; +use rustc::traits::{self, ObligationCause, ObligationCauseCode}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::{self, LvaluePreference, TypeAndMut, Ty, ClosureSubsts}; @@ -724,28 +724,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } /// Same as `try_coerce()`, but without side-effects. - pub fn can_coerce(&self, - expr_ty: Ty<'tcx>, - target: Ty<'tcx>) - -> bool { - // FIXME: This is a hack, but coercion wasn't made to be run - // in a probe. It leaks obligations and bounds and things out - // into the environment. For now we just save-and-restore the - // fulfillment context. - /*let saved_fulfillment_cx = - mem::replace( - &mut *self.inh.fulfillment_cx.borrow_mut(), - FulfillmentContext::new());*/ + pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool { let source = self.resolve_type_vars_with_obligations(expr_ty); debug!("coercion::can({:?} -> {:?})", source, target); let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable); let coerce = Coerce::new(self, cause); - let result = self.probe(|_| coerce.coerce::(&[], source, target)).is_ok(); - - //*self.inh.fulfillment_cx.borrow_mut() = saved_fulfillment_cx; - - result + self.probe(|_| coerce.coerce::(&[], source, target)).is_ok() } /// Given some expressions, their known unified type and another expression, diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 0fd98232becfa..4cc3f2dacdfe9 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -93,7 +93,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ast::DUMMY_NODE_ID); if suggestions.len() > 0 { err.help(&format!("here are some functions which \ - might fulfill your needs:\n - {}", + might fulfill your needs:\n{}", self.get_best_match(&suggestions).join("\n"))); } } diff --git a/src/test/ui/span/coerce-suggestions.rs b/src/test/ui/span/coerce-suggestions.rs index 3177e858ff4fd..bc3122bf71c0e 100644 --- a/src/test/ui/span/coerce-suggestions.rs +++ b/src/test/ui/span/coerce-suggestions.rs @@ -32,7 +32,6 @@ fn main() { //~| NOTE types differ in mutability //~| NOTE expected type `&mut std::string::String` //~| NOTE found type `&std::string::String` - //~| HELP try with `&mut y` test2(&y); //~^ ERROR E0308 //~| NOTE types differ in mutability diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 6a70b8ff851d7..220b2f471da9a 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -18,11 +18,7 @@ error[E0308]: mismatched types | = note: expected type `&str` found type `std::string::String` - = help: here are some functions which might fulfill your needs: - - .as_str() - - .trim() - - .trim_left() - - .trim_right() + = help: try with `&String::new()` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:30:10 @@ -34,18 +30,18 @@ error[E0308]: mismatched types found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:36:11 + --> $DIR/coerce-suggestions.rs:35:11 | -36 | test2(&y); +35 | test2(&y); | ^^ types differ in mutability | = note: expected type `&mut i32` found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:42:9 + --> $DIR/coerce-suggestions.rs:41:9 | -42 | f = box f; +41 | f = box f; | ^^^^^ cyclic type of infinite size | = note: expected type `_` From 946f8e6a59242a8112c6983d1336fef54bc55b9a Mon Sep 17 00:00:00 2001 From: Cameron Hart Date: Sat, 22 Apr 2017 09:38:23 +1000 Subject: [PATCH 33/33] Use primitive align for tagged enum fill. Hopefully will fix assert on ARM where vector types are being used as the fill type for enums containing repr aligned types greater than the largest possible native type, thus don't match the Layout's alignment and triggers an assert. --- src/librustc_trans/adt.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 5326e9344c89a..d1c1dd7436a5b 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -185,7 +185,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } } } - layout::General { discr, size, align, .. } => { + layout::General { discr, size, align, primitive_align, .. } => { // We need a representation that has: // * The alignment of the most-aligned field // * The size of the largest variant (rounded up to that alignment) @@ -198,14 +198,15 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // of the size. let size = size.bytes(); let align = align.abi(); + let primitive_align = primitive_align.abi(); assert!(align <= std::u32::MAX as u64); let discr_ty = Type::from_integer(cx, discr); let discr_size = discr.size().bytes(); let padded_discr_size = roundup(discr_size, align as u32); let variant_part_size = size-padded_discr_size; - let variant_fill = union_fill(cx, variant_part_size, align); + let variant_fill = union_fill(cx, variant_part_size, primitive_align); - assert_eq!(machine::llalign_of_min(cx, variant_fill), align as u32); + assert_eq!(machine::llalign_of_min(cx, variant_fill), primitive_align as u32); assert_eq!(padded_discr_size % discr_size, 0); // Ensure discr_ty can fill pad evenly let fields: Vec = [discr_ty,