Skip to content

don't calculate dtors if the self ty has impossible bounds - #162715

Open
sjwang05 wants to merge 1 commit into
rust-lang:mainfrom
sjwang05:no-impossible-dtors
Open

sjwang05 wants to merge 1 commit into
rust-lang:mainfrom
sjwang05:no-impossible-dtors

Conversation

@sjwang05

@sjwang05 sjwang05 commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

If we have multiple drop impls and a self ty that all have the same impossible bounds, we end up with duplicate drops with different DefIds, which causes us to ICE with a delayed bug in calculate_dtor. Coherence accepts these impls since they have impossible bounds, and nothing else in check_drop_impl checks this, leading to us accepting the code and subsequently ICEing from the delayed bug.

We now skip considering the current drop impl as a dtor candidate if the self ty and impl(s) have the same impossible bounds, since if the self ty is unnameable, it's impossible to construct an instance in the first place. Checking only the self ty is sufficient here, as check_drop_impl checks the case where the impossible bounds exist only on the drop impl, and we're not allowed in general to have a struct whose bounds are more restrictive than its impl's, so the only case we're currently missing is the case where the self ty and the Drop impls have the exact same impossible bounds.

My reasoning for accepting such code instead of emitting an error is that, in general, we allow users to write code with impossible or trivial bounds, so long as they never try to actually name or run that code. This PR makes Drop behave the same way.

This results in overlapping impossible drop impls behaving like other traits: for instance, the following compiles on nightly today:

pub trait Foo {
    fn foo(&self) {}
}

pub struct Thing<T>(T)
where
    [T]: Sized;

impl<T> Foo for Thing<T> where [T]: Sized {}
impl<T> Foo for Thing<T> where [T]: Sized {} // removing this impl is ok as well

Although this ICE only seems to be reachable for sync drop, not making the same change for async drop makes the following program, which currently compiles on nightly, stop compiling:

#![feature(async_drop)]
use core::future::AsyncDrop;
use core::pin::Pin;

pub struct Thing<T>(T) where [T]: Sized;

impl<T> Drop for Thing<T> where [T]: Sized {
    fn drop(&mut self) {}
}
impl<T> AsyncDrop for Thing<T> where [T]: Sized {
    async fn drop(self: Pin<&mut Self>) {}
}

which feels like something we don't want.

cc #159118 (comment)

fixes #153947

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 13, 2026
@rustbot

rustbot commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

r? @oli-obk

rustbot has assigned @oli-obk.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler, types
  • compiler, types expanded to 76 candidates
  • Random selection from 18 candidates

///
/// 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

@rust-log-analyzer

This comment has been minimized.

@rustbot

rustbot commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

This PR changes a file inside tests/crashes. If a crash was fixed, please move into the corresponding ui subdir and add 'Fixes #' to the PR description to autoclose the issue upon merge.

@oli-obk

oli-obk commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

#150387 may keep existing. Can it be reproed with just specialization and no impossible bounds?

@rust-bors

This comment has been minimized.

@sjwang05

sjwang05 commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

#150387 may keep existing. Can it be reproed with just specialization and no impossible bounds?

I don't think one exists if you only use specialization and nothing else, but this program does ICE with const drop, so I've removed that issue from the PR description:

#![feature(min_specialization, const_trait_impl, const_destruct)]
struct Thing;
impl Drop for Thing { default fn drop(&mut self) {} }
const impl Drop for Thing { fn drop(&mut self) {} }

since the const impl specializes the non-const one, and none of them break dropck's rules, so we get a delayed bug :D

@rustbot

rustbot commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@oli-obk oli-obk added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 18, 2026
@oli-obk

oli-obk commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

since the const impl specializes the non-const one, and none of them break dropck's rules, so we get a delayed bug :D

😨 Aaaaa

Nice find

@oli-obk

oli-obk commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

@bors r+

@rust-bors

rust-bors Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 11c2b3d has been approved by oli-obk

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 18, 2026
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 18, 2026
…i-obk

don't calculate dtors if the self ty has impossible bounds

If we have multiple drop impls and a self ty that all have the same impossible bounds, we end up with duplicate `drop`s with different `DefId`s, which causes us to ICE with a delayed bug in `calculate_dtor`. Coherence accepts these impls since they have impossible bounds, and nothing else in `check_drop_impl` checks this, leading to us accepting the code and subsequently ICEing from the delayed bug.

We now skip considering the current drop impl as a dtor candidate if the self ty and impl(s) have the same impossible bounds, since if the self ty is unnameable, it's impossible to construct an instance in the first place. Checking only the self ty is sufficient here, as `check_drop_impl` checks the case where the impossible bounds exist only on the drop impl, and we're not allowed in general to have a struct whose bounds are more restrictive than its impl's, so the only case we're currently missing is the case where the self ty and the `Drop` impls have the exact same impossible bounds.

