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
41 changes: 40 additions & 1 deletion compiler/rustc_hir_analysis/src/check/always_applicable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_errors::codes::*;
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode};
use rustc_middle::span_bug;
use rustc_middle::ty::util::CheckRegions;
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
Expand Down Expand Up @@ -134,6 +134,45 @@ pub(crate) fn check_negative_auto_trait_impl<'tcx>(
}
}

/// Checks if the self ty's where-clauses are able to be proven. For instance, if we have multiple
/// overlapping drop impls, and we have `[T]: Sized` on both the impls and the self ty, we shouldn't
/// error or ICE, since neither the ADT nor the impls are nameable in practice.
///
/// We already emit errors for the case where the impossible bound exists only on the self ty, or
/// only on the impl(s).
pub(crate) fn is_impossible_self_ty(tcx: TyCtxt<'_>, adt_did: LocalDefId) -> bool {

@sjwang05 sjwang05 Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I basically lifted this logic wholesale from the is_impossible_associated_item query, though I couldn't really find a nice way to make them into a single shared thing, since is_impossible_associated_item filters the obligations to those that only mention the parent item's generics before registering them with the ocx.

View changes since the review

let clauses = tcx.clauses_of(adt_did).clauses;
if clauses.is_empty() {
return false;
}

// Be conservative in cases where we have `W<T: ?Sized>` and a method like `Self: Sized`,
// since that method *may* have some substitutions where the predicates hold.
//
// This replicates the logic we use in coherence.
let infcx = tcx
.infer_ctxt()
.ignoring_regions()
.with_next_trait_solver(true)
.enable_next_solver_overflow_fcw(false)
.build(TypingMode::Coherence);
let param_env = ty::ParamEnv::empty();
let args = infcx.fresh_args_for_item(tcx.def_span(adt_did), adt_did.to_def_id());

let obligations = clauses.iter().map(|(clause, span)| {
Obligation::new(
tcx,
ObligationCause::dummy_with_span(*span),
param_env,
ty::EarlyBinder::bind(tcx, *clause).instantiate(tcx, args).skip_norm_wip(),
)
});

let ocx = ObligationCtxt::new(&infcx);
ocx.register_obligations(obligations);
ocx.try_evaluate_obligations().has_errors()
}

fn ensure_impl_params_and_item_params_correspond<'tcx>(
tcx: TyCtxt<'tcx>,
impl_def_id: LocalDefId,
Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,11 @@ pub(super) fn provide(providers: &mut Providers) {
}

fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor> {
let dtor = tcx.calculate_dtor(def_id, always_applicable::check_drop_impl);
let dtor = tcx.calculate_dtor(
def_id,
always_applicable::check_drop_impl,
always_applicable::is_impossible_self_ty,
);
if dtor.is_none() && tcx.features().async_drop() {
if let Some(async_dtor) = adt_async_destructor(tcx, def_id) {
// When type has AsyncDrop impl, but doesn't have Drop impl, generate error
Expand All @@ -138,7 +142,11 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor>
}

fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
let result = tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl);
let result = tcx.calculate_async_dtor(
def_id,
always_applicable::check_drop_impl,
always_applicable::is_impossible_self_ty,
);
// Async drop in libstd/libcore would become insta-stable — catch that mistake.
if result.is_some() && tcx.features().staged_api() {
span_bug!(tcx.def_span(def_id), "don't use async drop in libstd, it becomes insta-stable");
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ impl<'tcx> TyCtxt<'tcx> {
self,
adt_did: LocalDefId,
validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>,
impossible_self_ty: impl Fn(Self, LocalDefId) -> bool,
) -> Option<ty::Destructor> {
let drop_trait = self.lang_items().drop_trait()?;
self.ensure_result().coherent_trait(drop_trait).ok()?;
Expand All @@ -394,6 +395,11 @@ impl<'tcx> TyCtxt<'tcx> {
continue;
}

if impossible_self_ty(self, adt_did) {
// The self ty is unnameable, so it can't be constructed in the first place.
continue;
}

let Some(&item_id) = self.associated_item_def_ids(impl_did).first() else {
self.dcx()
.span_delayed_bug(self.def_span(impl_did), "Drop impl without drop function");
Expand Down Expand Up @@ -424,6 +430,7 @@ impl<'tcx> TyCtxt<'tcx> {
self,
adt_did: LocalDefId,
validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>,
impossible_self_ty: impl Fn(Self, LocalDefId) -> bool,
) -> Option<ty::AsyncDestructor> {
let async_drop_trait = self.lang_items().async_drop_trait()?;
self.ensure_result().coherent_trait(async_drop_trait).ok()?;
Expand All @@ -441,6 +448,11 @@ impl<'tcx> TyCtxt<'tcx> {
continue;
}

if impossible_self_ty(self, adt_did) {
// The self ty is unnameable, so it can't be constructed in the first place.
continue;
}

if let Some(old_impl_did) = dtor_candidate {
self.dcx()
.struct_span_err(self.def_span(impl_did), "multiple async drop impls found")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
//@ known-bug: #153947
//@ check-pass

// Regression test for #153947

#![expect(drop_bounds)]

pub struct Thing<T>(T) where [T]: Sized, Self: Drop;
impl<T> Drop for Thing<T> where [T]: Sized, Self: Drop {
fn drop(&mut self) {}
}
impl<T> Drop for Thing<T> where [T]: Sized, Self: Drop {
fn drop(&mut self) {}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
//@ known-bug: #150387
//@ check-pass

// Regression test for #150387

#![feature(min_specialization)]
#![allow(dead_code)]

Expand All @@ -10,4 +13,5 @@ impl<T> Drop for Thing<T> where [T]: Sized {
impl<T> Drop for Thing<T> where [T]: Sized {
fn drop(&mut self) {}
}

fn main() {}
Loading