do not leak auto traits in item bounds
This commit is contained in:
parent
f836ae4e66
commit
ce9d8678ca
3 changed files with 61 additions and 7 deletions
|
@ -164,6 +164,7 @@ where
|
||||||
ecx: &mut EvalCtxt<'_, D>,
|
ecx: &mut EvalCtxt<'_, D>,
|
||||||
goal: Goal<I, Self>,
|
goal: Goal<I, Self>,
|
||||||
) -> Result<Candidate<I>, NoSolution> {
|
) -> Result<Candidate<I>, NoSolution> {
|
||||||
|
let cx = ecx.cx();
|
||||||
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
||||||
return Err(NoSolution);
|
return Err(NoSolution);
|
||||||
}
|
}
|
||||||
|
@ -174,20 +175,37 @@ where
|
||||||
|
|
||||||
// Only consider auto impls of unsafe traits when there are no unsafe
|
// Only consider auto impls of unsafe traits when there are no unsafe
|
||||||
// fields.
|
// fields.
|
||||||
if ecx.cx().trait_is_unsafe(goal.predicate.def_id())
|
if cx.trait_is_unsafe(goal.predicate.def_id())
|
||||||
&& goal.predicate.self_ty().has_unsafe_fields()
|
&& goal.predicate.self_ty().has_unsafe_fields()
|
||||||
{
|
{
|
||||||
return Err(NoSolution);
|
return Err(NoSolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only look into opaque types during analysis for opaque types
|
// We leak the implemented auto traits of opaques outside of their defining scope.
|
||||||
// outside of their defining scope. Doing so for opaques in the
|
// This depends on `typeck` of the defining scope of that opaque, which may result in
|
||||||
// defining scope may require calling `typeck` on the same item we're
|
// fatal query cycles.
|
||||||
// currently type checking, which will result in a fatal cycle that
|
//
|
||||||
// ideally we want to avoid, since we can make progress on this goal
|
// We only get to this point if we're outside of the defining scope as we'd otherwise
|
||||||
// via an alias bound or a locally-inferred hidden type instead.
|
// be able to normalize the opaque type. We may also cycle in case `typeck` of a defining
|
||||||
|
// scope relies on the current context, e.g. either because it also leaks auto trait
|
||||||
|
// bounds of opaques defined in the current context or by evaluating the current item.
|
||||||
|
//
|
||||||
|
// To avoid this we don't try to leak auto trait bounds if they can also be proven via
|
||||||
|
// item bounds of the opaque. These bounds are always applicable as auto traits must not
|
||||||
|
// have any generic parameters. They would also get preferred over the impl candidate
|
||||||
|
// when merging candidates anyways.
|
||||||
|
//
|
||||||
|
// See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs.
|
||||||
if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
|
if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
|
||||||
debug_assert!(ecx.opaque_type_is_rigid(opaque_ty.def_id));
|
debug_assert!(ecx.opaque_type_is_rigid(opaque_ty.def_id));
|
||||||
|
for item_bound in cx.item_self_bounds(opaque_ty.def_id).skip_binder() {
|
||||||
|
if item_bound
|
||||||
|
.as_trait_clause()
|
||||||
|
.is_some_and(|b| b.def_id() == goal.predicate.def_id())
|
||||||
|
{
|
||||||
|
return Err(NoSolution);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
//@ check-pass
|
||||||
|
//@ revisions: current next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||||
|
|
||||||
|
// When proving auto trait bounds, make sure that we depend on auto trait
|
||||||
|
// leakage if we can also prove it via an item bound.
|
||||||
|
fn is_send<T: Send>(_: T) {}
|
||||||
|
|
||||||
|
fn direct() -> impl Send {
|
||||||
|
is_send(check(false)); // leaks auto traits, depends on `check`
|
||||||
|
1u16
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Indir: Send {}
|
||||||
|
impl Indir for u32 {}
|
||||||
|
fn indir() -> impl Indir {
|
||||||
|
is_send(check(false)); // leaks auto traits, depends on `check`
|
||||||
|
1u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(b: bool) -> impl Sized {
|
||||||
|
if b {
|
||||||
|
// must not leak auto traits, as we otherwise get a query cycle.
|
||||||
|
is_send(direct());
|
||||||
|
is_send(indir());
|
||||||
|
}
|
||||||
|
1u64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
check(true);
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
//@ check-pass
|
//@ check-pass
|
||||||
|
//@ revisions: current next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||||
|
|
||||||
// Make sure that refinement checking doesn't cause a cycle in `Instance::resolve`
|
// Make sure that refinement checking doesn't cause a cycle in `Instance::resolve`
|
||||||
// which calls `compare_impl_item`.
|
// which calls `compare_impl_item`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue