diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9d8c84f407..1e983cfd726 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -97,33 +97,38 @@ system internals, try asking in [`#rust-internals`][pound-rust-internals]. Before you can start building the compiler you need to configure the build for your system. In most cases, that will just mean using the defaults provided -for Rust. Configuring involves invoking the `configure` script in the project -root. +for Rust. -``` -./configure -``` +To change configuration, you must copy the file `src/bootstrap/config.toml.example` +to `config.toml` in the directory from which you will be running the build, and +change the settings provided. -There are large number of options accepted by this script to alter the -configuration used later in the build process. Some options to note: +There are large number of options provided in this config file that will alter the +configuration used in the build process. Some options to note: -- `--enable-debug` - Build a debug version of the compiler (disables optimizations, - which speeds up compilation of stage1 rustc) -- `--enable-optimize` - Enable optimizations (can be used with `--enable-debug` - to make a debug build with optimizations) -- `--disable-valgrind-rpass` - Don't run tests with valgrind -- `--enable-clang` - Prefer clang to gcc for building dependencies (e.g., LLVM) -- `--enable-ccache` - Invoke clang/gcc with ccache to re-use object files between builds -- `--enable-compiler-docs` - Build compiler documentation +#### `[llvm]`: +- `ccache = true` - Use ccache when building llvm -To see a full list of options, run `./configure --help`. +#### `[build]`: +- `compiler-docs = true` - Build compiler documentation + +#### `[rust]`: +- `debuginfo = true` - Build a compiler with debuginfo +- `optimize = false` - Disable optimizations to speed up compilation of stage1 rust + +For more options, the `config.toml` file contains commented out defaults, with +descriptions of what each option will do. + +Note: Previously the `./configure` script was used to configure this +project. It can still be used, but it's recommended to use a `config.toml` +file. If you still have a `config.mk` file in your directory - from +`./configure` - you may need to delete it for `config.toml` to work. ### Building -Although the `./configure` script will generate a `Makefile`, this is actually -just a thin veneer over the actual build system driver, `x.py`. This file, at -the root of the repository, is used to build, test, and document various parts -of the compiler. You can execute it as: +The build system uses the `x.py` script to control the build process. This script +is used to build, test, and document various parts of the compiler. You can +execute it as: ```sh python x.py build @@ -185,6 +190,9 @@ To learn about all possible rules you can execute, run: python x.py build --help --verbose ``` +Note: Previously `./configure` and `make` were used to build this project. +They are still available, but `x.py` is the recommended build system. + ### Useful commands Some common invocations of `x.py` are: @@ -235,8 +243,8 @@ feature. We use the 'fork and pull' model described there. Please make pull requests against the `master` branch. -Compiling all of `make check` can take a while. When testing your pull request, -consider using one of the more specialized `make` targets to cut down on the +Compiling all of `./x.py test` can take a while. When testing your pull request, +consider using one of the more specialized `./x.py` targets to cut down on the amount of time you have to wait. You need to have built the compiler at least once before running these will work, but that’s only one full build rather than one each time. @@ -307,7 +315,7 @@ To find documentation-related issues, sort by the [A-docs label][adocs]. [adocs]: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AA-docs -In many cases, you don't need a full `make doc`. You can use `rustdoc` directly +In many cases, you don't need a full `./x.py doc`. You can use `rustdoc` directly to check small fixes. For example, `rustdoc src/doc/reference.md` will render reference to `doc/reference.html`. The CSS might be messed up, but you can verify that the HTML is right. diff --git a/README.md b/README.md index c1218e9c600..93415adc423 100644 --- a/README.md +++ b/README.md @@ -35,15 +35,15 @@ Read ["Installing Rust"] from [The Book]. 3. Build and install: ```sh - $ ./configure - $ make && sudo make install + $ ./x.py build && sudo ./x.py dist --install ``` - > ***Note:*** Install locations can be adjusted by passing a `--prefix` - > argument to `configure`. Various other options are also supported – pass - > `--help` for more information on them. + > ***Note:*** Install locations can be adjusted by copying the config file + > from `./src/bootstrap/config.toml.example` to `./config.toml`, and + > adjusting the `prefix` option under `[install]`. Various other options are + > also supported, and are documented in the config file. - When complete, `sudo make install` will place several programs into + When complete, `sudo ./x.py dist --install` will place several programs into `/usr/local/bin`: `rustc`, the Rust compiler, and `rustdoc`, the API-documentation tool. This install does not include [Cargo], Rust's package manager, which you may also want to build. @@ -59,7 +59,6 @@ for interop with software produced by Visual Studio use the MSVC build of Rust; for interop with GNU software built using the MinGW/MSYS2 toolchain use the GNU build. - #### MinGW [MSYS2][msys2] can be used to easily build Rust on Windows: @@ -94,11 +93,10 @@ build. mingw-w64-x86_64-gcc ``` -4. Navigate to Rust's source code (or clone it), then configure and build it: +4. Navigate to Rust's source code (or clone it), then build it: ```sh - $ ./configure - $ make && make install + $ ./x.py build && ./x.py dist --install ``` #### MSVC @@ -114,13 +112,6 @@ shell with: > python x.py build ``` -If you're running inside of an msys shell, however, you can run: - -```sh -$ ./configure --build=x86_64-pc-windows-msvc -$ make && make install -``` - Currently building Rust only works with some known versions of Visual Studio. If you have a more recent version installed the build system doesn't understand then you may need to force rustbuild to use an older version. This can be done @@ -131,13 +122,43 @@ CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64. python x.py build ``` +#### Specifying an ABI + +Each specific ABI can also be used from either environment (for example, using +the GNU ABI in powershell) by using an explicit build triple. The available +Windows build triples are: +- GNU ABI (using GCC) + - `i686-pc-windows-gnu` + - `x86_64-pc-windows-gnu` +- The MSVC ABI + - `i686-pc-windows-msvc` + - `x86_64-pc-windows-msvc` + +The build triple can be specified by either specifying `--build=ABI` when +invoking `x.py` commands, or by copying the `config.toml` file (as described +in Building From Source), and modifying the `build` option under the `[build]` +section. + +### Configure and Make + +While it's not the recommended build system, this project also provides a +configure script and makefile (the latter of which just invokes `x.py`). + +```sh +$ ./configure +$ make && sudo make install +``` + +When using the configure script, the generated config.mk` file may override the +`config.toml` file. To go back to the `config.toml` file, delete the generated +`config.mk` file. + ## Building Documentation If you’d like to build the documentation, it’s almost the same: ```sh -$ ./configure -$ make docs +$ ./x.py doc ``` The generated documentation will appear in a top-level `doc` directory, diff --git a/src/doc/book/src/lifetimes.md b/src/doc/book/src/lifetimes.md index 8bca13c28f0..042d9af9717 100644 --- a/src/doc/book/src/lifetimes.md +++ b/src/doc/book/src/lifetimes.md @@ -349,8 +349,8 @@ to it. ## Lifetime Elision -Rust supports powerful local type inference in the bodies of functions but not in their item signatures. -It's forbidden to allow reasoning about types based on the item signature alone. +Rust supports powerful local type inference in the bodies of functions, but it +deliberately does not perform any reasoning about types for item signatures. However, for ergonomic reasons, a very restricted secondary inference algorithm called “lifetime elision” does apply when judging lifetimes. Lifetime elision is concerned solely with inferring lifetime parameters using three easily memorizable and unambiguous rules. This means lifetime elision diff --git a/src/doc/book/src/procedural-macros.md b/src/doc/book/src/procedural-macros.md index 4f5a6a7c033..e02b5a6cdd7 100644 --- a/src/doc/book/src/procedural-macros.md +++ b/src/doc/book/src/procedural-macros.md @@ -128,7 +128,7 @@ pub fn hello_world(input: TokenStream) -> TokenStream { So there is a lot going on here. We have introduced two new crates: [`syn`] and [`quote`]. As you may have noticed, `input: TokenSteam` is immediately converted to a `String`. This `String` is a string representation of the Rust code for which -we are deriving `HelloWorld` for. At the moment, the only thing you can do with a +we are deriving `HelloWorld`. At the moment, the only thing you can do with a `TokenStream` is convert it to a string. A richer API will exist in the future. So what we really need is to be able to _parse_ Rust code into something diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index e6c9e1ed38e..dc5a662cdb0 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -65,12 +65,15 @@ pub struct Error; /// A collection of methods that are required to format a message into a stream. /// /// This trait is the type which this modules requires when formatting -/// information. This is similar to the standard library's `io::Write` trait, +/// information. This is similar to the standard library's [`io::Write`] trait, /// but it is only intended for use in libcore. /// /// This trait should generally not be implemented by consumers of the standard -/// library. The `write!` macro accepts an instance of `io::Write`, and the -/// `io::Write` trait is favored over implementing this trait. +/// library. The [`write!`] macro accepts an instance of [`io::Write`], and the +/// [`io::Write`] trait is favored over implementing this trait. +/// +/// [`write!`]: ../../std/macro.write.html +/// [`io::Write`]: ../../std/io/trait.Write.html #[stable(feature = "rust1", since = "1.0.0")] pub trait Write { /// Writes a slice of bytes into this writer, returning whether the write @@ -82,29 +85,79 @@ pub trait Write { /// /// # Errors /// - /// This function will return an instance of `Error` on error. + /// This function will return an instance of [`Error`] on error. + /// + /// [`Error`]: struct.Error.html + /// + /// # Examples + /// + /// ``` + /// use std::fmt::{Error, Write}; + /// + /// fn writer(f: &mut W, s: &str) -> Result<(), Error> { + /// f.write_str(s) + /// } + /// + /// let mut buf = String::new(); + /// writer(&mut buf, "hola").unwrap(); + /// assert_eq!(&buf, "hola"); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn write_str(&mut self, s: &str) -> Result; - /// Writes a `char` into this writer, returning whether the write succeeded. + /// Writes a [`char`] into this writer, returning whether the write succeeded. /// - /// A single `char` may be encoded as more than one byte. + /// A single [`char`] may be encoded as more than one byte. /// This method can only succeed if the entire byte sequence was successfully /// written, and this method will not return until all data has been /// written or an error occurs. /// /// # Errors /// - /// This function will return an instance of `Error` on error. + /// This function will return an instance of [`Error`] on error. + /// + /// [`char`]: ../../std/primitive.char.html + /// [`Error`]: struct.Error.html + /// + /// # Examples + /// + /// ``` + /// use std::fmt::{Error, Write}; + /// + /// fn writer(f: &mut W, c: char) -> Result<(), Error> { + /// f.write_char(c) + /// } + /// + /// let mut buf = String::new(); + /// writer(&mut buf, 'a').unwrap(); + /// writer(&mut buf, 'b').unwrap(); + /// assert_eq!(&buf, "ab"); + /// ``` #[stable(feature = "fmt_write_char", since = "1.1.0")] fn write_char(&mut self, c: char) -> Result { self.write_str(c.encode_utf8(&mut [0; 4])) } - /// Glue for usage of the `write!` macro with implementors of this trait. + /// Glue for usage of the [`write!`] macro with implementors of this trait. /// /// This method should generally not be invoked manually, but rather through - /// the `write!` macro itself. + /// the [`write!`] macro itself. + /// + /// [`write!`]: ../../std/macro.write.html + /// + /// # Examples + /// + /// ``` + /// use std::fmt::{Error, Write}; + /// + /// fn writer(f: &mut W, s: &str) -> Result<(), Error> { + /// f.write_fmt(format_args!("{}", s)) + /// } + /// + /// let mut buf = String::new(); + /// writer(&mut buf, "world").unwrap(); + /// assert_eq!(&buf, "world"); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn write_fmt(&mut self, args: Arguments) -> Result { // This Adapter is needed to allow `self` (of type `&mut diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 70ca5fe83a9..f850fd97727 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -21,7 +21,6 @@ use super::{ SelectionContext, SelectionError, ObjectSafetyViolation, - MethodViolationCode, }; use fmt_macros::{Parser, Piece, Position}; @@ -267,61 +266,63 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let span = obligation.cause.span; let mut report = None; - for item in self.tcx.get_attrs(def_id).iter() { - if item.check_name("rustc_on_unimplemented") { - let err_sp = item.meta().span.substitute_dummy(span); - let trait_str = self.tcx.item_path_str(trait_ref.def_id); - if let Some(istring) = item.value_str() { - let istring = &*istring.as_str(); - let generics = self.tcx.item_generics(trait_ref.def_id); - let generic_map = generics.types.iter().map(|param| { - (param.name.as_str().to_string(), - trait_ref.substs.type_for_def(param).to_string()) - }).collect::>(); - let parser = Parser::new(istring); - let mut errored = false; - let err: String = parser.filter_map(|p| { - match p { - Piece::String(s) => Some(s), - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => match generic_map.get(s) { - Some(val) => Some(val), - None => { - span_err!(self.tcx.sess, err_sp, E0272, - "the #[rustc_on_unimplemented] \ - attribute on \ - trait definition for {} refers to \ - non-existent type parameter {}", - trait_str, s); - errored = true; - None - } - }, - _ => { - span_err!(self.tcx.sess, err_sp, E0273, - "the #[rustc_on_unimplemented] attribute \ - on trait definition for {} must have \ - named format arguments, eg \ - `#[rustc_on_unimplemented = \ - \"foo {{T}}\"]`", trait_str); + if let Some(item) = self.tcx + .get_attrs(def_id) + .into_iter() + .filter(|a| a.check_name("rustc_on_unimplemented")) + .next() + { + let err_sp = item.meta().span.substitute_dummy(span); + let trait_str = self.tcx.item_path_str(trait_ref.def_id); + if let Some(istring) = item.value_str() { + let istring = &*istring.as_str(); + let generics = self.tcx.item_generics(trait_ref.def_id); + let generic_map = generics.types.iter().map(|param| { + (param.name.as_str().to_string(), + trait_ref.substs.type_for_def(param).to_string()) + }).collect::>(); + let parser = Parser::new(istring); + let mut errored = false; + let err: String = parser.filter_map(|p| { + match p { + Piece::String(s) => Some(s), + Piece::NextArgument(a) => match a.position { + Position::ArgumentNamed(s) => match generic_map.get(s) { + Some(val) => Some(val), + None => { + span_err!(self.tcx.sess, err_sp, E0272, + "the #[rustc_on_unimplemented] \ + attribute on \ + trait definition for {} refers to \ + non-existent type parameter {}", + trait_str, s); errored = true; None } + }, + _ => { + span_err!(self.tcx.sess, err_sp, E0273, + "the #[rustc_on_unimplemented] attribute \ + on trait definition for {} must have \ + named format arguments, eg \ + `#[rustc_on_unimplemented = \ + \"foo {{T}}\"]`", trait_str); + errored = true; + None } } - }).collect(); - // Report only if the format string checks out - if !errored { - report = Some(err); } - } else { - span_err!(self.tcx.sess, err_sp, E0274, - "the #[rustc_on_unimplemented] attribute on \ - trait definition for {} must have a value, \ - eg `#[rustc_on_unimplemented = \"foo\"]`", - trait_str); + }).collect(); + // Report only if the format string checks out + if !errored { + report = Some(err); } - break; + } else { + span_err!(self.tcx.sess, err_sp, E0274, + "the #[rustc_on_unimplemented] attribute on \ + trait definition for {} must have a value, \ + eg `#[rustc_on_unimplemented = \"foo\"]`", + trait_str); } } report @@ -359,34 +360,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } fn report_similar_impl_candidates(&self, - trait_ref: ty::PolyTraitRef<'tcx>, + impl_candidates: Vec>, err: &mut DiagnosticBuilder) { - let simp = fast_reject::simplify_type(self.tcx, - trait_ref.skip_binder().self_ty(), - true); - let mut impl_candidates = Vec::new(); - let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id()); - - match simp { - Some(simp) => trait_def.for_each_impl(self.tcx, |def_id| { - let imp = self.tcx.impl_trait_ref(def_id).unwrap(); - let imp_simp = fast_reject::simplify_type(self.tcx, - imp.self_ty(), - true); - if let Some(imp_simp) = imp_simp { - if simp != imp_simp { - return; - } - } - impl_candidates.push(imp); - }), - None => trait_def.for_each_impl(self.tcx, |def_id| { - impl_candidates.push( - self.tcx.impl_trait_ref(def_id).unwrap()); - }) - }; - if impl_candidates.is_empty() { return; } @@ -525,127 +501,118 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { lint_id) .emit(); return; - } else { - match obligation.predicate { - ty::Predicate::Trait(ref trait_predicate) => { - let trait_predicate = - self.resolve_type_vars_if_possible(trait_predicate); + } + match obligation.predicate { + ty::Predicate::Trait(ref trait_predicate) => { + let trait_predicate = + self.resolve_type_vars_if_possible(trait_predicate); - if self.tcx.sess.has_errors() && trait_predicate.references_error() { - return; - } else { - let trait_ref = trait_predicate.to_poly_trait_ref(); - let (post_message, pre_message) = match self.get_parent_trait_ref( - &obligation.cause.code) - { - Some(t) => { - (format!(" in `{}`", t), format!("within `{}`, ", t)) - } - None => (String::new(), String::new()), - }; - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0277, - "the trait bound `{}` is not satisfied{}", - trait_ref.to_predicate(), - post_message); - err.span_label(span, - &format!("{}the trait `{}` is not \ - implemented for `{}`", - pre_message, - trait_ref, - trait_ref.self_ty())); - - // Try to report a help message - - if !trait_ref.has_infer_types() && - self.predicate_can_apply(trait_ref) { - // If a where-clause may be useful, remind the - // user that they can add it. - // - // don't display an on-unimplemented note, as - // these notes will often be of the form - // "the type `T` can't be frobnicated" - // which is somewhat confusing. - err.help(&format!("consider adding a `where {}` bound", - trait_ref.to_predicate())); - } else if let Some(s) = self.on_unimplemented_note(trait_ref, - obligation) { - // If it has a custom "#[rustc_on_unimplemented]" - // error message, let's display it! - err.note(&s); - } else { - // If we can't show anything useful, try to find - // similar impls. - let impl_candidates = - self.find_similar_impl_candidates(trait_ref); - if impl_candidates.len() > 0 { - self.report_similar_impl_candidates(trait_ref, &mut err); - } - } - err - } - } - - ty::Predicate::Equate(ref predicate) => { - let predicate = self.resolve_type_vars_if_possible(predicate); - let err = self.equality_predicate(&obligation.cause, - &predicate).err().unwrap(); - struct_span_err!(self.tcx.sess, span, E0278, - "the requirement `{}` is not satisfied (`{}`)", - predicate, err) - } - - ty::Predicate::RegionOutlives(ref predicate) => { - let predicate = self.resolve_type_vars_if_possible(predicate); - let err = self.region_outlives_predicate(&obligation.cause, - &predicate).err().unwrap(); - struct_span_err!(self.tcx.sess, span, E0279, - "the requirement `{}` is not satisfied (`{}`)", - predicate, err) - } - - ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => { - let predicate = - self.resolve_type_vars_if_possible(&obligation.predicate); - struct_span_err!(self.tcx.sess, span, E0280, - "the requirement `{}` is not satisfied", - predicate) - } - - ty::Predicate::ObjectSafe(trait_def_id) => { - let violations = self.tcx.object_safety_violations(trait_def_id); - self.tcx.report_object_safety_error(span, - trait_def_id, - violations) - } - - ty::Predicate::ClosureKind(closure_def_id, kind) => { - let found_kind = self.closure_kind(closure_def_id).unwrap(); - let closure_span = self.tcx.hir.span_if_local(closure_def_id).unwrap(); - let mut err = struct_span_err!( - self.tcx.sess, closure_span, E0525, - "expected a closure that implements the `{}` trait, \ - but this closure only implements `{}`", - kind, - found_kind); - err.span_note( - obligation.cause.span, - &format!("the requirement to implement \ - `{}` derives from here", kind)); - err.emit(); + if self.tcx.sess.has_errors() && trait_predicate.references_error() { return; } + let trait_ref = trait_predicate.to_poly_trait_ref(); + let (post_message, pre_message) = + self.get_parent_trait_ref(&obligation.cause.code) + .map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t))) + .unwrap_or((String::new(), String::new())); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0277, + "the trait bound `{}` is not satisfied{}", + trait_ref.to_predicate(), + post_message); + err.span_label(span, + &format!("{}the trait `{}` is not \ + implemented for `{}`", + pre_message, + trait_ref, + trait_ref.self_ty())); - ty::Predicate::WellFormed(ty) => { - // WF predicates cannot themselves make - // errors. They can only block due to - // ambiguity; otherwise, they always - // degenerate into other obligations - // (which may fail). - span_bug!(span, "WF predicate not satisfied for {:?}", ty); + // Try to report a help message + + if !trait_ref.has_infer_types() && + self.predicate_can_apply(trait_ref) { + // If a where-clause may be useful, remind the + // user that they can add it. + // + // don't display an on-unimplemented note, as + // these notes will often be of the form + // "the type `T` can't be frobnicated" + // which is somewhat confusing. + err.help(&format!("consider adding a `where {}` bound", + trait_ref.to_predicate())); + } else if let Some(s) = self.on_unimplemented_note(trait_ref, + obligation) { + // If it has a custom "#[rustc_on_unimplemented]" + // error message, let's display it! + err.note(&s); + } else { + // If we can't show anything useful, try to find + // similar impls. + let impl_candidates = self.find_similar_impl_candidates(trait_ref); + self.report_similar_impl_candidates(impl_candidates, &mut err); } + err + } + + ty::Predicate::Equate(ref predicate) => { + let predicate = self.resolve_type_vars_if_possible(predicate); + let err = self.equality_predicate(&obligation.cause, + &predicate).err().unwrap(); + struct_span_err!(self.tcx.sess, span, E0278, + "the requirement `{}` is not satisfied (`{}`)", + predicate, err) + } + + ty::Predicate::RegionOutlives(ref predicate) => { + let predicate = self.resolve_type_vars_if_possible(predicate); + let err = self.region_outlives_predicate(&obligation.cause, + &predicate).err().unwrap(); + struct_span_err!(self.tcx.sess, span, E0279, + "the requirement `{}` is not satisfied (`{}`)", + predicate, err) + } + + ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => { + let predicate = + self.resolve_type_vars_if_possible(&obligation.predicate); + struct_span_err!(self.tcx.sess, span, E0280, + "the requirement `{}` is not satisfied", + predicate) + } + + ty::Predicate::ObjectSafe(trait_def_id) => { + let violations = self.tcx.object_safety_violations(trait_def_id); + self.tcx.report_object_safety_error(span, + trait_def_id, + violations) + } + + ty::Predicate::ClosureKind(closure_def_id, kind) => { + let found_kind = self.closure_kind(closure_def_id).unwrap(); + let closure_span = self.tcx.hir.span_if_local(closure_def_id).unwrap(); + let mut err = struct_span_err!( + self.tcx.sess, closure_span, E0525, + "expected a closure that implements the `{}` trait, \ + but this closure only implements `{}`", + kind, + found_kind); + err.span_note( + obligation.cause.span, + &format!("the requirement to implement \ + `{}` derives from here", kind)); + err.emit(); + return; + } + + ty::Predicate::WellFormed(ty) => { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); } } } @@ -713,38 +680,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { if !reported_violations.insert(violation.clone()) { continue; } - let buf; - let note = match violation { - ObjectSafetyViolation::SizedSelf => { - "the trait cannot require that `Self : Sized`" - } - - ObjectSafetyViolation::SupertraitSelf => { - "the trait cannot use `Self` as a type parameter \ - in the supertrait listing" - } - - ObjectSafetyViolation::Method(name, - MethodViolationCode::StaticMethod) => { - buf = format!("method `{}` has no receiver", name); - &buf - } - - ObjectSafetyViolation::Method(name, - MethodViolationCode::ReferencesSelf) => { - buf = format!("method `{}` references the `Self` type \ - in its arguments or return type", - name); - &buf - } - - ObjectSafetyViolation::Method(name, - MethodViolationCode::Generic) => { - buf = format!("method `{}` has generic type parameters", name); - &buf - } - }; - err.note(note); + err.note(&violation.error_msg()); } err } @@ -774,46 +710,46 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let trait_ref = data.to_poly_trait_ref(); let self_ty = trait_ref.self_ty(); if predicate.references_error() { - } else { - // Typically, this ambiguity should only happen if - // there are unresolved type inference variables - // (otherwise it would suggest a coherence - // failure). But given #21974 that is not necessarily - // the case -- we can have multiple where clauses that - // are only distinguished by a region, which results - // in an ambiguity even when all types are fully - // known, since we don't dispatch based on region - // relationships. + return; + } + // Typically, this ambiguity should only happen if + // there are unresolved type inference variables + // (otherwise it would suggest a coherence + // failure). But given #21974 that is not necessarily + // the case -- we can have multiple where clauses that + // are only distinguished by a region, which results + // in an ambiguity even when all types are fully + // known, since we don't dispatch based on region + // relationships. - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we - // could just skip over all checks where the self-ty is an - // inference variable, but I was afraid that there might be an - // inference variable created, registered as an obligation, and - // then never forced by writeback, and hence by skipping here we'd - // be ignoring the fact that we don't KNOW the type works - // out. Though even that would probably be harmless, given that - // we're only talking about builtin traits, which are known to be - // inhabited. But in any case I just threw in this check for - // has_errors() to be sure that compilation isn't happening - // anyway. In that case, why inundate the user. - if !self.tcx.sess.has_errors() { - if - self.tcx.lang_items.sized_trait() - .map_or(false, |sized_id| sized_id == trait_ref.def_id()) - { - self.need_type_info(obligation, self_ty); - } else { - let mut err = struct_span_err!(self.tcx.sess, - obligation.cause.span, E0283, - "type annotations required: \ - cannot resolve `{}`", - predicate); - self.note_obligation_cause(&mut err, obligation); - err.emit(); - } + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. But in any case I just threw in this check for + // has_errors() to be sure that compilation isn't happening + // anyway. In that case, why inundate the user. + if !self.tcx.sess.has_errors() { + if + self.tcx.lang_items.sized_trait() + .map_or(false, |sized_id| sized_id == trait_ref.def_id()) + { + self.need_type_info(obligation, self_ty); + } else { + let mut err = struct_span_err!(self.tcx.sess, + obligation.cause.span, E0283, + "type annotations required: \ + cannot resolve `{}`", + predicate); + self.note_obligation_cause(&mut err, obligation); + err.emit(); } } } diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs index 60808fbc741..2ebe0d459fa 100644 --- a/src/librustc/traits/object_safety.rs +++ b/src/librustc/traits/object_safety.rs @@ -23,6 +23,7 @@ use hir::def_id::DefId; use traits; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::subst::Substs; +use std::borrow::Cow; use syntax::ast; #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -38,6 +39,25 @@ pub enum ObjectSafetyViolation { Method(ast::Name, MethodViolationCode), } +impl ObjectSafetyViolation { + pub fn error_msg(&self) -> Cow<'static, str> { + match *self { + ObjectSafetyViolation::SizedSelf => + "the trait cannot require that `Self : Sized`".into(), + ObjectSafetyViolation::SupertraitSelf => + "the trait cannot use `Self` as a type parameter \ + in the supertrait listing".into(), + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod) => + format!("method `{}` has no receiver", name).into(), + ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelf) => + format!("method `{}` references the `Self` type \ + in its arguments or return type", name).into(), + ObjectSafetyViolation::Method(name, MethodViolationCode::Generic) => + format!("method `{}` has generic type parameters", name).into(), + } + } +} + /// Reasons a method might not be object-safe. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum MethodViolationCode { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 55b6f61148d..656d0ddf09a 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1097,24 +1097,6 @@ impl<'tcx> InstantiatedPredicates<'tcx> { } } -impl<'tcx> TraitRef<'tcx> { - pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) -> TraitRef<'tcx> { - TraitRef { def_id: def_id, substs: substs } - } - - pub fn self_ty(&self) -> Ty<'tcx> { - self.substs.type_at(0) - } - - pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator> + 'a { - // Select only the "input types" from a trait-reference. For - // now this is all the types that appear in the - // trait-reference, but it should eventually exclude - // associated types. - self.substs.types() - } -} - /// When type checking, we use the `ParameterEnvironment` to track /// details about the type/lifetime parameters that are in scope. /// It primarily stores the bounds information. diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index aa2990679b6..e686d620191 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -389,6 +389,24 @@ pub struct TraitRef<'tcx> { pub substs: &'tcx Substs<'tcx>, } +impl<'tcx> TraitRef<'tcx> { + pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) -> TraitRef<'tcx> { + TraitRef { def_id: def_id, substs: substs } + } + + pub fn self_ty(&self) -> Ty<'tcx> { + self.substs.type_at(0) + } + + pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator> + 'a { + // Select only the "input types" from a trait-reference. For + // now this is all the types that appear in the + // trait-reference, but it should eventually exclude + // associated types. + self.substs.types() + } +} + pub type PolyTraitRef<'tcx> = Binder>; impl<'tcx> PolyTraitRef<'tcx> { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index ae4c94d4b38..bb39c8c4f22 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1878,7 +1878,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec 0 => - format!(" ({} #{})", + format!(" ({} #{})", Escape(&stab.feature), tracker_url, issue_no, issue_no), (false, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 => format!(" (#{})", Escape(&tracker_url), issue_no, @@ -1890,12 +1890,12 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec\ 🔬 \ - This is a nightly-only experimental API.  {}\ + This is a nightly-only experimental API. {}\ ", - unstable_extra)); + unstable_extra)); } else { let text = format!("🔬 \ - This is a nightly-only experimental API.  {}\ + This is a nightly-only experimental API. {}\ {}", unstable_extra, MarkdownHtml(&stab.unstable_reason)); stability.push(format!("
{}
", diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 4ff35738b50..f846ef3e69e 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -27,6 +27,31 @@ //! //! assert!(ecode.success()); //! ``` +//! +//! Calling a command with input and reading its output: +//! +//! ```no_run +//! use std::process::{Command, Stdio}; +//! use std::io::Write; +//! +//! let mut child = Command::new("/bin/cat") +//! .stdin(Stdio::piped()) +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("failed to execute child"); +//! +//! { +//! // limited borrow of stdin +//! let stdin = child.stdin.as_mut().expect("failed to get stdin"); +//! stdin.write_all(b"test").expect("failed to write to stdin"); +//! } +//! +//! let output = child +//! .wait_with_output() +//! .expect("failed to wait on child"); +//! +//! assert_eq!(b"test", output.stdout.as_slice()); +//! ``` #![stable(feature = "process", since = "1.0.0")] diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 7ee1c98565c..68c7e88f67f 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -18,12 +18,57 @@ use time::Duration; /// A type indicating whether a timed wait on a condition variable returned /// due to a time out or not. +/// +/// It is returned by the [`wait_timeout`] method. +/// +/// [`wait_timeout`]: struct.Condvar.html#method.wait_timeout #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[stable(feature = "wait_timeout", since = "1.5.0")] pub struct WaitTimeoutResult(bool); impl WaitTimeoutResult { /// Returns whether the wait was known to have timed out. + /// + /// # Examples + /// + /// This example spawns a thread which will update the boolean value and + /// then wait 100 milliseconds before notifying the condvar. + /// + /// The main thread will wait with a timeout on the condvar and then leave + /// once the boolean has been updated and notified. + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// // We update the boolean value. + /// *started = true; + /// // Let's wait 20 milliseconds before notifying the condvar. + /// thread::sleep(Duration::from_millis(20)); + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let &(ref lock, ref cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// loop { + /// // Let's put a timeout on the condvar's wait. + /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); + /// // 10 milliseconds have passed, or maybe the value changed! + /// started = result.0; + /// if *started == true { + /// // We received the notification and the value has been updated, we can leave. + /// break + /// } + /// } + /// ``` #[stable(feature = "wait_timeout", since = "1.5.0")] pub fn timed_out(&self) -> bool { self.0 @@ -55,15 +100,16 @@ impl WaitTimeoutResult { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = pair.clone(); /// -/// // Inside of our lock, spawn a new thread, and then wait for it to start +/// // Inside of our lock, spawn a new thread, and then wait for it to start. /// thread::spawn(move|| { /// let &(ref lock, ref cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; +/// // We notify the condvar that the value has changed. /// cvar.notify_one(); /// }); /// -/// // wait for the thread to start up +/// // Wait for the thread to start up. /// let &(ref lock, ref cvar) = &*pair; /// let mut started = lock.lock().unwrap(); /// while !*started { @@ -79,6 +125,14 @@ pub struct Condvar { impl Condvar { /// Creates a new condition variable which is ready to be waited on and /// notified. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Condvar; + /// + /// let condvar = Condvar::new(); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new() -> Condvar { let mut c = Condvar { @@ -95,10 +149,10 @@ impl Condvar { /// notification. /// /// This function will atomically unlock the mutex specified (represented by - /// `mutex_guard`) and block the current thread. This means that any calls - /// to `notify_*()` which happen logically after the mutex is unlocked are - /// candidates to wake this thread up. When this function call returns, the - /// lock specified will have been re-acquired. + /// `guard`) and block the current thread. This means that any calls + /// to [`notify_one()`] or [`notify_all()`] which happen logically after the + /// mutex is unlocked are candidates to wake this thread up. When this + /// function call returns, the lock specified will have been re-acquired. /// /// Note that this function is susceptible to spurious wakeups. Condition /// variables normally have a boolean predicate associated with them, and @@ -109,14 +163,46 @@ impl Condvar { /// /// This function will return an error if the mutex being waited on is /// poisoned when this thread re-acquires the lock. For more information, - /// see information about poisoning on the Mutex type. + /// see information about [poisoning] on the [`Mutex`] type. /// /// # Panics /// - /// This function will `panic!()` if it is used with more than one mutex + /// This function will [`panic!()`] if it is used with more than one mutex /// over time. Each condition variable is dynamically bound to exactly one /// mutex to ensure defined behavior across platforms. If this functionality /// is not desired, then unsafe primitives in `sys` are provided. + /// + /// [`notify_one()`]: #method.notify_one + /// [`notify_all()`]: #method.notify_all + /// [poisoning]: ../sync/struct.Mutex.html#poisoning + /// [`Mutex`]: ../sync/struct.Mutex.html + /// [`panic!()`]: ../../std/macro.panic.html + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let &(ref lock, ref cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // As long as the value inside the `Mutex` is false, we wait. + /// while !*started { + /// started = cvar.wait(started).unwrap(); + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult> { @@ -136,7 +222,7 @@ impl Condvar { /// Waits on this condition variable for a notification, timing out after a /// specified duration. /// - /// The semantics of this function are equivalent to `wait()` + /// The semantics of this function are equivalent to [`wait`] /// except that the thread will be blocked for roughly no longer /// than `ms` milliseconds. This method should not be used for /// precise timing due to anomalies such as preemption or platform @@ -150,8 +236,42 @@ impl Condvar { /// The returned boolean is `false` only if the timeout is known /// to have elapsed. /// - /// Like `wait`, the lock specified will be re-acquired when this function + /// Like [`wait`], the lock specified will be re-acquired when this function /// returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait`]: #method.wait + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let &(ref lock, ref cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // As long as the value inside the `Mutex` is false, we wait. + /// loop { + /// let result = cvar.wait_timeout_ms(started, 10).unwrap(); + /// // 10 milliseconds have passed, or maybe the value changed! + /// started = result.0; + /// if *started == true { + /// // We received the notification and the value has been updated, we can leave. + /// break + /// } + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::sync::Condvar::wait_timeout`")] pub fn wait_timeout_ms<'a, T>(&self, guard: MutexGuard<'a, T>, ms: u32) @@ -165,7 +285,7 @@ impl Condvar { /// Waits on this condition variable for a notification, timing out after a /// specified duration. /// - /// The semantics of this function are equivalent to `wait()` except that + /// The semantics of this function are equivalent to [`wait`] except that /// the thread will be blocked for roughly no longer than `dur`. This /// method should not be used for precise timing due to anomalies such as /// preemption or platform differences that may not cause the maximum @@ -175,11 +295,47 @@ impl Condvar { /// measured with a monotonic clock, and not affected by the changes made to /// the system time. /// - /// The returned `WaitTimeoutResult` value indicates if the timeout is + /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed. /// - /// Like `wait`, the lock specified will be re-acquired when this function + /// Like [`wait`], the lock specified will be re-acquired when this function /// returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait`]: #method.wait + /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let &(ref lock, ref cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // as long as the value inside the `Mutex` is false, we wait + /// loop { + /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); + /// // 10 milliseconds have passed, or maybe the value changed! + /// started = result.0; + /// if *started == true { + /// // We received the notification and the value has been updated, we can leave. + /// break + /// } + /// } + /// ``` #[stable(feature = "wait_timeout", since = "1.5.0")] pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, dur: Duration) @@ -200,10 +356,40 @@ impl Condvar { /// Wakes up one blocked thread on this condvar. /// /// If there is a blocked thread on this condition variable, then it will - /// be woken up from its call to `wait` or `wait_timeout`. Calls to + /// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to /// `notify_one` are not buffered in any way. /// - /// To wake up all threads, see `notify_all()`. + /// To wake up all threads, see [`notify_all()`]. + /// + /// [`wait`]: #method.wait + /// [`wait_timeout`]: #method.wait_timeout + /// [`notify_all()`]: #method.notify_all + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let &(ref lock, ref cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // As long as the value inside the `Mutex` is false, we wait. + /// while !*started { + /// started = cvar.wait(started).unwrap(); + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn notify_one(&self) { unsafe { self.inner.notify_one() } @@ -215,7 +401,35 @@ impl Condvar { /// variable are awoken. Calls to `notify_all()` are not buffered in any /// way. /// - /// To wake up only one thread, see `notify_one()`. + /// To wake up only one thread, see [`notify_one()`]. + /// + /// [`notify_one()`]: #method.notify_one + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_all(); + /// }); + /// + /// // Wait for the thread to start up. + /// let &(ref lock, ref cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // As long as the value inside the `Mutex` is false, we wait. + /// while !*started { + /// started = cvar.wait(started).unwrap(); + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn notify_all(&self) { unsafe { self.inner.notify_all() } diff --git a/src/test/compile-fail/feature-gate-unboxed-closures.rs b/src/test/compile-fail/feature-gate-unboxed-closures.rs new file mode 100644 index 00000000000..40050217744 --- /dev/null +++ b/src/test/compile-fail/feature-gate-unboxed-closures.rs @@ -0,0 +1,24 @@ +// 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. + +struct Test; + +impl FnOnce<(u32, u32)> for Test { + type Output = u32; + + extern "rust-call" fn call_once(self, (a, b): (u32, u32)) -> u32 { + a + b + } + //~^^^ ERROR rust-call ABI is subject to change (see issue #29625) +} + +fn main() { + assert_eq!(Test(1u32, 2u32), 3u32); +} diff --git a/src/test/rustdoc/issue-27759.rs b/src/test/rustdoc/issue-27759.rs index fe40ce2bd7d..e82e93230aa 100644 --- a/src/test/rustdoc/issue-27759.rs +++ b/src/test/rustdoc/issue-27759.rs @@ -14,12 +14,12 @@ #![unstable(feature="test", issue="27759")] // @has issue_27759/unstable/index.html -// @has - 'test' +// @has - 'test ' // @has - '#27759' #[unstable(feature="test", issue="27759")] pub mod unstable { // @has issue_27759/unstable/fn.issue.html - // @has - 'test_function' + // @has - 'test_function ' // @has - '#1234567890' #[unstable(feature="test_function", issue="1234567890")] pub fn issue() {} diff --git a/src/test/rustdoc/issue-32374.rs b/src/test/rustdoc/issue-32374.rs index 5cca3708292..6d1f8bc1cf9 100644 --- a/src/test/rustdoc/issue-32374.rs +++ b/src/test/rustdoc/issue-32374.rs @@ -18,10 +18,10 @@ // @has issue_32374/struct.T.html '//*[@class="stab deprecated"]' \ // 'Deprecated since 1.0.0: text' -// @has - 'test' +// @has - 'test ' // @has - '#32374' // @matches issue_32374/struct.T.html '//*[@class="stab unstable"]' \ -// '🔬 This is a nightly-only experimental API. \(test #32374\)$' +// '🔬 This is a nightly-only experimental API. \(test #32374\)$' #[rustc_deprecated(since = "1.0.0", reason = "text")] #[unstable(feature = "test", issue = "32374")] pub struct T; @@ -29,11 +29,11 @@ pub struct T; // @has issue_32374/struct.U.html '//*[@class="stab deprecated"]' \ // 'Deprecated since 1.0.0: deprecated' // @has issue_32374/struct.U.html '//*[@class="stab unstable"]' \ -// '🔬 This is a nightly-only experimental API. (test #32374)' +// '🔬 This is a nightly-only experimental API. (test #32374)' // @has issue_32374/struct.U.html '//details' \ -// '🔬 This is a nightly-only experimental API. (test #32374)' +// '🔬 This is a nightly-only experimental API. (test #32374)' // @has issue_32374/struct.U.html '//summary' \ -// '🔬 This is a nightly-only experimental API. (test #32374)' +// '🔬 This is a nightly-only experimental API. (test #32374)' // @has issue_32374/struct.U.html '//details/p' \ // 'unstable' #[rustc_deprecated(since = "1.0.0", reason = "deprecated")] diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 2c81382bc9b..c84fefd872e 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -169,8 +169,8 @@ pub fn check(path: &Path, bad: &mut bool) { let whitelist = vec![ "abi_ptx", "simd", "cfg_target_has_atomic", - "unboxed_closures", "stmt_expr_attributes", - "cfg_target_thread_local", "unwind_attributes" + "stmt_expr_attributes", + "cfg_target_thread_local", "unwind_attributes", ]; // Only check the number of lang features.