Skip to content

anonymize all bound vars, not just regions #99730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 30, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions compiler/rustc_infer/src/infer/canonical/substitute.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html

use crate::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::fold::{FnMutDelegate, TypeFoldable};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, TyCtxt};

@@ -71,21 +71,21 @@ where
if var_values.var_values.is_empty() {
value
} else {
let fld_r = |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() {
GenericArgKind::Lifetime(l) => l,
r => bug!("{:?} is a region but value is {:?}", br, r),
let delegate = FnMutDelegate {
regions: |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() {
GenericArgKind::Lifetime(l) => l,
r => bug!("{:?} is a region but value is {:?}", br, r),
},
types: |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() {
GenericArgKind::Type(ty) => ty,
r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
},
consts: |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() {
GenericArgKind::Const(ct) => ct,
c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
},
};

let fld_t = |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() {
GenericArgKind::Type(ty) => ty,
r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
};

let fld_c = |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() {
GenericArgKind::Const(ct) => ct,
c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
};

tcx.replace_escaping_bound_vars_uncached(value, fld_r, fld_t, fld_c)
tcx.replace_escaping_bound_vars_uncached(value, delegate)
}
}
45 changes: 23 additions & 22 deletions compiler/rustc_infer/src/infer/higher_ranked/mod.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
use super::combine::CombineFields;
use super::{HigherRankedType, InferCtxt};
use crate::infer::CombinedSnapshot;
use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Binder, TypeFoldable};

@@ -79,31 +80,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

let next_universe = self.create_next_universe();

let fld_r = |br: ty::BoundRegion| {
self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion {
universe: next_universe,
name: br.kind,
}))
};

let fld_t = |bound_ty: ty::BoundTy| {
self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
universe: next_universe,
name: bound_ty.var,
}))
};

let fld_c = |bound_var: ty::BoundVar, ty| {
self.tcx.mk_const(ty::ConstS {
kind: ty::ConstKind::Placeholder(ty::PlaceholderConst {
let delegate = FnMutDelegate {
regions: |br: ty::BoundRegion| {
self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion {
universe: next_universe,
name: br.kind,
}))
},
types: |bound_ty: ty::BoundTy| {
self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
universe: next_universe,
name: ty::BoundConst { var: bound_var, ty },
}),
ty,
})
name: bound_ty.var,
}))
},
consts: |bound_var: ty::BoundVar, ty| {
self.tcx.mk_const(ty::ConstS {
kind: ty::ConstKind::Placeholder(ty::PlaceholderConst {
universe: next_universe,
name: ty::BoundConst { var: bound_var, ty },
}),
ty,
})
},
};

let result = self.tcx.replace_bound_vars_uncached(binder, fld_r, fld_t, fld_c);
let result = self.tcx.replace_bound_vars_uncached(binder, delegate);
debug!(?next_universe, ?result);
result
}
75 changes: 50 additions & 25 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
use rustc_middle::traits::select;
use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::BoundVarReplacerDelegate;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::relate::RelateResult;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef};
@@ -1564,32 +1565,56 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
return inner;
}

let mut region_map = FxHashMap::default();
let fld_r = |br: ty::BoundRegion| {
*region_map
.entry(br)
.or_insert_with(|| self.next_region_var(LateBoundRegion(span, br.kind, lbrct)))
};
struct ToFreshVars<'a, 'tcx> {
infcx: &'a InferCtxt<'a, 'tcx>,
span: Span,
lbrct: LateBoundRegionConversionTime,
map: FxHashMap<ty::BoundVar, ty::GenericArg<'tcx>>,
}

let mut ty_map = FxHashMap::default();
let fld_t = |bt: ty::BoundTy| {
*ty_map.entry(bt).or_insert_with(|| {
self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span,
})
})
};
let mut ct_map = FxHashMap::default();
let fld_c = |bc: ty::BoundVar, ty| {
*ct_map.entry(bc).or_insert_with(|| {
self.next_const_var(
ty,
ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span },
)
})
};
self.tcx.replace_bound_vars_uncached(value, fld_r, fld_t, fld_c)
impl<'tcx> BoundVarReplacerDelegate<'tcx> for ToFreshVars<'_, 'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
self.map
.entry(br.var)
.or_insert_with(|| {
self.infcx
.next_region_var(LateBoundRegion(self.span, br.kind, self.lbrct))
.into()
})
.expect_region()
}
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
self.map
.entry(bt.var)
.or_insert_with(|| {
self.infcx
.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span: self.span,
})
.into()
})
.expect_ty()
}
fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> {
self.map
.entry(bv)
.or_insert_with(|| {
self.infcx
.next_const_var(
ty,
ConstVariableOrigin {
kind: ConstVariableOriginKind::MiscVariable,
span: self.span,
},
)
.into()
})
.expect_const()
}
}
let delegate = ToFreshVars { infcx: self, span, lbrct, map: Default::default() };
self.tcx.replace_bound_vars_uncached(value, delegate)
}

/// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method.
4 changes: 2 additions & 2 deletions compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ pub fn anonymize_predicate<'tcx>(
tcx: TyCtxt<'tcx>,
pred: ty::Predicate<'tcx>,
) -> ty::Predicate<'tcx> {
let new = tcx.anonymize_late_bound_regions(pred.kind());
let new = tcx.anonymize_bound_vars(pred.kind());
tcx.reuse_or_mk_predicate(pred, new)
}

@@ -334,7 +334,7 @@ pub fn transitive_bounds_that_define_assoc_type<'tcx>(

std::iter::from_fn(move || {
while let Some(trait_ref) = stack.pop() {
let anon_trait_ref = tcx.anonymize_late_bound_regions(trait_ref);
let anon_trait_ref = tcx.anonymize_bound_vars(trait_ref);
if visited.insert(anon_trait_ref) {
let super_predicates = tcx.super_predicates_that_define_assoc_type((
trait_ref.def_id(),
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/erase_regions.rs
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ impl<'tcx> TypeFolder<'tcx> for RegionEraserVisitor<'tcx> {
where
T: TypeFoldable<'tcx>,
{
let u = self.tcx.anonymize_late_bound_regions(t);
let u = self.tcx.anonymize_bound_vars(t);
u.super_fold_with(self)
}

193 changes: 122 additions & 71 deletions compiler/rustc_middle/src/ty/fold.rs
Original file line number Diff line number Diff line change
@@ -44,7 +44,8 @@
//! - u.fold_with(folder)
//! ```
use crate::mir;
use crate::ty::{self, Binder, Ty, TyCtxt, TypeVisitable};
use crate::ty::{self, Binder, BoundTy, Ty, TyCtxt, TypeVisitable};
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::DefId;

use std::collections::BTreeMap;
@@ -370,31 +371,55 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> {
///////////////////////////////////////////////////////////////////////////
// Bound vars replacer

pub trait BoundVarReplacerDelegate<'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>;
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>;
fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx>;
}

pub struct FnMutDelegate<R, T, C> {
pub regions: R,
pub types: T,
pub consts: C,
}
impl<'tcx, R, T, C> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<R, T, C>
where
R: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
T: FnMut(ty::BoundTy) -> Ty<'tcx>,
C: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>,
{
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
(self.regions)(br)
}
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
(self.types)(bt)
}
fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> {
(self.consts)(bv, ty)
}
}

/// Replaces the escaping bound vars (late bound regions or bound types) in a type.
struct BoundVarReplacer<'a, 'tcx> {
struct BoundVarReplacer<'tcx, D> {
tcx: TyCtxt<'tcx>,

/// As with `RegionFolder`, represents the index of a binder *just outside*
/// the ones we have visited.
current_index: ty::DebruijnIndex,

fld_r: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a),
fld_t: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a),
fld_c: &'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a),
delegate: D,
}

impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> {
fn new(
tcx: TyCtxt<'tcx>,
fld_r: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a),
fld_t: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a),
fld_c: &'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a),
) -> Self {
BoundVarReplacer { tcx, current_index: ty::INNERMOST, fld_r, fld_t, fld_c }
impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> {
fn new(tcx: TyCtxt<'tcx>, delegate: D) -> Self {
BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate }
}
}

impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> {
impl<'tcx, D> TypeFolder<'tcx> for BoundVarReplacer<'tcx, D>
where
D: BoundVarReplacerDelegate<'tcx>,
{
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
self.tcx
}
@@ -412,7 +437,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> {
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match *t.kind() {
ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
let ty = (self.fld_t)(bound_ty);
let ty = self.delegate.replace_ty(bound_ty);
ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32())
}
_ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
@@ -423,14 +448,14 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> {
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
ty::ReLateBound(debruijn, br) if debruijn == self.current_index => {
let region = (self.fld_r)(br);
let region = self.delegate.replace_region(br);
if let ty::ReLateBound(debruijn1, br) = *region {
// If the callback returns a late-bound region,
// that region should always use the INNERMOST
// debruijn index. Then we adjust it to the
// correct depth.
assert_eq!(debruijn1, ty::INNERMOST);
self.tcx.mk_region(ty::ReLateBound(debruijn, br))
self.tcx.reuse_or_mk_region(region, ty::ReLateBound(debruijn, br))
} else {
region
}
@@ -442,7 +467,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> {
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
match ct.kind() {
ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
let ct = (self.fld_c)(bound_const, ct.ty());
let ct = self.delegate.replace_const(bound_const, ct.ty());
ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32())
}
_ => ct.super_fold_with(self),
@@ -486,64 +511,51 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn replace_late_bound_regions_uncached<T, F>(
self,
value: Binder<'tcx, T>,
mut fld_r: F,
replace_regions: F,
) -> T
where
F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
T: TypeFoldable<'tcx>,
{
let mut fld_t = |b| bug!("unexpected bound ty in binder: {b:?}");
let mut fld_c = |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}");
let value = value.skip_binder();
if !value.has_escaping_bound_vars() {
value
} else {
let mut replacer = BoundVarReplacer::new(self, &mut fld_r, &mut fld_t, &mut fld_c);
let delegate = FnMutDelegate {
regions: replace_regions,
types: |b| bug!("unexpected bound ty in binder: {b:?}"),
consts: |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"),
};
let mut replacer = BoundVarReplacer::new(self, delegate);
value.fold_with(&mut replacer)
}
}

/// Replaces all escaping bound vars. The `fld_r` closure replaces escaping
/// bound regions; the `fld_t` closure replaces escaping bound types and the `fld_c`
/// closure replaces escaping bound consts.
pub fn replace_escaping_bound_vars_uncached<T, F, G, H>(
pub fn replace_escaping_bound_vars_uncached<T: TypeFoldable<'tcx>>(
self,
value: T,
mut fld_r: F,
mut fld_t: G,
mut fld_c: H,
) -> T
where
F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
G: FnMut(ty::BoundTy) -> Ty<'tcx>,
H: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>,
T: TypeFoldable<'tcx>,
{
delegate: impl BoundVarReplacerDelegate<'tcx>,
) -> T {
if !value.has_escaping_bound_vars() {
value
} else {
let mut replacer = BoundVarReplacer::new(self, &mut fld_r, &mut fld_t, &mut fld_c);
let mut replacer = BoundVarReplacer::new(self, delegate);
value.fold_with(&mut replacer)
}
}

/// Replaces all types or regions bound by the given `Binder`. The `fld_r`
/// closure replaces bound regions, the `fld_t` closure replaces bound
/// types, and `fld_c` replaces bound constants.
pub fn replace_bound_vars_uncached<T, F, G, H>(
pub fn replace_bound_vars_uncached<T: TypeFoldable<'tcx>>(
self,
value: Binder<'tcx, T>,
fld_r: F,
fld_t: G,
fld_c: H,
) -> T
where
F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
G: FnMut(ty::BoundTy) -> Ty<'tcx>,
H: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>,
T: TypeFoldable<'tcx>,
{
self.replace_escaping_bound_vars_uncached(value.skip_binder(), fld_r, fld_t, fld_c)
delegate: impl BoundVarReplacerDelegate<'tcx>,
) -> T {
self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate)
}

/// Replaces any late-bound regions bound in `value` with
@@ -568,34 +580,28 @@ impl<'tcx> TyCtxt<'tcx> {
where
T: TypeFoldable<'tcx>,
{
let shift_bv = |bv: ty::BoundVar| ty::BoundVar::from_usize(bv.as_usize() + bound_vars);
self.replace_escaping_bound_vars_uncached(
value,
|r| {
self.mk_region(ty::ReLateBound(
ty::INNERMOST,
ty::BoundRegion {
var: ty::BoundVar::from_usize(r.var.as_usize() + bound_vars),
kind: r.kind,
},
))
},
|t| {
self.mk_ty(ty::Bound(
ty::INNERMOST,
ty::BoundTy {
var: ty::BoundVar::from_usize(t.var.as_usize() + bound_vars),
kind: t.kind,
},
))
},
|c, ty| {
self.mk_const(ty::ConstS {
kind: ty::ConstKind::Bound(
FnMutDelegate {
regions: |r: ty::BoundRegion| {
self.mk_region(ty::ReLateBound(
ty::INNERMOST,
ty::BoundVar::from_usize(c.as_usize() + bound_vars),
),
ty,
})
ty::BoundRegion { var: shift_bv(r.var), kind: r.kind },
))
},
types: |t: ty::BoundTy| {
self.mk_ty(ty::Bound(
ty::INNERMOST,
ty::BoundTy { var: shift_bv(t.var), kind: t.kind },
))
},
consts: |c, ty: Ty<'tcx>| {
self.mk_const(ty::ConstS {
kind: ty::ConstKind::Bound(ty::INNERMOST, shift_bv(c)),
ty,
})
},
},
)
}
@@ -638,6 +644,51 @@ impl<'tcx> TyCtxt<'tcx> {
);
Binder::bind_with_vars(inner, bound_vars)
}

/// Anonymize all bound variables in `value`, this is mostly used to improve caching.
pub fn anonymize_bound_vars<T>(self, value: Binder<'tcx, T>) -> Binder<'tcx, T>
where
T: TypeFoldable<'tcx>,
{
struct Anonymize<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind>,
}
impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
let entry = self.map.entry(br.var);
let index = entry.index();
let var = ty::BoundVar::from_usize(index);
let kind = entry
.or_insert_with(|| ty::BoundVariableKind::Region(ty::BrAnon(index as u32)))
.expect_region();
let br = ty::BoundRegion { var, kind };
self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br))
}
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
let entry = self.map.entry(bt.var);
let index = entry.index();
let var = ty::BoundVar::from_usize(index);
let kind = entry
.or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon))
.expect_ty();
self.tcx.mk_ty(ty::Bound(ty::INNERMOST, BoundTy { var, kind }))
}
fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> {
let entry = self.map.entry(bv);
let index = entry.index();
let var = ty::BoundVar::from_usize(index);
let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
self.tcx.mk_const(ty::ConstS { ty, kind: ty::ConstKind::Bound(ty::INNERMOST, var) })
}
}

