@@ -11,6 +11,7 @@ use rustc_type_ir::visit::TypeVisitableExt as _;
11
11
use rustc_type_ir:: { self as ty, Interner , TypingMode , Upcast as _, elaborate} ;
12
12
use tracing:: { debug, instrument} ;
13
13
14
+ use super :: trait_goals:: TraitGoalProvenVia ;
14
15
use crate :: delegate:: SolverDelegate ;
15
16
use crate :: solve:: inspect:: ProbeKind ;
16
17
use crate :: solve:: {
@@ -337,15 +338,6 @@ where
337
338
338
339
self . assemble_param_env_candidates ( goal, & mut candidates) ;
339
340
340
- match self . typing_mode ( ) {
341
- TypingMode :: Coherence => { }
342
- TypingMode :: Analysis { .. }
343
- | TypingMode :: PostBorrowckAnalysis { .. }
344
- | TypingMode :: PostAnalysis => {
345
- self . discard_impls_shadowed_by_env ( goal, & mut candidates) ;
346
- }
347
- }
348
-
349
341
candidates
350
342
}
351
343
@@ -500,7 +492,7 @@ where
500
492
goal : Goal < I , G > ,
501
493
candidates : & mut Vec < Candidate < I > > ,
502
494
) {
503
- for ( i, assumption) in goal. param_env . caller_bounds ( ) . into_iter ( ) . enumerate ( ) {
495
+ for ( i, assumption) in goal. param_env . caller_bounds ( ) . iter ( ) . enumerate ( ) {
504
496
candidates. extend ( G :: probe_and_consider_implied_clause (
505
497
self ,
506
498
CandidateSource :: ParamEnv ( i) ,
@@ -733,72 +725,64 @@ where
733
725
} )
734
726
}
735
727
736
- /// If there's a where-bound for the current goal, do not use any impl candidates
737
- /// to prove the current goal. Most importantly, if there is a where-bound which does
738
- /// not specify any associated types, we do not allow normalizing the associated type
739
- /// by using an impl, even if it would apply.
728
+ /// We sadly can't simply take all possible candidates for normalization goals
729
+ /// and check whether they result in the same constraints. We want to make sure
730
+ /// that trying to normalize an alias doesn't result in constraints which aren't
731
+ /// otherwise required.
732
+ ///
733
+ /// Most notably, when proving a trait goal by via a where-bound, we should not
734
+ /// normalize via impls which have stricter region constraints than the where-bound:
735
+ ///
736
+ /// ```rust
737
+ /// trait Trait<'a> {
738
+ /// type Assoc;
739
+ /// }
740
+ ///
741
+ /// impl<'a, T: 'a> Trait<'a> for T {
742
+ /// type Assoc = u32;
743
+ /// }
744
+ ///
745
+ /// fn with_bound<'a, T: Trait<'a>>(_value: T::Assoc) {}
746
+ /// ```
740
747
///
741
- /// <https://github.com/rust-lang/trait-system-refactor-initiative/issues/76>
742
- // FIXME(@lcnr): The current structure here makes me unhappy and feels ugly. idk how
743
- // to improve this however. However, this should make it fairly straightforward to refine
744
- // the filtering going forward, so it seems alright-ish for now.
745
- #[ instrument( level = "debug" , skip( self , goal) ) ]
746
- fn discard_impls_shadowed_by_env < G : GoalKind < D > > (
748
+ /// The where-bound of `with_bound` doesn't specify the associated type, so we would
749
+ /// only be able to normalize `<T as Trait<'a>>::Assoc` by using the impl. This impl
750
+ /// adds a `T: 'a` bound however, which would result in a region error. Given that the
751
+ /// user explicitly wrote that `T: Trait<'a>` holds, this is undesirable and we instead
752
+ /// treat the alias as rigid.
753
+ ///
754
+ /// See trait-system-refactor-initiative#124 for more details.
755
+ #[ instrument( level = "debug" , skip( self ) , ret) ]
756
+ pub ( super ) fn merge_candidates (
747
757
& mut self ,
748
- goal : Goal < I , G > ,
749
- candidates : & mut Vec < Candidate < I > > ,
750
- ) {
751
- let cx = self . cx ( ) ;
752
- let trait_goal: Goal < I , ty:: TraitPredicate < I > > =
753
- goal. with ( cx, goal. predicate . trait_ref ( cx) ) ;
754
-
755
- let mut trait_candidates_from_env = vec ! [ ] ;
756
- self . probe ( |_| ProbeKind :: ShadowedEnvProbing ) . enter ( |ecx| {
757
- ecx. assemble_param_env_candidates ( trait_goal, & mut trait_candidates_from_env) ;
758
- ecx. assemble_alias_bound_candidates ( trait_goal, & mut trait_candidates_from_env) ;
759
- } ) ;
758
+ proven_via : Option < TraitGoalProvenVia > ,
759
+ candidates : Vec < Candidate < I > > ,
760
+ ) -> QueryResult < I > {
761
+ let Some ( proven_via) = proven_via else {
762
+ // We don't care about overflow. If proving the trait goal overflowed, then
763
+ // it's enough to report an overflow error for that, we don't also have to
764
+ // overflow during normalization.
765
+ return Ok ( self . make_ambiguous_response_no_constraints ( MaybeCause :: Ambiguity ) ) ;
766
+ } ;
760
767
761
- if !trait_candidates_from_env. is_empty ( ) {
762
- let trait_env_result = self . merge_candidates ( trait_candidates_from_env) ;
763
- match trait_env_result. unwrap ( ) . value . certainty {
764
- // If proving the trait goal succeeds by using the env,
765
- // we freely drop all impl candidates.
766
- //
767
- // FIXME(@lcnr): It feels like this could easily hide
768
- // a forced ambiguity candidate added earlier.
769
- // This feels dangerous.
770
- Certainty :: Yes => {
771
- candidates. retain ( |c| match c. source {
772
- CandidateSource :: Impl ( _) | CandidateSource :: BuiltinImpl ( _) => {
773
- debug ! ( ?c, "discard impl candidate" ) ;
774
- false
775
- }
776
- CandidateSource :: ParamEnv ( _) | CandidateSource :: AliasBound => true ,
777
- CandidateSource :: CoherenceUnknowable => panic ! ( "uh oh" ) ,
778
- } ) ;
779
- }
780
- // If it is still ambiguous we instead just force the whole goal
781
- // to be ambig and wait for inference constraints. See
782
- // tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs
783
- Certainty :: Maybe ( cause) => {
784
- debug ! ( ?cause, "force ambiguity" ) ;
785
- * candidates = self . forced_ambiguity ( cause) . into_iter ( ) . collect ( ) ;
786
- }
787
- }
788
- }
789
- }
768
+ let responses: Vec < _ > = match proven_via {
769
+ // Even when a trait bound has been proven using a where-bound, we
770
+ // still need to consider alias-bounds for normalization, see
771
+ // tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
772
+ //
773
+ // FIXME(const_trait_impl): should this behavior also be used by
774
+ // constness checking. Doing so is *at least theoretically* breaking,
775
+ // see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
776
+ TraitGoalProvenVia :: ParamEnv | TraitGoalProvenVia :: AliasBound => candidates
777
+ . iter ( )
778
+ . filter ( |c| {
779
+ matches ! ( c. source, CandidateSource :: AliasBound | CandidateSource :: ParamEnv ( _) )
780
+ } )
781
+ . map ( |c| c. result )
782
+ . collect ( ) ,
783
+ TraitGoalProvenVia :: Misc => candidates. iter ( ) . map ( |c| c. result ) . collect ( ) ,
784
+ } ;
790
785
791
- /// If there are multiple ways to prove a trait or projection goal, we have
792
- /// to somehow try to merge the candidates into one. If that fails, we return
793
- /// ambiguity.
794
- #[ instrument( level = "debug" , skip( self ) , ret) ]
795
- pub ( super ) fn merge_candidates ( & mut self , candidates : Vec < Candidate < I > > ) -> QueryResult < I > {
796
- // First try merging all candidates. This is complete and fully sound.
797
- let responses = candidates. iter ( ) . map ( |c| c. result ) . collect :: < Vec < _ > > ( ) ;
798
- if let Some ( result) = self . try_merge_responses ( & responses) {
799
- return Ok ( result) ;
800
- } else {
801
- self . flounder ( & responses)
802
- }
786
+ self . try_merge_responses ( & responses) . map_or_else ( || self . flounder ( & responses) , Ok )
803
787
}
804
788
}
0 commit comments