diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index ca9874ac727a1..d3a6aa2da52a9 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -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}; @@ -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 { + let clauses = tcx.clauses_of(adt_did).clauses; + if clauses.is_empty() { + return false; + } + + // Be conservative in cases where we have `W` 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, diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 9ce935c4389a5..9e3f90ec293b7 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -126,7 +126,11 @@ pub(super) fn provide(providers: &mut Providers) { } fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - 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 @@ -138,7 +142,11 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option } fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - 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"); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 5361d9268f6a6..d6d7cb00f4cce 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -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 { let drop_trait = self.lang_items().drop_trait()?; self.ensure_result().coherent_trait(drop_trait).ok()?; @@ -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"); @@ -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 { let async_drop_trait = self.lang_items().async_drop_trait()?; self.ensure_result().coherent_trait(async_drop_trait).ok()?; @@ -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") diff --git a/tests/crashes/153947.rs b/tests/ui/dropck/overlapping_impossible_drop.rs similarity index 83% rename from tests/crashes/153947.rs rename to tests/ui/dropck/overlapping_impossible_drop.rs index 39bc8c074cfc0..6200572b9b72e 100644 --- a/tests/crashes/153947.rs +++ b/tests/ui/dropck/overlapping_impossible_drop.rs @@ -1,5 +1,9 @@ -//@ known-bug: #153947 +//@ check-pass + +// Regression test for #153947 + #![expect(drop_bounds)] + pub struct Thing(T) where [T]: Sized, Self: Drop; impl Drop for Thing where [T]: Sized, Self: Drop { fn drop(&mut self) {} @@ -7,4 +11,5 @@ impl Drop for Thing where [T]: Sized, Self: Drop { impl Drop for Thing where [T]: Sized, Self: Drop { fn drop(&mut self) {} } + fn main() {} diff --git a/tests/crashes/150387.rs b/tests/ui/dropck/overlapping_impossible_drop_min_specialization.rs similarity index 84% rename from tests/crashes/150387.rs rename to tests/ui/dropck/overlapping_impossible_drop_min_specialization.rs index b29ce143fb4ea..e201c75f4f4b3 100644 --- a/tests/crashes/150387.rs +++ b/tests/ui/dropck/overlapping_impossible_drop_min_specialization.rs @@ -1,4 +1,7 @@ -//@ known-bug: #150387 +//@ check-pass + +// Regression test for #150387 + #![feature(min_specialization)] #![allow(dead_code)] @@ -10,4 +13,5 @@ impl Drop for Thing where [T]: Sized { impl Drop for Thing where [T]: Sized { fn drop(&mut self) {} } + fn main() {}