Skip to content

Commit 6303109

Browse files
frank-king王俊吉
authored andcommitted
Add Drop::pin_drop for structually pinned types
1 parent c90bcb9 commit 6303109

25 files changed

+599
-35
lines changed

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ hir_analysis_coercion_between_struct_same_note = expected coercion between the s
116116
117117
hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found
118118
119+
hir_analysis_conflict_impl_drop_and_pin_drop = conflict implementation of `Drop::drop` and `Drop::pin_drop`
120+
.drop_label = `drop(&mut self)` implemented here
121+
.pin_drop_label = `pin_drop(&pin mut self)` implemented here
122+
.suggestion = remove this implementation
123+
119124
hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `const` traits
120125
.label = can't be applied to `{$trait_name}`
121126
.note = `{$trait_name}` can't be used with `{$modifier}` because it isn't `const`
@@ -441,6 +446,13 @@ hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a t
441446
hir_analysis_parenthesized_fn_trait_expansion =
442447
parenthesized trait syntax expands to `{$expanded_type}`
443448
449+
hir_analysis_pin_v2_without_pin_drop =
450+
`{$adt_name}` must implement `pin_drop`
451+
.note = `{$adt_name}` is marked `#[pin_v2]` here
452+
.help = structurally pinned types must keep `Pin`'s safety contract
453+
.pin_drop_sugg = implement `pin_drop` instead
454+
.remove_pin_v2_sugg = remove the `#[pin_v2]` attribute if it is not intended for structurally pinning
455+
444456
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
445457
.label = not allowed in type signatures
446458

compiler/rustc_hir_analysis/src/check/always_applicable.rs

