Skip to content

Commit cc7b49a

Browse files
committed
Fix vtable ICE on unsatisfied supertrait bounds
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.
1 parent 86c839f commit cc7b49a

14 files changed

Lines changed: 215 additions & 43 deletions

compiler/rustc_middle/src/ty/instance.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -625,25 +625,36 @@ impl<'tcx> Instance<'tcx> {
625625
})
626626
}
627627

628-
pub fn expect_resolve_for_vtable(
628+
/// Fallible version of [`Instance::expect_resolve_for_vtable`].
629+
///
630+
/// Returns `Err` if instance resolution fails, e.g. due to a broken impl with unsatisfied
631+
/// supertrait bounds.
632+
// FIXME(#153596): Once `impossible_predicates` is sound again, use that to reject impossible
633+
// vtable entries up front and remove this function.
634+
pub fn try_resolve_for_vtable(
629635
tcx: TyCtxt<'tcx>,
630636
typing_env: ty::TypingEnv<'tcx>,
631637
def_id: DefId,
632638
args: GenericArgsRef<'tcx>,
633-
span: Span,
634-
) -> Instance<'tcx> {
635-
debug!("resolve_for_vtable(def_id={:?}, args={:?})", def_id, args);
639+
) -> Result<Instance<'tcx>, ErrorGuaranteed> {
640+
debug!("try_resolve_for_vtable(def_id={:?}, args={:?})", def_id, args);
636641
let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
637642
let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty()
638643
&& fn_sig.input(0).skip_binder().is_param(0)
639644
&& tcx.generics_of(def_id).has_self;
640645

641646
if is_vtable_shim {
642647
debug!(" => associated item with unsizeable self: Self");
643-
return Instance { def: InstanceKind::VTableShim(def_id), args };
648+
return Ok(Instance { def: InstanceKind::VTableShim(def_id), args });
644649
}
645650

646-
let mut resolved = Instance::expect_resolve(tcx, typing_env, def_id, args, span);
651+
let mut resolved =
652+
Instance::try_resolve(tcx, typing_env, def_id, args)?.ok_or_else(|| {
653+
tcx.dcx().delayed_bug(format!(
654+
"failed to resolve instance for vtable: {}",
655+
tcx.def_path_str_with_args(def_id, args)
656+
))
657+
})?;
647658

648659
let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable);
649660
match resolved.def {
@@ -700,7 +711,25 @@ impl<'tcx> Instance<'tcx> {
700711
_ => {}
701712
}
702713

703-
resolved
714+
Ok(resolved)
715+
}
716+
717+
pub fn expect_resolve_for_vtable(
718+
tcx: TyCtxt<'tcx>,
719+
typing_env: ty::TypingEnv<'tcx>,
720+
def_id: DefId,
721+
args: GenericArgsRef<'tcx>,
722+
span: Span,
723+
) -> Instance<'tcx> {
724+
Instance::try_resolve_for_vtable(tcx, typing_env, def_id, args).unwrap_or_else(|e| {
725+
let span =
726+
if span.is_dummy() && def_id.is_local() { tcx.def_span(def_id) } else { span };
727+
span_bug!(
728+
span,
729+
"failed to resolve instance for {}: {e:#?}",
730+
tcx.def_path_str_with_args(def_id, args)
731+
)
732+
})
704733
}
705734

706735
pub fn resolve_closure(

compiler/rustc_trait_selection/src/traits/vtable.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use rustc_middle::query::Providers;
88
use rustc_middle::ty::{
99
self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry,
1010
};
11-
use rustc_span::DUMMY_SP;
1211
use smallvec::{SmallVec, smallvec};
1312
use tracing::debug;
1413

@@ -285,15 +284,22 @@ fn vtable_entries<'tcx>(
285284
return VtblEntry::Vacant;
286285
}
287286

288-
let instance = ty::Instance::expect_resolve_for_vtable(
287+
match ty::Instance::try_resolve_for_vtable(
289288
tcx,
290289
ty::TypingEnv::fully_monomorphized(),
291290
def_id,
292291
args,
293-
DUMMY_SP,
294-
);
295-
296-
VtblEntry::Method(instance)
292+
) {
293+
Ok(instance) => VtblEntry::Method(instance),
294+
Err(_guar) => {
295+
// This can happen when building a vtable for a type
296+
// whose impl has unsatisfied supertrait bounds (an
297+
// error for which has already been emitted). In that
298+
// case the supertrait's methods can't be resolved,
299+
// so we mark the entry as vacant.
300+
VtblEntry::Vacant
301+
}
302+
}
297303
});
298304

