1
Fork 0

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

This commit is contained in:
Michael Goulet 2025-03-25 18:57:04 +00:00
parent 2196affd01
commit 4f2baaa9c6
3 changed files with 63 additions and 28 deletions

View file

@ -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_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)),
)
};

View file

@ -27,21 +27,6 @@ help: consider further restricting type parameter `Bug` with trait `Foo`
LL | pub trait ThriftService<Bug: NotFoo + Foo>:
| +++++
error[E0277]: the trait bound `Bug: Foo` is not satisfied
--> $DIR/issue-59324.rs:16:5
|
LL | / fn get_service(
LL | |
LL | |
LL | | &self,
LL | | ) -> Self::AssocType;
| |_________________________^ the trait `Foo` is not implemented for `Bug`
|
help: consider further restricting type parameter `Bug` with trait `Foo`
|
LL | pub trait ThriftService<Bug: NotFoo + Foo>:
| +++++
error[E0277]: the trait bound `Bug: Foo` is not satisfied
--> $DIR/issue-59324.rs:16:5
|
@ -64,6 +49,21 @@ help: this trait has no implementations, consider adding one
LL | pub trait Foo: NotFoo {
| ^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `Bug: Foo` is not satisfied
--> $DIR/issue-59324.rs:16:5
|
LL | / fn get_service(
LL | |
LL | |
LL | | &self,
LL | | ) -> Self::AssocType;
| |_________________________^ the trait `Foo` is not implemented for `Bug`
|
help: consider further restricting type parameter `Bug` with trait `Foo`
|
LL | pub trait ThriftService<Bug: NotFoo + Foo>:
| +++++
error[E0277]: the trait bound `Bug: Foo` is not satisfied
--> $DIR/issue-59324.rs:20:10
|

View file

@ -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.
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() {}