Skip to content

Commit bf38cae

Browse files
Rollup merge of #131042 - compiler-errors:supertrait-vtable, r=lcnr
Instantiate binders in `supertrait_vtable_slot` `supertrait_vtable_slot` was previously using structural equality when probing for the vtable slot, which led to an ICE since we need a *subtype* match, not an exact match. Fixes #131027 r? lcnr
2 parents 344b6a1 + 7c552d5 commit bf38cae

File tree

3 files changed

+89
-33
lines changed

3 files changed

+89
-33
lines changed

compiler/rustc_trait_selection/src/traits/engine.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ use rustc_infer::infer::canonical::{
99
Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse,
1010
};
1111
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
12-
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError};
12+
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace};
1313
use rustc_macros::extension;
1414
use rustc_middle::arena::ArenaAllocatable;
1515
use rustc_middle::traits::query::NoSolution;
1616
use rustc_middle::ty::error::TypeError;
1717
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance};
18+
use rustc_type_ir::relate::Relate;
1819

1920
use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine};
2021
use crate::error_reporting::InferCtxtErrorExt;
@@ -133,6 +134,20 @@ where
133134
.map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
134135
}
135136

137+
pub fn eq_trace<T: Relate<TyCtxt<'tcx>>>(
138+
&self,
139+
cause: &ObligationCause<'tcx>,
140+
param_env: ty::ParamEnv<'tcx>,
141+
trace: TypeTrace<'tcx>,
142+
expected: T,
143+
actual: T,
144+
) -> Result<(), TypeError<'tcx>> {
145+
self.infcx
146+
.at(cause, param_env)
147+
.eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual)
148+
.map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
149+
}
150+
136151
/// Checks whether `expected` is a subtype of `actual`: `expected <: actual`.
137152
pub fn sub<T: ToTrace<'tcx>>(
138153
&self,

compiler/rustc_trait_selection/src/traits/vtable.rs

+65-25
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use std::fmt::Debug;
22
use std::ops::ControlFlow;
33

44
use rustc_hir::def_id::DefId;
5+
use rustc_infer::infer::at::ToTrace;
6+
use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt};
7+
use rustc_infer::traits::ObligationCause;
58
use rustc_infer::traits::util::PredicateSet;
69
use rustc_middle::bug;
710
use rustc_middle::query::Providers;
@@ -13,7 +16,7 @@ use smallvec::{SmallVec, smallvec};
1316
use tracing::debug;
1417

1518
use crate::errors::DumpVTableEntries;
16-
use crate::traits::{impossible_predicates, is_vtable_safe_method};
19+
use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method};
1720

1821
#[derive(Clone, Debug)]
1922
pub enum VtblSegment<'tcx> {
@@ -22,6 +25,8 @@ pub enum VtblSegment<'tcx> {
2225
}
2326

2427
/// Prepare the segments for a vtable
28+
// FIXME: This should take a `PolyExistentialTraitRef`, since we don't care
29+
// about our `Self` type here.
2530
pub fn prepare_vtable_segments<'tcx, T>(
2631
tcx: TyCtxt<'tcx>,
2732
trait_ref: ty::PolyTraitRef<'tcx>,
@@ -327,14 +332,10 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
327332
let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
328333
bug!();
329334
};
330-
let source_principal = tcx
331-
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap())
332-
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
335+
let source_principal =
336+
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
333337

334-
let target_principal = tcx
335-
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), key)
336-
// We don't care about the self type, since it will always be the same thing.
337-
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
338+
let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key));
338339

