2015-08-18 21:59:21 +00:00
|
|
|
//! See docs in build/expr/mod.rs
|
|
|
|
|
2024-09-05 17:45:40 +00:00
|
|
|
use rustc_ast::{AsmMacro, InlineAsmOptions};
|
2019-10-19 20:01:36 +00:00
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
2020-07-24 08:00:10 +00:00
|
|
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
2020-01-05 01:37:57 +00:00
|
|
|
use rustc_hir as hir;
|
2020-04-15 22:00:22 +00:00
|
|
|
use rustc_middle::mir::*;
|
2024-05-08 09:31:05 +00:00
|
|
|
use rustc_middle::span_bug;
|
2021-04-04 16:42:17 +00:00
|
|
|
use rustc_middle::thir::*;
|
2022-02-16 09:54:36 +00:00
|
|
|
use rustc_middle::ty::CanonicalUserTypeAnnotation;
|
2024-01-12 07:21:42 +00:00
|
|
|
use rustc_span::source_map::Spanned;
|
2024-05-22 04:35:22 +00:00
|
|
|
use tracing::{debug, instrument};
|
2017-04-18 12:05:27 +00:00
|
|
|
|
2024-12-16 01:44:54 +00:00
|
|
|
use crate::builder::expr::category::{Category, RvalueFunc};
|
|
|
|
use crate::builder::matches::DeclareLetBindings;
|
|
|
|
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
|
2024-07-28 22:13:50 +00:00
|
|
|
|
2019-06-01 11:38:36 +00:00
|
|
|
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))]
|
2022-05-20 23:51:09 +00:00
|
|
|
pub(crate) fn expr_into_dest(
|
2018-09-06 21:34:26 +00:00
|
|
|
&mut self,
|
2020-03-31 17:08:48 +00:00
|
|
|
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;
|
2016-06-07 16:21:56 +00:00
|
|
|
let source_info = this.source_info(expr_span);
|
2015-08-18 21:59:21 +00:00
|
|
|
|
2021-01-09 17:00:45 +00:00
|
|
|
let expr_is_block_or_scope =
|
|
|
|
matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
|
2018-09-21 22:51:48 +00:00
|
|
|
|
|
|
|
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);
|
2020-07-24 08:00:10 +00:00
|
|
|
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)
|
2020-07-24 08:00:10 +00:00
|
|
|
})
|
|
|
|
})
|
2015-08-18 21:59:21 +00:00
|
|
|
}
|
2022-08-24 02:02:17 +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,
|
|
|
|
),
|
2021-09-01 21:52:17 +00:00
|
|
|
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);
|
2021-09-01 21:52:17 +00:00
|
|
|
let condition_scope = this.local_scope();
|
|
|
|
|
2024-03-06 03:56:13 +00:00
|
|
|
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?
|
2024-03-06 03:56:13 +00:00
|
|
|
let source_info = if this.is_let(cond) {
|
|
|
|
let variable_scope =
|
2024-02-27 11:57:52 +00:00
|
|
|
this.new_source_scope(then_span, LintLevel::Inherited);
|
2024-03-06 03:56:13 +00:00
|
|
|
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.
|
2024-03-06 03:56:13 +00:00
|
|
|
let (then_block, else_block) =
|
|
|
|
this.in_if_then_scope(condition_scope, then_span, |this| {
|
2024-07-06 01:45:47 +00:00
|
|
|
let then_blk = this
|
|
|
|
.then_else_break(
|
|
|
|
block,
|
|
|
|
cond,
|
|
|
|
Some(condition_scope), // Temp scope
|
|
|
|
source_info,
|
|
|
|
DeclareLetBindings::Yes, // Declare `let` bindings normally
|
|
|
|
)
|
|
|
|
.into_block();
|
2022-06-10 06:09:04 +00:00
|
|
|
|
2024-03-06 04:22:39 +00:00
|
|
|
// Lower the `then` arm into its block.
|
2024-03-06 03:56:13 +00:00
|
|
|
this.expr_into_dest(destination, then_blk, then)
|
|
|
|
});
|
2024-03-06 04:22:39 +00:00
|
|
|
|
|
|
|
// Pack `(then_block, else_block)` into `BlockAnd<BasicBlock>`.
|
2024-03-06 03:56:13 +00:00
|
|
|
then_block.and(else_block)
|
|
|
|
},
|
2021-09-01 21:52:17 +00:00
|
|
|
);
|
|
|
|
|
2024-03-06 03:56:13 +00:00
|
|
|
// Unpack `BlockAnd<BasicBlock>` into `(then_blk, else_blk)`.
|
|
|
|
let (then_blk, mut else_blk);
|
|
|
|
else_blk = unpack!(then_blk = then_and_else_blocks);
|
|
|
|
|
2024-03-06 04:11:21 +00:00
|
|
|
// If there is an `else` arm, lower it into `else_blk`.
|
|
|
|
if let Some(else_expr) = else_opt {
|
2024-07-06 01:55:54 +00:00
|
|
|
else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block();
|
2021-01-01 18:38:11 +00:00
|
|
|
} else {
|
2024-03-06 04:11:21 +00:00
|
|
|
// 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);
|
2024-03-06 04:11:21 +00:00
|
|
|
}
|
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()
|
2021-01-09 17:00:45 +00:00
|
|
|
}
|
2024-03-02 06:45:38 +00:00
|
|
|
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
|
|
|
}
|
2016-08-02 07:56:20 +00:00
|
|
|
ExprKind::NeverToAny { source } => {
|
2023-12-15 15:16:24 +00:00
|
|
|
let source_expr = &this.thir[source];
|
2021-01-09 17:00:45 +00:00
|
|
|
let is_call =
|
2023-12-15 15:16:24 +00:00
|
|
|
matches!(source_expr.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
|
2016-06-27 17:44:50 +00:00
|
|
|
|
2019-12-08 21:50:23 +00:00
|
|
|
// (#66975) Source could be a const of type `!`, so has to
|
|
|
|
// exist in the generated MIR.
|
2021-01-09 17:00:45 +00:00
|
|
|
unpack!(
|
2024-09-01 17:13:07 +00:00
|
|
|
block =
|
|
|
|
this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
|
2021-01-09 17:00:45 +00:00
|
|
|
);
|
2016-06-25 10:42:52 +00:00
|
|
|
|
2016-06-27 17:44:50 +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()
|
2016-08-02 05:46:39 +00:00
|
|
|
} else {
|
2018-09-06 21:34:26 +00:00
|
|
|
this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
|
2016-06-27 17:44:50 +00:00
|
|
|
let end_block = this.cfg.start_new_block();
|
|
|
|
end_block.unit()
|
|
|
|
}
|
2016-06-25 10:42:52 +00:00
|
|
|
}
|
2023-08-01 21:34:55 +00:00
|
|
|
ExprKind::LogicalOp { op, lhs, rhs } => {
|
2023-06-20 08:29:39 +00:00
|
|
|
let condition_scope = this.local_scope();
|
|
|
|
let source_info = this.source_info(expr.span);
|
2024-05-03 09:56:13 +00:00
|
|
|
|
|
|
|
this.visit_coverage_branch_operation(op, expr.span);
|
|
|
|
|
2023-08-01 21:34:55 +00:00
|
|
|
// We first evaluate the left-hand side of the predicate ...
|
2023-06-20 08:29:39 +00:00
|
|
|
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,
|
2024-02-27 06:43:07 +00:00
|
|
|
Some(condition_scope), // Temp scope
|
2023-06-20 08:29:39 +00:00
|
|
|
source_info,
|
2024-02-27 06:43:07 +00:00
|
|
|
// This flag controls how inner `let` expressions are lowered,
|
|
|
|
// but either way there shouldn't be any of those in here.
|
2024-06-26 01:34:31 +00:00
|
|
|
DeclareLetBindings::LetNotPermitted,
|
2023-06-20 08:29:39 +00:00
|
|
|
)
|
|
|
|
});
|
2023-08-01 21:34:55 +00:00
|
|
|
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.
|
2023-06-20 08:29:39 +00:00
|
|
|
this.cfg.push_assign_constant(
|
2023-08-01 21:34:55 +00:00
|
|
|
short_circuit,
|
2018-09-06 21:34:26 +00:00
|
|
|
source_info,
|
|
|
|
destination,
|
2023-09-20 18:51:14 +00:00
|
|
|
ConstOperand {
|
2023-06-20 08:29:39 +00:00
|
|
|
span: expr.span,
|
2021-03-03 15:35:54 +00:00
|
|
|
user_ty: None,
|
2023-09-20 18:51:14 +00:00
|
|
|
const_: Const::from_bool(this.tcx, constant),
|
2021-03-03 15:35:54 +00:00
|
|
|
},
|
2018-09-06 21:34:26 +00:00
|
|
|
);
|
2024-07-06 01:45:47 +00:00
|
|
|
let mut rhs_block =
|
|
|
|
this.expr_into_dest(destination, continuation, rhs).into_block();
|
2024-05-01 07:33:33 +00:00
|
|
|
// 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);
|
|
|
|
|
2023-06-20 08:29:39 +00:00
|
|
|
let target = this.cfg.start_new_block();
|
2024-05-01 07:33:33 +00:00
|
|
|
this.cfg.goto(rhs_block, source_info, target);
|
2023-08-01 21:34:55 +00:00
|
|
|
this.cfg.goto(short_circuit, source_info, target);
|
2023-06-20 08:29:39 +00:00
|
|
|
target.unit()
|
2015-08-18 21:59:21 +00:00
|
|
|
}
|
2019-06-19 15:21:28 +00:00
|
|
|
ExprKind::Loop { body } => {
|
2018-02-07 19:25:08 +00:00
|
|
|
// [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();
|
|
|
|
|
2019-12-20 17:27:05 +00:00
|
|
|
// Start the loop.
|
|
|
|
this.cfg.goto(block, source_info, loop_block);
|
2015-08-18 21:59:21 +00:00
|
|
|
|
2021-01-22 03:35:05 +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();
|
2022-10-08 22:47:59 +00:00
|
|
|
this.cfg.terminate(loop_block, source_info, TerminatorKind::FalseUnwind {
|
|
|
|
real_target: body_block,
|
|
|
|
unwind: UnwindAction::Continue,
|
2021-01-22 03:35:05 +00:00
|
|
|
});
|
|
|
|
this.diverge_from(loop_block);
|
|
|
|
|
2021-08-22 16:15:49 +00:00
|
|
|
// The “return” value of the loop body must always be a unit. We therefore
|
2021-01-22 03:35:05 +00:00
|
|
|
// 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.
|
2024-07-06 01:45:47 +00:00
|
|
|
let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block();
|
2021-01-22 03:35:05 +00:00
|
|
|
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
|
|
|
}
|
2021-04-03 17:58:46 +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));
|
2024-06-21 09:20:18 +00:00
|
|
|
let args: Box<[_]> = args
|
2020-12-30 19:09:26 +00:00
|
|
|
.into_iter()
|
2021-04-03 17:58:46 +00:00
|
|
|
.copied()
|
2024-01-12 07:21:42 +00:00
|
|
|
.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();
|
2017-04-18 12:05:27 +00:00
|
|
|
|
2020-12-30 19:09:26 +00:00
|
|
|
let success = this.cfg.start_new_block();
|
2017-04-18 12:05:27 +00:00
|
|
|
|
2020-12-30 19:09:26 +00:00
|
|
|
this.record_operands_moved(&args);
|
2017-04-18 12:05:27 +00:00
|
|
|
|
2021-02-24 20:29:09 +00:00
|
|
|
debug!("expr_into_dest: fn_span={:?}", fn_span);
|
2019-09-19 15:41:10 +00:00
|
|
|
|
2020-12-30 19:09:26 +00:00
|
|
|
this.cfg.terminate(block, source_info, TerminatorKind::Call {
|
|
|
|
func: fun,
|
|
|
|
args,
|
2022-10-08 22:47:59 +00:00
|
|
|
unwind: UnwindAction::Continue,
|
2022-04-16 13:27:54 +00:00
|
|
|
destination,
|
2022-01-25 00:00:00 +00:00
|
|
|
// 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
|
2024-11-15 12:53:31 +00:00
|
|
|
.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),
|
2023-06-18 05:24:38 +00:00
|
|
|
call_source: if from_hir_call {
|
|
|
|
CallSource::Normal
|
|
|
|
} else {
|
|
|
|
CallSource::OverloadedOperator
|
2020-12-30 19:09:26 +00:00
|
|
|
},
|
2024-09-22 23:05:04 +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),
|
2019-10-19 20:01:36 +00:00
|
|
|
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)),
|
2019-10-19 20:01:36 +00:00
|
|
|
};
|
2021-03-06 21:24:04 +00:00
|
|
|
let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
|
2019-10-19 20:01:36 +00:00
|
|
|
this.cfg.push_assign(block, source_info, destination, borrow);
|
|
|
|
block.unit()
|
|
|
|
}
|
2024-08-12 08:57:57 +00:00
|
|
|
ExprKind::RawBorrow { mutability, arg } => {
|
2019-12-20 21:39:20 +00:00
|
|
|
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),
|
2019-04-20 17:06:03 +00:00
|
|
|
};
|
2024-08-12 08:57:57 +00:00
|
|
|
let address_of = Rvalue::RawPtr(mutability, unpack!(block = place));
|
2019-04-20 17:06:03 +00:00
|
|
|
this.cfg.push_assign(block, source_info, destination, address_of);
|
|
|
|
block.unit()
|
|
|
|
}
|
2022-08-24 02:59:06 +00:00
|
|
|
ExprKind::Adt(box AdtExpr {
|
2021-06-13 13:39:57 +00:00
|
|
|
adt_def,
|
|
|
|
variant_index,
|
2023-07-11 21:35:29 +00:00
|
|
|
args,
|
2022-08-24 00:37:41 +00:00
|
|
|
ref user_ty,
|
2021-06-13 13:39:57 +00:00
|
|
|
ref fields,
|
|
|
|
ref base,
|
|
|
|
}) => {
|
2019-10-19 20:01:36 +00:00
|
|
|
// See the notes for `ExprKind::Array` in `as_rvalue` and for
|
|
|
|
// `ExprKind::Borrow` above.
|
|
|
|
let is_union = adt_def.is_union();
|
2023-04-02 03:11:38 +00:00
|
|
|
let active_field_index = is_union.then(|| fields[0].name);
|
2019-10-19 20:01:36 +00:00
|
|
|
|
2024-09-01 17:13:07 +00:00
|
|
|
let scope = this.local_temp_lifetime();
|
2019-10-19 20:01:36 +00:00
|
|
|
|
|
|
|
// first process the set of fields that were provided
|
|
|
|
// (evaluating them in order given by user)
|
|
|
|
let fields_map: FxHashMap<_, _> = fields
|
|
|
|
.into_iter()
|
2021-04-03 17:58:46 +00:00
|
|
|
.map(|f| {
|
|
|
|
(
|
|
|
|
f.name,
|
|
|
|
unpack!(
|
2021-09-06 21:59:24 +00:00
|
|
|
block = this.as_operand(
|
|
|
|
block,
|
2024-09-01 17:13:07 +00:00
|
|
|
scope,
|
2023-12-15 15:16:24 +00:00
|
|
|
f.expr,
|
2023-03-09 16:55:20 +00:00
|
|
|
LocalInfo::AggregateTemp,
|
2022-03-09 17:10:48 +00:00
|
|
|
NeedsTemporary::Maybe,
|
2021-09-06 21:59:24 +00:00
|
|
|
)
|
2021-04-03 17:58:46 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
})
|
2019-10-19 20:01:36 +00:00
|
|
|
.collect();
|
|
|
|
|
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
|
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) => {
|
2024-12-14 18:04:09 +00:00
|
|
|
itertools::zip_eq(field_names, field_types)
|
|
|
|
.map(|(n, &ty)| match fields_map.get(&n) {
|
2024-08-24 17:22:48 +00:00
|
|
|
Some(v) => v.clone(),
|
|
|
|
None => match variant.fields[n].value {
|
|
|
|
Some(def) => {
|
2024-12-14 18:04:09 +00:00
|
|
|
let value = Const::Unevaluated(
|
|
|
|
UnevaluatedConst::new(def, args),
|
|
|
|
ty,
|
|
|
|
);
|
|
|
|
Operand::Constant(Box::new(ConstOperand {
|
|
|
|
span: expr_span,
|
|
|
|
user_ty: None,
|
|
|
|
const_: value,
|
|
|
|
}))
|
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()
|
|
|
|
}
|
2020-04-15 22:00:22 +00:00
|
|
|
};
|
2019-10-19 20:01:36 +00:00
|
|
|
|
|
|
|
let inferred_ty = expr.ty;
|
2022-08-26 01:53:54 +00:00
|
|
|
let user_ty = user_ty.as_ref().map(|user_ty| {
|
2019-10-19 20:01:36 +00:00
|
|
|
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
|
|
|
|
span: source_info.span,
|
2022-08-26 01:53:54 +00:00
|
|
|
user_ty: user_ty.clone(),
|
2019-10-19 20:01:36 +00:00
|
|
|
inferred_ty,
|
|
|
|
})
|
|
|
|
});
|
2021-08-05 01:16:19 +00:00
|
|
|
let adt = Box::new(AggregateKind::Adt(
|
2022-03-04 20:28:41 +00:00
|
|
|
adt_def.did(),
|
2021-03-06 21:24:04 +00:00
|
|
|
variant_index,
|
2023-07-11 21:35:29 +00:00
|
|
|
args,
|
2019-10-19 20:01:36 +00:00
|
|
|
user_ty,
|
|
|
|
active_field_index,
|
2021-08-05 01:16:19 +00:00
|
|
|
));
|
2019-10-19 20:01:36 +00:00
|
|
|
this.cfg.push_assign(
|
|
|
|
block,
|
|
|
|
source_info,
|
|
|
|
destination,
|
|
|
|
Rvalue::Aggregate(adt, fields),
|
|
|
|
);
|
|
|
|
block.unit()
|
|
|
|
}
|
2022-08-24 02:59:06 +00:00
|
|
|
ExprKind::InlineAsm(box InlineAsmExpr {
|
2024-09-05 17:45:40 +00:00
|
|
|
asm_macro,
|
2022-08-24 02:59:06 +00:00
|
|
|
template,
|
|
|
|
ref operands,
|
|
|
|
options,
|
|
|
|
line_spans,
|
|
|
|
}) => {
|
2021-04-04 00:24:02 +00:00
|
|
|
use rustc_middle::{mir, thir};
|
2023-12-26 16:07:35 +00:00
|
|
|
|
|
|
|
let destination_block = this.cfg.start_new_block();
|
2024-09-05 17:45:40 +00:00
|
|
|
let mut targets =
|
|
|
|
if asm_macro.diverges(options) { vec![] } else { vec![destination_block] };
|
2023-12-26 16:07:35 +00:00
|
|
|
|
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)),
|
2021-04-03 17:58:46 +00:00
|
|
|
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 {
|
2023-09-20 18:51:14 +00:00
|
|
|
value: Box::new(ConstOperand {
|
|
|
|
span,
|
|
|
|
user_ty: None,
|
|
|
|
const_: value,
|
|
|
|
}),
|
2021-04-06 04:50:55 +00:00
|
|
|
}
|
|
|
|
}
|
2022-03-01 00:50:56 +00:00
|
|
|
thir::InlineAsmOperand::SymFn { value, span } => {
|
|
|
|
mir::InlineAsmOperand::SymFn {
|
2023-09-20 18:51:14 +00:00
|
|
|
value: Box::new(ConstOperand {
|
|
|
|
span,
|
|
|
|
user_ty: None,
|
|
|
|
const_: value,
|
|
|
|
}),
|
2022-03-01 00:50:56 +00:00
|
|
|
}
|
|
|
|
}
|
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
|
|
|
}
|
2023-12-26 16:07:35 +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();
|
2024-07-06 01:45:47 +00:00
|
|
|
let target =
|
|
|
|
this.ast_block(tmp, target, block, source_info).into_block();
|
2023-12-26 16:07:35 +00:00
|
|
|
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();
|
|
|
|
|
2023-12-26 16:07:35 +00:00
|
|
|
if !expr.ty.is_never() {
|
2021-09-27 21:06:16 +00:00
|
|
|
this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
|
|
|
|
}
|
2020-02-14 18:17:50 +00:00
|
|
|
|
2024-09-05 17:45:40 +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 {
|
2024-09-05 17:45:40 +00:00
|
|
|
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,
|
2024-06-21 09:20:18 +00:00
|
|
|
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
|
|
|
},
|
|
|
|
});
|
2021-08-30 00:23:33 +00:00
|
|
|
if options.contains(InlineAsmOptions::MAY_UNWIND) {
|
|
|
|
this.diverge_from(block);
|
|
|
|
}
|
2021-09-27 21:06:16 +00:00
|
|
|
destination_block.unit()
|
2020-02-14 18:17:50 +00:00
|
|
|
}
|
2019-10-19 20:01:36 +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
|
2022-01-12 00:00:00 +00:00
|
|
|
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
|
2024-07-06 01:55:54 +00:00
|
|
|
block = this.stmt_expr(block, expr_id, None).into_block();
|
2021-03-03 15:35:54 +00:00
|
|
|
this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
|
2017-11-27 15:58:59 +00:00
|
|
|
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 { .. } => {
|
2024-07-06 01:55:54 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2018-06-27 21:04:45 +00:00
|
|
|
// Avoid creating a temporary
|
2018-09-24 21:53:28 +00:00
|
|
|
ExprKind::VarRef { .. }
|
2020-11-17 06:52:14 +00:00
|
|
|
| ExprKind::UpvarRef { .. }
|
2018-09-24 21:53:28 +00:00
|
|
|
| ExprKind::PlaceTypeAscription { .. }
|
|
|
|
| ExprKind::ValueTypeAscription { .. } => {
|
2018-06-27 21:04:45 +00:00
|
|
|
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));
|
2018-06-27 21:04:45 +00:00
|
|
|
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);
|
2018-06-27 21:04:45 +00:00
|
|
|
block.unit()
|
|
|
|
}
|
2018-09-06 21:34:26 +00:00
|
|
|
ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
|
2021-03-14 14:06:18 +00:00
|
|
|
debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
|
2018-06-27 21:04:45 +00:00
|
|
|
|
|
|
|
// 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.
|
2019-07-29 22:07:28 +00:00
|
|
|
if !destination.projection.is_empty() {
|
2020-05-06 00:17:38 +00:00
|
|
|
this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
|
2018-06-27 21:04:45 +00:00
|
|
|
}
|
|
|
|
|
2023-12-15 15:16:24 +00:00
|
|
|
let place = unpack!(block = this.as_place(block, expr_id));
|
2018-06-27 21:04:45 +00:00
|
|
|
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);
|
2018-06-27 21:04:45 +00:00
|
|
|
block.unit()
|
|
|
|
}
|
|
|
|
|
2020-01-25 01:31:32 +00:00
|
|
|
ExprKind::Yield { value } => {
|
2024-09-01 17:13:07 +00:00
|
|
|
let scope = this.local_temp_lifetime();
|
2022-03-09 17:10:48 +00:00
|
|
|
let value = unpack!(
|
2024-09-01 17:13:07 +00:00
|
|
|
block =
|
|
|
|
this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
|
2022-03-09 17:10:48 +00:00
|
|
|
);
|
2020-01-25 01:31:32 +00:00
|
|
|
let resume = this.cfg.start_new_block();
|
2019-11-16 13:23:31 +00:00
|
|
|
this.cfg.terminate(block, source_info, TerminatorKind::Yield {
|
|
|
|
value,
|
|
|
|
resume,
|
|
|
|
resume_arg: destination,
|
|
|
|
drop: None,
|
2020-01-25 01:31:32 +00:00
|
|
|
});
|
2023-10-19 21:46:28 +00:00
|
|
|
this.coroutine_drop_cleanup(block);
|
2020-01-25 01:31:32 +00:00
|
|
|
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 { .. }
|
2023-07-05 18:07:03 +00:00
|
|
|
| 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 { .. }
|
2022-03-23 07:47:11 +00:00
|
|
|
| ExprKind::NonHirLiteral { .. }
|
2022-07-03 15:17:23 +00:00
|
|
|
| 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
|
2018-11-27 02:59:49 +00:00
|
|
|
// 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()
|
|
|
|
}
|
2018-09-21 22:51:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if !expr_is_block_or_scope {
|
|
|
|
let popped = this.block_context.pop();
|
|
|
|
assert!(popped.is_some());
|
2015-08-18 21:59:21 +00:00
|
|
|
}
|
2018-09-21 22:51:48 +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
|
|
|
}
|