Skip to content

Commit 0dbaae4

Browse files
Make alias bounds sound in the new solver
1 parent 3a37c2f commit 0dbaae4

File tree

6 files changed

+233
-2
lines changed

6 files changed

+233
-2
lines changed

compiler/rustc_trait_selection/src/solve/assembly/mod.rs

+114-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_data_structures::fx::FxIndexSet;
88
use rustc_hir::def_id::DefId;
99
use rustc_infer::traits::query::NoSolution;
1010
use rustc_infer::traits::util::elaborate;
11+
use rustc_infer::traits::Reveal;
1112
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
1213
use rustc_middle::ty::fast_reject::TreatProjections;
1314
use rustc_middle::ty::TypeFoldable;
@@ -87,7 +88,9 @@ pub(super) enum CandidateSource {
8788
}
8889

8990
/// Methods used to assemble candidates for either trait or projection goals.
90-
pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
91+
pub(super) trait GoalKind<'tcx>:
92+
TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
93+
{
9194
fn self_ty(self) -> Ty<'tcx>;
9295

9396
fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>;
@@ -106,6 +109,16 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
106109
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
107110
) -> QueryResult<'tcx>;
108111

112+
/// Consider a bound originating from the item bounds of an alias. For this we
113+
/// require that the well-formed requirements of the self type of the goal
114+
/// are "satisfied from the param-env".
115+
/// See [`EvalCtxt::validate_alias_bound_self_from_param_env`].
116+
fn consider_alias_bound_candidate(
117+
ecx: &mut EvalCtxt<'_, 'tcx>,
118+
goal: Goal<'tcx, Self>,
119+
assumption: ty::Predicate<'tcx>,
120+
) -> QueryResult<'tcx>;
121+
109122
// Consider a clause specifically for a `dyn Trait` self type. This requires
110123
// additionally checking all of the supertraits and object bounds to hold,
111124
// since they're not implied by the well-formedness of the object type.
@@ -463,7 +476,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
463476

464477
for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs)
465478
{
466-
match G::consider_implied_clause(self, goal, assumption, []) {
479+
match G::consider_alias_bound_candidate(self, goal, assumption) {
467480
Ok(result) => {
468481
candidates.push(Candidate { source: CandidateSource::AliasBound, result })
469482
}
@@ -472,6 +485,105 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
472485
}
473486
}
474487

488+
/// Check that we are allowed to use an alias bound originating from the self
489+
/// type of this goal. This means something different depending on the self type's
490+
/// alias kind.
491+
///
492+
/// * Projection: Given a goal with a self type such as `<Ty as Trait>::Assoc`,
493+
/// we require that the bound `Ty: Trait` can be proven using either a nested alias
494+
/// bound candidate, or a param-env candidate.
495+
///
496+
/// * Opaque: The param-env must be in `Reveal::UserFacing` mode. Otherwise,
497+
/// the goal should be proven by using the hidden type instead.
498+
#[instrument(level = "debug", skip(self), ret)]
499+
pub(super) fn validate_alias_bound_self_from_param_env<G: GoalKind<'tcx>>(
500+
&mut self,
501+
goal: Goal<'tcx, G>,
502+
) -> QueryResult<'tcx> {
503+
match *goal.predicate.self_ty().kind() {
504+
ty::Alias(ty::Projection, projection_ty) => {
505+
let mut param_env_candidates = vec![];
506+
let self_trait_ref = projection_ty.trait_ref(self.tcx());
507+
508+
if self_trait_ref.self_ty().is_ty_var() {
509+
return self
510+
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
511+
}
512+
513+
let trait_goal: Goal<'_, ty::TraitPredicate<'tcx>> = goal.with(
514+
self.tcx(),
515+
ty::TraitPredicate {
516+
trait_ref: self_trait_ref,
517+
constness: ty::BoundConstness::NotConst,
518+
polarity: ty::ImplPolarity::Positive,
519+
},
520+
);
521+
522+
self.assemble_param_env_candidates(trait_goal, &mut param_env_candidates);
523+
// FIXME: We probably need some sort of recursion depth check here.
524+
// Can't come up with an example yet, though, and the worst case
525+
// we can have is a compiler stack overflow...
526+
self.assemble_alias_bound_candidates(trait_goal, &mut param_env_candidates);
527+
528+
// FIXME: We must also consider alias-bound candidates for a peculiar
529+
// class of built-in candidates that I'll call "defaulted" built-ins.
530+
//
531+
// For example, we always know that `T: Pointee` is implemented, but
532+
// we do not always know what `<T as Pointee>::Metadata` actually is,
533+
// similar to if we had a user-defined impl with a `default type ...`.
534+
// For these traits, since we're not able to always normalize their
535+
// associated types to a concrete type, we must consider their alias bounds
536+
// instead, so we can prove bounds such as `<T as Pointee>::Metadata: Copy`.
537+
self.assemble_alias_bound_candidates_for_builtin_impl_default_items(
538+
trait_goal,
539+
&mut param_env_candidates,
540+
);
541+
542+
self.merge_candidates(param_env_candidates)
543+
}
544+
ty::Alias(ty::Opaque, _opaque_ty) => match goal.param_env.reveal() {
545+
Reveal::UserFacing => {
546+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
547+
}
548+
Reveal::All => return Err(NoSolution),
549+
},
550+
_ => bug!("only expected to be called on alias tys"),
551+
}
552+
}
553+
554+
/// Assemble a subset of builtin impl candidates for a class of candidates called
555+
/// "defaulted" built-in traits.
556+
///
557+
/// For example, we always know that `T: Pointee` is implemented, but we do not
558+
/// always know what `<T as Pointee>::Metadata` actually is! See the comment in
559+
/// [`EvalCtxt::validate_alias_bound_self_from_param_env`] for more detail.
560+
#[instrument(level = "debug", skip_all)]
561+
fn assemble_alias_bound_candidates_for_builtin_impl_default_items<G: GoalKind<'tcx>>(
562+
&mut self,
563+
goal: Goal<'tcx, G>,
564+
candidates: &mut Vec<Candidate<'tcx>>,
565+
) {
566+
let lang_items = self.tcx().lang_items();
567+
let trait_def_id = goal.predicate.trait_def_id(self.tcx());
568+
569+
// You probably shouldn't add anything to this list unless you
570+
// know what you're doing.
571+
let result = if lang_items.pointee_trait() == Some(trait_def_id) {
572+
G::consider_builtin_pointee_candidate(self, goal)
573+
} else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
574+
G::consider_builtin_discriminant_kind_candidate(self, goal)
575+
} else {
576+
Err(NoSolution)
577+
};
578+
579+
match result {
580+
Ok(result) => {
581+
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
582+
}
583+
Err(NoSolution) => (),
584+
}
585+
}
586+
475587
#[instrument(level = "debug", skip_all)]
476588
fn assemble_object_bound_candidates<G: GoalKind<'tcx>>(
477589
&mut self,

