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 17172c5333502..6d93634d616ee 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 @@ -60,7 +60,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:?}`") } @@ -106,15 +107,6 @@ where .map(Unnormalized::skip_norm_wip) .collect(), )), - - 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(), - ])) - } } } 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 51ec0bcbf5ec2..f2c85509b9d42 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -236,19 +236,11 @@ 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, .. }) = goal.predicate.self_ty().kind() { debug_assert!(is_rigid == ty::IsRigid::Yes); - - 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()); - } - } + return ecx.consider_auto_trait_candidate_for_opaque_ty(goal, def_id, args); } // We need to make sure to stall any coroutines we are inferring to avoid query cycles. @@ -1286,6 +1278,52 @@ 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() { + match self.opaque_accesses.rerun_always(RerunReason::AutoTraitLeakage)? {} + } + + 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) + }); + + let opaque_types_may_leak = match self.typing_mode() { + TypingMode::PostTypeckUntilBorrowck { .. } => false, + TypingMode::Coherence + | TypingMode::Typeck { .. } + | TypingMode::PostBorrowck { .. } + | TypingMode::Reflection + | TypingMode::PostAnalysis + | TypingMode::Codegen + | TypingMode::ErasedNotCoherence(MayBeErased) => true, + }; + + match candidate { + Ok(candidate) if has_only_region_constraints(candidate.result) => Ok(candidate), + Ok(candidate) if !opaque_types_may_leak => 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/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..d1b4e54ff3bb2 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs @@ -0,0 +1,38 @@ +//@ ignore-compare-mode-next-solver +//@ compile-flags: -Znext-solver +//@ aux-build:opaque-auto-trait-leakage.rs + +//! Regression test for https://github.com/rust-lang/rust/issues/134578. +//! Leaking the auto traits of a foreign opaque must not constrain inference +//! variables in the caller, as that would leak the hidden type itself. Here +//! that would constrain `NameMe` to a closure from the auxiliary crate and +//! ICE in typeck. The hidden type may still show up in the diagnostic. + +#![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 mismatch resolving `::Assoc == {closure@define::{closure#0}}` + 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..b70fde561cd43 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.stderr @@ -0,0 +1,30 @@ +error[E0271]: type mismatch resolving `::Assoc == {closure@define::{closure#0}}` + --> $DIR/opaque-hidden-ty-inference.rs:31:31 + | +LL | let opaque = require_auto(define::()); + | ------------ ^^^^^^^^^^^^^ expected closure, found `!` + | | + | required by a bound introduced by this call + | + ::: $DIR/auxiliary/opaque-auto-trait-leakage.rs:14:29 + | +LL | WaddupGamers(None::, || ()) + | -- the expected closure + | + = note: expected closure `{closure@dep::define::{closure#0}}` + found type `!` + = note: required for `WaddupGamers::{closure#0}}>` to implement `Unpin` +note: required because it appears within the type `impl Sized` + --> $DIR/auxiliary/opaque-auto-trait-leakage.rs:13:23 + | +LL | pub fn define() -> impl Sized { + | ^^^^^^^^^^ +note: required by a bound in `require_auto` + --> $DIR/opaque-hidden-ty-inference.rs:18: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 E0271`.