Skip to content

Commit 9b7201c

Browse files
committed
Add Drop::pin_drop for pinned drops
1 parent b00998a commit 9b7201c

File tree

11 files changed

+514
-162
lines changed

11 files changed

+514
-162
lines changed

Cargo.lock

Lines changed: 226 additions & 152 deletions
Large diffs are not rendered by default.

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ hir_analysis_coercion_between_struct_same_note = expected coercion between the s
117117
118118
hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found
119119
120+
hir_analysis_conflict_impl_drop_and_pin_drop = conflict implementation of `Drop::drop` and `Drop::pin_drop`
121+
.drop_label = `drop(&mut self)` implemented here
122+
.pin_drop_label = `pin_drop(&pin mut self)` implemented here
123+
.suggestion = remove this implementation
124+
120125
hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `const` traits
121126
.label = can't be applied to `{$trait_name}`
122127
.note = `{$trait_name}` can't be used with `{$modifier}` because it isn't `const`

compiler/rustc_hir_analysis/src/check/always_applicable.rs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
use rustc_data_structures::fx::FxHashSet;
88
use rustc_errors::codes::*;
99
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
10+
use rustc_hir as hir;
1011
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
1112
use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
1213
use rustc_middle::span_bug;
1314
use rustc_middle::ty::util::CheckRegions;
1415
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypingMode};
16+
use rustc_span::{Span, sym};
1517
use rustc_trait_selection::regions::InferCtxtRegionExt;
1618
use rustc_trait_selection::traits::{self, ObligationCtxt};
1719

