rustc_mir_build/builder/expr/
stmt.rs

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