1 use crate::build::scope::BreakableTarget; 2 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; 3 use rustc_middle::middle::region; 4 use rustc_middle::mir::*; 5 use rustc_middle::thir::*; 6 7 impl<'a, 'tcx> Builder<'a, 'tcx> { 8 /// Builds a block of MIR statements to evaluate the THIR `expr`. 9 /// 10 /// The `statement_scope` is used if a statement temporary must be dropped. stmt_expr( &mut self, mut block: BasicBlock, expr: &Expr<'tcx>, statement_scope: Option<region::Scope>, ) -> BlockAnd<()>11 pub(crate) fn stmt_expr( 12 &mut self, 13 mut block: BasicBlock, 14 expr: &Expr<'tcx>, 15 statement_scope: Option<region::Scope>, 16 ) -> BlockAnd<()> { 17 let this = self; 18 let expr_span = expr.span; 19 let source_info = this.source_info(expr.span); 20 // Handle a number of expressions that don't need a destination at all. This 21 // avoids needing a mountain of temporary `()` variables. 22 match expr.kind { 23 ExprKind::Scope { region_scope, lint_level, value } => { 24 this.in_scope((region_scope, source_info), lint_level, |this| { 25 this.stmt_expr(block, &this.thir[value], statement_scope) 26 }) 27 } 28 ExprKind::Assign { lhs, rhs } => { 29 let lhs = &this.thir[lhs]; 30 let rhs = &this.thir[rhs]; 31 let lhs_span = lhs.span; 32 33 // Note: we evaluate assignments right-to-left. This 34 // is better for borrowck interaction with overloaded 35 // operators like x[j] = x[i]. 36 37 debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr); 38 this.block_context.push(BlockFrame::SubExpr); 39 40 // Generate better code for things that don't need to be 41 // dropped. 42 if lhs.ty.needs_drop(this.tcx, this.param_env) { 43 let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); 44 let lhs = unpack!(block = this.as_place(block, lhs)); 45 unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs)); 46 } else { 47 let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); 48 let lhs = unpack!(block = this.as_place(block, lhs)); 49 this.cfg.push_assign(block, source_info, lhs, rhs); 50 } 51 52 this.block_context.pop(); 53 block.unit() 54 } 55 ExprKind::AssignOp { op, lhs, rhs } => { 56 // FIXME(#28160) there is an interesting semantics 57 // question raised here -- should we "freeze" the 58 // value of the lhs here? I'm inclined to think not, 59 // since it seems closer to the semantics of the 60 // overloaded version, which takes `&mut self`. This 61 // only affects weird things like `x += {x += 1; x}` 62 // -- is that equal to `x + (x + 1)` or `2*(x+1)`? 63 64 let lhs = &this.thir[lhs]; 65 let rhs = &this.thir[rhs]; 66 let lhs_ty = lhs.ty; 67 68 debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr); 69 this.block_context.push(BlockFrame::SubExpr); 70 71 // As above, RTL. 72 let rhs = unpack!(block = this.as_local_operand(block, rhs)); 73 let lhs = unpack!(block = this.as_place(block, lhs)); 74 75 // we don't have to drop prior contents or anything 76 // because AssignOp is only legal for Copy types 77 // (overloaded ops should be desugared into a call). 78 let result = unpack!( 79 block = 80 this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs) 81 ); 82 this.cfg.push_assign(block, source_info, lhs, result); 83 84 this.block_context.pop(); 85 block.unit() 86 } 87 ExprKind::Continue { label } => { 88 this.break_scope(block, None, BreakableTarget::Continue(label), source_info) 89 } 90 ExprKind::Break { label, value } => this.break_scope( 91 block, 92 value.map(|value| &this.thir[value]), 93 BreakableTarget::Break(label), 94 source_info, 95 ), 96 ExprKind::Return { value } => this.break_scope( 97 block, 98 value.map(|value| &this.thir[value]), 99 BreakableTarget::Return, 100 source_info, 101 ), 102 // FIXME(explicit_tail_calls): properly lower tail calls here 103 ExprKind::Become { value } => this.break_scope( 104 block, 105 Some(&this.thir[value]), 106 BreakableTarget::Return, 107 source_info, 108 ), 109 _ => { 110 assert!( 111 statement_scope.is_some(), 112 "Should not be calling `stmt_expr` on a general expression \ 113 without a statement scope", 114 ); 115 116 // Issue #54382: When creating temp for the value of 117 // expression like: 118 // 119 // `{ side_effects(); { let l = stuff(); the_value } }` 120 // 121 // it is usually better to focus on `the_value` rather 122 // than the entirety of block(s) surrounding it. 123 let adjusted_span = 124 if let ExprKind::Block { block } = expr.kind 125 && let Some(tail_ex) = this.thir[block].expr 126 { 127 let mut expr = &this.thir[tail_ex]; 128 loop { 129 match expr.kind { 130 ExprKind::Block { block } 131 if let Some(nested_expr) = this.thir[block].expr => 132 { 133 expr = &this.thir[nested_expr]; 134 } 135 ExprKind::Scope { value: nested_expr, .. } => { 136 expr = &this.thir[nested_expr]; 137 } 138 _ => break, 139 } 140 } 141 this.block_context.push(BlockFrame::TailExpr { 142 tail_result_is_ignored: true, 143 span: expr.span, 144 }); 145 Some(expr.span) 146 } else { 147 None 148 }; 149 150 let temp = 151 unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not)); 152 153 if let Some(span) = adjusted_span { 154 this.local_decls[temp].source_info.span = span; 155 this.block_context.pop(); 156 } 157 158 block.unit() 159 } 160 } 161 } 162 } 163