From 513ea6405b121e589dafad738b600cab5baa2ab6 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 23 Mar 2020 18:39:25 +0100 Subject: [PATCH 01/10] add missing visit_consts --- src/librustc/traits/structural_impls.rs | 14 ++++++++++++++ src/librustc/ty/fold.rs | 20 +++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index a5efea9e5fa4d..b1fb02a67b3ff 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -273,6 +273,20 @@ impl<'tcx> TypeVisitor<'tcx> for BoundNamesCollector { t.super_visit_with(self) } + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + match c.val { + ty::ConstKind::Bound(debruijn, bound_var) if debruijn == self.binder_index => { + self.types.insert( + bound_var.as_u32(), + Symbol::intern(&format!("^{}", bound_var.as_u32())), + ); + } + _ => (), + } + + c.super_visit_with(self) + } + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { match r { ty::ReLateBound(index, br) if *index == self.binder_index => match br { diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 4adca6c7d9772..3f4f2407f1e6e 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -978,17 +978,27 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { // ignore the inputs to a projection, as they may not appear // in the normalized form if self.just_constrained { - match t.kind { - ty::Projection(..) | ty::Opaque(..) => { - return false; - } - _ => {} + if let ty::Projection(..) | ty::Opaque(..) = t.kind { + return false; } } t.super_visit_with(self) } + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + // if we are only looking for "constrained" region, we have to + // ignore the inputs of an unevaluated const, as they may not appear + // in the normalized form + if self.just_constrained { + if let ty::ConstKind::Unevaluated(..) = c.val { + return false; + } + } + + c.super_visit_with(self) + } + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { if let ty::ReLateBound(debruijn, br) = *r { if debruijn == self.current_index { From bda976d42db4abc496bb8673246b34e667b48e6f Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 23 Mar 2020 19:20:28 +0100 Subject: [PATCH 02/10] add missing const super folds --- src/librustc_trait_selection/traits/project.rs | 1 + src/librustc_trait_selection/traits/query/normalize.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs index 6b14f6959bfb9..0968f470d13c0 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/src/librustc_trait_selection/traits/project.rs @@ -387,6 +387,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { } fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + let constant = constant.super_fold_with(self); constant.eval(self.selcx.tcx(), self.param_env) } } diff --git a/src/librustc_trait_selection/traits/query/normalize.rs b/src/librustc_trait_selection/traits/query/normalize.rs index 99412fafcfa8d..77128bc8c8ab4 100644 --- a/src/librustc_trait_selection/traits/query/normalize.rs +++ b/src/librustc_trait_selection/traits/query/normalize.rs @@ -191,6 +191,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { } fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + let constant = constant.super_fold_with(self); constant.eval(self.infcx.tcx, self.param_env) } } From d7ecc8c9bc2727579b22f155f1b7da42b6eee8e3 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 23 Mar 2020 19:22:19 +0100 Subject: [PATCH 03/10] query normalize_generic_arg_after_erasing_regions --- src/librustc/dep_graph/dep_node.rs | 2 +- src/librustc/query/mod.rs | 8 ++++---- src/librustc/ty/normalize_erasing_regions.rs | 14 +++++++++++--- src/librustc/ty/query/keys.rs | 13 ++++++++++++- src/librustc/ty/query/mod.rs | 2 +- src/librustc/ty/subst.rs | 8 ++++++++ src/librustc_session/session.rs | 8 ++++---- src/librustc_traits/normalize_erasing_regions.rs | 15 ++++++++------- 8 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index b32fa2cda8012..61d63f4623249 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -59,7 +59,7 @@ use crate::traits::query::{ CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, }; -use crate::ty::subst::SubstsRef; +use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 54f5103f736ec..86855cb0ef0c2 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -9,7 +9,7 @@ use crate::traits::query::{ }; use crate::ty::query::queries; use crate::ty::query::QueryDescription; -use crate::ty::subst::SubstsRef; +use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; @@ -1114,9 +1114,9 @@ rustc_queries! { } /// Do not call this query directly: invoke `normalize_erasing_regions` instead. - query normalize_ty_after_erasing_regions( - goal: ParamEnvAnd<'tcx, Ty<'tcx>> - ) -> Ty<'tcx> { + query normalize_generic_arg_after_erasing_regions( + goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> + ) -> GenericArg<'tcx> { desc { "normalizing `{:?}`", goal } } diff --git a/src/librustc/ty/normalize_erasing_regions.rs b/src/librustc/ty/normalize_erasing_regions.rs index cbaabd8e1f137..e49bf6f8e67dc 100644 --- a/src/librustc/ty/normalize_erasing_regions.rs +++ b/src/librustc/ty/normalize_erasing_regions.rs @@ -4,8 +4,8 @@ //! //! The methods in this file use a `TypeFolder` to recursively process //! contents, invoking the underlying -//! `normalize_ty_after_erasing_regions` query for each type found -//! within. (This underlying query is what is cached.) +//! `normalize_generic_arg_after_erasing_regions` query for each type +//! or constant found within. (This underlying query is what is cached.) use crate::ty::fold::{TypeFoldable, TypeFolder}; use crate::ty::subst::{Subst, SubstsRef}; @@ -94,6 +94,14 @@ impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.tcx.normalize_ty_after_erasing_regions(self.param_env.and(ty)) + self.tcx + .normalize_generic_arg_after_erasing_regions(self.param_env.and(ty.into())) + .expect_ty() + } + + fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + self.tcx + .normalize_generic_arg_after_erasing_regions(self.param_env.and(c.into())) + .expect_const() } } diff --git a/src/librustc/ty/query/keys.rs b/src/librustc/ty/query/keys.rs index 6073d3a545f6d..6be1f04efca2b 100644 --- a/src/librustc/ty/query/keys.rs +++ b/src/librustc/ty/query/keys.rs @@ -5,7 +5,7 @@ use crate::mir; use crate::traits; use crate::ty::fast_reject::SimplifiedType; use crate::ty::query::caches::DefaultCacheSelector; -use crate::ty::subst::SubstsRef; +use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::{self, Ty, TyCtxt}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_span::symbol::Symbol; @@ -194,6 +194,17 @@ impl<'tcx> Key for ty::PolyTraitRef<'tcx> { } } +impl<'tcx> Key for GenericArg<'tcx> { + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + LOCAL_CRATE + } + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for &'tcx ty::Const<'tcx> { type CacheSelector = DefaultCacheSelector; diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 32ba13b1dbe9a..1094eb4940346 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -31,7 +31,7 @@ use crate::traits::specialization_graph; use crate::traits::Clauses; use crate::traits::{self, Vtable}; use crate::ty::steal::Steal; -use crate::ty::subst::SubstsRef; +use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; use crate::util::common::ErrorReported; diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index a3acc14856e1f..0f4485a705046 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -128,6 +128,14 @@ impl<'tcx> GenericArg<'tcx> { _ => bug!("expected a type, but found another kind"), } } + + /// Unpack the `GenericArg` as a const when it is known certainly to be a const. + pub fn expect_const(self) -> &'tcx ty::Const<'tcx> { + match self.unpack() { + GenericArgKind::Const(c) => c, + _ => bug!("expected a const, but found another kind"), + } + } } impl<'a, 'tcx> Lift<'tcx> for GenericArg<'a> { diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index 80f59aff69137..b3d75143c5639 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -150,7 +150,7 @@ pub struct PerfStats { /// Total number of values canonicalized queries constructed. pub queries_canonicalized: AtomicUsize, /// Number of times this query is invoked. - pub normalize_ty_after_erasing_regions: AtomicUsize, + pub normalize_generic_arg_after_erasing_regions: AtomicUsize, /// Number of times this query is invoked. pub normalize_projection_ty: AtomicUsize, } @@ -707,8 +707,8 @@ impl Session { self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) ); println!( - "normalize_ty_after_erasing_regions: {}", - self.perf_stats.normalize_ty_after_erasing_regions.load(Ordering::Relaxed) + "normalize_generic_arg_after_erasing_regions: {}", + self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) ); println!( "normalize_projection_ty: {}", @@ -1080,7 +1080,7 @@ fn build_session_( symbol_hash_time: Lock::new(Duration::from_secs(0)), decode_def_path_tables_time: Lock::new(Duration::from_secs(0)), queries_canonicalized: AtomicUsize::new(0), - normalize_ty_after_erasing_regions: AtomicUsize::new(0), + normalize_generic_arg_after_erasing_regions: AtomicUsize::new(0), normalize_projection_ty: AtomicUsize::new(0), }, code_stats: Default::default(), diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/src/librustc_traits/normalize_erasing_regions.rs index c2fb237a05b54..065cf38eb2490 100644 --- a/src/librustc_traits/normalize_erasing_regions.rs +++ b/src/librustc_traits/normalize_erasing_regions.rs @@ -1,23 +1,24 @@ use rustc::traits::query::NoSolution; use rustc::ty::query::Providers; -use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc::ty::subst::GenericArg; +use rustc::ty::{self, ParamEnvAnd, TyCtxt}; use rustc_infer::infer::TyCtxtInferExt; use rustc_trait_selection::traits::query::normalize::AtExt; use rustc_trait_selection::traits::{Normalized, ObligationCause}; use std::sync::atomic::Ordering; crate fn provide(p: &mut Providers<'_>) { - *p = Providers { normalize_ty_after_erasing_regions, ..*p }; + *p = Providers { normalize_generic_arg_after_erasing_regions, ..*p }; } -fn normalize_ty_after_erasing_regions<'tcx>( +fn normalize_generic_arg_after_erasing_regions<'tcx>( tcx: TyCtxt<'tcx>, - goal: ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> Ty<'tcx> { - debug!("normalize_ty_after_erasing_regions(goal={:#?})", goal); + goal: ParamEnvAnd<'tcx, GenericArg<'tcx>>, +) -> GenericArg<'tcx> { + debug!("normalize_generic_arg_after_erasing_regions(goal={:#?})", goal); let ParamEnvAnd { param_env, value } = goal; - tcx.sess.perf_stats.normalize_ty_after_erasing_regions.fetch_add(1, Ordering::Relaxed); + tcx.sess.perf_stats.normalize_generic_arg_after_erasing_regions.fetch_add(1, Ordering::Relaxed); tcx.infer_ctxt().enter(|infcx| { let cause = ObligationCause::dummy(); match infcx.at(&cause, param_env).normalize(&value) { From 6c4d5d989684da657e51629c01ca0667a4a90937 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Tue, 24 Mar 2020 11:24:24 +0100 Subject: [PATCH 04/10] improve normalize cycle error --- src/librustc/query/mod.rs | 2 +- src/librustc/ty/normalize_erasing_regions.rs | 10 +++--- .../associated-const/defaults-cyclic-fail.rs | 2 +- .../defaults-cyclic-fail.stderr | 32 +++++++++++++------ src/test/ui/consts/const-size_of-cycle.stderr | 2 +- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 86855cb0ef0c2..1cc5c6e6f4ae7 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -1117,7 +1117,7 @@ rustc_queries! { query normalize_generic_arg_after_erasing_regions( goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> ) -> GenericArg<'tcx> { - desc { "normalizing `{:?}`", goal } + desc { "normalizing `{}`", goal.value } } query implied_outlives_bounds( diff --git a/src/librustc/ty/normalize_erasing_regions.rs b/src/librustc/ty/normalize_erasing_regions.rs index e49bf6f8e67dc..2f0a57c59eb14 100644 --- a/src/librustc/ty/normalize_erasing_regions.rs +++ b/src/librustc/ty/normalize_erasing_regions.rs @@ -94,14 +94,12 @@ impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.tcx - .normalize_generic_arg_after_erasing_regions(self.param_env.and(ty.into())) - .expect_ty() + let arg = self.param_env.and(ty.into()); + self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_ty() } fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - self.tcx - .normalize_generic_arg_after_erasing_regions(self.param_env.and(c.into())) - .expect_const() + let arg = self.param_env.and(c.into()); + self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_const() } } diff --git a/src/test/ui/associated-const/defaults-cyclic-fail.rs b/src/test/ui/associated-const/defaults-cyclic-fail.rs index 9b899ee316a0e..9fb1bbebc9610 100644 --- a/src/test/ui/associated-const/defaults-cyclic-fail.rs +++ b/src/test/ui/associated-const/defaults-cyclic-fail.rs @@ -1,9 +1,9 @@ // build-fail +//~^ ERROR cycle detected when normalizing `<() as Tr>::A` // Cyclic assoc. const defaults don't error unless *used* trait Tr { const A: u8 = Self::B; - //~^ ERROR cycle detected when const-evaluating + checking `Tr::A` const B: u8 = Self::A; } diff --git a/src/test/ui/associated-const/defaults-cyclic-fail.stderr b/src/test/ui/associated-const/defaults-cyclic-fail.stderr index 940182d4aa676..6b2fbe5be4e30 100644 --- a/src/test/ui/associated-const/defaults-cyclic-fail.stderr +++ b/src/test/ui/associated-const/defaults-cyclic-fail.stderr @@ -1,30 +1,42 @@ -error[E0391]: cycle detected when const-evaluating + checking `Tr::A` - --> $DIR/defaults-cyclic-fail.rs:5:5 +error[E0391]: cycle detected when normalizing `<() as Tr>::A` + | +note: ...which requires const-evaluating + checking `Tr::A`... + --> $DIR/defaults-cyclic-fail.rs:6:5 | LL | const A: u8 = Self::B; | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `Tr::A`... + --> $DIR/defaults-cyclic-fail.rs:6:5 | +LL | const A: u8 = Self::B; + | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating `Tr::A`... - --> $DIR/defaults-cyclic-fail.rs:5:19 + --> $DIR/defaults-cyclic-fail.rs:6:5 | LL | const A: u8 = Self::B; - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires normalizing `<() as Tr>::B`... +note: ...which requires const-evaluating + checking `Tr::B`... + --> $DIR/defaults-cyclic-fail.rs:8:5 + | +LL | const B: u8 = Self::A; + | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Tr::B`... --> $DIR/defaults-cyclic-fail.rs:8:5 | LL | const B: u8 = Self::A; | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating `Tr::B`... - --> $DIR/defaults-cyclic-fail.rs:8:19 + --> $DIR/defaults-cyclic-fail.rs:8:5 | LL | const B: u8 = Self::A; - | ^^^^^^^ - = note: ...which again requires const-evaluating + checking `Tr::A`, completing the cycle + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires normalizing `<() as Tr>::A`, completing the cycle note: cycle used when const-evaluating `main` - --> $DIR/defaults-cyclic-fail.rs:16:16 + --> $DIR/defaults-cyclic-fail.rs:14:1 | -LL | assert_eq!(<() as Tr>::A, 0); - | ^^^^^^^^^^^^^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/consts/const-size_of-cycle.stderr b/src/test/ui/consts/const-size_of-cycle.stderr index c03b7a19ffc61..aac3622c6de40 100644 --- a/src/test/ui/consts/const-size_of-cycle.stderr +++ b/src/test/ui/consts/const-size_of-cycle.stderr @@ -25,7 +25,7 @@ note: ...which requires const-evaluating + checking `std::intrinsics::size_of`.. LL | pub fn size_of() -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Foo`... - = note: ...which requires normalizing `ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: All, def_id: None }, value: [u8; _] }`... + = note: ...which requires normalizing `[u8; _]`... = note: ...which again requires const-evaluating + checking `Foo::bytes::{{constant}}#0`, completing the cycle note: cycle used when processing `Foo` --> $DIR/const-size_of-cycle.rs:7:1 From 11763d48cf3104656f1e6c902f1d27d7cd16418f Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Tue, 24 Mar 2020 16:56:12 +0100 Subject: [PATCH 05/10] update mir opt test --- src/test/mir-opt/inline/inline-into-box-place.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/mir-opt/inline/inline-into-box-place.rs b/src/test/mir-opt/inline/inline-into-box-place.rs index f368bdef6f8e2..500238de4c5ab 100644 --- a/src/test/mir-opt/inline/inline-into-box-place.rs +++ b/src/test/mir-opt/inline/inline-into-box-place.rs @@ -55,7 +55,7 @@ fn main() { // StorageLive(_2); // _2 = Box(std::vec::Vec); // _4 = &mut (*_2); -// ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec::::NEW; +// ((*_4).0: alloc::raw_vec::RawVec) = const ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), undef_mask: UndefMask { blocks: [65535], len: Size { raw: 16 } }, size: Size { raw: 16 }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }: alloc::raw_vec::RawVec::; // ((*_4).1: usize) = const 0usize; // _1 = move _2; // StorageDead(_2); From 212e6ce7bf67d6475ec4fdfebfcf9f99704b2aa2 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sun, 22 Mar 2020 16:03:34 -0700 Subject: [PATCH 06/10] Implement Fuse with Option The former `done` flag was roughly similar to an `Option` tag, but left the possibity of misuse. By using a real `Option`, we can set `None` when the iterator is exhausted, removing any way to call it again. We also allow niche layout this way, so the `Fuse` may be smaller. The `FusedIterator` specialization does want to ignore the possibility of exhaustion though, so it uses `unsafe { intrinsics::unreachable() }` to optimize that branch away. The entire `Fuse` implementation is now isolated in its own module to contain that unsafety. --- src/libcore/iter/adapters/fuse.rs | 338 ++++++++++++++++++++++++++++++ src/libcore/iter/adapters/mod.rs | 257 +---------------------- 2 files changed, 340 insertions(+), 255 deletions(-) create mode 100644 src/libcore/iter/adapters/fuse.rs diff --git a/src/libcore/iter/adapters/fuse.rs b/src/libcore/iter/adapters/fuse.rs new file mode 100644 index 0000000000000..f5fd075662209 --- /dev/null +++ b/src/libcore/iter/adapters/fuse.rs @@ -0,0 +1,338 @@ +use crate::intrinsics; +use crate::iter::{ + DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedRandomAccess, +}; +use crate::ops::Try; + +/// An iterator that yields `None` forever after the underlying iterator +/// yields `None` once. +/// +/// This `struct` is created by the [`fuse`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`fuse`]: trait.Iterator.html#method.fuse +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Fuse { + // NOTE: for `I: FusedIterator`, this is always assumed `Some`! + iter: Option, +} +impl Fuse { + pub(in crate::iter) fn new(iter: I) -> Fuse { + Fuse { iter: Some(iter) } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Fuse where I: Iterator {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Fuse +where + I: Iterator, +{ + type Item = ::Item; + + #[inline] + default fn next(&mut self) -> Option<::Item> { + let next = self.iter.as_mut()?.next(); + if next.is_none() { + self.iter = None; + } + next + } + + #[inline] + default fn nth(&mut self, n: usize) -> Option { + let nth = self.iter.as_mut()?.nth(n); + if nth.is_none() { + self.iter = None; + } + nth + } + + #[inline] + default fn last(self) -> Option { + self.iter?.last() + } + + #[inline] + default fn count(self) -> usize { + self.iter.map_or(0, I::count) + } + + #[inline] + default fn size_hint(&self) -> (usize, Option) { + self.iter.as_ref().map_or((0, Some(0)), I::size_hint) + } + + #[inline] + default fn try_fold(&mut self, mut acc: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + if let Some(ref mut iter) = self.iter { + acc = iter.try_fold(acc, fold)?; + self.iter = None; + } + Try::from_ok(acc) + } + + #[inline] + default fn fold(self, mut acc: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if let Some(iter) = self.iter { + acc = iter.fold(acc, fold); + } + acc + } + + #[inline] + default fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + let found = self.iter.as_mut()?.find(predicate); + if found.is_none() { + self.iter = None; + } + found + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Fuse +where + I: DoubleEndedIterator, +{ + #[inline] + default fn next_back(&mut self) -> Option<::Item> { + let next = self.iter.as_mut()?.next_back(); + if next.is_none() { + self.iter = None; + } + next + } + + #[inline] + default fn nth_back(&mut self, n: usize) -> Option<::Item> { + let nth = self.iter.as_mut()?.nth_back(n); + if nth.is_none() { + self.iter = None; + } + nth + } + + #[inline] + default fn try_rfold(&mut self, mut acc: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + if let Some(ref mut iter) = self.iter { + acc = iter.try_rfold(acc, fold)?; + self.iter = None; + } + Try::from_ok(acc) + } + + #[inline] + default fn rfold(self, mut acc: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if let Some(iter) = self.iter { + acc = iter.rfold(acc, fold); + } + acc + } + + #[inline] + default fn rfind

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + let found = self.iter.as_mut()?.rfind(predicate); + if found.is_none() { + self.iter = None; + } + found + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Fuse +where + I: ExactSizeIterator, +{ + default fn len(&self) -> usize { + self.iter.as_ref().map_or(0, I::len) + } + + default fn is_empty(&self) -> bool { + self.iter.as_ref().map_or(true, I::is_empty) + } +} + +// NOTE: for `I: FusedIterator`, we assume that the iterator is always `Some` +impl Fuse { + #[inline(always)] + fn as_inner(&self) -> &I { + match self.iter { + Some(ref iter) => iter, + // SAFETY: the specialized iterator never sets `None` + None => unsafe { intrinsics::unreachable() }, + } + } + + #[inline(always)] + fn as_inner_mut(&mut self) -> &mut I { + match self.iter { + Some(ref mut iter) => iter, + // SAFETY: the specialized iterator never sets `None` + None => unsafe { intrinsics::unreachable() }, + } + } + + #[inline(always)] + fn into_inner(self) -> I { + match self.iter { + Some(iter) => iter, + // SAFETY: the specialized iterator never sets `None` + None => unsafe { intrinsics::unreachable() }, + } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl Iterator for Fuse +where + I: FusedIterator, +{ + #[inline] + fn next(&mut self) -> Option<::Item> { + self.as_inner_mut().next() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.as_inner_mut().nth(n) + } + + #[inline] + fn last(self) -> Option { + self.into_inner().last() + } + + #[inline] + fn count(self) -> usize { + self.into_inner().count() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.as_inner().size_hint() + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.as_inner_mut().try_fold(init, fold) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.into_inner().fold(init, fold) + } + + #[inline] + fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + self.as_inner_mut().find(predicate) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl DoubleEndedIterator for Fuse +where + I: DoubleEndedIterator + FusedIterator, +{ + #[inline] + fn next_back(&mut self) -> Option<::Item> { + self.as_inner_mut().next_back() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option<::Item> { + self.as_inner_mut().nth_back(n) + } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.as_inner_mut().try_rfold(init, fold) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.into_inner().rfold(init, fold) + } + + #[inline] + fn rfind

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + self.as_inner_mut().rfind(predicate) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Fuse +where + I: ExactSizeIterator + FusedIterator, +{ + fn len(&self) -> usize { + self.as_inner().len() + } + + fn is_empty(&self) -> bool { + self.as_inner().is_empty() + } +} + +unsafe impl TrustedRandomAccess for Fuse +where + I: TrustedRandomAccess + FusedIterator, +{ + unsafe fn get_unchecked(&mut self, i: usize) -> I::Item { + self.as_inner_mut().get_unchecked(i) + } + + fn may_have_side_effect() -> bool { + I::may_have_side_effect() + } +} diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs index 6759a6b2d7349..16738543eb3af 100644 --- a/src/libcore/iter/adapters/mod.rs +++ b/src/libcore/iter/adapters/mod.rs @@ -9,11 +9,13 @@ use super::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, Tru mod chain; mod flatten; +mod fuse; mod zip; pub use self::chain::Chain; #[stable(feature = "rust1", since = "1.0.0")] pub use self::flatten::{FlatMap, Flatten}; +pub use self::fuse::Fuse; pub(crate) use self::zip::TrustedRandomAccess; pub use self::zip::Zip; @@ -2238,261 +2240,6 @@ where } } -/// An iterator that yields `None` forever after the underlying iterator -/// yields `None` once. -/// -/// This `struct` is created by the [`fuse`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`fuse`]: trait.Iterator.html#method.fuse -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Fuse { - iter: I, - done: bool, -} -impl Fuse { - pub(super) fn new(iter: I) -> Fuse { - Fuse { iter, done: false } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Fuse where I: Iterator {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Fuse -where - I: Iterator, -{ - type Item = ::Item; - - #[inline] - default fn next(&mut self) -> Option<::Item> { - if self.done { - None - } else { - let next = self.iter.next(); - self.done = next.is_none(); - next - } - } - - #[inline] - default fn nth(&mut self, n: usize) -> Option { - if self.done { - None - } else { - let nth = self.iter.nth(n); - self.done = nth.is_none(); - nth - } - } - - #[inline] - default fn last(self) -> Option { - if self.done { None } else { self.iter.last() } - } - - #[inline] - default fn count(self) -> usize { - if self.done { 0 } else { self.iter.count() } - } - - #[inline] - default fn size_hint(&self) -> (usize, Option) { - if self.done { (0, Some(0)) } else { self.iter.size_hint() } - } - - #[inline] - default fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - if self.done { - Try::from_ok(init) - } else { - let acc = self.iter.try_fold(init, fold)?; - self.done = true; - Try::from_ok(acc) - } - } - - #[inline] - default fn fold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - if self.done { init } else { self.iter.fold(init, fold) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Fuse -where - I: DoubleEndedIterator, -{ - #[inline] - default fn next_back(&mut self) -> Option<::Item> { - if self.done { - None - } else { - let next = self.iter.next_back(); - self.done = next.is_none(); - next - } - } - - #[inline] - default fn nth_back(&mut self, n: usize) -> Option<::Item> { - if self.done { - None - } else { - let nth = self.iter.nth_back(n); - self.done = nth.is_none(); - nth - } - } - - #[inline] - default fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - if self.done { - Try::from_ok(init) - } else { - let acc = self.iter.try_rfold(init, fold)?; - self.done = true; - Try::from_ok(acc) - } - } - - #[inline] - default fn rfold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - if self.done { init } else { self.iter.rfold(init, fold) } - } -} - -unsafe impl TrustedRandomAccess for Fuse -where - I: TrustedRandomAccess, -{ - unsafe fn get_unchecked(&mut self, i: usize) -> I::Item { - self.iter.get_unchecked(i) - } - - fn may_have_side_effect() -> bool { - I::may_have_side_effect() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl Iterator for Fuse -where - I: FusedIterator, -{ - #[inline] - fn next(&mut self) -> Option<::Item> { - self.iter.next() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.iter.nth(n) - } - - #[inline] - fn last(self) -> Option { - self.iter.last() - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_fold(init, fold) - } - - #[inline] - fn fold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(init, fold) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl DoubleEndedIterator for Fuse -where - I: DoubleEndedIterator + FusedIterator, -{ - #[inline] - fn next_back(&mut self) -> Option<::Item> { - self.iter.next_back() - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option<::Item> { - self.iter.nth_back(n) - } - - #[inline] - fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_rfold(init, fold) - } - - #[inline] - fn rfold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.rfold(init, fold) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Fuse -where - I: ExactSizeIterator, -{ - fn len(&self) -> usize { - self.iter.len() - } - - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - /// An iterator that calls a function with a reference to each element before /// yielding it. /// From 7ddd61b9ffb2be9d54c3db02e889ef96718cb642 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 25 Mar 2020 00:12:49 +0100 Subject: [PATCH 07/10] avoid catching InterpError --- src/librustc/mir/interpret/allocation.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 946b6add40a7e..e6b5a2c2c5921 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -372,7 +372,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; // Undef check happens *after* we established that the alignment is correct. // We must not return `Ok()` for unaligned pointers! - if self.check_defined(ptr, size).is_err() { + if self.is_defined(ptr, size).is_err() { // This inflates undefined bytes to the entire scalar, even if only a few // bytes are undefined. return Ok(ScalarMaybeUndef::Undef); @@ -557,13 +557,19 @@ impl<'tcx, Tag: Copy, Extra> Allocation { } /// Undefined bytes. -impl<'tcx, Tag, Extra> Allocation { +impl<'tcx, Tag: Copy, Extra> Allocation { + /// Checks whether the given range is entirely defined. + /// + /// Returns `Ok(())` if it's defined. Otherwise returns the index of the byte + /// at which the first undefined access begins. + fn is_defined(&self, ptr: Pointer, size: Size) -> Result<(), Size> { + self.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) + } + /// Checks that a range of bytes is defined. If not, returns the `ReadUndefBytes` /// error which will report the first byte which is undefined. - #[inline] fn check_defined(&self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { - self.undef_mask - .is_range_defined(ptr.offset, ptr.offset + size) + self.is_defined(ptr, size) .or_else(|idx| throw_ub!(InvalidUndefBytes(Some(Pointer::new(ptr.alloc_id, idx))))) } From bedc3587cee29d63d78f943dd332b1d01b16ee6d Mon Sep 17 00:00:00 2001 From: Youngsuk Kim Date: Tue, 24 Mar 2020 21:34:36 -0400 Subject: [PATCH 08/10] fix type name typo in doc comments InterpCtx => InterpCx (rustc_mir::interpret::InterpCx) --- src/librustc_mir/interpret/memory.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 110f2ffd9d78c..f01e6c2e842eb 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -473,7 +473,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } /// Gives raw access to the `Allocation`, without bounds or alignment checks. - /// Use the higher-level, `PlaceTy`- and `OpTy`-based APIs in `InterpCtx` instead! + /// Use the higher-level, `PlaceTy`- and `OpTy`-based APIs in `InterpCx` instead! pub fn get_raw( &self, id: AllocId, @@ -510,7 +510,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } /// Gives raw mutable access to the `Allocation`, without bounds or alignment checks. - /// Use the higher-level, `PlaceTy`- and `OpTy`-based APIs in `InterpCtx` instead! + /// Use the higher-level, `PlaceTy`- and `OpTy`-based APIs in `InterpCx` instead! pub fn get_raw_mut( &mut self, id: AllocId, From f8e3da5ea22268dd9f7ff61c133aca3e8c64206f Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Wed, 25 Mar 2020 16:07:36 +0100 Subject: [PATCH 09/10] run test only on 64bit --- src/test/mir-opt/inline/inline-into-box-place.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/mir-opt/inline/inline-into-box-place.rs b/src/test/mir-opt/inline/inline-into-box-place.rs index 500238de4c5ab..fcb7b4c4fe69d 100644 --- a/src/test/mir-opt/inline/inline-into-box-place.rs +++ b/src/test/mir-opt/inline/inline-into-box-place.rs @@ -1,6 +1,7 @@ // ignore-tidy-linelength // ignore-wasm32-bare compiled with panic=abort by default // compile-flags: -Z mir-opt-level=3 +// only-64bit FIXME: the mir representation of RawVec depends on ptr size #![feature(box_syntax)] fn main() { From 4f429c074b865e2ae7a4dc52eb6f2e5db9244e48 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 25 Mar 2020 11:09:00 -0700 Subject: [PATCH 10/10] impl TrustedRandomAccess for Fuse without FusedIterator --- src/libcore/iter/adapters/fuse.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libcore/iter/adapters/fuse.rs b/src/libcore/iter/adapters/fuse.rs index f5fd075662209..a60ca64ec87c8 100644 --- a/src/libcore/iter/adapters/fuse.rs +++ b/src/libcore/iter/adapters/fuse.rs @@ -326,10 +326,14 @@ where unsafe impl TrustedRandomAccess for Fuse where - I: TrustedRandomAccess + FusedIterator, + I: TrustedRandomAccess, { unsafe fn get_unchecked(&mut self, i: usize) -> I::Item { - self.as_inner_mut().get_unchecked(i) + match self.iter { + Some(ref mut iter) => iter.get_unchecked(i), + // SAFETY: the caller asserts there is an item at `i`, so we're not exhausted. + None => intrinsics::unreachable(), + } } fn may_have_side_effect() -> bool {