Skip to content

Commit 49a638d

Browse files
authored
Rollup merge of #157280 - Dnreikronos:existential_trait_ref_escaping_bound_vars_assert, r=lcnr
traits: Allow escaping self types in ExistentialTraitRef::with_self_ty Fixes #157122 WF-checking recurses through higher-ranked binders without instantiating them, so a `dyn Trait` nested inside a `for<'a>` binder reaches the `ty::Dynamic` arm of `WfPredicates::visit_ty` while still carrying escaping bound vars. Building the principal trait ref there via `ExistentialTraitRef::with_self_ty` hands that escaping self type to a `debug_assert!(!self_ty.has_escaping_bound_vars())`, which ICEs once the assertion is enabled. The assert was accidentally disabled in 2018 refactor #53816, and the `// FIXME(#157122)` left in its place asks whether to remove it or fix the fallout. An escaping bound var is expected for trait objects, and we already catch trait refs with escaping bound vars at the places that actually use them — so creating one here is fine. Rather than work around the assert in WF, this removes the assert and its stale FIXME from `with_self_ty`. Two regression tests pin the behavior: - `wf-dyn-in-hrtb-bound-issue-157122.rs` reproduces the original ICE (`dyn` nested in an HRTB bound). - `wf-dyn-in-hrtb-bound-const-mismatch.rs` checks that the `ConstArgHasType` obligation this arm reads off still fires, so an ill-typed const argument on such a `dyn` keeps erroring instead of compiling clean.
2 parents 00c9f84 + 3669c24 commit 49a638d

4 files changed

Lines changed: 61 additions & 7 deletions

File tree

compiler/rustc_type_ir/src/predicate.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -429,13 +429,6 @@ impl<I: Interner> ExistentialTraitRef<I> {
429429
/// Therefore, you must specify *some* self type to perform the conversion.
430430
/// A common choice is the trait object type itself or some kind of dummy type.
431431
pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> TraitRef<I> {
432-
// FIXME(#157122): This assertion was accidentally commented out in refactoring PR #53816
433-
// back in 2018 but nowadays it can actually trigger. Either remove this
434-
// comment entirely if the assertion is incorrect or uncomment it and fix
435-
// the fallout!
436-
// otherwise the escaping vars would be captured by the binder
437-
//debug_assert!(!self_ty.has_escaping_bound_vars());
438-
439432
TraitRef::new(interner, self.def_id, [self_ty.into()].into_iter().chain(self.args.iter()))
440433
}
441434
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Companion to #157122 / `wf-dyn-in-hrtb-bound-issue-157122.rs`.
2+
//
3+
// That test covers an ICE in `WfPredicates::visit_ty` where a `dyn Trait` nested
4+
// inside a `for<'a>` binder reached `ExistentialTraitRef::with_self_ty` with an
5+
// escaping self type. The fix removes the offending `debug_assert!` rather than
6+
// skipping the block. Skipping would have been a silent regression: the block
7+
// emits the `ConstArgHasType` obligation that type-checks a `dyn`'s const
8+
// argument, so dropping it makes an ill-typed const argument compile.
9+
//
10+
// Here `B` has type `bool` but `HasConst` expects `const N: usize`, and the
11+
// `dyn HasConst<'a, B>` carries the escaping late-bound `'a`. This must still be
12+
// rejected, exactly as it is without the surrounding `for<'a>` binder.
13+
14+
trait HasConst<'a, const N: usize> {}
15+
16+
fn nested<const B: bool>(_f: &dyn for<'a> Fn(&'a (), &'a dyn HasConst<'a, B>)) {}
17+
//~^ ERROR the constant `B` is not of type `usize`
18+
19+
fn main() {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: the constant `B` is not of type `usize`
2+
--> $DIR/wf-dyn-in-hrtb-bound-const-mismatch.rs:16:30
3+
|
4+
LL | fn nested<const B: bool>(_f: &dyn for<'a> Fn(&'a (), &'a dyn HasConst<'a, B>)) {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
6+
|
7+
note: required by a const generic parameter in `HasConst`
8+
--> $DIR/wf-dyn-in-hrtb-bound-const-mismatch.rs:14:20
9+
|
10+
LL | trait HasConst<'a, const N: usize> {}
11+
| ^^^^^^^^^^^^^^ required by this const generic parameter in `HasConst`
12+
13+
error: aborting due to 1 previous error
14+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ check-pass
2+
3+
// Regression test for #157122.
4+
//
5+
// When WF-checking a type, the visitor recurses through higher-ranked binders
6+
// without instantiating them, so a `dyn Trait` nested inside a `for<'a>` binder
7+
// reaches the `ty::Dynamic` arm of `WfPredicates::visit_ty` while still carrying
8+
// escaping bound vars. Building the principal trait ref there via
9+
// `ExistentialTraitRef::with_self_ty` passed that escaping self type to a
10+
// `debug_assert!(!self_ty.has_escaping_bound_vars())`, which ICEs once the
11+
// assertion is enabled. Creating a trait ref with an escaping self type is fine
12+
// -- escaping bound vars are caught where they are actually used -- so the
13+
// assertion was removed rather than worked around. The `ConstArgHasType` check
14+
// this arm reads off still runs; see `wf-dyn-in-hrtb-bound-const-mismatch.rs`.
15+
// Distilled from `itertools`'s `FormatWith` `Display` impl.
16+
17+
use std::fmt;
18+
19+
fn call<F>(mut f: F)
20+
where
21+
F: FnMut(&mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
22+
{
23+
let _ = f(&mut |_disp: &dyn fmt::Display| Ok(()));
24+
}
25+
26+
fn main() {
27+
call(|cb| cb(&0i32));
28+
}

0 commit comments

Comments
 (0)