Skip to main content

rustc_mir_build/builder/expr/
stmt.rs

1use rustc_middle::middle::region::{self, TempLifetime};
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, LintLevel};
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; // See "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, hir_id, value } => {
29                this.in_scope((region_scope, source_info), LintLevel::Explicit(hir_id), |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                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/expr/stmt.rs:40",
                        "rustc_mir_build::builder::expr::stmt",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/stmt.rs"),
                        ::tracing_core::__macro_support::Option::Some(40u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::stmt"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("stmt_expr Assign block_context.push(SubExpr) : {0:?}",
                                                    expr) as &dyn Value))])
            });
    } else { ; }
};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 = { let BlockAnd(b, v) = this.as_local_rvalue(block, rhs); block = b; v }unpack!(block = this.as_local_rvalue(block, rhs));
47                    let lhs = { let BlockAnd(b, v) = this.as_place(block, lhs); block = b; v }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 = { let BlockAnd(b, v) = this.as_local_rvalue(block, rhs); block = b; v }unpack!(block = this.as_local_rvalue(block, rhs));
52                    let lhs = { let BlockAnd(b, v) = this.as_place(block, lhs); block = b; v }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                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/expr/stmt.rs:70",
                        "rustc_mir_build::builder::expr::stmt",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/stmt.rs"),
                        ::tracing_core::__macro_support::Option::Some(70u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::stmt"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("stmt_expr AssignOp block_context.push(SubExpr) : {0:?}",
                                                    expr) as &dyn Value))])
            });
    } else { ; }
};debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr);
71                this.block_context.push(BlockFrame::SubExpr);
72
73                // As above, RTL.
74                let rhs = { let BlockAnd(b, v) = this.as_local_operand(block, rhs); block = b; v }unpack!(block = this.as_local_operand(block, rhs));
75                let lhs = { let BlockAnd(b, v) = this.as_place(block, lhs); block = b; v }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 = {
    let BlockAnd(b, v) =
        this.build_binary_op(block, op.into(), expr_span, lhs_ty,
            Operand::Copy(lhs), rhs);
    block = b;
    v
}unpack!(
81                    block = this.build_binary_op(
82                        block,
83                        op.into(),
84                        expr_span,
85                        lhs_ty,
86                        Operand::Copy(lhs),
87                        rhs
88                    )
89                );
90                this.cfg.push_assign(block, source_info, lhs, result);
91
92                this.block_context.pop();
93                block.unit()
94            }
95            ExprKind::Continue { label } => {
96                this.break_scope(block, None, BreakableTarget::Continue(label), source_info)
97            }
98            ExprKind::Break { label, value } => {
99                this.break_scope(block, value, BreakableTarget::Break(label), source_info)
100            }
101            ExprKind::ConstContinue { label, value } => {
102                this.break_const_continuable_scope(block, value, label, source_info)
103            }
104            ExprKind::Return { value } => {
105                this.break_scope(block, value, BreakableTarget::Return, source_info)
106            }
107            ExprKind::Become { value } => {
108                let v = &this.thir[value];
109                let ExprKind::Scope { value, hir_id, region_scope } = v.kind else {
110                    ::rustc_middle::util::bug::span_bug_fmt(v.span,
    format_args!("`thir_check_tail_calls` should have disallowed this {0:?}",
        v))span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}")
111                };
112
113                let v = &this.thir[value];
114                let ExprKind::Call { ref args, fun, fn_span, .. } = v.kind else {
115                    ::rustc_middle::util::bug::span_bug_fmt(v.span,
    format_args!("`thir_check_tail_calls` should have disallowed this {0:?}",
        v))span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}")
116                };
117
118                this.in_scope((region_scope, source_info), LintLevel::Explicit(hir_id), |this| {
119                    let fun = { let BlockAnd(b, v) = this.as_local_operand(block, fun); block = b; v }unpack!(block = this.as_local_operand(block, fun));
120                    let args: Box<[_]> = args
121                        .into_iter()
122                        .copied()
123                        .map(|arg| Spanned {
124                            node: { let BlockAnd(b, v) = this.as_local_call_operand(block, arg); block = b; v }unpack!(block = this.as_local_call_operand(block, arg)),
125                            span: this.thir.exprs[arg].span,
126                        })
127                        .collect();
128
129                    this.record_operands_moved(&args);
130
131                    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/expr/stmt.rs:131",
                        "rustc_mir_build::builder::expr::stmt",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/stmt.rs"),
                        ::tracing_core::__macro_support::Option::Some(131u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::stmt"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("expr_into_dest: fn_span={0:?}",
                                                    fn_span) as &dyn Value))])
            });
    } else { ; }
};debug!("expr_into_dest: fn_span={:?}", fn_span);
132
133                    {
    let BlockAnd(b, v) = this.break_for_tail_call(block, &args, source_info);
    block = b;
    v
};unpack!(block = this.break_for_tail_call(block, &args, source_info));
134
135                    this.cfg.terminate(
136                        block,
137                        source_info,
138                        TerminatorKind::TailCall { func: fun, args, fn_span },
139                    );
140
141                    this.cfg.start_new_block().unit()
142                })
143            }
144            _ => {
145                if !statement_scope.is_some() {
    {
        ::core::panicking::panic_fmt(format_args!("Should not be calling `stmt_expr` on a general expression without a statement scope"));
    }
};assert!(
146                    statement_scope.is_some(),
147                    "Should not be calling `stmt_expr` on a general expression \
148                     without a statement scope",
149                );
150
151                // Issue #54382: When creating temp for the value of
152                // expression like:
153                //
154                // `{ side_effects(); { let l = stuff(); the_value } }`
155                //
156                // it is usually better to focus on `the_value` rather
157                // than the entirety of block(s) surrounding it.
158                let adjusted_span = if let ExprKind::Block { block } = expr.kind
159                    && let Some(tail_ex) = this.thir[block].expr
160                {
161                    let mut expr = &this.thir[tail_ex];
162                    loop {
163                        match expr.kind {
164                            ExprKind::Block { block }
165                                if let Some(nested_expr) = this.thir[block].expr =>
166                            {
167                                expr = &this.thir[nested_expr];
168                            }
169                            ExprKind::Scope { value: nested_expr, .. } => {
170                                expr = &this.thir[nested_expr];
171                            }
172                            _ => break,
173                        }
174                    }
175                    this.block_context.push(BlockFrame::TailExpr {
176                        info: BlockTailInfo { tail_result_is_ignored: true, span: expr.span },
177                    });
178                    Some(expr.span)
179                } else {
180                    None
181                };
182
183                let temp = {
    let BlockAnd(b, v) =
        this.as_temp(block,
            TempLifetime {
                temp_lifetime: statement_scope,
                backwards_incompatible: None,
            }, expr_id, Mutability::Not);
    block = b;
    v
}unpack!(
184                    block = this.as_temp(
185                        block,
186                        TempLifetime {
187                            temp_lifetime: statement_scope,
188                            backwards_incompatible: None
189                        },
190                        expr_id,
191                        Mutability::Not
192                    )
193                );
194
195                if let Some(span) = adjusted_span {
196                    this.local_decls[temp].source_info.span = span;
197                    this.block_context.pop();
198                }
199
200                block.unit()
201            }
202        }
203    }
204}