From 22d582a38d3a25e9ec1a6062a91ae0d805e0cd26 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 1 Feb 2024 23:48:04 +0000 Subject: For a rigid projection, recursively look at the self type's item bounds --- .../src/solve/assembly/mod.rs | 129 ++++++++++++++------- 1 file changed, 88 insertions(+), 41 deletions(-) (limited to 'compiler/rustc_trait_selection/src/solve') diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 239072dfc8e..150894ba83e 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -542,50 +542,97 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, candidates: &mut Vec>, ) { - let alias_ty = match goal.predicate.self_ty().kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(_, _) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Slice(_) - | ty::RawPtr(_) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(_) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never - | ty::Tuple(_) - | ty::Param(_) - | ty::Placeholder(..) - | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Alias(ty::Inherent, _) - | ty::Alias(ty::Weak, _) - | ty::Error(_) => return, - ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"), - // Excluding IATs and type aliases here as they don't have meaningful item bounds. - ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty, - }; + let _ = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { + let mut self_ty = goal.predicate.self_ty(); + + // For some deeply nested `::A::B::C::D` rigid associated type, + // we should explore the item bounds for all levels, since the + // `associated_type_bounds` feature means that a parent associated + // type may carry bounds for a nested associated type. + loop { + let (kind, alias_ty) = match *self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Error(_) => break, + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"), + + // If we hit infer when normalizing the self type of an alias, + // then bail with ambiguity. + ty::Infer(ty::TyVar(_)) => { + if let Ok(result) = ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + { + candidates + .push(Candidate { source: CandidateSource::AliasBound, result }); + } + break; + } - for assumption in - self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args) - { - match G::consider_alias_bound_candidate(self, goal, assumption) { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::AliasBound, result }) + ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty), + ty::Alias(ty::Inherent | ty::Weak, _) => { + unreachable!("Weak and Inherent aliases should have been normalized away") + } + }; + + for assumption in + ecx.tcx().item_bounds(alias_ty.def_id).instantiate(ecx.tcx(), alias_ty.args) + { + match G::consider_alias_bound_candidate(ecx, goal, assumption) { + Ok(result) => { + candidates + .push(Candidate { source: CandidateSource::AliasBound, result }); + } + Err(NoSolution) => {} + } + } + + // If we have a projection, check that its self type is a rigid projection. + // If so, continue searching. + if kind == ty::Projection { + match ecx.try_normalize_ty(goal.param_env, alias_ty.self_ty()) { + Some(next_self_ty) => self_ty = next_self_ty, + None => { + if let Ok(result) = ecx + .evaluate_added_goals_and_make_canonical_response( + Certainty::OVERFLOW, + ) + { + candidates.push(Candidate { + source: CandidateSource::AliasBound, + result, + }); + } + break; + } + } + } else { + break; } - Err(NoSolution) => (), } - } + }); } /// Check that we are allowed to use an alias bound originating from the self -- cgit 1.4.1-3-g733a5 From 7057188c549cb38e72843c9c344f77659bf67bc9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 7 Feb 2024 00:19:12 +0000 Subject: make it recursive --- .../src/solve/assembly/mod.rs | 174 +++++++++++---------- .../rustc_trait_selection/src/traits/project.rs | 8 +- .../src/traits/select/candidate_assembly.rs | 6 +- 3 files changed, 95 insertions(+), 93 deletions(-) (limited to 'compiler/rustc_trait_selection/src/solve') diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 150894ba83e..380f7ac2d2d 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -542,97 +542,103 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, candidates: &mut Vec>, ) { - let _ = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { - let mut self_ty = goal.predicate.self_ty(); - - // For some deeply nested `::A::B::C::D` rigid associated type, - // we should explore the item bounds for all levels, since the - // `associated_type_bounds` feature means that a parent associated - // type may carry bounds for a nested associated type. - loop { - let (kind, alias_ty) = match *self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(_, _) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Slice(_) - | ty::RawPtr(_) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(_) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never - | ty::Tuple(_) - | ty::Param(_) - | ty::Placeholder(..) - | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Error(_) => break, - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"), - - // If we hit infer when normalizing the self type of an alias, - // then bail with ambiguity. - ty::Infer(ty::TyVar(_)) => { - if let Ok(result) = ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - { - candidates - .push(Candidate { source: CandidateSource::AliasBound, result }); - } - break; - } + let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { + ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates); + }); + } - ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty), - ty::Alias(ty::Inherent | ty::Weak, _) => { - unreachable!("Weak and Inherent aliases should have been normalized away") - } - }; + /// For some deeply nested `::A::B::C::D` rigid associated type, + /// we should explore the item bounds for all levels, since the + /// `associated_type_bounds` feature means that a parent associated + /// type may carry bounds for a nested associated type. + /// + /// If we have a projection, check that its self type is a rigid projection. + /// If so, continue searching by recursively calling after normalization. + // FIXME: This may recurse infinitely, but I can't seem to trigger it without + // hitting another overflow error something. Add a depth parameter needed later. + fn assemble_alias_bound_candidates_recur>( + &mut self, + self_ty: Ty<'tcx>, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let (kind, alias_ty) = match *self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Error(_) => return, + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) | ty::Bound(..) => { + bug!("unexpected self type for `{goal:?}`") + } - for assumption in - ecx.tcx().item_bounds(alias_ty.def_id).instantiate(ecx.tcx(), alias_ty.args) + ty::Infer(ty::TyVar(_)) => { + // If we hit infer when normalizing the self type of an alias, + // then bail with ambiguity. We should never encounter this on + // the *first* iteration of this recursive function. + if let Ok(result) = + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) { - match G::consider_alias_bound_candidate(ecx, goal, assumption) { - Ok(result) => { - candidates - .push(Candidate { source: CandidateSource::AliasBound, result }); - } - Err(NoSolution) => {} - } + candidates.push(Candidate { source: CandidateSource::AliasBound, result }); } + return; + } - // If we have a projection, check that its self type is a rigid projection. - // If so, continue searching. - if kind == ty::Projection { - match ecx.try_normalize_ty(goal.param_env, alias_ty.self_ty()) { - Some(next_self_ty) => self_ty = next_self_ty, - None => { - if let Ok(result) = ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::OVERFLOW, - ) - { - candidates.push(Candidate { - source: CandidateSource::AliasBound, - result, - }); - } - break; - } - } - } else { - break; + ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty), + ty::Alias(ty::Inherent | ty::Weak, _) => { + unreachable!("Weak and Inherent aliases should have been normalized away already") + } + }; + + for assumption in + self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args) + { + match G::consider_alias_bound_candidate(self, goal, assumption) { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::AliasBound, result }); } + Err(NoSolution) => {} } - }); + } + + if kind != ty::Projection { + return; + } + + match self.try_normalize_ty(goal.param_env, alias_ty.self_ty()) { + // Recurse on the self type of the projection. + Some(next_self_ty) => { + self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates); + } + // Bail if we overflow when normalizing, adding an ambiguous candidate. + None => { + if let Ok(result) = + self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) + { + candidates.push(Candidate { source: CandidateSource::AliasBound, result }); + } + } + } } /// Check that we are allowed to use an alias bound originating from the self diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 8f47e45df0c..8cfbd7d3ce6 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1645,11 +1645,9 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( ControlFlow::Continue(()) }, - || { - // `ProjectionCandidateSet` is borrowed in the above closure, - // so just mark ambiguous outside of the closure. - ambiguous = true; - }, + // `ProjectionCandidateSet` is borrowed in the above closure, + // so just mark ambiguous outside of the closure. + || ambiguous = true, ); if ambiguous { diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 248d75aaec7..27dbe0351da 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -201,10 +201,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ControlFlow::Continue(()) }, - || { - // On ambiguity. - candidates.ambiguous = true; - }, + // On ambiguity. + || candidates.ambiguous = true, ); }); } -- cgit 1.4.1-3-g733a5