mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-09 21:42:44 +00:00
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
This commit is contained in:
commit
bf38caea65
@ -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;
|
||||
@ -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,
|
||||
|
@ -2,6 +2,9 @@ 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;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::query::Providers;
|
||||
@ -13,7 +16,7 @@ use smallvec::{SmallVec, smallvec};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::errors::DumpVTableEntries;
|
||||
use crate::traits::{impossible_predicates, is_vtable_safe_method};
|
||||
use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum VtblSegment<'tcx> {
|
||||
@ -22,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>,
|
||||
@ -327,14 +332,10 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
|
||||
let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
|
||||
bug!();
|
||||
};
|
||||
let source_principal = tcx
|
||||
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap())
|
||||
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||
let source_principal =
|
||||
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||
|
||||
let target_principal = tcx
|
||||
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), key)
|
||||
// We don't care about the self type, since it will always be the same thing.
|
||||
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||
let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key));
|
||||
|
||||
let vtable_segment_callback = {
|
||||
let mut vptr_offset = 0;
|
||||
@ -343,15 +344,18 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
|
||||
VtblSegment::MetadataDSA => {
|
||||
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
|
||||
}
|
||||
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
|
||||
if tcx
|
||||
.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref)
|
||||
== target_principal
|
||||
{
|
||||
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
|
||||
if trait_refs_are_compatible(
|
||||
tcx,
|
||||
vtable_principal
|
||||
.map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
|
||||
target_principal,
|
||||
) {
|
||||
return ControlFlow::Break(vptr_offset);
|
||||
}
|
||||
|
||||
vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len();
|
||||
vptr_offset +=
|
||||
tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
|
||||
|
||||
if emit_vptr {
|
||||
vptr_offset += 1;
|
||||
@ -383,17 +387,14 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
|
||||
let ty::Dynamic(target, _, _) = *target.kind() else {
|
||||
bug!();
|
||||
};
|
||||
let target_principal = tcx
|
||||
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), 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 {
|
||||
bug!();
|
||||
};
|
||||
let source_principal = tcx
|
||||
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap())
|
||||
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||
let source_principal =
|
||||
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||
|
||||
let vtable_segment_callback = {
|
||||
let mut vptr_offset = 0;
|
||||
@ -402,11 +403,15 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
|
||||
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 tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), 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 {
|
||||
@ -426,6 +431,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,
|
||||
|
@ -1,21 +1,22 @@
|
||||
//@ revisions: current next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ check-pass
|
||||
//@ build-pass
|
||||
|
||||
// We should be able to instantiate a binder during trait upcasting.
|
||||
// This test could be `check-pass`, but we should make sure that we
|
||||
// do so in both trait solvers.
|
||||
// Check that we are able to instantiate a binder during trait upcasting,
|
||||
// and that it doesn't cause any issues with codegen either.
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
trait Supertrait<'a, 'b> {}
|
||||
trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {}
|
||||
|
||||
impl<'a> Supertrait<'a, 'a> for () {}
|
||||
impl<'a> Subtrait<'a, 'a> for () {}
|
||||
impl Supertrait<'_, '_> for () {}
|
||||
impl Subtrait<'_, '_> for () {}
|
||||
fn ok(x: &dyn for<'a, 'b> Subtrait<'a, 'b>) -> &dyn for<'a> Supertrait<'a, 'a> {
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
fn main() {
|
||||
ok(&());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user