@@ -70,7 +72,9 @@ pub(crate) fn check_drop_impl(
7072
drop_impl_did,
7173
adt_def.did(),
7274
adt_to_impl_args,
73-
)
75+
)?;
76+
77+
ensure_coherent_unpin(tcx, drop_impl_did, adt_def.did(), adt_to_impl_args)
7478
}
7579
_ => {
7680
span_bug!(tcx.def_span(drop_impl_did), "incoherent impl of Drop");
@@ -294,3 +298,82 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>(
294298

295299
Ok(())
296300
}
301+
302+
/// This function checks whether `Self: !Unpin` for implementation of `Drop`.
303+
/// - If `Drop::drop` is implemented, there must be no `Self: !Unpin` implementation.
304+
/// - If `Drop::pin_drop` is implemented, there must be a `Self: !Unpin` implementation.
305+
fn ensure_coherent_unpin<'tcx>(
306+
tcx: TyCtxt<'tcx>,
307+
drop_impl_did: LocalDefId,
308+
adt_def_id: DefId,
309+
adt_to_impl_args: GenericArgsRef<'tcx>,
310+
) -> Result<(), ErrorGuaranteed> {
311+
let item_impl = tcx.hir_expect_item(drop_impl_did).expect_impl();
312+
let mut drop_spans = None;
313+
let mut pin_drop_spans = None;
314+
for &impl_item_id in item_impl.items {
315+
let impl_item = tcx.hir_impl_item(impl_item_id);
316+
if let hir::ImplItemKind::Fn(fn_sig, _) = impl_item.kind {
317+
match impl_item.ident.name {
318+
sym::drop => drop_spans = Some((fn_sig.span, impl_item.span)),
319+
sym::pin_drop => pin_drop_spans = Some((fn_sig.span, impl_item.span)),
320+
_ => {}
321+
}
322+
}
323+
}
324+
let self_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args);
325+
let negative_unpin_impl = find_negative_unpin_impl(tcx, self_ty, tcx.def_span(drop_impl_did));
326+
327+
match (drop_spans, pin_drop_spans) {
328+
(None, None) => {
329+
return Err(tcx
330+
.dcx()
331+
.span_delayed_bug(tcx.def_span(drop_impl_did), "unexpected empty impl of `Drop`"));
332+
}
333+
// FIXME(pin_ergonomics): reject `Drop::drop` for types that support pin-projection.
334+
(Some((_span, _)), None) => {}
335+
(None, Some((_span, _))) => {
336+
debug_assert!(
337+
tcx.features().pin_ergonomics(),
338+
"`Drop::pin_drop` should be guarded by the library feature gate"
339+
);
340+
// FIXME(pin_ergonomics): reject `Drop::pin_drop` for types that don't support pin-projection.
341+
}
342+
(
343+
Some((drop_span, drop_span_with_body)),
344+
Some((pin_drop_span, pin_drop_span_with_body)),
345+
) => {
346+
let remove_sugg = match negative_unpin_impl {
347+
// without `impl !Unpin`, should pick `drop(&mut self)`, and remove `pin_drop(&pin mut self)`
348+
None => pin_drop_span_with_body,
349+
// with `impl !Unpin`, should pick `pin_drop(&pin mut self)`, and remove `drop(&mut self)`
350+
Some(_) => drop_span_with_body,
351+
};
352+
return Err(tcx.dcx().emit_err(crate::errors::ConflictImplDropAndPinDrop {
353+
span: tcx.def_span(drop_impl_did),
354+
drop_span,
355+
pin_drop_span,
356+
remove_sugg,
357+
}));
358+
}
359+
}
360+
Ok(())
361+
}
362+
363+
fn find_negative_unpin_impl<'tcx>(
364+
tcx: TyCtxt<'tcx>,
365+
self_ty: Ty<'tcx>,
366+
span: Span,
367+
) -> Option<LocalDefId> {
368+
let mut negative_unpin_impl_did = None;
369+
tcx.for_each_relevant_impl(tcx.require_lang_item(hir::LangItem::Unpin, span), self_ty, |did| {
370+
if tcx.impl_polarity(did) == ty::ImplPolarity::Negative {
371+
if negative_unpin_impl_did.is_some() {
372+
tcx.dcx()
373+
.span_delayed_bug(tcx.def_span(did), "duplicated `!Unpin` implementations");
374+
}
375+
negative_unpin_impl_did = Some(did.expect_local());
376+
}
377+
});
378+
negative_unpin_impl_did
379+
}

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,3 +1707,16 @@ pub(crate) struct AsyncDropWithoutSyncDrop {
17071707
#[primary_span]
17081708
pub span: Span,
17091709
}
1710+
1711+
#[derive(Diagnostic)]
1712+
#[diag(hir_analysis_conflict_impl_drop_and_pin_drop)]
1713+
pub(crate) struct ConflictImplDropAndPinDrop {
1714+
#[primary_span]
1715+
pub span: Span,
1716+
#[label(hir_analysis_drop_label)]
1717+
pub drop_span: Span,
1718+
#[label(hir_analysis_pin_drop_label)]
1719+
pub pin_drop_span: Span,
1720+
#[suggestion(applicability = "machine-applicable", code = "", style = "normal")]
1721+
pub remove_sugg: Span,
1722+
}

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ pub(crate) fn check_legal_trait_for_method_call(
3737
receiver: Option<Span>,
3838
expr_span: Span,
3939
trait_id: DefId,
40-
_body_id: DefId,
40+
body_id: DefId,
4141
) -> Result<(), ErrorGuaranteed> {
42-
if tcx.is_lang_item(trait_id, LangItem::Drop) {
42+
if tcx.is_lang_item(trait_id, LangItem::Drop)
43+
// Allow calling `Drop::pin_drop` in `Drop::drop`
44+
&& let Some(parent) = tcx.opt_parent(body_id)
45+
&& !tcx.is_lang_item(parent, LangItem::Drop)
46+
{
4347
let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
4448
errors::ExplicitDestructorCallSugg::Snippet {
4549
lo: expr_span.shrink_to_lo(),

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,7 @@ symbols! {
16501650
pic,
16511651
pie,
16521652
pin,
1653+
pin_drop,
16531654
pin_ergonomics,
16541655
pin_macro,
16551656
platform_intrinsics,

library/core/src/ops/drop.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::pin::Pin;
2+
13
/// Custom code within the destructor.
24
///
35
/// When a value is no longer needed, Rust will run a "destructor" on that value.
@@ -204,6 +206,7 @@
204206
#[lang = "drop"]
205207
#[stable(feature = "rust1", since = "1.0.0")]
206208
#[rustc_const_unstable(feature = "const_destruct", issue = "133214")]
209+
#[rustc_must_implement_one_of(drop, pin_drop)]
207210
pub const trait Drop {
208211
/// Executes the destructor for this type.
209212
///
@@ -237,5 +240,28 @@ pub const trait Drop {
237240
/// [`mem::drop`]: drop
238241
/// [`ptr::drop_in_place`]: crate::ptr::drop_in_place
239242
#[stable(feature = "rust1", since = "1.0.0")]
240-
fn drop(&mut self);
243+
fn drop(&mut self) {
244+
// SAFETY: `self` is pinned till after dropped.
245+
Drop::pin_drop(unsafe { Pin::new_unchecked(self) })
246+
}
247+
248+
/// Execute the destructor for this type, but different to [`Drop::drop`], it requires `self`
249+
/// to be pinned.
250+
///
251+
/// By implementing this method instead of [`Drop::drop`], the receiver type [`Pin<&mut Self>`]
252+
/// makes sure that the value is pinned until it is deallocated (See [`std::pin` module docs] for
253+
/// more details), which enables us to support field projections of `Self` type safely.
254+
///
255+
/// [`Drop::drop`] and [`Drop::pin_drop`] are mutual exclusive, which means you should
256+
/// and should only implement either one of them.
257+
///
258+
// FIXME(pin_ergonomics): add requirements: `pin_drop` must be and must only be used
259+
// for types that support pin-projection.
260+
/// [`Drop::drop`]: crate::ops::Drop::drop
261+
/// [`Unpin`]: crate::marker::Unpin
262+
/// [`!Unpin`]: crate::marker::Unpin
263+
/// [`Pin<&mut Self>`]: crate::pin::Pin
264+
/// [`std::pin` module docs]: crate::pin
265+
#[unstable(feature = "pin_ergonomics", issue = "130494")]
266+
fn pin_drop(self: Pin<&mut Self>) {}
241267
}

tests/ui/async-await/async-drop/unexpected-sort.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
#![feature(async_drop)]
77
use std::future::AsyncDrop;
88
struct a;
9-
impl Drop for a { //~ ERROR: not all trait items implemented, missing: `drop`
9+
impl Drop for a {
10+
//~ ERROR: not all trait items implemented, missing one of: `drop`, `pin_drop`
1011
fn b() {} //~ ERROR: method `b` is not a member of trait `Drop`
1112
}
12-
impl AsyncDrop for a { //~ ERROR: not all trait items implemented, missing: `drop`
13+
impl AsyncDrop for a {
14+
//~ ERROR: not all trait items implemented, missing: `drop`
1315
type c = ();
1416
//~^ ERROR: type `c` is not a member of trait `AsyncDrop`
1517
}

tests/ui/async-await/async-drop/unexpected-sort.stderr

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ error[E0437]: type `c` is not a member of trait `AsyncDrop`
1010
LL | type c = ();
1111
| ^^^^^^^^^^^^ not a member of trait `AsyncDrop`
1212

13-
error[E0046]: not all trait items implemented, missing: `drop`
13+
error[E0046]: not all trait items implemented, missing one of: `drop`, `pin_drop`
1414
--> $DIR/unexpected-sort.rs:9:1
1515
|
1616
LL | impl Drop for a {
17-
| ^^^^^^^^^^^^^^^ missing `drop` in implementation
18-
|
19-
= help: implement the missing item: `fn drop(&mut self) { todo!() }`
17+
| ^^^^^^^^^^^^^^^ missing one of `drop`, `pin_drop` in implementation
2018

2119
error[E0046]: not all trait items implemented, missing: `drop`
2220
--> $DIR/unexpected-sort.rs:12:1
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//@ edition:2024
2+
#![feature(pin_ergonomics)]
3+
#![allow(incomplete_features)]
4+
5+
// This test ensures that at least and at most one of `drop` and `pin_drop`
6+
// are implemented for types that implement `Drop`.
7+
8+
mod drop_only {
9+
struct Foo;
10+
11+
impl Drop for Foo {
12+
fn drop(&mut self) {} // ok, only `drop` is implemented
13+
}
14+
}
15+
16+
mod pin_drop_only {
17+
struct Foo;
18+
19+
impl Drop for Foo {
20+
fn pin_drop(&pin mut self) {} // ok, only `pin_drop` is implemented
21+
}
22+
}
23+
24+
mod both {
25+
struct Foo;
26+
27+
impl Drop for Foo {
28+
//~^ ERROR conflict implementation of `Drop::drop` and `Drop::pin_drop`
29+
fn drop(&mut self) {}
30+
fn pin_drop(&pin mut self) {}
31+
}
32+
}
33+
34+
mod neither {
35+
struct Foo;
36+
37+
impl Drop for Foo {} //~ ERROR not all trait items implemented, missing one of: `drop`, `pin_drop` [E0046]
38+
}
39+
40+
mod drop_wrong_type {
41+
struct Foo;
42+
43+
impl Drop for Foo {
44+
fn drop(&pin mut self) {} //~ ERROR method `drop` has an incompatible type for trait [E0053]
45+
}
46+
}
47+
48+
mod pin_drop_wrong_type {
49+
struct Foo;
50+
51+
impl Drop for Foo {
52+
fn pin_drop(&mut self) {} //~ ERROR method `pin_drop` has an incompatible type for trait [E0053]
53+
}
54+
}
55+
56+
mod explicit_call_pin_drop {
57+
struct Foo;
58+
59+
impl Drop for Foo {
60+
fn drop(&mut self) {
61+
Drop::pin_drop(todo!()); //~ ERROR explicit use of destructor method [E0040]
62+
}
63+
}
64+
}
65+
66+
mod explicit_call_drop {
67+
struct Foo;
68+
69+
impl Drop for Foo {
70+
fn pin_drop(&pin mut self) {
71+
Drop::drop(todo!()); //~ ERROR explicit use of destructor method [E0040]
72+
}
73+
}
74+
}
75+
76+
fn main() {}

0 commit comments

Comments
 (0)