From 085a48e7983d2b3b1f6d6fbeb127dd90b2a1377c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 23 Jan 2023 22:33:59 +0000 Subject: [PATCH 1/3] Implement unsizing in the new trait solver --- .../src/solve/assembly.rs | 10 + .../src/solve/project_goals.rs | 7 + .../src/solve/trait_goals.rs | 175 ++++++++++++++++++ tests/ui/traits/new-solver/unsize-good.rs | 25 +++ 4 files changed, 217 insertions(+) create mode 100644 tests/ui/traits/new-solver/unsize-good.rs diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index e44fd82ba22..e6c36d37ffb 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -173,6 +173,14 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + + // Implement unsizing. 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>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -303,6 +311,8 @@ 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 { Err(NoSolution) }; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index b175a6dde17..00b4fa67d68 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -554,6 +554,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { .to_predicate(tcx), ) } + + fn consider_builtin_unsize_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Unsize` does not have an associated type: {:?}", goal); + } } /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 1ea8fb8fd3d..28144da1b1e 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -8,6 +8,7 @@ use super::{Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::util::supertraits; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitable}; @@ -238,6 +239,180 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { .to_predicate(tcx), ) } + + fn consider_builtin_unsize_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); + let a_ty = goal.predicate.self_ty(); + let b_ty = goal.predicate.trait_ref.substs.type_at(1); + if b_ty.is_ty_var() { + return ecx.make_canonical_response(Certainty::AMBIGUOUS); + } + ecx.infcx.probe(|_| { + match (a_ty.kind(), b_ty.kind()) { + // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` + ( + &ty::Dynamic(a_data, a_region, ty::Dyn), + &ty::Dynamic(b_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 Err(NoSolution); + } + + // 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() { + // 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 = a_data + .iter() + .filter(|a| { + matches!( + a.skip_binder(), + ty::ExistentialPredicate::Trait(_) | 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(new_a_data); + let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn); + + // We also require that A's lifetime outlives B's lifetime. + let mut nested_obligations = ecx.infcx.eq(goal.param_env, new_a_ty, b_ty)?; + nested_obligations.push(goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)))); + + ecx.evaluate_all_and_make_canonical_response(nested_obligations) + } else if let Some(a_principal) = a_data.principal() + && let Some(b_principal) = b_data.principal() + && supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) + .any(|trait_ref| trait_ref.def_id() == b_principal.def_id()) + { + // FIXME: Intentionally ignoring `need_migrate_deref_output_trait_object` here for now. + // Confirm upcasting candidate + todo!() + } else { + Err(NoSolution) + } + } + // `T` -> `dyn Trait` unsizing + (_, &ty::Dynamic(data, region, ty::Dyn)) => { + // Can only unsize to an object-safe type + // FIXME: Can auto traits be *not* object safe? + if data + .auto_traits() + .chain(data.principal_def_id()) + .any(|def_id| !tcx.is_object_safe(def_id)) + { + return Err(NoSolution); + } + + let Some(sized_def_id) = tcx.lang_items().sized_trait() else { + return Err(NoSolution); + }; + let nested_goals: Vec<_> = data + .iter() + // 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) + .map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))) + .chain([ + // The type must be Sized to be unsized. + goal.with( + tcx, + ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [a_ty])), + ), + // The type must outlive the lifetime of the `dyn` we're unsizing into. + goal.with( + tcx, + ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), + ), + ]) + .collect(); + + ecx.evaluate_all_and_make_canonical_response(nested_goals) + } + // `[T; n]` -> `[T]` unsizing + (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { + // We just require that the element type stays the same + let nested_goals = ecx.infcx.eq(goal.param_env, a_elem_ty, b_elem_ty)?; + ecx.evaluate_all_and_make_canonical_response(nested_goals) + } + // Struct unsizing `Struct` -> `Struct` where `T: Unsize` + (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) + if a_def.is_struct() && a_def.did() == b_def.did() => + { + let unsizing_params = tcx.unsizing_params_for_adt(a_def.did()); + // We must be unsizing some type parameters. This also implies + // that the struct has a tail field. + if unsizing_params.is_empty() { + return Err(NoSolution); + } + + let tail_field = a_def + .non_enum_variant() + .fields + .last() + .expect("expected unsized ADT to have a tail field"); + let tail_field_ty = tcx.bound_type_of(tail_field.did); + + let a_tail_ty = tail_field_ty.subst(tcx, a_substs); + let b_tail_ty = tail_field_ty.subst(tcx, b_substs); + + // Substitute just the unsizing params from B into A. The type after + // this substitution must be equal to B. This is so we don't unsize + // unrelated type parameters. + let new_a_substs = tcx.mk_substs(a_substs.iter().enumerate().map(|(i, a)| { + if unsizing_params.contains(i as u32) { b_substs[i] } else { a } + })); + let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs); + + // Finally, we require that `TailA: Unsize` for the tail field + // types. + let mut nested_goals = ecx.infcx.eq(goal.param_env, unsized_a_ty, b_ty)?; + nested_goals.push(goal.with( + tcx, + ty::Binder::dummy( + tcx.mk_trait_ref(goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), + ), + )); + + ecx.evaluate_all_and_make_canonical_response(nested_goals) + } + // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize` + (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) + if a_tys.len() == b_tys.len() && !a_tys.is_empty() => + { + let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); + let b_last_ty = b_tys.last().unwrap(); + + // Substitute just the tail field of B., and require that they're equal. + let unsized_a_ty = tcx.mk_tup(a_rest_tys.iter().chain([b_last_ty])); + let mut nested_goals = ecx.infcx.eq(goal.param_env, unsized_a_ty, b_ty)?; + + // Similar to ADTs, require that the rest of the fields are equal. + nested_goals.push(goal.with( + tcx, + ty::Binder::dummy( + tcx.mk_trait_ref(goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), + ), + )); + + ecx.evaluate_all_and_make_canonical_response(nested_goals) + } + _ => Err(NoSolution), + } + }) + } } impl<'tcx> EvalCtxt<'_, 'tcx> { diff --git a/tests/ui/traits/new-solver/unsize-good.rs b/tests/ui/traits/new-solver/unsize-good.rs new file mode 100644 index 00000000000..87ed9cfd10a --- /dev/null +++ b/tests/ui/traits/new-solver/unsize-good.rs @@ -0,0 +1,25 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +#![feature(unsized_tuple_coercion)] + +trait Foo {} + +impl Foo for i32 {} + +fn main() { + // Unsizing via struct + let _: Box = Box::new(1i32); + + // Slice unsizing + let y = [1, 2, 3]; + let _: &[i32] = &y; + + // Tuple unsizing + let hi = (1i32,); + let _: &(dyn Foo,) = &hi; + + // Dropping auto traits + let a: &(dyn Foo + Send) = &1; + let _: &dyn Foo = a; +} From c24844048ff023aeaf47080634507677ffbe7001 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 23 Jan 2023 23:56:54 +0000 Subject: [PATCH 2/3] Trait upcasting support in new solver --- .../src/solve/assembly.rs | 21 ++- .../src/solve/project_goals.rs | 7 + .../src/solve/trait_goals.rs | 138 +++++++++++------- .../traits/new-solver/upcast-right-substs.rs | 14 ++ .../traits/new-solver/upcast-wrong-substs.rs | 13 ++ .../new-solver/upcast-wrong-substs.stderr | 14 ++ 6 files changed, 148 insertions(+), 59 deletions(-) create mode 100644 tests/ui/traits/new-solver/upcast-right-substs.rs create mode 100644 tests/ui/traits/new-solver/upcast-wrong-substs.rs create mode 100644 tests/ui/traits/new-solver/upcast-wrong-substs.stderr diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index e6c36d37ffb..1c4e498c57e 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -174,13 +174,20 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // Implement unsizing. 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. + // 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_unsize_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -323,6 +330,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } Err(NoSolution) => (), } + + // 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_unsize_candidates(self, goal) { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }); + } + } } fn assemble_param_env_candidates>( diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 00b4fa67d68..f9506446aba 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -561,6 +561,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) -> QueryResult<'tcx> { bug!("`Unsize` does not have an associated type: {:?}", goal); } + + fn consider_builtin_dyn_unsize_candidates( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec> { + bug!("`Unsize` does not have an associated type: {:?}", goal); + } } /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 28144da1b1e..c79f1f05512 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -4,7 +4,7 @@ use std::iter; use super::assembly::{self, Candidate, CandidateSource}; use super::infcx_ext::InferCtxtExt; -use super::{Certainty, EvalCtxt, Goal, QueryResult}; +use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; @@ -253,57 +253,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.infcx.probe(|_| { match (a_ty.kind(), b_ty.kind()) { // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` - ( - &ty::Dynamic(a_data, a_region, ty::Dyn), - &ty::Dynamic(b_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 Err(NoSolution); - } - - // 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() { - // 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 = a_data - .iter() - .filter(|a| { - matches!( - a.skip_binder(), - ty::ExistentialPredicate::Trait(_) | 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(new_a_data); - let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - let mut nested_obligations = ecx.infcx.eq(goal.param_env, new_a_ty, b_ty)?; - nested_obligations.push(goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)))); - - ecx.evaluate_all_and_make_canonical_response(nested_obligations) - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() - && supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) - .any(|trait_ref| trait_ref.def_id() == b_principal.def_id()) - { - // FIXME: Intentionally ignoring `need_migrate_deref_output_trait_object` here for now. - // Confirm upcasting candidate - todo!() - } else { - Err(NoSolution) - } + (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { + // Dyn upcasting is handled separately, since due to upcasting, + // when there are two supertraits that differ by substs, we + // may return more than one query response. + return Err(NoSolution); } // `T` -> `dyn Trait` unsizing (_, &ty::Dynamic(data, region, ty::Dyn)) => { @@ -332,10 +286,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [a_ty])), ), // The type must outlive the lifetime of the `dyn` we're unsizing into. - goal.with( - tcx, - ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), - ), + goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))), ]) .collect(); @@ -413,6 +364,81 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } }) } + + fn consider_builtin_dyn_unsize_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec> { + let tcx = ecx.tcx(); + + let a_ty = goal.predicate.self_ty(); + let b_ty = goal.predicate.trait_ref.substs.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![]; + }; + + // 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 responses = vec![]; + let mut unsize_dyn_to_principal = |principal: Option>| { + let _ = ecx.infcx.probe(|_| -> 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(new_a_data); + let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn); + + // We also require that A's lifetime outlives B's lifetime. + let mut nested_obligations = ecx.infcx.eq(goal.param_env, new_a_ty, b_ty)?; + nested_obligations.push( + goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), + ); + + responses.push(ecx.evaluate_all_and_make_canonical_response(nested_obligations)?); + + Ok(()) + }); + }; + + // 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() { + unsize_dyn_to_principal(a_data.principal()); + } 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)); + unsize_dyn_to_principal(Some(erased_trait_ref)); + } + } + + responses + } } impl<'tcx> EvalCtxt<'_, 'tcx> { diff --git a/tests/ui/traits/new-solver/upcast-right-substs.rs b/tests/ui/traits/new-solver/upcast-right-substs.rs new file mode 100644 index 00000000000..c19c82acf24 --- /dev/null +++ b/tests/ui/traits/new-solver/upcast-right-substs.rs @@ -0,0 +1,14 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +#![feature(trait_upcasting)] + +trait Foo: Bar + Bar {} + +trait Bar {} + +fn main() { + let x: &dyn Foo = todo!(); + let y: &dyn Bar = x; + let z: &dyn Bar = x; +} diff --git a/tests/ui/traits/new-solver/upcast-wrong-substs.rs b/tests/ui/traits/new-solver/upcast-wrong-substs.rs new file mode 100644 index 00000000000..f2d04d932bb --- /dev/null +++ b/tests/ui/traits/new-solver/upcast-wrong-substs.rs @@ -0,0 +1,13 @@ +// compile-flags: -Ztrait-solver=next + +#![feature(trait_upcasting)] + +trait Foo: Bar + Bar {} + +trait Bar {} + +fn main() { + let x: &dyn Foo = todo!(); + let y: &dyn Bar = x; + //~^ ERROR mismatched types +} diff --git a/tests/ui/traits/new-solver/upcast-wrong-substs.stderr b/tests/ui/traits/new-solver/upcast-wrong-substs.stderr new file mode 100644 index 00000000000..8623f395f59 --- /dev/null +++ b/tests/ui/traits/new-solver/upcast-wrong-substs.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/upcast-wrong-substs.rs:11:30 + | +LL | let y: &dyn Bar = x; + | --------------- ^ expected trait `Bar`, found trait `Foo` + | | + | expected due to this + | + = note: expected reference `&dyn Bar` + found reference `&dyn Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From f7fc0b712103d8832b305efa72e4c9851161bbdf Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 25 Jan 2023 17:02:16 +0000 Subject: [PATCH 3/3] nits --- .../src/solve/assembly.rs | 4 +-- .../src/solve/project_goals.rs | 2 +- .../src/solve/trait_goals.rs | 26 +++++++++---------- compiler/rustc_ty_utils/src/ty.rs | 4 --- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 1c4e498c57e..5690b6536bb 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -184,7 +184,7 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { // `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_unsize_candidates( + fn consider_builtin_dyn_upcast_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec>; @@ -334,7 +334,7 @@ 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_unsize_candidates(self, goal) { + for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }); } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index f9506446aba..879f18843c9 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -562,7 +562,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { bug!("`Unsize` does not have an associated type: {:?}", goal); } - fn consider_builtin_dyn_unsize_candidates( + fn consider_builtin_dyn_upcast_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec> { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index c79f1f05512..29ee9da38e0 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -262,11 +262,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // `T` -> `dyn Trait` unsizing (_, &ty::Dynamic(data, region, ty::Dyn)) => { // Can only unsize to an object-safe type - // FIXME: Can auto traits be *not* object safe? if data - .auto_traits() - .chain(data.principal_def_id()) - .any(|def_id| !tcx.is_object_safe(def_id)) + .principal_def_id() + .map_or(false, |def_id| !tcx.check_is_object_safe(def_id)) { return Err(NoSolution); } @@ -365,7 +363,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } - fn consider_builtin_dyn_unsize_candidates( + fn consider_builtin_dyn_upcast_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec> { @@ -387,9 +385,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return vec![]; } - let mut responses = vec![]; let mut unsize_dyn_to_principal = |principal: Option>| { - let _ = ecx.infcx.probe(|_| -> Result<(), NoSolution> { + ecx.infcx.probe(|_| -> 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. @@ -414,16 +411,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), ); - responses.push(ecx.evaluate_all_and_make_canonical_response(nested_obligations)?); - - Ok(()) - }); + ecx.evaluate_all_and_make_canonical_response(nested_obligations) + }) }; + 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() { - unsize_dyn_to_principal(a_data.principal()); + 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() { @@ -433,7 +431,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } let erased_trait_ref = super_trait_ref .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - unsize_dyn_to_principal(Some(erased_trait_ref)); + if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { + responses.push(response); + } } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index b5005c1d8d8..41e837e8b75 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -426,10 +426,6 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet