Skip to content

Fix vtable ICE on unsatisfied supertrait bounds#154129

Open
cdown wants to merge 1 commit intorust-lang:mainfrom
cdown:cdown/2026-03-20/vtable_ice
Open

Fix vtable ICE on unsatisfied supertrait bounds#154129
cdown wants to merge 1 commit intorust-lang:mainfrom
cdown:cdown/2026-03-20/vtable_ice

Conversation

@cdown
Copy link
Contributor

@cdown cdown commented Mar 20, 2026

When a trait impl doesn't satisfy its supertrait bounds (for example, impl<T: Send> Bar for T where trait Bar: Droppable but T has no Droppable bound), the compiler emits E0277 and then then continues compilation.

If a static initialiser then upcasts through this broken impl, const evaluation builds the vtable and tries to resolve methods for the unsatisfied supertrait (for example, <bool as Droppable>::drop).

One might think that the impossible_predicates guard in vtable_entries should catch this, but it doesn't, because it calls predicates_of(method).instantiate_own(), which only includes the method's own where clauses, and the parent trait's Self: Trait predicate lives on the trait definition, not the method, so the guard passes and we fall through to expect_resolve_for_vtable, which hits a span_bug! when resolution returns Ok(None).

There was a previous attempt to fix this in #152287, which worked by switching to instantiate_and_check_impossible_predicates, which includes parent predicates and the trait ref. But unfortunately this is unsound and was reverted because impossible_predicates has a pre-existing unsoundness with coroutine witness types and opaque types (see #153596).

Let's take a different approach that avoids impossible_predicates entirely. Let's extract the core of expect_resolve_for_vtable into a fallible try_resolve_for_vtable that returns Result<Instance, ErrorGuaranteed>, and on resolution failure emit a delayed_bug. vtable_entries then can use this and return VtblEntry::Vacant on the error path.

Fixes #154073, #137190

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 20, 2026
@rustbot
Copy link
Collaborator

rustbot commented Mar 20, 2026

r? @mati865

rustbot has assigned @mati865.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 69 candidates
  • Random selection from 14 candidates

@rustbot

This comment has been minimized.

@cdown cdown force-pushed the cdown/2026-03-20/vtable_ice branch from 6e6d9f7 to e35a678 Compare March 20, 2026 08:57
@cdown
Copy link
Contributor Author

cdown commented Mar 20, 2026

r? @lcnr

@rustbot rustbot assigned lcnr and unassigned mati865 Mar 20, 2026
@rust-log-analyzer

This comment has been minimized.

@cdown
Copy link
Contributor Author

cdown commented Mar 20, 2026

^ that's because we happen to have different resolution ordering after this change. but it's the test that needs adjusting really, it shouldn't care which method it hits, only that it should hit the type length limit

@cdown cdown force-pushed the cdown/2026-03-20/vtable_ice branch from e35a678 to 114961e Compare March 20, 2026 11:18
@rustbot
Copy link
Collaborator

rustbot commented Mar 20, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@@ -1,4 +1,4 @@
//~ ERROR reached the type-length limit
//~? ERROR reached the type-length limit
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is because of the change in ordering.

= note: consider using `--verbose` to print the full type name to the console

error: reached the type-length limit while instantiating `<{closure@...} as FnMut<()>>::call_mut`
error: reached the type-length limit while instantiating `<{closure@...} as FnOnce<()>>::call_once`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is also because of the change in ordering.

@cdown cdown force-pushed the cdown/2026-03-20/vtable_ice branch from 114961e to 998ce6b Compare March 20, 2026 11:51
@rust-log-analyzer

This comment has been minimized.

@cdown cdown force-pushed the cdown/2026-03-20/vtable_ice branch from 998ce6b to ed879e6 Compare March 20, 2026 15:59
@theemathas
Copy link
Contributor

Please also test this code from #154073 (comment)

trait Bar: Sync {
    fn method(&self);
} 
impl<T: Sync> Bar for T {
    // missing method
}
static BAR: &dyn Sync = &false as &dyn Bar;
fn main(){}

@cdown cdown force-pushed the cdown/2026-03-20/vtable_ice branch from ed879e6 to af0e626 Compare March 21, 2026 10:59
@cdown
Copy link
Contributor Author

cdown commented Mar 21, 2026

Thanks, added.

When a trait impl doesn't satisfy its supertrait bounds (for example,
`impl<T: Send> Bar for T` where `trait Bar: Droppable` but `T` has no
`Droppable` bound), the compiler emits E0277 and then then continues
compilation.

If a static initialiser then upcasts through this broken impl, const
evaluation builds the vtable and tries to resolve methods for the
unsatisfied supertrait (for example, `<bool as Droppable>::drop`).

One might think that the `impossible_predicates` guard in
`vtable_entries` should catch this, but it doesn't, because it calls
`predicates_of(method).instantiate_own()`, which only includes the
method's own where clauses, and the parent trait's `Self: Trait`
predicate lives on the trait definition, not the method, so the guard
passes and we fall through to `expect_resolve_for_vtable`, which hits a
`span_bug!` when resolution returns `Ok(None)`.

There was a previous attempt to fix this in issue 152287, which worked
by switching to `instantiate_and_check_impossible_predicates`, which
includes parent predicates and the trait ref. But unfortunately this is
unsound and was reverted because `impossible_predicates` has a
pre-existing unsoundness with coroutine witness types and opaque types
(see issue 153596).

Let's take a different approach that avoids `impossible_predicates`
entirely. Let's extract the core of `expect_resolve_for_vtable` into a
fallible `try_resolve_for_vtable` that returns `Result<Instance,
ErrorGuaranteed>`, and on resolution failure emit a `delayed_bug`.
`vtable_entries` then can use this and return `VtblEntry::Vacant` on the
error path.
@cdown cdown force-pushed the cdown/2026-03-20/vtable_ice branch from af0e626 to dd711dd Compare March 21, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ICE]: failed to resolve instance for <bool as Droppable>::drop

6 participants