My reasoning for accepting such code instead of emitting an error is that, in general, we allow users to write code with impossible or trivial bounds, so long as they never try to actually name or run that code. This PR makes `Drop` behave the same way.

This results in overlapping impossible drop impls behaving like other traits: for instance, the following compiles on nightly today:

```rs
pub trait Foo {
    fn foo(&self) {}
}

pub struct Thing<T>(T)
where
    [T]: Sized;

impl<T> Foo for Thing<T> where [T]: Sized {}
impl<T> Foo for Thing<T> where [T]: Sized {} // removing this impl is ok as well
```

Although this ICE only seems to be reachable for sync drop, not making the same change for async drop makes the following program, which currently compiles on nightly, stop compiling:

```rs
#![feature(async_drop)]
use core::future::AsyncDrop;
use core::pin::Pin;

pub struct Thing<T>(T) where [T]: Sized;

impl<T> Drop for Thing<T> where [T]: Sized {
    fn drop(&mut self) {}
}
impl<T> AsyncDrop for Thing<T> where [T]: Sized {
    async fn drop(self: Pin<&mut Self>) {}
}
```

which feels like something we don't want.

cc rust-lang#159118 (comment)

fixes rust-lang#153947
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 18, 2026
…i-obk

don't calculate dtors if the self ty has impossible bounds

If we have multiple drop impls and a self ty that all have the same impossible bounds, we end up with duplicate `drop`s with different `DefId`s, which causes us to ICE with a delayed bug in `calculate_dtor`. Coherence accepts these impls since they have impossible bounds, and nothing else in `check_drop_impl` checks this, leading to us accepting the code and subsequently ICEing from the delayed bug.

We now skip considering the current drop impl as a dtor candidate if the self ty and impl(s) have the same impossible bounds, since if the self ty is unnameable, it's impossible to construct an instance in the first place. Checking only the self ty is sufficient here, as `check_drop_impl` checks the case where the impossible bounds exist only on the drop impl, and we're not allowed in general to have a struct whose bounds are more restrictive than its impl's, so the only case we're currently missing is the case where the self ty and the `Drop` impls have the exact same impossible bounds.

My reasoning for accepting such code instead of emitting an error is that, in general, we allow users to write code with impossible or trivial bounds, so long as they never try to actually name or run that code. This PR makes `Drop` behave the same way.

This results in overlapping impossible drop impls behaving like other traits: for instance, the following compiles on nightly today:

```rs
pub trait Foo {
    fn foo(&self) {}
}

pub struct Thing<T>(T)
where
    [T]: Sized;

impl<T> Foo for Thing<T> where [T]: Sized {}
impl<T> Foo for Thing<T> where [T]: Sized {} // removing this impl is ok as well
```

Although this ICE only seems to be reachable for sync drop, not making the same change for async drop makes the following program, which currently compiles on nightly, stop compiling:

```rs
#![feature(async_drop)]
use core::future::AsyncDrop;
use core::pin::Pin;

pub struct Thing<T>(T) where [T]: Sized;

impl<T> Drop for Thing<T> where [T]: Sized {
    fn drop(&mut self) {}
}
impl<T> AsyncDrop for Thing<T> where [T]: Sized {
    async fn drop(self: Pin<&mut Self>) {}
}
```

which feels like something we don't want.

cc rust-lang#159118 (comment)

fixes rust-lang#153947
rust-bors Bot pushed a commit that referenced this pull request Sep 18, 2026
…uwer

Rollup of 20 pull requests

Successful merges:

 - #160401 (sparc: make ABI consistent with clang)
 - #162715 (don't calculate dtors if the self ty has impossible bounds)
 - #162740 (stdarch subtree update)
 - #162824 (link Enzyme and the offload with in-tree lld if possible)
 - #162946 (Simplify query stack printing)
 - #161005 (fix: unfulfilled nested dead code lint)
 - #161246 (Normalize non-rigid aliases in ty_known_to_outlive)
 - #161803 (Fix docs of make_ascii_lowercase/make_ascii_upercase)
 - #162256 (Add mentions to sync back `RELEASES.md` to the `main` branch)
 - #162661 (simplify `Target::GenericParam`)
 - #162666 (Tidy footnote in `platform-support.md`)
 - #162803 (docs(num): add documentation for `NonZero::from_str`)
 - #162879 (use u64 limbs in core::num::bignum)
 - #162903 (PassWrapper: adapt to LLVM reading exception model from module flag)
 - #162905 (c-variadic: add checks for windows i686)
 - #162906 (Move more `rustdoc-html` tests in the right location)
 - #162922 (An assortment of polonius tweaks)
 - #162929 (Update unicode_data to Unicode version 18.0.0)
 - #162930 (Use niche length type for strlen to guarantee `isize::MAX` bound)
 - #162960 (Guard types with unstable `Allocator` params ahead of partial stabilization)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 18, 2026
…i-obk

don't calculate dtors if the self ty has impossible bounds

