1use rustc_ast::{AsmMacro, InlineAsmOptions};
4use rustc_data_structures::fx::FxHashMap;
5use rustc_data_structures::stack::ensure_sufficient_stack;
6use rustc_hir as hir;
7use rustc_hir::lang_items::LangItem;
8use rustc_middle::mir::*;
9use rustc_middle::span_bug;
10use rustc_middle::thir::*;
11use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
12use rustc_span::DUMMY_SP;
13use rustc_span::source_map::Spanned;
14use rustc_trait_selection::infer::InferCtxtExt;
15use tracing::{debug, instrument};
16
17use crate::builder::expr::category::{Category, RvalueFunc};
18use crate::builder::matches::{DeclareLetBindings, HasMatchGuard};
19use crate::builder::scope::LintLevel;
20use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
21use crate::errors::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};
22
23impl<'a, 'tcx> Builder<'a, 'tcx> {
24 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("expr_into_dest",
"rustc_mir_build::builder::expr::into",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/into.rs"),
::tracing_core::__macro_support::Option::Some(26u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::into"),
::tracing_core::field::FieldSet::new(&["destination",
"block", "expr_id"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&destination)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&block)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr_id)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: BlockAnd<()> = loop {};
return __tracing_attr_fake_return;
}
{
let this = self;
let expr = &this.thir[expr_id];
let expr_span = expr.span;
let source_info = this.source_info(expr_span);
let expr_is_block_or_scope =
#[allow(non_exhaustive_omitted_patterns)] match expr.kind {
ExprKind::Block { .. } | ExprKind::Scope { .. } => true,
_ => false,
};
if !expr_is_block_or_scope {
this.block_context.push(BlockFrame::SubExpr);
}
let block_and =
match expr.kind {
ExprKind::Scope { region_scope, hir_id, value } => {
let region_scope = (region_scope, source_info);
ensure_sufficient_stack(||
{
this.in_scope(region_scope, LintLevel::Explicit(hir_id),
|this| { this.expr_into_dest(destination, block, value) })
})
}
ExprKind::Block { block: ast_block } => {
this.ast_block(destination, block, ast_block, source_info)
}
ExprKind::Match { scrutinee, ref arms, .. } =>
this.match_expr(destination, block, scrutinee, arms,
expr_span, this.thir[scrutinee].span),
ExprKind::If { cond, then, else_opt, if_then_scope } => {
let then_span = this.thir[then].span;
let then_source_info = this.source_info(then_span);
let condition_scope = this.local_scope();
let then_and_else_blocks =
this.in_scope((if_then_scope, then_source_info),
LintLevel::Inherited,
|this|
{
let source_info =
if this.is_let(cond) {
let variable_scope =
this.new_source_scope(then_span, LintLevel::Inherited);
this.source_scope = variable_scope;
SourceInfo { span: then_span, scope: variable_scope }
} else { this.source_info(then_span) };
let (then_block, else_block) =
this.in_if_then_scope(condition_scope, then_span,
|this|
{
let then_blk =
this.then_else_break(block, cond, Some(condition_scope),
source_info, DeclareLetBindings::Yes).into_block();
this.expr_into_dest(destination, then_blk, then)
});
then_block.and(else_block)
});
let (then_blk, mut else_blk);
else_blk =
{
let BlockAnd(b, v) = then_and_else_blocks;
then_blk = b;
v
};
if let Some(else_expr) = else_opt {
else_blk =
this.expr_into_dest(destination, else_blk,
else_expr).into_block();
} else {
let correct_si = this.source_info(expr_span.shrink_to_hi());
this.cfg.push_assign_unit(else_blk, correct_si, destination,
this.tcx);
}
let join_block = this.cfg.start_new_block();
this.cfg.goto(then_blk, source_info, join_block);
this.cfg.goto(else_blk, source_info, join_block);
join_block.unit()
}
ExprKind::Let { .. } => {
::rustc_middle::util::bug::span_bug_fmt(expr_span,
format_args!("unexpected let expression outside of if or match-guard"));
}
ExprKind::NeverToAny { source } => {
let source_expr = &this.thir[source];
let is_call =
#[allow(non_exhaustive_omitted_patterns)] match source_expr.kind
{
ExprKind::Call { .. } | ExprKind::InlineAsm { .. } => true,
_ => false,
};
{
let BlockAnd(b, v) =
this.as_temp(block, this.local_temp_lifetime(), source,
Mutability::Mut);
block = b;
v
};
if is_call {
block.unit()
} else {
this.cfg.terminate(block, source_info,
TerminatorKind::Unreachable);
let end_block = this.cfg.start_new_block();
end_block.unit()
}
}
ExprKind::LogicalOp { op, lhs, rhs } => {
let condition_scope = this.local_scope();
let source_info = this.source_info(expr.span);
let (then_block, else_block) =
this.in_if_then_scope(condition_scope, expr.span,
|this|
{
this.then_else_break(block, lhs, Some(condition_scope),
source_info, DeclareLetBindings::LetNotPermitted)
});
let (short_circuit, continuation, constant) =
match op {
LogicalOp::And => (else_block, then_block, false),
LogicalOp::Or => (then_block, else_block, true),
};
this.cfg.push_assign_constant(short_circuit, source_info,
destination,
ConstOperand {
span: expr.span,
user_ty: None,
const_: Const::from_bool(this.tcx, constant),
});
let mut rhs_block =
this.expr_into_dest(destination, continuation,
rhs).into_block();
this.visit_coverage_standalone_condition(rhs, destination,
&mut rhs_block);
let target = this.cfg.start_new_block();
this.cfg.goto(rhs_block, source_info, target);
this.cfg.goto(short_circuit, source_info, target);
target.unit()
}
ExprKind::Loop { body } => {
let loop_block = this.cfg.start_new_block();
this.cfg.goto(block, source_info, loop_block);
this.in_breakable_scope(Some(loop_block), destination,
expr_span,
move |this|
{
let body_block = this.cfg.start_new_block();
this.cfg.terminate(loop_block, source_info,
TerminatorKind::FalseUnwind {
real_target: body_block,
unwind: UnwindAction::Continue,
});
this.diverge_from(loop_block);
let tmp = this.get_unit_temp();
let body_block_end =
this.expr_into_dest(tmp, body_block, body).into_block();
this.cfg.goto(body_block_end, source_info, loop_block);
None
})
}
ExprKind::LoopMatch {
state,
region_scope,
match_data: box LoopMatchMatchData {
box ref arms, span: match_span, scrutinee
} } => {
fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
match ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool |
ty::Char => true,
ty::Adt(adt_def, _) =>
match adt_def.adt_kind() {
ty::AdtKind::Struct | ty::AdtKind::Union => false,
ty::AdtKind::Enum => {
adt_def.variants().iter().all(|v| v.fields.is_empty())
}
},
_ => false,
}
}
let state_ty = this.thir.exprs[state].ty;
if !is_supported_loop_match_type(state_ty) {
let span = this.thir.exprs[state].span;
this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType {
span,
ty: state_ty,
})
}
let loop_block = this.cfg.start_new_block();
this.cfg.goto(block, source_info, loop_block);
this.in_breakable_scope(Some(loop_block), destination,
expr_span,
|this|
{
let mut body_block = this.cfg.start_new_block();
this.cfg.terminate(loop_block, source_info,
TerminatorKind::FalseUnwind {
real_target: body_block,
unwind: UnwindAction::Continue,
});
this.diverge_from(loop_block);
let scrutinee_span = this.thir.exprs[scrutinee].span;
let scrutinee_place_builder =
{
let BlockAnd(b, v) =
this.lower_scrutinee(body_block, scrutinee, scrutinee_span);
body_block = b;
v
};
let match_start_span =
match_span.shrink_to_lo().to(scrutinee_span);
let mut patterns = Vec::with_capacity(arms.len());
for &arm_id in arms.iter() {
let arm = &this.thir[arm_id];
if let Some(guard) = arm.guard {
let span = this.thir.exprs[guard].span;
this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
}
patterns.push((&*arm.pattern, HasMatchGuard::No));
}
let built_tree =
this.lower_match_tree(body_block, scrutinee_span,
&scrutinee_place_builder, match_start_span, patterns,
false);
let state_place = scrutinee_place_builder.to_place(this);
{
let BlockAnd(b, v) =
this.in_scope((region_scope, source_info),
LintLevel::Inherited,
move |this|
{
this.in_breakable_scope(None, state_place, expr_span,
|this|
{
Some(this.in_const_continuable_scope(Box::from(arms),
built_tree.clone(), state_place, expr_span,
|this|
{
this.lower_match_arms(state_place, scrutinee_place_builder,
scrutinee_span, arms, built_tree,
this.source_info(match_span))
}))
})
});
body_block = b;
v
};
this.cfg.goto(body_block, source_info, loop_block);
None
})
}
ExprKind::Call {
ty: _, fun, ref args, from_hir_call, fn_span } => {
let fun =
{
let BlockAnd(b, v) = this.as_local_operand(block, fun);
block = b;
v
};
let args: Box<[_]> =
args.into_iter().copied().map(|arg|
Spanned {
node: {
let BlockAnd(b, v) = this.as_local_call_operand(block, arg);
block = b;
v
},
span: this.thir.exprs[arg].span,
}).collect();
let success = this.cfg.start_new_block();
this.record_operands_moved(&args);
{
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/into.rs:384",
"rustc_mir_build::builder::expr::into",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/into.rs"),
::tracing_core::__macro_support::Option::Some(384u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::into"),
::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 { ; }
};
this.cfg.terminate(block, source_info,
TerminatorKind::Call {
func: fun,
args,
unwind: UnwindAction::Continue,
destination,
target: Some(success),
call_source: if from_hir_call {
CallSource::Normal
} else { CallSource::OverloadedOperator },
fn_span,
});
this.diverge_from(block);
success.unit()
}
ExprKind::ByUse { expr, span } => {
let place =
{
let BlockAnd(b, v) = this.as_place(block, expr);
block = b;
v
};
let ty = place.ty(&this.local_decls, this.tcx).ty;
if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env),
ty) {
this.cfg.push_assign(block, source_info, destination,
Rvalue::Use(Operand::Copy(place)));
block.unit()
} else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env,
ty) {
let success = this.cfg.start_new_block();
let clone_trait =
this.tcx.require_lang_item(LangItem::Clone, span);
let clone_fn =
this.tcx.associated_item_def_ids(clone_trait)[0];
let func =
Operand::function_handle(this.tcx, clone_fn, [ty.into()],
expr_span);
let ref_ty =
Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
let ref_place = this.temp(ref_ty, span);
this.cfg.push_assign(block, source_info, ref_place,
Rvalue::Ref(this.tcx.lifetimes.re_erased,
BorrowKind::Shared, place));
this.cfg.terminate(block, source_info,
TerminatorKind::Call {
func,
args: [Spanned {
node: Operand::Move(ref_place),
span: DUMMY_SP,
}].into(),
destination,
target: Some(success),
unwind: UnwindAction::Unreachable,
call_source: CallSource::Use,
fn_span: expr_span,
});
success.unit()
} else {
this.cfg.push_assign(block, source_info, destination,
Rvalue::Use(Operand::Move(place)));
block.unit()
}
}
ExprKind::Use { source } =>
this.expr_into_dest(destination, block, source),
ExprKind::Borrow { arg, borrow_kind } => {
let arg_place =
match borrow_kind {
BorrowKind::Shared => {
{
let BlockAnd(b, v) = this.as_read_only_place(block, arg);
block = b;
v
}
}
_ => {
let BlockAnd(b, v) = this.as_place(block, arg);
block = b;
v
}
};
let borrow =
Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind,
arg_place);
this.cfg.push_assign(block, source_info, destination,
borrow);
block.unit()
}
ExprKind::RawBorrow { mutability, arg } => {
let place =
match mutability {
hir::Mutability::Not => this.as_read_only_place(block, arg),
hir::Mutability::Mut => this.as_place(block, arg),
};
let address_of =
Rvalue::RawPtr(mutability.into(),
{ let BlockAnd(b, v) = place; block = b; v });
this.cfg.push_assign(block, source_info, destination,
address_of);
block.unit()
}
ExprKind::Adt(box AdtExpr {
adt_def,
variant_index,
args,
ref user_ty,
ref fields,
ref base }) => {
let is_union = adt_def.is_union();
let active_field_index = is_union.then(|| fields[0].name);
let scope = this.local_temp_lifetime();
let fields_map: FxHashMap<_, _> =
fields.into_iter().map(|f|
{
(f.name,
{
let BlockAnd(b, v) =
this.as_operand(block, scope, f.expr,
LocalInfo::AggregateTemp, NeedsTemporary::Maybe);
block = b;
v
})
}).collect();
let variant = adt_def.variant(variant_index);
let field_names = variant.fields.indices();
let fields =
match base {
AdtExprBase::None => {
field_names.filter_map(|n|
fields_map.get(&n).cloned()).collect()
}
AdtExprBase::Base(FruInfo { base, field_types }) => {
let place_builder =
{
let BlockAnd(b, v) = this.as_place_builder(block, *base);
block = b;
v
};
itertools::zip_eq(field_names,
&**field_types).map(|(n, ty)|
match fields_map.get(&n) {
Some(v) => v.clone(),
None => {
let place =
place_builder.clone_project(PlaceElem::Field(n, *ty));
this.consume_by_copy_or_move(place.to_place(this))
}
}).collect()
}
AdtExprBase::DefaultFields(field_types) => {
itertools::zip_eq(field_names,
field_types).map(|(n, &ty)|
match fields_map.get(&n) {
Some(v) => v.clone(),
None =>
match variant.fields[n].value {
Some(def) => {
let value =
Const::Unevaluated(UnevaluatedConst::new(def, args), ty);
Operand::Constant(Box::new(ConstOperand {
span: expr_span,
user_ty: None,
const_: value,
}))
}
None => {
let name = variant.fields[n].name;
::rustc_middle::util::bug::span_bug_fmt(expr_span,
format_args!("missing mandatory field `{0}` of type `{1}`",
name, ty));
}
},
}).collect()
}
};
let inferred_ty = expr.ty;
let user_ty =
user_ty.as_ref().map(|user_ty|
{
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
span: source_info.span,
user_ty: user_ty.clone(),
inferred_ty,
})
});
let adt =
Box::new(AggregateKind::Adt(adt_def.did(), variant_index,
args, user_ty, active_field_index));
this.cfg.push_assign(block, source_info, destination,
Rvalue::Aggregate(adt, fields));
block.unit()
}
ExprKind::InlineAsm(box InlineAsmExpr {
asm_macro, template, ref operands, options, line_spans }) =>
{
use rustc_middle::{mir, thir};
let destination_block = this.cfg.start_new_block();
let mut targets =
if asm_macro.diverges(options) {
::alloc::vec::Vec::new()
} else {
<[_]>::into_vec(::alloc::boxed::box_new([destination_block]))
};
let operands =
operands.into_iter().map(|op|
match *op {
thir::InlineAsmOperand::In { reg, expr } =>
mir::InlineAsmOperand::In {
reg,
value: {
let BlockAnd(b, v) = this.as_local_operand(block, expr);
block = b;
v
},
},
thir::InlineAsmOperand::Out { reg, late, expr } => {
mir::InlineAsmOperand::Out {
reg,
late,
place: expr.map(|expr|
{
let BlockAnd(b, v) = this.as_place(block, expr);
block = b;
v
}),
}
}
thir::InlineAsmOperand::InOut { reg, late, expr } => {
let place =
{
let BlockAnd(b, v) = this.as_place(block, expr);
block = b;
v
};
mir::InlineAsmOperand::InOut {
reg,
late,
in_value: Operand::Copy(place),
out_place: Some(place),
}
}
thir::InlineAsmOperand::SplitInOut {
reg, late, in_expr, out_expr } => {
mir::InlineAsmOperand::InOut {
reg,
late,
in_value: {
let BlockAnd(b, v) = this.as_local_operand(block, in_expr);
block = b;
v
},
out_place: out_expr.map(|out_expr|
{
{
let BlockAnd(b, v) = this.as_place(block, out_expr);
block = b;
v
}
}),
}
}
thir::InlineAsmOperand::Const { value, span } => {
mir::InlineAsmOperand::Const {
value: Box::new(ConstOperand {
span,
user_ty: None,
const_: value,
}),
}
}
thir::InlineAsmOperand::SymFn { value } =>
mir::InlineAsmOperand::SymFn {
value: Box::new(this.as_constant(&this.thir[value])),
},
thir::InlineAsmOperand::SymStatic { def_id } => {
mir::InlineAsmOperand::SymStatic { def_id }
}
thir::InlineAsmOperand::Label { block } => {
let target = this.cfg.start_new_block();
let target_index = targets.len();
targets.push(target);
let tmp = this.get_unit_temp();
let target =
this.ast_block(tmp, target, block,
source_info).into_block();
this.cfg.terminate(target, source_info,
TerminatorKind::Goto { target: destination_block });
mir::InlineAsmOperand::Label { target_index }
}
}).collect();
if !expr.ty.is_never() {
this.cfg.push_assign_unit(block, source_info, destination,
this.tcx);
}
let asm_macro =
match asm_macro {
AsmMacro::Asm | AsmMacro::GlobalAsm => InlineAsmMacro::Asm,
AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
};
this.cfg.terminate(block, source_info,
TerminatorKind::InlineAsm {
asm_macro,
template,
operands,
options,
line_spans,
targets: targets.into_boxed_slice(),
unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
UnwindAction::Continue
} else { UnwindAction::Unreachable },
});
if options.contains(InlineAsmOptions::MAY_UNWIND) {
this.diverge_from(block);
}
destination_block.unit()
}
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
block = this.stmt_expr(block, expr_id, None).into_block();
this.cfg.push_assign_unit(block, source_info, destination,
this.tcx);
block.unit()
}
ExprKind::Continue { .. } | ExprKind::ConstContinue { .. } |
ExprKind::Break { .. } | ExprKind::Return { .. } |
ExprKind::Become { .. } => {
block = this.stmt_expr(block, expr_id, None).into_block();
block.unit()
}
ExprKind::VarRef { .. } | ExprKind::UpvarRef { .. } |
ExprKind::PlaceTypeAscription { .. } |
ExprKind::ValueTypeAscription { .. } |
ExprKind::PlaceUnwrapUnsafeBinder { .. } |
ExprKind::ValueUnwrapUnsafeBinder { .. } => {
if true {
if !(Category::of(&expr.kind) == Some(Category::Place)) {
::core::panicking::panic("assertion failed: Category::of(&expr.kind) == Some(Category::Place)")
};
};
let place =
{
let BlockAnd(b, v) = this.as_place(block, expr_id);
block = b;
v
};
let rvalue =
Rvalue::Use(this.consume_by_copy_or_move(place));
this.cfg.push_assign(block, source_info, destination,
rvalue);
block.unit()
}
ExprKind::Index { .. } | ExprKind::Deref { .. } |
ExprKind::Field { .. } => {
if true {
match (&Category::of(&expr.kind), &Some(Category::Place)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};
if !destination.projection.is_empty() {
this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
}
let place =
{
let BlockAnd(b, v) = this.as_place(block, expr_id);
block = b;
v
};
let rvalue =
Rvalue::Use(this.consume_by_copy_or_move(place));
this.cfg.push_assign(block, source_info, destination,
rvalue);
block.unit()
}
ExprKind::Yield { value } => {
let scope = this.local_temp_lifetime();
let value =
{
let BlockAnd(b, v) =
this.as_operand(block, scope, value, LocalInfo::Boring,
NeedsTemporary::No);
block = b;
v
};
let resume = this.cfg.start_new_block();
this.cfg.terminate(block, source_info,
TerminatorKind::Yield {
value,
resume,
resume_arg: destination,
drop: None,
});
this.coroutine_drop_cleanup(block);
resume.unit()
}
ExprKind::Unary { .. } | ExprKind::Binary { .. } |
ExprKind::Box { .. } | ExprKind::Cast { .. } |
ExprKind::PointerCoercion { .. } | ExprKind::Repeat { .. } |
ExprKind::Array { .. } | ExprKind::Tuple { .. } |
ExprKind::Closure { .. } | ExprKind::ConstBlock { .. } |
ExprKind::Literal { .. } | ExprKind::NamedConst { .. } |
ExprKind::NonHirLiteral { .. } | ExprKind::ZstLiteral { .. }
| ExprKind::ConstParam { .. } | ExprKind::ThreadLocalRef(_)
| ExprKind::StaticRef { .. } | ExprKind::WrapUnsafeBinder {
.. } => {
if true {
if !match Category::of(&expr.kind).unwrap() {
Category::Rvalue(RvalueFunc::Into) => false,
Category::Place => false,
_ => true,
} {
::core::panicking::panic("assertion failed: match Category::of(&expr.kind).unwrap() {\n Category::Rvalue(RvalueFunc::Into) => false,\n Category::Place => false,\n _ => true,\n}")
};
};
let rvalue =
{
let BlockAnd(b, v) = this.as_local_rvalue(block, expr_id);
block = b;
v
};
this.cfg.push_assign(block, source_info, destination,
rvalue);
block.unit()
}
};
if !expr_is_block_or_scope {
let popped = this.block_context.pop();
if !popped.is_some() {
::core::panicking::panic("assertion failed: popped.is_some()")
};
}
block_and
}
}
}#[instrument(level = "debug", skip(self))]
27 pub(crate) fn expr_into_dest(
28 &mut self,
29 destination: Place<'tcx>,
30 mut block: BasicBlock,
31 expr_id: ExprId,
32 ) -> BlockAnd<()> {
33 let this = self; let expr = &this.thir[expr_id];
38 let expr_span = expr.span;
39 let source_info = this.source_info(expr_span);
40
41 let expr_is_block_or_scope =
42 matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
43
44 if !expr_is_block_or_scope {
45 this.block_context.push(BlockFrame::SubExpr);
46 }
47
48 let block_and = match expr.kind {
49 ExprKind::Scope { region_scope, hir_id, value } => {
50 let region_scope = (region_scope, source_info);
51 ensure_sufficient_stack(|| {
52 this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
53 this.expr_into_dest(destination, block, value)
54 })
55 })
56 }
57 ExprKind::Block { block: ast_block } => {
58 this.ast_block(destination, block, ast_block, source_info)
59 }
60 ExprKind::Match { scrutinee, ref arms, .. } => this.match_expr(
61 destination,
62 block,
63 scrutinee,
64 arms,
65 expr_span,
66 this.thir[scrutinee].span,
67 ),
68 ExprKind::If { cond, then, else_opt, if_then_scope } => {
69 let then_span = this.thir[then].span;
70 let then_source_info = this.source_info(then_span);
71 let condition_scope = this.local_scope();
72
73 let then_and_else_blocks = this.in_scope(
74 (if_then_scope, then_source_info),
75 LintLevel::Inherited,
76 |this| {
77 let source_info = if this.is_let(cond) {
79 let variable_scope =
80 this.new_source_scope(then_span, LintLevel::Inherited);
81 this.source_scope = variable_scope;
82 SourceInfo { span: then_span, scope: variable_scope }
83 } else {
84 this.source_info(then_span)
85 };
86
87 let (then_block, else_block) =
89 this.in_if_then_scope(condition_scope, then_span, |this| {
90 let then_blk = this
91 .then_else_break(
92 block,
93 cond,
94 Some(condition_scope), source_info,
96 DeclareLetBindings::Yes, )
98 .into_block();
99
100 this.expr_into_dest(destination, then_blk, then)
102 });
103
104 then_block.and(else_block)
106 },
107 );
108
109 let (then_blk, mut else_blk);
111 else_blk = unpack!(then_blk = then_and_else_blocks);
112
113 if let Some(else_expr) = else_opt {
115 else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block();
116 } else {
117 let correct_si = this.source_info(expr_span.shrink_to_hi());
120 this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
121 }
122
123 let join_block = this.cfg.start_new_block();
126 this.cfg.goto(then_blk, source_info, join_block);
127 this.cfg.goto(else_blk, source_info, join_block);
128 join_block.unit()
129 }
130 ExprKind::Let { .. } => {
131 span_bug!(expr_span, "unexpected let expression outside of if or match-guard");
136 }
137 ExprKind::NeverToAny { source } => {
138 let source_expr = &this.thir[source];
139 let is_call =
140 matches!(source_expr.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
141
142 unpack!(
145 block =
146 this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
147 );
148
149 if is_call {
152 block.unit()
153 } else {
154 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
155 let end_block = this.cfg.start_new_block();
156 end_block.unit()
157 }
158 }
159 ExprKind::LogicalOp { op, lhs, rhs } => {
160 let condition_scope = this.local_scope();
161 let source_info = this.source_info(expr.span);
162
163 let (then_block, else_block) =
165 this.in_if_then_scope(condition_scope, expr.span, |this| {
166 this.then_else_break(
167 block,
168 lhs,
169 Some(condition_scope), source_info,
171 DeclareLetBindings::LetNotPermitted,
174 )
175 });
176 let (short_circuit, continuation, constant) = match op {
177 LogicalOp::And => (else_block, then_block, false),
178 LogicalOp::Or => (then_block, else_block, true),
179 };
180 this.cfg.push_assign_constant(
187 short_circuit,
188 source_info,
189 destination,
190 ConstOperand {
191 span: expr.span,
192 user_ty: None,
193 const_: Const::from_bool(this.tcx, constant),
194 },
195 );
196 let mut rhs_block =
197 this.expr_into_dest(destination, continuation, rhs).into_block();
198 this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block);
201
202 let target = this.cfg.start_new_block();
203 this.cfg.goto(rhs_block, source_info, target);
204 this.cfg.goto(short_circuit, source_info, target);
205 target.unit()
206 }
207 ExprKind::Loop { body } => {
208 let loop_block = this.cfg.start_new_block();
219
220 this.cfg.goto(block, source_info, loop_block);
222
223 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
224 let body_block = this.cfg.start_new_block();
226 this.cfg.terminate(
227 loop_block,
228 source_info,
229 TerminatorKind::FalseUnwind {
230 real_target: body_block,
231 unwind: UnwindAction::Continue,
232 },
233 );
234 this.diverge_from(loop_block);
235
236 let tmp = this.get_unit_temp();
239 let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block();
241 this.cfg.goto(body_block_end, source_info, loop_block);
242
243 None
245 })
246 }
247 ExprKind::LoopMatch {
248 state,
249 region_scope,
250 match_data: box LoopMatchMatchData { box ref arms, span: match_span, scrutinee },
251 } => {
252 fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
260 match ty.kind() {
261 ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => true,
262 ty::Adt(adt_def, _) => match adt_def.adt_kind() {
263 ty::AdtKind::Struct | ty::AdtKind::Union => false,
264 ty::AdtKind::Enum => {
265 adt_def.variants().iter().all(|v| v.fields.is_empty())
266 }
267 },
268 _ => false,
269 }
270 }
271
272 let state_ty = this.thir.exprs[state].ty;
273 if !is_supported_loop_match_type(state_ty) {
274 let span = this.thir.exprs[state].span;
275 this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType { span, ty: state_ty })
276 }
277
278 let loop_block = this.cfg.start_new_block();
279
280 this.cfg.goto(block, source_info, loop_block);
282
283 this.in_breakable_scope(Some(loop_block), destination, expr_span, |this| {
284 let mut body_block = this.cfg.start_new_block();
286 this.cfg.terminate(
287 loop_block,
288 source_info,
289 TerminatorKind::FalseUnwind {
290 real_target: body_block,
291 unwind: UnwindAction::Continue,
292 },
293 );
294 this.diverge_from(loop_block);
295
296 let scrutinee_span = this.thir.exprs[scrutinee].span;
298 let scrutinee_place_builder = unpack!(
299 body_block = this.lower_scrutinee(body_block, scrutinee, scrutinee_span)
300 );
301
302 let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
303
304 let mut patterns = Vec::with_capacity(arms.len());
305 for &arm_id in arms.iter() {
306 let arm = &this.thir[arm_id];
307
308 if let Some(guard) = arm.guard {
309 let span = this.thir.exprs[guard].span;
310 this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
311 }
312
313 patterns.push((&*arm.pattern, HasMatchGuard::No));
314 }
315
316 let built_tree = this.lower_match_tree(
320 body_block,
321 scrutinee_span,
322 &scrutinee_place_builder,
323 match_start_span,
324 patterns,
325 false,
326 );
327
328 let state_place = scrutinee_place_builder.to_place(this);
329
330 unpack!(
337 body_block = this.in_scope(
338 (region_scope, source_info),
339 LintLevel::Inherited,
340 move |this| {
341 this.in_breakable_scope(None, state_place, expr_span, |this| {
342 Some(this.in_const_continuable_scope(
343 Box::from(arms),
344 built_tree.clone(),
345 state_place,
346 expr_span,
347 |this| {
348 this.lower_match_arms(
349 state_place,
350 scrutinee_place_builder,
351 scrutinee_span,
352 arms,
353 built_tree,
354 this.source_info(match_span),
355 )
356 },
357 ))
358 })
359 }
360 )
361 );
362
363 this.cfg.goto(body_block, source_info, loop_block);
364
365 None
367 })
368 }
369 ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
370 let fun = unpack!(block = this.as_local_operand(block, fun));
371 let args: Box<[_]> = args
372 .into_iter()
373 .copied()
374 .map(|arg| Spanned {
375 node: unpack!(block = this.as_local_call_operand(block, arg)),
376 span: this.thir.exprs[arg].span,
377 })
378 .collect();
379
380 let success = this.cfg.start_new_block();
381
382 this.record_operands_moved(&args);
383
384 debug!("expr_into_dest: fn_span={:?}", fn_span);
385
386 this.cfg.terminate(
387 block,
388 source_info,
389 TerminatorKind::Call {
390 func: fun,
391 args,
392 unwind: UnwindAction::Continue,
393 destination,
394 target: Some(success),
395 call_source: if from_hir_call {
396 CallSource::Normal
397 } else {
398 CallSource::OverloadedOperator
399 },
400 fn_span,
401 },
402 );
403 this.diverge_from(block);
404 success.unit()
405 }
406 ExprKind::ByUse { expr, span } => {
407 let place = unpack!(block = this.as_place(block, expr));
408 let ty = place.ty(&this.local_decls, this.tcx).ty;
409
410 if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) {
411 this.cfg.push_assign(
412 block,
413 source_info,
414 destination,
415 Rvalue::Use(Operand::Copy(place)),
416 );
417 block.unit()
418 } else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) {
419 let success = this.cfg.start_new_block();
421 let clone_trait = this.tcx.require_lang_item(LangItem::Clone, span);
422 let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0];
423 let func = Operand::function_handle(this.tcx, clone_fn, [ty.into()], expr_span);
424 let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
425 let ref_place = this.temp(ref_ty, span);
426 this.cfg.push_assign(
427 block,
428 source_info,
429 ref_place,
430 Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place),
431 );
432 this.cfg.terminate(
433 block,
434 source_info,
435 TerminatorKind::Call {
436 func,
437 args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }]
438 .into(),
439 destination,
440 target: Some(success),
441 unwind: UnwindAction::Unreachable,
442 call_source: CallSource::Use,
443 fn_span: expr_span,
444 },
445 );
446 success.unit()
447 } else {
448 this.cfg.push_assign(
449 block,
450 source_info,
451 destination,
452 Rvalue::Use(Operand::Move(place)),
453 );
454 block.unit()
455 }
456 }
457 ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
458 ExprKind::Borrow { arg, borrow_kind } => {
459 let arg_place = match borrow_kind {
465 BorrowKind::Shared => {
466 unpack!(block = this.as_read_only_place(block, arg))
467 }
468 _ => unpack!(block = this.as_place(block, arg)),
469 };
470 let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
471 this.cfg.push_assign(block, source_info, destination, borrow);
472 block.unit()
473 }
474 ExprKind::RawBorrow { mutability, arg } => {
475 let place = match mutability {
476 hir::Mutability::Not => this.as_read_only_place(block, arg),
477 hir::Mutability::Mut => this.as_place(block, arg),
478 };
479 let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place));
480 this.cfg.push_assign(block, source_info, destination, address_of);
481 block.unit()
482 }
483 ExprKind::Adt(box AdtExpr {
484 adt_def,
485 variant_index,
486 args,
487 ref user_ty,
488 ref fields,
489 ref base,
490 }) => {
491 let is_union = adt_def.is_union();
494 let active_field_index = is_union.then(|| fields[0].name);
495
496 let scope = this.local_temp_lifetime();
497
498 let fields_map: FxHashMap<_, _> = fields
501 .into_iter()
502 .map(|f| {
503 (
504 f.name,
505 unpack!(
506 block = this.as_operand(
507 block,
508 scope,
509 f.expr,
510 LocalInfo::AggregateTemp,
511 NeedsTemporary::Maybe,
512 )
513 ),
514 )
515 })
516 .collect();
517
518 let variant = adt_def.variant(variant_index);
519 let field_names = variant.fields.indices();
520
521 let fields = match base {
522 AdtExprBase::None => {
523 field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
524 }
525 AdtExprBase::Base(FruInfo { base, field_types }) => {
526 let place_builder = unpack!(block = this.as_place_builder(block, *base));
527
528 itertools::zip_eq(field_names, &**field_types)
532 .map(|(n, ty)| match fields_map.get(&n) {
533 Some(v) => v.clone(),
534 None => {
535 let place =
536 place_builder.clone_project(PlaceElem::Field(n, *ty));
537 this.consume_by_copy_or_move(place.to_place(this))
538 }
539 })
540 .collect()
541 }
542 AdtExprBase::DefaultFields(field_types) => {
543 itertools::zip_eq(field_names, field_types)
544 .map(|(n, &ty)| match fields_map.get(&n) {
545 Some(v) => v.clone(),
546 None => match variant.fields[n].value {
547 Some(def) => {
548 let value = Const::Unevaluated(
549 UnevaluatedConst::new(def, args),
550 ty,
551 );
552 Operand::Constant(Box::new(ConstOperand {
553 span: expr_span,
554 user_ty: None,
555 const_: value,
556 }))
557 }
558 None => {
559 let name = variant.fields[n].name;
560 span_bug!(
561 expr_span,
562 "missing mandatory field `{name}` of type `{ty}`",
563 );
564 }
565 },
566 })
567 .collect()
568 }
569 };
570
571 let inferred_ty = expr.ty;
572 let user_ty = user_ty.as_ref().map(|user_ty| {
573 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
574 span: source_info.span,
575 user_ty: user_ty.clone(),
576 inferred_ty,
577 })
578 });
579 let adt = Box::new(AggregateKind::Adt(
580 adt_def.did(),
581 variant_index,
582 args,
583 user_ty,
584 active_field_index,
585 ));
586 this.cfg.push_assign(
587 block,
588 source_info,
589 destination,
590 Rvalue::Aggregate(adt, fields),
591 );
592 block.unit()
593 }
594 ExprKind::InlineAsm(box InlineAsmExpr {
595 asm_macro,
596 template,
597 ref operands,
598 options,
599 line_spans,
600 }) => {
601 use rustc_middle::{mir, thir};
602
603 let destination_block = this.cfg.start_new_block();
604 let mut targets =
605 if asm_macro.diverges(options) { vec![] } else { vec![destination_block] };
606
607 let operands = operands
608 .into_iter()
609 .map(|op| match *op {
610 thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
611 reg,
612 value: unpack!(block = this.as_local_operand(block, expr)),
613 },
614 thir::InlineAsmOperand::Out { reg, late, expr } => {
615 mir::InlineAsmOperand::Out {
616 reg,
617 late,
618 place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
619 }
620 }
621 thir::InlineAsmOperand::InOut { reg, late, expr } => {
622 let place = unpack!(block = this.as_place(block, expr));
623 mir::InlineAsmOperand::InOut {
624 reg,
625 late,
626 in_value: Operand::Copy(place),
628 out_place: Some(place),
629 }
630 }
631 thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
632 mir::InlineAsmOperand::InOut {
633 reg,
634 late,
635 in_value: unpack!(block = this.as_local_operand(block, in_expr)),
636 out_place: out_expr.map(|out_expr| {
637 unpack!(block = this.as_place(block, out_expr))
638 }),
639 }
640 }
641 thir::InlineAsmOperand::Const { value, span } => {
642 mir::InlineAsmOperand::Const {
643 value: Box::new(ConstOperand {
644 span,
645 user_ty: None,
646 const_: value,
647 }),
648 }
649 }
650 thir::InlineAsmOperand::SymFn { value } => mir::InlineAsmOperand::SymFn {
651 value: Box::new(this.as_constant(&this.thir[value])),
652 },
653 thir::InlineAsmOperand::SymStatic { def_id } => {
654 mir::InlineAsmOperand::SymStatic { def_id }
655 }
656 thir::InlineAsmOperand::Label { block } => {
657 let target = this.cfg.start_new_block();
658 let target_index = targets.len();
659 targets.push(target);
660
661 let tmp = this.get_unit_temp();
662 let target =
663 this.ast_block(tmp, target, block, source_info).into_block();
664 this.cfg.terminate(
665 target,
666 source_info,
667 TerminatorKind::Goto { target: destination_block },
668 );
669
670 mir::InlineAsmOperand::Label { target_index }
671 }
672 })
673 .collect();
674
675 if !expr.ty.is_never() {
676 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
677 }
678
679 let asm_macro = match asm_macro {
680 AsmMacro::Asm | AsmMacro::GlobalAsm => InlineAsmMacro::Asm,
681 AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
682 };
683
684 this.cfg.terminate(
685 block,
686 source_info,
687 TerminatorKind::InlineAsm {
688 asm_macro,
689 template,
690 operands,
691 options,
692 line_spans,
693 targets: targets.into_boxed_slice(),
694 unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
695 UnwindAction::Continue
696 } else {
697 UnwindAction::Unreachable
698 },
699 },
700 );
701 if options.contains(InlineAsmOptions::MAY_UNWIND) {
702 this.diverge_from(block);
703 }
704 destination_block.unit()
705 }
706
707 ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
709 block = this.stmt_expr(block, expr_id, None).into_block();
710 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
711 block.unit()
712 }
713
714 ExprKind::Continue { .. }
715 | ExprKind::ConstContinue { .. }
716 | ExprKind::Break { .. }
717 | ExprKind::Return { .. }
718 | ExprKind::Become { .. } => {
719 block = this.stmt_expr(block, expr_id, None).into_block();
720 block.unit()
722 }
723
724 ExprKind::VarRef { .. }
726 | ExprKind::UpvarRef { .. }
727 | ExprKind::PlaceTypeAscription { .. }
728 | ExprKind::ValueTypeAscription { .. }
729 | ExprKind::PlaceUnwrapUnsafeBinder { .. }
730 | ExprKind::ValueUnwrapUnsafeBinder { .. } => {
731 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
732
733 let place = unpack!(block = this.as_place(block, expr_id));
734 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
735 this.cfg.push_assign(block, source_info, destination, rvalue);
736 block.unit()
737 }
738 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
739 debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
740
741 if !destination.projection.is_empty() {
745 this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
746 }
747
748 let place = unpack!(block = this.as_place(block, expr_id));
749 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
750 this.cfg.push_assign(block, source_info, destination, rvalue);
751 block.unit()
752 }
753
754 ExprKind::Yield { value } => {
755 let scope = this.local_temp_lifetime();
756 let value = unpack!(
757 block =
758 this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
759 );
760 let resume = this.cfg.start_new_block();
761 this.cfg.terminate(
762 block,
763 source_info,
764 TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
765 );
766 this.coroutine_drop_cleanup(block);
767 resume.unit()
768 }
769
770 ExprKind::Unary { .. }
772 | ExprKind::Binary { .. }
773 | ExprKind::Box { .. }
774 | ExprKind::Cast { .. }
775 | ExprKind::PointerCoercion { .. }
776 | ExprKind::Repeat { .. }
777 | ExprKind::Array { .. }
778 | ExprKind::Tuple { .. }
779 | ExprKind::Closure { .. }
780 | ExprKind::ConstBlock { .. }
781 | ExprKind::Literal { .. }
782 | ExprKind::NamedConst { .. }
783 | ExprKind::NonHirLiteral { .. }
784 | ExprKind::ZstLiteral { .. }
785 | ExprKind::ConstParam { .. }
786 | ExprKind::ThreadLocalRef(_)
787 | ExprKind::StaticRef { .. }
788 | ExprKind::WrapUnsafeBinder { .. } => {
789 debug_assert!(match Category::of(&expr.kind).unwrap() {
790 Category::Rvalue(RvalueFunc::Into) => false,
792
793 Category::Place => false,
797
798 _ => true,
799 });
800
801 let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id));
802 this.cfg.push_assign(block, source_info, destination, rvalue);
803 block.unit()
804 }
805 };
806
807 if !expr_is_block_or_scope {
808 let popped = this.block_context.pop();
809 assert!(popped.is_some());
810 }
811
812 block_and
813 }
814
815 fn is_let(&self, expr: ExprId) -> bool {
816 match self.thir[expr].kind {
817 ExprKind::Let { .. } => true,
818 ExprKind::Scope { value, .. } => self.is_let(value),
819 _ => false,
820 }
821 }
822}