From 3cd8baa9d1f4bbfdc059e33a43198f31a6c3aeea Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jan 2023 21:24:06 +0000 Subject: [PATCH 01/10] trait_goals: Assemble alias and object candidates --- .../src/solve/assembly.rs | 24 +++++ .../src/solve/project_goals.rs | 16 ++++ .../src/solve/trait_goals.rs | 89 ++++++++++++++++++- 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index e9ddad11ff23e..9c7a35382091e 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -44,6 +44,18 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { goal: Goal<'tcx, Self>, impl_def_id: DefId, ); + + fn consider_alias_bound_candidates( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + alias_ty: ty::AliasTy<'tcx>, + ); + + fn consider_object_bound_candidates( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + object_bounds: &'tcx ty::List>, + ); } /// An abstraction which correctly deals with the canonical results for candidates. @@ -69,6 +81,8 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { acx.assemble_impl_candidates(goal); + acx.assemble_bound_candidates(goal); + acx.candidates } @@ -147,4 +161,14 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id), ); } + + fn assemble_bound_candidates(&mut self, goal: Goal<'tcx, G>) { + match *goal.predicate.self_ty().kind() { + ty::Alias(_, alias_ty) => G::consider_alias_bound_candidates(self, goal, alias_ty), + ty::Dynamic(predicates, _, _) => { + G::consider_object_bound_candidates(self, goal, predicates) + } + _ => {} + } + } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 3d649bea19ddf..834e8eba14c91 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -211,6 +211,22 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); }) } + + fn consider_alias_bound_candidates( + _acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + _goal: Goal<'tcx, ProjectionPredicate<'tcx>>, + _alias_ty: ty::AliasTy<'tcx>, + ) { + todo!() + } + + fn consider_object_bound_candidates( + _acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + _goal: Goal<'tcx, Self>, + _object_bounds: &'tcx ty::List>, + ) { + todo!() + } } /// 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 c69cc39acb53c..d0e68d0e46d7f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -5,8 +5,9 @@ use std::iter; use super::assembly::{self, AssemblyCtxt}; use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; -use rustc_infer::infer::InferOk; +use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime}; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::util::supertraits; use rustc_infer::traits::ObligationCause; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::TraitPredicate; @@ -36,6 +37,8 @@ pub(super) enum CandidateSource { /// We know that `::Assoc: OtherTrait` holds by looking at /// the bounds on `Trait::Assoc`. AliasBound(usize), + /// Implementation of `Trait` or its supertraits for a `dyn Trait + Send + Sync`. + ObjectBound(usize), /// A builtin implementation for some specific traits, used in cases /// where we cannot rely an ordinary library implementations. /// @@ -68,7 +71,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_impl_candidate( acx: &mut AssemblyCtxt<'_, 'tcx, Self>, - goal: Goal<'tcx, TraitPredicate<'tcx>>, + goal: Goal<'tcx, Self>, impl_def_id: DefId, ) { let tcx = acx.cx.tcx; @@ -108,6 +111,87 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); }) } + + fn consider_alias_bound_candidates( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + alias_ty: ty::AliasTy<'tcx>, + ) { + for (idx, (predicate, _)) in acx + .cx + .tcx + .bound_explicit_item_bounds(alias_ty.def_id) + .subst_iter_copied(acx.cx.tcx, alias_ty.substs) + .enumerate() + { + let Some(poly_trait_pred) = predicate.to_opt_poly_trait_pred() else { continue }; + if poly_trait_pred.skip_binder().def_id() != goal.predicate.def_id() { + continue; + }; + // FIXME: constness? polarity? + let poly_trait_ref = poly_trait_pred.map_bound(|trait_pred| trait_pred.trait_ref); + // FIXME: Faster to do a filter first with a rejection context? + match_poly_trait_ref_against_goal( + acx, + goal, + poly_trait_ref, + CandidateSource::AliasBound(idx), + ); + } + } + + fn consider_object_bound_candidates( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + object_bounds: &'tcx ty::List>, + ) { + if let Some(principal_trait_ref) = object_bounds.principal() { + let principal_trait_ref = + principal_trait_ref.with_self_ty(acx.cx.tcx, goal.predicate.self_ty()); + + for (idx, poly_trait_ref) in supertraits(acx.cx.tcx, principal_trait_ref).enumerate() { + if poly_trait_ref.skip_binder().def_id != goal.predicate.def_id() { + continue; + }; + match_poly_trait_ref_against_goal( + acx, + goal, + poly_trait_ref, + CandidateSource::ObjectBound(idx), + ); + } + } + } +} + +fn match_poly_trait_ref_against_goal<'tcx>( + acx: &mut AssemblyCtxt<'_, 'tcx, TraitPredicate<'tcx>>, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + trait_ref: ty::PolyTraitRef<'tcx>, + candidate: impl FnOnce() -> CandidateSource, +) { + acx.infcx.probe(|_| { + let trait_ref = acx.infcx.replace_bound_vars_with_fresh_vars( + DUMMY_SP, + LateBoundRegionConversionTime::HigherRankedType, + trait_ref, + ); + + let Ok(InferOk { obligations, .. }) = acx + .infcx + .at(&ObligationCause::dummy(), goal.param_env) + .define_opaque_types(false) + .sup(goal.predicate.trait_ref, trait_ref) + .map_err(|e| debug!("failed to equate trait refs: {e:?}")) + else { + return + }; + + let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); + + let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; + acx.try_insert_candidate(candidate(), certainty); + }) } impl<'tcx> EvalCtxt<'tcx> { @@ -169,6 +253,7 @@ impl<'tcx> EvalCtxt<'tcx> { (CandidateSource::Impl(_), _) | (CandidateSource::ParamEnv(_), _) | (CandidateSource::AliasBound(_), _) + | (CandidateSource::ObjectBound(_), _) | (CandidateSource::Builtin, _) | (CandidateSource::AutoImpl, _) => unimplemented!(), } From f7ed4f24e5195e4c3ac9aa0ea2ed576452c61ed0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jan 2023 22:35:46 +0000 Subject: [PATCH 02/10] trait_goals: Handle auto trait bounds in objects --- .../rustc_trait_selection/src/solve/trait_goals.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index d0e68d0e46d7f..aa38c3946775a 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -3,7 +3,7 @@ use std::iter; use super::assembly::{self, AssemblyCtxt}; -use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult}; +use super::{CanonicalGoal, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime}; use rustc_infer::traits::query::NoSolution; @@ -39,6 +39,9 @@ pub(super) enum CandidateSource { AliasBound(usize), /// Implementation of `Trait` or its supertraits for a `dyn Trait + Send + Sync`. ObjectBound(usize), + /// Implementation of `Send` or other explicitly listed *auto* traits for + /// a `dyn Trait + Send + Sync` + ObjectAutoBound(usize), /// A builtin implementation for some specific traits, used in cases /// where we cannot rely an ordinary library implementations. /// @@ -161,6 +164,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ); } } + + for (idx, predicate) in object_bounds.iter().enumerate() { + let ty::ExistentialPredicate::AutoTrait(def_id) = predicate.skip_binder() else { continue }; + if def_id != goal.predicate.def_id() { + continue; + } + acx.try_insert_candidate(CandidateSource::ObjectAutoBound(idx), Certainty::Yes); + } } } @@ -254,6 +265,7 @@ impl<'tcx> EvalCtxt<'tcx> { | (CandidateSource::ParamEnv(_), _) | (CandidateSource::AliasBound(_), _) | (CandidateSource::ObjectBound(_), _) + | (CandidateSource::ObjectAutoBound(_), _) | (CandidateSource::Builtin, _) | (CandidateSource::AutoImpl, _) => unimplemented!(), } From 8f3aa6decd58a55c56f71c38d4faf20925e2d121 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jan 2023 22:40:40 +0000 Subject: [PATCH 03/10] Make auto object predicates more efficient --- .../rustc_trait_selection/src/solve/trait_goals.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index aa38c3946775a..f7b32d714fddc 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -41,7 +41,7 @@ pub(super) enum CandidateSource { ObjectBound(usize), /// Implementation of `Send` or other explicitly listed *auto* traits for /// a `dyn Trait + Send + Sync` - ObjectAutoBound(usize), + ObjectAutoBound, /// A builtin implementation for some specific traits, used in cases /// where we cannot rely an ordinary library implementations. /// @@ -165,12 +165,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } } - for (idx, predicate) in object_bounds.iter().enumerate() { - let ty::ExistentialPredicate::AutoTrait(def_id) = predicate.skip_binder() else { continue }; - if def_id != goal.predicate.def_id() { - continue; - } - acx.try_insert_candidate(CandidateSource::ObjectAutoBound(idx), Certainty::Yes); + if object_bounds.auto_traits().any(|def_id| def_id == goal.predicate.def_id()) { + acx.try_insert_candidate(CandidateSource::ObjectAutoBound, Certainty::Yes); } } } @@ -265,7 +261,7 @@ impl<'tcx> EvalCtxt<'tcx> { | (CandidateSource::ParamEnv(_), _) | (CandidateSource::AliasBound(_), _) | (CandidateSource::ObjectBound(_), _) - | (CandidateSource::ObjectAutoBound(_), _) + | (CandidateSource::ObjectAutoBound, _) | (CandidateSource::Builtin, _) | (CandidateSource::AutoImpl, _) => unimplemented!(), } From 5e0859cae262750cdfb97d57465050eeb05e43b5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jan 2023 21:35:38 +0000 Subject: [PATCH 04/10] trait_goals: param-env candidates --- .../src/solve/assembly.rs | 11 ++++++++++ .../src/solve/project_goals.rs | 7 ++++++ .../src/solve/trait_goals.rs | 22 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 9c7a35382091e..0691aa5ae5386 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -56,6 +56,11 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { goal: Goal<'tcx, Self>, object_bounds: &'tcx ty::List>, ); + + fn consider_param_env_candidates( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + ); } /// An abstraction which correctly deals with the canonical results for candidates. @@ -83,6 +88,8 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { acx.assemble_bound_candidates(goal); + acx.assemble_param_env_candidates(goal); + acx.candidates } @@ -171,4 +178,8 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { _ => {} } } + + fn assemble_param_env_candidates(&mut self, goal: Goal<'tcx, G>) { + G::consider_param_env_candidates(self, goal); + } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 834e8eba14c91..f8e8e0d5a9982 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -227,6 +227,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) { todo!() } + + fn consider_param_env_candidates( + _acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + _goal: Goal<'tcx, Self>, + ) { + todo!() + } } /// 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 f7b32d714fddc..83aa2b2d47240 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -169,6 +169,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { acx.try_insert_candidate(CandidateSource::ObjectAutoBound, Certainty::Yes); } } + + fn consider_param_env_candidates( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + ) { + for (idx, predicate) in goal.param_env.caller_bounds().iter().enumerate() { + let Some(poly_trait_pred) = predicate.to_opt_poly_trait_pred() else { continue }; + if poly_trait_pred.skip_binder().def_id() != goal.predicate.def_id() { + continue; + }; + // FIXME: constness? polarity? + let poly_trait_ref = poly_trait_pred.map_bound(|trait_pred| trait_pred.trait_ref); + // FIXME: Faster to do a filter first with a rejection context? + + match_poly_trait_ref_against_goal( + acx, + goal, + poly_trait_ref, + CandidateSource::ParamEnv(idx), + ); + } + } } fn match_poly_trait_ref_against_goal<'tcx>( From b32f73e8e6065ea60c4132d5188a5b2bcaeff164 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jan 2023 22:14:38 +0000 Subject: [PATCH 05/10] trait_goals: Auto traits --- .../src/solve/assembly.rs | 13 +++ .../src/solve/project_goals.rs | 7 ++ .../src/solve/trait_goals.rs | 96 ++++++++++++++++++- 3 files changed, 115 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 0691aa5ae5386..5bfdea2b45b61 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -61,6 +61,11 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { acx: &mut AssemblyCtxt<'_, 'tcx, Self>, goal: Goal<'tcx, Self>, ); + + fn consider_auto_trait_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + ); } /// An abstraction which correctly deals with the canonical results for candidates. @@ -90,6 +95,8 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { acx.assemble_param_env_candidates(goal); + acx.assemble_auto_trait_candidates(goal); + acx.candidates } @@ -182,4 +189,10 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { fn assemble_param_env_candidates(&mut self, goal: Goal<'tcx, G>) { G::consider_param_env_candidates(self, goal); } + + fn assemble_auto_trait_candidates(&mut self, goal: Goal<'tcx, G>) { + if self.cx.tcx.trait_is_auto(goal.predicate.trait_def_id(self.cx.tcx)) { + G::consider_auto_trait_candidate(self, goal); + } + } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index f8e8e0d5a9982..cfb2f26057977 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -234,6 +234,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) { todo!() } + + fn consider_auto_trait_candidate( + _acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + _goal: Goal<'tcx, Self>, + ) { + // Auto traits never have associated types + } } /// 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 83aa2b2d47240..8ecc1be2aec19 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -5,7 +5,7 @@ use std::iter; use super::assembly::{self, AssemblyCtxt}; use super::{CanonicalGoal, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; -use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime}; +use rustc_infer::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; use rustc_infer::traits::ObligationCause; @@ -191,6 +191,29 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ); } } + + fn consider_auto_trait_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + ) { + // FIXME: We need to give auto trait candidates less precedence than impl candidates? + acx.infcx.probe(|_| { + let Ok(constituent_tys) = + instantiate_constituent_tys_for_auto_trait(acx.infcx, goal.predicate.self_ty()) else { return }; + let nested_goals = constituent_tys + .into_iter() + .map(|ty| { + Goal::new( + acx.cx.tcx, + goal.param_env, + ty::Binder::dummy(goal.predicate.with_self_ty(acx.cx.tcx, ty)), + ) + }) + .collect(); + let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; + acx.try_insert_candidate(CandidateSource::AutoImpl, certainty); + }) + } } fn match_poly_trait_ref_against_goal<'tcx>( @@ -223,6 +246,77 @@ fn match_poly_trait_ref_against_goal<'tcx>( }) } +// 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>, ()> { + 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(()) + } + + 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)]) + } + } +} + impl<'tcx> EvalCtxt<'tcx> { pub(super) fn compute_trait_goal( &mut self, From 7105d392a013576ed22f1dab322f38ccd71293ba Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 3 Jan 2023 01:47:15 +0000 Subject: [PATCH 06/10] trait_goals: Implement Fn-like goals --- .../src/solve/assembly.rs | 34 +++++++++++++++ .../src/solve/project_goals.rs | 9 ++++ .../src/solve/trait_goals.rs | 43 ++++++++++++++++++- .../rustc_trait_selection/src/traits/mod.rs | 1 + 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 5bfdea2b45b61..16d85e6524419 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -1,5 +1,7 @@ //! Code shared by trait and projection goals for candidate assembly. +use crate::traits::TupleArgumentsFlag; + use super::infcx_ext::InferCtxtExt; use super::{ fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, @@ -66,6 +68,13 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { acx: &mut AssemblyCtxt<'_, 'tcx, Self>, goal: Goal<'tcx, Self>, ); + + fn consider_fn_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + bound_sig: ty::PolyFnSig<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ); } /// An abstraction which correctly deals with the canonical results for candidates. @@ -97,6 +106,8 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { acx.assemble_auto_trait_candidates(goal); + acx.assemble_fn_like_candidates(goal); + acx.candidates } @@ -195,4 +206,27 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { G::consider_auto_trait_candidate(self, goal); } } + + fn assemble_fn_like_candidates(&mut self, goal: Goal<'tcx, G>) { + let tcx = self.cx.tcx; + let trait_def_id = goal.predicate.trait_def_id(tcx); + if let Some(goal_kind) = tcx.fn_trait_kind_from_def_id(trait_def_id) { + match *goal.predicate.self_ty().kind() { + ty::FnDef(def_id, substs) => { + G::consider_fn_candidate(self, goal, tcx.bound_fn_sig(def_id).subst(tcx, substs), TupleArgumentsFlag::Yes) + } + ty::FnPtr(sig) => { + G::consider_fn_candidate(self, goal, sig, TupleArgumentsFlag::Yes) + } + ty::Closure(_, substs) => { + if let Some(kind) = self.infcx.closure_kind(substs) + && kind.extends(goal_kind) + { + G::consider_fn_candidate(self, goal, substs.as_closure().sig(), TupleArgumentsFlag::No) + } + } + _ => {} + } + } + } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index cfb2f26057977..6601879ea1c81 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -241,6 +241,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) { // Auto traits never have associated types } + + fn consider_fn_candidate( + _acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + _goal: Goal<'tcx, Self>, + _bound_sig: ty::PolyFnSig<'tcx>, + _tuple_arguments: crate::traits::TupleArgumentsFlag, + ) { + todo!() + } } /// 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 8ecc1be2aec19..bbb7ec15141e8 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,9 +2,12 @@ use std::iter; +use crate::traits::TupleArgumentsFlag; + use super::assembly::{self, AssemblyCtxt}; use super::{CanonicalGoal, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; +use rustc_hir::Unsafety; use rustc_infer::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; @@ -13,6 +16,7 @@ use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::TraitPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; +use rustc_target::spec::abi::Abi; #[allow(dead_code)] // FIXME: implement and use all variants. #[derive(Debug, Clone, Copy)] @@ -53,6 +57,9 @@ pub(super) enum CandidateSource { /// at the constituent types of the `self_ty` to check whether the auto trait /// is implemented for those. AutoImpl, + /// An automatic impl for `Fn`/`FnMut`/`FnOnce` for fn pointers, fn items, + /// and closures. + Fn, } type Candidate<'tcx> = assembly::Candidate<'tcx, TraitPredicate<'tcx>>; @@ -214,6 +221,39 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { acx.try_insert_candidate(CandidateSource::AutoImpl, certainty); }) } + + fn consider_fn_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + bound_sig: ty::PolyFnSig<'tcx>, + tuple_args_flag: TupleArgumentsFlag, + ) { + if bound_sig.unsafety() != Unsafety::Normal || bound_sig.c_variadic() { + return; + } + + // Binder skipped here (*) + let (arguments_tuple, expected_abi) = match tuple_args_flag { + TupleArgumentsFlag::No => (bound_sig.skip_binder().inputs()[0], Abi::RustCall), + TupleArgumentsFlag::Yes => { + (acx.cx.tcx.intern_tup(bound_sig.skip_binder().inputs()), Abi::Rust) + } + }; + if expected_abi != bound_sig.abi() { + return; + } + // (*) Rebound here + let found_trait_ref = bound_sig.rebind( + acx.cx + .tcx + .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), arguments_tuple]), + ); + + acx.infcx.probe(|_| { + // FIXME: This needs to validate that `fn() -> TY` has `TY: Sized`. + match_poly_trait_ref_against_goal(acx, goal, found_trait_ref, CandidateSource::Fn); + }) + } } fn match_poly_trait_ref_against_goal<'tcx>( @@ -379,7 +419,8 @@ impl<'tcx> EvalCtxt<'tcx> { | (CandidateSource::ObjectBound(_), _) | (CandidateSource::ObjectAutoBound, _) | (CandidateSource::Builtin, _) - | (CandidateSource::AutoImpl, _) => unimplemented!(), + | (CandidateSource::AutoImpl, _) + | (CandidateSource::Fn, _) => unimplemented!(), } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 37b40a2f75adc..340922220384c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -60,6 +60,7 @@ pub use self::specialize::{specialization_graph, translate_substs, OverlapError} pub use self::structural_match::{ search_for_adt_const_param_violation, search_for_structural_match_violation, }; +pub use self::util::TupleArgumentsFlag; pub use self::util::{ elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span, elaborate_trait_ref, elaborate_trait_refs, From a54ce9a6f8dbbf8bcfc88309369371957ac0dda9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 3 Jan 2023 03:50:15 +0000 Subject: [PATCH 07/10] trait_goals: Implement support for trait aliases --- compiler/rustc_middle/src/ty/mod.rs | 4 ++++ .../src/solve/assembly.rs | 13 ++++++++++++ .../src/solve/project_goals.rs | 7 +++++++ .../src/solve/trait_goals.rs | 21 +++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 993e95b351484..230429d325ff1 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1252,6 +1252,10 @@ impl<'tcx> InstantiatedPredicates<'tcx> { pub fn is_empty(&self) -> bool { self.predicates.is_empty() } + + pub fn into_iter(self) -> impl Iterator, Span)> { + std::iter::zip(self.predicates, self.spans) + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable, Lift)] diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 16d85e6524419..821029e5ceb44 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -47,6 +47,11 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { impl_def_id: DefId, ); + fn consider_trait_alias_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + ); + fn consider_alias_bound_candidates( acx: &mut AssemblyCtxt<'_, 'tcx, Self>, goal: Goal<'tcx, Self>, @@ -106,6 +111,8 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { acx.assemble_auto_trait_candidates(goal); + acx.assemble_trait_alias_candidates(goal); + acx.assemble_fn_like_candidates(goal); acx.candidates @@ -207,6 +214,12 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { } } + fn assemble_trait_alias_candidates(&mut self, goal: Goal<'tcx, G>) { + if self.cx.tcx.is_trait_alias(goal.predicate.trait_def_id(self.cx.tcx)) { + G::consider_trait_alias_candidate(self, goal); + } + } + fn assemble_fn_like_candidates(&mut self, goal: Goal<'tcx, G>) { let tcx = self.cx.tcx; let trait_def_id = goal.predicate.trait_def_id(tcx); diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 6601879ea1c81..d1e0b05646074 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -212,6 +212,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }) } + fn consider_trait_alias_candidate( + _acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + _goal: Goal<'tcx, Self>, + ) { + // Trait aliases never have (their own) associated types + } + fn consider_alias_bound_candidates( _acx: &mut AssemblyCtxt<'_, 'tcx, Self>, _goal: Goal<'tcx, ProjectionPredicate<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index bbb7ec15141e8..48c97a2808dee 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -23,6 +23,8 @@ use rustc_target::spec::abi::Abi; pub(super) enum CandidateSource { /// Some user-defined impl with the given `DefId`. Impl(DefId), + /// The automatic implementation of a trait alias. + TraitAlias, /// The n-th caller bound in the `param_env` of our goal. /// /// This is pretty much always a bound from the `where`-clauses of the @@ -122,6 +124,24 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } + fn consider_trait_alias_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + ) { + let tcx = acx.cx.tcx; + acx.infcx.probe(|_| { + let nested_goals = tcx + .predicates_of(goal.predicate.def_id()) + .instantiate_own(tcx, goal.predicate.trait_ref.substs) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)) + .collect(); + let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; + acx.try_insert_candidate(CandidateSource::TraitAlias, certainty); + }) + } + fn consider_alias_bound_candidates( acx: &mut AssemblyCtxt<'_, 'tcx, Self>, goal: Goal<'tcx, Self>, @@ -414,6 +434,7 @@ impl<'tcx> EvalCtxt<'tcx> { // FIXME: implement this match (candidate.source, other.source) { (CandidateSource::Impl(_), _) + | (CandidateSource::TraitAlias, _) | (CandidateSource::ParamEnv(_), _) | (CandidateSource::AliasBound(_), _) | (CandidateSource::ObjectBound(_), _) From d14b648330dc2515ee3e2fe7e54764c26635ba5f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 7 Jan 2023 21:14:47 +0000 Subject: [PATCH 08/10] shallow-resolve normalization candidates --- compiler/rustc_trait_selection/src/solve/assembly.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 821029e5ceb44..c875d6555bc6f 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -157,7 +157,10 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items. - let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); + let goal = goal.with( + tcx, + goal.predicate.with_self_ty(tcx, self.infcx.shallow_resolve(normalized_ty)), + ); let mut orig_values = OriginalQueryValues::default(); let goal = self.infcx.canonicalize_query(goal, &mut orig_values); let normalized_candidates = From 3d993302cd09968244d9e68e3db2ff3cb3651176 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 7 Jan 2023 21:53:33 +0000 Subject: [PATCH 09/10] trait_goals: Some builtin traits --- .../src/solve/assembly.rs | 7 +++ .../src/solve/project_goals.rs | 7 +++ .../src/solve/trait_goals.rs | 49 ++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index c875d6555bc6f..67ee168bccb1d 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -80,6 +80,11 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { bound_sig: ty::PolyFnSig<'tcx>, tuple_arguments: TupleArgumentsFlag, ); + + fn consider_builtin_trait_candidates( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + ); } /// An abstraction which correctly deals with the canonical results for candidates. @@ -115,6 +120,8 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { acx.assemble_fn_like_candidates(goal); + G::consider_builtin_trait_candidates(&mut acx, goal); + acx.candidates } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index d1e0b05646074..68cdc24204788 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -257,6 +257,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) { todo!() } + + fn consider_builtin_trait_candidates( + _acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + _goal: Goal<'tcx, Self>, + ) { + todo!(); + } } /// 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 48c97a2808dee..0c790203ae72d 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -13,8 +13,8 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; use rustc_infer::traits::ObligationCause; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::TraitPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{TraitPredicate, TypeVisitable}; use rustc_span::DUMMY_SP; use rustc_target::spec::abi::Abi; @@ -274,6 +274,53 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { match_poly_trait_ref_against_goal(acx, goal, found_trait_ref, CandidateSource::Fn); }) } + + fn consider_builtin_trait_candidates( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + ) { + let lang_items = acx.cx.tcx.lang_items(); + let trait_def_id = goal.predicate.def_id(); + let self_ty = goal.predicate.self_ty(); + + if Some(trait_def_id) == lang_items.sized_trait() { + if self_ty.is_trivially_sized(acx.cx.tcx) { + acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes); + } + } else if Some(trait_def_id) == lang_items.copy_trait() + || Some(trait_def_id) == lang_items.clone_trait() + { + // FIXME + } else if Some(trait_def_id) == lang_items.discriminant_kind_trait() + || Some(trait_def_id) == lang_items.pointee_trait() + { + // `Pointee` and `DiscriminantKind` are implemented by all traits unconditionally + acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes); + } else if Some(trait_def_id) == lang_items.tuple_trait() { + match *self_ty.kind() { + ty::Infer(ty::TyVar(_)) => todo!("ambiguous"), + ty::Tuple(_) => acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes), + _ => {} + } + } else if Some(trait_def_id) == lang_items.pointer_sized() { + let erased_self_ty = acx.cx.tcx.erase_regions(self_ty); + if erased_self_ty.has_non_region_infer() { + todo!("ambiguous") + } + let usize_layout = + acx.cx.tcx.layout_of(ty::ParamEnv::empty().and(acx.cx.tcx.types.usize)).unwrap(); + if let Ok(layout) = acx.cx.tcx.layout_of(goal.param_env.and(self_ty)) + && layout.layout.size() == usize_layout.layout.size() + && layout.layout.align().abi == usize_layout.layout.align().abi + { + acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes); + } + } else if Some(trait_def_id) == lang_items.coerce_unsized_trait() + || Some(trait_def_id) == lang_items.unsize_trait() + { + // FIXME + } + } } fn match_poly_trait_ref_against_goal<'tcx>( From 779a435e8dff2c38e493ca9c9a0dee20ee0cbec2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 14 Jan 2023 05:14:45 +0000 Subject: [PATCH 10/10] Refactor traits that have constituent components --- .../src/solve/trait_goals.rs | 266 ++++++++++++++---- 1 file changed, 218 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 0c790203ae72d..a15f4b2f89edf 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -5,9 +5,10 @@ use std::iter; use crate::traits::TupleArgumentsFlag; use super::assembly::{self, AssemblyCtxt}; -use super::{CanonicalGoal, Certainty, EvalCtxt, Goal, QueryResult}; +use super::{CanonicalGoal, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; + use rustc_hir::def_id::DefId; -use rustc_hir::Unsafety; +use rustc_hir::{Movability, Mutability, Unsafety}; use rustc_infer::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; @@ -225,20 +226,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) { // FIXME: We need to give auto trait candidates less precedence than impl candidates? acx.infcx.probe(|_| { - let Ok(constituent_tys) = - instantiate_constituent_tys_for_auto_trait(acx.infcx, goal.predicate.self_ty()) else { return }; - let nested_goals = constituent_tys - .into_iter() - .map(|ty| { - Goal::new( - acx.cx.tcx, - goal.param_env, - ty::Binder::dummy(goal.predicate.with_self_ty(acx.cx.tcx, ty)), - ) - }) - .collect(); - let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; - acx.try_insert_candidate(CandidateSource::AutoImpl, certainty); + let components = + instantiate_constituent_tys_for_auto_trait(acx.infcx, goal.predicate.self_ty()); + evaluate_goal_for_components(acx, goal, components, CandidateSource::AutoImpl); }) } @@ -284,13 +274,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let self_ty = goal.predicate.self_ty(); if Some(trait_def_id) == lang_items.sized_trait() { - if self_ty.is_trivially_sized(acx.cx.tcx) { - acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes); - } + acx.infcx.probe(|_| { + let components = instantiate_constituent_tys_for_sized_trait(acx.infcx, self_ty); + evaluate_goal_for_components(acx, goal, components, CandidateSource::Builtin); + }) } else if Some(trait_def_id) == lang_items.copy_trait() || Some(trait_def_id) == lang_items.clone_trait() { - // FIXME + acx.infcx.probe(|_| { + let components = + instantiate_constituent_tys_for_copy_clone_trait(acx.infcx, self_ty); + evaluate_goal_for_components(acx, goal, components, CandidateSource::Builtin); + }) } else if Some(trait_def_id) == lang_items.discriminant_kind_trait() || Some(trait_def_id) == lang_items.pointee_trait() { @@ -298,22 +293,32 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes); } else if Some(trait_def_id) == lang_items.tuple_trait() { match *self_ty.kind() { - ty::Infer(ty::TyVar(_)) => todo!("ambiguous"), + ty::Infer(ty::TyVar(_)) => acx.try_insert_candidate( + CandidateSource::Builtin, + Certainty::Maybe(MaybeCause::Ambiguity), + ), ty::Tuple(_) => acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes), _ => {} } } else if Some(trait_def_id) == lang_items.pointer_sized() { let erased_self_ty = acx.cx.tcx.erase_regions(self_ty); if erased_self_ty.has_non_region_infer() { - todo!("ambiguous") - } - let usize_layout = - acx.cx.tcx.layout_of(ty::ParamEnv::empty().and(acx.cx.tcx.types.usize)).unwrap(); - if let Ok(layout) = acx.cx.tcx.layout_of(goal.param_env.and(self_ty)) - && layout.layout.size() == usize_layout.layout.size() - && layout.layout.align().abi == usize_layout.layout.align().abi - { - acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes); + acx.try_insert_candidate( + CandidateSource::Builtin, + Certainty::Maybe(MaybeCause::Ambiguity), + ) + } else { + let usize_layout = acx + .cx + .tcx + .layout_of(ty::ParamEnv::empty().and(acx.cx.tcx.types.usize)) + .unwrap(); + if let Ok(layout) = acx.cx.tcx.layout_of(goal.param_env.and(self_ty)) + && layout.layout.size() == usize_layout.layout.size() + && layout.layout.align().abi == usize_layout.layout.align().abi + { + acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes); + } } } else if Some(trait_def_id) == lang_items.coerce_unsized_trait() || Some(trait_def_id) == lang_items.unsize_trait() @@ -327,7 +332,7 @@ fn match_poly_trait_ref_against_goal<'tcx>( acx: &mut AssemblyCtxt<'_, 'tcx, TraitPredicate<'tcx>>, goal: Goal<'tcx, TraitPredicate<'tcx>>, trait_ref: ty::PolyTraitRef<'tcx>, - candidate: impl FnOnce() -> CandidateSource, + candidate: CandidateSource, ) { acx.infcx.probe(|_| { let trait_ref = acx.infcx.replace_bound_vars_with_fresh_vars( @@ -349,10 +354,49 @@ fn match_poly_trait_ref_against_goal<'tcx>( let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; - acx.try_insert_candidate(candidate(), certainty); + acx.try_insert_candidate(candidate, certainty); }) } +// Evaluate the goal with a new set of self types, combined with a certainty. +fn evaluate_goal_for_components<'tcx>( + acx: &mut AssemblyCtxt<'_, 'tcx, TraitPredicate<'tcx>>, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + components: ComponentsAndCertainty<'tcx>, + candidate: CandidateSource, +) { + let components = match components { + ComponentsAndCertainty::Yes(components) => components, + ComponentsAndCertainty::Maybe => { + acx.try_insert_candidate(candidate, Certainty::Maybe(MaybeCause::Ambiguity)); + return; + } + ComponentsAndCertainty::No => { + return; + } + }; + + let nested_goals = components + .into_iter() + .map(|ty| { + Goal::new( + acx.cx.tcx, + goal.param_env, + ty::Binder::dummy(goal.predicate.with_self_ty(acx.cx.tcx, ty)), + ) + }) + .collect(); + + let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; + acx.try_insert_candidate(candidate, certainty); +} + +enum ComponentsAndCertainty<'tcx> { + Yes(Vec>), + Maybe, + No, +} + // Calculates the constituent types of a type for `auto trait` purposes. // // For types with an "existential" binder, i.e. generator witnesses, we also @@ -360,7 +404,7 @@ fn match_poly_trait_ref_against_goal<'tcx>( fn instantiate_constituent_tys_for_auto_trait<'tcx>( infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>, -) -> Result>, ()> { +) -> ComponentsAndCertainty<'tcx> { let tcx = infcx.tcx; match *ty.kind() { ty::Uint(_) @@ -373,53 +417,179 @@ fn instantiate_constituent_tys_for_auto_trait<'tcx>( | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never - | ty::Char => Ok(vec![]), + | ty::Char => ComponentsAndCertainty::Yes(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(()) - } + | ty::Bound(..) => ComponentsAndCertainty::No, + + ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { - Ok(vec![element_ty]) + ComponentsAndCertainty::Yes(vec![element_ty]) } - ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]), + ty::Array(element_ty, _) | ty::Slice(element_ty) => { + ComponentsAndCertainty::Yes(vec![element_ty]) + } ty::Tuple(ref tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - Ok(tys.iter().collect()) + ComponentsAndCertainty::Yes(tys.iter().collect()) } - ty::Closure(_, ref substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]), + ty::Closure(_, ref substs) => { + ComponentsAndCertainty::Yes(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()]) + ComponentsAndCertainty::Yes(vec![ + generator_substs.tupled_upvars_ty(), + generator_substs.witness(), + ]) } ty::GeneratorWitness(types) => { - Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec()) + ComponentsAndCertainty::Yes(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) if def.is_phantom_data() => { + ComponentsAndCertainty::Yes(vec![substs.type_at(0)]) + } - ty::Adt(def, substs) => Ok(def.all_fields().map(|f| f.ty(tcx, substs)).collect()), + ty::Adt(def, substs) => { + ComponentsAndCertainty::Yes(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)]) + ComponentsAndCertainty::Yes(vec![tcx.bound_type_of(def_id).subst(tcx, substs)]) + } + } +} + +fn instantiate_constituent_tys_for_sized_trait<'tcx>( + infcx: &InferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> ComponentsAndCertainty<'tcx> { + 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(_) => ComponentsAndCertainty::Yes(vec![]), + + ty::Str + | ty::Slice(_) + | ty::Dynamic(..) + | ty::Foreign(..) + | ty::Alias(..) + | ty::Param(_) => ComponentsAndCertainty::No, + + ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe, + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), + + ty::Tuple(tys) => ComponentsAndCertainty::Yes(tys.to_vec()), + + ty::Adt(def, substs) => { + let sized_crit = def.sized_constraint(infcx.tcx); + ComponentsAndCertainty::Yes( + 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>, +) -> ComponentsAndCertainty<'tcx> { + match *ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Error(_) => ComponentsAndCertainty::Yes(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(..) => ComponentsAndCertainty::No, + + ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Generator(_, _, Movability::Static) + | ty::Foreign(..) + | ty::Ref(_, _, Mutability::Mut) + | ty::Adt(_, _) + | ty::Alias(_, _) + | ty::Param(_) => ComponentsAndCertainty::No, + + ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe, + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(), + + ty::Tuple(tys) => ComponentsAndCertainty::Yes(tys.to_vec()), + + ty::Closure(_, substs) => match *substs.as_closure().tupled_upvars_ty().kind() { + ty::Tuple(tys) => ComponentsAndCertainty::Yes(tys.to_vec()), + ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe, + _ => 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) => ComponentsAndCertainty::Yes( + tys.iter().chain([generator.witness()]).collect(), + ), + ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe, + _ => bug!(), + } + } else { + ComponentsAndCertainty::No + } + } + + ty::GeneratorWitness(types) => { + ComponentsAndCertainty::Yes(infcx.replace_bound_vars_with_placeholders(types).to_vec()) } } }