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 @@ -58,7 +58,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:?}`")
}

Expand Down Expand Up @@ -104,15 +105,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(),
]))
}
}
}

Expand Down
70 changes: 47 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 @@ -245,35 +245,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, .. }) =
Comment thread
bit-aloo marked this conversation as resolved.
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());
}
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()) {
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 Expand Up @@ -1288,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<I, TraitPredicate<I>>,
def_id: I::OpaqueTyId,
args: I::GenericArgs,
) -> Result<Candidate<I>, 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::ops::ControlFlow;

use rustc_hir::attrs::lang_items::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,
Expand Down Expand Up @@ -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::IsRigid::Yes, ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }) =
trait_pred.self_ty().kind()
else {
return false;
};

!matches!(tcx.opaque_ty_origin(*def_id), rustc_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.
Expand Down Expand Up @@ -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() {
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,37 @@
//@ ignore-compare-mode-next-solver
//@ compile-flags: -Znext-solver
//@ aux-build:opaque-auto-trait-leakage.rs

Comment thread
lcnr marked this conversation as resolved.
//! 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)]

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 `impl Sized` cannot be unpinned
let closure;
loop {}
return closure;
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0277]: `impl Sized` cannot be unpinned
--> $DIR/opaque-hidden-ty-inference.rs:30:31
|
LL | let opaque = require_auto(define::<T>());
| ------------ ^^^^^^^^^^^^^ the trait `Unpin` is not implemented for `impl Sized`
| |
| required by a bound introduced by this call
|
= 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:17: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 E0277`.
Loading