Skip to content

Commit 145ff27

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 bda4b30 commit 145ff27

File tree

1 file changed

+63
-9
lines changed

1 file changed

+63
-9
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+63-9
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ 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};
4949
use rustc_infer::traits::{
5050
IfExpressionCause, MatchExpressionArmCause, Obligation, PredicateObligation,
51-
PredicateObligations,
51+
PredicateObligations, ScrubbedTraitError, TraitEngine,
5252
};
5353
use rustc_middle::lint::in_external_macro;
5454
use rustc_middle::span_bug;
@@ -58,13 +58,14 @@ 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 _;
6565
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
6666
use rustc_trait_selection::traits::{
6767
self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
68+
StructurallyNormalizeExt,
6869
};
6970
use smallvec::{SmallVec, smallvec};
7071
use tracing::{debug, instrument};
@@ -582,6 +583,64 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
582583

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

586645
// Use a FIFO queue for this custom fulfillment procedure.
587646
//
@@ -590,12 +649,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
590649
// and almost never more than 3. By using a SmallVec we avoid an
591650
// allocation, at the (very small) cost of (occasionally) having to
592651
// shift subsequent elements down when removing the front element.
593-
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new(
594-
self.tcx,
595-
cause,
596-
self.fcx.param_env,
597-
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target])
598-
)];
652+
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![root_obligation];
599653

600654
let mut has_unsized_tuple_coercion = false;
601655
let mut has_trait_upcasting_coercion = None;

0 commit comments

Comments
 (0)