Skip to content

Commit ce9d867

Browse files
committed
do not leak auto traits in item bounds
1 parent f836ae4 commit ce9d867

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+25-7
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ where
164164
ecx: &mut EvalCtxt<'_, D>,
165165
goal: Goal<I, Self>,
166166
) -> Result<Candidate<I>, NoSolution> {
167+
let cx = ecx.cx();
167168
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
168169
return Err(NoSolution);
169170
}
@@ -174,20 +175,37 @@ where
174175

175176
// Only consider auto impls of unsafe traits when there are no unsafe
176177
// fields.
177-
if ecx.cx().trait_is_unsafe(goal.predicate.def_id())
178+
if cx.trait_is_unsafe(goal.predicate.def_id())
178179
&& goal.predicate.self_ty().has_unsafe_fields()
179180
{
180181
return Err(NoSolution);
181182
}
182183

183-
// We only look into opaque types during analysis for opaque types
184-
// outside of their defining scope. Doing so for opaques in the
185-
// defining scope may require calling `typeck` on the same item we're
186-
// currently type checking, which will result in a fatal cycle that
187-
// ideally we want to avoid, since we can make progress on this goal
188-
// via an alias bound or a locally-inferred hidden type instead.
184+
// We leak the implemented auto traits of opaques outside of their defining scope.
185+
// This depends on `typeck` of the defining scope of that opaque, which may result in
186+
// fatal query cycles.
187+
//
188+
// We only get to this point if we're outside of the defining scope as we'd otherwise
189+
// be able to normalize the opaque type. We may also cycle in case `typeck` of a defining
190+
// scope relies on the current context, e.g. either because it also leaks auto trait
191+
// bounds of opaques defined in the current context or by evaluating the current item.
192+
//
193+
// To avoid this we don't try to leak auto trait bounds if they can also be proven via
194+
// item bounds of the opaque. These bounds are always applicable as auto traits must not
195+
// have any generic parameters. They would also get preferred over the impl candidate
196+
// when merging candidates anyways.
197+
//
198+
// See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs.
189199
if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
190200
debug_assert!(ecx.opaque_type_is_rigid(opaque_ty.def_id));
201+
for item_bound in cx.item_self_bounds(opaque_ty.def_id).skip_binder() {
202+
if item_bound
203+
.as_trait_clause()
204+
.is_some_and(|b| b.def_id() == goal.predicate.def_id())
205+
{
206+
return Err(NoSolution);
207+
}
208+
}
191209
}
192210

193211
ecx.probe_and_evaluate_goal_for_constituent_tys(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ check-pass
2+
//@ revisions: current next
3+
//@[next] compile-flags: -Znext-solver
4+
//@ ignore-compare-mode-next-solver (explicit revisions)
5+
6+
// When proving auto trait bounds, make sure that we depend on auto trait
7+
// leakage if we can also prove it via an item bound.
8+
fn is_send<T: Send>(_: T) {}
9+
10+
fn direct() -> impl Send {
11+
is_send(check(false)); // leaks auto traits, depends on `check`
12+
1u16
13+
}
14+
15+
trait Indir: Send {}
16+
impl Indir for u32 {}
17+
fn indir() -> impl Indir {
18+
is_send(check(false)); // leaks auto traits, depends on `check`
19+
1u32
20+
}
21+
22+
fn check(b: bool) -> impl Sized {
23+
if b {
24+
// must not leak auto traits, as we otherwise get a query cycle.
25+
is_send(direct());
26+
is_send(indir());
27+
}
28+
1u64
29+
}
30+
31+
fn main() {
32+
check(true);
33+
}

tests/ui/impl-trait/in-trait/refine-cycle.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
//@ check-pass
2+
//@ revisions: current next
3+
//@[next] compile-flags: -Znext-solver
4+
//@ ignore-compare-mode-next-solver (explicit revisions)
25

36
// Make sure that refinement checking doesn't cause a cycle in `Instance::resolve`
47
// which calls `compare_impl_item`.

0 commit comments

Comments
 (0)