diff --git a/RELEASES.md b/RELEASES.md index 83c44774da283..841467b69c986 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,151 @@ +Version 1.33.0 (2019-02-28) +========================== + +Language +-------- +- [You can now use the `cfg(target_vendor)` attribute.][57465] E.g. + `#[cfg(target_vendor="linux")] fn main() { println!("Hello Linux!"); }` +- [Integer patterns such as in a match expression can now be exhaustive.][56362] + E.g. You can have match statement on a `u8` that covers `0..=255` and + you would no longer be required to have a `_ => unreachable!()` case. +- [You can now have multiple patterns in `if let` and `while let` + expressions.][57532] You can do this with the same syntax as a `match` + expression. E.g. + ```rust + enum Creature { + Crab(String), + Lobster(String), + Person(String), + } + + fn main() { + let state = Creature::Crab("Ferris"); + + if let Creature::Crab(name) | Creature::Person(name) = state { + println!("This creature's name is: {}", name); + } + } + ``` +- [You can now have irrefutable `if let` and `while let` patterns.][57535] Using + this feature will by default produce a warning as this behaviour can be + unintuitive. E.g. `if let _ = 5 {}` +- [You can now use `let` bindings, assignments, expression statements, + and irrefutable pattern destructuring in const functions.][57175] +- [You can now call unsafe const functions.][57067] E.g. + ```rust + const unsafe fn foo() -> i32 { 5 } + const fn bar() -> i32 { + unsafe { foo() } + } + ``` +- [You can now specify multiple attributes in a `cfg_attr` attribute.][57332] + E.g. `#[cfg_attr(all(), must_use, optimize)]` +- [You can now specify a specific alignment with the `#[repr(packed)]` + attribute.][57049] E.g. `#[repr(packed(2))] struct Foo(i16, i32);` is a struct + with an alignment of 2 bytes and a size of 6 bytes. +- [You can now import an item from a module as an `_`.][56303] This allows you to + import a trait's impls, and not have the name in the namespace. E.g. + ```rust + use std::io::Read as _; + + // Allowed as there is only one `Read` in the module. + pub trait Read {} + ``` +- [`extern` functions will now abort by default when panicking.][55982] + This was previously undefined behaviour. + +Compiler +-------- +- [You can now set a linker flavor for `rustc` with the `-Clinker-flavor` + command line argument.][56351] +- [The mininum required LLVM version has been bumped to 6.0.][56642] +- [Added support for the PowerPC64 architecture on FreeBSD.][57615] +- [The `x86_64-fortanix-unknown-sgx` target support has been upgraded to + tier 2 support.][57130] Visit the [platform support][platform-support] page for + information on Rust's platform support. +- [Added support for the `thumbv7neon-linux-androideabi` and + `thumbv7neon-unknown-linux-gnueabihf` targets.][56947] +- [Added support for the `x86_64-unknown-uefi` target.][56769] + +Libraries +--------- +- [The methods `overflowing_{add, sub, mul, shl, shr}` are now `const` + functions for all numeric types.][57566] +- [The methods `rotate_left`, `rotate_right`, and `wrapping_{add, sub, mul, shl, shr}` + are now `const` functions for all numeric types.][57105] +- [The methods `is_positive` and `is_negative` are now `const` functions for + all signed numeric types.][57105] +- [The `get` method for all `NonZero` types is now `const`.][57167] +- [The methods `count_ones`, `count_zeros`, `leading_zeros`, `trailing_zeros`, + `swap_bytes`, `from_be`, `from_le`, `to_be`, `to_le` are now `const` for all + numeric types.][57234] +- [`Ipv4Addr::new` is now a `const` function][57234] + +Stabilized APIs +--------------- +- [`unix::FileExt::read_exact_at`] +- [`unix::FileExt::write_all_at`] +- [`Option::transpose`] +- [`Result::transpose`] +- [`convert::identity`] +- [`pin::Pin`] +- [`marker::Unpin`] +- [`marker::PhantomPinned`] +- [`Vec::resize_with`] +- [`VecDeque::resize_with`] +- [`Duration::as_millis`] +- [`Duration::as_micros`] +- [`Duration::as_nanos`] + + +Cargo +----- +- [Cargo should now rebuild a crate if a file was modified during the initial + build.][cargo/6484] + +Compatibility Notes +------------------- +- The methods `str::{trim_left, trim_right, trim_left_matches, trim_right_matches}` + are now deprecated in the standard library, and their usage will now produce a warning. + Please use the `str::{trim_start, trim_end, trim_start_matches, trim_end_matches}` + methods instead. + +[57615]: https://github.com/rust-lang/rust/pull/57615/ +[57465]: https://github.com/rust-lang/rust/pull/57465/ +[57532]: https://github.com/rust-lang/rust/pull/57532/ +[57535]: https://github.com/rust-lang/rust/pull/57535/ +[57566]: https://github.com/rust-lang/rust/pull/57566/ +[57130]: https://github.com/rust-lang/rust/pull/57130/ +[57167]: https://github.com/rust-lang/rust/pull/57167/ +[57175]: https://github.com/rust-lang/rust/pull/57175/ +[57234]: https://github.com/rust-lang/rust/pull/57234/ +[57332]: https://github.com/rust-lang/rust/pull/57332/ +[56947]: https://github.com/rust-lang/rust/pull/56947/ +[57049]: https://github.com/rust-lang/rust/pull/57049/ +[57067]: https://github.com/rust-lang/rust/pull/57067/ +[56769]: https://github.com/rust-lang/rust/pull/56769/ +[56642]: https://github.com/rust-lang/rust/pull/56642/ +[56303]: https://github.com/rust-lang/rust/pull/56303/ +[56351]: https://github.com/rust-lang/rust/pull/56351/ +[55982]: https://github.com/rust-lang/rust/pull/55982/ +[56362]: https://github.com/rust-lang/rust/pull/56362 +[57105]: https://github.com/rust-lang/rust/pull/57105 +[cargo/6484]: https://github.com/rust-lang/cargo/pull/6484/ +[`unix::FileExt::read_exact_at`]: https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html#method.read_exact_at +[`unix::FileExt::write_all_at`]: https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html#method.write_all_at +[`Option::transpose`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.transpose +[`Result::transpose`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.transpose +[`convert::identity`]: https://doc.rust-lang.org/std/convert/fn.identity.html +[`pin::Pin`]: https://doc.rust-lang.org/std/pin/struct.Pin.html +[`marker::Unpin`]: https://doc.rust-lang.org/stable/std/marker/trait.Unpin.html +[`marker::PhantomPinned`]: https://doc.rust-lang.org/nightly/std/marker/struct.PhantomPinned.html +[`Vec::resize_with`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.resize_with +[`VecDeque::resize_with`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.resize_with +[`Duration::as_millis`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.as_millis +[`Duration::as_micros`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.as_micros +[`Duration::as_nanos`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.as_nanos +[platform-support]: https://forge.rust-lang.org/platform-support.html + Version 1.32.0 (2019-01-17) ========================== diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index dddfa3f158e71..229dafc5fdc3a 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1610,6 +1610,7 @@ impl_is_zero!(u64, |x| x == 0); impl_is_zero!(u128, |x| x == 0); impl_is_zero!(usize, |x| x == 0); +impl_is_zero!(bool, |x| x == false); impl_is_zero!(char, |x| x == '\0'); impl_is_zero!(f32, |x: f32| x.to_bits() == 0); diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index a3e9cfa949312..e7efd9728b94a 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -1,6 +1,6 @@ use convert::TryFrom; use mem; -use ops::{self, Add, Sub}; +use ops::{self, Add, Sub, Try}; use usize; use super::{FusedIterator, TrustedLen}; @@ -368,11 +368,11 @@ impl Iterator for ops::RangeInclusive { Some(Less) => { self.is_empty = Some(false); self.start = plus_n.add_one(); - return Some(plus_n) + return Some(plus_n); } Some(Equal) => { self.is_empty = Some(true); - return Some(plus_n) + return Some(plus_n); } _ => {} } @@ -382,6 +382,34 @@ impl Iterator for ops::RangeInclusive { None } + #[inline] + fn try_fold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + self.compute_is_empty(); + + if self.is_empty() { + return Try::from_ok(init); + } + + let mut accum = init; + + while self.start < self.end { + let n = self.start.add_one(); + let n = mem::replace(&mut self.start, n); + accum = f(accum, n)?; + } + + self.is_empty = Some(true); + + if self.start == self.end { + accum = f(accum, self.start.clone())?; + } + + Try::from_ok(accum) + } + #[inline] fn last(mut self) -> Option { self.next_back() @@ -415,6 +443,33 @@ impl DoubleEndedIterator for ops::RangeInclusive { self.end.clone() }) } + + #[inline] + fn try_rfold(&mut self, init: B, mut f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + self.compute_is_empty(); + + if self.is_empty() { + return Try::from_ok(init); + } + + let mut accum = init; + + while self.start < self.end { + let n = self.end.sub_one(); + let n = mem::replace(&mut self.end, n); + accum = f(accum, n)?; + } + + self.is_empty = Some(true); + + if self.start == self.end { + accum = f(accum, self.start.clone())?; + } + + Try::from_ok(accum) + } } #[stable(feature = "fused", since = "1.26.0")] diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index b3dd5d20299c1..998b597d5e113 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -334,12 +334,14 @@ pub struct RangeInclusive { trait RangeInclusiveEquality: Sized { fn canonicalized_is_empty(range: &RangeInclusive) -> bool; } + impl RangeInclusiveEquality for T { #[inline] default fn canonicalized_is_empty(range: &RangeInclusive) -> bool { range.is_empty.unwrap_or_default() } } + impl RangeInclusiveEquality for T { #[inline] fn canonicalized_is_empty(range: &RangeInclusive) -> bool { diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 9b76a4af98824..d880abb181c20 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -1741,19 +1741,37 @@ fn test_range_inclusive_folds() { assert_eq!((1..=10).sum::(), 55); assert_eq!((1..=10).rev().sum::(), 55); - let mut it = 40..=50; + let mut it = 44..=50; assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it, 44..=50); + assert_eq!(it, 47..=50); + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it, 50..=50); + assert_eq!(it.try_fold(0, i8::checked_add), Some(50)); + assert!(it.is_empty()); + assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); + assert!(it.is_empty()); + + let mut it = 40..=47; + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it, 40..=44); assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it, 44..=47); + assert_eq!(it, 40..=41); + assert_eq!(it.try_rfold(0, i8::checked_add), Some(81)); + assert!(it.is_empty()); + assert_eq!(it.try_rfold(0, i8::checked_add), Some(0)); + assert!(it.is_empty()); let mut it = 10..=20; assert_eq!(it.try_fold(0, |a,b| Some(a+b)), Some(165)); assert!(it.is_empty()); + assert_eq!(it.try_fold(0, |a,b| Some(a+b)), Some(0)); + assert!(it.is_empty()); let mut it = 10..=20; assert_eq!(it.try_rfold(0, |a,b| Some(a+b)), Some(165)); assert!(it.is_empty()); + assert_eq!(it.try_rfold(0, |a,b| Some(a+b)), Some(0)); + assert!(it.is_empty()); } #[test] diff --git a/src/libcore/time.rs b/src/libcore/time.rs index ac7e11754aa3a..91161ca477e39 100644 --- a/src/libcore/time.rs +++ b/src/libcore/time.rs @@ -23,22 +23,6 @@ const MILLIS_PER_SEC: u64 = 1_000; const MICROS_PER_SEC: u64 = 1_000_000; const MAX_NANOS_F64: f64 = ((u64::MAX as u128 + 1)*(NANOS_PER_SEC as u128)) as f64; -/// The duration of one second. -#[unstable(feature = "duration_constants", issue = "57391")] -pub const SECOND: Duration = Duration::from_secs(1); - -/// The duration of one millisecond. -#[unstable(feature = "duration_constants", issue = "57391")] -pub const MILLISECOND: Duration = Duration::from_millis(1); - -/// The duration of one microsecond. -#[unstable(feature = "duration_constants", issue = "57391")] -pub const MICROSECOND: Duration = Duration::from_micros(1); - -/// The duration of one nanosecond. -#[unstable(feature = "duration_constants", issue = "57391")] -pub const NANOSECOND: Duration = Duration::from_nanos(1); - /// A `Duration` type to represent a span of time, typically used for system /// timeouts. /// @@ -75,6 +59,58 @@ pub struct Duration { } impl Duration { + /// The duration of one second. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::SECOND, Duration::from_secs(1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const SECOND: Duration = Duration::from_secs(1); + + /// The duration of one millisecond. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::MILLISECOND, Duration::from_millis(1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MILLISECOND: Duration = Duration::from_millis(1); + + /// The duration of one microsecond. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::MICROSECOND, Duration::from_micros(1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MICROSECOND: Duration = Duration::from_micros(1); + + /// The duration of one nanosecond. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::NANOSECOND, Duration::from_nanos(1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const NANOSECOND: Duration = Duration::from_nanos(1); + /// Creates a new `Duration` from the specified number of whole seconds and /// additional nanoseconds. /// diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index edc97cd28a52a..8c20a6ea55ad0 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -58,8 +58,9 @@ pub unsafe extern fn __rust_start_panic(_payload: usize) -> u32 { #[cfg(all(target_vendor="fortanix", target_env="sgx"))] unsafe fn abort() -> ! { - extern "C" { pub fn panic_exit() -> !; } - panic_exit(); + // call std::sys::abort_internal + extern "C" { pub fn __rust_abort() -> !; } + __rust_abort(); } } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 9f48a628274db..f6b68682886e7 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1124,19 +1124,19 @@ impl<'a> LoweringContext<'a> { TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( span, delim, - self.lower_token_stream(tts.into()).into(), + self.lower_token_stream(tts), ).into(), } } fn lower_token(&mut self, token: Token, span: Span) -> TokenStream { match token { - Token::Interpolated(_) => {} - other => return TokenTree::Token(span, other).into(), + Token::Interpolated(nt) => { + let tts = nt.to_tokenstream(&self.sess.parse_sess, span); + self.lower_token_stream(tts) + } + other => TokenTree::Token(span, other).into(), } - - let tts = token.interpolated_to_tokenstream(&self.sess.parse_sess, span); - self.lower_token_stream(tts) } fn lower_arm(&mut self, arm: &Arm) -> hir::Arm { diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 8fe10a85ef380..72aa9570cc2ff 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -339,7 +339,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { fn visit_token(&mut self, t: Token) { if let Token::Interpolated(nt) = t { - if let token::NtExpr(ref expr) = nt.0 { + if let token::NtExpr(ref expr) = *nt { if let ExprKind::Mac(..) = expr.node { self.visit_macro_invoc(expr.id); } diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs index 7430cd3f70961..9787b07ef8cc2 100644 --- a/src/librustc_codegen_llvm/allocator.rs +++ b/src/librustc_codegen_llvm/allocator.rs @@ -9,7 +9,7 @@ use rustc_allocator::{ALLOCATOR_METHODS, AllocatorTy}; use crate::ModuleLlvm; use crate::llvm::{self, False, True}; -pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) { +pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &mut ModuleLlvm, kind: AllocatorKind) { let llcx = &*mods.llcx; let llmod = mods.llmod(); let usize = match &tcx.sess.target.target.target_pointer_width[..] { diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 33531bb69485b..7b2e8ec3df6bb 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -46,7 +46,7 @@ use crate::value::Value; pub fn write_metadata<'a, 'gcx>( tcx: TyCtxt<'a, 'gcx, 'gcx>, - llvm_module: &ModuleLlvm + llvm_module: &mut ModuleLlvm ) -> EncodedMetadata { use std::io::Write; use flate2::Compression; diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index 9219f42d69235..9a75b8495ded3 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -120,11 +120,11 @@ impl ExtraBackendMethods for LlvmCodegenBackend { fn write_metadata<'b, 'gcx>( &self, tcx: TyCtxt<'b, 'gcx, 'gcx>, - metadata: &ModuleLlvm + metadata: &mut ModuleLlvm ) -> EncodedMetadata { base::write_metadata(tcx, metadata) } - fn codegen_allocator(&self, tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) { + fn codegen_allocator(&self, tcx: TyCtxt, mods: &mut ModuleLlvm, kind: AllocatorKind) { unsafe { allocator::codegen(tcx, mods, kind) } } fn compile_codegen_unit<'a, 'tcx: 'a>( diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 7aa75f139d2ae..92f5c39fe5a18 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -551,9 +551,9 @@ pub fn codegen_crate( &["crate"], Some("metadata")).as_str() .to_string(); - let metadata_llvm_module = backend.new_metadata(tcx, &metadata_cgu_name); + let mut metadata_llvm_module = backend.new_metadata(tcx, &metadata_cgu_name); let metadata = time(tcx.sess, "write metadata", || { - backend.write_metadata(tcx, &metadata_llvm_module) + backend.write_metadata(tcx, &mut metadata_llvm_module) }); tcx.sess.profiler(|p| p.end_activity(ProfileCategory::Codegen)); @@ -636,9 +636,9 @@ pub fn codegen_crate( &["crate"], Some("allocator")).as_str() .to_string(); - let modules = backend.new_metadata(tcx, &llmod_id); + let mut modules = backend.new_metadata(tcx, &llmod_id); time(tcx.sess, "write allocator module", || { - backend.codegen_allocator(tcx, &modules, kind) + backend.codegen_allocator(tcx, &mut modules, kind) }); Some(ModuleCodegen { diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/src/librustc_codegen_ssa/traits/backend.rs index 73c7614d91393..6f92024ea8af3 100644 --- a/src/librustc_codegen_ssa/traits/backend.rs +++ b/src/librustc_codegen_ssa/traits/backend.rs @@ -36,9 +36,9 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se fn write_metadata<'b, 'gcx>( &self, tcx: TyCtxt<'b, 'gcx, 'gcx>, - metadata: &Self::Module, + metadata: &mut Self::Module, ) -> EncodedMetadata; - fn codegen_allocator(&self, tcx: TyCtxt, mods: &Self::Module, kind: AllocatorKind); + fn codegen_allocator(&self, tcx: TyCtxt, mods: &mut Self::Module, kind: AllocatorKind); fn compile_codegen_unit<'a, 'tcx: 'a>( &self, tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 4d65862375a96..739125729d41a 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -130,6 +130,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); let mut is_loop_move = false; + let is_partial_move = move_site_vec.iter().any(|move_site| { + let move_out = self.move_data.moves[(*move_site).moi]; + let moved_place = &self.move_data.move_paths[move_out.path].place; + used_place != moved_place && used_place.is_prefix_of(moved_place) + }); for move_site in &move_site_vec { let move_out = self.move_data.moves[(*move_site).moi]; let moved_place = &self.move_data.move_paths[move_out.path].place; @@ -175,8 +180,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.span_label( span, format!( - "value {} here after move", - desired_action.as_verb_in_past_tense() + "value {} here {}", + desired_action.as_verb_in_past_tense(), + if is_partial_move { "after partial move" } else { "after move" }, ), ); } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index a82f8df154725..29de5308a3cdb 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -1025,7 +1025,7 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { fn visit_token(&mut self, t: Token) { if let Token::Interpolated(nt) = t { - if let token::NtExpr(ref expr) = nt.0 { + if let token::NtExpr(ref expr) = *nt { if let ast::ExprKind::Mac(..) = expr.node { self.visit_invoc(expr.id); } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 10f234fd3ca6a..fe13be0b12664 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2234,7 +2234,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir_id, def_id, substs, user_self_ty, self.tag(), ); - if !substs.is_noop() { + if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { let canonicalized = self.infcx.canonicalize_user_type_annotation( &UserType::TypeOf(def_id, UserSubsts { substs, @@ -2429,15 +2429,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let ty = self.to_ty(ast_ty); debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); - // If the type given by the user has free regions, save it for - // later, since NLL would like to enforce those. Also pass in - // types that involve projections, since those can resolve to - // `'static` bounds (modulo #54940, which hopefully will be - // fixed by the time you see this comment, dear reader, - // although I have my doubts). Also pass in types with inference - // types, because they may be repeated. Other sorts of things - // are already sufficiently enforced with erased regions. =) - if ty.has_free_regions() || ty.has_projections() || ty.has_infer_types() { + if Self::can_contain_user_lifetime_bounds(ty) { let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty)); debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); self.tables.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); @@ -2446,6 +2438,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty } + // If the type given by the user has free regions, save it for later, since + // NLL would like to enforce those. Also pass in types that involve + // projections, since those can resolve to `'static` bounds (modulo #54940, + // which hopefully will be fixed by the time you see this comment, dear + // reader, although I have my doubts). Also pass in types with inference + // types, because they may be repeated. Other sorts of things are already + // sufficiently enforced with erased regions. =) + fn can_contain_user_lifetime_bounds(t: T) -> bool + where + T: TypeFoldable<'tcx> + { + t.has_free_regions() || t.has_projections() || t.has_infer_types() + } + pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.tables.borrow().node_types().get(id) { Some(&t) => t, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ea9034ae3a1e0..1bc2d9fc87e4b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -974,11 +974,13 @@ impl Attributes { "https://doc.rust-lang.org/nightly", }; // This is a primitive so the url is done "by hand". + let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); Some((s.clone(), - format!("{}{}std/primitive.{}.html", + format!("{}{}std/primitive.{}.html{}", url, if !url.ends_with('/') { "/" } else { "" }, - fragment))) + &fragment[..tail], + &fragment[tail..]))) } else { panic!("This isn't a primitive?!"); } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index d037154272db6..418289f98ea2e 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1117,7 +1117,11 @@ themePicker.onblur = handleThemeButtonsBlur; // with rustdoc running in parallel. all_indexes.sort(); let mut w = try_err!(File::create(&dst), &dst); - try_err!(writeln!(&mut w, "var N=null,E=\"\",T=\"t\",U=\"u\",searchIndex={{}};"), &dst); + if options.enable_minification { + try_err!(writeln!(&mut w, "var N=null,E=\"\",T=\"t\",U=\"u\",searchIndex={{}};"), &dst); + } else { + try_err!(writeln!(&mut w, "var searchIndex={{}};"), &dst); + } try_err!(write_minify_replacer(&mut w, &format!("{}\n{}", variables.join(""), all_indexes.join("\n")), options.enable_minification), diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 7f7fffbba100e..979d2bf271241 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1,6 +1,7 @@ use rustc::lint as lint; use rustc::hir; use rustc::hir::def::Def; +use rustc::hir::def_id::DefId; use rustc::ty; use syntax; use syntax::ast::{self, Ident, NodeId}; @@ -126,6 +127,17 @@ impl<'a, 'tcx, 'rcx> LinkCollector<'a, 'tcx, 'rcx> { path = name.clone(); } } + if let Some(prim) = is_primitive(&path, false) { + let did = primitive_impl(cx, &path).ok_or(())?; + return cx.tcx.associated_items(did) + .find(|item| item.ident.name == item_name) + .and_then(|item| match item.kind { + ty::AssociatedKind::Method => Some("method"), + _ => None, + }) + .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name)))) + .ok_or(()); + } // FIXME: `with_scope` requires the `NodeId` of a module. let ty = cx.resolver.borrow_mut() @@ -589,3 +601,26 @@ fn is_primitive(path_str: &str, is_val: bool) -> Option { PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1) } } + +fn primitive_impl(cx: &DocContext, path_str: &str) -> Option { + let tcx = cx.tcx; + match path_str { + "u8" => tcx.lang_items().u8_impl(), + "u16" => tcx.lang_items().u16_impl(), + "u32" => tcx.lang_items().u32_impl(), + "u64" => tcx.lang_items().u64_impl(), + "u128" => tcx.lang_items().u128_impl(), + "usize" => tcx.lang_items().usize_impl(), + "i8" => tcx.lang_items().i8_impl(), + "i16" => tcx.lang_items().i16_impl(), + "i32" => tcx.lang_items().i32_impl(), + "i64" => tcx.lang_items().i64_impl(), + "i128" => tcx.lang_items().i128_impl(), + "isize" => tcx.lang_items().isize_impl(), + "f32" => tcx.lang_items().f32_impl(), + "f64" => tcx.lang_items().f64_impl(), + "str" => tcx.lang_items().str_impl(), + "char" => tcx.lang_items().char_impl(), + _ => None, + } +} diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index b634ea43e34e5..e6d546b97da14 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -286,6 +286,9 @@ pub use self::stdio::{_print, _eprint}; #[doc(no_inline, hidden)] pub use self::stdio::{set_panic, set_print}; +// Used inside the standard library for panic output. +pub(crate) use self::stdio::stderr_raw; + pub mod prelude; mod buffered; mod cursor; diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 0324568e6fb52..9b60badd3388d 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -32,7 +32,9 @@ struct StdoutRaw(stdio::Stdout); /// /// This handle is not synchronized or buffered in any fashion. Constructed via /// the `std::io::stdio::stderr_raw` function. -struct StderrRaw(stdio::Stderr); +/// +/// Not exposed, but used inside the standard library for panic output. +pub(crate) struct StderrRaw(stdio::Stderr); /// Constructs a new raw handle to the standard input of this process. /// @@ -61,7 +63,9 @@ fn stdout_raw() -> io::Result { stdio::Stdout::new().map(StdoutRaw) } /// /// The returned handle has no external synchronization or buffering layered on /// top. -fn stderr_raw() -> io::Result { stdio::Stderr::new().map(StderrRaw) } +/// +/// Not exposed, but used inside the standard library for panic output. +pub(crate) fn stderr_raw() -> io::Result { stdio::Stderr::new().map(StderrRaw) } impl Read for StdinRaw { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } diff --git a/src/libstd/sys/cloudabi/stdio.rs b/src/libstd/sys/cloudabi/stdio.rs index 2cd3477cd519d..81d79213f615c 100644 --- a/src/libstd/sys/cloudabi/stdio.rs +++ b/src/libstd/sys/cloudabi/stdio.rs @@ -9,8 +9,10 @@ impl Stdin { pub fn new() -> io::Result { Ok(Stdin(())) } +} - pub fn read(&self, _: &mut [u8]) -> io::Result { +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { Ok(0) } } @@ -19,15 +21,17 @@ impl Stdout { pub fn new() -> io::Result { Ok(Stdout(())) } +} - pub fn write(&self, _: &[u8]) -> io::Result { +impl io::Write for Stdout { + fn write(&mut self, _buf: &[u8]) -> io::Result { Err(io::Error::new( io::ErrorKind::BrokenPipe, "Stdout is not connected to any output in this environment", )) } - pub fn flush(&self) -> io::Result<()> { + fn flush(&mut self) -> io::Result<()> { Ok(()) } } @@ -36,29 +40,18 @@ impl Stderr { pub fn new() -> io::Result { Ok(Stderr(())) } +} - pub fn write(&self, _: &[u8]) -> io::Result { +impl io::Write for Stderr { + fn write(&mut self, _buf: &[u8]) -> io::Result { Err(io::Error::new( io::ErrorKind::BrokenPipe, "Stderr is not connected to any output in this environment", )) } - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } -} - -// FIXME: right now this raw stderr handle is used in a few places because -// std::io::stderr_raw isn't exposed, but once that's exposed this impl -// should go away -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - Stderr::write(self, data) - } - fn flush(&mut self) -> io::Result<()> { - Stderr::flush(self) + Ok(()) } } diff --git a/src/libstd/sys/redox/stdio.rs b/src/libstd/sys/redox/stdio.rs index e814b0942c14d..b4eb01fd6cc52 100644 --- a/src/libstd/sys/redox/stdio.rs +++ b/src/libstd/sys/redox/stdio.rs @@ -8,10 +8,12 @@ pub struct Stderr(()); impl Stdin { pub fn new() -> io::Result { Ok(Stdin(())) } +} - pub fn read(&self, data: &mut [u8]) -> io::Result { +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { let fd = FileDesc::new(0); - let ret = fd.read(data); + let ret = fd.read(buf); fd.into_raw(); ret } @@ -19,44 +21,35 @@ impl Stdin { impl Stdout { pub fn new() -> io::Result { Ok(Stdout(())) } +} - pub fn write(&self, data: &[u8]) -> io::Result { +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { let fd = FileDesc::new(1); - let ret = fd.write(data); + let ret = fd.write(buf); fd.into_raw(); ret } - pub fn flush(&self) -> io::Result<()> { + fn flush(&mut self) -> io::Result<()> { cvt(syscall::fsync(1)).and(Ok(())) } } impl Stderr { pub fn new() -> io::Result { Ok(Stderr(())) } +} - pub fn write(&self, data: &[u8]) -> io::Result { +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { let fd = FileDesc::new(2); - let ret = fd.write(data); + let ret = fd.write(buf); fd.into_raw(); ret } - pub fn flush(&self) -> io::Result<()> { - cvt(syscall::fsync(2)).and(Ok(())) - } -} - -// FIXME: right now this raw stderr handle is used in a few places because -// std::io::stderr_raw isn't exposed, but once that's exposed this impl -// should go away -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - Stderr::write(self, data) - } - fn flush(&mut self) -> io::Result<()> { - Stderr::flush(self) + cvt(syscall::fsync(2)).and(Ok(())) } } @@ -67,5 +60,5 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { - Stderr::new().ok() + io::stderr_raw().ok() } diff --git a/src/libstd/sys/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs index 33163a556c16d..4dfbe86d14f4d 100644 --- a/src/libstd/sys/sgx/rwlock.rs +++ b/src/libstd/sys/sgx/rwlock.rs @@ -204,6 +204,7 @@ pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { } #[no_mangle] +// NB. used by both libunwind and libpanic_abort pub unsafe extern "C" fn __rust_abort() { ::sys::abort_internal(); } diff --git a/src/libstd/sys/sgx/stdio.rs b/src/libstd/sys/sgx/stdio.rs index 6f206cd9a51d7..57d66ed9a853c 100644 --- a/src/libstd/sys/sgx/stdio.rs +++ b/src/libstd/sys/sgx/stdio.rs @@ -16,46 +16,39 @@ fn with_std_fd R, R>(fd: abi::Fd, f: F) -> R { impl Stdin { pub fn new() -> io::Result { Ok(Stdin(())) } +} - pub fn read(&self, data: &mut [u8]) -> io::Result { - with_std_fd(abi::FD_STDIN, |fd| fd.read(data)) +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + with_std_fd(abi::FD_STDIN, |fd| fd.read(buf)) } } impl Stdout { pub fn new() -> io::Result { Ok(Stdout(())) } +} - pub fn write(&self, data: &[u8]) -> io::Result { - with_std_fd(abi::FD_STDOUT, |fd| fd.write(data)) +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + with_std_fd(abi::FD_STDOUT, |fd| fd.write(buf)) } - pub fn flush(&self) -> io::Result<()> { + fn flush(&mut self) -> io::Result<()> { with_std_fd(abi::FD_STDOUT, |fd| fd.flush()) } } impl Stderr { pub fn new() -> io::Result { Ok(Stderr(())) } - - pub fn write(&self, data: &[u8]) -> io::Result { - with_std_fd(abi::FD_STDERR, |fd| fd.write(data)) - } - - pub fn flush(&self) -> io::Result<()> { - with_std_fd(abi::FD_STDERR, |fd| fd.flush()) - } } -// FIXME: right now this raw stderr handle is used in a few places because -// std::io::stderr_raw isn't exposed, but once that's exposed this impl -// should go away impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - Stderr::write(self, data) + fn write(&mut self, buf: &[u8]) -> io::Result { + with_std_fd(abi::FD_STDERR, |fd| fd.write(buf)) } fn flush(&mut self) -> io::Result<()> { - Stderr::flush(self) + with_std_fd(abi::FD_STDERR, |fd| fd.flush()) } } diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 715f2eafb2d9b..82bd2fbf77612 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -8,10 +8,12 @@ pub struct Stderr(()); impl Stdin { pub fn new() -> io::Result { Ok(Stdin(())) } +} - pub fn read(&self, data: &mut [u8]) -> io::Result { +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { let fd = FileDesc::new(libc::STDIN_FILENO); - let ret = fd.read(data); + let ret = fd.read(buf); fd.into_raw(); // do not close this FD ret } @@ -19,44 +21,35 @@ impl Stdin { impl Stdout { pub fn new() -> io::Result { Ok(Stdout(())) } +} - pub fn write(&self, data: &[u8]) -> io::Result { +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { let fd = FileDesc::new(libc::STDOUT_FILENO); - let ret = fd.write(data); + let ret = fd.write(buf); fd.into_raw(); // do not close this FD ret } - pub fn flush(&self) -> io::Result<()> { + fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl Stderr { pub fn new() -> io::Result { Ok(Stderr(())) } +} - pub fn write(&self, data: &[u8]) -> io::Result { +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { let fd = FileDesc::new(libc::STDERR_FILENO); - let ret = fd.write(data); + let ret = fd.write(buf); fd.into_raw(); // do not close this FD ret } - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } -} - -// FIXME: right now this raw stderr handle is used in a few places because -// std::io::stderr_raw isn't exposed, but once that's exposed this impl -// should go away -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - Stderr::write(self, data) - } - fn flush(&mut self) -> io::Result<()> { - Stderr::flush(self) + Ok(()) } } @@ -67,5 +60,5 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { - Stderr::new().ok() + io::stderr_raw().ok() } diff --git a/src/libstd/sys/wasm/stdio.rs b/src/libstd/sys/wasm/stdio.rs index 201619b2fb139..657655004e9bf 100644 --- a/src/libstd/sys/wasm/stdio.rs +++ b/src/libstd/sys/wasm/stdio.rs @@ -9,9 +9,11 @@ impl Stdin { pub fn new() -> io::Result { Ok(Stdin) } +} - pub fn read(&self, data: &mut [u8]) -> io::Result { - Ok(ReadSysCall::perform(0, data)) +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + Ok(ReadSysCall::perform(0, buf)) } } @@ -19,13 +21,15 @@ impl Stdout { pub fn new() -> io::Result { Ok(Stdout) } +} - pub fn write(&self, data: &[u8]) -> io::Result { - WriteSysCall::perform(1, data); - Ok(data.len()) +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + WriteSysCall::perform(1, buf); + Ok(buf.len()) } - pub fn flush(&self) -> io::Result<()> { + fn flush(&mut self) -> io::Result<()> { Ok(()) } } @@ -34,23 +38,16 @@ impl Stderr { pub fn new() -> io::Result { Ok(Stderr) } - - pub fn write(&self, data: &[u8]) -> io::Result { - WriteSysCall::perform(2, data); - Ok(data.len()) - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } } impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - (&*self).write(data) + fn write(&mut self, buf: &[u8]) -> io::Result { + WriteSysCall::perform(2, buf); + Ok(buf.len()) } + fn flush(&mut self) -> io::Result<()> { - (&*self).flush() + Ok(()) } } @@ -62,7 +59,7 @@ pub fn is_ebadf(_err: &io::Error) -> bool { pub fn panic_output() -> Option { if cfg!(feature = "wasm_syscall") { - Stderr::new().ok() + io::stderr_raw().ok() } else { None } diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 89038da6295f2..22fcb3cf8df89 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -220,13 +220,27 @@ pub trait OpenOptionsExt { /// the specified value (or combines it with `custom_flags` and `attributes` /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). /// - /// By default, `security_qos_flags` is set to `SECURITY_ANONYMOUS`. For - /// information about possible values, see [Impersonation Levels] on the - /// Windows Dev Center site. - /// + /// By default `security_qos_flags` is not set. It should be specified when + /// opening a named pipe, to control to which degree a server process can + /// act on behalf of a client process (security impersonation level). + /// + /// When `security_qos_flags` is not set a malicious program can gain the + /// elevated privileges of a privileged Rust process when it allows opening + /// user-specified paths, by tricking it into opening a named pipe. So + /// arguably `security_qos_flags` should also be set when opening arbitrary + /// paths. However the bits can then conflict with other flags, specifically + /// `FILE_FLAG_OPEN_NO_RECALL`. + /// + /// For information about possible values, see [Impersonation Levels] on the + /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set + /// automatically when using this method. + /// # Examples /// /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const SECURITY_IDENTIFICATION: u32 = 0; } /// use std::fs::OpenOptions; /// use std::os::windows::prelude::*; /// @@ -235,9 +249,9 @@ pub trait OpenOptionsExt { /// .create(true) /// /// // Sets the flag value to `SecurityIdentification`. - /// .security_qos_flags(1) + /// .security_qos_flags(winapi::SECURITY_IDENTIFICATION) /// - /// .open("foo.txt"); + /// .open("\\.\pipe\MyPipe"); /// ``` /// /// [`CreateFile`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858.aspx diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 533b8ae9ba2c0..e1f815a525c98 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -191,7 +191,11 @@ impl OpenOptions { pub fn access_mode(&mut self, access_mode: u32) { self.access_mode = Some(access_mode); } pub fn share_mode(&mut self, share_mode: u32) { self.share_mode = share_mode; } pub fn attributes(&mut self, attrs: u32) { self.attributes = attrs; } - pub fn security_qos_flags(&mut self, flags: u32) { self.security_qos_flags = flags; } + pub fn security_qos_flags(&mut self, flags: u32) { + // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can + // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on. + self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT; + } pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) { self.security_attributes = attrs as usize; } @@ -239,7 +243,6 @@ impl OpenOptions { self.custom_flags | self.attributes | self.security_qos_flags | - if self.security_qos_flags != 0 { c::SECURITY_SQOS_PRESENT } else { 0 } | if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 } } } diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 08a166bd8c504..2527168a968c4 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -252,9 +252,9 @@ impl Stdio { // should still be unavailable so propagate the // INVALID_HANDLE_VALUE. Stdio::Inherit => { - match stdio::get(stdio_id) { + match stdio::get_handle(stdio_id) { Ok(io) => { - let io = Handle::new(io.handle()); + let io = Handle::new(io); let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); io.into_raw(); diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index 0ea19a855257b..5963541a89338 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -1,220 +1,283 @@ #![unstable(issue = "0", feature = "windows_stdio")] -use io::prelude::*; - +use char::decode_utf16; use cmp; -use io::{self, Cursor}; +use io; use ptr; use str; -use sync::Mutex; use sys::c; use sys::cvt; use sys::handle::Handle; -pub enum Output { - Console(c::HANDLE), - Pipe(c::HANDLE), -} - +// Don't cache handles but get them fresh for every read/write. This allows us to track changes to +// the value over time (such as if a process calls `SetStdHandle` while it's running). See #40490. pub struct Stdin { - utf8: Mutex>>, + surrogate: u16, } pub struct Stdout; pub struct Stderr; -pub fn get(handle: c::DWORD) -> io::Result { - let handle = unsafe { c::GetStdHandle(handle) }; +// Apparently Windows doesn't handle large reads on stdin or writes to stdout/stderr well (see +// #13304 for details). +// +// From MSDN (2011): "The storage for this buffer is allocated from a shared heap for the +// process that is 64 KB in size. The maximum size of the buffer will depend on heap usage." +// +// We choose the cap at 8 KiB because libuv does the same, and it seems to be acceptable so far. +const MAX_BUFFER_SIZE: usize = 8192; + +// The standard buffer size of BufReader for Stdin should be able to hold 3x more bytes than there +// are `u16`'s in MAX_BUFFER_SIZE. This ensures the read data can always be completely decoded from +// UTF-16 to UTF-8. +pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3; + +pub fn get_handle(handle_id: c::DWORD) -> io::Result { + let handle = unsafe { c::GetStdHandle(handle_id) }; if handle == c::INVALID_HANDLE_VALUE { Err(io::Error::last_os_error()) } else if handle.is_null() { Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32)) } else { - let mut out = 0; - match unsafe { c::GetConsoleMode(handle, &mut out) } { - 0 => Ok(Output::Pipe(handle)), - _ => Ok(Output::Console(handle)), - } + Ok(handle) } } -fn write(handle: c::DWORD, data: &[u8]) -> io::Result { - let handle = match get(handle)? { - Output::Console(c) => c, - Output::Pipe(p) => { - let handle = Handle::new(p); - let ret = handle.write(data); - handle.into_raw(); - return ret - } - }; +fn is_console(handle: c::HANDLE) -> bool { + // `GetConsoleMode` will return false (0) if this is a pipe (we don't care about the reported + // mode). This will only detect Windows Console, not other terminals connected to a pipe like + // MSYS. Which is exactly what we need, as only Windows Console needs a conversion to UTF-16. + let mut mode = 0; + unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } +} - // As with stdin on windows, stdout often can't handle writes of large - // sizes. For an example, see #14940. For this reason, don't try to - // write the entire output buffer on windows. - // - // For some other references, it appears that this problem has been - // encountered by others [1] [2]. We choose the number 8K just because - // libuv does the same. +fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { + let handle = get_handle(handle_id)?; + if !is_console(handle) { + let handle = Handle::new(handle); + let ret = handle.write(data); + handle.into_raw(); // Don't close the handle + return ret; + } + + // As the console is meant for presenting text, we assume bytes of `data` come from a string + // and are encoded as UTF-8, which needs to be encoded as UTF-16. // - // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232 - // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html - const OUT_MAX: usize = 8192; - let len = cmp::min(data.len(), OUT_MAX); + // If the data is not valid UTF-8 we write out as many bytes as are valid. + // Only when there are no valid bytes (which will happen on the next call), return an error. + let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2); let utf8 = match str::from_utf8(&data[..len]) { Ok(s) => s, - Err(ref e) if e.valid_up_to() == 0 => return Err(invalid_encoding()), + Err(ref e) if e.valid_up_to() == 0 => { + return Err(io::Error::new(io::ErrorKind::InvalidData, + "Windows stdio in console mode does not support writing non-UTF-8 byte sequences")) + }, Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(), }; - let utf16 = utf8.encode_utf16().collect::>(); + let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2]; + let mut len_utf16 = 0; + for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) { + *dest = chr; + len_utf16 += 1; + } + let utf16 = &utf16[..len_utf16]; + + let mut written = write_u16s(handle, &utf16)?; + + // Figure out how many bytes of as UTF-8 were written away as UTF-16. + if written == utf16.len() { + Ok(utf8.len()) + } else { + // Make sure we didn't end up writing only half of a surrogate pair (even though the chance + // is tiny). Because it is not possible for user code to re-slice `data` in such a way that + // a missing surrogate can be produced (and also because of the UTF-8 validation above), + // write the missing surrogate out now. + // Buffering it would mean we have to lie about the number of bytes written. + let first_char_remaining = utf16[written]; + if first_char_remaining >= 0xDCEE && first_char_remaining <= 0xDFFF { // low surrogate + // We just hope this works, and give up otherwise + let _ = write_u16s(handle, &utf16[written..written+1]); + written += 1; + } + // Calculate the number of bytes of `utf8` that were actually written. + let mut count = 0; + for ch in utf16[..written].iter() { + count += match ch { + 0x0000 ..= 0x007F => 1, + 0x0080 ..= 0x07FF => 2, + 0xDCEE ..= 0xDFFF => 1, // Low surrogate. We already counted 3 bytes for the other. + _ => 3, + }; + } + debug_assert!(String::from_utf16(&utf16[..written]).unwrap() == utf8[..count]); + Ok(count) + } +} + +fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result { let mut written = 0; cvt(unsafe { c::WriteConsoleW(handle, - utf16.as_ptr() as c::LPCVOID, - utf16.len() as u32, + data.as_ptr() as c::LPCVOID, + data.len() as u32, &mut written, ptr::null_mut()) })?; - - // FIXME if this only partially writes the utf16 buffer then we need to - // figure out how many bytes of `data` were actually written - assert_eq!(written as usize, utf16.len()); - Ok(utf8.len()) + Ok(written as usize) } impl Stdin { pub fn new() -> io::Result { - Ok(Stdin { - utf8: Mutex::new(Cursor::new(Vec::new())), - }) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let handle = match get(c::STD_INPUT_HANDLE)? { - Output::Console(c) => c, - Output::Pipe(p) => { - let handle = Handle::new(p); - let ret = handle.read(buf); - handle.into_raw(); - return ret - } - }; - let mut utf8 = self.utf8.lock().unwrap(); - // Read more if the buffer is empty - if utf8.position() as usize == utf8.get_ref().len() { - let mut utf16 = vec![0u16; 0x1000]; - let mut num = 0; - let mut input_control = readconsole_input_control(CTRL_Z_MASK); - cvt(unsafe { - c::ReadConsoleW(handle, - utf16.as_mut_ptr() as c::LPVOID, - utf16.len() as u32, - &mut num, - &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL) - })?; - utf16.truncate(num as usize); - // FIXME: what to do about this data that has already been read? - let mut data = match String::from_utf16(&utf16) { - Ok(utf8) => utf8.into_bytes(), - Err(..) => return Err(invalid_encoding()), - }; - if let Some(&last_byte) = data.last() { - if last_byte == CTRL_Z { - data.pop(); - } - } - *utf8 = Cursor::new(data); - } - - // MemReader shouldn't error here since we just filled it - utf8.read(buf) + Ok(Stdin { surrogate: 0 }) } } -#[unstable(reason = "not public", issue = "0", feature = "fd_read")] -impl<'a> Read for &'a Stdin { +impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) + let handle = get_handle(c::STD_INPUT_HANDLE)?; + if !is_console(handle) { + let handle = Handle::new(handle); + let ret = handle.read(buf); + handle.into_raw(); // Don't close the handle + return ret; + } + + if buf.len() == 0 { + return Ok(0); + } else if buf.len() < 4 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "Windows stdin in console mode does not support a buffer too small to \ + guarantee holding one arbitrary UTF-8 character (4 bytes)")) + } + + let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2]; + // In the worst case, an UTF-8 string can take 3 bytes for every `u16` of an UTF-16. So + // we can read at most a third of `buf.len()` chars and uphold the guarantee no data gets + // lost. + let amount = cmp::min(buf.len() / 3, utf16_buf.len()); + let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?; + + utf16_to_utf8(&utf16_buf[..read], buf) } } -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout) - } - pub fn write(&self, data: &[u8]) -> io::Result { - write(c::STD_OUTPUT_HANDLE, data) +// We assume that if the last `u16` is an unpaired surrogate they got sliced apart by our +// buffer size, and keep it around for the next read hoping to put them together. +// This is a best effort, and may not work if we are not the only reader on Stdin. +fn read_u16s_fixup_surrogates(handle: c::HANDLE, + buf: &mut [u16], + mut amount: usize, + surrogate: &mut u16) -> io::Result +{ + // Insert possibly remaining unpaired surrogate from last read. + let mut start = 0; + if *surrogate != 0 { + buf[0] = *surrogate; + *surrogate = 0; + start = 1; + if amount == 1 { + // Special case: `Stdin::read` guarantees we can always read at least one new `u16` + // and combine it with an unpaired surrogate, because the UTF-8 buffer is at least + // 4 bytes. + amount = 2; + } } + let mut amount = read_u16s(handle, &mut buf[start..amount])? + start; - pub fn flush(&self) -> io::Result<()> { - Ok(()) + if amount > 0 { + let last_char = buf[amount - 1]; + if last_char >= 0xD800 && last_char <= 0xDBFF { // high surrogate + *surrogate = last_char; + amount -= 1; + } } + Ok(amount) } -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr) +fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result { + // Configure the `pInputControl` parameter to not only return on `\r\n` but also Ctrl-Z, the + // traditional DOS method to indicate end of character stream / user input (SUB). + // See #38274 and https://stackoverflow.com/questions/43836040/win-api-readconsole. + const CTRL_Z: u16 = 0x1A; + const CTRL_Z_MASK: c::ULONG = 1 << CTRL_Z; + let mut input_control = c::CONSOLE_READCONSOLE_CONTROL { + nLength: ::mem::size_of::() as c::ULONG, + nInitialChars: 0, + dwCtrlWakeupMask: CTRL_Z_MASK, + dwControlKeyState: 0, + }; + + let mut amount = 0; + cvt(unsafe { + c::ReadConsoleW(handle, + buf.as_mut_ptr() as c::LPVOID, + buf.len() as u32, + &mut amount, + &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL) + })?; + + if amount > 0 && buf[amount as usize - 1] == CTRL_Z { + amount -= 1; } + Ok(amount as usize) +} - pub fn write(&self, data: &[u8]) -> io::Result { - write(c::STD_ERROR_HANDLE, data) +#[allow(unused)] +fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { + let mut written = 0; + for chr in decode_utf16(utf16.iter().cloned()) { + match chr { + Ok(chr) => { + chr.encode_utf8(&mut utf8[written..]); + written += chr.len_utf8(); + } + Err(_) => { + // We can't really do any better than forget all data and return an error. + return Err(io::Error::new(io::ErrorKind::InvalidData, + "Windows stdin in console mode does not support non-UTF-16 input; \ + encountered unpaired surrogate")) + } + } } + Ok(written) +} - pub fn flush(&self) -> io::Result<()> { - Ok(()) +impl Stdout { + pub fn new() -> io::Result { + Ok(Stdout) } } -// FIXME: right now this raw stderr handle is used in a few places because -// std::io::stderr_raw isn't exposed, but once that's exposed this impl -// should go away -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - Stderr::write(self, data) +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + write(c::STD_OUTPUT_HANDLE, buf) } fn flush(&mut self) -> io::Result<()> { - Stderr::flush(self) + Ok(()) } } -impl Output { - pub fn handle(&self) -> c::HANDLE { - match *self { - Output::Console(c) => c, - Output::Pipe(c) => c, - } +impl Stderr { + pub fn new() -> io::Result { + Ok(Stderr) } } -fn invalid_encoding() -> io::Error { - io::Error::new(io::ErrorKind::InvalidData, - "Windows stdio in console mode does not support non-UTF-8 byte sequences; \ - see https://github.com/rust-lang/rust/issues/23344") -} +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + write(c::STD_ERROR_HANDLE, buf) + } -fn readconsole_input_control(wakeup_mask: c::ULONG) -> c::CONSOLE_READCONSOLE_CONTROL { - c::CONSOLE_READCONSOLE_CONTROL { - nLength: ::mem::size_of::() as c::ULONG, - nInitialChars: 0, - dwCtrlWakeupMask: wakeup_mask, - dwControlKeyState: 0, + fn flush(&mut self) -> io::Result<()> { + Ok(()) } } -const CTRL_Z: u8 = 0x1A; -const CTRL_Z_MASK: c::ULONG = 0x4000000; //1 << 0x1A - pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32) } -// The default buffer capacity is 64k, but apparently windows -// doesn't like 64k reads on stdin. See #13304 for details, but the -// idea is that on windows we use a slightly smaller buffer that's -// been seen to be acceptable. -pub const STDIN_BUF_SIZE: usize = 8 * 1024; - pub fn panic_output() -> Option { - Stderr::new().ok() + io::stderr_raw().ok() } diff --git a/src/libstd/time.rs b/src/libstd/time.rs index e1c2b2b1d4f30..e1f5e1fcb938d 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -23,9 +23,6 @@ use sys_common::mutex::Mutex; #[stable(feature = "time", since = "1.3.0")] pub use core::time::Duration; -#[unstable(feature = "duration_constants", issue = "57391")] -pub use core::time::{SECOND, MILLISECOND, MICROSECOND, NANOSECOND}; - /// A measurement of a monotonically nondecreasing clock. /// Opaque and useful only with `Duration`. /// diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs index 420f7426ad786..b5fc850731404 100644 --- a/src/libsyntax/attr/mod.rs +++ b/src/libsyntax/attr/mod.rs @@ -517,7 +517,7 @@ impl MetaItem { let span = span.with_hi(segments.last().unwrap().ident.span.hi()); Path { span, segments } } - Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => match nt.0 { + Some(TokenTree::Token(_, Token::Interpolated(nt))) => match *nt { token::Nonterminal::NtIdent(ident, _) => Path::from_ident(ident), token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()), token::Nonterminal::NtPath(ref path) => path.clone(), @@ -682,7 +682,7 @@ impl LitKind { match token { Token::Ident(ident, false) if ident.name == "true" => Some(LitKind::Bool(true)), Token::Ident(ident, false) if ident.name == "false" => Some(LitKind::Bool(false)), - Token::Interpolated(ref nt) => match nt.0 { + Token::Interpolated(nt) => match *nt { token::NtExpr(ref v) | token::NtLiteral(ref v) => match v.node { ExprKind::Lit(ref lit) => Some(lit.node.clone()), _ => None, diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 5980261593dbf..452cc2f2c65cc 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -266,7 +266,7 @@ impl TTMacroExpander for F impl MutVisitor for AvoidInterpolatedIdents { fn visit_tt(&mut self, tt: &mut tokenstream::TokenTree) { if let tokenstream::TokenTree::Token(_, token::Interpolated(nt)) = tt { - if let token::NtIdent(ident, is_raw) = nt.0 { + if let token::NtIdent(ident, is_raw) = **nt { *tt = tokenstream::TokenTree::Token(ident.span, token::Ident(ident, is_raw)); } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index f50663f97853f..b805213bb1a4c 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -25,6 +25,7 @@ use syntax_pos::{Span, DUMMY_SP, FileName}; use syntax_pos::hygiene::ExpnFormat; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lrc; use std::fs; use std::io::ErrorKind; use std::{iter, mem}; @@ -584,14 +585,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } AttrProcMacro(ref mac, ..) => { self.gate_proc_macro_attr_item(attr.span, &item); - let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item { + let item_tok = TokenTree::Token(DUMMY_SP, Token::Interpolated(Lrc::new(match item { Annotatable::Item(item) => token::NtItem(item), Annotatable::TraitItem(item) => token::NtTraitItem(item.into_inner()), Annotatable::ImplItem(item) => token::NtImplItem(item.into_inner()), Annotatable::ForeignItem(item) => token::NtForeignItem(item.into_inner()), Annotatable::Stmt(stmt) => token::NtStmt(stmt.into_inner()), Annotatable::Expr(expr) => token::NtExpr(expr), - })).into(); + }))).into(); let input = self.extract_proc_macro_attr_input(attr.tokens, attr.span); let tok_result = mac.expand(self.cx, attr.span, input, item_tok); let res = self.parse_ast_fragment(tok_result, invoc.fragment_kind, diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 5de1ccec8609e..fe1cffb092b1c 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -88,6 +88,7 @@ use smallvec::{smallvec, SmallVec}; use syntax_pos::Span; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lrc; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::mem; use std::ops::{Deref, DerefMut}; @@ -179,7 +180,7 @@ struct MatcherPos<'root, 'tt: 'root> { /// all bound matches from the submatcher into the shared top-level `matches` vector. If `sep` /// and `up` are `Some`, then `matches` is _not_ the shared top-level list. Instead, if one /// wants the shared `matches`, one should use `up.matches`. - matches: Box<[Rc]>, + matches: Box<[Lrc]>, /// The position in `matches` corresponding to the first metavar in this matcher's sequence of /// token trees. In other words, the first metavar in the first token of `top_elts` corresponds /// to `matches[match_lo]`. @@ -218,7 +219,7 @@ struct MatcherPos<'root, 'tt: 'root> { impl<'root, 'tt> MatcherPos<'root, 'tt> { /// Adds `m` as a named match for the `idx`-th metavar. fn push_match(&mut self, idx: usize, m: NamedMatch) { - let matches = Rc::make_mut(&mut self.matches[idx]); + let matches = Lrc::make_mut(&mut self.matches[idx]); matches.push(m); } } @@ -295,11 +296,11 @@ pub fn count_names(ms: &[TokenTree]) -> usize { } /// `len` `Vec`s (initially shared and empty) that will store matches of metavars. -fn create_matches(len: usize) -> Box<[Rc]> { +fn create_matches(len: usize) -> Box<[Lrc]> { if len == 0 { vec![] } else { - let empty_matches = Rc::new(SmallVec::new()); + let empty_matches = Lrc::new(SmallVec::new()); vec![empty_matches; len] }.into_boxed_slice() } @@ -353,8 +354,8 @@ fn initial_matcher_pos<'root, 'tt>(ms: &'tt [TokenTree], open: Span) -> MatcherP /// token tree it was derived from. #[derive(Debug, Clone)] pub enum NamedMatch { - MatchedSeq(Rc, DelimSpan), - MatchedNonterminal(Rc), + MatchedSeq(Lrc, DelimSpan), + MatchedNonterminal(Lrc), } /// Takes a sequence of token trees `ms` representing a matcher which successfully matched input @@ -561,7 +562,7 @@ fn inner_parse_loop<'root, 'tt>( new_item.match_cur += seq.num_captures; new_item.idx += 1; for idx in item.match_cur..item.match_cur + seq.num_captures { - new_item.push_match(idx, MatchedSeq(Rc::new(smallvec![]), sp)); + new_item.push_match(idx, MatchedSeq(Lrc::new(smallvec![]), sp)); } cur_items.push(new_item); } @@ -707,7 +708,7 @@ pub fn parse( let matches = eof_items[0] .matches .iter_mut() - .map(|dv| Rc::make_mut(dv).pop().unwrap()); + .map(|dv| Lrc::make_mut(dv).pop().unwrap()); return nameize(sess, ms, matches); } else if eof_items.len() > 1 { return Error( @@ -780,7 +781,7 @@ pub fn parse( let match_cur = item.match_cur; item.push_match( match_cur, - MatchedNonterminal(Rc::new(parse_nt(&mut parser, span, &ident.as_str()))), + MatchedNonterminal(Lrc::new(parse_nt(&mut parser, span, &ident.as_str()))), ); item.idx += 1; item.match_cur += 1; @@ -829,7 +830,7 @@ fn may_begin_with(name: &str, token: &Token) -> bool { }, "block" => match *token { Token::OpenDelim(token::Brace) => true, - Token::Interpolated(ref nt) => match nt.0 { + Token::Interpolated(ref nt) => match **nt { token::NtItem(_) | token::NtPat(_) | token::NtTy(_) @@ -843,9 +844,9 @@ fn may_begin_with(name: &str, token: &Token) -> bool { }, "path" | "meta" => match *token { Token::ModSep | Token::Ident(..) => true, - Token::Interpolated(ref nt) => match nt.0 { + Token::Interpolated(ref nt) => match **nt { token::NtPath(_) | token::NtMeta(_) => true, - _ => may_be_ident(&nt.0), + _ => may_be_ident(&nt), }, _ => false, }, @@ -862,12 +863,12 @@ fn may_begin_with(name: &str, token: &Token) -> bool { Token::ModSep | // path Token::Lt | // path (UFCS constant) Token::BinOp(token::Shl) => true, // path (double UFCS) - Token::Interpolated(ref nt) => may_be_ident(&nt.0), + Token::Interpolated(ref nt) => may_be_ident(nt), _ => false, }, "lifetime" => match *token { Token::Lifetime(_) => true, - Token::Interpolated(ref nt) => match nt.0 { + Token::Interpolated(ref nt) => match **nt { token::NtLifetime(_) | token::NtTT(_) => true, _ => false, }, diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index b9a50cc6488dd..bd2adb5ac13ba 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -149,7 +149,7 @@ pub fn transcribe(cx: &ExtCtxt<'_>, result.push(tt.clone().into()); } else { sp = sp.apply_mark(cx.current_expansion.mark); - let token = TokenTree::Token(sp, Token::interpolated((**nt).clone())); + let token = TokenTree::Token(sp, Token::Interpolated(nt.clone())); result.push(token.into()); } } else { diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index fd7003d30a1dc..86849f580d081 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -581,9 +581,8 @@ pub fn noop_visit_token(t: &mut Token, vis: &mut T) { token::Ident(id, _is_raw) => vis.visit_ident(id), token::Lifetime(id) => vis.visit_ident(id), token::Interpolated(nt) => { - let nt = Lrc::make_mut(nt); - vis.visit_interpolated(&mut nt.0); - nt.1 = token::LazyTokenStream::new(); + let mut nt = Lrc::make_mut(nt); + vis.visit_interpolated(&mut nt); } _ => {} } diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index b36ca0574cb8d..9020c8c6a2dc6 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -141,7 +141,7 @@ impl<'a> Parser<'a> { /// The delimiters or `=` are still put into the resulting token stream. crate fn parse_meta_item_unrestricted(&mut self) -> PResult<'a, (ast::Path, TokenStream)> { let meta = match self.token { - token::Interpolated(ref nt) => match nt.0 { + token::Interpolated(ref nt) => match **nt { Nonterminal::NtMeta(ref meta) => Some(meta.clone()), _ => None, }, @@ -227,7 +227,7 @@ impl<'a> Parser<'a> { /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> { let nt_meta = match self.token { - token::Interpolated(ref nt) => match nt.0 { + token::Interpolated(ref nt) => match **nt { token::NtMeta(ref e) => Some(e.clone()), _ => None, }, diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index babe0eef20f8c..a7cde5fbb92cd 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -125,6 +125,28 @@ impl<'a> StringReader<'a> { Ok(ret_val) } + /// Immutably extract string if found at current position with given delimiters + pub fn peek_delimited(&self, from_ch: char, to_ch: char) -> Option { + let mut pos = self.pos; + let mut idx = self.src_index(pos); + let mut ch = char_at(&self.src, idx); + if ch != from_ch { + return None; + } + pos = pos + Pos::from_usize(ch.len_utf8()); + let start_pos = pos; + idx = self.src_index(pos); + while idx < self.end_src_index { + ch = char_at(&self.src, idx); + if ch == to_ch { + return Some(self.src[self.src_index(start_pos)..self.src_index(pos)].to_string()); + } + pos = pos + Pos::from_usize(ch.len_utf8()); + idx = self.src_index(pos); + } + return None; + } + fn try_real_token(&mut self) -> Result { let mut t = self.try_next_token()?; loop { diff --git a/src/libsyntax/parse/lexer/unicode_chars.rs b/src/libsyntax/parse/lexer/unicode_chars.rs index 7da4284c0e4aa..94ce6297fbefb 100644 --- a/src/libsyntax/parse/lexer/unicode_chars.rs +++ b/src/libsyntax/parse/lexer/unicode_chars.rs @@ -1,7 +1,7 @@ // Characters and their corresponding confusables were collected from // http://www.unicode.org/Public/security/10.0.0/confusables.txt -use syntax_pos::{Span, NO_EXPANSION}; +use syntax_pos::{Span, Pos, NO_EXPANSION}; use errors::{Applicability, DiagnosticBuilder}; use super::StringReader; @@ -333,14 +333,27 @@ crate fn check_for_substitution<'a>(reader: &StringReader<'a>, let span = Span::new(reader.pos, reader.next_pos, NO_EXPANSION); match ASCII_ARRAY.iter().find(|&&(c, _)| c == ascii_char) { Some(&(ascii_char, ascii_name)) => { - let msg = - format!("Unicode character '{}' ({}) looks like '{}' ({}), but it is not", - ch, u_name, ascii_char, ascii_name); - err.span_suggestion( - span, - &msg, - ascii_char.to_string(), - Applicability::MaybeIncorrect); + // special help suggestion for "directed" double quotes + if let Some(s) = reader.peek_delimited('“', '”') { + let msg = format!("Unicode characters '“' (Left Double Quotation Mark) and \ + '”' (Right Double Quotation Mark) look like '{}' ({}), but are not", + ascii_char, ascii_name); + err.span_suggestion( + Span::new(reader.pos, reader.next_pos + Pos::from_usize(s.len()) + + Pos::from_usize('”'.len_utf8()), NO_EXPANSION), + &msg, + format!("\"{}\"", s), + Applicability::MaybeIncorrect); + } else { + let msg = + format!("Unicode character '{}' ({}) looks like '{}' ({}), but it is not", + ch, u_name, ascii_char, ascii_name); + err.span_suggestion( + span, + &msg, + ascii_char.to_string(), + Applicability::MaybeIncorrect); + } true }, None => { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e6a48912c48b4..5a753e1f8c8a5 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -119,7 +119,7 @@ enum BlockMode { macro_rules! maybe_whole_expr { ($p:expr) => { if let token::Interpolated(nt) = $p.token.clone() { - match nt.0 { + match *nt { token::NtExpr(ref e) | token::NtLiteral(ref e) => { $p.bump(); return Ok((*e).clone()); @@ -146,7 +146,7 @@ macro_rules! maybe_whole_expr { macro_rules! maybe_whole { ($p:expr, $constructor:ident, |$x:ident| $e:expr) => { if let token::Interpolated(nt) = $p.token.clone() { - if let token::$constructor($x) = nt.0.clone() { + if let token::$constructor($x) = (*nt).clone() { $p.bump(); return Ok($e); } @@ -1184,8 +1184,10 @@ impl<'a> Parser<'a> { match ate { Some(_) => { // See doc comment for `unmatched_angle_bracket_count`. - self.unmatched_angle_bracket_count -= 1; - debug!("expect_gt: (decrement) count={:?}", self.unmatched_angle_bracket_count); + if self.unmatched_angle_bracket_count > 0 { + self.unmatched_angle_bracket_count -= 1; + debug!("expect_gt: (decrement) count={:?}", self.unmatched_angle_bracket_count); + } Ok(()) }, @@ -1570,7 +1572,7 @@ impl<'a> Parser<'a> { Some(body) } token::Interpolated(ref nt) => { - match &nt.0 { + match **nt { token::NtBlock(..) => { *at_end = true; let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; @@ -1913,7 +1915,7 @@ impl<'a> Parser<'a> { fn is_named_argument(&mut self) -> bool { let offset = match self.token { - token::Interpolated(ref nt) => match nt.0 { + token::Interpolated(ref nt) => match **nt { token::NtPat(..) => return self.look_ahead(1, |t| t == &token::Colon), _ => 0, } @@ -2099,7 +2101,7 @@ impl<'a> Parser<'a> { /// Matches `token_lit = LIT_INTEGER | ...`. fn parse_lit_token(&mut self) -> PResult<'a, LitKind> { let out = match self.token { - token::Interpolated(ref nt) => match nt.0 { + token::Interpolated(ref nt) => match **nt { token::NtExpr(ref v) | token::NtLiteral(ref v) => match v.node { ExprKind::Lit(ref lit) => { lit.node.clone() } _ => { return self.unexpected_last(&self.token); } @@ -2248,8 +2250,10 @@ impl<'a> Parser<'a> { // See doc comment for `unmatched_angle_bracket_count`. self.expect(&token::Gt)?; - self.unmatched_angle_bracket_count -= 1; - debug!("parse_qpath: (decrement) count={:?}", self.unmatched_angle_bracket_count); + if self.unmatched_angle_bracket_count > 0 { + self.unmatched_angle_bracket_count -= 1; + debug!("parse_qpath: (decrement) count={:?}", self.unmatched_angle_bracket_count); + } self.expect(&token::ModSep)?; @@ -2299,7 +2303,7 @@ impl<'a> Parser<'a> { /// attributes. pub fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, ast::Path> { let meta_ident = match self.token { - token::Interpolated(ref nt) => match nt.0 { + token::Interpolated(ref nt) => match **nt { token::NtMeta(ref meta) => match meta.node { ast::MetaItemKind::Word => Some(meta.ident.clone()), _ => None, @@ -3271,7 +3275,7 @@ impl<'a> Parser<'a> { self.meta_var_span = Some(self.span); // Interpolated identifier and lifetime tokens are replaced with usual identifier // and lifetime tokens, so the former are never encountered during normal parsing. - match nt.0 { + match **nt { token::NtIdent(ident, is_raw) => (token::Ident(ident, is_raw), ident.span), token::NtLifetime(ident) => (token::Lifetime(ident), ident.span), _ => return, @@ -3403,7 +3407,7 @@ impl<'a> Parser<'a> { // can't continue an expression after an ident token::Ident(ident, is_raw) => token::ident_can_begin_expr(ident, is_raw), token::Literal(..) | token::Pound => true, - token::Interpolated(ref nt) => match nt.0 { + token::Interpolated(ref nt) => match **nt { token::NtIdent(..) | token::NtExpr(..) | token::NtBlock(..) | token::NtPath(..) => true, _ => false, diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index ff7f3e0bfaef3..eec422d6266c3 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -13,16 +13,15 @@ use crate::syntax::parse::parse_stream_from_source_str; use crate::syntax::parse::parser::emit_unclosed_delims; use crate::tokenstream::{self, DelimSpan, TokenStream, TokenTree}; -use serialize::{Decodable, Decoder, Encodable, Encoder}; use syntax_pos::symbol::{self, Symbol}; use syntax_pos::{self, Span, FileName}; use log::info; -use std::{cmp, fmt}; +use std::fmt; use std::mem; #[cfg(target_arch = "x86_64")] use rustc_data_structures::static_assert; -use rustc_data_structures::sync::{Lrc, Lock}; +use rustc_data_structures::sync::Lrc; #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] pub enum BinOpToken { @@ -87,7 +86,7 @@ impl Lit { } } - // See comments in `interpolated_to_tokenstream` for why we care about + // See comments in `Nonterminal::to_tokenstream` for why we care about // *probably* equal here rather than actual equality fn probably_equal_for_proc_macro(&self, other: &Lit) -> bool { mem::discriminant(self) == mem::discriminant(other) @@ -184,9 +183,8 @@ pub enum Token { Ident(ast::Ident, /* is_raw */ bool), Lifetime(ast::Ident), - // The `LazyTokenStream` is a pure function of the `Nonterminal`, - // and so the `LazyTokenStream` can be ignored by Eq, Hash, etc. - Interpolated(Lrc<(Nonterminal, LazyTokenStream)>), + Interpolated(Lrc), + // Can be expanded into several tokens. /// A doc comment. DocComment(ast::Name), @@ -209,10 +207,6 @@ pub enum Token { static_assert!(MEM_SIZE_OF_STATEMENT: mem::size_of::() == 16); impl Token { - pub fn interpolated(nt: Nonterminal) -> Token { - Token::Interpolated(Lrc::new((nt, LazyTokenStream::new()))) - } - /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary. pub fn from_ast_ident(ident: ast::Ident) -> Token { Ident(ident, ident.is_raw_guess()) @@ -244,7 +238,7 @@ impl Token { ModSep | // global path Lifetime(..) | // labeled loop Pound => true, // expression attributes - Interpolated(ref nt) => match nt.0 { + Interpolated(ref nt) => match **nt { NtLiteral(..) | NtIdent(..) | NtExpr(..) | @@ -272,7 +266,7 @@ impl Token { Lifetime(..) | // lifetime bound in trait object Lt | BinOp(Shl) | // associated path ModSep => true, // global path - Interpolated(ref nt) => match nt.0 { + Interpolated(ref nt) => match **nt { NtIdent(..) | NtTy(..) | NtPath(..) | NtLifetime(..) => true, _ => false, }, @@ -284,7 +278,7 @@ impl Token { pub fn can_begin_const_arg(&self) -> bool { match self { OpenDelim(Brace) => true, - Interpolated(ref nt) => match nt.0 { + Interpolated(ref nt) => match **nt { NtExpr(..) => true, NtBlock(..) => true, NtLiteral(..) => true, @@ -316,7 +310,7 @@ impl Token { BinOp(Minus) => true, Ident(ident, false) if ident.name == keywords::True.name() => true, Ident(ident, false) if ident.name == keywords::False.name() => true, - Interpolated(ref nt) => match nt.0 { + Interpolated(ref nt) => match **nt { NtLiteral(..) => true, _ => false, }, @@ -328,7 +322,7 @@ impl Token { pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> { match *self { Ident(ident, is_raw) => Some((ident, is_raw)), - Interpolated(ref nt) => match nt.0 { + Interpolated(ref nt) => match **nt { NtIdent(ident, is_raw) => Some((ident, is_raw)), _ => None, }, @@ -339,7 +333,7 @@ impl Token { pub fn lifetime(&self) -> Option { match *self { Lifetime(ident) => Some(ident), - Interpolated(ref nt) => match nt.0 { + Interpolated(ref nt) => match **nt { NtLifetime(ident) => Some(ident), _ => None, }, @@ -367,7 +361,7 @@ impl Token { /// Returns `true` if the token is an interpolated path. fn is_path(&self) -> bool { if let Interpolated(ref nt) = *self { - if let NtPath(..) = nt.0 { + if let NtPath(..) = **nt { return true; } } @@ -508,98 +502,7 @@ impl Token { } } - pub fn interpolated_to_tokenstream(&self, sess: &ParseSess, span: Span) - -> TokenStream - { - let nt = match *self { - Token::Interpolated(ref nt) => nt, - _ => panic!("only works on interpolated tokens"), - }; - - // An `Interpolated` token means that we have a `Nonterminal` - // which is often a parsed AST item. At this point we now need - // to convert the parsed AST to an actual token stream, e.g. - // un-parse it basically. - // - // Unfortunately there's not really a great way to do that in a - // guaranteed lossless fashion right now. The fallback here is - // to just stringify the AST node and reparse it, but this loses - // all span information. - // - // As a result, some AST nodes are annotated with the token - // stream they came from. Here we attempt to extract these - // lossless token streams before we fall back to the - // stringification. - let mut tokens = None; - - match nt.0 { - Nonterminal::NtItem(ref item) => { - tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span); - } - Nonterminal::NtTraitItem(ref item) => { - tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span); - } - Nonterminal::NtImplItem(ref item) => { - tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span); - } - Nonterminal::NtIdent(ident, is_raw) => { - let token = Token::Ident(ident, is_raw); - tokens = Some(TokenTree::Token(ident.span, token).into()); - } - Nonterminal::NtLifetime(ident) => { - let token = Token::Lifetime(ident); - tokens = Some(TokenTree::Token(ident.span, token).into()); - } - Nonterminal::NtTT(ref tt) => { - tokens = Some(tt.clone().into()); - } - _ => {} - } - - let tokens_for_real = nt.1.force(|| { - // FIXME(#43081): Avoid this pretty-print + reparse hack - let source = pprust::token_to_string(self); - let filename = FileName::macro_expansion_source_code(&source); - let (tokens, errors) = parse_stream_from_source_str( - filename, source, sess, Some(span)); - emit_unclosed_delims(&errors, &sess.span_diagnostic); - tokens - }); - - // During early phases of the compiler the AST could get modified - // directly (e.g., attributes added or removed) and the internal cache - // of tokens my not be invalidated or updated. Consequently if the - // "lossless" token stream disagrees with our actual stringification - // (which has historically been much more battle-tested) then we go - // with the lossy stream anyway (losing span information). - // - // Note that the comparison isn't `==` here to avoid comparing spans, - // but it *also* is a "probable" equality which is a pretty weird - // definition. We mostly want to catch actual changes to the AST - // like a `#[cfg]` being processed or some weird `macro_rules!` - // expansion. - // - // What we *don't* want to catch is the fact that a user-defined - // literal like `0xf` is stringified as `15`, causing the cached token - // stream to not be literal `==` token-wise (ignoring spans) to the - // token stream we got from stringification. - // - // Instead the "probably equal" check here is "does each token - // recursively have the same discriminant?" We basically don't look at - // the token values here and assume that such fine grained token stream - // modifications, including adding/removing typically non-semantic - // tokens such as extra braces and commas, don't happen. - if let Some(tokens) = tokens { - if tokens.probably_equal_for_proc_macro(&tokens_for_real) { - return tokens - } - info!("cached tokens found, but they're not \"probably equal\", \ - going with stringified version"); - } - return tokens_for_real - } - - // See comments in `interpolated_to_tokenstream` for why we care about + // See comments in `Nonterminal::to_tokenstream` for why we care about // *probably* equal here rather than actual equality crate fn probably_equal_for_proc_macro(&self, other: &Token) -> bool { if mem::discriminant(self) != mem::discriminant(other) { @@ -731,61 +634,94 @@ impl fmt::Debug for Nonterminal { } } -crate fn is_op(tok: &Token) -> bool { - match *tok { - OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | - Ident(..) | Lifetime(..) | Interpolated(..) | - Whitespace | Comment | Shebang(..) | Eof => false, - _ => true, - } -} - -#[derive(Clone)] -pub struct LazyTokenStream(Lock>); - -impl cmp::Eq for LazyTokenStream {} -impl PartialEq for LazyTokenStream { - fn eq(&self, _other: &LazyTokenStream) -> bool { - true - } -} - -impl fmt::Debug for LazyTokenStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.clone().0.into_inner(), f) - } -} +impl Nonterminal { + pub fn to_tokenstream(&self, sess: &ParseSess, span: Span) -> TokenStream { + // A `Nonterminal` is often a parsed AST item. At this point we now + // need to convert the parsed AST to an actual token stream, e.g. + // un-parse it basically. + // + // Unfortunately there's not really a great way to do that in a + // guaranteed lossless fashion right now. The fallback here is to just + // stringify the AST node and reparse it, but this loses all span + // information. + // + // As a result, some AST nodes are annotated with the token stream they + // came from. Here we attempt to extract these lossless token streams + // before we fall back to the stringification. + let tokens = match *self { + Nonterminal::NtItem(ref item) => { + prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span) + } + Nonterminal::NtTraitItem(ref item) => { + prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span) + } + Nonterminal::NtImplItem(ref item) => { + prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span) + } + Nonterminal::NtIdent(ident, is_raw) => { + let token = Token::Ident(ident, is_raw); + Some(TokenTree::Token(ident.span, token).into()) + } + Nonterminal::NtLifetime(ident) => { + let token = Token::Lifetime(ident); + Some(TokenTree::Token(ident.span, token).into()) + } + Nonterminal::NtTT(ref tt) => { + Some(tt.clone().into()) + } + _ => None, + }; -impl LazyTokenStream { - pub fn new() -> Self { - LazyTokenStream(Lock::new(None)) - } + // FIXME(#43081): Avoid this pretty-print + reparse hack + let source = pprust::nonterminal_to_string(self); + let filename = FileName::macro_expansion_source_code(&source); + let (tokens_for_real, errors) = + parse_stream_from_source_str(filename, source, sess, Some(span)); + emit_unclosed_delims(&errors, &sess.span_diagnostic); - fn force TokenStream>(&self, f: F) -> TokenStream { - let mut opt_stream = self.0.lock(); - if opt_stream.is_none() { - *opt_stream = Some(f()); + // During early phases of the compiler the AST could get modified + // directly (e.g., attributes added or removed) and the internal cache + // of tokens my not be invalidated or updated. Consequently if the + // "lossless" token stream disagrees with our actual stringification + // (which has historically been much more battle-tested) then we go + // with the lossy stream anyway (losing span information). + // + // Note that the comparison isn't `==` here to avoid comparing spans, + // but it *also* is a "probable" equality which is a pretty weird + // definition. We mostly want to catch actual changes to the AST + // like a `#[cfg]` being processed or some weird `macro_rules!` + // expansion. + // + // What we *don't* want to catch is the fact that a user-defined + // literal like `0xf` is stringified as `15`, causing the cached token + // stream to not be literal `==` token-wise (ignoring spans) to the + // token stream we got from stringification. + // + // Instead the "probably equal" check here is "does each token + // recursively have the same discriminant?" We basically don't look at + // the token values here and assume that such fine grained token stream + // modifications, including adding/removing typically non-semantic + // tokens such as extra braces and commas, don't happen. + if let Some(tokens) = tokens { + if tokens.probably_equal_for_proc_macro(&tokens_for_real) { + return tokens + } + info!("cached tokens found, but they're not \"probably equal\", \ + going with stringified version"); } - opt_stream.clone().unwrap() - } -} - -impl Encodable for LazyTokenStream { - fn encode(&self, _: &mut S) -> Result<(), S::Error> { - Ok(()) + return tokens_for_real } } -impl Decodable for LazyTokenStream { - fn decode(_: &mut D) -> Result { - Ok(LazyTokenStream::new()) +crate fn is_op(tok: &Token) -> bool { + match *tok { + OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | + Ident(..) | Lifetime(..) | Interpolated(..) | + Whitespace | Comment | Shebang(..) | Eof => false, + _ => true, } } -impl ::std::hash::Hash for LazyTokenStream { - fn hash(&self, _hasher: &mut H) {} -} - fn prepend_attrs(sess: &ParseSess, attrs: &[ast::Attribute], tokens: Option<&tokenstream::TokenStream>, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index cdf805176a293..dcf9815f6d1ba 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -4,7 +4,7 @@ use crate::ast::{Attribute, MacDelimiter, GenericArg}; use crate::util::parser::{self, AssocOp, Fixity}; use crate::attr; use crate::source_map::{self, SourceMap, Spanned}; -use crate::parse::token::{self, BinOpToken, Token}; +use crate::parse::token::{self, BinOpToken, Nonterminal, Token}; use crate::parse::lexer::comments; use crate::parse::{self, ParseSess}; use crate::print::pp::{self, Breaks}; @@ -257,29 +257,33 @@ pub fn token_to_string(tok: &Token) -> String { token::Comment => "/* */".to_string(), token::Shebang(s) => format!("/* shebang: {}*/", s), - token::Interpolated(ref nt) => match nt.0 { - token::NtExpr(ref e) => expr_to_string(e), - token::NtMeta(ref e) => meta_item_to_string(e), - token::NtTy(ref e) => ty_to_string(e), - token::NtPath(ref e) => path_to_string(e), - token::NtItem(ref e) => item_to_string(e), - token::NtBlock(ref e) => block_to_string(e), - token::NtStmt(ref e) => stmt_to_string(e), - token::NtPat(ref e) => pat_to_string(e), - token::NtIdent(e, false) => ident_to_string(e), - token::NtIdent(e, true) => format!("r#{}", ident_to_string(e)), - token::NtLifetime(e) => ident_to_string(e), - token::NtLiteral(ref e) => expr_to_string(e), - token::NtTT(ref tree) => tt_to_string(tree.clone()), - token::NtArm(ref e) => arm_to_string(e), - token::NtImplItem(ref e) => impl_item_to_string(e), - token::NtTraitItem(ref e) => trait_item_to_string(e), - token::NtGenerics(ref e) => generic_params_to_string(&e.params), - token::NtWhereClause(ref e) => where_clause_to_string(e), - token::NtArg(ref e) => arg_to_string(e), - token::NtVis(ref e) => vis_to_string(e), - token::NtForeignItem(ref e) => foreign_item_to_string(e), - } + token::Interpolated(ref nt) => nonterminal_to_string(nt), + } +} + +pub fn nonterminal_to_string(nt: &Nonterminal) -> String { + match *nt { + token::NtExpr(ref e) => expr_to_string(e), + token::NtMeta(ref e) => meta_item_to_string(e), + token::NtTy(ref e) => ty_to_string(e), + token::NtPath(ref e) => path_to_string(e), + token::NtItem(ref e) => item_to_string(e), + token::NtBlock(ref e) => block_to_string(e), + token::NtStmt(ref e) => stmt_to_string(e), + token::NtPat(ref e) => pat_to_string(e), + token::NtIdent(e, false) => ident_to_string(e), + token::NtIdent(e, true) => format!("r#{}", ident_to_string(e)), + token::NtLifetime(e) => ident_to_string(e), + token::NtLiteral(ref e) => expr_to_string(e), + token::NtTT(ref tree) => tt_to_string(tree.clone()), + token::NtArm(ref e) => arm_to_string(e), + token::NtImplItem(ref e) => impl_item_to_string(e), + token::NtTraitItem(ref e) => trait_item_to_string(e), + token::NtGenerics(ref e) => generic_params_to_string(&e.params), + token::NtWhereClause(ref e) => where_clause_to_string(e), + token::NtArg(ref e) => arg_to_string(e), + token::NtVis(ref e) => vis_to_string(e), + token::NtForeignItem(ref e) => foreign_item_to_string(e), } } diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index c4f2cffb09708..283679e758b54 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -72,7 +72,7 @@ impl TokenTree { } } - // See comments in `interpolated_to_tokenstream` for why we care about + // See comments in `Nonterminal::to_tokenstream` for why we care about // *probably* equal here rather than actual equality // // This is otherwise the same as `eq_unspanned`, only recursing with a @@ -310,7 +310,7 @@ impl TokenStream { t1.next().is_none() && t2.next().is_none() } - // See comments in `interpolated_to_tokenstream` for why we care about + // See comments in `Nonterminal::to_tokenstream` for why we care about // *probably* equal here rather than actual equality // // This is otherwise the same as `eq_unspanned`, only recursing with a diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs index 6aba4d83cd27c..cfc3c931598a1 100644 --- a/src/libsyntax_ext/deriving/custom.rs +++ b/src/libsyntax_ext/deriving/custom.rs @@ -2,6 +2,7 @@ use crate::proc_macro_impl::EXEC_STRATEGY; use crate::proc_macro_server; use errors::FatalError; +use rustc_data_structures::sync::Lrc; use syntax::ast::{self, ItemKind, Attribute, Mac}; use syntax::attr::{mark_used, mark_known}; use syntax::source_map::Span; @@ -65,7 +66,7 @@ impl MultiItemModifier for ProcMacroDerive { // Mark attributes as known, and used. MarkAttrs(&self.attrs).visit_item(&item); - let token = Token::interpolated(token::NtItem(item)); + let token = Token::Interpolated(Lrc::new(token::NtItem(item))); let input = tokenstream::TokenTree::Token(DUMMY_SP, token).into(); let server = proc_macro_server::Rustc::new(ecx); diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs index fd82dac5ab6d8..699539b62f515 100644 --- a/src/libsyntax_ext/proc_macro_server.rs +++ b/src/libsyntax_ext/proc_macro_server.rs @@ -178,8 +178,8 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> tt!(Punct::new('#', false)) } - Interpolated(_) => { - let stream = token.interpolated_to_tokenstream(sess, span); + Interpolated(nt) => { + let stream = nt.to_tokenstream(sess, span); TokenTree::Group(Group { delimiter: Delimiter::None, stream, diff --git a/src/test/rustdoc/intra-link-prim-methods.rs b/src/test/rustdoc/intra-link-prim-methods.rs new file mode 100644 index 0000000000000..af0426b22c557 --- /dev/null +++ b/src/test/rustdoc/intra-link-prim-methods.rs @@ -0,0 +1,3 @@ +#![deny(intra_doc_link_resolution_failure)] + +//! A [`char`] and its [`char::len_utf8`]. diff --git a/src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr b/src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr index bdec94b4f84b0..99cbf64fd5d46 100644 --- a/src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr +++ b/src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr @@ -20,7 +20,7 @@ error[E0382]: use of moved value: `line2` LL | let _moved = (line2.origin, line2.middle); | ------------ value moved here LL | line2.consume(); //[ast]~ ERROR use of partially moved value: `line2` [E0382] - | ^^^^^ value used here after move + | ^^^^^ value used here after partial move | = note: move occurs because `line2.middle` has type `Point`, which does not implement the `Copy` trait diff --git a/src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr b/src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr index bdec94b4f84b0..99cbf64fd5d46 100644 --- a/src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr +++ b/src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr @@ -20,7 +20,7 @@ error[E0382]: use of moved value: `line2` LL | let _moved = (line2.origin, line2.middle); | ------------ value moved here LL | line2.consume(); //[ast]~ ERROR use of partially moved value: `line2` [E0382] - | ^^^^^ value used here after move + | ^^^^^ value used here after partial move | = note: move occurs because `line2.middle` has type `Point`, which does not implement the `Copy` trait diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3.rs b/src/test/ui/consts/const-eval/const-eval-overflow-3.rs index 95deb04fb01d5..6fd8e9cbc806b 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3.rs +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3.rs @@ -3,11 +3,11 @@ // // This test is checking the count in an array expression. -// FIXME (#23926): the error output is not consistent between a -// self-hosted and a cross-compiled setup; therefore resorting to -// error-pattern for now. -// error-pattern: attempt to add with overflow + + + + #![allow(unused_imports)] @@ -18,6 +18,7 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_I : [u32; (i8::MAX as usize) + 1] = [0; (i8::MAX + 1) as usize]; +//~^ ERROR evaluation of constant value failed fn main() { foo(&A_I8_I[..]); diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3b.rs b/src/test/ui/consts/const-eval/const-eval-overflow-3b.rs index 5a6be0c9bd38b..d9b06370dff79 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3b.rs +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3b.rs @@ -7,11 +7,11 @@ // types for the left- and right-hand sides of the addition do not // match (as well as overflow). -// FIXME (#23926): the error output is not consistent between a -// self-hosted and a cross-compiled setup; therefore resorting to -// error-pattern for now. -// error-pattern: mismatched types + + + + #![allow(unused_imports)] @@ -22,6 +22,8 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_I : [u32; (i8::MAX as usize) + 1] = [0; (i8::MAX + 1u8) as usize]; +//~^ ERROR mismatched types +//~| ERROR cannot add `u8` to `i8` fn main() { foo(&A_I8_I[..]); diff --git a/src/test/ui/huge-array-simple.rs b/src/test/ui/huge-array-simple.rs index 5fa874c1fa0d3..8b244a47232fa 100644 --- a/src/test/ui/huge-array-simple.rs +++ b/src/test/ui/huge-array-simple.rs @@ -1,11 +1,16 @@ -// FIXME (#23926): the error output is not consistent between a -// self-hosted and a cross-compiled setup. Skipping for now. - -// ignore-test FIXME(#23926) +// error-pattern: too big for the current architecture +// normalize-stderr-test "; \d+]" -> "; N]" #![allow(exceeding_bitshifts)] +#[cfg(target_pointer_width = "64")] fn main() { let _fat : [u8; (1<<61)+(1<<31)] = [0; (1u64<<61) as usize +(1u64<<31) as usize]; } + +#[cfg(target_pointer_width = "32")] +fn main() { + let _fat : [u8; (1<<31)+(1<<15)] = + [0; (1u32<<31) as usize +(1u32<<15) as usize]; +} diff --git a/src/test/ui/huge-array-simple.stderr b/src/test/ui/huge-array-simple.stderr new file mode 100644 index 0000000000000..3e9c86296cec2 --- /dev/null +++ b/src/test/ui/huge-array-simple.stderr @@ -0,0 +1,4 @@ +error: the type `[u8; N]` is too big for the current architecture + +error: aborting due to previous error + diff --git a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.nll.stderr b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.nll.stderr index 99550235f6969..5f0d2b42671de 100644 --- a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.nll.stderr +++ b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.nll.stderr @@ -5,7 +5,7 @@ LL | Some(right) => consume(right), | ----- value moved here ... LL | consume(node) + r //~ ERROR use of partially moved value: `node` - | ^^^^ value used here after move + | ^^^^ value used here after partial move | = note: move occurs because value has type `std::boxed::Box`, which does not implement the `Copy` trait diff --git a/src/test/ui/moves/moves-based-on-type-match-bindings.nll.stderr b/src/test/ui/moves/moves-based-on-type-match-bindings.nll.stderr index 481c492e16469..6d523fc09c08b 100644 --- a/src/test/ui/moves/moves-based-on-type-match-bindings.nll.stderr +++ b/src/test/ui/moves/moves-based-on-type-match-bindings.nll.stderr @@ -5,7 +5,7 @@ LL | Foo {f} => {} | - value moved here ... LL | touch(&x); //~ ERROR use of partially moved value: `x` - | ^^ value borrowed here after move + | ^^ value borrowed here after partial move | = note: move occurs because `x.f` has type `std::string::String`, which does not implement the `Copy` trait diff --git a/src/test/ui/nll/move-subpaths-moves-root.stderr b/src/test/ui/nll/move-subpaths-moves-root.stderr index 0dd396f855dc5..8b52cc113ccc5 100644 --- a/src/test/ui/nll/move-subpaths-moves-root.stderr +++ b/src/test/ui/nll/move-subpaths-moves-root.stderr @@ -4,7 +4,7 @@ error[E0382]: use of moved value: `x` LL | drop(x.0); | --- value moved here LL | drop(x); //~ ERROR use of moved value - | ^ value used here after move + | ^ value used here after partial move | = note: move occurs because `x.0` has type `std::vec::Vec`, which does not implement the `Copy` trait diff --git a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs index 5dcd41078c787..45f56836d18b5 100644 --- a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs +++ b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs @@ -15,5 +15,7 @@ fn main() { SomeStruct::<_> { t: 22 }; // Nothing interesting given, no annotation. - SomeStruct:: { t: 22 }; //~ ERROR [u32] + SomeStruct:: { t: 22 }; // No lifetime bounds given. + + SomeStruct::<&'static u32> { t: &22 }; //~ ERROR [&ReStatic u32] } diff --git a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr index 123c26195d006..6e24da094e0d2 100644 --- a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr +++ b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr @@ -1,8 +1,8 @@ -error: user substs: UserSubsts { substs: [u32], user_self_ty: None } - --> $DIR/dump-adt-brace-struct.rs:18:5 +error: user substs: UserSubsts { substs: [&ReStatic u32], user_self_ty: None } + --> $DIR/dump-adt-brace-struct.rs:20:5 | -LL | SomeStruct:: { t: 22 }; //~ ERROR [u32] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | SomeStruct::<&'static u32> { t: &22 }; //~ ERROR [&ReStatic u32] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.rs b/src/test/ui/nll/user-annotations/dump-fn-method.rs index 7551a9474dc08..b689f18c22593 100644 --- a/src/test/ui/nll/user-annotations/dump-fn-method.rs +++ b/src/test/ui/nll/user-annotations/dump-fn-method.rs @@ -11,7 +11,7 @@ trait Bazoom { fn method(&self, arg: T, arg2: U) { } } -impl Bazoom for T { +impl Bazoom for S { } fn foo<'a, T>(_: T) { } @@ -22,20 +22,29 @@ fn main() { let x = foo; x(22); - // Here: `u32` is given. - let x = foo::; //~ ERROR [u32] + // Here: `u32` is given, which doesn't contain any lifetimes, so we don't + // have any annotation. + let x = foo::; x(22); + let x = foo::<&'static u32>; //~ ERROR [&ReStatic u32] + x(&22); + // Here: we only want the `T` to be given, the rest should be variables. // // (`T` refers to the declaration of `Bazoom`) let x = <_ as Bazoom>::method::<_>; //~ ERROR [^0, u32, ^1] x(&22, 44, 66); - // Here: all are given - let x = >::method::; //~ ERROR [u8, u16, u32] + // Here: all are given and definitely contain no lifetimes, so we + // don't have any annotation. + let x = >::method::; x(&22, 44, 66); + // Here: all are given and we have a lifetime. + let x = >::method::; //~ ERROR [u8, &ReStatic u16, u32] + x(&22, &44, 66); + // Here: we want in particular that *only* the method `U` // annotation is given, the rest are variables. // diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.stderr b/src/test/ui/nll/user-annotations/dump-fn-method.stderr index a1a4e43e8a3e9..04ceb8e5f8495 100644 --- a/src/test/ui/nll/user-annotations/dump-fn-method.stderr +++ b/src/test/ui/nll/user-annotations/dump-fn-method.stderr @@ -1,23 +1,23 @@ -error: user substs: UserSubsts { substs: [u32], user_self_ty: None } - --> $DIR/dump-fn-method.rs:26:13 +error: user substs: UserSubsts { substs: [&ReStatic u32], user_self_ty: None } + --> $DIR/dump-fn-method.rs:30:13 | -LL | let x = foo::; //~ ERROR [u32] - | ^^^^^^^^^^ +LL | let x = foo::<&'static u32>; //~ ERROR [&ReStatic u32] + | ^^^^^^^^^^^^^^^^^^^ error: user substs: UserSubsts { substs: [^0, u32, ^1], user_self_ty: None } - --> $DIR/dump-fn-method.rs:32:13 + --> $DIR/dump-fn-method.rs:36:13 | LL | let x = <_ as Bazoom>::method::<_>; //~ ERROR [^0, u32, ^1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: user substs: UserSubsts { substs: [u8, u16, u32], user_self_ty: None } - --> $DIR/dump-fn-method.rs:36:13 +error: user substs: UserSubsts { substs: [u8, &ReStatic u16, u32], user_self_ty: None } + --> $DIR/dump-fn-method.rs:45:13 | -LL | let x = >::method::; //~ ERROR [u8, u16, u32] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let x = >::method::; //~ ERROR [u8, &ReStatic u16, u32] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: user substs: UserSubsts { substs: [^0, ^1, u32], user_self_ty: None } - --> $DIR/dump-fn-method.rs:44:5 + --> $DIR/dump-fn-method.rs:53:5 | LL | y.method::(44, 66); //~ ERROR [^0, ^1, u32] | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/nll/user-annotations/inherent-associated-constants.rs b/src/test/ui/nll/user-annotations/inherent-associated-constants.rs new file mode 100644 index 0000000000000..2490187605ac1 --- /dev/null +++ b/src/test/ui/nll/user-annotations/inherent-associated-constants.rs @@ -0,0 +1,17 @@ +#![feature(nll)] + +struct A<'a>(&'a ()); + +impl A<'static> { + const IC: i32 = 10; +} + +fn non_wf_associated_const<'a>(x: i32) { + A::<'a>::IC; //~ ERROR lifetime may not live long enough +} + +fn wf_associated_const<'a>(x: i32) { + A::<'static>::IC; +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/inherent-associated-constants.stderr b/src/test/ui/nll/user-annotations/inherent-associated-constants.stderr new file mode 100644 index 0000000000000..785b39ec887a0 --- /dev/null +++ b/src/test/ui/nll/user-annotations/inherent-associated-constants.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/inherent-associated-constants.rs:10:5 + | +LL | fn non_wf_associated_const<'a>(x: i32) { + | -- lifetime `'a` defined here +LL | A::<'a>::IC; //~ ERROR lifetime may not live long enough + | ^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/unicode-quote-chars.rs b/src/test/ui/parser/unicode-quote-chars.rs new file mode 100644 index 0000000000000..69644211b8a11 --- /dev/null +++ b/src/test/ui/parser/unicode-quote-chars.rs @@ -0,0 +1,7 @@ +// ignore-tidy-linelength + +fn main() { + println!(“hello world”); + //~^ ERROR unknown start of token: \u{201c} + //~^^ HELP Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '"' (Quotation Mark), but are not +} diff --git a/src/test/ui/parser/unicode-quote-chars.stderr b/src/test/ui/parser/unicode-quote-chars.stderr new file mode 100644 index 0000000000000..315e20cf854cd --- /dev/null +++ b/src/test/ui/parser/unicode-quote-chars.stderr @@ -0,0 +1,12 @@ +error: unknown start of token: /u{201c} + --> $DIR/unicode-quote-chars.rs:4:14 + | +LL | println!(“hello world”); + | ^ +help: Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '"' (Quotation Mark), but are not + | +LL | println!("hello world"); + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/ref-suggestion.nll.stderr b/src/test/ui/ref-suggestion.nll.stderr index bef8dcca921e6..402f3c77cdb7b 100644 --- a/src/test/ui/ref-suggestion.nll.stderr +++ b/src/test/ui/ref-suggestion.nll.stderr @@ -25,7 +25,7 @@ LL | (Some(y), ()) => {}, | - value moved here ... LL | x; //~ ERROR use of partially moved value - | ^ value used here after move + | ^ value used here after partial move | = note: move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait diff --git a/src/test/ui/unsized-locals/borrow-after-move.nll.stderr b/src/test/ui/unsized-locals/borrow-after-move.nll.stderr index 0e6a6f6369a15..010e182674b75 100644 --- a/src/test/ui/unsized-locals/borrow-after-move.nll.stderr +++ b/src/test/ui/unsized-locals/borrow-after-move.nll.stderr @@ -5,7 +5,7 @@ LL | let y = *x; | -- value moved here LL | drop_unsized(y); LL | println!("{}", &x); - | ^^ value borrowed here after move + | ^^ value borrowed here after partial move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait @@ -27,7 +27,7 @@ LL | let y = *x; | -- value moved here LL | y.foo(); LL | println!("{}", &x); - | ^^ value borrowed here after move + | ^^ value borrowed here after partial move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait @@ -48,7 +48,7 @@ error[E0382]: borrow of moved value: `x` LL | x.foo(); | - value moved here LL | println!("{}", &x); - | ^^ value borrowed here after move + | ^^ value borrowed here after partial move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait diff --git a/src/test/ui/unsized-locals/double-move.nll.stderr b/src/test/ui/unsized-locals/double-move.nll.stderr index e40289af5ad3d..c0c3e436f535b 100644 --- a/src/test/ui/unsized-locals/double-move.nll.stderr +++ b/src/test/ui/unsized-locals/double-move.nll.stderr @@ -14,7 +14,7 @@ error[E0382]: use of moved value: `x` LL | let _y = *x; | -- value moved here LL | drop_unsized(x); //~ERROR use of moved value - | ^ value used here after move + | ^ value used here after partial move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait