Skip to content

Commit 3839f06

Browse files
committed
check that UnsizeCoerce may hold before trying unsizing coercion
this prevents us from trying unsizing coercion in cases like `*const W<dyn T>` -> `*const dyn T`, where it would later cause a compilation error since `W<dyn T>: Sized` and `W<dyn T>: T` do not hold.
1 parent 25e1215 commit 3839f06

File tree

1 file changed

+60
-8
lines changed

1 file changed

+60
-8
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+60-8
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ use rustc_abi::ExternAbi;
4141
use rustc_attr_parsing::InlineAttr;
4242
use rustc_errors::codes::*;
4343
use rustc_errors::{Applicability, Diag, struct_span_code_err};
44-
use rustc_hir as hir;
4544
use rustc_hir::def_id::{DefId, LocalDefId};
45+
use rustc_hir::{self as hir, LangItem};
4646
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4747
use rustc_infer::infer::relate::RelateResult;
4848
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -58,7 +58,7 @@ use rustc_middle::ty::adjustment::{
5858
};
5959
use rustc_middle::ty::error::TypeError;
6060
use rustc_middle::ty::visit::TypeVisitableExt;
61-
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
61+
use rustc_middle::ty::{self, AliasTy, GenericArgsRef, Ty, TyCtxt};
6262
use rustc_session::parse::feature_err;
6363
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span, sym};
6464
use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -590,6 +590,63 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
590590

591591
// Create an obligation for `Source: CoerceUnsized<Target>`.
592592
let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target });
593+
let root_obligation = Obligation::new(
594+
self.tcx,
595+
cause.clone(),
596+
self.fcx.param_env,
597+
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]),
598+
);
599+
600+
// If the root `Source: CoerceUnsized<Target>` obligation can't possibly hold,
601+
// we don't have to assume that this is unsizing coercion (it will always lead to an error)
602+
//
603+
// However, we don't want to bail early all the time, since the unholdable obligations
604+
// may be interesting for diagnostics (such as trying to coerce `&T` to `&dyn Id<This = U>`),
605+
// so we only bail if there (likely) is another way to convert the types.
606+
if !self.infcx.predicate_may_hold(&root_obligation) {
607+
if let Some(dyn_metadata_adt_def_id) = self.tcx.lang_items().get(LangItem::DynMetadata)
608+
&& let Some(metadata_type_def_id) = self.tcx.lang_items().get(LangItem::Metadata)
609+
{
610+
self.probe(|_| {
611+
let ocx = ObligationCtxt::new(&self.infcx);
612+
613+
// returns `true` if `<ty as Pointee>::Metadata` is `DynMetadata<_>`
614+
let has_dyn_trait_metadata = |ty| {
615+
let metadata_ty: Result<_, _> = ocx.structurally_normalize_ty(
616+
&ObligationCause::dummy(),
617+
self.fcx.param_env,
618+
Ty::new_alias(
619+
self.tcx,
620+
ty::AliasTyKind::Projection,
621+
AliasTy::new(self.tcx, metadata_type_def_id, [ty]),
622+
),
623+
);
624+
625+
metadata_ty.is_ok_and(|metadata_ty| {
626+
metadata_ty
627+
.ty_adt_def()
628+
.is_some_and(|d| d.did() == dyn_metadata_adt_def_id)
629+
})
630+
};
631+
632+
// If both types are raw pointers to a (wrapper over a) trait object,
633+
// this might be a cast like `*const W<dyn Trait> -> *const dyn Trait`.
634+
// So it's better to bail and try that. (even if the cast is not possible, for
635+
// example due to vtables not matching, cast diagnostic will likely still be better)
636+
//
637+
// N.B. use `target`, not `coerce_target` (the latter is a var)
638+
if let &ty::RawPtr(source_pointee, _) = coerce_source.kind()
639+
&& let &ty::RawPtr(target_pointee, _) = target.kind()
640+
&& has_dyn_trait_metadata(source_pointee)
641+
&& has_dyn_trait_metadata(target_pointee)
642+
{
643+
return Err(TypeError::Mismatch);
644+
}
645+
646+
Ok(())
647+
})?;
648+
}
649+
}
593650

594651
// Use a FIFO queue for this custom fulfillment procedure.
595652
//
@@ -598,12 +655,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
598655
// and almost never more than 3. By using a SmallVec we avoid an
599656
// allocation, at the (very small) cost of (occasionally) having to
600657
// shift subsequent elements down when removing the front element.
601-
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new(
602-
self.tcx,
603-
cause,
604-
self.fcx.param_env,
605-
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target])
606-
)];
658+
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![root_obligation];
607659

608660
let mut has_unsized_tuple_coercion = false;
609661
let mut has_trait_upcasting_coercion = None;

0 commit comments

Comments
 (0)