From 80e76e47c7d08b59b0f8cb389492ed31718c0808 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 18 Jul 2026 13:03:55 +0000 Subject: [PATCH 1/6] add opaque test --- .../auxiliary/opaque-auto-trait-leakage.rs | 15 +++++++++ .../opaque-hidden-ty-inference.rs | 32 +++++++++++++++++++ .../opaque-hidden-ty-inference.stderr | 18 +++++++++++ 3 files changed, 65 insertions(+) create mode 100644 tests/ui/impl-trait/auto-trait-leakage/auxiliary/opaque-auto-trait-leakage.rs create mode 100644 tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs create mode 100644 tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr diff --git a/tests/ui/impl-trait/auto-trait-leakage/auxiliary/opaque-auto-trait-leakage.rs b/tests/ui/impl-trait/auto-trait-leakage/auxiliary/opaque-auto-trait-leakage.rs new file mode 100644 index 0000000000000..d7a2c4a8642eb --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-leakage/auxiliary/opaque-auto-trait-leakage.rs @@ -0,0 +1,15 @@ +pub struct WaddupGamers(Option, U); + +impl, U> Unpin for WaddupGamers {} + +pub trait Leak { + type Assoc; +} + +impl Leak for T { + type Assoc = T; +} + +pub fn define() -> impl Sized { + WaddupGamers(None::, || ()) +} diff --git a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs new file mode 100644 index 0000000000000..97a9e4d393356 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs @@ -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(x: T) -> T { + x +} + +type NameMe = impl Sized; + +#[define_opaque(NameMe)] +fn leak() -> NameMe +where + T: Leak>, +{ + // Proving `impl Sized: Unpin` must not constrain `NameMe` + // to the foreign closure hidden inside `define`. + let opaque = require_auto(define::()); + //~^ ERROR type annotations needed + let closure; + loop {} + return closure; +} + +fn main() {} diff --git a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr new file mode 100644 index 0000000000000..d42546ed9ed14 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr @@ -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::()); + | ------------ ^^^^^^^^^^^^^ + | | + | 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(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`. From 6c7dbaf9166aa60018faf0bad39d515dfd5f1a39 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 20 Jul 2026 07:00:56 +0000 Subject: [PATCH 2/6] next-solver: avoid leaking opaque hidden types via auto trait probes --- .../src/solve/trait_goals.rs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 924406b7b18d4..d3816eca19a74 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -245,7 +245,8 @@ 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 }, .. }) = + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = goal.predicate.self_ty().kind() { debug_assert!(is_rigid == ty::IsRigid::Yes); @@ -262,6 +263,29 @@ where return Err(NoSolution.into()); } } + + 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(); + return 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), + }; } // We need to make sure to stall any coroutines we are inferring to avoid query cycles. @@ -270,7 +294,7 @@ where } ecx.probe_and_evaluate_goal_for_constituent_tys( - CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + source, goal, structural_traits::instantiate_constituent_tys_for_auto_trait, ) From 764e206f6421fa7dc9c8b0bf83b1b4cadf373866 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Jul 2026 06:55:05 +0000 Subject: [PATCH 3/6] define consider_auto_trait_candidate_for_opaque_ty and call it from consider_auto_trait_candidate --- .../src/solve/assembly/structural_traits.rs | 67 ++++++++++++++++--- .../src/solve/trait_goals.rs | 56 +++------------- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index ae78d68865de3..b0a477a1ea5c3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -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, Region, TypeFoldable, - TypeSuperFoldable, Unnormalized, Upcast as _, elaborate, + self as ty, Binder, FallibleTypeFolder, Interner, Movability, Mutability, Region, + 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)] @@ -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( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal>, + def_id: I::OpaqueTyId, + args: I::GenericArgs, +) -> Result, NoSolutionOrRerunNonErased> +where + D: SolverDelegate, + 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), } } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index d3816eca19a74..030c32ce54a3c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -245,59 +245,23 @@ where // when merging candidates anyways. // // See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs. - let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = 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; } - 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(); - return 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), - }; - } - - // 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( - source, - goal, - structural_traits::instantiate_constituent_tys_for_auto_trait, - ) } fn consider_trait_alias_candidate( From 568806f86129e5066425b18c0328d3f1dbadd3de Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 25 Jul 2026 17:41:18 +0000 Subject: [PATCH 4/6] make the match exhaustive again --- .../src/solve/assembly/structural_traits.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index b0a477a1ea5c3..eff148509c305 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -62,7 +62,8 @@ where | ty::Placeholder(..) | ty::Alias(ty::IsRigid::No, _) | ty::Bound(..) - | ty::Infer(_) => { + | ty::Infer(_) + | ty::Alias(ty::IsRigid::Yes, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { panic!("unexpected type `{ty:?}`") } @@ -108,8 +109,6 @@ where .map(Unnormalized::skip_norm_wip) .collect(), )), - // Opaque types are already handled earlier - _ => unreachable!(), } } @@ -146,17 +145,9 @@ where 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), } } From f6687b0594f0a097068a2483c3276400ea6971c8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 25 Jul 2026 17:42:07 +0000 Subject: [PATCH 5/6] Don't let thte diagnostic leak the inner types of opaque types --- .../src/solve/fulfill/derive_errors.rs | 60 ++++++++++++++++++- .../opaque-hidden-ty-inference.rs | 2 +- .../opaque-hidden-ty-inference.stderr | 9 +-- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index bb75ae6247e38..637379a86a313 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_hir::attrs::lang_items::{self as hir, LangItem}; use rustc_infer::infer::InferCtxt; -use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; +use rustc_infer::traits::solve::{BuiltinImplSource, CandidateSource, GoalSource, MaybeCause}; use rustc_infer::traits::{ self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, @@ -248,6 +248,55 @@ impl<'tcx> BestObligation<'tcx> { candidates } + fn is_opaque_auto_trait_candidate( + &self, + tcx: TyCtxt<'tcx>, + candidate: &inspect::InspectCandidate<'_, 'tcx>, + pred: ty::Predicate<'tcx>, + ) -> bool { + self.is_builtin_misc_trait_candidate(candidate) + && self.is_positive_auto_trait_predicate_with_opaque_self(tcx, pred) + } + + fn is_builtin_misc_trait_candidate( + &self, + candidate: &inspect::InspectCandidate<'_, 'tcx>, + ) -> bool { + matches!( + candidate.kind(), + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result: _, + } + ) + } + + fn is_positive_auto_trait_predicate_with_opaque_self( + &self, + tcx: TyCtxt<'tcx>, + pred: ty::Predicate<'tcx>, + ) -> bool { + let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = + pred.kind().skip_binder() + else { + return false; + }; + + if trait_pred.polarity != ty::PredicatePolarity::Positive + || !tcx.trait_is_auto(trait_pred.def_id()) + { + return false; + } + + let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }) = + trait_pred.self_ty().kind() + else { + return false; + }; + + !matches!(tcx.opaque_ty_origin(*def_id), hir::OpaqueTyOrigin::AsyncFn { .. }) + } + /// HACK: We walk the nested obligations for a well-formed arg manually, /// since there's nontrivial logic in `wf.rs` to set up an obligation cause. /// Ideally we'd be able to track this better. @@ -447,6 +496,13 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { return ControlFlow::Break(self.obligation.clone()); } + // Don't walk into opaque auto trait candidates, as doing so would expose + // the opaque's hidden type in diagnostics outside of its defining scope. + if self.is_opaque_auto_trait_candidate(tcx, candidate, pred) { + trace!("opaque auto trait candidate -> exit"); + return ControlFlow::Break(self.obligation.clone()); + } + // FIXME: Also, what about considering >1 layer up the stack? May be necessary // for normalizes-to. let child_mode = match pred.kind().skip_binder() { diff --git a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs index 97a9e4d393356..1c840182655d2 100644 --- a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs +++ b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs @@ -23,7 +23,7 @@ where // Proving `impl Sized: Unpin` must not constrain `NameMe` // to the foreign closure hidden inside `define`. let opaque = require_auto(define::()); - //~^ ERROR type annotations needed + //~^ ERROR `impl Sized` cannot be unpinned let closure; loop {} return closure; diff --git a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr index d42546ed9ed14..48418a942aa08 100644 --- a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr +++ b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr @@ -1,12 +1,13 @@ -error[E0283]: type annotations needed: cannot satisfy `impl Sized: Unpin` +error[E0277]: `impl Sized` cannot be unpinned --> $DIR/opaque-hidden-ty-inference.rs:25:31 | LL | let opaque = require_auto(define::()); - | ------------ ^^^^^^^^^^^^^ + | ------------ ^^^^^^^^^^^^^ the trait `Unpin` is not implemented for `impl Sized` | | | required by a bound introduced by this call | - = note: cannot satisfy `impl Sized: Unpin` + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope note: required by a bound in `require_auto` --> $DIR/opaque-hidden-ty-inference.rs:12:20 | @@ -15,4 +16,4 @@ LL | fn require_auto(x: T) -> T { error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0283`. +For more information about this error, try `rustc --explain E0277`. From d974280e959b0b3db533d2605a7d2c37111bb994 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 30 Jul 2026 10:07:40 +0000 Subject: [PATCH 6/6] Add description to test and move consider_auto_trait_candidate_for_opaque_ty in trait goaks --- .../src/solve/assembly/structural_traits.rs | 52 ++----------------- .../src/solve/trait_goals.rs | 38 +++++++++++++- .../src/solve/fulfill/derive_errors.rs | 6 +-- .../opaque-hidden-ty-inference.rs | 5 ++ .../opaque-hidden-ty-inference.stderr | 4 +- 5 files changed, 51 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index eff148509c305..2f9228ef7e9e8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -5,21 +5,17 @@ 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, Region, - TraitPredicate, TypeFoldable, TypeSuperFoldable, Unnormalized, Upcast as _, elaborate, + self as ty, Binder, FallibleTypeFolder, Interner, Movability, Mutability, Region, 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, BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, - NoSolution, has_only_region_constraints, -}; +use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution}; // Calculates the constituent types of a type for `auto trait` purposes. #[instrument(level = "trace", skip(ecx), ret)] @@ -112,46 +108,6 @@ where } } -pub(in crate::solve) fn consider_auto_trait_candidate_for_opaque_ty( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal>, - def_id: I::OpaqueTyId, - args: I::GenericArgs, -) -> Result, NoSolutionOrRerunNonErased> -where - D: SolverDelegate, - 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()); - } - } - - 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) - }); - - match candidate { - Ok(candidate) if has_only_region_constraints(candidate.result) => Ok(candidate), - Ok(_) => ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS), - Err(err) => Err(err), - } -} - #[instrument(level = "trace", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_sizedness_trait( ecx: &EvalCtxt<'_, D>, diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 030c32ce54a3c..6be05c185bd0a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -249,7 +249,7 @@ where goal.predicate.self_ty().kind() { debug_assert!(is_rigid == ty::IsRigid::Yes); - structural_traits::consider_auto_trait_candidate_for_opaque_ty(ecx, goal, def_id, args) + ecx.consider_auto_trait_candidate_for_opaque_ty(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()) { @@ -1276,6 +1276,42 @@ where .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } + fn consider_auto_trait_candidate_for_opaque_ty( + &mut self, + goal: Goal>, + def_id: I::OpaqueTyId, + args: I::GenericArgs, + ) -> Result, NoSolutionOrRerunNonErased> { + let cx = self.cx(); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + if self.opaque_accesses.might_rerun() { + return match self.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()); + } + } + + let candidate = self.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) + }); + + match candidate { + Ok(candidate) if has_only_region_constraints(candidate.result) => Ok(candidate), + Ok(_) => self.forced_ambiguity(MaybeInfo::AMBIGUOUS), + Err(err) => Err(err), + } + } + // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 637379a86a313..6a1f32eb5c7f8 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -1,6 +1,6 @@ use std::ops::ControlFlow; -use rustc_hir::attrs::lang_items::{self as hir, LangItem}; +use rustc_hir::attrs::lang_items::LangItem; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::solve::{BuiltinImplSource, CandidateSource, GoalSource, MaybeCause}; use rustc_infer::traits::{ @@ -288,13 +288,13 @@ impl<'tcx> BestObligation<'tcx> { return false; } - let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }) = + let ty::Alias(ty::IsRigid::Yes, ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }) = trait_pred.self_ty().kind() else { return false; }; - !matches!(tcx.opaque_ty_origin(*def_id), hir::OpaqueTyOrigin::AsyncFn { .. }) + !matches!(tcx.opaque_ty_origin(*def_id), rustc_hir::OpaqueTyOrigin::AsyncFn { .. }) } /// HACK: We walk the nested obligations for a well-formed arg manually, diff --git a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs index 1c840182655d2..c5bc085efe2a1 100644 --- a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs +++ b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs @@ -2,6 +2,11 @@ //@ compile-flags: -Znext-solver //@ aux-build:opaque-auto-trait-leakage.rs +//! Regression test for https://github.com/rust-lang/rust/issues/134578. +//! When reporting a failed auto-trait obligation for an opaque type, diagnostics +//! must not reveal the opaque's hidden type. In this test, the error should refer +//! only to `impl Sized`, without exposing the concrete type from the auxiliary crate. + #![feature(type_alias_impl_trait)] #![allow(unused)] diff --git a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr index 48418a942aa08..b7b4bba931e26 100644 --- a/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr +++ b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr @@ -1,5 +1,5 @@ error[E0277]: `impl Sized` cannot be unpinned - --> $DIR/opaque-hidden-ty-inference.rs:25:31 + --> $DIR/opaque-hidden-ty-inference.rs:30:31 | LL | let opaque = require_auto(define::()); | ------------ ^^^^^^^^^^^^^ the trait `Unpin` is not implemented for `impl Sized` @@ -9,7 +9,7 @@ LL | let opaque = require_auto(define::()); = note: consider using the `pin!` macro consider using `Box::pin` if you need to access the pinned value outside of the current scope note: required by a bound in `require_auto` - --> $DIR/opaque-hidden-ty-inference.rs:12:20 + --> $DIR/opaque-hidden-ty-inference.rs:17:20 | LL | fn require_auto(x: T) -> T { | ^^^^^ required by this bound in `require_auto`