66
77use std:: ops:: ControlFlow ;
88
9+ use itertools:: Itertools ;
10+ use rustc_data_structures:: fx:: FxHashMap ;
911use rustc_errors:: FatalError ;
1012use rustc_hir as hir;
1113use rustc_hir:: attrs:: lang_items:: LangItem ;
12- use rustc_hir:: def_id:: DefId ;
14+ use rustc_hir:: def_id:: { CRATE_DEF_ID , DefId } ;
15+ use rustc_infer:: infer:: BoundRegionConversionTime ;
1316use rustc_middle:: query:: Providers ;
1417use rustc_middle:: ty:: {
15- self , EarlyBinder , GenericArgs , Ty , TyCtxt , TypeFoldable , TypeFolder , TypeSuperFoldable ,
16- TypeSuperVisitable , TypeVisitable , TypeVisitableExt , TypeVisitor , TypingMode , Unnormalized ,
17- Upcast , elaborate,
18+ self , Clause , EarlyBinder , GenericArgs , PolyProjectionPredicate , ProjectionPredicate , Ty ,
19+ TyCtxt , TypeFoldable , TypeFolder , TypeSuperFoldable , TypeSuperVisitable , TypeVisitable ,
20+ TypeVisitableExt , TypeVisitor , TypingMode , Unnormalized , Upcast , elaborate,
1821} ;
1922use rustc_span:: { DUMMY_SP , Span } ;
2023use smallvec:: SmallVec ;
@@ -23,9 +26,10 @@ use tracing::{debug, instrument};
2326use super :: elaborate;
2427use crate :: infer:: TyCtxtInferExt ;
2528pub use crate :: traits:: DynCompatibilityViolation ;
29+ use crate :: traits:: coherence:: plug_infer_with_placeholders;
2630use crate :: traits:: query:: evaluate_obligation:: InferCtxtExt ;
2731use crate :: traits:: {
28- AssocConstViolation , MethodViolation , Obligation , ObligationCause ,
32+ AssocConstViolation , MethodViolation , Obligation , ObligationCause , ObligationCtxt ,
2933 normalize_param_env_or_error, util,
3034} ;
3135
@@ -55,7 +59,8 @@ fn dyn_compatibility_violations(
5559 debug ! ( "dyn_compatibility_violations: {:?}" , trait_def_id) ;
5660 tcx. arena . alloc_from_iter (
5761 elaborate:: supertrait_def_ids ( tcx, trait_def_id)
58- . flat_map ( |def_id| dyn_compatibility_violations_for_trait ( tcx, def_id) ) ,
62+ . flat_map ( |def_id| dyn_compatibility_violations_for_trait ( tcx, def_id) )
63+ . chain ( incoherent_supertrait_assocs ( tcx, trait_def_id) ) ,
5964 )
6065}
6166
@@ -979,6 +984,103 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalRpititVisitor<'tcx> {
979984 }
980985}
981986
987+ /// Computes [`DynCompatibilityViolation::IncoherentSupertraitAssocs`]
988+ #[ instrument( level = "debug" , skip( tcx) ) ]
989+ fn incoherent_supertrait_assocs (
990+ tcx : TyCtxt < ' _ > ,
991+ trait_def_id : DefId ,
992+ ) -> impl Iterator < Item = DynCompatibilityViolation > {
993+ let clauses = tcx
994+ . clauses_of ( trait_def_id)
995+ . instantiate_identity ( tcx)
996+ . clauses
997+ . into_iter ( )
998+ . map ( Unnormalized :: skip_norm_wip) ;
999+ // Map from associated items to projection predicates that apply to them.
1000+ let mut preds_for_assoc = FxHashMap :: < DefId , Vec < PolyProjectionPredicate < ' _ > > > :: default ( ) ;
1001+ elaborate ( tcx, clauses) . filter_map ( Clause :: as_projection_clause) . flat_map ( move |proj| {
1002+ let prev_projs = preds_for_assoc. entry ( proj. item_def_id ( ) ) . or_default ( ) ;
1003+ let violations: Vec < _ > = prev_projs
1004+ . iter ( )
1005+ . copied ( )
1006+ . filter ( move |& prev_proj| {
1007+ !does_pair_have_coherent_supertrait_assocs ( tcx, trait_def_id, prev_proj, proj)
1008+ } )
1009+ . map ( move |_| {
1010+ DynCompatibilityViolation :: IncoherentSupertraitAssocs (
1011+ tcx. item_name ( proj. item_def_id ( ) ) ,
1012+ tcx. def_ident_span ( proj. item_def_id ( ) )
1013+ . expect ( "Associated items should have a def_ident_span" ) ,
1014+ )
1015+ } )
1016+ . collect ( ) ;
1017+ prev_projs. push ( proj) ;
1018+ violations
1019+ } )
1020+ }
1021+
1022+ #[ instrument( level = "debug" , skip( tcx) , ret) ]
1023+ fn does_pair_have_coherent_supertrait_assocs < ' tcx > (
1024+ tcx : TyCtxt < ' tcx > ,
1025+ trait_def_id : DefId ,
1026+ proj_1 : PolyProjectionPredicate < ' tcx > ,
1027+ proj_2 : PolyProjectionPredicate < ' tcx > ,
1028+ ) -> bool {
1029+ let infcx = tcx
1030+ . infer_ctxt ( )
1031+ . with_next_trait_solver ( tcx. next_trait_solver_in_coherence ( ) )
1032+ . build ( TypingMode :: Coherence ) ;
1033+
1034+ // We instantiate type parameters in the two projections with the same
1035+ // fresh inference variables.
1036+ let trait_args = infcx. fresh_args_for_item ( DUMMY_SP , trait_def_id) ;
1037+ let process_proj = |proj : PolyProjectionPredicate < ' tcx > | -> ProjectionPredicate < ' tcx > {
1038+ let instantiated_proj = EarlyBinder :: bind ( tcx, proj) . instantiate ( tcx, trait_args) ;
1039+ infcx. instantiate_binder_with_fresh_vars (
1040+ DUMMY_SP ,
1041+ BoundRegionConversionTime :: AssocTypeProjection ( proj. item_def_id ( ) ) ,
1042+ instantiated_proj. skip_norm_wip ( ) ,
1043+ )
1044+ } ;
1045+ let proj_1 = process_proj ( proj_1) ;
1046+ let proj_2 = process_proj ( proj_2) ;
1047+ assert_eq ! (
1048+ proj_1. projection_term. kind, proj_2. projection_term. kind,
1049+ "should compare the same projection kind"
1050+ ) ;
1051+
1052+ let ocx = ObligationCtxt :: new ( & infcx) ;
1053+ let param_env = tcx. param_env ( trait_def_id) ;
1054+ // Constrain the two projections to be on the same trait, including generics.
1055+ // If this fails, then the two projections do not conflict with each other,
1056+ // as they're projecting different things.
1057+ let can_equate_generics =
1058+ proj_1. projection_term . args . iter ( ) . zip_eq ( proj_2. projection_term . args ) . all (
1059+ |( arg_1, arg_2) | ocx. eq ( & ObligationCause :: dummy ( ) , param_env, arg_1, arg_2) . is_ok ( ) ,
1060+ ) ;
1061+ // FIXME: Is it sound to return true here if we call .resolve_regions() here,
1062+ // and that produces an error?
1063+ if !can_equate_generics || ocx. try_evaluate_obligations ( ) . has_errors ( ) {
1064+ return true ;
1065+ }
1066+ // Discard any ambiguous obligations. In doing so, we're conservatively assuming that
1067+ // the two projections might apply to the same trait-with-generics. This is sound, since
1068+ // it can only cause this function to return false when it could have returned true,
1069+ // which at worst can only cause a trait to be marked as dyn-incompatible.
1070+ // FIXME: Is this strictly necessary? Can retaining the ambiguous obligations
1071+ // to the following check cause any problems?
1072+ drop ( ocx) ;
1073+
1074+ // Now that we've constrained the two projections to be on the same thing,
1075+ // we check whether the two terms are necessarily equal to each other.
1076+ // If they are, then the two projections are coherent.
1077+ plug_infer_with_placeholders ( & infcx, ty:: UniverseIndex :: ROOT , ( proj_1, proj_2) ) ;
1078+ let ocx = ObligationCtxt :: new ( & infcx) ;
1079+ ocx. eq ( & ObligationCause :: dummy ( ) , param_env, proj_1. term , proj_2. term ) . is_ok ( )
1080+ && ocx. evaluate_obligations_error_on_ambiguity ( ) . no_errors ( )
1081+ && ocx. resolve_regions ( CRATE_DEF_ID , param_env, [ ] ) . is_empty ( )
1082+ }
1083+
9821084pub ( crate ) fn provide ( providers : & mut Providers ) {
9831085 * providers = Providers {
9841086 dyn_compatibility_violations,
0 commit comments