rust/compiler/rustc_mir_build/src/builder/expr/into.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

651 lines
29 KiB
Rust
Raw Normal View History

2015-08-18 21:59:21 +00:00
//! See docs in build/expr/mod.rs
use rustc_ast::{AsmMacro, InlineAsmOptions};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
2021-04-04 16:42:17 +00:00
use rustc_middle::thir::*;
use rustc_middle::ty::CanonicalUserTypeAnnotation;
use rustc_span::source_map::Spanned;
use tracing::{debug, instrument};
use crate::builder::expr::category::{Category, RvalueFunc};
use crate::builder::matches::DeclareLetBindings;
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
impl<'a, 'tcx> Builder<'a, 'tcx> {
2015-08-18 21:59:21 +00:00
/// Compile `expr`, storing the result into `destination`, which
/// is assumed to be uninitialized.
2022-09-13 08:51:45 +00:00
#[instrument(level = "debug", skip(self))]
pub(crate) fn expr_into_dest(
2018-09-06 21:34:26 +00:00
&mut self,
destination: Place<'tcx>,
2018-09-06 21:34:26 +00:00
mut block: BasicBlock,
2023-12-15 15:16:24 +00:00
expr_id: ExprId,
2018-09-06 21:34:26 +00:00
) -> BlockAnd<()> {
2015-08-18 21:59:21 +00:00
// since we frequently have to reference `self` from within a
// closure, where `self` would be shadowed, it's easier to
// just use the name `this` uniformly
let this = self;
2023-12-15 15:16:24 +00:00
let expr = &this.thir[expr_id];
2015-08-18 21:59:21 +00:00
let expr_span = expr.span;
let source_info = this.source_info(expr_span);
2015-08-18 21:59:21 +00:00
let expr_is_block_or_scope =
matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
if !expr_is_block_or_scope {
this.block_context.push(BlockFrame::SubExpr);
}
2021-03-06 21:24:04 +00:00
let block_and = match expr.kind {
2018-09-06 21:34:26 +00:00
ExprKind::Scope { region_scope, lint_level, value } => {
2021-03-06 21:24:04 +00:00
let region_scope = (region_scope, source_info);
ensure_sufficient_stack(|| {
2021-03-06 21:24:04 +00:00
this.in_scope(region_scope, lint_level, |this| {
2023-12-15 15:16:24 +00:00
this.expr_into_dest(destination, block, value)
})
})
2015-08-18 21:59:21 +00:00
}
ExprKind::Block { block: ast_block } => {
2021-03-06 21:24:04 +00:00
this.ast_block(destination, block, ast_block, source_info)
2015-08-18 21:59:21 +00:00
}
2023-12-15 15:16:24 +00:00
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 } => {
2023-12-15 15:16:24 +00:00
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| {
2024-03-06 04:22:39 +00:00
// FIXME: Does this need extra logic to handle let-chains?
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)
};
2024-03-06 04:22:39 +00:00
// Lower the condition, and have it branch into `then` and `else` blocks.
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), // Temp scope
source_info,
DeclareLetBindings::Yes, // Declare `let` bindings normally
)
.into_block();
2024-03-06 04:22:39 +00:00
// Lower the `then` arm into its block.
this.expr_into_dest(destination, then_blk, then)
});
2024-03-06 04:22:39 +00:00
// Pack `(then_block, else_block)` into `BlockAnd<BasicBlock>`.
then_block.and(else_block)
},
);
// Unpack `BlockAnd<BasicBlock>` into `(then_blk, else_blk)`.
let (then_blk, mut else_blk);
else_blk = unpack!(then_blk = then_and_else_blocks);
// If there is an `else` arm, lower it into `else_blk`.
if let Some(else_expr) = else_opt {
else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block();
2021-01-01 18:38:11 +00:00
} else {
// There is no `else` arm, so we know both arms have type `()`.
// Generate the implicit `else {}` by assigning unit.
2021-01-01 18:38:11 +00:00
let correct_si = this.source_info(expr_span.shrink_to_hi());
2021-08-08 14:49:13 +00:00
this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
}
2021-01-01 18:38:11 +00:00
2024-03-06 04:22:39 +00:00
// The `then` and `else` arms have been lowered into their respective
// blocks, so make both of them meet up in a new block.
2021-01-01 18:38:11 +00:00
let join_block = this.cfg.start_new_block();
2022-01-18 22:38:17 +00:00
this.cfg.goto(then_blk, source_info, join_block);
this.cfg.goto(else_blk, source_info, join_block);
2021-01-01 18:38:11 +00:00
join_block.unit()
}
ExprKind::Let { .. } => {
// After desugaring, `let` expressions should only appear inside `if`
// expressions or `match` guards, possibly nested within a let-chain.
// In both cases they are specifically handled by the lowerings of
// those expressions, so this case is currently unreachable.
span_bug!(expr_span, "unexpected let expression outside of if or match-guard");
2021-08-08 14:49:13 +00:00
}
ExprKind::NeverToAny { source } => {
2023-12-15 15:16:24 +00:00
let source_expr = &this.thir[source];
let is_call =
2023-12-15 15:16:24 +00:00
matches!(source_expr.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
// (#66975) Source could be a const of type `!`, so has to
// exist in the generated MIR.
unpack!(
block =
this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
);
2016-06-25 10:42:52 +00:00
// This is an optimization. If the expression was a call then we already have an
// unreachable block. Don't bother to terminate it and create a new one.
if is_call {
block.unit()
} else {
2018-09-06 21:34:26 +00:00
this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
let end_block = this.cfg.start_new_block();
end_block.unit()
}
2016-06-25 10:42:52 +00:00
}
ExprKind::LogicalOp { op, lhs, rhs } => {
let condition_scope = this.local_scope();
let source_info = this.source_info(expr.span);
this.visit_coverage_branch_operation(op, expr.span);
// We first evaluate the left-hand side of the predicate ...
let (then_block, else_block) =
this.in_if_then_scope(condition_scope, expr.span, |this| {
this.then_else_break(
block,
2023-12-15 15:16:24 +00:00
lhs,
Some(condition_scope), // Temp scope
source_info,
// This flag controls how inner `let` expressions are lowered,
// but either way there shouldn't be any of those in here.
DeclareLetBindings::LetNotPermitted,
)
});
let (short_circuit, continuation, constant) = match op {
LogicalOp::And => (else_block, then_block, false),
LogicalOp::Or => (then_block, else_block, true),
};
// At this point, the control flow splits into a short-circuiting path
// and a continuation path.
// - If the operator is `&&`, passing `lhs` leads to continuation of evaluation on `rhs`;
// failing it leads to the short-circuting path which assigns `false` to the place.
// - If the operator is `||`, failing `lhs` leads to continuation of evaluation on `rhs`;
// passing it leads to the short-circuting path which assigns `true` to the place.
this.cfg.push_assign_constant(
short_circuit,
2018-09-06 21:34:26 +00:00
source_info,
destination,
ConstOperand {
span: expr.span,
user_ty: None,
const_: Const::from_bool(this.tcx, constant),
},
2018-09-06 21:34:26 +00:00
);
let mut rhs_block =
this.expr_into_dest(destination, continuation, rhs).into_block();
// Instrument the lowered RHS's value for condition coverage.
// (Does nothing if condition coverage is not enabled.)
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()
2015-08-18 21:59:21 +00:00
}
2019-06-19 15:21:28 +00:00
ExprKind::Loop { body } => {
// [block]
// |
// [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
// | ^ |
// false link | |
// | +-----------------------------------------+
// +-> [diverge_cleanup]
// The false link is required to make sure borrowck considers unwinds through the
// body, even when the exact code in the body cannot unwind
2015-08-18 21:59:21 +00:00
let loop_block = this.cfg.start_new_block();
// Start the loop.
this.cfg.goto(block, source_info, loop_block);
2015-08-18 21:59:21 +00:00
this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
// conduct the test, if necessary
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);
// The “return” value of the loop body must always be a unit. We therefore
// introduce a unit temporary as the destination for the loop body.
let tmp = this.get_unit_temp();
// Execute the body, branching back to the test.
let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block();
this.cfg.goto(body_block_end, source_info, loop_block);
// Loops are only exited by `break` expressions.
None
})
2015-08-18 21:59:21 +00:00
}
ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
2023-12-15 15:16:24 +00:00
let fun = unpack!(block = this.as_local_operand(block, fun));
let args: Box<[_]> = args
2020-12-30 19:09:26 +00:00
.into_iter()
.copied()
.map(|arg| Spanned {
node: unpack!(block = this.as_local_call_operand(block, arg)),
span: this.thir.exprs[arg].span,
})
2020-12-30 19:09:26 +00:00
.collect();
2020-12-30 19:09:26 +00:00
let success = this.cfg.start_new_block();
2020-12-30 19:09:26 +00:00
this.record_operands_moved(&args);
2021-02-24 20:29:09 +00:00
debug!("expr_into_dest: fn_span={:?}", fn_span);
2020-12-30 19:09:26 +00:00
this.cfg.terminate(block, source_info, TerminatorKind::Call {
func: fun,
args,
unwind: UnwindAction::Continue,
destination,
// The presence or absence of a return edge affects control-flow sensitive
// MIR checks and ultimately whether code is accepted or not. We can only
// omit the return edge if a return type is visibly uninhabited to a module
// that makes the call.
2022-10-23 22:32:17 +00:00
target: expr
.ty
.is_inhabited_from(
this.tcx,
this.parent_module,
this.infcx.typing_env(this.param_env),
)
2022-10-23 22:32:17 +00:00
.then_some(success),
call_source: if from_hir_call {
CallSource::Normal
} else {
CallSource::OverloadedOperator
2020-12-30 19:09:26 +00:00
},
fn_span,
2020-12-30 19:09:26 +00:00
});
this.diverge_from(block);
success.unit()
2015-08-18 21:59:21 +00:00
}
2023-12-15 15:16:24 +00:00
ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
ExprKind::Borrow { arg, borrow_kind } => {
// We don't do this in `as_rvalue` because we use `as_place`
// for borrow expressions, so we cannot create an `RValue` that
// remains valid across user code. `as_rvalue` is usually called
// by this method anyway, so this shouldn't cause too many
// unnecessary temporaries.
let arg_place = match borrow_kind {
2023-12-15 15:16:24 +00:00
BorrowKind::Shared => {
unpack!(block = this.as_read_only_place(block, arg))
}
2021-03-06 21:24:04 +00:00
_ => unpack!(block = this.as_place(block, arg)),
};
2021-03-06 21:24:04 +00:00
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 {
2021-03-06 21:24:04 +00:00
hir::Mutability::Not => this.as_read_only_place(block, arg),
hir::Mutability::Mut => this.as_place(block, arg),
};
let address_of = Rvalue::RawPtr(mutability, unpack!(block = place));
this.cfg.push_assign(block, source_info, destination, address_of);
block.unit()
}
ExprKind::Adt(box AdtExpr {
2021-06-13 13:39:57 +00:00
adt_def,
variant_index,
args,
ref user_ty,
2021-06-13 13:39:57 +00:00
ref fields,
ref base,
}) => {
// See the notes for `ExprKind::Array` in `as_rvalue` and for
// `ExprKind::Borrow` above.
let is_union = adt_def.is_union();
let active_field_index = is_union.then(|| fields[0].name);
let scope = this.local_temp_lifetime();
// first process the set of fields that were provided
// (evaluating them in order given by user)
let fields_map: FxHashMap<_, _> = fields
.into_iter()
.map(|f| {
(
f.name,
unpack!(
block = this.as_operand(
block,
scope,
2023-12-15 15:16:24 +00:00
f.expr,
LocalInfo::AggregateTemp,
NeedsTemporary::Maybe,
)
),
)
})
.collect();
Introduce `default_field_values` feature Initial implementation of `#[feature(default_field_values]`, proposed in https://github.com/rust-lang/rfcs/pull/3681. Support default fields in enum struct variant Allow default values in an enum struct variant definition: ```rust pub enum Bar { Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Allow using `..` without a base on an enum struct variant ```rust Bar::Foo { .. } ``` `#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants. Support `#[derive(Default)]` on enum struct variants with all defaulted fields ```rust pub enum Bar { #[default] Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Check for missing fields in typeck instead of mir_build. Expand test with `const` param case (needs `generic_const_exprs` enabled). Properly instantiate MIR const The following works: ```rust struct S<A> { a: Vec<A> = Vec::new(), } S::<i32> { .. } ``` Add lint for default fields that will always fail const-eval We *allow* this to happen for API writers that might want to rely on users' getting a compile error when using the default field, different to the error that they would get when the field isn't default. We could change this to *always* error instead of being a lint, if we wanted. This will *not* catch errors for partially evaluated consts, like when the expression relies on a const parameter. Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`: - Suggest adding a base expression if there are missing fields. - Suggest enabling the feature if all the missing fields have optional values. - Suggest removing `..` if there are no missing fields.
2024-08-24 17:22:48 +00:00
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 = unpack!(block = this.as_place_builder(block, *base));
2024-12-06 19:43:01 +00:00
// We desugar FRU as we lower to MIR, so for each
Introduce `default_field_values` feature Initial implementation of `#[feature(default_field_values]`, proposed in https://github.com/rust-lang/rfcs/pull/3681. Support default fields in enum struct variant Allow default values in an enum struct variant definition: ```rust pub enum Bar { Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Allow using `..` without a base on an enum struct variant ```rust Bar::Foo { .. } ``` `#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants. Support `#[derive(Default)]` on enum struct variants with all defaulted fields ```rust pub enum Bar { #[default] Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Check for missing fields in typeck instead of mir_build. Expand test with `const` param case (needs `generic_const_exprs` enabled). Properly instantiate MIR const The following works: ```rust struct S<A> { a: Vec<A> = Vec::new(), } S::<i32> { .. } ``` Add lint for default fields that will always fail const-eval We *allow* this to happen for API writers that might want to rely on users' getting a compile error when using the default field, different to the error that they would get when the field isn't default. We could change this to *always* error instead of being a lint, if we wanted. This will *not* catch errors for partially evaluated consts, like when the expression relies on a const parameter. Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`: - Suggest adding a base expression if there are missing fields. - Suggest enabling the feature if all the missing fields have optional values. - Suggest removing `..` if there are no missing fields.
2024-08-24 17:22:48 +00:00
// base-supplied field, generate an operand that
// reads it from the base.
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) {
Introduce `default_field_values` feature Initial implementation of `#[feature(default_field_values]`, proposed in https://github.com/rust-lang/rfcs/pull/3681. Support default fields in enum struct variant Allow default values in an enum struct variant definition: ```rust pub enum Bar { Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Allow using `..` without a base on an enum struct variant ```rust Bar::Foo { .. } ``` `#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants. Support `#[derive(Default)]` on enum struct variants with all defaulted fields ```rust pub enum Bar { #[default] Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Check for missing fields in typeck instead of mir_build. Expand test with `const` param case (needs `generic_const_exprs` enabled). Properly instantiate MIR const The following works: ```rust struct S<A> { a: Vec<A> = Vec::new(), } S::<i32> { .. } ``` Add lint for default fields that will always fail const-eval We *allow* this to happen for API writers that might want to rely on users' getting a compile error when using the default field, different to the error that they would get when the field isn't default. We could change this to *always* error instead of being a lint, if we wanted. This will *not* catch errors for partially evaluated consts, like when the expression relies on a const parameter. Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`: - Suggest adding a base expression if there are missing fields. - Suggest enabling the feature if all the missing fields have optional values. - Suggest removing `..` if there are no missing fields.
2024-08-24 17:22:48 +00:00
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,
}))
Introduce `default_field_values` feature Initial implementation of `#[feature(default_field_values]`, proposed in https://github.com/rust-lang/rfcs/pull/3681. Support default fields in enum struct variant Allow default values in an enum struct variant definition: ```rust pub enum Bar { Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Allow using `..` without a base on an enum struct variant ```rust Bar::Foo { .. } ``` `#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants. Support `#[derive(Default)]` on enum struct variants with all defaulted fields ```rust pub enum Bar { #[default] Foo { bar: S = S, baz: i32 = 42 + 3, } } ``` Check for missing fields in typeck instead of mir_build. Expand test with `const` param case (needs `generic_const_exprs` enabled). Properly instantiate MIR const The following works: ```rust struct S<A> { a: Vec<A> = Vec::new(), } S::<i32> { .. } ``` Add lint for default fields that will always fail const-eval We *allow* this to happen for API writers that might want to rely on users' getting a compile error when using the default field, different to the error that they would get when the field isn't default. We could change this to *always* error instead of being a lint, if we wanted. This will *not* catch errors for partially evaluated consts, like when the expression relies on a const parameter. Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`: - Suggest adding a base expression if there are missing fields. - Suggest enabling the feature if all the missing fields have optional values. - Suggest removing `..` if there are no missing fields.
2024-08-24 17:22:48 +00:00
}
None => {
let name = variant.fields[n].name;
span_bug!(
expr_span,
"missing mandatory field `{name}` of type `{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,
})
});
2021-08-05 01:16:19 +00:00
let adt = Box::new(AggregateKind::Adt(
adt_def.did(),
2021-03-06 21:24:04 +00:00
variant_index,
args,
user_ty,
active_field_index,
2021-08-05 01:16:19 +00:00
));
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) { vec![] } else { vec![destination_block] };
2020-02-14 18:17:50 +00:00
let operands = operands
.into_iter()
2021-03-06 21:24:04 +00:00
.map(|op| match *op {
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
2021-03-06 21:24:04 +00:00
reg,
2023-12-15 15:16:24 +00:00
value: unpack!(block = this.as_local_operand(block, expr)),
2020-02-14 18:17:50 +00:00
},
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::Out { reg, late, expr } => {
2020-02-14 18:17:50 +00:00
mir::InlineAsmOperand::Out {
2021-03-06 21:24:04 +00:00
reg,
late,
2023-12-15 15:16:24 +00:00
place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
2020-02-14 18:17:50 +00:00
}
}
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::InOut { reg, late, expr } => {
2023-12-15 15:16:24 +00:00
let place = unpack!(block = this.as_place(block, expr));
2020-02-14 18:17:50 +00:00
mir::InlineAsmOperand::InOut {
2021-03-06 21:24:04 +00:00
reg,
late,
2020-02-14 18:17:50 +00:00
// This works because asm operands must be Copy
in_value: Operand::Copy(place),
out_place: Some(place),
}
}
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
2020-02-14 18:17:50 +00:00
mir::InlineAsmOperand::InOut {
2021-03-06 21:24:04 +00:00
reg,
late,
2023-12-15 15:16:24 +00:00
in_value: unpack!(block = this.as_local_operand(block, in_expr)),
out_place: out_expr.map(|out_expr| {
2023-12-15 15:16:24 +00:00
unpack!(block = this.as_place(block, out_expr))
2020-02-14 18:17:50 +00:00
}),
}
}
2021-04-06 04:50:55 +00:00
thir::InlineAsmOperand::Const { value, span } => {
mir::InlineAsmOperand::Const {
value: Box::new(ConstOperand {
span,
user_ty: None,
const_: value,
}),
2021-04-06 04:50:55 +00:00
}
}
thir::InlineAsmOperand::SymFn { value, span } => {
mir::InlineAsmOperand::SymFn {
value: Box::new(ConstOperand {
span,
user_ty: None,
const_: value,
}),
}
}
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::SymStatic { def_id } => {
2021-03-06 21:24:04 +00:00
mir::InlineAsmOperand::SymStatic { def_id }
2020-02-14 18:17:50 +00:00
}
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 }
2023-12-25 21:19:04 +00:00
}
2020-02-14 18:17:50 +00:00
})
.collect();
if !expr.ty.is_never() {
this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
}
2020-02-14 18:17:50 +00:00
let asm_macro = match asm_macro {
AsmMacro::Asm => InlineAsmMacro::Asm,
AsmMacro::GlobalAsm => {
span_bug!(expr_span, "unexpected global_asm! in inline asm")
}
AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
};
2020-02-14 18:17:50 +00:00
this.cfg.terminate(block, source_info, TerminatorKind::InlineAsm {
asm_macro,
2020-02-14 18:17:50 +00:00
template,
operands,
2021-03-06 21:24:04 +00:00
options,
2020-05-26 19:07:59 +00:00
line_spans,
targets: targets.into_boxed_slice(),
2022-10-10 21:40:40 +00:00
unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
UnwindAction::Continue
} else {
UnwindAction::Unreachable
2020-02-14 18:17:50 +00:00
},
});
if options.contains(InlineAsmOptions::MAY_UNWIND) {
this.diverge_from(block);
}
destination_block.unit()
2020-02-14 18:17:50 +00:00
}
Various improvements to MIR and LLVM IR Construction Primarily affects the MIR construction, which indirectly improves LLVM IR generation, but some LLVM IR changes have been made too. * Handle "statement expressions" more intelligently. These are expressions that always evaluate to `()`. Previously a temporary would be generated as a destination to translate into, which is unnecessary. This affects assignment, augmented assignment, `return`, `break` and `continue`. * Avoid inserting drops for non-drop types in more places. Scheduled drops were already skipped for types that we knew wouldn't need dropping at construction time. However manually-inserted drops like those for `x` in `x = y;` were still generated. `build_drop` now takes a type parameter like its `schedule_drop` counterpart and checks to see if the type needs dropping. * Avoid generating an extra temporary for an assignment where the types involved don't need dropping. Previously an expression like `a = b + 1;` would result in a temporary for `b + 1`. This is so the RHS can be evaluated, then the LHS evaluated and dropped and have everything work correctly. However, this isn't necessary if the `LHS` doesn't need a drop, as we can just overwrite the existing value. * Improves lvalue analysis to allow treating an `Rvalue::Use` as an operand in certain conditions. The reason for it never being an operand is so it can be zeroed/drop-filled, but this is only true for types that need dropping. The first two changes result in significantly fewer MIR blocks being generated, as previously almost every statement would end up generating a new block due to the drop of the `()` temporary being generated.
2016-04-15 00:36:16 +00:00
// These cases don't actually need a destination
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()
Various improvements to MIR and LLVM IR Construction Primarily affects the MIR construction, which indirectly improves LLVM IR generation, but some LLVM IR changes have been made too. * Handle "statement expressions" more intelligently. These are expressions that always evaluate to `()`. Previously a temporary would be generated as a destination to translate into, which is unnecessary. This affects assignment, augmented assignment, `return`, `break` and `continue`. * Avoid inserting drops for non-drop types in more places. Scheduled drops were already skipped for types that we knew wouldn't need dropping at construction time. However manually-inserted drops like those for `x` in `x = y;` were still generated. `build_drop` now takes a type parameter like its `schedule_drop` counterpart and checks to see if the type needs dropping. * Avoid generating an extra temporary for an assignment where the types involved don't need dropping. Previously an expression like `a = b + 1;` would result in a temporary for `b + 1`. This is so the RHS can be evaluated, then the LHS evaluated and dropped and have everything work correctly. However, this isn't necessary if the `LHS` doesn't need a drop, as we can just overwrite the existing value. * Improves lvalue analysis to allow treating an `Rvalue::Use` as an operand in certain conditions. The reason for it never being an operand is so it can be zeroed/drop-filled, but this is only true for types that need dropping. The first two changes result in significantly fewer MIR blocks being generated, as previously almost every statement would end up generating a new block due to the drop of the `()` temporary being generated.
2016-04-15 00:36:16 +00:00
}
2022-11-21 12:47:42 +00:00
ExprKind::Continue { .. }
| ExprKind::Break { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. } => {
block = this.stmt_expr(block, expr_id, None).into_block();
2020-07-16 21:09:51 +00:00
// No assign, as these have type `!`.
block.unit()
}
// Avoid creating a temporary
2018-09-24 21:53:28 +00:00
ExprKind::VarRef { .. }
| ExprKind::UpvarRef { .. }
2018-09-24 21:53:28 +00:00
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. } => {
debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
2023-12-15 15:16:24 +00:00
let place = unpack!(block = this.as_place(block, expr_id));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
2018-09-06 21:34:26 +00:00
this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
}
2018-09-06 21:34:26 +00:00
ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
// Create a "fake" temporary variable so that we check that the
// value is Sized. Usually, this is caught in type checking, but
// in the case of box expr there is no such check.
if !destination.projection.is_empty() {
this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
}
2023-12-15 15:16:24 +00:00
let place = unpack!(block = this.as_place(block, expr_id));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
2018-09-06 21:34:26 +00:00
this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
}
ExprKind::Yield { value } => {
let scope = this.local_temp_lifetime();
let value = unpack!(
block =
this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
);
let resume = this.cfg.start_new_block();
this.cfg.terminate(block, source_info, TerminatorKind::Yield {
value,
resume,
resume_arg: destination,
drop: None,
});
2023-10-19 21:46:28 +00:00
this.coroutine_drop_cleanup(block);
resume.unit()
}
2015-08-18 21:59:21 +00:00
// these are the cases that are more naturally handled by some other mode
2018-09-06 21:34:26 +00:00
ExprKind::Unary { .. }
| ExprKind::Binary { .. }
| ExprKind::Box { .. }
| ExprKind::Cast { .. }
| ExprKind::PointerCoercion { .. }
2018-09-06 21:34:26 +00:00
| ExprKind::Repeat { .. }
| ExprKind::Array { .. }
| ExprKind::Tuple { .. }
| ExprKind::Closure { .. }
2020-10-06 20:51:15 +00:00
| ExprKind::ConstBlock { .. }
2018-09-06 21:34:26 +00:00
| ExprKind::Literal { .. }
2022-03-11 11:07:53 +00:00
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ZstLiteral { .. }
2022-03-11 11:07:53 +00:00
| ExprKind::ConstParam { .. }
2020-05-02 19:44:25 +00:00
| ExprKind::ThreadLocalRef(_)
2022-09-11 07:37:49 +00:00
| ExprKind::StaticRef { .. }
| ExprKind::OffsetOf { .. } => {
2015-08-18 21:59:21 +00:00
debug_assert!(match Category::of(&expr.kind).unwrap() {
2018-09-24 21:53:28 +00:00
// should be handled above
2015-08-18 21:59:21 +00:00
Category::Rvalue(RvalueFunc::Into) => false,
2018-09-24 21:53:28 +00:00
// must be handled above or else we get an
// infinite loop in the builder; see
// e.g., `ExprKind::VarRef` above
2018-09-24 21:53:28 +00:00
Category::Place => false,
2015-08-18 21:59:21 +00:00
_ => true,
});
2023-12-15 15:16:24 +00:00
let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id));
2018-11-20 19:07:17 +00:00
this.cfg.push_assign(block, source_info, destination, rvalue);
2015-08-18 21:59:21 +00:00
block.unit()
}
};
if !expr_is_block_or_scope {
let popped = this.block_context.pop();
assert!(popped.is_some());
2015-08-18 21:59:21 +00:00
}
block_and
2015-08-18 21:59:21 +00:00
}
2022-06-10 21:12:58 +00:00
fn is_let(&self, expr: ExprId) -> bool {
match self.thir[expr].kind {
ExprKind::Let { .. } => true,
ExprKind::Scope { value, .. } => self.is_let(value),
_ => false,
}
}
2015-08-18 21:59:21 +00:00
}