summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_build
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /compiler/rustc_mir_build
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_mir_build')
-rw-r--r--compiler/rustc_mir_build/Cargo.toml26
-rw-r--r--compiler/rustc_mir_build/src/build/block.rs240
-rw-r--r--compiler/rustc_mir_build/src/build/cfg.rs113
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs152
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_operand.rs184
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_place.rs820
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_rvalue.rs694
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_temp.rs119
-rw-r--r--compiler/rustc_mir_build/src/build/expr/category.rs92
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs599
-rw-r--r--compiler/rustc_mir_build/src/build/expr/mod.rs70
-rw-r--r--compiler/rustc_mir_build/src/build/expr/stmt.rs149
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs2354
-rw-r--r--compiler/rustc_mir_build/src/build/matches/simplify.rs318
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs837
-rw-r--r--compiler/rustc_mir_build/src/build/matches/util.rs109
-rw-r--r--compiler/rustc_mir_build/src/build/misc.rs75
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs1171
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs1395
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs680
-rw-r--r--compiler/rustc_mir_build/src/lib.rs35
-rw-r--r--compiler/rustc_mir_build/src/lints.rs166
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs52
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/block.rs126
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs1117
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/mod.rs101
-rw-r--r--compiler/rustc_mir_build/src/thir/mod.rs13
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs1162
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs601
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs1711
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs802
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs978
-rw-r--r--compiler/rustc_mir_build/src/thir/util.rs31
33 files changed, 17092 insertions, 0 deletions
diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml
new file mode 100644
index 000000000..30f90e383
--- /dev/null
+++ b/compiler/rustc_mir_build/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "rustc_mir_build"
+version = "0.0.0"
+edition = "2021"
+
+[lib]
+doctest = false
+
+[dependencies]
+rustc_arena = { path = "../rustc_arena" }
+tracing = "0.1"
+rustc_middle = { path = "../rustc_middle" }
+rustc_apfloat = { path = "../rustc_apfloat" }
+rustc_attr = { path = "../rustc_attr" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_index = { path = "../rustc_index" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_infer = { path = "../rustc_infer" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_session = { path = "../rustc_session" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
+rustc_trait_selection = { path = "../rustc_trait_selection" }
+rustc_ast = { path = "../rustc_ast" }
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs
new file mode 100644
index 000000000..687560012
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/block.rs
@@ -0,0 +1,240 @@
+use crate::build::matches::ArmHasGuard;
+use crate::build::ForGuard::OutsideGuard;
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use rustc_middle::thir::*;
+use rustc_middle::{mir::*, ty};
+use rustc_span::Span;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ pub(crate) fn ast_block(
+ &mut self,
+ destination: Place<'tcx>,
+ block: BasicBlock,
+ ast_block: &Block,
+ source_info: SourceInfo,
+ ) -> BlockAnd<()> {
+ let Block {
+ region_scope,
+ opt_destruction_scope,
+ span,
+ ref stmts,
+ expr,
+ targeted_by_break,
+ safety_mode,
+ } = *ast_block;
+ let expr = expr.map(|expr| &self.thir[expr]);
+ self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
+ this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
+ if targeted_by_break {
+ this.in_breakable_scope(None, destination, span, |this| {
+ Some(this.ast_block_stmts(
+ destination,
+ block,
+ span,
+ &stmts,
+ expr,
+ safety_mode,
+ ))
+ })
+ } else {
+ this.ast_block_stmts(destination, block, span, &stmts, expr, safety_mode)
+ }
+ })
+ })
+ }
+
+ fn ast_block_stmts(
+ &mut self,
+ destination: Place<'tcx>,
+ mut block: BasicBlock,
+ span: Span,
+ stmts: &[StmtId],
+ expr: Option<&Expr<'tcx>>,
+ safety_mode: BlockSafety,
+ ) -> BlockAnd<()> {
+ let this = self;
+
+ // This convoluted structure is to avoid using recursion as we walk down a list
+ // of statements. Basically, the structure we get back is something like:
+ //
+ // let x = <init> in {
+ // expr1;
+ // let y = <init> in {
+ // expr2;
+ // expr3;
+ // ...
+ // }
+ // }
+ //
+ // The let bindings are valid till the end of block so all we have to do is to pop all
+ // the let-scopes at the end.
+ //
+ // First we build all the statements in the block.
+ let mut let_scope_stack = Vec::with_capacity(8);
+ let outer_source_scope = this.source_scope;
+ let outer_in_scope_unsafe = this.in_scope_unsafe;
+ this.update_source_scope_for_safety_mode(span, safety_mode);
+
+ let source_info = this.source_info(span);
+ for stmt in stmts {
+ let Stmt { ref kind, opt_destruction_scope } = this.thir[*stmt];
+ match kind {
+ StmtKind::Expr { scope, expr } => {
+ this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
+ unpack!(
+ block = this.in_opt_scope(
+ opt_destruction_scope.map(|de| (de, source_info)),
+ |this| {
+ let si = (*scope, source_info);
+ this.in_scope(si, LintLevel::Inherited, |this| {
+ this.stmt_expr(block, &this.thir[*expr], Some(*scope))
+ })
+ }
+ )
+ );
+ }
+ StmtKind::Let {
+ remainder_scope,
+ init_scope,
+ ref pattern,
+ initializer,
+ lint_level,
+ else_block,
+ } => {
+ let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild);
+ this.block_context.push(BlockFrame::Statement { ignores_expr_result });
+
+ // Enter the remainder scope, i.e., the bindings' destruction scope.
+ this.push_scope((*remainder_scope, source_info));
+ let_scope_stack.push(remainder_scope);
+
+ // Declare the bindings, which may create a source scope.
+ let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
+
+ let visibility_scope =
+ Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
+
+ // Evaluate the initializer, if present.
+ if let Some(init) = initializer {
+ let init = &this.thir[*init];
+ let initializer_span = init.span;
+
+ unpack!(
+ block = this.in_opt_scope(
+ opt_destruction_scope.map(|de| (de, source_info)),
+ |this| {
+ let scope = (*init_scope, source_info);
+ this.in_scope(scope, *lint_level, |this| {
+ if let Some(else_block) = else_block {
+ this.ast_let_else(
+ block,
+ init,
+ initializer_span,
+ else_block,
+ visibility_scope,
+ *remainder_scope,
+ remainder_span,
+ pattern,
+ )
+ } else {
+ this.declare_bindings(
+ visibility_scope,
+ remainder_span,
+ pattern,
+ ArmHasGuard(false),
+ Some((None, initializer_span)),
+ );
+ this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern
+ }
+ })
+ },
+ )
+ )
+ } else {
+ let scope = (*init_scope, source_info);
+ unpack!(this.in_scope(scope, *lint_level, |this| {
+ this.declare_bindings(
+ visibility_scope,
+ remainder_span,
+ pattern,
+ ArmHasGuard(false),
+ None,
+ );
+ block.unit()
+ }));
+
+ debug!("ast_block_stmts: pattern={:?}", pattern);
+ this.visit_primary_bindings(
+ pattern,
+ UserTypeProjections::none(),
+ &mut |this, _, _, _, node, span, _, _| {
+ this.storage_live_binding(block, node, span, OutsideGuard, true);
+ this.schedule_drop_for_binding(node, span, OutsideGuard);
+ },
+ )
+ }
+
+ // Enter the visibility scope, after evaluating the initializer.
+ if let Some(source_scope) = visibility_scope {
+ this.source_scope = source_scope;
+ }
+ }
+ }
+
+ let popped = this.block_context.pop();
+ assert!(popped.map_or(false, |bf| bf.is_statement()));
+ }
+
+ // Then, the block may have an optional trailing expression which is a “return” value
+ // of the block, which is stored into `destination`.
+ let tcx = this.tcx;
+ let destination_ty = destination.ty(&this.local_decls, tcx).ty;
+ if let Some(expr) = expr {
+ let tail_result_is_ignored =
+ destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
+ this.block_context
+ .push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span });
+
+ unpack!(block = this.expr_into_dest(destination, block, expr));
+ let popped = this.block_context.pop();
+
+ assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
+ } else {
+ // If a block has no trailing expression, then it is given an implicit return type.
+ // This return type is usually `()`, unless the block is diverging, in which case the
+ // return type is `!`. For the unit type, we need to actually return the unit, but in
+ // the case of `!`, no return value is required, as the block will never return.
+ // Opaque types of empty bodies also need this unit assignment, in order to infer that their
+ // type is actually unit. Otherwise there will be no defining use found in the MIR.
+ if destination_ty.is_unit() || matches!(destination_ty.kind(), ty::Opaque(..)) {
+ // We only want to assign an implicit `()` as the return value of the block if the
+ // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
+ this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
+ }
+ }
+ // Finally, we pop all the let scopes before exiting out from the scope of block
+ // itself.
+ for scope in let_scope_stack.into_iter().rev() {
+ unpack!(block = this.pop_scope((*scope, source_info), block));
+ }
+ // Restore the original source scope.
+ this.source_scope = outer_source_scope;
+ this.in_scope_unsafe = outer_in_scope_unsafe;
+ block.unit()
+ }
+
+ /// If we are entering an unsafe block, create a new source scope
+ fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
+ debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
+ let new_unsafety = match safety_mode {
+ BlockSafety::Safe => return,
+ BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe,
+ BlockSafety::ExplicitUnsafe(hir_id) => {
+ self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id);
+ Safety::ExplicitUnsafe(hir_id)
+ }
+ };
+
+ self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety));
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs
new file mode 100644
index 000000000..d7b4b1f73
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/cfg.rs
@@ -0,0 +1,113 @@
+//! Routines for manipulating the control-flow graph.
+
+use crate::build::CFG;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+
+impl<'tcx> CFG<'tcx> {
+ pub(crate) fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> {
+ &self.basic_blocks[blk]
+ }
+
+ pub(crate) fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<'tcx> {
+ &mut self.basic_blocks[blk]
+ }
+
+ // llvm.org/PR32488 makes this function use an excess of stack space. Mark
+ // it as #[inline(never)] to keep rustc's stack use in check.
+ #[inline(never)]
+ pub(crate) fn start_new_block(&mut self) -> BasicBlock {
+ self.basic_blocks.push(BasicBlockData::new(None))
+ }
+
+ pub(crate) fn start_new_cleanup_block(&mut self) -> BasicBlock {
+ let bb = self.start_new_block();
+ self.block_data_mut(bb).is_cleanup = true;
+ bb
+ }
+
+ pub(crate) fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) {
+ debug!("push({:?}, {:?})", block, statement);
+ self.block_data_mut(block).statements.push(statement);
+ }
+
+ pub(crate) fn push_assign(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ place: Place<'tcx>,
+ rvalue: Rvalue<'tcx>,
+ ) {
+ self.push(
+ block,
+ Statement { source_info, kind: StatementKind::Assign(Box::new((place, rvalue))) },
+ );
+ }
+
+ pub(crate) fn push_assign_constant(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ temp: Place<'tcx>,
+ constant: Constant<'tcx>,
+ ) {
+ self.push_assign(
+ block,
+ source_info,
+ temp,
+ Rvalue::Use(Operand::Constant(Box::new(constant))),
+ );
+ }
+
+ pub(crate) fn push_assign_unit(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ place: Place<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ ) {
+ self.push_assign(
+ block,
+ source_info,
+ place,
+ Rvalue::Use(Operand::Constant(Box::new(Constant {
+ span: source_info.span,
+ user_ty: None,
+ literal: ConstantKind::zero_sized(tcx.types.unit),
+ }))),
+ );
+ }
+
+ pub(crate) fn push_fake_read(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ cause: FakeReadCause,
+ place: Place<'tcx>,
+ ) {
+ let kind = StatementKind::FakeRead(Box::new((cause, place)));
+ let stmt = Statement { source_info, kind };
+ self.push(block, stmt);
+ }
+
+ pub(crate) fn terminate(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ kind: TerminatorKind<'tcx>,
+ ) {
+ debug!("terminating block {:?} <- {:?}", block, kind);
+ debug_assert!(
+ self.block_data(block).terminator.is_none(),
+ "terminate: block {:?}={:?} already has a terminator set",
+ block,
+ self.block_data(block)
+ );
+ self.block_data_mut(block).terminator = Some(Terminator { source_info, kind });
+ }
+
+ /// In the `origin` block, push a `goto -> target` terminator.
+ pub(crate) fn goto(&mut self, origin: BasicBlock, source_info: SourceInfo, target: BasicBlock) {
+ self.terminate(origin, source_info, TerminatorKind::Goto { target })
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
new file mode 100644
index 000000000..648d10b9e
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -0,0 +1,152 @@
+//! See docs in build/expr/mod.rs
+
+use crate::build::{parse_float_into_constval, Builder};
+use rustc_ast as ast;
+use rustc_hir::def_id::DefId;
+use rustc_middle::mir::interpret::{
+ Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar,
+};
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt};
+use rustc_target::abi::Size;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Compile `expr`, yielding a compile-time constant. Assumes that
+ /// `expr` is a valid compile-time constant!
+ pub(crate) fn as_constant(&mut self, expr: &Expr<'tcx>) -> Constant<'tcx> {
+ let create_uneval_from_def_id =
+ |tcx: TyCtxt<'tcx>, def_id: DefId, ty: Ty<'tcx>, substs: SubstsRef<'tcx>| {
+ let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
+ tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Unevaluated(uneval), ty })
+ };
+
+ let this = self;
+ let tcx = this.tcx;
+ let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
+ match *kind {
+ ExprKind::Scope { region_scope: _, lint_level: _, value } => {
+ this.as_constant(&this.thir[value])
+ }
+ ExprKind::Literal { lit, neg } => {
+ let literal =
+ match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) {
+ Ok(c) => c,
+ Err(LitToConstError::Reported) => ConstantKind::Ty(tcx.const_error(ty)),
+ Err(LitToConstError::TypeError) => {
+ bug!("encountered type error in `lit_to_mir_constant")
+ }
+ };
+
+ Constant { span, user_ty: None, literal }
+ }
+ ExprKind::NonHirLiteral { lit, user_ty } => {
+ let user_ty = user_ty.map(|user_ty| {
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span,
+ user_ty,
+ inferred_ty: ty,
+ })
+ });
+ let literal = ConstantKind::Val(ConstValue::Scalar(Scalar::Int(lit)), ty);
+
+ Constant { span, user_ty: user_ty, literal }
+ }
+ ExprKind::ZstLiteral { user_ty } => {
+ let user_ty = user_ty.map(|user_ty| {
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span,
+ user_ty,
+ inferred_ty: ty,
+ })
+ });
+ let literal = ConstantKind::Val(ConstValue::ZeroSized, ty);
+
+ Constant { span, user_ty: user_ty, literal }
+ }
+ ExprKind::NamedConst { def_id, substs, user_ty } => {
+ let user_ty = user_ty.map(|user_ty| {
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span,
+ user_ty,
+ inferred_ty: ty,
+ })
+ });
+ let literal = ConstantKind::Ty(create_uneval_from_def_id(tcx, def_id, ty, substs));
+
+ Constant { user_ty, span, literal }
+ }
+ ExprKind::ConstParam { param, def_id: _ } => {
+ let const_param =
+ tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Param(param), ty: expr.ty });
+ let literal = ConstantKind::Ty(const_param);
+
+ Constant { user_ty: None, span, literal }
+ }
+ ExprKind::ConstBlock { did: def_id, substs } => {
+ let literal = ConstantKind::Ty(create_uneval_from_def_id(tcx, def_id, ty, substs));
+
+ Constant { user_ty: None, span, literal }
+ }
+ ExprKind::StaticRef { alloc_id, ty, .. } => {
+ let const_val = ConstValue::Scalar(Scalar::from_pointer(alloc_id.into(), &tcx));
+ let literal = ConstantKind::Val(const_val, ty);
+
+ Constant { span, user_ty: None, literal }
+ }
+ _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
+ }
+ }
+}
+
+#[instrument(skip(tcx, lit_input))]
+pub(crate) fn lit_to_mir_constant<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ lit_input: LitToConstInput<'tcx>,
+) -> Result<ConstantKind<'tcx>, LitToConstError> {
+ let LitToConstInput { lit, ty, neg } = lit_input;
+ let trunc = |n| {
+ let param_ty = ty::ParamEnv::reveal_all().and(ty);
+ let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
+ trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
+ let result = width.truncate(n);
+ trace!("trunc result: {}", result);
+ Ok(ConstValue::Scalar(Scalar::from_uint(result, width)))
+ };
+
+ let value = match (lit, &ty.kind()) {
+ (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
+ let s = s.as_str();
+ let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());
+ let allocation = tcx.intern_const_alloc(allocation);
+ ConstValue::Slice { data: allocation, start: 0, end: s.len() }
+ }
+ (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _))
+ if matches!(inner_ty.kind(), ty::Slice(_)) =>
+ {
+ let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
+ let allocation = tcx.intern_const_alloc(allocation);
+ ConstValue::Slice { data: allocation, start: 0, end: data.len() }
+ }
+ (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
+ let id = tcx.allocate_bytes(data);
+ ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
+ }
+ (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
+ ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
+ }
+ (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
+ trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?
+ }
+ (ast::LitKind::Float(n, _), ty::Float(fty)) => {
+ parse_float_into_constval(*n, *fty, neg).ok_or(LitToConstError::Reported)?
+ }
+ (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)),
+ (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)),
+ (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported),
+ _ => return Err(LitToConstError::TypeError),
+ };
+
+ Ok(ConstantKind::Val(value, ty))
+}
diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs
new file mode 100644
index 000000000..e707c373f
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs
@@ -0,0 +1,184 @@
+//! See docs in build/expr/mod.rs
+
+use crate::build::expr::category::Category;
+use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
+use rustc_middle::middle::region;
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Returns an operand suitable for use until the end of the current
+ /// scope expression.
+ ///
+ /// The operand returned from this function will *not be valid*
+ /// after the current enclosing `ExprKind::Scope` has ended, so
+ /// please do *not* return it from functions to avoid bad
+ /// miscompiles.
+ pub(crate) fn as_local_operand(
+ &mut self,
+ block: BasicBlock,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<Operand<'tcx>> {
+ let local_scope = self.local_scope();
+ self.as_operand(block, Some(local_scope), expr, None, NeedsTemporary::Maybe)
+ }
+
+ /// Returns an operand suitable for use until the end of the current scope expression and
+ /// suitable also to be passed as function arguments.
+ ///
+ /// The operand returned from this function will *not be valid* after an ExprKind::Scope is
+ /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
+ /// operand suitable for use as a call argument. This is almost always equivalent to
+ /// `as_operand`, except for the particular case of passing values of (potentially) unsized
+ /// types "by value" (see details below).
+ ///
+ /// The operand returned from this function will *not be valid*
+ /// after the current enclosing `ExprKind::Scope` has ended, so
+ /// please do *not* return it from functions to avoid bad
+ /// miscompiles.
+ ///
+ /// # Parameters of unsized types
+ ///
+ /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
+ /// local variable of unsized type. For example, consider this program:
+ ///
+ /// ```
+ /// #![feature(unsized_locals, unsized_fn_params)]
+ /// # use core::fmt::Debug;
+ /// fn foo(p: dyn Debug) { dbg!(p); }
+ ///
+ /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); }
+ /// ```
+ ///
+ /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
+ ///
+ /// ```ignore (illustrative)
+ /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
+ /// foo(tmp0)
+ /// ```
+ ///
+ /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
+ /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
+ /// that we create *stores the entire box*, and the parameter to the call itself will be
+ /// `*tmp0`:
+ ///
+ /// ```ignore (illustrative)
+ /// let tmp0 = box_p; call foo(*tmp0)
+ /// ```
+ ///
+ /// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
+ /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
+ /// calls are compiled means that this parameter will be passed "by reference", meaning that we
+ /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
+ /// value to the stack.
+ ///
+ /// See #68034 for more details.
+ pub(crate) fn as_local_call_operand(
+ &mut self,
+ block: BasicBlock,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<Operand<'tcx>> {
+ let local_scope = self.local_scope();
+ self.as_call_operand(block, Some(local_scope), expr)
+ }
+
+ /// Compile `expr` into a value that can be used as an operand.
+ /// If `expr` is a place like `x`, this will introduce a
+ /// temporary `tmp = x`, so that we capture the value of `x` at
+ /// this time.
+ ///
+ /// If we end up needing to create a temporary, then we will use
+ /// `local_info` as its `LocalInfo`, unless `as_temporary`
+ /// has already assigned it a non-`None` `LocalInfo`.
+ /// Normally, you should use `None` for `local_info`
+ ///
+ /// The operand is known to be live until the end of `scope`.
+ ///
+ /// Like `as_local_call_operand`, except that the argument will
+ /// not be valid once `scope` ends.
+ #[instrument(level = "debug", skip(self, scope))]
+ pub(crate) fn as_operand(
+ &mut self,
+ mut block: BasicBlock,
+ scope: Option<region::Scope>,
+ expr: &Expr<'tcx>,
+ local_info: Option<Box<LocalInfo<'tcx>>>,
+ needs_temporary: NeedsTemporary,
+ ) -> BlockAnd<Operand<'tcx>> {
+ let this = self;
+
+ if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
+ let source_info = this.source_info(expr.span);
+ let region_scope = (region_scope, source_info);
+ return this.in_scope(region_scope, lint_level, |this| {
+ this.as_operand(block, scope, &this.thir[value], local_info, needs_temporary)
+ });
+ }
+
+ let category = Category::of(&expr.kind).unwrap();
+ debug!(?category, ?expr.kind);
+ match category {
+ Category::Constant if let NeedsTemporary::No = needs_temporary || !expr.ty.needs_drop(this.tcx, this.param_env) => {
+ let constant = this.as_constant(expr);
+ block.and(Operand::Constant(Box::new(constant)))
+ }
+ Category::Constant | Category::Place | Category::Rvalue(..) => {
+ let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
+ if this.local_decls[operand].local_info.is_none() {
+ this.local_decls[operand].local_info = local_info;
+ }
+ block.and(Operand::Move(Place::from(operand)))
+ }
+ }
+ }
+
+ pub(crate) fn as_call_operand(
+ &mut self,
+ mut block: BasicBlock,
+ scope: Option<region::Scope>,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<Operand<'tcx>> {
+ debug!("as_call_operand(block={:?}, expr={:?})", block, expr);
+ let this = self;
+
+ if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
+ let source_info = this.source_info(expr.span);
+ let region_scope = (region_scope, source_info);
+ return this.in_scope(region_scope, lint_level, |this| {
+ this.as_call_operand(block, scope, &this.thir[value])
+ });
+ }
+
+ let tcx = this.tcx;
+
+ if tcx.features().unsized_fn_params {
+ let ty = expr.ty;
+ let span = expr.span;
+ let param_env = this.param_env;
+
+ if !ty.is_sized(tcx.at(span), param_env) {
+ // !sized means !copy, so this is an unsized move
+ assert!(!ty.is_copy_modulo_regions(tcx.at(span), param_env));
+
+ // As described above, detect the case where we are passing a value of unsized
+ // type, and that value is coming from the deref of a box.
+ if let ExprKind::Deref { arg } = expr.kind {
+ // Generate let tmp0 = arg0
+ let operand = unpack!(
+ block = this.as_temp(block, scope, &this.thir[arg], Mutability::Mut)
+ );
+
+ // Return the operand *tmp0 to be used as the call argument
+ let place = Place {
+ local: operand,
+ projection: tcx.intern_place_elems(&[PlaceElem::Deref]),
+ };
+
+ return block.and(Operand::Move(place));
+ }
+ }
+ }
+
+ this.as_operand(block, scope, expr, None, NeedsTemporary::Maybe)
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs
new file mode 100644
index 000000000..0c06aad4e
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -0,0 +1,820 @@
+//! See docs in build/expr/mod.rs
+
+use crate::build::expr::category::Category;
+use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
+use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use rustc_hir::def_id::LocalDefId;
+use rustc_middle::hir::place::Projection as HirProjection;
+use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
+use rustc_middle::middle::region;
+use rustc_middle::mir::AssertKind::BoundsCheck;
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+use rustc_middle::ty::AdtDef;
+use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
+use rustc_span::Span;
+use rustc_target::abi::VariantIdx;
+
+use rustc_index::vec::Idx;
+
+use std::iter;
+
+/// The "outermost" place that holds this value.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub(crate) enum PlaceBase {
+ /// Denotes the start of a `Place`.
+ Local(Local),
+
+ /// When building place for an expression within a closure, the place might start off a
+ /// captured path. When `capture_disjoint_fields` is enabled, we might not know the capture
+ /// index (within the desugared closure) of the captured path until most of the projections
+ /// are applied. We use `PlaceBase::Upvar` to keep track of the root variable off of which the
+ /// captured path starts, the closure the capture belongs to and the trait the closure
+ /// implements.
+ ///
+ /// Once we have figured out the capture index, we can convert the place builder to start from
+ /// `PlaceBase::Local`.
+ ///
+ /// Consider the following example
+ /// ```rust
+ /// let t = (((10, 10), 10), 10);
+ ///
+ /// let c = || {
+ /// println!("{}", t.0.0.0);
+ /// };
+ /// ```
+ /// Here the THIR expression for `t.0.0.0` will be something like
+ ///
+ /// ```ignore (illustrative)
+ /// * Field(0)
+ /// * Field(0)
+ /// * Field(0)
+ /// * UpvarRef(t)
+ /// ```
+ ///
+ /// When `capture_disjoint_fields` is enabled, `t.0.0.0` is captured and we won't be able to
+ /// figure out that it is captured until all the `Field` projections are applied.
+ Upvar {
+ /// HirId of the upvar
+ var_hir_id: LocalVarId,
+ /// DefId of the closure
+ closure_def_id: LocalDefId,
+ /// The trait closure implements, `Fn`, `FnMut`, `FnOnce`
+ closure_kind: ty::ClosureKind,
+ },
+}
+
+/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
+/// place by pushing more and more projections onto the end, and then convert the final set into a
+/// place using the `into_place` method.
+///
+/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
+/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
+#[derive(Clone, Debug, PartialEq)]
+pub(crate) struct PlaceBuilder<'tcx> {
+ base: PlaceBase,
+ projection: Vec<PlaceElem<'tcx>>,
+}
+
+/// Given a list of MIR projections, convert them to list of HIR ProjectionKind.
+/// The projections are truncated to represent a path that might be captured by a
+/// closure/generator. This implies the vector returned from this function doesn't contain
+/// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be
+/// part of a path that is captured by a closure. We stop applying projections once we see the first
+/// projection that isn't captured by a closure.
+fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
+ mir_projections: &[PlaceElem<'tcx>],
+) -> Vec<HirProjectionKind> {
+ let mut hir_projections = Vec::new();
+ let mut variant = None;
+
+ for mir_projection in mir_projections {
+ let hir_projection = match mir_projection {
+ ProjectionElem::Deref => HirProjectionKind::Deref,
+ ProjectionElem::Field(field, _) => {
+ let variant = variant.unwrap_or(VariantIdx::new(0));
+ HirProjectionKind::Field(field.index() as u32, variant)
+ }
+ ProjectionElem::Downcast(.., idx) => {
+ // We don't expect to see multi-variant enums here, as earlier
+ // phases will have truncated them already. However, there can
+ // still be downcasts, thanks to single-variant enums.
+ // We keep track of VariantIdx so we can use this information
+ // if the next ProjectionElem is a Field.
+ variant = Some(*idx);
+ continue;
+ }
+ ProjectionElem::Index(..)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. } => {
+ // We don't capture array-access projections.
+ // We can stop here as arrays are captured completely.
+ break;
+ }
+ };
+ variant = None;
+ hir_projections.push(hir_projection);
+ }
+
+ hir_projections
+}
+
+/// Return true if the `proj_possible_ancestor` represents an ancestor path
+/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
+/// assuming they both start off of the same root variable.
+///
+/// **Note:** It's the caller's responsibility to ensure that both lists of projections
+/// start off of the same root variable.
+///
+/// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
+/// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
+/// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
+/// 2. Since we only look at the projections here function will return `bar.x` as an a valid
+/// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
+/// list are being applied to the same root variable.
+fn is_ancestor_or_same_capture(
+ proj_possible_ancestor: &[HirProjectionKind],
+ proj_capture: &[HirProjectionKind],
+) -> bool {
+ // We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false.
+ // Therefore we can't just check if all projections are same in the zipped iterator below.
+ if proj_possible_ancestor.len() > proj_capture.len() {
+ return false;
+ }
+
+ iter::zip(proj_possible_ancestor, proj_capture).all(|(a, b)| a == b)
+}
+
+/// Computes the index of a capture within the desugared closure provided the closure's
+/// `closure_min_captures` and the capture's index of the capture in the
+/// `ty::MinCaptureList` of the root variable `var_hir_id`.
+fn compute_capture_idx<'tcx>(
+ closure_min_captures: &ty::RootVariableMinCaptureList<'tcx>,
+ var_hir_id: LocalVarId,
+ root_var_idx: usize,
+) -> usize {
+ let mut res = 0;
+ for (var_id, capture_list) in closure_min_captures {
+ if *var_id == var_hir_id.0 {
+ res += root_var_idx;
+ break;
+ } else {
+ res += capture_list.len();
+ }
+ }
+
+ res
+}
+
+/// Given a closure, returns the index of a capture within the desugared closure struct and the
+/// `ty::CapturedPlace` which is the ancestor of the Place represented using the `var_hir_id`
+/// and `projection`.
+///
+/// Note there will be at most one ancestor for any given Place.
+///
+/// Returns None, when the ancestor is not found.
+fn find_capture_matching_projections<'a, 'tcx>(
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+ var_hir_id: LocalVarId,
+ closure_def_id: LocalDefId,
+ projections: &[PlaceElem<'tcx>],
+) -> Option<(usize, &'a ty::CapturedPlace<'tcx>)> {
+ let closure_min_captures = typeck_results.closure_min_captures.get(&closure_def_id)?;
+ let root_variable_min_captures = closure_min_captures.get(&var_hir_id.0)?;
+
+ let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections);
+
+ // If an ancestor is found, `idx` is the index within the list of captured places
+ // for root variable `var_hir_id` and `capture` is the `ty::CapturedPlace` itself.
+ let (idx, capture) = root_variable_min_captures.iter().enumerate().find(|(_, capture)| {
+ let possible_ancestor_proj_kinds: Vec<_> =
+ capture.place.projections.iter().map(|proj| proj.kind).collect();
+ is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections)
+ })?;
+
+ // Convert index to be from the perspective of the entire closure_min_captures map
+ // instead of just the root variable capture list
+ Some((compute_capture_idx(closure_min_captures, var_hir_id, idx), capture))
+}
+
+/// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
+/// `PlaceBuilder` now starts from `PlaceBase::Local`.
+///
+/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found.
+fn to_upvars_resolved_place_builder<'a, 'tcx>(
+ from_builder: PlaceBuilder<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
+ match from_builder.base {
+ PlaceBase::Local(_) => Ok(from_builder),
+ PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind } => {
+ let mut upvar_resolved_place_builder = PlaceBuilder::from(ty::CAPTURE_STRUCT_LOCAL);
+ match closure_kind {
+ ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
+ upvar_resolved_place_builder = upvar_resolved_place_builder.deref();
+ }
+ ty::ClosureKind::FnOnce => {}
+ }
+
+ let Some((capture_index, capture)) =
+ find_capture_matching_projections(
+ typeck_results,
+ var_hir_id,
+ closure_def_id,
+ &from_builder.projection,
+ ) else {
+ let closure_span = tcx.def_span(closure_def_id);
+ if !enable_precise_capture(tcx, closure_span) {
+ bug!(
+ "No associated capture found for {:?}[{:#?}] even though \
+ capture_disjoint_fields isn't enabled",
+ var_hir_id,
+ from_builder.projection
+ )
+ } else {
+ debug!(
+ "No associated capture found for {:?}[{:#?}]",
+ var_hir_id, from_builder.projection,
+ );
+ }
+ return Err(from_builder);
+ };
+
+ // We won't be building MIR if the closure wasn't local
+ let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id);
+ let closure_ty = typeck_results.node_type(closure_hir_id);
+
+ let substs = match closure_ty.kind() {
+ ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
+ ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
+ _ => bug!("Lowering capture for non-closure type {:?}", closure_ty),
+ };
+
+ // Access the capture by accessing the field within the Closure struct.
+ //
+ // We must have inferred the capture types since we are building MIR, therefore
+ // it's safe to call `tuple_element_ty` and we can unwrap here because
+ // we know that the capture exists and is the `capture_index`-th capture.
+ let var_ty = substs.tupled_upvars_ty().tuple_fields()[capture_index];
+
+ upvar_resolved_place_builder =
+ upvar_resolved_place_builder.field(Field::new(capture_index), var_ty);
+
+ // If the variable is captured via ByRef(Immutable/Mutable) Borrow,
+ // we need to deref it
+ upvar_resolved_place_builder = match capture.info.capture_kind {
+ ty::UpvarCapture::ByRef(_) => upvar_resolved_place_builder.deref(),
+ ty::UpvarCapture::ByValue => upvar_resolved_place_builder,
+ };
+
+ // We used some of the projections to build the capture itself,
+ // now we apply the remaining to the upvar resolved place.
+ let remaining_projections = strip_prefix(
+ capture.place.base_ty,
+ from_builder.projection,
+ &capture.place.projections,
+ );
+ upvar_resolved_place_builder.projection.extend(remaining_projections);
+
+ Ok(upvar_resolved_place_builder)
+ }
+ }
+}
+
+/// Returns projections remaining after stripping an initial prefix of HIR
+/// projections.
+///
+/// Supports only HIR projection kinds that represent a path that might be
+/// captured by a closure or a generator, i.e., an `Index` or a `Subslice`
+/// projection kinds are unsupported.
+fn strip_prefix<'tcx>(
+ mut base_ty: Ty<'tcx>,
+ projections: Vec<PlaceElem<'tcx>>,
+ prefix_projections: &[HirProjection<'tcx>],
+) -> impl Iterator<Item = PlaceElem<'tcx>> {
+ let mut iter = projections.into_iter();
+ for projection in prefix_projections {
+ match projection.kind {
+ HirProjectionKind::Deref => {
+ assert!(matches!(iter.next(), Some(ProjectionElem::Deref)));
+ }
+ HirProjectionKind::Field(..) => {
+ if base_ty.is_enum() {
+ assert!(matches!(iter.next(), Some(ProjectionElem::Downcast(..))));
+ }
+ assert!(matches!(iter.next(), Some(ProjectionElem::Field(..))));
+ }
+ HirProjectionKind::Index | HirProjectionKind::Subslice => {
+ bug!("unexpected projection kind: {:?}", projection);
+ }
+ }
+ base_ty = projection.ty;
+ }
+ iter
+}
+
+impl<'tcx> PlaceBuilder<'tcx> {
+ pub(crate) fn into_place<'a>(
+ self,
+ tcx: TyCtxt<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+ ) -> Place<'tcx> {
+ if let PlaceBase::Local(local) = self.base {
+ Place { local, projection: tcx.intern_place_elems(&self.projection) }
+ } else {
+ self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results)
+ }
+ }
+
+ fn expect_upvars_resolved<'a>(
+ self,
+ tcx: TyCtxt<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+ ) -> PlaceBuilder<'tcx> {
+ to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
+ }
+
+ /// Attempts to resolve the `PlaceBuilder`.
+ /// On success, it will return the resolved `PlaceBuilder`.
+ /// On failure, it will return itself.
+ ///
+ /// Upvars resolve may fail for a `PlaceBuilder` when attempting to
+ /// resolve a disjoint field whose root variable is not captured
+ /// (destructured assignments) or when attempting to resolve a root
+ /// variable (discriminant matching with only wildcard arm) that is
+ /// not captured. This can happen because the final mir that will be
+ /// generated doesn't require a read for this place. Failures will only
+ /// happen inside closures.
+ pub(crate) fn try_upvars_resolved<'a>(
+ self,
+ tcx: TyCtxt<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+ ) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
+ to_upvars_resolved_place_builder(self, tcx, typeck_results)
+ }
+
+ pub(crate) fn base(&self) -> PlaceBase {
+ self.base
+ }
+
+ pub(crate) fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
+ self.project(PlaceElem::Field(f, ty))
+ }
+
+ pub(crate) fn deref(self) -> Self {
+ self.project(PlaceElem::Deref)
+ }
+
+ pub(crate) fn downcast(self, adt_def: AdtDef<'tcx>, variant_index: VariantIdx) -> Self {
+ self.project(PlaceElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index))
+ }
+
+ fn index(self, index: Local) -> Self {
+ self.project(PlaceElem::Index(index))
+ }
+
+ pub(crate) fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
+ self.projection.push(elem);
+ self
+ }
+}
+
+impl<'tcx> From<Local> for PlaceBuilder<'tcx> {
+ fn from(local: Local) -> Self {
+ Self { base: PlaceBase::Local(local), projection: Vec::new() }
+ }
+}
+
+impl<'tcx> From<PlaceBase> for PlaceBuilder<'tcx> {
+ fn from(base: PlaceBase) -> Self {
+ Self { base, projection: Vec::new() }
+ }
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Compile `expr`, yielding a place that we can move from etc.
+ ///
+ /// WARNING: Any user code might:
+ /// * Invalidate any slice bounds checks performed.
+ /// * Change the address that this `Place` refers to.
+ /// * Modify the memory that this place refers to.
+ /// * Invalidate the memory that this place refers to, this will be caught
+ /// by borrow checking.
+ ///
+ /// Extra care is needed if any user code is allowed to run between calling
+ /// this method and using it, as is the case for `match` and index
+ /// expressions.
+ pub(crate) fn as_place(
+ &mut self,
+ mut block: BasicBlock,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<Place<'tcx>> {
+ let place_builder = unpack!(block = self.as_place_builder(block, expr));
+ block.and(place_builder.into_place(self.tcx, self.typeck_results))
+ }
+
+ /// This is used when constructing a compound `Place`, so that we can avoid creating
+ /// intermediate `Place` values until we know the full set of projections.
+ pub(crate) fn as_place_builder(
+ &mut self,
+ block: BasicBlock,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ self.expr_as_place(block, expr, Mutability::Mut, None)
+ }
+
+ /// Compile `expr`, yielding a place that we can move from etc.
+ /// Mutability note: The caller of this method promises only to read from the resulting
+ /// place. The place itself may or may not be mutable:
+ /// * If this expr is a place expr like a.b, then we will return that place.
+ /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
+ pub(crate) fn as_read_only_place(
+ &mut self,
+ mut block: BasicBlock,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<Place<'tcx>> {
+ let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
+ block.and(place_builder.into_place(self.tcx, self.typeck_results))
+ }
+
+ /// This is used when constructing a compound `Place`, so that we can avoid creating
+ /// intermediate `Place` values until we know the full set of projections.
+ /// Mutability note: The caller of this method promises only to read from the resulting
+ /// place. The place itself may or may not be mutable:
+ /// * If this expr is a place expr like a.b, then we will return that place.
+ /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
+ fn as_read_only_place_builder(
+ &mut self,
+ block: BasicBlock,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ self.expr_as_place(block, expr, Mutability::Not, None)
+ }
+
+ fn expr_as_place(
+ &mut self,
+ mut block: BasicBlock,
+ expr: &Expr<'tcx>,
+ mutability: Mutability,
+ fake_borrow_temps: Option<&mut Vec<Local>>,
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability);
+
+ let this = self;
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr_span);
+ match expr.kind {
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ this.in_scope((region_scope, source_info), lint_level, |this| {
+ this.expr_as_place(block, &this.thir[value], mutability, fake_borrow_temps)
+ })
+ }
+ ExprKind::Field { lhs, variant_index, name } => {
+ let lhs = &this.thir[lhs];
+ let mut place_builder =
+ unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,));
+ if let ty::Adt(adt_def, _) = lhs.ty.kind() {
+ if adt_def.is_enum() {
+ place_builder = place_builder.downcast(*adt_def, variant_index);
+ }
+ }
+ block.and(place_builder.field(name, expr.ty))
+ }
+ ExprKind::Deref { arg } => {
+ let place_builder = unpack!(
+ block =
+ this.expr_as_place(block, &this.thir[arg], mutability, fake_borrow_temps,)
+ );
+ block.and(place_builder.deref())
+ }
+ ExprKind::Index { lhs, index } => this.lower_index_expression(
+ block,
+ &this.thir[lhs],
+ &this.thir[index],
+ mutability,
+ fake_borrow_temps,
+ expr.temp_lifetime,
+ expr_span,
+ source_info,
+ ),
+ ExprKind::UpvarRef { closure_def_id, var_hir_id } => {
+ this.lower_captured_upvar(block, closure_def_id.expect_local(), var_hir_id)
+ }
+
+ ExprKind::VarRef { id } => {
+ let place_builder = if this.is_bound_var_in_guard(id) {
+ let index = this.var_local_id(id, RefWithinGuard);
+ PlaceBuilder::from(index).deref()
+ } else {
+ let index = this.var_local_id(id, OutsideGuard);
+ PlaceBuilder::from(index)
+ };
+ block.and(place_builder)
+ }
+
+ ExprKind::PlaceTypeAscription { source, user_ty } => {
+ let place_builder = unpack!(
+ block = this.expr_as_place(
+ block,
+ &this.thir[source],
+ mutability,
+ fake_borrow_temps,
+ )
+ );
+ if let Some(user_ty) = user_ty {
+ let annotation_index =
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span: source_info.span,
+ user_ty,
+ inferred_ty: expr.ty,
+ });
+
+ let place = place_builder.clone().into_place(this.tcx, this.typeck_results);
+ this.cfg.push(
+ block,
+ Statement {
+ source_info,
+ kind: StatementKind::AscribeUserType(
+ Box::new((
+ place,
+ UserTypeProjection { base: annotation_index, projs: vec![] },
+ )),
+ Variance::Invariant,
+ ),
+ },
+ );
+ }
+ block.and(place_builder)
+ }
+ ExprKind::ValueTypeAscription { source, user_ty } => {
+ let source = &this.thir[source];
+ let temp =
+ unpack!(block = this.as_temp(block, source.temp_lifetime, source, mutability));
+ if let Some(user_ty) = user_ty {
+ let annotation_index =
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span: source_info.span,
+ user_ty,
+ inferred_ty: expr.ty,
+ });
+ this.cfg.push(
+ block,
+ Statement {
+ source_info,
+ kind: StatementKind::AscribeUserType(
+ Box::new((
+ Place::from(temp),
+ UserTypeProjection { base: annotation_index, projs: vec![] },
+ )),
+ Variance::Invariant,
+ ),
+ },
+ );
+ }
+ block.and(PlaceBuilder::from(temp))
+ }
+
+ ExprKind::Array { .. }
+ | ExprKind::Tuple { .. }
+ | ExprKind::Adt { .. }
+ | ExprKind::Closure { .. }
+ | ExprKind::Unary { .. }
+ | ExprKind::Binary { .. }
+ | ExprKind::LogicalOp { .. }
+ | ExprKind::Box { .. }
+ | ExprKind::Cast { .. }
+ | ExprKind::Use { .. }
+ | ExprKind::NeverToAny { .. }
+ | ExprKind::Pointer { .. }
+ | ExprKind::Repeat { .. }
+ | ExprKind::Borrow { .. }
+ | ExprKind::AddressOf { .. }
+ | ExprKind::Match { .. }
+ | ExprKind::If { .. }
+ | ExprKind::Loop { .. }
+ | ExprKind::Block { .. }
+ | ExprKind::Let { .. }
+ | ExprKind::Assign { .. }
+ | ExprKind::AssignOp { .. }
+ | ExprKind::Break { .. }
+ | ExprKind::Continue { .. }
+ | ExprKind::Return { .. }
+ | ExprKind::Literal { .. }
+ | ExprKind::NamedConst { .. }
+ | ExprKind::NonHirLiteral { .. }
+ | ExprKind::ZstLiteral { .. }
+ | ExprKind::ConstParam { .. }
+ | ExprKind::ConstBlock { .. }
+ | ExprKind::StaticRef { .. }
+ | ExprKind::InlineAsm { .. }
+ | ExprKind::Yield { .. }
+ | ExprKind::ThreadLocalRef(_)
+ | ExprKind::Call { .. } => {
+ // these are not places, so we need to make a temporary.
+ debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place)));
+ let temp =
+ unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
+ block.and(PlaceBuilder::from(temp))
+ }
+ }
+ }
+
+ /// Lower a captured upvar. Note we might not know the actual capture index,
+ /// so we create a place starting from `PlaceBase::Upvar`, which will be resolved
+ /// once all projections that allow us to identify a capture have been applied.
+ fn lower_captured_upvar(
+ &mut self,
+ block: BasicBlock,
+ closure_def_id: LocalDefId,
+ var_hir_id: LocalVarId,
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ let closure_ty =
+ self.typeck_results.node_type(self.tcx.hir().local_def_id_to_hir_id(closure_def_id));
+
+ let closure_kind = if let ty::Closure(_, closure_substs) = closure_ty.kind() {
+ self.infcx.closure_kind(closure_substs).unwrap()
+ } else {
+ // Generators are considered FnOnce.
+ ty::ClosureKind::FnOnce
+ };
+
+ block.and(PlaceBuilder::from(PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind }))
+ }
+
+ /// Lower an index expression
+ ///
+ /// This has two complications;
+ ///
+ /// * We need to do a bounds check.
+ /// * We need to ensure that the bounds check can't be invalidated using an
+ /// expression like `x[1][{x = y; 2}]`. We use fake borrows here to ensure
+ /// that this is the case.
+ fn lower_index_expression(
+ &mut self,
+ mut block: BasicBlock,
+ base: &Expr<'tcx>,
+ index: &Expr<'tcx>,
+ mutability: Mutability,
+ fake_borrow_temps: Option<&mut Vec<Local>>,
+ temp_lifetime: Option<region::Scope>,
+ expr_span: Span,
+ source_info: SourceInfo,
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ let base_fake_borrow_temps = &mut Vec::new();
+ let is_outermost_index = fake_borrow_temps.is_none();
+ let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
+
+ let mut base_place =
+ unpack!(block = self.expr_as_place(block, base, mutability, Some(fake_borrow_temps),));
+
+ // Making this a *fresh* temporary means we do not have to worry about
+ // the index changing later: Nothing will ever change this temporary.
+ // The "retagging" transformation (for Stacked Borrows) relies on this.
+ let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,));
+
+ block = self.bounds_check(block, base_place.clone(), idx, expr_span, source_info);
+
+ if is_outermost_index {
+ self.read_fake_borrows(block, fake_borrow_temps, source_info)
+ } else {
+ base_place = base_place.expect_upvars_resolved(self.tcx, self.typeck_results);
+ self.add_fake_borrows_of_base(
+ &base_place,
+ block,
+ fake_borrow_temps,
+ expr_span,
+ source_info,
+ );
+ }
+
+ block.and(base_place.index(idx))
+ }
+
+ fn bounds_check(
+ &mut self,
+ block: BasicBlock,
+ slice: PlaceBuilder<'tcx>,
+ index: Local,
+ expr_span: Span,
+ source_info: SourceInfo,
+ ) -> BasicBlock {
+ let usize_ty = self.tcx.types.usize;
+ let bool_ty = self.tcx.types.bool;
+ // bounds check:
+ let len = self.temp(usize_ty, expr_span);
+ let lt = self.temp(bool_ty, expr_span);
+
+ // len = len(slice)
+ self.cfg.push_assign(
+ block,
+ source_info,
+ len,
+ Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)),
+ );
+ // lt = idx < len
+ self.cfg.push_assign(
+ block,
+ source_info,
+ lt,
+ Rvalue::BinaryOp(
+ BinOp::Lt,
+ Box::new((Operand::Copy(Place::from(index)), Operand::Copy(len))),
+ ),
+ );
+ let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) };
+ // assert!(lt, "...")
+ self.assert(block, Operand::Move(lt), true, msg, expr_span)
+ }
+
+ fn add_fake_borrows_of_base(
+ &mut self,
+ base_place: &PlaceBuilder<'tcx>,
+ block: BasicBlock,
+ fake_borrow_temps: &mut Vec<Local>,
+ expr_span: Span,
+ source_info: SourceInfo,
+ ) {
+ let tcx = self.tcx;
+ let local = match base_place.base {
+ PlaceBase::Local(local) => local,
+ PlaceBase::Upvar { .. } => bug!("Expected PlacseBase::Local found Upvar"),
+ };
+
+ let place_ty = Place::ty_from(local, &base_place.projection, &self.local_decls, tcx);
+ if let ty::Slice(_) = place_ty.ty.kind() {
+ // We need to create fake borrows to ensure that the bounds
+ // check that we just did stays valid. Since we can't assign to
+ // unsized values, we only need to ensure that none of the
+ // pointers in the base place are modified.
+ for (idx, elem) in base_place.projection.iter().enumerate().rev() {
+ match elem {
+ ProjectionElem::Deref => {
+ let fake_borrow_deref_ty = Place::ty_from(
+ local,
+ &base_place.projection[..idx],
+ &self.local_decls,
+ tcx,
+ )
+ .ty;
+ let fake_borrow_ty =
+ tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+ let fake_borrow_temp =
+ self.local_decls.push(LocalDecl::new(fake_borrow_ty, expr_span));
+ let projection = tcx.intern_place_elems(&base_place.projection[..idx]);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ fake_borrow_temp.into(),
+ Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ BorrowKind::Shallow,
+ Place { local, projection },
+ ),
+ );
+ fake_borrow_temps.push(fake_borrow_temp);
+ }
+ ProjectionElem::Index(_) => {
+ let index_ty = Place::ty_from(
+ local,
+ &base_place.projection[..idx],
+ &self.local_decls,
+ tcx,
+ );
+ match index_ty.ty.kind() {
+ // The previous index expression has already
+ // done any index expressions needed here.
+ ty::Slice(_) => break,
+ ty::Array(..) => (),
+ _ => bug!("unexpected index base"),
+ }
+ }
+ ProjectionElem::Field(..)
+ | ProjectionElem::Downcast(..)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. } => (),
+ }
+ }
+ }
+ }
+
+ fn read_fake_borrows(
+ &mut self,
+ bb: BasicBlock,
+ fake_borrow_temps: &mut Vec<Local>,
+ source_info: SourceInfo,
+ ) {
+ // All indexes have been evaluated now, read all of the
+ // fake borrows so that they are live across those index
+ // expressions.
+ for temp in fake_borrow_temps {
+ self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp));
+ }
+ }
+}
+
+/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if
+/// user is using Rust Edition 2021 or higher.
+fn enable_precise_capture(tcx: TyCtxt<'_>, closure_span: Span) -> bool {
+ tcx.features().capture_disjoint_fields || closure_span.rust_2021()
+}
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
new file mode 100644
index 000000000..15f2d17c4
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -0,0 +1,694 @@
+//! See docs in `build/expr/mod.rs`.
+
+use rustc_index::vec::Idx;
+use rustc_middle::ty::util::IntTypeExt;
+
+use crate::build::expr::as_place::PlaceBase;
+use crate::build::expr::category::{Category, RvalueFunc};
+use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
+use rustc_hir::lang_items::LangItem;
+use rustc_middle::middle::region;
+use rustc_middle::mir::AssertKind;
+use rustc_middle::mir::Place;
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+use rustc_middle::ty::cast::CastTy;
+use rustc_middle::ty::{self, Ty, UpvarSubsts};
+use rustc_span::Span;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Returns an rvalue suitable for use until the end of the current
+ /// scope expression.
+ ///
+ /// The operand returned from this function will *not be valid* after
+ /// an ExprKind::Scope is passed, so please do *not* return it from
+ /// functions to avoid bad miscompiles.
+ pub(crate) fn as_local_rvalue(
+ &mut self,
+ block: BasicBlock,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<Rvalue<'tcx>> {
+ let local_scope = self.local_scope();
+ self.as_rvalue(block, Some(local_scope), expr)
+ }
+
+ /// Compile `expr`, yielding an rvalue.
+ pub(crate) fn as_rvalue(
+ &mut self,
+ mut block: BasicBlock,
+ scope: Option<region::Scope>,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<Rvalue<'tcx>> {
+ debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr);
+
+ let this = self;
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr_span);
+
+ match expr.kind {
+ ExprKind::ThreadLocalRef(did) => block.and(Rvalue::ThreadLocalRef(did)),
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ let region_scope = (region_scope, source_info);
+ this.in_scope(region_scope, lint_level, |this| {
+ this.as_rvalue(block, scope, &this.thir[value])
+ })
+ }
+ ExprKind::Repeat { value, count } => {
+ if Some(0) == count.try_eval_usize(this.tcx, this.param_env) {
+ this.build_zero_repeat(block, value, scope, source_info)
+ } else {
+ let value_operand = unpack!(
+ block = this.as_operand(
+ block,
+ scope,
+ &this.thir[value],
+ None,
+ NeedsTemporary::No
+ )
+ );
+ block.and(Rvalue::Repeat(value_operand, count))
+ }
+ }
+ ExprKind::Binary { op, lhs, rhs } => {
+ let lhs = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[lhs], None, NeedsTemporary::Maybe)
+ );
+ let rhs = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[rhs], None, NeedsTemporary::No)
+ );
+ this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs)
+ }
+ ExprKind::Unary { op, arg } => {
+ let arg = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[arg], None, NeedsTemporary::No)
+ );
+ // Check for -MIN on signed integers
+ if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() {
+ let bool_ty = this.tcx.types.bool;
+
+ let minval = this.minval_literal(expr_span, expr.ty);
+ let is_min = this.temp(bool_ty, expr_span);
+
+ this.cfg.push_assign(
+ block,
+ source_info,
+ is_min,
+ Rvalue::BinaryOp(BinOp::Eq, Box::new((arg.to_copy(), minval))),
+ );
+
+ block = this.assert(
+ block,
+ Operand::Move(is_min),
+ false,
+ AssertKind::OverflowNeg(arg.to_copy()),
+ expr_span,
+ );
+ }
+ block.and(Rvalue::UnaryOp(op, arg))
+ }
+ ExprKind::Box { value } => {
+ let value = &this.thir[value];
+ let tcx = this.tcx;
+
+ // `exchange_malloc` is unsafe but box is safe, so need a new scope.
+ let synth_scope = this.new_source_scope(
+ expr_span,
+ LintLevel::Inherited,
+ Some(Safety::BuiltinUnsafe),
+ );
+ let synth_info = SourceInfo { span: expr_span, scope: synth_scope };
+
+ let size = this.temp(tcx.types.usize, expr_span);
+ this.cfg.push_assign(
+ block,
+ synth_info,
+ size,
+ Rvalue::NullaryOp(NullOp::SizeOf, value.ty),
+ );
+
+ let align = this.temp(tcx.types.usize, expr_span);
+ this.cfg.push_assign(
+ block,
+ synth_info,
+ align,
+ Rvalue::NullaryOp(NullOp::AlignOf, value.ty),
+ );
+
+ // malloc some memory of suitable size and align:
+ let exchange_malloc = Operand::function_handle(
+ tcx,
+ tcx.require_lang_item(LangItem::ExchangeMalloc, Some(expr_span)),
+ ty::List::empty(),
+ expr_span,
+ );
+ let storage = this.temp(tcx.mk_mut_ptr(tcx.types.u8), expr_span);
+ let success = this.cfg.start_new_block();
+ this.cfg.terminate(
+ block,
+ synth_info,
+ TerminatorKind::Call {
+ func: exchange_malloc,
+ args: vec![Operand::Move(size), Operand::Move(align)],
+ destination: storage,
+ target: Some(success),
+ cleanup: None,
+ from_hir_call: false,
+ fn_span: expr_span,
+ },
+ );
+ this.diverge_from(block);
+ block = success;
+
+ // The `Box<T>` temporary created here is not a part of the HIR,
+ // and therefore is not considered during generator auto-trait
+ // determination. See the comment about `box` at `yield_in_scope`.
+ let result = this.local_decls.push(LocalDecl::new(expr.ty, expr_span).internal());
+ this.cfg.push(
+ block,
+ Statement { source_info, kind: StatementKind::StorageLive(result) },
+ );
+ if let Some(scope) = scope {
+ // schedule a shallow free of that memory, lest we unwind:
+ this.schedule_drop_storage_and_value(expr_span, scope, result);
+ }
+
+ // Transmute `*mut u8` to the box (thus far, uninitialized):
+ let box_ = Rvalue::ShallowInitBox(Operand::Move(storage), value.ty);
+ this.cfg.push_assign(block, source_info, Place::from(result), box_);
+
+ // initialize the box contents:
+ unpack!(
+ block = this.expr_into_dest(
+ this.tcx.mk_place_deref(Place::from(result)),
+ block,
+ value
+ )
+ );
+ block.and(Rvalue::Use(Operand::Move(Place::from(result))))
+ }
+ ExprKind::Cast { source } => {
+ let source = &this.thir[source];
+
+ // Casting an enum to an integer is equivalent to computing the discriminant and casting the
+ // discriminant. Previously every backend had to repeat the logic for this operation. Now we
+ // create all the steps directly in MIR with operations all backends need to support anyway.
+ let (source, ty) = if let ty::Adt(adt_def, ..) = source.ty.kind() && adt_def.is_enum() {
+ let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
+ let place = unpack!(block = this.as_place(block, source));
+ let discr = this.temp(discr_ty, source.span);
+ this.cfg.push_assign(
+ block,
+ source_info,
+ discr,
+ Rvalue::Discriminant(place),
+ );
+
+ (Operand::Move(discr), discr_ty)
+ } else {
+ let ty = source.ty;
+ let source = unpack!(
+ block = this.as_operand(block, scope, source, None, NeedsTemporary::No)
+ );
+ (source, ty)
+ };
+ let from_ty = CastTy::from_ty(ty);
+ let cast_ty = CastTy::from_ty(expr.ty);
+ let cast_kind = match (from_ty, cast_ty) {
+ (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
+ CastKind::PointerExposeAddress
+ }
+ (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => {
+ CastKind::PointerFromExposedAddress
+ }
+ (_, _) => CastKind::Misc,
+ };
+ block.and(Rvalue::Cast(cast_kind, source, expr.ty))
+ }
+ ExprKind::Pointer { cast, source } => {
+ let source = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No)
+ );
+ block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty))
+ }
+ ExprKind::Array { ref fields } => {
+ // (*) We would (maybe) be closer to codegen if we
+ // handled this and other aggregate cases via
+ // `into()`, not `as_rvalue` -- in that case, instead
+ // of generating
+ //
+ // let tmp1 = ...1;
+ // let tmp2 = ...2;
+ // dest = Rvalue::Aggregate(Foo, [tmp1, tmp2])
+ //
+ // we could just generate
+ //
+ // dest.f = ...1;
+ // dest.g = ...2;
+ //
+ // The problem is that then we would need to:
+ //
+ // (a) have a more complex mechanism for handling
+ // partial cleanup;
+ // (b) distinguish the case where the type `Foo` has a
+ // destructor, in which case creating an instance
+ // as a whole "arms" the destructor, and you can't
+ // write individual fields; and,
+ // (c) handle the case where the type Foo has no
+ // fields. We don't want `let x: ();` to compile
+ // to the same MIR as `let x = ();`.
+
+ // first process the set of fields
+ let el_ty = expr.ty.sequence_element_type(this.tcx);
+ let fields: Vec<_> = fields
+ .into_iter()
+ .copied()
+ .map(|f| {
+ unpack!(
+ block = this.as_operand(
+ block,
+ scope,
+ &this.thir[f],
+ None,
+ NeedsTemporary::Maybe
+ )
+ )
+ })
+ .collect();
+
+ block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(el_ty)), fields))
+ }
+ ExprKind::Tuple { ref fields } => {
+ // see (*) above
+ // first process the set of fields
+ let fields: Vec<_> = fields
+ .into_iter()
+ .copied()
+ .map(|f| {
+ unpack!(
+ block = this.as_operand(
+ block,
+ scope,
+ &this.thir[f],
+ None,
+ NeedsTemporary::Maybe
+ )
+ )
+ })
+ .collect();
+
+ block.and(Rvalue::Aggregate(Box::new(AggregateKind::Tuple), fields))
+ }
+ ExprKind::Closure { closure_id, substs, ref upvars, movability, ref fake_reads } => {
+ // Convert the closure fake reads, if any, from `ExprRef` to mir `Place`
+ // and push the fake reads.
+ // This must come before creating the operands. This is required in case
+ // there is a fake read and a borrow of the same path, since otherwise the
+ // fake read might interfere with the borrow. Consider an example like this
+ // one:
+ // ```
+ // let mut x = 0;
+ // let c = || {
+ // &mut x; // mutable borrow of `x`
+ // match x { _ => () } // fake read of `x`
+ // };
+ // ```
+ //
+ for (thir_place, cause, hir_id) in fake_reads.into_iter() {
+ let place_builder =
+ unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
+
+ if let Ok(place_builder_resolved) =
+ place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
+ {
+ let mir_place =
+ place_builder_resolved.into_place(this.tcx, this.typeck_results);
+ this.cfg.push_fake_read(
+ block,
+ this.source_info(this.tcx.hir().span(*hir_id)),
+ *cause,
+ mir_place,
+ );
+ }
+ }
+
+ // see (*) above
+ let operands: Vec<_> = upvars
+ .into_iter()
+ .copied()
+ .map(|upvar| {
+ let upvar = &this.thir[upvar];
+ match Category::of(&upvar.kind) {
+ // Use as_place to avoid creating a temporary when
+ // moving a variable into a closure, so that
+ // borrowck knows which variables to mark as being
+ // used as mut. This is OK here because the upvar
+ // expressions have no side effects and act on
+ // disjoint places.
+ // This occurs when capturing by copy/move, while
+ // by reference captures use as_operand
+ Some(Category::Place) => {
+ let place = unpack!(block = this.as_place(block, upvar));
+ this.consume_by_copy_or_move(place)
+ }
+ _ => {
+ // Turn mutable borrow captures into unique
+ // borrow captures when capturing an immutable
+ // variable. This is sound because the mutation
+ // that caused the capture will cause an error.
+ match upvar.kind {
+ ExprKind::Borrow {
+ borrow_kind:
+ BorrowKind::Mut { allow_two_phase_borrow: false },
+ arg,
+ } => unpack!(
+ block = this.limit_capture_mutability(
+ upvar.span,
+ upvar.ty,
+ scope,
+ block,
+ &this.thir[arg],
+ )
+ ),
+ _ => {
+ unpack!(
+ block = this.as_operand(
+ block,
+ scope,
+ upvar,
+ None,
+ NeedsTemporary::Maybe
+ )
+ )
+ }
+ }
+ }
+ }
+ })
+ .collect();
+
+ let result = match substs {
+ UpvarSubsts::Generator(substs) => {
+ // We implicitly set the discriminant to 0. See
+ // librustc_mir/transform/deaggregator.rs for details.
+ let movability = movability.unwrap();
+ Box::new(AggregateKind::Generator(closure_id, substs, movability))
+ }
+ UpvarSubsts::Closure(substs) => {
+ Box::new(AggregateKind::Closure(closure_id, substs))
+ }
+ };
+ block.and(Rvalue::Aggregate(result, operands))
+ }
+ ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
+ block = unpack!(this.stmt_expr(block, expr, None));
+ block.and(Rvalue::Use(Operand::Constant(Box::new(Constant {
+ span: expr_span,
+ user_ty: None,
+ literal: ConstantKind::zero_sized(this.tcx.types.unit),
+ }))))
+ }
+
+ ExprKind::Literal { .. }
+ | ExprKind::NamedConst { .. }
+ | ExprKind::NonHirLiteral { .. }
+ | ExprKind::ZstLiteral { .. }
+ | ExprKind::ConstParam { .. }
+ | ExprKind::ConstBlock { .. }
+ | ExprKind::StaticRef { .. } => {
+ let constant = this.as_constant(expr);
+ block.and(Rvalue::Use(Operand::Constant(Box::new(constant))))
+ }
+
+ ExprKind::Yield { .. }
+ | ExprKind::Block { .. }
+ | ExprKind::Match { .. }
+ | ExprKind::If { .. }
+ | ExprKind::NeverToAny { .. }
+ | ExprKind::Use { .. }
+ | ExprKind::Borrow { .. }
+ | ExprKind::AddressOf { .. }
+ | ExprKind::Adt { .. }
+ | ExprKind::Loop { .. }
+ | ExprKind::LogicalOp { .. }
+ | ExprKind::Call { .. }
+ | ExprKind::Field { .. }
+ | ExprKind::Let { .. }
+ | ExprKind::Deref { .. }
+ | ExprKind::Index { .. }
+ | ExprKind::VarRef { .. }
+ | ExprKind::UpvarRef { .. }
+ | ExprKind::Break { .. }
+ | ExprKind::Continue { .. }
+ | ExprKind::Return { .. }
+ | ExprKind::InlineAsm { .. }
+ | ExprKind::PlaceTypeAscription { .. }
+ | ExprKind::ValueTypeAscription { .. } => {
+ // these do not have corresponding `Rvalue` variants,
+ // so make an operand and then return that
+ debug_assert!(!matches!(
+ Category::of(&expr.kind),
+ Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant)
+ ));
+ let operand =
+ unpack!(block = this.as_operand(block, scope, expr, None, NeedsTemporary::No));
+ block.and(Rvalue::Use(operand))
+ }
+ }
+ }
+
+ pub(crate) fn build_binary_op(
+ &mut self,
+ mut block: BasicBlock,
+ op: BinOp,
+ span: Span,
+ ty: Ty<'tcx>,
+ lhs: Operand<'tcx>,
+ rhs: Operand<'tcx>,
+ ) -> BlockAnd<Rvalue<'tcx>> {
+ let source_info = self.source_info(span);
+ let bool_ty = self.tcx.types.bool;
+ if self.check_overflow && op.is_checkable() && ty.is_integral() {
+ let result_tup = self.tcx.intern_tup(&[ty, bool_ty]);
+ let result_value = self.temp(result_tup, span);
+
+ self.cfg.push_assign(
+ block,
+ source_info,
+ result_value,
+ Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))),
+ );
+ let val_fld = Field::new(0);
+ let of_fld = Field::new(1);
+
+ let tcx = self.tcx;
+ let val = tcx.mk_place_field(result_value, val_fld, ty);
+ let of = tcx.mk_place_field(result_value, of_fld, bool_ty);
+
+ let err = AssertKind::Overflow(op, lhs, rhs);
+
+ block = self.assert(block, Operand::Move(of), false, err, span);
+
+ block.and(Rvalue::Use(Operand::Move(val)))
+ } else {
+ if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
+ // Checking division and remainder is more complex, since we 1. always check
+ // and 2. there are two possible failure cases, divide-by-zero and overflow.
+
+ let zero_err = if op == BinOp::Div {
+ AssertKind::DivisionByZero(lhs.to_copy())
+ } else {
+ AssertKind::RemainderByZero(lhs.to_copy())
+ };
+ let overflow_err = AssertKind::Overflow(op, lhs.to_copy(), rhs.to_copy());
+
+ // Check for / 0
+ let is_zero = self.temp(bool_ty, span);
+ let zero = self.zero_literal(span, ty);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ is_zero,
+ Rvalue::BinaryOp(BinOp::Eq, Box::new((rhs.to_copy(), zero))),
+ );
+
+ block = self.assert(block, Operand::Move(is_zero), false, zero_err, span);
+
+ // We only need to check for the overflow in one case:
+ // MIN / -1, and only for signed values.
+ if ty.is_signed() {
+ let neg_1 = self.neg_1_literal(span, ty);
+ let min = self.minval_literal(span, ty);
+
+ let is_neg_1 = self.temp(bool_ty, span);
+ let is_min = self.temp(bool_ty, span);
+ let of = self.temp(bool_ty, span);
+
+ // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
+
+ self.cfg.push_assign(
+ block,
+ source_info,
+ is_neg_1,
+ Rvalue::BinaryOp(BinOp::Eq, Box::new((rhs.to_copy(), neg_1))),
+ );
+ self.cfg.push_assign(
+ block,
+ source_info,
+ is_min,
+ Rvalue::BinaryOp(BinOp::Eq, Box::new((lhs.to_copy(), min))),
+ );
+
+ let is_neg_1 = Operand::Move(is_neg_1);
+ let is_min = Operand::Move(is_min);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ of,
+ Rvalue::BinaryOp(BinOp::BitAnd, Box::new((is_neg_1, is_min))),
+ );
+
+ block = self.assert(block, Operand::Move(of), false, overflow_err, span);
+ }
+ }
+
+ block.and(Rvalue::BinaryOp(op, Box::new((lhs, rhs))))
+ }
+ }
+
+ fn build_zero_repeat(
+ &mut self,
+ mut block: BasicBlock,
+ value: ExprId,
+ scope: Option<region::Scope>,
+ outer_source_info: SourceInfo,
+ ) -> BlockAnd<Rvalue<'tcx>> {
+ let this = self;
+ let value = &this.thir[value];
+ let elem_ty = value.ty;
+ if let Some(Category::Constant) = Category::of(&value.kind) {
+ // Repeating a const does nothing
+ } else {
+ // For a non-const, we may need to generate an appropriate `Drop`
+ let value_operand =
+ unpack!(block = this.as_operand(block, scope, value, None, NeedsTemporary::No));
+ if let Operand::Move(to_drop) = value_operand {
+ let success = this.cfg.start_new_block();
+ this.cfg.terminate(
+ block,
+ outer_source_info,
+ TerminatorKind::Drop { place: to_drop, target: success, unwind: None },
+ );
+ this.diverge_from(block);
+ block = success;
+ }
+ this.record_operands_moved(&[value_operand]);
+ }
+ block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(elem_ty)), Vec::new()))
+ }
+
+ fn limit_capture_mutability(
+ &mut self,
+ upvar_span: Span,
+ upvar_ty: Ty<'tcx>,
+ temp_lifetime: Option<region::Scope>,
+ mut block: BasicBlock,
+ arg: &Expr<'tcx>,
+ ) -> BlockAnd<Operand<'tcx>> {
+ let this = self;
+
+ let source_info = this.source_info(upvar_span);
+ let temp = this.local_decls.push(LocalDecl::new(upvar_ty, upvar_span));
+
+ this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
+
+ let arg_place_builder = unpack!(block = this.as_place_builder(block, arg));
+
+ let mutability = match arg_place_builder.base() {
+ // We are capturing a path that starts off a local variable in the parent.
+ // The mutability of the current capture is same as the mutability
+ // of the local declaration in the parent.
+ PlaceBase::Local(local) => this.local_decls[local].mutability,
+ // Parent is a closure and we are capturing a path that is captured
+ // by the parent itself. The mutability of the current capture
+ // is same as that of the capture in the parent closure.
+ PlaceBase::Upvar { .. } => {
+ let enclosing_upvars_resolved =
+ arg_place_builder.clone().into_place(this.tcx, this.typeck_results);
+
+ match enclosing_upvars_resolved.as_ref() {
+ PlaceRef {
+ local,
+ projection: &[ProjectionElem::Field(upvar_index, _), ..],
+ }
+ | PlaceRef {
+ local,
+ projection:
+ &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..],
+ } => {
+ // Not in a closure
+ debug_assert!(
+ local == ty::CAPTURE_STRUCT_LOCAL,
+ "Expected local to be Local(1), found {:?}",
+ local
+ );
+ // Not in a closure
+ debug_assert!(
+ this.upvar_mutbls.len() > upvar_index.index(),
+ "Unexpected capture place, upvar_mutbls={:#?}, upvar_index={:?}",
+ this.upvar_mutbls,
+ upvar_index
+ );
+ this.upvar_mutbls[upvar_index.index()]
+ }
+ _ => bug!("Unexpected capture place"),
+ }
+ }
+ };
+
+ let borrow_kind = match mutability {
+ Mutability::Not => BorrowKind::Unique,
+ Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+ };
+
+ let arg_place = arg_place_builder.into_place(this.tcx, this.typeck_results);
+
+ this.cfg.push_assign(
+ block,
+ source_info,
+ Place::from(temp),
+ Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place),
+ );
+
+ // See the comment in `expr_as_temp` and on the `rvalue_scopes` field for why
+ // this can be `None`.
+ if let Some(temp_lifetime) = temp_lifetime {
+ this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp);
+ }
+
+ block.and(Operand::Move(Place::from(temp)))
+ }
+
+ // Helper to get a `-1` value of the appropriate type
+ fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
+ let param_ty = ty::ParamEnv::empty().and(ty);
+ let size = self.tcx.layout_of(param_ty).unwrap().size;
+ let literal = ConstantKind::from_bits(self.tcx, size.unsigned_int_max(), param_ty);
+
+ self.literal_operand(span, literal)
+ }
+
+ // Helper to get the minimum value of the appropriate type
+ fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
+ assert!(ty.is_signed());
+ let param_ty = ty::ParamEnv::empty().and(ty);
+ let bits = self.tcx.layout_of(param_ty).unwrap().size.bits();
+ let n = 1 << (bits - 1);
+ let literal = ConstantKind::from_bits(self.tcx, n, param_ty);
+
+ self.literal_operand(span, literal)
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs
new file mode 100644
index 000000000..724b72f87
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs
@@ -0,0 +1,119 @@
+//! See docs in build/expr/mod.rs
+
+use crate::build::scope::DropKind;
+use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_middle::middle::region;
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Compile `expr` into a fresh temporary. This is used when building
+ /// up rvalues so as to freeze the value that will be consumed.
+ pub(crate) fn as_temp(
+ &mut self,
+ block: BasicBlock,
+ temp_lifetime: Option<region::Scope>,
+ expr: &Expr<'tcx>,
+ mutability: Mutability,
+ ) -> BlockAnd<Local> {
+ // this is the only place in mir building that we need to truly need to worry about
+ // infinite recursion. Everything else does recurse, too, but it always gets broken up
+ // at some point by inserting an intermediate temporary
+ ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
+ }
+
+ fn as_temp_inner(
+ &mut self,
+ mut block: BasicBlock,
+ temp_lifetime: Option<region::Scope>,
+ expr: &Expr<'tcx>,
+ mutability: Mutability,
+ ) -> BlockAnd<Local> {
+ debug!(
+ "as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
+ block, temp_lifetime, expr, mutability
+ );
+ let this = self;
+
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr_span);
+ if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
+ return this.in_scope((region_scope, source_info), lint_level, |this| {
+ this.as_temp(block, temp_lifetime, &this.thir[value], mutability)
+ });
+ }
+
+ let expr_ty = expr.ty;
+ let temp = {
+ let mut local_decl = LocalDecl::new(expr_ty, expr_span);
+ if mutability == Mutability::Not {
+ local_decl = local_decl.immutable();
+ }
+
+ debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
+ // Find out whether this temp is being created within the
+ // tail expression of a block whose result is ignored.
+ if let Some(tail_info) = this.block_context.currently_in_block_tail() {
+ local_decl = local_decl.block_tail(tail_info);
+ }
+ match expr.kind {
+ ExprKind::StaticRef { def_id, .. } => {
+ assert!(!this.tcx.is_thread_local_static(def_id));
+ local_decl.internal = true;
+ local_decl.local_info =
+ Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: false }));
+ }
+ ExprKind::ThreadLocalRef(def_id) => {
+ assert!(this.tcx.is_thread_local_static(def_id));
+ local_decl.internal = true;
+ local_decl.local_info =
+ Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: true }));
+ }
+ ExprKind::NamedConst { def_id, .. } | ExprKind::ConstParam { def_id, .. } => {
+ local_decl.local_info = Some(Box::new(LocalInfo::ConstRef { def_id }));
+ }
+ _ => {}
+ }
+ this.local_decls.push(local_decl)
+ };
+ let temp_place = Place::from(temp);
+
+ match expr.kind {
+ // Don't bother with StorageLive and Dead for these temporaries,
+ // they are never assigned.
+ ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
+ ExprKind::Block { body: Block { expr: None, targeted_by_break: false, .. } }
+ if expr_ty.is_never() => {}
+ _ => {
+ this.cfg
+ .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
+
+ // In constants, `temp_lifetime` is `None` for temporaries that
+ // live for the `'static` lifetime. Thus we do not drop these
+ // temporaries and simply leak them.
+ // This is equivalent to what `let x = &foo();` does in
+ // functions. The temporary is lifted to their surrounding
+ // scope. In a function that means the temporary lives until
+ // just before the function returns. In constants that means it
+ // outlives the constant's initialization value computation.
+ // Anything outliving a constant must have the `'static`
+ // lifetime and live forever.
+ // Anything with a shorter lifetime (e.g the `&foo()` in
+ // `bar(&foo())` or anything within a block will keep the
+ // regular drops just like runtime code.
+ if let Some(temp_lifetime) = temp_lifetime {
+ this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
+ }
+ }
+ }
+
+ unpack!(block = this.expr_into_dest(temp_place, block, expr));
+
+ if let Some(temp_lifetime) = temp_lifetime {
+ this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
+ }
+
+ block.and(temp)
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs
new file mode 100644
index 000000000..a4386319d
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/category.rs
@@ -0,0 +1,92 @@
+use rustc_middle::thir::*;
+
+#[derive(Debug, PartialEq)]
+pub(crate) enum Category {
+ // An assignable memory location like `x`, `x.f`, `foo()[3]`, that
+ // sort of thing. Something that could appear on the LHS of an `=`
+ // sign.
+ Place,
+
+ // A literal like `23` or `"foo"`. Does not include constant
+ // expressions like `3 + 5`.
+ Constant,
+
+ // Something that generates a new value at runtime, like `x + y`
+ // or `foo()`.
+ Rvalue(RvalueFunc),
+}
+
+// Rvalues fall into different "styles" that will determine which fn
+// is best suited to generate them.
+#[derive(Debug, PartialEq)]
+pub(crate) enum RvalueFunc {
+ // Best generated by `into`. This is generally exprs that
+ // cause branching, like `match`, but also includes calls.
+ Into,
+
+ // Best generated by `as_rvalue`. This is usually the case.
+ AsRvalue,
+}
+
+/// Determines the category for a given expression. Note that scope
+/// and paren expressions have no category.
+impl Category {
+ pub(crate) fn of(ek: &ExprKind<'_>) -> Option<Category> {
+ match *ek {
+ ExprKind::Scope { .. } => None,
+
+ ExprKind::Field { .. }
+ | ExprKind::Deref { .. }
+ | ExprKind::Index { .. }
+ | ExprKind::UpvarRef { .. }
+ | ExprKind::VarRef { .. }
+ | ExprKind::PlaceTypeAscription { .. }
+ | ExprKind::ValueTypeAscription { .. } => Some(Category::Place),
+
+ ExprKind::LogicalOp { .. }
+ | ExprKind::Match { .. }
+ | ExprKind::If { .. }
+ | ExprKind::Let { .. }
+ | ExprKind::NeverToAny { .. }
+ | ExprKind::Use { .. }
+ | ExprKind::Adt { .. }
+ | ExprKind::Borrow { .. }
+ | ExprKind::AddressOf { .. }
+ | ExprKind::Yield { .. }
+ | ExprKind::Call { .. }
+ | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
+
+ ExprKind::Array { .. }
+ | ExprKind::Tuple { .. }
+ | ExprKind::Closure { .. }
+ | ExprKind::Unary { .. }
+ | ExprKind::Binary { .. }
+ | ExprKind::Box { .. }
+ | ExprKind::Cast { .. }
+ | ExprKind::Pointer { .. }
+ | ExprKind::Repeat { .. }
+ | ExprKind::Assign { .. }
+ | ExprKind::AssignOp { .. }
+ | ExprKind::ThreadLocalRef(_) => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
+
+ ExprKind::ConstBlock { .. }
+ | ExprKind::Literal { .. }
+ | ExprKind::NonHirLiteral { .. }
+ | ExprKind::ZstLiteral { .. }
+ | ExprKind::ConstParam { .. }
+ | ExprKind::StaticRef { .. }
+ | ExprKind::NamedConst { .. } => Some(Category::Constant),
+
+ ExprKind::Loop { .. }
+ | ExprKind::Block { .. }
+ | ExprKind::Break { .. }
+ | ExprKind::Continue { .. }
+ | ExprKind::Return { .. } =>
+ // FIXME(#27840) these probably want their own
+ // category, like "nonterminating"
+ {
+ Some(Category::Rvalue(RvalueFunc::Into))
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
new file mode 100644
index 000000000..017d43d10
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -0,0 +1,599 @@
+//! See docs in build/expr/mod.rs
+
+use crate::build::expr::category::{Category, RvalueFunc};
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
+use rustc_ast::InlineAsmOptions;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_hir as hir;
+use rustc_index::vec::Idx;
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+use rustc_middle::ty::CanonicalUserTypeAnnotation;
+use std::iter;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Compile `expr`, storing the result into `destination`, which
+ /// is assumed to be uninitialized.
+ pub(crate) fn expr_into_dest(
+ &mut self,
+ destination: Place<'tcx>,
+ mut block: BasicBlock,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<()> {
+ debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
+
+ // 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;
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr_span);
+
+ 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);
+ }
+
+ let block_and = match expr.kind {
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ let region_scope = (region_scope, source_info);
+ ensure_sufficient_stack(|| {
+ this.in_scope(region_scope, lint_level, |this| {
+ this.expr_into_dest(destination, block, &this.thir[value])
+ })
+ })
+ }
+ ExprKind::Block { body: ref ast_block } => {
+ this.ast_block(destination, block, ast_block, source_info)
+ }
+ ExprKind::Match { scrutinee, ref arms } => {
+ this.match_expr(destination, expr_span, block, &this.thir[scrutinee], arms)
+ }
+ ExprKind::If { cond, then, else_opt, if_then_scope } => {
+ let then_blk;
+ let then_expr = &this.thir[then];
+ let then_source_info = this.source_info(then_expr.span);
+ let condition_scope = this.local_scope();
+
+ let mut else_blk = unpack!(
+ then_blk = 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_expr.span,
+ LintLevel::Inherited,
+ None,
+ );
+ this.source_scope = variable_scope;
+ SourceInfo { span: then_expr.span, scope: variable_scope }
+ } else {
+ this.source_info(then_expr.span)
+ };
+ let (then_block, else_block) =
+ this.in_if_then_scope(condition_scope, |this| {
+ let then_blk = unpack!(this.then_else_break(
+ block,
+ &this.thir[cond],
+ Some(condition_scope),
+ condition_scope,
+ source_info
+ ));
+
+ this.expr_into_dest(destination, then_blk, then_expr)
+ });
+ then_block.and(else_block)
+ },
+ )
+ );
+
+ else_blk = if let Some(else_opt) = else_opt {
+ unpack!(this.expr_into_dest(destination, else_blk, &this.thir[else_opt]))
+ } else {
+ // Body of the `if` expression without an `else` clause must return `()`, thus
+ // we implicitly generate an `else {}` if it is not specified.
+ let correct_si = this.source_info(expr_span.shrink_to_hi());
+ this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
+ else_blk
+ };
+
+ let join_block = this.cfg.start_new_block();
+ this.cfg.goto(then_blk, source_info, join_block);
+ this.cfg.goto(else_blk, source_info, join_block);
+ join_block.unit()
+ }
+ ExprKind::Let { expr, ref pat } => {
+ let scope = this.local_scope();
+ let (true_block, false_block) = this.in_if_then_scope(scope, |this| {
+ this.lower_let_expr(block, &this.thir[expr], pat, scope, None, expr_span)
+ });
+
+ this.cfg.push_assign_constant(
+ true_block,
+ source_info,
+ destination,
+ Constant {
+ span: expr_span,
+ user_ty: None,
+ literal: ConstantKind::from_bool(this.tcx, true),
+ },
+ );
+
+ this.cfg.push_assign_constant(
+ false_block,
+ source_info,
+ destination,
+ Constant {
+ span: expr_span,
+ user_ty: None,
+ literal: ConstantKind::from_bool(this.tcx, false),
+ },
+ );
+
+ 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::NeverToAny { source } => {
+ let source = &this.thir[source];
+ let is_call =
+ matches!(source.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, Some(this.local_scope()), source, Mutability::Mut,)
+ );
+
+ // 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 {
+ this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
+ let end_block = this.cfg.start_new_block();
+ end_block.unit()
+ }
+ }
+ ExprKind::LogicalOp { op, lhs, rhs } => {
+ // And:
+ //
+ // [block: If(lhs)] -true-> [else_block: dest = (rhs)]
+ // | (false)
+ // [shortcurcuit_block: dest = false]
+ //
+ // Or:
+ //
+ // [block: If(lhs)] -false-> [else_block: dest = (rhs)]
+ // | (true)
+ // [shortcurcuit_block: dest = true]
+
+ let (shortcircuit_block, mut else_block, join_block) = (
+ this.cfg.start_new_block(),
+ this.cfg.start_new_block(),
+ this.cfg.start_new_block(),
+ );
+
+ let lhs = unpack!(block = this.as_local_operand(block, &this.thir[lhs]));
+ let blocks = match op {
+ LogicalOp::And => (else_block, shortcircuit_block),
+ LogicalOp::Or => (shortcircuit_block, else_block),
+ };
+ let term = TerminatorKind::if_(this.tcx, lhs, blocks.0, blocks.1);
+ this.cfg.terminate(block, source_info, term);
+
+ this.cfg.push_assign_constant(
+ shortcircuit_block,
+ source_info,
+ destination,
+ Constant {
+ span: expr_span,
+ user_ty: None,
+ literal: match op {
+ LogicalOp::And => ConstantKind::from_bool(this.tcx, false),
+ LogicalOp::Or => ConstantKind::from_bool(this.tcx, true),
+ },
+ },
+ );
+ this.cfg.goto(shortcircuit_block, source_info, join_block);
+
+ let rhs = unpack!(else_block = this.as_local_operand(else_block, &this.thir[rhs]));
+ this.cfg.push_assign(else_block, source_info, destination, Rvalue::Use(rhs));
+ this.cfg.goto(else_block, source_info, join_block);
+
+ join_block.unit()
+ }
+ 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
+
+ let loop_block = this.cfg.start_new_block();
+
+ // Start the loop.
+ this.cfg.goto(block, source_info, loop_block);
+
+ 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: None },
+ );
+ 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 =
+ unpack!(this.expr_into_dest(tmp, body_block, &this.thir[body]));
+ this.cfg.goto(body_block_end, source_info, loop_block);
+
+ // Loops are only exited by `break` expressions.
+ None
+ })
+ }
+ ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
+ let fun = unpack!(block = this.as_local_operand(block, &this.thir[fun]));
+ let args: Vec<_> = args
+ .into_iter()
+ .copied()
+ .map(|arg| unpack!(block = this.as_local_call_operand(block, &this.thir[arg])))
+ .collect();
+
+ let success = this.cfg.start_new_block();
+
+ this.record_operands_moved(&args);
+
+ debug!("expr_into_dest: fn_span={:?}", fn_span);
+
+ this.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Call {
+ func: fun,
+ args,
+ cleanup: None,
+ 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.
+ target: if this.tcx.is_ty_uninhabited_from(
+ this.parent_module,
+ expr.ty,
+ this.param_env,
+ ) {
+ None
+ } else {
+ Some(success)
+ },
+ from_hir_call,
+ fn_span,
+ },
+ );
+ this.diverge_from(block);
+ success.unit()
+ }
+ ExprKind::Use { source } => this.expr_into_dest(destination, block, &this.thir[source]),
+ ExprKind::Borrow { arg, borrow_kind } => {
+ let arg = &this.thir[arg];
+ // 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 {
+ BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
+ _ => unpack!(block = this.as_place(block, arg)),
+ };
+ 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::AddressOf { mutability, arg } => {
+ let arg = &this.thir[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::AddressOf(mutability, unpack!(block = place));
+ this.cfg.push_assign(block, source_info, destination, address_of);
+ block.unit()
+ }
+ ExprKind::Adt(box Adt {
+ adt_def,
+ variant_index,
+ substs,
+ user_ty,
+ 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 = if is_union { Some(fields[0].name.index()) } else { None };
+
+ let scope = this.local_scope();
+
+ // 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| {
+ let local_info = Box::new(LocalInfo::AggregateTemp);
+ (
+ f.name,
+ unpack!(
+ block = this.as_operand(
+ block,
+ Some(scope),
+ &this.thir[f.expr],
+ Some(local_info),
+ NeedsTemporary::Maybe,
+ )
+ ),
+ )
+ })
+ .collect();
+
+ let field_names: Vec<_> =
+ (0..adt_def.variant(variant_index).fields.len()).map(Field::new).collect();
+
+ let fields: Vec<_> = if let Some(FruInfo { base, field_types }) = base {
+ let place_builder =
+ unpack!(block = this.as_place_builder(block, &this.thir[*base]));
+
+ // MIR does not natively support FRU, so for each
+ // base-supplied field, generate an operand that
+ // reads it from the base.
+ iter::zip(field_names, &**field_types)
+ .map(|(n, ty)| match fields_map.get(&n) {
+ Some(v) => v.clone(),
+ None => {
+ let place_builder = place_builder.clone();
+ this.consume_by_copy_or_move(
+ place_builder
+ .field(n, *ty)
+ .into_place(this.tcx, this.typeck_results),
+ )
+ }
+ })
+ .collect()
+ } else {
+ field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
+ };
+
+ let inferred_ty = expr.ty;
+ let user_ty = user_ty.map(|ty| {
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span: source_info.span,
+ user_ty: ty,
+ inferred_ty,
+ })
+ });
+ let adt = Box::new(AggregateKind::Adt(
+ adt_def.did(),
+ variant_index,
+ substs,
+ user_ty,
+ active_field_index,
+ ));
+ this.cfg.push_assign(
+ block,
+ source_info,
+ destination,
+ Rvalue::Aggregate(adt, fields),
+ );
+ block.unit()
+ }
+ ExprKind::InlineAsm { template, ref operands, options, line_spans } => {
+ use rustc_middle::{mir, thir};
+ let operands = operands
+ .into_iter()
+ .map(|op| match *op {
+ thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
+ reg,
+ value: unpack!(block = this.as_local_operand(block, &this.thir[expr])),
+ },
+ thir::InlineAsmOperand::Out { reg, late, expr } => {
+ mir::InlineAsmOperand::Out {
+ reg,
+ late,
+ place: expr.map(|expr| {
+ unpack!(block = this.as_place(block, &this.thir[expr]))
+ }),
+ }
+ }
+ thir::InlineAsmOperand::InOut { reg, late, expr } => {
+ let place = unpack!(block = this.as_place(block, &this.thir[expr]));
+ mir::InlineAsmOperand::InOut {
+ reg,
+ late,
+ // This works because asm operands must be Copy
+ 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: unpack!(
+ block = this.as_local_operand(block, &this.thir[in_expr])
+ ),
+ out_place: out_expr.map(|out_expr| {
+ unpack!(block = this.as_place(block, &this.thir[out_expr]))
+ }),
+ }
+ }
+ thir::InlineAsmOperand::Const { value, span } => {
+ mir::InlineAsmOperand::Const {
+ value: Box::new(Constant { span, user_ty: None, literal: value }),
+ }
+ }
+ thir::InlineAsmOperand::SymFn { value, span } => {
+ mir::InlineAsmOperand::SymFn {
+ value: Box::new(Constant { span, user_ty: None, literal: value }),
+ }
+ }
+ thir::InlineAsmOperand::SymStatic { def_id } => {
+ mir::InlineAsmOperand::SymStatic { def_id }
+ }
+ })
+ .collect();
+
+ if !options.contains(InlineAsmOptions::NORETURN) {
+ this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
+ }
+
+ let destination_block = this.cfg.start_new_block();
+ this.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::InlineAsm {
+ template,
+ operands,
+ options,
+ line_spans,
+ destination: if options.contains(InlineAsmOptions::NORETURN) {
+ None
+ } else {
+ Some(destination_block)
+ },
+ cleanup: None,
+ },
+ );
+ if options.contains(InlineAsmOptions::MAY_UNWIND) {
+ this.diverge_from(block);
+ }
+ destination_block.unit()
+ }
+
+ // These cases don't actually need a destination
+ ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
+ unpack!(block = this.stmt_expr(block, expr, None));
+ this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
+ block.unit()
+ }
+
+ ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => {
+ unpack!(block = this.stmt_expr(block, expr, None));
+ // No assign, as these have type `!`.
+ block.unit()
+ }
+
+ // Avoid creating a temporary
+ ExprKind::VarRef { .. }
+ | ExprKind::UpvarRef { .. }
+ | ExprKind::PlaceTypeAscription { .. }
+ | ExprKind::ValueTypeAscription { .. } => {
+ debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
+
+ let place = unpack!(block = this.as_place(block, expr));
+ let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
+ this.cfg.push_assign(block, source_info, destination, rvalue);
+ block.unit()
+ }
+ ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
+ 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));
+ }
+
+ let place = unpack!(block = this.as_place(block, expr));
+ let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
+ this.cfg.push_assign(block, source_info, destination, rvalue);
+ block.unit()
+ }
+
+ ExprKind::Yield { value } => {
+ let scope = this.local_scope();
+ let value = unpack!(
+ block = this.as_operand(
+ block,
+ Some(scope),
+ &this.thir[value],
+ None,
+ NeedsTemporary::No
+ )
+ );
+ let resume = this.cfg.start_new_block();
+ this.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
+ );
+ this.generator_drop_cleanup(block);
+ resume.unit()
+ }
+
+ // these are the cases that are more naturally handled by some other mode
+ ExprKind::Unary { .. }
+ | ExprKind::Binary { .. }
+ | ExprKind::Box { .. }
+ | ExprKind::Cast { .. }
+ | ExprKind::Pointer { .. }
+ | ExprKind::Repeat { .. }
+ | ExprKind::Array { .. }
+ | ExprKind::Tuple { .. }
+ | ExprKind::Closure { .. }
+ | ExprKind::ConstBlock { .. }
+ | ExprKind::Literal { .. }
+ | ExprKind::NamedConst { .. }
+ | ExprKind::NonHirLiteral { .. }
+ | ExprKind::ZstLiteral { .. }
+ | ExprKind::ConstParam { .. }
+ | ExprKind::ThreadLocalRef(_)
+ | ExprKind::StaticRef { .. } => {
+ debug_assert!(match Category::of(&expr.kind).unwrap() {
+ // should be handled above
+ Category::Rvalue(RvalueFunc::Into) => false,
+
+ // must be handled above or else we get an
+ // infinite loop in the builder; see
+ // e.g., `ExprKind::VarRef` above
+ Category::Place => false,
+
+ _ => true,
+ });
+
+ let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
+ this.cfg.push_assign(block, source_info, destination, rvalue);
+ block.unit()
+ }
+ };
+
+ if !expr_is_block_or_scope {
+ let popped = this.block_context.pop();
+ assert!(popped.is_some());
+ }
+
+ block_and
+ }
+
+ fn is_let(&self, expr: ExprId) -> bool {
+ match self.thir[expr].kind {
+ ExprKind::Let { .. } => true,
+ ExprKind::Scope { value, .. } => self.is_let(value),
+ _ => false,
+ }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/expr/mod.rs b/compiler/rustc_mir_build/src/build/expr/mod.rs
new file mode 100644
index 000000000..f5ae060d6
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/mod.rs
@@ -0,0 +1,70 @@
+//! Builds MIR from expressions. As a caller into this module, you
+//! have many options, but the first thing you have to decide is
+//! whether you are evaluating this expression for its *value*, its
+//! *location*, or as a *constant*.
+//!
+//! Typically, you want the value: e.g., if you are doing `expr_a +
+//! expr_b`, you want the values of those expressions. In that case,
+//! you want one of the following functions. Note that if the expr has
+//! a type that is not `Copy`, then using any of these functions will
+//! "move" the value out of its current home (if any).
+//!
+//! - `expr_into_dest` -- writes the value into a specific location, which
+//! should be uninitialized
+//! - `as_operand` -- evaluates the value and yields an `Operand`,
+//! suitable for use as an argument to an `Rvalue`
+//! - `as_temp` -- evaluates into a temporary; this is similar to `as_operand`
+//! except it always returns a fresh place, even for constants
+//! - `as_rvalue` -- yields an `Rvalue`, suitable for use in an assignment;
+//! as of this writing, never needed outside of the `expr` module itself
+//!
+//! Sometimes though want the expression's *location*. An example
+//! would be during a match statement, or the operand of the `&`
+//! operator. In that case, you want `as_place`. This will create a
+//! temporary if necessary.
+//!
+//! Finally, if it's a constant you seek, then call
+//! `as_constant`. This creates a `Constant<H>`, but naturally it can
+//! only be used on constant expressions and hence is needed only in
+//! very limited contexts.
+//!
+//! ### Implementation notes
+//!
+//! For any given kind of expression, there is generally one way that
+//! can be lowered most naturally. This is specified by the
+//! `Category::of` function in the `category` module. For example, a
+//! struct expression (or other expression that creates a new value)
+//! is typically easiest to write in terms of `as_rvalue` or `into`,
+//! whereas a reference to a field is easiest to write in terms of
+//! `as_place`. (The exception to this is scope and paren
+//! expressions, which have no category.)
+//!
+//! Therefore, the various functions above make use of one another in
+//! a descending fashion. For any given expression, you should pick
+//! the most suitable spot to implement it, and then just let the
+//! other fns cycle around. The handoff works like this:
+//!
+//! - `into(place)` -> fallback is to create a rvalue with `as_rvalue` and assign it to `place`
+//! - `as_rvalue` -> fallback is to create an Operand with `as_operand` and use `Rvalue::use`
+//! - `as_operand` -> either invokes `as_constant` or `as_temp`
+//! - `as_constant` -> (no fallback)
+//! - `as_temp` -> creates a temporary and either calls `as_place` or `into`
+//! - `as_place` -> for rvalues, falls back to `as_temp` and returns that
+//!
+//! As you can see, there is a cycle where `into` can (in theory) fallback to `as_temp`
+//! which can fallback to `into`. So if one of the `ExprKind` variants is not, in fact,
+//! implemented in the category where it is supposed to be, there will be a problem.
+//!
+//! Of those fallbacks, the most interesting one is `into`, because
+//! it discriminates based on the category of the expression. This is
+//! basically the point where the "by value" operations are bridged
+//! over to the "by reference" mode (`as_place`).
+
+pub(crate) mod as_constant;
+mod as_operand;
+pub mod as_place;
+mod as_rvalue;
+mod as_temp;
+pub mod category;
+mod into;
+mod stmt;
diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs
new file mode 100644
index 000000000..a7e1331aa
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs
@@ -0,0 +1,149 @@
+use crate::build::scope::BreakableTarget;
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use rustc_middle::middle::region;
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Builds a block of MIR statements to evaluate the THIR `expr`.
+ /// If the original expression was an AST statement,
+ /// (e.g., `some().code(&here());`) then `opt_stmt_span` is the
+ /// span of that statement (including its semicolon, if any).
+ /// The scope is used if a statement temporary must be dropped.
+ pub(crate) fn stmt_expr(
+ &mut self,
+ mut block: BasicBlock,
+ expr: &Expr<'tcx>,
+ statement_scope: Option<region::Scope>,
+ ) -> BlockAnd<()> {
+ let this = self;
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr.span);
+ // Handle a number of expressions that don't need a destination at all. This
+ // avoids needing a mountain of temporary `()` variables.
+ match expr.kind {
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ this.in_scope((region_scope, source_info), lint_level, |this| {
+ this.stmt_expr(block, &this.thir[value], statement_scope)
+ })
+ }
+ ExprKind::Assign { lhs, rhs } => {
+ let lhs = &this.thir[lhs];
+ let rhs = &this.thir[rhs];
+ let lhs_span = lhs.span;
+
+ // Note: we evaluate assignments right-to-left. This
+ // is better for borrowck interaction with overloaded
+ // operators like x[j] = x[i].
+
+ debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr);
+ this.block_context.push(BlockFrame::SubExpr);
+
+ // Generate better code for things that don't need to be
+ // dropped.
+ if lhs.ty.needs_drop(this.tcx, this.param_env) {
+ let rhs = unpack!(block = this.as_local_operand(block, rhs));
+ let lhs = unpack!(block = this.as_place(block, lhs));
+ unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs));
+ } else {
+ let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
+ let lhs = unpack!(block = this.as_place(block, lhs));
+ this.cfg.push_assign(block, source_info, lhs, rhs);
+ }
+
+ this.block_context.pop();
+ block.unit()
+ }
+ ExprKind::AssignOp { op, lhs, rhs } => {
+ // FIXME(#28160) there is an interesting semantics
+ // question raised here -- should we "freeze" the
+ // value of the lhs here? I'm inclined to think not,
+ // since it seems closer to the semantics of the
+ // overloaded version, which takes `&mut self`. This
+ // only affects weird things like `x += {x += 1; x}`
+ // -- is that equal to `x + (x + 1)` or `2*(x+1)`?
+
+ let lhs = &this.thir[lhs];
+ let rhs = &this.thir[rhs];
+ let lhs_ty = lhs.ty;
+
+ debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr);
+ this.block_context.push(BlockFrame::SubExpr);
+
+ // As above, RTL.
+ let rhs = unpack!(block = this.as_local_operand(block, rhs));
+ let lhs = unpack!(block = this.as_place(block, lhs));
+
+ // we don't have to drop prior contents or anything
+ // because AssignOp is only legal for Copy types
+ // (overloaded ops should be desugared into a call).
+ let result = unpack!(
+ block =
+ this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs)
+ );
+ this.cfg.push_assign(block, source_info, lhs, result);
+
+ this.block_context.pop();
+ block.unit()
+ }
+ ExprKind::Continue { label } => {
+ this.break_scope(block, None, BreakableTarget::Continue(label), source_info)
+ }
+ ExprKind::Break { label, value } => this.break_scope(
+ block,
+ value.map(|value| &this.thir[value]),
+ BreakableTarget::Break(label),
+ source_info,
+ ),
+ ExprKind::Return { value } => this.break_scope(
+ block,
+ value.map(|value| &this.thir[value]),
+ BreakableTarget::Return,
+ source_info,
+ ),
+ _ => {
+ assert!(
+ statement_scope.is_some(),
+ "Should not be calling `stmt_expr` on a general expression \
+ without a statement scope",
+ );
+
+ // Issue #54382: When creating temp for the value of
+ // expression like:
+ //
+ // `{ side_effects(); { let l = stuff(); the_value } }`
+ //
+ // it is usually better to focus on `the_value` rather
+ // than the entirety of block(s) surrounding it.
+ let adjusted_span = (|| {
+ if let ExprKind::Block { body } = &expr.kind && let Some(tail_ex) = body.expr {
+ let mut expr = &this.thir[tail_ex];
+ while let ExprKind::Block {
+ body: Block { expr: Some(nested_expr), .. },
+ }
+ | ExprKind::Scope { value: nested_expr, .. } = expr.kind
+ {
+ expr = &this.thir[nested_expr];
+ }
+ this.block_context.push(BlockFrame::TailExpr {
+ tail_result_is_ignored: true,
+ span: expr.span,
+ });
+ return Some(expr.span);
+ }
+ None
+ })();
+
+ let temp =
+ unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not));
+
+ if let Some(span) = adjusted_span {
+ this.local_decls[temp].source_info.span = span;
+ this.block_context.pop();
+ }
+
+ block.unit()
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
new file mode 100644
index 000000000..58b1564cc
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -0,0 +1,2354 @@
+//! Code related to match expressions. These are sufficiently complex to
+//! warrant their own module and submodules. :) This main module includes the
+//! high-level algorithm, the submodules contain the details.
+//!
+//! This also includes code for pattern bindings in `let` statements and
+//! function parameters.
+
+use crate::build::expr::as_place::PlaceBuilder;
+use crate::build::scope::DropKind;
+use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
+use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
+use rustc_data_structures::{
+ fx::{FxHashSet, FxIndexMap, FxIndexSet},
+ stack::ensure_sufficient_stack,
+};
+use rustc_index::bit_set::BitSet;
+use rustc_middle::middle::region;
+use rustc_middle::mir::*;
+use rustc_middle::thir::{self, *};
+use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
+use rustc_span::symbol::Symbol;
+use rustc_span::{BytePos, Pos, Span};
+use rustc_target::abi::VariantIdx;
+use smallvec::{smallvec, SmallVec};
+
+// helper functions, broken out by category:
+mod simplify;
+mod test;
+mod util;
+
+use std::borrow::Borrow;
+use std::convert::TryFrom;
+use std::mem;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ pub(crate) fn then_else_break(
+ &mut self,
+ mut block: BasicBlock,
+ expr: &Expr<'tcx>,
+ temp_scope_override: Option<region::Scope>,
+ break_scope: region::Scope,
+ variable_source_info: SourceInfo,
+ ) -> BlockAnd<()> {
+ let this = self;
+ let expr_span = expr.span;
+
+ match expr.kind {
+ ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
+ let lhs_then_block = unpack!(this.then_else_break(
+ block,
+ &this.thir[lhs],
+ temp_scope_override,
+ break_scope,
+ variable_source_info,
+ ));
+
+ let rhs_then_block = unpack!(this.then_else_break(
+ lhs_then_block,
+ &this.thir[rhs],
+ temp_scope_override,
+ break_scope,
+ variable_source_info,
+ ));
+
+ rhs_then_block.unit()
+ }
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ let region_scope = (region_scope, this.source_info(expr_span));
+ this.in_scope(region_scope, lint_level, |this| {
+ this.then_else_break(
+ block,
+ &this.thir[value],
+ temp_scope_override,
+ break_scope,
+ variable_source_info,
+ )
+ })
+ }
+ ExprKind::Let { expr, ref pat } => this.lower_let_expr(
+ block,
+ &this.thir[expr],
+ pat,
+ break_scope,
+ Some(variable_source_info.scope),
+ variable_source_info.span,
+ ),
+ _ => {
+ let temp_scope = temp_scope_override.unwrap_or_else(|| this.local_scope());
+ let mutability = Mutability::Mut;
+ let place =
+ unpack!(block = this.as_temp(block, Some(temp_scope), expr, mutability));
+ let operand = Operand::Move(Place::from(place));
+
+ let then_block = this.cfg.start_new_block();
+ let else_block = this.cfg.start_new_block();
+ let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block);
+
+ let source_info = this.source_info(expr_span);
+ this.cfg.terminate(block, source_info, term);
+ this.break_for_else(else_block, break_scope, source_info);
+
+ then_block.unit()
+ }
+ }
+ }
+
+ /// Generates MIR for a `match` expression.
+ ///
+ /// The MIR that we generate for a match looks like this.
+ ///
+ /// ```text
+ /// [ 0. Pre-match ]
+ /// |
+ /// [ 1. Evaluate Scrutinee (expression being matched on) ]
+ /// [ (fake read of scrutinee) ]
+ /// |
+ /// [ 2. Decision tree -- check discriminants ] <--------+
+ /// | |
+ /// | (once a specific arm is chosen) |
+ /// | |
+ /// [pre_binding_block] [otherwise_block]
+ /// | |
+ /// [ 3. Create "guard bindings" for arm ] |
+ /// [ (create fake borrows) ] |
+ /// | |
+ /// [ 4. Execute guard code ] |
+ /// [ (read fake borrows) ] --(guard is false)-----------+
+ /// |
+ /// | (guard results in true)
+ /// |
+ /// [ 5. Create real bindings and execute arm ]
+ /// |
+ /// [ Exit match ]
+ /// ```
+ ///
+ /// All of the different arms have been stacked on top of each other to
+ /// simplify the diagram. For an arm with no guard the blocks marked 3 and
+ /// 4 and the fake borrows are omitted.
+ ///
+ /// We generate MIR in the following steps:
+ ///
+ /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]).
+ /// 2. Create the decision tree ([Builder::lower_match_tree]).
+ /// 3. Determine the fake borrows that are needed from the places that were
+ /// matched against and create the required temporaries for them
+ /// ([Builder::calculate_fake_borrows]).
+ /// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]).
+ ///
+ /// ## False edges
+ ///
+ /// We don't want to have the exact structure of the decision tree be
+ /// visible through borrow checking. False edges ensure that the CFG as
+ /// seen by borrow checking doesn't encode this. False edges are added:
+ ///
+ /// * From each pre-binding block to the next pre-binding block.
+ /// * From each otherwise block to the next pre-binding block.
+ #[tracing::instrument(level = "debug", skip(self, arms))]
+ pub(crate) fn match_expr(
+ &mut self,
+ destination: Place<'tcx>,
+ span: Span,
+ mut block: BasicBlock,
+ scrutinee: &Expr<'tcx>,
+ arms: &[ArmId],
+ ) -> BlockAnd<()> {
+ let scrutinee_span = scrutinee.span;
+ let scrutinee_place =
+ unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,));
+
+ let mut arm_candidates = self.create_match_candidates(scrutinee_place.clone(), &arms);
+
+ let match_has_guard = arms.iter().copied().any(|arm| self.thir[arm].guard.is_some());
+ let mut candidates =
+ arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::<Vec<_>>();
+
+ let match_start_span = span.shrink_to_lo().to(scrutinee.span);
+
+ let fake_borrow_temps = self.lower_match_tree(
+ block,
+ scrutinee_span,
+ match_start_span,
+ match_has_guard,
+ &mut candidates,
+ );
+
+ self.lower_match_arms(
+ destination,
+ scrutinee_place,
+ scrutinee_span,
+ arm_candidates,
+ self.source_info(span),
+ fake_borrow_temps,
+ )
+ }
+
+ /// Evaluate the scrutinee and add the fake read of it.
+ fn lower_scrutinee(
+ &mut self,
+ mut block: BasicBlock,
+ scrutinee: &Expr<'tcx>,
+ scrutinee_span: Span,
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee));
+ // Matching on a `scrutinee_place` with an uninhabited type doesn't
+ // generate any memory reads by itself, and so if the place "expression"
+ // contains unsafe operations like raw pointer dereferences or union
+ // field projections, we wouldn't know to require an `unsafe` block
+ // around a `match` equivalent to `std::intrinsics::unreachable()`.
+ // See issue #47412 for this hole being discovered in the wild.
+ //
+ // HACK(eddyb) Work around the above issue by adding a dummy inspection
+ // of `scrutinee_place`, specifically by applying `ReadForMatch`.
+ //
+ // NOTE: ReadForMatch also checks that the scrutinee is initialized.
+ // This is currently needed to not allow matching on an uninitialized,
+ // uninhabited value. If we get never patterns, those will check that
+ // the place is initialized, and so this read would only be used to
+ // check safety.
+ let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
+ let source_info = self.source_info(scrutinee_span);
+
+ if let Ok(scrutinee_builder) =
+ scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
+ {
+ let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results);
+ self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
+ }
+
+ block.and(scrutinee_place_builder)
+ }
+
+ /// Create the initial `Candidate`s for a `match` expression.
+ fn create_match_candidates<'pat>(
+ &mut self,
+ scrutinee: PlaceBuilder<'tcx>,
+ arms: &'pat [ArmId],
+ ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)>
+ where
+ 'a: 'pat,
+ {
+ // Assemble a list of candidates: there is one candidate per pattern,
+ // which means there may be more than one candidate *per arm*.
+ arms.iter()
+ .copied()
+ .map(|arm| {
+ let arm = &self.thir[arm];
+ let arm_has_guard = arm.guard.is_some();
+ let arm_candidate = Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard);
+ (arm, arm_candidate)
+ })
+ .collect()
+ }
+
+ /// Create the decision tree for the match expression, starting from `block`.
+ ///
+ /// Modifies `candidates` to store the bindings and type ascriptions for
+ /// that candidate.
+ ///
+ /// Returns the places that need fake borrows because we bind or test them.
+ fn lower_match_tree<'pat>(
+ &mut self,
+ block: BasicBlock,
+ scrutinee_span: Span,
+ match_start_span: Span,
+ match_has_guard: bool,
+ candidates: &mut [&mut Candidate<'pat, 'tcx>],
+ ) -> Vec<(Place<'tcx>, Local)> {
+ // The set of places that we are creating fake borrows of. If there are
+ // no match guards then we don't need any fake borrows, so don't track
+ // them.
+ let mut fake_borrows = match_has_guard.then(FxIndexSet::default);
+
+ let mut otherwise = None;
+
+ // This will generate code to test scrutinee_place and
+ // branch to the appropriate arm block
+ self.match_candidates(
+ match_start_span,
+ scrutinee_span,
+ block,
+ &mut otherwise,
+ candidates,
+ &mut fake_borrows,
+ );
+
+ if let Some(otherwise_block) = otherwise {
+ // See the doc comment on `match_candidates` for why we may have an
+ // otherwise block. Match checking will ensure this is actually
+ // unreachable.
+ let source_info = self.source_info(scrutinee_span);
+ self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
+ }
+
+ // Link each leaf candidate to the `pre_binding_block` of the next one.
+ let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
+
+ for candidate in candidates {
+ candidate.visit_leaves(|leaf_candidate| {
+ if let Some(ref mut prev) = previous_candidate {
+ prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block;
+ }
+ previous_candidate = Some(leaf_candidate);
+ });
+ }
+
+ if let Some(ref borrows) = fake_borrows {
+ self.calculate_fake_borrows(borrows, scrutinee_span)
+ } else {
+ Vec::new()
+ }
+ }
+
+ /// Lower the bindings, guards and arm bodies of a `match` expression.
+ ///
+ /// The decision tree should have already been created
+ /// (by [Builder::lower_match_tree]).
+ ///
+ /// `outer_source_info` is the SourceInfo for the whole match.
+ fn lower_match_arms(
+ &mut self,
+ destination: Place<'tcx>,
+ scrutinee_place_builder: PlaceBuilder<'tcx>,
+ scrutinee_span: Span,
+ arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
+ outer_source_info: SourceInfo,
+ fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
+ ) -> BlockAnd<()> {
+ let arm_end_blocks: Vec<_> = arm_candidates
+ .into_iter()
+ .map(|(arm, candidate)| {
+ debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate);
+
+ let arm_source_info = self.source_info(arm.span);
+ let arm_scope = (arm.scope, arm_source_info);
+ let match_scope = self.local_scope();
+ self.in_scope(arm_scope, arm.lint_level, |this| {
+ // `try_upvars_resolved` may fail if it is unable to resolve the given
+ // `PlaceBuilder` inside a closure. In this case, we don't want to include
+ // a scrutinee place. `scrutinee_place_builder` will fail to be resolved
+ // if the only match arm is a wildcard (`_`).
+ // Example:
+ // ```
+ // let foo = (0, 1);
+ // let c = || {
+ // match foo { _ => () };
+ // };
+ // ```
+ let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
+ let scrutinee_place: Place<'tcx>;
+ if let Ok(scrutinee_builder) = scrutinee_place_builder
+ .clone()
+ .try_upvars_resolved(this.tcx, this.typeck_results)
+ {
+ scrutinee_place =
+ scrutinee_builder.into_place(this.tcx, this.typeck_results);
+ opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
+ }
+ let scope = this.declare_bindings(
+ None,
+ arm.span,
+ &arm.pattern,
+ ArmHasGuard(arm.guard.is_some()),
+ opt_scrutinee_place,
+ );
+
+ let arm_block = this.bind_pattern(
+ outer_source_info,
+ candidate,
+ arm.guard.as_ref(),
+ &fake_borrow_temps,
+ scrutinee_span,
+ Some(arm.span),
+ Some(arm.scope),
+ Some(match_scope),
+ );
+
+ if let Some(source_scope) = scope {
+ this.source_scope = source_scope;
+ }
+
+ this.expr_into_dest(destination, arm_block, &&this.thir[arm.body])
+ })
+ })
+ .collect();
+
+ // all the arm blocks will rejoin here
+ let end_block = self.cfg.start_new_block();
+
+ let end_brace = self.source_info(
+ outer_source_info.span.with_lo(outer_source_info.span.hi() - BytePos::from_usize(1)),
+ );
+ for arm_block in arm_end_blocks {
+ let block = &self.cfg.basic_blocks[arm_block.0];
+ let last_location = block.statements.last().map(|s| s.source_info);
+
+ self.cfg.goto(unpack!(arm_block), last_location.unwrap_or(end_brace), end_block);
+ }
+
+ self.source_scope = outer_source_info.scope;
+
+ end_block.unit()
+ }
+
+ /// Binds the variables and ascribes types for a given `match` arm or
+ /// `let` binding.
+ ///
+ /// Also check if the guard matches, if it's provided.
+ /// `arm_scope` should be `Some` if and only if this is called for a
+ /// `match` arm.
+ fn bind_pattern(
+ &mut self,
+ outer_source_info: SourceInfo,
+ candidate: Candidate<'_, 'tcx>,
+ guard: Option<&Guard<'tcx>>,
+ fake_borrow_temps: &[(Place<'tcx>, Local)],
+ scrutinee_span: Span,
+ arm_span: Option<Span>,
+ arm_scope: Option<region::Scope>,
+ match_scope: Option<region::Scope>,
+ ) -> BasicBlock {
+ if candidate.subcandidates.is_empty() {
+ // Avoid generating another `BasicBlock` when we only have one
+ // candidate.
+ self.bind_and_guard_matched_candidate(
+ candidate,
+ &[],
+ guard,
+ fake_borrow_temps,
+ scrutinee_span,
+ arm_span,
+ match_scope,
+ true,
+ )
+ } else {
+ // It's helpful to avoid scheduling drops multiple times to save
+ // drop elaboration from having to clean up the extra drops.
+ //
+ // If we are in a `let` then we only schedule drops for the first
+ // candidate.
+ //
+ // If we're in a `match` arm then we could have a case like so:
+ //
+ // Ok(x) | Err(x) if return => { /* ... */ }
+ //
+ // In this case we don't want a drop of `x` scheduled when we
+ // return: it isn't bound by move until right before enter the arm.
+ // To handle this we instead unschedule it's drop after each time
+ // we lower the guard.
+ let target_block = self.cfg.start_new_block();
+ let mut schedule_drops = true;
+ // We keep a stack of all of the bindings and type ascriptions
+ // from the parent candidates that we visit, that also need to
+ // be bound for each candidate.
+ traverse_candidate(
+ candidate,
+ &mut Vec::new(),
+ &mut |leaf_candidate, parent_bindings| {
+ if let Some(arm_scope) = arm_scope {
+ self.clear_top_scope(arm_scope);
+ }
+ let binding_end = self.bind_and_guard_matched_candidate(
+ leaf_candidate,
+ parent_bindings,
+ guard,
+ &fake_borrow_temps,
+ scrutinee_span,
+ arm_span,
+ match_scope,
+ schedule_drops,
+ );
+ if arm_scope.is_none() {
+ schedule_drops = false;
+ }
+ self.cfg.goto(binding_end, outer_source_info, target_block);
+ },
+ |inner_candidate, parent_bindings| {
+ parent_bindings.push((inner_candidate.bindings, inner_candidate.ascriptions));
+ inner_candidate.subcandidates.into_iter()
+ },
+ |parent_bindings| {
+ parent_bindings.pop();
+ },
+ );
+
+ target_block
+ }
+ }
+
+ pub(super) fn expr_into_pattern(
+ &mut self,
+ mut block: BasicBlock,
+ irrefutable_pat: Pat<'tcx>,
+ initializer: &Expr<'tcx>,
+ ) -> BlockAnd<()> {
+ match *irrefutable_pat.kind {
+ // Optimize the case of `let x = ...` to write directly into `x`
+ PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => {
+ let place =
+ self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
+ unpack!(block = self.expr_into_dest(place, block, initializer));
+
+ // Inject a fake read, see comments on `FakeReadCause::ForLet`.
+ let source_info = self.source_info(irrefutable_pat.span);
+ self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);
+
+ self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
+ block.unit()
+ }
+
+ // Optimize the case of `let x: T = ...` to write directly
+ // into `x` and then require that `T == typeof(x)`.
+ //
+ // Weirdly, this is needed to prevent the
+ // `intrinsic-move-val.rs` test case from crashing. That
+ // test works with uninitialized values in a rather
+ // dubious way, so it may be that the test is kind of
+ // broken.
+ PatKind::AscribeUserType {
+ subpattern:
+ Pat {
+ kind:
+ box PatKind::Binding {
+ mode: BindingMode::ByValue,
+ var,
+ subpattern: None,
+ ..
+ },
+ ..
+ },
+ ascription: thir::Ascription { annotation, variance: _ },
+ } => {
+ let place =
+ self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
+ unpack!(block = self.expr_into_dest(place, block, initializer));
+
+ // Inject a fake read, see comments on `FakeReadCause::ForLet`.
+ let pattern_source_info = self.source_info(irrefutable_pat.span);
+ let cause_let = FakeReadCause::ForLet(None);
+ self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);
+
+ let ty_source_info = self.source_info(annotation.span);
+
+ let base = self.canonical_user_type_annotations.push(annotation);
+ self.cfg.push(
+ block,
+ Statement {
+ source_info: ty_source_info,
+ kind: StatementKind::AscribeUserType(
+ Box::new((place, UserTypeProjection { base, projs: Vec::new() })),
+ // We always use invariant as the variance here. This is because the
+ // variance field from the ascription refers to the variance to use
+ // when applying the type to the value being matched, but this
+ // ascription applies rather to the type of the binding. e.g., in this
+ // example:
+ //
+ // ```
+ // let x: T = <expr>
+ // ```
+ //
+ // We are creating an ascription that defines the type of `x` to be
+ // exactly `T` (i.e., with invariance). The variance field, in
+ // contrast, is intended to be used to relate `T` to the type of
+ // `<expr>`.
+ ty::Variance::Invariant,
+ ),
+ },
+ );
+
+ self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
+ block.unit()
+ }
+
+ _ => {
+ let place_builder = unpack!(block = self.as_place_builder(block, initializer));
+ self.place_into_pattern(block, irrefutable_pat, place_builder, true)
+ }
+ }
+ }
+
+ pub(crate) fn place_into_pattern(
+ &mut self,
+ block: BasicBlock,
+ irrefutable_pat: Pat<'tcx>,
+ initializer: PlaceBuilder<'tcx>,
+ set_match_place: bool,
+ ) -> BlockAnd<()> {
+ let mut candidate = Candidate::new(initializer.clone(), &irrefutable_pat, false);
+ let fake_borrow_temps = self.lower_match_tree(
+ block,
+ irrefutable_pat.span,
+ irrefutable_pat.span,
+ false,
+ &mut [&mut candidate],
+ );
+ // For matches and function arguments, the place that is being matched
+ // can be set when creating the variables. But the place for
+ // let PATTERN = ... might not even exist until we do the assignment.
+ // so we set it here instead.
+ if set_match_place {
+ let mut candidate_ref = &candidate;
+ while let Some(next) = {
+ for binding in &candidate_ref.bindings {
+ let local = self.var_local_id(binding.var_id, OutsideGuard);
+
+ let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
+ VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
+ )))) = self.local_decls[local].local_info else {
+ bug!("Let binding to non-user variable.")
+ };
+ // `try_upvars_resolved` may fail if it is unable to resolve the given
+ // `PlaceBuilder` inside a closure. In this case, we don't want to include
+ // a scrutinee place. `scrutinee_place_builder` will fail for destructured
+ // assignments. This is because a closure only captures the precise places
+ // that it will read and as a result a closure may not capture the entire
+ // tuple/struct and rather have individual places that will be read in the
+ // final MIR.
+ // Example:
+ // ```
+ // let foo = (0, 1);
+ // let c = || {
+ // let (v1, v2) = foo;
+ // };
+ // ```
+ if let Ok(match_pair_resolved) =
+ initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
+ {
+ let place = match_pair_resolved.into_place(self.tcx, self.typeck_results);
+ *match_place = Some(place);
+ }
+ }
+ // All of the subcandidates should bind the same locals, so we
+ // only visit the first one.
+ candidate_ref.subcandidates.get(0)
+ } {
+ candidate_ref = next;
+ }
+ }
+
+ self.bind_pattern(
+ self.source_info(irrefutable_pat.span),
+ candidate,
+ None,
+ &fake_borrow_temps,
+ irrefutable_pat.span,
+ None,
+ None,
+ None,
+ )
+ .unit()
+ }
+
+ /// Declares the bindings of the given patterns and returns the visibility
+ /// scope for the bindings in these patterns, if such a scope had to be
+ /// created. NOTE: Declaring the bindings should always be done in their
+ /// drop scope.
+ pub(crate) fn declare_bindings(
+ &mut self,
+ mut visibility_scope: Option<SourceScope>,
+ scope_span: Span,
+ pattern: &Pat<'tcx>,
+ has_guard: ArmHasGuard,
+ opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
+ ) -> Option<SourceScope> {
+ debug!("declare_bindings: pattern={:?}", pattern);
+ self.visit_primary_bindings(
+ &pattern,
+ UserTypeProjections::none(),
+ &mut |this, mutability, name, mode, var, span, ty, user_ty| {
+ if visibility_scope.is_none() {
+ visibility_scope =
+ Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
+ }
+ let source_info = SourceInfo { span, scope: this.source_scope };
+ let visibility_scope = visibility_scope.unwrap();
+ this.declare_binding(
+ source_info,
+ visibility_scope,
+ mutability,
+ name,
+ mode,
+ var,
+ ty,
+ user_ty,
+ has_guard,
+ opt_match_place.map(|(x, y)| (x.cloned(), y)),
+ pattern.span,
+ );
+ },
+ );
+ visibility_scope
+ }
+
+ pub(crate) fn storage_live_binding(
+ &mut self,
+ block: BasicBlock,
+ var: LocalVarId,
+ span: Span,
+ for_guard: ForGuard,
+ schedule_drop: bool,
+ ) -> Place<'tcx> {
+ let local_id = self.var_local_id(var, for_guard);
+ let source_info = self.source_info(span);
+ self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
+ // Altough there is almost always scope for given variable in corner cases
+ // like #92893 we might get variable with no scope.
+ if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) && schedule_drop{
+ self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
+ }
+ Place::from(local_id)
+ }
+
+ pub(crate) fn schedule_drop_for_binding(
+ &mut self,
+ var: LocalVarId,
+ span: Span,
+ for_guard: ForGuard,
+ ) {
+ let local_id = self.var_local_id(var, for_guard);
+ if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) {
+ self.schedule_drop(span, region_scope, local_id, DropKind::Value);
+ }
+ }
+
+ /// Visit all of the primary bindings in a patterns, that is, visit the
+ /// leftmost occurrence of each variable bound in a pattern. A variable
+ /// will occur more than once in an or-pattern.
+ pub(super) fn visit_primary_bindings(
+ &mut self,
+ pattern: &Pat<'tcx>,
+ pattern_user_ty: UserTypeProjections,
+ f: &mut impl FnMut(
+ &mut Self,
+ Mutability,
+ Symbol,
+ BindingMode,
+ LocalVarId,
+ Span,
+ Ty<'tcx>,
+ UserTypeProjections,
+ ),
+ ) {
+ debug!(
+ "visit_primary_bindings: pattern={:?} pattern_user_ty={:?}",
+ pattern, pattern_user_ty
+ );
+ match *pattern.kind {
+ PatKind::Binding {
+ mutability,
+ name,
+ mode,
+ var,
+ ty,
+ ref subpattern,
+ is_primary,
+ ..
+ } => {
+ if is_primary {
+ f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone());
+ }
+ if let Some(subpattern) = subpattern.as_ref() {
+ self.visit_primary_bindings(subpattern, pattern_user_ty, f);
+ }
+ }
+
+ PatKind::Array { ref prefix, ref slice, ref suffix }
+ | PatKind::Slice { ref prefix, ref slice, ref suffix } => {
+ let from = u64::try_from(prefix.len()).unwrap();
+ let to = u64::try_from(suffix.len()).unwrap();
+ for subpattern in prefix {
+ self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f);
+ }
+ for subpattern in slice {
+ self.visit_primary_bindings(
+ subpattern,
+ pattern_user_ty.clone().subslice(from, to),
+ f,
+ );
+ }
+ for subpattern in suffix {
+ self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f);
+ }
+ }
+
+ PatKind::Constant { .. } | PatKind::Range { .. } | PatKind::Wild => {}
+
+ PatKind::Deref { ref subpattern } => {
+ self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f);
+ }
+
+ PatKind::AscribeUserType {
+ ref subpattern,
+ ascription: thir::Ascription { ref annotation, variance: _ },
+ } => {
+ // This corresponds to something like
+ //
+ // ```
+ // let A::<'a>(_): A<'static> = ...;
+ // ```
+ //
+ // Note that the variance doesn't apply here, as we are tracking the effect
+ // of `user_ty` on any bindings contained with subpattern.
+
+ let projection = UserTypeProjection {
+ base: self.canonical_user_type_annotations.push(annotation.clone()),
+ projs: Vec::new(),
+ };
+ let subpattern_user_ty =
+ pattern_user_ty.push_projection(&projection, annotation.span);
+ self.visit_primary_bindings(subpattern, subpattern_user_ty, f)
+ }
+
+ PatKind::Leaf { ref subpatterns } => {
+ for subpattern in subpatterns {
+ let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field);
+ debug!("visit_primary_bindings: subpattern_user_ty={:?}", subpattern_user_ty);
+ self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f);
+ }
+ }
+
+ PatKind::Variant { adt_def, substs: _, variant_index, ref subpatterns } => {
+ for subpattern in subpatterns {
+ let subpattern_user_ty =
+ pattern_user_ty.clone().variant(adt_def, variant_index, subpattern.field);
+ self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f);
+ }
+ }
+ PatKind::Or { ref pats } => {
+ // In cases where we recover from errors the primary bindings
+ // may not all be in the leftmost subpattern. For example in
+ // `let (x | y) = ...`, the primary binding of `y` occurs in
+ // the right subpattern
+ for subpattern in pats {
+ self.visit_primary_bindings(subpattern, pattern_user_ty.clone(), f);
+ }
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
+struct Candidate<'pat, 'tcx> {
+ /// [`Span`] of the original pattern that gave rise to this candidate.
+ span: Span,
+
+ /// Whether this `Candidate` has a guard.
+ has_guard: bool,
+
+ /// All of these must be satisfied...
+ match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
+
+ /// ...these bindings established...
+ bindings: Vec<Binding<'tcx>>,
+
+ /// ...and these types asserted...
+ ascriptions: Vec<Ascription<'tcx>>,
+
+ /// ...and if this is non-empty, one of these subcandidates also has to match...
+ subcandidates: Vec<Candidate<'pat, 'tcx>>,
+
+ /// ...and the guard must be evaluated; if it's `false` then branch to `otherwise_block`.
+ otherwise_block: Option<BasicBlock>,
+
+ /// The block before the `bindings` have been established.
+ pre_binding_block: Option<BasicBlock>,
+ /// The pre-binding block of the next candidate.
+ next_candidate_pre_binding_block: Option<BasicBlock>,
+}
+
+impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
+ fn new(place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self {
+ Candidate {
+ span: pattern.span,
+ has_guard,
+ match_pairs: smallvec![MatchPair { place, pattern }],
+ bindings: Vec::new(),
+ ascriptions: Vec::new(),
+ subcandidates: Vec::new(),
+ otherwise_block: None,
+ pre_binding_block: None,
+ next_candidate_pre_binding_block: None,
+ }
+ }
+
+ /// Visit the leaf candidates (those with no subcandidates) contained in
+ /// this candidate.
+ fn visit_leaves<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) {
+ traverse_candidate(
+ self,
+ &mut (),
+ &mut move |c, _| visit_leaf(c),
+ move |c, _| c.subcandidates.iter_mut(),
+ |_| {},
+ );
+ }
+}
+
+/// A depth-first traversal of the `Candidate` and all of its recursive
+/// subcandidates.
+fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>(
+ candidate: C,
+ context: &mut T,
+ visit_leaf: &mut impl FnMut(C, &mut T),
+ get_children: impl Copy + Fn(C, &mut T) -> I,
+ complete_children: impl Copy + Fn(&mut T),
+) where
+ C: Borrow<Candidate<'pat, 'tcx>>,
+ I: Iterator<Item = C>,
+{
+ if candidate.borrow().subcandidates.is_empty() {
+ visit_leaf(candidate, context)
+ } else {
+ for child in get_children(candidate, context) {
+ traverse_candidate(child, context, visit_leaf, get_children, complete_children);
+ }
+ complete_children(context)
+ }
+}
+
+#[derive(Clone, Debug)]
+struct Binding<'tcx> {
+ span: Span,
+ source: Place<'tcx>,
+ var_id: LocalVarId,
+ binding_mode: BindingMode,
+}
+
+/// Indicates that the type of `source` must be a subtype of the
+/// user-given type `user_ty`; this is basically a no-op but can
+/// influence region inference.
+#[derive(Clone, Debug)]
+struct Ascription<'tcx> {
+ source: Place<'tcx>,
+ annotation: CanonicalUserTypeAnnotation<'tcx>,
+ variance: ty::Variance,
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct MatchPair<'pat, 'tcx> {
+ // this place...
+ place: PlaceBuilder<'tcx>,
+
+ // ... must match this pattern.
+ pattern: &'pat Pat<'tcx>,
+}
+
+/// See [`Test`] for more.
+#[derive(Clone, Debug, PartialEq)]
+enum TestKind<'tcx> {
+ /// Test what enum variant a value is.
+ Switch {
+ /// The enum type being tested.
+ adt_def: ty::AdtDef<'tcx>,
+ /// The set of variants that we should create a branch for. We also
+ /// create an additional "otherwise" case.
+ variants: BitSet<VariantIdx>,
+ },
+
+ /// Test what value an integer, `bool`, or `char` has.
+ SwitchInt {
+ /// The type of the value that we're testing.
+ switch_ty: Ty<'tcx>,
+ /// The (ordered) set of values that we test for.
+ ///
+ /// For integers and `char`s we create a branch to each of the values in
+ /// `options`, as well as an "otherwise" branch for all other values, even
+ /// in the (rare) case that `options` is exhaustive.
+ ///
+ /// For `bool` we always generate two edges, one for `true` and one for
+ /// `false`.
+ options: FxIndexMap<ConstantKind<'tcx>, u128>,
+ },
+
+ /// Test for equality with value, possibly after an unsizing coercion to
+ /// `ty`,
+ Eq {
+ value: ConstantKind<'tcx>,
+ // Integer types are handled by `SwitchInt`, and constants with ADT
+ // types are converted back into patterns, so this can only be `&str`,
+ // `&[T]`, `f32` or `f64`.
+ ty: Ty<'tcx>,
+ },
+
+ /// Test whether the value falls within an inclusive or exclusive range
+ Range(PatRange<'tcx>),
+
+ /// Test that the length of the slice is equal to `len`.
+ Len { len: u64, op: BinOp },
+}
+
+/// A test to perform to determine which [`Candidate`] matches a value.
+///
+/// [`Test`] is just the test to perform; it does not include the value
+/// to be tested.
+#[derive(Debug)]
+pub(crate) struct Test<'tcx> {
+ span: Span,
+ kind: TestKind<'tcx>,
+}
+
+/// `ArmHasGuard` is a wrapper around a boolean flag. It indicates whether
+/// a match arm has a guard expression attached to it.
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct ArmHasGuard(pub(crate) bool);
+
+///////////////////////////////////////////////////////////////////////////
+// Main matching algorithm
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// The main match algorithm. It begins with a set of candidates
+ /// `candidates` and has the job of generating code to determine
+ /// which of these candidates, if any, is the correct one. The
+ /// candidates are sorted such that the first item in the list
+ /// has the highest priority. When a candidate is found to match
+ /// the value, we will set and generate a branch to the appropriate
+ /// pre-binding block.
+ ///
+ /// If we find that *NONE* of the candidates apply, we branch to the
+ /// `otherwise_block`, setting it to `Some` if required. In principle, this
+ /// means that the input list was not exhaustive, though at present we
+ /// sometimes are not smart enough to recognize all exhaustive inputs.
+ ///
+ /// It might be surprising that the input can be non-exhaustive.
+ /// Indeed, initially, it is not, because all matches are
+ /// exhaustive in Rust. But during processing we sometimes divide
+ /// up the list of candidates and recurse with a non-exhaustive
+ /// list. This is important to keep the size of the generated code
+ /// under control. See [`Builder::test_candidates`] for more details.
+ ///
+ /// If `fake_borrows` is `Some`, then places which need fake borrows
+ /// will be added to it.
+ ///
+ /// For an example of a case where we set `otherwise_block`, even for an
+ /// exhaustive match, consider:
+ ///
+ /// ```
+ /// # fn foo(x: (bool, bool)) {
+ /// match x {
+ /// (true, true) => (),
+ /// (_, false) => (),
+ /// (false, true) => (),
+ /// }
+ /// # }
+ /// ```
+ ///
+ /// For this match, we check if `x.0` matches `true` (for the first
+ /// arm). If it doesn't match, we check `x.1`. If `x.1` is `true` we check
+ /// if `x.0` matches `false` (for the third arm). In the (impossible at
+ /// runtime) case when `x.0` is now `true`, we branch to
+ /// `otherwise_block`.
+ fn match_candidates<'pat>(
+ &mut self,
+ span: Span,
+ scrutinee_span: Span,
+ start_block: BasicBlock,
+ otherwise_block: &mut Option<BasicBlock>,
+ candidates: &mut [&mut Candidate<'pat, 'tcx>],
+ fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
+ ) {
+ debug!(
+ "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
+ span, candidates, start_block, otherwise_block,
+ );
+
+ // Start by simplifying candidates. Once this process is complete, all
+ // the match pairs which remain require some form of test, whether it
+ // be a switch or pattern comparison.
+ let mut split_or_candidate = false;
+ for candidate in &mut *candidates {
+ split_or_candidate |= self.simplify_candidate(candidate);
+ }
+
+ ensure_sufficient_stack(|| {
+ if split_or_candidate {
+ // At least one of the candidates has been split into subcandidates.
+ // We need to change the candidate list to include those.
+ let mut new_candidates = Vec::new();
+
+ for candidate in candidates {
+ candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate));
+ }
+ self.match_simplified_candidates(
+ span,
+ scrutinee_span,
+ start_block,
+ otherwise_block,
+ &mut *new_candidates,
+ fake_borrows,
+ );
+ } else {
+ self.match_simplified_candidates(
+ span,
+ scrutinee_span,
+ start_block,
+ otherwise_block,
+ candidates,
+ fake_borrows,
+ );
+ }
+ });
+ }
+
+ fn match_simplified_candidates(
+ &mut self,
+ span: Span,
+ scrutinee_span: Span,
+ start_block: BasicBlock,
+ otherwise_block: &mut Option<BasicBlock>,
+ candidates: &mut [&mut Candidate<'_, 'tcx>],
+ fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
+ ) {
+ // The candidates are sorted by priority. Check to see whether the
+ // higher priority candidates (and hence at the front of the slice)
+ // have satisfied all their match pairs.
+ let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
+ debug!("match_candidates: {:?} candidates fully matched", fully_matched);
+ let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
+
+ let block = if !matched_candidates.is_empty() {
+ let otherwise_block =
+ self.select_matched_candidates(matched_candidates, start_block, fake_borrows);
+
+ if let Some(last_otherwise_block) = otherwise_block {
+ last_otherwise_block
+ } else {
+ // Any remaining candidates are unreachable.
+ if unmatched_candidates.is_empty() {
+ return;
+ }
+ self.cfg.start_new_block()
+ }
+ } else {
+ start_block
+ };
+
+ // If there are no candidates that still need testing, we're
+ // done. Since all matches are exhaustive, execution should
+ // never reach this point.
+ if unmatched_candidates.is_empty() {
+ let source_info = self.source_info(span);
+ if let Some(otherwise) = *otherwise_block {
+ self.cfg.goto(block, source_info, otherwise);
+ } else {
+ *otherwise_block = Some(block);
+ }
+ return;
+ }
+
+ // Test for the remaining candidates.
+ self.test_candidates_with_or(
+ span,
+ scrutinee_span,
+ unmatched_candidates,
+ block,
+ otherwise_block,
+ fake_borrows,
+ );
+ }
+
+ /// Link up matched candidates.
+ ///
+ /// For example, if we have something like this:
+ ///
+ /// ```ignore (illustrative)
+ /// ...
+ /// Some(x) if cond1 => ...
+ /// Some(x) => ...
+ /// Some(x) if cond2 => ...
+ /// ...
+ /// ```
+ ///
+ /// We generate real edges from:
+ ///
+ /// * `start_block` to the [pre-binding block] of the first pattern,
+ /// * the [otherwise block] of the first pattern to the second pattern,
+ /// * the [otherwise block] of the third pattern to a block with an
+ /// [`Unreachable` terminator](TerminatorKind::Unreachable).
+ ///
+ /// In addition, we add fake edges from the otherwise blocks to the
+ /// pre-binding block of the next candidate in the original set of
+ /// candidates.
+ ///
+ /// [pre-binding block]: Candidate::pre_binding_block
+ /// [otherwise block]: Candidate::otherwise_block
+ fn select_matched_candidates(
+ &mut self,
+ matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
+ start_block: BasicBlock,
+ fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
+ ) -> Option<BasicBlock> {
+ debug_assert!(
+ !matched_candidates.is_empty(),
+ "select_matched_candidates called with no candidates",
+ );
+ debug_assert!(
+ matched_candidates.iter().all(|c| c.subcandidates.is_empty()),
+ "subcandidates should be empty in select_matched_candidates",
+ );
+
+ // Insert a borrows of prefixes of places that are bound and are
+ // behind a dereference projection.
+ //
+ // These borrows are taken to avoid situations like the following:
+ //
+ // match x[10] {
+ // _ if { x = &[0]; false } => (),
+ // y => (), // Out of bounds array access!
+ // }
+ //
+ // match *x {
+ // // y is bound by reference in the guard and then by copy in the
+ // // arm, so y is 2 in the arm!
+ // y if { y == 1 && (x = &2) == () } => y,
+ // _ => 3,
+ // }
+ if let Some(fake_borrows) = fake_borrows {
+ for Binding { source, .. } in
+ matched_candidates.iter().flat_map(|candidate| &candidate.bindings)
+ {
+ if let Some(i) =
+ source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref)
+ {
+ let proj_base = &source.projection[..i];
+
+ fake_borrows.insert(Place {
+ local: source.local,
+ projection: self.tcx.intern_place_elems(proj_base),
+ });
+ }
+ }
+ }
+
+ let fully_matched_with_guard = matched_candidates
+ .iter()
+ .position(|c| !c.has_guard)
+ .unwrap_or(matched_candidates.len() - 1);
+
+ let (reachable_candidates, unreachable_candidates) =
+ matched_candidates.split_at_mut(fully_matched_with_guard + 1);
+
+ let mut next_prebinding = start_block;
+
+ for candidate in reachable_candidates.iter_mut() {
+ assert!(candidate.otherwise_block.is_none());
+ assert!(candidate.pre_binding_block.is_none());
+ candidate.pre_binding_block = Some(next_prebinding);
+ if candidate.has_guard {
+ // Create the otherwise block for this candidate, which is the
+ // pre-binding block for the next candidate.
+ next_prebinding = self.cfg.start_new_block();
+ candidate.otherwise_block = Some(next_prebinding);
+ }
+ }
+
+ debug!(
+ "match_candidates: add pre_binding_blocks for unreachable {:?}",
+ unreachable_candidates,
+ );
+ for candidate in unreachable_candidates {
+ assert!(candidate.pre_binding_block.is_none());
+ candidate.pre_binding_block = Some(self.cfg.start_new_block());
+ }
+
+ reachable_candidates.last_mut().unwrap().otherwise_block
+ }
+
+ /// Tests a candidate where there are only or-patterns left to test, or
+ /// forwards to [Builder::test_candidates].
+ ///
+ /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
+ /// so:
+ ///
+ /// ```text
+ /// [ start ]
+ /// |
+ /// [ match P, Q ]
+ /// |
+ /// +----------------------------------------+------------------------------------+
+ /// | | |
+ /// V V V
+ /// [ P matches ] [ Q matches ] [ otherwise ]
+ /// | | |
+ /// V V |
+ /// [ match R, S ] [ match R, S ] |
+ /// | | |
+ /// +--------------+------------+ +--------------+------------+ |
+ /// | | | | | | |
+ /// V V V V V V |
+ /// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] |
+ /// | | | | | | |
+ /// +--------------+------------|------------+--------------+ | |
+ /// | | | |
+ /// | +----------------------------------------+--------+
+ /// | |
+ /// V V
+ /// [ Success ] [ Failure ]
+ /// ```
+ ///
+ /// In practice there are some complications:
+ ///
+ /// * If there's a guard, then the otherwise branch of the first match on
+ /// `R | S` goes to a test for whether `Q` matches, and the control flow
+ /// doesn't merge into a single success block until after the guard is
+ /// tested.
+ /// * If neither `P` or `Q` has any bindings or type ascriptions and there
+ /// isn't a match guard, then we create a smaller CFG like:
+ ///
+ /// ```text
+ /// ...
+ /// +---------------+------------+
+ /// | | |
+ /// [ P matches ] [ Q matches ] [ otherwise ]
+ /// | | |
+ /// +---------------+ |
+ /// | ...
+ /// [ match R, S ]
+ /// |
+ /// ...
+ /// ```
+ fn test_candidates_with_or(
+ &mut self,
+ span: Span,
+ scrutinee_span: Span,
+ candidates: &mut [&mut Candidate<'_, 'tcx>],
+ block: BasicBlock,
+ otherwise_block: &mut Option<BasicBlock>,
+ fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
+ ) {
+ let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
+
+ // All of the or-patterns have been sorted to the end, so if the first
+ // pattern is an or-pattern we only have or-patterns.
+ match *first_candidate.match_pairs[0].pattern.kind {
+ PatKind::Or { .. } => (),
+ _ => {
+ self.test_candidates(
+ span,
+ scrutinee_span,
+ candidates,
+ block,
+ otherwise_block,
+ fake_borrows,
+ );
+ return;
+ }
+ }
+
+ let match_pairs = mem::take(&mut first_candidate.match_pairs);
+ first_candidate.pre_binding_block = Some(block);
+
+ let mut otherwise = None;
+ for match_pair in match_pairs {
+ let PatKind::Or { ref pats } = &*match_pair.pattern.kind else {
+ bug!("Or-patterns should have been sorted to the end");
+ };
+ let or_span = match_pair.pattern.span;
+ let place = match_pair.place;
+
+ first_candidate.visit_leaves(|leaf_candidate| {
+ self.test_or_pattern(
+ leaf_candidate,
+ &mut otherwise,
+ pats,
+ or_span,
+ place.clone(),
+ fake_borrows,
+ );
+ });
+ }
+
+ let remainder_start = otherwise.unwrap_or_else(|| self.cfg.start_new_block());
+
+ self.match_candidates(
+ span,
+ scrutinee_span,
+ remainder_start,
+ otherwise_block,
+ remaining_candidates,
+ fake_borrows,
+ )
+ }
+
+ fn test_or_pattern<'pat>(
+ &mut self,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ otherwise: &mut Option<BasicBlock>,
+ pats: &'pat [Pat<'tcx>],
+ or_span: Span,
+ place: PlaceBuilder<'tcx>,
+ fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
+ ) {
+ debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats);
+ let mut or_candidates: Vec<_> = pats
+ .iter()
+ .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard))
+ .collect();
+ let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
+ let otherwise = if candidate.otherwise_block.is_some() {
+ &mut candidate.otherwise_block
+ } else {
+ otherwise
+ };
+ self.match_candidates(
+ or_span,
+ or_span,
+ candidate.pre_binding_block.unwrap(),
+ otherwise,
+ &mut or_candidate_refs,
+ fake_borrows,
+ );
+ candidate.subcandidates = or_candidates;
+ self.merge_trivial_subcandidates(candidate, self.source_info(or_span));
+ }
+
+ /// Try to merge all of the subcandidates of the given candidate into one.
+ /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`.
+ fn merge_trivial_subcandidates(
+ &mut self,
+ candidate: &mut Candidate<'_, 'tcx>,
+ source_info: SourceInfo,
+ ) {
+ if candidate.subcandidates.is_empty() || candidate.has_guard {
+ // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
+ return;
+ }
+
+ let mut can_merge = true;
+
+ // Not `Iterator::all` because we don't want to short-circuit.
+ for subcandidate in &mut candidate.subcandidates {
+ self.merge_trivial_subcandidates(subcandidate, source_info);
+
+ // FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
+ can_merge &= subcandidate.subcandidates.is_empty()
+ && subcandidate.bindings.is_empty()
+ && subcandidate.ascriptions.is_empty();
+ }
+
+ if can_merge {
+ let any_matches = self.cfg.start_new_block();
+ for subcandidate in mem::take(&mut candidate.subcandidates) {
+ let or_block = subcandidate.pre_binding_block.unwrap();
+ self.cfg.goto(or_block, source_info, any_matches);
+ }
+ candidate.pre_binding_block = Some(any_matches);
+ }
+ }
+
+ /// This is the most subtle part of the matching algorithm. At
+ /// this point, the input candidates have been fully simplified,
+ /// and so we know that all remaining match-pairs require some
+ /// sort of test. To decide what test to perform, we take the highest
+ /// priority candidate (the first one in the list, as of January 2021)
+ /// and extract the first match-pair from the list. From this we decide
+ /// what kind of test is needed using [`Builder::test`], defined in the
+ /// [`test` module](mod@test).
+ ///
+ /// *Note:* taking the first match pair is somewhat arbitrary, and
+ /// we might do better here by choosing more carefully what to
+ /// test.
+ ///
+ /// For example, consider the following possible match-pairs:
+ ///
+ /// 1. `x @ Some(P)` -- we will do a [`Switch`] to decide what variant `x` has
+ /// 2. `x @ 22` -- we will do a [`SwitchInt`] to decide what value `x` has
+ /// 3. `x @ 3..5` -- we will do a [`Range`] test to decide what range `x` falls in
+ /// 4. etc.
+ ///
+ /// [`Switch`]: TestKind::Switch
+ /// [`SwitchInt`]: TestKind::SwitchInt
+ /// [`Range`]: TestKind::Range
+ ///
+ /// Once we know what sort of test we are going to perform, this
+ /// test may also help us winnow down our candidates. So we walk over
+ /// the candidates (from high to low priority) and check. This
+ /// gives us, for each outcome of the test, a transformed list of
+ /// candidates. For example, if we are testing `x.0`'s variant,
+ /// and we have a candidate `(x.0 @ Some(v), x.1 @ 22)`,
+ /// then we would have a resulting candidate of `((x.0 as Some).0 @ v, x.1 @ 22)`.
+ /// Note that the first match-pair is now simpler (and, in fact, irrefutable).
+ ///
+ /// But there may also be candidates that the test just doesn't
+ /// apply to. The classical example involves wildcards:
+ ///
+ /// ```
+ /// # let (x, y, z) = (true, true, true);
+ /// match (x, y, z) {
+ /// (true , _ , true ) => true, // (0)
+ /// (_ , true , _ ) => true, // (1)
+ /// (false, false, _ ) => false, // (2)
+ /// (true , _ , false) => false, // (3)
+ /// }
+ /// # ;
+ /// ```
+ ///
+ /// In that case, after we test on `x`, there are 2 overlapping candidate
+ /// sets:
+ ///
+ /// - If the outcome is that `x` is true, candidates 0, 1, and 3
+ /// - If the outcome is that `x` is false, candidates 1 and 2
+ ///
+ /// Here, the traditional "decision tree" method would generate 2
+ /// separate code-paths for the 2 separate cases.
+ ///
+ /// In some cases, this duplication can create an exponential amount of
+ /// code. This is most easily seen by noticing that this method terminates
+ /// with precisely the reachable arms being reachable - but that problem
+ /// is trivially NP-complete:
+ ///
+ /// ```ignore (illustrative)
+ /// match (var0, var1, var2, var3, ...) {
+ /// (true , _ , _ , false, true, ...) => false,
+ /// (_ , true, true , false, _ , ...) => false,
+ /// (false, _ , false, false, _ , ...) => false,
+ /// ...
+ /// _ => true
+ /// }
+ /// ```
+ ///
+ /// Here the last arm is reachable only if there is an assignment to
+ /// the variables that does not match any of the literals. Therefore,
+ /// compilation would take an exponential amount of time in some cases.
+ ///
+ /// That kind of exponential worst-case might not occur in practice, but
+ /// our simplistic treatment of constants and guards would make it occur
+ /// in very common situations - for example [#29740]:
+ ///
+ /// ```ignore (illustrative)
+ /// match x {
+ /// "foo" if foo_guard => ...,
+ /// "bar" if bar_guard => ...,
+ /// "baz" if baz_guard => ...,
+ /// ...
+ /// }
+ /// ```
+ ///
+ /// [#29740]: https://github.com/rust-lang/rust/issues/29740
+ ///
+ /// Here we first test the match-pair `x @ "foo"`, which is an [`Eq` test].
+ ///
+ /// [`Eq` test]: TestKind::Eq
+ ///
+ /// It might seem that we would end up with 2 disjoint candidate
+ /// sets, consisting of the first candidate or the other two, but our
+ /// algorithm doesn't reason about `"foo"` being distinct from the other
+ /// constants; it considers the latter arms to potentially match after
+ /// both outcomes, which obviously leads to an exponential number
+ /// of tests.
+ ///
+ /// To avoid these kinds of problems, our algorithm tries to ensure
+ /// the amount of generated tests is linear. When we do a k-way test,
+ /// we return an additional "unmatched" set alongside the obvious `k`
+ /// sets. When we encounter a candidate that would be present in more
+ /// than one of the sets, we put it and all candidates below it into the
+ /// "unmatched" set. This ensures these `k+1` sets are disjoint.
+ ///
+ /// After we perform our test, we branch into the appropriate candidate
+ /// set and recurse with `match_candidates`. These sub-matches are
+ /// obviously non-exhaustive - as we discarded our otherwise set - so
+ /// we set their continuation to do `match_candidates` on the
+ /// "unmatched" set (which is again non-exhaustive).
+ ///
+ /// If you apply this to the above test, you basically wind up
+ /// with an if-else-if chain, testing each candidate in turn,
+ /// which is precisely what we want.
+ ///
+ /// In addition to avoiding exponential-time blowups, this algorithm
+ /// also has the nice property that each guard and arm is only generated
+ /// once.
+ fn test_candidates<'pat, 'b, 'c>(
+ &mut self,
+ span: Span,
+ scrutinee_span: Span,
+ mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
+ block: BasicBlock,
+ otherwise_block: &mut Option<BasicBlock>,
+ fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
+ ) {
+ // extract the match-pair from the highest priority candidate
+ let match_pair = &candidates.first().unwrap().match_pairs[0];
+ let mut test = self.test(match_pair);
+ let match_place = match_pair.place.clone();
+
+ // most of the time, the test to perform is simply a function
+ // of the main candidate; but for a test like SwitchInt, we
+ // may want to add cases based on the candidates that are
+ // available
+ match test.kind {
+ TestKind::SwitchInt { switch_ty, ref mut options } => {
+ for candidate in candidates.iter() {
+ if !self.add_cases_to_switch(&match_place, candidate, switch_ty, options) {
+ break;
+ }
+ }
+ }
+ TestKind::Switch { adt_def: _, ref mut variants } => {
+ for candidate in candidates.iter() {
+ if !self.add_variants_to_switch(&match_place, candidate, variants) {
+ break;
+ }
+ }
+ }
+ _ => {}
+ }
+
+ // Insert a Shallow borrow of any places that is switched on.
+ if let Some(fb) = fake_borrows && let Ok(match_place_resolved) =
+ match_place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
+ {
+ let resolved_place = match_place_resolved.into_place(self.tcx, self.typeck_results);
+ fb.insert(resolved_place);
+ }
+
+ // perform the test, branching to one of N blocks. For each of
+ // those N possible outcomes, create a (initially empty)
+ // vector of candidates. Those are the candidates that still
+ // apply if the test has that particular outcome.
+ debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair);
+ let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
+ target_candidates.resize_with(test.targets(), Default::default);
+
+ let total_candidate_count = candidates.len();
+
+ // Sort the candidates into the appropriate vector in
+ // `target_candidates`. Note that at some point we may
+ // encounter a candidate where the test is not relevant; at
+ // that point, we stop sorting.
+ while let Some(candidate) = candidates.first_mut() {
+ let Some(idx) = self.sort_candidate(&match_place.clone(), &test, candidate) else {
+ break;
+ };
+ let (candidate, rest) = candidates.split_first_mut().unwrap();
+ target_candidates[idx].push(candidate);
+ candidates = rest;
+ }
+ // at least the first candidate ought to be tested
+ assert!(total_candidate_count > candidates.len());
+ debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len());
+ debug!("test_candidates: untested_candidates: {}", candidates.len());
+
+ // HACK(matthewjasper) This is a closure so that we can let the test
+ // create its blocks before the rest of the match. This currently
+ // improves the speed of llvm when optimizing long string literal
+ // matches
+ let make_target_blocks = move |this: &mut Self| -> Vec<BasicBlock> {
+ // The block that we should branch to if none of the
+ // `target_candidates` match. This is either the block where we
+ // start matching the untested candidates if there are any,
+ // otherwise it's the `otherwise_block`.
+ let remainder_start = &mut None;
+ let remainder_start =
+ if candidates.is_empty() { &mut *otherwise_block } else { remainder_start };
+
+ // For each outcome of test, process the candidates that still
+ // apply. Collect a list of blocks where control flow will
+ // branch if one of the `target_candidate` sets is not
+ // exhaustive.
+ let target_blocks: Vec<_> = target_candidates
+ .into_iter()
+ .map(|mut candidates| {
+ if !candidates.is_empty() {
+ let candidate_start = this.cfg.start_new_block();
+ this.match_candidates(
+ span,
+ scrutinee_span,
+ candidate_start,
+ remainder_start,
+ &mut *candidates,
+ fake_borrows,
+ );
+ candidate_start
+ } else {
+ *remainder_start.get_or_insert_with(|| this.cfg.start_new_block())
+ }
+ })
+ .collect();
+
+ if !candidates.is_empty() {
+ let remainder_start = remainder_start.unwrap_or_else(|| this.cfg.start_new_block());
+ this.match_candidates(
+ span,
+ scrutinee_span,
+ remainder_start,
+ otherwise_block,
+ candidates,
+ fake_borrows,
+ );
+ };
+
+ target_blocks
+ };
+
+ self.perform_test(span, scrutinee_span, block, match_place, &test, make_target_blocks);
+ }
+
+ /// Determine the fake borrows that are needed from a set of places that
+ /// have to be stable across match guards.
+ ///
+ /// Returns a list of places that need a fake borrow and the temporary
+ /// that's used to store the fake borrow.
+ ///
+ /// Match exhaustiveness checking is not able to handle the case where the
+ /// place being matched on is mutated in the guards. We add "fake borrows"
+ /// to the guards that prevent any mutation of the place being matched.
+ /// There are a some subtleties:
+ ///
+ /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared
+ /// reference, the borrow isn't even tracked. As such we have to add fake
+ /// borrows of any prefixes of a place
+ /// 2. We don't want `match x { _ => (), }` to conflict with mutable
+ /// borrows of `x`, so we only add fake borrows for places which are
+ /// bound or tested by the match.
+ /// 3. We don't want the fake borrows to conflict with `ref mut` bindings,
+ /// so we use a special BorrowKind for them.
+ /// 4. The fake borrows may be of places in inactive variants, so it would
+ /// be UB to generate code for them. They therefore have to be removed
+ /// by a MIR pass run after borrow checking.
+ fn calculate_fake_borrows<'b>(
+ &mut self,
+ fake_borrows: &'b FxIndexSet<Place<'tcx>>,
+ temp_span: Span,
+ ) -> Vec<(Place<'tcx>, Local)> {
+ let tcx = self.tcx;
+
+ debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
+
+ let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
+
+ // Insert a Shallow borrow of the prefixes of any fake borrows.
+ for place in fake_borrows {
+ let mut cursor = place.projection.as_ref();
+ while let [proj_base @ .., elem] = cursor {
+ cursor = proj_base;
+
+ if let ProjectionElem::Deref = elem {
+ // Insert a shallow borrow after a deref. For other
+ // projections the borrow of prefix_cursor will
+ // conflict with any mutation of base.
+ all_fake_borrows.push(PlaceRef { local: place.local, projection: proj_base });
+ }
+ }
+
+ all_fake_borrows.push(place.as_ref());
+ }
+
+ // Deduplicate
+ let mut dedup = FxHashSet::default();
+ all_fake_borrows.retain(|b| dedup.insert(*b));
+
+ debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
+
+ all_fake_borrows
+ .into_iter()
+ .map(|matched_place_ref| {
+ let matched_place = Place {
+ local: matched_place_ref.local,
+ projection: tcx.intern_place_elems(matched_place_ref.projection),
+ };
+ let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
+ let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+ let fake_borrow_temp =
+ self.local_decls.push(LocalDecl::new(fake_borrow_ty, temp_span));
+
+ (matched_place, fake_borrow_temp)
+ })
+ .collect()
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Pat binding - used for `let` and function parameters as well.
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ pub(crate) fn lower_let_expr(
+ &mut self,
+ mut block: BasicBlock,
+ expr: &Expr<'tcx>,
+ pat: &Pat<'tcx>,
+ else_target: region::Scope,
+ source_scope: Option<SourceScope>,
+ span: Span,
+ ) -> BlockAnd<()> {
+ let expr_span = expr.span;
+ let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span));
+ let wildcard = Pat::wildcard_from_ty(pat.ty);
+ let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false);
+ let mut otherwise_candidate = Candidate::new(expr_place_builder.clone(), &wildcard, false);
+ let fake_borrow_temps = self.lower_match_tree(
+ block,
+ pat.span,
+ pat.span,
+ false,
+ &mut [&mut guard_candidate, &mut otherwise_candidate],
+ );
+ let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None;
+ let expr_place: Place<'tcx>;
+ if let Ok(expr_builder) =
+ expr_place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
+ {
+ expr_place = expr_builder.into_place(self.tcx, self.typeck_results);
+ opt_expr_place = Some((Some(&expr_place), expr_span));
+ }
+ let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
+ self.break_for_else(otherwise_post_guard_block, else_target, self.source_info(expr_span));
+
+ self.declare_bindings(
+ source_scope,
+ pat.span.to(span),
+ pat,
+ ArmHasGuard(false),
+ opt_expr_place,
+ );
+
+ let post_guard_block = self.bind_pattern(
+ self.source_info(pat.span),
+ guard_candidate,
+ None,
+ &fake_borrow_temps,
+ expr.span,
+ None,
+ None,
+ None,
+ );
+
+ post_guard_block.unit()
+ }
+
+ /// Initializes each of the bindings from the candidate by
+ /// moving/copying/ref'ing the source as appropriate. Tests the guard, if
+ /// any, and then branches to the arm. Returns the block for the case where
+ /// the guard succeeds.
+ ///
+ /// Note: we do not check earlier that if there is a guard,
+ /// there cannot be move bindings. We avoid a use-after-move by only
+ /// moving the binding once the guard has evaluated to true (see below).
+ fn bind_and_guard_matched_candidate<'pat>(
+ &mut self,
+ candidate: Candidate<'pat, 'tcx>,
+ parent_bindings: &[(Vec<Binding<'tcx>>, Vec<Ascription<'tcx>>)],
+ guard: Option<&Guard<'tcx>>,
+ fake_borrows: &[(Place<'tcx>, Local)],
+ scrutinee_span: Span,
+ arm_span: Option<Span>,
+ match_scope: Option<region::Scope>,
+ schedule_drops: bool,
+ ) -> BasicBlock {
+ debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
+
+ debug_assert!(candidate.match_pairs.is_empty());
+
+ let candidate_source_info = self.source_info(candidate.span);
+
+ let mut block = candidate.pre_binding_block.unwrap();
+
+ if candidate.next_candidate_pre_binding_block.is_some() {
+ let fresh_block = self.cfg.start_new_block();
+ self.false_edges(
+ block,
+ fresh_block,
+ candidate.next_candidate_pre_binding_block,
+ candidate_source_info,
+ );
+ block = fresh_block;
+ }
+
+ self.ascribe_types(
+ block,
+ parent_bindings
+ .iter()
+ .flat_map(|(_, ascriptions)| ascriptions)
+ .cloned()
+ .chain(candidate.ascriptions),
+ );
+
+ // rust-lang/rust#27282: The `autoref` business deserves some
+ // explanation here.
+ //
+ // The intent of the `autoref` flag is that when it is true,
+ // then any pattern bindings of type T will map to a `&T`
+ // within the context of the guard expression, but will
+ // continue to map to a `T` in the context of the arm body. To
+ // avoid surfacing this distinction in the user source code
+ // (which would be a severe change to the language and require
+ // far more revision to the compiler), when `autoref` is true,
+ // then any occurrence of the identifier in the guard
+ // expression will automatically get a deref op applied to it.
+ //
+ // So an input like:
+ //
+ // ```
+ // let place = Foo::new();
+ // match place { foo if inspect(foo)
+ // => feed(foo), ... }
+ // ```
+ //
+ // will be treated as if it were really something like:
+ //
+ // ```
+ // let place = Foo::new();
+ // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) }
+ // => { let tmp2 = place; feed(tmp2) }, ... }
+ //
+ // And an input like:
+ //
+ // ```
+ // let place = Foo::new();
+ // match place { ref mut foo if inspect(foo)
+ // => feed(foo), ... }
+ // ```
+ //
+ // will be treated as if it were really something like:
+ //
+ // ```
+ // let place = Foo::new();
+ // match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) }
+ // => { let tmp2 = &mut place; feed(tmp2) }, ... }
+ // ```
+ //
+ // In short, any pattern binding will always look like *some*
+ // kind of `&T` within the guard at least in terms of how the
+ // MIR-borrowck views it, and this will ensure that guard
+ // expressions cannot mutate their the match inputs via such
+ // bindings. (It also ensures that guard expressions can at
+ // most *copy* values from such bindings; non-Copy things
+ // cannot be moved via pattern bindings in guard expressions.)
+ //
+ // ----
+ //
+ // Implementation notes (under assumption `autoref` is true).
+ //
+ // To encode the distinction above, we must inject the
+ // temporaries `tmp1` and `tmp2`.
+ //
+ // There are two cases of interest: binding by-value, and binding by-ref.
+ //
+ // 1. Binding by-value: Things are simple.
+ //
+ // * Establishing `tmp1` creates a reference into the
+ // matched place. This code is emitted by
+ // bind_matched_candidate_for_guard.
+ //
+ // * `tmp2` is only initialized "lazily", after we have
+ // checked the guard. Thus, the code that can trigger
+ // moves out of the candidate can only fire after the
+ // guard evaluated to true. This initialization code is
+ // emitted by bind_matched_candidate_for_arm.
+ //
+ // 2. Binding by-reference: Things are tricky.
+ //
+ // * Here, the guard expression wants a `&&` or `&&mut`
+ // into the original input. This means we need to borrow
+ // the reference that we create for the arm.
+ // * So we eagerly create the reference for the arm and then take a
+ // reference to that.
+ if let Some(guard) = guard {
+ let tcx = self.tcx;
+ let bindings = parent_bindings
+ .iter()
+ .flat_map(|(bindings, _)| bindings)
+ .chain(&candidate.bindings);
+
+ self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
+ let guard_frame = GuardFrame {
+ locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(),
+ };
+ debug!("entering guard building context: {:?}", guard_frame);
+ self.guard_context.push(guard_frame);
+
+ let re_erased = tcx.lifetimes.re_erased;
+ let scrutinee_source_info = self.source_info(scrutinee_span);
+ for &(place, temp) in fake_borrows {
+ let borrow = Rvalue::Ref(re_erased, BorrowKind::Shallow, place);
+ self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
+ }
+
+ let arm_span = arm_span.unwrap();
+ let match_scope = match_scope.unwrap();
+ let mut guard_span = rustc_span::DUMMY_SP;
+
+ let (post_guard_block, otherwise_post_guard_block) =
+ self.in_if_then_scope(match_scope, |this| match *guard {
+ Guard::If(e) => {
+ let e = &this.thir[e];
+ guard_span = e.span;
+ this.then_else_break(
+ block,
+ e,
+ None,
+ match_scope,
+ this.source_info(arm_span),
+ )
+ }
+ Guard::IfLet(ref pat, scrutinee) => {
+ let s = &this.thir[scrutinee];
+ guard_span = s.span;
+ this.lower_let_expr(block, s, pat, match_scope, None, arm_span)
+ }
+ });
+
+ let source_info = self.source_info(guard_span);
+ let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span));
+ let guard_frame = self.guard_context.pop().unwrap();
+ debug!("Exiting guard building context with locals: {:?}", guard_frame);
+
+ for &(_, temp) in fake_borrows {
+ let cause = FakeReadCause::ForMatchGuard;
+ self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp));
+ }
+
+ let otherwise_block = candidate.otherwise_block.unwrap_or_else(|| {
+ let unreachable = self.cfg.start_new_block();
+ self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
+ unreachable
+ });
+ self.false_edges(
+ otherwise_post_guard_block,
+ otherwise_block,
+ candidate.next_candidate_pre_binding_block,
+ source_info,
+ );
+
+ // We want to ensure that the matched candidates are bound
+ // after we have confirmed this candidate *and* any
+ // associated guard; Binding them on `block` is too soon,
+ // because that would be before we've checked the result
+ // from the guard.
+ //
+ // But binding them on the arm is *too late*, because
+ // then all of the candidates for a single arm would be
+ // bound in the same place, that would cause a case like:
+ //
+ // ```rust
+ // match (30, 2) {
+ // (mut x, 1) | (2, mut x) if { true } => { ... }
+ // ... // ^^^^^^^ (this is `arm_block`)
+ // }
+ // ```
+ //
+ // would yield an `arm_block` something like:
+ //
+ // ```
+ // StorageLive(_4); // _4 is `x`
+ // _4 = &mut (_1.0: i32); // this is handling `(mut x, 1)` case
+ // _4 = &mut (_1.1: i32); // this is handling `(2, mut x)` case
+ // ```
+ //
+ // and that is clearly not correct.
+ let by_value_bindings = parent_bindings
+ .iter()
+ .flat_map(|(bindings, _)| bindings)
+ .chain(&candidate.bindings)
+ .filter(|binding| matches!(binding.binding_mode, BindingMode::ByValue));
+ // Read all of the by reference bindings to ensure that the
+ // place they refer to can't be modified by the guard.
+ for binding in by_value_bindings.clone() {
+ let local_id = self.var_local_id(binding.var_id, RefWithinGuard);
+ let cause = FakeReadCause::ForGuardBinding;
+ self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
+ }
+ assert!(schedule_drops, "patterns with guards must schedule drops");
+ self.bind_matched_candidate_for_arm_body(post_guard_block, true, by_value_bindings);
+
+ post_guard_block
+ } else {
+ // (Here, it is not too early to bind the matched
+ // candidate on `block`, because there is no guard result
+ // that we have to inspect before we bind them.)
+ self.bind_matched_candidate_for_arm_body(
+ block,
+ schedule_drops,
+ parent_bindings
+ .iter()
+ .flat_map(|(bindings, _)| bindings)
+ .chain(&candidate.bindings),
+ );
+ block
+ }
+ }
+
+ /// Append `AscribeUserType` statements onto the end of `block`
+ /// for each ascription
+ fn ascribe_types(
+ &mut self,
+ block: BasicBlock,
+ ascriptions: impl IntoIterator<Item = Ascription<'tcx>>,
+ ) {
+ for ascription in ascriptions {
+ let source_info = self.source_info(ascription.annotation.span);
+
+ let base = self.canonical_user_type_annotations.push(ascription.annotation);
+ self.cfg.push(
+ block,
+ Statement {
+ source_info,
+ kind: StatementKind::AscribeUserType(
+ Box::new((
+ ascription.source,
+ UserTypeProjection { base, projs: Vec::new() },
+ )),
+ ascription.variance,
+ ),
+ },
+ );
+ }
+ }
+
+ fn bind_matched_candidate_for_guard<'b>(
+ &mut self,
+ block: BasicBlock,
+ schedule_drops: bool,
+ bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
+ ) where
+ 'tcx: 'b,
+ {
+ debug!("bind_matched_candidate_for_guard(block={:?})", block);
+
+ // Assign each of the bindings. Since we are binding for a
+ // guard expression, this will never trigger moves out of the
+ // candidate.
+ let re_erased = self.tcx.lifetimes.re_erased;
+ for binding in bindings {
+ debug!("bind_matched_candidate_for_guard(binding={:?})", binding);
+ let source_info = self.source_info(binding.span);
+
+ // For each pattern ident P of type T, `ref_for_guard` is
+ // a reference R: &T pointing to the location matched by
+ // the pattern, and every occurrence of P within a guard
+ // denotes *R.
+ let ref_for_guard = self.storage_live_binding(
+ block,
+ binding.var_id,
+ binding.span,
+ RefWithinGuard,
+ schedule_drops,
+ );
+ match binding.binding_mode {
+ BindingMode::ByValue => {
+ let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source);
+ self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
+ }
+ BindingMode::ByRef(borrow_kind) => {
+ let value_for_arm = self.storage_live_binding(
+ block,
+ binding.var_id,
+ binding.span,
+ OutsideGuard,
+ schedule_drops,
+ );
+
+ let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source);
+ self.cfg.push_assign(block, source_info, value_for_arm, rvalue);
+ let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm);
+ self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
+ }
+ }
+ }
+ }
+
+ fn bind_matched_candidate_for_arm_body<'b>(
+ &mut self,
+ block: BasicBlock,
+ schedule_drops: bool,
+ bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
+ ) where
+ 'tcx: 'b,
+ {
+ debug!("bind_matched_candidate_for_arm_body(block={:?})", block);
+
+ let re_erased = self.tcx.lifetimes.re_erased;
+ // Assign each of the bindings. This may trigger moves out of the candidate.
+ for binding in bindings {
+ let source_info = self.source_info(binding.span);
+ let local = self.storage_live_binding(
+ block,
+ binding.var_id,
+ binding.span,
+ OutsideGuard,
+ schedule_drops,
+ );
+ if schedule_drops {
+ self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
+ }
+ let rvalue = match binding.binding_mode {
+ BindingMode::ByValue => Rvalue::Use(self.consume_by_copy_or_move(binding.source)),
+ BindingMode::ByRef(borrow_kind) => {
+ Rvalue::Ref(re_erased, borrow_kind, binding.source)
+ }
+ };
+ self.cfg.push_assign(block, source_info, local, rvalue);
+ }
+ }
+
+ /// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where the bound
+ /// `var` has type `T` in the arm body) in a pattern maps to 2 locals. The
+ /// first local is a binding for occurrences of `var` in the guard, which
+ /// will have type `&T`. The second local is a binding for occurrences of
+ /// `var` in the arm body, which will have type `T`.
+ fn declare_binding(
+ &mut self,
+ source_info: SourceInfo,
+ visibility_scope: SourceScope,
+ mutability: Mutability,
+ name: Symbol,
+ mode: BindingMode,
+ var_id: LocalVarId,
+ var_ty: Ty<'tcx>,
+ user_ty: UserTypeProjections,
+ has_guard: ArmHasGuard,
+ opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
+ pat_span: Span,
+ ) {
+ debug!(
+ "declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
+ visibility_scope={:?}, source_info={:?})",
+ var_id, name, mode, var_ty, visibility_scope, source_info
+ );
+
+ let tcx = self.tcx;
+ let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
+ let binding_mode = match mode {
+ BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
+ BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
+ };
+ debug!("declare_binding: user_ty={:?}", user_ty);
+ let local = LocalDecl::<'tcx> {
+ mutability,
+ ty: var_ty,
+ user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) },
+ source_info,
+ internal: false,
+ is_block_tail: None,
+ local_info: Some(Box::new(LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
+ VarBindingForm {
+ binding_mode,
+ // hypothetically, `visit_primary_bindings` could try to unzip
+ // an outermost hir::Ty as we descend, matching up
+ // idents in pat; but complex w/ unclear UI payoff.
+ // Instead, just abandon providing diagnostic info.
+ opt_ty_info: None,
+ opt_match_place,
+ pat_span,
+ },
+ ))))),
+ };
+ let for_arm_body = self.local_decls.push(local);
+ self.var_debug_info.push(VarDebugInfo {
+ name,
+ source_info: debug_source_info,
+ value: VarDebugInfoContents::Place(for_arm_body.into()),
+ });
+ let locals = if has_guard.0 {
+ let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
+ // This variable isn't mutated but has a name, so has to be
+ // immutable to avoid the unused mut lint.
+ mutability: Mutability::Not,
+ ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty),
+ user_ty: None,
+ source_info,
+ internal: false,
+ is_block_tail: None,
+ local_info: Some(Box::new(LocalInfo::User(ClearCrossCrate::Set(
+ BindingForm::RefForGuard,
+ )))),
+ });
+ self.var_debug_info.push(VarDebugInfo {
+ name,
+ source_info: debug_source_info,
+ value: VarDebugInfoContents::Place(ref_for_guard.into()),
+ });
+ LocalsForNode::ForGuard { ref_for_guard, for_arm_body }
+ } else {
+ LocalsForNode::One(for_arm_body)
+ };
+ debug!("declare_binding: vars={:?}", locals);
+ self.var_indices.insert(var_id, locals);
+ }
+
+ pub(crate) fn ast_let_else(
+ &mut self,
+ mut block: BasicBlock,
+ init: &Expr<'tcx>,
+ initializer_span: Span,
+ else_block: &Block,
+ visibility_scope: Option<SourceScope>,
+ remainder_scope: region::Scope,
+ remainder_span: Span,
+ pattern: &Pat<'tcx>,
+ ) -> BlockAnd<()> {
+ let (matching, failure) = self.in_if_then_scope(remainder_scope, |this| {
+ let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span));
+ let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
+ let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
+ this.declare_bindings(
+ visibility_scope,
+ remainder_span,
+ pattern,
+ ArmHasGuard(false),
+ Some((None, initializer_span)),
+ );
+ let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
+ let fake_borrow_temps = this.lower_match_tree(
+ block,
+ initializer_span,
+ pattern.span,
+ false,
+ &mut [&mut candidate, &mut wildcard],
+ );
+ // This block is for the matching case
+ let matching = this.bind_pattern(
+ this.source_info(pattern.span),
+ candidate,
+ None,
+ &fake_borrow_temps,
+ initializer_span,
+ None,
+ None,
+ None,
+ );
+ // This block is for the failure case
+ let failure = this.bind_pattern(
+ this.source_info(else_block.span),
+ wildcard,
+ None,
+ &fake_borrow_temps,
+ initializer_span,
+ None,
+ None,
+ None,
+ );
+ this.break_for_else(failure, remainder_scope, this.source_info(initializer_span));
+ matching.unit()
+ });
+
+ // This place is not really used because this destination place
+ // should never be used to take values at the end of the failure
+ // block.
+ let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() };
+ let failure_block;
+ unpack!(
+ failure_block = self.ast_block(
+ dummy_place,
+ failure,
+ else_block,
+ self.source_info(else_block.span),
+ )
+ );
+ self.cfg.terminate(
+ failure_block,
+ self.source_info(else_block.span),
+ TerminatorKind::Unreachable,
+ );
+ matching.unit()
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs
new file mode 100644
index 000000000..c62989041
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs
@@ -0,0 +1,318 @@
+//! Simplifying Candidates
+//!
+//! *Simplifying* a match pair `place @ pattern` means breaking it down
+//! into bindings or other, simpler match pairs. For example:
+//!
+//! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]`
+//! - `place @ x` can be simplified to `[]` by binding `x` to `place`
+//!
+//! The `simplify_candidate` routine just repeatedly applies these
+//! sort of simplifications until there is nothing left to
+//! simplify. Match pairs cannot be simplified if they require some
+//! sort of test: for example, testing which variant an enum is, or
+//! testing a value against a constant.
+
+use crate::build::expr::as_place::PlaceBuilder;
+use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
+use crate::build::Builder;
+use rustc_hir::RangeEnd;
+use rustc_middle::thir::{self, *};
+use rustc_middle::ty;
+use rustc_middle::ty::layout::IntegerExt;
+use rustc_target::abi::{Integer, Size};
+
+use std::mem;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Simplify a candidate so that all match pairs require a test.
+ ///
+ /// This method will also split a candidate, in which the only
+ /// match-pair is an or-pattern, into multiple candidates.
+ /// This is so that
+ ///
+ /// match x {
+ /// 0 | 1 => { ... },
+ /// 2 | 3 => { ... },
+ /// }
+ ///
+ /// only generates a single switch. If this happens this method returns
+ /// `true`.
+ pub(super) fn simplify_candidate<'pat>(
+ &mut self,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) -> bool {
+ // repeatedly simplify match pairs until fixed point is reached
+ debug!(?candidate, "simplify_candidate");
+
+ // existing_bindings and new_bindings exists to keep the semantics in order.
+ // Reversing the binding order for bindings after `@` changes the binding order in places
+ // it shouldn't be changed, for example `let (Some(a), Some(b)) = (x, y)`
+ //
+ // To avoid this, the binding occurs in the following manner:
+ // * the bindings for one iteration of the following loop occurs in order (i.e. left to
+ // right)
+ // * the bindings from the previous iteration of the loop is prepended to the bindings from
+ // the current iteration (in the implementation this is done by mem::swap and extend)
+ // * after all iterations, these new bindings are then appended to the bindings that were
+ // preexisting (i.e. `candidate.binding` when the function was called).
+ //
+ // example:
+ // candidate.bindings = [1, 2, 3]
+ // binding in iter 1: [4, 5]
+ // binding in iter 2: [6, 7]
+ //
+ // final binding: [1, 2, 3, 6, 7, 4, 5]
+ let mut existing_bindings = mem::take(&mut candidate.bindings);
+ let mut new_bindings = Vec::new();
+ loop {
+ let match_pairs = mem::take(&mut candidate.match_pairs);
+
+ if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] =
+ &*match_pairs
+ {
+ existing_bindings.extend_from_slice(&new_bindings);
+ mem::swap(&mut candidate.bindings, &mut existing_bindings);
+ candidate.subcandidates =
+ self.create_or_subcandidates(candidate, place.clone(), pats);
+ return true;
+ }
+
+ let mut changed = false;
+ for match_pair in match_pairs {
+ match self.simplify_match_pair(match_pair, candidate) {
+ Ok(()) => {
+ changed = true;
+ }
+ Err(match_pair) => {
+ candidate.match_pairs.push(match_pair);
+ }
+ }
+ }
+ // Avoid issue #69971: the binding order should be right to left if there are more
+ // bindings after `@` to please the borrow checker
+ // Ex
+ // struct NonCopyStruct {
+ // copy_field: u32,
+ // }
+ //
+ // fn foo1(x: NonCopyStruct) {
+ // let y @ NonCopyStruct { copy_field: z } = x;
+ // // the above should turn into
+ // let z = x.copy_field;
+ // let y = x;
+ // }
+ candidate.bindings.extend_from_slice(&new_bindings);
+ mem::swap(&mut candidate.bindings, &mut new_bindings);
+ candidate.bindings.clear();
+
+ if !changed {
+ existing_bindings.extend_from_slice(&new_bindings);
+ mem::swap(&mut candidate.bindings, &mut existing_bindings);
+ // Move or-patterns to the end, because they can result in us
+ // creating additional candidates, so we want to test them as
+ // late as possible.
+ candidate
+ .match_pairs
+ .sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. }));
+ debug!(simplified = ?candidate, "simplify_candidate");
+ return false; // if we were not able to simplify any, done.
+ }
+ }
+ }
+
+ /// Given `candidate` that has a single or-pattern for its match-pairs,
+ /// creates a fresh candidate for each of its input subpatterns passed via
+ /// `pats`.
+ fn create_or_subcandidates<'pat>(
+ &mut self,
+ candidate: &Candidate<'pat, 'tcx>,
+ place: PlaceBuilder<'tcx>,
+ pats: &'pat [Pat<'tcx>],
+ ) -> Vec<Candidate<'pat, 'tcx>> {
+ pats.iter()
+ .map(|pat| {
+ let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard);
+ self.simplify_candidate(&mut candidate);
+ candidate
+ })
+ .collect()
+ }
+
+ /// Tries to simplify `match_pair`, returning `Ok(())` if
+ /// successful. If successful, new match pairs and bindings will
+ /// have been pushed into the candidate. If no simplification is
+ /// possible, `Err` is returned and no changes are made to
+ /// candidate.
+ fn simplify_match_pair<'pat>(
+ &mut self,
+ match_pair: MatchPair<'pat, 'tcx>,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) -> Result<(), MatchPair<'pat, 'tcx>> {
+ let tcx = self.tcx;
+ match *match_pair.pattern.kind {
+ PatKind::AscribeUserType {
+ ref subpattern,
+ ascription: thir::Ascription { ref annotation, variance },
+ } => {
+ // Apply the type ascription to the value at `match_pair.place`, which is the
+ if let Ok(place_resolved) =
+ match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
+ {
+ candidate.ascriptions.push(Ascription {
+ annotation: annotation.clone(),
+ source: place_resolved.into_place(self.tcx, self.typeck_results),
+ variance,
+ });
+ }
+
+ candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
+
+ Ok(())
+ }
+
+ PatKind::Wild => {
+ // nothing left to do
+ Ok(())
+ }
+
+ PatKind::Binding {
+ name: _,
+ mutability: _,
+ mode,
+ var,
+ ty: _,
+ ref subpattern,
+ is_primary: _,
+ } => {
+ if let Ok(place_resolved) =
+ match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
+ {
+ candidate.bindings.push(Binding {
+ span: match_pair.pattern.span,
+ source: place_resolved.into_place(self.tcx, self.typeck_results),
+ var_id: var,
+ binding_mode: mode,
+ });
+ }
+
+ if let Some(subpattern) = subpattern.as_ref() {
+ // this is the `x @ P` case; have to keep matching against `P` now
+ candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
+ }
+
+ Ok(())
+ }
+
+ PatKind::Constant { .. } => {
+ // FIXME normalize patterns when possible
+ Err(match_pair)
+ }
+
+ PatKind::Range(PatRange { lo, hi, end }) => {
+ let (range, bias) = match *lo.ty().kind() {
+ ty::Char => {
+ (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
+ }
+ ty::Int(ity) => {
+ let size = Integer::from_int_ty(&tcx, ity).size();
+ let max = size.truncate(u128::MAX);
+ let bias = 1u128 << (size.bits() - 1);
+ (Some((0, max, size)), bias)
+ }
+ ty::Uint(uty) => {
+ let size = Integer::from_uint_ty(&tcx, uty).size();
+ let max = size.truncate(u128::MAX);
+ (Some((0, max, size)), 0)
+ }
+ _ => (None, 0),
+ };
+ if let Some((min, max, sz)) = range {
+ // We want to compare ranges numerically, but the order of the bitwise
+ // representation of signed integers does not match their numeric order. Thus,
+ // to correct the ordering, we need to shift the range of signed integers to
+ // correct the comparison. This is achieved by XORing with a bias (see
+ // pattern/_match.rs for another pertinent example of this pattern).
+ //
+ // Also, for performance, it's important to only do the second `try_to_bits` if
+ // necessary.
+ let lo = lo.try_to_bits(sz).unwrap() ^ bias;
+ if lo <= min {
+ let hi = hi.try_to_bits(sz).unwrap() ^ bias;
+ if hi > max || hi == max && end == RangeEnd::Included {
+ // Irrefutable pattern match.
+ return Ok(());
+ }
+ }
+ }
+ Err(match_pair)
+ }
+
+ PatKind::Slice { ref prefix, ref slice, ref suffix } => {
+ if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
+ // irrefutable
+ self.prefix_slice_suffix(
+ &mut candidate.match_pairs,
+ &match_pair.place,
+ prefix,
+ slice.as_ref(),
+ suffix,
+ );
+ Ok(())
+ } else {
+ Err(match_pair)
+ }
+ }
+
+ PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
+ let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
+ i == variant_index || {
+ self.tcx.features().exhaustive_patterns
+ && !v
+ .uninhabited_from(
+ self.tcx,
+ substs,
+ adt_def.adt_kind(),
+ self.param_env,
+ )
+ .is_empty()
+ }
+ }) && (adt_def.did().is_local()
+ || !adt_def.is_variant_list_non_exhaustive());
+ if irrefutable {
+ let place_builder = match_pair.place.downcast(adt_def, variant_index);
+ candidate
+ .match_pairs
+ .extend(self.field_match_pairs(place_builder, subpatterns));
+ Ok(())
+ } else {
+ Err(match_pair)
+ }
+ }
+
+ PatKind::Array { ref prefix, ref slice, ref suffix } => {
+ self.prefix_slice_suffix(
+ &mut candidate.match_pairs,
+ &match_pair.place,
+ prefix,
+ slice.as_ref(),
+ suffix,
+ );
+ Ok(())
+ }
+
+ PatKind::Leaf { ref subpatterns } => {
+ // tuple struct, match subpats (if any)
+ candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
+ Ok(())
+ }
+
+ PatKind::Deref { ref subpattern } => {
+ let place_builder = match_pair.place.deref();
+ candidate.match_pairs.push(MatchPair::new(place_builder, subpattern));
+ Ok(())
+ }
+
+ PatKind::Or { .. } => Err(match_pair),
+ }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
new file mode 100644
index 000000000..598da80c5
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -0,0 +1,837 @@
+// Testing candidates
+//
+// After candidates have been simplified, the only match pairs that
+// remain are those that require some sort of test. The functions here
+// identify what tests are needed, perform the tests, and then filter
+// the candidates based on the result.
+
+use crate::build::expr::as_place::PlaceBuilder;
+use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
+use crate::build::Builder;
+use crate::thir::pattern::compare_const_vals;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_hir::{LangItem, RangeEnd};
+use rustc_index::bit_set::BitSet;
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+use rustc_middle::ty::subst::{GenericArg, Subst};
+use rustc_middle::ty::util::IntTypeExt;
+use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_span::def_id::DefId;
+use rustc_span::symbol::{sym, Symbol};
+use rustc_span::Span;
+use rustc_target::abi::VariantIdx;
+
+use std::cmp::Ordering;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Identifies what test is needed to decide if `match_pair` is applicable.
+ ///
+ /// It is a bug to call this with a not-fully-simplified pattern.
+ pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
+ match *match_pair.pattern.kind {
+ PatKind::Variant { adt_def, substs: _, variant_index: _, subpatterns: _ } => Test {
+ span: match_pair.pattern.span,
+ kind: TestKind::Switch {
+ adt_def,
+ variants: BitSet::new_empty(adt_def.variants().len()),
+ },
+ },
+
+ PatKind::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => {
+ // For integers, we use a `SwitchInt` match, which allows
+ // us to handle more cases.
+ Test {
+ span: match_pair.pattern.span,
+ kind: TestKind::SwitchInt {
+ switch_ty: match_pair.pattern.ty,
+
+ // these maps are empty to start; cases are
+ // added below in add_cases_to_switch
+ options: Default::default(),
+ },
+ }
+ }
+
+ PatKind::Constant { value } => Test {
+ span: match_pair.pattern.span,
+ kind: TestKind::Eq { value, ty: match_pair.pattern.ty },
+ },
+
+ PatKind::Range(range) => {
+ assert_eq!(range.lo.ty(), match_pair.pattern.ty);
+ assert_eq!(range.hi.ty(), match_pair.pattern.ty);
+ Test { span: match_pair.pattern.span, kind: TestKind::Range(range) }
+ }
+
+ PatKind::Slice { ref prefix, ref slice, ref suffix } => {
+ let len = prefix.len() + suffix.len();
+ let op = if slice.is_some() { BinOp::Ge } else { BinOp::Eq };
+ Test { span: match_pair.pattern.span, kind: TestKind::Len { len: len as u64, op } }
+ }
+
+ PatKind::Or { .. } => bug!("or-patterns should have already been handled"),
+
+ PatKind::AscribeUserType { .. }
+ | PatKind::Array { .. }
+ | PatKind::Wild
+ | PatKind::Binding { .. }
+ | PatKind::Leaf { .. }
+ | PatKind::Deref { .. } => self.error_simplifyable(match_pair),
+ }
+ }
+
+ pub(super) fn add_cases_to_switch<'pat>(
+ &mut self,
+ test_place: &PlaceBuilder<'tcx>,
+ candidate: &Candidate<'pat, 'tcx>,
+ switch_ty: Ty<'tcx>,
+ options: &mut FxIndexMap<ConstantKind<'tcx>, u128>,
+ ) -> bool {
+ let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place) else {
+ return false;
+ };
+
+ match *match_pair.pattern.kind {
+ PatKind::Constant { value } => {
+ options
+ .entry(value)
+ .or_insert_with(|| value.eval_bits(self.tcx, self.param_env, switch_ty));
+ true
+ }
+ PatKind::Variant { .. } => {
+ panic!("you should have called add_variants_to_switch instead!");
+ }
+ PatKind::Range(range) => {
+ // Check that none of the switch values are in the range.
+ self.values_not_contained_in_range(range, options).unwrap_or(false)
+ }
+ PatKind::Slice { .. }
+ | PatKind::Array { .. }
+ | PatKind::Wild
+ | PatKind::Or { .. }
+ | PatKind::Binding { .. }
+ | PatKind::AscribeUserType { .. }
+ | PatKind::Leaf { .. }
+ | PatKind::Deref { .. } => {
+ // don't know how to add these patterns to a switch
+ false
+ }
+ }
+ }
+
+ pub(super) fn add_variants_to_switch<'pat>(
+ &mut self,
+ test_place: &PlaceBuilder<'tcx>,
+ candidate: &Candidate<'pat, 'tcx>,
+ variants: &mut BitSet<VariantIdx>,
+ ) -> bool {
+ let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place) else {
+ return false;
+ };
+
+ match *match_pair.pattern.kind {
+ PatKind::Variant { adt_def: _, variant_index, .. } => {
+ // We have a pattern testing for variant `variant_index`
+ // set the corresponding index to true
+ variants.insert(variant_index);
+ true
+ }
+ _ => {
+ // don't know how to add these patterns to a switch
+ false
+ }
+ }
+ }
+
+ pub(super) fn perform_test(
+ &mut self,
+ match_start_span: Span,
+ scrutinee_span: Span,
+ block: BasicBlock,
+ place_builder: PlaceBuilder<'tcx>,
+ test: &Test<'tcx>,
+ make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
+ ) {
+ let place: Place<'tcx>;
+ if let Ok(test_place_builder) =
+ place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
+ {
+ place = test_place_builder.into_place(self.tcx, self.typeck_results);
+ } else {
+ return;
+ }
+ debug!(
+ "perform_test({:?}, {:?}: {:?}, {:?})",
+ block,
+ place,
+ place.ty(&self.local_decls, self.tcx),
+ test
+ );
+
+ let source_info = self.source_info(test.span);
+ match test.kind {
+ TestKind::Switch { adt_def, ref variants } => {
+ let target_blocks = make_target_blocks(self);
+ // Variants is a BitVec of indexes into adt_def.variants.
+ let num_enum_variants = adt_def.variants().len();
+ debug_assert_eq!(target_blocks.len(), num_enum_variants + 1);
+ let otherwise_block = *target_blocks.last().unwrap();
+ let tcx = self.tcx;
+ let switch_targets = SwitchTargets::new(
+ adt_def.discriminants(tcx).filter_map(|(idx, discr)| {
+ if variants.contains(idx) {
+ debug_assert_ne!(
+ target_blocks[idx.index()],
+ otherwise_block,
+ "no canididates for tested discriminant: {:?}",
+ discr,
+ );
+ Some((discr.val, target_blocks[idx.index()]))
+ } else {
+ debug_assert_eq!(
+ target_blocks[idx.index()],
+ otherwise_block,
+ "found canididates for untested discriminant: {:?}",
+ discr,
+ );
+ None
+ }
+ }),
+ otherwise_block,
+ );
+ debug!("num_enum_variants: {}, variants: {:?}", num_enum_variants, variants);
+ let discr_ty = adt_def.repr().discr_type().to_ty(tcx);
+ let discr = self.temp(discr_ty, test.span);
+ self.cfg.push_assign(
+ block,
+ self.source_info(scrutinee_span),
+ discr,
+ Rvalue::Discriminant(place),
+ );
+ self.cfg.terminate(
+ block,
+ self.source_info(match_start_span),
+ TerminatorKind::SwitchInt {
+ discr: Operand::Move(discr),
+ switch_ty: discr_ty,
+ targets: switch_targets,
+ },
+ );
+ }
+
+ TestKind::SwitchInt { switch_ty, ref options } => {
+ let target_blocks = make_target_blocks(self);
+ let terminator = if *switch_ty.kind() == ty::Bool {
+ assert!(!options.is_empty() && options.len() <= 2);
+ let [first_bb, second_bb] = *target_blocks else {
+ bug!("`TestKind::SwitchInt` on `bool` should have two targets")
+ };
+ let (true_bb, false_bb) = match options[0] {
+ 1 => (first_bb, second_bb),
+ 0 => (second_bb, first_bb),
+ v => span_bug!(test.span, "expected boolean value but got {:?}", v),
+ };
+ TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb)
+ } else {
+ // The switch may be inexhaustive so we have a catch all block
+ debug_assert_eq!(options.len() + 1, target_blocks.len());
+ let otherwise_block = *target_blocks.last().unwrap();
+ let switch_targets = SwitchTargets::new(
+ options.values().copied().zip(target_blocks),
+ otherwise_block,
+ );
+ TerminatorKind::SwitchInt {
+ discr: Operand::Copy(place),
+ switch_ty,
+ targets: switch_targets,
+ }
+ };
+ self.cfg.terminate(block, self.source_info(match_start_span), terminator);
+ }
+
+ TestKind::Eq { value, ty } => {
+ if !ty.is_scalar() {
+ // Use `PartialEq::eq` instead of `BinOp::Eq`
+ // (the binop can only handle primitives)
+ self.non_scalar_compare(
+ block,
+ make_target_blocks,
+ source_info,
+ value,
+ place,
+ ty,
+ );
+ } else if let [success, fail] = *make_target_blocks(self) {
+ assert_eq!(value.ty(), ty);
+ let expect = self.literal_operand(test.span, value);
+ let val = Operand::Copy(place);
+ self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
+ } else {
+ bug!("`TestKind::Eq` should have two target blocks");
+ }
+ }
+
+ TestKind::Range(PatRange { lo, hi, ref end }) => {
+ let lower_bound_success = self.cfg.start_new_block();
+ let target_blocks = make_target_blocks(self);
+
+ // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
+ let lo = self.literal_operand(test.span, lo);
+ let hi = self.literal_operand(test.span, hi);
+ let val = Operand::Copy(place);
+
+ let [success, fail] = *target_blocks else {
+ bug!("`TestKind::Range` should have two target blocks");
+ };
+ self.compare(
+ block,
+ lower_bound_success,
+ fail,
+ source_info,
+ BinOp::Le,
+ lo,
+ val.clone(),
+ );
+ let op = match *end {
+ RangeEnd::Included => BinOp::Le,
+ RangeEnd::Excluded => BinOp::Lt,
+ };
+ self.compare(lower_bound_success, success, fail, source_info, op, val, hi);
+ }
+
+ TestKind::Len { len, op } => {
+ let target_blocks = make_target_blocks(self);
+
+ let usize_ty = self.tcx.types.usize;
+ let actual = self.temp(usize_ty, test.span);
+
+ // actual = len(place)
+ self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place));
+
+ // expected = <N>
+ let expected = self.push_usize(block, source_info, len);
+
+ let [true_bb, false_bb] = *target_blocks else {
+ bug!("`TestKind::Len` should have two target blocks");
+ };
+ // result = actual == expected OR result = actual < expected
+ // branch based on result
+ self.compare(
+ block,
+ true_bb,
+ false_bb,
+ source_info,
+ op,
+ Operand::Move(actual),
+ Operand::Move(expected),
+ );
+ }
+ }
+ }
+
+ /// Compare using the provided built-in comparison operator
+ fn compare(
+ &mut self,
+ block: BasicBlock,
+ success_block: BasicBlock,
+ fail_block: BasicBlock,
+ source_info: SourceInfo,
+ op: BinOp,
+ left: Operand<'tcx>,
+ right: Operand<'tcx>,
+ ) {
+ let bool_ty = self.tcx.types.bool;
+ let result = self.temp(bool_ty, source_info.span);
+
+ // result = op(left, right)
+ self.cfg.push_assign(
+ block,
+ source_info,
+ result,
+ Rvalue::BinaryOp(op, Box::new((left, right))),
+ );
+
+ // branch based on result
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::if_(self.tcx, Operand::Move(result), success_block, fail_block),
+ );
+ }
+
+ /// Compare two `&T` values using `<T as std::compare::PartialEq>::eq`
+ fn non_scalar_compare(
+ &mut self,
+ block: BasicBlock,
+ make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
+ source_info: SourceInfo,
+ value: ConstantKind<'tcx>,
+ place: Place<'tcx>,
+ mut ty: Ty<'tcx>,
+ ) {
+ let mut expect = self.literal_operand(source_info.span, value);
+ let mut val = Operand::Copy(place);
+
+ // If we're using `b"..."` as a pattern, we need to insert an
+ // unsizing coercion, as the byte string has the type `&[u8; N]`.
+ //
+ // We want to do this even when the scrutinee is a reference to an
+ // array, so we can call `<[u8]>::eq` rather than having to find an
+ // `<[u8; N]>::eq`.
+ let unsize = |ty: Ty<'tcx>| match ty.kind() {
+ ty::Ref(region, rty, _) => match rty.kind() {
+ ty::Array(inner_ty, n) => Some((region, inner_ty, n)),
+ _ => None,
+ },
+ _ => None,
+ };
+ let opt_ref_ty = unsize(ty);
+ let opt_ref_test_ty = unsize(value.ty());
+ match (opt_ref_ty, opt_ref_test_ty) {
+ // nothing to do, neither is an array
+ (None, None) => {}
+ (Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => {
+ let tcx = self.tcx;
+ // make both a slice
+ ty = tcx.mk_imm_ref(*region, tcx.mk_slice(*elem_ty));
+ if opt_ref_ty.is_some() {
+ let temp = self.temp(ty, source_info.span);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ temp,
+ Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), val, ty),
+ );
+ val = Operand::Move(temp);
+ }
+ if opt_ref_test_ty.is_some() {
+ let slice = self.temp(ty, source_info.span);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ slice,
+ Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), expect, ty),
+ );
+ expect = Operand::Move(slice);
+ }
+ }
+ }
+
+ let ty::Ref(_, deref_ty, _) = *ty.kind() else {
+ bug!("non_scalar_compare called on non-reference type: {}", ty);
+ };
+
+ let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, None);
+ let method = trait_method(self.tcx, eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]);
+
+ let bool_ty = self.tcx.types.bool;
+ let eq_result = self.temp(bool_ty, source_info.span);
+ let eq_block = self.cfg.start_new_block();
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Call {
+ func: Operand::Constant(Box::new(Constant {
+ span: source_info.span,
+
+ // FIXME(#54571): This constant comes from user input (a
+ // constant in a pattern). Are there forms where users can add
+ // type annotations here? For example, an associated constant?
+ // Need to experiment.
+ user_ty: None,
+
+ literal: method,
+ })),
+ args: vec![val, expect],
+ destination: eq_result,
+ target: Some(eq_block),
+ cleanup: None,
+ from_hir_call: false,
+ fn_span: source_info.span,
+ },
+ );
+ self.diverge_from(block);
+
+ let [success_block, fail_block] = *make_target_blocks(self) else {
+ bug!("`TestKind::Eq` should have two target blocks")
+ };
+ // check the result
+ self.cfg.terminate(
+ eq_block,
+ source_info,
+ TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block),
+ );
+ }
+
+ /// Given that we are performing `test` against `test_place`, this job
+ /// sorts out what the status of `candidate` will be after the test. See
+ /// `test_candidates` for the usage of this function. The returned index is
+ /// the index that this candidate should be placed in the
+ /// `target_candidates` vec. The candidate may be modified to update its
+ /// `match_pairs`.
+ ///
+ /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is
+ /// a variant test, then we would modify the candidate to be `(x as
+ /// Option).0 @ P0` and return the index corresponding to the variant
+ /// `Some`.
+ ///
+ /// However, in some cases, the test may just not be relevant to candidate.
+ /// For example, suppose we are testing whether `foo.x == 22`, but in one
+ /// match arm we have `Foo { x: _, ... }`... in that case, the test for
+ /// what value `x` has has no particular relevance to this candidate. In
+ /// such cases, this function just returns None without doing anything.
+ /// This is used by the overall `match_candidates` algorithm to structure
+ /// the match as a whole. See `match_candidates` for more details.
+ ///
+ /// FIXME(#29623). In some cases, we have some tricky choices to make. for
+ /// example, if we are testing that `x == 22`, but the candidate is `x @
+ /// 13..55`, what should we do? In the event that the test is true, we know
+ /// that the candidate applies, but in the event of false, we don't know
+ /// that it *doesn't* apply. For now, we return false, indicate that the
+ /// test does not apply to this candidate, but it might be we can get
+ /// tighter match code if we do something a bit different.
+ pub(super) fn sort_candidate<'pat>(
+ &mut self,
+ test_place: &PlaceBuilder<'tcx>,
+ test: &Test<'tcx>,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) -> Option<usize> {
+ // Find the match_pair for this place (if any). At present,
+ // afaik, there can be at most one. (In the future, if we
+ // adopted a more general `@` operator, there might be more
+ // than one, but it'd be very unusual to have two sides that
+ // both require tests; you'd expect one side to be simplified
+ // away.)
+ let (match_pair_index, match_pair) =
+ candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?;
+
+ match (&test.kind, &*match_pair.pattern.kind) {
+ // If we are performing a variant switch, then this
+ // informs variant patterns, but nothing else.
+ (
+ &TestKind::Switch { adt_def: tested_adt_def, .. },
+ &PatKind::Variant { adt_def, variant_index, ref subpatterns, .. },
+ ) => {
+ assert_eq!(adt_def, tested_adt_def);
+ self.candidate_after_variant_switch(
+ match_pair_index,
+ adt_def,
+ variant_index,
+ subpatterns,
+ candidate,
+ );
+ Some(variant_index.as_usize())
+ }
+
+ (&TestKind::Switch { .. }, _) => None,
+
+ // If we are performing a switch over integers, then this informs integer
+ // equality, but nothing else.
+ //
+ // FIXME(#29623) we could use PatKind::Range to rule
+ // things out here, in some cases.
+ (
+ &TestKind::SwitchInt { switch_ty: _, ref options },
+ &PatKind::Constant { ref value },
+ ) if is_switch_ty(match_pair.pattern.ty) => {
+ let index = options.get_index_of(value).unwrap();
+ self.candidate_without_match_pair(match_pair_index, candidate);
+ Some(index)
+ }
+
+ (&TestKind::SwitchInt { switch_ty: _, ref options }, &PatKind::Range(range)) => {
+ let not_contained =
+ self.values_not_contained_in_range(range, options).unwrap_or(false);
+
+ if not_contained {
+ // No switch values are contained in the pattern range,
+ // so the pattern can be matched only if this test fails.
+ let otherwise = options.len();
+ Some(otherwise)
+ } else {
+ None
+ }
+ }
+
+ (&TestKind::SwitchInt { .. }, _) => None,
+
+ (
+ &TestKind::Len { len: test_len, op: BinOp::Eq },
+ &PatKind::Slice { ref prefix, ref slice, ref suffix },
+ ) => {
+ let pat_len = (prefix.len() + suffix.len()) as u64;
+ match (test_len.cmp(&pat_len), slice) {
+ (Ordering::Equal, &None) => {
+ // on true, min_len = len = $actual_length,
+ // on false, len != $actual_length
+ self.candidate_after_slice_test(
+ match_pair_index,
+ candidate,
+ prefix,
+ slice.as_ref(),
+ suffix,
+ );
+ Some(0)
+ }
+ (Ordering::Less, _) => {
+ // test_len < pat_len. If $actual_len = test_len,
+ // then $actual_len < pat_len and we don't have
+ // enough elements.
+ Some(1)
+ }
+ (Ordering::Equal | Ordering::Greater, &Some(_)) => {
+ // This can match both if $actual_len = test_len >= pat_len,
+ // and if $actual_len > test_len. We can't advance.
+ None
+ }
+ (Ordering::Greater, &None) => {
+ // test_len != pat_len, so if $actual_len = test_len, then
+ // $actual_len != pat_len.
+ Some(1)
+ }
+ }
+ }
+
+ (
+ &TestKind::Len { len: test_len, op: BinOp::Ge },
+ &PatKind::Slice { ref prefix, ref slice, ref suffix },
+ ) => {
+ // the test is `$actual_len >= test_len`
+ let pat_len = (prefix.len() + suffix.len()) as u64;
+ match (test_len.cmp(&pat_len), slice) {
+ (Ordering::Equal, &Some(_)) => {
+ // $actual_len >= test_len = pat_len,
+ // so we can match.
+ self.candidate_after_slice_test(
+ match_pair_index,
+ candidate,
+ prefix,
+ slice.as_ref(),
+ suffix,
+ );
+ Some(0)
+ }
+ (Ordering::Less, _) | (Ordering::Equal, &None) => {
+ // test_len <= pat_len. If $actual_len < test_len,
+ // then it is also < pat_len, so the test passing is
+ // necessary (but insufficient).
+ Some(0)
+ }
+ (Ordering::Greater, &None) => {
+ // test_len > pat_len. If $actual_len >= test_len > pat_len,
+ // then we know we won't have a match.
+ Some(1)
+ }
+ (Ordering::Greater, &Some(_)) => {
+ // test_len < pat_len, and is therefore less
+ // strict. This can still go both ways.
+ None
+ }
+ }
+ }
+
+ (&TestKind::Range(test), &PatKind::Range(pat)) => {
+ use std::cmp::Ordering::*;
+
+ if test == pat {
+ self.candidate_without_match_pair(match_pair_index, candidate);
+ return Some(0);
+ }
+
+ // For performance, it's important to only do the second
+ // `compare_const_vals` if necessary.
+ let no_overlap = if matches!(
+ (compare_const_vals(self.tcx, test.hi, pat.lo, self.param_env)?, test.end),
+ (Less, _) | (Equal, RangeEnd::Excluded) // test < pat
+ ) || matches!(
+ (compare_const_vals(self.tcx, test.lo, pat.hi, self.param_env)?, pat.end),
+ (Greater, _) | (Equal, RangeEnd::Excluded) // test > pat
+ ) {
+ Some(1)
+ } else {
+ None
+ };
+
+ // If the testing range does not overlap with pattern range,
+ // the pattern can be matched only if this test fails.
+ no_overlap
+ }
+
+ (&TestKind::Range(range), &PatKind::Constant { value }) => {
+ if let Some(false) = self.const_range_contains(range, value) {
+ // `value` is not contained in the testing range,
+ // so `value` can be matched only if this test fails.
+ Some(1)
+ } else {
+ None
+ }
+ }
+
+ (&TestKind::Range { .. }, _) => None,
+
+ (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => {
+ // The call to `self.test(&match_pair)` below is not actually used to generate any
+ // MIR. Instead, we just want to compare with `test` (the parameter of the method)
+ // to see if it is the same.
+ //
+ // However, at this point we can still encounter or-patterns that were extracted
+ // from previous calls to `sort_candidate`, so we need to manually address that
+ // case to avoid panicking in `self.test()`.
+ if let PatKind::Or { .. } = &*match_pair.pattern.kind {
+ return None;
+ }
+
+ // These are all binary tests.
+ //
+ // FIXME(#29623) we can be more clever here
+ let pattern_test = self.test(&match_pair);
+ if pattern_test.kind == test.kind {
+ self.candidate_without_match_pair(match_pair_index, candidate);
+ Some(0)
+ } else {
+ None
+ }
+ }
+ }
+ }
+
+ fn candidate_without_match_pair(
+ &mut self,
+ match_pair_index: usize,
+ candidate: &mut Candidate<'_, 'tcx>,
+ ) {
+ candidate.match_pairs.remove(match_pair_index);
+ }
+
+ fn candidate_after_slice_test<'pat>(
+ &mut self,
+ match_pair_index: usize,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ prefix: &'pat [Pat<'tcx>],
+ opt_slice: Option<&'pat Pat<'tcx>>,
+ suffix: &'pat [Pat<'tcx>],
+ ) {
+ let removed_place = candidate.match_pairs.remove(match_pair_index).place;
+ self.prefix_slice_suffix(
+ &mut candidate.match_pairs,
+ &removed_place,
+ prefix,
+ opt_slice,
+ suffix,
+ );
+ }
+
+ fn candidate_after_variant_switch<'pat>(
+ &mut self,
+ match_pair_index: usize,
+ adt_def: ty::AdtDef<'tcx>,
+ variant_index: VariantIdx,
+ subpatterns: &'pat [FieldPat<'tcx>],
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) {
+ let match_pair = candidate.match_pairs.remove(match_pair_index);
+
+ // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
+ // we want to create a set of derived match-patterns like
+ // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
+ let elem =
+ ProjectionElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index);
+ let downcast_place = match_pair.place.project(elem); // `(x as Variant)`
+ let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
+ // e.g., `(x as Variant).0`
+ let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty);
+ // e.g., `(x as Variant).0 @ P1`
+ MatchPair::new(place, &subpattern.pattern)
+ });
+
+ candidate.match_pairs.extend(consequent_match_pairs);
+ }
+
+ fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
+ span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern)
+ }
+
+ fn const_range_contains(
+ &self,
+ range: PatRange<'tcx>,
+ value: ConstantKind<'tcx>,
+ ) -> Option<bool> {
+ use std::cmp::Ordering::*;
+
+ // For performance, it's important to only do the second
+ // `compare_const_vals` if necessary.
+ Some(
+ matches!(compare_const_vals(self.tcx, range.lo, value, self.param_env)?, Less | Equal)
+ && matches!(
+ (compare_const_vals(self.tcx, value, range.hi, self.param_env)?, range.end),
+ (Less, _) | (Equal, RangeEnd::Included)
+ ),
+ )
+ }
+
+ fn values_not_contained_in_range(
+ &self,
+ range: PatRange<'tcx>,
+ options: &FxIndexMap<ConstantKind<'tcx>, u128>,
+ ) -> Option<bool> {
+ for &val in options.keys() {
+ if self.const_range_contains(range, val)? {
+ return Some(false);
+ }
+ }
+
+ Some(true)
+ }
+}
+
+impl Test<'_> {
+ pub(super) fn targets(&self) -> usize {
+ match self.kind {
+ TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } => 2,
+ TestKind::Switch { adt_def, .. } => {
+ // While the switch that we generate doesn't test for all
+ // variants, we have a target for each variant and the
+ // otherwise case, and we make sure that all of the cases not
+ // specified have the same block.
+ adt_def.variants().len() + 1
+ }
+ TestKind::SwitchInt { switch_ty, ref options, .. } => {
+ if switch_ty.is_bool() {
+ // `bool` is special cased in `perform_test` to always
+ // branch to two blocks.
+ 2
+ } else {
+ options.len() + 1
+ }
+ }
+ }
+ }
+}
+
+fn is_switch_ty(ty: Ty<'_>) -> bool {
+ ty.is_integral() || ty.is_char() || ty.is_bool()
+}
+
+fn trait_method<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_def_id: DefId,
+ method_name: Symbol,
+ self_ty: Ty<'tcx>,
+ params: &[GenericArg<'tcx>],
+) -> ConstantKind<'tcx> {
+ let substs = tcx.mk_substs_trait(self_ty, params);
+
+ // The unhygienic comparison here is acceptable because this is only
+ // used on known traits.
+ let item = tcx
+ .associated_items(trait_def_id)
+ .filter_by_name_unhygienic(method_name)
+ .find(|item| item.kind == ty::AssocKind::Fn)
+ .expect("trait method not found");
+
+ let method_ty = tcx.bound_type_of(item.def_id);
+ let method_ty = method_ty.subst(tcx, substs);
+
+ ConstantKind::zero_sized(method_ty)
+}
diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs
new file mode 100644
index 000000000..9a1e98d3b
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/matches/util.rs
@@ -0,0 +1,109 @@
+use crate::build::expr::as_place::PlaceBuilder;
+use crate::build::matches::MatchPair;
+use crate::build::Builder;
+use rustc_middle::mir::*;
+use rustc_middle::thir::*;
+use rustc_middle::ty;
+use smallvec::SmallVec;
+use std::convert::TryInto;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ pub(crate) fn field_match_pairs<'pat>(
+ &mut self,
+ place: PlaceBuilder<'tcx>,
+ subpatterns: &'pat [FieldPat<'tcx>],
+ ) -> Vec<MatchPair<'pat, 'tcx>> {
+ subpatterns
+ .iter()
+ .map(|fieldpat| {
+ let place = place.clone().field(fieldpat.field, fieldpat.pattern.ty);
+ MatchPair::new(place, &fieldpat.pattern)
+ })
+ .collect()
+ }
+
+ pub(crate) fn prefix_slice_suffix<'pat>(
+ &mut self,
+ match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
+ place: &PlaceBuilder<'tcx>,
+ prefix: &'pat [Pat<'tcx>],
+ opt_slice: Option<&'pat Pat<'tcx>>,
+ suffix: &'pat [Pat<'tcx>],
+ ) {
+ let tcx = self.tcx;
+ let (min_length, exact_size) = if let Ok(place_resolved) =
+ place.clone().try_upvars_resolved(tcx, self.typeck_results)
+ {
+ match place_resolved
+ .into_place(tcx, self.typeck_results)
+ .ty(&self.local_decls, tcx)
+ .ty
+ .kind()
+ {
+ ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true),
+ _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
+ }
+ } else {
+ ((prefix.len() + suffix.len()).try_into().unwrap(), false)
+ };
+
+ match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
+ let elem =
+ ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
+ let place = place.clone().project(elem);
+ MatchPair::new(place, subpattern)
+ }));
+
+ if let Some(subslice_pat) = opt_slice {
+ let suffix_len = suffix.len() as u64;
+ let subslice = place.clone().project(ProjectionElem::Subslice {
+ from: prefix.len() as u64,
+ to: if exact_size { min_length - suffix_len } else { suffix_len },
+ from_end: !exact_size,
+ });
+ match_pairs.push(MatchPair::new(subslice, subslice_pat));
+ }
+
+ match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
+ let end_offset = (idx + 1) as u64;
+ let elem = ProjectionElem::ConstantIndex {
+ offset: if exact_size { min_length - end_offset } else { end_offset },
+ min_length,
+ from_end: !exact_size,
+ };
+ let place = place.clone().project(elem);
+ MatchPair::new(place, subpattern)
+ }));
+ }
+
+ /// Creates a false edge to `imaginary_target` and a real edge to
+ /// real_target. If `imaginary_target` is none, or is the same as the real
+ /// target, a Goto is generated instead to simplify the generated MIR.
+ pub(crate) fn false_edges(
+ &mut self,
+ from_block: BasicBlock,
+ real_target: BasicBlock,
+ imaginary_target: Option<BasicBlock>,
+ source_info: SourceInfo,
+ ) {
+ match imaginary_target {
+ Some(target) if target != real_target => {
+ self.cfg.terminate(
+ from_block,
+ source_info,
+ TerminatorKind::FalseEdge { real_target, imaginary_target: target },
+ );
+ }
+ _ => self.cfg.goto(from_block, source_info, real_target),
+ }
+ }
+}
+
+impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
+ pub(crate) fn new(
+ place: PlaceBuilder<'tcx>,
+ pattern: &'pat Pat<'tcx>,
+ ) -> MatchPair<'pat, 'tcx> {
+ MatchPair { place, pattern }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs
new file mode 100644
index 000000000..86f466ff7
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/misc.rs
@@ -0,0 +1,75 @@
+//! Miscellaneous builder routines that are not specific to building any particular
+//! kind of thing.
+
+use crate::build::Builder;
+
+use rustc_middle::mir::*;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{Span, DUMMY_SP};
+use rustc_trait_selection::infer::InferCtxtExt;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Adds a new temporary value of type `ty` storing the result of
+ /// evaluating `expr`.
+ ///
+ /// N.B., **No cleanup is scheduled for this temporary.** You should
+ /// call `schedule_drop` once the temporary is initialized.
+ pub(crate) fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Place<'tcx> {
+ // Mark this local as internal to avoid temporaries with types not present in the
+ // user's code resulting in ICEs from the generator transform.
+ let temp = self.local_decls.push(LocalDecl::new(ty, span).internal());
+ let place = Place::from(temp);
+ debug!("temp: created temp {:?} with type {:?}", place, self.local_decls[temp].ty);
+ place
+ }
+
+ /// Convenience function for creating a literal operand, one
+ /// without any user type annotation.
+ pub(crate) fn literal_operand(
+ &mut self,
+ span: Span,
+ literal: ConstantKind<'tcx>,
+ ) -> Operand<'tcx> {
+ let constant = Box::new(Constant { span, user_ty: None, literal });
+ Operand::Constant(constant)
+ }
+
+ // Returns a zero literal operand for the appropriate type, works for
+ // bool, char and integers.
+ pub(crate) fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
+ let literal = ConstantKind::from_bits(self.tcx, 0, ty::ParamEnv::empty().and(ty));
+
+ self.literal_operand(span, literal)
+ }
+
+ pub(crate) fn push_usize(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ value: u64,
+ ) -> Place<'tcx> {
+ let usize_ty = self.tcx.types.usize;
+ let temp = self.temp(usize_ty, source_info.span);
+ self.cfg.push_assign_constant(
+ block,
+ source_info,
+ temp,
+ Constant {
+ span: source_info.span,
+ user_ty: None,
+ literal: ConstantKind::from_usize(self.tcx, value),
+ },
+ );
+ temp
+ }
+
+ pub(crate) fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> {
+ let tcx = self.tcx;
+ let ty = place.ty(&self.local_decls, tcx).ty;
+ if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, DUMMY_SP) {
+ Operand::Move(place)
+ } else {
+ Operand::Copy(place)
+ }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
new file mode 100644
index 000000000..12b8ceede
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -0,0 +1,1171 @@
+use crate::build;
+pub(crate) use crate::build::expr::as_constant::lit_to_mir_constant;
+use crate::build::expr::as_place::PlaceBuilder;
+use crate::build::scope::DropKind;
+use crate::thir::pattern::pat_from_hir;
+use rustc_apfloat::ieee::{Double, Single};
+use rustc_apfloat::Float;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{GeneratorKind, Node};
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
+use rustc_middle::middle::region;
+use rustc_middle::mir::interpret::ConstValue;
+use rustc_middle::mir::interpret::Scalar;
+use rustc_middle::mir::*;
+use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, LocalVarId, PatKind, Thir};
+use rustc_middle::ty::subst::Subst;
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable, TypeckResults};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+use rustc_span::Symbol;
+use rustc_target::spec::abi::Abi;
+
+use super::lints;
+
+pub(crate) fn mir_built<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def: ty::WithOptConstParam<LocalDefId>,
+) -> &'tcx rustc_data_structures::steal::Steal<Body<'tcx>> {
+ if let Some(def) = def.try_upgrade(tcx) {
+ return tcx.mir_built(def);
+ }
+
+ let mut body = mir_build(tcx, def);
+ if def.const_param_did.is_some() {
+ assert!(matches!(body.source.instance, ty::InstanceDef::Item(_)));
+ body.source = MirSource::from_instance(ty::InstanceDef::Item(def.to_global()));
+ }
+
+ tcx.alloc_steal_mir(body)
+}
+
+/// Construct the MIR for a given `DefId`.
+fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_> {
+ let id = tcx.hir().local_def_id_to_hir_id(def.did);
+ let body_owner_kind = tcx.hir().body_owner_kind(def.did);
+ let typeck_results = tcx.typeck_opt_const_arg(def);
+
+ // Ensure unsafeck and abstract const building is ran before we steal the THIR.
+ // We can't use `ensure()` for `thir_abstract_const` as it doesn't compute the query
+ // if inputs are green. This can cause ICEs when calling `thir_abstract_const` after
+ // THIR has been stolen if we haven't computed this query yet.
+ match def {
+ ty::WithOptConstParam { did, const_param_did: Some(const_param_did) } => {
+ tcx.ensure().thir_check_unsafety_for_const_arg((did, const_param_did));
+ drop(tcx.thir_abstract_const_of_const_arg((did, const_param_did)));
+ }
+ ty::WithOptConstParam { did, const_param_did: None } => {
+ tcx.ensure().thir_check_unsafety(did);
+ drop(tcx.thir_abstract_const(did));
+ }
+ }
+
+ // Figure out what primary body this item has.
+ let (body_id, return_ty_span, span_with_body) = match tcx.hir().get(id) {
+ Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Closure(hir::Closure { fn_decl, body, .. }),
+ ..
+ }) => (*body, fn_decl.output.span(), None),
+ Node::Item(hir::Item {
+ kind: hir::ItemKind::Fn(hir::FnSig { decl, .. }, _, body_id),
+ span,
+ ..
+ })
+ | Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Fn(hir::FnSig { decl, .. }, body_id),
+ span,
+ ..
+ })
+ | Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(hir::FnSig { decl, .. }, hir::TraitFn::Provided(body_id)),
+ span,
+ ..
+ }) => {
+ // Use the `Span` of the `Item/ImplItem/TraitItem` as the body span,
+ // since the def span of a function does not include the body
+ (*body_id, decl.output.span(), Some(*span))
+ }
+ Node::Item(hir::Item {
+ kind: hir::ItemKind::Static(ty, _, body_id) | hir::ItemKind::Const(ty, body_id),
+ ..
+ })
+ | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(ty, body_id), .. })
+ | Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Const(ty, Some(body_id)),
+ ..
+ }) => (*body_id, ty.span, None),
+ Node::AnonConst(hir::AnonConst { body, hir_id, .. }) => {
+ (*body, tcx.hir().span(*hir_id), None)
+ }
+
+ _ => span_bug!(tcx.hir().span(id), "can't build MIR for {:?}", def.did),
+ };
+
+ // If we don't have a specialized span for the body, just use the
+ // normal def span.
+ let span_with_body = span_with_body.unwrap_or_else(|| tcx.hir().span(id));
+
+ tcx.infer_ctxt().enter(|infcx| {
+ let body = if let Some(error_reported) = typeck_results.tainted_by_errors {
+ build::construct_error(&infcx, def, id, body_id, body_owner_kind, error_reported)
+ } else if body_owner_kind.is_fn_or_closure() {
+ // fetch the fully liberated fn signature (that is, all bound
+ // types/lifetimes replaced)
+ let fn_sig = typeck_results.liberated_fn_sigs()[id];
+ let fn_def_id = tcx.hir().local_def_id(id);
+
+ let safety = match fn_sig.unsafety {
+ hir::Unsafety::Normal => Safety::Safe,
+ hir::Unsafety::Unsafe => Safety::FnUnsafe,
+ };
+
+ let body = tcx.hir().body(body_id);
+ let (thir, expr) = tcx
+ .thir_body(def)
+ .unwrap_or_else(|_| (tcx.alloc_steal_thir(Thir::new()), ExprId::from_u32(0)));
+ // We ran all queries that depended on THIR at the beginning
+ // of `mir_build`, so now we can steal it
+ let thir = thir.steal();
+ let ty = tcx.type_of(fn_def_id);
+ let mut abi = fn_sig.abi;
+ let implicit_argument = match ty.kind() {
+ ty::Closure(..) => {
+ // HACK(eddyb) Avoid having RustCall on closures,
+ // as it adds unnecessary (and wrong) auto-tupling.
+ abi = Abi::Rust;
+ vec![ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None)]
+ }
+ ty::Generator(..) => {
+ let gen_ty = tcx.typeck_body(body_id).node_type(id);
+
+ // The resume argument may be missing, in that case we need to provide it here.
+ // It will always be `()` in this case.
+ if body.params.is_empty() {
+ vec![
+ ArgInfo(gen_ty, None, None, None),
+ ArgInfo(tcx.mk_unit(), None, None, None),
+ ]
+ } else {
+ vec![ArgInfo(gen_ty, None, None, None)]
+ }
+ }
+ _ => vec![],
+ };
+
+ let explicit_arguments = body.params.iter().enumerate().map(|(index, arg)| {
+ let owner_id = tcx.hir().body_owner(body_id);
+ let opt_ty_info;
+ let self_arg;
+ if let Some(ref fn_decl) = tcx.hir().fn_decl_by_hir_id(owner_id) {
+ opt_ty_info = fn_decl
+ .inputs
+ .get(index)
+ // Make sure that inferred closure args have no type span
+ .and_then(|ty| if arg.pat.span != ty.span { Some(ty.span) } else { None });
+ self_arg = if index == 0 && fn_decl.implicit_self.has_implicit_self() {
+ match fn_decl.implicit_self {
+ hir::ImplicitSelfKind::Imm => Some(ImplicitSelfKind::Imm),
+ hir::ImplicitSelfKind::Mut => Some(ImplicitSelfKind::Mut),
+ hir::ImplicitSelfKind::ImmRef => Some(ImplicitSelfKind::ImmRef),
+ hir::ImplicitSelfKind::MutRef => Some(ImplicitSelfKind::MutRef),
+ _ => None,
+ }
+ } else {
+ None
+ };
+ } else {
+ opt_ty_info = None;
+ self_arg = None;
+ }
+
+ // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
+ // (as it's created inside the body itself, not passed in from outside).
+ let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() {
+ let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(arg.span));
+
+ tcx.bound_type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()])
+ } else {
+ fn_sig.inputs()[index]
+ };
+
+ ArgInfo(ty, opt_ty_info, Some(&arg), self_arg)
+ });
+
+ let arguments = implicit_argument.into_iter().chain(explicit_arguments);
+
+ let (yield_ty, return_ty) = if body.generator_kind.is_some() {
+ let gen_ty = tcx.typeck_body(body_id).node_type(id);
+ let gen_sig = match gen_ty.kind() {
+ ty::Generator(_, gen_substs, ..) => gen_substs.as_generator().sig(),
+ _ => span_bug!(tcx.hir().span(id), "generator w/o generator type: {:?}", ty),
+ };
+ (Some(gen_sig.yield_ty), gen_sig.return_ty)
+ } else {
+ (None, fn_sig.output())
+ };
+
+ let mut mir = build::construct_fn(
+ &thir,
+ &infcx,
+ def,
+ id,
+ arguments,
+ safety,
+ abi,
+ return_ty,
+ return_ty_span,
+ body,
+ expr,
+ span_with_body,
+ );
+ if yield_ty.is_some() {
+ mir.generator.as_mut().unwrap().yield_ty = yield_ty;
+ }
+ mir
+ } else {
+ // Get the revealed type of this const. This is *not* the adjusted
+ // type of its body, which may be a subtype of this type. For
+ // example:
+ //
+ // fn foo(_: &()) {}
+ // static X: fn(&'static ()) = foo;
+ //
+ // The adjusted type of the body of X is `for<'a> fn(&'a ())` which
+ // is not the same as the type of X. We need the type of the return
+ // place to be the type of the constant because NLL typeck will
+ // equate them.
+
+ let return_ty = typeck_results.node_type(id);
+
+ let (thir, expr) = tcx
+ .thir_body(def)
+ .unwrap_or_else(|_| (tcx.alloc_steal_thir(Thir::new()), ExprId::from_u32(0)));
+ // We ran all queries that depended on THIR at the beginning
+ // of `mir_build`, so now we can steal it
+ let thir = thir.steal();
+
+ build::construct_const(&thir, &infcx, expr, def, id, return_ty, return_ty_span)
+ };
+
+ lints::check(tcx, &body);
+
+ // The borrow checker will replace all the regions here with its own
+ // inference variables. There's no point having non-erased regions here.
+ // The exception is `body.user_type_annotations`, which is used unmodified
+ // by borrow checking.
+ debug_assert!(
+ !(body.local_decls.has_free_regions()
+ || body.basic_blocks().has_free_regions()
+ || body.var_debug_info.has_free_regions()
+ || body.yield_ty().has_free_regions()),
+ "Unexpected free regions in MIR: {:?}",
+ body,
+ );
+
+ body
+ })
+}
+
+///////////////////////////////////////////////////////////////////////////
+// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
+
+fn liberated_closure_env_ty(
+ tcx: TyCtxt<'_>,
+ closure_expr_id: hir::HirId,
+ body_id: hir::BodyId,
+) -> Ty<'_> {
+ let closure_ty = tcx.typeck_body(body_id).node_type(closure_expr_id);
+
+ let ty::Closure(closure_def_id, closure_substs) = *closure_ty.kind() else {
+ bug!("closure expr does not have closure type: {:?}", closure_ty);
+ };
+
+ let bound_vars =
+ tcx.mk_bound_variable_kinds(std::iter::once(ty::BoundVariableKind::Region(ty::BrEnv)));
+ let br =
+ ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind: ty::BrEnv };
+ let env_region = ty::ReLateBound(ty::INNERMOST, br);
+ let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs, env_region).unwrap();
+ tcx.erase_late_bound_regions(ty::Binder::bind_with_vars(closure_env_ty, bound_vars))
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum BlockFrame {
+ /// Evaluation is currently within a statement.
+ ///
+ /// Examples include:
+ /// 1. `EXPR;`
+ /// 2. `let _ = EXPR;`
+ /// 3. `let x = EXPR;`
+ Statement {
+ /// If true, then statement discards result from evaluating
+ /// the expression (such as examples 1 and 2 above).
+ ignores_expr_result: bool,
+ },
+
+ /// Evaluation is currently within the tail expression of a block.
+ ///
+ /// Example: `{ STMT_1; STMT_2; EXPR }`
+ TailExpr {
+ /// If true, then the surrounding context of the block ignores
+ /// the result of evaluating the block's tail expression.
+ ///
+ /// Example: `let _ = { STMT_1; EXPR };`
+ tail_result_is_ignored: bool,
+
+ /// `Span` of the tail expression.
+ span: Span,
+ },
+
+ /// Generic mark meaning that the block occurred as a subexpression
+ /// where the result might be used.
+ ///
+ /// Examples: `foo(EXPR)`, `match EXPR { ... }`
+ SubExpr,
+}
+
+impl BlockFrame {
+ fn is_tail_expr(&self) -> bool {
+ match *self {
+ BlockFrame::TailExpr { .. } => true,
+
+ BlockFrame::Statement { .. } | BlockFrame::SubExpr => false,
+ }
+ }
+ fn is_statement(&self) -> bool {
+ match *self {
+ BlockFrame::Statement { .. } => true,
+
+ BlockFrame::TailExpr { .. } | BlockFrame::SubExpr => false,
+ }
+ }
+}
+
+#[derive(Debug)]
+struct BlockContext(Vec<BlockFrame>);
+
+struct Builder<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ typeck_results: &'tcx TypeckResults<'tcx>,
+ region_scope_tree: &'tcx region::ScopeTree,
+ param_env: ty::ParamEnv<'tcx>,
+
+ thir: &'a Thir<'tcx>,
+ cfg: CFG<'tcx>,
+
+ def_id: DefId,
+ hir_id: hir::HirId,
+ parent_module: DefId,
+ check_overflow: bool,
+ fn_span: Span,
+ arg_count: usize,
+ generator_kind: Option<GeneratorKind>,
+
+ /// The current set of scopes, updated as we traverse;
+ /// see the `scope` module for more details.
+ scopes: scope::Scopes<'tcx>,
+
+ /// The block-context: each time we build the code within an thir::Block,
+ /// we push a frame here tracking whether we are building a statement or
+ /// if we are pushing the tail expression of the block. This is used to
+ /// embed information in generated temps about whether they were created
+ /// for a block tail expression or not.
+ ///
+ /// It would be great if we could fold this into `self.scopes`
+ /// somehow, but right now I think that is very tightly tied to
+ /// the code generation in ways that we cannot (or should not)
+ /// start just throwing new entries onto that vector in order to
+ /// distinguish the context of EXPR1 from the context of EXPR2 in
+ /// `{ STMTS; EXPR1 } + EXPR2`.
+ block_context: BlockContext,
+
+ /// The current unsafe block in scope
+ in_scope_unsafe: Safety,
+
+ /// The vector of all scopes that we have created thus far;
+ /// we track this for debuginfo later.
+ source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
+ source_scope: SourceScope,
+
+ /// The guard-context: each time we build the guard expression for
+ /// a match arm, we push onto this stack, and then pop when we
+ /// finish building it.
+ guard_context: Vec<GuardFrame>,
+
+ /// Maps `HirId`s of variable bindings to the `Local`s created for them.
+ /// (A match binding can have two locals; the 2nd is for the arm's guard.)
+ var_indices: FxHashMap<LocalVarId, LocalsForNode>,
+ local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+ canonical_user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
+ upvar_mutbls: Vec<Mutability>,
+ unit_temp: Option<Place<'tcx>>,
+
+ var_debug_info: Vec<VarDebugInfo<'tcx>>,
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ fn is_bound_var_in_guard(&self, id: LocalVarId) -> bool {
+ self.guard_context.iter().any(|frame| frame.locals.iter().any(|local| local.id == id))
+ }
+
+ fn var_local_id(&self, id: LocalVarId, for_guard: ForGuard) -> Local {
+ self.var_indices[&id].local_id(for_guard)
+ }
+}
+
+impl BlockContext {
+ fn new() -> Self {
+ BlockContext(vec![])
+ }
+ fn push(&mut self, bf: BlockFrame) {
+ self.0.push(bf);
+ }
+ fn pop(&mut self) -> Option<BlockFrame> {
+ self.0.pop()
+ }
+
+ /// Traverses the frames on the `BlockContext`, searching for either
+ /// the first block-tail expression frame with no intervening
+ /// statement frame.
+ ///
+ /// Notably, this skips over `SubExpr` frames; this method is
+ /// meant to be used in the context of understanding the
+ /// relationship of a temp (created within some complicated
+ /// expression) with its containing expression, and whether the
+ /// value of that *containing expression* (not the temp!) is
+ /// ignored.
+ fn currently_in_block_tail(&self) -> Option<BlockTailInfo> {
+ for bf in self.0.iter().rev() {
+ match bf {
+ BlockFrame::SubExpr => continue,
+ BlockFrame::Statement { .. } => break,
+ &BlockFrame::TailExpr { tail_result_is_ignored, span } => {
+ return Some(BlockTailInfo { tail_result_is_ignored, span });
+ }
+ }
+ }
+
+ None
+ }
+
+ /// Looks at the topmost frame on the BlockContext and reports
+ /// whether its one that would discard a block tail result.
+ ///
+ /// Unlike `currently_within_ignored_tail_expression`, this does
+ /// *not* skip over `SubExpr` frames: here, we want to know
+ /// whether the block result itself is discarded.
+ fn currently_ignores_tail_results(&self) -> bool {
+ match self.0.last() {
+ // no context: conservatively assume result is read
+ None => false,
+
+ // sub-expression: block result feeds into some computation
+ Some(BlockFrame::SubExpr) => false,
+
+ // otherwise: use accumulated is_ignored state.
+ Some(
+ BlockFrame::TailExpr { tail_result_is_ignored: ignored, .. }
+ | BlockFrame::Statement { ignores_expr_result: ignored },
+ ) => *ignored,
+ }
+ }
+}
+
+#[derive(Debug)]
+enum LocalsForNode {
+ /// In the usual case, a `HirId` for an identifier maps to at most
+ /// one `Local` declaration.
+ One(Local),
+
+ /// The exceptional case is identifiers in a match arm's pattern
+ /// that are referenced in a guard of that match arm. For these,
+ /// we have `2` Locals.
+ ///
+ /// * `for_arm_body` is the Local used in the arm body (which is
+ /// just like the `One` case above),
+ ///
+ /// * `ref_for_guard` is the Local used in the arm's guard (which
+ /// is a reference to a temp that is an alias of
+ /// `for_arm_body`).
+ ForGuard { ref_for_guard: Local, for_arm_body: Local },
+}
+
+#[derive(Debug)]
+struct GuardFrameLocal {
+ id: LocalVarId,
+}
+
+impl GuardFrameLocal {
+ fn new(id: LocalVarId, _binding_mode: BindingMode) -> Self {
+ GuardFrameLocal { id }
+ }
+}
+
+#[derive(Debug)]
+struct GuardFrame {
+ /// These are the id's of names that are bound by patterns of the
+ /// arm of *this* guard.
+ ///
+ /// (Frames higher up the stack will have the id's bound in arms
+ /// further out, such as in a case like:
+ ///
+ /// match E1 {
+ /// P1(id1) if (... (match E2 { P2(id2) if ... => B2 })) => B1,
+ /// }
+ ///
+ /// here, when building for FIXME.
+ locals: Vec<GuardFrameLocal>,
+}
+
+/// `ForGuard` indicates whether we are talking about:
+/// 1. The variable for use outside of guard expressions, or
+/// 2. The temp that holds reference to (1.), which is actually what the
+/// guard expressions see.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum ForGuard {
+ RefWithinGuard,
+ OutsideGuard,
+}
+
+impl LocalsForNode {
+ fn local_id(&self, for_guard: ForGuard) -> Local {
+ match (self, for_guard) {
+ (&LocalsForNode::One(local_id), ForGuard::OutsideGuard)
+ | (
+ &LocalsForNode::ForGuard { ref_for_guard: local_id, .. },
+ ForGuard::RefWithinGuard,
+ )
+ | (&LocalsForNode::ForGuard { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) => {
+ local_id
+ }
+
+ (&LocalsForNode::One(_), ForGuard::RefWithinGuard) => {
+ bug!("anything with one local should never be within a guard.")
+ }
+ }
+ }
+}
+
+struct CFG<'tcx> {
+ basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+}
+
+rustc_index::newtype_index! {
+ struct ScopeId { .. }
+}
+
+#[derive(Debug)]
+enum NeedsTemporary {
+ /// Use this variant when whatever you are converting with `as_operand`
+ /// is the last thing you are converting. This means that if we introduced
+ /// an intermediate temporary, we'd only read it immediately after, so we can
+ /// also avoid it.
+ No,
+ /// For all cases where you aren't sure or that are too expensive to compute
+ /// for now. It is always safe to fall back to this.
+ Maybe,
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// The `BlockAnd` "monad" packages up the new basic block along with a
+/// produced value (sometimes just unit, of course). The `unpack!`
+/// macro (and methods below) makes working with `BlockAnd` much more
+/// convenient.
+
+#[must_use = "if you don't use one of these results, you're leaving a dangling edge"]
+struct BlockAnd<T>(BasicBlock, T);
+
+trait BlockAndExtension {
+ fn and<T>(self, v: T) -> BlockAnd<T>;
+ fn unit(self) -> BlockAnd<()>;
+}
+
+impl BlockAndExtension for BasicBlock {
+ fn and<T>(self, v: T) -> BlockAnd<T> {
+ BlockAnd(self, v)
+ }
+
+ fn unit(self) -> BlockAnd<()> {
+ BlockAnd(self, ())
+ }
+}
+
+/// Update a block pointer and return the value.
+/// Use it like `let x = unpack!(block = self.foo(block, foo))`.
+macro_rules! unpack {
+ ($x:ident = $c:expr) => {{
+ let BlockAnd(b, v) = $c;
+ $x = b;
+ v
+ }};
+
+ ($c:expr) => {{
+ let BlockAnd(b, ()) = $c;
+ b
+ }};
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// the main entry point for building MIR for a function
+
+struct ArgInfo<'tcx>(
+ Ty<'tcx>,
+ Option<Span>,
+ Option<&'tcx hir::Param<'tcx>>,
+ Option<ImplicitSelfKind>,
+);
+
+fn construct_fn<'tcx, A>(
+ thir: &Thir<'tcx>,
+ infcx: &InferCtxt<'_, 'tcx>,
+ fn_def: ty::WithOptConstParam<LocalDefId>,
+ fn_id: hir::HirId,
+ arguments: A,
+ safety: Safety,
+ abi: Abi,
+ return_ty: Ty<'tcx>,
+ return_ty_span: Span,
+ body: &'tcx hir::Body<'tcx>,
+ expr: ExprId,
+ span_with_body: Span,
+) -> Body<'tcx>
+where
+ A: Iterator<Item = ArgInfo<'tcx>>,
+{
+ let arguments: Vec<_> = arguments.collect();
+
+ let tcx = infcx.tcx;
+ let span = tcx.hir().span(fn_id);
+
+ let mut builder = Builder::new(
+ thir,
+ infcx,
+ fn_def,
+ fn_id,
+ span_with_body,
+ arguments.len(),
+ safety,
+ return_ty,
+ return_ty_span,
+ body.generator_kind,
+ );
+
+ let call_site_scope =
+ region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite };
+ let arg_scope =
+ region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::Arguments };
+ let source_info = builder.source_info(span);
+ let call_site_s = (call_site_scope, source_info);
+ unpack!(builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
+ let arg_scope_s = (arg_scope, source_info);
+ // Attribute epilogue to function's closing brace
+ let fn_end = span_with_body.shrink_to_hi();
+ let return_block =
+ unpack!(builder.in_breakable_scope(None, Place::return_place(), fn_end, |builder| {
+ Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
+ builder.args_and_body(
+ START_BLOCK,
+ fn_def.did,
+ &arguments,
+ arg_scope,
+ &thir[expr],
+ )
+ }))
+ }));
+ let source_info = builder.source_info(fn_end);
+ builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
+ builder.build_drop_trees();
+ return_block.unit()
+ }));
+
+ let spread_arg = if abi == Abi::RustCall {
+ // RustCall pseudo-ABI untuples the last argument.
+ Some(Local::new(arguments.len()))
+ } else {
+ None
+ };
+
+ let mut body = builder.finish();
+ body.spread_arg = spread_arg;
+ body
+}
+
+fn construct_const<'a, 'tcx>(
+ thir: &'a Thir<'tcx>,
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ expr: ExprId,
+ def: ty::WithOptConstParam<LocalDefId>,
+ hir_id: hir::HirId,
+ const_ty: Ty<'tcx>,
+ const_ty_span: Span,
+) -> Body<'tcx> {
+ let tcx = infcx.tcx;
+ let span = tcx.hir().span(hir_id);
+ let mut builder = Builder::new(
+ thir,
+ infcx,
+ def,
+ hir_id,
+ span,
+ 0,
+ Safety::Safe,
+ const_ty,
+ const_ty_span,
+ None,
+ );
+
+ let mut block = START_BLOCK;
+ unpack!(block = builder.expr_into_dest(Place::return_place(), block, &thir[expr]));
+
+ let source_info = builder.source_info(span);
+ builder.cfg.terminate(block, source_info, TerminatorKind::Return);
+
+ builder.build_drop_trees();
+
+ builder.finish()
+}
+
+/// Construct MIR for an item that has had errors in type checking.
+///
+/// This is required because we may still want to run MIR passes on an item
+/// with type errors, but normal MIR construction can't handle that in general.
+fn construct_error<'a, 'tcx>(
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ def: ty::WithOptConstParam<LocalDefId>,
+ hir_id: hir::HirId,
+ body_id: hir::BodyId,
+ body_owner_kind: hir::BodyOwnerKind,
+ err: ErrorGuaranteed,
+) -> Body<'tcx> {
+ let tcx = infcx.tcx;
+ let span = tcx.hir().span(hir_id);
+ let ty = tcx.ty_error();
+ let generator_kind = tcx.hir().body(body_id).generator_kind;
+ let num_params = match body_owner_kind {
+ hir::BodyOwnerKind::Fn => tcx.hir().fn_decl_by_hir_id(hir_id).unwrap().inputs.len(),
+ hir::BodyOwnerKind::Closure => {
+ if generator_kind.is_some() {
+ // Generators have an implicit `self` parameter *and* a possibly
+ // implicit resume parameter.
+ 2
+ } else {
+ // The implicit self parameter adds another local in MIR.
+ 1 + tcx.hir().fn_decl_by_hir_id(hir_id).unwrap().inputs.len()
+ }
+ }
+ hir::BodyOwnerKind::Const => 0,
+ hir::BodyOwnerKind::Static(_) => 0,
+ };
+ let mut cfg = CFG { basic_blocks: IndexVec::new() };
+ let mut source_scopes = IndexVec::new();
+ let mut local_decls = IndexVec::from_elem_n(LocalDecl::new(ty, span), 1);
+
+ cfg.start_new_block();
+ source_scopes.push(SourceScopeData {
+ span,
+ parent_scope: None,
+ inlined: None,
+ inlined_parent_scope: None,
+ local_data: ClearCrossCrate::Set(SourceScopeLocalData {
+ lint_root: hir_id,
+ safety: Safety::Safe,
+ }),
+ });
+ let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE };
+
+ // Some MIR passes will expect the number of parameters to match the
+ // function declaration.
+ for _ in 0..num_params {
+ local_decls.push(LocalDecl::with_source_info(ty, source_info));
+ }
+ cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
+
+ let mut body = Body::new(
+ MirSource::item(def.did.to_def_id()),
+ cfg.basic_blocks,
+ source_scopes,
+ local_decls,
+ IndexVec::new(),
+ num_params,
+ vec![],
+ span,
+ generator_kind,
+ Some(err),
+ );
+ body.generator.as_mut().map(|gen| gen.yield_ty = Some(ty));
+ body
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ fn new(
+ thir: &'a Thir<'tcx>,
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ def: ty::WithOptConstParam<LocalDefId>,
+ hir_id: hir::HirId,
+ span: Span,
+ arg_count: usize,
+ safety: Safety,
+ return_ty: Ty<'tcx>,
+ return_span: Span,
+ generator_kind: Option<GeneratorKind>,
+ ) -> Builder<'a, 'tcx> {
+ let tcx = infcx.tcx;
+ let attrs = tcx.hir().attrs(hir_id);
+ // Some functions always have overflow checks enabled,
+ // however, they may not get codegen'd, depending on
+ // the settings for the crate they are codegened in.
+ let mut check_overflow = tcx.sess.contains_name(attrs, sym::rustc_inherit_overflow_checks);
+ // Respect -C overflow-checks.
+ check_overflow |= tcx.sess.overflow_checks();
+ // Constants always need overflow checks.
+ check_overflow |= matches!(
+ tcx.hir().body_owner_kind(def.did),
+ hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_)
+ );
+
+ let lint_level = LintLevel::Explicit(hir_id);
+ let param_env = tcx.param_env(def.did);
+ let mut builder = Builder {
+ thir,
+ tcx,
+ infcx,
+ typeck_results: tcx.typeck_opt_const_arg(def),
+ region_scope_tree: tcx.region_scope_tree(def.did),
+ param_env,
+ def_id: def.did.to_def_id(),
+ hir_id,
+ parent_module: tcx.parent_module(hir_id).to_def_id(),
+ check_overflow,
+ cfg: CFG { basic_blocks: IndexVec::new() },
+ fn_span: span,
+ arg_count,
+ generator_kind,
+ scopes: scope::Scopes::new(),
+ block_context: BlockContext::new(),
+ source_scopes: IndexVec::new(),
+ source_scope: OUTERMOST_SOURCE_SCOPE,
+ guard_context: vec![],
+ in_scope_unsafe: safety,
+ local_decls: IndexVec::from_elem_n(LocalDecl::new(return_ty, return_span), 1),
+ canonical_user_type_annotations: IndexVec::new(),
+ upvar_mutbls: vec![],
+ var_indices: Default::default(),
+ unit_temp: None,
+ var_debug_info: vec![],
+ };
+
+ assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
+ assert_eq!(
+ builder.new_source_scope(span, lint_level, Some(safety)),
+ OUTERMOST_SOURCE_SCOPE
+ );
+ builder.source_scopes[OUTERMOST_SOURCE_SCOPE].parent_scope = None;
+
+ builder
+ }
+
+ fn finish(self) -> Body<'tcx> {
+ for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
+ if block.terminator.is_none() {
+ span_bug!(self.fn_span, "no terminator on block {:?}", index);
+ }
+ }
+
+ Body::new(
+ MirSource::item(self.def_id),
+ self.cfg.basic_blocks,
+ self.source_scopes,
+ self.local_decls,
+ self.canonical_user_type_annotations,
+ self.arg_count,
+ self.var_debug_info,
+ self.fn_span,
+ self.generator_kind,
+ self.typeck_results.tainted_by_errors,
+ )
+ }
+
+ fn args_and_body(
+ &mut self,
+ mut block: BasicBlock,
+ fn_def_id: LocalDefId,
+ arguments: &[ArgInfo<'tcx>],
+ argument_scope: region::Scope,
+ expr: &Expr<'tcx>,
+ ) -> BlockAnd<()> {
+ // Allocate locals for the function arguments
+ for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() {
+ let source_info =
+ SourceInfo::outermost(arg_opt.map_or(self.fn_span, |arg| arg.pat.span));
+ let arg_local = self.local_decls.push(LocalDecl::with_source_info(ty, source_info));
+
+ // If this is a simple binding pattern, give debuginfo a nice name.
+ if let Some(arg) = arg_opt && let Some(ident) = arg.pat.simple_ident() {
+ self.var_debug_info.push(VarDebugInfo {
+ name: ident.name,
+ source_info,
+ value: VarDebugInfoContents::Place(arg_local.into()),
+ });
+ }
+ }
+
+ let tcx = self.tcx;
+ let tcx_hir = tcx.hir();
+ let hir_typeck_results = self.typeck_results;
+
+ // In analyze_closure() in upvar.rs we gathered a list of upvars used by an
+ // indexed closure and we stored in a map called closure_min_captures in TypeckResults
+ // with the closure's DefId. Here, we run through that vec of UpvarIds for
+ // the given closure and use the necessary information to create upvar
+ // debuginfo and to fill `self.upvar_mutbls`.
+ if hir_typeck_results.closure_min_captures.get(&fn_def_id).is_some() {
+ let mut closure_env_projs = vec![];
+ let mut closure_ty = self.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
+ if let ty::Ref(_, ty, _) = closure_ty.kind() {
+ closure_env_projs.push(ProjectionElem::Deref);
+ closure_ty = *ty;
+ }
+ let upvar_substs = match closure_ty.kind() {
+ ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
+ ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
+ _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
+ };
+ let def_id = self.def_id.as_local().unwrap();
+ let capture_syms = tcx.symbols_for_closure_captures((def_id, fn_def_id));
+ let capture_tys = upvar_substs.upvar_tys();
+ let captures_with_tys = hir_typeck_results
+ .closure_min_captures_flattened(fn_def_id)
+ .zip(capture_tys.zip(capture_syms));
+
+ self.upvar_mutbls = captures_with_tys
+ .enumerate()
+ .map(|(i, (captured_place, (ty, sym)))| {
+ let capture = captured_place.info.capture_kind;
+ let var_id = match captured_place.place.base {
+ HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
+ _ => bug!("Expected an upvar"),
+ };
+
+ let mutability = captured_place.mutability;
+
+ let mut projs = closure_env_projs.clone();
+ projs.push(ProjectionElem::Field(Field::new(i), ty));
+ match capture {
+ ty::UpvarCapture::ByValue => {}
+ ty::UpvarCapture::ByRef(..) => {
+ projs.push(ProjectionElem::Deref);
+ }
+ };
+
+ self.var_debug_info.push(VarDebugInfo {
+ name: *sym,
+ source_info: SourceInfo::outermost(tcx_hir.span(var_id)),
+ value: VarDebugInfoContents::Place(Place {
+ local: ty::CAPTURE_STRUCT_LOCAL,
+ projection: tcx.intern_place_elems(&projs),
+ }),
+ });
+
+ mutability
+ })
+ .collect();
+ }
+
+ let mut scope = None;
+ // Bind the argument patterns
+ for (index, arg_info) in arguments.iter().enumerate() {
+ // Function arguments always get the first Local indices after the return place
+ let local = Local::new(index + 1);
+ let place = Place::from(local);
+ let &ArgInfo(_, opt_ty_info, arg_opt, ref self_binding) = arg_info;
+
+ // Make sure we drop (parts of) the argument even when not matched on.
+ self.schedule_drop(
+ arg_opt.as_ref().map_or(expr.span, |arg| arg.pat.span),
+ argument_scope,
+ local,
+ DropKind::Value,
+ );
+
+ let Some(arg) = arg_opt else {
+ continue;
+ };
+ let pat = match tcx.hir().get(arg.pat.hir_id) {
+ Node::Pat(pat) => pat,
+ node => bug!("pattern became {:?}", node),
+ };
+ let pattern = pat_from_hir(tcx, self.param_env, self.typeck_results, pat);
+ let original_source_scope = self.source_scope;
+ let span = pattern.span;
+ self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span);
+ match *pattern.kind {
+ // Don't introduce extra copies for simple bindings
+ PatKind::Binding {
+ mutability,
+ var,
+ mode: BindingMode::ByValue,
+ subpattern: None,
+ ..
+ } => {
+ self.local_decls[local].mutability = mutability;
+ self.local_decls[local].source_info.scope = self.source_scope;
+ self.local_decls[local].local_info = if let Some(kind) = self_binding {
+ Some(Box::new(LocalInfo::User(ClearCrossCrate::Set(
+ BindingForm::ImplicitSelf(*kind),
+ ))))
+ } else {
+ let binding_mode = ty::BindingMode::BindByValue(mutability);
+ Some(Box::new(LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
+ VarBindingForm {
+ binding_mode,
+ opt_ty_info,
+ opt_match_place: Some((Some(place), span)),
+ pat_span: span,
+ },
+ )))))
+ };
+ self.var_indices.insert(var, LocalsForNode::One(local));
+ }
+ _ => {
+ scope = self.declare_bindings(
+ scope,
+ expr.span,
+ &pattern,
+ matches::ArmHasGuard(false),
+ Some((Some(&place), span)),
+ );
+ let place_builder = PlaceBuilder::from(local);
+ unpack!(block = self.place_into_pattern(block, pattern, place_builder, false));
+ }
+ }
+ self.source_scope = original_source_scope;
+ }
+
+ // Enter the argument pattern bindings source scope, if it exists.
+ if let Some(source_scope) = scope {
+ self.source_scope = source_scope;
+ }
+
+ self.expr_into_dest(Place::return_place(), block, &expr)
+ }
+
+ fn set_correct_source_scope_for_arg(
+ &mut self,
+ arg_hir_id: hir::HirId,
+ original_source_scope: SourceScope,
+ pattern_span: Span,
+ ) {
+ let tcx = self.tcx;
+ let current_root = tcx.maybe_lint_level_root_bounded(arg_hir_id, self.hir_id);
+ let parent_root = tcx.maybe_lint_level_root_bounded(
+ self.source_scopes[original_source_scope]
+ .local_data
+ .as_ref()
+ .assert_crate_local()
+ .lint_root,
+ self.hir_id,
+ );
+ if current_root != parent_root {
+ self.source_scope =
+ self.new_source_scope(pattern_span, LintLevel::Explicit(current_root), None);
+ }
+ }
+
+ fn get_unit_temp(&mut self) -> Place<'tcx> {
+ match self.unit_temp {
+ Some(tmp) => tmp,
+ None => {
+ let ty = self.tcx.mk_unit();
+ let fn_span = self.fn_span;
+ let tmp = self.temp(ty, fn_span);
+ self.unit_temp = Some(tmp);
+ tmp
+ }
+ }
+ }
+}
+
+fn parse_float_into_constval<'tcx>(
+ num: Symbol,
+ float_ty: ty::FloatTy,
+ neg: bool,
+) -> Option<ConstValue<'tcx>> {
+ parse_float_into_scalar(num, float_ty, neg).map(ConstValue::Scalar)
+}
+
+pub(crate) fn parse_float_into_scalar(
+ num: Symbol,
+ float_ty: ty::FloatTy,
+ neg: bool,
+) -> Option<Scalar> {
+ let num = num.as_str();
+ match float_ty {
+ ty::FloatTy::F32 => {
+ let Ok(rust_f) = num.parse::<f32>() else { return None };
+ let mut f = num.parse::<Single>().unwrap_or_else(|e| {
+ panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
+ });
+
+ assert!(
+ u128::from(rust_f.to_bits()) == f.to_bits(),
+ "apfloat::ieee::Single gave different result for `{}`: \
+ {}({:#x}) vs Rust's {}({:#x})",
+ rust_f,
+ f,
+ f.to_bits(),
+ Single::from_bits(rust_f.to_bits().into()),
+ rust_f.to_bits()
+ );
+
+ if neg {
+ f = -f;
+ }
+
+ Some(Scalar::from_f32(f))
+ }
+ ty::FloatTy::F64 => {
+ let Ok(rust_f) = num.parse::<f64>() else { return None };
+ let mut f = num.parse::<Double>().unwrap_or_else(|e| {
+ panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e)
+ });
+
+ assert!(
+ u128::from(rust_f.to_bits()) == f.to_bits(),
+ "apfloat::ieee::Double gave different result for `{}`: \
+ {}({:#x}) vs Rust's {}({:#x})",
+ rust_f,
+ f,
+ f.to_bits(),
+ Double::from_bits(rust_f.to_bits().into()),
+ rust_f.to_bits()
+ );
+
+ if neg {
+ f = -f;
+ }
+
+ Some(Scalar::from_f64(f))
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Builder methods are broken up into modules, depending on what kind
+// of thing is being lowered. Note that they use the `unpack` macro
+// above extensively.
+
+mod block;
+mod cfg;
+mod expr;
+mod matches;
+mod misc;
+mod scope;
+
+pub(crate) use expr::category::Category as ExprCategory;
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
new file mode 100644
index 000000000..b2fd9f25b
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -0,0 +1,1395 @@
+/*!
+Managing the scope stack. The scopes are tied to lexical scopes, so as
+we descend the THIR, we push a scope on the stack, build its
+contents, and then pop it off. Every scope is named by a
+`region::Scope`.
+
+### SEME Regions
+
+When pushing a new [Scope], we record the current point in the graph (a
+basic block); this marks the entry to the scope. We then generate more
+stuff in the control-flow graph. Whenever the scope is exited, either
+via a `break` or `return` or just by fallthrough, that marks an exit
+from the scope. Each lexical scope thus corresponds to a single-entry,
+multiple-exit (SEME) region in the control-flow graph.
+
+For now, we record the `region::Scope` to each SEME region for later reference
+(see caveat in next paragraph). This is because destruction scopes are tied to
+them. This may change in the future so that MIR lowering determines its own
+destruction scopes.
+
+### Not so SEME Regions
+
+In the course of building matches, it sometimes happens that certain code
+(namely guards) gets executed multiple times. This means that the scope lexical
+scope may in fact correspond to multiple, disjoint SEME regions. So in fact our
+mapping is from one scope to a vector of SEME regions. Since the SEME regions
+are disjoint, the mapping is still one-to-one for the set of SEME regions that
+we're currently in.
+
+Also in matches, the scopes assigned to arms are not always even SEME regions!
+Each arm has a single region with one entry for each pattern. We manually
+manipulate the scheduled drops in this scope to avoid dropping things multiple
+times.
+
+### Drops
+
+The primary purpose for scopes is to insert drops: while building
+the contents, we also accumulate places that need to be dropped upon
+exit from each scope. This is done by calling `schedule_drop`. Once a
+drop is scheduled, whenever we branch out we will insert drops of all
+those places onto the outgoing edge. Note that we don't know the full
+set of scheduled drops up front, and so whenever we exit from the
+scope we only drop the values scheduled thus far. For example, consider
+the scope S corresponding to this loop:
+
+```
+# let cond = true;
+loop {
+ let x = ..;
+ if cond { break; }
+ let y = ..;
+}
+```
+
+When processing the `let x`, we will add one drop to the scope for
+`x`. The break will then insert a drop for `x`. When we process `let
+y`, we will add another drop (in fact, to a subscope, but let's ignore
+that for now); any later drops would also drop `y`.
+
+### Early exit
+
+There are numerous "normal" ways to early exit a scope: `break`,
+`continue`, `return` (panics are handled separately). Whenever an
+early exit occurs, the method `break_scope` is called. It is given the
+current point in execution where the early exit occurs, as well as the
+scope you want to branch to (note that all early exits from to some
+other enclosing scope). `break_scope` will record the set of drops currently
+scheduled in a [DropTree]. Later, before `in_breakable_scope` exits, the drops
+will be added to the CFG.
+
+Panics are handled in a similar fashion, except that the drops are added to the
+MIR once the rest of the function has finished being lowered. If a terminator
+can panic, call `diverge_from(block)` with the block containing the terminator
+`block`.
+
+### Breakable scopes
+
+In addition to the normal scope stack, we track a loop scope stack
+that contains only loops and breakable blocks. It tracks where a `break`,
+`continue` or `return` should go to.
+
+*/
+
+use std::mem;
+
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_index::vec::IndexVec;
+use rustc_middle::middle::region;
+use rustc_middle::mir::*;
+use rustc_middle::thir::{Expr, LintLevel};
+
+use rustc_span::{Span, DUMMY_SP};
+
+#[derive(Debug)]
+pub struct Scopes<'tcx> {
+ scopes: Vec<Scope>,
+
+ /// The current set of breakable scopes. See module comment for more details.
+ breakable_scopes: Vec<BreakableScope<'tcx>>,
+
+ /// The scope of the innermost if-then currently being lowered.
+ if_then_scope: Option<IfThenScope>,
+
+ /// Drops that need to be done on unwind paths. See the comment on
+ /// [DropTree] for more details.
+ unwind_drops: DropTree,
+
+ /// Drops that need to be done on paths to the `GeneratorDrop` terminator.
+ generator_drops: DropTree,
+}
+
+#[derive(Debug)]
+struct Scope {
+ /// The source scope this scope was created in.
+ source_scope: SourceScope,
+
+ /// the region span of this scope within source code.
+ region_scope: region::Scope,
+
+ /// set of places to drop when exiting this scope. This starts
+ /// out empty but grows as variables are declared during the
+ /// building process. This is a stack, so we always drop from the
+ /// end of the vector (top of the stack) first.
+ drops: Vec<DropData>,
+
+ moved_locals: Vec<Local>,
+
+ /// The drop index that will drop everything in and below this scope on an
+ /// unwind path.
+ cached_unwind_block: Option<DropIdx>,
+
+ /// The drop index that will drop everything in and below this scope on a
+ /// generator drop path.
+ cached_generator_drop_block: Option<DropIdx>,
+}
+
+#[derive(Clone, Copy, Debug)]
+struct DropData {
+ /// The `Span` where drop obligation was incurred (typically where place was
+ /// declared)
+ source_info: SourceInfo,
+
+ /// local to drop
+ local: Local,
+
+ /// Whether this is a value Drop or a StorageDead.
+ kind: DropKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub(crate) enum DropKind {
+ Value,
+ Storage,
+}
+
+#[derive(Debug)]
+struct BreakableScope<'tcx> {
+ /// Region scope of the loop
+ region_scope: region::Scope,
+ /// The destination of the loop/block expression itself (i.e., where to put
+ /// the result of a `break` or `return` expression)
+ break_destination: Place<'tcx>,
+ /// Drops that happen on the `break`/`return` path.
+ break_drops: DropTree,
+ /// Drops that happen on the `continue` path.
+ continue_drops: Option<DropTree>,
+}
+
+#[derive(Debug)]
+struct IfThenScope {
+ /// The if-then scope or arm scope
+ region_scope: region::Scope,
+ /// Drops that happen on the `else` path.
+ else_drops: DropTree,
+}
+
+/// The target of an expression that breaks out of a scope
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum BreakableTarget {
+ Continue(region::Scope),
+ Break(region::Scope),
+ Return,
+}
+
+rustc_index::newtype_index! {
+ struct DropIdx { .. }
+}
+
+const ROOT_NODE: DropIdx = DropIdx::from_u32(0);
+
+/// A tree of drops that we have deferred lowering. It's used for:
+///
+/// * Drops on unwind paths
+/// * Drops on generator drop paths (when a suspended generator is dropped)
+/// * Drops on return and loop exit paths
+/// * Drops on the else path in an `if let` chain
+///
+/// Once no more nodes could be added to the tree, we lower it to MIR in one go
+/// in `build_mir`.
+#[derive(Debug)]
+struct DropTree {
+ /// Drops in the tree.
+ drops: IndexVec<DropIdx, (DropData, DropIdx)>,
+ /// Map for finding the inverse of the `next_drop` relation:
+ ///
+ /// `previous_drops[(drops[i].1, drops[i].0.local, drops[i].0.kind)] == i`
+ previous_drops: FxHashMap<(DropIdx, Local, DropKind), DropIdx>,
+ /// Edges into the `DropTree` that need to be added once it's lowered.
+ entry_points: Vec<(DropIdx, BasicBlock)>,
+}
+
+impl Scope {
+ /// Whether there's anything to do for the cleanup path, that is,
+ /// when unwinding through this scope. This includes destructors,
+ /// but not StorageDead statements, which don't get emitted at all
+ /// for unwinding, for several reasons:
+ /// * clang doesn't emit llvm.lifetime.end for C++ unwinding
+ /// * LLVM's memory dependency analysis can't handle it atm
+ /// * polluting the cleanup MIR with StorageDead creates
+ /// landing pads even though there's no actual destructors
+ /// * freeing up stack space has no effect during unwinding
+ /// Note that for generators we do emit StorageDeads, for the
+ /// use of optimizations in the MIR generator transform.
+ fn needs_cleanup(&self) -> bool {
+ self.drops.iter().any(|drop| match drop.kind {
+ DropKind::Value => true,
+ DropKind::Storage => false,
+ })
+ }
+
+ fn invalidate_cache(&mut self) {
+ self.cached_unwind_block = None;
+ self.cached_generator_drop_block = None;
+ }
+}
+
+/// A trait that determined how [DropTree] creates its blocks and
+/// links to any entry nodes.
+trait DropTreeBuilder<'tcx> {
+ /// Create a new block for the tree. This should call either
+ /// `cfg.start_new_block()` or `cfg.start_new_cleanup_block()`.
+ fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock;
+
+ /// Links a block outside the drop tree, `from`, to the block `to` inside
+ /// the drop tree.
+ fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock);
+}
+
+impl DropTree {
+ fn new() -> Self {
+ // The root node of the tree doesn't represent a drop, but instead
+ // represents the block in the tree that should be jumped to once all
+ // of the required drops have been performed.
+ let fake_source_info = SourceInfo::outermost(DUMMY_SP);
+ let fake_data =
+ DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage };
+ let drop_idx = DropIdx::MAX;
+ let drops = IndexVec::from_elem_n((fake_data, drop_idx), 1);
+ Self { drops, entry_points: Vec::new(), previous_drops: FxHashMap::default() }
+ }
+
+ fn add_drop(&mut self, drop: DropData, next: DropIdx) -> DropIdx {
+ let drops = &mut self.drops;
+ *self
+ .previous_drops
+ .entry((next, drop.local, drop.kind))
+ .or_insert_with(|| drops.push((drop, next)))
+ }
+
+ fn add_entry(&mut self, from: BasicBlock, to: DropIdx) {
+ debug_assert!(to < self.drops.next_index());
+ self.entry_points.push((to, from));
+ }
+
+ /// Builds the MIR for a given drop tree.
+ ///
+ /// `blocks` should have the same length as `self.drops`, and may have its
+ /// first value set to some already existing block.
+ fn build_mir<'tcx, T: DropTreeBuilder<'tcx>>(
+ &mut self,
+ cfg: &mut CFG<'tcx>,
+ blocks: &mut IndexVec<DropIdx, Option<BasicBlock>>,
+ ) {
+ debug!("DropTree::build_mir(drops = {:#?})", self);
+ assert_eq!(blocks.len(), self.drops.len());
+
+ self.assign_blocks::<T>(cfg, blocks);
+ self.link_blocks(cfg, blocks)
+ }
+
+ /// Assign blocks for all of the drops in the drop tree that need them.
+ fn assign_blocks<'tcx, T: DropTreeBuilder<'tcx>>(
+ &mut self,
+ cfg: &mut CFG<'tcx>,
+ blocks: &mut IndexVec<DropIdx, Option<BasicBlock>>,
+ ) {
+ // StorageDead statements can share blocks with each other and also with
+ // a Drop terminator. We iterate through the drops to find which drops
+ // need their own block.
+ #[derive(Clone, Copy)]
+ enum Block {
+ // This drop is unreachable
+ None,
+ // This drop is only reachable through the `StorageDead` with the
+ // specified index.
+ Shares(DropIdx),
+ // This drop has more than one way of being reached, or it is
+ // branched to from outside the tree, or its predecessor is a
+ // `Value` drop.
+ Own,
+ }
+
+ let mut needs_block = IndexVec::from_elem(Block::None, &self.drops);
+ if blocks[ROOT_NODE].is_some() {
+ // In some cases (such as drops for `continue`) the root node
+ // already has a block. In this case, make sure that we don't
+ // override it.
+ needs_block[ROOT_NODE] = Block::Own;
+ }
+
+ // Sort so that we only need to check the last value.
+ let entry_points = &mut self.entry_points;
+ entry_points.sort();
+
+ for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() {
+ if entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) {
+ let block = *blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg));
+ needs_block[drop_idx] = Block::Own;
+ while entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) {
+ let entry_block = entry_points.pop().unwrap().1;
+ T::add_entry(cfg, entry_block, block);
+ }
+ }
+ match needs_block[drop_idx] {
+ Block::None => continue,
+ Block::Own => {
+ blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg));
+ }
+ Block::Shares(pred) => {
+ blocks[drop_idx] = blocks[pred];
+ }
+ }
+ if let DropKind::Value = drop_data.0.kind {
+ needs_block[drop_data.1] = Block::Own;
+ } else if drop_idx != ROOT_NODE {
+ match &mut needs_block[drop_data.1] {
+ pred @ Block::None => *pred = Block::Shares(drop_idx),
+ pred @ Block::Shares(_) => *pred = Block::Own,
+ Block::Own => (),
+ }
+ }
+ }
+
+ debug!("assign_blocks: blocks = {:#?}", blocks);
+ assert!(entry_points.is_empty());
+ }
+
+ fn link_blocks<'tcx>(
+ &self,
+ cfg: &mut CFG<'tcx>,
+ blocks: &IndexVec<DropIdx, Option<BasicBlock>>,
+ ) {
+ for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() {
+ let Some(block) = blocks[drop_idx] else { continue };
+ match drop_data.0.kind {
+ DropKind::Value => {
+ let terminator = TerminatorKind::Drop {
+ target: blocks[drop_data.1].unwrap(),
+ // The caller will handle this if needed.
+ unwind: None,
+ place: drop_data.0.local.into(),
+ };
+ cfg.terminate(block, drop_data.0.source_info, terminator);
+ }
+ // Root nodes don't correspond to a drop.
+ DropKind::Storage if drop_idx == ROOT_NODE => {}
+ DropKind::Storage => {
+ let stmt = Statement {
+ source_info: drop_data.0.source_info,
+ kind: StatementKind::StorageDead(drop_data.0.local),
+ };
+ cfg.push(block, stmt);
+ let target = blocks[drop_data.1].unwrap();
+ if target != block {
+ // Diagnostics don't use this `Span` but debuginfo
+ // might. Since we don't want breakpoints to be placed
+ // here, especially when this is on an unwind path, we
+ // use `DUMMY_SP`.
+ let source_info = SourceInfo { span: DUMMY_SP, ..drop_data.0.source_info };
+ let terminator = TerminatorKind::Goto { target };
+ cfg.terminate(block, source_info, terminator);
+ }
+ }
+ }
+ }
+ }
+}
+
+impl<'tcx> Scopes<'tcx> {
+ pub(crate) fn new() -> Self {
+ Self {
+ scopes: Vec::new(),
+ breakable_scopes: Vec::new(),
+ if_then_scope: None,
+ unwind_drops: DropTree::new(),
+ generator_drops: DropTree::new(),
+ }
+ }
+
+ fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) {
+ debug!("push_scope({:?})", region_scope);
+ self.scopes.push(Scope {
+ source_scope: vis_scope,
+ region_scope: region_scope.0,
+ drops: vec![],
+ moved_locals: vec![],
+ cached_unwind_block: None,
+ cached_generator_drop_block: None,
+ });
+ }
+
+ fn pop_scope(&mut self, region_scope: (region::Scope, SourceInfo)) -> Scope {
+ let scope = self.scopes.pop().unwrap();
+ assert_eq!(scope.region_scope, region_scope.0);
+ scope
+ }
+
+ fn scope_index(&self, region_scope: region::Scope, span: Span) -> usize {
+ self.scopes
+ .iter()
+ .rposition(|scope| scope.region_scope == region_scope)
+ .unwrap_or_else(|| span_bug!(span, "region_scope {:?} does not enclose", region_scope))
+ }
+
+ /// Returns the topmost active scope, which is known to be alive until
+ /// the next scope expression.
+ fn topmost(&self) -> region::Scope {
+ self.scopes.last().expect("topmost_scope: no scopes present").region_scope
+ }
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ // Adding and removing scopes
+ // ==========================
+ // Start a breakable scope, which tracks where `continue`, `break` and
+ // `return` should branch to.
+ pub(crate) fn in_breakable_scope<F>(
+ &mut self,
+ loop_block: Option<BasicBlock>,
+ break_destination: Place<'tcx>,
+ span: Span,
+ f: F,
+ ) -> BlockAnd<()>
+ where
+ F: FnOnce(&mut Builder<'a, 'tcx>) -> Option<BlockAnd<()>>,
+ {
+ let region_scope = self.scopes.topmost();
+ let scope = BreakableScope {
+ region_scope,
+ break_destination,
+ break_drops: DropTree::new(),
+ continue_drops: loop_block.map(|_| DropTree::new()),
+ };
+ self.scopes.breakable_scopes.push(scope);
+ let normal_exit_block = f(self);
+ let breakable_scope = self.scopes.breakable_scopes.pop().unwrap();
+ assert!(breakable_scope.region_scope == region_scope);
+ let break_block = self.build_exit_tree(breakable_scope.break_drops, None);
+ if let Some(drops) = breakable_scope.continue_drops {
+ self.build_exit_tree(drops, loop_block);
+ }
+ match (normal_exit_block, break_block) {
+ (Some(block), None) | (None, Some(block)) => block,
+ (None, None) => self.cfg.start_new_block().unit(),
+ (Some(normal_block), Some(exit_block)) => {
+ let target = self.cfg.start_new_block();
+ let source_info = self.source_info(span);
+ self.cfg.terminate(
+ unpack!(normal_block),
+ source_info,
+ TerminatorKind::Goto { target },
+ );
+ self.cfg.terminate(
+ unpack!(exit_block),
+ source_info,
+ TerminatorKind::Goto { target },
+ );
+ target.unit()
+ }
+ }
+ }
+
+ /// Start an if-then scope which tracks drop for `if` expressions and `if`
+ /// guards.
+ ///
+ /// For an if-let chain:
+ ///
+ /// if let Some(x) = a && let Some(y) = b && let Some(z) = c { ... }
+ ///
+ /// There are three possible ways the condition can be false and we may have
+ /// to drop `x`, `x` and `y`, or neither depending on which binding fails.
+ /// To handle this correctly we use a `DropTree` in a similar way to a
+ /// `loop` expression and 'break' out on all of the 'else' paths.
+ ///
+ /// Notes:
+ /// - We don't need to keep a stack of scopes in the `Builder` because the
+ /// 'else' paths will only leave the innermost scope.
+ /// - This is also used for match guards.
+ pub(crate) fn in_if_then_scope<F>(
+ &mut self,
+ region_scope: region::Scope,
+ f: F,
+ ) -> (BasicBlock, BasicBlock)
+ where
+ F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<()>,
+ {
+ let scope = IfThenScope { region_scope, else_drops: DropTree::new() };
+ let previous_scope = mem::replace(&mut self.scopes.if_then_scope, Some(scope));
+
+ let then_block = unpack!(f(self));
+
+ let if_then_scope = mem::replace(&mut self.scopes.if_then_scope, previous_scope).unwrap();
+ assert!(if_then_scope.region_scope == region_scope);
+
+ let else_block = self
+ .build_exit_tree(if_then_scope.else_drops, None)
+ .map_or_else(|| self.cfg.start_new_block(), |else_block_and| unpack!(else_block_and));
+
+ (then_block, else_block)
+ }
+
+ pub(crate) fn in_opt_scope<F, R>(
+ &mut self,
+ opt_scope: Option<(region::Scope, SourceInfo)>,
+ f: F,
+ ) -> BlockAnd<R>
+ where
+ F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
+ {
+ debug!("in_opt_scope(opt_scope={:?})", opt_scope);
+ if let Some(region_scope) = opt_scope {
+ self.push_scope(region_scope);
+ }
+ let mut block;
+ let rv = unpack!(block = f(self));
+ if let Some(region_scope) = opt_scope {
+ unpack!(block = self.pop_scope(region_scope, block));
+ }
+ debug!("in_scope: exiting opt_scope={:?} block={:?}", opt_scope, block);
+ block.and(rv)
+ }
+
+ /// Convenience wrapper that pushes a scope and then executes `f`
+ /// to build its contents, popping the scope afterwards.
+ pub(crate) fn in_scope<F, R>(
+ &mut self,
+ region_scope: (region::Scope, SourceInfo),
+ lint_level: LintLevel,
+ f: F,
+ ) -> BlockAnd<R>
+ where
+ F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
+ {
+ debug!("in_scope(region_scope={:?})", region_scope);
+ let source_scope = self.source_scope;
+ let tcx = self.tcx;
+ if let LintLevel::Explicit(current_hir_id) = lint_level {
+ // Use `maybe_lint_level_root_bounded` with `root_lint_level` as a bound
+ // to avoid adding Hir dependencies on our parents.
+ // We estimate the true lint roots here to avoid creating a lot of source scopes.
+
+ let parent_root = tcx.maybe_lint_level_root_bounded(
+ self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root,
+ self.hir_id,
+ );
+ let current_root = tcx.maybe_lint_level_root_bounded(current_hir_id, self.hir_id);
+
+ if parent_root != current_root {
+ self.source_scope = self.new_source_scope(
+ region_scope.1.span,
+ LintLevel::Explicit(current_root),
+ None,
+ );
+ }
+ }
+ self.push_scope(region_scope);
+ let mut block;
+ let rv = unpack!(block = f(self));
+ unpack!(block = self.pop_scope(region_scope, block));
+ self.source_scope = source_scope;
+ debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block);
+ block.and(rv)
+ }
+
+ /// Push a scope onto the stack. You can then build code in this
+ /// scope and call `pop_scope` afterwards. Note that these two
+ /// calls must be paired; using `in_scope` as a convenience
+ /// wrapper maybe preferable.
+ pub(crate) fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) {
+ self.scopes.push_scope(region_scope, self.source_scope);
+ }
+
+ /// Pops a scope, which should have region scope `region_scope`,
+ /// adding any drops onto the end of `block` that are needed.
+ /// This must match 1-to-1 with `push_scope`.
+ pub(crate) fn pop_scope(
+ &mut self,
+ region_scope: (region::Scope, SourceInfo),
+ mut block: BasicBlock,
+ ) -> BlockAnd<()> {
+ debug!("pop_scope({:?}, {:?})", region_scope, block);
+
+ block = self.leave_top_scope(block);
+
+ self.scopes.pop_scope(region_scope);
+
+ block.unit()
+ }
+
+ /// Sets up the drops for breaking from `block` to `target`.
+ pub(crate) fn break_scope(
+ &mut self,
+ mut block: BasicBlock,
+ value: Option<&Expr<'tcx>>,
+ target: BreakableTarget,
+ source_info: SourceInfo,
+ ) -> BlockAnd<()> {
+ let span = source_info.span;
+
+ let get_scope_index = |scope: region::Scope| {
+ // find the loop-scope by its `region::Scope`.
+ self.scopes
+ .breakable_scopes
+ .iter()
+ .rposition(|breakable_scope| breakable_scope.region_scope == scope)
+ .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
+ };
+ let (break_index, destination) = match target {
+ BreakableTarget::Return => {
+ let scope = &self.scopes.breakable_scopes[0];
+ if scope.break_destination != Place::return_place() {
+ span_bug!(span, "`return` in item with no return scope");
+ }
+ (0, Some(scope.break_destination))
+ }
+ BreakableTarget::Break(scope) => {
+ let break_index = get_scope_index(scope);
+ let scope = &self.scopes.breakable_scopes[break_index];
+ (break_index, Some(scope.break_destination))
+ }
+ BreakableTarget::Continue(scope) => {
+ let break_index = get_scope_index(scope);
+ (break_index, None)
+ }
+ };
+
+ if let Some(destination) = destination {
+ if let Some(value) = value {
+ debug!("stmt_expr Break val block_context.push(SubExpr)");
+ self.block_context.push(BlockFrame::SubExpr);
+ unpack!(block = self.expr_into_dest(destination, block, value));
+ self.block_context.pop();
+ } else {
+ self.cfg.push_assign_unit(block, source_info, destination, self.tcx)
+ }
+ } else {
+ assert!(value.is_none(), "`return` and `break` should have a destination");
+ if self.tcx.sess.instrument_coverage() {
+ // Unlike `break` and `return`, which push an `Assign` statement to MIR, from which
+ // a Coverage code region can be generated, `continue` needs no `Assign`; but
+ // without one, the `InstrumentCoverage` MIR pass cannot generate a code region for
+ // `continue`. Coverage will be missing unless we add a dummy `Assign` to MIR.
+ self.add_dummy_assignment(span, block, source_info);
+ }
+ }
+
+ let region_scope = self.scopes.breakable_scopes[break_index].region_scope;
+ let scope_index = self.scopes.scope_index(region_scope, span);
+ let drops = if destination.is_some() {
+ &mut self.scopes.breakable_scopes[break_index].break_drops
+ } else {
+ self.scopes.breakable_scopes[break_index].continue_drops.as_mut().unwrap()
+ };
+ let mut drop_idx = ROOT_NODE;
+ for scope in &self.scopes.scopes[scope_index + 1..] {
+ for drop in &scope.drops {
+ drop_idx = drops.add_drop(*drop, drop_idx);
+ }
+ }
+ drops.add_entry(block, drop_idx);
+
+ // `build_drop_trees` doesn't have access to our source_info, so we
+ // create a dummy terminator now. `TerminatorKind::Resume` is used
+ // because MIR type checking will panic if it hasn't been overwritten.
+ self.cfg.terminate(block, source_info, TerminatorKind::Resume);
+
+ self.cfg.start_new_block().unit()
+ }
+
+ pub(crate) fn break_for_else(
+ &mut self,
+ block: BasicBlock,
+ target: region::Scope,
+ source_info: SourceInfo,
+ ) {
+ let scope_index = self.scopes.scope_index(target, source_info.span);
+ let if_then_scope = self
+ .scopes
+ .if_then_scope
+ .as_mut()
+ .unwrap_or_else(|| span_bug!(source_info.span, "no if-then scope found"));
+
+ assert_eq!(if_then_scope.region_scope, target, "breaking to incorrect scope");
+
+ let mut drop_idx = ROOT_NODE;
+ let drops = &mut if_then_scope.else_drops;
+ for scope in &self.scopes.scopes[scope_index + 1..] {
+ for drop in &scope.drops {
+ drop_idx = drops.add_drop(*drop, drop_idx);
+ }
+ }
+ drops.add_entry(block, drop_idx);
+
+ // `build_drop_trees` doesn't have access to our source_info, so we
+ // create a dummy terminator now. `TerminatorKind::Resume` is used
+ // because MIR type checking will panic if it hasn't been overwritten.
+ self.cfg.terminate(block, source_info, TerminatorKind::Resume);
+ }
+
+ // Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue`
+ // statement.
+ fn add_dummy_assignment(&mut self, span: Span, block: BasicBlock, source_info: SourceInfo) {
+ let local_decl = LocalDecl::new(self.tcx.mk_unit(), span).internal();
+ let temp_place = Place::from(self.local_decls.push(local_decl));
+ self.cfg.push_assign_unit(block, source_info, temp_place, self.tcx);
+ }
+
+ fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock {
+ // If we are emitting a `drop` statement, we need to have the cached
+ // diverge cleanup pads ready in case that drop panics.
+ let needs_cleanup = self.scopes.scopes.last().map_or(false, |scope| scope.needs_cleanup());
+ let is_generator = self.generator_kind.is_some();
+ let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX };
+
+ let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes");
+ unpack!(build_scope_drops(
+ &mut self.cfg,
+ &mut self.scopes.unwind_drops,
+ scope,
+ block,
+ unwind_to,
+ is_generator && needs_cleanup,
+ self.arg_count,
+ ))
+ }
+
+ /// Creates a new source scope, nested in the current one.
+ pub(crate) fn new_source_scope(
+ &mut self,
+ span: Span,
+ lint_level: LintLevel,
+ safety: Option<Safety>,
+ ) -> SourceScope {
+ let parent = self.source_scope;
+ debug!(
+ "new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}",
+ span,
+ lint_level,
+ safety,
+ parent,
+ self.source_scopes.get(parent)
+ );
+ let scope_local_data = SourceScopeLocalData {
+ lint_root: if let LintLevel::Explicit(lint_root) = lint_level {
+ lint_root
+ } else {
+ self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root
+ },
+ safety: safety.unwrap_or_else(|| {
+ self.source_scopes[parent].local_data.as_ref().assert_crate_local().safety
+ }),
+ };
+ self.source_scopes.push(SourceScopeData {
+ span,
+ parent_scope: Some(parent),
+ inlined: None,
+ inlined_parent_scope: None,
+ local_data: ClearCrossCrate::Set(scope_local_data),
+ })
+ }
+
+ /// Given a span and the current source scope, make a SourceInfo.
+ pub(crate) fn source_info(&self, span: Span) -> SourceInfo {
+ SourceInfo { span, scope: self.source_scope }
+ }
+
+ // Finding scopes
+ // ==============
+ /// Returns the scope that we should use as the lifetime of an
+ /// operand. Basically, an operand must live until it is consumed.
+ /// This is similar to, but not quite the same as, the temporary
+ /// scope (which can be larger or smaller).
+ ///
+ /// Consider:
+ /// ```ignore (illustrative)
+ /// let x = foo(bar(X, Y));
+ /// ```
+ /// We wish to pop the storage for X and Y after `bar()` is
+ /// called, not after the whole `let` is completed.
+ ///
+ /// As another example, if the second argument diverges:
+ /// ```ignore (illustrative)
+ /// foo(Box::new(2), panic!())
+ /// ```
+ /// We would allocate the box but then free it on the unwinding
+ /// path; we would also emit a free on the 'success' path from
+ /// panic, but that will turn out to be removed as dead-code.
+ pub(crate) fn local_scope(&self) -> region::Scope {
+ self.scopes.topmost()
+ }
+
+ // Scheduling drops
+ // ================
+ pub(crate) fn schedule_drop_storage_and_value(
+ &mut self,
+ span: Span,
+ region_scope: region::Scope,
+ local: Local,
+ ) {
+ self.schedule_drop(span, region_scope, local, DropKind::Storage);
+ self.schedule_drop(span, region_scope, local, DropKind::Value);
+ }
+
+ /// Indicates that `place` should be dropped on exit from `region_scope`.
+ ///
+ /// When called with `DropKind::Storage`, `place` shouldn't be the return
+ /// place, or a function parameter.
+ pub(crate) fn schedule_drop(
+ &mut self,
+ span: Span,
+ region_scope: region::Scope,
+ local: Local,
+ drop_kind: DropKind,
+ ) {
+ let needs_drop = match drop_kind {
+ DropKind::Value => {
+ if !self.local_decls[local].ty.needs_drop(self.tcx, self.param_env) {
+ return;
+ }
+ true
+ }
+ DropKind::Storage => {
+ if local.index() <= self.arg_count {
+ span_bug!(
+ span,
+ "`schedule_drop` called with local {:?} and arg_count {}",
+ local,
+ self.arg_count,
+ )
+ }
+ false
+ }
+ };
+
+ // When building drops, we try to cache chains of drops to reduce the
+ // number of `DropTree::add_drop` calls. This, however, means that
+ // whenever we add a drop into a scope which already had some entries
+ // in the drop tree built (and thus, cached) for it, we must invalidate
+ // all caches which might branch into the scope which had a drop just
+ // added to it. This is necessary, because otherwise some other code
+ // might use the cache to branch into already built chain of drops,
+ // essentially ignoring the newly added drop.
+ //
+ // For example consider there’s two scopes with a drop in each. These
+ // are built and thus the caches are filled:
+ //
+ // +--------------------------------------------------------+
+ // | +---------------------------------+ |
+ // | | +--------+ +-------------+ | +---------------+ |
+ // | | | return | <-+ | drop(outer) | <-+ | drop(middle) | |
+ // | | +--------+ +-------------+ | +---------------+ |
+ // | +------------|outer_scope cache|--+ |
+ // +------------------------------|middle_scope cache|------+
+ //
+ // Now, a new, inner-most scope is added along with a new drop into
+ // both inner-most and outer-most scopes:
+ //
+ // +------------------------------------------------------------+
+ // | +----------------------------------+ |
+ // | | +--------+ +-------------+ | +---------------+ | +-------------+
+ // | | | return | <+ | drop(new) | <-+ | drop(middle) | <--+| drop(inner) |
+ // | | +--------+ | | drop(outer) | | +---------------+ | +-------------+
+ // | | +-+ +-------------+ | |
+ // | +---|invalid outer_scope cache|----+ |
+ // +----=----------------|invalid middle_scope cache|-----------+
+ //
+ // If, when adding `drop(new)` we do not invalidate the cached blocks for both
+ // outer_scope and middle_scope, then, when building drops for the inner (right-most)
+ // scope, the old, cached blocks, without `drop(new)` will get used, producing the
+ // wrong results.
+ //
+ // Note that this code iterates scopes from the inner-most to the outer-most,
+ // invalidating caches of each scope visited. This way bare minimum of the
+ // caches gets invalidated. i.e., if a new drop is added into the middle scope, the
+ // cache of outer scope stays intact.
+ //
+ // Since we only cache drops for the unwind path and the generator drop
+ // path, we only need to invalidate the cache for drops that happen on
+ // the unwind or generator drop paths. This means that for
+ // non-generators we don't need to invalidate caches for `DropKind::Storage`.
+ let invalidate_caches = needs_drop || self.generator_kind.is_some();
+ for scope in self.scopes.scopes.iter_mut().rev() {
+ if invalidate_caches {
+ scope.invalidate_cache();
+ }
+
+ if scope.region_scope == region_scope {
+ let region_scope_span = region_scope.span(self.tcx, &self.region_scope_tree);
+ // Attribute scope exit drops to scope's closing brace.
+ let scope_end = self.tcx.sess.source_map().end_point(region_scope_span);
+
+ scope.drops.push(DropData {
+ source_info: SourceInfo { span: scope_end, scope: scope.source_scope },
+ local,
+ kind: drop_kind,
+ });
+
+ return;
+ }
+ }
+
+ span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local);
+ }
+
+ /// Indicates that the "local operand" stored in `local` is
+ /// *moved* at some point during execution (see `local_scope` for
+ /// more information about what a "local operand" is -- in short,
+ /// it's an intermediate operand created as part of preparing some
+ /// MIR instruction). We use this information to suppress
+ /// redundant drops on the non-unwind paths. This results in less
+ /// MIR, but also avoids spurious borrow check errors
+ /// (c.f. #64391).
+ ///
+ /// Example: when compiling the call to `foo` here:
+ ///
+ /// ```ignore (illustrative)
+ /// foo(bar(), ...)
+ /// ```
+ ///
+ /// we would evaluate `bar()` to an operand `_X`. We would also
+ /// schedule `_X` to be dropped when the expression scope for
+ /// `foo(bar())` is exited. This is relevant, for example, if the
+ /// later arguments should unwind (it would ensure that `_X` gets
+ /// dropped). However, if no unwind occurs, then `_X` will be
+ /// unconditionally consumed by the `call`:
+ ///
+ /// ```ignore (illustrative)
+ /// bb {
+ /// ...
+ /// _R = CALL(foo, _X, ...)
+ /// }
+ /// ```
+ ///
+ /// However, `_X` is still registered to be dropped, and so if we
+ /// do nothing else, we would generate a `DROP(_X)` that occurs
+ /// after the call. This will later be optimized out by the
+ /// drop-elaboration code, but in the meantime it can lead to
+ /// spurious borrow-check errors -- the problem, ironically, is
+ /// not the `DROP(_X)` itself, but the (spurious) unwind pathways
+ /// that it creates. See #64391 for an example.
+ pub(crate) fn record_operands_moved(&mut self, operands: &[Operand<'tcx>]) {
+ let local_scope = self.local_scope();
+ let scope = self.scopes.scopes.last_mut().unwrap();
+
+ assert_eq!(scope.region_scope, local_scope, "local scope is not the topmost scope!",);
+
+ // look for moves of a local variable, like `MOVE(_X)`
+ let locals_moved = operands.iter().flat_map(|operand| match operand {
+ Operand::Copy(_) | Operand::Constant(_) => None,
+ Operand::Move(place) => place.as_local(),
+ });
+
+ for local in locals_moved {
+ // check if we have a Drop for this operand and -- if so
+ // -- add it to the list of moved operands. Note that this
+ // local might not have been an operand created for this
+ // call, it could come from other places too.
+ if scope.drops.iter().any(|drop| drop.local == local && drop.kind == DropKind::Value) {
+ scope.moved_locals.push(local);
+ }
+ }
+ }
+
+ // Other
+ // =====
+ /// Returns the [DropIdx] for the innermost drop if the function unwound at
+ /// this point. The `DropIdx` will be created if it doesn't already exist.
+ fn diverge_cleanup(&mut self) -> DropIdx {
+ let is_generator = self.generator_kind.is_some();
+ let (uncached_scope, mut cached_drop) = self
+ .scopes
+ .scopes
+ .iter()
+ .enumerate()
+ .rev()
+ .find_map(|(scope_idx, scope)| {
+ scope.cached_unwind_block.map(|cached_block| (scope_idx + 1, cached_block))
+ })
+ .unwrap_or((0, ROOT_NODE));
+
+ for scope in &mut self.scopes.scopes[uncached_scope..] {
+ for drop in &scope.drops {
+ if is_generator || drop.kind == DropKind::Value {
+ cached_drop = self.scopes.unwind_drops.add_drop(*drop, cached_drop);
+ }
+ }
+ scope.cached_unwind_block = Some(cached_drop);
+ }
+
+ cached_drop
+ }
+
+ /// Prepares to create a path that performs all required cleanup for a
+ /// terminator that can unwind at the given basic block.
+ ///
+ /// This path terminates in Resume. The path isn't created until after all
+ /// of the non-unwind paths in this item have been lowered.
+ pub(crate) fn diverge_from(&mut self, start: BasicBlock) {
+ debug_assert!(
+ matches!(
+ self.cfg.block_data(start).terminator().kind,
+ TerminatorKind::Assert { .. }
+ | TerminatorKind::Call { .. }
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::InlineAsm { .. }
+ ),
+ "diverge_from called on block with terminator that cannot unwind."
+ );
+
+ let next_drop = self.diverge_cleanup();
+ self.scopes.unwind_drops.add_entry(start, next_drop);
+ }
+
+ /// Sets up a path that performs all required cleanup for dropping a
+ /// generator, starting from the given block that ends in
+ /// [TerminatorKind::Yield].
+ ///
+ /// This path terminates in GeneratorDrop.
+ pub(crate) fn generator_drop_cleanup(&mut self, yield_block: BasicBlock) {
+ debug_assert!(
+ matches!(
+ self.cfg.block_data(yield_block).terminator().kind,
+ TerminatorKind::Yield { .. }
+ ),
+ "generator_drop_cleanup called on block with non-yield terminator."
+ );
+ let (uncached_scope, mut cached_drop) = self
+ .scopes
+ .scopes
+ .iter()
+ .enumerate()
+ .rev()
+ .find_map(|(scope_idx, scope)| {
+ scope.cached_generator_drop_block.map(|cached_block| (scope_idx + 1, cached_block))
+ })
+ .unwrap_or((0, ROOT_NODE));
+
+ for scope in &mut self.scopes.scopes[uncached_scope..] {
+ for drop in &scope.drops {
+ cached_drop = self.scopes.generator_drops.add_drop(*drop, cached_drop);
+ }
+ scope.cached_generator_drop_block = Some(cached_drop);
+ }
+
+ self.scopes.generator_drops.add_entry(yield_block, cached_drop);
+ }
+
+ /// Utility function for *non*-scope code to build their own drops
+ pub(crate) fn build_drop_and_replace(
+ &mut self,
+ block: BasicBlock,
+ span: Span,
+ place: Place<'tcx>,
+ value: Operand<'tcx>,
+ ) -> BlockAnd<()> {
+ let source_info = self.source_info(span);
+ let next_target = self.cfg.start_new_block();
+
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::DropAndReplace { place, value, target: next_target, unwind: None },
+ );
+ self.diverge_from(block);
+
+ next_target.unit()
+ }
+
+ /// Creates an `Assert` terminator and return the success block.
+ /// If the boolean condition operand is not the expected value,
+ /// a runtime panic will be caused with the given message.
+ pub(crate) fn assert(
+ &mut self,
+ block: BasicBlock,
+ cond: Operand<'tcx>,
+ expected: bool,
+ msg: AssertMessage<'tcx>,
+ span: Span,
+ ) -> BasicBlock {
+ let source_info = self.source_info(span);
+ let success_block = self.cfg.start_new_block();
+
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Assert { cond, expected, msg, target: success_block, cleanup: None },
+ );
+ self.diverge_from(block);
+
+ success_block
+ }
+
+ /// Unschedules any drops in the top scope.
+ ///
+ /// This is only needed for `match` arm scopes, because they have one
+ /// entrance per pattern, but only one exit.
+ pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) {
+ let top_scope = self.scopes.scopes.last_mut().unwrap();
+
+ assert_eq!(top_scope.region_scope, region_scope);
+
+ top_scope.drops.clear();
+ top_scope.invalidate_cache();
+ }
+}
+
+/// Builds drops for `pop_scope` and `leave_top_scope`.
+fn build_scope_drops<'tcx>(
+ cfg: &mut CFG<'tcx>,
+ unwind_drops: &mut DropTree,
+ scope: &Scope,
+ mut block: BasicBlock,
+ mut unwind_to: DropIdx,
+ storage_dead_on_unwind: bool,
+ arg_count: usize,
+) -> BlockAnd<()> {
+ debug!("build_scope_drops({:?} -> {:?})", block, scope);
+
+ // Build up the drops in evaluation order. The end result will
+ // look like:
+ //
+ // [SDs, drops[n]] --..> [SDs, drop[1]] -> [SDs, drop[0]] -> [[SDs]]
+ // | | |
+ // : | |
+ // V V
+ // [drop[n]] -...-> [drop[1]] ------> [drop[0]] ------> [last_unwind_to]
+ //
+ // The horizontal arrows represent the execution path when the drops return
+ // successfully. The downwards arrows represent the execution path when the
+ // drops panic (panicking while unwinding will abort, so there's no need for
+ // another set of arrows).
+ //
+ // For generators, we unwind from a drop on a local to its StorageDead
+ // statement. For other functions we don't worry about StorageDead. The
+ // drops for the unwind path should have already been generated by
+ // `diverge_cleanup_gen`.
+
+ for drop_data in scope.drops.iter().rev() {
+ let source_info = drop_data.source_info;
+ let local = drop_data.local;
+
+ match drop_data.kind {
+ DropKind::Value => {
+ // `unwind_to` should drop the value that we're about to
+ // schedule. If dropping this value panics, then we continue
+ // with the *next* value on the unwind path.
+ debug_assert_eq!(unwind_drops.drops[unwind_to].0.local, drop_data.local);
+ debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind);
+ unwind_to = unwind_drops.drops[unwind_to].1;
+
+ // If the operand has been moved, and we are not on an unwind
+ // path, then don't generate the drop. (We only take this into
+ // account for non-unwind paths so as not to disturb the
+ // caching mechanism.)
+ if scope.moved_locals.iter().any(|&o| o == local) {
+ continue;
+ }
+
+ unwind_drops.add_entry(block, unwind_to);
+
+ let next = cfg.start_new_block();
+ cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Drop { place: local.into(), target: next, unwind: None },
+ );
+ block = next;
+ }
+ DropKind::Storage => {
+ if storage_dead_on_unwind {
+ debug_assert_eq!(unwind_drops.drops[unwind_to].0.local, drop_data.local);
+ debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind);
+ unwind_to = unwind_drops.drops[unwind_to].1;
+ }
+ // Only temps and vars need their storage dead.
+ assert!(local.index() > arg_count);
+ cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) });
+ }
+ }
+ }
+ block.unit()
+}
+
+impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
+ /// Build a drop tree for a breakable scope.
+ ///
+ /// If `continue_block` is `Some`, then the tree is for `continue` inside a
+ /// loop. Otherwise this is for `break` or `return`.
+ fn build_exit_tree(
+ &mut self,
+ mut drops: DropTree,
+ continue_block: Option<BasicBlock>,
+ ) -> Option<BlockAnd<()>> {
+ let mut blocks = IndexVec::from_elem(None, &drops.drops);
+ blocks[ROOT_NODE] = continue_block;
+
+ drops.build_mir::<ExitScopes>(&mut self.cfg, &mut blocks);
+
+ // Link the exit drop tree to unwind drop tree.
+ if drops.drops.iter().any(|(drop, _)| drop.kind == DropKind::Value) {
+ let unwind_target = self.diverge_cleanup();
+ let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1);
+ for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) {
+ match drop_data.0.kind {
+ DropKind::Storage => {
+ if self.generator_kind.is_some() {
+ let unwind_drop = self
+ .scopes
+ .unwind_drops
+ .add_drop(drop_data.0, unwind_indices[drop_data.1]);
+ unwind_indices.push(unwind_drop);
+ } else {
+ unwind_indices.push(unwind_indices[drop_data.1]);
+ }
+ }
+ DropKind::Value => {
+ let unwind_drop = self
+ .scopes
+ .unwind_drops
+ .add_drop(drop_data.0, unwind_indices[drop_data.1]);
+ self.scopes
+ .unwind_drops
+ .add_entry(blocks[drop_idx].unwrap(), unwind_indices[drop_data.1]);
+ unwind_indices.push(unwind_drop);
+ }
+ }
+ }
+ }
+ blocks[ROOT_NODE].map(BasicBlock::unit)
+ }
+
+ /// Build the unwind and generator drop trees.
+ pub(crate) fn build_drop_trees(&mut self) {
+ if self.generator_kind.is_some() {
+ self.build_generator_drop_trees();
+ } else {
+ Self::build_unwind_tree(
+ &mut self.cfg,
+ &mut self.scopes.unwind_drops,
+ self.fn_span,
+ &mut None,
+ );
+ }
+ }
+
+ fn build_generator_drop_trees(&mut self) {
+ // Build the drop tree for dropping the generator while it's suspended.
+ let drops = &mut self.scopes.generator_drops;
+ let cfg = &mut self.cfg;
+ let fn_span = self.fn_span;
+ let mut blocks = IndexVec::from_elem(None, &drops.drops);
+ drops.build_mir::<GeneratorDrop>(cfg, &mut blocks);
+ if let Some(root_block) = blocks[ROOT_NODE] {
+ cfg.terminate(
+ root_block,
+ SourceInfo::outermost(fn_span),
+ TerminatorKind::GeneratorDrop,
+ );
+ }
+
+ // Build the drop tree for unwinding in the normal control flow paths.
+ let resume_block = &mut None;
+ let unwind_drops = &mut self.scopes.unwind_drops;
+ Self::build_unwind_tree(cfg, unwind_drops, fn_span, resume_block);
+
+ // Build the drop tree for unwinding when dropping a suspended
+ // generator.
+ //
+ // This is a different tree to the standard unwind paths here to
+ // prevent drop elaboration from creating drop flags that would have
+ // to be captured by the generator. I'm not sure how important this
+ // optimization is, but it is here.
+ for (drop_idx, drop_data) in drops.drops.iter_enumerated() {
+ if let DropKind::Value = drop_data.0.kind {
+ debug_assert!(drop_data.1 < drops.drops.next_index());
+ drops.entry_points.push((drop_data.1, blocks[drop_idx].unwrap()));
+ }
+ }
+ Self::build_unwind_tree(cfg, drops, fn_span, resume_block);
+ }
+
+ fn build_unwind_tree(
+ cfg: &mut CFG<'tcx>,
+ drops: &mut DropTree,
+ fn_span: Span,
+ resume_block: &mut Option<BasicBlock>,
+ ) {
+ let mut blocks = IndexVec::from_elem(None, &drops.drops);
+ blocks[ROOT_NODE] = *resume_block;
+ drops.build_mir::<Unwind>(cfg, &mut blocks);
+ if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) {
+ cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::Resume);
+
+ *resume_block = blocks[ROOT_NODE];
+ }
+ }
+}
+
+// DropTreeBuilder implementations.
+
+struct ExitScopes;
+
+impl<'tcx> DropTreeBuilder<'tcx> for ExitScopes {
+ fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock {
+ cfg.start_new_block()
+ }
+ fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) {
+ cfg.block_data_mut(from).terminator_mut().kind = TerminatorKind::Goto { target: to };
+ }
+}
+
+struct GeneratorDrop;
+
+impl<'tcx> DropTreeBuilder<'tcx> for GeneratorDrop {
+ fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock {
+ cfg.start_new_block()
+ }
+ fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) {
+ let term = cfg.block_data_mut(from).terminator_mut();
+ if let TerminatorKind::Yield { ref mut drop, .. } = term.kind {
+ *drop = Some(to);
+ } else {
+ span_bug!(
+ term.source_info.span,
+ "cannot enter generator drop tree from {:?}",
+ term.kind
+ )
+ }
+ }
+}
+
+struct Unwind;
+
+impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
+ fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock {
+ cfg.start_new_cleanup_block()
+ }
+ fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) {
+ let term = &mut cfg.block_data_mut(from).terminator_mut();
+ match &mut term.kind {
+ TerminatorKind::Drop { unwind, .. }
+ | TerminatorKind::DropAndReplace { unwind, .. }
+ | TerminatorKind::FalseUnwind { unwind, .. }
+ | TerminatorKind::Call { cleanup: unwind, .. }
+ | TerminatorKind::Assert { cleanup: unwind, .. }
+ | TerminatorKind::InlineAsm { cleanup: unwind, .. } => {
+ *unwind = Some(to);
+ }
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::SwitchInt { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Yield { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::FalseEdge { .. } => {
+ span_bug!(term.source_info.span, "cannot unwind from {:?}", term.kind)
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
new file mode 100644
index 000000000..864caf0ba
--- /dev/null
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -0,0 +1,680 @@
+use crate::build::ExprCategory;
+use rustc_middle::thir::visit::{self, Visitor};
+
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_middle::mir::BorrowKind;
+use rustc_middle::thir::*;
+use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
+use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
+use rustc_session::lint::Level;
+use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_span::symbol::Symbol;
+use rustc_span::Span;
+
+use std::borrow::Cow;
+use std::ops::Bound;
+
+struct UnsafetyVisitor<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ thir: &'a Thir<'tcx>,
+ /// The `HirId` of the current scope, which would be the `HirId`
+ /// of the current HIR node, modulo adjustments. Used for lint levels.
+ hir_context: hir::HirId,
+ /// The current "safety context". This notably tracks whether we are in an
+ /// `unsafe` block, and whether it has been used.
+ safety_context: SafetyContext,
+ body_unsafety: BodyUnsafety,
+ /// The `#[target_feature]` attributes of the body. Used for checking
+ /// calls to functions with `#[target_feature]` (RFC 2396).
+ body_target_features: &'tcx [Symbol],
+ /// When inside the LHS of an assignment to a field, this is the type
+ /// of the LHS and the span of the assignment expression.
+ assignment_info: Option<(Ty<'tcx>, Span)>,
+ in_union_destructure: bool,
+ param_env: ParamEnv<'tcx>,
+ inside_adt: bool,
+}
+
+impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
+ fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
+ if let (
+ SafetyContext::UnsafeBlock { span: enclosing_span, .. },
+ SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
+ ) = (self.safety_context, safety_context)
+ {
+ self.warn_unused_unsafe(
+ hir_id,
+ block_span,
+ Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
+ );
+ f(self);
+ } else {
+ let prev_context = self.safety_context;
+ self.safety_context = safety_context;
+
+ f(self);
+
+ if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
+ self.warn_unused_unsafe(
+ hir_id,
+ span,
+ if self.unsafe_op_in_unsafe_fn_allowed() {
+ self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
+ } else {
+ None
+ },
+ );
+ }
+ self.safety_context = prev_context;
+ }
+ }
+
+ fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
+ let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
+ match self.safety_context {
+ SafetyContext::BuiltinUnsafeBlock => {}
+ SafetyContext::UnsafeBlock { ref mut used, .. } => {
+ if !self.body_unsafety.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed {
+ // Mark this block as useful
+ *used = true;
+ }
+ }
+ SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
+ SafetyContext::UnsafeFn => {
+ let (description, note) = kind.description_and_note(self.tcx);
+ // unsafe_op_in_unsafe_fn is disallowed
+ self.tcx.struct_span_lint_hir(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ self.hir_context,
+ span,
+ |lint| {
+ lint.build(&format!(
+ "{} is unsafe and requires unsafe block (error E0133)",
+ description,
+ ))
+ .span_label(span, kind.simple_description())
+ .note(note)
+ .emit();
+ },
+ )
+ }
+ SafetyContext::Safe => {
+ let (description, note) = kind.description_and_note(self.tcx);
+ let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0133,
+ "{} is unsafe and requires unsafe{} block",
+ description,
+ fn_sugg,
+ )
+ .span_label(span, kind.simple_description())
+ .note(note)
+ .emit();
+ }
+ }
+ }
+
+ fn warn_unused_unsafe(
+ &self,
+ hir_id: hir::HirId,
+ block_span: Span,
+ enclosing_unsafe: Option<(Span, &'static str)>,
+ ) {
+ let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
+ self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
+ let msg = "unnecessary `unsafe` block";
+ let mut db = lint.build(msg);
+ db.span_label(block_span, msg);
+ if let Some((span, kind)) = enclosing_unsafe {
+ db.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
+ }
+ db.emit();
+ });
+ }
+
+ /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
+ fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
+ self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
+ }
+}
+
+// Searches for accesses to layout constrained fields.
+struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
+ found: bool,
+ thir: &'a Thir<'tcx>,
+ tcx: TyCtxt<'tcx>,
+}
+
+impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
+ fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+ Self { found: false, thir, tcx }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
+ fn thir(&self) -> &'a Thir<'tcx> {
+ self.thir
+ }
+
+ fn visit_expr(&mut self, expr: &Expr<'tcx>) {
+ match expr.kind {
+ ExprKind::Field { lhs, .. } => {
+ if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
+ if (Bound::Unbounded, Bound::Unbounded)
+ != self.tcx.layout_scalar_valid_range(adt_def.did())
+ {
+ self.found = true;
+ }
+ }
+ visit::walk_expr(self, expr);
+ }
+
+ // Keep walking through the expression as long as we stay in the same
+ // place, i.e. the expression is a place expression and not a dereference
+ // (since dereferencing something leads us to a different place).
+ ExprKind::Deref { .. } => {}
+ ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => {
+ visit::walk_expr(self, expr);
+ }
+
+ _ => {}
+ }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
+ fn thir(&self) -> &'a Thir<'tcx> {
+ &self.thir
+ }
+
+ fn visit_block(&mut self, block: &Block) {
+ match block.safety_mode {
+ // compiler-generated unsafe code should not count towards the usefulness of
+ // an outer unsafe block
+ BlockSafety::BuiltinUnsafe => {
+ self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
+ visit::walk_block(this, block)
+ });
+ }
+ BlockSafety::ExplicitUnsafe(hir_id) => {
+ self.in_safety_context(
+ SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
+ |this| visit::walk_block(this, block),
+ );
+ }
+ BlockSafety::Safe => {
+ visit::walk_block(self, block);
+ }
+ }
+ }
+
+ fn visit_pat(&mut self, pat: &Pat<'tcx>) {
+ if self.in_union_destructure {
+ match *pat.kind {
+ // binding to a variable allows getting stuff out of variable
+ PatKind::Binding { .. }
+ // match is conditional on having this value
+ | PatKind::Constant { .. }
+ | PatKind::Variant { .. }
+ | PatKind::Leaf { .. }
+ | PatKind::Deref { .. }
+ | PatKind::Range { .. }
+ | PatKind::Slice { .. }
+ | PatKind::Array { .. } => {
+ self.requires_unsafe(pat.span, AccessToUnionField);
+ return; // we can return here since this already requires unsafe
+ }
+ // wildcard doesn't take anything
+ PatKind::Wild |
+ // these just wrap other patterns
+ PatKind::Or { .. } |
+ PatKind::AscribeUserType { .. } => {}
+ }
+ };
+
+ match &*pat.kind {
+ PatKind::Leaf { .. } => {
+ if let ty::Adt(adt_def, ..) = pat.ty.kind() {
+ if adt_def.is_union() {
+ let old_in_union_destructure =
+ std::mem::replace(&mut self.in_union_destructure, true);
+ visit::walk_pat(self, pat);
+ self.in_union_destructure = old_in_union_destructure;
+ } else if (Bound::Unbounded, Bound::Unbounded)
+ != self.tcx.layout_scalar_valid_range(adt_def.did())
+ {
+ let old_inside_adt = std::mem::replace(&mut self.inside_adt, true);
+ visit::walk_pat(self, pat);
+ self.inside_adt = old_inside_adt;
+ } else {
+ visit::walk_pat(self, pat);
+ }
+ } else {
+ visit::walk_pat(self, pat);
+ }
+ }
+ PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => {
+ if self.inside_adt {
+ let ty::Ref(_, ty, _) = ty.kind() else {
+ span_bug!(
+ pat.span,
+ "BindingMode::ByRef in pattern, but found non-reference type {}",
+ ty
+ );
+ };
+ match borrow_kind {
+ BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
+ if !ty.is_freeze(self.tcx.at(pat.span), self.param_env) {
+ self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
+ }
+ }
+ BorrowKind::Mut { .. } => {
+ self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
+ }
+ }
+ }
+ visit::walk_pat(self, pat);
+ }
+ PatKind::Deref { .. } => {
+ let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
+ visit::walk_pat(self, pat);
+ self.inside_adt = old_inside_adt;
+ }
+ _ => {
+ visit::walk_pat(self, pat);
+ }
+ }
+ }
+
+ fn visit_expr(&mut self, expr: &Expr<'tcx>) {
+ // could we be in the LHS of an assignment to a field?
+ match expr.kind {
+ ExprKind::Field { .. }
+ | ExprKind::VarRef { .. }
+ | ExprKind::UpvarRef { .. }
+ | ExprKind::Scope { .. }
+ | ExprKind::Cast { .. } => {}
+
+ ExprKind::AddressOf { .. }
+ | ExprKind::Adt { .. }
+ | ExprKind::Array { .. }
+ | ExprKind::Binary { .. }
+ | ExprKind::Block { .. }
+ | ExprKind::Borrow { .. }
+ | ExprKind::Literal { .. }
+ | ExprKind::NamedConst { .. }
+ | ExprKind::NonHirLiteral { .. }
+ | ExprKind::ZstLiteral { .. }
+ | ExprKind::ConstParam { .. }
+ | ExprKind::ConstBlock { .. }
+ | ExprKind::Deref { .. }
+ | ExprKind::Index { .. }
+ | ExprKind::NeverToAny { .. }
+ | ExprKind::PlaceTypeAscription { .. }
+ | ExprKind::ValueTypeAscription { .. }
+ | ExprKind::Pointer { .. }
+ | ExprKind::Repeat { .. }
+ | ExprKind::StaticRef { .. }
+ | ExprKind::ThreadLocalRef { .. }
+ | ExprKind::Tuple { .. }
+ | ExprKind::Unary { .. }
+ | ExprKind::Call { .. }
+ | ExprKind::Assign { .. }
+ | ExprKind::AssignOp { .. }
+ | ExprKind::Break { .. }
+ | ExprKind::Closure { .. }
+ | ExprKind::Continue { .. }
+ | ExprKind::Return { .. }
+ | ExprKind::Yield { .. }
+ | ExprKind::Loop { .. }
+ | ExprKind::Let { .. }
+ | ExprKind::Match { .. }
+ | ExprKind::Box { .. }
+ | ExprKind::If { .. }
+ | ExprKind::InlineAsm { .. }
+ | ExprKind::LogicalOp { .. }
+ | ExprKind::Use { .. } => {
+ // We don't need to save the old value and restore it
+ // because all the place expressions can't have more
+ // than one child.
+ self.assignment_info = None;
+ }
+ };
+ match expr.kind {
+ ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
+ let prev_id = self.hir_context;
+ self.hir_context = hir_id;
+ self.visit_expr(&self.thir[value]);
+ self.hir_context = prev_id;
+ return; // don't visit the whole expression
+ }
+ ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
+ if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
+ let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
+ Some(*func_id)
+ } else {
+ None
+ };
+ self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
+ } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
+ // If the called function has target features the calling function hasn't,
+ // the call requires `unsafe`. Don't check this on wasm
+ // targets, though. For more information on wasm see the
+ // is_like_wasm check in typeck/src/collect.rs
+ if !self.tcx.sess.target.options.is_like_wasm
+ && !self
+ .tcx
+ .codegen_fn_attrs(func_did)
+ .target_features
+ .iter()
+ .all(|feature| self.body_target_features.contains(feature))
+ {
+ self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
+ }
+ }
+ }
+ ExprKind::Deref { arg } => {
+ if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
+ if self.tcx.is_mutable_static(def_id) {
+ self.requires_unsafe(expr.span, UseOfMutableStatic);
+ } else if self.tcx.is_foreign_item(def_id) {
+ self.requires_unsafe(expr.span, UseOfExternStatic);
+ }
+ } else if self.thir[arg].ty.is_unsafe_ptr() {
+ self.requires_unsafe(expr.span, DerefOfRawPointer);
+ }
+ }
+ ExprKind::InlineAsm { .. } => {
+ self.requires_unsafe(expr.span, UseOfInlineAssembly);
+ }
+ ExprKind::Adt(box Adt {
+ adt_def,
+ variant_index: _,
+ substs: _,
+ user_ty: _,
+ fields: _,
+ base: _,
+ }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) {
+ (Bound::Unbounded, Bound::Unbounded) => {}
+ _ => self.requires_unsafe(expr.span, InitializingTypeWith),
+ },
+ ExprKind::Closure {
+ closure_id,
+ substs: _,
+ upvars: _,
+ movability: _,
+ fake_reads: _,
+ } => {
+ let closure_def = if let Some((did, const_param_id)) =
+ ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
+ {
+ ty::WithOptConstParam { did, const_param_did: Some(const_param_id) }
+ } else {
+ ty::WithOptConstParam::unknown(closure_id)
+ };
+ let (closure_thir, expr) = self.tcx.thir_body(closure_def).unwrap_or_else(|_| {
+ (self.tcx.alloc_steal_thir(Thir::new()), ExprId::from_u32(0))
+ });
+ let closure_thir = &closure_thir.borrow();
+ let hir_context = self.tcx.hir().local_def_id_to_hir_id(closure_id);
+ let mut closure_visitor =
+ UnsafetyVisitor { thir: closure_thir, hir_context, ..*self };
+ closure_visitor.visit_expr(&closure_thir[expr]);
+ // Unsafe blocks can be used in closures, make sure to take it into account
+ self.safety_context = closure_visitor.safety_context;
+ }
+ ExprKind::Field { lhs, .. } => {
+ let lhs = &self.thir[lhs];
+ if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
+ if let Some((assigned_ty, assignment_span)) = self.assignment_info {
+ if assigned_ty.needs_drop(self.tcx, self.param_env) {
+ // This would be unsafe, but should be outright impossible since we reject such unions.
+ self.tcx.sess.delay_span_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}"));
+ }
+ } else {
+ self.requires_unsafe(expr.span, AccessToUnionField);
+ }
+ }
+ }
+ ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
+ let lhs = &self.thir[lhs];
+ // First, check whether we are mutating a layout constrained field
+ let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
+ visit::walk_expr(&mut visitor, lhs);
+ if visitor.found {
+ self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
+ }
+
+ // Second, check for accesses to union fields
+ // don't have any special handling for AssignOp since it causes a read *and* write to lhs
+ if matches!(expr.kind, ExprKind::Assign { .. }) {
+ self.assignment_info = Some((lhs.ty, expr.span));
+ visit::walk_expr(self, lhs);
+ self.assignment_info = None;
+ visit::walk_expr(self, &self.thir()[rhs]);
+ return; // we have already visited everything by now
+ }
+ }
+ ExprKind::Borrow { borrow_kind, arg } => {
+ let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
+ visit::walk_expr(&mut visitor, expr);
+ if visitor.found {
+ match borrow_kind {
+ BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique
+ if !self.thir[arg]
+ .ty
+ .is_freeze(self.tcx.at(self.thir[arg].span), self.param_env) =>
+ {
+ self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
+ }
+ BorrowKind::Mut { .. } => {
+ self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
+ }
+ BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {}
+ }
+ }
+ }
+ ExprKind::Let { expr: expr_id, .. } => {
+ let let_expr = &self.thir[expr_id];
+ if let ty::Adt(adt_def, _) = let_expr.ty.kind() && adt_def.is_union() {
+ self.requires_unsafe(expr.span, AccessToUnionField);
+ }
+ }
+ _ => {}
+ }
+ visit::walk_expr(self, expr);
+ }
+}
+
+#[derive(Clone, Copy)]
+enum SafetyContext {
+ Safe,
+ BuiltinUnsafeBlock,
+ UnsafeFn,
+ UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
+}
+
+#[derive(Clone, Copy)]
+enum BodyUnsafety {
+ /// The body is not unsafe.
+ Safe,
+ /// The body is an unsafe function. The span points to
+ /// the signature of the function.
+ Unsafe(Span),
+}
+
+impl BodyUnsafety {
+ /// Returns whether the body is unsafe.
+ fn is_unsafe(&self) -> bool {
+ matches!(self, BodyUnsafety::Unsafe(_))
+ }
+
+ /// If the body is unsafe, returns the `Span` of its signature.
+ fn unsafe_fn_sig_span(self) -> Option<Span> {
+ match self {
+ BodyUnsafety::Unsafe(span) => Some(span),
+ BodyUnsafety::Safe => None,
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq)]
+enum UnsafeOpKind {
+ CallToUnsafeFunction(Option<DefId>),
+ UseOfInlineAssembly,
+ InitializingTypeWith,
+ UseOfMutableStatic,
+ UseOfExternStatic,
+ DerefOfRawPointer,
+ AccessToUnionField,
+ MutationOfLayoutConstrainedField,
+ BorrowOfLayoutConstrainedField,
+ CallToFunctionWith(DefId),
+}
+
+use UnsafeOpKind::*;
+
+impl UnsafeOpKind {
+ pub fn simple_description(&self) -> &'static str {
+ match self {
+ CallToUnsafeFunction(..) => "call to unsafe function",
+ UseOfInlineAssembly => "use of inline assembly",
+ InitializingTypeWith => "initializing type with `rustc_layout_scalar_valid_range` attr",
+ UseOfMutableStatic => "use of mutable static",
+ UseOfExternStatic => "use of extern static",
+ DerefOfRawPointer => "dereference of raw pointer",
+ AccessToUnionField => "access to union field",
+ MutationOfLayoutConstrainedField => "mutation of layout constrained field",
+ BorrowOfLayoutConstrainedField => {
+ "borrow of layout constrained field with interior mutability"
+ }
+ CallToFunctionWith(..) => "call to function with `#[target_feature]`",
+ }
+ }
+
+ pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) {
+ match self {
+ CallToUnsafeFunction(did) => (
+ if let Some(did) = did {
+ Cow::from(format!("call to unsafe function `{}`", tcx.def_path_str(*did)))
+ } else {
+ Cow::Borrowed(self.simple_description())
+ },
+ "consult the function's documentation for information on how to avoid undefined \
+ behavior",
+ ),
+ UseOfInlineAssembly => (
+ Cow::Borrowed(self.simple_description()),
+ "inline assembly is entirely unchecked and can cause undefined behavior",
+ ),
+ InitializingTypeWith => (
+ Cow::Borrowed(self.simple_description()),
+ "initializing a layout restricted type's field with a value outside the valid \
+ range is undefined behavior",
+ ),
+ UseOfMutableStatic => (
+ Cow::Borrowed(self.simple_description()),
+ "mutable statics can be mutated by multiple threads: aliasing violations or data \
+ races will cause undefined behavior",
+ ),
+ UseOfExternStatic => (
+ Cow::Borrowed(self.simple_description()),
+ "extern statics are not controlled by the Rust type system: invalid data, \
+ aliasing violations or data races will cause undefined behavior",
+ ),
+ DerefOfRawPointer => (
+ Cow::Borrowed(self.simple_description()),
+ "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
+ and cause data races: all of these are undefined behavior",
+ ),
+ AccessToUnionField => (
+ Cow::Borrowed(self.simple_description()),
+ "the field may not be properly initialized: using uninitialized data will cause \
+ undefined behavior",
+ ),
+ MutationOfLayoutConstrainedField => (
+ Cow::Borrowed(self.simple_description()),
+ "mutating layout constrained fields cannot statically be checked for valid values",
+ ),
+ BorrowOfLayoutConstrainedField => (
+ Cow::Borrowed(self.simple_description()),
+ "references to fields of layout constrained fields lose the constraints. Coupled \
+ with interior mutability, the field can be changed to invalid values",
+ ),
+ CallToFunctionWith(did) => (
+ Cow::from(format!(
+ "call to function `{}` with `#[target_feature]`",
+ tcx.def_path_str(*did)
+ )),
+ "can only be called if the required target features are available",
+ ),
+ }
+ }
+}
+
+pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
+ // THIR unsafeck is gated under `-Z thir-unsafeck`
+ if !tcx.sess.opts.unstable_opts.thir_unsafeck {
+ return;
+ }
+
+ // Closures are handled by their owner, if it has a body
+ if tcx.is_closure(def.did.to_def_id()) {
+ let hir = tcx.hir();
+ let owner = hir.enclosing_body_owner(hir.local_def_id_to_hir_id(def.did));
+ tcx.ensure().thir_check_unsafety(owner);
+ return;
+ }
+
+ let Ok((thir, expr)) = tcx.thir_body(def) else {
+ return
+ };
+ let thir = &thir.borrow();
+ // If `thir` is empty, a type error occurred, skip this body.
+ if thir.exprs.is_empty() {
+ return;
+ }
+
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
+ let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
+ if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
+ BodyUnsafety::Unsafe(fn_sig.span)
+ } else {
+ BodyUnsafety::Safe
+ }
+ });
+ let body_target_features = &tcx.body_codegen_attrs(def.did.to_def_id()).target_features;
+ let safety_context =
+ if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
+ let mut visitor = UnsafetyVisitor {
+ tcx,
+ thir,
+ safety_context,
+ hir_context: hir_id,
+ body_unsafety,
+ body_target_features,
+ assignment_info: None,
+ in_union_destructure: false,
+ param_env: tcx.param_env(def.did),
+ inside_adt: false,
+ };
+ visitor.visit_expr(&thir[expr]);
+}
+
+pub(crate) fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+ if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
+ tcx.thir_check_unsafety_for_const_arg(def)
+ } else {
+ check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
+ }
+}
+
+pub(crate) fn thir_check_unsafety_for_const_arg<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ (did, param_did): (LocalDefId, DefId),
+) {
+ check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
+}
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
new file mode 100644
index 000000000..11cd2a9aa
--- /dev/null
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -0,0 +1,35 @@
+//! Construction of MIR from HIR.
+//!
+//! This crate also contains the match exhaustiveness and usefulness checking.
+#![allow(rustc::potential_query_instability)]
+#![feature(box_patterns)]
+#![feature(control_flow_enum)]
+#![feature(if_let_guard)]
+#![feature(let_chains)]
+#![feature(let_else)]
+#![feature(min_specialization)]
+#![feature(once_cell)]
+#![recursion_limit = "256"]
+
+#[macro_use]
+extern crate tracing;
+#[macro_use]
+extern crate rustc_middle;
+
+mod build;
+mod check_unsafety;
+mod lints;
+pub mod thir;
+
+use rustc_middle::ty::query::Providers;
+
+pub fn provide(providers: &mut Providers) {
+ providers.check_match = thir::pattern::check_match;
+ providers.lit_to_const = thir::constant::lit_to_const;
+ providers.lit_to_mir_constant = build::lit_to_mir_constant;
+ providers.mir_built = build::mir_built;
+ providers.thir_check_unsafety = check_unsafety::thir_check_unsafety;
+ providers.thir_check_unsafety_for_const_arg = check_unsafety::thir_check_unsafety_for_const_arg;
+ providers.thir_body = thir::cx::thir_body;
+ providers.thir_tree = thir::cx::thir_tree;
+}
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs
new file mode 100644
index 000000000..54d549fd6
--- /dev/null
+++ b/compiler/rustc_mir_build/src/lints.rs
@@ -0,0 +1,166 @@
+use rustc_data_structures::graph::iterate::{
+ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
+};
+use rustc_hir::def::DefKind;
+use rustc_middle::mir::{BasicBlock, BasicBlocks, Body, Operand, TerminatorKind};
+use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
+use rustc_middle::ty::{self, Instance, TyCtxt};
+use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
+use rustc_span::Span;
+use std::ops::ControlFlow;
+
+pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
+ let def_id = body.source.def_id().expect_local();
+
+ if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) {
+ // If this is trait/impl method, extract the trait's substs.
+ let trait_substs = match tcx.trait_of_item(def_id.to_def_id()) {
+ Some(trait_def_id) => {
+ let trait_substs_count = tcx.generics_of(trait_def_id).count();
+ &InternalSubsts::identity_for_item(tcx, def_id.to_def_id())[..trait_substs_count]
+ }
+ _ => &[],
+ };
+
+ let mut vis = Search { tcx, body, reachable_recursive_calls: vec![], trait_substs };
+ if let Some(NonRecursive) =
+ TriColorDepthFirstSearch::new(&body.basic_blocks).run_from_start(&mut vis)
+ {
+ return;
+ }
+ if vis.reachable_recursive_calls.is_empty() {
+ return;
+ }
+
+ vis.reachable_recursive_calls.sort();
+
+ let sp = tcx.def_span(def_id);
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |lint| {
+ let mut db = lint.build("function cannot return without recursing");
+ db.span_label(sp, "cannot return without recursing");
+ // offer some help to the programmer.
+ for call_span in vis.reachable_recursive_calls {
+ db.span_label(call_span, "recursive call site");
+ }
+ db.help("a `loop` may express intention better if this is on purpose");
+ db.emit();
+ });
+ }
+}
+
+struct NonRecursive;
+
+struct Search<'mir, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ body: &'mir Body<'tcx>,
+ trait_substs: &'tcx [GenericArg<'tcx>],
+
+ reachable_recursive_calls: Vec<Span>,
+}
+
+impl<'mir, 'tcx> Search<'mir, 'tcx> {
+ /// Returns `true` if `func` refers to the function we are searching in.
+ fn is_recursive_call(&self, func: &Operand<'tcx>, args: &[Operand<'tcx>]) -> bool {
+ let Search { tcx, body, trait_substs, .. } = *self;
+ // Resolving function type to a specific instance that is being called is expensive. To
+ // avoid the cost we check the number of arguments first, which is sufficient to reject
+ // most of calls as non-recursive.
+ if args.len() != body.arg_count {
+ return false;
+ }
+ let caller = body.source.def_id();
+ let param_env = tcx.param_env(caller);
+
+ let func_ty = func.ty(body, tcx);
+ if let ty::FnDef(callee, substs) = *func_ty.kind() {
+ let normalized_substs = tcx.normalize_erasing_regions(param_env, substs);
+ let (callee, call_substs) = if let Ok(Some(instance)) =
+ Instance::resolve(tcx, param_env, callee, normalized_substs)
+ {
+ (instance.def_id(), instance.substs)
+ } else {
+ (callee, normalized_substs)
+ };
+
+ // FIXME(#57965): Make this work across function boundaries
+
+ // If this is a trait fn, the substs on the trait have to match, or we might be
+ // calling into an entirely different method (for example, a call from the default
+ // method in the trait to `<A as Trait<B>>::method`, where `A` and/or `B` are
+ // specific types).
+ return callee == caller && &call_substs[..trait_substs.len()] == trait_substs;
+ }
+
+ false
+ }
+}
+
+impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> {
+ type BreakVal = NonRecursive;
+
+ fn node_examined(
+ &mut self,
+ bb: BasicBlock,
+ prior_status: Option<NodeStatus>,
+ ) -> ControlFlow<Self::BreakVal> {
+ // Back-edge in the CFG (loop).
+ if let Some(NodeStatus::Visited) = prior_status {
+ return ControlFlow::Break(NonRecursive);
+ }
+
+ match self.body[bb].terminator().kind {
+ // These terminators return control flow to the caller.
+ TerminatorKind::Abort
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::Resume
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive),
+
+ // A diverging InlineAsm is treated as non-recursing
+ TerminatorKind::InlineAsm { destination, .. } => {
+ if destination.is_some() {
+ ControlFlow::CONTINUE
+ } else {
+ ControlFlow::Break(NonRecursive)
+ }
+ }
+
+ // These do not.
+ TerminatorKind::Assert { .. }
+ | TerminatorKind::Call { .. }
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::Goto { .. }
+ | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE,
+ }
+ }
+
+ fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow<Self::BreakVal> {
+ // When we examine a node for the last time, remember it if it is a recursive call.
+ let terminator = self.body[bb].terminator();
+ if let TerminatorKind::Call { func, args, .. } = &terminator.kind {
+ if self.is_recursive_call(func, args) {
+ self.reachable_recursive_calls.push(terminator.source_info.span);
+ }
+ }
+
+ ControlFlow::CONTINUE
+ }
+
+ fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool {
+ let terminator = self.body[bb].terminator();
+ if terminator.unwind() == Some(&Some(target)) && terminator.successors().count() > 1 {
+ return true;
+ }
+ // Don't traverse successors of recursive calls or false CFG edges.
+ match self.body[bb].terminator().kind {
+ TerminatorKind::Call { ref func, ref args, .. } => self.is_recursive_call(func, args),
+ TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == target,
+ _ => false,
+ }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
new file mode 100644
index 000000000..a7e4403a2
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -0,0 +1,52 @@
+use rustc_ast as ast;
+use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
+use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt};
+
+pub(crate) fn lit_to_const<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ lit_input: LitToConstInput<'tcx>,
+) -> Result<ty::Const<'tcx>, LitToConstError> {
+ let LitToConstInput { lit, ty, neg } = lit_input;
+
+ let trunc = |n| {
+ let param_ty = ParamEnv::reveal_all().and(ty);
+ let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
+ trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
+ let result = width.truncate(n);
+ trace!("trunc result: {}", result);
+
+ Ok(ScalarInt::try_from_uint(result, width)
+ .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)))
+ };
+
+ let valtree = match (lit, &ty.kind()) {
+ (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
+ let str_bytes = s.as_str().as_bytes();
+ ty::ValTree::from_raw_bytes(tcx, str_bytes)
+ }
+ (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _))
+ if matches!(inner_ty.kind(), ty::Slice(_)) =>
+ {
+ let bytes = data as &[u8];
+ ty::ValTree::from_raw_bytes(tcx, bytes)
+ }
+ (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
+ let bytes = data as &[u8];
+ ty::ValTree::from_raw_bytes(tcx, bytes)
+ }
+ (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
+ ty::ValTree::from_scalar_int((*n).into())
+ }
+ (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
+ let scalar_int =
+ trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?;
+ ty::ValTree::from_scalar_int(scalar_int)
+ }
+ (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
+ (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()),
+ (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported),
+ _ => return Err(LitToConstError::TypeError),
+ };
+
+ Ok(ty::Const::from_value(tcx, valtree, ty))
+}
diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs
new file mode 100644
index 000000000..dccaa61ed
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/cx/block.rs
@@ -0,0 +1,126 @@
+use crate::thir::cx::Cx;
+
+use rustc_hir as hir;
+use rustc_middle::middle::region;
+use rustc_middle::thir::*;
+use rustc_middle::ty;
+
+use rustc_index::vec::Idx;
+use rustc_middle::ty::CanonicalUserTypeAnnotation;
+
+impl<'tcx> Cx<'tcx> {
+ pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> Block {
+ // We have to eagerly lower the "spine" of the statements
+ // in order to get the lexical scoping correctly.
+ let stmts = self.mirror_stmts(block.hir_id.local_id, block.stmts);
+ let opt_destruction_scope =
+ self.region_scope_tree.opt_destruction_scope(block.hir_id.local_id);
+ Block {
+ targeted_by_break: block.targeted_by_break,
+ region_scope: region::Scope {
+ id: block.hir_id.local_id,
+ data: region::ScopeData::Node,
+ },
+ opt_destruction_scope,
+ span: block.span,
+ stmts,
+ expr: block.expr.map(|expr| self.mirror_expr(expr)),
+ safety_mode: match block.rules {
+ hir::BlockCheckMode::DefaultBlock => BlockSafety::Safe,
+ hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated) => {
+ BlockSafety::BuiltinUnsafe
+ }
+ hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) => {
+ BlockSafety::ExplicitUnsafe(block.hir_id)
+ }
+ },
+ }
+ }
+
+ fn mirror_stmts(
+ &mut self,
+ block_id: hir::ItemLocalId,
+ stmts: &'tcx [hir::Stmt<'tcx>],
+ ) -> Box<[StmtId]> {
+ stmts
+ .iter()
+ .enumerate()
+ .filter_map(|(index, stmt)| {
+ let hir_id = stmt.hir_id;
+ let opt_dxn_ext = self.region_scope_tree.opt_destruction_scope(hir_id.local_id);
+ match stmt.kind {
+ hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
+ let stmt = Stmt {
+ kind: StmtKind::Expr {
+ scope: region::Scope {
+ id: hir_id.local_id,
+ data: region::ScopeData::Node,
+ },
+ expr: self.mirror_expr(expr),
+ },
+ opt_destruction_scope: opt_dxn_ext,
+ };
+ Some(self.thir.stmts.push(stmt))
+ }
+ hir::StmtKind::Item(..) => {
+ // ignore for purposes of the MIR
+ None
+ }
+ hir::StmtKind::Local(ref local) => {
+ let remainder_scope = region::Scope {
+ id: block_id,
+ data: region::ScopeData::Remainder(region::FirstStatementIndex::new(
+ index,
+ )),
+ };
+
+ let else_block = local.els.map(|els| self.mirror_block(els));
+
+ let mut pattern = self.pattern_from_hir(local.pat);
+ debug!(?pattern);
+
+ if let Some(ty) = &local.ty {
+ if let Some(&user_ty) =
+ self.typeck_results.user_provided_types().get(ty.hir_id)
+ {
+ debug!("mirror_stmts: user_ty={:?}", user_ty);
+ let annotation = CanonicalUserTypeAnnotation {
+ user_ty,
+ span: ty.span,
+ inferred_ty: self.typeck_results.node_type(ty.hir_id),
+ };
+ pattern = Pat {
+ ty: pattern.ty,
+ span: pattern.span,
+ kind: Box::new(PatKind::AscribeUserType {
+ ascription: Ascription {
+ annotation,
+ variance: ty::Variance::Covariant,
+ },
+ subpattern: pattern,
+ }),
+ };
+ }
+ }
+
+ let stmt = Stmt {
+ kind: StmtKind::Let {
+ remainder_scope,
+ init_scope: region::Scope {
+ id: hir_id.local_id,
+ data: region::ScopeData::Node,
+ },
+ pattern,
+ initializer: local.init.map(|init| self.mirror_expr(init)),
+ else_block,
+ lint_level: LintLevel::Explicit(local.hir_id),
+ },
+ opt_destruction_scope: opt_dxn_ext,
+ };
+ Some(self.thir.stmts.push(stmt))
+ }
+ }
+ })
+ .collect()
+ }
+}
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
new file mode 100644
index 000000000..985601712
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -0,0 +1,1117 @@
+use crate::thir::cx::region::Scope;
+use crate::thir::cx::Cx;
+use crate::thir::util::UserAnnotatedTyHelpers;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_hir as hir;
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_index::vec::Idx;
+use rustc_middle::hir::place::Place as HirPlace;
+use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
+use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
+use rustc_middle::middle::region;
+use rustc_middle::mir::{self, BinOp, BorrowKind, Field, UnOp};
+use rustc_middle::thir::*;
+use rustc_middle::ty::adjustment::{
+ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast,
+};
+use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
+use rustc_middle::ty::{
+ self, AdtKind, InlineConstSubsts, InlineConstSubstsParts, ScalarInt, Ty, UpvarSubsts, UserType,
+};
+use rustc_span::def_id::DefId;
+use rustc_span::Span;
+use rustc_target::abi::VariantIdx;
+
+impl<'tcx> Cx<'tcx> {
+ pub(crate) fn mirror_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> ExprId {
+ // `mirror_expr` is recursing very deep. Make sure the stack doesn't overflow.
+ ensure_sufficient_stack(|| self.mirror_expr_inner(expr))
+ }
+
+ pub(crate) fn mirror_exprs(&mut self, exprs: &'tcx [hir::Expr<'tcx>]) -> Box<[ExprId]> {
+ exprs.iter().map(|expr| self.mirror_expr_inner(expr)).collect()
+ }
+
+ #[instrument(level = "trace", skip(self, hir_expr))]
+ pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
+ let temp_lifetime =
+ self.rvalue_scopes.temporary_scope(self.region_scope_tree, hir_expr.hir_id.local_id);
+ let expr_scope =
+ region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
+
+ trace!(?hir_expr.hir_id, ?hir_expr.span);
+
+ let mut expr = self.make_mirror_unadjusted(hir_expr);
+
+ let adjustment_span = match self.adjustment_span {
+ Some((hir_id, span)) if hir_id == hir_expr.hir_id => Some(span),
+ _ => None,
+ };
+
+ // Now apply adjustments, if any.
+ for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
+ trace!(?expr, ?adjustment);
+ let span = expr.span;
+ expr =
+ self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
+ }
+
+ // Next, wrap this up in the expr's scope.
+ expr = Expr {
+ temp_lifetime,
+ ty: expr.ty,
+ span: hir_expr.span,
+ kind: ExprKind::Scope {
+ region_scope: expr_scope,
+ value: self.thir.exprs.push(expr),
+ lint_level: LintLevel::Explicit(hir_expr.hir_id),
+ },
+ };
+
+ // Finally, create a destruction scope, if any.
+ if let Some(region_scope) =
+ self.region_scope_tree.opt_destruction_scope(hir_expr.hir_id.local_id)
+ {
+ expr = Expr {
+ temp_lifetime,
+ ty: expr.ty,
+ span: hir_expr.span,
+ kind: ExprKind::Scope {
+ region_scope,
+ value: self.thir.exprs.push(expr),
+ lint_level: LintLevel::Inherited,
+ },
+ };
+ }
+
+ // OK, all done!
+ self.thir.exprs.push(expr)
+ }
+
+ fn apply_adjustment(
+ &mut self,
+ hir_expr: &'tcx hir::Expr<'tcx>,
+ mut expr: Expr<'tcx>,
+ adjustment: &Adjustment<'tcx>,
+ mut span: Span,
+ ) -> Expr<'tcx> {
+ let Expr { temp_lifetime, .. } = expr;
+
+ // Adjust the span from the block, to the last expression of the
+ // block. This is a better span when returning a mutable reference
+ // with too short a lifetime. The error message will use the span
+ // from the assignment to the return place, which should only point
+ // at the returned value, not the entire function body.
+ //
+ // fn return_short_lived<'a>(x: &'a mut i32) -> &'static mut i32 {
+ // x
+ // // ^ error message points at this expression.
+ // }
+ let mut adjust_span = |expr: &mut Expr<'tcx>| {
+ if let ExprKind::Block { body } = &expr.kind {
+ if let Some(last_expr) = body.expr {
+ span = self.thir[last_expr].span;
+ expr.span = span;
+ }
+ }
+ };
+
+ let kind = match adjustment.kind {
+ Adjust::Pointer(PointerCast::Unsize) => {
+ adjust_span(&mut expr);
+ ExprKind::Pointer { cast: PointerCast::Unsize, source: self.thir.exprs.push(expr) }
+ }
+ Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: self.thir.exprs.push(expr) },
+ Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) },
+ Adjust::Deref(None) => {
+ adjust_span(&mut expr);
+ ExprKind::Deref { arg: self.thir.exprs.push(expr) }
+ }
+ Adjust::Deref(Some(deref)) => {
+ // We don't need to do call adjust_span here since
+ // deref coercions always start with a built-in deref.
+ let call = deref.method_call(self.tcx(), expr.ty);
+
+ expr = Expr {
+ temp_lifetime,
+ ty: self
+ .tcx
+ .mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }),
+ span,
+ kind: ExprKind::Borrow {
+ borrow_kind: deref.mutbl.to_borrow_kind(),
+ arg: self.thir.exprs.push(expr),
+ },
+ };
+
+ let expr = Box::new([self.thir.exprs.push(expr)]);
+
+ self.overloaded_place(hir_expr, adjustment.target, Some(call), expr, deref.span)
+ }
+ Adjust::Borrow(AutoBorrow::Ref(_, m)) => ExprKind::Borrow {
+ borrow_kind: m.to_borrow_kind(),
+ arg: self.thir.exprs.push(expr),
+ },
+ Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => {
+ ExprKind::AddressOf { mutability, arg: self.thir.exprs.push(expr) }
+ }
+ };
+
+ Expr { temp_lifetime, ty: adjustment.target, span, kind }
+ }
+
+ /// Lowers a cast expression.
+ ///
+ /// Dealing with user type annotations is left to the caller.
+ fn mirror_expr_cast(
+ &mut self,
+ source: &'tcx hir::Expr<'tcx>,
+ temp_lifetime: Option<Scope>,
+ span: Span,
+ ) -> ExprKind<'tcx> {
+ let tcx = self.tcx;
+
+ // Check to see if this cast is a "coercion cast", where the cast is actually done
+ // using a coercion (or is a no-op).
+ if self.typeck_results().is_coercion_cast(source.hir_id) {
+ // Convert the lexpr to a vexpr.
+ ExprKind::Use { source: self.mirror_expr(source) }
+ } else if self.typeck_results().expr_ty(source).is_region_ptr() {
+ // Special cased so that we can type check that the element
+ // type of the source matches the pointed to type of the
+ // destination.
+ ExprKind::Pointer {
+ source: self.mirror_expr(source),
+ cast: PointerCast::ArrayToPointer,
+ }
+ } else {
+ // check whether this is casting an enum variant discriminant
+ // to prevent cycles, we refer to the discriminant initializer
+ // which is always an integer and thus doesn't need to know the
+ // enum's layout (or its tag type) to compute it during const eval
+ // Example:
+ // enum Foo {
+ // A,
+ // B = A as isize + 4,
+ // }
+ // The correct solution would be to add symbolic computations to miri,
+ // so we wouldn't have to compute and store the actual value
+
+ let hir::ExprKind::Path(ref qpath) = source.kind else {
+ return ExprKind::Cast { source: self.mirror_expr(source)};
+ };
+
+ let res = self.typeck_results().qpath_res(qpath, source.hir_id);
+ let ty = self.typeck_results().node_type(source.hir_id);
+ let ty::Adt(adt_def, substs) = ty.kind() else {
+ return ExprKind::Cast { source: self.mirror_expr(source)};
+ };
+
+ let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res else {
+ return ExprKind::Cast { source: self.mirror_expr(source)};
+ };
+
+ let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id);
+ let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx);
+
+ use rustc_middle::ty::util::IntTypeExt;
+ let ty = adt_def.repr().discr_type();
+ let discr_ty = ty.to_ty(tcx);
+
+ let param_env_ty = self.param_env.and(discr_ty);
+ let size = tcx
+ .layout_of(param_env_ty)
+ .unwrap_or_else(|e| {
+ panic!("could not compute layout for {:?}: {:?}", param_env_ty, e)
+ })
+ .size;
+
+ let lit = ScalarInt::try_from_uint(discr_offset as u128, size).unwrap();
+ let kind = ExprKind::NonHirLiteral { lit, user_ty: None };
+ let offset = self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind });
+
+ let source = match discr_did {
+ // in case we are offsetting from a computed discriminant
+ // and not the beginning of discriminants (which is always `0`)
+ Some(did) => {
+ let kind = ExprKind::NamedConst { def_id: did, substs, user_ty: None };
+ let lhs =
+ self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind });
+ let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset };
+ self.thir.exprs.push(Expr {
+ temp_lifetime,
+ ty: discr_ty,
+ span: span,
+ kind: bin,
+ })
+ }
+ None => offset,
+ };
+
+ ExprKind::Cast { source }
+ }
+ }
+
+ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
+ let tcx = self.tcx;
+ let expr_ty = self.typeck_results().expr_ty(expr);
+ let expr_span = expr.span;
+ let temp_lifetime =
+ self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
+
+ let kind = match expr.kind {
+ // Here comes the interesting stuff:
+ hir::ExprKind::MethodCall(segment, ref args, fn_span) => {
+ // Rewrite a.b(c) into UFCS form like Trait::b(a, c)
+ let expr = self.method_callee(expr, segment.ident.span, None);
+ // When we apply adjustments to the receiver, use the span of
+ // the overall method call for better diagnostics. args[0]
+ // is guaranteed to exist, since a method call always has a receiver.
+ let old_adjustment_span = self.adjustment_span.replace((args[0].hir_id, expr_span));
+ tracing::info!("Using method span: {:?}", expr.span);
+ let args = self.mirror_exprs(args);
+ self.adjustment_span = old_adjustment_span;
+ ExprKind::Call {
+ ty: expr.ty,
+ fun: self.thir.exprs.push(expr),
+ args,
+ from_hir_call: true,
+ fn_span,
+ }
+ }
+
+ hir::ExprKind::Call(ref fun, ref args) => {
+ if self.typeck_results().is_method_call(expr) {
+ // The callee is something implementing Fn, FnMut, or FnOnce.
+ // Find the actual method implementation being called and
+ // build the appropriate UFCS call expression with the
+ // callee-object as expr parameter.
+
+ // rewrite f(u, v) into FnOnce::call_once(f, (u, v))
+
+ let method = self.method_callee(expr, fun.span, None);
+
+ let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e));
+ let tupled_args = Expr {
+ ty: tcx.mk_tup(arg_tys),
+ temp_lifetime,
+ span: expr.span,
+ kind: ExprKind::Tuple { fields: self.mirror_exprs(args) },
+ };
+ let tupled_args = self.thir.exprs.push(tupled_args);
+
+ ExprKind::Call {
+ ty: method.ty,
+ fun: self.thir.exprs.push(method),
+ args: Box::new([self.mirror_expr(fun), tupled_args]),
+ from_hir_call: true,
+ fn_span: expr.span,
+ }
+ } else {
+ let adt_data =
+ if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind {
+ // Tuple-like ADTs are represented as ExprKind::Call. We convert them here.
+ expr_ty.ty_adt_def().and_then(|adt_def| match path.res {
+ Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => {
+ Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id)))
+ }
+ Res::SelfCtor(..) => Some((adt_def, VariantIdx::new(0))),
+ _ => None,
+ })
+ } else {
+ None
+ };
+ if let Some((adt_def, index)) = adt_data {
+ let substs = self.typeck_results().node_substs(fun.hir_id);
+ let user_provided_types = self.typeck_results().user_provided_types();
+ let user_ty =
+ user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| {
+ if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value {
+ *did = adt_def.did();
+ }
+ u_ty
+ });
+ debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty);
+
+ let field_refs = args
+ .iter()
+ .enumerate()
+ .map(|(idx, e)| FieldExpr {
+ name: Field::new(idx),
+ expr: self.mirror_expr(e),
+ })
+ .collect();
+ ExprKind::Adt(Box::new(Adt {
+ adt_def,
+ substs,
+ variant_index: index,
+ fields: field_refs,
+ user_ty,
+ base: None,
+ }))
+ } else {
+ ExprKind::Call {
+ ty: self.typeck_results().node_type(fun.hir_id),
+ fun: self.mirror_expr(fun),
+ args: self.mirror_exprs(args),
+ from_hir_call: true,
+ fn_span: expr.span,
+ }
+ }
+ }
+ }
+
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, ref arg) => {
+ ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: self.mirror_expr(arg) }
+ }
+
+ hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutability, ref arg) => {
+ ExprKind::AddressOf { mutability, arg: self.mirror_expr(arg) }
+ }
+
+ hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: self.mirror_block(blk) },
+
+ hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
+ ExprKind::Assign { lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs) }
+ }
+
+ hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
+ if self.typeck_results().is_method_call(expr) {
+ let lhs = self.mirror_expr(lhs);
+ let rhs = self.mirror_expr(rhs);
+ self.overloaded_operator(expr, Box::new([lhs, rhs]))
+ } else {
+ ExprKind::AssignOp {
+ op: bin_op(op.node),
+ lhs: self.mirror_expr(lhs),
+ rhs: self.mirror_expr(rhs),
+ }
+ }
+ }
+
+ hir::ExprKind::Lit(ref lit) => ExprKind::Literal { lit, neg: false },
+
+ hir::ExprKind::Binary(op, ref lhs, ref rhs) => {
+ if self.typeck_results().is_method_call(expr) {
+ let lhs = self.mirror_expr(lhs);
+ let rhs = self.mirror_expr(rhs);
+ self.overloaded_operator(expr, Box::new([lhs, rhs]))
+ } else {
+ // FIXME overflow
+ match op.node {
+ hir::BinOpKind::And => ExprKind::LogicalOp {
+ op: LogicalOp::And,
+ lhs: self.mirror_expr(lhs),
+ rhs: self.mirror_expr(rhs),
+ },
+ hir::BinOpKind::Or => ExprKind::LogicalOp {
+ op: LogicalOp::Or,
+ lhs: self.mirror_expr(lhs),
+ rhs: self.mirror_expr(rhs),
+ },
+ _ => {
+ let op = bin_op(op.node);
+ ExprKind::Binary {
+ op,
+ lhs: self.mirror_expr(lhs),
+ rhs: self.mirror_expr(rhs),
+ }
+ }
+ }
+ }
+ }
+
+ hir::ExprKind::Index(ref lhs, ref index) => {
+ if self.typeck_results().is_method_call(expr) {
+ let lhs = self.mirror_expr(lhs);
+ let index = self.mirror_expr(index);
+ self.overloaded_place(expr, expr_ty, None, Box::new([lhs, index]), expr.span)
+ } else {
+ ExprKind::Index { lhs: self.mirror_expr(lhs), index: self.mirror_expr(index) }
+ }
+ }
+
+ hir::ExprKind::Unary(hir::UnOp::Deref, ref arg) => {
+ if self.typeck_results().is_method_call(expr) {
+ let arg = self.mirror_expr(arg);
+ self.overloaded_place(expr, expr_ty, None, Box::new([arg]), expr.span)
+ } else {
+ ExprKind::Deref { arg: self.mirror_expr(arg) }
+ }
+ }
+
+ hir::ExprKind::Unary(hir::UnOp::Not, ref arg) => {
+ if self.typeck_results().is_method_call(expr) {
+ let arg = self.mirror_expr(arg);
+ self.overloaded_operator(expr, Box::new([arg]))
+ } else {
+ ExprKind::Unary { op: UnOp::Not, arg: self.mirror_expr(arg) }
+ }
+ }
+
+ hir::ExprKind::Unary(hir::UnOp::Neg, ref arg) => {
+ if self.typeck_results().is_method_call(expr) {
+ let arg = self.mirror_expr(arg);
+ self.overloaded_operator(expr, Box::new([arg]))
+ } else if let hir::ExprKind::Lit(ref lit) = arg.kind {
+ ExprKind::Literal { lit, neg: true }
+ } else {
+ ExprKind::Unary { op: UnOp::Neg, arg: self.mirror_expr(arg) }
+ }
+ }
+
+ hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind() {
+ ty::Adt(adt, substs) => match adt.adt_kind() {
+ AdtKind::Struct | AdtKind::Union => {
+ let user_provided_types = self.typeck_results().user_provided_types();
+ let user_ty = user_provided_types.get(expr.hir_id).copied();
+ debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty);
+ ExprKind::Adt(Box::new(Adt {
+ adt_def: *adt,
+ variant_index: VariantIdx::new(0),
+ substs,
+ user_ty,
+ fields: self.field_refs(fields),
+ base: base.as_ref().map(|base| FruInfo {
+ base: self.mirror_expr(base),
+ field_types: self.typeck_results().fru_field_types()[expr.hir_id]
+ .iter()
+ .copied()
+ .collect(),
+ }),
+ }))
+ }
+ AdtKind::Enum => {
+ let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
+ match res {
+ Res::Def(DefKind::Variant, variant_id) => {
+ assert!(base.is_none());
+
+ let index = adt.variant_index_with_id(variant_id);
+ let user_provided_types =
+ self.typeck_results().user_provided_types();
+ let user_ty = user_provided_types.get(expr.hir_id).copied();
+ debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty);
+ ExprKind::Adt(Box::new(Adt {
+ adt_def: *adt,
+ variant_index: index,
+ substs,
+ user_ty,
+ fields: self.field_refs(fields),
+ base: None,
+ }))
+ }
+ _ => {
+ span_bug!(expr.span, "unexpected res: {:?}", res);
+ }
+ }
+ }
+ },
+ _ => {
+ span_bug!(expr.span, "unexpected type for struct literal: {:?}", expr_ty);
+ }
+ },
+
+ hir::ExprKind::Closure { .. } => {
+ let closure_ty = self.typeck_results().expr_ty(expr);
+ let (def_id, substs, movability) = match *closure_ty.kind() {
+ ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None),
+ ty::Generator(def_id, substs, movability) => {
+ (def_id, UpvarSubsts::Generator(substs), Some(movability))
+ }
+ _ => {
+ span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
+ }
+ };
+ let def_id = def_id.expect_local();
+
+ let upvars = self
+ .typeck_results
+ .closure_min_captures_flattened(def_id)
+ .zip(substs.upvar_tys())
+ .map(|(captured_place, ty)| {
+ let upvars = self.capture_upvar(expr, captured_place, ty);
+ self.thir.exprs.push(upvars)
+ })
+ .collect();
+
+ // Convert the closure fake reads, if any, from hir `Place` to ExprRef
+ let fake_reads = match self.typeck_results.closure_fake_reads.get(&def_id) {
+ Some(fake_reads) => fake_reads
+ .iter()
+ .map(|(place, cause, hir_id)| {
+ let expr = self.convert_captured_hir_place(expr, place.clone());
+ (self.thir.exprs.push(expr), *cause, *hir_id)
+ })
+ .collect(),
+ None => Vec::new(),
+ };
+
+ ExprKind::Closure { closure_id: def_id, substs, upvars, movability, fake_reads }
+ }
+
+ hir::ExprKind::Path(ref qpath) => {
+ let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
+ self.convert_path_expr(expr, res)
+ }
+
+ hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm {
+ template: asm.template,
+ operands: asm
+ .operands
+ .iter()
+ .map(|(op, _op_sp)| match *op {
+ hir::InlineAsmOperand::In { reg, ref expr } => {
+ InlineAsmOperand::In { reg, expr: self.mirror_expr(expr) }
+ }
+ hir::InlineAsmOperand::Out { reg, late, ref expr } => {
+ InlineAsmOperand::Out {
+ reg,
+ late,
+ expr: expr.as_ref().map(|expr| self.mirror_expr(expr)),
+ }
+ }
+ hir::InlineAsmOperand::InOut { reg, late, ref expr } => {
+ InlineAsmOperand::InOut { reg, late, expr: self.mirror_expr(expr) }
+ }
+ hir::InlineAsmOperand::SplitInOut {
+ reg,
+ late,
+ ref in_expr,
+ ref out_expr,
+ } => InlineAsmOperand::SplitInOut {
+ reg,
+ late,
+ in_expr: self.mirror_expr(in_expr),
+ out_expr: out_expr.as_ref().map(|expr| self.mirror_expr(expr)),
+ },
+ hir::InlineAsmOperand::Const { ref anon_const } => {
+ let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id);
+ let value = mir::ConstantKind::from_anon_const(
+ tcx,
+ anon_const_def_id,
+ self.param_env,
+ );
+ let span = tcx.hir().span(anon_const.hir_id);
+
+ InlineAsmOperand::Const { value, span }
+ }
+ hir::InlineAsmOperand::SymFn { ref anon_const } => {
+ let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id);
+ let value = mir::ConstantKind::from_anon_const(
+ tcx,
+ anon_const_def_id,
+ self.param_env,
+ );
+ let span = tcx.hir().span(anon_const.hir_id);
+
+ InlineAsmOperand::SymFn { value, span }
+ }
+ hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
+ InlineAsmOperand::SymStatic { def_id }
+ }
+ })
+ .collect(),
+ options: asm.options,
+ line_spans: asm.line_spans,
+ },
+
+ hir::ExprKind::ConstBlock(ref anon_const) => {
+ let ty = self.typeck_results().node_type(anon_const.hir_id);
+ let did = tcx.hir().local_def_id(anon_const.hir_id).to_def_id();
+ let typeck_root_def_id = tcx.typeck_root_def_id(did);
+ let parent_substs =
+ tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
+ let substs =
+ InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty })
+ .substs;
+
+ ExprKind::ConstBlock { did, substs }
+ }
+ // Now comes the rote stuff:
+ hir::ExprKind::Repeat(ref v, _) => {
+ let ty = self.typeck_results().expr_ty(expr);
+ let ty::Array(_, count) = ty.kind() else {
+ span_bug!(expr.span, "unexpected repeat expr ty: {:?}", ty);
+ };
+
+ ExprKind::Repeat { value: self.mirror_expr(v), count: *count }
+ }
+ hir::ExprKind::Ret(ref v) => {
+ ExprKind::Return { value: v.as_ref().map(|v| self.mirror_expr(v)) }
+ }
+ hir::ExprKind::Break(dest, ref value) => match dest.target_id {
+ Ok(target_id) => ExprKind::Break {
+ label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node },
+ value: value.as_ref().map(|value| self.mirror_expr(value)),
+ },
+ Err(err) => bug!("invalid loop id for break: {}", err),
+ },
+ hir::ExprKind::Continue(dest) => match dest.target_id {
+ Ok(loop_id) => ExprKind::Continue {
+ label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node },
+ },
+ Err(err) => bug!("invalid loop id for continue: {}", err),
+ },
+ hir::ExprKind::Let(let_expr) => ExprKind::Let {
+ expr: self.mirror_expr(let_expr.init),
+ pat: self.pattern_from_hir(let_expr.pat),
+ },
+ hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
+ if_then_scope: region::Scope {
+ id: then.hir_id.local_id,
+ data: region::ScopeData::IfThen,
+ },
+ cond: self.mirror_expr(cond),
+ then: self.mirror_expr(then),
+ else_opt: else_opt.map(|el| self.mirror_expr(el)),
+ },
+ hir::ExprKind::Match(ref discr, ref arms, _) => ExprKind::Match {
+ scrutinee: self.mirror_expr(discr),
+ arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
+ },
+ hir::ExprKind::Loop(ref body, ..) => {
+ let block_ty = self.typeck_results().node_type(body.hir_id);
+ let temp_lifetime = self
+ .rvalue_scopes
+ .temporary_scope(self.region_scope_tree, body.hir_id.local_id);
+ let block = self.mirror_block(body);
+ let body = self.thir.exprs.push(Expr {
+ ty: block_ty,
+ temp_lifetime,
+ span: block.span,
+ kind: ExprKind::Block { body: block },
+ });
+ ExprKind::Loop { body }
+ }
+ hir::ExprKind::Field(ref source, ..) => ExprKind::Field {
+ lhs: self.mirror_expr(source),
+ variant_index: VariantIdx::new(0),
+ name: Field::new(tcx.field_index(expr.hir_id, self.typeck_results)),
+ },
+ hir::ExprKind::Cast(ref source, ref cast_ty) => {
+ // Check for a user-given type annotation on this `cast`
+ let user_provided_types = self.typeck_results.user_provided_types();
+ let user_ty = user_provided_types.get(cast_ty.hir_id);
+
+ debug!(
+ "cast({:?}) has ty w/ hir_id {:?} and user provided ty {:?}",
+ expr, cast_ty.hir_id, user_ty,
+ );
+
+ let cast = self.mirror_expr_cast(*source, temp_lifetime, expr.span);
+
+ if let Some(user_ty) = user_ty {
+ // NOTE: Creating a new Expr and wrapping a Cast inside of it may be
+ // inefficient, revisit this when performance becomes an issue.
+ let cast_expr = self.thir.exprs.push(Expr {
+ temp_lifetime,
+ ty: expr_ty,
+ span: expr.span,
+ kind: cast,
+ });
+ debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty);
+
+ ExprKind::ValueTypeAscription { source: cast_expr, user_ty: Some(*user_ty) }
+ } else {
+ cast
+ }
+ }
+ hir::ExprKind::Type(ref source, ref ty) => {
+ let user_provided_types = self.typeck_results.user_provided_types();
+ let user_ty = user_provided_types.get(ty.hir_id).copied();
+ debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty);
+ let mirrored = self.mirror_expr(source);
+ if source.is_syntactic_place_expr() {
+ ExprKind::PlaceTypeAscription { source: mirrored, user_ty }
+ } else {
+ ExprKind::ValueTypeAscription { source: mirrored, user_ty }
+ }
+ }
+ hir::ExprKind::DropTemps(ref source) => {
+ ExprKind::Use { source: self.mirror_expr(source) }
+ }
+ hir::ExprKind::Box(ref value) => ExprKind::Box { value: self.mirror_expr(value) },
+ hir::ExprKind::Array(ref fields) => {
+ ExprKind::Array { fields: self.mirror_exprs(fields) }
+ }
+ hir::ExprKind::Tup(ref fields) => ExprKind::Tuple { fields: self.mirror_exprs(fields) },
+
+ hir::ExprKind::Yield(ref v, _) => ExprKind::Yield { value: self.mirror_expr(v) },
+ hir::ExprKind::Err => unreachable!(),
+ };
+
+ Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind }
+ }
+
+ fn user_substs_applied_to_res(
+ &mut self,
+ hir_id: hir::HirId,
+ res: Res,
+ ) -> Option<ty::CanonicalUserType<'tcx>> {
+ debug!("user_substs_applied_to_res: res={:?}", res);
+ let user_provided_type = match res {
+ // A reference to something callable -- e.g., a fn, method, or
+ // a tuple-struct or tuple-variant. This has the type of a
+ // `Fn` but with the user-given substitutions.
+ Res::Def(DefKind::Fn, _)
+ | Res::Def(DefKind::AssocFn, _)
+ | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
+ | Res::Def(DefKind::Const, _)
+ | Res::Def(DefKind::AssocConst, _) => {
+ self.typeck_results().user_provided_types().get(hir_id).copied()
+ }
+
+ // A unit struct/variant which is used as a value (e.g.,
+ // `None`). This has the type of the enum/struct that defines
+ // this variant -- but with the substitutions given by the
+ // user.
+ Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => {
+ self.user_substs_applied_to_ty_of_hir_id(hir_id)
+ }
+
+ // `Self` is used in expression as a tuple struct constructor or a unit struct constructor
+ Res::SelfCtor(_) => self.user_substs_applied_to_ty_of_hir_id(hir_id),
+
+ _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id),
+ };
+ debug!("user_substs_applied_to_res: user_provided_type={:?}", user_provided_type);
+ user_provided_type
+ }
+
+ fn method_callee(
+ &mut self,
+ expr: &hir::Expr<'_>,
+ span: Span,
+ overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
+ ) -> Expr<'tcx> {
+ let temp_lifetime =
+ self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
+ let (def_id, substs, user_ty) = match overloaded_callee {
+ Some((def_id, substs)) => (def_id, substs, None),
+ None => {
+ let (kind, def_id) =
+ self.typeck_results().type_dependent_def(expr.hir_id).unwrap_or_else(|| {
+ span_bug!(expr.span, "no type-dependent def for method callee")
+ });
+ let user_ty = self.user_substs_applied_to_res(expr.hir_id, Res::Def(kind, def_id));
+ debug!("method_callee: user_ty={:?}", user_ty);
+ (def_id, self.typeck_results().node_substs(expr.hir_id), user_ty)
+ }
+ };
+ let ty = self.tcx().mk_fn_def(def_id, substs);
+ Expr { temp_lifetime, ty, span, kind: ExprKind::ZstLiteral { user_ty } }
+ }
+
+ fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId {
+ let arm = Arm {
+ pattern: self.pattern_from_hir(&arm.pat),
+ guard: arm.guard.as_ref().map(|g| match g {
+ hir::Guard::If(ref e) => Guard::If(self.mirror_expr(e)),
+ hir::Guard::IfLet(ref l) => {
+ Guard::IfLet(self.pattern_from_hir(l.pat), self.mirror_expr(l.init))
+ }
+ }),
+ body: self.mirror_expr(arm.body),
+ lint_level: LintLevel::Explicit(arm.hir_id),
+ scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node },
+ span: arm.span,
+ };
+ self.thir.arms.push(arm)
+ }
+
+ fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKind<'tcx> {
+ let substs = self.typeck_results().node_substs(expr.hir_id);
+ match res {
+ // A regular function, constructor function or a constant.
+ Res::Def(DefKind::Fn, _)
+ | Res::Def(DefKind::AssocFn, _)
+ | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
+ | Res::SelfCtor(_) => {
+ let user_ty = self.user_substs_applied_to_res(expr.hir_id, res);
+ ExprKind::ZstLiteral { user_ty }
+ }
+
+ Res::Def(DefKind::ConstParam, def_id) => {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ let item_id = self.tcx.hir().get_parent_node(hir_id);
+ let item_def_id = self.tcx.hir().local_def_id(item_id);
+ let generics = self.tcx.generics_of(item_def_id);
+ let index = generics.param_def_id_to_index[&def_id];
+ let name = self.tcx.hir().name(hir_id);
+ let param = ty::ParamConst::new(index, name);
+
+ ExprKind::ConstParam { param, def_id }
+ }
+
+ Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => {
+ let user_ty = self.user_substs_applied_to_res(expr.hir_id, res);
+ ExprKind::NamedConst { def_id, substs, user_ty: user_ty }
+ }
+
+ Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => {
+ let user_provided_types = self.typeck_results.user_provided_types();
+ let user_provided_type = user_provided_types.get(expr.hir_id).copied();
+ debug!("convert_path_expr: user_provided_type={:?}", user_provided_type);
+ let ty = self.typeck_results().node_type(expr.hir_id);
+ match ty.kind() {
+ // A unit struct/variant which is used as a value.
+ // We return a completely different ExprKind here to account for this special case.
+ ty::Adt(adt_def, substs) => ExprKind::Adt(Box::new(Adt {
+ adt_def: *adt_def,
+ variant_index: adt_def.variant_index_with_ctor_id(def_id),
+ substs,
+ user_ty: user_provided_type,
+ fields: Box::new([]),
+ base: None,
+ })),
+ _ => bug!("unexpected ty: {:?}", ty),
+ }
+ }
+
+ // We encode uses of statics as a `*&STATIC` where the `&STATIC` part is
+ // a constant reference (or constant raw pointer for `static mut`) in MIR
+ Res::Def(DefKind::Static(_), id) => {
+ let ty = self.tcx.static_ptr_ty(id);
+ let temp_lifetime = self
+ .rvalue_scopes
+ .temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
+ let kind = if self.tcx.is_thread_local_static(id) {
+ ExprKind::ThreadLocalRef(id)
+ } else {
+ let alloc_id = self.tcx.create_static_alloc(id);
+ ExprKind::StaticRef { alloc_id, ty, def_id: id }
+ };
+ ExprKind::Deref {
+ arg: self.thir.exprs.push(Expr { ty, temp_lifetime, span: expr.span, kind }),
+ }
+ }
+
+ Res::Local(var_hir_id) => self.convert_var(var_hir_id),
+
+ _ => span_bug!(expr.span, "res `{:?}` not yet implemented", res),
+ }
+ }
+
+ fn convert_var(&mut self, var_hir_id: hir::HirId) -> ExprKind<'tcx> {
+ // We want upvars here not captures.
+ // Captures will be handled in MIR.
+ let is_upvar = self
+ .tcx
+ .upvars_mentioned(self.body_owner)
+ .map_or(false, |upvars| upvars.contains_key(&var_hir_id));
+
+ debug!(
+ "convert_var({:?}): is_upvar={}, body_owner={:?}",
+ var_hir_id, is_upvar, self.body_owner
+ );
+
+ if is_upvar {
+ ExprKind::UpvarRef {
+ closure_def_id: self.body_owner,
+ var_hir_id: LocalVarId(var_hir_id),
+ }
+ } else {
+ ExprKind::VarRef { id: LocalVarId(var_hir_id) }
+ }
+ }
+
+ fn overloaded_operator(
+ &mut self,
+ expr: &'tcx hir::Expr<'tcx>,
+ args: Box<[ExprId]>,
+ ) -> ExprKind<'tcx> {
+ let fun = self.method_callee(expr, expr.span, None);
+ let fun = self.thir.exprs.push(fun);
+ ExprKind::Call {
+ ty: self.thir[fun].ty,
+ fun,
+ args,
+ from_hir_call: false,
+ fn_span: expr.span,
+ }
+ }
+
+ fn overloaded_place(
+ &mut self,
+ expr: &'tcx hir::Expr<'tcx>,
+ place_ty: Ty<'tcx>,
+ overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
+ args: Box<[ExprId]>,
+ span: Span,
+ ) -> ExprKind<'tcx> {
+ // For an overloaded *x or x[y] expression of type T, the method
+ // call returns an &T and we must add the deref so that the types
+ // line up (this is because `*x` and `x[y]` represent places):
+
+ // Reconstruct the output assuming it's a reference with the
+ // same region and mutability as the receiver. This holds for
+ // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
+ let ty::Ref(region, _, mutbl) = *self.thir[args[0]].ty.kind() else {
+ span_bug!(span, "overloaded_place: receiver is not a reference");
+ };
+ let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
+
+ // construct the complete expression `foo()` for the overloaded call,
+ // which will yield the &T type
+ let temp_lifetime =
+ self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
+ let fun = self.method_callee(expr, span, overloaded_callee);
+ let fun = self.thir.exprs.push(fun);
+ let fun_ty = self.thir[fun].ty;
+ let ref_expr = self.thir.exprs.push(Expr {
+ temp_lifetime,
+ ty: ref_ty,
+ span,
+ kind: ExprKind::Call { ty: fun_ty, fun, args, from_hir_call: false, fn_span: span },
+ });
+
+ // construct and return a deref wrapper `*foo()`
+ ExprKind::Deref { arg: ref_expr }
+ }
+
+ fn convert_captured_hir_place(
+ &mut self,
+ closure_expr: &'tcx hir::Expr<'tcx>,
+ place: HirPlace<'tcx>,
+ ) -> Expr<'tcx> {
+ let temp_lifetime = self
+ .rvalue_scopes
+ .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
+ let var_ty = place.base_ty;
+
+ // The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path
+ // as it's seen for use within the closure and not at the time of closure creation.
+ //
+ // That is we see expect to see it start from a captured upvar and not something that is local
+ // to the closure's parent.
+ let var_hir_id = match place.base {
+ HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
+ base => bug!("Expected an upvar, found {:?}", base),
+ };
+
+ let mut captured_place_expr = Expr {
+ temp_lifetime,
+ ty: var_ty,
+ span: closure_expr.span,
+ kind: self.convert_var(var_hir_id),
+ };
+
+ for proj in place.projections.iter() {
+ let kind = match proj.kind {
+ HirProjectionKind::Deref => {
+ ExprKind::Deref { arg: self.thir.exprs.push(captured_place_expr) }
+ }
+ HirProjectionKind::Field(field, variant_index) => ExprKind::Field {
+ lhs: self.thir.exprs.push(captured_place_expr),
+ variant_index,
+ name: Field::new(field as usize),
+ },
+ HirProjectionKind::Index | HirProjectionKind::Subslice => {
+ // We don't capture these projections, so we can ignore them here
+ continue;
+ }
+ };
+
+ captured_place_expr =
+ Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind };
+ }
+
+ captured_place_expr
+ }
+
+ fn capture_upvar(
+ &mut self,
+ closure_expr: &'tcx hir::Expr<'tcx>,
+ captured_place: &'tcx ty::CapturedPlace<'tcx>,
+ upvar_ty: Ty<'tcx>,
+ ) -> Expr<'tcx> {
+ let upvar_capture = captured_place.info.capture_kind;
+ let captured_place_expr =
+ self.convert_captured_hir_place(closure_expr, captured_place.place.clone());
+ let temp_lifetime = self
+ .rvalue_scopes
+ .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
+
+ match upvar_capture {
+ ty::UpvarCapture::ByValue => captured_place_expr,
+ ty::UpvarCapture::ByRef(upvar_borrow) => {
+ let borrow_kind = match upvar_borrow {
+ ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
+ ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique,
+ ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false },
+ };
+ Expr {
+ temp_lifetime,
+ ty: upvar_ty,
+ span: closure_expr.span,
+ kind: ExprKind::Borrow {
+ borrow_kind,
+ arg: self.thir.exprs.push(captured_place_expr),
+ },
+ }
+ }
+ }
+ }
+
+ /// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExpr.
+ fn field_refs(&mut self, fields: &'tcx [hir::ExprField<'tcx>]) -> Box<[FieldExpr]> {
+ fields
+ .iter()
+ .map(|field| FieldExpr {
+ name: Field::new(self.tcx.field_index(field.hir_id, self.typeck_results)),
+ expr: self.mirror_expr(field.expr),
+ })
+ .collect()
+ }
+}
+
+trait ToBorrowKind {
+ fn to_borrow_kind(&self) -> BorrowKind;
+}
+
+impl ToBorrowKind for AutoBorrowMutability {
+ fn to_borrow_kind(&self) -> BorrowKind {
+ use rustc_middle::ty::adjustment::AllowTwoPhase;
+ match *self {
+ AutoBorrowMutability::Mut { allow_two_phase_borrow } => BorrowKind::Mut {
+ allow_two_phase_borrow: match allow_two_phase_borrow {
+ AllowTwoPhase::Yes => true,
+ AllowTwoPhase::No => false,
+ },
+ },
+ AutoBorrowMutability::Not => BorrowKind::Shared,
+ }
+ }
+}
+
+impl ToBorrowKind for hir::Mutability {
+ fn to_borrow_kind(&self) -> BorrowKind {
+ match *self {
+ hir::Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+ hir::Mutability::Not => BorrowKind::Shared,
+ }
+ }
+}
+
+fn bin_op(op: hir::BinOpKind) -> BinOp {
+ match op {
+ hir::BinOpKind::Add => BinOp::Add,
+ hir::BinOpKind::Sub => BinOp::Sub,
+ hir::BinOpKind::Mul => BinOp::Mul,
+ hir::BinOpKind::Div => BinOp::Div,
+ hir::BinOpKind::Rem => BinOp::Rem,
+ hir::BinOpKind::BitXor => BinOp::BitXor,
+ hir::BinOpKind::BitAnd => BinOp::BitAnd,
+ hir::BinOpKind::BitOr => BinOp::BitOr,
+ hir::BinOpKind::Shl => BinOp::Shl,
+ hir::BinOpKind::Shr => BinOp::Shr,
+ hir::BinOpKind::Eq => BinOp::Eq,
+ hir::BinOpKind::Lt => BinOp::Lt,
+ hir::BinOpKind::Le => BinOp::Le,
+ hir::BinOpKind::Ne => BinOp::Ne,
+ hir::BinOpKind::Ge => BinOp::Ge,
+ hir::BinOpKind::Gt => BinOp::Gt,
+ _ => bug!("no equivalent for ast binop {:?}", op),
+ }
+}
diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
new file mode 100644
index 000000000..f7351a4ca
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -0,0 +1,101 @@
+//! This module contains the functionality to convert from the wacky tcx data
+//! structures into the THIR. The `builder` is generally ignorant of the tcx,
+//! etc., and instead goes through the `Cx` for most of its work.
+
+use crate::thir::pattern::pat_from_hir;
+use crate::thir::util::UserAnnotatedTyHelpers;
+
+use rustc_data_structures::steal::Steal;
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::HirId;
+use rustc_hir::Node;
+use rustc_middle::middle::region;
+use rustc_middle::thir::*;
+use rustc_middle::ty::{self, RvalueScopes, TyCtxt};
+use rustc_span::Span;
+
+pub(crate) fn thir_body<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ owner_def: ty::WithOptConstParam<LocalDefId>,
+) -> Result<(&'tcx Steal<Thir<'tcx>>, ExprId), ErrorGuaranteed> {
+ let hir = tcx.hir();
+ let body = hir.body(hir.body_owned_by(owner_def.did));
+ let mut cx = Cx::new(tcx, owner_def);
+ if let Some(reported) = cx.typeck_results.tainted_by_errors {
+ return Err(reported);
+ }
+ let expr = cx.mirror_expr(&body.value);
+ Ok((tcx.alloc_steal_thir(cx.thir), expr))
+}
+
+pub(crate) fn thir_tree<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ owner_def: ty::WithOptConstParam<LocalDefId>,
+) -> String {
+ match thir_body(tcx, owner_def) {
+ Ok((thir, _)) => format!("{:#?}", thir.steal()),
+ Err(_) => "error".into(),
+ }
+}
+
+struct Cx<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ thir: Thir<'tcx>,
+
+ pub(crate) param_env: ty::ParamEnv<'tcx>,
+
+ pub(crate) region_scope_tree: &'tcx region::ScopeTree,
+ pub(crate) typeck_results: &'tcx ty::TypeckResults<'tcx>,
+ pub(crate) rvalue_scopes: &'tcx RvalueScopes,
+
+ /// When applying adjustments to the expression
+ /// with the given `HirId`, use the given `Span`,
+ /// instead of the usual span. This is used to
+ /// assign the span of an overall method call
+ /// (e.g. `my_val.foo()`) to the adjustment expressions
+ /// for the receiver.
+ adjustment_span: Option<(HirId, Span)>,
+
+ /// The `DefId` of the owner of this body.
+ body_owner: DefId,
+}
+
+impl<'tcx> Cx<'tcx> {
+ fn new(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) -> Cx<'tcx> {
+ let typeck_results = tcx.typeck_opt_const_arg(def);
+ Cx {
+ tcx,
+ thir: Thir::new(),
+ param_env: tcx.param_env(def.did),
+ region_scope_tree: tcx.region_scope_tree(def.did),
+ typeck_results,
+ rvalue_scopes: &typeck_results.rvalue_scopes,
+ body_owner: def.did.to_def_id(),
+ adjustment_span: None,
+ }
+ }
+
+ #[tracing::instrument(level = "debug", skip(self))]
+ pub(crate) fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> {
+ let p = match self.tcx.hir().get(p.hir_id) {
+ Node::Pat(p) => p,
+ node => bug!("pattern became {:?}", node),
+ };
+ pat_from_hir(self.tcx, self.param_env, self.typeck_results(), p)
+ }
+}
+
+impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn typeck_results(&self) -> &ty::TypeckResults<'tcx> {
+ self.typeck_results
+ }
+}
+
+mod block;
+mod expr;
diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs
new file mode 100644
index 000000000..e0e6ac266
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/mod.rs
@@ -0,0 +1,13 @@
+//! The MIR is built from some typed high-level IR
+//! (THIR). This section defines the THIR along with a trait for
+//! accessing it. The intention is to allow MIR construction to be
+//! unit-tested and separated from the Rust source and compiler data
+//! structures.
+
+pub(crate) mod constant;
+
+pub(crate) mod cx;
+
+pub(crate) mod pattern;
+
+mod util;
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
new file mode 100644
index 000000000..063c07647
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -0,0 +1,1162 @@
+use super::deconstruct_pat::{Constructor, DeconstructedPat};
+use super::usefulness::{
+ compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
+};
+use super::{PatCtxt, PatternError};
+
+use rustc_arena::TypedArena;
+use rustc_ast::Mutability;
+use rustc_errors::{
+ error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
+ ErrorGuaranteed, MultiSpan,
+};
+use rustc_hir as hir;
+use rustc_hir::def::*;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{HirId, Pat};
+use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+use rustc_session::lint::builtin::{
+ BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
+};
+use rustc_session::Session;
+use rustc_span::source_map::Spanned;
+use rustc_span::{BytePos, Span};
+
+pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
+ let body_id = match def_id.as_local() {
+ None => return,
+ Some(def_id) => tcx.hir().body_owned_by(def_id),
+ };
+
+ let pattern_arena = TypedArena::default();
+ let mut visitor = MatchVisitor {
+ tcx,
+ typeck_results: tcx.typeck_body(body_id),
+ param_env: tcx.param_env(def_id),
+ pattern_arena: &pattern_arena,
+ };
+ visitor.visit_body(tcx.hir().body(body_id));
+}
+
+fn create_e0004(
+ sess: &Session,
+ sp: Span,
+ error_message: String,
+) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+ struct_span_err!(sess, sp, E0004, "{}", &error_message)
+}
+
+#[derive(PartialEq)]
+enum RefutableFlag {
+ Irrefutable,
+ Refutable,
+}
+use RefutableFlag::*;
+
+struct MatchVisitor<'a, 'p, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
+}
+
+impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
+ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
+ intravisit::walk_expr(self, ex);
+ match &ex.kind {
+ hir::ExprKind::Match(scrut, arms, source) => {
+ self.check_match(scrut, arms, *source, ex.span)
+ }
+ hir::ExprKind::Let(hir::Let { pat, init, span, .. }) => {
+ self.check_let(pat, init, *span)
+ }
+ _ => {}
+ }
+ }
+
+ fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
+ intravisit::walk_local(self, loc);
+ let els = loc.els;
+ if let Some(init) = loc.init && els.is_some() {
+ self.check_let(&loc.pat, init, loc.span);
+ }
+
+ let (msg, sp) = match loc.source {
+ hir::LocalSource::Normal => ("local binding", Some(loc.span)),
+ hir::LocalSource::AsyncFn => ("async fn binding", None),
+ hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
+ hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
+ };
+ if els.is_none() {
+ self.check_irrefutable(&loc.pat, msg, sp);
+ }
+ }
+
+ fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
+ intravisit::walk_param(self, param);
+ self.check_irrefutable(&param.pat, "function argument", None);
+ }
+}
+
+impl PatCtxt<'_, '_> {
+ fn report_inlining_errors(&self) {
+ for error in &self.errors {
+ match *error {
+ PatternError::StaticInPattern(span) => {
+ self.span_e0158(span, "statics cannot be referenced in patterns")
+ }
+ PatternError::AssocConstInPattern(span) => {
+ self.span_e0158(span, "associated consts cannot be referenced in patterns")
+ }
+ PatternError::ConstParamInPattern(span) => {
+ self.span_e0158(span, "const parameters cannot be referenced in patterns")
+ }
+ PatternError::NonConstPath(span) => {
+ rustc_middle::mir::interpret::struct_error(
+ self.tcx.at(span),
+ "runtime values cannot be referenced in patterns",
+ )
+ .emit();
+ }
+ }
+ }
+ }
+
+ fn span_e0158(&self, span: Span, text: &str) {
+ struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit();
+ }
+}
+
+impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
+ fn check_patterns(&self, pat: &Pat<'_>, rf: RefutableFlag) {
+ pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
+ check_for_bindings_named_same_as_variants(self, pat, rf);
+ }
+
+ fn lower_pattern(
+ &self,
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ pat: &'tcx hir::Pat<'tcx>,
+ have_errors: &mut bool,
+ ) -> &'p DeconstructedPat<'p, 'tcx> {
+ let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.typeck_results);
+ patcx.include_lint_checks();
+ let pattern = patcx.lower_pattern(pat);
+ let pattern: &_ = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern));
+ if !patcx.errors.is_empty() {
+ *have_errors = true;
+ patcx.report_inlining_errors();
+ }
+ pattern
+ }
+
+ fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> {
+ MatchCheckCtxt {
+ tcx: self.tcx,
+ param_env: self.param_env,
+ module: self.tcx.parent_module(hir_id).to_def_id(),
+ pattern_arena: &self.pattern_arena,
+ }
+ }
+
+ fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, scrutinee: &hir::Expr<'_>, span: Span) {
+ self.check_patterns(pat, Refutable);
+ let mut cx = self.new_cx(scrutinee.hir_id);
+ let tpat = self.lower_pattern(&mut cx, pat, &mut false);
+ self.check_let_reachability(&mut cx, pat.hir_id, tpat, span);
+ }
+
+ fn check_match(
+ &mut self,
+ scrut: &hir::Expr<'_>,
+ hir_arms: &'tcx [hir::Arm<'tcx>],
+ source: hir::MatchSource,
+ expr_span: Span,
+ ) {
+ let mut cx = self.new_cx(scrut.hir_id);
+
+ for arm in hir_arms {
+ // Check the arm for some things unrelated to exhaustiveness.
+ self.check_patterns(&arm.pat, Refutable);
+ if let Some(hir::Guard::IfLet(ref let_expr)) = arm.guard {
+ self.check_patterns(let_expr.pat, Refutable);
+ let tpat = self.lower_pattern(&mut cx, let_expr.pat, &mut false);
+ self.check_let_reachability(&mut cx, let_expr.pat.hir_id, tpat, tpat.span());
+ }
+ }
+
+ let mut have_errors = false;
+
+ let arms: Vec<_> = hir_arms
+ .iter()
+ .map(|hir::Arm { pat, guard, .. }| MatchArm {
+ pat: self.lower_pattern(&mut cx, pat, &mut have_errors),
+ hir_id: pat.hir_id,
+ has_guard: guard.is_some(),
+ })
+ .collect();
+
+ // Bail out early if lowering failed.
+ if have_errors {
+ return;
+ }
+
+ let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
+ let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
+
+ match source {
+ // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
+ // when the iterator is an uninhabited type. unreachable_code will trigger instead.
+ hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
+ hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
+ report_arm_reachability(&cx, &report)
+ }
+ // Unreachable patterns in try and await expressions occur when one of
+ // the arms are an uninhabited type. Which is OK.
+ hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
+ }
+
+ // Check if the match is exhaustive.
+ let witnesses = report.non_exhaustiveness_witnesses;
+ if !witnesses.is_empty() {
+ if source == hir::MatchSource::ForLoopDesugar && hir_arms.len() == 2 {
+ // the for loop pattern is not irrefutable
+ let pat = hir_arms[1].pat.for_loop_some().unwrap();
+ self.check_irrefutable(pat, "`for` loop binding", None);
+ } else {
+ non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, hir_arms, expr_span);
+ }
+ }
+ }
+
+ fn check_let_reachability(
+ &mut self,
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ pat_id: HirId,
+ pat: &'p DeconstructedPat<'p, 'tcx>,
+ span: Span,
+ ) {
+ if self.check_let_chain(cx, pat_id) {
+ return;
+ }
+
+ if is_let_irrefutable(cx, pat_id, pat) {
+ irrefutable_let_pattern(cx.tcx, pat_id, span);
+ }
+ }
+
+ fn check_let_chain(&mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId) -> bool {
+ let hir = self.tcx.hir();
+ let parent = hir.get_parent_node(pat_id);
+
+ // First, figure out if the given pattern is part of a let chain,
+ // and if so, obtain the top node of the chain.
+ let mut top = parent;
+ let mut part_of_chain = false;
+ loop {
+ let new_top = hir.get_parent_node(top);
+ if let hir::Node::Expr(
+ hir::Expr {
+ kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, lhs, rhs),
+ ..
+ },
+ ..,
+ ) = hir.get(new_top)
+ {
+ // If this isn't the first iteration, we need to check
+ // if there is a let expr before us in the chain, so
+ // that we avoid doubly checking the let chain.
+
+ // The way a chain of &&s is encoded is ((let ... && let ...) && let ...) && let ...
+ // as && is left-to-right associative. Thus, we need to check rhs.
+ if part_of_chain && matches!(rhs.kind, hir::ExprKind::Let(..)) {
+ return true;
+ }
+ // If there is a let at the lhs, and we provide the rhs, we don't do any checking either.
+ if !part_of_chain && matches!(lhs.kind, hir::ExprKind::Let(..)) && rhs.hir_id == top
+ {
+ return true;
+ }
+ } else {
+ // We've reached the top.
+ break;
+ }
+
+ // Since this function is called within a let context, it is reasonable to assume that any parent
+ // `&&` infers a let chain
+ part_of_chain = true;
+ top = new_top;
+ }
+ if !part_of_chain {
+ return false;
+ }
+
+ // Second, obtain the refutabilities of all exprs in the chain,
+ // and record chain members that aren't let exprs.
+ let mut chain_refutabilities = Vec::new();
+ let hir::Node::Expr(top_expr) = hir.get(top) else {
+ // We ensure right above that it's an Expr
+ unreachable!()
+ };
+ let mut cur_expr = top_expr;
+ loop {
+ let mut add = |expr: &hir::Expr<'tcx>| {
+ let refutability = match expr.kind {
+ hir::ExprKind::Let(hir::Let { pat, init, span, .. }) => {
+ let mut ncx = self.new_cx(init.hir_id);
+ let tpat = self.lower_pattern(&mut ncx, pat, &mut false);
+
+ let refutable = !is_let_irrefutable(&mut ncx, pat.hir_id, tpat);
+ Some((*span, refutable))
+ }
+ _ => None,
+ };
+ chain_refutabilities.push(refutability);
+ };
+ if let hir::Expr {
+ kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, lhs, rhs),
+ ..
+ } = cur_expr
+ {
+ add(rhs);
+ cur_expr = lhs;
+ } else {
+ add(cur_expr);
+ break;
+ }
+ }
+ chain_refutabilities.reverse();
+
+ // Third, emit the actual warnings.
+
+ if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, false)))) {
+ // The entire chain is made up of irrefutable `let` statements
+ let let_source = let_source_parent(self.tcx, top, None);
+ irrefutable_let_patterns(
+ cx.tcx,
+ top,
+ let_source,
+ chain_refutabilities.len(),
+ top_expr.span,
+ );
+ return true;
+ }
+ let lint_affix = |affix: &[Option<(Span, bool)>], kind, suggestion| {
+ let span_start = affix[0].unwrap().0;
+ let span_end = affix.last().unwrap().unwrap().0;
+ let span = span_start.to(span_end);
+ let cnt = affix.len();
+ cx.tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, top, span, |lint| {
+ let s = pluralize!(cnt);
+ let mut diag = lint.build(&format!("{kind} irrefutable pattern{s} in let chain"));
+ diag.note(&format!(
+ "{these} pattern{s} will always match",
+ these = pluralize!("this", cnt),
+ ));
+ diag.help(&format!(
+ "consider moving {} {suggestion}",
+ if cnt > 1 { "them" } else { "it" }
+ ));
+ diag.emit()
+ });
+ };
+ if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 {
+ // The chain has a non-zero prefix of irrefutable `let` statements.
+
+ // Check if the let source is while, for there is no alternative place to put a prefix,
+ // and we shouldn't lint.
+ let let_source = let_source_parent(self.tcx, top, None);
+ if !matches!(let_source, LetSource::WhileLet) {
+ // Emit the lint
+ let prefix = &chain_refutabilities[..until];
+ lint_affix(prefix, "leading", "outside of the construct");
+ }
+ }
+ if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) {
+ // The chain has a non-empty suffix of irrefutable `let` statements
+ let suffix = &chain_refutabilities[from + 1..];
+ lint_affix(suffix, "trailing", "into the body");
+ }
+ true
+ }
+
+ fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
+ let mut cx = self.new_cx(pat.hir_id);
+
+ let pattern = self.lower_pattern(&mut cx, pat, &mut false);
+ let pattern_ty = pattern.ty();
+ let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
+ let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
+
+ // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
+ // only care about exhaustiveness here.
+ let witnesses = report.non_exhaustiveness_witnesses;
+ if witnesses.is_empty() {
+ // The pattern is irrefutable.
+ self.check_patterns(pat, Irrefutable);
+ return;
+ }
+
+ let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
+
+ let mut bindings = vec![];
+
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ pat.span,
+ E0005,
+ "refutable pattern in {}: {} not covered",
+ origin,
+ joined_patterns
+ );
+ let suggest_if_let = match &pat.kind {
+ hir::PatKind::Path(hir::QPath::Resolved(None, path))
+ if path.segments.len() == 1 && path.segments[0].args.is_none() =>
+ {
+ const_not_var(&mut err, cx.tcx, pat, path);
+ false
+ }
+ _ => {
+ pat.walk(&mut |pat: &hir::Pat<'_>| {
+ match pat.kind {
+ hir::PatKind::Binding(_, _, ident, _) => {
+ bindings.push(ident);
+ }
+ _ => {}
+ }
+ true
+ });
+
+ err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns));
+ true
+ }
+ };
+
+ if let (Some(span), true) = (sp, suggest_if_let) {
+ err.note(
+ "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
+ an `enum` with only one variant",
+ );
+ if self.tcx.sess.source_map().is_span_accessible(span) {
+ let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1));
+ let start_span = span.shrink_to_lo();
+ let end_span = semi_span.shrink_to_lo();
+ err.multipart_suggestion(
+ &format!(
+ "you might want to use `if let` to ignore the variant{} that {} matched",
+ pluralize!(witnesses.len()),
+ match witnesses.len() {
+ 1 => "isn't",
+ _ => "aren't",
+ },
+ ),
+ vec![
+ match &bindings[..] {
+ [] => (start_span, "if ".to_string()),
+ [binding] => (start_span, format!("let {} = if ", binding)),
+ bindings => (
+ start_span,
+ format!(
+ "let ({}) = if ",
+ bindings
+ .iter()
+ .map(|ident| ident.to_string())
+ .collect::<Vec<_>>()
+ .join(", ")
+ ),
+ ),
+ },
+ match &bindings[..] {
+ [] => (semi_span, " { todo!() }".to_string()),
+ [binding] => {
+ (end_span, format!(" {{ {} }} else {{ todo!() }}", binding))
+ }
+ bindings => (
+ end_span,
+ format!(
+ " {{ ({}) }} else {{ todo!() }}",
+ bindings
+ .iter()
+ .map(|ident| ident.to_string())
+ .collect::<Vec<_>>()
+ .join(", ")
+ ),
+ ),
+ },
+ ],
+ Applicability::HasPlaceholders,
+ );
+ if !bindings.is_empty() && cx.tcx.sess.is_nightly_build() {
+ err.span_suggestion_verbose(
+ semi_span.shrink_to_lo(),
+ &format!(
+ "alternatively, on nightly, you might want to use \
+ `#![feature(let_else)]` to handle the variant{} that {} matched",
+ pluralize!(witnesses.len()),
+ match witnesses.len() {
+ 1 => "isn't",
+ _ => "aren't",
+ },
+ ),
+ " else { todo!() }".to_string(),
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ err.note(
+ "for more information, visit \
+ https://doc.rust-lang.org/book/ch18-02-refutability.html",
+ );
+ }
+
+ adt_defined_here(&cx, &mut err, pattern_ty, &witnesses);
+ err.note(&format!("the matched value is of type `{}`", pattern_ty));
+ err.emit();
+ }
+}
+
+/// A path pattern was interpreted as a constant, not a new variable.
+/// This caused an irrefutable match failure in e.g. `let`.
+fn const_not_var(err: &mut Diagnostic, tcx: TyCtxt<'_>, pat: &Pat<'_>, path: &hir::Path<'_>) {
+ let descr = path.res.descr();
+ err.span_label(
+ pat.span,
+ format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
+ );
+
+ err.span_suggestion(
+ pat.span,
+ "introduce a variable instead",
+ format!("{}_var", path.segments[0].ident).to_lowercase(),
+ // Cannot use `MachineApplicable` as it's not really *always* correct
+ // because there may be such an identifier in scope or the user maybe
+ // really wanted to match against the constant. This is quite unlikely however.
+ Applicability::MaybeIncorrect,
+ );
+
+ if let Some(span) = tcx.hir().res_span(path.res) {
+ err.span_label(span, format!("{} defined here", descr));
+ }
+}
+
+fn check_for_bindings_named_same_as_variants(
+ cx: &MatchVisitor<'_, '_, '_>,
+ pat: &Pat<'_>,
+ rf: RefutableFlag,
+) {
+ pat.walk_always(|p| {
+ if let hir::PatKind::Binding(_, _, ident, None) = p.kind
+ && let Some(ty::BindByValue(hir::Mutability::Not)) =
+ cx.typeck_results.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span)
+ && let pat_ty = cx.typeck_results.pat_ty(p).peel_refs()
+ && let ty::Adt(edef, _) = pat_ty.kind()
+ && edef.is_enum()
+ && edef.variants().iter().any(|variant| {
+ variant.ident(cx.tcx) == ident && variant.ctor_kind == CtorKind::Const
+ })
+ {
+ let variant_count = edef.variants().len();
+ cx.tcx.struct_span_lint_hir(
+ BINDINGS_WITH_VARIANT_NAME,
+ p.hir_id,
+ p.span,
+ |lint| {
+ let ty_path = cx.tcx.def_path_str(edef.did());
+ let mut err = lint.build(&format!(
+ "pattern binding `{}` is named the same as one \
+ of the variants of the type `{}`",
+ ident, ty_path
+ ));
+ err.code(error_code!(E0170));
+ // If this is an irrefutable pattern, and there's > 1 variant,
+ // then we can't actually match on this. Applying the below
+ // suggestion would produce code that breaks on `check_irrefutable`.
+ if rf == Refutable || variant_count == 1 {
+ err.span_suggestion(
+ p.span,
+ "to match on the variant, qualify the path",
+ format!("{}::{}", ty_path, ident),
+ Applicability::MachineApplicable,
+ );
+ }
+ err.emit();
+ },
+ )
+ }
+ });
+}
+
+/// Checks for common cases of "catchall" patterns that may not be intended as such.
+fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
+ use Constructor::*;
+ match pat.ctor() {
+ Wildcard => true,
+ Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
+ _ => false,
+ }
+}
+
+fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
+ tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| {
+ let mut err = lint.build("unreachable pattern");
+ if let Some(catchall) = catchall {
+ // We had a catchall pattern, hint at that.
+ err.span_label(span, "unreachable pattern");
+ err.span_label(catchall, "matches any value");
+ }
+ err.emit();
+ });
+}
+
+fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
+ let source = let_source(tcx, id);
+ irrefutable_let_patterns(tcx, id, source, 1, span);
+}
+
+fn irrefutable_let_patterns(
+ tcx: TyCtxt<'_>,
+ id: HirId,
+ source: LetSource,
+ count: usize,
+ span: Span,
+) {
+ macro_rules! emit_diag {
+ (
+ $lint:expr,
+ $source_name:expr,
+ $note_sufix:expr,
+ $help_sufix:expr
+ ) => {{
+ let s = pluralize!(count);
+ let these = pluralize!("this", count);
+ let mut diag = $lint.build(&format!("irrefutable {} pattern{s}", $source_name));
+ diag.note(&format!("{these} pattern{s} will always match, so the {}", $note_sufix));
+ diag.help(concat!("consider ", $help_sufix));
+ diag.emit()
+ }};
+ }
+
+ let span = match source {
+ LetSource::LetElse(span) => span,
+ _ => span,
+ };
+ tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
+ LetSource::GenericLet => {
+ emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
+ }
+ LetSource::IfLet => {
+ emit_diag!(
+ lint,
+ "`if let`",
+ "`if let` is useless",
+ "replacing the `if let` with a `let`"
+ );
+ }
+ LetSource::IfLetGuard => {
+ emit_diag!(
+ lint,
+ "`if let` guard",
+ "guard is useless",
+ "removing the guard and adding a `let` inside the match arm"
+ );
+ }
+ LetSource::LetElse(..) => {
+ emit_diag!(
+ lint,
+ "`let...else`",
+ "`else` clause is useless",
+ "removing the `else` clause"
+ );
+ }
+ LetSource::WhileLet => {
+ emit_diag!(
+ lint,
+ "`while let`",
+ "loop will never exit",
+ "instead using a `loop { ... }` with a `let` inside it"
+ );
+ }
+ });
+}
+
+fn is_let_irrefutable<'p, 'tcx>(
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ pat_id: HirId,
+ pat: &'p DeconstructedPat<'p, 'tcx>,
+) -> bool {
+ let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
+ let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
+
+ // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
+ // This also reports unreachable sub-patterns though, so we can't just replace it with an
+ // `is_uninhabited` check.
+ report_arm_reachability(&cx, &report);
+
+ // If the list of witnesses is empty, the match is exhaustive,
+ // i.e. the `if let` pattern is irrefutable.
+ report.non_exhaustiveness_witnesses.is_empty()
+}
+
+/// Report unreachable arms, if any.
+fn report_arm_reachability<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ report: &UsefulnessReport<'p, 'tcx>,
+) {
+ use Reachability::*;
+ let mut catchall = None;
+ for (arm, is_useful) in report.arm_usefulness.iter() {
+ match is_useful {
+ Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
+ Reachable(unreachables) if unreachables.is_empty() => {}
+ // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
+ Reachable(unreachables) => {
+ let mut unreachables = unreachables.clone();
+ // Emit lints in the order in which they occur in the file.
+ unreachables.sort_unstable();
+ for span in unreachables {
+ unreachable_pattern(cx.tcx, span, arm.hir_id, None);
+ }
+ }
+ }
+ if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
+ catchall = Some(arm.pat.span());
+ }
+ }
+}
+
+/// Report that a match is not exhaustive.
+fn non_exhaustive_match<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ scrut_ty: Ty<'tcx>,
+ sp: Span,
+ witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
+ arms: &[hir::Arm<'tcx>],
+ expr_span: Span,
+) {
+ let is_empty_match = arms.is_empty();
+ let non_empty_enum = match scrut_ty.kind() {
+ ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
+ _ => false,
+ };
+ // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
+ // informative.
+ let mut err;
+ let pattern;
+ let mut patterns_len = 0;
+ if is_empty_match && !non_empty_enum {
+ err = create_e0004(
+ cx.tcx.sess,
+ sp,
+ format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
+ );
+ pattern = "_".to_string();
+ } else {
+ let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
+ err = create_e0004(
+ cx.tcx.sess,
+ sp,
+ format!("non-exhaustive patterns: {} not covered", joined_patterns),
+ );
+ err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
+ patterns_len = witnesses.len();
+ pattern = if witnesses.len() < 4 {
+ witnesses
+ .iter()
+ .map(|witness| witness.to_pat(cx).to_string())
+ .collect::<Vec<String>>()
+ .join(" | ")
+ } else {
+ "_".to_string()
+ };
+ };
+
+ let is_variant_list_non_exhaustive = match scrut_ty.kind() {
+ ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => true,
+ _ => false,
+ };
+
+ adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
+ err.note(&format!(
+ "the matched value is of type `{}`{}",
+ scrut_ty,
+ if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
+ ));
+ if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
+ && !is_empty_match
+ && witnesses.len() == 1
+ && matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
+ {
+ err.note(&format!(
+ "`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
+ exhaustively",
+ scrut_ty,
+ ));
+ if cx.tcx.sess.is_nightly_build() {
+ err.help(&format!(
+ "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
+ enable precise `{}` matching",
+ scrut_ty,
+ ));
+ }
+ }
+ if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
+ if cx.tcx.is_ty_uninhabited_from(cx.module, *sub_ty, cx.param_env) {
+ err.note("references are always considered inhabited");
+ }
+ }
+
+ let mut suggestion = None;
+ let sm = cx.tcx.sess.source_map();
+ match arms {
+ [] if sp.eq_ctxt(expr_span) => {
+ // Get the span for the empty match body `{}`.
+ let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
+ (format!("\n{}", snippet), " ")
+ } else {
+ (" ".to_string(), "")
+ };
+ suggestion = Some((
+ sp.shrink_to_hi().with_hi(expr_span.hi()),
+ format!(
+ " {{{indentation}{more}{pattern} => todo!(),{indentation}}}",
+ indentation = indentation,
+ more = more,
+ pattern = pattern,
+ ),
+ ));
+ }
+ [only] => {
+ let (pre_indentation, is_multiline) = if let Some(snippet) = sm.indentation_before(only.span)
+ && let Ok(with_trailing) = sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
+ && sm.is_multiline(with_trailing)
+ {
+ (format!("\n{}", snippet), true)
+ } else {
+ (" ".to_string(), false)
+ };
+ let comma = if matches!(only.body.kind, hir::ExprKind::Block(..))
+ && only.span.eq_ctxt(only.body.span)
+ && is_multiline
+ {
+ ""
+ } else {
+ ","
+ };
+ suggestion = Some((
+ only.span.shrink_to_hi(),
+ format!("{}{}{} => todo!()", comma, pre_indentation, pattern),
+ ));
+ }
+ [.., prev, last] if prev.span.eq_ctxt(last.span) => {
+ if let Ok(snippet) = sm.span_to_snippet(prev.span.between(last.span)) {
+ let comma = if matches!(last.body.kind, hir::ExprKind::Block(..))
+ && last.span.eq_ctxt(last.body.span)
+ {
+ ""
+ } else {
+ ","
+ };
+ suggestion = Some((
+ last.span.shrink_to_hi(),
+ format!(
+ "{}{}{} => todo!()",
+ comma,
+ snippet.strip_prefix(',').unwrap_or(&snippet),
+ pattern
+ ),
+ ));
+ }
+ }
+ _ => {}
+ }
+
+ let msg = format!(
+ "ensure that all possible cases are being handled by adding a match arm with a wildcard \
+ pattern{}{}",
+ if patterns_len > 1 && patterns_len < 4 && suggestion.is_some() {
+ ", a match arm with multiple or-patterns"
+ } else {
+ // we are either not suggesting anything, or suggesting `_`
+ ""
+ },
+ match patterns_len {
+ // non-exhaustive enum case
+ 0 if suggestion.is_some() => " as shown",
+ 0 => "",
+ 1 if suggestion.is_some() => " or an explicit pattern as shown",
+ 1 => " or an explicit pattern",
+ _ if suggestion.is_some() => " as shown, or multiple match arms",
+ _ => " or multiple match arms",
+ },
+ );
+ if let Some((span, sugg)) = suggestion {
+ err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
+ } else {
+ err.help(&msg);
+ }
+ err.emit();
+}
+
+pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ witnesses: &[DeconstructedPat<'p, 'tcx>],
+) -> String {
+ const LIMIT: usize = 3;
+ let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
+ match witnesses {
+ [] => bug!(),
+ [witness] => format!("`{}`", witness.to_pat(cx)),
+ [head @ .., tail] if head.len() < LIMIT => {
+ let head: Vec<_> = head.iter().map(pat_to_str).collect();
+ format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx))
+ }
+ _ => {
+ let (head, tail) = witnesses.split_at(LIMIT);
+ let head: Vec<_> = head.iter().map(pat_to_str).collect();
+ format!("`{}` and {} more", head.join("`, `"), tail.len())
+ }
+ }
+}
+
+pub(crate) fn pattern_not_covered_label(
+ witnesses: &[DeconstructedPat<'_, '_>],
+ joined_patterns: &str,
+) -> String {
+ format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
+}
+
+/// Point at the definition of non-covered `enum` variants.
+fn adt_defined_here<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ err: &mut Diagnostic,
+ ty: Ty<'tcx>,
+ witnesses: &[DeconstructedPat<'p, 'tcx>],
+) {
+ let ty = ty.peel_refs();
+ if let ty::Adt(def, _) = ty.kind() {
+ let mut spans = vec![];
+ if witnesses.len() < 5 {
+ for sp in maybe_point_at_variant(cx, *def, witnesses.iter()) {
+ spans.push(sp);
+ }
+ }
+ let def_span = cx
+ .tcx
+ .hir()
+ .get_if_local(def.did())
+ .and_then(|node| node.ident())
+ .map(|ident| ident.span)
+ .unwrap_or_else(|| cx.tcx.def_span(def.did()));
+ let mut span: MultiSpan =
+ if spans.is_empty() { def_span.into() } else { spans.clone().into() };
+
+ span.push_span_label(def_span, "");
+ for pat in spans {
+ span.push_span_label(pat, "not covered");
+ }
+ err.span_note(span, &format!("`{}` defined here", ty));
+ }
+}
+
+fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ def: AdtDef<'tcx>,
+ patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
+) -> Vec<Span> {
+ use Constructor::*;
+ let mut covered = vec![];
+ for pattern in patterns {
+ if let Variant(variant_index) = pattern.ctor() {
+ if let ty::Adt(this_def, _) = pattern.ty().kind() && this_def.did() != def.did() {
+ continue;
+ }
+ let sp = def.variant(*variant_index).ident(cx.tcx).span;
+ if covered.contains(&sp) {
+ // Don't point at variants that have already been covered due to other patterns to avoid
+ // visual clutter.
+ continue;
+ }
+ covered.push(sp);
+ }
+ covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
+ }
+ covered
+}
+
+/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
+fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId, span: Span) -> bool {
+ !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env)
+}
+
+/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
+///
+/// For example, this would reject:
+/// - `ref x @ Some(ref mut y)`,
+/// - `ref mut x @ Some(ref y)`,
+/// - `ref mut x @ Some(ref mut y)`,
+/// - `ref mut? x @ Some(y)`, and
+/// - `x @ Some(ref mut? y)`.
+///
+/// This analysis is *not* subsumed by NLL.
+fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>) {
+ // Extract `sub` in `binding @ sub`.
+ let (name, sub) = match &pat.kind {
+ hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),
+ _ => return,
+ };
+ let binding_span = pat.span.with_hi(name.span.hi());
+
+ let typeck_results = cx.typeck_results;
+ let sess = cx.tcx.sess;
+
+ // Get the binding move, extract the mutability if by-ref.
+ let mut_outer = match typeck_results.extract_binding_mode(sess, pat.hir_id, pat.span) {
+ Some(ty::BindByValue(_)) if is_binding_by_move(cx, pat.hir_id, pat.span) => {
+ // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
+ let mut conflicts_ref = Vec::new();
+ sub.each_binding(|_, hir_id, span, _| {
+ match typeck_results.extract_binding_mode(sess, hir_id, span) {
+ Some(ty::BindByValue(_)) | None => {}
+ Some(ty::BindByReference(_)) => conflicts_ref.push(span),
+ }
+ });
+ if !conflicts_ref.is_empty() {
+ let occurs_because = format!(
+ "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
+ name,
+ typeck_results.node_type(pat.hir_id),
+ );
+ sess.struct_span_err(pat.span, "borrow of moved value")
+ .span_label(binding_span, format!("value moved into `{}` here", name))
+ .span_label(binding_span, occurs_because)
+ .span_labels(conflicts_ref, "value borrowed here after move")
+ .emit();
+ }
+ return;
+ }
+ Some(ty::BindByValue(_)) | None => return,
+ Some(ty::BindByReference(m)) => m,
+ };
+
+ // We now have `ref $mut_outer binding @ sub` (semantically).
+ // Recurse into each binding in `sub` and find mutability or move conflicts.
+ let mut conflicts_move = Vec::new();
+ let mut conflicts_mut_mut = Vec::new();
+ let mut conflicts_mut_ref = Vec::new();
+ sub.each_binding(|_, hir_id, span, name| {
+ match typeck_results.extract_binding_mode(sess, hir_id, span) {
+ Some(ty::BindByReference(mut_inner)) => match (mut_outer, mut_inner) {
+ (Mutability::Not, Mutability::Not) => {} // Both sides are `ref`.
+ (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push((span, name)), // 2x `ref mut`.
+ _ => conflicts_mut_ref.push((span, name)), // `ref` + `ref mut` in either direction.
+ },
+ Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id, span) => {
+ conflicts_move.push((span, name)) // `ref mut?` + by-move conflict.
+ }
+ Some(ty::BindByValue(_)) | None => {} // `ref mut?` + by-copy is fine.
+ }
+ });
+
+ // Report errors if any.
+ if !conflicts_mut_mut.is_empty() {
+ // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
+ let mut err = sess
+ .struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time");
+ err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name));
+ for (span, name) in conflicts_mut_mut {
+ err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name));
+ }
+ for (span, name) in conflicts_mut_ref {
+ err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name));
+ }
+ for (span, name) in conflicts_move {
+ err.span_label(span, format!("also moved into `{}` here", name));
+ }
+ err.emit();
+ } else if !conflicts_mut_ref.is_empty() {
+ // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
+ let (primary, also) = match mut_outer {
+ Mutability::Mut => ("mutable", "immutable"),
+ Mutability::Not => ("immutable", "mutable"),
+ };
+ let msg =
+ format!("cannot borrow value as {} because it is also borrowed as {}", also, primary);
+ let mut err = sess.struct_span_err(pat.span, &msg);
+ err.span_label(binding_span, format!("{} borrow, by `{}`, occurs here", primary, name));
+ for (span, name) in conflicts_mut_ref {
+ err.span_label(span, format!("{} borrow, by `{}`, occurs here", also, name));
+ }
+ for (span, name) in conflicts_move {
+ err.span_label(span, format!("also moved into `{}` here", name));
+ }
+ err.emit();
+ } else if !conflicts_move.is_empty() {
+ // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
+ let mut err =
+ sess.struct_span_err(pat.span, "cannot move out of value because it is borrowed");
+ err.span_label(binding_span, format!("value borrowed, by `{}`, here", name));
+ for (span, name) in conflicts_move {
+ err.span_label(span, format!("value moved into `{}` here", name));
+ }
+ err.emit();
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum LetSource {
+ GenericLet,
+ IfLet,
+ IfLetGuard,
+ LetElse(Span),
+ WhileLet,
+}
+
+fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
+ let hir = tcx.hir();
+
+ let parent = hir.get_parent_node(pat_id);
+ let_source_parent(tcx, parent, Some(pat_id))
+}
+
+fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> LetSource {
+ let hir = tcx.hir();
+
+ let parent_node = hir.get(parent);
+
+ match parent_node {
+ hir::Node::Arm(hir::Arm {
+ guard: Some(hir::Guard::IfLet(&hir::Let { pat: hir::Pat { hir_id, .. }, .. })),
+ ..
+ }) if Some(*hir_id) == pat_id => {
+ return LetSource::IfLetGuard;
+ }
+ _ => {}
+ }
+
+ let parent_parent = hir.get_parent_node(parent);
+ let parent_parent_node = hir.get(parent_parent);
+ if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) =
+ parent_parent_node
+ {
+ return LetSource::LetElse(*span);
+ }
+
+ let parent_parent_parent = hir.get_parent_node(parent_parent);
+ let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
+ let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
+
+ if let hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
+ ..
+ }) = parent_parent_parent_parent_node
+ {
+ return LetSource::WhileLet;
+ }
+
+ if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If(..), .. }) = parent_parent_node {
+ return LetSource::IfLet;
+ }
+
+ LetSource::GenericLet
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
new file mode 100644
index 000000000..d6dd0f017
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -0,0 +1,601 @@
+use rustc_hir as hir;
+use rustc_index::vec::Idx;
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_middle::mir::{self, Field};
+use rustc_middle::thir::{FieldPat, Pat, PatKind};
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+use rustc_session::lint;
+use rustc_span::Span;
+use rustc_trait_selection::traits::predicate_for_trait_def;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
+
+use std::cell::Cell;
+
+use super::PatCtxt;
+
+impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
+ /// Converts an evaluated constant to a pattern (if possible).
+ /// This means aggregate values (like structs and enums) are converted
+ /// to a pattern that matches the value (as if you'd compared via structural equality).
+ #[instrument(level = "debug", skip(self))]
+ pub(super) fn const_to_pat(
+ &self,
+ cv: mir::ConstantKind<'tcx>,
+ id: hir::HirId,
+ span: Span,
+ mir_structural_match_violation: bool,
+ ) -> Pat<'tcx> {
+ let pat = self.tcx.infer_ctxt().enter(|infcx| {
+ let mut convert = ConstToPat::new(self, id, span, infcx);
+ convert.to_pat(cv, mir_structural_match_violation)
+ });
+
+ debug!(?pat);
+ pat
+ }
+}
+
+struct ConstToPat<'a, 'tcx> {
+ id: hir::HirId,
+ span: Span,
+ param_env: ty::ParamEnv<'tcx>,
+
+ // This tracks if we emitted some hard error for a given const value, so that
+ // we will not subsequently issue an irrelevant lint for the same const
+ // value.
+ saw_const_match_error: Cell<bool>,
+
+ // This tracks if we emitted some diagnostic for a given const value, so that
+ // we will not subsequently issue an irrelevant lint for the same const
+ // value.
+ saw_const_match_lint: Cell<bool>,
+
+ // For backcompat we need to keep allowing non-structurally-eq types behind references.
+ // See also all the `cant-hide-behind` tests.
+ behind_reference: Cell<bool>,
+
+ // inference context used for checking `T: Structural` bounds.
+ infcx: InferCtxt<'a, 'tcx>,
+
+ include_lint_checks: bool,
+
+ treat_byte_string_as_slice: bool,
+}
+
+mod fallback_to_const_ref {
+ #[derive(Debug)]
+ /// This error type signals that we encountered a non-struct-eq situation behind a reference.
+ /// We bubble this up in order to get back to the reference destructuring and make that emit
+ /// a const pattern instead of a deref pattern. This allows us to simply call `PartialEq::eq`
+ /// on such patterns (since that function takes a reference) and not have to jump through any
+ /// hoops to get a reference to the value.
+ pub(super) struct FallbackToConstRef(());
+
+ pub(super) fn fallback_to_const_ref<'a, 'tcx>(
+ c2p: &super::ConstToPat<'a, 'tcx>,
+ ) -> FallbackToConstRef {
+ assert!(c2p.behind_reference.get());
+ FallbackToConstRef(())
+ }
+}
+use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef};
+
+impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
+ fn new(
+ pat_ctxt: &PatCtxt<'_, 'tcx>,
+ id: hir::HirId,
+ span: Span,
+ infcx: InferCtxt<'a, 'tcx>,
+ ) -> Self {
+ trace!(?pat_ctxt.typeck_results.hir_owner);
+ ConstToPat {
+ id,
+ span,
+ infcx,
+ param_env: pat_ctxt.param_env,
+ include_lint_checks: pat_ctxt.include_lint_checks,
+ saw_const_match_error: Cell::new(false),
+ saw_const_match_lint: Cell::new(false),
+ behind_reference: Cell::new(false),
+ treat_byte_string_as_slice: pat_ctxt
+ .typeck_results
+ .treat_byte_string_as_slice
+ .contains(&id.local_id),
+ }
+ }
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> String {
+ let path = self.tcx().def_path_str(adt_def.did());
+ format!(
+ "to use a constant of type `{}` in a pattern, \
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+ path, path,
+ )
+ }
+
+ fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
+ traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
+ with_no_trimmed_paths!(match non_sm_ty.kind() {
+ ty::Adt(adt, _) => self.adt_derive_msg(*adt),
+ ty::Dynamic(..) => {
+ "trait objects cannot be used in patterns".to_string()
+ }
+ ty::Opaque(..) => {
+ "opaque types cannot be used in patterns".to_string()
+ }
+ ty::Closure(..) => {
+ "closures cannot be used in patterns".to_string()
+ }
+ ty::Generator(..) | ty::GeneratorWitness(..) => {
+ "generators cannot be used in patterns".to_string()
+ }
+ ty::Float(..) => {
+ "floating-point numbers cannot be used in patterns".to_string()
+ }
+ ty::FnPtr(..) => {
+ "function pointers cannot be used in patterns".to_string()
+ }
+ ty::RawPtr(..) => {
+ "raw pointers cannot be used in patterns".to_string()
+ }
+ _ => {
+ bug!("use of a value of `{non_sm_ty}` inside a pattern")
+ }
+ })
+ })
+ }
+
+ fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
+ ty.is_structural_eq_shallow(self.infcx.tcx)
+ }
+
+ fn to_pat(
+ &mut self,
+ cv: mir::ConstantKind<'tcx>,
+ mir_structural_match_violation: bool,
+ ) -> Pat<'tcx> {
+ trace!(self.treat_byte_string_as_slice);
+ // This method is just a wrapper handling a validity check; the heavy lifting is
+ // performed by the recursive `recur` method, which is not meant to be
+ // invoked except by this method.
+ //
+ // once indirect_structural_match is a full fledged error, this
+ // level of indirection can be eliminated
+
+ let inlined_const_as_pat = self.recur(cv, mir_structural_match_violation).unwrap();
+
+ if self.include_lint_checks && !self.saw_const_match_error.get() {
+ // If we were able to successfully convert the const to some pat,
+ // double-check that all types in the const implement `Structural`.
+
+ let structural = self.search_for_structural_match_violation(cv.ty());
+ debug!(
+ "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
+ cv.ty(),
+ structural
+ );
+
+ // This can occur because const qualification treats all associated constants as
+ // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them
+ // before it runs.
+ //
+ // FIXME(#73448): Find a way to bring const qualification into parity with
+ // `search_for_structural_match_violation`.
+ if structural.is_none() && mir_structural_match_violation {
+ warn!("MIR const-checker found novel structural match violation. See #73448.");
+ return inlined_const_as_pat;
+ }
+
+ if let Some(msg) = structural {
+ if !self.type_may_have_partial_eq_impl(cv.ty()) {
+ // span_fatal avoids ICE from resolution of non-existent method (rare case).
+ self.tcx().sess.span_fatal(self.span, &msg);
+ } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
+ self.tcx().struct_span_lint_hir(
+ lint::builtin::INDIRECT_STRUCTURAL_MATCH,
+ self.id,
+ self.span,
+ |lint| {
+ lint.build(&msg).emit();
+ },
+ );
+ } else {
+ debug!(
+ "`search_for_structural_match_violation` found one, but `CustomEq` was \
+ not in the qualifs for that `const`"
+ );
+ }
+ }
+ }
+
+ inlined_const_as_pat
+ }
+
+ fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
+ // double-check there even *is* a semantic `PartialEq` to dispatch to.
+ //
+ // (If there isn't, then we can safely issue a hard
+ // error, because that's never worked, due to compiler
+ // using `PartialEq::eq` in this scenario in the past.)
+ let partial_eq_trait_id =
+ self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
+ let obligation: PredicateObligation<'_> = predicate_for_trait_def(
+ self.tcx(),
+ self.param_env,
+ ObligationCause::misc(self.span, self.id),
+ partial_eq_trait_id,
+ 0,
+ ty,
+ &[],
+ );
+ // FIXME: should this call a `predicate_must_hold` variant instead?
+
+ let has_impl = self.infcx.predicate_may_hold(&obligation);
+
+ // Note: To fix rust-lang/rust#65466, we could just remove this type
+ // walk hack for function pointers, and unconditionally error
+ // if `PartialEq` is not implemented. However, that breaks stable
+ // code at the moment, because types like `for <'a> fn(&'a ())` do
+ // not *yet* implement `PartialEq`. So for now we leave this here.
+ has_impl
+ || ty.walk().any(|t| match t.unpack() {
+ ty::subst::GenericArgKind::Lifetime(_) => false,
+ ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
+ ty::subst::GenericArgKind::Const(_) => false,
+ })
+ }
+
+ fn field_pats(
+ &self,
+ vals: impl Iterator<Item = mir::ConstantKind<'tcx>>,
+ ) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
+ vals.enumerate()
+ .map(|(idx, val)| {
+ let field = Field::new(idx);
+ Ok(FieldPat { field, pattern: self.recur(val, false)? })
+ })
+ .collect()
+ }
+
+ // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
+ #[instrument(skip(self), level = "debug")]
+ fn recur(
+ &self,
+ cv: mir::ConstantKind<'tcx>,
+ mir_structural_match_violation: bool,
+ ) -> Result<Pat<'tcx>, FallbackToConstRef> {
+ let id = self.id;
+ let span = self.span;
+ let tcx = self.tcx();
+ let param_env = self.param_env;
+
+ let kind = match cv.ty().kind() {
+ ty::Float(_) => {
+ if self.include_lint_checks {
+ tcx.struct_span_lint_hir(
+ lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
+ id,
+ span,
+ |lint| {
+ lint.build("floating-point types cannot be used in patterns").emit();
+ },
+ );
+ }
+ PatKind::Constant { value: cv }
+ }
+ ty::Adt(adt_def, _) if adt_def.is_union() => {
+ // Matching on union fields is unsafe, we can't hide it in constants
+ self.saw_const_match_error.set(true);
+ let msg = "cannot use unions in constant patterns";
+ if self.include_lint_checks {
+ tcx.sess.span_err(span, msg);
+ } else {
+ tcx.sess.delay_span_bug(span, msg);
+ }
+ PatKind::Wild
+ }
+ ty::Adt(..)
+ if !self.type_may_have_partial_eq_impl(cv.ty())
+ // FIXME(#73448): Find a way to bring const qualification into parity with
+ // `search_for_structural_match_violation` and then remove this condition.
+ && self.search_for_structural_match_violation(cv.ty()).is_some() =>
+ {
+ // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
+ // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
+ let msg = self.search_for_structural_match_violation(cv.ty()).unwrap();
+ self.saw_const_match_error.set(true);
+ if self.include_lint_checks {
+ tcx.sess.span_err(self.span, &msg);
+ } else {
+ tcx.sess.delay_span_bug(self.span, &msg);
+ }
+ PatKind::Wild
+ }
+ // If the type is not structurally comparable, just emit the constant directly,
+ // causing the pattern match code to treat it opaquely.
+ // FIXME: This code doesn't emit errors itself, the caller emits the errors.
+ // So instead of specific errors, you just get blanket errors about the whole
+ // const type. See
+ // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
+ // details.
+ // Backwards compatibility hack because we can't cause hard errors on these
+ // types, so we compare them via `PartialEq::eq` at runtime.
+ ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => {
+ if self.include_lint_checks
+ && !self.saw_const_match_error.get()
+ && !self.saw_const_match_lint.get()
+ {
+ self.saw_const_match_lint.set(true);
+ tcx.struct_span_lint_hir(
+ lint::builtin::INDIRECT_STRUCTURAL_MATCH,
+ id,
+ span,
+ |lint| {
+ let msg = format!(
+ "to use a constant of type `{}` in a pattern, \
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+ cv.ty(),
+ cv.ty(),
+ );
+ lint.build(&msg).emit();
+ },
+ );
+ }
+ // Since we are behind a reference, we can just bubble the error up so we get a
+ // constant at reference type, making it easy to let the fallback call
+ // `PartialEq::eq` on it.
+ return Err(fallback_to_const_ref(self));
+ }
+ ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => {
+ debug!(
+ "adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
+ adt_def,
+ cv.ty()
+ );
+ let path = tcx.def_path_str(adt_def.did());
+ let msg = format!(
+ "to use a constant of type `{}` in a pattern, \
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+ path, path,
+ );
+ self.saw_const_match_error.set(true);
+ if self.include_lint_checks {
+ tcx.sess.span_err(span, &msg);
+ } else {
+ tcx.sess.delay_span_bug(span, &msg);
+ }
+ PatKind::Wild
+ }
+ ty::Adt(adt_def, substs) if adt_def.is_enum() => {
+ let destructured = tcx.destructure_mir_constant(param_env, cv);
+
+ PatKind::Variant {
+ adt_def: *adt_def,
+ substs,
+ variant_index: destructured
+ .variant
+ .expect("destructed const of adt without variant id"),
+ subpatterns: self.field_pats(destructured.fields.iter().copied())?,
+ }
+ }
+ ty::Tuple(_) | ty::Adt(_, _) => {
+ let destructured = tcx.destructure_mir_constant(param_env, cv);
+ PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? }
+ }
+ ty::Array(..) => PatKind::Array {
+ prefix: tcx
+ .destructure_mir_constant(param_env, cv)
+ .fields
+ .iter()
+ .map(|val| self.recur(*val, false))
+ .collect::<Result<_, _>>()?,
+ slice: None,
+ suffix: Vec::new(),
+ },
+ ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
+ // These are not allowed and will error elsewhere anyway.
+ ty::Dynamic(..) => {
+ self.saw_const_match_error.set(true);
+ let msg = format!("`{}` cannot be used in patterns", cv.ty());
+ if self.include_lint_checks {
+ tcx.sess.span_err(span, &msg);
+ } else {
+ tcx.sess.delay_span_bug(span, &msg);
+ }
+ PatKind::Wild
+ }
+ // `&str` is represented as `ConstValue::Slice`, let's keep using this
+ // optimization for now.
+ ty::Str => PatKind::Constant { value: cv },
+ // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
+ // matching against references, you can only use byte string literals.
+ // The typechecker has a special case for byte string literals, by treating them
+ // as slices. This means we turn `&[T; N]` constants into slice patterns, which
+ // has no negative effects on pattern matching, even if we're actually matching on
+ // arrays.
+ ty::Array(..) if !self.treat_byte_string_as_slice => {
+ let old = self.behind_reference.replace(true);
+ let array = tcx.deref_mir_constant(self.param_env.and(cv));
+ let val = PatKind::Deref {
+ subpattern: Pat {
+ kind: Box::new(PatKind::Array {
+ prefix: tcx
+ .destructure_mir_constant(param_env, array)
+ .fields
+ .iter()
+ .map(|val| self.recur(*val, false))
+ .collect::<Result<_, _>>()?,
+ slice: None,
+ suffix: vec![],
+ }),
+ span,
+ ty: *pointee_ty,
+ },
+ };
+ self.behind_reference.set(old);
+ val
+ }
+ ty::Array(elem_ty, _) |
+ // Cannot merge this with the catch all branch below, because the `const_deref`
+ // changes the type from slice to array, we need to keep the original type in the
+ // pattern.
+ ty::Slice(elem_ty) => {
+ let old = self.behind_reference.replace(true);
+ let array = tcx.deref_mir_constant(self.param_env.and(cv));
+ let val = PatKind::Deref {
+ subpattern: Pat {
+ kind: Box::new(PatKind::Slice {
+ prefix: tcx
+ .destructure_mir_constant(param_env, array)
+ .fields
+ .iter()
+ .map(|val| self.recur(*val, false))
+ .collect::<Result<_, _>>()?,
+ slice: None,
+ suffix: vec![],
+ }),
+ span,
+ ty: tcx.mk_slice(elem_ty),
+ },
+ };
+ self.behind_reference.set(old);
+ val
+ }
+ // Backwards compatibility hack: support references to non-structural types.
+ // We'll lower
+ // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
+ // reference. This makes the rest of the matching logic simpler as it doesn't have
+ // to figure out how to get a reference again.
+ ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => {
+ if self.behind_reference.get() {
+ if self.include_lint_checks
+ && !self.saw_const_match_error.get()
+ && !self.saw_const_match_lint.get()
+ {
+ self.saw_const_match_lint.set(true);
+ let msg = self.adt_derive_msg(adt_def);
+ self.tcx().struct_span_lint_hir(
+ lint::builtin::INDIRECT_STRUCTURAL_MATCH,
+ self.id,
+ self.span,
+ |lint| {lint.build(&msg).emit();},
+ );
+ }
+ PatKind::Constant { value: cv }
+ } else {
+ if !self.saw_const_match_error.get() {
+ self.saw_const_match_error.set(true);
+ let msg = self.adt_derive_msg(adt_def);
+ if self.include_lint_checks {
+ tcx.sess.span_err(span, &msg);
+ } else {
+ tcx.sess.delay_span_bug(span, &msg);
+ }
+ }
+ PatKind::Wild
+ }
+ }
+ // All other references are converted into deref patterns and then recursively
+ // convert the dereferenced constant to a pattern that is the sub-pattern of the
+ // deref pattern.
+ _ => {
+ if !pointee_ty.is_sized(tcx.at(span), param_env) {
+ // `tcx.deref_mir_constant()` below will ICE with an unsized type
+ // (except slices, which are handled in a separate arm above).
+ let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
+ if self.include_lint_checks {
+ tcx.sess.span_err(span, &msg);
+ } else {
+ tcx.sess.delay_span_bug(span, &msg);
+ }
+ PatKind::Wild
+ } else {
+ let old = self.behind_reference.replace(true);
+ // In case there are structural-match violations somewhere in this subpattern,
+ // we fall back to a const pattern. If we do not do this, we may end up with
+ // a !structural-match constant that is not of reference type, which makes it
+ // very hard to invoke `PartialEq::eq` on it as a fallback.
+ let val = match self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false) {
+ Ok(subpattern) => PatKind::Deref { subpattern },
+ Err(_) => PatKind::Constant { value: cv },
+ };
+ self.behind_reference.set(old);
+ val
+ }
+ }
+ },
+ ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
+ PatKind::Constant { value: cv }
+ }
+ ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => {
+ PatKind::Constant { value: cv }
+ }
+ // FIXME: these can have very surprising behaviour where optimization levels or other
+ // compilation choices change the runtime behaviour of the match.
+ // See https://github.com/rust-lang/rust/issues/70861 for examples.
+ ty::FnPtr(..) | ty::RawPtr(..) => {
+ if self.include_lint_checks
+ && !self.saw_const_match_error.get()
+ && !self.saw_const_match_lint.get()
+ {
+ self.saw_const_match_lint.set(true);
+ let msg = "function pointers and unsized pointers in patterns behave \
+ unpredictably and should not be relied upon. \
+ See https://github.com/rust-lang/rust/issues/70861 for details.";
+ tcx.struct_span_lint_hir(
+ lint::builtin::POINTER_STRUCTURAL_MATCH,
+ id,
+ span,
+ |lint| {
+ lint.build(msg).emit();
+ },
+ );
+ }
+ PatKind::Constant { value: cv }
+ }
+ _ => {
+ self.saw_const_match_error.set(true);
+ let msg = format!("`{}` cannot be used in patterns", cv.ty());
+ if self.include_lint_checks {
+ tcx.sess.span_err(span, &msg);
+ } else {
+ tcx.sess.delay_span_bug(span, &msg);
+ }
+ PatKind::Wild
+ }
+ };
+
+ if self.include_lint_checks
+ && !self.saw_const_match_error.get()
+ && !self.saw_const_match_lint.get()
+ && mir_structural_match_violation
+ // FIXME(#73448): Find a way to bring const qualification into parity with
+ // `search_for_structural_match_violation` and then remove this condition.
+ && self.search_for_structural_match_violation(cv.ty()).is_some()
+ {
+ self.saw_const_match_lint.set(true);
+ // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
+ // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
+ let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace(
+ "in a pattern,",
+ "in a pattern, the constant's initializer must be trivial or",
+ );
+ tcx.struct_span_lint_hir(
+ lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
+ id,
+ span,
+ |lint| {
+ lint.build(&msg).emit();
+ },
+ );
+ }
+
+ Ok(Pat { span, ty: cv.ty(), kind: Box::new(kind) })
+ }
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
new file mode 100644
index 000000000..8d6f8efb6
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -0,0 +1,1711 @@
+//! [`super::usefulness`] explains most of what is happening in this file. As explained there,
+//! values and patterns are made from constructors applied to fields. This file defines a
+//! `Constructor` enum, a `Fields` struct, and various operations to manipulate them and convert
+//! them from/to patterns.
+//!
+//! There's one idea that is not detailed in [`super::usefulness`] because the details are not
+//! needed there: _constructor splitting_.
+//!
+//! # Constructor splitting
+//!
+//! The idea is as follows: given a constructor `c` and a matrix, we want to specialize in turn
+//! with all the value constructors that are covered by `c`, and compute usefulness for each.
+//! Instead of listing all those constructors (which is intractable), we group those value
+//! constructors together as much as possible. Example:
+//!
+//! ```compile_fail,E0004
+//! match (0, false) {
+//! (0 ..=100, true) => {} // `p_1`
+//! (50..=150, false) => {} // `p_2`
+//! (0 ..=200, _) => {} // `q`
+//! }
+//! ```
+//!
+//! The naive approach would try all numbers in the range `0..=200`. But we can be a lot more
+//! clever: `0` and `1` for example will match the exact same rows, and return equivalent
+//! witnesses. In fact all of `0..50` would. We can thus restrict our exploration to 4
+//! constructors: `0..50`, `50..=100`, `101..=150` and `151..=200`. That is enough and infinitely
+//! more tractable.
+//!
+//! We capture this idea in a function `split(p_1 ... p_n, c)` which returns a list of constructors
+//! `c'` covered by `c`. Given such a `c'`, we require that all value ctors `c''` covered by `c'`
+//! return an equivalent set of witnesses after specializing and computing usefulness.
+//! In the example above, witnesses for specializing by `c''` covered by `0..50` will only differ
+//! in their first element.
+//!
+//! We usually also ask that the `c'` together cover all of the original `c`. However we allow
+//! skipping some constructors as long as it doesn't change whether the resulting list of witnesses
+//! is empty of not. We use this in the wildcard `_` case.
+//!
+//! Splitting is implemented in the [`Constructor::split`] function. We don't do splitting for
+//! or-patterns; instead we just try the alternatives one-by-one. For details on splitting
+//! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]; for slices, see
+//! [`SplitVarLenSlice`].
+
+use self::Constructor::*;
+use self::SliceKind::*;
+
+use super::compare_const_vals;
+use super::usefulness::{MatchCheckCtxt, PatCtxt};
+
+use rustc_data_structures::captures::Captures;
+use rustc_index::vec::Idx;
+
+use rustc_hir::{HirId, RangeEnd};
+use rustc_middle::mir::{self, Field};
+use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
+use rustc_middle::ty::layout::IntegerExt;
+use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
+use rustc_middle::{middle::stability::EvalResult, mir::interpret::ConstValue};
+use rustc_session::lint;
+use rustc_span::{Span, DUMMY_SP};
+use rustc_target::abi::{Integer, Size, VariantIdx};
+
+use smallvec::{smallvec, SmallVec};
+use std::cell::Cell;
+use std::cmp::{self, max, min, Ordering};
+use std::fmt;
+use std::iter::{once, IntoIterator};
+use std::ops::RangeInclusive;
+
+/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
+fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
+ fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
+ if let PatKind::Or { pats } = pat.kind.as_ref() {
+ for pat in pats {
+ expand(pat, vec);
+ }
+ } else {
+ vec.push(pat)
+ }
+ }
+
+ let mut pats = Vec::new();
+ expand(pat, &mut pats);
+ pats
+}
+
+/// An inclusive interval, used for precise integer exhaustiveness checking.
+/// `IntRange`s always store a contiguous range. This means that values are
+/// encoded such that `0` encodes the minimum value for the integer,
+/// regardless of the signedness.
+/// For example, the pattern `-128..=127i8` is encoded as `0..=255`.
+/// This makes comparisons and arithmetic on interval endpoints much more
+/// straightforward. See `signed_bias` for details.
+///
+/// `IntRange` is never used to encode an empty range or a "range" that wraps
+/// around the (offset) space: i.e., `range.lo <= range.hi`.
+#[derive(Clone, PartialEq, Eq)]
+pub(super) struct IntRange {
+ range: RangeInclusive<u128>,
+ /// Keeps the bias used for encoding the range. It depends on the type of the range and
+ /// possibly the pointer size of the current architecture. The algorithm ensures we never
+ /// compare `IntRange`s with different types/architectures.
+ bias: u128,
+}
+
+impl IntRange {
+ #[inline]
+ fn is_integral(ty: Ty<'_>) -> bool {
+ matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool)
+ }
+
+ fn is_singleton(&self) -> bool {
+ self.range.start() == self.range.end()
+ }
+
+ fn boundaries(&self) -> (u128, u128) {
+ (*self.range.start(), *self.range.end())
+ }
+
+ #[inline]
+ fn integral_size_and_signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Size, u128)> {
+ match *ty.kind() {
+ ty::Bool => Some((Size::from_bytes(1), 0)),
+ ty::Char => Some((Size::from_bytes(4), 0)),
+ ty::Int(ity) => {
+ let size = Integer::from_int_ty(&tcx, ity).size();
+ Some((size, 1u128 << (size.bits() as u128 - 1)))
+ }
+ ty::Uint(uty) => Some((Integer::from_uint_ty(&tcx, uty).size(), 0)),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ fn from_constant<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ value: mir::ConstantKind<'tcx>,
+ ) -> Option<IntRange> {
+ let ty = value.ty();
+ if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) {
+ let val = (|| {
+ match value {
+ mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) => {
+ // For this specific pattern we can skip a lot of effort and go
+ // straight to the result, after doing a bit of checking. (We
+ // could remove this branch and just fall through, which
+ // is more general but much slower.)
+ if let Ok(Ok(bits)) = scalar.to_bits_or_ptr_internal(target_size) {
+ return Some(bits);
+ } else {
+ return None;
+ }
+ }
+ mir::ConstantKind::Ty(c) => match c.kind() {
+ ty::ConstKind::Value(_) => bug!(
+ "encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val"
+ ),
+ _ => {}
+ },
+ _ => {}
+ }
+
+ // This is a more general form of the previous case.
+ value.try_eval_bits(tcx, param_env, ty)
+ })()?;
+ let val = val ^ bias;
+ Some(IntRange { range: val..=val, bias })
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn from_range<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ lo: u128,
+ hi: u128,
+ ty: Ty<'tcx>,
+ end: &RangeEnd,
+ ) -> Option<IntRange> {
+ if Self::is_integral(ty) {
+ // Perform a shift if the underlying types are signed,
+ // which makes the interval arithmetic simpler.
+ let bias = IntRange::signed_bias(tcx, ty);
+ let (lo, hi) = (lo ^ bias, hi ^ bias);
+ let offset = (*end == RangeEnd::Excluded) as u128;
+ if lo > hi || (lo == hi && *end == RangeEnd::Excluded) {
+ // This should have been caught earlier by E0030.
+ bug!("malformed range pattern: {}..={}", lo, (hi - offset));
+ }
+ Some(IntRange { range: lo..=(hi - offset), bias })
+ } else {
+ None
+ }
+ }
+
+ // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
+ fn signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> u128 {
+ match *ty.kind() {
+ ty::Int(ity) => {
+ let bits = Integer::from_int_ty(&tcx, ity).size().bits() as u128;
+ 1u128 << (bits - 1)
+ }
+ _ => 0,
+ }
+ }
+
+ fn is_subrange(&self, other: &Self) -> bool {
+ other.range.start() <= self.range.start() && self.range.end() <= other.range.end()
+ }
+
+ fn intersection(&self, other: &Self) -> Option<Self> {
+ let (lo, hi) = self.boundaries();
+ let (other_lo, other_hi) = other.boundaries();
+ if lo <= other_hi && other_lo <= hi {
+ Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), bias: self.bias })
+ } else {
+ None
+ }
+ }
+
+ fn suspicious_intersection(&self, other: &Self) -> bool {
+ // `false` in the following cases:
+ // 1 ---- // 1 ---------- // 1 ---- // 1 ----
+ // 2 ---------- // 2 ---- // 2 ---- // 2 ----
+ //
+ // The following are currently `false`, but could be `true` in the future (#64007):
+ // 1 --------- // 1 ---------
+ // 2 ---------- // 2 ----------
+ //
+ // `true` in the following cases:
+ // 1 ------- // 1 -------
+ // 2 -------- // 2 -------
+ let (lo, hi) = self.boundaries();
+ let (other_lo, other_hi) = other.boundaries();
+ (lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton()
+ }
+
+ /// Only used for displaying the range properly.
+ fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
+ let (lo, hi) = self.boundaries();
+
+ let bias = self.bias;
+ let (lo, hi) = (lo ^ bias, hi ^ bias);
+
+ let env = ty::ParamEnv::empty().and(ty);
+ let lo_const = mir::ConstantKind::from_bits(tcx, lo, env);
+ let hi_const = mir::ConstantKind::from_bits(tcx, hi, env);
+
+ let kind = if lo == hi {
+ PatKind::Constant { value: lo_const }
+ } else {
+ PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included })
+ };
+
+ Pat { ty, span: DUMMY_SP, kind: Box::new(kind) }
+ }
+
+ /// Lint on likely incorrect range patterns (#63987)
+ pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>(
+ &self,
+ pcx: &PatCtxt<'_, 'p, 'tcx>,
+ pats: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
+ column_count: usize,
+ hir_id: HirId,
+ ) {
+ if self.is_singleton() {
+ return;
+ }
+
+ if column_count != 1 {
+ // FIXME: for now, only check for overlapping ranges on simple range
+ // patterns. Otherwise with the current logic the following is detected
+ // as overlapping:
+ // ```
+ // match (0u8, true) {
+ // (0 ..= 125, false) => {}
+ // (125 ..= 255, true) => {}
+ // _ => {}
+ // }
+ // ```
+ return;
+ }
+
+ let overlaps: Vec<_> = pats
+ .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span())))
+ .filter(|(range, _)| self.suspicious_intersection(range))
+ .map(|(range, span)| (self.intersection(&range).unwrap(), span))
+ .collect();
+
+ if !overlaps.is_empty() {
+ pcx.cx.tcx.struct_span_lint_hir(
+ lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
+ hir_id,
+ pcx.span,
+ |lint| {
+ let mut err = lint.build("multiple patterns overlap on their endpoints");
+ for (int_range, span) in overlaps {
+ err.span_label(
+ span,
+ &format!(
+ "this range overlaps on `{}`...",
+ int_range.to_pat(pcx.cx.tcx, pcx.ty)
+ ),
+ );
+ }
+ err.span_label(pcx.span, "... with this range");
+ err.note("you likely meant to write mutually exclusive ranges");
+ err.emit();
+ },
+ );
+ }
+ }
+
+ /// See `Constructor::is_covered_by`
+ fn is_covered_by(&self, other: &Self) -> bool {
+ if self.intersection(other).is_some() {
+ // Constructor splitting should ensure that all intersections we encounter are actually
+ // inclusions.
+ assert!(self.is_subrange(other));
+ true
+ } else {
+ false
+ }
+ }
+}
+
+/// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and
+/// would be displayed as such. To render properly, convert to a pattern first.
+impl fmt::Debug for IntRange {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let (lo, hi) = self.boundaries();
+ let bias = self.bias;
+ let (lo, hi) = (lo ^ bias, hi ^ bias);
+ write!(f, "{}", lo)?;
+ write!(f, "{}", RangeEnd::Included)?;
+ write!(f, "{}", hi)
+ }
+}
+
+/// Represents a border between 2 integers. Because the intervals spanning borders must be able to
+/// cover every integer, we need to be able to represent 2^128 + 1 such borders.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+enum IntBorder {
+ JustBefore(u128),
+ AfterMax,
+}
+
+/// A range of integers that is partitioned into disjoint subranges. This does constructor
+/// splitting for integer ranges as explained at the top of the file.
+///
+/// This is fed multiple ranges, and returns an output that covers the input, but is split so that
+/// the only intersections between an output range and a seen range are inclusions. No output range
+/// straddles the boundary of one of the inputs.
+///
+/// The following input:
+/// ```text
+/// |-------------------------| // `self`
+/// |------| |----------| |----|
+/// |-------| |-------|
+/// ```
+/// would be iterated over as follows:
+/// ```text
+/// ||---|--||-|---|---|---|--|
+/// ```
+#[derive(Debug, Clone)]
+struct SplitIntRange {
+ /// The range we are splitting
+ range: IntRange,
+ /// The borders of ranges we have seen. They are all contained within `range`. This is kept
+ /// sorted.
+ borders: Vec<IntBorder>,
+}
+
+impl SplitIntRange {
+ fn new(range: IntRange) -> Self {
+ SplitIntRange { range, borders: Vec::new() }
+ }
+
+ /// Internal use
+ fn to_borders(r: IntRange) -> [IntBorder; 2] {
+ use IntBorder::*;
+ let (lo, hi) = r.boundaries();
+ let lo = JustBefore(lo);
+ let hi = match hi.checked_add(1) {
+ Some(m) => JustBefore(m),
+ None => AfterMax,
+ };
+ [lo, hi]
+ }
+
+ /// Add ranges relative to which we split.
+ fn split(&mut self, ranges: impl Iterator<Item = IntRange>) {
+ let this_range = &self.range;
+ let included_ranges = ranges.filter_map(|r| this_range.intersection(&r));
+ let included_borders = included_ranges.flat_map(|r| {
+ let borders = Self::to_borders(r);
+ once(borders[0]).chain(once(borders[1]))
+ });
+ self.borders.extend(included_borders);
+ self.borders.sort_unstable();
+ }
+
+ /// Iterate over the contained ranges.
+ fn iter<'a>(&'a self) -> impl Iterator<Item = IntRange> + Captures<'a> {
+ use IntBorder::*;
+
+ let self_range = Self::to_borders(self.range.clone());
+ // Start with the start of the range.
+ let mut prev_border = self_range[0];
+ self.borders
+ .iter()
+ .copied()
+ // End with the end of the range.
+ .chain(once(self_range[1]))
+ // List pairs of adjacent borders.
+ .map(move |border| {
+ let ret = (prev_border, border);
+ prev_border = border;
+ ret
+ })
+ // Skip duplicates.
+ .filter(|(prev_border, border)| prev_border != border)
+ // Finally, convert to ranges.
+ .map(move |(prev_border, border)| {
+ let range = match (prev_border, border) {
+ (JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
+ (JustBefore(n), AfterMax) => n..=u128::MAX,
+ _ => unreachable!(), // Ruled out by the sorting and filtering we did
+ };
+ IntRange { range, bias: self.range.bias }
+ })
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum SliceKind {
+ /// Patterns of length `n` (`[x, y]`).
+ FixedLen(usize),
+ /// Patterns using the `..` notation (`[x, .., y]`).
+ /// Captures any array constructor of `length >= i + j`.
+ /// In the case where `array_len` is `Some(_)`,
+ /// this indicates that we only care about the first `i` and the last `j` values of the array,
+ /// and everything in between is a wildcard `_`.
+ VarLen(usize, usize),
+}
+
+impl SliceKind {
+ fn arity(self) -> usize {
+ match self {
+ FixedLen(length) => length,
+ VarLen(prefix, suffix) => prefix + suffix,
+ }
+ }
+
+ /// Whether this pattern includes patterns of length `other_len`.
+ fn covers_length(self, other_len: usize) -> bool {
+ match self {
+ FixedLen(len) => len == other_len,
+ VarLen(prefix, suffix) => prefix + suffix <= other_len,
+ }
+ }
+}
+
+/// A constructor for array and slice patterns.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(super) struct Slice {
+ /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`.
+ array_len: Option<usize>,
+ /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`.
+ kind: SliceKind,
+}
+
+impl Slice {
+ fn new(array_len: Option<usize>, kind: SliceKind) -> Self {
+ let kind = match (array_len, kind) {
+ // If the middle `..` is empty, we effectively have a fixed-length pattern.
+ (Some(len), VarLen(prefix, suffix)) if prefix + suffix >= len => FixedLen(len),
+ _ => kind,
+ };
+ Slice { array_len, kind }
+ }
+
+ fn arity(self) -> usize {
+ self.kind.arity()
+ }
+
+ /// See `Constructor::is_covered_by`
+ fn is_covered_by(self, other: Self) -> bool {
+ other.kind.covers_length(self.arity())
+ }
+}
+
+/// This computes constructor splitting for variable-length slices, as explained at the top of the
+/// file.
+///
+/// A slice pattern `[x, .., y]` behaves like the infinite or-pattern `[x, y] | [x, _, y] | [x, _,
+/// _, y] | ...`. The corresponding value constructors are fixed-length array constructors above a
+/// given minimum length. We obviously can't list this infinitude of constructors. Thankfully,
+/// it turns out that for each finite set of slice patterns, all sufficiently large array lengths
+/// are equivalent.
+///
+/// Let's look at an example, where we are trying to split the last pattern:
+/// ```
+/// # fn foo(x: &[bool]) {
+/// match x {
+/// [true, true, ..] => {}
+/// [.., false, false] => {}
+/// [..] => {}
+/// }
+/// # }
+/// ```
+/// Here are the results of specialization for the first few lengths:
+/// ```
+/// # fn foo(x: &[bool]) { match x {
+/// // length 0
+/// [] => {}
+/// // length 1
+/// [_] => {}
+/// // length 2
+/// [true, true] => {}
+/// [false, false] => {}
+/// [_, _] => {}
+/// // length 3
+/// [true, true, _ ] => {}
+/// [_, false, false] => {}
+/// [_, _, _ ] => {}
+/// // length 4
+/// [true, true, _, _ ] => {}
+/// [_, _, false, false] => {}
+/// [_, _, _, _ ] => {}
+/// // length 5
+/// [true, true, _, _, _ ] => {}
+/// [_, _, _, false, false] => {}
+/// [_, _, _, _, _ ] => {}
+/// # _ => {}
+/// # }}
+/// ```
+///
+/// If we went above length 5, we would simply be inserting more columns full of wildcards in the
+/// middle. This means that the set of witnesses for length `l >= 5` if equivalent to the set for
+/// any other `l' >= 5`: simply add or remove wildcards in the middle to convert between them.
+///
+/// This applies to any set of slice patterns: there will be a length `L` above which all lengths
+/// behave the same. This is exactly what we need for constructor splitting. Therefore a
+/// variable-length slice can be split into a variable-length slice of minimal length `L`, and many
+/// fixed-length slices of lengths `< L`.
+///
+/// For each variable-length pattern `p` with a prefix of length `plₚ` and suffix of length `slₚ`,
+/// only the first `plₚ` and the last `slₚ` elements are examined. Therefore, as long as `L` is
+/// positive (to avoid concerns about empty types), all elements after the maximum prefix length
+/// and before the maximum suffix length are not examined by any variable-length pattern, and
+/// therefore can be added/removed without affecting them - creating equivalent patterns from any
+/// sufficiently-large length.
+///
+/// Of course, if fixed-length patterns exist, we must be sure that our length is large enough to
+/// miss them all, so we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))`
+///
+/// `max_slice` below will be made to have arity `L`.
+#[derive(Debug)]
+struct SplitVarLenSlice {
+ /// If the type is an array, this is its size.
+ array_len: Option<usize>,
+ /// The arity of the input slice.
+ arity: usize,
+ /// The smallest slice bigger than any slice seen. `max_slice.arity()` is the length `L`
+ /// described above.
+ max_slice: SliceKind,
+}
+
+impl SplitVarLenSlice {
+ fn new(prefix: usize, suffix: usize, array_len: Option<usize>) -> Self {
+ SplitVarLenSlice { array_len, arity: prefix + suffix, max_slice: VarLen(prefix, suffix) }
+ }
+
+ /// Pass a set of slices relative to which to split this one.
+ fn split(&mut self, slices: impl Iterator<Item = SliceKind>) {
+ let VarLen(max_prefix_len, max_suffix_len) = &mut self.max_slice else {
+ // No need to split
+ return;
+ };
+ // We grow `self.max_slice` to be larger than all slices encountered, as described above.
+ // For diagnostics, we keep the prefix and suffix lengths separate, but grow them so that
+ // `L = max_prefix_len + max_suffix_len`.
+ let mut max_fixed_len = 0;
+ for slice in slices {
+ match slice {
+ FixedLen(len) => {
+ max_fixed_len = cmp::max(max_fixed_len, len);
+ }
+ VarLen(prefix, suffix) => {
+ *max_prefix_len = cmp::max(*max_prefix_len, prefix);
+ *max_suffix_len = cmp::max(*max_suffix_len, suffix);
+ }
+ }
+ }
+ // We want `L = max(L, max_fixed_len + 1)`, modulo the fact that we keep prefix and
+ // suffix separate.
+ if max_fixed_len + 1 >= *max_prefix_len + *max_suffix_len {
+ // The subtraction can't overflow thanks to the above check.
+ // The new `max_prefix_len` is larger than its previous value.
+ *max_prefix_len = max_fixed_len + 1 - *max_suffix_len;
+ }
+
+ // We cap the arity of `max_slice` at the array size.
+ match self.array_len {
+ Some(len) if self.max_slice.arity() >= len => self.max_slice = FixedLen(len),
+ _ => {}
+ }
+ }
+
+ /// Iterate over the partition of this slice.
+ fn iter<'a>(&'a self) -> impl Iterator<Item = Slice> + Captures<'a> {
+ let smaller_lengths = match self.array_len {
+ // The only admissible fixed-length slice is one of the array size. Whether `max_slice`
+ // is fixed-length or variable-length, it will be the only relevant slice to output
+ // here.
+ Some(_) => 0..0, // empty range
+ // We cover all arities in the range `(self.arity..infinity)`. We split that range into
+ // two: lengths smaller than `max_slice.arity()` are treated independently as
+ // fixed-lengths slices, and lengths above are captured by `max_slice`.
+ None => self.arity..self.max_slice.arity(),
+ };
+ smaller_lengths
+ .map(FixedLen)
+ .chain(once(self.max_slice))
+ .map(move |kind| Slice::new(self.array_len, kind))
+ }
+}
+
+/// A value can be decomposed into a constructor applied to some fields. This struct represents
+/// the constructor. See also `Fields`.
+///
+/// `pat_constructor` retrieves the constructor corresponding to a pattern.
+/// `specialize_constructor` returns the list of fields corresponding to a pattern, given a
+/// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and
+/// `Fields`.
+#[derive(Clone, Debug, PartialEq)]
+pub(super) enum Constructor<'tcx> {
+ /// The constructor for patterns that have a single constructor, like tuples, struct patterns
+ /// and fixed-length arrays.
+ Single,
+ /// Enum variants.
+ Variant(VariantIdx),
+ /// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
+ IntRange(IntRange),
+ /// Ranges of floating-point literal values (`2.0..=5.2`).
+ FloatRange(mir::ConstantKind<'tcx>, mir::ConstantKind<'tcx>, RangeEnd),
+ /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
+ Str(mir::ConstantKind<'tcx>),
+ /// Array and slice patterns.
+ Slice(Slice),
+ /// Constants that must not be matched structurally. They are treated as black
+ /// boxes for the purposes of exhaustiveness: we must not inspect them, and they
+ /// don't count towards making a match exhaustive.
+ Opaque,
+ /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used
+ /// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
+ NonExhaustive,
+ /// Stands for constructors that are not seen in the matrix, as explained in the documentation
+ /// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns`
+ /// lint.
+ Missing { nonexhaustive_enum_missing_real_variants: bool },
+ /// Wildcard pattern.
+ Wildcard,
+ /// Or-pattern.
+ Or,
+}
+
+impl<'tcx> Constructor<'tcx> {
+ pub(super) fn is_wildcard(&self) -> bool {
+ matches!(self, Wildcard)
+ }
+
+ pub(super) fn is_non_exhaustive(&self) -> bool {
+ matches!(self, NonExhaustive)
+ }
+
+ fn as_int_range(&self) -> Option<&IntRange> {
+ match self {
+ IntRange(range) => Some(range),
+ _ => None,
+ }
+ }
+
+ fn as_slice(&self) -> Option<Slice> {
+ match self {
+ Slice(slice) => Some(*slice),
+ _ => None,
+ }
+ }
+
+ /// Checks if the `Constructor` is a variant and `TyCtxt::eval_stability` returns
+ /// `EvalResult::Deny { .. }`.
+ ///
+ /// This means that the variant has a stdlib unstable feature marking it.
+ pub(super) fn is_unstable_variant(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> bool {
+ if let Constructor::Variant(idx) = self && let ty::Adt(adt, _) = pcx.ty.kind() {
+ let variant_def_id = adt.variant(*idx).def_id;
+ // Filter variants that depend on a disabled unstable feature.
+ return matches!(
+ pcx.cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
+ EvalResult::Deny { .. }
+ );
+ }
+ false
+ }
+
+ /// Checks if the `Constructor` is a `Constructor::Variant` with a `#[doc(hidden)]`
+ /// attribute from a type not local to the current crate.
+ pub(super) fn is_doc_hidden_variant(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> bool {
+ if let Constructor::Variant(idx) = self && let ty::Adt(adt, _) = pcx.ty.kind() {
+ let variant_def_id = adt.variants()[*idx].def_id;
+ return pcx.cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
+ }
+ false
+ }
+
+ fn variant_index_for_adt(&self, adt: ty::AdtDef<'tcx>) -> VariantIdx {
+ match *self {
+ Variant(idx) => idx,
+ Single => {
+ assert!(!adt.is_enum());
+ VariantIdx::new(0)
+ }
+ _ => bug!("bad constructor {:?} for adt {:?}", self, adt),
+ }
+ }
+
+ /// The number of fields for this constructor. This must be kept in sync with
+ /// `Fields::wildcards`.
+ pub(super) fn arity(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> usize {
+ match self {
+ Single | Variant(_) => match pcx.ty.kind() {
+ ty::Tuple(fs) => fs.len(),
+ ty::Ref(..) => 1,
+ ty::Adt(adt, ..) => {
+ if adt.is_box() {
+ // The only legal patterns of type `Box` (outside `std`) are `_` and box
+ // patterns. If we're here we can assume this is a box pattern.
+ 1
+ } else {
+ let variant = &adt.variant(self.variant_index_for_adt(*adt));
+ Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count()
+ }
+ }
+ _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty),
+ },
+ Slice(slice) => slice.arity(),
+ Str(..)
+ | FloatRange(..)
+ | IntRange(..)
+ | NonExhaustive
+ | Opaque
+ | Missing { .. }
+ | Wildcard => 0,
+ Or => bug!("The `Or` constructor doesn't have a fixed arity"),
+ }
+ }
+
+ /// Some constructors (namely `Wildcard`, `IntRange` and `Slice`) actually stand for a set of actual
+ /// constructors (like variants, integers or fixed-sized slices). When specializing for these
+ /// constructors, we want to be specialising for the actual underlying constructors.
+ /// Naively, we would simply return the list of constructors they correspond to. We instead are
+ /// more clever: if there are constructors that we know will behave the same wrt the current
+ /// matrix, we keep them grouped. For example, all slices of a sufficiently large length
+ /// will either be all useful or all non-useful with a given matrix.
+ ///
+ /// See the branches for details on how the splitting is done.
+ ///
+ /// This function may discard some irrelevant constructors if this preserves behavior and
+ /// diagnostics. Eg. for the `_` case, we ignore the constructors already present in the
+ /// matrix, unless all of them are.
+ pub(super) fn split<'a>(
+ &self,
+ pcx: &PatCtxt<'_, '_, 'tcx>,
+ ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone,
+ ) -> SmallVec<[Self; 1]>
+ where
+ 'tcx: 'a,
+ {
+ match self {
+ Wildcard => {
+ let mut split_wildcard = SplitWildcard::new(pcx);
+ split_wildcard.split(pcx, ctors);
+ split_wildcard.into_ctors(pcx)
+ }
+ // Fast-track if the range is trivial. In particular, we don't do the overlapping
+ // ranges check.
+ IntRange(ctor_range) if !ctor_range.is_singleton() => {
+ let mut split_range = SplitIntRange::new(ctor_range.clone());
+ let int_ranges = ctors.filter_map(|ctor| ctor.as_int_range());
+ split_range.split(int_ranges.cloned());
+ split_range.iter().map(IntRange).collect()
+ }
+ &Slice(Slice { kind: VarLen(self_prefix, self_suffix), array_len }) => {
+ let mut split_self = SplitVarLenSlice::new(self_prefix, self_suffix, array_len);
+ let slices = ctors.filter_map(|c| c.as_slice()).map(|s| s.kind);
+ split_self.split(slices);
+ split_self.iter().map(Slice).collect()
+ }
+ // Any other constructor can be used unchanged.
+ _ => smallvec![self.clone()],
+ }
+ }
+
+ /// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`.
+ /// For the simple cases, this is simply checking for equality. For the "grouped" constructors,
+ /// this checks for inclusion.
+ // We inline because this has a single call site in `Matrix::specialize_constructor`.
+ #[inline]
+ pub(super) fn is_covered_by<'p>(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool {
+ // This must be kept in sync with `is_covered_by_any`.
+ match (self, other) {
+ // Wildcards cover anything
+ (_, Wildcard) => true,
+ // The missing ctors are not covered by anything in the matrix except wildcards.
+ (Missing { .. } | Wildcard, _) => false,
+
+ (Single, Single) => true,
+ (Variant(self_id), Variant(other_id)) => self_id == other_id,
+
+ (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range),
+ (
+ FloatRange(self_from, self_to, self_end),
+ FloatRange(other_from, other_to, other_end),
+ ) => {
+ match (
+ compare_const_vals(pcx.cx.tcx, *self_to, *other_to, pcx.cx.param_env),
+ compare_const_vals(pcx.cx.tcx, *self_from, *other_from, pcx.cx.param_env),
+ ) {
+ (Some(to), Some(from)) => {
+ (from == Ordering::Greater || from == Ordering::Equal)
+ && (to == Ordering::Less
+ || (other_end == self_end && to == Ordering::Equal))
+ }
+ _ => false,
+ }
+ }
+ (Str(self_val), Str(other_val)) => {
+ // FIXME Once valtrees are available we can directly use the bytes
+ // in the `Str` variant of the valtree for the comparison here.
+ self_val == other_val
+ }
+ (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice),
+
+ // We are trying to inspect an opaque constant. Thus we skip the row.
+ (Opaque, _) | (_, Opaque) => false,
+ // Only a wildcard pattern can match the special extra constructor.
+ (NonExhaustive, _) => false,
+
+ _ => span_bug!(
+ pcx.span,
+ "trying to compare incompatible constructors {:?} and {:?}",
+ self,
+ other
+ ),
+ }
+ }
+
+ /// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is
+ /// assumed to be built from `matrix.head_ctors()` with wildcards filtered out, and `self` is
+ /// assumed to have been split from a wildcard.
+ fn is_covered_by_any<'p>(
+ &self,
+ pcx: &PatCtxt<'_, 'p, 'tcx>,
+ used_ctors: &[Constructor<'tcx>],
+ ) -> bool {
+ if used_ctors.is_empty() {
+ return false;
+ }
+
+ // This must be kept in sync with `is_covered_by`.
+ match self {
+ // If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s.
+ Single => !used_ctors.is_empty(),
+ Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)),
+ IntRange(range) => used_ctors
+ .iter()
+ .filter_map(|c| c.as_int_range())
+ .any(|other| range.is_covered_by(other)),
+ Slice(slice) => used_ctors
+ .iter()
+ .filter_map(|c| c.as_slice())
+ .any(|other| slice.is_covered_by(other)),
+ // This constructor is never covered by anything else
+ NonExhaustive => false,
+ Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => {
+ span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self)
+ }
+ }
+ }
+}
+
+/// A wildcard constructor that we split relative to the constructors in the matrix, as explained
+/// at the top of the file.
+///
+/// A constructor that is not present in the matrix rows will only be covered by the rows that have
+/// wildcards. Thus we can group all of those constructors together; we call them "missing
+/// constructors". Splitting a wildcard would therefore list all present constructors individually
+/// (or grouped if they are integers or slices), and then all missing constructors together as a
+/// group.
+///
+/// However we can go further: since any constructor will match the wildcard rows, and having more
+/// rows can only reduce the amount of usefulness witnesses, we can skip the present constructors
+/// and only try the missing ones.
+/// This will not preserve the whole list of witnesses, but will preserve whether the list is empty
+/// or not. In fact this is quite natural from the point of view of diagnostics too. This is done
+/// in `to_ctors`: in some cases we only return `Missing`.
+#[derive(Debug)]
+pub(super) struct SplitWildcard<'tcx> {
+ /// Constructors seen in the matrix.
+ matrix_ctors: Vec<Constructor<'tcx>>,
+ /// All the constructors for this type
+ all_ctors: SmallVec<[Constructor<'tcx>; 1]>,
+}
+
+impl<'tcx> SplitWildcard<'tcx> {
+ pub(super) fn new<'p>(pcx: &PatCtxt<'_, 'p, 'tcx>) -> Self {
+ debug!("SplitWildcard::new({:?})", pcx.ty);
+ let cx = pcx.cx;
+ let make_range = |start, end| {
+ IntRange(
+ // `unwrap()` is ok because we know the type is an integer.
+ IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(),
+ )
+ };
+ // This determines the set of all possible constructors for the type `pcx.ty`. For numbers,
+ // arrays and slices we use ranges and variable-length slices when appropriate.
+ //
+ // If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that
+ // are statically impossible. E.g., for `Option<!>`, we do not include `Some(_)` in the
+ // returned list of constructors.
+ // Invariant: this is empty if and only if the type is uninhabited (as determined by
+ // `cx.is_uninhabited()`).
+ let all_ctors = match pcx.ty.kind() {
+ ty::Bool => smallvec![make_range(0, 1)],
+ ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
+ let len = len.eval_usize(cx.tcx, cx.param_env) as usize;
+ if len != 0 && cx.is_uninhabited(*sub_ty) {
+ smallvec![]
+ } else {
+ smallvec![Slice(Slice::new(Some(len), VarLen(0, 0)))]
+ }
+ }
+ // Treat arrays of a constant but unknown length like slices.
+ ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
+ let kind = if cx.is_uninhabited(*sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
+ smallvec![Slice(Slice::new(None, kind))]
+ }
+ ty::Adt(def, substs) if def.is_enum() => {
+ // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
+ // additional "unknown" constructor.
+ // There is no point in enumerating all possible variants, because the user can't
+ // actually match against them all themselves. So we always return only the fictitious
+ // constructor.
+ // E.g., in an example like:
+ //
+ // ```
+ // let err: io::ErrorKind = ...;
+ // match err {
+ // io::ErrorKind::NotFound => {},
+ // }
+ // ```
+ //
+ // we don't want to show every possible IO error, but instead have only `_` as the
+ // witness.
+ let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
+
+ let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
+
+ // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
+ // as though it had an "unknown" constructor to avoid exposing its emptiness. The
+ // exception is if the pattern is at the top level, because we want empty matches to be
+ // considered exhaustive.
+ let is_secretly_empty =
+ def.variants().is_empty() && !is_exhaustive_pat_feature && !pcx.is_top_level;
+
+ let mut ctors: SmallVec<[_; 1]> = def
+ .variants()
+ .iter_enumerated()
+ .filter(|(_, v)| {
+ // If `exhaustive_patterns` is enabled, we exclude variants known to be
+ // uninhabited.
+ let is_uninhabited = is_exhaustive_pat_feature
+ && v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env)
+ .contains(cx.tcx, cx.module);
+ !is_uninhabited
+ })
+ .map(|(idx, _)| Variant(idx))
+ .collect();
+
+ if is_secretly_empty || is_declared_nonexhaustive {
+ ctors.push(NonExhaustive);
+ }
+ ctors
+ }
+ ty::Char => {
+ smallvec![
+ // The valid Unicode Scalar Value ranges.
+ make_range('\u{0000}' as u128, '\u{D7FF}' as u128),
+ make_range('\u{E000}' as u128, '\u{10FFFF}' as u128),
+ ]
+ }
+ ty::Int(_) | ty::Uint(_)
+ if pcx.ty.is_ptr_sized_integral()
+ && !cx.tcx.features().precise_pointer_size_matching =>
+ {
+ // `usize`/`isize` are not allowed to be matched exhaustively unless the
+ // `precise_pointer_size_matching` feature is enabled. So we treat those types like
+ // `#[non_exhaustive]` enums by returning a special unmatchable constructor.
+ smallvec![NonExhaustive]
+ }
+ &ty::Int(ity) => {
+ let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128;
+ let min = 1u128 << (bits - 1);
+ let max = min - 1;
+ smallvec![make_range(min, max)]
+ }
+ &ty::Uint(uty) => {
+ let size = Integer::from_uint_ty(&cx.tcx, uty).size();
+ let max = size.truncate(u128::MAX);
+ smallvec![make_range(0, max)]
+ }
+ // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot
+ // expose its emptiness. The exception is if the pattern is at the top level, because we
+ // want empty matches to be considered exhaustive.
+ ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => {
+ smallvec![NonExhaustive]
+ }
+ ty::Never => smallvec![],
+ _ if cx.is_uninhabited(pcx.ty) => smallvec![],
+ ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => smallvec![Single],
+ // This type is one for which we cannot list constructors, like `str` or `f64`.
+ _ => smallvec![NonExhaustive],
+ };
+
+ SplitWildcard { matrix_ctors: Vec::new(), all_ctors }
+ }
+
+ /// Pass a set of constructors relative to which to split this one. Don't call twice, it won't
+ /// do what you want.
+ pub(super) fn split<'a>(
+ &mut self,
+ pcx: &PatCtxt<'_, '_, 'tcx>,
+ ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone,
+ ) where
+ 'tcx: 'a,
+ {
+ // Since `all_ctors` never contains wildcards, this won't recurse further.
+ self.all_ctors =
+ self.all_ctors.iter().flat_map(|ctor| ctor.split(pcx, ctors.clone())).collect();
+ self.matrix_ctors = ctors.filter(|c| !c.is_wildcard()).cloned().collect();
+ }
+
+ /// Whether there are any value constructors for this type that are not present in the matrix.
+ fn any_missing(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> bool {
+ self.iter_missing(pcx).next().is_some()
+ }
+
+ /// Iterate over the constructors for this type that are not present in the matrix.
+ pub(super) fn iter_missing<'a, 'p>(
+ &'a self,
+ pcx: &'a PatCtxt<'a, 'p, 'tcx>,
+ ) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'p> {
+ self.all_ctors.iter().filter(move |ctor| !ctor.is_covered_by_any(pcx, &self.matrix_ctors))
+ }
+
+ /// Return the set of constructors resulting from splitting the wildcard. As explained at the
+ /// top of the file, if any constructors are missing we can ignore the present ones.
+ fn into_ctors(self, pcx: &PatCtxt<'_, '_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> {
+ if self.any_missing(pcx) {
+ // Some constructors are missing, thus we can specialize with the special `Missing`
+ // constructor, which stands for those constructors that are not seen in the matrix,
+ // and matches the same rows as any of them (namely the wildcard rows). See the top of
+ // the file for details.
+ // However, when all constructors are missing we can also specialize with the full
+ // `Wildcard` constructor. The difference will depend on what we want in diagnostics.
+
+ // If some constructors are missing, we typically want to report those constructors,
+ // e.g.:
+ // ```
+ // enum Direction { N, S, E, W }
+ // let Direction::N = ...;
+ // ```
+ // we can report 3 witnesses: `S`, `E`, and `W`.
+ //
+ // However, if the user didn't actually specify a constructor
+ // in this arm, e.g., in
+ // ```
+ // let x: (Direction, Direction, bool) = ...;
+ // let (_, _, false) = x;
+ // ```
+ // we don't want to show all 16 possible witnesses `(<direction-1>, <direction-2>,
+ // true)` - we are satisfied with `(_, _, true)`. So if all constructors are missing we
+ // prefer to report just a wildcard `_`.
+ //
+ // The exception is: if we are at the top-level, for example in an empty match, we
+ // sometimes prefer reporting the list of constructors instead of just `_`.
+ let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty);
+ let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing {
+ if pcx.is_non_exhaustive {
+ Missing {
+ nonexhaustive_enum_missing_real_variants: self
+ .iter_missing(pcx)
+ .any(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx))),
+ }
+ } else {
+ Missing { nonexhaustive_enum_missing_real_variants: false }
+ }
+ } else {
+ Wildcard
+ };
+ return smallvec![ctor];
+ }
+
+ // All the constructors are present in the matrix, so we just go through them all.
+ self.all_ctors
+ }
+}
+
+/// A value can be decomposed into a constructor applied to some fields. This struct represents
+/// those fields, generalized to allow patterns in each field. See also `Constructor`.
+///
+/// This is constructed for a constructor using [`Fields::wildcards()`]. The idea is that
+/// [`Fields::wildcards()`] constructs a list of fields where all entries are wildcards, and then
+/// given a pattern we fill some of the fields with its subpatterns.
+/// In the following example `Fields::wildcards` returns `[_, _, _, _]`. Then in
+/// `extract_pattern_arguments` we fill some of the entries, and the result is
+/// `[Some(0), _, _, _]`.
+/// ```compile_fail,E0004
+/// # fn foo() -> [Option<u8>; 4] { [None; 4] }
+/// let x: [Option<u8>; 4] = foo();
+/// match x {
+/// [Some(0), ..] => {}
+/// }
+/// ```
+///
+/// Note that the number of fields of a constructor may not match the fields declared in the
+/// original struct/variant. This happens if a private or `non_exhaustive` field is uninhabited,
+/// because the code mustn't observe that it is uninhabited. In that case that field is not
+/// included in `fields`. For that reason, when you have a `mir::Field` you must use
+/// `index_with_declared_idx`.
+#[derive(Debug, Clone, Copy)]
+pub(super) struct Fields<'p, 'tcx> {
+ fields: &'p [DeconstructedPat<'p, 'tcx>],
+}
+
+impl<'p, 'tcx> Fields<'p, 'tcx> {
+ fn empty() -> Self {
+ Fields { fields: &[] }
+ }
+
+ fn singleton(cx: &MatchCheckCtxt<'p, 'tcx>, field: DeconstructedPat<'p, 'tcx>) -> Self {
+ let field: &_ = cx.pattern_arena.alloc(field);
+ Fields { fields: std::slice::from_ref(field) }
+ }
+
+ pub(super) fn from_iter(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ fields: impl IntoIterator<Item = DeconstructedPat<'p, 'tcx>>,
+ ) -> Self {
+ let fields: &[_] = cx.pattern_arena.alloc_from_iter(fields);
+ Fields { fields }
+ }
+
+ fn wildcards_from_tys(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ tys: impl IntoIterator<Item = Ty<'tcx>>,
+ ) -> Self {
+ Fields::from_iter(cx, tys.into_iter().map(DeconstructedPat::wildcard))
+ }
+
+ // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
+ // uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
+ // This lists the fields we keep along with their types.
+ fn list_variant_nonhidden_fields<'a>(
+ cx: &'a MatchCheckCtxt<'p, 'tcx>,
+ ty: Ty<'tcx>,
+ variant: &'a VariantDef,
+ ) -> impl Iterator<Item = (Field, Ty<'tcx>)> + Captures<'a> + Captures<'p> {
+ let ty::Adt(adt, substs) = ty.kind() else { bug!() };
+ // Whether we must not match the fields of this variant exhaustively.
+ let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local();
+
+ variant.fields.iter().enumerate().filter_map(move |(i, field)| {
+ let ty = field.ty(cx.tcx, substs);
+ // `field.ty()` doesn't normalize after substituting.
+ let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+ let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
+ let is_uninhabited = cx.is_uninhabited(ty);
+
+ if is_uninhabited && (!is_visible || is_non_exhaustive) {
+ None
+ } else {
+ Some((Field::new(i), ty))
+ }
+ })
+ }
+
+ /// Creates a new list of wildcard fields for a given constructor. The result must have a
+ /// length of `constructor.arity()`.
+ #[instrument(level = "trace")]
+ pub(super) fn wildcards(pcx: &PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self {
+ let ret = match constructor {
+ Single | Variant(_) => match pcx.ty.kind() {
+ ty::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter()),
+ ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty)),
+ ty::Adt(adt, substs) => {
+ if adt.is_box() {
+ // The only legal patterns of type `Box` (outside `std`) are `_` and box
+ // patterns. If we're here we can assume this is a box pattern.
+ Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0)))
+ } else {
+ let variant = &adt.variant(constructor.variant_index_for_adt(*adt));
+ let tys = Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant)
+ .map(|(_, ty)| ty);
+ Fields::wildcards_from_tys(pcx.cx, tys)
+ }
+ }
+ _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx),
+ },
+ Slice(slice) => match *pcx.ty.kind() {
+ ty::Slice(ty) | ty::Array(ty, _) => {
+ let arity = slice.arity();
+ Fields::wildcards_from_tys(pcx.cx, (0..arity).map(|_| ty))
+ }
+ _ => bug!("bad slice pattern {:?} {:?}", constructor, pcx),
+ },
+ Str(..)
+ | FloatRange(..)
+ | IntRange(..)
+ | NonExhaustive
+ | Opaque
+ | Missing { .. }
+ | Wildcard => Fields::empty(),
+ Or => {
+ bug!("called `Fields::wildcards` on an `Or` ctor")
+ }
+ };
+ debug!(?ret);
+ ret
+ }
+
+ /// Returns the list of patterns.
+ pub(super) fn iter_patterns<'a>(
+ &'a self,
+ ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
+ self.fields.iter()
+ }
+}
+
+/// Values and patterns can be represented as a constructor applied to some fields. This represents
+/// a pattern in this form.
+/// This also keeps track of whether the pattern has been found reachable during analysis. For this
+/// reason we should be careful not to clone patterns for which we care about that. Use
+/// `clone_and_forget_reachability` if you're sure.
+pub(crate) struct DeconstructedPat<'p, 'tcx> {
+ ctor: Constructor<'tcx>,
+ fields: Fields<'p, 'tcx>,
+ ty: Ty<'tcx>,
+ span: Span,
+ reachable: Cell<bool>,
+}
+
+impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
+ pub(super) fn wildcard(ty: Ty<'tcx>) -> Self {
+ Self::new(Wildcard, Fields::empty(), ty, DUMMY_SP)
+ }
+
+ pub(super) fn new(
+ ctor: Constructor<'tcx>,
+ fields: Fields<'p, 'tcx>,
+ ty: Ty<'tcx>,
+ span: Span,
+ ) -> Self {
+ DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) }
+ }
+
+ /// Construct a pattern that matches everything that starts with this constructor.
+ /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
+ /// `Some(_)`.
+ pub(super) fn wild_from_ctor(pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
+ let fields = Fields::wildcards(pcx, &ctor);
+ DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP)
+ }
+
+ /// Clone this value. This method emphasizes that cloning loses reachability information and
+ /// should be done carefully.
+ pub(super) fn clone_and_forget_reachability(&self) -> Self {
+ DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty, self.span)
+ }
+
+ pub(crate) fn from_pat(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &Pat<'tcx>) -> Self {
+ let mkpat = |pat| DeconstructedPat::from_pat(cx, pat);
+ let ctor;
+ let fields;
+ match pat.kind.as_ref() {
+ PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern),
+ PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat),
+ PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
+ ctor = Wildcard;
+ fields = Fields::empty();
+ }
+ PatKind::Deref { subpattern } => {
+ ctor = Single;
+ fields = Fields::singleton(cx, mkpat(subpattern));
+ }
+ PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
+ match pat.ty.kind() {
+ ty::Tuple(fs) => {
+ ctor = Single;
+ let mut wilds: SmallVec<[_; 2]> =
+ fs.iter().map(DeconstructedPat::wildcard).collect();
+ for pat in subpatterns {
+ wilds[pat.field.index()] = mkpat(&pat.pattern);
+ }
+ fields = Fields::from_iter(cx, wilds);
+ }
+ ty::Adt(adt, substs) if adt.is_box() => {
+ // The only legal patterns of type `Box` (outside `std`) are `_` and box
+ // patterns. If we're here we can assume this is a box pattern.
+ // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
+ // _)` or a box pattern. As a hack to avoid an ICE with the former, we
+ // ignore other fields than the first one. This will trigger an error later
+ // anyway.
+ // See https://github.com/rust-lang/rust/issues/82772 ,
+ // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977
+ // The problem is that we can't know from the type whether we'll match
+ // normally or through box-patterns. We'll have to figure out a proper
+ // solution when we introduce generalized deref patterns. Also need to
+ // prevent mixing of those two options.
+ let pat = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
+ let pat = if let Some(pat) = pat {
+ mkpat(&pat.pattern)
+ } else {
+ DeconstructedPat::wildcard(substs.type_at(0))
+ };
+ ctor = Single;
+ fields = Fields::singleton(cx, pat);
+ }
+ ty::Adt(adt, _) => {
+ ctor = match pat.kind.as_ref() {
+ PatKind::Leaf { .. } => Single,
+ PatKind::Variant { variant_index, .. } => Variant(*variant_index),
+ _ => bug!(),
+ };
+ let variant = &adt.variant(ctor.variant_index_for_adt(*adt));
+ // For each field in the variant, we store the relevant index into `self.fields` if any.
+ let mut field_id_to_id: Vec<Option<usize>> =
+ (0..variant.fields.len()).map(|_| None).collect();
+ let tys = Fields::list_variant_nonhidden_fields(cx, pat.ty, variant)
+ .enumerate()
+ .map(|(i, (field, ty))| {
+ field_id_to_id[field.index()] = Some(i);
+ ty
+ });
+ let mut wilds: SmallVec<[_; 2]> =
+ tys.map(DeconstructedPat::wildcard).collect();
+ for pat in subpatterns {
+ if let Some(i) = field_id_to_id[pat.field.index()] {
+ wilds[i] = mkpat(&pat.pattern);
+ }
+ }
+ fields = Fields::from_iter(cx, wilds);
+ }
+ _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
+ }
+ }
+ PatKind::Constant { value } => {
+ if let Some(int_range) = IntRange::from_constant(cx.tcx, cx.param_env, *value) {
+ ctor = IntRange(int_range);
+ fields = Fields::empty();
+ } else {
+ match pat.ty.kind() {
+ ty::Float(_) => {
+ ctor = FloatRange(*value, *value, RangeEnd::Included);
+ fields = Fields::empty();
+ }
+ ty::Ref(_, t, _) if t.is_str() => {
+ // We want a `&str` constant to behave like a `Deref` pattern, to be compatible
+ // with other `Deref` patterns. This could have been done in `const_to_pat`,
+ // but that causes issues with the rest of the matching code.
+ // So here, the constructor for a `"foo"` pattern is `&` (represented by
+ // `Single`), and has one field. That field has constructor `Str(value)` and no
+ // fields.
+ // Note: `t` is `str`, not `&str`.
+ let subpattern =
+ DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span);
+ ctor = Single;
+ fields = Fields::singleton(cx, subpattern)
+ }
+ // All constants that can be structurally matched have already been expanded
+ // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
+ // opaque.
+ _ => {
+ ctor = Opaque;
+ fields = Fields::empty();
+ }
+ }
+ }
+ }
+ &PatKind::Range(PatRange { lo, hi, end }) => {
+ let ty = lo.ty();
+ ctor = if let Some(int_range) = IntRange::from_range(
+ cx.tcx,
+ lo.eval_bits(cx.tcx, cx.param_env, lo.ty()),
+ hi.eval_bits(cx.tcx, cx.param_env, hi.ty()),
+ ty,
+ &end,
+ ) {
+ IntRange(int_range)
+ } else {
+ FloatRange(lo, hi, end)
+ };
+ fields = Fields::empty();
+ }
+ PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
+ let array_len = match pat.ty.kind() {
+ ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env) as usize),
+ ty::Slice(_) => None,
+ _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
+ };
+ let kind = if slice.is_some() {
+ VarLen(prefix.len(), suffix.len())
+ } else {
+ FixedLen(prefix.len() + suffix.len())
+ };
+ ctor = Slice(Slice::new(array_len, kind));
+ fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat));
+ }
+ PatKind::Or { .. } => {
+ ctor = Or;
+ let pats = expand_or_pat(pat);
+ fields = Fields::from_iter(cx, pats.into_iter().map(mkpat));
+ }
+ }
+ DeconstructedPat::new(ctor, fields, pat.ty, pat.span)
+ }
+
+ pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> {
+ let is_wildcard = |pat: &Pat<'_>| {
+ matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild)
+ };
+ let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx));
+ let pat = match &self.ctor {
+ Single | Variant(_) => match self.ty.kind() {
+ ty::Tuple(..) => PatKind::Leaf {
+ subpatterns: subpatterns
+ .enumerate()
+ .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p })
+ .collect(),
+ },
+ ty::Adt(adt_def, _) if adt_def.is_box() => {
+ // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
+ // of `std`). So this branch is only reachable when the feature is enabled and
+ // the pattern is a box pattern.
+ PatKind::Deref { subpattern: subpatterns.next().unwrap() }
+ }
+ ty::Adt(adt_def, substs) => {
+ let variant_index = self.ctor.variant_index_for_adt(*adt_def);
+ let variant = &adt_def.variant(variant_index);
+ let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant)
+ .zip(subpatterns)
+ .map(|((field, _ty), pattern)| FieldPat { field, pattern })
+ .collect();
+
+ if adt_def.is_enum() {
+ PatKind::Variant { adt_def: *adt_def, substs, variant_index, subpatterns }
+ } else {
+ PatKind::Leaf { subpatterns }
+ }
+ }
+ // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
+ // be careful to reconstruct the correct constant pattern here. However a string
+ // literal pattern will never be reported as a non-exhaustiveness witness, so we
+ // ignore this issue.
+ ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
+ _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty),
+ },
+ Slice(slice) => {
+ match slice.kind {
+ FixedLen(_) => PatKind::Slice {
+ prefix: subpatterns.collect(),
+ slice: None,
+ suffix: vec![],
+ },
+ VarLen(prefix, _) => {
+ let mut subpatterns = subpatterns.peekable();
+ let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
+ if slice.array_len.is_some() {
+ // Improves diagnostics a bit: if the type is a known-size array, instead
+ // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
+ // This is incorrect if the size is not known, since `[_, ..]` captures
+ // arrays of lengths `>= 1` whereas `[..]` captures any length.
+ while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
+ prefix.pop();
+ }
+ while subpatterns.peek().is_some()
+ && is_wildcard(subpatterns.peek().unwrap())
+ {
+ subpatterns.next();
+ }
+ }
+ let suffix: Vec<_> = subpatterns.collect();
+ let wild = Pat::wildcard_from_ty(self.ty);
+ PatKind::Slice { prefix, slice: Some(wild), suffix }
+ }
+ }
+ }
+ &Str(value) => PatKind::Constant { value },
+ &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
+ IntRange(range) => return range.to_pat(cx.tcx, self.ty),
+ Wildcard | NonExhaustive => PatKind::Wild,
+ Missing { .. } => bug!(
+ "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
+ `Missing` should have been processed in `apply_constructors`"
+ ),
+ Opaque | Or => {
+ bug!("can't convert to pattern: {:?}", self)
+ }
+ };
+
+ Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(pat) }
+ }
+
+ pub(super) fn is_or_pat(&self) -> bool {
+ matches!(self.ctor, Or)
+ }
+
+ pub(super) fn ctor(&self) -> &Constructor<'tcx> {
+ &self.ctor
+ }
+ pub(super) fn ty(&self) -> Ty<'tcx> {
+ self.ty
+ }
+ pub(super) fn span(&self) -> Span {
+ self.span
+ }
+
+ pub(super) fn iter_fields<'a>(
+ &'a self,
+ ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
+ self.fields.iter_patterns()
+ }
+
+ /// Specialize this pattern with a constructor.
+ /// `other_ctor` can be different from `self.ctor`, but must be covered by it.
+ pub(super) fn specialize<'a>(
+ &'a self,
+ pcx: &PatCtxt<'_, 'p, 'tcx>,
+ other_ctor: &Constructor<'tcx>,
+ ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
+ match (&self.ctor, other_ctor) {
+ (Wildcard, _) => {
+ // We return a wildcard for each field of `other_ctor`.
+ Fields::wildcards(pcx, other_ctor).iter_patterns().collect()
+ }
+ (Slice(self_slice), Slice(other_slice))
+ if self_slice.arity() != other_slice.arity() =>
+ {
+ // The only tricky case: two slices of different arity. Since `self_slice` covers
+ // `other_slice`, `self_slice` must be `VarLen`, i.e. of the form
+ // `[prefix, .., suffix]`. Moreover `other_slice` is guaranteed to have a larger
+ // arity. So we fill the middle part with enough wildcards to reach the length of
+ // the new, larger slice.
+ match self_slice.kind {
+ FixedLen(_) => bug!("{:?} doesn't cover {:?}", self_slice, other_slice),
+ VarLen(prefix, suffix) => {
+ let (ty::Slice(inner_ty) | ty::Array(inner_ty, _)) = *self.ty.kind() else {
+ bug!("bad slice pattern {:?} {:?}", self.ctor, self.ty);
+ };
+ let prefix = &self.fields.fields[..prefix];
+ let suffix = &self.fields.fields[self_slice.arity() - suffix..];
+ let wildcard: &_ =
+ pcx.cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty));
+ let extra_wildcards = other_slice.arity() - self_slice.arity();
+ let extra_wildcards = (0..extra_wildcards).map(|_| wildcard);
+ prefix.iter().chain(extra_wildcards).chain(suffix).collect()
+ }
+ }
+ }
+ _ => self.fields.iter_patterns().collect(),
+ }
+ }
+
+ /// We keep track for each pattern if it was ever reachable during the analysis. This is used
+ /// with `unreachable_spans` to report unreachable subpatterns arising from or patterns.
+ pub(super) fn set_reachable(&self) {
+ self.reachable.set(true)
+ }
+ pub(super) fn is_reachable(&self) -> bool {
+ self.reachable.get()
+ }
+
+ /// Report the spans of subpatterns that were not reachable, if any.
+ pub(super) fn unreachable_spans(&self) -> Vec<Span> {
+ let mut spans = Vec::new();
+ self.collect_unreachable_spans(&mut spans);
+ spans
+ }
+
+ fn collect_unreachable_spans(&self, spans: &mut Vec<Span>) {
+ // We don't look at subpatterns if we already reported the whole pattern as unreachable.
+ if !self.is_reachable() {
+ spans.push(self.span);
+ } else {
+ for p in self.iter_fields() {
+ p.collect_unreachable_spans(spans);
+ }
+ }
+ }
+}
+
+/// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a
+/// `Display` impl.
+impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Printing lists is a chore.
+ let mut first = true;
+ let mut start_or_continue = |s| {
+ if first {
+ first = false;
+ ""
+ } else {
+ s
+ }
+ };
+ let mut start_or_comma = || start_or_continue(", ");
+
+ match &self.ctor {
+ Single | Variant(_) => match self.ty.kind() {
+ ty::Adt(def, _) if def.is_box() => {
+ // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
+ // of `std`). So this branch is only reachable when the feature is enabled and
+ // the pattern is a box pattern.
+ let subpattern = self.iter_fields().next().unwrap();
+ write!(f, "box {:?}", subpattern)
+ }
+ ty::Adt(..) | ty::Tuple(..) => {
+ let variant = match self.ty.kind() {
+ ty::Adt(adt, _) => Some(adt.variant(self.ctor.variant_index_for_adt(*adt))),
+ ty::Tuple(_) => None,
+ _ => unreachable!(),
+ };
+
+ if let Some(variant) = variant {
+ write!(f, "{}", variant.name)?;
+ }
+
+ // Without `cx`, we can't know which field corresponds to which, so we can't
+ // get the names of the fields. Instead we just display everything as a tuple
+ // struct, which should be good enough.
+ write!(f, "(")?;
+ for p in self.iter_fields() {
+ write!(f, "{}", start_or_comma())?;
+ write!(f, "{:?}", p)?;
+ }
+ write!(f, ")")
+ }
+ // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
+ // be careful to detect strings here. However a string literal pattern will never
+ // be reported as a non-exhaustiveness witness, so we can ignore this issue.
+ ty::Ref(_, _, mutbl) => {
+ let subpattern = self.iter_fields().next().unwrap();
+ write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern)
+ }
+ _ => write!(f, "_"),
+ },
+ Slice(slice) => {
+ let mut subpatterns = self.fields.iter_patterns();
+ write!(f, "[")?;
+ match slice.kind {
+ FixedLen(_) => {
+ for p in subpatterns {
+ write!(f, "{}{:?}", start_or_comma(), p)?;
+ }
+ }
+ VarLen(prefix_len, _) => {
+ for p in subpatterns.by_ref().take(prefix_len) {
+ write!(f, "{}{:?}", start_or_comma(), p)?;
+ }
+ write!(f, "{}", start_or_comma())?;
+ write!(f, "..")?;
+ for p in subpatterns {
+ write!(f, "{}{:?}", start_or_comma(), p)?;
+ }
+ }
+ }
+ write!(f, "]")
+ }
+ &FloatRange(lo, hi, end) => {
+ write!(f, "{}", lo)?;
+ write!(f, "{}", end)?;
+ write!(f, "{}", hi)
+ }
+ IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render e.g. `false` as `0..=0`
+ Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty),
+ Or => {
+ for pat in self.iter_fields() {
+ write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
+ }
+ Ok(())
+ }
+ Str(value) => write!(f, "{}", value),
+ Opaque => write!(f, "<constant pattern>"),
+ }
+ }
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
new file mode 100644
index 000000000..a13748a2d
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -0,0 +1,802 @@
+//! Validation of patterns/matches.
+
+mod check_match;
+mod const_to_pat;
+mod deconstruct_pat;
+mod usefulness;
+
+pub(crate) use self::check_match::check_match;
+
+use crate::thir::util::UserAnnotatedTyHelpers;
+
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{CtorOf, DefKind, Res};
+use rustc_hir::pat_util::EnumerateAndAdjustIterator;
+use rustc_hir::RangeEnd;
+use rustc_index::vec::Idx;
+use rustc_middle::mir::interpret::{
+ ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar,
+};
+use rustc_middle::mir::{self, UserTypeProjection};
+use rustc_middle::mir::{BorrowKind, Field, Mutability};
+use rustc_middle::thir::{Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange};
+use rustc_middle::ty::subst::{GenericArg, SubstsRef};
+use rustc_middle::ty::CanonicalUserTypeAnnotation;
+use rustc_middle::ty::{self, AdtDef, ConstKind, DefIdTree, Region, Ty, TyCtxt, UserType};
+use rustc_span::{Span, Symbol};
+
+use std::cmp::Ordering;
+
+#[derive(Clone, Debug)]
+pub(crate) enum PatternError {
+ AssocConstInPattern(Span),
+ ConstParamInPattern(Span),
+ StaticInPattern(Span),
+ NonConstPath(Span),
+}
+
+pub(crate) struct PatCtxt<'a, 'tcx> {
+ pub(crate) tcx: TyCtxt<'tcx>,
+ pub(crate) param_env: ty::ParamEnv<'tcx>,
+ pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>,
+ pub(crate) errors: Vec<PatternError>,
+ include_lint_checks: bool,
+}
+
+pub(crate) fn pat_from_hir<'a, 'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+ pat: &'tcx hir::Pat<'tcx>,
+) -> Pat<'tcx> {
+ let mut pcx = PatCtxt::new(tcx, param_env, typeck_results);
+ let result = pcx.lower_pattern(pat);
+ if !pcx.errors.is_empty() {
+ let msg = format!("encountered errors lowering pattern: {:?}", pcx.errors);
+ tcx.sess.delay_span_bug(pat.span, &msg);
+ }
+ debug!("pat_from_hir({:?}) = {:?}", pat, result);
+ result
+}
+
+impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
+ pub(crate) fn new(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+ ) -> Self {
+ PatCtxt { tcx, param_env, typeck_results, errors: vec![], include_lint_checks: false }
+ }
+
+ pub(crate) fn include_lint_checks(&mut self) -> &mut Self {
+ self.include_lint_checks = true;
+ self
+ }
+
+ pub(crate) fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
+ // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
+ // pattern has the type that results *after* dereferencing. For example, in this code:
+ //
+ // ```
+ // match &&Some(0i32) {
+ // Some(n) => { ... },
+ // _ => { ... },
+ // }
+ // ```
+ //
+ // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option<i32>` (this is
+ // determined in rustc_typeck::check::match). The adjustments would be
+ //
+ // `vec![&&Option<i32>, &Option<i32>]`.
+ //
+ // Applying the adjustments, we want to instead output `&&Some(n)` (as a THIR pattern). So
+ // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the
+ // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted
+ // gets the least-dereferenced type).
+ let unadjusted_pat = self.lower_pattern_unadjusted(pat);
+ self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold(
+ unadjusted_pat,
+ |pat, ref_ty| {
+ debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty);
+ Pat {
+ span: pat.span,
+ ty: *ref_ty,
+ kind: Box::new(PatKind::Deref { subpattern: pat }),
+ }
+ },
+ )
+ }
+
+ fn lower_range_expr(
+ &mut self,
+ expr: &'tcx hir::Expr<'tcx>,
+ ) -> (PatKind<'tcx>, Option<Ascription<'tcx>>) {
+ match self.lower_lit(expr) {
+ PatKind::AscribeUserType { ascription, subpattern: Pat { kind: box kind, .. } } => {
+ (kind, Some(ascription))
+ }
+ kind => (kind, None),
+ }
+ }
+
+ fn lower_pattern_range(
+ &mut self,
+ ty: Ty<'tcx>,
+ lo: mir::ConstantKind<'tcx>,
+ hi: mir::ConstantKind<'tcx>,
+ end: RangeEnd,
+ span: Span,
+ ) -> PatKind<'tcx> {
+ assert_eq!(lo.ty(), ty);
+ assert_eq!(hi.ty(), ty);
+ let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env);
+ match (end, cmp) {
+ // `x..y` where `x < y`.
+ // Non-empty because the range includes at least `x`.
+ (RangeEnd::Excluded, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }),
+ // `x..y` where `x >= y`. The range is empty => error.
+ (RangeEnd::Excluded, _) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0579,
+ "lower range bound must be less than upper"
+ )
+ .emit();
+ PatKind::Wild
+ }
+ // `x..=y` where `x == y`.
+ (RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo },
+ // `x..=y` where `x < y`.
+ (RangeEnd::Included, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }),
+ // `x..=y` where `x > y` hence the range is empty => error.
+ (RangeEnd::Included, _) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0030,
+ "lower range bound must be less than or equal to upper"
+ );
+ err.span_label(span, "lower bound larger than upper bound");
+ if self.tcx.sess.teach(&err.get_code().unwrap()) {
+ err.note(
+ "When matching against a range, the compiler \
+ verifies that the range is non-empty. Range \
+ patterns include both end-points, so this is \
+ equivalent to requiring the start of the range \
+ to be less than or equal to the end of the range.",
+ );
+ }
+ err.emit();
+ PatKind::Wild
+ }
+ }
+ }
+
+ fn normalize_range_pattern_ends(
+ &self,
+ ty: Ty<'tcx>,
+ lo: Option<&PatKind<'tcx>>,
+ hi: Option<&PatKind<'tcx>>,
+ ) -> Option<(mir::ConstantKind<'tcx>, mir::ConstantKind<'tcx>)> {
+ match (lo, hi) {
+ (Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => {
+ Some((*lo, *hi))
+ }
+ (Some(PatKind::Constant { value: lo }), None) => {
+ let hi = ty.numeric_max_val(self.tcx)?;
+ Some((*lo, mir::ConstantKind::from_const(hi, self.tcx)))
+ }
+ (None, Some(PatKind::Constant { value: hi })) => {
+ let lo = ty.numeric_min_val(self.tcx)?;
+ Some((mir::ConstantKind::from_const(lo, self.tcx), *hi))
+ }
+ _ => None,
+ }
+ }
+
+ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
+ let mut ty = self.typeck_results.node_type(pat.hir_id);
+
+ let kind = match pat.kind {
+ hir::PatKind::Wild => PatKind::Wild,
+
+ hir::PatKind::Lit(value) => self.lower_lit(value),
+
+ hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
+ let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
+ let lo_span = lo_expr.map_or(pat.span, |e| e.span);
+ let lo = lo_expr.map(|e| self.lower_range_expr(e));
+ let hi = hi_expr.map(|e| self.lower_range_expr(e));
+
+ let (lp, hp) = (lo.as_ref().map(|x| &x.0), hi.as_ref().map(|x| &x.0));
+ let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) {
+ Some((lc, hc)) => self.lower_pattern_range(ty, lc, hc, end, lo_span),
+ None => {
+ let msg = &format!(
+ "found bad range pattern `{:?}` outside of error recovery",
+ (&lo, &hi),
+ );
+ self.tcx.sess.delay_span_bug(pat.span, msg);
+ PatKind::Wild
+ }
+ };
+
+ // If we are handling a range with associated constants (e.g.
+ // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
+ // constants somewhere. Have them on the range pattern.
+ for end in &[lo, hi] {
+ if let Some((_, Some(ascription))) = end {
+ let subpattern = Pat { span: pat.span, ty, kind: Box::new(kind) };
+ kind =
+ PatKind::AscribeUserType { ascription: ascription.clone(), subpattern };
+ }
+ }
+
+ kind
+ }
+
+ hir::PatKind::Path(ref qpath) => {
+ return self.lower_path(qpath, pat.hir_id, pat.span);
+ }
+
+ hir::PatKind::Ref(ref subpattern, _) | hir::PatKind::Box(ref subpattern) => {
+ PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
+ }
+
+ hir::PatKind::Slice(ref prefix, ref slice, ref suffix) => {
+ self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix)
+ }
+
+ hir::PatKind::Tuple(ref pats, ddpos) => {
+ let ty::Tuple(ref tys) = ty.kind() else {
+ span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty);
+ };
+ let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos);
+ PatKind::Leaf { subpatterns }
+ }
+
+ hir::PatKind::Binding(_, id, ident, ref sub) => {
+ let bm = *self
+ .typeck_results
+ .pat_binding_modes()
+ .get(pat.hir_id)
+ .expect("missing binding mode");
+ let (mutability, mode) = match bm {
+ ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue),
+ ty::BindByReference(hir::Mutability::Mut) => (
+ Mutability::Not,
+ BindingMode::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
+ ),
+ ty::BindByReference(hir::Mutability::Not) => {
+ (Mutability::Not, BindingMode::ByRef(BorrowKind::Shared))
+ }
+ };
+
+ // A ref x pattern is the same node used for x, and as such it has
+ // x's type, which is &T, where we want T (the type being matched).
+ let var_ty = ty;
+ if let ty::BindByReference(_) = bm {
+ if let ty::Ref(_, rty, _) = ty.kind() {
+ ty = *rty;
+ } else {
+ bug!("`ref {}` has wrong type {}", ident, ty);
+ }
+ };
+
+ PatKind::Binding {
+ mutability,
+ mode,
+ name: ident.name,
+ var: LocalVarId(id),
+ ty: var_ty,
+ subpattern: self.lower_opt_pattern(sub),
+ is_primary: id == pat.hir_id,
+ }
+ }
+
+ hir::PatKind::TupleStruct(ref qpath, ref pats, ddpos) => {
+ let res = self.typeck_results.qpath_res(qpath, pat.hir_id);
+ let ty::Adt(adt_def, _) = ty.kind() else {
+ span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty);
+ };
+ let variant_def = adt_def.variant_of_res(res);
+ let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos);
+ self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
+ }
+
+ hir::PatKind::Struct(ref qpath, ref fields, _) => {
+ let res = self.typeck_results.qpath_res(qpath, pat.hir_id);
+ let subpatterns = fields
+ .iter()
+ .map(|field| FieldPat {
+ field: Field::new(self.tcx.field_index(field.hir_id, self.typeck_results)),
+ pattern: self.lower_pattern(&field.pat),
+ })
+ .collect();
+
+ self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
+ }
+
+ hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
+ };
+
+ Pat { span: pat.span, ty, kind: Box::new(kind) }
+ }
+
+ fn lower_tuple_subpats(
+ &mut self,
+ pats: &'tcx [hir::Pat<'tcx>],
+ expected_len: usize,
+ gap_pos: Option<usize>,
+ ) -> Vec<FieldPat<'tcx>> {
+ pats.iter()
+ .enumerate_and_adjust(expected_len, gap_pos)
+ .map(|(i, subpattern)| FieldPat {
+ field: Field::new(i),
+ pattern: self.lower_pattern(subpattern),
+ })
+ .collect()
+ }
+
+ fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> {
+ pats.iter().map(|p| self.lower_pattern(p)).collect()
+ }
+
+ fn lower_opt_pattern(&mut self, pat: &'tcx Option<&'tcx hir::Pat<'tcx>>) -> Option<Pat<'tcx>> {
+ pat.as_ref().map(|p| self.lower_pattern(p))
+ }
+
+ fn slice_or_array_pattern(
+ &mut self,
+ span: Span,
+ ty: Ty<'tcx>,
+ prefix: &'tcx [hir::Pat<'tcx>],
+ slice: &'tcx Option<&'tcx hir::Pat<'tcx>>,
+ suffix: &'tcx [hir::Pat<'tcx>],
+ ) -> PatKind<'tcx> {
+ let prefix = self.lower_patterns(prefix);
+ let slice = self.lower_opt_pattern(slice);
+ let suffix = self.lower_patterns(suffix);
+ match ty.kind() {
+ // Matching a slice, `[T]`.
+ ty::Slice(..) => PatKind::Slice { prefix, slice, suffix },
+ // Fixed-length array, `[T; len]`.
+ ty::Array(_, len) => {
+ let len = len.eval_usize(self.tcx, self.param_env);
+ assert!(len >= prefix.len() as u64 + suffix.len() as u64);
+ PatKind::Array { prefix, slice, suffix }
+ }
+ _ => span_bug!(span, "bad slice pattern type {:?}", ty),
+ }
+ }
+
+ fn lower_variant_or_leaf(
+ &mut self,
+ res: Res,
+ hir_id: hir::HirId,
+ span: Span,
+ ty: Ty<'tcx>,
+ subpatterns: Vec<FieldPat<'tcx>>,
+ ) -> PatKind<'tcx> {
+ let res = match res {
+ Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
+ let variant_id = self.tcx.parent(variant_ctor_id);
+ Res::Def(DefKind::Variant, variant_id)
+ }
+ res => res,
+ };
+
+ let mut kind = match res {
+ Res::Def(DefKind::Variant, variant_id) => {
+ let enum_id = self.tcx.parent(variant_id);
+ let adt_def = self.tcx.adt_def(enum_id);
+ if adt_def.is_enum() {
+ let substs = match ty.kind() {
+ ty::Adt(_, substs) | ty::FnDef(_, substs) => substs,
+ ty::Error(_) => {
+ // Avoid ICE (#50585)
+ return PatKind::Wild;
+ }
+ _ => bug!("inappropriate type for def: {:?}", ty),
+ };
+ PatKind::Variant {
+ adt_def,
+ substs,
+ variant_index: adt_def.variant_index_with_id(variant_id),
+ subpatterns,
+ }
+ } else {
+ PatKind::Leaf { subpatterns }
+ }
+ }
+
+ Res::Def(
+ DefKind::Struct
+ | DefKind::Ctor(CtorOf::Struct, ..)
+ | DefKind::Union
+ | DefKind::TyAlias
+ | DefKind::AssocTy,
+ _,
+ )
+ | Res::SelfTy { .. }
+ | Res::SelfCtor(..) => PatKind::Leaf { subpatterns },
+ _ => {
+ let pattern_error = match res {
+ Res::Def(DefKind::ConstParam, _) => PatternError::ConstParamInPattern(span),
+ Res::Def(DefKind::Static(_), _) => PatternError::StaticInPattern(span),
+ _ => PatternError::NonConstPath(span),
+ };
+ self.errors.push(pattern_error);
+ PatKind::Wild
+ }
+ };
+
+ if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) {
+ debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span);
+ let annotation = CanonicalUserTypeAnnotation {
+ user_ty,
+ span,
+ inferred_ty: self.typeck_results.node_type(hir_id),
+ };
+ kind = PatKind::AscribeUserType {
+ subpattern: Pat { span, ty, kind: Box::new(kind) },
+ ascription: Ascription { annotation, variance: ty::Variance::Covariant },
+ };
+ }
+
+ kind
+ }
+
+ /// Takes a HIR Path. If the path is a constant, evaluates it and feeds
+ /// it to `const_to_pat`. Any other path (like enum variants without fields)
+ /// is converted to the corresponding pattern via `lower_variant_or_leaf`.
+ #[instrument(skip(self), level = "debug")]
+ fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> {
+ let ty = self.typeck_results.node_type(id);
+ let res = self.typeck_results.qpath_res(qpath, id);
+
+ let pat_from_kind = |kind| Pat { span, ty, kind: Box::new(kind) };
+
+ let (def_id, is_associated_const) = match res {
+ Res::Def(DefKind::Const, def_id) => (def_id, false),
+ Res::Def(DefKind::AssocConst, def_id) => (def_id, true),
+
+ _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])),
+ };
+
+ // Use `Reveal::All` here because patterns are always monomorphic even if their function
+ // isn't.
+ let param_env_reveal_all = self.param_env.with_reveal_all_normalized(self.tcx);
+ let substs = self.typeck_results.node_substs(id);
+ let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) {
+ Ok(Some(i)) => i,
+ Ok(None) => {
+ // It should be assoc consts if there's no error but we cannot resolve it.
+ debug_assert!(is_associated_const);
+
+ self.errors.push(PatternError::AssocConstInPattern(span));
+
+ return pat_from_kind(PatKind::Wild);
+ }
+
+ Err(_) => {
+ self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+ return pat_from_kind(PatKind::Wild);
+ }
+ };
+
+ // `mir_const_qualif` must be called with the `DefId` of the item where the const is
+ // defined, not where it is declared. The difference is significant for associated
+ // constants.
+ let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq;
+ debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation);
+
+ match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) {
+ Ok(literal) => {
+ let const_ = mir::ConstantKind::Val(literal, ty);
+ let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation);
+
+ if !is_associated_const {
+ return pattern;
+ }
+
+ let user_provided_types = self.typeck_results().user_provided_types();
+ if let Some(&user_ty) = user_provided_types.get(id) {
+ let annotation = CanonicalUserTypeAnnotation {
+ user_ty,
+ span,
+ inferred_ty: self.typeck_results().node_type(id),
+ };
+ Pat {
+ span,
+ kind: Box::new(PatKind::AscribeUserType {
+ subpattern: pattern,
+ ascription: Ascription {
+ annotation,
+ /// Note that use `Contravariant` here. See the
+ /// `variance` field documentation for details.
+ variance: ty::Variance::Contravariant,
+ },
+ }),
+ ty: const_.ty(),
+ }
+ } else {
+ pattern
+ }
+ }
+ Err(ErrorHandled::TooGeneric) => {
+ // While `Reported | Linted` cases will have diagnostics emitted already
+ // it is not true for TooGeneric case, so we need to give user more information.
+ self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter");
+ pat_from_kind(PatKind::Wild)
+ }
+ Err(_) => {
+ self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+ pat_from_kind(PatKind::Wild)
+ }
+ }
+ }
+
+ /// Converts inline const patterns.
+ fn lower_inline_const(
+ &mut self,
+ anon_const: &'tcx hir::AnonConst,
+ id: hir::HirId,
+ span: Span,
+ ) -> PatKind<'tcx> {
+ let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
+ let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const_def_id);
+
+ // Evaluate early like we do in `lower_path`.
+ let value = value.eval(self.tcx, self.param_env);
+
+ match value {
+ mir::ConstantKind::Ty(c) => {
+ match c.kind() {
+ ConstKind::Param(_) => {
+ self.errors.push(PatternError::ConstParamInPattern(span));
+ return PatKind::Wild;
+ }
+ ConstKind::Unevaluated(_) => {
+ // If we land here it means the const can't be evaluated because it's `TooGeneric`.
+ self.tcx
+ .sess
+ .span_err(span, "constant pattern depends on a generic parameter");
+ return PatKind::Wild;
+ }
+ _ => bug!("Expected either ConstKind::Param or ConstKind::Unevaluated"),
+ }
+ }
+ mir::ConstantKind::Val(_, _) => *self.const_to_pat(value, id, span, false).kind,
+ }
+ }
+
+ /// Converts literals, paths and negation of literals to patterns.
+ /// The special case for negation exists to allow things like `-128_i8`
+ /// which would overflow if we tried to evaluate `128_i8` and then negate
+ /// afterwards.
+ fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> {
+ let (lit, neg) = match expr.kind {
+ hir::ExprKind::Path(ref qpath) => {
+ return *self.lower_path(qpath, expr.hir_id, expr.span).kind;
+ }
+ hir::ExprKind::ConstBlock(ref anon_const) => {
+ return self.lower_inline_const(anon_const, expr.hir_id, expr.span);
+ }
+ hir::ExprKind::Lit(ref lit) => (lit, false),
+ hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => {
+ let hir::ExprKind::Lit(ref lit) = expr.kind else {
+ span_bug!(expr.span, "not a literal: {:?}", expr);
+ };
+ (lit, true)
+ }
+ _ => span_bug!(expr.span, "not a literal: {:?}", expr),
+ };
+
+ let lit_input =
+ LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg };
+ match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) {
+ Ok(constant) => *self.const_to_pat(constant, expr.hir_id, lit.span, false).kind,
+ Err(LitToConstError::Reported) => PatKind::Wild,
+ Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"),
+ }
+ }
+}
+
+impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn typeck_results(&self) -> &ty::TypeckResults<'tcx> {
+ self.typeck_results
+ }
+}
+
+pub(crate) trait PatternFoldable<'tcx>: Sized {
+ fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ self.super_fold_with(folder)
+ }
+
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self;
+}
+
+pub(crate) trait PatternFolder<'tcx>: Sized {
+ fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> {
+ pattern.super_fold_with(self)
+ }
+
+ fn fold_pattern_kind(&mut self, kind: &PatKind<'tcx>) -> PatKind<'tcx> {
+ kind.super_fold_with(self)
+ }
+}
+
+impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box<T> {
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ let content: T = (**self).fold_with(folder);
+ Box::new(content)
+ }
+}
+
+impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec<T> {
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ self.iter().map(|t| t.fold_with(folder)).collect()
+ }
+}
+
+impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option<T> {
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ self.as_ref().map(|t| t.fold_with(folder))
+ }
+}
+
+macro_rules! ClonePatternFoldableImpls {
+ (<$lt_tcx:tt> $($ty:ty),+) => {
+ $(
+ impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty {
+ fn super_fold_with<F: PatternFolder<$lt_tcx>>(&self, _: &mut F) -> Self {
+ Clone::clone(self)
+ }
+ }
+ )+
+ }
+}
+
+ClonePatternFoldableImpls! { <'tcx>
+ Span, Field, Mutability, Symbol, LocalVarId, usize, ty::Const<'tcx>,
+ Region<'tcx>, Ty<'tcx>, BindingMode, AdtDef<'tcx>,
+ SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>,
+ UserTypeProjection, CanonicalUserTypeAnnotation<'tcx>
+}
+
+impl<'tcx> PatternFoldable<'tcx> for FieldPat<'tcx> {
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ FieldPat { field: self.field.fold_with(folder), pattern: self.pattern.fold_with(folder) }
+ }
+}
+
+impl<'tcx> PatternFoldable<'tcx> for Pat<'tcx> {
+ fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ folder.fold_pattern(self)
+ }
+
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ Pat {
+ ty: self.ty.fold_with(folder),
+ span: self.span.fold_with(folder),
+ kind: self.kind.fold_with(folder),
+ }
+ }
+}
+
+impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> {
+ fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ folder.fold_pattern_kind(self)
+ }
+
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ match *self {
+ PatKind::Wild => PatKind::Wild,
+ PatKind::AscribeUserType {
+ ref subpattern,
+ ascription: Ascription { ref annotation, variance },
+ } => PatKind::AscribeUserType {
+ subpattern: subpattern.fold_with(folder),
+ ascription: Ascription { annotation: annotation.fold_with(folder), variance },
+ },
+ PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, is_primary } => {
+ PatKind::Binding {
+ mutability: mutability.fold_with(folder),
+ name: name.fold_with(folder),
+ mode: mode.fold_with(folder),
+ var: var.fold_with(folder),
+ ty: ty.fold_with(folder),
+ subpattern: subpattern.fold_with(folder),
+ is_primary,
+ }
+ }
+ PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
+ PatKind::Variant {
+ adt_def: adt_def.fold_with(folder),
+ substs: substs.fold_with(folder),
+ variant_index,
+ subpatterns: subpatterns.fold_with(folder),
+ }
+ }
+ PatKind::Leaf { ref subpatterns } => {
+ PatKind::Leaf { subpatterns: subpatterns.fold_with(folder) }
+ }
+ PatKind::Deref { ref subpattern } => {
+ PatKind::Deref { subpattern: subpattern.fold_with(folder) }
+ }
+ PatKind::Constant { value } => PatKind::Constant { value },
+ PatKind::Range(range) => PatKind::Range(range),
+ PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice {
+ prefix: prefix.fold_with(folder),
+ slice: slice.fold_with(folder),
+ suffix: suffix.fold_with(folder),
+ },
+ PatKind::Array { ref prefix, ref slice, ref suffix } => PatKind::Array {
+ prefix: prefix.fold_with(folder),
+ slice: slice.fold_with(folder),
+ suffix: suffix.fold_with(folder),
+ },
+ PatKind::Or { ref pats } => PatKind::Or { pats: pats.fold_with(folder) },
+ }
+ }
+}
+
+#[instrument(skip(tcx), level = "debug")]
+pub(crate) fn compare_const_vals<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ a: mir::ConstantKind<'tcx>,
+ b: mir::ConstantKind<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+) -> Option<Ordering> {
+ assert_eq!(a.ty(), b.ty());
+
+ let ty = a.ty();
+
+ // This code is hot when compiling matches with many ranges. So we
+ // special-case extraction of evaluated scalars for speed, for types where
+ // raw data comparisons are appropriate. E.g. `unicode-normalization` has
+ // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
+ // in this way.
+ match ty.kind() {
+ ty::Float(_) | ty::Int(_) => {} // require special handling, see below
+ _ => match (a, b) {
+ (
+ mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(a)), _a_ty),
+ mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(b)), _b_ty),
+ ) => return Some(a.cmp(&b)),
+ _ => {}
+ },
+ }
+
+ let a = a.eval_bits(tcx, param_env, ty);
+ let b = b.eval_bits(tcx, param_env, ty);
+
+ use rustc_apfloat::Float;
+ match *ty.kind() {
+ ty::Float(ty::FloatTy::F32) => {
+ let a = rustc_apfloat::ieee::Single::from_bits(a);
+ let b = rustc_apfloat::ieee::Single::from_bits(b);
+ a.partial_cmp(&b)
+ }
+ ty::Float(ty::FloatTy::F64) => {
+ let a = rustc_apfloat::ieee::Double::from_bits(a);
+ let b = rustc_apfloat::ieee::Double::from_bits(b);
+ a.partial_cmp(&b)
+ }
+ ty::Int(ity) => {
+ use rustc_middle::ty::layout::IntegerExt;
+ let size = rustc_target::abi::Integer::from_int_ty(&tcx, ity).size();
+ let a = size.sign_extend(a);
+ let b = size.sign_extend(b);
+ Some((a as i128).cmp(&(b as i128)))
+ }
+ _ => Some(a.cmp(&b)),
+ }
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
new file mode 100644
index 000000000..0a660ef30
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -0,0 +1,978 @@
+//! Note: tests specific to this file can be found in:
+//!
+//! - `ui/pattern/usefulness`
+//! - `ui/or-patterns`
+//! - `ui/consts/const_in_pattern`
+//! - `ui/rfc-2008-non-exhaustive`
+//! - `ui/half-open-range-patterns`
+//! - probably many others
+//!
+//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
+//! reason not to, for example if they depend on a particular feature like `or_patterns`.
+//!
+//! -----
+//!
+//! This file includes the logic for exhaustiveness and reachability checking for pattern-matching.
+//! Specifically, given a list of patterns for a type, we can tell whether:
+//! (a) each pattern is reachable (reachability)
+//! (b) the patterns cover every possible value for the type (exhaustiveness)
+//!
+//! The algorithm implemented here is a modified version of the one described in [this
+//! paper](http://moscova.inria.fr/~maranget/papers/warn/index.html). We have however generalized
+//! it to accommodate the variety of patterns that Rust supports. We thus explain our version here,
+//! without being as rigorous.
+//!
+//!
+//! # Summary
+//!
+//! The core of the algorithm is the notion of "usefulness". A pattern `q` is said to be *useful*
+//! relative to another pattern `p` of the same type if there is a value that is matched by `q` and
+//! not matched by `p`. This generalizes to many `p`s: `q` is useful w.r.t. a list of patterns
+//! `p_1 .. p_n` if there is a value that is matched by `q` and by none of the `p_i`. We write
+//! `usefulness(p_1 .. p_n, q)` for a function that returns a list of such values. The aim of this
+//! file is to compute it efficiently.
+//!
+//! This is enough to compute reachability: a pattern in a `match` expression is reachable iff it
+//! is useful w.r.t. the patterns above it:
+//! ```rust
+//! # fn foo(x: Option<i32>) {
+//! match x {
+//! Some(_) => {},
+//! None => {}, // reachable: `None` is matched by this but not the branch above
+//! Some(0) => {}, // unreachable: all the values this matches are already matched by
+//! // `Some(_)` above
+//! }
+//! # }
+//! ```
+//!
+//! This is also enough to compute exhaustiveness: a match is exhaustive iff the wildcard `_`
+//! pattern is _not_ useful w.r.t. the patterns in the match. The values returned by `usefulness`
+//! are used to tell the user which values are missing.
+//! ```compile_fail,E0004
+//! # fn foo(x: Option<i32>) {
+//! match x {
+//! Some(0) => {},
+//! None => {},
+//! // not exhaustive: `_` is useful because it matches `Some(1)`
+//! }
+//! # }
+//! ```
+//!
+//! The entrypoint of this file is the [`compute_match_usefulness`] function, which computes
+//! reachability for each match branch and exhaustiveness for the whole match.
+//!
+//!
+//! # Constructors and fields
+//!
+//! Note: we will often abbreviate "constructor" as "ctor".
+//!
+//! The idea that powers everything that is done in this file is the following: a (matchable)
+//! value is made from a constructor applied to a number of subvalues. Examples of constructors are
+//! `Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for a struct
+//! `Foo`), and `2` (the constructor for the number `2`). This is natural when we think of
+//! pattern-matching, and this is the basis for what follows.
+//!
+//! Some of the ctors listed above might feel weird: `None` and `2` don't take any arguments.
+//! That's ok: those are ctors that take a list of 0 arguments; they are the simplest case of
+//! ctors. We treat `2` as a ctor because `u64` and other number types behave exactly like a huge
+//! `enum`, with one variant for each number. This allows us to see any matchable value as made up
+//! from a tree of ctors, each having a set number of children. For example: `Foo { bar: None,
+//! baz: Ok(0) }` is made from 4 different ctors, namely `Foo{..}`, `None`, `Ok` and `0`.
+//!
+//! This idea can be extended to patterns: they are also made from constructors applied to fields.
+//! A pattern for a given type is allowed to use all the ctors for values of that type (which we
+//! call "value constructors"), but there are also pattern-only ctors. The most important one is
+//! the wildcard (`_`), and the others are integer ranges (`0..=10`), variable-length slices (`[x,
+//! ..]`), and or-patterns (`Ok(0) | Err(_)`). Examples of valid patterns are `42`, `Some(_)`, `Foo
+//! { bar: Some(0) | None, baz: _ }`. Note that a binder in a pattern (e.g. `Some(x)`) matches the
+//! same values as a wildcard (e.g. `Some(_)`), so we treat both as wildcards.
+//!
+//! From this deconstruction we can compute whether a given value matches a given pattern; we
+//! simply look at ctors one at a time. Given a pattern `p` and a value `v`, we want to compute
+//! `matches!(v, p)`. It's mostly straightforward: we compare the head ctors and when they match
+//! we compare their fields recursively. A few representative examples:
+//!
+//! - `matches!(v, _) := true`
+//! - `matches!((v0, v1), (p0, p1)) := matches!(v0, p0) && matches!(v1, p1)`
+//! - `matches!(Foo { bar: v0, baz: v1 }, Foo { bar: p0, baz: p1 }) := matches!(v0, p0) && matches!(v1, p1)`
+//! - `matches!(Ok(v0), Ok(p0)) := matches!(v0, p0)`
+//! - `matches!(Ok(v0), Err(p0)) := false` (incompatible variants)
+//! - `matches!(v, 1..=100) := matches!(v, 1) || ... || matches!(v, 100)`
+//! - `matches!([v0], [p0, .., p1]) := false` (incompatible lengths)
+//! - `matches!([v0, v1, v2], [p0, .., p1]) := matches!(v0, p0) && matches!(v2, p1)`
+//! - `matches!(v, p0 | p1) := matches!(v, p0) || matches!(v, p1)`
+//!
+//! Constructors, fields and relevant operations are defined in the [`super::deconstruct_pat`] module.
+//!
+//! Note: this constructors/fields distinction may not straightforwardly apply to every Rust type.
+//! For example a value of type `Rc<u64>` can't be deconstructed that way, and `&str` has an
+//! infinitude of constructors. There are also subtleties with visibility of fields and
+//! uninhabitedness and various other things. The constructors idea can be extended to handle most
+//! of these subtleties though; caveats are documented where relevant throughout the code.
+//!
+//! Whether constructors cover each other is computed by [`Constructor::is_covered_by`].
+//!
+//!
+//! # Specialization
+//!
+//! Recall that we wish to compute `usefulness(p_1 .. p_n, q)`: given a list of patterns `p_1 ..
+//! p_n` and a pattern `q`, all of the same type, we want to find a list of values (called
+//! "witnesses") that are matched by `q` and by none of the `p_i`. We obviously don't just
+//! enumerate all possible values. From the discussion above we see that we can proceed
+//! ctor-by-ctor: for each value ctor of the given type, we ask "is there a value that starts with
+//! this constructor and matches `q` and none of the `p_i`?". As we saw above, there's a lot we can
+//! say from knowing only the first constructor of our candidate value.
+//!
+//! Let's take the following example:
+//! ```compile_fail,E0004
+//! # enum Enum { Variant1(()), Variant2(Option<bool>, u32)}
+//! # fn foo(x: Enum) {
+//! match x {
+//! Enum::Variant1(_) => {} // `p1`
+//! Enum::Variant2(None, 0) => {} // `p2`
+//! Enum::Variant2(Some(_), 0) => {} // `q`
+//! }
+//! # }
+//! ```
+//!
+//! We can easily see that if our candidate value `v` starts with `Variant1` it will not match `q`.
+//! If `v = Variant2(v0, v1)` however, whether or not it matches `p2` and `q` will depend on `v0`
+//! and `v1`. In fact, such a `v` will be a witness of usefulness of `q` exactly when the tuple
+//! `(v0, v1)` is a witness of usefulness of `q'` in the following reduced match:
+//!
+//! ```compile_fail,E0004
+//! # fn foo(x: (Option<bool>, u32)) {
+//! match x {
+//! (None, 0) => {} // `p2'`
+//! (Some(_), 0) => {} // `q'`
+//! }
+//! # }
+//! ```
+//!
+//! This motivates a new step in computing usefulness, that we call _specialization_.
+//! Specialization consist of filtering a list of patterns for those that match a constructor, and
+//! then looking into the constructor's fields. This enables usefulness to be computed recursively.
+//!
+//! Instead of acting on a single pattern in each row, we will consider a list of patterns for each
+//! row, and we call such a list a _pattern-stack_. The idea is that we will specialize the
+//! leftmost pattern, which amounts to popping the constructor and pushing its fields, which feels
+//! like a stack. We note a pattern-stack simply with `[p_1 ... p_n]`.
+//! Here's a sequence of specializations of a list of pattern-stacks, to illustrate what's
+//! happening:
+//! ```ignore (illustrative)
+//! [Enum::Variant1(_)]
+//! [Enum::Variant2(None, 0)]
+//! [Enum::Variant2(Some(_), 0)]
+//! //==>> specialize with `Variant2`
+//! [None, 0]
+//! [Some(_), 0]
+//! //==>> specialize with `Some`
+//! [_, 0]
+//! //==>> specialize with `true` (say the type was `bool`)
+//! [0]
+//! //==>> specialize with `0`
+//! []
+//! ```
+//!
+//! The function `specialize(c, p)` takes a value constructor `c` and a pattern `p`, and returns 0
+//! or more pattern-stacks. If `c` does not match the head constructor of `p`, it returns nothing;
+//! otherwise if returns the fields of the constructor. This only returns more than one
+//! pattern-stack if `p` has a pattern-only constructor.
+//!
+//! - Specializing for the wrong constructor returns nothing
+//!
+//! `specialize(None, Some(p0)) := []`
+//!
+//! - Specializing for the correct constructor returns a single row with the fields
+//!
+//! `specialize(Variant1, Variant1(p0, p1, p2)) := [[p0, p1, p2]]`
+//!
+//! `specialize(Foo{..}, Foo { bar: p0, baz: p1 }) := [[p0, p1]]`
+//!
+//! - For or-patterns, we specialize each branch and concatenate the results
+//!
+//! `specialize(c, p0 | p1) := specialize(c, p0) ++ specialize(c, p1)`
+//!
+//! - We treat the other pattern constructors as if they were a large or-pattern of all the
+//! possibilities:
+//!
+//! `specialize(c, _) := specialize(c, Variant1(_) | Variant2(_, _) | ...)`
+//!
+//! `specialize(c, 1..=100) := specialize(c, 1 | ... | 100)`
+//!
+//! `specialize(c, [p0, .., p1]) := specialize(c, [p0, p1] | [p0, _, p1] | [p0, _, _, p1] | ...)`
+//!
+//! - If `c` is a pattern-only constructor, `specialize` is defined on a case-by-case basis. See
+//! the discussion about constructor splitting in [`super::deconstruct_pat`].
+//!
+//!
+//! We then extend this function to work with pattern-stacks as input, by acting on the first
+//! column and keeping the other columns untouched.
+//!
+//! Specialization for the whole matrix is done in [`Matrix::specialize_constructor`]. Note that
+//! or-patterns in the first column are expanded before being stored in the matrix. Specialization
+//! for a single patstack is done from a combination of [`Constructor::is_covered_by`] and
+//! [`PatStack::pop_head_constructor`]. The internals of how it's done mostly live in the
+//! [`Fields`] struct.
+//!
+//!
+//! # Computing usefulness
+//!
+//! We now have all we need to compute usefulness. The inputs to usefulness are a list of
+//! pattern-stacks `p_1 ... p_n` (one per row), and a new pattern_stack `q`. The paper and this
+//! file calls the list of patstacks a _matrix_. They must all have the same number of columns and
+//! the patterns in a given column must all have the same type. `usefulness` returns a (possibly
+//! empty) list of witnesses of usefulness. These witnesses will also be pattern-stacks.
+//!
+//! - base case: `n_columns == 0`.
+//! Since a pattern-stack functions like a tuple of patterns, an empty one functions like the
+//! unit type. Thus `q` is useful iff there are no rows above it, i.e. if `n == 0`.
+//!
+//! - inductive case: `n_columns > 0`.
+//! We need a way to list the constructors we want to try. We will be more clever in the next
+//! section but for now assume we list all value constructors for the type of the first column.
+//!
+//! - for each such ctor `c`:
+//!
+//! - for each `q'` returned by `specialize(c, q)`:
+//!
+//! - we compute `usefulness(specialize(c, p_1) ... specialize(c, p_n), q')`
+//!
+//! - for each witness found, we revert specialization by pushing the constructor `c` on top.
+//!
+//! - We return the concatenation of all the witnesses found, if any.
+//!
+//! Example:
+//! ```ignore (illustrative)
+//! [Some(true)] // p_1
+//! [None] // p_2
+//! [Some(_)] // q
+//! //==>> try `None`: `specialize(None, q)` returns nothing
+//! //==>> try `Some`: `specialize(Some, q)` returns a single row
+//! [true] // p_1'
+//! [_] // q'
+//! //==>> try `true`: `specialize(true, q')` returns a single row
+//! [] // p_1''
+//! [] // q''
+//! //==>> base case; `n != 0` so `q''` is not useful.
+//! //==>> go back up a step
+//! [true] // p_1'
+//! [_] // q'
+//! //==>> try `false`: `specialize(false, q')` returns a single row
+//! [] // q''
+//! //==>> base case; `n == 0` so `q''` is useful. We return the single witness `[]`
+//! witnesses:
+//! []
+//! //==>> undo the specialization with `false`
+//! witnesses:
+//! [false]
+//! //==>> undo the specialization with `Some`
+//! witnesses:
+//! [Some(false)]
+//! //==>> we have tried all the constructors. The output is the single witness `[Some(false)]`.
+//! ```
+//!
+//! This computation is done in [`is_useful`]. In practice we don't care about the list of
+//! witnesses when computing reachability; we only need to know whether any exist. We do keep the
+//! witnesses when computing exhaustiveness to report them to the user.
+//!
+//!
+//! # Making usefulness tractable: constructor splitting
+//!
+//! We're missing one last detail: which constructors do we list? Naively listing all value
+//! constructors cannot work for types like `u64` or `&str`, so we need to be more clever. The
+//! first obvious insight is that we only want to list constructors that are covered by the head
+//! constructor of `q`. If it's a value constructor, we only try that one. If it's a pattern-only
+//! constructor, we use the final clever idea for this algorithm: _constructor splitting_, where we
+//! group together constructors that behave the same.
+//!
+//! The details are not necessary to understand this file, so we explain them in
+//! [`super::deconstruct_pat`]. Splitting is done by the [`Constructor::split`] function.
+
+use self::ArmType::*;
+use self::Usefulness::*;
+
+use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label};
+use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
+
+use rustc_data_structures::captures::Captures;
+
+use rustc_arena::TypedArena;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_hir::def_id::DefId;
+use rustc_hir::HirId;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
+use rustc_span::{Span, DUMMY_SP};
+
+use smallvec::{smallvec, SmallVec};
+use std::fmt;
+use std::iter::once;
+
+pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
+ pub(crate) tcx: TyCtxt<'tcx>,
+ /// The module in which the match occurs. This is necessary for
+ /// checking inhabited-ness of types because whether a type is (visibly)
+ /// inhabited can depend on whether it was defined in the current module or
+ /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty
+ /// outside its module and should not be matchable with an empty match statement.
+ pub(crate) module: DefId,
+ pub(crate) param_env: ty::ParamEnv<'tcx>,
+ pub(crate) pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
+}
+
+impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
+ pub(super) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
+ if self.tcx.features().exhaustive_patterns {
+ self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env)
+ } else {
+ false
+ }
+ }
+
+ /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
+ pub(super) fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
+ match ty.kind() {
+ ty::Adt(def, ..) => {
+ def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did().is_local()
+ }
+ _ => false,
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+pub(super) struct PatCtxt<'a, 'p, 'tcx> {
+ pub(super) cx: &'a MatchCheckCtxt<'p, 'tcx>,
+ /// Type of the current column under investigation.
+ pub(super) ty: Ty<'tcx>,
+ /// Span of the current pattern under investigation.
+ pub(super) span: Span,
+ /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
+ /// subpattern.
+ pub(super) is_top_level: bool,
+ /// Whether the current pattern is from a `non_exhaustive` enum.
+ pub(super) is_non_exhaustive: bool,
+}
+
+impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("PatCtxt").field("ty", &self.ty).finish()
+ }
+}
+
+/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
+/// works well.
+#[derive(Clone)]
+struct PatStack<'p, 'tcx> {
+ pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>,
+}
+
+impl<'p, 'tcx> PatStack<'p, 'tcx> {
+ fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self {
+ Self::from_vec(smallvec![pat])
+ }
+
+ fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>) -> Self {
+ PatStack { pats: vec }
+ }
+
+ fn is_empty(&self) -> bool {
+ self.pats.is_empty()
+ }
+
+ fn len(&self) -> usize {
+ self.pats.len()
+ }
+
+ fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> {
+ self.pats[0]
+ }
+
+ fn iter(&self) -> impl Iterator<Item = &DeconstructedPat<'p, 'tcx>> {
+ self.pats.iter().copied()
+ }
+
+ // Recursively expand the first pattern into its subpatterns. Only useful if the pattern is an
+ // or-pattern. Panics if `self` is empty.
+ fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
+ self.head().iter_fields().map(move |pat| {
+ let mut new_patstack = PatStack::from_pattern(pat);
+ new_patstack.pats.extend_from_slice(&self.pats[1..]);
+ new_patstack
+ })
+ }
+
+ /// This computes `S(self.head().ctor(), self)`. See top of the file for explanations.
+ ///
+ /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
+ /// fields filled with wild patterns.
+ ///
+ /// This is roughly the inverse of `Constructor::apply`.
+ fn pop_head_constructor(
+ &self,
+ pcx: &PatCtxt<'_, 'p, 'tcx>,
+ ctor: &Constructor<'tcx>,
+ ) -> PatStack<'p, 'tcx> {
+ // We pop the head pattern and push the new fields extracted from the arguments of
+ // `self.head()`.
+ let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(pcx, ctor);
+ new_fields.extend_from_slice(&self.pats[1..]);
+ PatStack::from_vec(new_fields)
+ }
+}
+
+/// Pretty-printing for matrix row.
+impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "+")?;
+ for pat in self.iter() {
+ write!(f, " {:?} +", pat)?;
+ }
+ Ok(())
+ }
+}
+
+/// A 2D matrix.
+#[derive(Clone)]
+pub(super) struct Matrix<'p, 'tcx> {
+ patterns: Vec<PatStack<'p, 'tcx>>,
+}
+
+impl<'p, 'tcx> Matrix<'p, 'tcx> {
+ fn empty() -> Self {
+ Matrix { patterns: vec![] }
+ }
+
+ /// Number of columns of this matrix. `None` is the matrix is empty.
+ pub(super) fn column_count(&self) -> Option<usize> {
+ self.patterns.get(0).map(|r| r.len())
+ }
+
+ /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
+ /// expands it.
+ fn push(&mut self, row: PatStack<'p, 'tcx>) {
+ if !row.is_empty() && row.head().is_or_pat() {
+ self.patterns.extend(row.expand_or_pat());
+ } else {
+ self.patterns.push(row);
+ }
+ }
+
+ /// Iterate over the first component of each row
+ fn heads<'a>(
+ &'a self,
+ ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Clone + Captures<'a> {
+ self.patterns.iter().map(|r| r.head())
+ }
+
+ /// This computes `S(constructor, self)`. See top of the file for explanations.
+ fn specialize_constructor(
+ &self,
+ pcx: &PatCtxt<'_, 'p, 'tcx>,
+ ctor: &Constructor<'tcx>,
+ ) -> Matrix<'p, 'tcx> {
+ let mut matrix = Matrix::empty();
+ for row in &self.patterns {
+ if ctor.is_covered_by(pcx, row.head().ctor()) {
+ let new_row = row.pop_head_constructor(pcx, ctor);
+ matrix.push(new_row);
+ }
+ }
+ matrix
+ }
+}
+
+/// Pretty-printer for matrices of patterns, example:
+///
+/// ```text
+/// + _ + [] +
+/// + true + [First] +
+/// + true + [Second(true)] +
+/// + false + [_] +
+/// + _ + [_, _, tail @ ..] +
+/// ```
+impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "\n")?;
+
+ let Matrix { patterns: m, .. } = self;
+ let pretty_printed_matrix: Vec<Vec<String>> =
+ m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect();
+
+ let column_count = m.iter().map(|row| row.len()).next().unwrap_or(0);
+ assert!(m.iter().all(|row| row.len() == column_count));
+ let column_widths: Vec<usize> = (0..column_count)
+ .map(|col| pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0))
+ .collect();
+
+ for row in pretty_printed_matrix {
+ write!(f, "+")?;
+ for (column, pat_str) in row.into_iter().enumerate() {
+ write!(f, " ")?;
+ write!(f, "{:1$}", pat_str, column_widths[column])?;
+ write!(f, " +")?;
+ }
+ write!(f, "\n")?;
+ }
+ Ok(())
+ }
+}
+
+/// This carries the results of computing usefulness, as described at the top of the file. When
+/// checking usefulness of a match branch, we use the `NoWitnesses` variant, which also keeps track
+/// of potential unreachable sub-patterns (in the presence of or-patterns). When checking
+/// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of
+/// witnesses of non-exhaustiveness when there are any.
+/// Which variant to use is dictated by `ArmType`.
+#[derive(Debug)]
+enum Usefulness<'p, 'tcx> {
+ /// If we don't care about witnesses, simply remember if the pattern was useful.
+ NoWitnesses { useful: bool },
+ /// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole
+ /// pattern is unreachable.
+ WithWitnesses(Vec<Witness<'p, 'tcx>>),
+}
+
+impl<'p, 'tcx> Usefulness<'p, 'tcx> {
+ fn new_useful(preference: ArmType) -> Self {
+ match preference {
+ // A single (empty) witness of reachability.
+ FakeExtraWildcard => WithWitnesses(vec![Witness(vec![])]),
+ RealArm => NoWitnesses { useful: true },
+ }
+ }
+
+ fn new_not_useful(preference: ArmType) -> Self {
+ match preference {
+ FakeExtraWildcard => WithWitnesses(vec![]),
+ RealArm => NoWitnesses { useful: false },
+ }
+ }
+
+ fn is_useful(&self) -> bool {
+ match self {
+ Usefulness::NoWitnesses { useful } => *useful,
+ Usefulness::WithWitnesses(witnesses) => !witnesses.is_empty(),
+ }
+ }
+
+ /// Combine usefulnesses from two branches. This is an associative operation.
+ fn extend(&mut self, other: Self) {
+ match (&mut *self, other) {
+ (WithWitnesses(_), WithWitnesses(o)) if o.is_empty() => {}
+ (WithWitnesses(s), WithWitnesses(o)) if s.is_empty() => *self = WithWitnesses(o),
+ (WithWitnesses(s), WithWitnesses(o)) => s.extend(o),
+ (NoWitnesses { useful: s_useful }, NoWitnesses { useful: o_useful }) => {
+ *s_useful = *s_useful || o_useful
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ /// After calculating usefulness after a specialization, call this to reconstruct a usefulness
+ /// that makes sense for the matrix pre-specialization. This new usefulness can then be merged
+ /// with the results of specializing with the other constructors.
+ fn apply_constructor(
+ self,
+ pcx: &PatCtxt<'_, 'p, 'tcx>,
+ matrix: &Matrix<'p, 'tcx>, // used to compute missing ctors
+ ctor: &Constructor<'tcx>,
+ ) -> Self {
+ match self {
+ NoWitnesses { .. } => self,
+ WithWitnesses(ref witnesses) if witnesses.is_empty() => self,
+ WithWitnesses(witnesses) => {
+ let new_witnesses = if let Constructor::Missing { .. } = ctor {
+ // We got the special `Missing` constructor, so each of the missing constructors
+ // gives a new pattern that is not caught by the match. We list those patterns.
+ let new_patterns = if pcx.is_non_exhaustive {
+ // Here we don't want the user to try to list all variants, we want them to add
+ // a wildcard, so we only suggest that.
+ vec![DeconstructedPat::wildcard(pcx.ty)]
+ } else {
+ let mut split_wildcard = SplitWildcard::new(pcx);
+ split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
+
+ // This lets us know if we skipped any variants because they are marked
+ // `doc(hidden)` or they are unstable feature gate (only stdlib types).
+ let mut hide_variant_show_wild = false;
+ // Construct for each missing constructor a "wild" version of this
+ // constructor, that matches everything that can be built with
+ // it. For example, if `ctor` is a `Constructor::Variant` for
+ // `Option::Some`, we get the pattern `Some(_)`.
+ let mut new: Vec<DeconstructedPat<'_, '_>> = split_wildcard
+ .iter_missing(pcx)
+ .filter_map(|missing_ctor| {
+ // Check if this variant is marked `doc(hidden)`
+ if missing_ctor.is_doc_hidden_variant(pcx)
+ || missing_ctor.is_unstable_variant(pcx)
+ {
+ hide_variant_show_wild = true;
+ return None;
+ }
+ Some(DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone()))
+ })
+ .collect();
+
+ if hide_variant_show_wild {
+ new.push(DeconstructedPat::wildcard(pcx.ty));
+ }
+
+ new
+ };
+
+ witnesses
+ .into_iter()
+ .flat_map(|witness| {
+ new_patterns.iter().map(move |pat| {
+ Witness(
+ witness
+ .0
+ .iter()
+ .chain(once(pat))
+ .map(DeconstructedPat::clone_and_forget_reachability)
+ .collect(),
+ )
+ })
+ })
+ .collect()
+ } else {
+ witnesses
+ .into_iter()
+ .map(|witness| witness.apply_constructor(pcx, &ctor))
+ .collect()
+ };
+ WithWitnesses(new_witnesses)
+ }
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+enum ArmType {
+ FakeExtraWildcard,
+ RealArm,
+}
+
+/// A witness of non-exhaustiveness for error reporting, represented
+/// as a list of patterns (in reverse order of construction) with
+/// wildcards inside to represent elements that can take any inhabitant
+/// of the type as a value.
+///
+/// A witness against a list of patterns should have the same types
+/// and length as the pattern matched against. Because Rust `match`
+/// is always against a single pattern, at the end the witness will
+/// have length 1, but in the middle of the algorithm, it can contain
+/// multiple patterns.
+///
+/// For example, if we are constructing a witness for the match against
+///
+/// ```compile_fail,E0004
+/// # #![feature(type_ascription)]
+/// struct Pair(Option<(u32, u32)>, bool);
+/// # fn foo(p: Pair) {
+/// match (p: Pair) {
+/// Pair(None, _) => {}
+/// Pair(_, false) => {}
+/// }
+/// # }
+/// ```
+///
+/// We'll perform the following steps:
+/// 1. Start with an empty witness
+/// `Witness(vec![])`
+/// 2. Push a witness `true` against the `false`
+/// `Witness(vec![true])`
+/// 3. Push a witness `Some(_)` against the `None`
+/// `Witness(vec![true, Some(_)])`
+/// 4. Apply the `Pair` constructor to the witnesses
+/// `Witness(vec![Pair(Some(_), true)])`
+///
+/// The final `Pair(Some(_), true)` is then the resulting witness.
+#[derive(Debug)]
+pub(crate) struct Witness<'p, 'tcx>(Vec<DeconstructedPat<'p, 'tcx>>);
+
+impl<'p, 'tcx> Witness<'p, 'tcx> {
+ /// Asserts that the witness contains a single pattern, and returns it.
+ fn single_pattern(self) -> DeconstructedPat<'p, 'tcx> {
+ assert_eq!(self.0.len(), 1);
+ self.0.into_iter().next().unwrap()
+ }
+
+ /// Constructs a partial witness for a pattern given a list of
+ /// patterns expanded by the specialization step.
+ ///
+ /// When a pattern P is discovered to be useful, this function is used bottom-up
+ /// to reconstruct a complete witness, e.g., a pattern P' that covers a subset
+ /// of values, V, where each value in that set is not covered by any previously
+ /// used patterns and is covered by the pattern P'. Examples:
+ ///
+ /// left_ty: tuple of 3 elements
+ /// pats: [10, 20, _] => (10, 20, _)
+ ///
+ /// left_ty: struct X { a: (bool, &'static str), b: usize}
+ /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
+ fn apply_constructor(mut self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Self {
+ let pat = {
+ let len = self.0.len();
+ let arity = ctor.arity(pcx);
+ let pats = self.0.drain((len - arity)..).rev();
+ let fields = Fields::from_iter(pcx.cx, pats);
+ DeconstructedPat::new(ctor.clone(), fields, pcx.ty, DUMMY_SP)
+ };
+
+ self.0.push(pat);
+
+ self
+ }
+}
+
+/// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
+/// is not exhaustive enough.
+///
+/// NB: The partner lint for structs lives in `compiler/rustc_typeck/src/check/pat.rs`.
+fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ scrut_ty: Ty<'tcx>,
+ sp: Span,
+ hir_id: HirId,
+ witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
+) {
+ let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
+ cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, |build| {
+ let mut lint = build.build("some variants are not matched explicitly");
+ lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
+ lint.help(
+ "ensure that all variants are matched explicitly by adding the suggested match arms",
+ );
+ lint.note(&format!(
+ "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
+ scrut_ty,
+ ));
+ lint.emit();
+ });
+}
+
+/// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
+/// The algorithm from the paper has been modified to correctly handle empty
+/// types. The changes are:
+/// (0) We don't exit early if the pattern matrix has zero rows. We just
+/// continue to recurse over columns.
+/// (1) all_constructors will only return constructors that are statically
+/// possible. E.g., it will only return `Ok` for `Result<T, !>`.
+///
+/// This finds whether a (row) vector `v` of patterns is 'useful' in relation
+/// to a set of such vectors `m` - this is defined as there being a set of
+/// inputs that will match `v` but not any of the sets in `m`.
+///
+/// All the patterns at each column of the `matrix ++ v` matrix must have the same type.
+///
+/// This is used both for reachability checking (if a pattern isn't useful in
+/// relation to preceding patterns, it is not reachable) and exhaustiveness
+/// checking (if a wildcard pattern is useful in relation to a matrix, the
+/// matrix isn't exhaustive).
+///
+/// `is_under_guard` is used to inform if the pattern has a guard. If it
+/// has one it must not be inserted into the matrix. This shouldn't be
+/// relied on for soundness.
+#[instrument(level = "debug", skip(cx, matrix, hir_id))]
+fn is_useful<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ matrix: &Matrix<'p, 'tcx>,
+ v: &PatStack<'p, 'tcx>,
+ witness_preference: ArmType,
+ hir_id: HirId,
+ is_under_guard: bool,
+ is_top_level: bool,
+) -> Usefulness<'p, 'tcx> {
+ debug!(?matrix, ?v);
+ let Matrix { patterns: rows, .. } = matrix;
+
+ // The base case. We are pattern-matching on () and the return value is
+ // based on whether our matrix has a row or not.
+ // NOTE: This could potentially be optimized by checking rows.is_empty()
+ // first and then, if v is non-empty, the return value is based on whether
+ // the type of the tuple we're checking is inhabited or not.
+ if v.is_empty() {
+ let ret = if rows.is_empty() {
+ Usefulness::new_useful(witness_preference)
+ } else {
+ Usefulness::new_not_useful(witness_preference)
+ };
+ debug!(?ret);
+ return ret;
+ }
+
+ debug_assert!(rows.iter().all(|r| r.len() == v.len()));
+
+ // If the first pattern is an or-pattern, expand it.
+ let mut ret = Usefulness::new_not_useful(witness_preference);
+ if v.head().is_or_pat() {
+ debug!("expanding or-pattern");
+ // We try each or-pattern branch in turn.
+ let mut matrix = matrix.clone();
+ for v in v.expand_or_pat() {
+ debug!(?v);
+ let usefulness = ensure_sufficient_stack(|| {
+ is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)
+ });
+ debug!(?usefulness);
+ ret.extend(usefulness);
+ // If pattern has a guard don't add it to the matrix.
+ if !is_under_guard {
+ // We push the already-seen patterns into the matrix in order to detect redundant
+ // branches like `Some(_) | Some(0)`.
+ matrix.push(v);
+ }
+ }
+ } else {
+ let ty = v.head().ty();
+ let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
+ debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
+ let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
+
+ let v_ctor = v.head().ctor();
+ debug!(?v_ctor);
+ if let Constructor::IntRange(ctor_range) = &v_ctor {
+ // Lint on likely incorrect range patterns (#63987)
+ ctor_range.lint_overlapping_range_endpoints(
+ pcx,
+ matrix.heads(),
+ matrix.column_count().unwrap_or(0),
+ hir_id,
+ )
+ }
+ // We split the head constructor of `v`.
+ let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
+ let is_non_exhaustive_and_wild = is_non_exhaustive && v_ctor.is_wildcard();
+ // For each constructor, we compute whether there's a value that starts with it that would
+ // witness the usefulness of `v`.
+ let start_matrix = &matrix;
+ for ctor in split_ctors {
+ debug!("specialize({:?})", ctor);
+ // We cache the result of `Fields::wildcards` because it is used a lot.
+ let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor);
+ let v = v.pop_head_constructor(pcx, &ctor);
+ let usefulness = ensure_sufficient_stack(|| {
+ is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false)
+ });
+ let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor);
+
+ // When all the conditions are met we have a match with a `non_exhaustive` enum
+ // that has the potential to trigger the `non_exhaustive_omitted_patterns` lint.
+ // To understand the workings checkout `Constructor::split` and `SplitWildcard::new/into_ctors`
+ if is_non_exhaustive_and_wild
+ // We check that the match has a wildcard pattern and that that wildcard is useful,
+ // meaning there are variants that are covered by the wildcard. Without the check
+ // for `witness_preference` the lint would trigger on `if let NonExhaustiveEnum::A = foo {}`
+ && usefulness.is_useful() && matches!(witness_preference, RealArm)
+ && matches!(
+ &ctor,
+ Constructor::Missing { nonexhaustive_enum_missing_real_variants: true }
+ )
+ {
+ let patterns = {
+ let mut split_wildcard = SplitWildcard::new(pcx);
+ split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
+ // Construct for each missing constructor a "wild" version of this
+ // constructor, that matches everything that can be built with
+ // it. For example, if `ctor` is a `Constructor::Variant` for
+ // `Option::Some`, we get the pattern `Some(_)`.
+ split_wildcard
+ .iter_missing(pcx)
+ // Filter out the `NonExhaustive` because we want to list only real
+ // variants. Also remove any unstable feature gated variants.
+ // Because of how we computed `nonexhaustive_enum_missing_real_variants`,
+ // this will not return an empty `Vec`.
+ .filter(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx)))
+ .cloned()
+ .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
+ .collect::<Vec<_>>()
+ };
+
+ lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns);
+ }
+
+ ret.extend(usefulness);
+ }
+ }
+
+ if ret.is_useful() {
+ v.head().set_reachable();
+ }
+
+ debug!(?ret);
+ ret
+}
+
+/// The arm of a match expression.
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct MatchArm<'p, 'tcx> {
+ /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`.
+ pub(crate) pat: &'p DeconstructedPat<'p, 'tcx>,
+ pub(crate) hir_id: HirId,
+ pub(crate) has_guard: bool,
+}
+
+/// Indicates whether or not a given arm is reachable.
+#[derive(Clone, Debug)]
+pub(crate) enum Reachability {
+ /// The arm is reachable. This additionally carries a set of or-pattern branches that have been
+ /// found to be unreachable despite the overall arm being reachable. Used only in the presence
+ /// of or-patterns, otherwise it stays empty.
+ Reachable(Vec<Span>),
+ /// The arm is unreachable.
+ Unreachable,
+}
+
+/// The output of checking a match for exhaustiveness and arm reachability.
+pub(crate) struct UsefulnessReport<'p, 'tcx> {
+ /// For each arm of the input, whether that arm is reachable after the arms above it.
+ pub(crate) arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>,
+ /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
+ /// exhaustiveness.
+ pub(crate) non_exhaustiveness_witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
+}
+
+/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which
+/// of its arms are reachable.
+///
+/// Note: the input patterns must have been lowered through
+/// `check_match::MatchVisitor::lower_pattern`.
+#[instrument(skip(cx, arms), level = "debug")]
+pub(crate) fn compute_match_usefulness<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ arms: &[MatchArm<'p, 'tcx>],
+ scrut_hir_id: HirId,
+ scrut_ty: Ty<'tcx>,
+) -> UsefulnessReport<'p, 'tcx> {
+ let mut matrix = Matrix::empty();
+ let arm_usefulness: Vec<_> = arms
+ .iter()
+ .copied()
+ .map(|arm| {
+ debug!(?arm);
+ let v = PatStack::from_pattern(arm.pat);
+ is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true);
+ if !arm.has_guard {
+ matrix.push(v);
+ }
+ let reachability = if arm.pat.is_reachable() {
+ Reachability::Reachable(arm.pat.unreachable_spans())
+ } else {
+ Reachability::Unreachable
+ };
+ (arm, reachability)
+ })
+ .collect();
+
+ let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty));
+ let v = PatStack::from_pattern(wild_pattern);
+ let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true);
+ let non_exhaustiveness_witnesses = match usefulness {
+ WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(),
+ NoWitnesses { .. } => bug!(),
+ };
+ UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
+}
diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs
new file mode 100644
index 000000000..c58ed1ac0
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/util.rs
@@ -0,0 +1,31 @@
+use rustc_hir as hir;
+use rustc_middle::ty::{self, CanonicalUserType, TyCtxt, UserType};
+
+pub(crate) trait UserAnnotatedTyHelpers<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx>;
+
+ fn typeck_results(&self) -> &ty::TypeckResults<'tcx>;
+
+ /// Looks up the type associated with this hir-id and applies the
+ /// user-given substitutions; the hir-id must map to a suitable
+ /// type.
+ fn user_substs_applied_to_ty_of_hir_id(
+ &self,
+ hir_id: hir::HirId,
+ ) -> Option<CanonicalUserType<'tcx>> {
+ let user_provided_types = self.typeck_results().user_provided_types();
+ let mut user_ty = *user_provided_types.get(hir_id)?;
+ debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty);
+ let ty = self.typeck_results().node_type(hir_id);
+ match ty.kind() {
+ ty::Adt(adt_def, ..) => {
+ if let UserType::TypeOf(ref mut did, _) = &mut user_ty.value {
+ *did = adt_def.did();
+ }
+ Some(user_ty)
+ }
+ ty::FnDef(..) => Some(user_ty),
+ _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty),
+ }
+ }
+}