Lines changed: 72 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, adt_def.did(), drop_impl_did)?;
78+
79+
Ok(())
7480
}
7581
_ => {
7682
span_bug!(tcx.def_span(drop_impl_did), "incoherent impl of Drop");
@@ -291,3 +297,68 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>(
291297

292298
Ok(())
293299
}
300+
301+
/// This function checks at least and at most one of `Drop::drop` and `Drop::pin_drop` is implemented.
302+
/// It also checks that `Drop::pin_drop` must be implemented if `#[pin_v2]` is present on the type.
303+
fn check_drop_xor_pin_drop<'tcx>(
304+
tcx: TyCtxt<'tcx>,
305+
adt_def_id: DefId,
306+
drop_impl_did: LocalDefId,
307+
) -> Result<(), ErrorGuaranteed> {
308+
let item_impl = tcx.hir_expect_item(drop_impl_did).expect_impl();
309+
let mut drop_span = None;
310+
let mut pin_drop_span = None;
311+
for &impl_item_id in item_impl.items {
312+
let impl_item = tcx.hir_impl_item(impl_item_id);
313+
if let hir::ImplItemKind::Fn(fn_sig, _) = impl_item.kind {
314+
match impl_item.ident.name {
315+
sym::drop => drop_span = Some(fn_sig.span),
316+
sym::pin_drop => pin_drop_span = Some(fn_sig.span),
317+
_ => {}
318+
}
319+
}
320+
}
321+
322+
match (drop_span, pin_drop_span) {
323+
(None, None) => {
324+
if tcx.features().pin_ergonomics() {
325+
return Err(tcx.dcx().emit_err(crate::errors::MissingOneOfTraitItem {
326+
span: tcx.def_span(drop_impl_did),
327+
note: None,
328+
missing_items_msg: "drop`, `pin_drop".to_string(),
329+
}));
330+
} else {
331+
return Err(tcx
332+
.dcx()
333+
.span_delayed_bug(tcx.def_span(drop_impl_did), "missing `Drop::drop`"));
334+
}
335+
}
336+
(Some(span), None) => {
337+
if tcx.adt_def(adt_def_id).is_pin_project() {
338+
let pin_v2_span = tcx.get_attr(adt_def_id, sym::pin_v2).map(|attr| attr.span());
339+
let adt_name = tcx.item_name(adt_def_id);
340+
return Err(tcx.dcx().emit_err(crate::errors::PinV2WithoutPinDrop {
341+
span,
342+
pin_v2_span,
343+
adt_name,
344+
}));
345+
}
346+
}
347+
(None, Some(span)) => {
348+
if !tcx.features().pin_ergonomics() {
349+
return Err(tcx.dcx().span_delayed_bug(
350+
span,
351+
"`Drop::pin_drop` should be guarded by the library feature gate",
352+
));
353+
}
354+
}
355+
(Some(drop_span), Some(pin_drop_span)) => {
356+
return Err(tcx.dcx().emit_err(crate::errors::ConflictImplDropAndPinDrop {
357+
span: tcx.def_span(drop_impl_did),
358+
drop_span,
359+
pin_drop_span,
360+
}));
361+
}
362+
}
363+
Ok(())
364+
}

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_middle::ty::{
2424
TypeVisitable, TypeVisitableExt, fold_regions,
2525
};
2626
use rustc_session::lint::builtin::UNINHABITED_STATIC;
27+
use rustc_span::sym;
2728
use rustc_target::spec::{AbiMap, AbiMapping};
2829
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
2930
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ use rustc_middle::ty::{
9292
use rustc_middle::{bug, span_bug};
9393
use rustc_session::parse::feature_err;
9494
use rustc_span::def_id::CRATE_DEF_ID;
95-
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym};
95+
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw};
9696
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
9797
use rustc_trait_selection::error_reporting::infer::ObligationCauseExt as _;
9898
use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor;

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,3 +1718,31 @@ pub(crate) struct AsyncDropWithoutSyncDrop {
17181718
#[primary_span]
17191719
pub span: Span,
17201720
}
1721+
1722+
#[derive(Diagnostic)]
1723+
#[diag(hir_analysis_conflict_impl_drop_and_pin_drop)]
1724+
pub(crate) struct ConflictImplDropAndPinDrop {
1725+
#[primary_span]
1726+
pub span: Span,
1727+
#[label(hir_analysis_drop_label)]
1728+
pub drop_span: Span,
1729+
#[label(hir_analysis_pin_drop_label)]
1730+
pub pin_drop_span: Span,
1731+
}
1732+
1733+
#[derive(Diagnostic)]
1734+
#[diag(hir_analysis_pin_v2_without_pin_drop)]
1735+
#[help]
1736+
pub(crate) struct PinV2WithoutPinDrop {
1737+
#[primary_span]
1738+
#[suggestion(
1739+
hir_analysis_pin_drop_sugg,
1740+
code = "fn pin_drop(&pin mut self)",
1741+
applicability = "maybe-incorrect"
1742+
)]
1743+
pub span: Span,
1744+
#[note]
1745+
#[suggestion(hir_analysis_remove_pin_v2_sugg, code = "", applicability = "maybe-incorrect")]
1746+
pub pin_v2_span: Option<Span>,
1747+
pub adt_name: Symbol,
1748+
}

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
@@ -1682,6 +1682,7 @@ symbols! {
16821682
pic,
16831683
pie,
16841684
pin,
1685+
pin_drop,
16851686
pin_ergonomics,
16861687
pin_macro,
16871688
pin_v2,

library/core/src/ops/drop.rs

Lines changed: 28 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.
@@ -237,5 +239,30 @@ pub const trait Drop {
237239
/// [`mem::drop`]: drop
238240
/// [`ptr::drop_in_place`]: crate::ptr::drop_in_place
239241
#[stable(feature = "rust1", since = "1.0.0")]
240-
fn drop(&mut self);
242+
#[rustc_default_body_unstable(feature = "pin_ergonomics", issue = "130494")]
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+
/// For types that support pin-projection (i.e., marked with `#[pin_v2]`), this method must be used.
256+
///
257+
/// For any type, at least and at most one of [`Drop::drop`] and [`Drop::pin_drop`] should be
258+
/// implemented.
259+
///
260+
/// See also [`Drop::drop`] for more details.
261+
///
262+
/// [`Drop::drop`]: crate::ops::Drop::drop
263+
/// [`Drop::pin_drop`]: crate::ops::Drop::pin_drop
264+
/// [`Pin<&mut Self>`]: crate::pin::Pin
265+
/// [`std::pin` module docs]: crate::pin
266+
#[unstable(feature = "pin_ergonomics", issue = "130494")]
267+
fn pin_drop(self: Pin<&mut Self>) {}
241268
}

src/doc/book

tests/codegen-units/item-collection/drop-glue-eager.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ struct StructWithDrop {
1010

1111
impl Drop for StructWithDrop {
1212
//~ MONO_ITEM fn <StructWithDrop as std::ops::Drop>::drop
13+
//~ MONO_ITEM fn <StructWithDrop as std::ops::Drop>::pin_drop
1314
fn drop(&mut self) {}
1415
}
1516

@@ -24,6 +25,7 @@ enum EnumWithDrop {
2425

2526
impl Drop for EnumWithDrop {
2627
//~ MONO_ITEM fn <EnumWithDrop as std::ops::Drop>::drop
28+
//~ MONO_ITEM fn <EnumWithDrop as std::ops::Drop>::pin_drop
2729
fn drop(&mut self) {}
2830
}
2931

@@ -34,6 +36,7 @@ enum EnumNoDrop {
3436
// We should be able to monomorphize drops for struct with lifetimes.
3537
impl<'a> Drop for StructWithDropAndLt<'a> {
3638
//~ MONO_ITEM fn <StructWithDropAndLt<'_> as std::ops::Drop>::drop
39+
//~ MONO_ITEM fn <StructWithDropAndLt<'_> as std::ops::Drop>::pin_drop
3740
fn drop(&mut self) {}
3841
}
3942

@@ -52,5 +55,6 @@ struct StructWithLtAndPredicate<'a: 'a> {
5255
// We should be able to monomorphize drops for struct with lifetimes.
5356
impl<'a> Drop for StructWithLtAndPredicate<'a> {
5457
//~ MONO_ITEM fn <StructWithLtAndPredicate<'_> as std::ops::Drop>::drop
58+
//~ MONO_ITEM fn <StructWithLtAndPredicate<'_> as std::ops::Drop>::pin_drop
5559
fn drop(&mut self) {}
5660
}

0 commit comments

Comments
 (0)