From bb433914b5bdd6898200f8c1cadcfec27fbf7a26 Mon Sep 17 00:00:00 2001 From: Erin Power Date: Sat, 9 May 2020 02:13:02 +0200 Subject: [PATCH 01/28] Update RELEASES.md for 1.44.0 --- RELEASES.md | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 8ea481f7e18cd..b2c4675908e07 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,134 @@ +Version 1.44.0 (2020-06-04) +========================== + +Language +-------- +- [You can now use `async/.await` with `#[no_std]` enabled.][69033] +- [Added the `unused_braces` lint.][70081] + +**Syntax-only changes** + +- [Expansion-driven outline module parsing][69838] + +These are still rejected semantically, so you will likely receive an error but +these changes can be seen and parsed by macros and conditional compilation. + +Compiler +-------- +- [Rustc now respects the `-C codegen-units` flag in incremental mode.][70156] + Additionally when incremental mode rustc defaults to 256 codegen units. +- [Added tier 3\* support for the `aarch64-unknown-none` and + `aarch64-unknown-none-softfloat` targets.][68334] +- [Refactored `catch_unwind`, to have zero-cost unless unwinding is enabled and + a panic is thrown.][67502] + +Libraries +--------- +- [Special cased `vec![]` to map directly to `Vec::new()`.][70632] This allows + `vec![]` to be able to be used in `const` contexts. +- [`convert::Infallible` now implements `Hash`.][70281] +- [`OsString` now implements `DerefMut` and `IndexMut` returning + a `&mut OsStr`.][70048] +- [Unicode 13 is now supported.][69929] +- [`String` now implements `From<&mut str>`.][69661] +- [`IoSlice` now implements `Copy`.][69403] +- [`Vec` now implements `From<[T; N]>`.][68692] Where `N` is less than 32. + +Stabilized APIs +--------------- +- [`PathBuf::with_capacity`] +- [`PathBuf::capacity`] +- [`PathBuf::clear`] +- [`PathBuf::reserve`] +- [`PathBuf::reserve_exact`] +- [`PathBuf::shrink_to_fit`] +- [`f32::to_int_unchecked`] +- [`f64::to_int_unchecked`] +- [`Layout::align_to`] +- [`Layout::pad_to_align`] +- [`Layout::array`] + +Cargo +----- +- [Added the `cargo tree` command which will print a tree graph of + your dependencies.][cargo/8062] E.g. + ``` + mdbook v0.3.2 (/Users/src/rust/mdbook) + ├── ammonia v3.0.0 + │ ├── html5ever v0.24.0 + │ │ ├── log v0.4.8 + │ │ │ └── cfg-if v0.1.9 + │ │ ├── mac v0.1.1 + │ │ └── markup5ever v0.9.0 + │ │ ├── log v0.4.8 (*) + │ │ ├── phf v0.7.24 + │ │ │ └── phf_shared v0.7.24 + │ │ │ ├── siphasher v0.2.3 + │ │ │ └── unicase v1.4.2 + │ │ │ [build-dependencies] + │ │ │ └── version_check v0.1.5 + ... + ``` + +Misc +---- +- [Rustdoc now allows you to specify `--crate-version` to have rustdoc include + the version in the sidebar.][69494] + +Compatibility Notes +------------------- +- [Rustc now correctly generates static libraries on Windows GNU targets with + the `.a` extension, rather than the previous `.lib`.][70937] +- [Removed the `-C no_integrated_as` flag from rustc.][70345] +- [The `file_name` property in JSON output of macro errors now points the actual + source file rather than the previous format of ``.][70969] + **Note:** this may not point a file that actually exists on the user's system. +- [The minimum required external LLVM version has been bumped to LLVM 8.][71147] + +Internal Only +------------- +These changes provide no direct user facing benefits, but represent significant +improvements to the internals and overall performance of rustc and +related tools. + +- [dep_graph Avoid allocating a set on when the number reads are small.][69778] +- [Replace big JS dict with JSON parsing.][71250] + + +[71147]: https://github.com/rust-lang/rust/pull/71147/ +[71250]: https://github.com/rust-lang/rust/pull/71250/ +[70937]: https://github.com/rust-lang/rust/pull/70937/ +[70969]: https://github.com/rust-lang/rust/pull/70969/ +[70632]: https://github.com/rust-lang/rust/pull/70632/ +[70281]: https://github.com/rust-lang/rust/pull/70281/ +[70345]: https://github.com/rust-lang/rust/pull/70345/ +[70048]: https://github.com/rust-lang/rust/pull/70048/ +[70081]: https://github.com/rust-lang/rust/pull/70081/ +[70156]: https://github.com/rust-lang/rust/pull/70156/ +[69838]: https://github.com/rust-lang/rust/pull/69838/ +[69929]: https://github.com/rust-lang/rust/pull/69929/ +[69661]: https://github.com/rust-lang/rust/pull/69661/ +[69778]: https://github.com/rust-lang/rust/pull/69778/ +[69494]: https://github.com/rust-lang/rust/pull/69494/ +[69403]: https://github.com/rust-lang/rust/pull/69403/ +[69033]: https://github.com/rust-lang/rust/pull/69033/ +[68692]: https://github.com/rust-lang/rust/pull/68692/ +[68334]: https://github.com/rust-lang/rust/pull/68334/ +[67502]: https://github.com/rust-lang/rust/pull/67502/ +[cargo/8062]: https://github.com/rust-lang/cargo/pull/8062/ +[`PathBuf::with_capacity`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.with_capacity +[`PathBuf::capacity`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.capacity +[`PathBuf::clear`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.clear +[`PathBuf::reserve`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.reserve +[`PathBuf::reserve_exact`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.reserve_exact +[`PathBuf::shrink_to_fit`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.shrink_to_fit +[`f32::to_int_unchecked`]: https://doc.rust-lang.org/beta/std/primitive.f32.html#method.to_int_unchecked +[`f64::to_int_unchecked`]: https://doc.rust-lang.org/beta/std/primitive.f64.html#method.to_int_unchecked +[`Layout::align_to`]: https://doc.rust-lang.org/beta/std/alloc/struct.Layout.html#method.align_to +[`Layout::pad_to_align`]: https://doc.rust-lang.org/beta/std/alloc/struct.Layout.html#method.pad_to_align +[`Layout::array`]: https://doc.rust-lang.org/beta/std/alloc/struct.Layout.html#method.array + + Version 1.43.1 (2020-05-07) =========================== From bd5ed9e62f330971523fb75390b4a375bc1d875c Mon Sep 17 00:00:00 2001 From: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com> Date: Sat, 9 May 2020 16:30:00 +0200 Subject: [PATCH 02/28] Update RELEASES.md Co-authored-by: Jonas Schievink --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index b2c4675908e07..132e8a6d94850 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -16,7 +16,7 @@ these changes can be seen and parsed by macros and conditional compilation. Compiler -------- - [Rustc now respects the `-C codegen-units` flag in incremental mode.][70156] - Additionally when incremental mode rustc defaults to 256 codegen units. + Additionally when in incremental mode rustc defaults to 256 codegen units. - [Added tier 3\* support for the `aarch64-unknown-none` and `aarch64-unknown-none-softfloat` targets.][68334] - [Refactored `catch_unwind`, to have zero-cost unless unwinding is enabled and From 0f18203e85cfa1a140a53938487bbf82917c2e6b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 14 Apr 2020 12:49:15 +0200 Subject: [PATCH 03/28] Miri: refactor read_discriminant and make it return Scalar --- src/librustc_middle/mir/interpret/error.rs | 4 +- src/librustc_mir/interpret/intrinsics.rs | 10 +- src/librustc_mir/interpret/operand.rs | 139 ++++++++++++--------- src/librustc_mir/interpret/step.rs | 3 +- 4 files changed, 86 insertions(+), 70 deletions(-) diff --git a/src/librustc_middle/mir/interpret/error.rs b/src/librustc_middle/mir/interpret/error.rs index d32a147344992..fc588e049d7d8 100644 --- a/src/librustc_middle/mir/interpret/error.rs +++ b/src/librustc_middle/mir/interpret/error.rs @@ -1,4 +1,4 @@ -use super::{AllocId, Pointer, RawConst, ScalarMaybeUninit}; +use super::{AllocId, Pointer, RawConst, Scalar}; use crate::mir::interpret::ConstValue; use crate::ty::layout::LayoutError; @@ -391,7 +391,7 @@ pub enum UndefinedBehaviorInfo<'tcx> { /// Using a non-character `u32` as character. InvalidChar(u32), /// An enum discriminant was set to a value which was outside the range of valid values. - InvalidDiscriminant(ScalarMaybeUninit), + InvalidDiscriminant(Scalar), /// Using a pointer-not-to-a-function as function pointer. InvalidFunctionPointer(Pointer), /// Using a string that is not valid UTF-8, diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index fc4be82ad90ad..6c7db9fce4cf0 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -218,15 +218,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::discriminant_value => { let place = self.deref_operand(args[0])?; let discr_val = self.read_discriminant(place.into())?.0; - let scalar = match dest.layout.ty.kind { - ty::Int(_) => Scalar::from_int( - self.sign_extend(discr_val, dest.layout) as i128, - dest.layout.size, - ), - ty::Uint(_) => Scalar::from_uint(discr_val, dest.layout.size), - _ => bug!("invalid `discriminant_value` return layout: {:?}", dest.layout), - }; - self.write_scalar(scalar, dest)?; + self.write_scalar(discr_val, dest)?; } sym::unchecked_shl | sym::unchecked_shr diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index a3caa2048a1e7..7ad16a051fdec 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -15,8 +15,8 @@ use rustc_target::abi::{Abi, DiscriminantKind, HasDataLayout, Integer, LayoutOf, use rustc_target::abi::{VariantIdx, Variants}; use super::{ - from_known_layout, sign_extend, truncate, ConstValue, GlobalId, InterpCx, InterpResult, - MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, + from_known_layout, ConstValue, GlobalId, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, + Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -577,91 +577,112 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn read_discriminant( &self, rval: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (u128, VariantIdx)> { + ) -> InterpResult<'tcx, (Scalar, VariantIdx)> { trace!("read_discriminant_value {:#?}", rval.layout); - let (discr_layout, discr_kind, discr_index) = match rval.layout.variants { + let (discr_scalar_layout, discr_kind, discr_index) = match rval.layout.variants { Variants::Single { index } => { - let discr_val = rval - .layout - .ty - .discriminant_for_variant(*self.tcx, index) - .map_or(u128::from(index.as_u32()), |discr| discr.val); - return Ok((discr_val, index)); + let discr = match rval.layout.ty.discriminant_for_variant(*self.tcx, index) { + Some(discr) => { + // This type actually has discriminants. + let discr_layout = self.layout_of(discr.ty)?; + Scalar::from_uint(discr.val, discr_layout.size) + } + None => { + // On a type without actual discriminants, return variant idx as `u8`. + let discr_layout = self.layout_of(self.tcx.types.u8)?; + Scalar::from_uint(index.as_u32(), discr_layout.size) + } + }; + return Ok((discr, index)); } - Variants::Multiple { discr: ref discr_layout, ref discr_kind, discr_index, .. } => { - (discr_layout, discr_kind, discr_index) + Variants::Multiple { ref discr, ref discr_kind, discr_index, .. } => { + (discr, discr_kind, discr_index) } }; - // read raw discriminant value - let discr_op = self.operand_field(rval, discr_index)?; - let discr_val = self.read_immediate(discr_op)?; - let raw_discr = discr_val.to_scalar_or_undef(); - trace!("discr value: {:?}", raw_discr); - // post-process + // There are *three* types/layouts that come into play here: + // - The field storing the discriminant has a layout, which my be a pointer. + // This is `discr_val.layout`; we just use it for sanity checks. + // - The discriminant has a layout for tag storing purposes, which is always an integer. + // This is `discr_layout` and is used to interpret the value we read from the + // discriminant field. + // - The discriminant also has a type for typechecking, and that type's + // layout can be *different*. This is `discr_ty`, and is used for the `Scalar` + // we return. If necessary, a cast from `discr_layout` is performed. + + // Get layout for tag. + let discr_layout = self.layout_of(discr_scalar_layout.value.to_int_ty(*self.tcx))?; + + // Read discriminant value and sanity-check `discr_layout`. + let discr_val = self.read_immediate(self.operand_field(rval, discr_index)?)?; + assert_eq!(discr_layout.size, discr_val.layout.size); + assert_eq!(discr_layout.abi.is_signed(), discr_val.layout.abi.is_signed()); + let discr_val = discr_val.to_scalar()?; + trace!("discriminant value: {:?}", discr_val); + + // Get type used by typechecking. + let discr_ty = match rval.layout.ty.kind { + ty::Adt(adt, _) => { + let discr_int_ty = Integer::from_attr(self, adt.repr.discr_type()); + // The signedness of tag and discriminant is the same. + discr_int_ty.to_ty(*self.tcx, discr_layout.abi.is_signed()) + } + ty::Generator(_, substs, _) => { + let substs = substs.as_generator(); + substs.discr_ty(*self.tcx) + } + _ => bug!("multiple variants for non-adt non-generator"), + }; + + // Figure out which discriminant and variant this corresponds to. Ok(match *discr_kind { DiscriminantKind::Tag => { - let bits_discr = raw_discr - .not_undef() - .and_then(|raw_discr| self.force_bits(raw_discr, discr_val.layout.size)) - .map_err(|_| err_ub!(InvalidDiscriminant(raw_discr.erase_tag())))?; - let real_discr = if discr_val.layout.abi.is_signed() { - // going from layout tag type to typeck discriminant type - // requires first sign extending with the discriminant layout - let sexted = sign_extend(bits_discr, discr_val.layout.size); - // and then zeroing with the typeck discriminant type - let discr_ty = rval - .layout - .ty - .ty_adt_def() - .expect("tagged layout corresponds to adt") - .repr - .discr_type(); - let size = Integer::from_attr(self, discr_ty).size(); - truncate(sexted, size) - } else { - bits_discr - }; - // Make sure we catch invalid discriminants + let discr_bits = self + .force_bits(discr_val, discr_layout.size) + .map_err(|_| err_ub!(InvalidDiscriminant(discr_val.erase_tag())))?; + // Cast discriminant bits to the right type. + let discr_ty_layout = self.layout_of(discr_ty)?; + let discr_val_cast = + self.cast_from_scalar(discr_bits, discr_layout, discr_ty); + let discr_bits = discr_val_cast.assert_bits(discr_ty_layout.size); + // Find variant index for this tag, and catch invalid discriminants. let index = match rval.layout.ty.kind { ty::Adt(adt, _) => { - adt.discriminants(self.tcx.tcx).find(|(_, var)| var.val == real_discr) + adt.discriminants(self.tcx.tcx).find(|(_, var)| var.val == discr_bits) } ty::Generator(def_id, substs, _) => { let substs = substs.as_generator(); substs .discriminants(def_id, self.tcx.tcx) - .find(|(_, var)| var.val == real_discr) + .find(|(_, var)| var.val == discr_bits) } _ => bug!("tagged layout for non-adt non-generator"), } - .ok_or_else(|| err_ub!(InvalidDiscriminant(raw_discr.erase_tag())))?; - (real_discr, index.0) + .ok_or_else(|| err_ub!(InvalidDiscriminant(discr_val.erase_tag())))?; + // Return the cast value, and the index. + (discr_val_cast, index.0) } DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start } => { + // Compute the variant this discriminant corresponds to. With niche layout, + // tag and variant index are the same. let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); - let raw_discr = raw_discr - .not_undef() - .map_err(|_| err_ub!(InvalidDiscriminant(ScalarMaybeUninit::Uninit)))?; - match raw_discr.to_bits_or_ptr(discr_val.layout.size, self) { + let variant = match discr_val.to_bits_or_ptr(discr_layout.size, self) { Err(ptr) => { // The niche must be just 0 (which an inbounds pointer value never is) let ptr_valid = niche_start == 0 && variants_start == variants_end && !self.memory.ptr_may_be_null(ptr); if !ptr_valid { - throw_ub!(InvalidDiscriminant(raw_discr.erase_tag().into())) + throw_ub!(InvalidDiscriminant(discr_val.erase_tag())) } - (u128::from(dataful_variant.as_u32()), dataful_variant) + dataful_variant } - Ok(raw_discr) => { + Ok(bits_discr) => { // We need to use machine arithmetic to get the relative variant idx: // variant_index_relative = discr_val - niche_start_val - let discr_layout = - self.layout_of(discr_layout.value.to_int_ty(*self.tcx))?; - let discr_val = ImmTy::from_uint(raw_discr, discr_layout); + let discr_val = ImmTy::from_uint(bits_discr, discr_layout); let niche_start_val = ImmTy::from_uint(niche_start, discr_layout); let variant_index_relative_val = self.binary_op(mir::BinOp::Sub, discr_val, niche_start_val)?; @@ -684,12 +705,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .variants .len(); assert!(usize::try_from(variant_index).unwrap() < variants_len); - (u128::from(variant_index), VariantIdx::from_u32(variant_index)) + VariantIdx::from_u32(variant_index) } else { - (u128::from(dataful_variant.as_u32()), dataful_variant) + dataful_variant } } - } + }; + // Compute the size of the scalar we need to return. + // FIXME: Why do we not need to do a cast here like we do above? + let size = self.layout_of(discr_ty)?.size; + (Scalar::from_uint(variant.as_u32(), size), variant) } }) } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index fd9815975c19f..bd4df788057e2 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -262,8 +262,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Discriminant(place) => { let op = self.eval_place_to_op(place, None)?; let discr_val = self.read_discriminant(op)?.0; - let size = dest.layout.size; - self.write_scalar(Scalar::from_uint(discr_val, size), dest)?; + self.write_scalar(discr_val, dest)?; } } From 5a3971cdb8694717f56b8c1bc2cd597bb4ac1677 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 May 2020 12:10:13 +0200 Subject: [PATCH 04/28] comments and refactor variable names --- src/librustc_mir/interpret/operand.rs | 84 +++++++++++++++------------ 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 7ad16a051fdec..17dfe8b656c1a 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -580,7 +580,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, (Scalar, VariantIdx)> { trace!("read_discriminant_value {:#?}", rval.layout); - let (discr_scalar_layout, discr_kind, discr_index) = match rval.layout.variants { + // We use "discriminant" to refer to the value associated with a particualr enum variant. + // This is not to be confused with its "variant index", which is just determining its position in the + // declared list of variants -- they can differ with explicitly assigned discriminants. + // We use "tag" to refer to how the discriminant is encoded in memory, which can be either + // straight-forward (`DiscriminantKind::Tag`) or with a niche (`DiscriminantKind::Niche`). + // Unfortunately, the rest of the compiler calls the latter "discriminant", too, which makes things + // rather confusing. + let (tag_scalar_layout, tag_kind, tag_index) = match rval.layout.variants { Variants::Single { index } => { let discr = match rval.layout.ty.discriminant_for_variant(*self.tcx, index) { Some(discr) => { @@ -602,31 +609,31 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // There are *three* types/layouts that come into play here: - // - The field storing the discriminant has a layout, which my be a pointer. - // This is `discr_val.layout`; we just use it for sanity checks. - // - The discriminant has a layout for tag storing purposes, which is always an integer. - // This is `discr_layout` and is used to interpret the value we read from the - // discriminant field. - // - The discriminant also has a type for typechecking, and that type's - // layout can be *different*. This is `discr_ty`, and is used for the `Scalar` - // we return. If necessary, a cast from `discr_layout` is performed. + // - The discriminant has a type for typechecking. This is `discr_ty`, and is used for + // the `Scalar` we return. + // - The discriminant gets encoded as a tag/niche, with layout `tag_layout`. + // This is always an integer, and used to interpret the value we read from the + // tag field. For the return value, a cast to `discr_ty` is performed. + // - The field storing the tag has a layout, which is very similar to + // `tag_layout` but may be a pointer. This is `tag_val.layout`; + // we just use it for sanity checks. // Get layout for tag. - let discr_layout = self.layout_of(discr_scalar_layout.value.to_int_ty(*self.tcx))?; + let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?; - // Read discriminant value and sanity-check `discr_layout`. - let discr_val = self.read_immediate(self.operand_field(rval, discr_index)?)?; - assert_eq!(discr_layout.size, discr_val.layout.size); - assert_eq!(discr_layout.abi.is_signed(), discr_val.layout.abi.is_signed()); - let discr_val = discr_val.to_scalar()?; - trace!("discriminant value: {:?}", discr_val); + // Read tag and sanity-check `tag_layout`. + let tag_val = self.read_immediate(self.operand_field(rval, tag_index)?)?; + assert_eq!(tag_layout.size, tag_val.layout.size); + assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); + let tag_val = tag_val.to_scalar()?; + trace!("tag value: {:?}", tag_val); // Get type used by typechecking. let discr_ty = match rval.layout.ty.kind { ty::Adt(adt, _) => { let discr_int_ty = Integer::from_attr(self, adt.repr.discr_type()); // The signedness of tag and discriminant is the same. - discr_int_ty.to_ty(*self.tcx, discr_layout.abi.is_signed()) + discr_int_ty.to_ty(*self.tcx, tag_layout.abi.is_signed()) } ty::Generator(_, substs, _) => { let substs = substs.as_generator(); @@ -636,17 +643,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Figure out which discriminant and variant this corresponds to. - Ok(match *discr_kind { + Ok(match *tag_kind { DiscriminantKind::Tag => { - let discr_bits = self - .force_bits(discr_val, discr_layout.size) - .map_err(|_| err_ub!(InvalidDiscriminant(discr_val.erase_tag())))?; - // Cast discriminant bits to the right type. - let discr_ty_layout = self.layout_of(discr_ty)?; + let tag_bits = self + .force_bits(tag_val, tag_layout.size) + .map_err(|_| err_ub!(InvalidDiscriminant(tag_val.erase_tag())))?; + // Cast bits from tag layout to discriminant layout. + let discr_layout = self.layout_of(discr_ty)?; let discr_val_cast = - self.cast_from_scalar(discr_bits, discr_layout, discr_ty); - let discr_bits = discr_val_cast.assert_bits(discr_ty_layout.size); - // Find variant index for this tag, and catch invalid discriminants. + self.cast_from_scalar(tag_bits, tag_layout, discr_ty); + let discr_bits = discr_val_cast.assert_bits(discr_layout.size); + // Convert discriminant to variant index, and catch invalid discriminants. let index = match rval.layout.ty.kind { ty::Adt(adt, _) => { adt.discriminants(self.tcx.tcx).find(|(_, var)| var.val == discr_bits) @@ -659,36 +666,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => bug!("tagged layout for non-adt non-generator"), } - .ok_or_else(|| err_ub!(InvalidDiscriminant(discr_val.erase_tag())))?; + .ok_or_else(|| err_ub!(InvalidDiscriminant(tag_val.erase_tag())))?; // Return the cast value, and the index. (discr_val_cast, index.0) } DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start } => { - // Compute the variant this discriminant corresponds to. With niche layout, - // tag and variant index are the same. + // Compute the variant this niche value/"tag" corresponds to. With niche layout, + // discriminant (encoded in niche/tag) and variant index are the same. let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); - let variant = match discr_val.to_bits_or_ptr(discr_layout.size, self) { + let variant = match tag_val.to_bits_or_ptr(tag_layout.size, self) { Err(ptr) => { // The niche must be just 0 (which an inbounds pointer value never is) let ptr_valid = niche_start == 0 && variants_start == variants_end && !self.memory.ptr_may_be_null(ptr); if !ptr_valid { - throw_ub!(InvalidDiscriminant(discr_val.erase_tag())) + throw_ub!(InvalidDiscriminant(tag_val.erase_tag())) } dataful_variant } - Ok(bits_discr) => { + Ok(tag_bits) => { // We need to use machine arithmetic to get the relative variant idx: - // variant_index_relative = discr_val - niche_start_val - let discr_val = ImmTy::from_uint(bits_discr, discr_layout); - let niche_start_val = ImmTy::from_uint(niche_start, discr_layout); + // variant_index_relative = tag_val - niche_start_val + let tag_val = ImmTy::from_uint(tag_bits, tag_layout); + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); let variant_index_relative_val = - self.binary_op(mir::BinOp::Sub, discr_val, niche_start_val)?; + self.binary_op(mir::BinOp::Sub, tag_val, niche_start_val)?; let variant_index_relative = variant_index_relative_val .to_scalar()? - .assert_bits(discr_val.layout.size); + .assert_bits(tag_val.layout.size); // Check if this is in the range that indicates an actual discriminant. if variant_index_relative <= u128::from(variants_end - variants_start) { let variant_index_relative = u32::try_from(variant_index_relative) @@ -712,7 +719,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } }; // Compute the size of the scalar we need to return. - // FIXME: Why do we not need to do a cast here like we do above? + // No need to cast, because the variant index directly serves as discriminant and is + // encoded in the tag. let size = self.layout_of(discr_ty)?.size; (Scalar::from_uint(variant.as_u32(), size), variant) } From f8f80334872cf63412b0fe2e79b87505af2ba6a1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 May 2020 13:22:13 +0200 Subject: [PATCH 05/28] assert that types without discriminant use variant idx of 0 --- src/librustc_mir/interpret/operand.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 17dfe8b656c1a..81ef8172da77f 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -596,7 +596,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Scalar::from_uint(discr.val, discr_layout.size) } None => { - // On a type without actual discriminants, return variant idx as `u8`. + // On a type without actual discriminants, variant is 0. Return variant idx as `u8`. + assert_eq!(index.as_u32(), 0); let discr_layout = self.layout_of(self.tcx.types.u8)?; Scalar::from_uint(index.as_u32(), discr_layout.size) } From d94923ea469b4c104719071a82a4bc051fed77ac Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 May 2020 14:51:50 +0200 Subject: [PATCH 06/28] Format and more tracing --- src/librustc_mir/interpret/operand.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 81ef8172da77f..d46e6e387f508 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -642,6 +642,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => bug!("multiple variants for non-adt non-generator"), }; + trace!("discriminant type: {:?}", discr_ty); // Figure out which discriminant and variant this corresponds to. Ok(match *tag_kind { @@ -651,8 +652,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .map_err(|_| err_ub!(InvalidDiscriminant(tag_val.erase_tag())))?; // Cast bits from tag layout to discriminant layout. let discr_layout = self.layout_of(discr_ty)?; - let discr_val_cast = - self.cast_from_scalar(tag_bits, tag_layout, discr_ty); + let discr_val_cast = self.cast_from_scalar(tag_bits, tag_layout, discr_ty); let discr_bits = discr_val_cast.assert_bits(discr_layout.size); // Convert discriminant to variant index, and catch invalid discriminants. let index = match rval.layout.ty.kind { From 64fbe2fc485477406724a68372f4351dc7a08b0a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 May 2020 15:42:35 +0200 Subject: [PATCH 07/28] Add helper method for determining the type of a discriminant --- src/librustc_middle/mir/tcx.rs | 11 +---- src/librustc_middle/ty/sty.rs | 10 +++++ src/librustc_mir/interpret/operand.rs | 63 ++++++++++----------------- 3 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/librustc_middle/mir/tcx.rs b/src/librustc_middle/mir/tcx.rs index 17edd9f4cb643..174da7db1753d 100644 --- a/src/librustc_middle/mir/tcx.rs +++ b/src/librustc_middle/mir/tcx.rs @@ -5,7 +5,6 @@ use crate::mir::*; use crate::ty::subst::Subst; -use crate::ty::util::IntTypeExt; use crate::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; use rustc_target::abi::VariantIdx; @@ -175,15 +174,7 @@ impl<'tcx> Rvalue<'tcx> { } Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), Rvalue::Discriminant(ref place) => { - let ty = place.ty(local_decls, tcx).ty; - match ty.kind { - ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx), - ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), - _ => { - // This can only be `0`, for now, so `u8` will suffice. - tcx.types.u8 - } - } + place.ty(local_decls, tcx).ty.discriminant_type(tcx) } Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t), Rvalue::NullaryOp(NullOp::SizeOf, _) => tcx.types.usize, diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index 370702f7f221d..6cee224219b63 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -29,6 +29,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::marker::PhantomData; use std::ops::Range; +use ty::util::IntTypeExt; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] #[derive(HashStable, TypeFoldable, Lift)] @@ -2104,6 +2105,15 @@ impl<'tcx> TyS<'tcx> { } } + /// Returns the type of the discriminant of this type. + pub fn discriminant_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match self.kind { + ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx), + ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), + _ => bug!("{:?} does not have a discriminant", self), + } + } + /// When we create a closure, we record its kind (i.e., what trait /// it implements) into its `ClosureSubsts` using a type /// parameter. This is kind of a phantom type, except that the diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index d46e6e387f508..139871310fbf3 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -7,11 +7,11 @@ use std::fmt::Write; use rustc_errors::ErrorReported; use rustc_hir::def::Namespace; use rustc_macros::HashStable; -use rustc_middle::ty::layout::{IntegerExt, PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; use rustc_middle::ty::Ty; use rustc_middle::{mir, ty}; -use rustc_target::abi::{Abi, DiscriminantKind, HasDataLayout, Integer, LayoutOf, Size}; +use rustc_target::abi::{Abi, DiscriminantKind, HasDataLayout, LayoutOf, Size}; use rustc_target::abi::{VariantIdx, Variants}; use super::{ @@ -576,9 +576,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Read discriminant, return the runtime value as well as the variant index. pub fn read_discriminant( &self, - rval: OpTy<'tcx, M::PointerTag>, + op: OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, (Scalar, VariantIdx)> { - trace!("read_discriminant_value {:#?}", rval.layout); + trace!("read_discriminant_value {:#?}", op.layout); + + // Get type and layout of the discriminant. + let discr_layout = self.layout_of(op.layout.ty.discriminant_type(*self.tcx))?; + trace!("discriminant type: {:?}", discr_layout.ty); // We use "discriminant" to refer to the value associated with a particualr enum variant. // This is not to be confused with its "variant index", which is just determining its position in the @@ -587,18 +591,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // straight-forward (`DiscriminantKind::Tag`) or with a niche (`DiscriminantKind::Niche`). // Unfortunately, the rest of the compiler calls the latter "discriminant", too, which makes things // rather confusing. - let (tag_scalar_layout, tag_kind, tag_index) = match rval.layout.variants { + let (tag_scalar_layout, tag_kind, tag_index) = match op.layout.variants { Variants::Single { index } => { - let discr = match rval.layout.ty.discriminant_for_variant(*self.tcx, index) { + let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { Some(discr) => { // This type actually has discriminants. - let discr_layout = self.layout_of(discr.ty)?; + assert_eq!(discr.ty, discr_layout.ty); Scalar::from_uint(discr.val, discr_layout.size) } None => { - // On a type without actual discriminants, variant is 0. Return variant idx as `u8`. + // On a type without actual discriminants, variant is 0. assert_eq!(index.as_u32(), 0); - let discr_layout = self.layout_of(self.tcx.types.u8)?; Scalar::from_uint(index.as_u32(), discr_layout.size) } }; @@ -609,41 +612,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } }; - // There are *three* types/layouts that come into play here: - // - The discriminant has a type for typechecking. This is `discr_ty`, and is used for + // There are *three* layouts that come into play here: + // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for // the `Scalar` we return. - // - The discriminant gets encoded as a tag/niche, with layout `tag_layout`. - // This is always an integer, and used to interpret the value we read from the - // tag field. For the return value, a cast to `discr_ty` is performed. - // - The field storing the tag has a layout, which is very similar to - // `tag_layout` but may be a pointer. This is `tag_val.layout`; - // we just use it for sanity checks. + // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type, + // and used to interpret the value we read from the tag field. + // For the return value, a cast to `discr_layout` is performed. + // - The field storing the tag has a layout, which is very similar to `tag_layout` but + // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks. // Get layout for tag. let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?; // Read tag and sanity-check `tag_layout`. - let tag_val = self.read_immediate(self.operand_field(rval, tag_index)?)?; + let tag_val = self.read_immediate(self.operand_field(op, tag_index)?)?; assert_eq!(tag_layout.size, tag_val.layout.size); assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); let tag_val = tag_val.to_scalar()?; trace!("tag value: {:?}", tag_val); - // Get type used by typechecking. - let discr_ty = match rval.layout.ty.kind { - ty::Adt(adt, _) => { - let discr_int_ty = Integer::from_attr(self, adt.repr.discr_type()); - // The signedness of tag and discriminant is the same. - discr_int_ty.to_ty(*self.tcx, tag_layout.abi.is_signed()) - } - ty::Generator(_, substs, _) => { - let substs = substs.as_generator(); - substs.discr_ty(*self.tcx) - } - _ => bug!("multiple variants for non-adt non-generator"), - }; - trace!("discriminant type: {:?}", discr_ty); - // Figure out which discriminant and variant this corresponds to. Ok(match *tag_kind { DiscriminantKind::Tag => { @@ -651,11 +638,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .force_bits(tag_val, tag_layout.size) .map_err(|_| err_ub!(InvalidDiscriminant(tag_val.erase_tag())))?; // Cast bits from tag layout to discriminant layout. - let discr_layout = self.layout_of(discr_ty)?; - let discr_val_cast = self.cast_from_scalar(tag_bits, tag_layout, discr_ty); + let discr_val_cast = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); let discr_bits = discr_val_cast.assert_bits(discr_layout.size); // Convert discriminant to variant index, and catch invalid discriminants. - let index = match rval.layout.ty.kind { + let index = match op.layout.ty.kind { ty::Adt(adt, _) => { adt.discriminants(self.tcx.tcx).find(|(_, var)| var.val == discr_bits) } @@ -705,7 +691,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variant_index = variants_start .checked_add(variant_index_relative) .expect("overflow computing absolute variant idx"); - let variants_len = rval + let variants_len = op .layout .ty .ty_adt_def() @@ -722,8 +708,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Compute the size of the scalar we need to return. // No need to cast, because the variant index directly serves as discriminant and is // encoded in the tag. - let size = self.layout_of(discr_ty)?.size; - (Scalar::from_uint(variant.as_u32(), size), variant) + (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant) } }) } From ad7179d2a409faaf45465862f44efe7f989cd71e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 May 2020 17:24:33 +0200 Subject: [PATCH 08/28] fix discriminant_ty for non-enums --- src/librustc_middle/mir/tcx.rs | 4 +--- src/librustc_middle/ty/mod.rs | 5 +++++ src/librustc_middle/ty/sty.rs | 13 +++++++++---- src/librustc_mir/interpret/operand.rs | 4 ++-- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/librustc_middle/mir/tcx.rs b/src/librustc_middle/mir/tcx.rs index 174da7db1753d..3e172bd8d2a76 100644 --- a/src/librustc_middle/mir/tcx.rs +++ b/src/librustc_middle/mir/tcx.rs @@ -173,9 +173,7 @@ impl<'tcx> Rvalue<'tcx> { tcx.intern_tup(&[ty, tcx.types.bool]) } Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), - Rvalue::Discriminant(ref place) => { - place.ty(local_decls, tcx).ty.discriminant_type(tcx) - } + Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx), Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t), Rvalue::NullaryOp(NullOp::SizeOf, _) => tcx.types.usize, Rvalue::Aggregate(ref ak, ref ops) => match **ak { diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs index aad3c6889c3ce..4d050a7b48eac 100644 --- a/src/librustc_middle/ty/mod.rs +++ b/src/librustc_middle/ty/mod.rs @@ -2040,6 +2040,8 @@ impl ReprOptions { self.flags.contains(ReprFlags::HIDE_NICHE) } + /// Returns the discriminant type, given these `repr` options. + /// This must only be called on enums! pub fn discr_type(&self) -> attr::IntType { self.int.unwrap_or(attr::SignedInt(ast::IntTy::Isize)) } @@ -2272,6 +2274,7 @@ impl<'tcx> AdtDef { #[inline] pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { + assert!(self.is_enum()); let param_env = tcx.param_env(expr_did); let repr_type = self.repr.discr_type(); match tcx.const_eval_poly(expr_did) { @@ -2308,6 +2311,7 @@ impl<'tcx> AdtDef { &'tcx self, tcx: TyCtxt<'tcx>, ) -> impl Iterator)> + Captures<'tcx> { + assert!(self.is_enum()); let repr_type = self.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx); let mut prev_discr = None::>; @@ -2340,6 +2344,7 @@ impl<'tcx> AdtDef { tcx: TyCtxt<'tcx>, variant_index: VariantIdx, ) -> Discr<'tcx> { + assert!(self.is_enum()); let (val, offset) = self.discriminant_def_for_variant(variant_index); let explicit_value = val .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index 6cee224219b63..aacca710dd037 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -2097,7 +2097,9 @@ impl<'tcx> TyS<'tcx> { variant_index: VariantIdx, ) -> Option> { match self.kind { - TyKind::Adt(adt, _) => Some(adt.discriminant_for_variant(tcx, variant_index)), + TyKind::Adt(adt, _) if adt.is_enum() => { + Some(adt.discriminant_for_variant(tcx, variant_index)) + } TyKind::Generator(def_id, substs, _) => { Some(substs.as_generator().discriminant_for_variant(def_id, tcx, variant_index)) } @@ -2106,11 +2108,14 @@ impl<'tcx> TyS<'tcx> { } /// Returns the type of the discriminant of this type. - pub fn discriminant_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + pub fn discriminant_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match self.kind { - ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx), + ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), - _ => bug!("{:?} does not have a discriminant", self), + _ => { + // This can only be `0`, for now, so `u8` will suffice. + tcx.types.u8 + } } } diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 139871310fbf3..f546f6236d777 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -581,10 +581,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("read_discriminant_value {:#?}", op.layout); // Get type and layout of the discriminant. - let discr_layout = self.layout_of(op.layout.ty.discriminant_type(*self.tcx))?; + let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; trace!("discriminant type: {:?}", discr_layout.ty); - // We use "discriminant" to refer to the value associated with a particualr enum variant. + // We use "discriminant" to refer to the value associated with a particular enum variant. // This is not to be confused with its "variant index", which is just determining its position in the // declared list of variants -- they can differ with explicitly assigned discriminants. // We use "tag" to refer to how the discriminant is encoded in memory, which can be either From 1c30c9e92bdae1814dbae9367f214b4819cdd0de Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 26 May 2020 14:26:11 -0400 Subject: [PATCH 09/28] Don't bail out of trait selection when predicate references an error Fixes #72590 With PR #70551, observing a `ty::Error` guarantees that compilation is going to fail. Therefore, there are no soundness impliciations to continuing on when we encounter a `ty::Error` - we can only affect whether or not additional error messags are emitted. By not bailing out, we avoid incorrectly determining that types are `!Sized` when a type error is present, which allows us to avoid emitting additional spurious error messages. The original comment mentioned this code being shared by coherence - howver, this change resulted in no diagnostic changes in any of the existing tests. --- src/librustc_trait_selection/traits/select.rs | 11 -------- .../issue-72590-type-error-sized.rs | 22 +++++++++++++++ .../issue-72590-type-error-sized.stderr | 28 +++++++++++++++++++ 3 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 src/test/ui/async-await/issue-72590-type-error-sized.rs create mode 100644 src/test/ui/async-await/issue-72590-type-error-sized.stderr diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index 9b3381066a17b..32bd895f85162 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -1040,17 +1040,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, stack: &TraitObligationStack<'o, 'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if stack.obligation.predicate.references_error() { - // If we encounter a `Error`, we generally prefer the - // most "optimistic" result in response -- that is, the - // one least likely to report downstream errors. But - // because this routine is shared by coherence and by - // trait selection, there isn't an obvious "right" choice - // here in that respect, so we opt to just return - // ambiguity and let the upstream clients sort it out. - return Ok(None); - } - if let Some(conflict) = self.is_knowable(stack) { debug!("coherence stage: not knowable"); if self.intercrate_ambiguity_causes.is_some() { diff --git a/src/test/ui/async-await/issue-72590-type-error-sized.rs b/src/test/ui/async-await/issue-72590-type-error-sized.rs new file mode 100644 index 0000000000000..00e098d43e073 --- /dev/null +++ b/src/test/ui/async-await/issue-72590-type-error-sized.rs @@ -0,0 +1,22 @@ +// Regression test for issue #72590 +// Tests that we don't emit a spurious "size cannot be statically determined" error +// edition:2018 + +struct Foo { + foo: Nonexistent, //~ ERROR cannot find + other: str +} + +struct Bar { + test: Missing //~ ERROR cannot find +} + +impl Foo { + async fn frob(self) {} //~ ERROR the size +} + +impl Bar { + async fn myfn(self) {} +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-72590-type-error-sized.stderr b/src/test/ui/async-await/issue-72590-type-error-sized.stderr new file mode 100644 index 0000000000000..603895b598c16 --- /dev/null +++ b/src/test/ui/async-await/issue-72590-type-error-sized.stderr @@ -0,0 +1,28 @@ +error[E0412]: cannot find type `Nonexistent` in this scope + --> $DIR/issue-72590-type-error-sized.rs:6:10 + | +LL | foo: Nonexistent, + | ^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `Missing` in this scope + --> $DIR/issue-72590-type-error-sized.rs:11:11 + | +LL | test: Missing + | ^^^^^^^ not found in this scope + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/issue-72590-type-error-sized.rs:15:19 + | +LL | async fn frob(self) {} + | ^^^^ doesn't have a size known at compile-time + | + = help: within `Foo`, the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + = note: required because it appears within the type `Foo` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0412. +For more information about an error, try `rustc --explain E0277`. From ce81d15289867f7ec0bba959712d100d3ff6a653 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 28 May 2020 14:40:07 -0700 Subject: [PATCH 10/28] Add test to make sure -Wunused-crate-dependencies works with tests Make sure code in `#[test]` blocks counts as a use of a crate. --- src/test/ui/unused-crate-deps/test-use-ok.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/test/ui/unused-crate-deps/test-use-ok.rs diff --git a/src/test/ui/unused-crate-deps/test-use-ok.rs b/src/test/ui/unused-crate-deps/test-use-ok.rs new file mode 100644 index 0000000000000..66d6440c9cb9f --- /dev/null +++ b/src/test/ui/unused-crate-deps/test-use-ok.rs @@ -0,0 +1,15 @@ +// Test-only use OK + +// edition:2018 +// check-pass +// aux-crate:bar=bar.rs +// compile-flags:--test + +#![deny(unused_crate_dependencies)] + +fn main() {} + +#[test] +fn test_bar() { + assert_eq!(bar::BAR, "bar"); +} From 02cc593af04a4f8c338baa1f51c9a4c9c005d729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 29 May 2020 00:00:00 +0000 Subject: [PATCH 11/28] Remove unused mut from long-linker-command-lines test --- src/test/run-make-fulldeps/long-linker-command-lines/foo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs b/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs index 96fb16b1fcc8f..f313798de215b 100644 --- a/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs +++ b/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs @@ -90,7 +90,7 @@ fn main() { } let linker_args = read_linker_args(&ok); - for mut arg in linker_args.split('S') { + for arg in linker_args.split('S') { expected_libs.remove(arg); } From ed503acb460256c878609fa2624b6d37e79d6ee4 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 29 May 2020 00:19:06 -0400 Subject: [PATCH 12/28] Revert "Fix rebase fallout" This reverts commit 5685e4dd90ad2f4978c63b9097f0b568e4ce6e5c. --- Cargo.lock | 1 - src/librustc_ast/tokenstream.rs | 2 ++ src/librustc_parse/Cargo.toml | 1 - src/librustc_parse/lib.rs | 4 +--- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1cc254b9b9805..9516188cf366b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4143,7 +4143,6 @@ dependencies = [ "rustc_lexer", "rustc_session", "rustc_span", - "smallvec 1.4.0", "unicode-normalization", ] diff --git a/src/librustc_ast/tokenstream.rs b/src/librustc_ast/tokenstream.rs index 9d0199078fa6a..ff3469930c6d3 100644 --- a/src/librustc_ast/tokenstream.rs +++ b/src/librustc_ast/tokenstream.rs @@ -21,6 +21,8 @@ use rustc_macros::HashStable_Generic; use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; +use log::debug; + use std::{iter, mem}; /// When the main rust parser encounters a syntax-extension invocation, it diff --git a/src/librustc_parse/Cargo.toml b/src/librustc_parse/Cargo.toml index 0d31a8c7bc1fb..7164c67880863 100644 --- a/src/librustc_parse/Cargo.toml +++ b/src/librustc_parse/Cargo.toml @@ -12,7 +12,6 @@ doctest = false [dependencies] bitflags = "1.0" log = "0.4" -smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_feature = { path = "../librustc_feature" } diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 8ca3f6c5768af..b6e4e104fb5b9 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -14,9 +14,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; use rustc_session::parse::ParseSess; use rustc_span::symbol::kw; -use rustc_span::{FileName, SourceFile, Span, DUMMY_SP}; - -use smallvec::SmallVec; +use rustc_span::{FileName, SourceFile, Span}; use std::mem; use std::path::Path; From 4d4facbe4f0290c511c21167e48074786ad75efd Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 29 May 2020 00:19:08 -0400 Subject: [PATCH 13/28] Revert "Add test for macro_rules! invoking a proc-macro with capture groups" This reverts commit 30c00fd26a24f349df64a7c0f5c3490e9f624322. --- src/test/ui/proc-macro/macro-rules-capture.rs | 18 ------------------ .../ui/proc-macro/macro-rules-capture.stderr | 12 ------------ 2 files changed, 30 deletions(-) delete mode 100644 src/test/ui/proc-macro/macro-rules-capture.rs delete mode 100644 src/test/ui/proc-macro/macro-rules-capture.stderr diff --git a/src/test/ui/proc-macro/macro-rules-capture.rs b/src/test/ui/proc-macro/macro-rules-capture.rs deleted file mode 100644 index 37436567d70f0..0000000000000 --- a/src/test/ui/proc-macro/macro-rules-capture.rs +++ /dev/null @@ -1,18 +0,0 @@ -// aux-build: test-macros.rs - -extern crate test_macros; -use test_macros::recollect_attr; - -macro_rules! reemit { - ($name:ident => $($token:expr)*) => { - - #[recollect_attr] - pub fn $name() { - $($token)*; - } - } -} - -reemit! { foo => 45u32.into() } //~ ERROR type annotations - -fn main() {} diff --git a/src/test/ui/proc-macro/macro-rules-capture.stderr b/src/test/ui/proc-macro/macro-rules-capture.stderr deleted file mode 100644 index 6d512846ff785..0000000000000 --- a/src/test/ui/proc-macro/macro-rules-capture.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/macro-rules-capture.rs:16:24 - | -LL | reemit! { foo => 45u32.into() } - | ------^^^^-- - | | | - | | cannot infer type for type parameter `T` declared on the trait `Into` - | this method call resolves to `T` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0282`. From 1ae7de9a52d295a6c3d21af1915fc95951193b83 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 29 May 2020 00:19:10 -0400 Subject: [PATCH 14/28] Revert "Recursively expand nonterminals" This reverts commit 2af0218bf1ffca0750a352554f20a07b760a30a8. --- src/librustc_ast/tokenstream.rs | 2 + src/librustc_parse/lib.rs | 109 ++++---------------------------- 2 files changed, 16 insertions(+), 95 deletions(-) diff --git a/src/librustc_ast/tokenstream.rs b/src/librustc_ast/tokenstream.rs index ff3469930c6d3..7348c41dbe778 100644 --- a/src/librustc_ast/tokenstream.rs +++ b/src/librustc_ast/tokenstream.rs @@ -290,6 +290,8 @@ impl TokenStream { t1.next().is_none() && t2.next().is_none() } + + pub fn map_enumerated TokenTree>(self, mut f: F) -> TokenStream { TokenStream(Lrc::new( self.0 diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index b6e4e104fb5b9..8aea8388d08b9 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -7,20 +7,20 @@ #![feature(or_patterns)] use rustc_ast::ast; -use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, IsJoint, TokenStream, TokenTree}; +use rustc_ast::token::{self, Nonterminal, Token, TokenKind, DelimToken}; +use rustc_ast::tokenstream::{self, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::kw; use rustc_span::{FileName, SourceFile, Span}; +use rustc_span::symbol::kw; -use std::mem; use std::path::Path; use std::str; +use std::mem; -use log::{debug, info}; +use log::info; pub const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments"); @@ -308,7 +308,7 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke // modifications, including adding/removing typically non-semantic // tokens such as extra braces and commas, don't happen. if let Some(tokens) = tokens { - if tokenstream_probably_equal_for_proc_macro(&tokens, &tokens_for_real, sess) { + if tokenstream_probably_equal_for_proc_macro(&tokens, &tokens_for_real) { return tokens; } info!( @@ -389,11 +389,7 @@ fn prepend_attrs( // // This is otherwise the same as `eq_unspanned`, only recursing with a // different method. -pub fn tokenstream_probably_equal_for_proc_macro( - first: &TokenStream, - other: &TokenStream, - sess: &ParseSess, -) -> bool { +pub fn tokenstream_probably_equal_for_proc_macro(first: &TokenStream, other: &TokenStream) -> bool { // When checking for `probably_eq`, we ignore certain tokens that aren't // preserved in the AST. Because they are not preserved, the pretty // printer arbitrarily adds or removes them when printing as token @@ -421,83 +417,10 @@ pub fn tokenstream_probably_equal_for_proc_macro( true } - // When comparing two `TokenStream`s, we ignore the `IsJoint` information. - // - // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will - // use `Token.glue` on adjacent tokens with the proper `IsJoint`. - // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`) - // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent - // when determining if two `TokenStream`s are 'probably equal'. - // - // Therefore, we use `break_two_token_op` to convert all tokens - // to the 'unglued' form (if it exists). This ensures that two - // `TokenStream`s which differ only in how their tokens are glued - // will be considered 'probably equal', which allows us to keep spans. - // - // This is important when the original `TokenStream` contained - // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces - // will be omitted when we pretty-print, which can cause the original - // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`, - // leading to some tokens being 'glued' together in one stream but not - // the other. See #68489 for more details. - fn break_tokens(tree: TokenTree) -> impl Iterator { - // In almost all cases, we should have either zero or one levels - // of 'unglueing'. However, in some unusual cases, we may need - // to iterate breaking tokens mutliple times. For example: - // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]' - let mut token_trees: SmallVec<[_; 2]>; - if let TokenTree::Token(token) = &tree { - let mut out = SmallVec::<[_; 2]>::new(); - out.push(token.clone()); - // Iterate to fixpoint: - // * We start off with 'out' containing our initial token, and `temp` empty - // * If we are able to break any tokens in `out`, then `out` will have - // at least one more element than 'temp', so we will try to break tokens - // again. - // * If we cannot break any tokens in 'out', we are done - loop { - let mut temp = SmallVec::<[_; 2]>::new(); - let mut changed = false; - - for token in out.into_iter() { - if let Some((first, second)) = token.kind.break_two_token_op() { - temp.push(Token::new(first, DUMMY_SP)); - temp.push(Token::new(second, DUMMY_SP)); - changed = true; - } else { - temp.push(token); - } - } - out = temp; - if !changed { - break; - } - } - token_trees = out.into_iter().map(|t| TokenTree::Token(t)).collect(); - if token_trees.len() != 1 { - debug!("break_tokens: broke {:?} to {:?}", tree, token_trees); - } - } else { - token_trees = SmallVec::new(); - token_trees.push(tree); - } - token_trees.into_iter() - } - - let expand_nt = |tree: TokenTree| { - if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree { - nt_to_tokenstream(nt, sess, *span).into_trees() - } else { - TokenStream::new(vec![(tree, IsJoint::NonJoint)]).into_trees() - } - }; - - // Break tokens after we expand any nonterminals, so that we break tokens - // that are produced as a result of nonterminal expansion. - let mut t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); - let mut t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); + let mut t1 = first.trees().filter(semantic_tree); + let mut t2 = other.trees().filter(semantic_tree); for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { - if !tokentree_probably_equal_for_proc_macro(&t1, &t2, sess) { + if !tokentree_probably_equal_for_proc_macro(&t1, &t2) { return false; } } @@ -556,29 +479,25 @@ crate fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bo b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate) } - // Expanded by `tokenstream_probably_equal_for_proc_macro` - (&Interpolated(_), &Interpolated(_)) => unreachable!(), + (&Interpolated(_), &Interpolated(_)) => false, _ => panic!("forgot to add a token?"), } } + // 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 // different method. -pub fn tokentree_probably_equal_for_proc_macro( - first: &TokenTree, - other: &TokenTree, - sess: &ParseSess, -) -> bool { +pub fn tokentree_probably_equal_for_proc_macro(first: &TokenTree, other: &TokenTree) -> bool { match (first, other) { (TokenTree::Token(token), TokenTree::Token(token2)) => { token_probably_equal_for_proc_macro(token, token2) } (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { - delim == delim2 && tokenstream_probably_equal_for_proc_macro(&tts, &tts2, sess) + delim == delim2 && tokenstream_probably_equal_for_proc_macro(&tts, &tts2) } _ => false, } From 1c2b65b1e107abc20dede528d80cb47000f144e0 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 29 May 2020 00:19:11 -0400 Subject: [PATCH 15/28] Revert "Move functions to librustc_parse" This reverts commit 7a4c1865fb2f58c57e3f09645515dec8be3022c6. --- src/librustc_ast/token.rs | 56 ++++++++++++++ src/librustc_ast/tokenstream.rs | 121 +++++++++++++++++++++++++++++++ src/librustc_parse/lib.rs | 125 +------------------------------- 3 files changed, 179 insertions(+), 123 deletions(-) diff --git a/src/librustc_ast/token.rs b/src/librustc_ast/token.rs index 2e2bc380e844f..a5b9c2a95bbea 100644 --- a/src/librustc_ast/token.rs +++ b/src/librustc_ast/token.rs @@ -673,6 +673,62 @@ impl Token { Some(Token::new(kind, self.span.to(joint.span))) } + + // 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.kind) != mem::discriminant(&other.kind) { + return false; + } + match (&self.kind, &other.kind) { + (&Eq, &Eq) + | (&Lt, &Lt) + | (&Le, &Le) + | (&EqEq, &EqEq) + | (&Ne, &Ne) + | (&Ge, &Ge) + | (&Gt, &Gt) + | (&AndAnd, &AndAnd) + | (&OrOr, &OrOr) + | (&Not, &Not) + | (&Tilde, &Tilde) + | (&At, &At) + | (&Dot, &Dot) + | (&DotDot, &DotDot) + | (&DotDotDot, &DotDotDot) + | (&DotDotEq, &DotDotEq) + | (&Comma, &Comma) + | (&Semi, &Semi) + | (&Colon, &Colon) + | (&ModSep, &ModSep) + | (&RArrow, &RArrow) + | (&LArrow, &LArrow) + | (&FatArrow, &FatArrow) + | (&Pound, &Pound) + | (&Dollar, &Dollar) + | (&Question, &Question) + | (&Whitespace, &Whitespace) + | (&Comment, &Comment) + | (&Eof, &Eof) => true, + + (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b, + + (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b, + + (&DocComment(a), &DocComment(b)) | (&Shebang(a), &Shebang(b)) => a == b, + + (&Literal(a), &Literal(b)) => a == b, + + (&Lifetime(a), &Lifetime(b)) => a == b, + (&Ident(a, b), &Ident(c, d)) => { + b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate) + } + + (&Interpolated(_), &Interpolated(_)) => false, + + _ => panic!("forgot to add a token?"), + } + } } impl PartialEq for Token { diff --git a/src/librustc_ast/tokenstream.rs b/src/librustc_ast/tokenstream.rs index 7348c41dbe778..075aaa7e5bc01 100644 --- a/src/librustc_ast/tokenstream.rs +++ b/src/librustc_ast/tokenstream.rs @@ -68,6 +68,23 @@ impl TokenTree { } } + // 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 + // different method. + pub fn probably_equal_for_proc_macro(&self, other: &TokenTree) -> bool { + match (self, other) { + (TokenTree::Token(token), TokenTree::Token(token2)) => { + token.probably_equal_for_proc_macro(token2) + } + (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { + delim == delim2 && tts.probably_equal_for_proc_macro(&tts2) + } + _ => false, + } + } + /// Retrieves the TokenTree's span. pub fn span(&self) -> Span { match self { @@ -290,7 +307,111 @@ impl TokenStream { t1.next().is_none() && t2.next().is_none() } + // 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 + // different method. + pub fn probably_equal_for_proc_macro(&self, other: &TokenStream) -> bool { + // When checking for `probably_eq`, we ignore certain tokens that aren't + // preserved in the AST. Because they are not preserved, the pretty + // printer arbitrarily adds or removes them when printing as token + // streams, making a comparison between a token stream generated from an + // AST and a token stream which was parsed into an AST more reliable. + fn semantic_tree(tree: &TokenTree) -> bool { + if let TokenTree::Token(token) = tree { + if let + // The pretty printer tends to add trailing commas to + // everything, and in particular, after struct fields. + | token::Comma + // The pretty printer emits `NoDelim` as whitespace. + | token::OpenDelim(DelimToken::NoDelim) + | token::CloseDelim(DelimToken::NoDelim) + // The pretty printer collapses many semicolons into one. + | token::Semi + // The pretty printer collapses whitespace arbitrarily and can + // introduce whitespace from `NoDelim`. + | token::Whitespace + // The pretty printer can turn `$crate` into `::crate_name` + | token::ModSep = token.kind { + return false; + } + } + true + } + // When comparing two `TokenStream`s, we ignore the `IsJoint` information. + // + // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will + // use `Token.glue` on adjacent tokens with the proper `IsJoint`. + // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`) + // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent + // when determining if two `TokenStream`s are 'probably equal'. + // + // Therefore, we use `break_two_token_op` to convert all tokens + // to the 'unglued' form (if it exists). This ensures that two + // `TokenStream`s which differ only in how their tokens are glued + // will be considered 'probably equal', which allows us to keep spans. + // + // This is important when the original `TokenStream` contained + // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces + // will be omitted when we pretty-print, which can cause the original + // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`, + // leading to some tokens being 'glued' together in one stream but not + // the other. See #68489 for more details. + fn break_tokens(tree: TokenTree) -> impl Iterator { + // In almost all cases, we should have either zero or one levels + // of 'unglueing'. However, in some unusual cases, we may need + // to iterate breaking tokens mutliple times. For example: + // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]' + let mut token_trees: SmallVec<[_; 2]>; + if let TokenTree::Token(token) = &tree { + let mut out = SmallVec::<[_; 2]>::new(); + out.push(token.clone()); + // Iterate to fixpoint: + // * We start off with 'out' containing our initial token, and `temp` empty + // * If we are able to break any tokens in `out`, then `out` will have + // at least one more element than 'temp', so we will try to break tokens + // again. + // * If we cannot break any tokens in 'out', we are done + loop { + let mut temp = SmallVec::<[_; 2]>::new(); + let mut changed = false; + + for token in out.into_iter() { + if let Some((first, second)) = token.kind.break_two_token_op() { + temp.push(Token::new(first, DUMMY_SP)); + temp.push(Token::new(second, DUMMY_SP)); + changed = true; + } else { + temp.push(token); + } + } + out = temp; + if !changed { + break; + } + } + token_trees = out.into_iter().map(|t| TokenTree::Token(t)).collect(); + if token_trees.len() != 1 { + debug!("break_tokens: broke {:?} to {:?}", tree, token_trees); + } + } else { + token_trees = SmallVec::new(); + token_trees.push(tree); + } + token_trees.into_iter() + } + + let mut t1 = self.trees().filter(semantic_tree).flat_map(break_tokens); + let mut t2 = other.trees().filter(semantic_tree).flat_map(break_tokens); + for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { + if !t1.probably_equal_for_proc_macro(&t2) { + return false; + } + } + t1.next().is_none() && t2.next().is_none() + } pub fn map_enumerated TokenTree>(self, mut f: F) -> TokenStream { TokenStream(Lrc::new( diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 8aea8388d08b9..182de85857e5e 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -7,18 +7,16 @@ #![feature(or_patterns)] use rustc_ast::ast; -use rustc_ast::token::{self, Nonterminal, Token, TokenKind, DelimToken}; +use rustc_ast::token::{self, Nonterminal}; use rustc_ast::tokenstream::{self, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; use rustc_session::parse::ParseSess; use rustc_span::{FileName, SourceFile, Span}; -use rustc_span::symbol::kw; use std::path::Path; use std::str; -use std::mem; use log::info; @@ -308,7 +306,7 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke // modifications, including adding/removing typically non-semantic // tokens such as extra braces and commas, don't happen. if let Some(tokens) = tokens { - if tokenstream_probably_equal_for_proc_macro(&tokens, &tokens_for_real) { + if tokens.probably_equal_for_proc_macro(&tokens_for_real) { return tokens; } info!( @@ -383,122 +381,3 @@ fn prepend_attrs( builder.push(tokens.clone()); Some(builder.build()) } - -// 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 -// different method. -pub fn tokenstream_probably_equal_for_proc_macro(first: &TokenStream, other: &TokenStream) -> bool { - // When checking for `probably_eq`, we ignore certain tokens that aren't - // preserved in the AST. Because they are not preserved, the pretty - // printer arbitrarily adds or removes them when printing as token - // streams, making a comparison between a token stream generated from an - // AST and a token stream which was parsed into an AST more reliable. - fn semantic_tree(tree: &TokenTree) -> bool { - if let TokenTree::Token(token) = tree { - if let - // The pretty printer tends to add trailing commas to - // everything, and in particular, after struct fields. - | token::Comma - // The pretty printer emits `NoDelim` as whitespace. - | token::OpenDelim(DelimToken::NoDelim) - | token::CloseDelim(DelimToken::NoDelim) - // The pretty printer collapses many semicolons into one. - | token::Semi - // The pretty printer collapses whitespace arbitrarily and can - // introduce whitespace from `NoDelim`. - | token::Whitespace - // The pretty printer can turn `$crate` into `::crate_name` - | token::ModSep = token.kind { - return false; - } - } - true - } - - let mut t1 = first.trees().filter(semantic_tree); - let mut t2 = other.trees().filter(semantic_tree); - for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { - if !tokentree_probably_equal_for_proc_macro(&t1, &t2) { - return false; - } - } - t1.next().is_none() && t2.next().is_none() -} - -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -crate fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool { - use TokenKind::*; - - if mem::discriminant(&first.kind) != mem::discriminant(&other.kind) { - return false; - } - match (&first.kind, &other.kind) { - (&Eq, &Eq) - | (&Lt, &Lt) - | (&Le, &Le) - | (&EqEq, &EqEq) - | (&Ne, &Ne) - | (&Ge, &Ge) - | (&Gt, &Gt) - | (&AndAnd, &AndAnd) - | (&OrOr, &OrOr) - | (&Not, &Not) - | (&Tilde, &Tilde) - | (&At, &At) - | (&Dot, &Dot) - | (&DotDot, &DotDot) - | (&DotDotDot, &DotDotDot) - | (&DotDotEq, &DotDotEq) - | (&Comma, &Comma) - | (&Semi, &Semi) - | (&Colon, &Colon) - | (&ModSep, &ModSep) - | (&RArrow, &RArrow) - | (&LArrow, &LArrow) - | (&FatArrow, &FatArrow) - | (&Pound, &Pound) - | (&Dollar, &Dollar) - | (&Question, &Question) - | (&Whitespace, &Whitespace) - | (&Comment, &Comment) - | (&Eof, &Eof) => true, - - (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b, - - (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b, - - (&DocComment(a), &DocComment(b)) | (&Shebang(a), &Shebang(b)) => a == b, - - (&Literal(a), &Literal(b)) => a == b, - - (&Lifetime(a), &Lifetime(b)) => a == b, - (&Ident(a, b), &Ident(c, d)) => { - b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate) - } - - (&Interpolated(_), &Interpolated(_)) => false, - - _ => panic!("forgot to add a token?"), - } -} - - -// 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 -// different method. -pub fn tokentree_probably_equal_for_proc_macro(first: &TokenTree, other: &TokenTree) -> bool { - match (first, other) { - (TokenTree::Token(token), TokenTree::Token(token2)) => { - token_probably_equal_for_proc_macro(token, token2) - } - (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { - delim == delim2 && tokenstream_probably_equal_for_proc_macro(&tts, &tts2) - } - _ => false, - } -} From b802eebc67e92e100eaebe40c1eb4068c3b2fa2b Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 29 May 2020 00:35:02 -0400 Subject: [PATCH 16/28] Fix missing import lost in revert --- src/librustc_parse/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 182de85857e5e..be86b4b7c7720 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -18,7 +18,7 @@ use rustc_span::{FileName, SourceFile, Span}; use std::path::Path; use std::str; -use log::info; +use log::{debug, info}; pub const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments"); From 283358b081fa2b7e60f3d0d11da1a8e667b13769 Mon Sep 17 00:00:00 2001 From: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com> Date: Fri, 29 May 2020 11:36:50 +0200 Subject: [PATCH 17/28] Update RELEASES.md --- RELEASES.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 132e8a6d94850..510651891b272 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -17,10 +17,13 @@ Compiler -------- - [Rustc now respects the `-C codegen-units` flag in incremental mode.][70156] Additionally when in incremental mode rustc defaults to 256 codegen units. -- [Added tier 3\* support for the `aarch64-unknown-none` and - `aarch64-unknown-none-softfloat` targets.][68334] - [Refactored `catch_unwind`, to have zero-cost unless unwinding is enabled and a panic is thrown.][67502] +- [Added tier 3\* support for the `aarch64-unknown-none` and + `aarch64-unknown-none-softfloat` targets.][68334] +- [Added tier 3 support for `arm64-apple-tvos` and + `x86_64-apple-tvos` targets.][68191] + Libraries --------- @@ -33,6 +36,9 @@ Libraries - [`String` now implements `From<&mut str>`.][69661] - [`IoSlice` now implements `Copy`.][69403] - [`Vec` now implements `From<[T; N]>`.][68692] Where `N` is less than 32. +- [`proc_macro::LexError` now implements `fmt::Display` and `Error`.][68899] +- [`from_le_bytes`, `to_le_bytes`, `from_be_bytes`, and `to_be_bytes` methods are + now `const` for all integer types.][69373] Stabilized APIs --------------- @@ -84,6 +90,9 @@ Compatibility Notes source file rather than the previous format of ``.][70969] **Note:** this may not point a file that actually exists on the user's system. - [The minimum required external LLVM version has been bumped to LLVM 8.][71147] +- [`mem::{zeroed, uninitialised, MaybeUninit}` will now panic when used with types + that do not allow zero initialization such as `NonZeroU8`.][66059] This was + previously a warning. Internal Only ------------- @@ -94,7 +103,10 @@ related tools. - [dep_graph Avoid allocating a set on when the number reads are small.][69778] - [Replace big JS dict with JSON parsing.][71250] - +[69373]: https://github.com/rust-lang/rust/pull/69373/ +[66059]: https://github.com/rust-lang/rust/pull/66059/ +[68191]: https://github.com/rust-lang/rust/pull/68191/ +[68899]: https://github.com/rust-lang/rust/pull/68899/ [71147]: https://github.com/rust-lang/rust/pull/71147/ [71250]: https://github.com/rust-lang/rust/pull/71250/ [70937]: https://github.com/rust-lang/rust/pull/70937/ From 5b37ee1c2ace7b2605848d05b7cc40030ac44167 Mon Sep 17 00:00:00 2001 From: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com> Date: Fri, 29 May 2020 11:45:25 +0200 Subject: [PATCH 18/28] Update RELEASES.md --- RELEASES.md | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 510651891b272..3277f073b1df2 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,6 +9,14 @@ Language **Syntax-only changes** - [Expansion-driven outline module parsing][69838] +```rust +#[cfg(FALSE)] +mod foo { + mod bar { + mod baz; // `foo/bar/baz.rs` doesn't exist, but no error! + } +} +``` These are still rejected semantically, so you will likely receive an error but these changes can be seen and parsed by macros and conditional compilation. @@ -53,6 +61,7 @@ Stabilized APIs - [`Layout::align_to`] - [`Layout::pad_to_align`] - [`Layout::array`] +- [`Layout::extend`] Cargo ----- @@ -128,17 +137,18 @@ related tools. [68334]: https://github.com/rust-lang/rust/pull/68334/ [67502]: https://github.com/rust-lang/rust/pull/67502/ [cargo/8062]: https://github.com/rust-lang/cargo/pull/8062/ -[`PathBuf::with_capacity`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.with_capacity -[`PathBuf::capacity`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.capacity -[`PathBuf::clear`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.clear -[`PathBuf::reserve`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.reserve -[`PathBuf::reserve_exact`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.reserve_exact -[`PathBuf::shrink_to_fit`]: https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html#method.shrink_to_fit -[`f32::to_int_unchecked`]: https://doc.rust-lang.org/beta/std/primitive.f32.html#method.to_int_unchecked -[`f64::to_int_unchecked`]: https://doc.rust-lang.org/beta/std/primitive.f64.html#method.to_int_unchecked -[`Layout::align_to`]: https://doc.rust-lang.org/beta/std/alloc/struct.Layout.html#method.align_to -[`Layout::pad_to_align`]: https://doc.rust-lang.org/beta/std/alloc/struct.Layout.html#method.pad_to_align -[`Layout::array`]: https://doc.rust-lang.org/beta/std/alloc/struct.Layout.html#method.array +[`PathBuf::with_capacity`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.with_capacity +[`PathBuf::capacity`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.capacity +[`PathBuf::clear`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.clear +[`PathBuf::reserve`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.reserve +[`PathBuf::reserve_exact`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.reserve_exact +[`PathBuf::shrink_to_fit`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.shrink_to_fit +[`f32::to_int_unchecked`]: https://doc.rust-lang.org/std/primitive.f32.html#method.to_int_unchecked +[`f64::to_int_unchecked`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_int_unchecked +[`Layout::align_to`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.align_to +[`Layout::pad_to_align`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.pad_to_align +[`Layout::array`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.array +[`Layout::extend`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.extend Version 1.43.1 (2020-05-07) From 859863dad25cd5cd1ea15422c7b6bd280c6c6327 Mon Sep 17 00:00:00 2001 From: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com> Date: Fri, 29 May 2020 12:49:13 +0200 Subject: [PATCH 19/28] Update RELEASES.md --- RELEASES.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 3277f073b1df2..7ee61c34b7085 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -45,8 +45,9 @@ Libraries - [`IoSlice` now implements `Copy`.][69403] - [`Vec` now implements `From<[T; N]>`.][68692] Where `N` is less than 32. - [`proc_macro::LexError` now implements `fmt::Display` and `Error`.][68899] -- [`from_le_bytes`, `to_le_bytes`, `from_be_bytes`, and `to_be_bytes` methods are - now `const` for all integer types.][69373] +- [`from_le_bytes`, `to_le_bytes`, `from_be_bytes`, `to_be_bytes`, + `from_ne_bytes`, and `to_ne_bytes` methods are now `const` for all + integer types.][69373] Stabilized APIs --------------- From 692f4ec3b103a36830132bc941c2aa2abea4b7e7 Mon Sep 17 00:00:00 2001 From: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com> Date: Fri, 29 May 2020 18:25:34 +0200 Subject: [PATCH 20/28] Update RELEASES.md --- RELEASES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 7ee61c34b7085..7cba27e134a78 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -103,6 +103,11 @@ Compatibility Notes - [`mem::{zeroed, uninitialised, MaybeUninit}` will now panic when used with types that do not allow zero initialization such as `NonZeroU8`.][66059] This was previously a warning. +- [In 1.45.0 (the next release) converting a `f64` to `u32` using the `as` + operator has been defined as a saturating operation.][71269] This was previously + undefined behaviour, you can use the `{f64, f32}::to_int_unchecked` methods to + continue using the current behaviour which may desirable in rare performance + sensitive situations. Internal Only ------------- @@ -127,6 +132,7 @@ related tools. [70048]: https://github.com/rust-lang/rust/pull/70048/ [70081]: https://github.com/rust-lang/rust/pull/70081/ [70156]: https://github.com/rust-lang/rust/pull/70156/ +[71269]: https://github.com/rust-lang/rust/pull/71269/ [69838]: https://github.com/rust-lang/rust/pull/69838/ [69929]: https://github.com/rust-lang/rust/pull/69929/ [69661]: https://github.com/rust-lang/rust/pull/69661/ From 25bafc24db9559db2fb3688d824a2a41ada150e5 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Fri, 29 May 2020 20:41:26 +0200 Subject: [PATCH 21/28] remove mk_bool --- src/librustc_middle/ty/context.rs | 5 ----- src/librustc_typeck/check/op.rs | 8 ++++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs index 7a20014484190..49d751ea8aecc 100644 --- a/src/librustc_middle/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -2251,11 +2251,6 @@ impl<'tcx> TyCtxt<'tcx> { if self.features().never_type_fallback { self.types.never } else { self.types.unit } } - #[inline] - pub fn mk_bool(self) -> Ty<'tcx> { - self.mk_ty(Bool) - } - #[inline] pub fn mk_fn_def(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { self.mk_ty(FnDef(def_id, substs)) diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 00ff2af82e303..d89993e354768 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -121,9 +121,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; match BinOpCategory::from(op) { BinOpCategory::Shortcircuit => { - self.demand_suptype(*lhs_span, tcx.mk_bool(), lhs_ty); - self.demand_suptype(*rhs_span, tcx.mk_bool(), rhs_ty); - tcx.mk_bool() + self.demand_suptype(*lhs_span, tcx.types.bool, lhs_ty); + self.demand_suptype(*rhs_span, tcx.types.bool, rhs_ty); + tcx.types.bool } BinOpCategory::Shift => { @@ -140,7 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { BinOpCategory::Comparison => { // both LHS and RHS and result will have the same type self.demand_suptype(*rhs_span, lhs_ty, rhs_ty); - tcx.mk_bool() + tcx.types.bool } } } From 2f3dd7b187027e601b9711aefc2b1383c5cf5961 Mon Sep 17 00:00:00 2001 From: marmeladema Date: Sun, 24 May 2020 23:39:39 +0100 Subject: [PATCH 22/28] Remove remaining calls to `as_local_node_id` --- src/librustc_ast_lowering/item.rs | 11 +++--- src/librustc_hir/definitions.rs | 11 ------ src/librustc_resolve/build_reduced_graph.rs | 20 +++++++---- src/librustc_resolve/late.rs | 3 +- src/librustc_resolve/lib.rs | 40 +++++++++++++-------- src/librustc_resolve/macros.rs | 8 ++--- 6 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs index eced17c9245f2..47d10f86d03e2 100644 --- a/src/librustc_ast_lowering/item.rs +++ b/src/librustc_ast_lowering/item.rs @@ -1321,12 +1321,15 @@ impl<'hir> LoweringContext<'_, 'hir> { .get_partial_res(bound_pred.bounded_ty.id) .map(|d| d.base_res()) { - if let Some(node_id) = - self.resolver.definitions().as_local_node_id(def_id) - { + if let Some(def_id) = def_id.as_local() { for param in &generics.params { if let GenericParamKind::Type { .. } = param.kind { - if node_id == param.id { + if def_id + == self + .resolver + .definitions() + .local_def_id(param.id) + { add_bounds .entry(param.id) .or_default() diff --git a/src/librustc_hir/definitions.rs b/src/librustc_hir/definitions.rs index c7a0822d27dd6..c8971c2f9adbd 100644 --- a/src/librustc_hir/definitions.rs +++ b/src/librustc_hir/definitions.rs @@ -332,17 +332,6 @@ impl Definitions { }) } - #[inline] - pub fn as_local_node_id(&self, def_id: DefId) -> Option { - if let Some(def_id) = def_id.as_local() { - let node_id = self.def_id_to_node_id[def_id]; - if node_id != ast::DUMMY_NODE_ID { - return Some(node_id); - } - } - None - } - #[inline] pub fn as_local_hir_id(&self, def_id: LocalDefId) -> hir::HirId { self.local_def_id_to_hir_id(def_id) diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 92ea119d9a458..9ee3d989bf3f1 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -25,7 +25,7 @@ use rustc_errors::{struct_span_err, Applicability}; use rustc_expand::base::SyntaxExtension; use rustc_expand::expand::AstFragment; use rustc_hir::def::{self, *}; -use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX}; use rustc_metadata::creader::LoadedMacro; use rustc_middle::bug; use rustc_middle::hir::exports::Export; @@ -1150,15 +1150,22 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Mark the given macro as unused unless its name starts with `_`. // Macro uses will remove items from this set, and the remaining // items will be reported as `unused_macros`. - fn insert_unused_macro(&mut self, ident: Ident, node_id: NodeId, span: Span) { + fn insert_unused_macro( + &mut self, + ident: Ident, + def_id: LocalDefId, + node_id: NodeId, + span: Span, + ) { if !ident.as_str().starts_with('_') { - self.r.unused_macros.insert(node_id, span); + self.r.unused_macros.insert(def_id, (node_id, span)); } } fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScope<'a> { let parent_scope = self.parent_scope; let expansion = parent_scope.expansion; + let def_id = self.r.definitions.local_def_id(item.id); let (ext, ident, span, macro_rules) = match &item.kind { ItemKind::MacroDef(def) => { let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition())); @@ -1166,7 +1173,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ItemKind::Fn(..) => match Self::proc_macro_stub(item) { Some((macro_kind, ident, span)) => { - self.r.proc_macro_stubs.insert(item.id); + self.r.proc_macro_stubs.insert(def_id); (self.r.dummy_ext(macro_kind), ident, span, false) } None => return parent_scope.macro_rules, @@ -1174,7 +1181,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _ => unreachable!(), }; - let def_id = self.r.definitions.local_def_id(item.id); let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id()); self.r.macro_map.insert(def_id.to_def_id(), ext); self.r.local_macro_def_scopes.insert(def_id, parent_scope.module); @@ -1196,7 +1202,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport)); } else { self.r.check_reserved_macro_name(ident, res); - self.insert_unused_macro(ident, item.id, span); + self.insert_unused_macro(ident, def_id, item.id, span); } MacroRulesScope::Binding(self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding { parent_macro_rules_scope: parent_scope.macro_rules, @@ -1214,7 +1220,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _ => self.resolve_visibility(&item.vis), }; if vis != ty::Visibility::Public { - self.insert_unused_macro(ident, item.id, span); + self.insert_unused_macro(ident, def_id, item.id, span); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); self.parent_scope.macro_rules diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index f04813cf3bc7f..3b49b3b6ff7d2 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -1621,11 +1621,10 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let report_errors = |this: &mut Self, res: Option| { let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res); let def_id = this.parent_scope.module.normal_ancestor_id; - let node_id = this.r.definitions.as_local_node_id(def_id).unwrap(); let better = res.is_some(); let suggestion = if res.is_none() { this.report_missing_type_error(path) } else { None }; - this.r.use_injections.push(UseError { err, candidates, node_id, better, suggestion }); + this.r.use_injections.push(UseError { err, candidates, def_id, better, suggestion }); PartialRes::new(Res::Err) }; diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 015f1b6315c19..b50f9fe8e907d 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -23,7 +23,7 @@ use rustc_ast::ast::{self, FloatTy, IntTy, NodeId, UintTy}; use rustc_ast::ast::{Crate, CRATE_NODE_ID}; use rustc_ast::ast::{ItemKind, Path}; use rustc_ast::attr; -use rustc_ast::node_id::{NodeMap, NodeSet}; +use rustc_ast::node_id::NodeMap; use rustc_ast::unwrap_or; use rustc_ast::visit::{self, Visitor}; use rustc_ast_pretty::pprust; @@ -253,21 +253,31 @@ impl<'a> From<&'a ast::PathSegment> for Segment { } } -struct UsePlacementFinder { - target_module: NodeId, +struct UsePlacementFinder<'d> { + definitions: &'d Definitions, + target_module: LocalDefId, span: Option, found_use: bool, } -impl UsePlacementFinder { - fn check(krate: &Crate, target_module: NodeId) -> (Option, bool) { - let mut finder = UsePlacementFinder { target_module, span: None, found_use: false }; - visit::walk_crate(&mut finder, krate); - (finder.span, finder.found_use) +impl<'d> UsePlacementFinder<'d> { + fn check( + definitions: &'d Definitions, + krate: &Crate, + target_module: DefId, + ) -> (Option, bool) { + if let Some(target_module) = target_module.as_local() { + let mut finder = + UsePlacementFinder { definitions, target_module, span: None, found_use: false }; + visit::walk_crate(&mut finder, krate); + (finder.span, finder.found_use) + } else { + (None, false) + } } } -impl<'tcx> Visitor<'tcx> for UsePlacementFinder { +impl<'tcx, 'd> Visitor<'tcx> for UsePlacementFinder<'d> { fn visit_mod( &mut self, module: &'tcx ast::Mod, @@ -278,7 +288,7 @@ impl<'tcx> Visitor<'tcx> for UsePlacementFinder { if self.span.is_some() { return; } - if node_id != self.target_module { + if self.definitions.local_def_id(node_id) != self.target_module { visit::walk_mod(self, module); return; } @@ -611,7 +621,7 @@ struct UseError<'a> { /// Attach `use` statements for these candidates. candidates: Vec, /// The `NodeId` of the module to place the use-statements in. - node_id: NodeId, + def_id: DefId, /// Whether the diagnostic should state that it's "better". better: bool, /// Extra free form suggestion. Currently used to suggest new type parameter. @@ -926,8 +936,8 @@ pub struct Resolver<'a> { non_macro_attrs: [Lrc; 2], local_macro_def_scopes: FxHashMap>, ast_transform_scopes: FxHashMap>, - unused_macros: NodeMap, - proc_macro_stubs: NodeSet, + unused_macros: FxHashMap, + proc_macro_stubs: FxHashSet, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: Vec<(Ident, MacroKind, ParentScope<'a>, Option<&'a NameBinding<'a>>)>, @@ -2567,10 +2577,10 @@ impl<'a> Resolver<'a> { } fn report_with_use_injections(&mut self, krate: &Crate) { - for UseError { mut err, candidates, node_id, better, suggestion } in + for UseError { mut err, candidates, def_id, better, suggestion } in self.use_injections.drain(..) { - let (span, found_use) = UsePlacementFinder::check(krate, node_id); + let (span, found_use) = UsePlacementFinder::check(&self.definitions, krate, def_id); if !candidates.is_empty() { diagnostics::show_candidates(&mut err, span, &candidates, better, found_use); } else if let Some((span, msg, sugg, appl)) = suggestion { diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 7027c82626787..394d8dc4e1135 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -333,7 +333,7 @@ impl<'a> base::Resolver for Resolver<'a> { } fn check_unused_macros(&mut self) { - for (&node_id, &span) in self.unused_macros.iter() { + for (_, &(node_id, span)) in self.unused_macros.iter() { self.lint_buffer.buffer_lint(UNUSED_MACROS, node_id, span, "unused macro definition"); } } @@ -416,9 +416,9 @@ impl<'a> Resolver<'a> { match res { Res::Def(DefKind::Macro(_), def_id) => { - if let Some(node_id) = self.definitions.as_local_node_id(def_id) { - self.unused_macros.remove(&node_id); - if self.proc_macro_stubs.contains(&node_id) { + if let Some(def_id) = def_id.as_local() { + self.unused_macros.remove(&def_id); + if self.proc_macro_stubs.contains(&def_id) { self.session.span_err( path.span, "can't use a procedural macro from the same crate that defines it", From 27ed143ba9bb96e907f56c9f294c904a2cb26123 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Fri, 29 May 2020 16:03:46 -0400 Subject: [PATCH 23/28] fix diagnostics for `@ ..` binding pattern in tuples and tuple structs fix comment add newline for tidy fmt error... edit suggestion message change the suggestion message to better handle cases with binding modes Apply suggestions from estebank code review Co-authored-by: Esteban Kuber edits to address source review Apply suggestions from estebank code review #2 Co-authored-by: Esteban Kuber update test files --- src/librustc_ast_lowering/pat.rs | 33 ++++++++++++++++++++++--- src/test/ui/issues/issue-72574-1.rs | 8 ++++++ src/test/ui/issues/issue-72574-1.stderr | 14 +++++++++++ src/test/ui/issues/issue-72574-2.rs | 10 ++++++++ src/test/ui/issues/issue-72574-2.stderr | 14 +++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/issues/issue-72574-1.rs create mode 100644 src/test/ui/issues/issue-72574-1.stderr create mode 100644 src/test/ui/issues/issue-72574-2.rs create mode 100644 src/test/ui/issues/issue-72574-2.stderr diff --git a/src/librustc_ast_lowering/pat.rs b/src/librustc_ast_lowering/pat.rs index 496e401d06124..55c1f80266337 100644 --- a/src/librustc_ast_lowering/pat.rs +++ b/src/librustc_ast_lowering/pat.rs @@ -3,6 +3,7 @@ use super::{ImplTraitContext, LoweringContext, ParamMode}; use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_span::symbol::Ident; @@ -102,10 +103,36 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Note that unlike for slice patterns, // where `xs @ ..` is a legal sub-slice pattern, // it is not a legal sub-tuple pattern. - if pat.is_rest() { - rest = Some((idx, pat.span)); - break; + match pat.kind { + // Found a sub-tuple rest pattern + PatKind::Rest => { + rest = Some((idx, pat.span)); + break; + } + // Found a sub-tuple pattern `$binding_mode $ident @ ..`. + // This is not allowed as a sub-tuple pattern + PatKind::Ident(ref _bm, ident, Some(ref sub)) if sub.is_rest() => { + rest = Some((idx, pat.span)); + let sp = pat.span; + self.diagnostic() + .struct_span_err( + sp, + &format!("`{} @` is not allowed in a {}", ident.name, ctx), + ) + .span_label(sp, "this is only allowed in slice patterns") + .help("remove this and bind each tuple field independently") + .span_suggestion_verbose( + sp, + &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident), + "..".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(); + break; + } + _ => {} } + // It was not a sub-tuple pattern so lower it normally. elems.push(self.lower_pat(pat)); } diff --git a/src/test/ui/issues/issue-72574-1.rs b/src/test/ui/issues/issue-72574-1.rs new file mode 100644 index 0000000000000..efbb0bfb1508f --- /dev/null +++ b/src/test/ui/issues/issue-72574-1.rs @@ -0,0 +1,8 @@ +fn main() { + let x = (1, 2, 3); + match x { + (_a, _x @ ..) => {} + _ => {} + } +} +//~^^^^ ERROR `_x @` is not allowed in a tuple diff --git a/src/test/ui/issues/issue-72574-1.stderr b/src/test/ui/issues/issue-72574-1.stderr new file mode 100644 index 0000000000000..329f7d008d498 --- /dev/null +++ b/src/test/ui/issues/issue-72574-1.stderr @@ -0,0 +1,14 @@ +error: `_x @` is not allowed in a tuple + --> $DIR/issue-72574-1.rs:4:14 + | +LL | (_a, _x @ ..) => {} + | ^^^^^^^ this is only allowed in slice patterns + | + = help: remove this and bind each tuple field independently +help: if you don't need to use the contents of _x, discard the tuple's remaining fields + | +LL | (_a, ..) => {} + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-72574-2.rs b/src/test/ui/issues/issue-72574-2.rs new file mode 100644 index 0000000000000..0c8f6fcc50889 --- /dev/null +++ b/src/test/ui/issues/issue-72574-2.rs @@ -0,0 +1,10 @@ +struct Binder(i32, i32, i32); + +fn main() { + let x = Binder(1, 2, 3); + match x { + Binder(_a, _x @ ..) => {} + _ => {} + } +} +//~^^^^ ERROR `_x @` is not allowed in a tuple struct diff --git a/src/test/ui/issues/issue-72574-2.stderr b/src/test/ui/issues/issue-72574-2.stderr new file mode 100644 index 0000000000000..6faa57bcca6b1 --- /dev/null +++ b/src/test/ui/issues/issue-72574-2.stderr @@ -0,0 +1,14 @@ +error: `_x @` is not allowed in a tuple struct + --> $DIR/issue-72574-2.rs:6:20 + | +LL | Binder(_a, _x @ ..) => {} + | ^^^^^^^ this is only allowed in slice patterns + | + = help: remove this and bind each tuple field independently +help: if you don't need to use the contents of _x, discard the tuple's remaining fields + | +LL | Binder(_a, ..) => {} + | ^^ + +error: aborting due to previous error + From c4b6224ea46f57bb59df8d321d8f40e7f2900423 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 May 2020 00:02:30 +0200 Subject: [PATCH 24/28] more type sanity checks in Miri --- src/librustc_mir/interpret/operand.rs | 12 ++++++++++-- src/librustc_mir/interpret/place.rs | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index f546f6236d777..3cfc331ef8c4e 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -15,8 +15,8 @@ use rustc_target::abi::{Abi, DiscriminantKind, HasDataLayout, LayoutOf, Size}; use rustc_target::abi::{VariantIdx, Variants}; use super::{ - from_known_layout, ConstValue, GlobalId, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, - Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, + from_known_layout, mir_assign_valid_types, ConstValue, GlobalId, InterpCx, InterpResult, + MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -469,6 +469,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .try_fold(base_op, |op, elem| self.operand_projection(op, elem))?; trace!("eval_place_to_op: got {:?}", *op); + // Sanity-check the type we ended up with. + debug_assert!(mir_assign_valid_types( + *self.tcx, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + place.ty(&self.frame().body.local_decls, *self.tcx).ty + ))?, + op.layout, + )); Ok(op) } diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 6dadb8e4c67f4..f5e7c1a4823cf 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -638,6 +638,14 @@ where } self.dump_place(place_ty.place); + // Sanity-check the type we ended up with. + debug_assert!(mir_assign_valid_types( + *self.tcx, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + place.ty(&self.frame().body.local_decls, *self.tcx).ty + ))?, + place_ty.layout, + )); Ok(place_ty) } From 6700e186883a83008963d1fdba23eff2b1713e56 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 12 May 2020 20:09:55 -0700 Subject: [PATCH 25/28] Add Extend::{extend_one,extend_reserve} This adds new optional methods on `Extend`: `extend_one` add a single element to the collection, and `extend_reserve` pre-allocates space for the predicted number of incoming elements. These are used in `Iterator` for `partition` and `unzip` as they shuffle elements one-at-a-time into their respective collections. --- src/liballoc/collections/binary_heap.rs | 20 ++++++++++++++ src/liballoc/collections/btree/map.rs | 10 +++++++ src/liballoc/collections/btree/set.rs | 10 +++++++ src/liballoc/collections/linked_list.rs | 10 +++++++ src/liballoc/collections/vec_deque.rs | 20 ++++++++++++++ src/liballoc/lib.rs | 1 + src/liballoc/string.rs | 35 ++++++++++++++++++++++++ src/liballoc/vec.rs | 20 ++++++++++++++ src/libcore/iter/traits/collect.rs | 15 +++++++++- src/libcore/iter/traits/iterator.rs | 14 +++++++--- src/librustc_data_structures/lib.rs | 1 + src/librustc_data_structures/thin_vec.rs | 14 ++++++++++ src/librustc_index/lib.rs | 1 + src/librustc_index/vec.rs | 10 +++++++ src/librustc_infer/lib.rs | 1 + src/librustc_infer/traits/util.rs | 8 ++++++ src/libstd/collections/hash/map.rs | 29 ++++++++++++++++++++ src/libstd/collections/hash/set.rs | 20 ++++++++++++++ src/libstd/lib.rs | 1 + src/libstd/path.rs | 5 ++++ src/libstd/sys_common/wtf8.rs | 11 ++++++++ 21 files changed, 251 insertions(+), 5 deletions(-) diff --git a/src/liballoc/collections/binary_heap.rs b/src/liballoc/collections/binary_heap.rs index a3ef998918433..c2fe4691b34c0 100644 --- a/src/liballoc/collections/binary_heap.rs +++ b/src/liballoc/collections/binary_heap.rs @@ -1376,6 +1376,16 @@ impl Extend for BinaryHeap { fn extend>(&mut self, iter: I) { >::spec_extend(self, iter); } + + #[inline] + fn extend_one(&mut self, item: T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } impl> SpecExtend for BinaryHeap { @@ -1406,4 +1416,14 @@ impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index c6cb39b1bf511..fa1c09d9ece87 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -1901,6 +1901,11 @@ impl Extend<(K, V)> for BTreeMap { self.insert(k, v); }); } + + #[inline] + fn extend_one(&mut self, (k, v): (K, V)) { + self.insert(k, v); + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -1908,6 +1913,11 @@ impl<'a, K: Ord + Copy, V: Copy> Extend<(&'a K, &'a V)> for BTreeMap { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); } + + #[inline] + fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { + self.insert(k, v); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs index dee5fb878ff2a..525ef38c32fa2 100644 --- a/src/liballoc/collections/btree/set.rs +++ b/src/liballoc/collections/btree/set.rs @@ -1152,6 +1152,11 @@ impl Extend for BTreeSet { self.insert(elem); }); } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.insert(elem); + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -1159,6 +1164,11 @@ impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BTreeSet { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.insert(elem); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/collections/linked_list.rs b/src/liballoc/collections/linked_list.rs index cc0f07b822741..85f2505f756aa 100644 --- a/src/liballoc/collections/linked_list.rs +++ b/src/liballoc/collections/linked_list.rs @@ -1748,6 +1748,11 @@ impl Extend for LinkedList { fn extend>(&mut self, iter: I) { >::spec_extend(self, iter); } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.push_back(elem); + } } impl SpecExtend for LinkedList { @@ -1767,6 +1772,11 @@ impl<'a, T: 'a + Copy> Extend<&'a T> for LinkedList { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.push_back(elem); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index 540649c61b332..ae54d3971baac 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -2881,6 +2881,16 @@ impl Extend for VecDeque { } } } + + #[inline] + fn extend_one(&mut self, elem: A) { + self.push_back(elem); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -2888,6 +2898,16 @@ impl<'a, T: 'a + Copy> Extend<&'a T> for VecDeque { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &elem: &T) { + self.push_back(elem); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 7aaa91ee10d97..9bcfc9457f50e 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -93,6 +93,7 @@ #![feature(container_error_extra)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] +#![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(fundamental)] diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index f3fe1adebb141..0378ff5362a8b 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1799,6 +1799,16 @@ impl Extend for String { self.reserve(lower_bound); iterator.for_each(move |c| self.push(c)); } + + #[inline] + fn extend_one(&mut self, c: char) { + self.push(c); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -1806,6 +1816,16 @@ impl<'a> Extend<&'a char> for String { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &c: &'a char) { + self.push(c); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1813,6 +1833,11 @@ impl<'a> Extend<&'a str> for String { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |s| self.push_str(s)); } + + #[inline] + fn extend_one(&mut self, s: &'a str) { + self.push_str(s); + } } #[stable(feature = "extend_string", since = "1.4.0")] @@ -1820,6 +1845,11 @@ impl Extend for String { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |s| self.push_str(&s)); } + + #[inline] + fn extend_one(&mut self, s: String) { + self.push_str(&s); + } } #[stable(feature = "herd_cows", since = "1.19.0")] @@ -1827,6 +1857,11 @@ impl<'a> Extend> for String { fn extend>>(&mut self, iter: I) { iter.into_iter().for_each(move |s| self.push_str(&s)); } + + #[inline] + fn extend_one(&mut self, s: Cow<'a, str>) { + self.push_str(&s); + } } /// A convenience impl that delegates to the impl for `&str`. diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index d26cd77aae4b7..42fb1f8c737b3 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2045,6 +2045,16 @@ impl Extend for Vec { fn extend>(&mut self, iter: I) { >::spec_extend(self, iter.into_iter()) } + + #[inline] + fn extend_one(&mut self, item: T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } // Specialization trait used for Vec::from_iter and Vec::extend @@ -2316,6 +2326,16 @@ impl<'a, T: 'a + Copy> Extend<&'a T> for Vec { fn extend>(&mut self, iter: I) { self.spec_extend(iter.into_iter()) } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } macro_rules! __impl_slice_eq1 { diff --git a/src/libcore/iter/traits/collect.rs b/src/libcore/iter/traits/collect.rs index f21ab8dbc3737..da859db545d7e 100644 --- a/src/libcore/iter/traits/collect.rs +++ b/src/libcore/iter/traits/collect.rs @@ -322,7 +322,7 @@ impl IntoIterator for I { pub trait Extend { /// Extends a collection with the contents of an iterator. /// - /// As this is the only method for this trait, the [trait-level] docs + /// As this is the only required method for this trait, the [trait-level] docs /// contain more details. /// /// [trait-level]: trait.Extend.html @@ -341,6 +341,18 @@ pub trait Extend { /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn extend>(&mut self, iter: T); + + /// Extends a collection with exactly one element. + #[unstable(feature = "extend_one", issue = "none")] + fn extend_one(&mut self, item: A) { + self.extend(Some(item)); + } + + /// Reserves capacity in a collection for the given number of additional elements. + /// + /// The default implementation does nothing. + #[unstable(feature = "extend_one", issue = "none")] + fn extend_reserve(&mut self, _additional: usize) {} } #[stable(feature = "extend_for_unit", since = "1.28.0")] @@ -348,4 +360,5 @@ impl Extend<()> for () { fn extend>(&mut self, iter: T) { iter.into_iter().for_each(drop) } + fn extend_one(&mut self, _item: ()) {} } diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index 7f081f732fd58..a10b34d931d10 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs @@ -1711,9 +1711,9 @@ pub trait Iterator { ) -> impl FnMut((), T) + 'a { move |(), x| { if f(&x) { - left.extend(Some(x)); + left.extend_one(x); } else { - right.extend(Some(x)); + right.extend_one(x); } } } @@ -2686,14 +2686,20 @@ pub trait Iterator { us: &'a mut impl Extend, ) -> impl FnMut((), (A, B)) + 'a { move |(), (t, u)| { - ts.extend(Some(t)); - us.extend(Some(u)); + ts.extend_one(t); + us.extend_one(u); } } let mut ts: FromA = Default::default(); let mut us: FromB = Default::default(); + let (lower_bound, _) = self.size_hint(); + if lower_bound > 0 { + ts.extend_reserve(lower_bound); + us.extend_reserve(lower_bound); + } + self.fold((), extend(&mut ts, &mut us)); (ts, us) diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 7ee60176dbead..0b2e7cda1b4cc 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -22,6 +22,7 @@ #![feature(test)] #![feature(associated_type_bounds)] #![feature(thread_id_value)] +#![feature(extend_one)] #![allow(rustc::default_hash_types)] #[macro_use] diff --git a/src/librustc_data_structures/thin_vec.rs b/src/librustc_data_structures/thin_vec.rs index 2befc0aa50487..43002178eb971 100644 --- a/src/librustc_data_structures/thin_vec.rs +++ b/src/librustc_data_structures/thin_vec.rs @@ -53,6 +53,20 @@ impl Extend for ThinVec { ThinVec(None) => *self = iter.into_iter().collect::>().into(), } } + + fn extend_one(&mut self, item: T) { + match *self { + ThinVec(Some(ref mut vec)) => vec.push(item), + ThinVec(None) => *self = vec![item].into(), + } + } + + fn extend_reserve(&mut self, additional: usize) { + match *self { + ThinVec(Some(ref mut vec)) => vec.reserve(additional), + ThinVec(None) => *self = Vec::with_capacity(additional).into(), + } + } } impl, CTX> HashStable for ThinVec { diff --git a/src/librustc_index/lib.rs b/src/librustc_index/lib.rs index e8aa1a209e929..3effc41645011 100644 --- a/src/librustc_index/lib.rs +++ b/src/librustc_index/lib.rs @@ -2,6 +2,7 @@ #![feature(const_if_match)] #![feature(const_fn)] #![feature(const_panic)] +#![feature(extend_one)] #![feature(unboxed_closures)] #![feature(test)] #![feature(fn_traits)] diff --git a/src/librustc_index/vec.rs b/src/librustc_index/vec.rs index 67dcea58cf82b..4dde33283f575 100644 --- a/src/librustc_index/vec.rs +++ b/src/librustc_index/vec.rs @@ -736,6 +736,16 @@ impl Extend for IndexVec { fn extend>(&mut self, iter: J) { self.raw.extend(iter); } + + #[inline] + fn extend_one(&mut self, item: T) { + self.raw.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.raw.reserve(additional); + } } impl FromIterator for IndexVec { diff --git a/src/librustc_infer/lib.rs b/src/librustc_infer/lib.rs index 28d42cea6d300..ed04ee02b7203 100644 --- a/src/librustc_infer/lib.rs +++ b/src/librustc_infer/lib.rs @@ -16,6 +16,7 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(extend_one)] #![feature(never_type)] #![feature(or_patterns)] #![feature(range_is_empty)] diff --git a/src/librustc_infer/traits/util.rs b/src/librustc_infer/traits/util.rs index 17b7b4e680f5e..8081cac0067f1 100644 --- a/src/librustc_infer/traits/util.rs +++ b/src/librustc_infer/traits/util.rs @@ -81,6 +81,14 @@ impl Extend> for PredicateSet<'tcx> { self.insert(pred); } } + + fn extend_one(&mut self, pred: ty::Predicate<'tcx>) { + self.insert(pred); + } + + fn extend_reserve(&mut self, additional: usize) { + Extend::>::extend_reserve(&mut self.set, additional); + } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 56cf9be339194..a51f1998e4452 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2426,6 +2426,24 @@ where fn extend>(&mut self, iter: T) { self.base.extend(iter) } + + #[inline] + fn extend_one(&mut self, (k, v): (K, V)) { + self.base.insert(k, v); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + // self.base.extend_reserve(additional); + // FIXME: hashbrown should implement this method. + // But until then, use the same reservation logic: + + // Reserve the entire hint lower bound if the map is empty. + // Otherwise reserve half the hint (rounded up), so the map + // will only resize twice in the worst case. + let reserve = if self.is_empty() { additional } else { (additional + 1) / 2 }; + self.base.reserve(reserve); + } } #[stable(feature = "hash_extend_copy", since = "1.4.0")] @@ -2439,6 +2457,17 @@ where fn extend>(&mut self, iter: T) { self.base.extend(iter) } + + #[inline] + fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { + self.base.insert(k, v); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + // self.base.extend_reserve(additional); + Extend::<(K, V)>::extend_reserve(self, additional) + } } /// `RandomState` is the default state for [`HashMap`] types. diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index ca06457291cae..cb2f829803b85 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -970,6 +970,16 @@ where fn extend>(&mut self, iter: I) { self.map.extend(iter.into_iter().map(|k| (k, ()))); } + + #[inline] + fn extend_one(&mut self, item: T) { + self.map.insert(item, ()); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.map.extend_reserve(additional); + } } #[stable(feature = "hash_extend_copy", since = "1.4.0")] @@ -982,6 +992,16 @@ where fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.map.insert(item, ()); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + Extend::::extend_reserve(self, additional) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 72dfe2937f491..9ddaa100c0e9d 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -263,6 +263,7 @@ #![feature(duration_constants)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] +#![feature(extend_one)] #![feature(external_doc)] #![feature(fn_traits)] #![feature(format_args_nl)] diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 0fe5451bb9564..8ff7508ba6457 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1534,6 +1534,11 @@ impl> iter::Extend

for PathBuf { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |p| self.push(p.as_ref())); } + + #[inline] + fn extend_one(&mut self, p: P) { + self.push(p.as_ref()); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index a98407da44850..a5ba3daba3e87 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -386,6 +386,17 @@ impl Extend for Wtf8Buf { self.bytes.reserve(low); iterator.for_each(move |code_point| self.push(code_point)); } + + #[inline] + fn extend_one(&mut self, code_point: CodePoint) { + self.push(code_point); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + // Lower bound of one byte per code point (ASCII only) + self.bytes.reserve(additional); + } } /// A borrowed slice of well-formed WTF-8 data. From e4345547b967a1fed4d62f6f0c539b56a2131aa4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 26 May 2020 14:01:26 -0700 Subject: [PATCH 26/28] Use a canonical name for extend_reserve(additional) Co-authored-by: David Tolnay --- src/libcore/iter/traits/collect.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcore/iter/traits/collect.rs b/src/libcore/iter/traits/collect.rs index da859db545d7e..4a90aca148547 100644 --- a/src/libcore/iter/traits/collect.rs +++ b/src/libcore/iter/traits/collect.rs @@ -352,7 +352,9 @@ pub trait Extend { /// /// The default implementation does nothing. #[unstable(feature = "extend_one", issue = "none")] - fn extend_reserve(&mut self, _additional: usize) {} + fn extend_reserve(&mut self, additional: usize) { + let _ = additional; + } } #[stable(feature = "extend_for_unit", since = "1.28.0")] From 10efaa37de8411272de3623ce50714d4860dc561 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 26 May 2020 14:03:34 -0700 Subject: [PATCH 27/28] Remove an old comment from HashMap::extend_reserve --- src/libstd/collections/hash/map.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index a51f1998e4452..5ba5eff44076b 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2465,7 +2465,6 @@ where #[inline] fn extend_reserve(&mut self, additional: usize) { - // self.base.extend_reserve(additional); Extend::<(K, V)>::extend_reserve(self, additional) } } From a51b22a9fda2fff37b17ad7b7fa516b3385089a9 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 26 May 2020 14:15:29 -0700 Subject: [PATCH 28/28] Add extend_one tracking issue 72631 --- src/libcore/iter/traits/collect.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/iter/traits/collect.rs b/src/libcore/iter/traits/collect.rs index 4a90aca148547..9d20022b6ed6d 100644 --- a/src/libcore/iter/traits/collect.rs +++ b/src/libcore/iter/traits/collect.rs @@ -343,7 +343,7 @@ pub trait Extend { fn extend>(&mut self, iter: T); /// Extends a collection with exactly one element. - #[unstable(feature = "extend_one", issue = "none")] + #[unstable(feature = "extend_one", issue = "72631")] fn extend_one(&mut self, item: A) { self.extend(Some(item)); } @@ -351,7 +351,7 @@ pub trait Extend { /// Reserves capacity in a collection for the given number of additional elements. /// /// The default implementation does nothing. - #[unstable(feature = "extend_one", issue = "none")] + #[unstable(feature = "extend_one", issue = "72631")] fn extend_reserve(&mut self, additional: usize) { let _ = additional; }