1use std::{fmt, iter, mem};
2
3use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
4use rustc_hir::def::DefKind;
5use rustc_hir::lang_items::LangItem;
6use rustc_index::Idx;
7use rustc_middle::mir::*;
8use rustc_middle::ty::adjustment::PointerCoercion;
9use rustc_middle::ty::util::IntTypeExt;
10use rustc_middle::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt};
11use rustc_middle::{bug, span_bug, traits};
12use rustc_span::DUMMY_SP;
13use rustc_span::source_map::{Spanned, dummy_spanned};
14use tracing::{debug, instrument};
15
16use crate::patch::MirPatch;
17
18#[derive(Debug)]
20pub(crate) enum DropStyle {
21 Dead,
23
24 Static,
27
28 Conditional,
30
31 Open,
37}
38
39#[derive(Debug)]
41pub(crate) enum DropFlagMode {
42 Shallow,
44 Deep,
46}
47
48#[derive(Copy, Clone, Debug)]
50pub(crate) enum Unwind {
51 To(BasicBlock),
53 InCleanup,
55}
56
57impl Unwind {
58 fn is_cleanup(self) -> bool {
59 match self {
60 Unwind::To(..) => false,
61 Unwind::InCleanup => true,
62 }
63 }
64
65 fn into_action(self) -> UnwindAction {
66 match self {
67 Unwind::To(bb) => UnwindAction::Cleanup(bb),
68 Unwind::InCleanup => UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
69 }
70 }
71
72 fn map<F>(self, f: F) -> Self
73 where
74 F: FnOnce(BasicBlock) -> BasicBlock,
75 {
76 match self {
77 Unwind::To(bb) => Unwind::To(f(bb)),
78 Unwind::InCleanup => Unwind::InCleanup,
79 }
80 }
81}
82
83pub(crate) trait DropElaborator<'a, 'tcx>: fmt::Debug {
84 type Path: Copy + fmt::Debug;
90
91 fn patch_ref(&self) -> &MirPatch<'tcx>;
94 fn patch(&mut self) -> &mut MirPatch<'tcx>;
95 fn body(&self) -> &'a Body<'tcx>;
96 fn tcx(&self) -> TyCtxt<'tcx>;
97 fn typing_env(&self) -> ty::TypingEnv<'tcx>;
98 fn allow_async_drops(&self) -> bool;
99
100 fn terminator_loc(&self, bb: BasicBlock) -> Location;
101
102 fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle;
106
107 fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>>;
109
110 fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode);
115
116 fn field_subpath(&self, path: Self::Path, field: FieldIdx) -> Option<Self::Path>;
122
123 fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path>;
129
130 fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option<Self::Path>;
134
135 fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option<Self::Path>;
141}
142
143#[derive(Debug)]
144struct DropCtxt<'a, 'b, 'tcx, D>
145where
146 D: DropElaborator<'b, 'tcx>,
147{
148 elaborator: &'a mut D,
149
150 source_info: SourceInfo,
151
152 place: Place<'tcx>,
153 path: D::Path,
154 succ: BasicBlock,
155 unwind: Unwind,
156 dropline: Option<BasicBlock>,
157}
158
159pub(crate) fn elaborate_drop<'b, 'tcx, D>(
168 elaborator: &mut D,
169 source_info: SourceInfo,
170 place: Place<'tcx>,
171 path: D::Path,
172 succ: BasicBlock,
173 unwind: Unwind,
174 bb: BasicBlock,
175 dropline: Option<BasicBlock>,
176) where
177 D: DropElaborator<'b, 'tcx>,
178 'tcx: 'b,
179{
180 DropCtxt { elaborator, source_info, place, path, succ, unwind, dropline }.elaborate_drop(bb)
181}
182
183impl<'a, 'b, 'tcx, D> DropCtxt<'a, 'b, 'tcx, D>
184where
185 D: DropElaborator<'b, 'tcx>,
186 'tcx: 'b,
187{
188 #[instrument(level = "trace", skip(self), ret)]
189 fn place_ty(&self, place: Place<'tcx>) -> Ty<'tcx> {
190 if place.local < self.elaborator.body().local_decls.next_index() {
191 place.ty(self.elaborator.body(), self.tcx()).ty
192 } else {
193 PlaceTy::from_ty(self.elaborator.patch_ref().local_ty(place.local))
195 .multi_projection_ty(self.elaborator.tcx(), place.projection)
196 .ty
197 }
198 }
199
200 fn tcx(&self) -> TyCtxt<'tcx> {
201 self.elaborator.tcx()
202 }
203
204 fn build_async_drop(
212 &mut self,
213 place: Place<'tcx>,
214 drop_ty: Ty<'tcx>,
215 bb: Option<BasicBlock>,
216 succ: BasicBlock,
217 unwind: Unwind,
218 dropline: Option<BasicBlock>,
219 call_destructor_only: bool,
220 ) -> BasicBlock {
221 let tcx = self.tcx();
222 let span = self.source_info.span;
223
224 let pin_obj_bb = bb.unwrap_or_else(|| {
225 self.elaborator.patch().new_block(BasicBlockData {
226 statements: vec![],
227 terminator: Some(Terminator {
228 source_info: self.source_info,
230 kind: TerminatorKind::Return,
231 }),
232 is_cleanup: false,
233 })
234 });
235
236 let (fut_ty, drop_fn_def_id, trait_args) = if call_destructor_only {
237 let trait_ref = ty::TraitRef::new(
239 tcx,
240 tcx.require_lang_item(LangItem::AsyncDrop, Some(span)),
241 [drop_ty],
242 );
243 let (drop_trait, trait_args) = match tcx.codegen_select_candidate(
244 ty::TypingEnv::fully_monomorphized().as_query_input(trait_ref),
245 ) {
246 Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData {
247 impl_def_id,
248 args,
249 ..
250 })) => (*impl_def_id, *args),
251 impl_source => {
252 span_bug!(span, "invalid `AsyncDrop` impl_source: {:?}", impl_source);
253 }
254 };
255 let Some(drop_fn_def_id) = tcx
259 .associated_item_def_ids(drop_trait)
260 .first()
261 .and_then(|def_id| {
262 if tcx.def_kind(def_id) == DefKind::AssocFn
263 && tcx.check_args_compatible(*def_id, trait_args)
264 {
265 Some(def_id)
266 } else {
267 None
268 }
269 })
270 .copied()
271 else {
272 tcx.dcx().span_delayed_bug(
273 self.elaborator.body().span,
274 "AsyncDrop type without correct `async fn drop(...)`.",
275 );
276 self.elaborator.patch().patch_terminator(
277 pin_obj_bb,
278 TerminatorKind::Drop {
279 place,
280 target: succ,
281 unwind: unwind.into_action(),
282 replace: false,
283 drop: None,
284 async_fut: None,
285 },
286 );
287 return pin_obj_bb;
288 };
289 let drop_fn = Ty::new_fn_def(tcx, drop_fn_def_id, trait_args);
290 let sig = drop_fn.fn_sig(tcx);
291 let sig = tcx.instantiate_bound_regions_with_erased(sig);
292 (sig.output(), drop_fn_def_id, trait_args)
293 } else {
294 let drop_fn_def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, Some(span));
296 let trait_args = tcx.mk_args(&[drop_ty.into()]);
297 let sig = tcx.fn_sig(drop_fn_def_id).instantiate(tcx, trait_args);
298 let sig = tcx.instantiate_bound_regions_with_erased(sig);
299 (sig.output(), drop_fn_def_id, trait_args)
300 };
301
302 let fut = Place::from(self.new_temp(fut_ty));
303
304 let obj_ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, drop_ty);
306 let obj_ref_place = Place::from(self.new_temp(obj_ref_ty));
307
308 let term_loc = self.elaborator.terminator_loc(pin_obj_bb);
309 self.elaborator.patch().add_assign(
310 term_loc,
311 obj_ref_place,
312 Rvalue::Ref(
313 tcx.lifetimes.re_erased,
314 BorrowKind::Mut { kind: MutBorrowKind::Default },
315 place,
316 ),
317 );
318
319 let pin_obj_new_unchecked_fn = Ty::new_fn_def(
321 tcx,
322 tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)),
323 [GenericArg::from(obj_ref_ty)],
324 );
325 let pin_obj_ty = pin_obj_new_unchecked_fn.fn_sig(tcx).output().no_bound_vars().unwrap();
326 let pin_obj_place = Place::from(self.new_temp(pin_obj_ty));
327 let pin_obj_new_unchecked_fn = Operand::Constant(Box::new(ConstOperand {
328 span,
329 user_ty: None,
330 const_: Const::zero_sized(pin_obj_new_unchecked_fn),
331 }));
332
333 let drop_term_bb = self.new_block(
335 unwind,
336 TerminatorKind::Drop {
337 place,
338 target: succ,
339 unwind: unwind.into_action(),
340 replace: false,
341 drop: dropline,
342 async_fut: Some(fut.local),
343 },
344 );
345
346 let mut call_statements = Vec::new();
348 let drop_arg = if call_destructor_only {
349 pin_obj_place
350 } else {
351 let ty::Adt(adt_def, adt_args) = pin_obj_ty.kind() else {
352 bug!();
353 };
354 let obj_ptr_ty = Ty::new_mut_ptr(tcx, drop_ty);
355 let unwrap_ty = adt_def.non_enum_variant().fields[FieldIdx::ZERO].ty(tcx, adt_args);
356 let obj_ref_place = Place::from(self.new_temp(unwrap_ty));
357 call_statements.push(self.assign(
358 obj_ref_place,
359 Rvalue::Use(Operand::Copy(tcx.mk_place_field(
360 pin_obj_place,
361 FieldIdx::ZERO,
362 unwrap_ty,
363 ))),
364 ));
365
366 let obj_ptr_place = Place::from(self.new_temp(obj_ptr_ty));
367
368 let addr = Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_deref(obj_ref_place));
369 call_statements.push(self.assign(obj_ptr_place, addr));
370 obj_ptr_place
371 };
372 call_statements.push(Statement {
373 source_info: self.source_info,
374 kind: StatementKind::StorageLive(fut.local),
375 });
376
377 let call_drop_bb = self.new_block_with_statements(
378 unwind,
379 call_statements,
380 TerminatorKind::Call {
381 func: Operand::function_handle(tcx, drop_fn_def_id, trait_args, span),
382 args: [Spanned { node: Operand::Move(drop_arg), span: DUMMY_SP }].into(),
383 destination: fut,
384 target: Some(drop_term_bb),
385 unwind: unwind.into_action(),
386 call_source: CallSource::Misc,
387 fn_span: self.source_info.span,
388 },
389 );
390
391 self.elaborator.patch().add_statement(
393 Location { block: self.succ, statement_index: 0 },
394 StatementKind::StorageDead(fut.local),
395 );
396
397 self.elaborator.patch().patch_terminator(
399 pin_obj_bb,
400 TerminatorKind::Call {
401 func: pin_obj_new_unchecked_fn,
402 args: [dummy_spanned(Operand::Move(obj_ref_place))].into(),
403 destination: pin_obj_place,
404 target: Some(call_drop_bb),
405 unwind: unwind.into_action(),
406 call_source: CallSource::Misc,
407 fn_span: span,
408 },
409 );
410 pin_obj_bb
411 }
412
413 fn build_drop(&mut self, bb: BasicBlock) {
414 let drop_ty = self.place_ty(self.place);
415 if self.tcx().features().async_drop()
416 && self.elaborator.body().coroutine.is_some()
417 && self.elaborator.allow_async_drops()
418 && !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup
419 && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env())
420 {
421 self.build_async_drop(
422 self.place,
423 drop_ty,
424 Some(bb),
425 self.succ,
426 self.unwind,
427 self.dropline,
428 false,
429 );
430 } else {
431 self.elaborator.patch().patch_terminator(
432 bb,
433 TerminatorKind::Drop {
434 place: self.place,
435 target: self.succ,
436 unwind: self.unwind.into_action(),
437 replace: false,
438 drop: None,
439 async_fut: None,
440 },
441 );
442 }
443 }
444
445 #[instrument(level = "debug")]
464 fn elaborate_drop(&mut self, bb: BasicBlock) {
465 match self.elaborator.drop_style(self.path, DropFlagMode::Deep) {
466 DropStyle::Dead => {
467 self.elaborator
468 .patch()
469 .patch_terminator(bb, TerminatorKind::Goto { target: self.succ });
470 }
471 DropStyle::Static => {
472 self.build_drop(bb);
473 }
474 DropStyle::Conditional => {
475 let drop_bb = self.complete_drop(self.succ, self.unwind);
476 self.elaborator
477 .patch()
478 .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb });
479 }
480 DropStyle::Open => {
481 let drop_bb = self.open_drop();
482 self.elaborator
483 .patch()
484 .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb });
485 }
486 }
487 }
488
489 fn move_paths_for_fields(
492 &self,
493 base_place: Place<'tcx>,
494 variant_path: D::Path,
495 variant: &'tcx ty::VariantDef,
496 args: GenericArgsRef<'tcx>,
497 ) -> Vec<(Place<'tcx>, Option<D::Path>)> {
498 variant
499 .fields
500 .iter_enumerated()
501 .map(|(field_idx, field)| {
502 let subpath = self.elaborator.field_subpath(variant_path, field_idx);
503 let tcx = self.tcx();
504
505 assert_eq!(self.elaborator.typing_env().typing_mode, ty::TypingMode::PostAnalysis);
506 let field_ty = match tcx.try_normalize_erasing_regions(
507 self.elaborator.typing_env(),
508 field.ty(tcx, args),
509 ) {
510 Ok(t) => t,
511 Err(_) => Ty::new_error(
512 self.tcx(),
513 self.tcx().dcx().span_delayed_bug(
514 self.elaborator.body().span,
515 "Error normalizing in drop elaboration.",
516 ),
517 ),
518 };
519
520 (tcx.mk_place_field(base_place, field_idx, field_ty), subpath)
521 })
522 .collect()
523 }
524
525 fn drop_subpath(
526 &mut self,
527 place: Place<'tcx>,
528 path: Option<D::Path>,
529 succ: BasicBlock,
530 unwind: Unwind,
531 dropline: Option<BasicBlock>,
532 ) -> BasicBlock {
533 if let Some(path) = path {
534 debug!("drop_subpath: for std field {:?}", place);
535
536 DropCtxt {
537 elaborator: self.elaborator,
538 source_info: self.source_info,
539 path,
540 place,
541 succ,
542 unwind,
543 dropline,
544 }
545 .elaborated_drop_block()
546 } else {
547 debug!("drop_subpath: for rest field {:?}", place);
548
549 DropCtxt {
550 elaborator: self.elaborator,
551 source_info: self.source_info,
552 place,
553 succ,
554 unwind,
555 dropline,
556 path: self.path,
559 }
560 .complete_drop(succ, unwind)
561 }
562 }
563
564 fn drop_halfladder(
575 &mut self,
576 unwind_ladder: &[Unwind],
577 dropline_ladder: &[Option<BasicBlock>],
578 mut succ: BasicBlock,
579 fields: &[(Place<'tcx>, Option<D::Path>)],
580 ) -> Vec<BasicBlock> {
581 iter::once(succ)
582 .chain(itertools::izip!(fields.iter().rev(), unwind_ladder, dropline_ladder).map(
583 |(&(place, path), &unwind_succ, &dropline_to)| {
584 succ = self.drop_subpath(place, path, succ, unwind_succ, dropline_to);
585 succ
586 },
587 ))
588 .collect()
589 }
590
591 fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind, Option<BasicBlock>) {
592 (
596 self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind),
597 self.unwind,
598 self.dropline,
599 )
600 }
601
602 fn drop_ladder(
636 &mut self,
637 fields: Vec<(Place<'tcx>, Option<D::Path>)>,
638 succ: BasicBlock,
639 unwind: Unwind,
640 dropline: Option<BasicBlock>,
641 ) -> (BasicBlock, Unwind, Option<BasicBlock>) {
642 debug!("drop_ladder({:?}, {:?})", self, fields);
643 assert!(
644 if unwind.is_cleanup() { dropline.is_none() } else { true },
645 "Dropline is set for cleanup drop ladder"
646 );
647
648 let mut fields = fields;
649 fields.retain(|&(place, _)| {
650 self.place_ty(place).needs_drop(self.tcx(), self.elaborator.typing_env())
651 });
652
653 debug!("drop_ladder - fields needing drop: {:?}", fields);
654
655 let dropline_ladder: Vec<Option<BasicBlock>> = vec![None; fields.len() + 1];
656 let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1];
657 let unwind_ladder: Vec<_> = if let Unwind::To(succ) = unwind {
658 let halfladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields);
659 halfladder.into_iter().map(Unwind::To).collect()
660 } else {
661 unwind_ladder
662 };
663 let dropline_ladder: Vec<_> = if let Some(succ) = dropline {
664 let halfladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields);
665 halfladder.into_iter().map(Some).collect()
666 } else {
667 dropline_ladder
668 };
669
670 let normal_ladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields);
671
672 (
673 *normal_ladder.last().unwrap(),
674 *unwind_ladder.last().unwrap(),
675 *dropline_ladder.last().unwrap(),
676 )
677 }
678
679 fn open_drop_for_tuple(&mut self, tys: &[Ty<'tcx>]) -> BasicBlock {
680 debug!("open_drop_for_tuple({:?}, {:?})", self, tys);
681
682 let fields = tys
683 .iter()
684 .enumerate()
685 .map(|(i, &ty)| {
686 (
687 self.tcx().mk_place_field(self.place, FieldIdx::new(i), ty),
688 self.elaborator.field_subpath(self.path, FieldIdx::new(i)),
689 )
690 })
691 .collect();
692
693 let (succ, unwind, dropline) = self.drop_ladder_bottom();
694 self.drop_ladder(fields, succ, unwind, dropline).0
695 }
696
697 #[instrument(level = "debug", ret)]
699 fn open_drop_for_box_contents(
700 &mut self,
701 adt: ty::AdtDef<'tcx>,
702 args: GenericArgsRef<'tcx>,
703 succ: BasicBlock,
704 unwind: Unwind,
705 dropline: Option<BasicBlock>,
706 ) -> BasicBlock {
707 let unique_ty = adt.non_enum_variant().fields[FieldIdx::ZERO].ty(self.tcx(), args);
710 let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant();
711 let nonnull_ty = unique_variant.fields[FieldIdx::ZERO].ty(self.tcx(), args);
712 let ptr_ty = Ty::new_imm_ptr(self.tcx(), args[0].expect_ty());
713
714 let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::ZERO, unique_ty);
715 let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::ZERO, nonnull_ty);
716
717 let ptr_local = self.new_temp(ptr_ty);
718
719 let interior = self.tcx().mk_place_deref(Place::from(ptr_local));
720 let interior_path = self.elaborator.deref_subpath(self.path);
721
722 let do_drop_bb = self.drop_subpath(interior, interior_path, succ, unwind, dropline);
723
724 let setup_bbd = BasicBlockData {
725 statements: vec![self.assign(
726 Place::from(ptr_local),
727 Rvalue::Cast(CastKind::Transmute, Operand::Copy(nonnull_place), ptr_ty),
728 )],
729 terminator: Some(Terminator {
730 kind: TerminatorKind::Goto { target: do_drop_bb },
731 source_info: self.source_info,
732 }),
733 is_cleanup: unwind.is_cleanup(),
734 };
735 self.elaborator.patch().new_block(setup_bbd)
736 }
737
738 #[instrument(level = "debug", ret)]
739 fn open_drop_for_adt(
740 &mut self,
741 adt: ty::AdtDef<'tcx>,
742 args: GenericArgsRef<'tcx>,
743 ) -> BasicBlock {
744 if adt.variants().is_empty() {
745 return self.elaborator.patch().new_block(BasicBlockData {
746 statements: vec![],
747 terminator: Some(Terminator {
748 source_info: self.source_info,
749 kind: TerminatorKind::Unreachable,
750 }),
751 is_cleanup: self.unwind.is_cleanup(),
752 });
753 }
754
755 let skip_contents = adt.is_union() || adt.is_manually_drop();
756 let contents_drop = if skip_contents {
757 (self.succ, self.unwind, self.dropline)
758 } else {
759 self.open_drop_for_adt_contents(adt, args)
760 };
761
762 if adt.is_box() {
763 let succ = self.destructor_call_block_sync((contents_drop.0, contents_drop.1));
765 let unwind = contents_drop
766 .1
767 .map(|unwind| self.destructor_call_block_sync((unwind, Unwind::InCleanup)));
768 let dropline = contents_drop
769 .2
770 .map(|dropline| self.destructor_call_block_sync((dropline, contents_drop.1)));
771
772 self.open_drop_for_box_contents(adt, args, succ, unwind, dropline)
773 } else if adt.has_dtor(self.tcx()) {
774 self.destructor_call_block(contents_drop)
775 } else {
776 contents_drop.0
777 }
778 }
779
780 fn open_drop_for_adt_contents(
781 &mut self,
782 adt: ty::AdtDef<'tcx>,
783 args: GenericArgsRef<'tcx>,
784 ) -> (BasicBlock, Unwind, Option<BasicBlock>) {
785 let (succ, unwind, dropline) = self.drop_ladder_bottom();
786 if !adt.is_enum() {
787 let fields =
788 self.move_paths_for_fields(self.place, self.path, adt.variant(FIRST_VARIANT), args);
789 self.drop_ladder(fields, succ, unwind, dropline)
790 } else {
791 self.open_drop_for_multivariant(adt, args, succ, unwind, dropline)
792 }
793 }
794
795 fn open_drop_for_multivariant(
796 &mut self,
797 adt: ty::AdtDef<'tcx>,
798 args: GenericArgsRef<'tcx>,
799 succ: BasicBlock,
800 unwind: Unwind,
801 dropline: Option<BasicBlock>,
802 ) -> (BasicBlock, Unwind, Option<BasicBlock>) {
803 let mut values = Vec::with_capacity(adt.variants().len());
804 let mut normal_blocks = Vec::with_capacity(adt.variants().len());
805 let mut unwind_blocks =
806 if unwind.is_cleanup() { None } else { Some(Vec::with_capacity(adt.variants().len())) };
807 let mut dropline_blocks =
808 if dropline.is_none() { None } else { Some(Vec::with_capacity(adt.variants().len())) };
809
810 let mut have_otherwise_with_drop_glue = false;
811 let mut have_otherwise = false;
812 let tcx = self.tcx();
813
814 for (variant_index, discr) in adt.discriminants(tcx) {
815 let variant = &adt.variant(variant_index);
816 let subpath = self.elaborator.downcast_subpath(self.path, variant_index);
817
818 if let Some(variant_path) = subpath {
819 let base_place = tcx.mk_place_elem(
820 self.place,
821 ProjectionElem::Downcast(Some(variant.name), variant_index),
822 );
823 let fields = self.move_paths_for_fields(base_place, variant_path, variant, args);
824 values.push(discr.val);
825 if let Unwind::To(unwind) = unwind {
826 let unwind_blocks = unwind_blocks.as_mut().unwrap();
845 let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1];
846 let dropline_ladder: Vec<Option<BasicBlock>> = vec![None; fields.len() + 1];
847 let halfladder =
848 self.drop_halfladder(&unwind_ladder, &dropline_ladder, unwind, &fields);
849 unwind_blocks.push(halfladder.last().cloned().unwrap());
850 }
851 let (normal, _, drop_bb) = self.drop_ladder(fields, succ, unwind, dropline);
852 normal_blocks.push(normal);
853 if dropline.is_some() {
854 dropline_blocks.as_mut().unwrap().push(drop_bb.unwrap());
855 }
856 } else {
857 have_otherwise = true;
858
859 let typing_env = self.elaborator.typing_env();
860 let have_field_with_drop_glue = variant
861 .fields
862 .iter()
863 .any(|field| field.ty(tcx, args).needs_drop(tcx, typing_env));
864 if have_field_with_drop_glue {
865 have_otherwise_with_drop_glue = true;
866 }
867 }
868 }
869
870 if !have_otherwise {
871 values.pop();
872 } else if !have_otherwise_with_drop_glue {
873 normal_blocks.push(self.goto_block(succ, unwind));
874 if let Unwind::To(unwind) = unwind {
875 unwind_blocks.as_mut().unwrap().push(self.goto_block(unwind, Unwind::InCleanup));
876 }
877 } else {
878 normal_blocks.push(self.drop_block(succ, unwind));
879 if let Unwind::To(unwind) = unwind {
880 unwind_blocks.as_mut().unwrap().push(self.drop_block(unwind, Unwind::InCleanup));
881 }
882 }
883
884 (
885 self.adt_switch_block(adt, normal_blocks, &values, succ, unwind),
886 unwind.map(|unwind| {
887 self.adt_switch_block(
888 adt,
889 unwind_blocks.unwrap(),
890 &values,
891 unwind,
892 Unwind::InCleanup,
893 )
894 }),
895 dropline.map(|dropline| {
896 self.adt_switch_block(adt, dropline_blocks.unwrap(), &values, dropline, unwind)
897 }),
898 )
899 }
900
901 fn adt_switch_block(
902 &mut self,
903 adt: ty::AdtDef<'tcx>,
904 blocks: Vec<BasicBlock>,
905 values: &[u128],
906 succ: BasicBlock,
907 unwind: Unwind,
908 ) -> BasicBlock {
909 let discr_ty = adt.repr().discr_type().to_ty(self.tcx());
917 let discr = Place::from(self.new_temp(discr_ty));
918 let discr_rv = Rvalue::Discriminant(self.place);
919 let switch_block = BasicBlockData {
920 statements: vec![self.assign(discr, discr_rv)],
921 terminator: Some(Terminator {
922 source_info: self.source_info,
923 kind: TerminatorKind::SwitchInt {
924 discr: Operand::Move(discr),
925 targets: SwitchTargets::new(
926 values.iter().copied().zip(blocks.iter().copied()),
927 *blocks.last().unwrap(),
928 ),
929 },
930 }),
931 is_cleanup: unwind.is_cleanup(),
932 };
933 let switch_block = self.elaborator.patch().new_block(switch_block);
934 self.drop_flag_test_block(switch_block, succ, unwind)
935 }
936
937 fn destructor_call_block_sync(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock {
938 debug!("destructor_call_block_sync({:?}, {:?})", self, succ);
939 let tcx = self.tcx();
940 let drop_trait = tcx.require_lang_item(LangItem::Drop, None);
941 let drop_fn = tcx.associated_item_def_ids(drop_trait)[0];
942 let ty = self.place_ty(self.place);
943
944 let ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty);
945 let ref_place = self.new_temp(ref_ty);
946 let unit_temp = Place::from(self.new_temp(tcx.types.unit));
947
948 let result = BasicBlockData {
949 statements: vec![self.assign(
950 Place::from(ref_place),
951 Rvalue::Ref(
952 tcx.lifetimes.re_erased,
953 BorrowKind::Mut { kind: MutBorrowKind::Default },
954 self.place,
955 ),
956 )],
957 terminator: Some(Terminator {
958 kind: TerminatorKind::Call {
959 func: Operand::function_handle(
960 tcx,
961 drop_fn,
962 [ty.into()],
963 self.source_info.span,
964 ),
965 args: [Spanned { node: Operand::Move(Place::from(ref_place)), span: DUMMY_SP }]
966 .into(),
967 destination: unit_temp,
968 target: Some(succ),
969 unwind: unwind.into_action(),
970 call_source: CallSource::Misc,
971 fn_span: self.source_info.span,
972 },
973 source_info: self.source_info,
974 }),
975 is_cleanup: unwind.is_cleanup(),
976 };
977
978 let destructor_block = self.elaborator.patch().new_block(result);
979
980 let block_start = Location { block: destructor_block, statement_index: 0 };
981 self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
982
983 self.drop_flag_test_block(destructor_block, succ, unwind)
984 }
985
986 fn destructor_call_block(
987 &mut self,
988 (succ, unwind, dropline): (BasicBlock, Unwind, Option<BasicBlock>),
989 ) -> BasicBlock {
990 debug!("destructor_call_block({:?}, {:?})", self, succ);
991 let ty = self.place_ty(self.place);
992 if self.tcx().features().async_drop()
993 && self.elaborator.body().coroutine.is_some()
994 && self.elaborator.allow_async_drops()
995 && !unwind.is_cleanup()
996 && ty.is_async_drop(self.tcx(), self.elaborator.typing_env())
997 {
998 let destructor_block =
999 self.build_async_drop(self.place, ty, None, succ, unwind, dropline, true);
1000
1001 let block_start = Location { block: destructor_block, statement_index: 0 };
1002 self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
1003
1004 self.drop_flag_test_block(destructor_block, succ, unwind)
1005 } else {
1006 self.destructor_call_block_sync((succ, unwind))
1007 }
1008 }
1009
1010 fn drop_loop(
1022 &mut self,
1023 succ: BasicBlock,
1024 cur: Local,
1025 len: Local,
1026 ety: Ty<'tcx>,
1027 unwind: Unwind,
1028 dropline: Option<BasicBlock>,
1029 ) -> BasicBlock {
1030 let copy = |place: Place<'tcx>| Operand::Copy(place);
1031 let move_ = |place: Place<'tcx>| Operand::Move(place);
1032 let tcx = self.tcx();
1033
1034 let ptr_ty = Ty::new_mut_ptr(tcx, ety);
1035 let ptr = Place::from(self.new_temp(ptr_ty));
1036 let can_go = Place::from(self.new_temp(tcx.types.bool));
1037 let one = self.constant_usize(1);
1038
1039 let drop_block = BasicBlockData {
1040 statements: vec![
1041 self.assign(
1042 ptr,
1043 Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_index(self.place, cur)),
1044 ),
1045 self.assign(
1046 cur.into(),
1047 Rvalue::BinaryOp(BinOp::Add, Box::new((move_(cur.into()), one))),
1048 ),
1049 ],
1050 is_cleanup: unwind.is_cleanup(),
1051 terminator: Some(Terminator {
1052 source_info: self.source_info,
1053 kind: TerminatorKind::Unreachable,
1055 }),
1056 };
1057 let drop_block = self.elaborator.patch().new_block(drop_block);
1058
1059 let loop_block = BasicBlockData {
1060 statements: vec![self.assign(
1061 can_go,
1062 Rvalue::BinaryOp(BinOp::Eq, Box::new((copy(Place::from(cur)), copy(len.into())))),
1063 )],
1064 is_cleanup: unwind.is_cleanup(),
1065 terminator: Some(Terminator {
1066 source_info: self.source_info,
1067 kind: TerminatorKind::if_(move_(can_go), succ, drop_block),
1068 }),
1069 };
1070 let loop_block = self.elaborator.patch().new_block(loop_block);
1071
1072 let place = tcx.mk_place_deref(ptr);
1073 if self.tcx().features().async_drop()
1074 && self.elaborator.body().coroutine.is_some()
1075 && self.elaborator.allow_async_drops()
1076 && !unwind.is_cleanup()
1077 && ety.needs_async_drop(self.tcx(), self.elaborator.typing_env())
1078 {
1079 self.build_async_drop(
1080 place,
1081 ety,
1082 Some(drop_block),
1083 loop_block,
1084 unwind,
1085 dropline,
1086 false,
1087 );
1088 } else {
1089 self.elaborator.patch().patch_terminator(
1090 drop_block,
1091 TerminatorKind::Drop {
1092 place,
1093 target: loop_block,
1094 unwind: unwind.into_action(),
1095 replace: false,
1096 drop: None,
1097 async_fut: None,
1098 },
1099 );
1100 }
1101 loop_block
1102 }
1103
1104 fn open_drop_for_array(
1105 &mut self,
1106 array_ty: Ty<'tcx>,
1107 ety: Ty<'tcx>,
1108 opt_size: Option<u64>,
1109 ) -> BasicBlock {
1110 debug!("open_drop_for_array({:?}, {:?}, {:?})", array_ty, ety, opt_size);
1111 let tcx = self.tcx();
1112
1113 if let Some(size) = opt_size {
1114 enum ProjectionKind<Path> {
1115 Drop(std::ops::Range<u64>),
1116 Keep(u64, Path),
1117 }
1118 let mut drop_ranges = vec![];
1123 let mut dropping = true;
1124 let mut start = 0;
1125 for i in 0..size {
1126 let path = self.elaborator.array_subpath(self.path, i, size);
1127 if dropping && path.is_some() {
1128 drop_ranges.push(ProjectionKind::Drop(start..i));
1129 dropping = false;
1130 } else if !dropping && path.is_none() {
1131 dropping = true;
1132 start = i;
1133 }
1134 if let Some(path) = path {
1135 drop_ranges.push(ProjectionKind::Keep(i, path));
1136 }
1137 }
1138 if !drop_ranges.is_empty() {
1139 if dropping {
1140 drop_ranges.push(ProjectionKind::Drop(start..size));
1141 }
1142 let fields = drop_ranges
1143 .iter()
1144 .rev()
1145 .map(|p| {
1146 let (project, path) = match p {
1147 ProjectionKind::Drop(r) => (
1148 ProjectionElem::Subslice {
1149 from: r.start,
1150 to: r.end,
1151 from_end: false,
1152 },
1153 None,
1154 ),
1155 &ProjectionKind::Keep(offset, path) => (
1156 ProjectionElem::ConstantIndex {
1157 offset,
1158 min_length: size,
1159 from_end: false,
1160 },
1161 Some(path),
1162 ),
1163 };
1164 (tcx.mk_place_elem(self.place, project), path)
1165 })
1166 .collect::<Vec<_>>();
1167 let (succ, unwind, dropline) = self.drop_ladder_bottom();
1168 return self.drop_ladder(fields, succ, unwind, dropline).0;
1169 }
1170 }
1171
1172 let array_ptr_ty = Ty::new_mut_ptr(tcx, array_ty);
1173 let array_ptr = self.new_temp(array_ptr_ty);
1174
1175 let slice_ty = Ty::new_slice(tcx, ety);
1176 let slice_ptr_ty = Ty::new_mut_ptr(tcx, slice_ty);
1177 let slice_ptr = self.new_temp(slice_ptr_ty);
1178
1179 let mut delegate_block = BasicBlockData {
1180 statements: vec![
1181 self.assign(Place::from(array_ptr), Rvalue::RawPtr(RawPtrKind::Mut, self.place)),
1182 self.assign(
1183 Place::from(slice_ptr),
1184 Rvalue::Cast(
1185 CastKind::PointerCoercion(
1186 PointerCoercion::Unsize,
1187 CoercionSource::Implicit,
1188 ),
1189 Operand::Move(Place::from(array_ptr)),
1190 slice_ptr_ty,
1191 ),
1192 ),
1193 ],
1194 is_cleanup: self.unwind.is_cleanup(),
1195 terminator: None,
1196 };
1197
1198 let array_place = mem::replace(
1199 &mut self.place,
1200 Place::from(slice_ptr).project_deeper(&[PlaceElem::Deref], tcx),
1201 );
1202 let slice_block = self.drop_loop_trio_for_slice(ety);
1203 self.place = array_place;
1204
1205 delegate_block.terminator = Some(Terminator {
1206 source_info: self.source_info,
1207 kind: TerminatorKind::Goto { target: slice_block },
1208 });
1209 self.elaborator.patch().new_block(delegate_block)
1210 }
1211
1212 fn drop_loop_trio_for_slice(&mut self, ety: Ty<'tcx>) -> BasicBlock {
1215 debug!("drop_loop_trio_for_slice({:?})", ety);
1216 let tcx = self.tcx();
1217 let len = self.new_temp(tcx.types.usize);
1218 let cur = self.new_temp(tcx.types.usize);
1219
1220 let unwind = self
1221 .unwind
1222 .map(|unwind| self.drop_loop(unwind, cur, len, ety, Unwind::InCleanup, None));
1223
1224 let dropline =
1225 self.dropline.map(|dropline| self.drop_loop(dropline, cur, len, ety, unwind, None));
1226
1227 let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind, dropline);
1228
1229 let [PlaceElem::Deref] = self.place.projection.as_slice() else {
1230 span_bug!(
1231 self.source_info.span,
1232 "Expected place for slice drop shim to be *_n, but it's {:?}",
1233 self.place,
1234 );
1235 };
1236
1237 let zero = self.constant_usize(0);
1238 let block = BasicBlockData {
1239 statements: vec![
1240 self.assign(
1241 len.into(),
1242 Rvalue::UnaryOp(
1243 UnOp::PtrMetadata,
1244 Operand::Copy(Place::from(self.place.local)),
1245 ),
1246 ),
1247 self.assign(cur.into(), Rvalue::Use(zero)),
1248 ],
1249 is_cleanup: unwind.is_cleanup(),
1250 terminator: Some(Terminator {
1251 source_info: self.source_info,
1252 kind: TerminatorKind::Goto { target: loop_block },
1253 }),
1254 };
1255
1256 let drop_block = self.elaborator.patch().new_block(block);
1257 let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind);
1259 self.drop_flag_test_block(reset_block, self.succ, unwind)
1260 }
1261
1262 fn open_drop(&mut self) -> BasicBlock {
1271 let ty = self.place_ty(self.place);
1272 match ty.kind() {
1273 ty::Closure(_, args) => self.open_drop_for_tuple(args.as_closure().upvar_tys()),
1274 ty::CoroutineClosure(_, args) => {
1275 self.open_drop_for_tuple(args.as_coroutine_closure().upvar_tys())
1276 }
1277 ty::Coroutine(_, args) => self.open_drop_for_tuple(args.as_coroutine().upvar_tys()),
1284 ty::Tuple(fields) => self.open_drop_for_tuple(fields),
1285 ty::Adt(def, args) => self.open_drop_for_adt(*def, args),
1286 ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind),
1287 ty::Array(ety, size) => {
1288 let size = size.try_to_target_usize(self.tcx());
1289 self.open_drop_for_array(ty, *ety, size)
1290 }
1291 ty::Slice(ety) => self.drop_loop_trio_for_slice(*ety),
1292
1293 ty::UnsafeBinder(_) => {
1294 self.tcx().dcx().span_delayed_bug(
1297 self.source_info.span,
1298 "open drop for unsafe binder shouldn't be encountered",
1299 );
1300 self.elaborator.patch().new_block(BasicBlockData {
1301 statements: vec![],
1302 terminator: Some(Terminator {
1303 source_info: self.source_info,
1304 kind: TerminatorKind::Unreachable,
1305 }),
1306 is_cleanup: self.unwind.is_cleanup(),
1307 })
1308 }
1309
1310 _ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty),
1311 }
1312 }
1313
1314 fn complete_drop(&mut self, succ: BasicBlock, unwind: Unwind) -> BasicBlock {
1315 debug!("complete_drop(succ={:?}, unwind={:?})", succ, unwind);
1316
1317 let drop_block = self.drop_block(succ, unwind);
1318
1319 self.drop_flag_test_block(drop_block, succ, unwind)
1320 }
1321
1322 fn drop_flag_reset_block(
1325 &mut self,
1326 mode: DropFlagMode,
1327 succ: BasicBlock,
1328 unwind: Unwind,
1329 ) -> BasicBlock {
1330 debug!("drop_flag_reset_block({:?},{:?})", self, mode);
1331
1332 if unwind.is_cleanup() {
1333 return succ;
1336 }
1337 let block = self.new_block(unwind, TerminatorKind::Goto { target: succ });
1338 let block_start = Location { block, statement_index: 0 };
1339 self.elaborator.clear_drop_flag(block_start, self.path, mode);
1340 block
1341 }
1342
1343 fn elaborated_drop_block(&mut self) -> BasicBlock {
1344 debug!("elaborated_drop_block({:?})", self);
1345 let blk = self.drop_block_simple(self.succ, self.unwind);
1346 self.elaborate_drop(blk);
1347 blk
1348 }
1349
1350 fn drop_block_simple(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
1351 let block = TerminatorKind::Drop {
1352 place: self.place,
1353 target,
1354 unwind: unwind.into_action(),
1355 replace: false,
1356 drop: self.dropline,
1357 async_fut: None,
1358 };
1359 self.new_block(unwind, block)
1360 }
1361
1362 fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
1363 let drop_ty = self.place_ty(self.place);
1364 if self.tcx().features().async_drop()
1365 && self.elaborator.body().coroutine.is_some()
1366 && self.elaborator.allow_async_drops()
1367 && !unwind.is_cleanup()
1368 && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env())
1369 {
1370 self.build_async_drop(
1371 self.place,
1372 drop_ty,
1373 None,
1374 self.succ,
1375 unwind,
1376 self.dropline,
1377 false,
1378 )
1379 } else {
1380 let block = TerminatorKind::Drop {
1381 place: self.place,
1382 target,
1383 unwind: unwind.into_action(),
1384 replace: false,
1385 drop: None,
1386 async_fut: None,
1387 };
1388 self.new_block(unwind, block)
1389 }
1390 }
1391
1392 fn goto_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
1393 let block = TerminatorKind::Goto { target };
1394 self.new_block(unwind, block)
1395 }
1396
1397 fn drop_flag_test_block(
1403 &mut self,
1404 on_set: BasicBlock,
1405 on_unset: BasicBlock,
1406 unwind: Unwind,
1407 ) -> BasicBlock {
1408 let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow);
1409 debug!(
1410 "drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}",
1411 self, on_set, on_unset, unwind, style
1412 );
1413
1414 match style {
1415 DropStyle::Dead => on_unset,
1416 DropStyle::Static => on_set,
1417 DropStyle::Conditional | DropStyle::Open => {
1418 let flag = self.elaborator.get_drop_flag(self.path).unwrap();
1419 let term = TerminatorKind::if_(flag, on_set, on_unset);
1420 self.new_block(unwind, term)
1421 }
1422 }
1423 }
1424
1425 fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock {
1426 self.elaborator.patch().new_block(BasicBlockData {
1427 statements: vec![],
1428 terminator: Some(Terminator { source_info: self.source_info, kind: k }),
1429 is_cleanup: unwind.is_cleanup(),
1430 })
1431 }
1432
1433 fn new_block_with_statements(
1434 &mut self,
1435 unwind: Unwind,
1436 statements: Vec<Statement<'tcx>>,
1437 k: TerminatorKind<'tcx>,
1438 ) -> BasicBlock {
1439 self.elaborator.patch().new_block(BasicBlockData {
1440 statements,
1441 terminator: Some(Terminator { source_info: self.source_info, kind: k }),
1442 is_cleanup: unwind.is_cleanup(),
1443 })
1444 }
1445
1446 fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
1447 self.elaborator.patch().new_temp(ty, self.source_info.span)
1448 }
1449
1450 fn constant_usize(&self, val: u16) -> Operand<'tcx> {
1451 Operand::Constant(Box::new(ConstOperand {
1452 span: self.source_info.span,
1453 user_ty: None,
1454 const_: Const::from_usize(self.tcx(), val.into()),
1455 }))
1456 }
1457
1458 fn assign(&self, lhs: Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> {
1459 Statement {
1460 source_info: self.source_info,
1461 kind: StatementKind::Assign(Box::new((lhs, rhs))),
1462 }
1463 }
1464}