299305
entries.extend(own_entries);

tests/crashes/137190-2.rs

Lines changed: 0 additions & 18 deletions
This file was deleted.

tests/crashes/137190-3.rs

Lines changed: 0 additions & 10 deletions
This file was deleted.

tests/ui/limits/type-length-limit-enforcement.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//~ ERROR reached the type-length limit
1+
//~? ERROR reached the type-length limit
22

33
//! Checks the enforcement of the type-length limit
44
//! and its configurability via `#![type_length_limit]`.

tests/ui/limits/type-length-limit-enforcement.stderr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ LL | drop::<Option<A>>(None);
88
= note: the full name for the type has been written to '$TEST_BUILD_DIR/type-length-limit-enforcement.long-type-$LONG_TYPE_HASH.txt'
99
= note: consider using `--verbose` to print the full type name to the console
1010

11-
error: reached the type-length limit while instantiating `<{closure@...} as FnMut<()>>::call_mut`
11+
error: reached the type-length limit while instantiating `<{closure@...} as FnOnce<()>>::call_once`
12+
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
1213
|
1314
= help: consider adding a `#![type_length_limit="10"]` attribute to your crate
1415
= note: the full name for the type has been written to '$TEST_BUILD_DIR/type-length-limit-enforcement.long-type-$LONG_TYPE_HASH.txt'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Regression test for #154073.
2+
// Verify that we don't ICE when building vtable entries
3+
// for a trait whose impl is missing a required method body.
4+
5+
//@ compile-flags: --crate-type lib
6+
7+
trait Bar: Sync {
8+
fn method(&self);
9+
}
10+
impl<T: Sync> Bar for T {
11+
//~^ ERROR not all trait items implemented
12+
}
13+
14+
static BAR: &dyn Sync = &false as &dyn Bar;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0046]: not all trait items implemented, missing: `method`
2+
--> $DIR/vtable-missing-method-impl.rs:10:1
3+
|
4+
LL | fn method(&self);
5+
| ----------------- `method` from trait
6+
LL | }
7+
LL | impl<T: Sync> Bar for T {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `method` in implementation
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0046`.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Regression test for #154073.
2+
// Verify that we don't ICE when building vtable entries
3+
// for a trait whose supertrait is not implemented, during
4+
// const evaluation of a static initializer.
5+
6+
//@ compile-flags: --crate-type lib
7+
8+
trait Bar: Send + Sync + Droppable {}
9+
10+
impl<T: Send> Bar for T {}
11+
//~^ ERROR the trait bound `T: Droppable` is not satisfied
12+
//~| ERROR `T` cannot be shared between threads safely
13+
14+
trait Droppable {
15+
fn drop(&self);
16+
}
17+
18+
const fn upcast(x: &dyn Bar) -> &(dyn Send + Sync) {
19+
x
20+
}
21+
22+
static BAR: &(dyn Send + Sync) = upcast(&false);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0277]: the trait bound `T: Droppable` is not satisfied
2+
--> $DIR/vtable-unsatisfied-supertrait-const.rs:10:23
3+
|
4+
LL | impl<T: Send> Bar for T {}
5+
| ^ the trait `Droppable` is not implemented for `T`
6+
|
7+
note: required by a bound in `Bar`
8+
--> $DIR/vtable-unsatisfied-supertrait-const.rs:8:26
9+
|
10+
LL | trait Bar: Send + Sync + Droppable {}
11+
| ^^^^^^^^^ required by this bound in `Bar`
12+
help: consider further restricting type parameter `T` with trait `Droppable`
13+
|
14+
LL | impl<T: Send + Droppable> Bar for T {}
15+
| +++++++++++
16+
17+
error[E0277]: `T` cannot be shared between threads safely
18+
--> $DIR/vtable-unsatisfied-supertrait-const.rs:10:23
19+
|
20+
LL | impl<T: Send> Bar for T {}
21+
| ^ `T` cannot be shared between threads safely
22+
|
23+
note: required by a bound in `Bar`
24+
--> $DIR/vtable-unsatisfied-supertrait-const.rs:8:19
25+
|
26+
LL | trait Bar: Send + Sync + Droppable {}
27+
| ^^^^ required by this bound in `Bar`
28+
help: consider further restricting type parameter `T` with trait `Sync`
29+
|
30+
LL | impl<T: Send + std::marker::Sync> Bar for T {}
31+
| +++++++++++++++++++
32+
33+
error: aborting due to 2 previous errors
34+
35+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)