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
214 make_coroutine_state_argument_indirect(tcx, &mut body);
215
216 simplify::remove_dead_blocks(&mut body);
219
220 deref_finder(tcx, &mut body, false);
222
223 let coroutine_instance = body.source.instance;
225 let drop_glue = tcx.require_lang_item(LangItem::DropGlue, body.span);
226 let drop_instance = InstanceKind::Shim(ShimKind::DropGlue(drop_glue, Some(coroutine_ty)));
227
228 body.source.instance = coroutine_instance;
231 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop", &body) {
232 dumper.dump_mir(&body);
233 }
234 body.source.instance = drop_instance;
235
236 body.phase = MirPhase::Runtime(RuntimePhase::Initial);
242
243 body
244}
245
246#[tracing::instrument(level = "trace", skip(tcx, transform, body))]
248pub(super) fn create_coroutine_drop_shim_async<'tcx>(
249 tcx: TyCtxt<'tcx>,
250 transform: &TransformVisitor<'tcx>,
251 body: &Body<'tcx>,
252 drop_clean: BasicBlock,
253 can_unwind: bool,
254) -> Body<'tcx> {
255 let mut body = body.clone();
256 let _ = body.coroutine.take();
259
260 FixReturnPendingVisitor { tcx }.visit_body(&mut body);
261
262 if can_unwind {
264 generate_poison_block_and_redirect_unwinds_there(transform, &mut body);
265 }
266
267 let source_info = SourceInfo::outermost(body.span);
268
269 let mut cases = create_cases(&mut body, transform, Operation::AsyncDrop);
270
271 cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean));
272
273 use rustc_middle::mir::AssertKind::ResumedAfterPanic;
274 if can_unwind {
276 cases.insert(
277 1,
278 (
279 CoroutineArgs::POISONED,
280 insert_panic_block(tcx, &mut body, ResumedAfterPanic(transform.coroutine_kind)),
281 ),
282 );
283 }
284
285 let default_block = insert_poll_ready_block(tcx, &mut body);
288 insert_switch(&mut body, cases, transform, default_block);
289
290 for block in body.basic_blocks_mut() {
291 let kind = &mut block.terminator_mut().kind;
292 if let TerminatorKind::CoroutineDrop = *kind {
293 *kind = TerminatorKind::Return;
294 block.statements.push(return_poll_ready_assign(tcx, source_info));
295 }
296 }
297
298 let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, body.span));
300 let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
301 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
302
303 match transform.coroutine_kind {
304 CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
307 make_coroutine_state_argument_indirect(tcx, &mut body);
308 }
309
310 _ => {
311 make_coroutine_state_argument_pinned(tcx, &mut body);
312 }
313 }
314
315 simplify::remove_dead_blocks(&mut body);
318
319 pm::run_passes_no_validate(
320 tcx,
321 &mut body,
322 &[&abort_unwinding_calls::AbortUnwindingCalls],
323 None,
324 );
325
326 deref_finder(tcx, &mut body, false);
328
329 if transform.coroutine_kind.is_async_desugaring() {
330 transform_async_context(tcx, &mut body);
331 }
332
333 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_async", &body) {
334 dumper.dump_mir(&body);
335 }
336
337 body
338}
339
340pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>(
343 tcx: TyCtxt<'tcx>,
344 body: &Body<'tcx>,
345 coroutine_kind: CoroutineKind,
346) -> Body<'tcx> {
347 let mut body = body.clone();
348 let _ = body.coroutine.take();
351 let basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>> = IndexVec::new();
352 body.basic_blocks = BasicBlocks::new(basic_blocks);
353 body.var_debug_info.clear();
354
355 body.local_decls.truncate(1 + body.arg_count);
357
358 let source_info = SourceInfo::outermost(body.span);
359
360 let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, body.span));
362 let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
363 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
364
365 let call_bb = body.basic_blocks_mut().push(BasicBlockData::new(None, false));
367
368 let ret_bb = insert_poll_ready_block(tcx, &mut body);
370
371 let kind = TerminatorKind::Drop {
372 place: Place::from(SELF_ARG),
373 target: ret_bb,
374 unwind: UnwindAction::Continue,
375 replace: false,
376 drop: None,
377 };
378 body.basic_blocks_mut()[call_bb].terminator =
379 Some(Terminator { source_info, kind, attributes: ThinVec::new() });
380
381 deref_finder(tcx, &mut body, false);
383
384 if coroutine_kind.is_async_desugaring() {
385 transform_async_context(tcx, &mut body);
386 }
387
388 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_proxy_async", &body) {
389 dumper.dump_mir(&body);
390 }
391
392 body
393}