]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_build/src/build/expr/into.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / compiler / rustc_mir_build / src / build / expr / into.rs
CommitLineData
e9174d1e
SL
1//! See docs in build/expr/mod.rs
2
9fa01778
XL
3use crate::build::expr::category::{Category, RvalueFunc};
4use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
3dfed10e 5use rustc_ast::InlineAsmOptions;
60c5eb7d 6use rustc_data_structures::fx::FxHashMap;
3dfed10e 7use rustc_data_structures::stack::ensure_sufficient_stack;
dfeec247 8use rustc_hir as hir;
6a06907d 9use rustc_index::vec::Idx;
ba9703b0 10use rustc_middle::mir::*;
17df50a5 11use rustc_middle::thir::*;
6a06907d 12use rustc_middle::ty::{self, CanonicalUserTypeAnnotation};
cdc7bbd5 13use std::iter;
cc61c64b 14
dc9dc135 15impl<'a, 'tcx> Builder<'a, 'tcx> {
e9174d1e
SL
16 /// Compile `expr`, storing the result into `destination`, which
17 /// is assumed to be uninitialized.
6a06907d 18 crate fn expr_into_dest(
b7449926 19 &mut self,
ba9703b0 20 destination: Place<'tcx>,
b7449926 21 mut block: BasicBlock,
17df50a5 22 expr: &Expr<'tcx>,
b7449926 23 ) -> BlockAnd<()> {
6a06907d 24 debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
e9174d1e
SL
25
26 // since we frequently have to reference `self` from within a
27 // closure, where `self` would be shadowed, it's easier to
28 // just use the name `this` uniformly
29 let this = self;
30 let expr_span = expr.span;
3157f602 31 let source_info = this.source_info(expr_span);
e9174d1e 32
5869c6ff
XL
33 let expr_is_block_or_scope =
34 matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
0bf4aa26
XL
35
36 if !expr_is_block_or_scope {
37 this.block_context.push(BlockFrame::SubExpr);
38 }
39
40 let block_and = match expr.kind {
dfeec247 41 ExprKind::Scope { region_scope, lint_level, value } => {
ea8adc8c 42 let region_scope = (region_scope, source_info);
3dfed10e
XL
43 ensure_sufficient_stack(|| {
44 this.in_scope(region_scope, lint_level, |this| {
17df50a5 45 this.expr_into_dest(destination, block, &this.thir[value])
3dfed10e
XL
46 })
47 })
e9174d1e 48 }
6a06907d 49 ExprKind::Block { body: ref ast_block } => {
cc61c64b 50 this.ast_block(destination, block, ast_block, source_info)
e9174d1e 51 }
17df50a5
XL
52 ExprKind::Match { scrutinee, ref arms } => {
53 this.match_expr(destination, expr_span, block, &this.thir[scrutinee], arms)
e9174d1e 54 }
5869c6ff
XL
55 ExprKind::If { cond, then, else_opt } => {
56 let place = unpack!(
17df50a5
XL
57 block = this.as_temp(
58 block,
59 Some(this.local_scope()),
60 &this.thir[cond],
61 Mutability::Mut
62 )
5869c6ff
XL
63 );
64 let operand = Operand::Move(Place::from(place));
65
66 let mut then_block = this.cfg.start_new_block();
67 let mut else_block = this.cfg.start_new_block();
6a06907d 68 let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block);
5869c6ff
XL
69 this.cfg.terminate(block, source_info, term);
70
17df50a5
XL
71 unpack!(
72 then_block = this.expr_into_dest(destination, then_block, &this.thir[then])
73 );
5869c6ff 74 else_block = if let Some(else_opt) = else_opt {
17df50a5 75 unpack!(this.expr_into_dest(destination, else_block, &this.thir[else_opt]))
5869c6ff
XL
76 } else {
77 // Body of the `if` expression without an `else` clause must return `()`, thus
78 // we implicitly generate a `else {}` if it is not specified.
79 let correct_si = this.source_info(expr_span.shrink_to_hi());
6a06907d 80 this.cfg.push_assign_unit(else_block, correct_si, destination, this.tcx);
5869c6ff
XL
81 else_block
82 };
83
84 let join_block = this.cfg.start_new_block();
85 this.cfg.terminate(
86 then_block,
87 source_info,
88 TerminatorKind::Goto { target: join_block },
89 );
90 this.cfg.terminate(
91 else_block,
92 source_info,
93 TerminatorKind::Goto { target: join_block },
94 );
95
96 join_block.unit()
97 }
5bcae85e 98 ExprKind::NeverToAny { source } => {
17df50a5 99 let source = &this.thir[source];
5869c6ff
XL
100 let is_call =
101 matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
5bcae85e 102
60c5eb7d
XL
103 // (#66975) Source could be a const of type `!`, so has to
104 // exist in the generated MIR.
5869c6ff
XL
105 unpack!(
106 block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut,)
107 );
5bcae85e
SL
108
109 // This is an optimization. If the expression was a call then we already have an
110 // unreachable block. Don't bother to terminate it and create a new one.
111 if is_call {
112 block.unit()
113 } else {
dfeec247 114 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
5bcae85e
SL
115 let end_block = this.cfg.start_new_block();
116 end_block.unit()
117 }
118 }
e9174d1e
SL
119 ExprKind::LogicalOp { op, lhs, rhs } => {
120 // And:
121 //
cdc7bbd5
XL
122 // [block: If(lhs)] -true-> [else_block: dest = (rhs)]
123 // | (false)
124 // [shortcurcuit_block: dest = false]
e9174d1e
SL
125 //
126 // Or:
127 //
cdc7bbd5
XL
128 // [block: If(lhs)] -false-> [else_block: dest = (rhs)]
129 // | (true)
130 // [shortcurcuit_block: dest = true]
e9174d1e 131
cdc7bbd5 132 let (shortcircuit_block, mut else_block, join_block) = (
b7449926
XL
133 this.cfg.start_new_block(),
134 this.cfg.start_new_block(),
135 this.cfg.start_new_block(),
136 );
e9174d1e 137
17df50a5 138 let lhs = unpack!(block = this.as_local_operand(block, &this.thir[lhs]));
e9174d1e 139 let blocks = match op {
cdc7bbd5
XL
140 LogicalOp::And => (else_block, shortcircuit_block),
141 LogicalOp::Or => (shortcircuit_block, else_block),
e9174d1e 142 };
6a06907d 143 let term = TerminatorKind::if_(this.tcx, lhs, blocks.0, blocks.1);
8bb4bdeb 144 this.cfg.terminate(block, source_info, term);
e9174d1e 145
e9174d1e 146 this.cfg.push_assign_constant(
cdc7bbd5 147 shortcircuit_block,
b7449926
XL
148 source_info,
149 destination,
6a06907d
XL
150 Constant {
151 span: expr_span,
152 user_ty: None,
cdc7bbd5
XL
153 literal: match op {
154 LogicalOp::And => ty::Const::from_bool(this.tcx, false).into(),
155 LogicalOp::Or => ty::Const::from_bool(this.tcx, true).into(),
156 },
6a06907d 157 },
b7449926 158 );
cdc7bbd5 159 this.cfg.goto(shortcircuit_block, source_info, join_block);
416331ca 160
17df50a5 161 let rhs = unpack!(else_block = this.as_local_operand(else_block, &this.thir[rhs]));
cdc7bbd5
XL
162 this.cfg.push_assign(else_block, source_info, destination, Rvalue::Use(rhs));
163 this.cfg.goto(else_block, source_info, join_block);
e9174d1e
SL
164
165 join_block.unit()
166 }
416331ca 167 ExprKind::Loop { body } => {
2c00a5a8
XL
168 // [block]
169 // |
170 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
171 // | ^ |
172 // false link | |
173 // | +-----------------------------------------+
174 // +-> [diverge_cleanup]
175 // The false link is required to make sure borrowck considers unwinds through the
176 // body, even when the exact code in the body cannot unwind
e9174d1e
SL
177
178 let loop_block = this.cfg.start_new_block();
e9174d1e 179
dfeec247
XL
180 // Start the loop.
181 this.cfg.goto(block, source_info, loop_block);
e9174d1e 182
29967ef6 183 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
ba9703b0
XL
184 // conduct the test, if necessary
185 let body_block = this.cfg.start_new_block();
ba9703b0
XL
186 this.cfg.terminate(
187 loop_block,
188 source_info,
29967ef6 189 TerminatorKind::FalseUnwind { real_target: body_block, unwind: None },
ba9703b0 190 );
29967ef6 191 this.diverge_from(loop_block);
ba9703b0
XL
192
193 // The “return” value of the loop body must always be an unit. We therefore
194 // introduce a unit temporary as the destination for the loop body.
195 let tmp = this.get_unit_temp();
196 // Execute the body, branching back to the test.
17df50a5
XL
197 let body_block_end =
198 unpack!(this.expr_into_dest(tmp, body_block, &this.thir[body]));
ba9703b0 199 this.cfg.goto(body_block_end, source_info, loop_block);
29967ef6
XL
200
201 // Loops are only exited by `break` expressions.
202 None
203 })
e9174d1e 204 }
17df50a5
XL
205 ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
206 let fun = unpack!(block = this.as_local_operand(block, &this.thir[fun]));
5869c6ff
XL
207 let args: Vec<_> = args
208 .into_iter()
17df50a5
XL
209 .copied()
210 .map(|arg| unpack!(block = this.as_local_call_operand(block, &this.thir[arg])))
5869c6ff 211 .collect();
cc61c64b 212
5869c6ff 213 let success = this.cfg.start_new_block();
e1599b0c 214
5869c6ff 215 this.record_operands_moved(&args);
e1599b0c 216
6a06907d 217 debug!("expr_into_dest: fn_span={:?}", fn_span);
f035d41b 218
5869c6ff
XL
219 this.cfg.terminate(
220 block,
221 source_info,
222 TerminatorKind::Call {
223 func: fun,
224 args,
225 cleanup: None,
226 // FIXME(varkor): replace this with an uninhabitedness-based check.
227 // This requires getting access to the current module to call
228 // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
229 destination: if expr.ty.is_never() {
230 None
231 } else {
232 Some((destination, success))
b7449926 233 },
5869c6ff
XL
234 from_hir_call,
235 fn_span,
236 },
237 );
238 this.diverge_from(block);
239 success.unit()
e9174d1e 240 }
17df50a5 241 ExprKind::Use { source } => this.expr_into_dest(destination, block, &this.thir[source]),
60c5eb7d 242 ExprKind::Borrow { arg, borrow_kind } => {
17df50a5 243 let arg = &this.thir[arg];
60c5eb7d
XL
244 // We don't do this in `as_rvalue` because we use `as_place`
245 // for borrow expressions, so we cannot create an `RValue` that
246 // remains valid across user code. `as_rvalue` is usually called
247 // by this method anyway, so this shouldn't cause too many
248 // unnecessary temporaries.
249 let arg_place = match borrow_kind {
250 BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
251 _ => unpack!(block = this.as_place(block, arg)),
252 };
6a06907d 253 let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
60c5eb7d
XL
254 this.cfg.push_assign(block, source_info, destination, borrow);
255 block.unit()
256 }
dfeec247 257 ExprKind::AddressOf { mutability, arg } => {
17df50a5 258 let arg = &this.thir[arg];
dfeec247
XL
259 let place = match mutability {
260 hir::Mutability::Not => this.as_read_only_place(block, arg),
261 hir::Mutability::Mut => this.as_place(block, arg),
262 };
263 let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
264 this.cfg.push_assign(block, source_info, destination, address_of);
265 block.unit()
266 }
136023e0
XL
267 ExprKind::Adt(box Adt {
268 adt_def,
269 variant_index,
270 substs,
271 user_ty,
272 ref fields,
273 ref base,
274 }) => {
60c5eb7d
XL
275 // See the notes for `ExprKind::Array` in `as_rvalue` and for
276 // `ExprKind::Borrow` above.
277 let is_union = adt_def.is_union();
dfeec247 278 let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
60c5eb7d 279
dfeec247 280 let scope = this.local_scope();
60c5eb7d
XL
281
282 // first process the set of fields that were provided
283 // (evaluating them in order given by user)
284 let fields_map: FxHashMap<_, _> = fields
285 .into_iter()
17df50a5
XL
286 .map(|f| {
287 (
288 f.name,
289 unpack!(
290 block = this.as_operand(block, Some(scope), &this.thir[f.expr])
291 ),
292 )
293 })
dfeec247 294 .collect();
60c5eb7d 295
6a06907d
XL
296 let field_names: Vec<_> =
297 (0..adt_def.variants[variant_index].fields.len()).map(Field::new).collect();
60c5eb7d 298
fc512014 299 let fields: Vec<_> = if let Some(FruInfo { base, field_types }) = base {
17df50a5
XL
300 let place_builder =
301 unpack!(block = this.as_place_builder(block, &this.thir[*base]));
ba9703b0
XL
302
303 // MIR does not natively support FRU, so for each
304 // base-supplied field, generate an operand that
305 // reads it from the base.
17df50a5 306 iter::zip(field_names, &**field_types)
ba9703b0
XL
307 .map(|(n, ty)| match fields_map.get(&n) {
308 Some(v) => v.clone(),
5869c6ff
XL
309 None => {
310 let place_builder = place_builder.clone();
311 this.consume_by_copy_or_move(
312 place_builder
313 .field(n, ty)
6a06907d 314 .into_place(this.tcx, this.typeck_results),
5869c6ff
XL
315 )
316 }
ba9703b0
XL
317 })
318 .collect()
319 } else {
320 field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
321 };
60c5eb7d
XL
322
323 let inferred_ty = expr.ty;
324 let user_ty = user_ty.map(|ty| {
325 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
326 span: source_info.span,
327 user_ty: ty,
328 inferred_ty,
329 })
330 });
331 let adt = box AggregateKind::Adt(
332 adt_def,
333 variant_index,
334 substs,
335 user_ty,
336 active_field_index,
337 );
338 this.cfg.push_assign(
339 block,
340 source_info,
341 destination,
dfeec247 342 Rvalue::Aggregate(adt, fields),
60c5eb7d
XL
343 );
344 block.unit()
345 }
17df50a5
XL
346 ExprKind::InlineAsm { template, ref operands, options, line_spans } => {
347 use rustc_middle::{mir, thir};
f9f354fc
XL
348 let operands = operands
349 .into_iter()
6a06907d 350 .map(|op| match *op {
3dfed10e 351 thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
f9f354fc 352 reg,
17df50a5 353 value: unpack!(block = this.as_local_operand(block, &this.thir[expr])),
f9f354fc 354 },
3dfed10e 355 thir::InlineAsmOperand::Out { reg, late, expr } => {
f9f354fc
XL
356 mir::InlineAsmOperand::Out {
357 reg,
358 late,
17df50a5
XL
359 place: expr.map(|expr| {
360 unpack!(block = this.as_place(block, &this.thir[expr]))
361 }),
f9f354fc
XL
362 }
363 }
3dfed10e 364 thir::InlineAsmOperand::InOut { reg, late, expr } => {
17df50a5 365 let place = unpack!(block = this.as_place(block, &this.thir[expr]));
f9f354fc
XL
366 mir::InlineAsmOperand::InOut {
367 reg,
368 late,
369 // This works because asm operands must be Copy
370 in_value: Operand::Copy(place),
371 out_place: Some(place),
372 }
373 }
3dfed10e 374 thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
f9f354fc
XL
375 mir::InlineAsmOperand::InOut {
376 reg,
377 late,
17df50a5
XL
378 in_value: unpack!(
379 block = this.as_local_operand(block, &this.thir[in_expr])
380 ),
381 out_place: out_expr.map(|out_expr| {
382 unpack!(block = this.as_place(block, &this.thir[out_expr]))
f9f354fc
XL
383 }),
384 }
385 }
cdc7bbd5
XL
386 thir::InlineAsmOperand::Const { value, span } => {
387 mir::InlineAsmOperand::Const {
388 value: box Constant { span, user_ty: None, literal: value.into() },
389 }
390 }
17df50a5
XL
391 thir::InlineAsmOperand::SymFn { expr } => mir::InlineAsmOperand::SymFn {
392 value: box this.as_constant(&this.thir[expr]),
393 },
3dfed10e 394 thir::InlineAsmOperand::SymStatic { def_id } => {
f035d41b 395 mir::InlineAsmOperand::SymStatic { def_id }
f9f354fc
XL
396 }
397 })
398 .collect();
399
400 let destination = this.cfg.start_new_block();
401
402 this.cfg.terminate(
403 block,
404 source_info,
405 TerminatorKind::InlineAsm {
406 template,
407 operands,
408 options,
409 line_spans,
410 destination: if options.contains(InlineAsmOptions::NORETURN) {
411 None
412 } else {
413 Some(destination)
414 },
415 },
416 );
417 destination.unit()
418 }
60c5eb7d 419
a7813a04 420 // These cases don't actually need a destination
b7449926
XL
421 ExprKind::Assign { .. }
422 | ExprKind::AssignOp { .. }
3dfed10e 423 | ExprKind::LlvmInlineAsm { .. } => {
a1dfa0c6 424 unpack!(block = this.stmt_expr(block, expr, None));
6a06907d 425 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
ff7c6d11 426 block.unit()
a7813a04
XL
427 }
428
3dfed10e
XL
429 ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => {
430 unpack!(block = this.stmt_expr(block, expr, None));
431 // No assign, as these have type `!`.
432 block.unit()
433 }
434
8faf50e0 435 // Avoid creating a temporary
dfeec247 436 ExprKind::VarRef { .. }
fc512014 437 | ExprKind::UpvarRef { .. }
dfeec247
XL
438 | ExprKind::PlaceTypeAscription { .. }
439 | ExprKind::ValueTypeAscription { .. } => {
8faf50e0
XL
440 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
441
442 let place = unpack!(block = this.as_place(block, expr));
443 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
dfeec247 444 this.cfg.push_assign(block, source_info, destination, rvalue);
8faf50e0
XL
445 block.unit()
446 }
b7449926 447 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
6a06907d 448 debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
8faf50e0
XL
449
450 // Create a "fake" temporary variable so that we check that the
451 // value is Sized. Usually, this is caught in type checking, but
452 // in the case of box expr there is no such check.
e1599b0c 453 if !destination.projection.is_empty() {
f9f354fc 454 this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
8faf50e0
XL
455 }
456
8faf50e0
XL
457 let place = unpack!(block = this.as_place(block, expr));
458 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
dfeec247 459 this.cfg.push_assign(block, source_info, destination, rvalue);
8faf50e0
XL
460 block.unit()
461 }
462
74b04a01
XL
463 ExprKind::Yield { value } => {
464 let scope = this.local_scope();
17df50a5 465 let value = unpack!(block = this.as_operand(block, Some(scope), &this.thir[value]));
74b04a01 466 let resume = this.cfg.start_new_block();
74b04a01
XL
467 this.cfg.terminate(
468 block,
469 source_info,
29967ef6 470 TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
74b04a01 471 );
29967ef6 472 this.generator_drop_cleanup(block);
74b04a01
XL
473 resume.unit()
474 }
475
e9174d1e 476 // these are the cases that are more naturally handled by some other mode
b7449926
XL
477 ExprKind::Unary { .. }
478 | ExprKind::Binary { .. }
479 | ExprKind::Box { .. }
480 | ExprKind::Cast { .. }
48663c56 481 | ExprKind::Pointer { .. }
b7449926 482 | ExprKind::Repeat { .. }
b7449926
XL
483 | ExprKind::Array { .. }
484 | ExprKind::Tuple { .. }
b7449926 485 | ExprKind::Closure { .. }
29967ef6 486 | ExprKind::ConstBlock { .. }
b7449926 487 | ExprKind::Literal { .. }
f9f354fc 488 | ExprKind::ThreadLocalRef(_)
74b04a01 489 | ExprKind::StaticRef { .. } => {
e9174d1e 490 debug_assert!(match Category::of(&expr.kind).unwrap() {
0bf4aa26 491 // should be handled above
e9174d1e 492 Category::Rvalue(RvalueFunc::Into) => false,
0bf4aa26
XL
493
494 // must be handled above or else we get an
495 // infinite loop in the builder; see
0731742a 496 // e.g., `ExprKind::VarRef` above
0bf4aa26
XL
497 Category::Place => false,
498
e9174d1e
SL
499 _ => true,
500 });
501
8bb4bdeb 502 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
0731742a 503 this.cfg.push_assign(block, source_info, destination, rvalue);
e9174d1e
SL
504 block.unit()
505 }
0bf4aa26
XL
506 };
507
508 if !expr_is_block_or_scope {
509 let popped = this.block_context.pop();
510 assert!(popped.is_some());
e9174d1e 511 }
0bf4aa26
XL
512
513 block_and
e9174d1e 514 }
e9174d1e 515}