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 if let Some(local) = place.as_local()
96 && local == SELF_ARG
97 {
98 (target, unwind, source_info, *drop)
99 } else {
100 continue;
101 }
102 }
103 _ => continue,
104 };
105 let unwind = if block_data.is_cleanup {
106 Unwind::InCleanup
107 } else {
108 Unwind::To(match *unwind {
109 UnwindAction::Cleanup(tgt) => tgt,
110 UnwindAction::Continue => elaborator.patch.resume_block(),
111 UnwindAction::Unreachable => elaborator.patch.unreachable_cleanup_block(),
112 UnwindAction::Terminate(reason) => elaborator.patch.terminate_block(reason),
113 })
114 };
115 elaborate_drop(
116 &mut elaborator,
117 *source_info,
118 Place::from(SELF_ARG),
119 (),
120 *target,
121 unwind,
122 block,
123 dropline,
124 );
125 }
126 elaborator.patch.apply(body);
127}
128
129#[tracing::instrument(level = "trace", skip(tcx, body), ret)]
130pub(super) fn insert_clean_drop<'tcx>(
131 tcx: TyCtxt<'tcx>,
132 body: &mut Body<'tcx>,
133 has_async_drops: bool,
134) -> BasicBlock {
135 let return_block = if has_async_drops {
136 insert_poll_ready_block(tcx, body)
137 } else {
138 insert_term_block(body, TerminatorKind::Return)
139 };
140
141 let dropline = None;
145
146 let term = TerminatorKind::Drop {
147 place: Place::from(SELF_ARG),
148 target: return_block,
149 unwind: UnwindAction::Continue,
150 replace: false,
151 drop: dropline,
152 };
153
154 insert_term_block(body, term)
156}
157
158#[tracing::instrument(level = "trace", skip(tcx, transform, body))]
159pub(super) fn create_coroutine_drop_shim<'tcx>(
160 tcx: TyCtxt<'tcx>,
161 transform: &TransformVisitor<'tcx>,
162 coroutine_ty: Ty<'tcx>,
163 body: &Body<'tcx>,
164 drop_clean: BasicBlock,
165) -> Body<'tcx> {
166 let mut body = body.clone();
167 let _ = body.coroutine.take();
170 body.arg_count = 1;
173
174 let source_info = SourceInfo::outermost(body.span);
175
176 let mut cases = create_cases(&mut body, transform, Operation::Drop);
177
178 cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean));
179
180 let default_block = insert_term_block(&mut body, TerminatorKind::Return);
184 insert_switch(&mut body, cases, transform, default_block);
185
186 for block in body.basic_blocks_mut() {
187 let kind = &mut block.terminator_mut().kind;
188 if let TerminatorKind::CoroutineDrop = *kind {
189 *kind = TerminatorKind::Return;
190 }
191 }
192
193 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info);
195
196 make_coroutine_state_argument_indirect(tcx, &mut body);
197
198 simplify::remove_dead_blocks(&mut body);
201
202 deref_finder(tcx, &mut body, false);
204
205 let coroutine_instance = body.source.instance;
207 let drop_glue = tcx.require_lang_item(LangItem::DropGlue, body.span);
208 let drop_instance = InstanceKind::DropGlue(drop_glue, Some(coroutine_ty));
209
210 body.source.instance = coroutine_instance;
213 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop", &body) {
214 dumper.dump_mir(&body);
215 }
216 body.source.instance = drop_instance;
217
218 body.phase = MirPhase::Runtime(RuntimePhase::Initial);
224
225 body
226}
227
228#[tracing::instrument(level = "trace", skip(tcx, transform, body))]
230pub(super) fn create_coroutine_drop_shim_async<'tcx>(
231 tcx: TyCtxt<'tcx>,
232 transform: &TransformVisitor<'tcx>,
233 body: &Body<'tcx>,
234 drop_clean: BasicBlock,
235 can_unwind: bool,
236) -> Body<'tcx> {
237 let mut body = body.clone();
238 let _ = body.coroutine.take();
241
242 FixReturnPendingVisitor { tcx }.visit_body(&mut body);
243
244 if can_unwind {
246 generate_poison_block_and_redirect_unwinds_there(transform, &mut body);
247 }
248
249 let source_info = SourceInfo::outermost(body.span);
250
251 let mut cases = create_cases(&mut body, transform, Operation::AsyncDrop);
252
253 cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean));
254
255 use rustc_middle::mir::AssertKind::ResumedAfterPanic;
256 if can_unwind {
258 cases.insert(
259 1,
260 (
261 CoroutineArgs::POISONED,
262 insert_panic_block(tcx, &mut body, ResumedAfterPanic(transform.coroutine_kind)),
263 ),
264 );
265 }
266
267 let default_block = insert_poll_ready_block(tcx, &mut body);
270 insert_switch(&mut body, cases, transform, default_block);
271
272 for block in body.basic_blocks_mut() {
273 let kind = &mut block.terminator_mut().kind;
274 if let TerminatorKind::CoroutineDrop = *kind {
275 *kind = TerminatorKind::Return;
276 block.statements.push(return_poll_ready_assign(tcx, source_info));
277 }
278 }
279
280 let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, body.span));
282 let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
283 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
284
285 match transform.coroutine_kind {
286 CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
289 make_coroutine_state_argument_indirect(tcx, &mut body);
290 }
291
292 _ => {
293 make_coroutine_state_argument_pinned(tcx, &mut body);
294 }
295 }
296
297 simplify::remove_dead_blocks(&mut body);
300
301 pm::run_passes_no_validate(
302 tcx,
303 &mut body,
304 &[&abort_unwinding_calls::AbortUnwindingCalls],
305 None,
306 );
307
308 deref_finder(tcx, &mut body, false);
310
311 if transform.coroutine_kind.is_async_desugaring() {
312 transform_async_context(tcx, &mut body);
313 }
314
315 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_async", &body) {
316 dumper.dump_mir(&body);
317 }
318
319 body
320}
321
322pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>(
325 tcx: TyCtxt<'tcx>,
326 body: &Body<'tcx>,
327 coroutine_kind: CoroutineKind,
328) -> Body<'tcx> {
329 let mut body = body.clone();
330 let _ = body.coroutine.take();
333 let basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>> = IndexVec::new();
334 body.basic_blocks = BasicBlocks::new(basic_blocks);
335 body.var_debug_info.clear();
336
337 body.local_decls.truncate(1 + body.arg_count);
339
340 let source_info = SourceInfo::outermost(body.span);
341
342 let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, body.span));
344 let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
345 body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
346
347 let call_bb = body.basic_blocks_mut().push(BasicBlockData::new(None, false));
349
350 let ret_bb = insert_poll_ready_block(tcx, &mut body);
352
353 let kind = TerminatorKind::Drop {
354 place: Place::from(SELF_ARG),
355 target: ret_bb,
356 unwind: UnwindAction::Continue,
357 replace: false,
358 drop: None,
359 };
360 body.basic_blocks_mut()[call_bb].terminator = Some(Terminator { source_info, kind });
361
362 deref_finder(tcx, &mut body, false);
364
365 if coroutine_kind.is_async_desugaring() {
366 transform_async_context(tcx, &mut body);
367 }
368
369 if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_proxy_async", &body) {
370 dumper.dump_mir(&body);
371 }
372
373 body
374}