Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ use derive_where::derive_where;
use rustc_type_ir::data_structures::HashMap;
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::{SolverProjectionLangItem, SolverTraitLangItem};
use rustc_type_ir::solve::SizedTraitKind;
use rustc_type_ir::solve::inspect::ProbeKind;
use rustc_type_ir::solve::{MaybeInfo, NoSolutionOrRerunNonErased, RerunReason, SizedTraitKind};
use rustc_type_ir::{
self as ty, Binder, FallibleTypeFolder, Interner, Movability, Mutability, TypeFoldable,
TypeSuperFoldable, Unnormalized, Upcast as _, elaborate,
self as ty, Binder, FallibleTypeFolder, Interner, Movability, Mutability, TraitPredicate,
TypeFoldable, TypeSuperFoldable, Unnormalized, Upcast as _, elaborate,
};
use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
use tracing::instrument;

use super::Candidate;
use crate::delegate::SolverDelegate;
use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution};
use crate::solve::{
AdtDestructorKind, BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource,
NoSolution, has_only_region_constraints,
};

// Calculates the constituent types of a type for `auto trait` purposes.
#[instrument(level = "trace", skip(ecx), ret)]
Expand Down Expand Up @@ -104,15 +108,56 @@ where
.map(Unnormalized::skip_norm_wip)
.collect(),
)),
// Opaque types are already handled earlier
_ => unreachable!(),
}
}

pub(in crate::solve) fn consider_auto_trait_candidate_for_opaque_ty<D, I>(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, TraitPredicate<I>>,
def_id: I::OpaqueTyId,
args: I::GenericArgs,
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
let cx = ecx.cx();
let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
if ecx.opaque_accesses.might_rerun() {
return match ecx.opaque_accesses.rerun_always(RerunReason::AutoTraitLeakage) {
Err(e) => Err(e.into()),
};
}

for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() {
if item_bound.as_trait_clause().is_some_and(|b| b.def_id() == goal.predicate.def_id()) {
return Err(NoSolution.into());
}
}

ty::Alias(ty::IsRigid::Yes, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => {
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
Ok(ty::Binder::dummy(vec![
cx.type_of(def_id.into()).instantiate(cx, args).skip_norm_wip(),
]))
let candidate = ecx.probe_trait_candidate(source).enter(|ecx| {
let hidden_ty = cx.type_of(def_id.into()).instantiate(cx, args).skip_norm_wip();
ecx.add_goal(
GoalSource::ImplWhereBound,
goal.with(cx, goal.predicate.with_replaced_self_ty(cx, hidden_ty)),
)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
});

// If this hidden-type proof would constrain non-region inference,
// treating it as a hard success can reveal the hidden type to the
// caller. A hard failure can do the same when it depends on caller
// bounds. For now, we are very conservative with caller bounds.
let param_env_may_leak_hidden_ty = !goal.param_env.caller_bounds().is_empty();
match candidate {
Ok(candidate) if has_only_region_constraints(candidate.result) => Ok(candidate),
Ok(_) => ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS),
Err(NoSolutionOrRerunNonErased::NoSolution(_)) if param_env_may_leak_hidden_ty => {
ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS)
}
Err(err) => Err(err),
}
}

Expand Down
34 changes: 11 additions & 23 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,35 +235,23 @@ where
// when merging candidates anyways.
//
// See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs.
if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) =
if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) =

@lcnr lcnr Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

hmm, please move this entire branch into a sub function and change this function to an if else

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in this 2e546e0

goal.predicate.self_ty().kind()
{
debug_assert!(is_rigid == ty::IsRigid::Yes);
if ecx.opaque_accesses.might_rerun() {
ecx.opaque_accesses.rerun_always(RerunReason::AutoTraitLeakage)?;
return Err(NoSolution.into());
}

for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() {
if item_bound
.as_trait_clause()
.is_some_and(|b| b.def_id() == goal.predicate.def_id())
{
return Err(NoSolution.into());
}
structural_traits::consider_auto_trait_candidate_for_opaque_ty(ecx, goal, def_id, args)
} else {
// We need to make sure to stall any coroutines we are inferring to avoid query cycles.
if let Some(cand) = ecx.try_stall_coroutine(goal.predicate.self_ty()) {
return cand;
}
}

// We need to make sure to stall any coroutines we are inferring to avoid query cycles.
if let Some(cand) = ecx.try_stall_coroutine(goal.predicate.self_ty()) {
return cand;
ecx.probe_and_evaluate_goal_for_constituent_tys(
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
goal,
structural_traits::instantiate_constituent_tys_for_auto_trait,
)
}

ecx.probe_and_evaluate_goal_for_constituent_tys(
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
goal,
structural_traits::instantiate_constituent_tys_for_auto_trait,
)
}

fn consider_trait_alias_candidate(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pub struct WaddupGamers<T, U>(Option<T>, U);

impl<T: Leak<Assoc = U>, U> Unpin for WaddupGamers<T, U> {}

pub trait Leak {
type Assoc;
}

impl<T> Leak for T {
type Assoc = T;
}

pub fn define<T>() -> impl Sized {
WaddupGamers(None::<T>, || ())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//@ ignore-compare-mode-next-solver
//@ compile-flags: -Znext-solver
//@ aux-build:opaque-auto-trait-leakage.rs

#![feature(type_alias_impl_trait)]
#![allow(unused)]

extern crate opaque_auto_trait_leakage as dep;

use dep::*;

fn require_auto<T: Unpin>(x: T) -> T {
x
}

type NameMe<T> = impl Sized;

#[define_opaque(NameMe)]
fn leak<T>() -> NameMe<T>
where
T: Leak<Assoc = NameMe<T>>,
{
// Proving `impl Sized: Unpin` must not constrain `NameMe<T>`
// to the foreign closure hidden inside `define`.
let opaque = require_auto(define::<T>());
//~^ ERROR type annotations needed
let closure;
loop {}
return closure;
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0283]: type annotations needed: cannot satisfy `impl Sized: Unpin`
--> $DIR/opaque-hidden-ty-inference.rs:25:31
|
LL | let opaque = require_auto(define::<T>());
| ------------ ^^^^^^^^^^^^^
| |
| required by a bound introduced by this call
|
= note: cannot satisfy `impl Sized: Unpin`
note: required by a bound in `require_auto`
--> $DIR/opaque-hidden-ty-inference.rs:12:20
|
LL | fn require_auto<T: Unpin>(x: T) -> T {
| ^^^^^ required by this bound in `require_auto`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.
Loading