Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not mix normalized and unnormalized caller bounds when constructing param-env for receiver_is_dispatchable #138941

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
Original file line number Diff line number Diff line change
@@ -583,27 +583,36 @@ fn receiver_is_dispatchable<'tcx>(
// create a modified param env, with `Self: Unsize<U>` and `U: Trait` (and all of
// its supertraits) added to caller bounds. `U: ?Sized` is already implied here.
let param_env = {
let param_env = tcx.param_env(method.def_id);
// N.B. We generally want to emulate the construction of the `unnormalized_param_env`
// in the param-env query here. The fact that we don't just start with the clauses
// in the param-env of the method is because those are already normalized, and mixing
// normalized and unnormalized copies of predicates in `normalize_param_env_or_error`
// will cause ambiguity that the user can't really avoid.
//
// We leave out certain complexities of the param-env query here. Specifically, we:
// 1. Do not add `~const` bounds since there are no `dyn const Trait`s.
// 2. Do not add RPITIT self projection bounds for defaulted methods, since we
// are not constructing a param-env for "inside" of the body of the defaulted
// method, so we don't really care about projecting to a specific RPIT type,
// and because RPITITs are not dyn compatible (yet).
let mut predicates = tcx.predicates_of(method.def_id).instantiate_identity(tcx).predicates;

// Self: Unsize<U>
let unsize_predicate =
ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]).upcast(tcx);
ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]);
predicates.push(unsize_predicate.upcast(tcx));

// U: Trait<Arg1, ..., ArgN>
let trait_predicate = {
let trait_def_id = method.trait_container(tcx).unwrap();
let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
});

ty::TraitRef::new_from_args(tcx, trait_def_id, args).upcast(tcx)
};
let trait_def_id = method.trait_container(tcx).unwrap();
let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
});
let trait_predicate = ty::TraitRef::new_from_args(tcx, trait_def_id, args);
predicates.push(trait_predicate.upcast(tcx));

normalize_param_env_or_error(
tcx,
ty::ParamEnv::new(tcx.mk_clauses_from_iter(
param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]),
)),
ty::ParamEnv::new(tcx.mk_clauses(&predicates)),
ObligationCause::dummy_with_span(tcx.def_span(method.def_id)),
)
};
26 changes: 13 additions & 13 deletions tests/ui/associated-types/issue-59324.stderr
Original file line number Diff line number Diff line change
@@ -36,11 +36,18 @@ LL | |
LL | | &self,
LL | | ) -> Self::AssocType;
| |_________________________^ the trait `Foo` is not implemented for `Bug`

error[E0277]: the trait bound `(): Foo` is not satisfied
--> $DIR/issue-59324.rs:24:29
|
help: consider further restricting type parameter `Bug` with trait `Foo`
LL | fn with_factory<H>(factory: dyn ThriftService<()>) {}
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
|
LL | pub trait ThriftService<Bug: NotFoo + Foo>:
| +++++
help: this trait has no implementations, consider adding one
--> $DIR/issue-59324.rs:3:1
|
LL | pub trait Foo: NotFoo {
| ^^^^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `Bug: Foo` is not satisfied
--> $DIR/issue-59324.rs:16:5
@@ -51,18 +58,11 @@ LL | |
LL | | &self,
LL | | ) -> Self::AssocType;
| |_________________________^ the trait `Foo` is not implemented for `Bug`

error[E0277]: the trait bound `(): Foo` is not satisfied
--> $DIR/issue-59324.rs:24:29
|
LL | fn with_factory<H>(factory: dyn ThriftService<()>) {}
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
|
help: this trait has no implementations, consider adding one
--> $DIR/issue-59324.rs:3:1
help: consider further restricting type parameter `Bug` with trait `Foo`
|
LL | pub trait Foo: NotFoo {
| ^^^^^^^^^^^^^^^^^^^^^
LL | pub trait ThriftService<Bug: NotFoo + Foo>:
| +++++

error[E0277]: the trait bound `Bug: Foo` is not satisfied
--> $DIR/issue-59324.rs:20:10
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//@ check-pass

// Regression test for <https://github.com/rust-lang/rust/issues/138937>.

// Previously, we'd take the normalized param env's clauses which included
// `<PF as TraitC>::Value = i32`, which comes from the supertraits of `TraitD`
// after normalizing `<PF as TraitC>::Value = <PF as TraitD>::Scalar`. Since
// `normalize_param_env_or_error` ends up re-elaborating `PF: TraitD`, we'd
// end up with both versions of this predicate (normalized and unnormalized).
// Since these projections preds are not equal, we'd fail with ambiguity.
Copy link
Member

@BoxyUwU BoxyUwU Mar 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its during normalize_param_env_or_error that we error due to there being two candidates? or is this a weird case where it doesnt wind up actually normalizing like it should

Copy link
Member Author

@compiler-errors compiler-errors Mar 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We error during normalize_param_env_or_error b/c we have two candidates for normalizing <PF as TraitC>::Value, which shows up in the supertrait TraitB<Self::Value>. The first candidate comes from normalizing the param-env the first time in the param_env query, and the second comes from the second time we normalize it.


trait TraitB<T> {}

trait TraitC: TraitB<Self::Value> {
type Value;
}

trait TraitD: TraitC<Value = Self::Scalar> {
type Scalar;
}

trait TraitE {
fn apply<PF: TraitD<Scalar = i32>>(&self);
}

fn main() {}
Loading
Oops, something went wrong.