|
7 | 7 | use rustc_data_structures::fx::FxHashSet;
|
8 | 8 | use rustc_errors::codes::*;
|
9 | 9 | use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
|
| 10 | +use rustc_hir as hir; |
10 | 11 | use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
|
11 | 12 | use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
|
12 | 13 | use rustc_middle::span_bug;
|
13 | 14 | use rustc_middle::ty::util::CheckRegions;
|
14 | 15 | use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypingMode};
|
| 16 | +use rustc_span::{Span, sym}; |
15 | 17 | use rustc_trait_selection::regions::InferCtxtRegionExt;
|
16 | 18 | use rustc_trait_selection::traits::{self, ObligationCtxt};
|
17 | 19 |
|
@@ -70,7 +72,9 @@ pub(crate) fn check_drop_impl(
|
70 | 72 | drop_impl_did,
|
71 | 73 | adt_def.did(),
|
72 | 74 | adt_to_impl_args,
|
73 |
| - ) |
| 75 | + )?; |
| 76 | + |
| 77 | + ensure_coherent_unpin(tcx, drop_impl_did, adt_def.did(), adt_to_impl_args) |
74 | 78 | }
|
75 | 79 | _ => {
|
76 | 80 | span_bug!(tcx.def_span(drop_impl_did), "incoherent impl of Drop");
|
@@ -294,3 +298,102 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>(
|
294 | 298 |
|
295 | 299 | Ok(())
|
296 | 300 | }
|
| 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 | + (Some((span, _)), None) => { |
| 334 | + if let Some(negative_unpin_impl) = negative_unpin_impl { |
| 335 | + return Err(tcx.dcx().emit_err(crate::errors::ImplDropForNegativeUnpinType { |
| 336 | + span, |
| 337 | + negative_unpin_span: tcx.def_span(negative_unpin_impl), |
| 338 | + self_ty, |
| 339 | + })); |
| 340 | + } |
| 341 | + } |
| 342 | + (None, Some((span, _))) => { |
| 343 | + debug_assert!( |
| 344 | + tcx.features().pin_ergonomics(), |
| 345 | + "`Drop::pin_drop` should be guarded by the library feature gate" |
| 346 | + ); |
| 347 | + if negative_unpin_impl.is_none() { |
| 348 | + return Err(tcx.dcx().emit_err( |
| 349 | + crate::errors::ImplPinDropForNotNegativeUnpinType { |
| 350 | + span, |
| 351 | + impl_sugg_span: tcx.def_span(drop_impl_did).shrink_to_lo(), |
| 352 | + impl_generics_snippet: tcx |
| 353 | + .sess |
| 354 | + .source_map() |
| 355 | + .span_to_snippet(item_impl.generics.span) |
| 356 | + .unwrap_or_default(), |
| 357 | + self_ty, |
| 358 | + }, |
| 359 | + )); |
| 360 | + } |
| 361 | + } |
| 362 | + ( |
| 363 | + Some((drop_span, drop_span_with_body)), |
| 364 | + Some((pin_drop_span, pin_drop_span_with_body)), |
| 365 | + ) => { |
| 366 | + let remove_sugg = match negative_unpin_impl { |
| 367 | + // without `impl !Unpin`, should pick `drop(&mut self)`, and remove `pin_drop(&pin mut self)` |
| 368 | + None => pin_drop_span_with_body, |
| 369 | + // with `impl !Unpin`, should pick `pin_drop(&pin mut self)`, and remove `drop(&mut self)` |
| 370 | + Some(_) => drop_span_with_body, |
| 371 | + }; |
| 372 | + return Err(tcx.dcx().emit_err(crate::errors::ConflictImplDropAndPinDrop { |
| 373 | + span: tcx.def_span(drop_impl_did), |
| 374 | + drop_span, |
| 375 | + pin_drop_span, |
| 376 | + remove_sugg, |
| 377 | + })); |
| 378 | + } |
| 379 | + } |
| 380 | + Ok(()) |
| 381 | +} |
| 382 | + |
| 383 | +fn find_negative_unpin_impl<'tcx>( |
| 384 | + tcx: TyCtxt<'tcx>, |
| 385 | + self_ty: Ty<'tcx>, |
| 386 | + span: Span, |
| 387 | +) -> Option<LocalDefId> { |
| 388 | + let mut negative_unpin_impl_did = None; |
| 389 | + tcx.for_each_relevant_impl(tcx.require_lang_item(hir::LangItem::Unpin, span), self_ty, |did| { |
| 390 | + if tcx.impl_polarity(did) == ty::ImplPolarity::Negative { |
| 391 | + if negative_unpin_impl_did.is_some() { |
| 392 | + tcx.dcx() |
| 393 | + .span_delayed_bug(tcx.def_span(did), "duplicated `!Unpin` implementations"); |
| 394 | + } |
| 395 | + negative_unpin_impl_did = Some(did.expect_local()); |
| 396 | + } |
| 397 | + }); |
| 398 | + negative_unpin_impl_did |
| 399 | +} |
0 commit comments