Skip to content

Commit

Permalink
Extract trait_refs_are_compatible, make it instantiate binders
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Sep 30, 2024
1 parent af3f212 commit d87e0ca
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 28 deletions.
17 changes: 16 additions & 1 deletion compiler/rustc_trait_selection/src/traits/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ use rustc_infer::infer::canonical::{
Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse,
};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError};
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace};
use rustc_macros::extension;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance};
use rustc_type_ir::relate::Relate;

use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine};
use crate::error_reporting::InferCtxtErrorExt;
Expand Down Expand Up @@ -133,6 +134,20 @@ where
.map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
}

pub fn eq_trace<T: Relate<TyCtxt<'tcx>>>(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
trace: TypeTrace<'tcx>,
expected: T,
actual: T,
) -> Result<(), TypeError<'tcx>> {
self.infcx
.at(cause, param_env)
.eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual)
.map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
}

/// Checks whether `expected` is a subtype of `actual`: `expected <: actual`.
pub fn sub<T: ToTrace<'tcx>>(
&self,
Expand Down
75 changes: 48 additions & 27 deletions compiler/rustc_trait_selection/src/traits/vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::fmt::Debug;
use std::ops::ControlFlow;

use rustc_hir::def_id::DefId;
use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::util::PredicateSet;
Expand All @@ -24,6 +25,8 @@ pub enum VtblSegment<'tcx> {
}

/// Prepare the segments for a vtable
// FIXME: This should take a `PolyExistentialTraitRef`, since we don't care
// about our `Self` type here.
pub fn prepare_vtable_segments<'tcx, T>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
Expand Down Expand Up @@ -385,7 +388,7 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
let ty::Dynamic(target, _, _) = *target.kind() else {
bug!();
};
let target_principal = target.principal()?.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
let target_principal = target.principal()?;

// Given that we have a target principal, it is a bug for there not to be a source principal.
let ty::Dynamic(source, _, _) = *source.kind() else {
Expand All @@ -394,39 +397,22 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
let source_principal =
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);

let infcx = tcx.infer_ctxt().build();
let param_env = ty::ParamEnv::reveal_all();
let trait_refs_are_compatible =
|source: ty::PolyTraitRef<'tcx>, target: ty::PolyTraitRef<'tcx>| {
infcx.probe(|_| {
let ocx = ObligationCtxt::new(&infcx);
let source = ocx.normalize(&ObligationCause::dummy(), param_env, source);
let target = ocx.normalize(&ObligationCause::dummy(), param_env, target);
infcx.enter_forall(target, |target| {
let source = infcx.instantiate_binder_with_fresh_vars(
DUMMY_SP,
BoundRegionConversionTime::HigherRankedType,
source,
);
let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target, source)
else {
return false;
};
ocx.select_all_or_error().is_empty()
})
})
};

let vtable_segment_callback = {
let mut vptr_offset = 0;
move |segment| {
match segment {
VtblSegment::MetadataDSA => {
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len();
if trait_refs_are_compatible(trait_ref, target_principal) {
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
vptr_offset +=
tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
if trait_refs_are_compatible(
tcx,
vtable_principal
.map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
target_principal,
) {
if emit_vptr {
return ControlFlow::Break(Some(vptr_offset));
} else {
Expand All @@ -446,6 +432,41 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
}

fn trait_refs_are_compatible<'tcx>(
tcx: TyCtxt<'tcx>,
hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>,
hr_target_principal: ty::PolyExistentialTraitRef<'tcx>,
) -> bool {
if hr_vtable_principal.def_id() != hr_target_principal.def_id() {
return false;
}

let infcx = tcx.infer_ctxt().build();
let param_env = ty::ParamEnv::reveal_all();
let ocx = ObligationCtxt::new(&infcx);
let hr_source_principal =
ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal);
let hr_target_principal =
ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal);
infcx.enter_forall(hr_target_principal, |target_principal| {
let source_principal = infcx.instantiate_binder_with_fresh_vars(
DUMMY_SP,
BoundRegionConversionTime::HigherRankedType,
hr_source_principal,
);
let Ok(()) = ocx.eq_trace(
&ObligationCause::dummy(),
param_env,
ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal),
target_principal,
source_principal,
) else {
return false;
};
ocx.select_all_or_error().is_empty()
})
}

pub(super) fn provide(providers: &mut Providers) {
*providers = Providers {
own_existential_vtable_entries,
Expand Down

0 comments on commit d87e0ca

Please sign in to comment.