From 7e66c0b7edaf680f26c6170d81ce18869345046e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 6 Jul 2023 02:53:21 +0000 Subject: [PATCH 1/5] Normalize the RHS of an unsize goal --- .../rustc_middle/src/traits/solve/inspect.rs | 6 +- .../src/traits/solve/inspect/format.rs | 3 + .../src/solve/eval_ctxt/select.rs | 10 +- .../src/solve/trait_goals.rs | 196 ++++++++++++------ .../traits/new-solver/normalize-unsize-rhs.rs | 21 ++ 5 files changed, 168 insertions(+), 68 deletions(-) create mode 100644 tests/ui/traits/new-solver/normalize-unsize-rhs.rs diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 8698cf8602262..e793f481995f9 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -73,8 +73,12 @@ pub struct GoalCandidate<'tcx> { pub enum CandidateKind<'tcx> { /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, + DynUpcastingAssembly, /// A normal candidate for proving a goal - Candidate { name: String, result: QueryResult<'tcx> }, + Candidate { + name: String, + result: QueryResult<'tcx>, + }, } impl Debug for GoalCandidate<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index d0d2080b6a160..39f84279259c6 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { CandidateKind::NormalizedSelfTyAssembly => { writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } + CandidateKind::DynUpcastingAssembly => { + writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:") + } CandidateKind::Candidate { name, result } => { writeln!(self.f, "CANDIDATE {}: {:?}", name, result) } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index a6d118d8cc29d..27d877b84ddff 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -330,9 +330,13 @@ fn rematch_unsize<'tcx>( mut nested: Vec>, ) -> SelectionResult<'tcx, Selection<'tcx>> { let tcx = infcx.tcx; - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - + let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); + let b_ty = structurally_normalize( + goal.predicate.trait_ref.args.type_at(1), + infcx, + goal.param_env, + &mut nested, + ); match (a_ty.kind(), b_ty.kind()) { (_, &ty::Dynamic(data, region, ty::Dyn)) => { // Check that the type implements all of the predicates of the def-id. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 930e62d638831..69b30f0a1a886 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,12 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use super::assembly::{self, structural_traits}; +use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::solve::inspect::CandidateKind; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; use rustc_middle::traits::Reveal; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -376,11 +378,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tcx = ecx.tcx(); let a_ty = goal.predicate.self_ty(); let b_ty = goal.predicate.trait_ref.args.type_at(1); - if b_ty.is_ty_var() { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); - } + ecx.probe_candidate("builtin unsize").enter(|ecx| { + let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else { + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( + MaybeCause::Overflow, + )); + }; + match (a_ty.kind(), b_ty.kind()) { + (_, ty::Infer(ty::TyVar(_))) => { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { // Dyn upcasting is handled separately, since due to upcasting, @@ -489,74 +498,90 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tcx = ecx.tcx(); - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { - return vec![]; - }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { - return vec![]; - }; + // Need to wrap in a probe since `normalize_non_self_ty` has side-effects. + ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { + let a_ty = goal.predicate.self_ty(); + let b_ty = goal.predicate.trait_ref.args.type_at(1); + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + return vec![]; + }; - // All of a's auto traits need to be in b's auto traits. - let auto_traits_compatible = - b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); - if !auto_traits_compatible { - return vec![]; - } + // We don't care about `ty::Infer` here or errors here, since we'll + // register an ambiguous/error response in the other unsize candidate + // assembly function. + let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else { + return vec![]; + }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + return vec![]; + }; - let mut unsize_dyn_to_principal = |principal: Option>| { - ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); + // All of a's auto traits need to be in b's auto traits. + let auto_traits_compatible = + b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); + if !auto_traits_compatible { + return vec![]; + } - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal( - goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - }; + let mut unsize_dyn_to_principal = |principal: Option< + ty::PolyExistentialTraitRef<'tcx>, + >| { + ecx.probe_candidate("upcast dyn to principle").enter( + |ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); + + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(goal.param_env, new_a_ty, b_ty)?; + ecx.add_goal(goal.with( + tcx, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }, + ) + }; - let mut responses = vec![]; - // If the principal def ids match (or are both none), then we're not doing - // trait upcasting. We're just removing auto traits (or shortening the lifetime). - if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { - responses.push(response); - } - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() - { - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { - if super_trait_ref.def_id() != b_principal.def_id() { - continue; - } - let erased_trait_ref = super_trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { + let mut responses = vec![]; + // If the principal def ids match (or are both none), then we're not doing + // trait upcasting. We're just removing auto traits (or shortening the lifetime). + if a_data.principal_def_id() == b_data.principal_def_id() { + if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { responses.push(response); } + } else if let Some(a_principal) = a_data.principal() + && let Some(b_principal) = b_data.principal() + { + for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { + if super_trait_ref.def_id() != b_principal.def_id() { + continue; + } + let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }); + if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { + responses.push(response); + } + } } - } - responses + responses + }) } fn consider_builtin_discriminant_kind_candidate( @@ -750,4 +775,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) } + + /// Normalize a non-self type when it is structually matched on when solving + /// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty` + /// for the self type, but for other goals, additional normalization of other + /// arguments may be needed to completely implement the semantics of the trait. + /// + /// This is required when structurally matching on any trait argument that is + /// not the self type. + fn normalize_non_self_ty( + &mut self, + mut ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result>, NoSolution> { + if !matches!(ty.kind(), ty::Alias(..)) { + return Ok(Some(ty)); + } + + self.repeat_while_none( + |_| Ok(None), + |ecx| { + let ty::Alias(_, projection_ty) = *ty.kind() else { + return Some(Ok(Some(ty))); + }; + + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = Goal::new( + ecx.tcx(), + param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + ecx.add_goal(normalizes_to_goal); + if let Err(err) = ecx.try_evaluate_added_goals() { + return Some(Err(err)); + } + + ty = ecx.resolve_vars_if_possible(normalized_ty); + None + }, + ) + } } diff --git a/tests/ui/traits/new-solver/normalize-unsize-rhs.rs b/tests/ui/traits/new-solver/normalize-unsize-rhs.rs new file mode 100644 index 0000000000000..1bb5c9b4111a7 --- /dev/null +++ b/tests/ui/traits/new-solver/normalize-unsize-rhs.rs @@ -0,0 +1,21 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait A {} +trait B: A {} + +impl A for usize {} +impl B for usize {} + +trait Mirror { + type Assoc: ?Sized; +} + +impl Mirror for T { + type Assoc = T; +} + +fn main() { + let x = Box::new(1usize) as Box<::Assoc>; + let y = x as Box<::Assoc>; +} From 24eefd08e25b8ffec2df092f0add812ed58875a0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 16 Jul 2023 22:11:03 +0000 Subject: [PATCH 2/5] Make sure to detect trait upcasting coercion even after normalization --- compiler/rustc_hir_typeck/src/coercion.rs | 37 +++++-------------- .../src/solve/eval_ctxt/select.rs | 8 +++- .../traits/new-solver/normalize-unsize-rhs.rs | 2 + .../trait-upcast-lhs-needs-normalization.rs | 2 + 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 3b1d9c8905b40..f8efe4ad585c0 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -622,7 +622,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]) )]; - let mut has_unsized_tuple_coercion = false; let mut has_trait_upcasting_coercion = None; // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid @@ -636,22 +635,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) if traits.contains(&trait_pred.def_id()) => { - let trait_pred = self.resolve_vars_if_possible(trait_pred); - if unsize_did == trait_pred.def_id() { - let self_ty = trait_pred.self_ty(); - let unsize_ty = trait_pred.trait_ref.args[1].expect_ty(); - if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) = - (self_ty.kind(), unsize_ty.kind()) - && data_a.principal_def_id() != data_b.principal_def_id() - { - debug!("coerce_unsized: found trait upcasting coercion"); - has_trait_upcasting_coercion = Some((self_ty, unsize_ty)); - } - if let ty::Tuple(..) = unsize_ty.kind() { - debug!("coerce_unsized: found unsized tuple coercion"); - has_unsized_tuple_coercion = true; - } - } trait_pred } _ => { @@ -659,6 +642,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { continue; } }; + let trait_pred = self.resolve_vars_if_possible(trait_pred); match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { // Uncertain or unimplemented. Ok(None) => { @@ -701,18 +685,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // be silent, as it causes a type mismatch later. } - Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), - } - } + Ok(Some(impl_source)) => { + if matches!(impl_source, traits::ImplSource::TraitUpcasting(..)) { + has_trait_upcasting_coercion = + Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); + } - if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { - feature_err( - &self.tcx.sess.parse_sess, - sym::unsized_tuple_coercion, - self.cause.span, - "unsized tuple coercion is not stable enough for use and is subject to change", - ) - .emit(); + queue.extend(impl_source.nested_obligations()) + } + } } if let Some((sub, sup)) = has_trait_upcasting_coercion diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 27d877b84ddff..1cedbb6b76151 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -315,7 +315,13 @@ fn rematch_object<'tcx>( // If we're upcasting, get the offset of the vtable pointer, otherwise get // the base of the vtable. Ok(Some(if is_upcasting { - ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + // If source and target trait def ids are identical, + // then we are simply removing auto traits. + if source_trait_ref.def_id() == target_trait_ref.def_id() { + ImplSource::Builtin(nested) + } else { + ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + } } else { ImplSource::Object(ImplSourceObjectData { vtable_base, nested }) })) diff --git a/tests/ui/traits/new-solver/normalize-unsize-rhs.rs b/tests/ui/traits/new-solver/normalize-unsize-rhs.rs index 1bb5c9b4111a7..a398ab4f2f2cd 100644 --- a/tests/ui/traits/new-solver/normalize-unsize-rhs.rs +++ b/tests/ui/traits/new-solver/normalize-unsize-rhs.rs @@ -1,6 +1,8 @@ // compile-flags: -Ztrait-solver=next // check-pass +#![feature(trait_upcasting)] + trait A {} trait B: A {} diff --git a/tests/ui/traits/new-solver/trait-upcast-lhs-needs-normalization.rs b/tests/ui/traits/new-solver/trait-upcast-lhs-needs-normalization.rs index 79114b93b78df..43cd773bf3ce6 100644 --- a/tests/ui/traits/new-solver/trait-upcast-lhs-needs-normalization.rs +++ b/tests/ui/traits/new-solver/trait-upcast-lhs-needs-normalization.rs @@ -1,6 +1,8 @@ // check-pass // compile-flags: -Ztrait-solver=next +#![feature(trait_upcasting)] + pub trait A {} pub trait B: A {} From c02d1a65534244532aba0f42a6719d87084865d2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 16 Jul 2023 22:42:46 +0000 Subject: [PATCH 3/5] Restore tuple unsizing feature gate --- compiler/rustc_hir_typeck/src/coercion.rs | 24 +++++++++++++++--- compiler/rustc_middle/src/traits/mod.rs | 12 ++++++--- .../src/traits/structural_impls.rs | 2 ++ .../src/solve/eval_ctxt/select.rs | 3 +++ .../src/traits/project.rs | 6 +++-- .../src/traits/select/confirmation.rs | 25 ++++++++++++------- compiler/rustc_ty_utils/src/instance.rs | 4 ++- 7 files changed, 57 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index f8efe4ad585c0..9a25669ed31d3 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -622,6 +622,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]) )]; + let mut has_unsized_tuple_coercion = false; let mut has_trait_upcasting_coercion = None; // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid @@ -686,11 +687,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } Ok(Some(impl_source)) => { - if matches!(impl_source, traits::ImplSource::TraitUpcasting(..)) { - has_trait_upcasting_coercion = - Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); + match impl_source { + traits::ImplSource::TraitUpcasting(..) => { + has_trait_upcasting_coercion = + Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); + } + traits::ImplSource::TupleUnsizing(_) => { + has_unsized_tuple_coercion = true; + } + _ => {} } - queue.extend(impl_source.nested_obligations()) } } @@ -711,6 +717,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { err.emit(); } + if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { + feature_err( + &self.tcx.sess.parse_sess, + sym::unsized_tuple_coercion, + self.cause.span, + "unsized tuple coercion is not stable enough for use and is subject to change", + ) + .emit(); + } + Ok(coercion) } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index b7ffed57a0be0..fb791d564a266 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -657,6 +657,9 @@ pub enum ImplSource<'tcx, N> { /// Successful resolution for a builtin trait. Builtin(Vec), + // Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y` + TupleUnsizing(Vec), + /// ImplSource for trait upcasting coercion TraitUpcasting(ImplSourceTraitUpcastingData), } @@ -665,7 +668,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { pub fn nested_obligations(self) -> Vec { match self { ImplSource::UserDefined(i) => i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => n, + ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n, ImplSource::Object(d) => d.nested, ImplSource::TraitUpcasting(d) => d.nested, } @@ -674,7 +677,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { pub fn borrow_nested_obligations(&self) -> &[N] { match self { ImplSource::UserDefined(i) => &i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => &n, + ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => &n, ImplSource::Object(d) => &d.nested, ImplSource::TraitUpcasting(d) => &d.nested, } @@ -683,7 +686,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { match self { ImplSource::UserDefined(i) => &mut i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => n, + ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n, ImplSource::Object(d) => &mut d.nested, ImplSource::TraitUpcasting(d) => &mut d.nested, } @@ -701,6 +704,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { }), ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct), ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()), + ImplSource::TupleUnsizing(n) => { + ImplSource::TupleUnsizing(n.into_iter().map(f).collect()) + } ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData { vtable_base: o.vtable_base, nested: o.nested.into_iter().map(f).collect(), diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index e2cd118500b65..b7bce05300d25 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -17,6 +17,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { write!(f, "ImplSourceParamData({:?}, {:?})", n, ct) } + super::ImplSource::TupleUnsizing(ref d) => write!(f, "{:?}", d), + super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 1cedbb6b76151..e2f91d7668dbf 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -456,6 +456,9 @@ fn rematch_unsize<'tcx>( goal.param_env, ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), )); + + // We need to be able to detect tuple unsizing to require its feature gate. + return Ok(Some(ImplSource::TupleUnsizing(nested))); } // FIXME: We *could* ICE here if either: // 1. the certainty is `Certainty::Yes`, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index a39fc1f177185..18470d520f7d6 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1925,7 +1925,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // why we special case object types. false } - | super::ImplSource::TraitUpcasting(_) => { + super::ImplSource::TraitUpcasting(_) + | super::ImplSource::TupleUnsizing(_) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -2005,7 +2006,8 @@ fn confirm_select_candidate<'cx, 'tcx>( } super::ImplSource::Object(_) | super::ImplSource::Param(..) - | super::ImplSource::TraitUpcasting(_) => { + | super::ImplSource::TraitUpcasting(_) + | super::ImplSource::TupleUnsizing(_) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 2cb2895b47642..f423d40b99497 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -114,8 +114,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } BuiltinUnsizeCandidate => { - let data = self.confirm_builtin_unsize_candidate(obligation)?; - ImplSource::Builtin(data) + let source = + self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + let target = self.infcx.shallow_resolve(target); + let data = self.confirm_builtin_unsize_candidate(obligation, source, target)?; + // If the source and target are both unsize goals, then we need to signify that + // this is tuple unsizing so that during unsized coercion we require the proper + // feature gate. + if matches!(source.kind(), ty::Tuple(..)) && matches!(target.kind(), ty::Tuple(..)) + { + ImplSource::TupleUnsizing(data) + } else { + ImplSource::Builtin(data) + } } TraitUpcastingUnsizeCandidate(idx) => { @@ -1000,15 +1012,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_builtin_unsize_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, + source: Ty<'tcx>, + target: Ty<'tcx>, ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); - - // `assemble_candidates_for_unsizing` should ensure there are no late-bound - // regions here. See the comment there for more details. - let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); - let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); - let target = self.infcx.shallow_resolve(target); - debug!(?source, ?target, "confirm_builtin_unsize_candidate"); let mut nested = vec![]; diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 16b1836ba9faf..88eb33fee379a 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -290,7 +290,9 @@ fn resolve_associated_item<'tcx>( None } } - traits::ImplSource::Param(..) | traits::ImplSource::TraitUpcasting(_) => None, + traits::ImplSource::Param(..) + | traits::ImplSource::TraitUpcasting(_) + | traits::ImplSource::TupleUnsizing(_) => None, }) } From de81007d1310580e6060f2356f1938e45cafb92c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Jul 2023 18:36:34 +0000 Subject: [PATCH 4/5] Consolidate trait upcasting and unsize into one normalization --- .../src/solve/assembly/mod.rs | 25 +- .../src/solve/eval_ctxt/select.rs | 13 +- .../src/solve/project_goals.rs | 13 +- .../src/solve/trait_goals.rs | 383 ++++++++++-------- 4 files changed, 237 insertions(+), 197 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 6920e790e71ae..942f408847207 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -94,6 +94,7 @@ pub(super) enum CandidateSource { #[derive(Debug, Clone, Copy)] pub(super) enum BuiltinImplSource { TraitUpcasting, + TupleUnsize, Object, Misc, Ambiguity, @@ -281,20 +282,19 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + /// Consider (possibly several) goals to upcast or unsize a type to another + /// type. + /// /// The most common forms of unsizing are array to slice, and concrete (Sized) /// type into a `dyn Trait`. ADTs and Tuples can also have their final field /// unsized if it's generic. - fn consider_builtin_unsize_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; - + /// /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or /// if `Trait2` is a (transitive) supertrait of `Trait2`. - fn consider_builtin_dyn_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + fn consider_builtin_unsize_and_upcast_candidates( + _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec>; + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, @@ -610,8 +610,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_future_candidate(self, goal) } else if lang_items.gen_trait() == Some(trait_def_id) { G::consider_builtin_generator_candidate(self, goal) - } else if lang_items.unsize_trait() == Some(trait_def_id) { - G::consider_builtin_unsize_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { G::consider_builtin_discriminant_kind_candidate(self, goal) } else if lang_items.destruct_trait() == Some(trait_def_id) { @@ -633,11 +631,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar + Bar` and `dyn Foo: Unsize>` if lang_items.unsize_trait() == Some(trait_def_id) { - for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { - candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting), - result, - }); + for (result, source) in G::consider_builtin_unsize_and_upcast_candidates(self, goal) { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result }); } } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index e2f91d7668dbf..cdfbaedb8c23a 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -116,10 +116,19 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { ), ) => rematch_object(self, goal, nested_obligations), - (Certainty::Maybe(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) + ( + Certainty::Maybe(_), + CandidateSource::BuiltinImpl( + BuiltinImplSource::Misc | BuiltinImplSource::TupleUnsize, + ), + ) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { + rematch_unsize(self, goal, nested_obligations) + } + + (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::TupleUnsize)) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { - rematch_unsize(self, goal, nested_obligations) + Ok(Some(ImplSource::TupleUnsizing(nested_obligations))) } // Technically some builtin impls have nested obligations, but if diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 222ed9939ba24..0f4fec50427ad 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,6 +1,6 @@ use crate::traits::specialization_graph; -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, BuiltinImplSource}; use super::EvalCtxt; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -502,17 +502,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidate( + fn consider_builtin_unsize_and_upcast_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Unsize` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_dyn_upcast_candidates( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec> { + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { bug!("`Unsize` does not have an associated type: {:?}", goal); } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 69b30f0a1a886..7f813c58c84ec 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,6 +1,6 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, BuiltinImplSource}; use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; @@ -8,7 +8,7 @@ use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::Reveal; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -367,7 +367,59 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidate( + fn consider_builtin_unsize_and_upcast_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return vec![]; + } + + ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { + let a_ty = goal.predicate.self_ty(); + // We need to normalize the b_ty since it's matched structurally + // in the other functions below. + let b_ty = match ecx + .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) + { + Ok(Some(b_ty)) if !b_ty.is_ty_var() => b_ty, + Ok(_) => { + return vec![( + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .unwrap(), + BuiltinImplSource::Ambiguity, + )]; + } + Err(_) => return vec![], + }; + + let mut results = vec![]; + results.extend( + ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty) + .into_iter() + .map(|resp| (resp, BuiltinImplSource::TraitUpcasting)), + ); + results.extend( + ecx.consider_builtin_unsize_candidate(goal.param_env, a_ty, b_ty).into_iter().map( + |resp| { + // If we're unsizing from tuple -> tuple, detect + let source = + if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..))) + { + BuiltinImplSource::TupleUnsize + } else { + BuiltinImplSource::Misc + }; + (resp, source) + }, + ), + ); + + results + }) + } + + fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { @@ -375,20 +427,72 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); } - let tcx = ecx.tcx(); - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); + // `DiscriminantKind` is automatically implemented for every type. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } - ecx.probe_candidate("builtin unsize").enter(|ecx| { - let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( - MaybeCause::Overflow, - )); - }; + fn consider_builtin_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + if !goal.param_env.is_const() { + // `Destruct` is automatically implemented for every type in + // non-const environments. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver + Err(NoSolution) + } + } + + fn consider_builtin_transmute_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + // `rustc_transmute` does not have support for type or const params + if goal.has_non_region_placeholders() { + return Err(NoSolution); + } + + // Erase regions because we compute layouts in `rustc_transmute`, + // which will ICE for region vars. + let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); + + let Some(assume) = + rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3)) + else { + return Err(NoSolution); + }; + let certainty = ecx.is_transmutable( + rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, + args.type_at(2), + assume, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + } +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + fn consider_builtin_unsize_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, + ) -> QueryResult<'tcx> { + self.probe_candidate("builtin unsize").enter(|ecx| { + let tcx = ecx.tcx(); match (a_ty.kind(), b_ty.kind()) { - (_, ty::Infer(ty::TyVar(_))) => { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => { + bug!("unexpected type variable in unsize goal") } // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { @@ -413,20 +517,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Check that the type implements all of the predicates of the def-id. // (i.e. the principal, all of the associated types match, and any auto traits) ecx.add_goals( - data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), + data.iter() + .map(|pred| Goal::new(tcx, param_env, pred.with_self_ty(tcx, a_ty))), ); // The type must be Sized to be unsized. - ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty]))); + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::TraitRef::new(tcx, sized_def_id, [a_ty]), + )); // The type must outlive the lifetime of the `dyn` we're unsizing into. - ecx.add_goal( - goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))), - ); + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), + )); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // `[T; n]` -> `[T]` unsizing (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { // We just require that the element type stays the same - ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?; + ecx.eq(param_env, a_elem_ty, b_elem_ty)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // Struct unsizing `Struct` -> `Struct` where `T: Unsize` @@ -457,10 +568,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Finally, we require that `TailA: Unsize` for the tail field // types. - ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; - ecx.add_goal(goal.with( + ecx.eq(param_env, unsized_a_ty, b_ty)?; + ecx.add_goal(Goal::new( tcx, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), + param_env, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_tail_ty, b_tail_ty], + ), )); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } @@ -474,12 +590,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Substitute just the tail field of B., and require that they're equal. let unsized_a_ty = Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); - ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; + ecx.eq(param_env, unsized_a_ty, b_ty)?; // Similar to ADTs, require that the rest of the fields are equal. - ecx.add_goal(goal.with( + ecx.add_goal(Goal::new( tcx, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), + param_env, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [*a_last_ty, *b_last_ty], + ), )); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } @@ -489,164 +610,86 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_dyn_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + &mut self, + param_env: ty::ParamEnv<'tcx>, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, ) -> Vec> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return vec![]; + if a_ty.is_ty_var() || b_ty.is_ty_var() { + bug!("unexpected type variable in unsize goal") } - let tcx = ecx.tcx(); - - // Need to wrap in a probe since `normalize_non_self_ty` has side-effects. - ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { - return vec![]; - }; + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + return vec![]; + }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + return vec![]; + }; - // We don't care about `ty::Infer` here or errors here, since we'll - // register an ambiguous/error response in the other unsize candidate - // assembly function. - let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else { - return vec![]; - }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { - return vec![]; - }; + let tcx = self.tcx(); + // All of a's auto traits need to be in b's auto traits. + let auto_traits_compatible = + b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); + if !auto_traits_compatible { + return vec![]; + } - // All of a's auto traits need to be in b's auto traits. - let auto_traits_compatible = - b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); - if !auto_traits_compatible { - return vec![]; - } + let mut unsize_dyn_to_principal = |principal: Option>| { + self.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - let mut unsize_dyn_to_principal = |principal: Option< - ty::PolyExistentialTraitRef<'tcx>, - >| { - ecx.probe_candidate("upcast dyn to principle").enter( - |ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - ) - }; + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(param_env, new_a_ty, b_ty)?; + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }; - let mut responses = vec![]; - // If the principal def ids match (or are both none), then we're not doing - // trait upcasting. We're just removing auto traits (or shortening the lifetime). - if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { - responses.push(response); + let mut responses = vec![]; + // If the principal def ids match (or are both none), then we're not doing + // trait upcasting. We're just removing auto traits (or shortening the lifetime). + if a_data.principal_def_id() == b_data.principal_def_id() { + if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { + responses.push(response); + } + } else if let Some(a_principal) = a_data.principal() + && let Some(b_principal) = b_data.principal() + { + for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { + if super_trait_ref.def_id() != b_principal.def_id() { + continue; } - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() - { - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { - if super_trait_ref.def_id() != b_principal.def_id() { - continue; - } - let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| { - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) - }); - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { - responses.push(response); - } + let erased_trait_ref = super_trait_ref + .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { + responses.push(response); } } - - responses - }) - } - - fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); - } - - // `DiscriminantKind` is automatically implemented for every type. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - - fn consider_builtin_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); } - if !goal.param_env.is_const() { - // `Destruct` is automatically implemented for every type in - // non-const environments. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver - Err(NoSolution) - } + responses } - fn consider_builtin_transmute_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); - } - - // `rustc_transmute` does not have support for type or const params - if goal.has_non_region_placeholders() { - return Err(NoSolution); - } - - // Erase regions because we compute layouts in `rustc_transmute`, - // which will ICE for region vars. - let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); - - let Some(assume) = - rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3)) - else { - return Err(NoSolution); - }; - - let certainty = ecx.is_transmutable( - rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, - args.type_at(2), - assume, - )?; - ecx.evaluate_added_goals_and_make_canonical_response(certainty) - } -} - -impl<'tcx> EvalCtxt<'_, 'tcx> { // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering From a7ed9c1da7a3d850be67e5cf5ae2fcabeb42de3b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 24 Jul 2023 22:02:52 +0000 Subject: [PATCH 5/5] Make everything builtin! --- .../src/transform/check_consts/check.rs | 5 +- .../src/transform/check_consts/qualifs.rs | 4 +- compiler/rustc_hir_typeck/src/coercion.rs | 11 +- compiler/rustc_middle/src/macros.rs | 4 +- compiler/rustc_middle/src/mir/basic_blocks.rs | 4 +- .../rustc_middle/src/mir/interpret/error.rs | 4 +- compiler/rustc_middle/src/mir/mod.rs | 4 +- compiler/rustc_middle/src/traits/mod.rs | 80 +++----- compiler/rustc_middle/src/traits/select.rs | 4 +- .../src/traits/structural_impls.rs | 36 +--- .../rustc_middle/src/ty/abstract_const.rs | 4 +- compiler/rustc_middle/src/ty/binding.rs | 2 +- .../src/solve/assembly/mod.rs | 115 ++++++------ .../src/solve/eval_ctxt.rs | 36 ++++ .../src/solve/eval_ctxt/select.rs | 171 +++--------------- .../src/solve/project_goals.rs | 3 +- .../src/solve/trait_goals.rs | 150 ++++++++------- .../src/traits/project.rs | 28 +-- .../src/traits/select/confirmation.rs | 88 +++++---- .../rustc_trait_selection/src/traits/util.rs | 6 +- .../src/traits/vtable.rs | 5 +- compiler/rustc_ty_utils/src/instance.rs | 19 +- .../clippy_utils/src/qualify_min_const_fn.rs | 4 +- 23 files changed, 344 insertions(+), 443 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index d8b8fa927c7ee..15a7c0536cbbc 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -8,6 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgKind, GenericArgs}; use rustc_middle::ty::{TraitRef, TypeVisitableExt}; @@ -766,7 +767,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { }; match implsrc { - Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => { + Ok(Some(ImplSource::Param(ty::BoundConstness::ConstIfConst, _))) => { debug!( "const_trait_impl: provided {:?} via where-clause in {:?}", trait_ref, param_env @@ -774,7 +775,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } // Closure: Fn{Once|Mut} - Ok(Some(ImplSource::Builtin(_))) + Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _))) if trait_ref.self_ty().is_closure() && tcx.fn_trait_kind_from_def_id(trait_id).is_some() => { diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 0ef7ace696532..b3730dd26487a 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -7,6 +7,7 @@ use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir; use rustc_middle::mir::*; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_trait_selection::traits::{ self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, @@ -172,7 +173,8 @@ impl Qualif for NeedsNonConstDrop { if !matches!( impl_src, - ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + ImplSource::Builtin(BuiltinImplSource::Misc, _) + | ImplSource::Param(ty::BoundConstness::ConstIfConst, _) ) { // If our const destruct candidate is not ConstDestruct or implied by the param env, // then it's bad diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 9a25669ed31d3..56e485a4a3c1e 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -46,6 +46,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_middle::lint::in_external_macro; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, }; @@ -687,12 +688,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } Ok(Some(impl_source)) => { + // Some builtin coercions are still unstable so we detect + // these here and emit a feature error if coercion doesn't fail + // due to another reason. match impl_source { - traits::ImplSource::TraitUpcasting(..) => { + traits::ImplSource::Builtin( + BuiltinImplSource::TraitUpcasting { .. }, + _, + ) => { has_trait_upcasting_coercion = Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); } - traits::ImplSource::TupleUnsizing(_) => { + traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { has_unsized_tuple_coercion = true; } _ => {} diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index cd1c6c330bc1e..fca16d8e509f1 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -43,7 +43,7 @@ macro_rules! span_bug { #[macro_export] macro_rules! CloneLiftImpls { - ($($ty:ty,)+) => { + ($($ty:ty),+ $(,)?) => { $( impl<'tcx> $crate::ty::Lift<'tcx> for $ty { type Lifted = Self; @@ -59,7 +59,7 @@ macro_rules! CloneLiftImpls { /// allocated data** (i.e., don't need to be folded). #[macro_export] macro_rules! TrivialTypeTraversalImpls { - ($($ty:ty,)+) => { + ($($ty:ty),+ $(,)?) => { $( impl<'tcx> $crate::ty::fold::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty { fn try_fold_with>>( diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 7722e7b47cffa..0ad17e819c744 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -178,9 +178,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> { } } -TrivialTypeTraversalAndLiftImpls! { - Cache, -} +TrivialTypeTraversalAndLiftImpls! { Cache } impl Encodable for Cache { #[inline] diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 372452ea29a8d..0b532c4badcc6 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -66,9 +66,7 @@ impl Into for ReportedErrorInfo { } } -TrivialTypeTraversalAndLiftImpls! { - ErrorHandled, -} +TrivialTypeTraversalAndLiftImpls! { ErrorHandled } pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 97f53a59fd66c..3a9585485155f 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -706,9 +706,7 @@ pub enum BindingForm<'tcx> { RefForGuard, } -TrivialTypeTraversalAndLiftImpls! { - BindingForm<'tcx>, -} +TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> } mod binding_form_impl { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index fb791d564a266..a2b33bb2737ab 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -649,46 +649,31 @@ pub enum ImplSource<'tcx, N> { /// for some type parameter. The `Vec` represents the /// obligations incurred from normalizing the where-clause (if /// any). - Param(Vec, ty::BoundConstness), + Param(ty::BoundConstness, Vec), - /// Virtual calls through an object. - Object(ImplSourceObjectData), - - /// Successful resolution for a builtin trait. - Builtin(Vec), - - // Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y` - TupleUnsizing(Vec), - - /// ImplSource for trait upcasting coercion - TraitUpcasting(ImplSourceTraitUpcastingData), + /// Successful resolution for a builtin impl. + Builtin(BuiltinImplSource, Vec), } impl<'tcx, N> ImplSource<'tcx, N> { pub fn nested_obligations(self) -> Vec { match self { ImplSource::UserDefined(i) => i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n, - ImplSource::Object(d) => d.nested, - ImplSource::TraitUpcasting(d) => d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n, } } pub fn borrow_nested_obligations(&self) -> &[N] { match self { ImplSource::UserDefined(i) => &i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => &n, - ImplSource::Object(d) => &d.nested, - ImplSource::TraitUpcasting(d) => &d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => &n, } } pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { match self { ImplSource::UserDefined(i) => &mut i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n, - ImplSource::Object(d) => &mut d.nested, - ImplSource::TraitUpcasting(d) => &mut d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n, } } @@ -702,20 +687,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { args: i.args, nested: i.nested.into_iter().map(f).collect(), }), - ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct), - ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()), - ImplSource::TupleUnsizing(n) => { - ImplSource::TupleUnsizing(n.into_iter().map(f).collect()) - } - ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData { - vtable_base: o.vtable_base, - nested: o.nested.into_iter().map(f).collect(), - }), - ImplSource::TraitUpcasting(d) => { - ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { - vtable_vptr_slot: d.vtable_vptr_slot, - nested: d.nested.into_iter().map(f).collect(), - }) + ImplSource::Param(ct, n) => ImplSource::Param(ct, n.into_iter().map(f).collect()), + ImplSource::Builtin(source, n) => { + ImplSource::Builtin(source, n.into_iter().map(f).collect()) } } } @@ -739,29 +713,31 @@ pub struct ImplSourceUserDefinedData<'tcx, N> { pub nested: Vec, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct ImplSourceTraitUpcastingData { +#[derive(Copy, Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Debug)] +pub enum BuiltinImplSource { + /// Some builtin impl we don't need to differentiate. This should be used + /// unless more specific information is necessary. + Misc, + /// A builtin impl for trait objects. + /// + /// The vtable is formed by concatenating together the method lists of + /// the base object trait and all supertraits, pointers to supertrait vtable will + /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods + /// in that vtable. + Object { vtable_base: usize }, /// The vtable is formed by concatenating together the method lists of /// the base object trait and all supertraits, pointers to supertrait vtable will /// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable /// within that vtable. - pub vtable_vptr_slot: Option, - - pub nested: Vec, + TraitUpcasting { vtable_vptr_slot: Option }, + /// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`. + /// + /// This needs to be a separate variant as it is still unstable and we need to emit + /// a feature error when using it on stable. + TupleUnsizing, } -#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct ImplSourceObjectData { - /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits, pointers to supertrait vtable will - /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods - /// in that vtable. - pub vtable_base: usize, - - pub nested: Vec, -} +TrivialTypeTraversalAndLiftImpls! { BuiltinImplSource } #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] pub enum ObjectSafetyViolation { diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index f2dda003b99c5..a90d58f5fc17a 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -304,9 +304,7 @@ impl From for OverflowError { } } -TrivialTypeTraversalAndLiftImpls! { - OverflowError, -} +TrivialTypeTraversalAndLiftImpls! { OverflowError } impl<'tcx> From for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index b7bce05300d25..530d1ec5d3530 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -6,20 +6,16 @@ use std::fmt; impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - super::ImplSource::UserDefined(ref v) => write!(f, "{:?}", v), + match self { + super::ImplSource::UserDefined(v) => write!(f, "{:?}", v), - super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d), - - super::ImplSource::Object(ref d) => write!(f, "{:?}", d), + super::ImplSource::Builtin(source, d) => { + write!(f, "Builtin({source:?}, {d:?})") + } - super::ImplSource::Param(ref n, ct) => { + super::ImplSource::Param(ct, n) => { write!(f, "ImplSourceParamData({:?}, {:?})", n, ct) } - - super::ImplSource::TupleUnsizing(ref d) => write!(f, "{:?}", d), - - super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), } } } @@ -33,23 +29,3 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx, ) } } - -impl fmt::Debug for traits::ImplSourceTraitUpcastingData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceTraitUpcastingData(vtable_vptr_slot={:?}, nested={:?})", - self.vtable_vptr_slot, self.nested - ) - } -} - -impl fmt::Debug for traits::ImplSourceObjectData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceObjectData(vtable_base={}, nested={:?})", - self.vtable_base, self.nested - ) - } -} diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 0364a620810a0..cdd8351499b41 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -27,9 +27,7 @@ impl From for NotConstEvaluatable { } } -TrivialTypeTraversalAndLiftImpls! { - NotConstEvaluatable, -} +TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable } pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs index a5b05a4f9b526..2fec8ac90956e 100644 --- a/compiler/rustc_middle/src/ty/binding.rs +++ b/compiler/rustc_middle/src/ty/binding.rs @@ -6,7 +6,7 @@ pub enum BindingMode { BindByValue(Mutability), } -TrivialTypeTraversalAndLiftImpls! { BindingMode, } +TrivialTypeTraversalAndLiftImpls! { BindingMode } impl BindingMode { pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode { diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 942f408847207..ab90db6ff5869 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -3,17 +3,16 @@ use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use crate::traits::coherence; -use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::util::elaborate; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; -use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{fast_reject, TypeFoldable}; +use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; use std::fmt::Debug; @@ -89,17 +88,6 @@ pub(super) enum CandidateSource { AliasBound, } -/// Records additional information about what kind of built-in impl this is. -/// This should only be used by selection. -#[derive(Debug, Clone, Copy)] -pub(super) enum BuiltinImplSource { - TraitUpcasting, - TupleUnsize, - Object, - Misc, - Ambiguity, -} - /// Methods used to assemble candidates for either trait or projection goals. pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq + std::fmt::Display @@ -282,20 +270,6 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - /// Consider (possibly several) goals to upcast or unsize a type to another - /// type. - /// - /// The most common forms of unsizing are array to slice, and concrete (Sized) - /// type into a `dyn Trait`. ADTs and Tuples can also have their final field - /// unsized if it's generic. - /// - /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or - /// if `Trait2` is a (transitive) supertrait of `Trait2`. - fn consider_builtin_unsize_and_upcast_candidates( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; - fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -310,6 +284,25 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + + /// Consider (possibly several) candidates to upcast or unsize a type to another + /// type. + /// + /// The most common forms of unsizing are array to slice, and concrete (Sized) + /// type into a `dyn Trait`. ADTs and Tuples can also have their final field + /// unsized if it's generic. + /// + /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or + /// if `Trait2` is a (transitive) supertrait of `Trait2`. + /// + /// We return the `BuiltinImplSource` for each candidate as it is needed + /// for unsize coercion in hir typeck and because it is difficult to + /// otherwise recompute this for codegen. This is a bit of a mess but the + /// easiest way to maintain the existing behavior for now. + fn consider_builtin_unsize_and_upcast_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -343,7 +336,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Option>> { goal.predicate.self_ty().is_ty_var().then(|| { vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result: self .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) .unwrap(), @@ -412,7 +405,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Certainty::Maybe(MaybeCause::Overflow), )?; Ok(vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result, }]) }, @@ -848,29 +841,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Dynamic(bounds, ..) => bounds, }; - let own_bounds: FxIndexSet<_> = - bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect(); - for assumption in elaborate(tcx, own_bounds.iter().copied()) - // we only care about bounds that match the `Self` type - .filter_only_self() - { - // FIXME: Predicates are fully elaborated in the object type's existential bounds - // list. We want to only consider these pre-elaborated projections, and not other - // projection predicates that we reach by elaborating the principal trait ref, - // since that'll cause ambiguity. - // - // We can remove this when we have implemented lifetime intersections in responses. - if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) { - continue; + // Consider all of the auto-trait and projection bounds, which don't + // need to be recorded as a `BuiltinImplSource::Object` since they don't + // really have a vtable base... + for bound in bounds { + match bound.skip_binder() { + ty::ExistentialPredicate::Trait(_) => { + // Skip principal + } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => { + match G::consider_object_bound_candidate( + self, + goal, + bound.with_self_ty(tcx, self_ty), + ) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), + Err(NoSolution) => (), + } + } } + } - match G::consider_object_bound_candidate(self, goal, assumption) { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object), - result, - }), - Err(NoSolution) => (), - } + // FIXME: We only need to do *any* of this if we're considering a trait goal, + // since we don't need to look at any supertrait or anything if we are doing + // a projection goal. + if let Some(principal) = bounds.principal() { + let principal_trait_ref = principal.with_self_ty(tcx, self_ty); + self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| { + match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object { + vtable_base, + }), + result, + }), + Err(NoSolution) => (), + } + }); } } @@ -890,7 +901,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) { Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result, }), // FIXME: This will be reachable at some point if we're in diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 6e0aa08c307bd..b04d77f0a1a3a 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -25,6 +25,7 @@ use std::io::Write; use std::ops::ControlFlow; use crate::traits::specialization_graph; +use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use super::inspect::ProofTreeBuilder; use super::search_graph::{self, OverflowHandler}; @@ -920,4 +921,39 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Err(ErrorHandled::TooGeneric) => None, } } + + /// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor` + /// for every trait ref encountered (including the principal). Passes both the vtable + /// base and the (optional) vptr slot. + pub(super) fn walk_vtable( + &mut self, + principal: ty::PolyTraitRef<'tcx>, + mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option), + ) { + let tcx = self.tcx(); + let mut offset = 0; + prepare_vtable_segments::<()>(tcx, principal, |segment| { + match segment { + VtblSegment::MetadataDSA => { + offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref); + + supertrait_visitor( + self, + trait_ref, + offset, + emit_vptr.then(|| offset + own_vtable_entries), + ); + + offset += own_vtable_entries; + if emit_vptr { + offset += 1; + } + } + } + ControlFlow::Continue(()) + }); + } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index cdfbaedb8c23a..6045001510e42 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -1,25 +1,20 @@ -use std::ops::ControlFlow; - use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; -use rustc_infer::traits::util::supertraits; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt}; use rustc_infer::traits::{ Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine, }; use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal}; use rustc_middle::traits::{ - ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, - ObligationCause, SelectionError, + BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError, }; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource}; +use crate::solve::assembly::{Candidate, CandidateSource}; use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; use crate::solve::inspect::ProofTreeBuilder; use crate::solve::search_graph::OverflowHandler; -use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use crate::traits::StructurallyNormalizeExt; use crate::traits::TraitEngineExt; @@ -105,47 +100,26 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { rematch_impl(self, goal, def_id, nested_obligations) } - // Rematching the dyn upcast or object goal will instantiate the same nested - // goals that would have caused the ambiguity, so we can still make progress here - // regardless. - // FIXME: This doesn't actually check the object bounds hold here. - ( - _, - CandidateSource::BuiltinImpl( - BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting, - ), - ) => rematch_object(self, goal, nested_obligations), - - ( - Certainty::Maybe(_), - CandidateSource::BuiltinImpl( - BuiltinImplSource::Misc | BuiltinImplSource::TupleUnsize, - ), - ) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { - rematch_unsize(self, goal, nested_obligations) - } - - (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::TupleUnsize)) + (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src)) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { - Ok(Some(ImplSource::TupleUnsizing(nested_obligations))) + rematch_unsize(self, goal, nested_obligations, src) } // Technically some builtin impls have nested obligations, but if // `Certainty::Yes`, then they should've all been verified and don't // need re-checking. - (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => { - Ok(Some(ImplSource::Builtin(nested_obligations))) + (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => { + Ok(Some(ImplSource::Builtin(src, nested_obligations))) } // It's fine not to do anything to rematch these, since there are no // nested obligations. (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => { - Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst))) + Ok(Some(ImplSource::Param(ty::BoundConstness::NotConst, nested_obligations))) } - (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity)) - | (Certainty::Maybe(_), _) => Ok(None), + (Certainty::Maybe(_), _) => Ok(None), } } } @@ -192,11 +166,12 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( } (_, CandidateSource::ParamEnv(_)) => true, + // FIXME: we could prefer earlier vtable bases perhaps... ( - CandidateSource::BuiltinImpl(BuiltinImplSource::Object), - CandidateSource::BuiltinImpl(BuiltinImplSource::Object), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), ) => false, - (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true, + (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true, (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => { tcx.specializes((other_def_id, victim_def_id)) @@ -234,108 +209,6 @@ fn rematch_impl<'tcx>( Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested }))) } -fn rematch_object<'tcx>( - infcx: &InferCtxt<'tcx>, - goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, - mut nested: Vec>, -) -> SelectionResult<'tcx, Selection<'tcx>> { - let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); - let ty::Dynamic(data, _, source_kind) = *a_ty.kind() else { bug!() }; - let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, a_ty); - - let (is_upcasting, target_trait_ref_unnormalized) = - if Some(goal.predicate.def_id()) == infcx.tcx.lang_items().unsize_trait() { - assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*"); - let b_ty = structurally_normalize( - goal.predicate.trait_ref.args.type_at(1), - infcx, - goal.param_env, - &mut nested, - ); - if let ty::Dynamic(data, _, ty::Dyn) = *b_ty.kind() { - // FIXME: We also need to ensure that the source lifetime outlives the - // target lifetime. This doesn't matter for codegen, though, and only - // *really* matters if the goal's certainty is ambiguous. - (true, data.principal().unwrap().with_self_ty(infcx.tcx, a_ty)) - } else { - bug!() - } - } else { - (false, ty::Binder::dummy(goal.predicate.trait_ref)) - }; - - let mut target_trait_ref = None; - for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) { - let result = infcx.commit_if_ok(|_| { - infcx.at(&ObligationCause::dummy(), goal.param_env).eq( - DefineOpaqueTypes::No, - target_trait_ref_unnormalized, - candidate_trait_ref, - ) - - // FIXME: We probably should at least shallowly verify these... - }); - - match result { - Ok(InferOk { value: (), obligations }) => { - target_trait_ref = Some(candidate_trait_ref); - nested.extend(obligations); - break; - } - Err(_) => continue, - } - } - - let target_trait_ref = target_trait_ref.unwrap(); - - let mut offset = 0; - let Some((vtable_base, vtable_vptr_slot)) = - prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| { - match segment { - VtblSegment::MetadataDSA => { - offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref); - - if trait_ref == target_trait_ref { - if emit_vptr { - return ControlFlow::Break(( - offset, - Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)), - )); - } else { - return ControlFlow::Break((offset, None)); - } - } - - offset += own_vtable_entries; - if emit_vptr { - offset += 1; - } - } - } - ControlFlow::Continue(()) - }) - else { - bug!(); - }; - - // If we're upcasting, get the offset of the vtable pointer, otherwise get - // the base of the vtable. - Ok(Some(if is_upcasting { - // If source and target trait def ids are identical, - // then we are simply removing auto traits. - if source_trait_ref.def_id() == target_trait_ref.def_id() { - ImplSource::Builtin(nested) - } else { - ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) - } - } else { - ImplSource::Object(ImplSourceObjectData { vtable_base, nested }) - })) -} - /// The `Unsize` trait is particularly important to coercion, so we try rematch it. /// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait /// goal assembly in the solver, both for soundness and in order to avoid ICEs. @@ -343,6 +216,7 @@ fn rematch_unsize<'tcx>( infcx: &InferCtxt<'tcx>, goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, mut nested: Vec>, + source: BuiltinImplSource, ) -> SelectionResult<'tcx, Selection<'tcx>> { let tcx = infcx.tcx; let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); @@ -379,6 +253,8 @@ fn rematch_unsize<'tcx>( goal.param_env, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), )); + + Ok(Some(ImplSource::Builtin(source, nested))) } // `[T; n]` -> `[T]` unsizing (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { @@ -389,6 +265,8 @@ fn rematch_unsize<'tcx>( .expect("expected rematch to succeed") .into_obligations(), ); + + Ok(Some(ImplSource::Builtin(source, nested))) } // Struct unsizing `Struct` -> `Struct` where `T: Unsize` (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) @@ -439,6 +317,8 @@ fn rematch_unsize<'tcx>( goal.param_env, ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), )); + + Ok(Some(ImplSource::Builtin(source, nested))) } // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize` (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) @@ -467,15 +347,18 @@ fn rematch_unsize<'tcx>( )); // We need to be able to detect tuple unsizing to require its feature gate. - return Ok(Some(ImplSource::TupleUnsizing(nested))); + assert_eq!( + source, + BuiltinImplSource::TupleUnsizing, + "compiler-errors wants to know if this can ever be triggered..." + ); + Ok(Some(ImplSource::Builtin(source, nested))) } // FIXME: We *could* ICE here if either: // 1. the certainty is `Certainty::Yes`, // 2. we're in codegen (which should mean `Certainty::Yes`). - _ => return Ok(None), + _ => Ok(None), } - - Ok(Some(ImplSource::Builtin(nested))) } fn structurally_normalize<'tcx>( diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 0f4fec50427ad..03ade738bb633 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,6 +1,6 @@ use crate::traits::specialization_graph; -use super::assembly::{self, structural_traits, BuiltinImplSource}; +use super::assembly::{self, structural_traits}; use super::EvalCtxt; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -10,6 +10,7 @@ use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 7f813c58c84ec..24ea9b5ea124e 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,15 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits, BuiltinImplSource}; +use super::assembly::{self, structural_traits}; use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::util::supertraits; use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; -use rustc_middle::traits::Reveal; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; @@ -382,37 +381,48 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let b_ty = match ecx .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) { - Ok(Some(b_ty)) if !b_ty.is_ty_var() => b_ty, - Ok(_) => { - return vec![( - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + Ok(Some(b_ty)) => { + // If we have a type var, then bail with ambiguity. + if b_ty.is_ty_var() { + return vec![( + ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ) .unwrap(), - BuiltinImplSource::Ambiguity, + BuiltinImplSource::Misc, + )]; + } else { + b_ty + } + } + Ok(None) => { + return vec![( + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( + MaybeCause::Overflow, + )) + .unwrap(), + BuiltinImplSource::Misc, )]; } Err(_) => return vec![], }; let mut results = vec![]; + results.extend(ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty)); results.extend( - ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty) + ecx.consider_builtin_unsize_candidate(goal.with(ecx.tcx(), (a_ty, b_ty))) .into_iter() - .map(|resp| (resp, BuiltinImplSource::TraitUpcasting)), - ); - results.extend( - ecx.consider_builtin_unsize_candidate(goal.param_env, a_ty, b_ty).into_iter().map( - |resp| { + .map(|resp| { // If we're unsizing from tuple -> tuple, detect let source = if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..))) { - BuiltinImplSource::TupleUnsize + BuiltinImplSource::TupleUnsizing } else { BuiltinImplSource::Misc }; (resp, source) - }, - ), + }), ); results @@ -484,10 +494,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> { fn consider_builtin_unsize_candidate( &mut self, - param_env: ty::ParamEnv<'tcx>, - a_ty: Ty<'tcx>, - b_ty: Ty<'tcx>, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, ) -> QueryResult<'tcx> { + let Goal { param_env, predicate: (a_ty, b_ty) } = goal; self.probe_candidate("builtin unsize").enter(|ecx| { let tcx = ecx.tcx(); match (a_ty.kind(), b_ty.kind()) { @@ -614,7 +623,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, a_ty: Ty<'tcx>, b_ty: Ty<'tcx>, - ) -> Vec> { + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { if a_ty.is_ty_var() || b_ty.is_ty_var() { bug!("unexpected type variable in unsize goal") } @@ -634,57 +643,64 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return vec![]; } - let mut unsize_dyn_to_principal = |principal: Option>| { - self.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(param_env, new_a_ty, b_ty)?; - ecx.add_goal(Goal::new( - tcx, - param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - }; + // Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with + // the supertrait principal and subtyping the types. + let unsize_dyn_to_principal = + |ecx: &mut Self, principal: Option>| { + ecx.probe_candidate("upcast dyn to principle").enter( + |ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); + + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(param_env, new_a_ty, b_ty)?; + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }, + ) + }; let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { - responses.push(response); - } - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() - { - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { - if super_trait_ref.def_id() != b_principal.def_id() { - continue; - } - let erased_trait_ref = super_trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { - responses.push(response); - } + if let Ok(resp) = unsize_dyn_to_principal(self, a_data.principal()) { + responses.push((resp, BuiltinImplSource::Misc)); } + } else if let Some(a_principal) = a_data.principal() { + self.walk_vtable( + a_principal.with_self_ty(tcx, a_ty), + |ecx, new_a_principal, _, vtable_vptr_slot| { + if let Ok(resp) = unsize_dyn_to_principal( + ecx, + Some(new_a_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + })), + ) { + responses + .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot })); + } + }, + ); } responses diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 18470d520f7d6..a35dd1f1a591a 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -4,7 +4,6 @@ use super::check_args_compatible; use super::specialization_graph; use super::translate_args; use super::util; -use super::ImplSourceUserDefinedData; use super::MismatchedProjectionTypes; use super::Obligation; use super::ObligationCause; @@ -13,6 +12,9 @@ use super::Selection; use super::SelectionContext; use super::SelectionError; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; +use rustc_middle::traits::BuiltinImplSource; +use rustc_middle::traits::ImplSource; +use rustc_middle::traits::ImplSourceUserDefinedData; use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -1717,7 +1719,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( }; let eligible = match &impl_source { - super::ImplSource::UserDefined(impl_data) => { + ImplSource::UserDefined(impl_data) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in // codegen (i.e., projection mode is not "any"), and the @@ -1767,7 +1769,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } } } - super::ImplSource::Builtin(..) => { + ImplSource::Builtin(BuiltinImplSource::Misc, _) => { // While a builtin impl may be known to exist, the associated type may not yet // be known. Any type with multiple potential associated types is therefore // not eligible. @@ -1891,7 +1893,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( bug!("unexpected builtin trait with associated type: {trait_ref:?}") } } - super::ImplSource::Param(..) => { + ImplSource::Param(..) => { // This case tell us nothing about the value of an // associated type. Consider: // @@ -1919,14 +1921,14 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // in `assemble_candidates_from_param_env`. false } - super::ImplSource::Object(_) => { + ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => { // Handled by the `Object` projection candidate. See // `assemble_candidates_from_object_ty` for an explanation of // why we special case object types. false } - super::ImplSource::TraitUpcasting(_) - | super::ImplSource::TupleUnsizing(_) => { + ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -1986,8 +1988,8 @@ fn confirm_select_candidate<'cx, 'tcx>( impl_source: Selection<'tcx>, ) -> Progress<'tcx> { match impl_source { - super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), - super::ImplSource::Builtin(data) => { + ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), + ImplSource::Builtin(BuiltinImplSource::Misc, data) => { let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx()); let lang_items = selcx.tcx().lang_items(); if lang_items.gen_trait() == Some(trait_def_id) { @@ -2004,10 +2006,10 @@ fn confirm_select_candidate<'cx, 'tcx>( confirm_builtin_candidate(selcx, obligation, data) } } - super::ImplSource::Object(_) - | super::ImplSource::Param(..) - | super::ImplSource::TraitUpcasting(_) - | super::ImplSource::TupleUnsizing(_) => { + ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) + | ImplSource::Param(..) + | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index f423d40b99497..92b9364adfa13 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -11,7 +11,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; -use rustc_middle::traits::SelectionOutputTypeParameterMismatch; +use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch}; use rustc_middle::ty::{ self, Binder, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt, @@ -26,9 +26,9 @@ use crate::traits::vtable::{ }; use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, - ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, - Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation, - PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented, + ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, + OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection, + SelectionError, TraitNotObjectSafe, Unimplemented, }; use super::BuiltinImplConditions; @@ -48,18 +48,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut impl_src = match candidate { BuiltinCandidate { has_nested } => { let data = self.confirm_builtin_candidate(obligation, has_nested); - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } TransmutabilityCandidate => { let data = self.confirm_transmutability_candidate(obligation)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } ParamCandidate(param) => { let obligations = self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); - ImplSource::Param(obligations, param.skip_binder().constness) + ImplSource::Param(param.skip_binder().constness, obligations) } ImplCandidate(impl_def_id) => { @@ -68,76 +68,57 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { AutoImplCandidate => { let data = self.confirm_auto_impl_candidate(obligation)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } ProjectionCandidate(idx, constness) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; - ImplSource::Param(obligations, constness) + ImplSource::Param(constness, obligations) } - ObjectCandidate(idx) => { - let data = self.confirm_object_candidate(obligation, idx)?; - ImplSource::Object(data) - } + ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?, ClosureCandidate { .. } => { let vtable_closure = self.confirm_closure_candidate(obligation)?; - ImplSource::Builtin(vtable_closure) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure) } GeneratorCandidate => { let vtable_generator = self.confirm_generator_candidate(obligation)?; - ImplSource::Builtin(vtable_generator) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_generator) } FutureCandidate => { let vtable_future = self.confirm_future_candidate(obligation)?; - ImplSource::Builtin(vtable_future) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future) } FnPointerCandidate { is_const } => { let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } TraitAliasCandidate => { let data = self.confirm_trait_alias_candidate(obligation); - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } BuiltinObjectCandidate => { // This indicates something like `Trait + Send: Send`. In this case, we know that // this holds because that's what the object type is telling us, and there's really // no additional obligations to prove and no types in particular to unify, etc. - ImplSource::Builtin(Vec::new()) + ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new()) } - BuiltinUnsizeCandidate => { - let source = - self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); - let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); - let target = self.infcx.shallow_resolve(target); - let data = self.confirm_builtin_unsize_candidate(obligation, source, target)?; - // If the source and target are both unsize goals, then we need to signify that - // this is tuple unsizing so that during unsized coercion we require the proper - // feature gate. - if matches!(source.kind(), ty::Tuple(..)) && matches!(target.kind(), ty::Tuple(..)) - { - ImplSource::TupleUnsizing(data) - } else { - ImplSource::Builtin(data) - } - } + BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?, TraitUpcastingUnsizeCandidate(idx) => { - let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; - ImplSource::TraitUpcasting(data) + self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } ConstDestructCandidate(def_id) => { let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } }; @@ -496,7 +477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, index: usize, - ) -> Result>, SelectionError<'tcx>> { + ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); debug!(?obligation, ?index, "confirm_object_candidate"); @@ -660,7 +641,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)), ); - Ok(ImplSourceObjectData { vtable_base, nested }) + Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested)) } fn confirm_fn_pointer_candidate( @@ -909,7 +890,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, idx: usize, - ) -> Result>, SelectionError<'tcx>> { + ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); // `assemble_candidates_for_unsizing` should ensure there are no late-bound @@ -1006,19 +987,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let vtable_vptr_slot = prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap(); - Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested)) } fn confirm_builtin_unsize_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - source: Ty<'tcx>, - target: Ty<'tcx>, - ) -> Result>, SelectionError<'tcx>> { + ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + let target = self.infcx.shallow_resolve(target); debug!(?source, ?target, "confirm_builtin_unsize_candidate"); let mut nested = vec![]; + let src; match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). (&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b)) @@ -1062,6 +1048,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, obligation.predicate.rebind(outlives), )); + + src = BuiltinImplSource::Misc; } // `T` -> `Trait` @@ -1108,6 +1096,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested.push(predicate_to_obligation( ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx), )); + + src = BuiltinImplSource::Misc; } // `[T; n]` -> `[T]` @@ -1118,6 +1108,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .eq(DefineOpaqueTypes::No, b, a) .map_err(|_| Unimplemented)?; nested.extend(obligations); + + src = BuiltinImplSource::Misc; } // `Struct` -> `Struct` @@ -1174,6 +1166,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ), ); nested.push(tail_unsize_obligation); + + src = BuiltinImplSource::Misc; } // `(.., T)` -> `(.., U)` @@ -1201,12 +1195,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]), ); nested.push(last_unsize_obligation); + + src = BuiltinImplSource::TupleUnsizing; } _ => bug!("source: {source}, target: {target}"), }; - Ok(nested) + Ok(ImplSource::Builtin(src, nested)) } fn confirm_const_destruct_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 7b34908c4bed4..3eab885a089b8 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -241,9 +241,9 @@ pub fn upcast_choices<'tcx>( /// Given an upcast trait object described by `object`, returns the /// index of the method `method_def_id` (which should be part of /// `object.upcast_trait_ref`) within the vtable for `object`. -pub fn get_vtable_index_of_object_method<'tcx, N>( +pub fn get_vtable_index_of_object_method<'tcx>( tcx: TyCtxt<'tcx>, - object: &super::ImplSourceObjectData, + vtable_base: usize, method_def_id: DefId, ) -> Option { // Count number of methods preceding the one we are selecting and @@ -252,7 +252,7 @@ pub fn get_vtable_index_of_object_method<'tcx, N>( .iter() .copied() .position(|def_id| def_id == method_def_id) - .map(|index| object.vtable_base + index) + .map(|index| vtable_base + index) } pub fn closure_trait_ref_and_return_type<'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index b4f614e3e2202..c4cc89aa9077c 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -5,6 +5,7 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::traits::util::PredicateSet; use rustc_infer::traits::ImplSource; use rustc_middle::query::Providers; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; @@ -384,8 +385,8 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]); match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) { - Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => { - implsrc_traitcasting.vtable_vptr_slot + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => { + *vtable_vptr_slot } otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"), } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 88eb33fee379a..a21b5ef05e60b 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -2,7 +2,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::query::Providers; -use rustc_middle::traits::CodegenObligationError; +use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt}; use rustc_span::sym; @@ -177,12 +177,15 @@ fn resolve_associated_item<'tcx>( Some(ty::Instance::new(leaf_def.item.def_id, args)) } - traits::ImplSource::Object(ref data) => { - traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| { - Instance { def: ty::InstanceDef::Virtual(trait_item_id, index), args: rcvr_args } - }) + traits::ImplSource::Builtin(BuiltinImplSource::Object { vtable_base }, _) => { + traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map( + |index| Instance { + def: ty::InstanceDef::Virtual(trait_item_id, index), + args: rcvr_args, + }, + ) } - traits::ImplSource::Builtin(..) => { + traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => { let lang_items = tcx.lang_items(); if Some(trait_ref.def_id) == lang_items.clone_trait() { // FIXME(eddyb) use lang items for methods instead of names. @@ -291,8 +294,8 @@ fn resolve_associated_item<'tcx>( } } traits::ImplSource::Param(..) - | traits::ImplSource::TraitUpcasting(_) - | traits::ImplSource::TupleUnsizing(_) => None, + | traits::ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => None, }) } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 1c29d614fa349..e563e41ab2aa4 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::{ Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; -use rustc_middle::traits::{ImplSource, ObligationCause}; +use rustc_middle::traits::{ImplSource, ObligationCause, BuiltinImplSource}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, BoundConstness, GenericArgKind, TraitRef, Ty, TyCtxt}; use rustc_semver::RustcVersion; @@ -411,7 +411,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> if !matches!( impl_src, - ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(ty::BoundConstness::ConstIfConst, _) ) { return false; }