Skip to content

Commit 515d805

Browse files
committed
Introduce expand_weak_alias_tys
1 parent 05ce209 commit 515d805

13 files changed

+165
-41
lines changed

compiler/rustc_middle/src/ty/flags.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,10 @@ impl FlagComputation {
181181

182182
&ty::Alias(kind, data) => {
183183
self.add_flags(match kind {
184-
ty::Weak | ty::Projection => TypeFlags::HAS_TY_PROJECTION,
185-
ty::Inherent => TypeFlags::HAS_TY_INHERENT,
184+
ty::Projection => TypeFlags::HAS_TY_PROJECTION,
185+
ty::Weak => TypeFlags::HAS_TY_WEAK,
186186
ty::Opaque => TypeFlags::HAS_TY_OPAQUE,
187+
ty::Inherent => TypeFlags::HAS_TY_INHERENT,
187188
});
188189

189190
self.add_alias_ty(data);

compiler/rustc_middle/src/ty/util.rs

+61
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::ty::{GenericArgKind, GenericArgsRef};
1111
use rustc_apfloat::Float as _;
1212
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1313
use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
14+
use rustc_data_structures::stack::ensure_sufficient_stack;
1415
use rustc_errors::ErrorGuaranteed;
1516
use rustc_hir as hir;
1617
use rustc_hir::def::{CtorOf, DefKind, Res};
@@ -867,6 +868,30 @@ impl<'tcx> TyCtxt<'tcx> {
867868

868869
self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param))
869870
}
871+
872+
/// Expand any [weak alias types][weak] contained within the given `value`.
873+
///
874+
/// This should be used over other normalization routines in situations where
875+
/// it's important not to normalize other alias types and where the predicates
876+
/// on the corresponding type alias shouldn't be taken into consideration.
877+
///
878+
/// Whenever possible **prefer not to use this function**! Instead, use standard
879+
/// normalization routines or if feasible don't normalize at all.
880+
///
881+
/// This function comes in handy if you want to mimic the behavior of eager
882+
/// type alias expansion in a localized manner.
883+
///
884+
/// <div class="warning">
885+
/// This delays a bug on overflow! Therefore you need to be certain that the
886+
/// contained types get fully normalized at a later stage. Note that even on
887+
/// overflow all well-behaved weak alias types get expanded correctly, so the
888+
/// result is still useful.
889+
/// </div>
890+
///
891+
/// [weak]: ty::Weak
892+
pub fn expand_weak_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, value: T) -> T {
893+
value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 })
894+
}
870895
}
871896

872897
struct OpaqueTypeExpander<'tcx> {
@@ -1002,6 +1027,42 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
10021027
}
10031028
}
10041029

