@@ -11,6 +11,7 @@ use rustc_type_ir::visit::TypeVisitableExt as _;
1111use rustc_type_ir:: { self as ty, Interner , TypingMode , Upcast as _, elaborate} ;
1212use tracing:: { debug, instrument} ;
1313
14+ use super :: trait_goals:: TraitGoalProvenVia ;
1415use crate :: delegate:: SolverDelegate ;
1516use crate :: solve:: inspect:: ProbeKind ;
1617use crate :: solve:: {
@@ -337,15 +338,6 @@ where
337338
338339 self . assemble_param_env_candidates ( goal, & mut candidates) ;
339340
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-
349341 candidates
350342 }
351343
@@ -500,7 +492,7 @@ where
500492 goal : Goal < I , G > ,
501493 candidates : & mut Vec < Candidate < I > > ,
502494 ) {
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 ( ) {
504496 candidates. extend ( G :: probe_and_consider_implied_clause (
505497 self ,
506498 CandidateSource :: ParamEnv ( i) ,
@@ -733,72 +725,64 @@ where
733725 } )
734726 }
735727
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+ /// ```
740747 ///
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 (
747757 & 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+ } ;
760767
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+ } ;
790785
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 )
803787 }
804788}
0 commit comments