1use rustc_abi::FieldIdx;
4use rustc_ast::{AsmMacro, InlineAsmOptions};
5use rustc_attr_ir::lang_items::LangItem;
6use rustc_data_structures::fx::FxHashMap;
7use rustc_data_structures::thin_vec::ThinVec;
8use rustc_hir as hir;
9use rustc_middle::mir::*;
10use rustc_middle::thir::*;
11use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
12use rustc_span::{DUMMY_SP, Spanned, span_bug, sym};
13use rustc_trait_selection::infer::InferCtxtExt;
14use tracing::{debug, instrument};
15
16use crate::builder::expr::category::{Category, RvalueFunc};
17use crate::builder::matches::{DeclareLetBindings, Exhaustive, HasMatchGuard, LowerIfCondArgs};
18use crate::builder::scope::LintLevel;
19use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
20use crate::diagnostics::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};
21
22impl<'a, 'tcx> Builder<'a, 'tcx> {
23 {}
#[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("/rustc-dev/92ffebeceaa0a071066f233873c2438e73d70fe7/compiler/rustc_mir_build/src/builder/expr/into.rs"),
::tracing_core::__macro_support::Option::Some(25u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::into"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("destination")
}> =
::tracing::__macro_support::FieldName::new("destination");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("block")
}> =
::tracing::__macro_support::FieldName::new("block");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("expr_id")
}> =
::tracing::__macro_support::FieldName::new("expr_id");
NAME.as_str()
}], ::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};
meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&destination)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&block)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr_id)
as &dyn ::tracing::field::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);
this.in_scope(region_scope, LintLevel::Explicit(hir_id),
|this|
{
this.push_coverage_point_for_expr(block, source_info,
hir_id);
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)
}
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 true_and_false_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 (true_block, false_block) =
this.in_if_then_scope(condition_scope, then_span,
|this|
{
let true_block =
this.lower_if_condition(block, cond,
LowerIfCondArgs {
temp_scope_override: Some(condition_scope),
variable_source_info: source_info,
declare_let_bindings: DeclareLetBindings::Yes,
}).into_block();
this.expr_into_dest(destination, true_block, then)
});
true_block.and(false_block)
});
let (true_block, mut false_block);
false_block =
{
let BlockAnd(b, v) = true_and_false_blocks;
true_block = b;
v
};
if let Some(else_expr) = else_opt {
false_block =
this.expr_into_dest(destination, false_block,
else_expr).into_block();
} else {
let correct_si = this.source_info(expr_span.shrink_to_hi());
this.push_coverage_point_for_implicit_else(false_block,
correct_si, expr);
this.cfg.push_assign_unit(false_block, correct_si,
destination, this.tcx);
}
let join_block = this.cfg.start_new_block();
this.cfg.goto(true_block, source_info, join_block);
this.cfg.goto(false_block, source_info, join_block);
join_block.unit()
}
ExprKind::Let { .. } => {
::rustc_span::macros::bug_impl(Some(expr_span),
format_args!("unexpected let expression outside of if or match-guard"),
Location::caller());
}
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 (true_block, false_block) =
this.in_if_then_scope(condition_scope, expr.span,
|this|
{
this.lower_if_condition(block, lhs,
LowerIfCondArgs {
temp_scope_override: Some(condition_scope),
variable_source_info: source_info,
declare_let_bindings: DeclareLetBindings::LetNotPermitted,
})
});
let (short_circuit_block, short_circuit_value,
continue_block) =
match op {
LogicalOp::And => (false_block, false, true_block),
LogicalOp::Or => (true_block, true, false_block),
};
this.cfg.push_assign_constant(short_circuit_block,
source_info, destination,
ConstOperand {
span: expr.span,
user_ty: None,
const_: Const::from_bool(this.tcx, short_circuit_value),
});
let mut rhs_block =
this.expr_into_dest(destination, continue_block,
rhs).into_block();
this.visit_coverage_standalone_condition(rhs, destination,
&mut rhs_block);
let join_block = this.cfg.start_new_block();
this.cfg.goto(rhs_block, source_info, join_block);
this.cfg.goto(short_circuit_block, source_info, join_block);
join_block.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();
let loop_hint_attrs =
this.thir.loop_hint_attrs.get(&expr_id).map(|attrs|
attrs.clone()).unwrap_or_else(|| ThinVec::new());
this.cfg.goto_with_hints(body_block_end, source_info,
loop_block, loop_hint_attrs);
None
})
}
ExprKind::LoopMatch {
state,
region_scope,
match_data: LoopMatchMatchData {
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);
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,
Exhaustive::Yes);
let state_place = scrutinee_place_builder.to_place(this);
let state_result = this.temp(state_ty, expr_span);
let state_result_place = Place::from(state_result);
{
let BlockAnd(b, v) =
this.in_scope((region_scope, source_info),
LintLevel::Inherited,
move |this|
{
this.in_const_continuable_scope(arms.clone(),
built_tree.clone(), state_place, expr_span,
|this|
{
let block =
this.in_breakable_scope(None, state_result_place, expr_span,
|this|
{
Some(this.lower_match_arms(state_result_place,
scrutinee_place_builder, scrutinee_span, arms, built_tree,
this.source_info(match_span)))
}).into_block();
this.cfg.push_assign(block, source_info, state_place,
Rvalue::Use(this.consume_by_copy_or_move(state_result_place),
WithRetag::Yes));
block.unit()
})
});
body_block = b;
v
};
this.cfg.goto(body_block, source_info, loop_block);
None
})
}
ExprKind::Call { ty, fun, ref args, .. } if
let ty::FnDef(def_id, generic_args) = *ty.kind() &&
let Some(intrinsic) = this.tcx.intrinsic(def_id) &&
#[allow(non_exhaustive_omitted_patterns)] match intrinsic.name
{
sym::write_via_move | sym::write_box_via_move => true,
_ => false,
} => {
let generic_args = generic_args.no_bound_vars().unwrap();
let _fun =
{
let BlockAnd(b, v) = this.as_local_operand(block, fun);
block = b;
v
};
match intrinsic.name {
sym::write_via_move => {
if !destination.ty(&this.local_decls, this.tcx).ty.is_unit()
{
::core::panicking::panic("assertion failed: destination.ty(&this.local_decls, this.tcx).ty.is_unit()")
};
let [ptr, val] =
**args else {
::rustc_span::macros::bug_impl(Some(expr_span),
format_args!("invalid write_via_move call"),
Location::caller())
};
let Some(ptr) =
{
let BlockAnd(b, v) = this.as_local_operand(block, ptr);
block = b;
v
}.place() else {
::rustc_span::macros::bug_impl(Some(expr_span),
format_args!("invalid write_via_move call"),
Location::caller())
};
let ptr_deref =
ptr.project_deeper(&[ProjectionElem::Deref], this.tcx);
this.expr_into_dest(ptr_deref, block, val)
}
sym::write_box_via_move => {
let [b, val] =
**args else {
::rustc_span::macros::bug_impl(Some(expr_span),
format_args!("invalid init_box_via_move call"),
Location::caller())
};
let Some(b) =
{
let BlockAnd(b, v) = this.as_local_operand(block, b);
block = b;
v
}.place() else {
::rustc_span::macros::bug_impl(Some(expr_span),
format_args!("invalid init_box_via_move call"),
Location::caller())
};
let tcx = this.tcx;
let decls = &this.local_decls;
let place = b.project_deeper(&[ProjectionElem::Deref], tcx);
let place =
place.project_to_field(FieldIdx::from_u32(1), decls, tcx);
let place =
place.project_to_field(FieldIdx::ZERO, decls, tcx);
{
match (&place.ty(decls, tcx).ty, &generic_args.type_at(0)) {
(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);
}
}
}
};
{
let BlockAnd(b, v) = this.expr_into_dest(place, block, val);
block = b;
v
};
this.cfg.push_assign(block, source_info, destination,
Rvalue::Use(Operand::Move(b), WithRetag::Yes));
block.unit()
}
_ =>
::rustc_span::macros::bug_impl(None,
format_args!("impossible case reached"),
Location::caller()),
}
}
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 /rustc-dev/92ffebeceaa0a071066f233873c2438e73d70fe7/compiler/rustc_mir_build/src/builder/expr/into.rs:500",
"rustc_mir_build::builder::expr::into",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/92ffebeceaa0a071066f233873c2438e73d70fe7/compiler/rustc_mir_build/src/builder/expr/into.rs"),
::tracing_core::__macro_support::Option::Some(500u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("expr_into_dest: fn_span={0:?}",
fn_span) as &dyn ::tracing::field::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), WithRetag::Yes));
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), WithRetag::Yes));
block.unit()
}
}
ExprKind::ValueExpr { 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(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_span::macros::bug_impl(Some(expr_span),
format_args!("missing mandatory field `{0}` of type `{1}`",
name, ty), Location::caller());
}
},
}).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(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 {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[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(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),
WithRetag::Yes);
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),
WithRetag::Yes);
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::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()
}
ExprKind::Reborrow { source, mutability, target } => {
let place =
{
let BlockAnd(b, v) = this.as_place(block, source);
block = b;
v
};
this.cfg.push_assign(block, source_info, destination,
Rvalue::Reborrow(target, mutability, place));
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))]
26 pub(crate) fn expr_into_dest(
27 &mut self,
28 destination: Place<'tcx>,
29 mut block: BasicBlock,
30 expr_id: ExprId,
31 ) -> BlockAnd<()> {
32 let this = self; let expr = &this.thir[expr_id];
37 let expr_span = expr.span;
38 let source_info = this.source_info(expr_span);
39
40 let expr_is_block_or_scope =
41 matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
42
43 if !expr_is_block_or_scope {
44 this.block_context.push(BlockFrame::SubExpr);
45 }
46
47 let block_and = match expr.kind {
48 ExprKind::Scope { region_scope, hir_id, value } => {
49 let region_scope = (region_scope, source_info);
50 this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
51 this.push_coverage_point_for_expr(block, source_info, hir_id);
52 this.expr_into_dest(destination, block, value)
53 })
54 }
55 ExprKind::Block { block: ast_block } => {
56 this.ast_block(destination, block, ast_block, source_info)
57 }
58 ExprKind::Match { scrutinee, ref arms, .. } => {
59 this.match_expr(destination, block, scrutinee, arms, expr_span)
60 }
61 ExprKind::If { cond, then, else_opt, if_then_scope } => {
62 let then_span = this.thir[then].span;
63 let then_source_info = this.source_info(then_span);
64 let condition_scope = this.local_scope();
65
66 let true_and_false_blocks = this.in_scope(
67 (if_then_scope, then_source_info),
68 LintLevel::Inherited,
69 |this| {
70 let source_info = if this.is_let(cond) {
72 let variable_scope =
73 this.new_source_scope(then_span, LintLevel::Inherited);
74 this.source_scope = variable_scope;
75 SourceInfo { span: then_span, scope: variable_scope }
76 } else {
77 this.source_info(then_span)
78 };
79
80 let (true_block, false_block) =
82 this.in_if_then_scope(condition_scope, then_span, |this| {
83 let true_block = this
84 .lower_if_condition(
85 block,
86 cond,
87 LowerIfCondArgs {
88 temp_scope_override: Some(condition_scope),
89 variable_source_info: source_info,
90 declare_let_bindings: DeclareLetBindings::Yes,
91 },
92 )
93 .into_block();
94
95 this.expr_into_dest(destination, true_block, then)
97 });
98
99 true_block.and(false_block)
101 },
102 );
103
104 let (true_block, mut false_block);
106 false_block = unpack!(true_block = true_and_false_blocks);
107
108 if let Some(else_expr) = else_opt {
110 false_block =
111 this.expr_into_dest(destination, false_block, else_expr).into_block();
112 } else {
113 let correct_si = this.source_info(expr_span.shrink_to_hi());
116 this.push_coverage_point_for_implicit_else(false_block, correct_si, expr);
117 this.cfg.push_assign_unit(false_block, correct_si, destination, this.tcx);
118 }
119
120 let join_block = this.cfg.start_new_block();
123 this.cfg.goto(true_block, source_info, join_block);
124 this.cfg.goto(false_block, source_info, join_block);
125 join_block.unit()
126 }
127 ExprKind::Let { .. } => {
128 span_bug!(expr_span, "unexpected let expression outside of if or match-guard");
133 }
134 ExprKind::NeverToAny { source } => {
135 let source_expr = &this.thir[source];
136 let is_call =
137 matches!(source_expr.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
138
139 unpack!(
142 block =
143 this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
144 );
145
146 if is_call {
149 block.unit()
150 } else {
151 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
152 let end_block = this.cfg.start_new_block();
153 end_block.unit()
154 }
155 }
156 ExprKind::LogicalOp { op, lhs, rhs } => {
157 let condition_scope = this.local_scope();
158 let source_info = this.source_info(expr.span);
159
160 let (true_block, false_block) =
162 this.in_if_then_scope(condition_scope, expr.span, |this| {
163 this.lower_if_condition(
164 block,
165 lhs,
166 LowerIfCondArgs {
167 temp_scope_override: Some(condition_scope),
168 variable_source_info: source_info,
169 declare_let_bindings: DeclareLetBindings::LetNotPermitted,
170 },
171 )
172 });
173
174 let (short_circuit_block, short_circuit_value, continue_block) = match op {
181 LogicalOp::And => (false_block, false, true_block),
182 LogicalOp::Or => (true_block, true, false_block),
183 };
184 this.cfg.push_assign_constant(
185 short_circuit_block,
186 source_info,
187 destination,
188 ConstOperand {
189 span: expr.span,
190 user_ty: None,
191 const_: Const::from_bool(this.tcx, short_circuit_value),
192 },
193 );
194 let mut rhs_block =
195 this.expr_into_dest(destination, continue_block, rhs).into_block();
196 this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block);
199
200 let join_block = this.cfg.start_new_block();
202 this.cfg.goto(rhs_block, source_info, join_block);
203 this.cfg.goto(short_circuit_block, source_info, join_block);
204 join_block.unit()
205 }
206 ExprKind::Loop { body } => {
207 let loop_block = this.cfg.start_new_block();
218
219 this.cfg.goto(block, source_info, loop_block);
221
222 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
223 let body_block = this.cfg.start_new_block();
225 this.cfg.terminate(
226 loop_block,
227 source_info,
228 TerminatorKind::FalseUnwind {
229 real_target: body_block,
230 unwind: UnwindAction::Continue,
231 },
232 );
233 this.diverge_from(loop_block);
234
235 let tmp = this.get_unit_temp();
238 let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block();
240
241 let loop_hint_attrs = this
242 .thir
243 .loop_hint_attrs
244 .get(&expr_id)
245 .map(|attrs| attrs.clone())
246 .unwrap_or_else(|| ThinVec::new());
247 this.cfg.goto_with_hints(
248 body_block_end,
249 source_info,
250 loop_block,
251 loop_hint_attrs,
252 );
253
254 None
256 })
257 }
258 ExprKind::LoopMatch {
259 state,
260 region_scope,
261 match_data: LoopMatchMatchData { ref arms, span: match_span, scrutinee },
262 } => {
263 fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
271 match ty.kind() {
272 ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => true,
273 ty::Adt(adt_def, _) => match adt_def.adt_kind() {
274 ty::AdtKind::Struct | ty::AdtKind::Union => false,
275 ty::AdtKind::Enum => {
276 adt_def.variants().iter().all(|v| v.fields.is_empty())
277 }
278 },
279 _ => false,
280 }
281 }
282
283 let state_ty = this.thir.exprs[state].ty;
284 if !is_supported_loop_match_type(state_ty) {
285 let span = this.thir.exprs[state].span;
286 this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType { span, ty: state_ty })
287 }
288
289 let loop_block = this.cfg.start_new_block();
290
291 this.cfg.goto(block, source_info, loop_block);
293
294 this.in_breakable_scope(Some(loop_block), destination, expr_span, |this| {
295 let mut body_block = this.cfg.start_new_block();
297 this.cfg.terminate(
298 loop_block,
299 source_info,
300 TerminatorKind::FalseUnwind {
301 real_target: body_block,
302 unwind: UnwindAction::Continue,
303 },
304 );
305 this.diverge_from(loop_block);
306
307 let scrutinee_span = this.thir.exprs[scrutinee].span;
309 let scrutinee_place_builder =
310 unpack!(body_block = this.lower_scrutinee(body_block, scrutinee));
311
312 let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
313
314 let mut patterns = Vec::with_capacity(arms.len());
315 for &arm_id in arms.iter() {
316 let arm = &this.thir[arm_id];
317
318 if let Some(guard) = arm.guard {
319 let span = this.thir.exprs[guard].span;
320 this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
321 }
322
323 patterns.push((&*arm.pattern, HasMatchGuard::No));
324 }
325
326 let built_tree = this.lower_match_tree(
330 body_block,
331 scrutinee_span,
332 &scrutinee_place_builder,
333 match_start_span,
334 patterns,
335 Exhaustive::Yes,
336 );
337
338 let state_place = scrutinee_place_builder.to_place(this);
339 let state_result = this.temp(state_ty, expr_span);
340 let state_result_place = Place::from(state_result);
341
342 unpack!(
355 body_block = this.in_scope(
356 (region_scope, source_info),
357 LintLevel::Inherited,
358 move |this| {
359 this.in_const_continuable_scope(
360 arms.clone(),
361 built_tree.clone(),
362 state_place,
363 expr_span,
364 |this| {
365 let block = this
366 .in_breakable_scope(
367 None,
368 state_result_place,
369 expr_span,
370 |this| {
371 Some(this.lower_match_arms(
372 state_result_place,
373 scrutinee_place_builder,
374 scrutinee_span,
375 arms,
376 built_tree,
377 this.source_info(match_span),
378 ))
379 },
380 )
381 .into_block();
382
383 this.cfg.push_assign(
384 block,
385 source_info,
386 state_place,
387 Rvalue::Use(
388 this.consume_by_copy_or_move(state_result_place),
389 WithRetag::Yes,
390 ),
391 );
392 block.unit()
393 },
394 )
395 }
396 )
397 );
398
399 this.cfg.goto(body_block, source_info, loop_block);
400
401 None
403 })
404 }
405 ExprKind::Call { ty, fun, ref args, .. }
408 if let ty::FnDef(def_id, generic_args) = *ty.kind()
409 && let Some(intrinsic) = this.tcx.intrinsic(def_id)
410 && matches!(intrinsic.name, sym::write_via_move | sym::write_box_via_move) =>
411 {
412 let generic_args = generic_args.no_bound_vars().unwrap();
413 let _fun = unpack!(block = this.as_local_operand(block, fun));
416
417 match intrinsic.name {
418 sym::write_via_move => {
419 assert!(destination.ty(&this.local_decls, this.tcx).ty.is_unit());
424
425 let [ptr, val] = **args else {
427 span_bug!(expr_span, "invalid write_via_move call")
428 };
429 let Some(ptr) = unpack!(block = this.as_local_operand(block, ptr)).place()
430 else {
431 span_bug!(expr_span, "invalid write_via_move call")
432 };
433 let ptr_deref = ptr.project_deeper(&[ProjectionElem::Deref], this.tcx);
434 this.expr_into_dest(ptr_deref, block, val)
435 }
436 sym::write_box_via_move => {
437 let [b, val] = **args else {
451 span_bug!(expr_span, "invalid init_box_via_move call")
452 };
453 let Some(b) = unpack!(block = this.as_local_operand(block, b)).place()
454 else {
455 span_bug!(expr_span, "invalid init_box_via_move call")
456 };
457 let tcx = this.tcx;
458 let decls = &this.local_decls;
459
460 let place = b.project_deeper(&[ProjectionElem::Deref], tcx);
462 let place = place.project_to_field(FieldIdx::from_u32(1), decls, tcx);
464 let place = place.project_to_field(FieldIdx::ZERO, decls, tcx);
466 assert_eq!(place.ty(decls, tcx).ty, generic_args.type_at(0));
468
469 unpack!(block = this.expr_into_dest(place, block, val));
471
472 this.cfg.push_assign(
474 block,
475 source_info,
476 destination,
477 Rvalue::Use(Operand::Move(b), WithRetag::Yes),
479 );
480 block.unit()
481 }
482 _ => rustc_span::bug!(),
483 }
484 }
485 ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
486 let fun = unpack!(block = this.as_local_operand(block, fun));
487 let args: Box<[_]> = args
488 .into_iter()
489 .copied()
490 .map(|arg| Spanned {
491 node: unpack!(block = this.as_local_call_operand(block, arg)),
492 span: this.thir.exprs[arg].span,
493 })
494 .collect();
495
496 let success = this.cfg.start_new_block();
497
498 this.record_operands_moved(&args);
499
500 debug!("expr_into_dest: fn_span={:?}", fn_span);
501
502 this.cfg.terminate(
503 block,
504 source_info,
505 TerminatorKind::Call {
506 func: fun,
507 args,
508 unwind: UnwindAction::Continue,
509 destination,
510 target: Some(success),
511 call_source: if from_hir_call {
512 CallSource::Normal
513 } else {
514 CallSource::OverloadedOperator
515 },
516 fn_span,
517 },
518 );
519 this.diverge_from(block);
520 success.unit()
521 }
522 ExprKind::ByUse { expr, span } => {
523 let place = unpack!(block = this.as_place(block, expr));
524 let ty = place.ty(&this.local_decls, this.tcx).ty;
525
526 if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) {
527 this.cfg.push_assign(
528 block,
529 source_info,
530 destination,
531 Rvalue::Use(Operand::Copy(place), WithRetag::Yes),
532 );
533 block.unit()
534 } else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) {
535 let success = this.cfg.start_new_block();
537 let clone_trait = this.tcx.require_lang_item(LangItem::Clone, span);
538 let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0];
539 let func =
540 Operand::function_handle(this.tcx, clone_fn, &[ty.into()], expr_span);
541 let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
542 let ref_place = this.temp(ref_ty, span);
543 this.cfg.push_assign(
544 block,
545 source_info,
546 ref_place,
547 Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place),
548 );
549 this.cfg.terminate(
550 block,
551 source_info,
552 TerminatorKind::Call {
553 func,
554 args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }]
555 .into(),
556 destination,
557 target: Some(success),
558 unwind: UnwindAction::Unreachable,
559 call_source: CallSource::Use,
560 fn_span: expr_span,
561 },
562 );
563 success.unit()
564 } else {
565 this.cfg.push_assign(
566 block,
567 source_info,
568 destination,
569 Rvalue::Use(Operand::Move(place), WithRetag::Yes),
570 );
571 block.unit()
572 }
573 }
574 ExprKind::ValueExpr { source } => this.expr_into_dest(destination, block, source),
575 ExprKind::Borrow { arg, borrow_kind } => {
576 let arg_place = match borrow_kind {
582 BorrowKind::Shared => {
583 unpack!(block = this.as_read_only_place(block, arg))
584 }
585 _ => unpack!(block = this.as_place(block, arg)),
586 };
587 let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
588 this.cfg.push_assign(block, source_info, destination, borrow);
589 block.unit()
590 }
591 ExprKind::RawBorrow { mutability, arg } => {
592 let place = match mutability {
593 hir::Mutability::Not => this.as_read_only_place(block, arg),
594 hir::Mutability::Mut => this.as_place(block, arg),
595 };
596 let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place));
597 this.cfg.push_assign(block, source_info, destination, address_of);
598 block.unit()
599 }
600 ExprKind::Adt(AdtExpr {
601 adt_def,
602 variant_index,
603 args,
604 ref user_ty,
605 ref fields,
606 ref base,
607 }) => {
608 let is_union = adt_def.is_union();
611 let active_field_index = is_union.then(|| fields[0].name);
612
613 let scope = this.local_temp_lifetime();
614
615 let fields_map: FxHashMap<_, _> = fields
618 .into_iter()
619 .map(|f| {
620 (
621 f.name,
622 unpack!(
623 block = this.as_operand(
624 block,
625 scope,
626 f.expr,
627 LocalInfo::AggregateTemp,
628 NeedsTemporary::Maybe,
629 )
630 ),
631 )
632 })
633 .collect();
634
635 let variant = adt_def.variant(variant_index);
636 let field_names = variant.fields.indices();
637
638 let fields = match base {
639 AdtExprBase::None => {
640 field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
641 }
642 AdtExprBase::Base(FruInfo { base, field_types }) => {
643 let place_builder = unpack!(block = this.as_place_builder(block, *base));
644
645 itertools::zip_eq(field_names, &**field_types)
649 .map(|(n, ty)| match fields_map.get(&n) {
650 Some(v) => v.clone(),
651 None => {
652 let place =
653 place_builder.clone_project(PlaceElem::Field(n, *ty));
654 this.consume_by_copy_or_move(place.to_place(this))
655 }
656 })
657 .collect()
658 }
659 AdtExprBase::DefaultFields(field_types) => {
660 itertools::zip_eq(field_names, field_types)
661 .map(|(n, &ty)| match fields_map.get(&n) {
662 Some(v) => v.clone(),
663 None => match variant.fields[n].value {
664 Some(def) => {
665 let value = Const::Unevaluated(
666 UnevaluatedConst::new(def, args),
667 ty,
668 );
669 Operand::Constant(Box::new(ConstOperand {
670 span: expr_span,
671 user_ty: None,
672 const_: value,
673 }))
674 }
675 None => {
676 let name = variant.fields[n].name;
677 span_bug!(
678 expr_span,
679 "missing mandatory field `{name}` of type `{ty}`",
680 );
681 }
682 },
683 })
684 .collect()
685 }
686 };
687
688 let inferred_ty = expr.ty;
689 let user_ty = user_ty.as_ref().map(|user_ty| {
690 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
691 span: source_info.span,
692 user_ty: user_ty.clone(),
693 inferred_ty,
694 })
695 });
696 let adt = Box::new(AggregateKind::Adt(
697 adt_def.did(),
698 variant_index,
699 args,
700 user_ty,
701 active_field_index,
702 ));
703 this.cfg.push_assign(
704 block,
705 source_info,
706 destination,
707 Rvalue::Aggregate(adt, fields),
708 );
709 block.unit()
710 }
711 ExprKind::InlineAsm(InlineAsmExpr {
712 asm_macro,
713 template,
714 ref operands,
715 options,
716 line_spans,
717 }) => {
718 use rustc_middle::{mir, thir};
719
720 let destination_block = this.cfg.start_new_block();
721 let mut targets =
722 if asm_macro.diverges(options) { vec![] } else { vec![destination_block] };
723
724 let operands = operands
725 .into_iter()
726 .map(|op| match *op {
727 thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
728 reg,
729 value: unpack!(block = this.as_local_operand(block, expr)),
730 },
731 thir::InlineAsmOperand::Out { reg, late, expr } => {
732 mir::InlineAsmOperand::Out {
733 reg,
734 late,
735 place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
736 }
737 }
738 thir::InlineAsmOperand::InOut { reg, late, expr } => {
739 let place = unpack!(block = this.as_place(block, expr));
740 mir::InlineAsmOperand::InOut {
741 reg,
742 late,
743 in_value: Operand::Copy(place),
745 out_place: Some(place),
746 }
747 }
748 thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
749 mir::InlineAsmOperand::InOut {
750 reg,
751 late,
752 in_value: unpack!(block = this.as_local_operand(block, in_expr)),
753 out_place: out_expr.map(|out_expr| {
754 unpack!(block = this.as_place(block, out_expr))
755 }),
756 }
757 }
758 thir::InlineAsmOperand::Const { value, span } => {
759 mir::InlineAsmOperand::Const {
760 value: Box::new(ConstOperand {
761 span,
762 user_ty: None,
763 const_: value,
764 }),
765 }
766 }
767 thir::InlineAsmOperand::SymFn { value } => mir::InlineAsmOperand::SymFn {
768 value: Box::new(this.as_constant(&this.thir[value])),
769 },
770 thir::InlineAsmOperand::SymStatic { def_id } => {
771 mir::InlineAsmOperand::SymStatic { def_id }
772 }
773 thir::InlineAsmOperand::Label { block } => {
774 let target = this.cfg.start_new_block();
775 let target_index = targets.len();
776 targets.push(target);
777
778 let tmp = this.get_unit_temp();
779 let target =
780 this.ast_block(tmp, target, block, source_info).into_block();
781 this.cfg.terminate(
782 target,
783 source_info,
784 TerminatorKind::goto(destination_block),
785 );
786
787 mir::InlineAsmOperand::Label { target_index }
788 }
789 })
790 .collect();
791
792 if !expr.ty.is_never() {
793 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
794 }
795
796 let asm_macro = match asm_macro {
797 AsmMacro::Asm | AsmMacro::GlobalAsm => InlineAsmMacro::Asm,
798 AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
799 };
800
801 this.cfg.terminate(
802 block,
803 source_info,
804 TerminatorKind::InlineAsm {
805 asm_macro,
806 template,
807 operands,
808 options,
809 line_spans,
810 targets: targets.into_boxed_slice(),
811 unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
812 UnwindAction::Continue
813 } else {
814 UnwindAction::Unreachable
815 },
816 },
817 );
818 if options.contains(InlineAsmOptions::MAY_UNWIND) {
819 this.diverge_from(block);
820 }
821 destination_block.unit()
822 }
823
824 ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
826 block = this.stmt_expr(block, expr_id, None).into_block();
827 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
828 block.unit()
829 }
830
831 ExprKind::Continue { .. }
832 | ExprKind::ConstContinue { .. }
833 | ExprKind::Break { .. }
834 | ExprKind::Return { .. }
835 | ExprKind::Become { .. } => {
836 block = this.stmt_expr(block, expr_id, None).into_block();
837 block.unit()
839 }
840
841 ExprKind::VarRef { .. }
843 | ExprKind::UpvarRef { .. }
844 | ExprKind::PlaceTypeAscription { .. }
845 | ExprKind::ValueTypeAscription { .. }
846 | ExprKind::PlaceUnwrapUnsafeBinder { .. }
847 | ExprKind::ValueUnwrapUnsafeBinder { .. } => {
848 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
849
850 let place = unpack!(block = this.as_place(block, expr_id));
851 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place), WithRetag::Yes);
852 this.cfg.push_assign(block, source_info, destination, rvalue);
853 block.unit()
854 }
855 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
856 debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
857
858 if !destination.projection.is_empty() {
862 this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
863 }
864
865 let place = unpack!(block = this.as_place(block, expr_id));
866 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place), WithRetag::Yes);
867 this.cfg.push_assign(block, source_info, destination, rvalue);
868 block.unit()
869 }
870
871 ExprKind::Yield { value } => {
872 let scope = this.local_temp_lifetime();
873 let value = unpack!(
874 block =
875 this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
876 );
877 let resume = this.cfg.start_new_block();
878 this.cfg.terminate(
879 block,
880 source_info,
881 TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
882 );
883 this.coroutine_drop_cleanup(block);
884 resume.unit()
885 }
886
887 ExprKind::Unary { .. }
889 | ExprKind::Binary { .. }
890 | ExprKind::Cast { .. }
891 | ExprKind::PointerCoercion { .. }
892 | ExprKind::Repeat { .. }
893 | ExprKind::Array { .. }
894 | ExprKind::Tuple { .. }
895 | ExprKind::Closure { .. }
896 | ExprKind::ConstBlock { .. }
897 | ExprKind::Literal { .. }
898 | ExprKind::NamedConst { .. }
899 | ExprKind::NonHirLiteral { .. }
900 | ExprKind::ZstLiteral { .. }
901 | ExprKind::ConstParam { .. }
902 | ExprKind::ThreadLocalRef(_)
903 | ExprKind::StaticRef { .. }
904 | ExprKind::WrapUnsafeBinder { .. } => {
905 debug_assert!(match Category::of(&expr.kind).unwrap() {
906 Category::Rvalue(RvalueFunc::Into) => false,
908
909 Category::Place => false,
913
914 _ => true,
915 });
916
917 let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id));
918 this.cfg.push_assign(block, source_info, destination, rvalue);
919 block.unit()
920 }
921 ExprKind::Reborrow { source, mutability, target } => {
922 let place = unpack!(block = this.as_place(block, source));
923 this.cfg.push_assign(
924 block,
925 source_info,
926 destination,
927 Rvalue::Reborrow(target, mutability, place),
928 );
929 block.unit()
930 }
931 };
932
933 if !expr_is_block_or_scope {
934 let popped = this.block_context.pop();
935 assert!(popped.is_some());
936 }
937
938 block_and
939 }
940
941 fn is_let(&self, expr: ExprId) -> bool {
942 match self.thir[expr].kind {
943 ExprKind::Let { .. } => true,
944 ExprKind::Scope { value, .. } => self.is_let(value),
945 _ => false,
946 }
947 }
948}