1030+
struct WeakAliasTypeExpander<'tcx> {
1031+
tcx: TyCtxt<'tcx>,
1032+
depth: usize,
1033+
}
1034+
1035+
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> {
1036+
fn interner(&self) -> TyCtxt<'tcx> {
1037+
self.tcx
1038+
}
1039+
1040+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
1041+
if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
1042+
return ty;
1043+
}
1044+
let ty::Alias(ty::Weak, alias) = ty.kind() else {
1045+
return ty.super_fold_with(self);
1046+
};
1047+
if !self.tcx.recursion_limit().value_within_limit(self.depth) {
1048+
let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type");
1049+
return Ty::new_error(self.tcx, guar);
1050+
}
1051+
1052+
self.depth += 1;
1053+
ensure_sufficient_stack(|| {
1054+
self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self)
1055+
})
1056+
}
1057+
1058+
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
1059+
if !ct.ty().has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
1060+
return ct;
1061+
}
1062+
ct.super_fold_with(self)
1063+
}
1064+
}
1065+
10051066
impl<'tcx> Ty<'tcx> {
10061067
/// Returns the `Size` for primitive types (bool, uint, int, char, float).
10071068
pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size {

compiler/rustc_middle/src/ty/visit.rs

+13-12
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,13 @@ impl<'tcx> TyCtxt<'tcx> {
131131
fn collect_late_bound_regions<T>(
132132
self,
133133
value: &Binder<'tcx, T>,
134-
just_constraint: bool,
134+
just_constrained: bool,
135135
) -> FxHashSet<ty::BoundRegionKind>
136136
where
137137
T: TypeVisitable<TyCtxt<'tcx>>,
138138
{
139-
let mut collector = LateBoundRegionsCollector::new(just_constraint);
139+
let mut collector = LateBoundRegionsCollector::new(self, just_constrained);
140+
let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value };
140141
let result = value.as_ref().skip_binder().visit_with(&mut collector);
141142
assert!(result.is_continue()); // should never have stopped early
142143
collector.regions
@@ -258,11 +259,7 @@ struct LateBoundRegionsCollector {
258259

259260
impl LateBoundRegionsCollector {
260261
fn new(just_constrained: bool) -> Self {
261-
LateBoundRegionsCollector {
262-
current_index: ty::INNERMOST,
263-
regions: Default::default(),
264-
just_constrained,
265-
}
262+
Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained }
266263
}
267264
}
268265

@@ -278,12 +275,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
278275
}
279276

280277
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
281-
// if we are only looking for "constrained" region, we have to
282-
// ignore the inputs to a projection, as they may not appear
283-
// in the normalized form
284278
if self.just_constrained {
285-
if let ty::Alias(..) = t.kind() {
286-
return ControlFlow::Continue(());
279+
match t.kind() {
280+
// If we are only looking for "constrained" regions, we have to ignore the
281+
// inputs to a projection as they may not appear in the normalized form.
282+
ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => {
283+
return ControlFlow::Continue(());
284+
}
285+
// All weak alias types should've been expanded beforehand.
286+
ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"),
287+
_ => {}
287288
}
288289
}
289290

compiler/rustc_trait_selection/src/traits/normalize.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,17 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
101101
value: &T,
102102
reveal: Reveal,
103103
) -> bool {
104+
let mut flags = ty::TypeFlags::HAS_TY_PROJECTION
105+
| ty::TypeFlags::HAS_TY_WEAK
106+
| ty::TypeFlags::HAS_TY_INHERENT
107+
| ty::TypeFlags::HAS_CT_PROJECTION;
108+
104109
match reveal {
105-
Reveal::UserFacing => value.has_type_flags(
106-
ty::TypeFlags::HAS_TY_PROJECTION
107-
| ty::TypeFlags::HAS_TY_INHERENT
108-
| ty::TypeFlags::HAS_CT_PROJECTION,
109-
),
110-
Reveal::All => value.has_type_flags(
111-
ty::TypeFlags::HAS_TY_PROJECTION
112-
| ty::TypeFlags::HAS_TY_INHERENT
113-
| ty::TypeFlags::HAS_TY_OPAQUE
114-
| ty::TypeFlags::HAS_CT_PROJECTION,
115-
),
110+
Reveal::UserFacing => {}
111+
Reveal::All => flags |= ty::TypeFlags::HAS_TY_OPAQUE,
116112
}
113+
114+
value.has_type_flags(flags)
117115
}
118116

119117
struct AssocTypeNormalizer<'a, 'b, 'tcx> {

compiler/rustc_type_ir/src/flags.rs

+17-14
Original file line numberDiff line numberDiff line change
@@ -69,55 +69,58 @@ bitflags! {
6969

7070
/// Does this have `Projection`?
7171
const HAS_TY_PROJECTION = 1 << 10;
72-
/// Does this have `Inherent`?
73-
const HAS_TY_INHERENT = 1 << 11;
72+
/// Does this have `Weak`?
73+
const HAS_TY_WEAK = 1 << 11;
7474
/// Does this have `Opaque`?
7575
const HAS_TY_OPAQUE = 1 << 12;
76+
/// Does this have `Inherent`?
77+
const HAS_TY_INHERENT = 1 << 13;
7678
/// Does this have `ConstKind::Unevaluated`?
77-
const HAS_CT_PROJECTION = 1 << 13;
79+
const HAS_CT_PROJECTION = 1 << 14;
7880

7981
/// Could this type be normalized further?
8082
const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits()
83+
| TypeFlags::HAS_TY_WEAK.bits()
8184
| TypeFlags::HAS_TY_OPAQUE.bits()
8285
| TypeFlags::HAS_TY_INHERENT.bits()
8386
| TypeFlags::HAS_CT_PROJECTION.bits();
8487

8588
/// Is an error type/const reachable?
86-
const HAS_ERROR = 1 << 14;
89+
const HAS_ERROR = 1 << 15;
8790

8891
/// Does this have any region that "appears free" in the type?
8992
/// Basically anything but `ReBound` and `ReErased`.
90-
const HAS_FREE_REGIONS = 1 << 15;
93+
const HAS_FREE_REGIONS = 1 << 16;
9194

9295
/// Does this have any `ReBound` regions?
93-
const HAS_RE_BOUND = 1 << 16;
96+
const HAS_RE_BOUND = 1 << 17;
9497
/// Does this have any `Bound` types?
95-
const HAS_TY_BOUND = 1 << 17;
98+
const HAS_TY_BOUND = 1 << 18;
9699
/// Does this have any `ConstKind::Bound` consts?
97-
const HAS_CT_BOUND = 1 << 18;
100+
const HAS_CT_BOUND = 1 << 19;
98101
/// Does this have any bound variables?
99102
/// Used to check if a global bound is safe to evaluate.
100103
const HAS_BOUND_VARS = TypeFlags::HAS_RE_BOUND.bits()
101104
| TypeFlags::HAS_TY_BOUND.bits()
102105
| TypeFlags::HAS_CT_BOUND.bits();
103106

104107
/// Does this have any `ReErased` regions?
105-
const HAS_RE_ERASED = 1 << 19;
108+
const HAS_RE_ERASED = 1 << 20;
106109

107110
/// Does this value have parameters/placeholders/inference variables which could be
108111
/// replaced later, in a way that would change the results of `impl` specialization?
109-
const STILL_FURTHER_SPECIALIZABLE = 1 << 20;
112+
const STILL_FURTHER_SPECIALIZABLE = 1 << 21;
110113

111114
/// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`?
112-
const HAS_TY_FRESH = 1 << 21;
115+
const HAS_TY_FRESH = 1 << 22;
113116

114117
/// Does this value have `InferConst::Fresh`?
115-
const HAS_CT_FRESH = 1 << 22;
118+
const HAS_CT_FRESH = 1 << 23;
116119

117120
/// Does this have `Coroutine` or `CoroutineWitness`?
118-
const HAS_TY_COROUTINE = 1 << 23;
121+
const HAS_TY_COROUTINE = 1 << 24;
119122

120123
/// Does this have any binders with bound vars (e.g. that need to be anonymized)?
121-
const HAS_BINDER_VARS = 1 << 24;
124+
const HAS_BINDER_VARS = 1 << 25;
122125
}
123126
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ check-pass
2+
// Weak alias types constrain late-bound regions if their normalized form constrains them.
3+
4+
#![feature(lazy_type_alias)]
5+
#![allow(incomplete_features)]
6+
7+
type Ref<'a> = &'a ();
8+
9+
type FnPtr = for<'a> fn(Ref<'a>) -> &'a (); // OK
10+
type DynCl = dyn for<'a> Fn(Ref<'a>) -> &'a (); // OK
11+
12+
fn map0(_: Ref) -> Ref { &() } // OK
13+
fn map1(_: Ref<'_>) -> Ref<'_> { &() } // OK
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Weak alias types only constrain late-bound regions if their normalized form constrains them.
2+
3+
#![feature(lazy_type_alias)]
4+
#![allow(incomplete_features)]
5+
6+
type NotInjective<'a> = <() as Discard>::Output<'a>;
7+
8+
type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a ();
9+
//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types
10+
type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>;
11+
//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types
12+
type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a ();
13+
//~^ ERROR references lifetime `'a`, which does not appear in the trait input types
14+
15+
trait Discard { type Output<'a>; }
16+
impl Discard for () { type Output<'_a> = (); }
17+
18+
type NotInjectiveEither<'a, Linchpin> = Linchpin
19+
where
20+
Linchpin: Fn() -> &'a ();
21+
22+
23+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types
2+
--> $DIR/unconstrained-late-bound-regions.rs:8:47
3+
|
4+
LL | type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a ();
5+
| ^^^^^^
6+
7+
error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types
8+
--> $DIR/unconstrained-late-bound-regions.rs:10:57
9+
|
10+
LL | type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>;
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types
14+
--> $DIR/unconstrained-late-bound-regions.rs:12:50
15+
|
16+
LL | type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a ();
17+
| ^^^^^^
18+
19+
error: aborting due to 3 previous errors
20+
21+
Some errors have detailed explanations: E0581, E0582.
22+
For more information about an error, try `rustc --explain E0581`.

tests/ui/lazy-type-alias/unconstrained-param-due-to-overflow.stderr renamed to tests/ui/lazy-type-alias/unconstrained-params-in-impl-due-to-overflow.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
2-
--> $DIR/unconstrained-param-due-to-overflow.rs:4:6
2+
--> $DIR/unconstrained-params-in-impl-due-to-overflow.rs:4:6
33
|
44
LL | impl<T> Loop<T> {}
55
| ^ unconstrained type parameter

tests/ui/lazy-type-alias/unconstrained-params.stderr renamed to tests/ui/lazy-type-alias/unconstrained-params-in-impl.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
2-
--> $DIR/unconstrained-params.rs:4:6
2+
--> $DIR/unconstrained-params-in-impl.rs:4:6
33
|
44
LL | impl<T> NotInjective<T> {}
55
| ^ unconstrained type parameter

0 commit comments

Comments
 (0)