@@ -284,6 +284,21 @@ where
284284 ) -> Vec < Candidate < I > > ;
285285}
286286
287+ /// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit
288+ /// candidate assembly to param-env and alias-bound candidates.
289+ ///
290+ /// On top of being a micro-optimization, as it avoids doing unnecessary work when
291+ /// a param-env trait bound candidate shadows impls for normalization, this is also
292+ /// required to prevent query cycles due to RPITIT inference. See the issue at:
293+ /// <https://github.com/rust-lang/trait-system-refactor-initiative/issues/173>.
294+ pub ( super ) enum AssembleCandidatesFrom {
295+ All ,
296+ /// Only assemble candidates from the environment and alias bounds, ignoring
297+ /// user-written and built-in impls. We only expect `ParamEnv` and `AliasBound`
298+ /// candidates to be assembled.
299+ EnvAndBounds ,
300+ }
301+
287302impl < D , I > EvalCtxt < ' _ , D >
288303where
289304 D : SolverDelegate < Interner = I > ,
@@ -292,6 +307,7 @@ where
292307 pub ( super ) fn assemble_and_evaluate_candidates < G : GoalKind < D > > (
293308 & mut self ,
294309 goal : Goal < I , G > ,
310+ assemble_from : AssembleCandidatesFrom ,
295311 ) -> Vec < Candidate < I > > {
296312 let Ok ( normalized_self_ty) =
297313 self . structurally_normalize_ty ( goal. param_env , goal. predicate . self_ty ( ) )
@@ -318,16 +334,18 @@ where
318334 }
319335 }
320336
321- self . assemble_impl_candidates ( goal, & mut candidates) ;
322-
323- self . assemble_builtin_impl_candidates ( goal, & mut candidates) ;
324-
325337 self . assemble_alias_bound_candidates ( goal, & mut candidates) ;
326-
327- self . assemble_object_bound_candidates ( goal, & mut candidates) ;
328-
329338 self . assemble_param_env_candidates ( goal, & mut candidates) ;
330339
340+ match assemble_from {
341+ AssembleCandidatesFrom :: All => {
342+ self . assemble_impl_candidates ( goal, & mut candidates) ;
343+ self . assemble_builtin_impl_candidates ( goal, & mut candidates) ;
344+ self . assemble_object_bound_candidates ( goal, & mut candidates) ;
345+ }
346+ AssembleCandidatesFrom :: EnvAndBounds => { }
347+ }
348+
331349 candidates
332350 }
333351
@@ -750,6 +768,9 @@ where
750768 } )
751769 }
752770
771+ /// Assemble and merge candidates for goals which are related to an underlying trait
772+ /// goal. Right now, this is normalizes-to and host effect goals.
773+ ///
753774 /// We sadly can't simply take all possible candidates for normalization goals
754775 /// and check whether they result in the same constraints. We want to make sure
755776 /// that trying to normalize an alias doesn't result in constraints which aren't
@@ -778,47 +799,41 @@ where
778799 ///
779800 /// See trait-system-refactor-initiative#124 for more details.
780801 #[ instrument( level = "debug" , skip( self , inject_normalize_to_rigid_candidate) , ret) ]
781- pub ( super ) fn merge_candidates (
802+ pub ( super ) fn assemble_and_merge_candidates < G : GoalKind < D > > (
782803 & mut self ,
783804 proven_via : Option < TraitGoalProvenVia > ,
784- candidates : Vec < Candidate < I > > ,
805+ goal : Goal < I , G > ,
785806 inject_normalize_to_rigid_candidate : impl FnOnce ( & mut EvalCtxt < ' _ , D > ) -> QueryResult < I > ,
786807 ) -> QueryResult < I > {
787808 let Some ( proven_via) = proven_via else {
788809 // We don't care about overflow. If proving the trait goal overflowed, then
789810 // it's enough to report an overflow error for that, we don't also have to
790811 // overflow during normalization.
791- return Ok ( self . make_ambiguous_response_no_constraints ( MaybeCause :: Ambiguity ) ) ;
812+ return Ok ( self . forced_ambiguity ( MaybeCause :: Ambiguity ) ? . result ) ;
792813 } ;
793814
794815 match proven_via {
795816 TraitGoalProvenVia :: ParamEnv | TraitGoalProvenVia :: AliasBound => {
796- let mut considered_candidates = Vec :: new ( ) ;
797- considered_candidates. extend (
798- candidates
799- . iter ( )
800- . filter ( |c| matches ! ( c. source, CandidateSource :: ParamEnv ( _) ) )
801- . map ( |c| c. result ) ,
802- ) ;
803-
804817 // Even when a trait bound has been proven using a where-bound, we
805818 // still need to consider alias-bounds for normalization, see
806- // tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
807- //
819+ // `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
820+ let candidates_from_env_and_bounds: Vec < _ > = self
821+ . assemble_and_evaluate_candidates ( goal, AssembleCandidatesFrom :: EnvAndBounds ) ;
822+
808823 // We still need to prefer where-bounds over alias-bounds however.
809- // See tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs.
810- //
811- // FIXME(const_trait_impl): should this behavior also be used by
812- // constness checking. Doing so is *at least theoretically* breaking,
813- // see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
814- if considered_candidates . is_empty ( ) {
815- considered_candidates . extend (
816- candidates
817- . iter ( )
818- . filter ( |c| matches ! ( c . source , CandidateSource :: AliasBound ) )
819- . map ( |c| c . result ) ,
820- ) ;
821- }
824+ // See ` tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs` .
825+ let mut considered_candidates : Vec < _ > = if candidates_from_env_and_bounds
826+ . iter ( )
827+ . any ( |c| matches ! ( c . source , CandidateSource :: ParamEnv ( _ ) ) )
828+ {
829+ candidates_from_env_and_bounds
830+ . into_iter ( )
831+ . filter ( |c| matches ! ( c . source , CandidateSource :: ParamEnv ( _ ) ) )
832+ . map ( |c| c . result )
833+ . collect ( )
834+ } else {
835+ candidates_from_env_and_bounds . into_iter ( ) . map ( |c| c . result ) . collect ( )
836+ } ;
822837
823838 // If the trait goal has been proven by using the environment, we want to treat
824839 // aliases as rigid if there are no applicable projection bounds in the environment.
@@ -835,6 +850,9 @@ where
835850 }
836851 }
837852 TraitGoalProvenVia :: Misc => {
853+ let candidates =
854+ self . assemble_and_evaluate_candidates ( goal, AssembleCandidatesFrom :: All ) ;
855+
838856 // Prefer "orphaned" param-env normalization predicates, which are used
839857 // (for example, and ideally only) when proving item bounds for an impl.
840858 let candidates_from_env: Vec < _ > = candidates
0 commit comments