Skip to content

Commit 9b07833

Browse files
committed
Add Drop::pin_drop for structually pinned types
1 parent b49ecc9 commit 9b07833

23 files changed

Lines changed: 742 additions & 36 deletions

compiler/rustc_hir_analysis/src/check/always_applicable.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
1212
use rustc_middle::span_bug;
1313
use rustc_middle::ty::util::CheckRegions;
1414
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypingMode};
15+
use rustc_span::sym;
1516
use rustc_trait_selection::regions::InferCtxtRegionExt;
1617
use rustc_trait_selection::traits::{self, ObligationCtxt};
1718

@@ -70,7 +71,11 @@ pub(crate) fn check_drop_impl(
7071
drop_impl_did,
7172
adt_def.did(),
7273
adt_to_impl_args,
73-
)
74+
)?;
75+
76+
check_drop_xor_pin_drop(tcx, adt_def.did(), drop_impl_did)?;
77+
78+
Ok(())
7479
}
7580
_ => {
7681
span_bug!(tcx.def_span(drop_impl_did), "incoherent impl of Drop");
@@ -291,3 +296,68 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>(
291296

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

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use rustc_middle::ty::{
2525
};
2626
use rustc_session::lint::builtin::UNINHABITED_STATIC;
2727
use rustc_span::source_map::Spanned;
28+
use rustc_span::sym;
2829
use rustc_target::spec::{AbiMap, AbiMapping};
2930
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
3031
use rustc_trait_selection::traits;
@@ -1321,6 +1322,15 @@ fn check_impl_items_against_trait<'tcx>(
13211322
if !is_implemented_here {
13221323
let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id));
13231324
match tcx.eval_default_body_stability(trait_item_id, full_impl_span) {
1325+
// When the feature `pin_ergonomics` is disabled, we report `Drop::drop` is missing,
1326+
// instead of `Drop::drop` is unstable that might be confusing.
1327+
EvalResult::Deny { .. }
1328+
if !tcx.features().pin_ergonomics()
1329+
&& tcx.is_lang_item(trait_ref.def_id, hir::LangItem::Drop)
1330+
&& tcx.item_name(trait_item_id) == sym::drop =>
1331+
{
1332+
missing_items.push(tcx.associated_item(trait_item_id));
1333+
}
13241334
EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable(
13251335
tcx,
13261336
full_impl_span,

compiler/rustc_hir_analysis/src/check/mod.rs

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

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,3 +1908,35 @@ pub(crate) struct ImplUnpinForPinProjectedType {
19081908
pub adt_span: Span,
19091909
pub adt_name: Symbol,
19101910
}
1911+
1912+
#[derive(Diagnostic)]
1913+
#[diag("conflicting implementations of `Drop::drop` and `Drop::pin_drop`")]
1914+
pub(crate) struct ConflictImplDropAndPinDrop {
1915+
#[primary_span]
1916+
pub span: Span,
1917+
#[label("`drop(&mut self)` implemented here")]
1918+
pub drop_span: Span,
1919+
#[label("`pin_drop(&pin mut self)` implemented here")]
1920+
pub pin_drop_span: Span,
1921+
}
1922+
1923+
#[derive(Diagnostic)]
1924+
#[diag("`{$adt_name}` must implement `pin_drop`")]
1925+
#[help("structurally pinned types must keep `Pin`'s safety contract")]
1926+
pub(crate) struct PinV2WithoutPinDrop {
1927+
#[primary_span]
1928+
#[suggestion(
1929+
"implement `pin_drop` instead",
1930+
code = "fn pin_drop(&pin mut self)",
1931+
applicability = "maybe-incorrect"
1932+
)]
1933+
pub span: Span,
1934+
#[note("`{$adt_name}` is marked `#[pin_v2]` here")]
1935+
#[suggestion(
1936+
"remove the `#[pin_v2]` attribute if it is not intended for structurally pinning",
1937+
code = "",
1938+
applicability = "maybe-incorrect"
1939+
)]
1940+
pub pin_v2_span: Option<Span>,
1941+
pub adt_name: Symbol,
1942+
}

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@ 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+
&& !tcx.is_lang_item(tcx.parent(body_id), LangItem::Drop)
45+
{
4346
let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
4447
errors::ExplicitDestructorCallSugg::Snippet {
4548
lo: expr_span.shrink_to_lo(),

compiler/rustc_mir_build/src/check_unsafety.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,11 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
515515
CallToFunctionWith { function: func_did, missing, build_enabled },
516516
);
517517
}
518+
if let Some(trait_did) = self.tcx.trait_of_assoc(func_did)
519+
&& self.tcx.is_lang_item(trait_did, hir::LangItem::Drop)
520+
{
521+
self.requires_unsafe(expr.span, CallDropExplicitly(func_did));
522+
}
518523
}
519524
}
520525
ExprKind::RawBorrow { arg, .. } => {
@@ -743,6 +748,8 @@ enum UnsafeOpKind {
743748
build_enabled: Vec<Symbol>,
744749
},
745750
UnsafeBinderCast,
751+
/// Calling `Drop::drop` or `Drop::pin_drop` explicitly.
752+
CallDropExplicitly(DefId),
746753
}
747754

748755
use UnsafeOpKind::*;
@@ -920,6 +927,9 @@ impl UnsafeOpKind {
920927
unsafe_not_inherited_note,
921928
},
922929
),
930+
CallDropExplicitly(_) => {
931+
span_bug!(span, "`Drop::drop` or `Drop::pin_drop` should not be called explicitly")
932+
}
923933
}
924934
}
925935

@@ -1137,6 +1147,13 @@ impl UnsafeOpKind {
11371147
UnsafeBinderCast => {
11381148
dcx.emit_err(UnsafeBinderCastRequiresUnsafe { span, unsafe_not_inherited_note });
11391149
}
1150+
CallDropExplicitly(did) => {
1151+
dcx.emit_err(CallDropExplicitlyRequiresUnsafe {
1152+
span,
1153+
unsafe_not_inherited_note,
1154+
function: tcx.def_path_str(*did),
1155+
});
1156+
}
11401157
}
11411158
}
11421159
}

compiler/rustc_mir_build/src/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,16 @@ pub(crate) struct UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
603603
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
604604
}
605605

606+
#[derive(Diagnostic)]
607+
#[diag("call `{$function}` explicitly is unsafe and requires unsafe block", code = E0133)]
608+
pub(crate) struct CallDropExplicitlyRequiresUnsafe {
609+
#[primary_span]
610+
pub(crate) span: Span,
611+
pub(crate) function: String,
612+
#[subdiagnostic]
613+
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
614+
}
615+
606616
#[derive(Subdiagnostic)]
607617
#[label("items do not inherit unsafety from separate enclosing items")]
608618
pub(crate) struct UnsafeNotInheritedNote {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,7 @@ symbols! {
14931493
pic,
14941494
pie,
14951495
pin,
1496+
pin_drop,
14961497
pin_ergonomics,
14971498
pin_v2,
14981499
platform_intrinsics,

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+
unsafe { Drop::pin_drop(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
}

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)