diff options
13 files changed, 165 insertions, 41 deletions
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 0f4b5fe228c..18cf5445e56 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -181,9 +181,10 @@ impl FlagComputation { &ty::Alias(kind, data) => { self.add_flags(match kind { - ty::Weak | ty::Projection => TypeFlags::HAS_TY_PROJECTION, - ty::Inherent => TypeFlags::HAS_TY_INHERENT, + ty::Projection => TypeFlags::HAS_TY_PROJECTION, + ty::Weak => TypeFlags::HAS_TY_WEAK, ty::Opaque => TypeFlags::HAS_TY_OPAQUE, + ty::Inherent => TypeFlags::HAS_TY_INHERENT, }); self.add_alias_ty(data); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 3f539945841..ff9d1ed6ae9 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -11,6 +11,7 @@ use crate::ty::{GenericArgKind, GenericArgsRef}; use rustc_apfloat::Float as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -867,6 +868,30 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param)) } + + /// Expand any [weak alias types][weak] contained within the given `value`. + /// + /// This should be used over other normalization routines in situations where + /// it's important not to normalize other alias types and where the predicates + /// on the corresponding type alias shouldn't be taken into consideration. + /// + /// Whenever possible **prefer not to use this function**! Instead, use standard + /// normalization routines or if feasible don't normalize at all. + /// + /// This function comes in handy if you want to mimic the behavior of eager + /// type alias expansion in a localized manner. + /// + /// <div class="warning"> + /// This delays a bug on overflow! Therefore you need to be certain that the + /// contained types get fully normalized at a later stage. Note that even on + /// overflow all well-behaved weak alias types get expanded correctly, so the + /// result is still useful. + /// </div> + /// + /// [weak]: ty::Weak + pub fn expand_weak_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, value: T) -> T { + value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 }) + } } struct OpaqueTypeExpander<'tcx> { @@ -1002,6 +1027,42 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> { } } +struct WeakAliasTypeExpander<'tcx> { + tcx: TyCtxt<'tcx>, + depth: usize, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { + return ty; + } + let ty::Alias(ty::Weak, alias) = ty.kind() else { + return ty.super_fold_with(self); + }; + if !self.tcx.recursion_limit().value_within_limit(self.depth) { + let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type"); + return Ty::new_error(self.tcx, guar); + } + + self.depth += 1; + ensure_sufficient_stack(|| { + self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self) + }) + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if !ct.ty().has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { + return ct; + } + ct.super_fold_with(self) + } +} + impl<'tcx> Ty<'tcx> { /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size { diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 59292a281ed..1de2ceecae7 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -131,12 +131,13 @@ impl<'tcx> TyCtxt<'tcx> { fn collect_late_bound_regions<T>( self, value: &Binder<'tcx, T>, - just_constraint: bool, + just_constrained: bool, ) -> FxHashSet<ty::BoundRegionKind> where T: TypeVisitable<TyCtxt<'tcx>>, { - let mut collector = LateBoundRegionsCollector::new(just_constraint); + let mut collector = LateBoundRegionsCollector::new(self, just_constrained); + let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value }; let result = value.as_ref().skip_binder().visit_with(&mut collector); assert!(result.is_continue()); // should never have stopped early collector.regions @@ -258,11 +259,7 @@ struct LateBoundRegionsCollector { impl LateBoundRegionsCollector { fn new(just_constrained: bool) -> Self { - LateBoundRegionsCollector { - current_index: ty::INNERMOST, - regions: Default::default(), - just_constrained, - } + Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained } } } @@ -278,12 +275,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector { } fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - // if we are only looking for "constrained" region, we have to - // ignore the inputs to a projection, as they may not appear - // in the normalized form if self.just_constrained { - if let ty::Alias(..) = t.kind() { - return ControlFlow::Continue(()); + match t.kind() { + // If we are only looking for "constrained" regions, we have to ignore the + // inputs to a projection as they may not appear in the normalized form. + ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => { + return ControlFlow::Continue(()); + } + // All weak alias types should've been expanded beforehand. + ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"), + _ => {} } } diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index f37d917b4a0..429e5a5d7a4 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -101,19 +101,17 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( value: &T, reveal: Reveal, ) -> bool { + let mut flags = ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_WEAK + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_CT_PROJECTION; + match reveal { - Reveal::UserFacing => value.has_type_flags( - ty::TypeFlags::HAS_TY_PROJECTION - | ty::TypeFlags::HAS_TY_INHERENT - | ty::TypeFlags::HAS_CT_PROJECTION, - ), - Reveal::All => value.has_type_flags( - ty::TypeFlags::HAS_TY_PROJECTION - | ty::TypeFlags::HAS_TY_INHERENT - | ty::TypeFlags::HAS_TY_OPAQUE - | ty::TypeFlags::HAS_CT_PROJECTION, - ), + Reveal::UserFacing => {} + Reveal::All => flags |= ty::TypeFlags::HAS_TY_OPAQUE, } + + value.has_type_flags(flags) } struct AssocTypeNormalizer<'a, 'b, 'tcx> { diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 1da26cfc242..b38ef2ad84d 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -69,32 +69,35 @@ bitflags! { /// Does this have `Projection`? const HAS_TY_PROJECTION = 1 << 10; - /// Does this have `Inherent`? - const HAS_TY_INHERENT = 1 << 11; + /// Does this have `Weak`? + const HAS_TY_WEAK = 1 << 11; /// Does this have `Opaque`? const HAS_TY_OPAQUE = 1 << 12; + /// Does this have `Inherent`? + const HAS_TY_INHERENT = 1 << 13; /// Does this have `ConstKind::Unevaluated`? - const HAS_CT_PROJECTION = 1 << 13; + const HAS_CT_PROJECTION = 1 << 14; /// Could this type be normalized further? const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits() + | TypeFlags::HAS_TY_WEAK.bits() | TypeFlags::HAS_TY_OPAQUE.bits() | TypeFlags::HAS_TY_INHERENT.bits() | TypeFlags::HAS_CT_PROJECTION.bits(); /// Is an error type/const reachable? - const HAS_ERROR = 1 << 14; + const HAS_ERROR = 1 << 15; /// Does this have any region that "appears free" in the type? /// Basically anything but `ReBound` and `ReErased`. - const HAS_FREE_REGIONS = 1 << 15; + const HAS_FREE_REGIONS = 1 << 16; /// Does this have any `ReBound` regions? - const HAS_RE_BOUND = 1 << 16; + const HAS_RE_BOUND = 1 << 17; /// Does this have any `Bound` types? - const HAS_TY_BOUND = 1 << 17; + const HAS_TY_BOUND = 1 << 18; /// Does this have any `ConstKind::Bound` consts? - const HAS_CT_BOUND = 1 << 18; + const HAS_CT_BOUND = 1 << 19; /// Does this have any bound variables? /// Used to check if a global bound is safe to evaluate. const HAS_BOUND_VARS = TypeFlags::HAS_RE_BOUND.bits() @@ -102,22 +105,22 @@ bitflags! { | TypeFlags::HAS_CT_BOUND.bits(); /// Does this have any `ReErased` regions? - const HAS_RE_ERASED = 1 << 19; + const HAS_RE_ERASED = 1 << 20; /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 20; + const STILL_FURTHER_SPECIALIZABLE = 1 << 21; /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 21; + const HAS_TY_FRESH = 1 << 22; /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 22; + const HAS_CT_FRESH = 1 << 23; /// Does this have `Coroutine` or `CoroutineWitness`? - const HAS_TY_COROUTINE = 1 << 23; + const HAS_TY_COROUTINE = 1 << 24; /// Does this have any binders with bound vars (e.g. that need to be anonymized)? - const HAS_BINDER_VARS = 1 << 24; + const HAS_BINDER_VARS = 1 << 25; } } diff --git a/tests/ui/lazy-type-alias/constrained-late-bound-regions.rs b/tests/ui/lazy-type-alias/constrained-late-bound-regions.rs new file mode 100644 index 00000000000..e759e72d745 --- /dev/null +++ b/tests/ui/lazy-type-alias/constrained-late-bound-regions.rs @@ -0,0 +1,15 @@ +//@ check-pass +// Weak alias types constrain late-bound regions if their normalized form constrains them. + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Ref<'a> = &'a (); + +type FnPtr = for<'a> fn(Ref<'a>) -> &'a (); // OK +type DynCl = dyn for<'a> Fn(Ref<'a>) -> &'a (); // OK + +fn map0(_: Ref) -> Ref { &() } // OK +fn map1(_: Ref<'_>) -> Ref<'_> { &() } // OK + +fn main() {} diff --git a/tests/ui/lazy-type-alias/constrained-params.rs b/tests/ui/lazy-type-alias/constrained-params-in-impl.rs index 59693dd5461..59693dd5461 100644 --- a/tests/ui/lazy-type-alias/constrained-params.rs +++ b/tests/ui/lazy-type-alias/constrained-params-in-impl.rs diff --git a/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.rs b/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.rs new file mode 100644 index 00000000000..844570e22d2 --- /dev/null +++ b/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.rs @@ -0,0 +1,23 @@ +// Weak alias types only constrain late-bound regions if their normalized form constrains them. + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type NotInjective<'a> = <() as Discard>::Output<'a>; + +type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a (); +//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types +type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>; +//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types +type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a (); +//~^ ERROR references lifetime `'a`, which does not appear in the trait input types + +trait Discard { type Output<'a>; } +impl Discard for () { type Output<'_a> = (); } + +type NotInjectiveEither<'a, Linchpin> = Linchpin +where + Linchpin: Fn() -> &'a (); + + +fn main() {} diff --git a/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.stderr b/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.stderr new file mode 100644 index 00000000000..241c7761c60 --- /dev/null +++ b/tests/ui/lazy-type-alias/unconstrained-late-bound-regions.stderr @@ -0,0 +1,22 @@ +error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types + --> $DIR/unconstrained-late-bound-regions.rs:8:47 + | +LL | type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a (); + | ^^^^^^ + +error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types + --> $DIR/unconstrained-late-bound-regions.rs:10:57 + | +LL | type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/unconstrained-late-bound-regions.rs:12:50 + | +LL | type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a (); + | ^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0581, E0582. +For more information about an error, try `rustc --explain E0581`. diff --git a/tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.rs b/tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.rs index eceefa719ec..eceefa719ec 100644 --- a/tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.rs +++ b/tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.rs diff --git a/tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.stderr b/tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.stderr index 9af6f5dda0b..b65c84226ce 100644 --- a/tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.stderr +++ b/tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.stderr @@ -1,5 +1,5 @@ error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/unconstrained-param-due-to-overflow.rs:4:6 + --> $DIR/unconstrained-params-in-impl-due-to-overflow.rs:4:6 | LL | impl<T> Loop<T> {} | ^ unconstrained type parameter diff --git a/tests/ui/lazy-type-alias/unconstrained-params.rs b/tests/ui/lazy-type-alias/unconstrained-params-in-impl.rs index d58938b3070..d58938b3070 100644 --- a/tests/ui/lazy-type-alias/unconstrained-params.rs +++ b/tests/ui/lazy-type-alias/unconstrained-params-in-impl.rs diff --git a/tests/ui/lazy-type-alias/unconstrained-params.stderr b/tests/ui/lazy-type-alias/unconstrained-params-in-impl.stderr index 3c52a06c319..2419c78cba8 100644 --- a/tests/ui/lazy-type-alias/unconstrained-params.stderr +++ b/tests/ui/lazy-type-alias/unconstrained-params-in-impl.stderr @@ -1,5 +1,5 @@ error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/unconstrained-params.rs:4:6 + --> $DIR/unconstrained-params-in-impl.rs:4:6 | LL | impl<T> NotInjective<T> {} | ^ unconstrained type parameter |
