Skip to content

Commit 7b4bb5c

Browse files
committed
Add Drop::pin_drop for pinned drops
1 parent b00998a commit 7b4bb5c

25 files changed

+609
-211
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: 54 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::sym;
1517
use rustc_trait_selection::regions::InferCtxtRegionExt;
1618
use rustc_trait_selection::traits::{self, ObligationCtxt};
1719

@@ -70,7 +72,11 @@ pub(crate) fn check_drop_impl(
7072
drop_impl_did,
7173
adt_def.did(),
7274
adt_to_impl_args,
73-
)
75+
)?;
76+
77+
check_drop_xor_pin_drop(tcx, drop_impl_did)?;
78+
79+
Ok(())
7480
}
7581
_ => {
7682
span_bug!(tcx.def_span(drop_impl_did), "incoherent impl of Drop");
@@ -294,3 +300,50 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>(
294300

295301
Ok(())
296302
}
303+
304+
/// This function checks at least and at most one of `Drop::drop` and `Drop::pin_drop` is implemented.
305+
fn check_drop_xor_pin_drop<'tcx>(
306+
tcx: TyCtxt<'tcx>,
307+
drop_impl_did: LocalDefId,
308+
) -> Result<(), ErrorGuaranteed> {
309+
let item_impl = tcx.hir_expect_item(drop_impl_did).expect_impl();
310+
let mut drop_span = None;
311+
let mut pin_drop_span = None;
312+
for &impl_item_id in item_impl.items {
313+
let impl_item = tcx.hir_impl_item(impl_item_id);
314+
if let hir::ImplItemKind::Fn(fn_sig, _) = impl_item.kind {
315+
match impl_item.ident.name {
316+
sym::drop => drop_span = Some(fn_sig.span),
317+
sym::pin_drop => pin_drop_span = Some(fn_sig.span),
318+
_ => {}
319+
}
320+
}
321+
}
322+
323+
match (drop_span, pin_drop_span) {
324+
(None, None) => {
325+
return Err(tcx
326+
.dcx()
327+
.span_delayed_bug(tcx.def_span(drop_impl_did), "unexpected empty impl of `Drop`"));
328+
}
329+
// FIXME(pin_ergonomics): reject `Drop::drop` for types that support pin-projection.
330+
(Some(_span), None) => {}
331+
(None, Some(span)) => {
332+
if !tcx.features().pin_ergonomics() {
333+
tcx.dcx().span_delayed_bug(
334+
span,
335+
"`Drop::pin_drop` should be guarded by the library feature gate",
336+
);
337+
}
338+
// FIXME(pin_ergonomics): reject `Drop::pin_drop` for types that don't support pin-projection.
339+
}
340+
(Some(drop_span), Some(pin_drop_span)) => {
341+
return Err(tcx.dcx().emit_err(crate::errors::ConflictImplDropAndPinDrop {
342+
span: tcx.def_span(drop_impl_did),
343+
drop_span,
344+
pin_drop_span,
345+
}));
346+
}
347+
}
348+
Ok(())
349+
}

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,3 +1707,14 @@ 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+
}

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: 26 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,27 @@ 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+
/// At least and at most one of [`Drop::drop`] and [`Drop::pin_drop`] should be implemented.
256+
///
257+
// FIXME(pin_ergonomics): add requirements: `pin_drop` must be and must only be used
258+
// for types that support pin-projection.
259+
/// [`Drop::drop`]: crate::ops::Drop::drop
260+
/// [`Unpin`]: crate::marker::Unpin
261+
/// [`!Unpin`]: crate::marker::Unpin
262+
/// [`Pin<&mut Self>`]: crate::pin::Pin
263+
/// [`std::pin` module docs]: crate::pin
264+
#[unstable(feature = "pin_ergonomics", issue = "130494")]
265+
fn pin_drop(self: Pin<&mut Self>) {}
241266
}

tests/ui/associated-types/associated-types-for-unimpl-trait.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ trait Get {
88
}
99

1010
trait Other {
11-
fn uhoh<U: Get>(&self, foo: U, bar: <Self as Get>::Value) where Self: Sized, Self: Get {}
11+
fn uhoh<U: Get>(&self, foo: U, bar: <Self as Get>::Value) where Self: Sized, Self: Get, Self: Get {}
1212
//~^ ERROR the trait bound `Self: Get` is not satisfied
1313
//~| ERROR the trait bound `Self: Get` is not satisfied
1414
}

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: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
error[E0407]: method `b` is not a member of trait `Drop`
2-
--> $DIR/unexpected-sort.rs:10:5
2+
--> $DIR/unexpected-sort.rs:11:5
33
|
44
LL | fn b() {}
55
| ^^^^^^^^^ not a member of trait `Drop`
66

77
error[E0437]: type `c` is not a member of trait `AsyncDrop`
8-
--> $DIR/unexpected-sort.rs:13:5
8+
--> $DIR/unexpected-sort.rs:15:5
99
|
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`
22-
--> $DIR/unexpected-sort.rs:12:1
20+
--> $DIR/unexpected-sort.rs:13:1
2321
|
2422
LL | impl AsyncDrop for a {
2523
| ^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation

0 commit comments

Comments
 (0)