Skip to content

Commit

Permalink
address review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukas Markeffsky committed Mar 4, 2024
1 parent aa55f6d commit 6bd970d
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,41 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {

#[extension(pub(super) trait InferCtxtPrivExt<'tcx>)]
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn can_match_trait(
&self,
goal: ty::TraitPredicate<'tcx>,
assumption: ty::PolyTraitPredicate<'tcx>,
) -> bool {
if goal.polarity != assumption.polarity() {
return false;
}

let trait_goal = goal.trait_ref;
let trait_assumption = self.instantiate_binder_with_fresh_vars(
DUMMY_SP,
infer::BoundRegionConversionTime::HigherRankedType,
assumption.to_poly_trait_ref(),
);

self.can_eq(ty::ParamEnv::empty(), trait_goal, trait_assumption)
}

fn can_match_projection(
&self,
goal: ty::ProjectionPredicate<'tcx>,
assumption: ty::PolyProjectionPredicate<'tcx>,
) -> bool {
let assumption = self.instantiate_binder_with_fresh_vars(
DUMMY_SP,
infer::BoundRegionConversionTime::HigherRankedType,
assumption,
);

let param_env = ty::ParamEnv::empty();
self.can_eq(param_env, goal.projection_ty, assumption.projection_ty)
&& self.can_eq(param_env, goal.term, assumption.term)
}

// returns if `cond` not occurring implies that `error` does not occur - i.e., that
// `error` occurring implies that `cond` occurs.
fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool {
Expand All @@ -1390,39 +1425,27 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}

if let Some(error) = error.to_opt_poly_trait_pred() {
elaborate(self.tcx, std::iter::once(cond))
.filter_map(|implied| implied.to_opt_poly_trait_pred())
.any(|implied| {
if error.polarity() != implied.polarity() {
return false;
}
let error = error.to_poly_trait_ref();
let implied = implied.to_poly_trait_ref();
// FIXME: I'm just not taking associated types at all here.
// Eventually I'll need to implement param-env-aware
// `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic.
let param_env = ty::ParamEnv::empty();
let is_implied = self.can_sub(param_env, error, implied);
if is_implied {
debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implied);
}
is_implied
})
self.enter_forall(error, |error| {
elaborate(self.tcx, std::iter::once(cond))
.filter_map(|implied| implied.to_opt_poly_trait_pred())
.any(|implied| {
let is_implied = self.can_match_trait(error, implied);
if is_implied {
debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implied);
}
is_implied
})
})
} else if let Some(error) = error.to_opt_poly_projection_pred() {
self.enter_forall(error, |error| {
elaborate(self.tcx, std::iter::once(cond))
.filter_map(|implied| implied.to_opt_poly_projection_pred())
.any(|implied| {
self.enter_forall(implied, |implied| {
let param_env = ty::ParamEnv::empty();
let is_implied =
self.can_eq(param_env, error.projection_ty, implied.projection_ty)
&& self.can_eq(param_env, error.term, implied.term);
if is_implied {
debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implied);
}
is_implied
})
let is_implied = self.can_match_projection(error, implied);
if is_implied {
debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implied);
}
is_implied
})
})
} else {
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ enum Elaborate {
///
/// And a super predicate of `TargetTrait` that has any of the following forms:
///
/// 1. `<OtherType as OtherTrait>::Assoc = <TargetType as TargetTrait>::Assoc`
/// 2. `<<TargetType as TargetTrait>::Assoc as OtherTrait>::Assoc = OtherType`
/// 1. `<OtherType as OtherTrait>::Assoc == <TargetType as TargetTrait>::Assoc`
/// 2. `<<TargetType as TargetTrait>::Assoc as OtherTrait>::Assoc == OtherType`
/// 3. `<TargetType as TargetTrait>::Assoc: OtherTrait`
///
/// Replace the span of the cause with the span of the associated item:
Expand Down Expand Up @@ -292,6 +292,9 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
}

// Form 2: A projection obligation for an associated item failed to be met.
// We overwrite the span from above to ensure that a bound like
// `Self::Assoc1: Trait<OtherAssoc = Self::Assoc2>` gets the same
// span for both obligations that it is lowered to.
if let Some(impl_item_span) = ty_to_impl_span(proj.self_ty()) {
cause.span = impl_item_span;
}
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/trait-bounds/super-assoc-mismatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,28 @@ impl BoundOnGat for u8 {
fn trivial_bound() where (): Sub {}
//~^ ERROR the trait bound `(): Sub` is not satisfied

// The following is an edge case where the unsatisfied projection predicate
// `<<u8 as MultiAssoc>::Assoc1<()> as SuperGeneric<u16>>::Assoc == <u8 as MultiAssoc>::Assoc2`
// contains both associated types of `MultiAssoc`. To suppress the error about the unsatisfied
// super projection, the error's span must be equal to the span of the unsatisfied trait error.
trait SuperGeneric<T> {
type Assoc;
}
trait SubGeneric<T>: SuperGeneric<T, Assoc = T> {}
trait MultiAssoc
where
Self::Assoc1<()>: SubGeneric<Self::Assoc2>
{
type Assoc1<T>;
type Assoc2;
}
impl SuperGeneric<u16> for () {
type Assoc = u8;
}
impl MultiAssoc for u8 {
type Assoc1<T> = ();
//~^ ERROR the trait bound `(): SubGeneric<u16>` is not satisfied
type Assoc2 = u16;
}

fn main() {}
22 changes: 21 additions & 1 deletion tests/ui/trait-bounds/super-assoc-mismatch.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ LL | trait Sub: Super<Assoc = u16> {}
= help: see issue #48214
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable

error: aborting due to 5 previous errors
error[E0277]: the trait bound `(): SubGeneric<u16>` is not satisfied
--> $DIR/super-assoc-mismatch.rs:55:22
|
LL | type Assoc1<T> = ();
| ^^ the trait `SubGeneric<u16>` is not implemented for `()`, which is required by `<u8 as MultiAssoc>::Assoc1<()>: SubGeneric<<u8 as MultiAssoc>::Assoc2>`
|
help: this trait has no implementations, consider adding one
--> $DIR/super-assoc-mismatch.rs:43:1
|
LL | trait SubGeneric<T>: SuperGeneric<T, Assoc = T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `MultiAssoc`
--> $DIR/super-assoc-mismatch.rs:46:23
|
LL | trait MultiAssoc
| ---------- required by a bound in this trait
LL | where
LL | Self::Assoc1<()>: SubGeneric<Self::Assoc2>
| ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MultiAssoc`

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0277`.

0 comments on commit 6bd970d

Please sign in to comment.