Rollup merge of #130866 - compiler-errors:dyn-instantiate-binder, r=lcnr
Allow instantiating object trait binder when upcasting This PR fixes two bugs (that probably need an FCP). ### We use equality rather than subtyping for upcasting dyn conversions This code should be valid: ```rust #![feature(trait_upcasting)] trait Foo: for<'h> Bar<'h> {} trait Bar<'a> {} fn foo(x: &dyn Foo) { let y: &dyn Bar<'static> = x; } ``` But instead: ``` error[E0308]: mismatched types --> src/lib.rs:7:32 | 7 | let y: &dyn Bar<'static> = x; | ^ one type is more general than the other | = note: expected existential trait ref `for<'h> Bar<'h>` found existential trait ref `Bar<'_>` ``` And so should this: ```rust #![feature(trait_upcasting)] fn foo(x: &dyn for<'h> Fn(&'h ())) { let y: &dyn FnOnce(&'static ()) = x; } ``` But instead: ``` error[E0308]: mismatched types --> src/lib.rs:4:39 | 4 | let y: &dyn FnOnce(&'static ()) = x; | ^ one type is more general than the other | = note: expected existential trait ref `for<'h> FnOnce<(&'h (),)>` found existential trait ref `FnOnce<(&(),)>` ``` Specifically, both of these fail because we use *equality* when comparing the supertrait to the *target* of the unsize goal. For the first example, since our supertrait is `for<'h> Bar<'h>` but our target is `Bar<'static>`, there's a higher-ranked type mismatch even though we *should* be able to instantiate that supertrait binder when upcasting. Similarly for the second example. ### New solver uses equality rather than subtyping for no-op (i.e. non-upcasting) dyn conversions This code should be valid in the new solver, like it is with the old solver: ```rust // -Znext-solver fn foo<'a>(x: &mut for<'h> dyn Fn(&'h ())) { let _: &mut dyn Fn(&'a ()) = x; } ``` But instead: ``` error: lifetime may not live long enough --> <source>:2:11 | 1 | fn foo<'a>(x: &mut dyn for<'h> Fn(&'h ())) { | -- lifetime `'a` defined here 2 | let _: &mut dyn Fn(&'a ()) = x; | ^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` | = note: requirement occurs because of a mutable reference to `dyn Fn(&())` ``` Specifically, this fails because we try to coerce `&mut dyn for<'h> Fn(&'h ())` to `&mut dyn Fn(&'a ())`, which registers an `dyn for<'h> Fn(&'h ()): dyn Fn(&'a ())` goal. This fails because the new solver uses *equating* rather than *subtyping* in `Unsize` goals. This is *mostly* not a problem... You may wonder why the same code passes on the new solver for immutable references: ``` // -Znext-solver fn foo<'a>(x: &dyn Fn(&())) { let _: &dyn Fn(&'a ()) = x; // works } ``` That's because in this case, we first try to coerce via `Unsize`, but due to the leak check the goal fails. Then, later in coercion, we fall back to a simple subtyping operation, which *does* work. Since `&T` is covariant over `T`, but `&mut T` is invariant, that's where the discrepancy between these two examples crops up. --- r? lcnr or reassign :D
This commit is contained in:
commit
4e510daed7
10 changed files with 229 additions and 202 deletions
|
@ -448,10 +448,10 @@ where
|
|||
}
|
||||
}
|
||||
} else {
|
||||
self.delegate.enter_forall(kind, |kind| {
|
||||
let goal = goal.with(self.cx(), ty::Binder::dummy(kind));
|
||||
self.add_goal(GoalSource::InstantiateHigherRanked, goal);
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
self.enter_forall(kind, |ecx, kind| {
|
||||
let goal = goal.with(ecx.cx(), ty::Binder::dummy(kind));
|
||||
ecx.add_goal(GoalSource::InstantiateHigherRanked, goal);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -840,12 +840,14 @@ where
|
|||
self.delegate.instantiate_binder_with_infer(value)
|
||||
}
|
||||
|
||||
/// `enter_forall`, but takes `&mut self` and passes it back through the
|
||||
/// callback since it can't be aliased during the call.
|
||||
pub(super) fn enter_forall<T: TypeFoldable<I> + Copy, U>(
|
||||
&self,
|
||||
&mut self,
|
||||
value: ty::Binder<I, T>,
|
||||
f: impl FnOnce(T) -> U,
|
||||
f: impl FnOnce(&mut Self, T) -> U,
|
||||
) -> U {
|
||||
self.delegate.enter_forall(value, f)
|
||||
self.delegate.enter_forall(value, |value| f(self, value))
|
||||
}
|
||||
|
||||
pub(super) fn resolve_vars_if_possible<T>(&self, value: T) -> T
|
||||
|
|
|
@ -895,10 +895,13 @@ where
|
|||
source_projection.item_def_id() == target_projection.item_def_id()
|
||||
&& ecx
|
||||
.probe(|_| ProbeKind::UpcastProjectionCompatibility)
|
||||
.enter(|ecx| -> Result<(), NoSolution> {
|
||||
ecx.eq(param_env, source_projection, target_projection)?;
|
||||
let _ = ecx.try_evaluate_added_goals()?;
|
||||
Ok(())
|
||||
.enter(|ecx| -> Result<_, NoSolution> {
|
||||
ecx.enter_forall(target_projection, |ecx, target_projection| {
|
||||
let source_projection =
|
||||
ecx.instantiate_binder_with_infer(source_projection);
|
||||
ecx.eq(param_env, source_projection, target_projection)?;
|
||||
ecx.try_evaluate_added_goals()
|
||||
})
|
||||
})
|
||||
.is_ok()
|
||||
};
|
||||
|
@ -909,11 +912,14 @@ where
|
|||
// Check that a's supertrait (upcast_principal) is compatible
|
||||
// with the target (b_ty).
|
||||
ty::ExistentialPredicate::Trait(target_principal) => {
|
||||
ecx.eq(
|
||||
param_env,
|
||||
upcast_principal.unwrap(),
|
||||
bound.rebind(target_principal),
|
||||
)?;
|
||||
let source_principal = upcast_principal.unwrap();
|
||||
let target_principal = bound.rebind(target_principal);
|
||||
ecx.enter_forall(target_principal, |ecx, target_principal| {
|
||||
let source_principal =
|
||||
ecx.instantiate_binder_with_infer(source_principal);
|
||||
ecx.eq(param_env, source_principal, target_principal)?;
|
||||
ecx.try_evaluate_added_goals()
|
||||
})?;
|
||||
}
|
||||
// Check that b_ty's projection is satisfied by exactly one of
|
||||
// a_ty's projections. First, we look through the list to see if
|
||||
|
@ -934,7 +940,12 @@ where
|
|||
Certainty::AMBIGUOUS,
|
||||
);
|
||||
}
|
||||
ecx.eq(param_env, source_projection, target_projection)?;
|
||||
ecx.enter_forall(target_projection, |ecx, target_projection| {
|
||||
let source_projection =
|
||||
ecx.instantiate_binder_with_infer(source_projection);
|
||||
ecx.eq(param_env, source_projection, target_projection)?;
|
||||
ecx.try_evaluate_added_goals()
|
||||
})?;
|
||||
}
|
||||
// Check that b_ty's auto traits are present in a_ty's bounds.
|
||||
ty::ExistentialPredicate::AutoTrait(def_id) => {
|
||||
|
@ -1187,17 +1198,15 @@ where
|
|||
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
self.probe_trait_candidate(source).enter(|ecx| {
|
||||
ecx.add_goals(
|
||||
GoalSource::ImplWhereBound,
|
||||
constituent_tys(ecx, goal.predicate.self_ty())?
|
||||
.into_iter()
|
||||
.map(|ty| {
|
||||
ecx.enter_forall(ty, |ty| {
|
||||
goal.with(ecx.cx(), goal.predicate.with_self_ty(ecx.cx(), ty))
|
||||
})
|
||||
let goals = constituent_tys(ecx, goal.predicate.self_ty())?
|
||||
.into_iter()
|
||||
.map(|ty| {
|
||||
ecx.enter_forall(ty, |ecx, ty| {
|
||||
goal.with(ecx.cx(), goal.predicate.with_self_ty(ecx.cx(), ty))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
ecx.add_goals(GoalSource::ImplWhereBound, goals);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue