diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index e9961d3ad0862..4cc8025ea3c23 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -9,10 +9,15 @@ use crate::errors; use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::LangItem; +use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::ExistentialPredicateStableCmpExt; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::parse::feature_err; use rustc_span::{sym, ErrorGuaranteed}; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_type_ir::elaborate; mod builtin; mod inherent_impls; @@ -223,5 +228,106 @@ fn check_object_overlap<'tcx>( } } } + + even_cooler_object_overlap(tcx, impl_def_id, trait_ref.def_id); + Ok(()) } + +fn even_cooler_object_overlap<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: LocalDefId, + trait_def_id: DefId, +) { + if !tcx.is_object_safe(trait_def_id) { + return; + } + + let infcx = &tcx.infer_ctxt().with_next_trait_solver(true).intercrate(true).build(); + + let def_span = tcx.def_span(impl_def_id); + let impl_args = infcx.fresh_args_for_item(def_span, impl_def_id.to_def_id()); + let trait_ref = + tcx.impl_trait_ref(impl_def_id).expect("impl of trait").instantiate(tcx, impl_args); + + let principal = ty::Binder::dummy(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), + )); + let mut assoc_tys = elaborate::supertraits(tcx, ty::Binder::dummy(trait_ref)) + .flat_map(|trait_ref| { + tcx.associated_items(trait_ref.def_id()) + .in_definition_order() + .filter(|assoc_item| assoc_item.kind == ty::AssocKind::Type) + .filter(|assoc_item| !tcx.generics_require_sized_self(assoc_item.def_id)) + .map(move |assoc_item| { + trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Projection( + ty::ExistentialProjection::erase_self_ty( + tcx, + ty::ProjectionPredicate { + projection_term: ty::AliasTerm::new( + tcx, + assoc_item.def_id, + trait_ref.args, + ), + term: infcx.next_ty_var(def_span).into(), + }, + ), + ) + }) + }) + }) + .collect::>(); + assoc_tys.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + + let dyn_ty = Ty::new_dynamic( + tcx, + tcx.mk_poly_existential_predicates_from_iter([principal].into_iter().chain(assoc_tys)), + infcx.next_region_var(RegionVariableOrigin::MiscVariable(def_span)), + ty::Dyn, + ); + + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = ObligationCause::misc(def_span, impl_def_id); + let Ok(()) = ocx.eq(&cause, ty::ParamEnv::empty(), trait_ref.self_ty(), dyn_ty) else { + return; + }; + + ocx.register_obligations( + tcx.predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .into_iter() + .map(|(clause, _)| Obligation::new(tcx, cause.clone(), ty::ParamEnv::empty(), clause)), + ); + + let errors_and_ambiguities = ocx.select_all_or_error(); + // We only care about the obligations that are *definitely* true errors. + // Ambiguities do not prove the disjointness of two impls. + let (true_errors, _ambiguities): (Vec<_>, Vec<_>) = + errors_and_ambiguities.into_iter().partition(|error| error.is_true_error()); + + if true_errors.is_empty() { + let associated_type_spans: Vec<_> = tcx + .associated_items(impl_def_id) + .in_definition_order() + .filter(|assoc_item| assoc_item.kind == ty::AssocKind::Type) + .map(|assoc_item| tcx.def_span(assoc_item.def_id)) + .collect(); + if !associated_type_spans.is_empty() { + tcx.dcx() + .struct_span_err( + def_span, + format!( + "this impl overlaps with built-in trait impl for `{}`", + infcx.resolve_vars_if_possible(dyn_ty) + ), + ) + .with_span_note( + associated_type_spans, + "this impl may be unsound because it could provide different values \ + for these associated types", + ) + .emit(); + } + } +}