Skip to content

Commit 68643ec

Browse files
committed
Add Drop::pin_drop for structually pinned types
1 parent 140044c commit 68643ec

26 files changed

+713
-37
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 = conflicting implementations 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: 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 = tcx.get_attr(adt_def_id, sym::pin_v2).map(|attr| attr.span());
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
@@ -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;
@@ -1278,6 +1279,15 @@ fn check_impl_items_against_trait<'tcx>(
12781279
if !is_implemented_here {
12791280
let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id));
12801281
match tcx.eval_default_body_stability(trait_item_id, full_impl_span) {
1282+
// When the feature `pin_ergonomics` is disabled, we report `Drop::drop` is missing,
1283+
// instead of `Drop::drop` is unstable that might be confusing.
1284+
EvalResult::Deny { .. }
1285+
if !tcx.features().pin_ergonomics()
1286+
&& tcx.is_lang_item(trait_ref.def_id, hir::LangItem::Drop)
1287+
&& tcx.item_name(trait_item_id) == sym::drop =>
1288+
{
1289+
missing_items.push(tcx.associated_item(trait_item_id));
1290+
}
12811291
EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable(
12821292
tcx,
12831293
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
@@ -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
@@ -1708,3 +1708,31 @@ pub(crate) struct AsyncDropWithoutSyncDrop {
17081708
#[primary_span]
17091709
pub span: Span,
17101710
}
1711+
1712+
#[derive(Diagnostic)]
1713+
#[diag(hir_analysis_conflict_impl_drop_and_pin_drop)]
1714+
pub(crate) struct ConflictImplDropAndPinDrop {
1715+
#[primary_span]
1716+
pub span: Span,
1717+
#[label(hir_analysis_drop_label)]
1718+
pub drop_span: Span,
1719+
#[label(hir_analysis_pin_drop_label)]
1720+
pub pin_drop_span: Span,
1721+
}
1722+
1723+
#[derive(Diagnostic)]
1724+
#[diag(hir_analysis_pin_v2_without_pin_drop)]
1725+
#[help]
1726+
pub(crate) struct PinV2WithoutPinDrop {
1727+
#[primary_span]
1728+
#[suggestion(
1729+
hir_analysis_pin_drop_sugg,
1730+
code = "fn pin_drop(&pin mut self)",
1731+
applicability = "maybe-incorrect"
1732+
)]
1733+
pub span: Span,
1734+
#[note]
1735+
#[suggestion(hir_analysis_remove_pin_v2_sugg, code = "", applicability = "maybe-incorrect")]
1736+
pub pin_v2_span: Option<Span>,
1737+
pub adt_name: Symbol,
1738+
}

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/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ mir_build_borrow_of_moved_value = borrow of moved value
2626
.value_borrowed_label = value borrowed here after move
2727
.suggestion = borrow this binding in the pattern to avoid moving the value
2828
29+
mir_build_call_drop_explicitly_requires_unsafe =
30+
call `{$function}` explicitly is unsafe and requires unsafe block
31+
2932
mir_build_call_to_deprecated_safe_fn_requires_unsafe =
3033
call to deprecated safe function `{$function}` is unsafe and requires unsafe block
3134
.note = consult the function's documentation for information on how to avoid undefined behavior

compiler/rustc_mir_build/src/check_unsafety.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,11 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
542542
CallToFunctionWith { function: func_did, missing, build_enabled },
543543
);
544544
}
545+
if let Some(trait_did) = self.tcx.trait_of_assoc(func_did)
546+
&& self.tcx.is_lang_item(trait_did, hir::LangItem::Drop)
547+
{
548+
self.requires_unsafe(expr.span, CallDropExplicitly(func_did));
549+
}
545550
}
546551
}
547552
ExprKind::RawBorrow { arg, .. } => {
@@ -770,6 +775,8 @@ enum UnsafeOpKind {
770775
build_enabled: Vec<Symbol>,
771776
},
772777
UnsafeBinderCast,
778+
/// Calling `Drop::drop` or `Drop::pin_drop` explicitly.
779+
CallDropExplicitly(DefId),
773780
}
774781

775782
use UnsafeOpKind::*;
@@ -947,6 +954,9 @@ impl UnsafeOpKind {
947954
unsafe_not_inherited_note,
948955
},
949956
),
957+
CallDropExplicitly(_) => {
958+
span_bug!(span, "`Drop::drop` or `Drop::pin_drop` should not be called explicitly")
959+
}
950960
}
951961
}
952962

@@ -1164,6 +1174,13 @@ impl UnsafeOpKind {
11641174
UnsafeBinderCast => {
11651175
dcx.emit_err(UnsafeBinderCastRequiresUnsafe { span, unsafe_not_inherited_note });
11661176
}
1177+
CallDropExplicitly(did) => {
1178+
dcx.emit_err(CallDropExplicitlyRequiresUnsafe {
1179+
span,
1180+
unsafe_not_inherited_note,
1181+
function: tcx.def_path_str(*did),
1182+
});
1183+
}
11671184
}
11681185
}
11691186
}

compiler/rustc_mir_build/src/errors.rs

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

535+
#[derive(Diagnostic)]
536+
#[diag(mir_build_call_drop_explicitly_requires_unsafe, code = E0133)]
537+
pub(crate) struct CallDropExplicitlyRequiresUnsafe {
538+
#[primary_span]
539+
pub(crate) span: Span,
540+
pub(crate) function: String,
541+
#[subdiagnostic]
542+
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
543+
}
544+
535545
#[derive(Subdiagnostic)]
536546
#[label(mir_build_unsafe_not_inherited)]
537547
pub(crate) struct UnsafeNotInheritedNote {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ symbols! {
16881688
pic,
16891689
pie,
16901690
pin,
1691+
pin_drop,
16911692
pin_ergonomics,
16921693
pin_macro,
16931694
pin_v2,

0 commit comments

Comments
 (0)