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 } else if let Rvalue::Use(Operand::Constant(constant), _) = rvalue {
31 if let Some(async_gen_pending_def_id) = self.tcx.lang_items().async_gen_pending()
32 && let Const::Unevaluated(unevaluated, _) = constant.const_
33 && unevaluated.def == async_gen_pending_def_id
34 {
35 let poll_def_id = self.tcx.lang_items().poll().unwrap();
36 *rvalue = Rvalue::Aggregate(
37 Box::new(AggregateKind::Adt(
38 poll_def_id,
39 VariantIdx::from_u32(1),
40 self.tcx.mk_args(&[self.tcx.types.unit.into()]),
41 None,
42 None,
43 )),
44 IndexVec::new(),
45 );
46 }
47 }
48 }
49}
50
51#[tracing::instrument(level = "trace", skip(body), ret)]
55pub(super) fn has_async_drops<'tcx>(body: &mut Body<'tcx>) -> bool {
56 let mut has_async_drops = false;
57
58 let mut dropline: DenseBitSet<BasicBlock> = DenseBitSet::new_empty(body.basic_blocks.len());
59 for (bb, data) in traversal::reverse_postorder(body) {
60 if data.is_cleanup {
62 continue;
63 }
64
65 if let TerminatorKind::Yield { drop, .. } = data.terminator().kind {
66 if dropline.contains(bb) {
67 has_async_drops = true
68 }
69 if let Some(v) = drop {
70 dropline.insert(v);
71 }
72 }
73
74 if dropline.contains(bb) {
75 data.terminator().successors().for_each(|v| {
76 dropline.insert(v);
77 });
78 }
79 }
80
81 has_async_drops
82}
83
84#[tracing::instrument(level = "trace", skip(tcx, body))]
85pub(super) fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
86 use crate::elaborate_drop::{Unwind, elaborate_drop};
87 use crate::patch::MirPatch;
88 use crate::shim::DropShimElaborator;
89
90 let typing_env = body.typing_env(tcx);
94
95 let mut elaborator = DropShimElaborator {
96 body,
97 patch: MirPatch::new(body),
98 tcx,
99 typing_env,
100 produce_async_drops: false,
104 };
105
106 for (block, block_data) in body.basic_blocks.iter_enumerated() {
107 let (target, unwind, source_info, dropline) = match block_data.terminator() {
108 Terminator {
109 source_info,
110 kind: TerminatorKind::Drop { place, target, unwind, replace: _, drop },
111 ..
112 } => {
113 if let Some(local) = place.as_local()
114 && local == SELF_ARG
115 {
116 (target, unwind, source_info, *drop)
117 } else {
118 continue;
119 }
120 }
121 _ => continue,
122 };
123 let unwind = if block_data.is_cleanup {
124 Unwind::InCleanup
125 } else {
126 Unwind::To(match *unwind {
127 UnwindAction::Cleanup(tgt) => tgt,
128 UnwindAction::Continue => elaborator.patch.resume_block(),
129 UnwindAction::Unreachable => elaborator.patch.unreachable_cleanup_block(),
130 UnwindAction::Terminate(reason) => elaborator.patch.terminate_block(reason),
131 })
132 };
133 elaborate_drop(
134 &mut elaborator,
135 *source_info,
136 Place::from(SELF_ARG),
137 (),
138 *target,
139 unwind,
140 block,
141 dropline,
142 );
143 }
144 elaborator.patch.apply(body);
145}
146
147#[tracing::instrument(level = "trace", skip(tcx, body), ret)]
148pub(super) fn insert_clean_drop<'tcx>(
149 tcx: TyCtxt<'tcx>,
150 body: &mut Body<'tcx>,
151 has_async_drops: bool,
152) -> BasicBlock {
153 let return_block = if has_async_drops {
154 insert_poll_ready_block(tcx, body)
155 } else {
156 insert_term_block(body, TerminatorKind::Return)
157 };
158
159 let dropline = None;
163
164 let term = TerminatorKind::Drop {
165 place: Place::from(SELF_ARG),
166 target: return_block,
167 unwind: UnwindAction::Continue,
168 replace: false,
169 drop: dropline,
170 };
171
172 insert_term_block(body, term)
174}
175
176#[tracing::instrument(level = "trace", skip(tcx, transform, body))]
177pub(super) fn create_coroutine_drop_shim<'tcx>(
178 tcx: TyCtxt<'tcx>,
179 transform: &TransformVisitor<'tcx>,
180 coroutine_ty: Ty<'tcx>,
181 body: &Body<'tcx>,
182 drop_clean: BasicBlock,
183) -> Body<'tcx> {
184 let mut body = body.clone();
185 let _ = body.coroutine.take();
188 body.arg_count = 1;
191
192 let source_info = SourceInfo::outermost(body.span);
193
194 let mut cases = create_cases(&mut body, transform, Operation::Drop);
195
196 cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean));
197
198 let default_block = insert_term_block(&mut body, TerminatorKind::Return);
202 insert_switch(&mut body, cases, transform, default_block);
203
204 for block in body.basic_blocks_mut() {
205 let kind = &mut block.terminator_mut().kind;
206 if let TerminatorKind::CoroutineDrop = *kind {
207 *kind = TerminatorKind::Return;
208 }
209 }
210
211 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info);
213 body.basic_blocks_mut()[START_BLOCK].statements.push(Statement::new(
214 source_info,
215 StatementKind::Assign(Box::new((
216 Place::return_place(),
217 Rvalue::Use(
218 Operand::zero_sized_constant(tcx.types.unit, source_info.span),
219 WithRetag::Yes,
220 ),
221 ))),
222 ));
223
224 make_coroutine_state_argument_indirect(tcx, &mut body);
225
226 simplify::remove_dead_blocks(&mut body);
229
230 deref_finder(tcx, &mut body, false);
232
233 let coroutine_instance = body.source.instance;
235 let drop_glue = tcx.require_lang_item(LangItem::DropGlue, body.span);
236 let drop_instance = InstanceKind::Shim(ShimKind::DropGlue(drop_glue, Some(coroutine_ty)));
237
238 body.source.instance = coroutine_instance;
241 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop", &body) {
242 dumper.dump_mir(&body);
243 }
244 body.source.instance = drop_instance;
245
246 body.phase = MirPhase::Runtime(RuntimePhase::Initial);
252
253 body
254}
255
256#[tracing::instrument(level = "trace", skip(tcx, transform, body))]
258pub(super) fn create_coroutine_drop_shim_async<'tcx>(
259 tcx: TyCtxt<'tcx>,
260 transform: &TransformVisitor<'tcx>,
261 body: &Body<'tcx>,
262 drop_clean: BasicBlock,
263 can_unwind: bool,
264) -> Body<'tcx> {
265 let mut body = body.clone();
266 let _ = body.coroutine.take();
269
270 FixReturnPendingVisitor { tcx }.visit_body(&mut body);
271
272 if can_unwind {
274 generate_poison_block_and_redirect_unwinds_there(transform, &mut body);
275 }
276
277 let source_info = SourceInfo::outermost(body.span);
278
279 let mut cases = create_cases(&mut body, transform, Operation::AsyncDrop);
280
281 cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean));
282
283 use rustc_middle::mir::AssertKind::ResumedAfterPanic;
284 if can_unwind {
286 cases.insert(
287 1,
288 (
289 CoroutineArgs::POISONED,
290 if tcx.sess.opts.unstable_opts.async_panic {
291 insert_panic_block(tcx, &mut body, ResumedAfterPanic(transform.coroutine_kind))
292 } else {
293 insert_poll_pending_block(tcx, &mut body, transform.old_ret_ty)
294 },
295 ),
296 );
297 }
298
299 let default_block = insert_poll_ready_block(tcx, &mut body);
302 insert_switch(&mut body, cases, transform, default_block);
303
304 for block in body.basic_blocks_mut() {
305 let kind = &mut block.terminator_mut().kind;
306 if let TerminatorKind::CoroutineDrop = *kind {
307 *kind = TerminatorKind::Return;
308 block.statements.push(return_poll_ready_assign(tcx, source_info));
309 }
310 }
311
312 let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, body.span));
314 let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
315 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
316
317 match transform.coroutine_kind {
318 CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
321 make_coroutine_state_argument_indirect(tcx, &mut body);
322 }
323
324 _ => {
325 make_coroutine_state_argument_pinned(tcx, &mut body);
326 }
327 }
328
329 simplify::remove_dead_blocks(&mut body);
332
333 pm::run_passes_no_validate(
334 tcx,
335 &mut body,
336 &[&abort_unwinding_calls::AbortUnwindingCalls],
337 None,
338 );
339
340 deref_finder(tcx, &mut body, false);
342
343 if transform.coroutine_kind.is_async_desugaring() {
344 transform_async_context(tcx, &mut body);
345 }
346
347 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_async", &body) {
348 dumper.dump_mir(&body);
349 }
350
351 body
352}
353
354pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>(
357 tcx: TyCtxt<'tcx>,
358 body: &Body<'tcx>,
359 coroutine_kind: CoroutineKind,
360) -> Body<'tcx> {
361 let mut body = body.clone();
362 let _ = body.coroutine.take();
365 let basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>> = IndexVec::new();
366 body.basic_blocks = BasicBlocks::new(basic_blocks);
367 body.var_debug_info.clear();
368
369 body.local_decls.truncate(1 + body.arg_count);
371
372 let source_info = SourceInfo::outermost(body.span);
373
374 let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, body.span));
376 let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
377 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
378
379 let call_bb = body.basic_blocks_mut().push(BasicBlockData::maybe_unterminated(None, false));
381
382 let ret_bb = insert_poll_ready_block(tcx, &mut body);
384
385 let kind = TerminatorKind::Drop {
386 place: Place::from(SELF_ARG),
387 target: ret_bb,
388 unwind: UnwindAction::Continue,
389 replace: false,
390 drop: None,
391 };
392 body.basic_blocks_mut()[call_bb].terminator = Some(Terminator { source_info, kind });
393
394 make_coroutine_state_argument_pinned(tcx, &mut body);
395
396 deref_finder(tcx, &mut body, false);
398
399 if coroutine_kind.is_async_desugaring() {
400 transform_async_context(tcx, &mut body);
401 }
402
403 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_proxy_async", &body) {
404 dumper.dump_mir(&body);
405 }
406
407 body
408}