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