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