339340
let vtable_segment_callback = {
340341
let mut vptr_offset = 0;
@@ -343,15 +344,18 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
343344
VtblSegment::MetadataDSA => {
344345
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
345346
}
346-
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
347-
if tcx
348-
.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref)
349-
== target_principal
350-
{
347+
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
348+
if trait_refs_are_compatible(
349+
tcx,
350+
vtable_principal
351+
.map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
352+
target_principal,
353+
) {
351354
return ControlFlow::Break(vptr_offset);
352355
}
353356

354-
vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len();
357+
vptr_offset +=
358+
tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
355359

356360
if emit_vptr {
357361
vptr_offset += 1;
@@ -383,17 +387,14 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
383387
let ty::Dynamic(target, _, _) = *target.kind() else {
384388
bug!();
385389
};
386-
let target_principal = tcx
387-
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal()?)
388-
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
390+
let target_principal = target.principal()?;
389391

390392
// Given that we have a target principal, it is a bug for there not to be a source principal.
391393
let ty::Dynamic(source, _, _) = *source.kind() else {
392394
bug!();
393395
};
394-
let source_principal = tcx
395-
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap())
396-
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
396+
let source_principal =
397+
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
397398

398399
let vtable_segment_callback = {
399400
let mut vptr_offset = 0;
@@ -402,11 +403,15 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
402403
VtblSegment::MetadataDSA => {
403404
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
404405
}
405-
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
406-
vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len();
407-
if tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), trait_ref)
408-
== target_principal
409-
{
406+
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
407+
vptr_offset +=
408+
tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
409+
if trait_refs_are_compatible(
410+
tcx,
411+
vtable_principal
412+
.map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
413+
target_principal,
414+
) {
410415
if emit_vptr {
411416
return ControlFlow::Break(Some(vptr_offset));
412417
} else {
@@ -426,6 +431,41 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
426431
prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
427432
}
428433

434+
fn trait_refs_are_compatible<'tcx>(
435+
tcx: TyCtxt<'tcx>,
436+
hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>,
437+
hr_target_principal: ty::PolyExistentialTraitRef<'tcx>,
438+
) -> bool {
439+
if hr_vtable_principal.def_id() != hr_target_principal.def_id() {
440+
return false;
441+
}
442+
443+
let infcx = tcx.infer_ctxt().build();
444+
let param_env = ty::ParamEnv::reveal_all();
445+
let ocx = ObligationCtxt::new(&infcx);
446+
let hr_source_principal =
447+
ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal);
448+
let hr_target_principal =
449+
ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal);
450+
infcx.enter_forall(hr_target_principal, |target_principal| {
451+
let source_principal = infcx.instantiate_binder_with_fresh_vars(
452+
DUMMY_SP,
453+
BoundRegionConversionTime::HigherRankedType,
454+
hr_source_principal,
455+
);
456+
let Ok(()) = ocx.eq_trace(
457+
&ObligationCause::dummy(),
458+
param_env,
459+
ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal),
460+
target_principal,
461+
source_principal,
462+
) else {
463+
return false;
464+
};
465+
ocx.select_all_or_error().is_empty()
466+
})
467+
}
468+
429469
pub(super) fn provide(providers: &mut Providers) {
430470
*providers = Providers {
431471
own_existential_vtable_entries,
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
//@ revisions: current next
22
//@ ignore-compare-mode-next-solver (explicit revisions)
33
//@[next] compile-flags: -Znext-solver
4-
//@ check-pass
4+
//@ build-pass
55

6-
// We should be able to instantiate a binder during trait upcasting.
7-
// This test could be `check-pass`, but we should make sure that we
8-
// do so in both trait solvers.
6+
// Check that we are able to instantiate a binder during trait upcasting,
7+
// and that it doesn't cause any issues with codegen either.
98

109
#![feature(trait_upcasting)]
1110

1211
trait Supertrait<'a, 'b> {}
1312
trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {}
1413

15-
impl<'a> Supertrait<'a, 'a> for () {}
16-
impl<'a> Subtrait<'a, 'a> for () {}
14+
impl Supertrait<'_, '_> for () {}
15+
impl Subtrait<'_, '_> for () {}
1716
fn ok(x: &dyn for<'a, 'b> Subtrait<'a, 'b>) -> &dyn for<'a> Supertrait<'a, 'a> {
1817
x
1918
}
2019

21-
fn main() {}
20+
fn main() {
21+
ok(&());
22+
}

0 commit comments

Comments
 (0)