From b84b1da2dbacdfd7e33428540062380b56bfd8de Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 17 Jan 2023 19:29:52 +0000 Subject: [PATCH 1/7] Canonicalize trait solver response inside probe --- .../src/solve/assembly.rs | 22 +++++++------------ .../rustc_trait_selection/src/solve/mod.rs | 7 ++++++ .../src/solve/project_goals.rs | 10 ++++----- .../src/solve/trait_goals.rs | 10 ++++----- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index cd6e4d2bccd5c..c8611294449e9 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -1,7 +1,7 @@ //! Code shared by trait and projection goals for candidate assembly. use super::infcx_ext::InferCtxtExt; -use super::{CanonicalResponse, Certainty, EvalCtxt, Goal}; +use super::{CanonicalResponse, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::TypeFoldable; @@ -89,18 +89,18 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, impl_def_id: DefId, - ) -> Result; + ) -> QueryResult<'tcx>; fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Result; + ) -> QueryResult<'tcx>; fn consider_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - ) -> Result; + ) -> QueryResult<'tcx>; } impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn assemble_and_evaluate_candidates>( @@ -180,9 +180,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { tcx.for_each_relevant_impl( goal.predicate.trait_def_id(tcx), goal.predicate.self_ty(), - |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) - .and_then(|certainty| self.make_canonical_response(certainty)) - { + |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) { Ok(result) => candidates .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), Err(NoSolution) => (), @@ -203,7 +201,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Err(NoSolution) }; - match result.and_then(|certainty| self.make_canonical_response(certainty)) { + match result { Ok(result) => { candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) } @@ -217,9 +215,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec>, ) { for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { - match G::consider_assumption(self, goal, assumption) - .and_then(|certainty| self.make_canonical_response(certainty)) - { + match G::consider_assumption(self, goal, assumption) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result }) } @@ -268,9 +264,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .subst_iter_copied(self.tcx(), alias_ty.substs) .enumerate() { - match G::consider_assumption(self, goal, assumption) - .and_then(|certainty| self.make_canonical_response(certainty)) - { + match G::consider_assumption(self, goal, assumption) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::AliasBound(i), result }) } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 579cd6a2d59cd..32eb84635b536 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -313,6 +313,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } }) } + + fn evaluate_all_and_make_canonical_response( + &mut self, + goals: Vec>>, + ) -> QueryResult<'tcx> { + self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty)) + } } #[instrument(level = "debug", skip(infcx), ret)] diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 0658836fb9cd3..1d85d31705ad4 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -191,7 +191,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, impl_def_id: DefId, - ) -> Result { + ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); @@ -229,7 +229,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { impl_def_id )? else { let certainty = Certainty::Maybe(MaybeCause::Ambiguity); - return Ok(trait_ref_certainty.unify_and(certainty)); + return ecx.make_canonical_response(trait_ref_certainty.unify_and(certainty)); }; if !assoc_def.item.defaultness(tcx).has_value() { @@ -286,14 +286,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { let rhs_certainty = ecx.evaluate_all(nested_goals).expect("failed to unify with unconstrained term"); - Ok(trait_ref_certainty.unify_and(rhs_certainty)) + ecx.make_canonical_response(trait_ref_certainty.unify_and(rhs_certainty)) }) } fn consider_builtin_sized_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Result { + ) -> QueryResult<'tcx> { bug!("`Sized` does not have an associated type: {:?}", goal); } @@ -301,7 +301,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { _ecx: &mut EvalCtxt<'_, 'tcx>, _goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - ) -> Result { + ) -> QueryResult<'tcx> { if let Some(_poly_projection_pred) = assumption.to_opt_poly_projection_pred() { unimplemented!() } else { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index bbe175d5cc853..111758c77d99c 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -4,7 +4,7 @@ use std::iter; use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; -use super::{Certainty, EvalCtxt, Goal, QueryResult}; +use super::{EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; @@ -29,7 +29,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, - ) -> Result { + ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -53,14 +53,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { .into_iter() .map(|pred| goal.with(tcx, pred)); nested_goals.extend(where_clause_bounds); - ecx.evaluate_all(nested_goals) + ecx.evaluate_all_and_make_canonical_response(nested_goals) }) } fn consider_builtin_sized_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, _goal: Goal<'tcx, Self>, - ) -> Result { + ) -> QueryResult<'tcx> { unimplemented!(); } @@ -68,7 +68,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { _ecx: &mut EvalCtxt<'_, 'tcx>, _goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - ) -> Result { + ) -> QueryResult<'tcx> { if let Some(_poly_trait_pred) = assumption.to_opt_poly_trait_pred() { unimplemented!() } else { From f99b273d577914ee0c9073e2e6144aee17354717 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 17 Jan 2023 19:50:50 +0000 Subject: [PATCH 2/7] implement consider_assumption --- .../src/solve/infcx_ext.rs | 26 ++++++++++++++ .../src/solve/project_goals.rs | 35 ++++++++++++++++--- .../src/solve/trait_goals.rs | 19 +++++++--- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/infcx_ext.rs b/compiler/rustc_trait_selection/src/solve/infcx_ext.rs index 9b7feb5053787..47e6c93016a0e 100644 --- a/compiler/rustc_trait_selection/src/solve/infcx_ext.rs +++ b/compiler/rustc_trait_selection/src/solve/infcx_ext.rs @@ -25,6 +25,13 @@ pub(super) trait InferCtxtExt<'tcx> { lhs: T, rhs: T, ) -> Result>>, NoSolution>; + + fn sup>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + rhs: T, + ) -> Result>>, NoSolution>; } impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { @@ -59,4 +66,23 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { NoSolution }) } + + #[instrument(level = "debug", skip(self, param_env), ret)] + fn sup>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + rhs: T, + ) -> Result>>, NoSolution> { + self.at(&ObligationCause::dummy(), param_env) + .define_opaque_types(false) + .sup(lhs, rhs) + .map(|InferOk { value: (), obligations }| { + obligations.into_iter().map(|o| o.into()).collect() + }) + .map_err(|e| { + debug!(?e, "failed to sup"); + NoSolution + }) + } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 1d85d31705ad4..9ebcb4e4657d8 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -6,7 +6,7 @@ use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_infer::infer::InferCtxt; +use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; @@ -298,12 +298,37 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { } fn consider_assumption( - _ecx: &mut EvalCtxt<'_, 'tcx>, - _goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, ) -> QueryResult<'tcx> { - if let Some(_poly_projection_pred) = assumption.to_opt_poly_projection_pred() { - unimplemented!() + if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() { + ecx.infcx.probe(|_| { + let assumption_projection_pred = ecx.infcx.replace_bound_vars_with_fresh_vars( + DUMMY_SP, + LateBoundRegionConversionTime::HigherRankedType, + poly_projection_pred, + ); + let nested_goals = ecx.infcx.sup( + goal.param_env, + goal.predicate.projection_ty, + assumption_projection_pred.projection_ty, + )?; + let subst_certainty = ecx.evaluate_all(nested_goals)?; + + // The term of our goal should be fully unconstrained, so this should never fail. + // + // It can however be ambiguous when the resolved type is a projection. + let nested_goals = ecx + .infcx + .eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) + .expect("failed to unify with unconstrained term"); + let rhs_certainty = ecx + .evaluate_all(nested_goals) + .expect("failed to unify with unconstrained term"); + + ecx.make_canonical_response(subst_certainty.unify_and(rhs_certainty)) + }) } else { Err(NoSolution) } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 111758c77d99c..362424b0d1431 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -6,10 +6,11 @@ use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; use super::{EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; +use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::TraitPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{ToPolyTraitRef, TraitPredicate}; use rustc_span::DUMMY_SP; impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { @@ -65,12 +66,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_assumption( - _ecx: &mut EvalCtxt<'_, 'tcx>, - _goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, ) -> QueryResult<'tcx> { - if let Some(_poly_trait_pred) = assumption.to_opt_poly_trait_pred() { - unimplemented!() + if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() { + // FIXME: Constness and polarity + ecx.infcx.probe(|_| { + let nested_goals = ecx.infcx.sup( + goal.param_env, + ty::Binder::dummy(goal.predicate.trait_ref), + poly_trait_pred.to_poly_trait_ref(), + )?; + ecx.evaluate_all_and_make_canonical_response(nested_goals) + }) } else { Err(NoSolution) } From 3d87a8e84850e2f5edc0f87ab2f4d3c2b67a48ac Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 17 Jan 2023 18:19:11 +0000 Subject: [PATCH 3/7] Assemble object bound candidates --- .../src/solve/assembly.rs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index c8611294449e9..0a82c14e226fd 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -4,6 +4,7 @@ use super::infcx_ext::InferCtxtExt; use super::{CanonicalResponse, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::util::elaborate_predicates; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use std::fmt::Debug; @@ -119,6 +120,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.assemble_alias_bound_candidates(goal, &mut candidates); + self.assemble_object_bound_candidates(goal, &mut candidates); + candidates } @@ -272,4 +275,53 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } } + + fn assemble_object_bound_candidates>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let self_ty = goal.predicate.self_ty(); + let bounds = 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::Alias(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(_) + | ty::Never + | ty::Tuple(_) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(_) + | ty::Error(_) => return, + ty::Bound(..) => bug!("unexpected bound type: {goal:?}"), + ty::Dynamic(bounds, ..) => bounds, + }; + + let tcx = self.tcx(); + for assumption in + elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty))) + { + match G::consider_assumption(self, goal, assumption.predicate) + { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) + } + Err(NoSolution) => (), + } + } + } } From 45aa5c0f90124e926662f2c2c2d9efca065e0397 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 17 Jan 2023 20:16:30 +0000 Subject: [PATCH 4/7] Auto and alias traits --- compiler/rustc_middle/src/ty/mod.rs | 5 + .../src/solve/assembly.rs | 25 +++- .../src/solve/project_goals.rs | 28 +++- .../src/solve/trait_goals.rs | 132 ++++++++++++++++-- 4 files changed, 169 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e68f27cc0fd5d..db7dd2e5e75d7 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2382,6 +2382,11 @@ impl<'tcx> TyCtxt<'tcx> { self.trait_def(trait_def_id).has_auto_impl } + /// Returns `true` if this is a trait alias. + pub fn trait_is_alias(self, trait_def_id: DefId) -> bool { + self.def_kind(trait_def_id) == DefKind::TraitAlias + } + pub fn trait_is_coinductive(self, trait_def_id: DefId) -> bool { self.trait_is_auto(trait_def_id) || self.lang_items().sized_trait() == Some(trait_def_id) } diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 0a82c14e226fd..0759c42338249 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -92,15 +92,25 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { impl_def_id: DefId, ) -> QueryResult<'tcx>; - fn consider_builtin_sized_candidate( + fn consider_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, ) -> QueryResult<'tcx>; - fn consider_assumption( + fn consider_auto_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + fn consider_trait_alias_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, ) -> QueryResult<'tcx>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -198,7 +208,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) { let lang_items = self.tcx().lang_items(); let trait_def_id = goal.predicate.trait_def_id(self.tcx()); - let result = if lang_items.sized_trait() == Some(trait_def_id) { + let result = if self.tcx().trait_is_auto(trait_def_id) { + G::consider_auto_trait_candidate(self, goal) + } else if self.tcx().trait_is_alias(trait_def_id) { + G::consider_trait_alias_candidate(self, goal) + } else if lang_items.sized_trait() == Some(trait_def_id) { G::consider_builtin_sized_candidate(self, goal) } else { Err(NoSolution) @@ -315,8 +329,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { for assumption in elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty))) { - match G::consider_assumption(self, goal, assumption.predicate) - { + match G::consider_assumption(self, goal, assumption.predicate) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 9ebcb4e4657d8..3c74de09802d5 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -290,13 +290,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }) } - fn consider_builtin_sized_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Sized` does not have an associated type: {:?}", goal); - } - fn consider_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -333,6 +326,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { Err(NoSolution) } } + + fn consider_auto_trait_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("auto traits do not have associated types: {:?}", goal); + } + + fn consider_trait_alias_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("trait aliases do not have associated types: {:?}", goal); + } + + fn consider_builtin_sized_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Sized` does not have an associated type: {:?}", goal); + } } /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 362424b0d1431..1f18de2e7a9e0 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -6,7 +6,7 @@ use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; use super::{EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; -use rustc_infer::infer::LateBoundRegionConversionTime; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -58,13 +58,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } - fn consider_builtin_sized_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - _goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - unimplemented!(); - } - fn consider_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -84,9 +77,61 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) } } + + fn consider_auto_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + ecx.infcx.probe(|_| { + let constituent_tys = + instantiate_constituent_tys_for_auto_trait(ecx.infcx, goal.predicate.self_ty())?; + ecx.evaluate_goal_for_constituent_tys_and_make_canonical_response(goal, constituent_tys) + }) + } + + fn consider_trait_alias_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); + + ecx.infcx.probe(|_| { + let nested_obligations = tcx + .predicates_of(goal.predicate.def_id()) + .instantiate(tcx, goal.predicate.trait_ref.substs); + ecx.evaluate_all_and_make_canonical_response( + nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)).collect(), + ) + }) + } + + fn consider_builtin_sized_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + _goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + unimplemented!(); + } } impl<'tcx> EvalCtxt<'_, 'tcx> { + fn evaluate_goal_for_constituent_tys_and_make_canonical_response( + &mut self, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + constituent_tys: Vec>, + ) -> QueryResult<'tcx> { + self.evaluate_all_and_make_canonical_response( + constituent_tys + .into_iter() + .map(|ty| { + goal.with( + self.tcx(), + ty::Binder::dummy(goal.predicate.with_self_ty(self.tcx(), ty)), + ) + }) + .collect(), + ) + } + pub(super) fn compute_trait_goal( &mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>, @@ -162,3 +207,74 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidate } } + +// Calculates the constituent types of a type for `auto trait` purposes. +// +// For types with an "existential" binder, i.e. generator witnesses, we also +// instantiate the binder with placeholders eagerly. +fn instantiate_constituent_tys_for_auto_trait<'tcx>( + infcx: &InferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Result>, NoSolution> { + let tcx = infcx.tcx; + match *ty.kind() { + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Str + | ty::Error(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Never + | ty::Char => Ok(vec![]), + + ty::Placeholder(..) + | ty::Dynamic(..) + | ty::Param(..) + | ty::Foreign(..) + | ty::Alias(ty::Projection, ..) + | ty::Bound(..) + | ty::Infer(ty::TyVar(_)) => { + // FIXME: Do we need to mark anything as ambiguous here? Yeah? + Err(NoSolution) + } + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), + + ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + Ok(vec![element_ty]) + } + + ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]), + + ty::Tuple(ref tys) => { + // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet + Ok(tys.iter().collect()) + } + + ty::Closure(_, ref substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]), + + ty::Generator(_, ref substs, _) => { + let generator_substs = substs.as_generator(); + Ok(vec![generator_substs.tupled_upvars_ty(), generator_substs.witness()]) + } + + ty::GeneratorWitness(types) => { + Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec()) + } + + // For `PhantomData`, we pass `T`. + ty::Adt(def, substs) if def.is_phantom_data() => Ok(vec![substs.type_at(0)]), + + ty::Adt(def, substs) => Ok(def.all_fields().map(|f| f.ty(tcx, substs)).collect()), + + ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + Ok(vec![tcx.bound_type_of(def_id).subst(tcx, substs)]) + } + } +} From 685c32fd858acf107108abd6d35782532a0064e2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 17 Jan 2023 20:24:58 +0000 Subject: [PATCH 5/7] Sized, Copy/Clone --- .../src/solve/assembly.rs | 10 ++ .../src/solve/project_goals.rs | 7 + .../src/solve/trait_goals.rs | 137 +++++++++++++++++- 3 files changed, 151 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 0759c42338249..2336fb53aec28 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -112,7 +112,13 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + + fn consider_builtin_copy_clone_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; } + impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn assemble_and_evaluate_candidates>( &mut self, @@ -214,6 +220,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_trait_alias_candidate(self, goal) } else if lang_items.sized_trait() == Some(trait_def_id) { G::consider_builtin_sized_candidate(self, goal) + } else if lang_items.copy_trait() == Some(trait_def_id) + || lang_items.clone_trait() == Some(trait_def_id) + { + G::consider_builtin_copy_clone_candidate(self, goal) } else { Err(NoSolution) }; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 3c74de09802d5..5c1f3f02e93a8 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -347,6 +347,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) -> QueryResult<'tcx> { bug!("`Sized` does not have an associated type: {:?}", goal); } + + fn consider_builtin_copy_clone_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal); + } } /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 1f18de2e7a9e0..4d94265dc07f5 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -6,6 +6,7 @@ use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; use super::{EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; +use rustc_hir::{Movability, Mutability}; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; @@ -106,10 +107,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_sized_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - _goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - unimplemented!(); + ecx.infcx.probe(|_| { + let constituent_tys = + instantiate_constituent_tys_for_sized_trait(ecx.infcx, goal.predicate.self_ty())?; + ecx.evaluate_goal_for_constituent_tys_and_make_canonical_response(goal, constituent_tys) + }) + } + + fn consider_builtin_copy_clone_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + ecx.infcx.probe(|_| { + let constituent_tys = instantiate_constituent_tys_for_copy_clone_trait( + ecx.infcx, + goal.predicate.self_ty(), + )?; + ecx.evaluate_goal_for_constituent_tys_and_make_canonical_response(goal, constituent_tys) + }) } } @@ -278,3 +296,116 @@ fn instantiate_constituent_tys_for_auto_trait<'tcx>( } } } + +fn instantiate_constituent_tys_for_sized_trait<'tcx>( + infcx: &InferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Result>, NoSolution> { + match *ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::Never + | ty::Dynamic(_, _, ty::DynStar) + | ty::Error(_) => Ok(vec![]), + + ty::Str + | ty::Slice(_) + | ty::Dynamic(..) + | ty::Foreign(..) + | ty::Alias(..) + | ty::Param(_) => Err(NoSolution), + + ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), + + ty::Tuple(tys) => Ok(tys.to_vec()), + + ty::Adt(def, substs) => { + let sized_crit = def.sized_constraint(infcx.tcx); + Ok(sized_crit + .0 + .iter() + .map(|ty| sized_crit.rebind(*ty).subst(infcx.tcx, substs)) + .collect()) + } + } +} + +fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( + infcx: &InferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Result>, NoSolution> { + match *ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Error(_) => Ok(vec![]), + + // Implementations are provided in core + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, Mutability::Not) + | ty::Array(..) => Err(NoSolution), + + ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Generator(_, _, Movability::Static) + | ty::Foreign(..) + | ty::Ref(_, _, Mutability::Mut) + | ty::Adt(_, _) + | ty::Alias(_, _) + | ty::Param(_) => Err(NoSolution), + + ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), + + ty::Tuple(tys) => Ok(tys.to_vec()), + + ty::Closure(_, substs) => match *substs.as_closure().tupled_upvars_ty().kind() { + ty::Tuple(tys) => Ok(tys.to_vec()), + ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), + _ => bug!(), + }, + + ty::Generator(_, substs, Movability::Movable) => { + if infcx.tcx.features().generator_clone { + let generator = substs.as_generator(); + match *generator.tupled_upvars_ty().kind() { + ty::Tuple(tys) => Ok(tys.iter().chain([generator.witness()]).collect()), + ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), + _ => bug!(), + } + } else { + Err(NoSolution) + } + } + + ty::GeneratorWitness(types) => { + Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec()) + } + } +} From 34127c50803cc0a3c68f1f15480399e19d3813af Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 18 Jan 2023 14:40:16 +0000 Subject: [PATCH 6/7] no subtyping in the new trait solver --- .../src/solve/infcx_ext.rs | 36 +++++++------------ .../src/solve/project_goals.rs | 11 +++--- .../src/solve/trait_goals.rs | 10 +++--- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/infcx_ext.rs b/compiler/rustc_trait_selection/src/solve/infcx_ext.rs index 47e6c93016a0e..42f597c781d25 100644 --- a/compiler/rustc_trait_selection/src/solve/infcx_ext.rs +++ b/compiler/rustc_trait_selection/src/solve/infcx_ext.rs @@ -1,10 +1,10 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; use rustc_span::DUMMY_SP; use super::Goal; @@ -26,12 +26,10 @@ pub(super) trait InferCtxtExt<'tcx> { rhs: T, ) -> Result>>, NoSolution>; - fn sup>( + fn instantiate_bound_vars_with_infer + Copy>( &self, - param_env: ty::ParamEnv<'tcx>, - lhs: T, - rhs: T, - ) -> Result>>, NoSolution>; + value: ty::Binder<'tcx, T>, + ) -> T; } impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { @@ -67,22 +65,14 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }) } - #[instrument(level = "debug", skip(self, param_env), ret)] - fn sup>( + fn instantiate_bound_vars_with_infer + Copy>( &self, - param_env: ty::ParamEnv<'tcx>, - lhs: T, - rhs: T, - ) -> Result>>, NoSolution> { - self.at(&ObligationCause::dummy(), param_env) - .define_opaque_types(false) - .sup(lhs, rhs) - .map(|InferOk { value: (), obligations }| { - obligations.into_iter().map(|o| o.into()).collect() - }) - .map_err(|e| { - debug!(?e, "failed to sup"); - NoSolution - }) + value: ty::Binder<'tcx, T>, + ) -> T { + self.replace_bound_vars_with_fresh_vars( + DUMMY_SP, + LateBoundRegionConversionTime::HigherRankedType, + value, + ) } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 5c1f3f02e93a8..00c7edf0ef8a3 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -6,7 +6,7 @@ use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; @@ -297,12 +297,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) -> QueryResult<'tcx> { if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() { ecx.infcx.probe(|_| { - let assumption_projection_pred = ecx.infcx.replace_bound_vars_with_fresh_vars( - DUMMY_SP, - LateBoundRegionConversionTime::HigherRankedType, - poly_projection_pred, - ); - let nested_goals = ecx.infcx.sup( + let assumption_projection_pred = + ecx.infcx.instantiate_bound_vars_with_infer(poly_projection_pred); + let nested_goals = ecx.infcx.eq( goal.param_env, goal.predicate.projection_ty, assumption_projection_pred.projection_ty, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 4d94265dc07f5..d89759f4dd487 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -10,8 +10,8 @@ use rustc_hir::{Movability, Mutability}; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::ty::TraitPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{ToPolyTraitRef, TraitPredicate}; use rustc_span::DUMMY_SP; impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { @@ -67,10 +67,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() { // FIXME: Constness and polarity ecx.infcx.probe(|_| { - let nested_goals = ecx.infcx.sup( + let assumption_trait_pred = + ecx.infcx.instantiate_bound_vars_with_infer(poly_trait_pred); + let nested_goals = ecx.infcx.eq( goal.param_env, - ty::Binder::dummy(goal.predicate.trait_ref), - poly_trait_pred.to_poly_trait_ref(), + goal.predicate.trait_ref, + assumption_trait_pred.trait_ref, )?; ecx.evaluate_all_and_make_canonical_response(nested_goals) }) From f672436f04938da11c74664f6665f28018f0a390 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 18 Jan 2023 14:56:44 +0000 Subject: [PATCH 7/7] Handle structural traits more gracefully --- .../src/solve/trait_goals.rs | 248 +++--------------- .../solve/trait_goals/structural_traits.rs | 179 +++++++++++++ 2 files changed, 212 insertions(+), 215 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index d89759f4dd487..1ebcfd03c14ea 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -6,7 +6,6 @@ use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; use super::{EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; -use rustc_hir::{Movability, Mutability}; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; @@ -14,6 +13,8 @@ use rustc_middle::ty::TraitPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; +mod structural_traits; + impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn self_ty(self) -> Ty<'tcx> { self.self_ty() @@ -85,11 +86,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - ecx.infcx.probe(|_| { - let constituent_tys = - instantiate_constituent_tys_for_auto_trait(ecx.infcx, goal.predicate.self_ty())?; - ecx.evaluate_goal_for_constituent_tys_and_make_canonical_response(goal, constituent_tys) - }) + ecx.probe_and_evaluate_goal_for_constituent_tys( + goal, + structural_traits::instantiate_constituent_tys_for_auto_trait, + ) } fn consider_trait_alias_candidate( @@ -112,44 +112,46 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - ecx.infcx.probe(|_| { - let constituent_tys = - instantiate_constituent_tys_for_sized_trait(ecx.infcx, goal.predicate.self_ty())?; - ecx.evaluate_goal_for_constituent_tys_and_make_canonical_response(goal, constituent_tys) - }) + ecx.probe_and_evaluate_goal_for_constituent_tys( + goal, + structural_traits::instantiate_constituent_tys_for_sized_trait, + ) } fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - ecx.infcx.probe(|_| { - let constituent_tys = instantiate_constituent_tys_for_copy_clone_trait( - ecx.infcx, - goal.predicate.self_ty(), - )?; - ecx.evaluate_goal_for_constituent_tys_and_make_canonical_response(goal, constituent_tys) - }) + ecx.probe_and_evaluate_goal_for_constituent_tys( + goal, + structural_traits::instantiate_constituent_tys_for_copy_clone_trait, + ) } } impl<'tcx> EvalCtxt<'_, 'tcx> { - fn evaluate_goal_for_constituent_tys_and_make_canonical_response( + /// Convenience function for traits that are structural, i.e. that only + /// have nested subgoals that only change the self type. Unlike other + /// evaluate-like helpers, this does a probe, so it doesn't need to be + /// wrapped in one. + fn probe_and_evaluate_goal_for_constituent_tys( &mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>, - constituent_tys: Vec>, + constituent_tys: impl Fn(&InferCtxt<'tcx>, Ty<'tcx>) -> Result>, NoSolution>, ) -> QueryResult<'tcx> { - self.evaluate_all_and_make_canonical_response( - constituent_tys - .into_iter() - .map(|ty| { - goal.with( - self.tcx(), - ty::Binder::dummy(goal.predicate.with_self_ty(self.tcx(), ty)), - ) - }) - .collect(), - ) + self.infcx.probe(|_| { + self.evaluate_all_and_make_canonical_response( + constituent_tys(self.infcx, goal.predicate.self_ty())? + .into_iter() + .map(|ty| { + goal.with( + self.tcx(), + ty::Binder::dummy(goal.predicate.with_self_ty(self.tcx(), ty)), + ) + }) + .collect(), + ) + }) } pub(super) fn compute_trait_goal( @@ -227,187 +229,3 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidate } } - -// Calculates the constituent types of a type for `auto trait` purposes. -// -// For types with an "existential" binder, i.e. generator witnesses, we also -// instantiate the binder with placeholders eagerly. -fn instantiate_constituent_tys_for_auto_trait<'tcx>( - infcx: &InferCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Result>, NoSolution> { - let tcx = infcx.tcx; - match *ty.kind() { - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Str - | ty::Error(_) - | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Never - | ty::Char => Ok(vec![]), - - ty::Placeholder(..) - | ty::Dynamic(..) - | ty::Param(..) - | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) - | ty::Bound(..) - | ty::Infer(ty::TyVar(_)) => { - // FIXME: Do we need to mark anything as ambiguous here? Yeah? - Err(NoSolution) - } - - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), - - ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { - Ok(vec![element_ty]) - } - - ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]), - - ty::Tuple(ref tys) => { - // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - Ok(tys.iter().collect()) - } - - ty::Closure(_, ref substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]), - - ty::Generator(_, ref substs, _) => { - let generator_substs = substs.as_generator(); - Ok(vec![generator_substs.tupled_upvars_ty(), generator_substs.witness()]) - } - - ty::GeneratorWitness(types) => { - Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec()) - } - - // For `PhantomData`, we pass `T`. - ty::Adt(def, substs) if def.is_phantom_data() => Ok(vec![substs.type_at(0)]), - - ty::Adt(def, substs) => Ok(def.all_fields().map(|f| f.ty(tcx, substs)).collect()), - - ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { - // We can resolve the `impl Trait` to its concrete type, - // which enforces a DAG between the functions requiring - // the auto trait bounds in question. - Ok(vec![tcx.bound_type_of(def_id).subst(tcx, substs)]) - } - } -} - -fn instantiate_constituent_tys_for_sized_trait<'tcx>( - infcx: &InferCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Result>, NoSolution> { - match *ty.kind() { - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::RawPtr(..) - | ty::Char - | ty::Ref(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Array(..) - | ty::Closure(..) - | ty::Never - | ty::Dynamic(_, _, ty::DynStar) - | ty::Error(_) => Ok(vec![]), - - ty::Str - | ty::Slice(_) - | ty::Dynamic(..) - | ty::Foreign(..) - | ty::Alias(..) - | ty::Param(_) => Err(NoSolution), - - ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), - - ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), - - ty::Tuple(tys) => Ok(tys.to_vec()), - - ty::Adt(def, substs) => { - let sized_crit = def.sized_constraint(infcx.tcx); - Ok(sized_crit - .0 - .iter() - .map(|ty| sized_crit.rebind(*ty).subst(infcx.tcx, substs)) - .collect()) - } - } -} - -fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( - infcx: &InferCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Result>, NoSolution> { - match *ty.kind() { - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Error(_) => Ok(vec![]), - - // Implementations are provided in core - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, Mutability::Not) - | ty::Array(..) => Err(NoSolution), - - ty::Dynamic(..) - | ty::Str - | ty::Slice(_) - | ty::Generator(_, _, Movability::Static) - | ty::Foreign(..) - | ty::Ref(_, _, Mutability::Mut) - | ty::Adt(_, _) - | ty::Alias(_, _) - | ty::Param(_) => Err(NoSolution), - - ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), - - ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), - - ty::Tuple(tys) => Ok(tys.to_vec()), - - ty::Closure(_, substs) => match *substs.as_closure().tupled_upvars_ty().kind() { - ty::Tuple(tys) => Ok(tys.to_vec()), - ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), - _ => bug!(), - }, - - ty::Generator(_, substs, Movability::Movable) => { - if infcx.tcx.features().generator_clone { - let generator = substs.as_generator(); - match *generator.tupled_upvars_ty().kind() { - ty::Tuple(tys) => Ok(tys.iter().chain([generator.witness()]).collect()), - ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), - _ => bug!(), - } - } else { - Err(NoSolution) - } - } - - ty::GeneratorWitness(types) => { - Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec()) - } - } -} diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs new file mode 100644 index 0000000000000..bbc0c77253278 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs @@ -0,0 +1,179 @@ +use rustc_hir::{Movability, Mutability}; +use rustc_infer::{infer::InferCtxt, traits::query::NoSolution}; +use rustc_middle::ty::{self, Ty}; + +// Calculates the constituent types of a type for `auto trait` purposes. +// +// For types with an "existential" binder, i.e. generator witnesses, we also +// instantiate the binder with placeholders eagerly. +pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>( + infcx: &InferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Result>, NoSolution> { + let tcx = infcx.tcx; + match *ty.kind() { + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Str + | ty::Error(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Never + | ty::Char => Ok(vec![]), + + ty::Placeholder(..) + | ty::Dynamic(..) + | ty::Param(..) + | ty::Foreign(..) + | ty::Alias(ty::Projection, ..) + | ty::Bound(..) + | ty::Infer(ty::TyVar(_)) => { + // FIXME: Do we need to mark anything as ambiguous here? Yeah? + Err(NoSolution) + } + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), + + ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + Ok(vec![element_ty]) + } + + ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]), + + ty::Tuple(ref tys) => { + // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet + Ok(tys.iter().collect()) + } + + ty::Closure(_, ref substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]), + + ty::Generator(_, ref substs, _) => { + let generator_substs = substs.as_generator(); + Ok(vec![generator_substs.tupled_upvars_ty(), generator_substs.witness()]) + } + + ty::GeneratorWitness(types) => { + Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec()) + } + + // For `PhantomData`, we pass `T`. + ty::Adt(def, substs) if def.is_phantom_data() => Ok(vec![substs.type_at(0)]), + + ty::Adt(def, substs) => Ok(def.all_fields().map(|f| f.ty(tcx, substs)).collect()), + + ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + Ok(vec![tcx.bound_type_of(def_id).subst(tcx, substs)]) + } + } +} + +pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>( + infcx: &InferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Result>, NoSolution> { + match *ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::Never + | ty::Dynamic(_, _, ty::DynStar) + | ty::Error(_) => Ok(vec![]), + + ty::Str + | ty::Slice(_) + | ty::Dynamic(..) + | ty::Foreign(..) + | ty::Alias(..) + | ty::Param(_) => Err(NoSolution), + + ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), + + ty::Tuple(tys) => Ok(tys.to_vec()), + + ty::Adt(def, substs) => { + let sized_crit = def.sized_constraint(infcx.tcx); + Ok(sized_crit + .0 + .iter() + .map(|ty| sized_crit.rebind(*ty).subst(infcx.tcx, substs)) + .collect()) + } + } +} + +pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( + infcx: &InferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Result>, NoSolution> { + match *ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Error(_) => Ok(vec![]), + + // Implementations are provided in core + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, Mutability::Not) + | ty::Array(..) => Err(NoSolution), + + ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Generator(_, _, Movability::Static) + | ty::Foreign(..) + | ty::Ref(_, _, Mutability::Mut) + | ty::Adt(_, _) + | ty::Alias(_, _) + | ty::Param(_) => Err(NoSolution), + + ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"), + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), + + ty::Tuple(tys) => Ok(tys.to_vec()), + + ty::Closure(_, substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]), + + ty::Generator(_, substs, Movability::Movable) => { + if infcx.tcx.features().generator_clone { + let generator = substs.as_generator(); + Ok(vec![generator.tupled_upvars_ty(), generator.witness()]) + } else { + Err(NoSolution) + } + } + + ty::GeneratorWitness(types) => { + Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec()) + } + } +}