compiler/rustc_trait_selection/src/solve/project_goals.rs

+24
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
8383
}
8484
}
8585

86+
fn consider_alias_bound_candidate(
87+
ecx: &mut EvalCtxt<'_, 'tcx>,
88+
goal: Goal<'tcx, Self>,
89+
assumption: ty::Predicate<'tcx>,
90+
) -> QueryResult<'tcx> {
91+
if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
92+
&& poly_projection_pred.projection_def_id() == goal.predicate.def_id()
93+
{
94+
ecx.probe(|ecx| {
95+
let assumption_projection_pred =
96+
ecx.instantiate_binder_with_infer(poly_projection_pred);
97+
ecx.eq(
98+
goal.param_env,
99+
goal.predicate.projection_ty,
100+
assumption_projection_pred.projection_ty,
101+
)?;
102+
ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)?;
103+
ecx.validate_alias_bound_self_from_param_env(goal)
104+
})
105+
} else {
106+
Err(NoSolution)
107+
}
108+
}
109+
86110
fn consider_object_bound_candidate(
87111
ecx: &mut EvalCtxt<'_, 'tcx>,
88112
goal: Goal<'tcx, Self>,

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+24
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
105105
}
106106
}
107107

108+
fn consider_alias_bound_candidate(
109+
ecx: &mut EvalCtxt<'_, 'tcx>,
110+
goal: Goal<'tcx, Self>,
111+
assumption: ty::Predicate<'tcx>,
112+
) -> QueryResult<'tcx> {
113+
if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
114+
&& poly_trait_pred.def_id() == goal.predicate.def_id()
115+
{
116+
// FIXME: Constness and polarity
117+
ecx.probe(|ecx| {
118+
let assumption_trait_pred =
119+
ecx.instantiate_binder_with_infer(poly_trait_pred);
120+
ecx.eq(
121+
goal.param_env,
122+
goal.predicate.trait_ref,
123+
assumption_trait_pred.trait_ref,
124+
)?;
125+
ecx.validate_alias_bound_self_from_param_env(goal)
126+
})
127+
} else {
128+
Err(NoSolution)
129+
}
130+
}
131+
108132
fn consider_object_bound_candidate(
109133
ecx: &mut EvalCtxt<'_, 'tcx>,
110134
goal: Goal<'tcx, Self>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// compile-flags: -Ztrait-solver=next
2+
3+
// Makes sure that alias bounds are not unsound!
4+
5+
#![feature(trivial_bounds)]
6+
7+
trait Foo {
8+
type Item: Copy
9+
where
10+
<Self as Foo>::Item: Copy;
11+
12+
fn copy_me(x: &Self::Item) -> Self::Item {
13+
*x
14+
}
15+
}
16+
17+
impl Foo for () {
18+
type Item = String where String: Copy;
19+
}
20+
21+
fn main() {
22+
let x = String::from("hello, world");
23+
drop(<() as Foo>::copy_me(&x));
24+
//~^ ERROR `<() as Foo>::Item: Copy` is not satisfied
25+
//~| ERROR `<() as Foo>::Item` is not well-formed
26+
println!("{x}");
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0277]: the trait bound `<() as Foo>::Item: Copy` is not satisfied
2+
--> $DIR/alias-bound-unsound.rs:23:10
3+
|
4+
LL | drop(<() as Foo>::copy_me(&x));
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `<() as Foo>::Item`
6+
|
7+
note: required by a bound in `Foo::Item`
8+
--> $DIR/alias-bound-unsound.rs:10:30
9+
|
10+
LL | type Item: Copy
11+
| ---- required by a bound in this associated type
12+
LL | where
13+
LL | <Self as Foo>::Item: Copy;
14+
| ^^^^ required by this bound in `Foo::Item`
15+
16+
error: the type `<() as Foo>::Item` is not well-formed
17+
--> $DIR/alias-bound-unsound.rs:23:10
18+
|
19+
LL | drop(<() as Foo>::copy_me(&x));
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^
21+
22+
error: aborting due to 2 previous errors
23+
24+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
trait A {
5+
type A: B;
6+
}
7+
8+
trait B {
9+
type B: C;
10+
}
11+
12+
trait C {}
13+
14+
fn needs_c<T: C>() {}
15+
16+
fn test<T: A>() {
17+
needs_c::<<T::A as B>::B>();
18+
}
19+
20+
fn main() {}

0 commit comments

Comments
 (0)