let mut map = Default::default();
let delegate = Anonymize { tcx: self, map: &mut map };
let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate);
let bound_vars = self.mk_bound_variable_kinds(map.into_values());
Binder::bind_with_vars(inner, bound_vars)
}
}

///////////////////////////////////////////////////////////////////////////
23 changes: 23 additions & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
@@ -976,6 +976,29 @@ pub enum BoundVariableKind {
Const,
}

impl BoundVariableKind {
pub fn expect_region(self) -> BoundRegionKind {
match self {
BoundVariableKind::Region(lt) => lt,
_ => bug!("expected a region, but found another kind"),
}
}

pub fn expect_ty(self) -> BoundTyKind {
match self {
BoundVariableKind::Ty(ty) => ty,
_ => bug!("expected a type, but found another kind"),
}
}

pub fn expect_const(self) {
match self {
BoundVariableKind::Const => (),
_ => bug!("expected a const, but found another kind"),
}
}
}

/// Binder is a binder for higher-ranked lifetimes or types. It is part of the
/// compiler's representation for things like `for<'a> Fn(&'a isize)`
/// (which would be represented by the type `PolyTraitRef ==
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/ty/subst.rs
Original file line number Diff line number Diff line change
@@ -164,6 +164,14 @@ impl<'tcx> GenericArg<'tcx> {
}
}

