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:
commit
57370828b9
@ -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());
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user