If we have multiple drop impls and a self ty that all have the same impossible bounds, we end up with duplicate `drop`s with different `DefId`s, which causes us to ICE with a delayed bug in `calculate_dtor`. Coherence accepts these impls since they have impossible bounds, and nothing else in `check_drop_impl` checks this, leading to us accepting the code and subsequently ICEing from the delayed bug.

We now skip considering the current drop impl as a dtor candidate if the self ty and impl(s) have the same impossible bounds, since if the self ty is unnameable, it's impossible to construct an instance in the first place. Checking only the self ty is sufficient here, as `check_drop_impl` checks the case where the impossible bounds exist only on the drop impl, and we're not allowed in general to have a struct whose bounds are more restrictive than its impl's, so the only case we're currently missing is the case where the self ty and the `Drop` impls have the exact same impossible bounds.

My reasoning for accepting such code instead of emitting an error is that, in general, we allow users to write code with impossible or trivial bounds, so long as they never try to actually name or run that code. This PR makes `Drop` behave the same way.

This results in overlapping impossible drop impls behaving like other traits: for instance, the following compiles on nightly today:

```rs
pub trait Foo {
    fn foo(&self) {}
}

pub struct Thing<T>(T)
where
    [T]: Sized;

impl<T> Foo for Thing<T> where [T]: Sized {}
impl<T> Foo for Thing<T> where [T]: Sized {} // removing this impl is ok as well
```

Although this ICE only seems to be reachable for sync drop, not making the same change for async drop makes the following program, which currently compiles on nightly, stop compiling:

```rs
#![feature(async_drop)]
use core::future::AsyncDrop;
use core::pin::Pin;

pub struct Thing<T>(T) where [T]: Sized;

impl<T> Drop for Thing<T> where [T]: Sized {
    fn drop(&mut self) {}
}
impl<T> AsyncDrop for Thing<T> where [T]: Sized {
    async fn drop(self: Pin<&mut Self>) {}
}
```

which feels like something we don't want.

cc rust-lang#159118 (comment)

fixes rust-lang#153947
rust-bors Bot pushed a commit that referenced this pull request Sep 18, 2026
…uwer

Rollup of 19 pull requests

Successful merges:

 - #160401 (sparc: make ABI consistent with clang)
 - #162715 (don't calculate dtors if the self ty has impossible bounds)
 - #162740 (stdarch subtree update)
 - #162946 (Simplify query stack printing)
 - #161005 (fix: unfulfilled nested dead code lint)
 - #161246 (Normalize non-rigid aliases in ty_known_to_outlive)
 - #161803 (Fix docs of make_ascii_lowercase/make_ascii_upercase)
 - #162256 (Add mentions to sync back `RELEASES.md` to the `main` branch)
 - #162661 (simplify `Target::GenericParam`)
 - #162666 (Tidy footnote in `platform-support.md`)
 - #162803 (docs(num): add documentation for `NonZero::from_str`)
 - #162879 (use u64 limbs in core::num::bignum)
 - #162903 (PassWrapper: adapt to LLVM reading exception model from module flag)
 - #162905 (c-variadic: add checks for windows i686)
 - #162906 (Move more `rustdoc-html` tests in the right location)
 - #162922 (An assortment of polonius tweaks)
 - #162929 (Update unicode_data to Unicode version 18.0.0)
 - #162930 (Use niche length type for strlen to guarantee `isize::MAX` bound)
 - #162960 (Guard types with unstable `Allocator` params ahead of partial stabilization)
rust-bors Bot pushed a commit that referenced this pull request Sep 18, 2026
…uwer

Rollup of 19 pull requests

Successful merges:

 - #160401 (sparc: make ABI consistent with clang)
 - #162715 (don't calculate dtors if the self ty has impossible bounds)
 - #162740 (stdarch subtree update)
 - #162946 (Simplify query stack printing)
 - #161005 (fix: unfulfilled nested dead code lint)
 - #161246 (Normalize non-rigid aliases in ty_known_to_outlive)
 - #161803 (Fix docs of make_ascii_lowercase/make_ascii_upercase)
 - #162256 (Add mentions to sync back `RELEASES.md` to the `main` branch)
 - #162661 (simplify `Target::GenericParam`)
 - #162666 (Tidy footnote in `platform-support.md`)
 - #162803 (docs(num): add documentation for `NonZero::from_str`)
 - #162879 (use u64 limbs in core::num::bignum)
 - #162903 (PassWrapper: adapt to LLVM reading exception model from module flag)
 - #162905 (c-variadic: add checks for windows i686)
 - #162906 (Move more `rustdoc-html` tests in the right location)
 - #162922 (An assortment of polonius tweaks)
 - #162929 (Update unicode_data to Unicode version 18.0.0)
 - #162930 (Use niche length type for strlen to guarantee `isize::MAX` bound)
 - #162960 (Guard types with unstable `Allocator` params ahead of partial stabilization)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ICE]: ICE with multiple Drop impls with impossible bound and Self: Drop

4 participants