Rollup merge of #115834 - compiler-errors:binder-vars, r=jackh726

Properly consider binder vars in `HasTypeFlagsVisitor`

Given a PolyTraitRef like `for<'a> Ty: Trait` (where neither `Ty` nor `Trait` mention `'a`), we do *not* return true for `.has_type_flags(TypeFlags::HAS_LATE_BOUND)`, even though binders are supposed to act as if they have late-bound vars even if they don't mention them in their bound value: 31ae3b2bdb. This is because we use `HasTypeFlagsVisitor`, which only computes the type flags for `Ty`, `Const` and `Region` and `Predicates`, and we consequently skip any binders (and setting flags for their vars) that are not contained in one of these types.

This ends up causing a problem, because when we call `TyCtxt::erase_regions` (which both erases regions *and* anonymizes bound vars), we will skip such a PolyTraitRef, not anonymizing it, and therefore not making it structurally equal to other binders. This breaks vtable computations.

This PR computes the flags for all binders we enter in `HasTypeFlagsVisitor` if we're looking for `TypeFlags::HAS_LATE_BOUND` (or `TypeFlags::HAS_{RE,TY,CT}_LATE_BOUND`).

Fixes #115807
This commit is contained in:
Matthias Krüger 2023-09-14 19:12:32 +02:00 committed by GitHub
commit 57370828b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 15 deletions

View File

@ -34,6 +34,26 @@ pub fn for_const(c: ty::Const<'_>) -> TypeFlags {
result.flags result.flags
} }
pub fn bound_var_flags(vars: &ty::List<ty::BoundVariableKind>) -> FlagComputation {
let mut computation = FlagComputation::new();
for bv in vars {
match bv {
ty::BoundVariableKind::Ty(_) => {
computation.flags |= TypeFlags::HAS_TY_LATE_BOUND;
}
ty::BoundVariableKind::Region(_) => {
computation.flags |= TypeFlags::HAS_RE_LATE_BOUND;
}
ty::BoundVariableKind::Const => {
computation.flags |= TypeFlags::HAS_CT_LATE_BOUND;
}
}
}
computation
}
fn add_flags(&mut self, flags: TypeFlags) { fn add_flags(&mut self, flags: TypeFlags) {
self.flags = self.flags | flags; self.flags = self.flags | flags;
} }
@ -57,21 +77,7 @@ fn bound_computation<T, F>(&mut self, value: ty::Binder<'_, T>, f: F)
where where
F: FnOnce(&mut Self, T), F: FnOnce(&mut Self, T),
{ {
let mut computation = FlagComputation::new(); let mut computation = FlagComputation::bound_var_flags(value.bound_vars());
for bv in value.bound_vars() {
match bv {
ty::BoundVariableKind::Ty(_) => {
computation.flags |= TypeFlags::HAS_TY_LATE_BOUND;
}
ty::BoundVariableKind::Region(_) => {
computation.flags |= TypeFlags::HAS_RE_LATE_BOUND;
}
ty::BoundVariableKind::Const => {
computation.flags |= TypeFlags::HAS_CT_LATE_BOUND;
}
}
}
f(&mut computation, value.skip_binder()); f(&mut computation, value.skip_binder());

View File

@ -481,9 +481,33 @@ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// `Ty`/`Const`/`Predicate`, but not within those types. This is because the // `Ty`/`Const`/`Predicate`, but not within those types. This is because the
// type flags at the outer layer are enough. So it's faster than it first // type flags at the outer layer are enough. So it's faster than it first
// looks, particular for `Ty`/`Predicate` where it's just a field access. // looks, particular for `Ty`/`Predicate` where it's just a field access.
//
// N.B. The only case where this isn't totally true is binders, which also
// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that
// are present, regardless of whether those bound variables are used. This
// is important for anonymization of binders in `TyCtxt::erase_regions`. We
// specifically detect this case in `visit_binder`.
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor { impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
type BreakTy = FoundFlags; type BreakTy = FoundFlags;
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
&mut self,
t: &Binder<'tcx, T>,
) -> ControlFlow<Self::BreakTy> {
// If we're looking for any of the HAS_*_LATE_BOUND flags, we need to
// additionally consider the bound vars on the binder itself, even if
// the contents of a the binder (e.g. a `TraitRef`) doesn't reference
// the bound vars.
if self.flags.intersects(TypeFlags::HAS_LATE_BOUND) {
let bound_var_flags = FlagComputation::bound_var_flags(t.bound_vars());
if bound_var_flags.flags.intersects(self.flags) {
return ControlFlow::Break(FoundFlags);
}
}
t.super_visit_with(self)
}
#[inline] #[inline]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
// Note: no `super_visit_with` call. // Note: no `super_visit_with` call.

View File

@ -0,0 +1,27 @@
// build-pass
// issue: #115807
trait Chip: for<'a> TraitWithLifetime<'a> + SomeMarker {
fn compute(&self);
}
trait SomeMarker {}
trait TraitWithLifetime<'a>: SomeMarker {}
trait Machine {
fn run();
}
struct BasicMachine;
impl Machine for BasicMachine {
fn run() {
let chips: [&dyn Chip; 0] = [];
let _ = chips.map(|chip| chip.compute());
}
}
fn main() {
BasicMachine::run();
}