/// Unpack the `GenericArg` as a region when it is known certainly to be a region.
pub fn expect_region(self) -> ty::Region<'tcx> {
match self.unpack() {
GenericArgKind::Lifetime(lt) => lt,
_ => bug!("expected a region, but found another kind"),
}
}

/// Unpack the `GenericArg` as a type when it is known certainly to be a type.
/// This is true in cases where `Substs` is used in places where the kinds are known
/// to be limited (e.g. in tuples, where the only parameters are type parameters).
4 changes: 2 additions & 2 deletions compiler/rustc_typeck/src/check/dropck.rs
Original file line number Diff line number Diff line change
@@ -318,8 +318,8 @@ impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> {

// Anonymizing the LBRs is necessary to solve (Issue #59497).
// After we do so, it should be totally fine to skip the binders.
let anon_a = self.tcx.anonymize_late_bound_regions(a);
let anon_b = self.tcx.anonymize_late_bound_regions(b);
let anon_a = self.tcx.anonymize_bound_vars(a);
let anon_b = self.tcx.anonymize_bound_vars(b);
self.relate(anon_a.skip_binder(), anon_b.skip_binder())?;

Ok(a)
14 changes: 14 additions & 0 deletions src/test/ui/generic-associated-types/anonymize-bound-vars.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// check-pass
//
// regression test for #98702
#![feature(generic_associated_types)]

trait Foo {
type Assoc<T>;
}

impl Foo for () {
type Assoc<T> = [T; 2*2];
}

fn main() {}