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