rustc_mir_transform/coroutine/
drop.rs1use super::*;
4
5struct FixReturnPendingVisitor<'tcx> {
7 tcx: TyCtxt<'tcx>,
8}
9
10impl<'tcx> MutVisitor<'tcx> for FixReturnPendingVisitor<'tcx> {
11 fn tcx(&self) -> TyCtxt<'tcx> {
12 self.tcx
13 }
14
15 fn visit_assign(
16 &mut self,
17 place: &mut Place<'tcx>,
18 rvalue: &mut Rvalue<'tcx>,
19 _location: Location,
20 ) {
21 if place.local != RETURN_PLACE {
22 return;
23 }
24
25 if let Rvalue::Aggregate(kind, _) = rvalue
27 && let AggregateKind::Adt(_, _, ref mut args, _, _) = **kind
28 {
29 *args = self.tcx.mk_args(&[self.tcx.types.unit.into()]);
30 }
31 }
32}
33
34#[tracing::instrument(level = "trace", skip(body), ret)]
38pub(super) fn has_async_drops<'tcx>(body: &mut Body<'tcx>) -> bool {
39 let mut has_async_drops = false;
40
41 let mut dropline: DenseBitSet<BasicBlock> = DenseBitSet::new_empty(body.basic_blocks.len());
42 for (bb, data) in traversal::reverse_postorder(body) {
43 if data.is_cleanup {
45 continue;
46 }
47
48 if let TerminatorKind::Yield { drop, .. } = data.terminator().kind {
49 if dropline.contains(bb) {
50 has_async_drops = true
51 }
52 if let Some(v) = drop {
53 dropline.insert(v);
54 }
55 }
56
57 if dropline.contains(bb) {
58 data.terminator().successors().for_each(|v| {
59 dropline.insert(v);
60 });
61 }
62 }
63
64 has_async_drops
65}
66
67#[tracing::instrument(level = "trace", skip(tcx, body))]
68pub(super) fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
69 use crate::elaborate_drop::{Unwind, elaborate_drop};
70 use crate::patch::MirPatch;
71 use crate::shim::DropShimElaborator;
72
73 let typing_env = body.typing_env(tcx);
77
78 let mut elaborator = DropShimElaborator {
79 body,
80 patch: MirPatch::new(body),
81 tcx,
82 typing_env,
83 produce_async_drops: false,
87 };
88
89 for (block, block_data) in body.basic_blocks.iter_enumerated() {
90 let (target, unwind, source_info, dropline) = match block_data.terminator() {
91 Terminator {
92 source_info,
93 kind: TerminatorKind::Drop { place, target, unwind, replace: _, drop },
94 ..
95 } => {
96 if let Some(local) = place.as_local()
97 && local == SELF_ARG
98 {
99 (target, unwind, source_info, *drop)
100 } else {
101 continue;
102 }
103 }
104 _ => continue,
105 };
106 let unwind = if block_data.is_cleanup {
107 Unwind::InCleanup
108 } else {
109 Unwind::To(match *unwind {
110 UnwindAction::Cleanup(tgt) => tgt,
111 UnwindAction::Continue => elaborator.patch.resume_block(),
112 UnwindAction::Unreachable => elaborator.patch.unreachable_cleanup_block(),
113 UnwindAction::Terminate(reason) => elaborator.patch.terminate_block(reason),
114 })
115 };
116 elaborate_drop(
117 &mut elaborator,
118 *source_info,
119 Place::from(SELF_ARG),
120 (),
121 *target,
122 unwind,
123 block,
124 dropline,
125 );
126 }
127 elaborator.patch.apply(body);
128}
129
130#[tracing::instrument(level = "trace", skip(tcx, body), ret)]
131pub(super) fn insert_clean_drop<'tcx>(
132 tcx: TyCtxt<'tcx>,
133 body: &mut Body<'tcx>,
134 has_async_drops: bool,
135) -> BasicBlock {
136 let return_block = if has_async_drops {
137 insert_poll_ready_block(tcx, body)
138 } else {
139 insert_term_block(body, TerminatorKind::Return)
140 };
141
142 let dropline = None;
146
147 let term = TerminatorKind::Drop {
148 place: Place::from(SELF_ARG),
149 target: return_block,
150 unwind: UnwindAction::Continue,
151 replace: false,
152 drop: dropline,
153 };
154
155 insert_term_block(body, term)
157}
158
159#[tracing::instrument(level = "trace", skip(tcx, transform, body))]
160pub(super) fn create_coroutine_drop_shim<'tcx>(
161 tcx: TyCtxt<'tcx>,
162 transform: &TransformVisitor<'tcx>,
163 coroutine_ty: Ty<'tcx>,
164 body: &Body<'tcx>,
165 drop_clean: BasicBlock,
166) -> Body<'tcx> {
167 let mut body = body.clone();
168 let _ = body.coroutine.take();
171 body.arg_count = 1;
174
175 let source_info = SourceInfo::outermost(body.span);
176
177 let mut cases = create_cases(&mut body, transform, Operation::Drop);
178
179 cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean));
180
181 let default_block = insert_term_block(&mut body, TerminatorKind::Return);
185 insert_switch(&mut body, cases, transform, default_block);
186
187 for block in body.basic_blocks_mut() {
188 let kind = &mut block.terminator_mut().kind;
189 if let TerminatorKind::CoroutineDrop = *kind {
190 *kind = TerminatorKind::Return;
191 }
192 }
193
194 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info);
196
197 make_coroutine_state_argument_indirect(tcx, &mut body);
198
199 simplify::remove_dead_blocks(&mut body);
202
203 deref_finder(tcx, &mut body, false);
205
206 let coroutine_instance = body.source.instance;
208 let drop_glue = tcx.require_lang_item(LangItem::DropGlue, body.span);
209 let drop_instance = InstanceKind::DropGlue(drop_glue, Some(coroutine_ty));
210
211 body.source.instance = coroutine_instance;
214 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop", &body) {
215 dumper.dump_mir(&body);
216 }
217 body.source.instance = drop_instance;
218
219 body.phase = MirPhase::Runtime(RuntimePhase::Initial);
225
226 body
227}
228
229#[tracing::instrument(level = "trace", skip(tcx, transform, body))]
231pub(super) fn create_coroutine_drop_shim_async<'tcx>(
232 tcx: TyCtxt<'tcx>,
233 transform: &TransformVisitor<'tcx>,
234 body: &Body<'tcx>,
235 drop_clean: BasicBlock,
236 can_unwind: bool,
237) -> Body<'tcx> {
238 let mut body = body.clone();
239 let _ = body.coroutine.take();
242
243 FixReturnPendingVisitor { tcx }.visit_body(&mut body);
244
245 if can_unwind {
247 generate_poison_block_and_redirect_unwinds_there(transform, &mut body);
248 }
249
250 let source_info = SourceInfo::outermost(body.span);
251
252 let mut cases = create_cases(&mut body, transform, Operation::AsyncDrop);
253
254 cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean));
255
256 use rustc_middle::mir::AssertKind::ResumedAfterPanic;
257 if can_unwind {
259 cases.insert(
260 1,
261 (
262 CoroutineArgs::POISONED,
263 insert_panic_block(tcx, &mut body, ResumedAfterPanic(transform.coroutine_kind)),
264 ),
265 );
266 }
267
268 let default_block = insert_poll_ready_block(tcx, &mut body);
271 insert_switch(&mut body, cases, transform, default_block);
272
273 for block in body.basic_blocks_mut() {
274 let kind = &mut block.terminator_mut().kind;
275 if let TerminatorKind::CoroutineDrop = *kind {
276 *kind = TerminatorKind::Return;
277 block.statements.push(return_poll_ready_assign(tcx, source_info));
278 }
279 }
280
281 let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, body.span));
283 let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
284 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
285
286 match transform.coroutine_kind {
287 CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
290 make_coroutine_state_argument_indirect(tcx, &mut body);
291 }
292
293 _ => {
294 make_coroutine_state_argument_pinned(tcx, &mut body);
295 }
296 }
297
298 simplify::remove_dead_blocks(&mut body);
301
302 pm::run_passes_no_validate(
303 tcx,
304 &mut body,
305 &[&abort_unwinding_calls::AbortUnwindingCalls],
306 None,
307 );
308
309 deref_finder(tcx, &mut body, false);
311
312 if transform.coroutine_kind.is_async_desugaring() {
313 transform_async_context(tcx, &mut body);
314 }
315
316 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_async", &body) {
317 dumper.dump_mir(&body);
318 }
319
320 body
321}
322
323pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>(
326 tcx: TyCtxt<'tcx>,
327 body: &Body<'tcx>,
328 coroutine_kind: CoroutineKind,
329) -> Body<'tcx> {
330 let mut body = body.clone();
331 let _ = body.coroutine.take();
334 let basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>> = IndexVec::new();
335 body.basic_blocks = BasicBlocks::new(basic_blocks);
336 body.var_debug_info.clear();
337
338 body.local_decls.truncate(1 + body.arg_count);
340
341 let source_info = SourceInfo::outermost(body.span);
342
343 let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, body.span));
345 let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
346 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
347
348 let call_bb = body.basic_blocks_mut().push(BasicBlockData::new(None, false));
350
351 let ret_bb = insert_poll_ready_block(tcx, &mut body);
353
354 let kind = TerminatorKind::Drop {
355 place: Place::from(SELF_ARG),
356 target: ret_bb,
357 unwind: UnwindAction::Continue,
358 replace: false,
359 drop: None,
360 };
361 body.basic_blocks_mut()[call_bb].terminator =
362 Some(Terminator { source_info, kind, attributes: ThinVec::new() });
363
364 deref_finder(tcx, &mut body, false);
366
367 if coroutine_kind.is_async_desugaring() {
368 transform_async_context(tcx, &mut body);
369 }
370
371 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_proxy_async", &body) {
372 dumper.dump_mir(&body);
373 }
374
375 body
376}