diff options
Diffstat (limited to 'compiler/rustc_mir_build/src/thir')
-rw-r--r-- | compiler/rustc_mir_build/src/thir/cx/block.rs | 5 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/cx/expr.rs | 21 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/check_match.rs | 727 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs | 54 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs | 53 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/mod.rs | 57 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/usefulness.rs | 36 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/print.rs | 2 |
8 files changed, 447 insertions, 508 deletions
diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 321353ca2..8aacec53f 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -105,6 +105,10 @@ impl<'tcx> Cx<'tcx> { } } + let span = match local.init { + Some(init) => local.span.with_hi(init.span.hi()), + None => local.span, + }; let stmt = Stmt { kind: StmtKind::Let { remainder_scope, @@ -116,6 +120,7 @@ impl<'tcx> Cx<'tcx> { initializer: local.init.map(|init| self.mirror_expr(init)), else_block, lint_level: LintLevel::Explicit(local.hir_id), + span, }, opt_destruction_scope: opt_dxn_ext, }; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 9086412c0..8e2e92e6f 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -10,7 +10,7 @@ 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::mir::{self, BinOp, BorrowKind, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast, @@ -20,7 +20,7 @@ use rustc_middle::ty::{ self, AdtKind, InlineConstSubsts, InlineConstSubstsParts, ScalarInt, Ty, UpvarSubsts, UserType, }; use rustc_span::{sym, Span}; -use rustc_target::abi::VariantIdx; +use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; impl<'tcx> Cx<'tcx> { pub(crate) fn mirror_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> ExprId { @@ -185,7 +185,7 @@ impl<'tcx> Cx<'tcx> { 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() { + } else if self.typeck_results().expr_ty(source).is_ref() { // Special cased so that we can type check that the element // type of the source matches the pointed to type of the // destination. @@ -357,7 +357,7 @@ impl<'tcx> Cx<'tcx> { 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))), + Res::SelfCtor(..) => Some((adt_def, FIRST_VARIANT)), _ => None, }) } else { @@ -379,7 +379,7 @@ impl<'tcx> Cx<'tcx> { .iter() .enumerate() .map(|(idx, e)| FieldExpr { - name: Field::new(idx), + name: FieldIdx::new(idx), expr: self.mirror_expr(e), }) .collect(); @@ -510,7 +510,7 @@ impl<'tcx> Cx<'tcx> { debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty); ExprKind::Adt(Box::new(AdtExpr { adt_def: *adt, - variant_index: VariantIdx::new(0), + variant_index: FIRST_VARIANT, substs, user_ty, fields: self.field_refs(fields), @@ -732,8 +732,8 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Field(ref source, ..) => ExprKind::Field { lhs: self.mirror_expr(source), - variant_index: VariantIdx::new(0), - name: Field::new(self.typeck_results.field_index(expr.hir_id)), + variant_index: FIRST_VARIANT, + name: self.typeck_results.field_index(expr.hir_id), }, hir::ExprKind::Cast(ref source, ref cast_ty) => { // Check for a user-given type annotation on this `cast` @@ -780,7 +780,6 @@ impl<'tcx> Cx<'tcx> { 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) } } @@ -1054,7 +1053,7 @@ impl<'tcx> Cx<'tcx> { HirProjectionKind::Field(field, variant_index) => ExprKind::Field { lhs: self.thir.exprs.push(captured_place_expr), variant_index, - name: Field::new(field as usize), + name: field, }, HirProjectionKind::Index | HirProjectionKind::Subslice => { // We don't capture these projections, so we can ignore them here @@ -1108,7 +1107,7 @@ impl<'tcx> Cx<'tcx> { fields .iter() .map(|field| FieldExpr { - name: Field::new(self.typeck_results.field_index(field.hir_id)), + name: self.typeck_results.field_index(field.hir_id), expr: self.mirror_expr(field.expr), }) .collect() diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 2640ca56b..8f58db504 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -2,45 +2,48 @@ use super::deconstruct_pat::{Constructor, DeconstructedPat}; use super::usefulness::{ compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport, }; -use super::{PatCtxt, PatternError}; use crate::errors::*; -use hir::{ExprKind, PatKind}; use rustc_arena::TypedArena; -use rustc_ast::{LitKind, Mutability}; +use rustc_ast::Mutability; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ 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_hir::def_id::LocalDefId; +use rustc_hir::HirId; +use rustc_middle::thir::visit::{self, Visitor}; +use rustc_middle::thir::*; use rustc_middle::ty::print::with_no_trimmed_paths; 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), - }; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::Span; +pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let Ok((thir, expr)) = tcx.thir_body(ty::WithOptConstParam::unknown(def_id)) else { return }; + let thir = thir.borrow(); let pattern_arena = TypedArena::default(); let mut visitor = MatchVisitor { tcx, - typeck_results: tcx.typeck_body(body_id), + thir: &*thir, param_env: tcx.param_env(def_id), + lint_level: tcx.hir().local_def_id_to_hir_id(def_id), + let_source: LetSource::None, pattern_arena: &pattern_arena, }; - visitor.visit_body(tcx.hir().body(body_id)); + visitor.visit_expr(&thir[expr]); + for param in thir.params.iter() { + if let Some(box ref pattern) = param.pat { + visitor.check_irrefutable(pattern, "function argument", None); + } + } } fn create_e0004( @@ -58,77 +61,132 @@ enum RefutableFlag { } use RefutableFlag::*; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum LetSource { + None, + IfLet, + IfLetGuard, + LetElse, + WhileLet, +} + struct MatchVisitor<'a, 'p, 'tcx> { tcx: TyCtxt<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, + thir: &'a Thir<'tcx>, + lint_level: HirId, + let_source: LetSource, 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) +impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> { + fn thir(&self) -> &'a Thir<'tcx> { + self.thir + } + + #[instrument(level = "trace", skip(self))] + fn visit_arm(&mut self, arm: &Arm<'tcx>) { + match arm.guard { + Some(Guard::If(expr)) => { + self.with_let_source(LetSource::IfLetGuard, |this| { + this.visit_expr(&this.thir[expr]) + }); } - hir::ExprKind::Let(hir::Let { pat, init, span, .. }) => { - self.check_let(pat, init, *span) + Some(Guard::IfLet(ref pat, expr)) => { + self.with_let_source(LetSource::IfLetGuard, |this| { + this.check_let(pat, expr, LetSource::IfLetGuard, pat.span); + this.visit_pat(pat); + this.visit_expr(&this.thir[expr]); + }); } - _ => {} + None => {} } + self.visit_pat(&arm.pattern); + self.visit_expr(&self.thir[arm.body]); } - 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() { - // Build a span without the else { ... } as we don't want to underline - // the entire else block in the IDE setting. - let span = loc.span.with_hi(init.span.hi()); - self.check_let(&loc.pat, init, 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), + #[instrument(level = "trace", skip(self))] + fn visit_expr(&mut self, ex: &Expr<'tcx>) { + match ex.kind { + ExprKind::Scope { value, lint_level, .. } => { + let old_lint_level = self.lint_level; + if let LintLevel::Explicit(hir_id) = lint_level { + self.lint_level = hir_id; + } + self.visit_expr(&self.thir[value]); + self.lint_level = old_lint_level; + return; + } + ExprKind::If { cond, then, else_opt, if_then_scope: _ } => { + // Give a specific `let_source` for the condition. + let let_source = match ex.span.desugaring_kind() { + Some(DesugaringKind::WhileLoop) => LetSource::WhileLet, + _ => LetSource::IfLet, + }; + self.with_let_source(let_source, |this| this.visit_expr(&self.thir[cond])); + self.with_let_source(LetSource::None, |this| { + this.visit_expr(&this.thir[then]); + if let Some(else_) = else_opt { + this.visit_expr(&this.thir[else_]); + } + }); + return; + } + ExprKind::Match { scrutinee, box ref arms } => { + let source = match ex.span.desugaring_kind() { + Some(DesugaringKind::ForLoop) => hir::MatchSource::ForLoopDesugar, + Some(DesugaringKind::QuestionMark) => hir::MatchSource::TryDesugar, + Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar, + _ => hir::MatchSource::Normal, + }; + self.check_match(scrutinee, arms, source, ex.span); + } + ExprKind::Let { box ref pat, expr } => { + self.check_let(pat, expr, self.let_source, ex.span); + } + ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => { + self.check_let_chain(self.let_source, ex.span, lhs, rhs); + } + _ => {} }; - if els.is_none() { - self.check_irrefutable(&loc.pat, msg, sp); - } + self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex)); } - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - intravisit::walk_param(self, param); - self.check_irrefutable(¶m.pat, "function argument", None); - } -} - -impl PatCtxt<'_, '_> { - fn report_inlining_errors(&self) { - for error in &self.errors { - match *error { - PatternError::StaticInPattern(span) => { - self.tcx.sess.emit_err(StaticInPattern { span }); + fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) { + let old_lint_level = self.lint_level; + match stmt.kind { + StmtKind::Let { + box ref pattern, initializer, else_block, lint_level, span, .. + } => { + if let LintLevel::Explicit(lint_level) = lint_level { + self.lint_level = lint_level; } - PatternError::AssocConstInPattern(span) => { - self.tcx.sess.emit_err(AssocConstInPattern { span }); - } - PatternError::ConstParamInPattern(span) => { - self.tcx.sess.emit_err(ConstParamInPattern { span }); + + if let Some(initializer) = initializer && else_block.is_some() { + self.check_let(pattern, initializer, LetSource::LetElse, span); } - PatternError::NonConstPath(span) => { - self.tcx.sess.emit_err(NonConstPath { span }); + + if else_block.is_none() { + self.check_irrefutable(pattern, "local binding", Some(span)); } } + _ => {} } + visit::walk_stmt(self, stmt); + self.lint_level = old_lint_level; } } impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { - fn check_patterns(&self, pat: &Pat<'_>, rf: RefutableFlag) { + #[instrument(level = "trace", skip(self, f))] + fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) { + let old_let_source = self.let_source; + self.let_source = let_source; + ensure_sufficient_stack(|| f(self)); + self.let_source = old_let_source; + } + + fn check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag) { pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat)); check_for_bindings_named_same_as_variants(self, pat, rf); } @@ -136,73 +194,63 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { fn lower_pattern( &self, cx: &mut MatchCheckCtxt<'p, 'tcx>, - pat: &'tcx hir::Pat<'tcx>, - have_errors: &mut bool, + pattern: &Pat<'tcx>, ) -> &'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 + cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern)) } - fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> { + fn new_cx(&self, hir_id: HirId, refutable: bool) -> 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, + refutable, } } - fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, scrutinee: &hir::Expr<'_>, span: Span) { + #[instrument(level = "trace", skip(self))] + fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: ExprId, source: LetSource, span: Span) { + if let LetSource::None = source { + return; + } 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); + let mut cx = self.new_cx(self.lint_level, true); + let tpat = self.lower_pattern(&mut cx, pat); + self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span); } fn check_match( &mut self, - scrut: &hir::Expr<'_>, - hir_arms: &'tcx [hir::Arm<'tcx>], + scrut: ExprId, + arms: &[ArmId], source: hir::MatchSource, expr_span: Span, ) { - let mut cx = self.new_cx(scrut.hir_id); + let mut cx = self.new_cx(self.lint_level, true); - for arm in hir_arms { + for &arm in 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 arm = &self.thir.arms[arm]; + self.check_patterns(&arm.pattern, Refutable); } - let mut have_errors = false; - - let arms: Vec<_> = hir_arms + let tarms: Vec<_> = 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(), + .map(|&arm| { + let arm = &self.thir.arms[arm]; + let hir_id = match arm.lint_level { + LintLevel::Explicit(hir_id) => hir_id, + LintLevel::Inherited => self.lint_level, + }; + let pat = self.lower_pattern(&mut cx, &arm.pattern); + MatchArm { pat, hir_id, has_guard: arm.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); + let scrut = &self.thir[scrut]; + let scrut_ty = scrut.ty; + let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty); match source { // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }` @@ -219,12 +267,18 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { // 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 { + if source == hir::MatchSource::ForLoopDesugar && 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); + let pat = &self.thir[arms[1]].pattern; + // `pat` should be `Some(<pat_field>)` from a desugared for loop. + debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop)); + let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() }; + let [pat_field] = &subpatterns[..] else { bug!() }; + self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None); } else { - non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, hir_arms, expr_span); + non_exhaustive_match( + &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span, + ); } } } @@ -233,114 +287,93 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { &mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId, + source: LetSource, 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); + irrefutable_let_patterns(cx.tcx, pat_id, source, 1, span); } } - fn check_let_chain(&mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId) -> bool { - let hir = self.tcx.hir(); - let parent = hir.parent_id(pat_id); + #[instrument(level = "trace", skip(self))] + fn check_let_chain( + &mut self, + let_source: LetSource, + top_expr_span: Span, + mut lhs: ExprId, + rhs: ExprId, + ) { + if let LetSource::None = let_source { + return; + } - // 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.parent_id(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; + // Lint level enclosing the next `lhs`. + let mut cur_lint_level = self.lint_level; + + // 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 add = |expr: ExprId, mut local_lint_level| { + // `local_lint_level` is the lint level enclosing the pattern inside `expr`. + let mut expr = &self.thir[expr]; + debug!(?expr, ?local_lint_level, "add"); + // Fast-forward through scopes. + while let ExprKind::Scope { value, lint_level, .. } = expr.kind { + if let LintLevel::Explicit(hir_id) = lint_level { + local_lint_level = hir_id } - // 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; + expr = &self.thir[value]; + } + debug!(?expr, ?local_lint_level, "after scopes"); + match expr.kind { + ExprKind::Let { box ref pat, expr: _ } => { + let mut ncx = self.new_cx(local_lint_level, true); + let tpat = self.lower_pattern(&mut ncx, pat); + let refutable = !is_let_irrefutable(&mut ncx, local_lint_level, tpat); + Some((expr.span, refutable)) } - } else { - // We've reached the top. - break; + _ => None, } + }; - // 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; - } + // Let chains recurse on the left, so we start by adding the rightmost. + chain_refutabilities.push(add(rhs, cur_lint_level)); - // 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 + while let ExprKind::Scope { value, lint_level, .. } = self.thir[lhs].kind { + if let LintLevel::Explicit(hir_id) = lint_level { + cur_lint_level = hir_id + } + lhs = value; + } + if let ExprKind::LogicalOp { op: LogicalOp::And, lhs: new_lhs, rhs: expr } = + self.thir[lhs].kind { - add(rhs); - cur_expr = lhs; + chain_refutabilities.push(add(expr, cur_lint_level)); + lhs = new_lhs; } else { - add(cur_expr); + chain_refutabilities.push(add(lhs, cur_lint_level)); break; } } + debug!(?chain_refutabilities); 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, + self.tcx, + self.lint_level, let_source, chain_refutabilities.len(), - top_expr.span, + top_expr_span, ); - return true; + return; } + 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. @@ -350,7 +383,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { // so can't always be moved out. // FIXME: Add checking whether the bindings are actually used in the prefix, // and lint if they are not. - let let_source = let_source_parent(self.tcx, top, None); if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) { // Emit the lint let prefix = &chain_refutabilities[..until]; @@ -358,9 +390,10 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let span_end = prefix.last().unwrap().unwrap().0; let span = span_start.to(span_end); let count = prefix.len(); - cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, LeadingIrrefutableLetPatterns { count }); + self.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, self.lint_level, span, LeadingIrrefutableLetPatterns { count }); } } + 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..]; @@ -368,18 +401,18 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let span_end = suffix.last().unwrap().unwrap().0; let span = span_start.to(span_end); let count = suffix.len(); - cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, TrailingIrrefutableLetPatterns { count }); + self.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, self.lint_level, span, TrailingIrrefutableLetPatterns { count }); } - true } - fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) { - let mut cx = self.new_cx(pat.hir_id); + #[instrument(level = "trace", skip(self))] + fn check_irrefutable(&self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) { + let mut cx = self.new_cx(self.lint_level, false); - let pattern = self.lower_pattern(&mut cx, pat, &mut false); + let pattern = self.lower_pattern(&mut cx, pat); let pattern_ty = pattern.ty(); - let arm = MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }; - let report = compute_match_usefulness(&cx, &[arm], pat.hir_id, pattern_ty); + let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false }; + let report = compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty); // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We // only care about exhaustiveness here. @@ -390,58 +423,45 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { return; } - let (inform, interpreted_as_const, res_defined_here,let_suggestion, misc_suggestion) = - if let hir::PatKind::Path(hir::QPath::Resolved( - None, - hir::Path { - segments: &[hir::PathSegment { args: None, res, ident, .. }], - .. - }, - )) = &pat.kind - { - ( - None, - Some(InterpretedAsConst { - span: pat.span, - article: res.article(), - variable: ident.to_string().to_lowercase(), - res, - }), - try { - ResDefinedHere { - def_span: cx.tcx.hir().res_span(res)?, - res, - } - }, - None, - None, - ) - } else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) { - let mut bindings = vec![]; - pat.walk_always(&mut |pat: &hir::Pat<'_>| { - if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { - bindings.push(ident); - } + let inform = sp.is_some().then_some(Inform); + let mut let_suggestion = None; + let mut misc_suggestion = None; + let mut interpreted_as_const = None; + if let PatKind::Constant { .. } = pat.kind + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) + { + // If the pattern to match is an integer literal: + if snippet.chars().all(|c| c.is_digit(10)) { + // Then give a suggestion, the user might've meant to create a binding instead. + misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral { + start_span: pat.span.shrink_to_lo() }); - 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(); - let count = witnesses.len(); - - // If the pattern to match is an integer literal: - let int_suggestion = if - let PatKind::Lit(expr) = &pat.kind - && bindings.is_empty() - && let ExprKind::Lit(Spanned { node: LitKind::Int(_, _), span }) = expr.kind { - // Then give a suggestion, the user might've meant to create a binding instead. - Some(MiscPatternSuggestion::AttemptedIntegerLiteral { start_span: span.shrink_to_lo() }) - } else { None }; - - let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }}; - (sp.map(|_|Inform), None, None, Some(let_suggestion), int_suggestion) - } else{ - (sp.map(|_|Inform), None, None, None, None) - }; + } else if snippet.chars().all(|c| c.is_alphanumeric() || c == '_') { + interpreted_as_const = Some(InterpretedAsConst { + span: pat.span, + variable: snippet, + }); + } + } + + if let Some(span) = sp + && self.tcx.sess.source_map().is_span_accessible(span) + && interpreted_as_const.is_none() + { + let mut bindings = vec![]; + pat.each_binding(|name, _, _, _| bindings.push(name)); + + let semi_span = span.shrink_to_hi(); + let start_span = span.shrink_to_lo(); + let end_span = semi_span.shrink_to_lo(); + let count = witnesses.len(); + + let_suggestion = Some(if bindings.is_empty() { + SuggestLet::If { start_span, semi_span, count } + } else { + SuggestLet::Else { end_span, count } + }); + }; let adt_defined_here = try { let ty = pattern_ty.peel_refs(); @@ -465,7 +485,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { pattern_ty, let_suggestion, misc_suggestion, - res_defined_here, adt_defined_here, }); } @@ -477,14 +496,18 @@ fn check_for_bindings_named_same_as_variants( 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() + if let PatKind::Binding { + name, + mode: BindingMode::ByValue, + mutability: Mutability::Not, + subpattern: None, + ty, + .. + } = p.kind + && let ty::Adt(edef, _) = ty.peel_refs().kind() && edef.is_enum() && edef.variants().iter().any(|variant| { - variant.ident(cx.tcx) == ident && variant.ctor_kind() == Some(CtorKind::Const) + variant.name == name && variant.ctor_kind() == Some(CtorKind::Const) }) { let variant_count = edef.variants().len(); @@ -493,7 +516,7 @@ fn check_for_bindings_named_same_as_variants( }); cx.tcx.emit_spanned_lint( BINDINGS_WITH_VARIANT_NAME, - p.hir_id, + cx.lint_level, p.span, BindingsWithVariantName { // If this is an irrefutable pattern, and there's > 1 variant, @@ -503,7 +526,7 @@ fn check_for_bindings_named_same_as_variants( Some(p.span) } else { None }, ty_path, - ident, + name, }, ) } @@ -529,11 +552,6 @@ fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option< ); } -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, @@ -548,7 +566,7 @@ fn irrefutable_let_patterns( } match source { - LetSource::GenericLet => emit_diag!(IrrefutableLetPatternsGenericLet), + LetSource::None => bug!(), LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet), LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard), LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse), @@ -604,10 +622,11 @@ fn report_arm_reachability<'p, 'tcx>( /// Report that a match is not exhaustive. fn non_exhaustive_match<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, + thir: &Thir<'tcx>, scrut_ty: Ty<'tcx>, sp: Span, witnesses: Vec<DeconstructedPat<'p, 'tcx>>, - arms: &[hir::Arm<'tcx>], + arms: &[ArmId], expr_span: Span, ) { let is_empty_match = arms.is_empty(); @@ -705,6 +724,7 @@ fn non_exhaustive_match<'p, 'tcx>( )); } [only] => { + let only = &thir[*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) @@ -713,8 +733,9 @@ fn non_exhaustive_match<'p, 'tcx>( } else { (" ".to_string(), false) }; - let comma = if matches!(only.body.kind, hir::ExprKind::Block(..)) - && only.span.eq_ctxt(only.body.span) + let only_body = &thir[only.body]; + let comma = if matches!(only_body.kind, ExprKind::Block { .. }) + && only.span.eq_ctxt(only_body.span) && is_multiline { "" @@ -726,24 +747,29 @@ fn non_exhaustive_match<'p, 'tcx>( format!("{}{}{} => todo!()", comma, pre_indentation, pattern), )); } - [.., prev, last] if prev.span.eq_ctxt(last.span) => { - let comma = if matches!(last.body.kind, hir::ExprKind::Block(..)) - && last.span.eq_ctxt(last.body.span) - { - "" - } else { - "," - }; - let spacing = if sm.is_multiline(prev.span.between(last.span)) { - sm.indentation_before(last.span).map(|indent| format!("\n{indent}")) - } else { - Some(" ".to_string()) - }; - if let Some(spacing) = spacing { - suggestion = Some(( - last.span.shrink_to_hi(), - format!("{}{}{} => todo!()", comma, spacing, pattern), - )); + [.., prev, last] => { + let prev = &thir[*prev]; + let last = &thir[*last]; + if prev.span.eq_ctxt(last.span) { + let last_body = &thir[last.body]; + let comma = if matches!(last_body.kind, ExprKind::Block { .. }) + && last.span.eq_ctxt(last_body.span) + { + "" + } else { + "," + }; + let spacing = if sm.is_multiline(prev.span.between(last.span)) { + sm.indentation_before(last.span).map(|indent| format!("\n{indent}")) + } else { + Some(" ".to_string()) + }; + if let Some(spacing) = spacing { + suggestion = Some(( + last.span.shrink_to_hi(), + format!("{}{}{} => todo!()", comma, spacing, pattern), + )); + } } } _ => {} @@ -863,10 +889,6 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( } /// 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) -> bool { - !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx, cx.param_env) -} - /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns. /// /// For example, this would reject: @@ -877,45 +899,36 @@ fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId) -> bool { /// - `x @ Some(ref mut? y)`. /// /// This analysis is *not* subsumed by NLL. -fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>) { +fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) { // 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 PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else { return }; + + let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env); - 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) => { + let mut_outer = match mode { + BindingMode::ByValue if is_binding_by_move(ty) => { // 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), - } + sub.each_binding(|_, mode, _, span| match mode { + BindingMode::ByValue => {} + BindingMode::ByRef(_) => conflicts_ref.push(span), }); if !conflicts_ref.is_empty() { sess.emit_err(BorrowOfMovedValue { - span: pat.span, - binding_span, + binding_span: pat.span, conflicts_ref, name, - ty: typeck_results.node_type(pat.hir_id), - suggest_borrowing: pat - .span - .contains(binding_span) - .then(|| binding_span.shrink_to_lo()), + ty, + suggest_borrowing: Some(pat.span.shrink_to_lo()), }); } return; } - Some(ty::BindByValue(_)) | None => return, - Some(ty::BindByReference(m)) => m, + BindingMode::ByValue => return, + BindingMode::ByRef(m) => m.mutability(), }; // We now have `ref $mut_outer binding @ sub` (semantically). @@ -923,9 +936,9 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa 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) { + sub.each_binding(|name, mode, ty, span| { + match mode { + BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) { // Both sides are `ref`. (Mutability::Not, Mutability::Not) => {} // 2x `ref mut`. @@ -939,10 +952,10 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa conflicts_mut_ref.push(Conflict::Ref { span, name }) } }, - Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id) => { + BindingMode::ByValue if is_binding_by_move(ty) => { conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict. } - Some(ty::BindByValue(_)) | None => {} // `ref mut?` + by-copy is fine. + BindingMode::ByValue => {} // `ref mut?` + by-copy is fine. } }); @@ -950,92 +963,30 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa let report_mut_ref = !conflicts_mut_ref.is_empty(); let report_move_conflict = !conflicts_move.is_empty(); - let mut occurences = match mut_outer { - Mutability::Mut => vec![Conflict::Mut { span: binding_span, name }], - Mutability::Not => vec![Conflict::Ref { span: binding_span, name }], + let mut occurrences = match mut_outer { + Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }], + Mutability::Not => vec![Conflict::Ref { span: pat.span, name }], }; - occurences.extend(conflicts_mut_mut); - occurences.extend(conflicts_mut_ref); - occurences.extend(conflicts_move); + occurrences.extend(conflicts_mut_mut); + occurrences.extend(conflicts_mut_ref); + occurrences.extend(conflicts_move); // Report errors if any. if report_mut_mut { // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`. - sess.emit_err(MultipleMutBorrows { span: pat.span, occurences }); + sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences }); } else if report_mut_ref { // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse. match mut_outer { Mutability::Mut => { - sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurences }); + sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences }); } Mutability::Not => { - sess.emit_err(AlreadyBorrowed { span: pat.span, occurences }); + sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences }); } }; } else if report_move_conflict { // Report by-ref and by-move conflicts, e.g. `ref x @ y`. - sess.emit_err(MovedWhileBorrowed { span: pat.span, occurences }); - } -} - -#[derive(Clone, Copy, Debug)] -pub enum LetSource { - GenericLet, - IfLet, - IfLetGuard, - LetElse, - WhileLet, -} - -fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { - let hir = tcx.hir(); - - let parent = hir.parent_id(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.parent_id(parent); - let parent_parent_node = hir.get(parent_parent); - match parent_parent_node { - hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), .. }) => { - return LetSource::LetElse; - } - hir::Node::Arm(hir::Arm { guard: Some(hir::Guard::If(_)), .. }) => { - return LetSource::IfLetGuard; - } - _ => {} + sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences }); } - - let parent_parent_parent = hir.parent_id(parent_parent); - let parent_parent_parent_parent = hir.parent_id(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 index ff88d0013..32d0404bd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,14 +1,15 @@ use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_middle::mir::{self, Field}; +use rustc_infer::traits::Obligation; +use rustc_middle::mir; use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::Span; -use rustc_trait_selection::traits::predicate_for_trait_def; +use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation}; +use rustc_trait_selection::traits::{self, ObligationCause}; use std::cell::Cell; @@ -58,8 +59,6 @@ struct ConstToPat<'tcx> { // inference context used for checking `T: Structural` bounds. infcx: InferCtxt<'tcx>, - include_lint_checks: bool, - treat_byte_string_as_slice: bool, } @@ -92,7 +91,6 @@ impl<'tcx> ConstToPat<'tcx> { 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), @@ -133,7 +131,7 @@ impl<'tcx> ConstToPat<'tcx> { }) }); - if self.include_lint_checks && !self.saw_const_match_error.get() { + if !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`. @@ -189,17 +187,15 @@ impl<'tcx> ConstToPat<'tcx> { // 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( + let partial_eq_obligation = Obligation::new( self.tcx(), + ObligationCause::dummy(), self.param_env, - ObligationCause::misc(self.span, self.id.owner.def_id), - partial_eq_trait_id, - 0, - [ty, ty], + self.tcx().mk_trait_ref(partial_eq_trait_id, [ty, ty]), ); - // FIXME: should this call a `predicate_must_hold` variant instead? - let has_impl = self.infcx.predicate_may_hold(&obligation); + // FIXME: should this call a `predicate_must_hold` variant instead? + let has_impl = self.infcx.predicate_may_hold(&partial_eq_obligation); // Note: To fix rust-lang/rust#65466, we could just remove this type // walk hack for function pointers, and unconditionally error @@ -220,7 +216,7 @@ impl<'tcx> ConstToPat<'tcx> { ) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> { vals.enumerate() .map(|(idx, val)| { - let field = Field::new(idx); + let field = FieldIdx::new(idx); Ok(FieldPat { field, pattern: self.recur(val, false)? }) }) .collect() @@ -240,21 +236,19 @@ impl<'tcx> ConstToPat<'tcx> { let kind = match cv.ty().kind() { ty::Float(_) => { - if self.include_lint_checks { tcx.emit_spanned_lint( lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, FloatPattern, ); - } 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 err = UnionPattern { span }; - tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + tcx.sess.emit_err(err); PatKind::Wild } ty::Adt(..) @@ -268,7 +262,7 @@ impl<'tcx> ConstToPat<'tcx> { { self.saw_const_match_error.set(true); let err = TypeNotStructural { span, non_sm_ty }; - tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + tcx.sess.emit_err(err); PatKind::Wild } // If the type is not structurally comparable, just emit the constant directly, @@ -281,8 +275,7 @@ impl<'tcx> ConstToPat<'tcx> { // 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() + if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); @@ -306,7 +299,7 @@ impl<'tcx> ConstToPat<'tcx> { ); self.saw_const_match_error.set(true); let err = TypeNotStructural { span, non_sm_ty: cv.ty() }; - tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + tcx.sess.emit_err(err); PatKind::Wild } ty::Adt(adt_def, substs) if adt_def.is_enum() => { @@ -340,7 +333,7 @@ impl<'tcx> ConstToPat<'tcx> { ty::Dynamic(..) => { self.saw_const_match_error.set(true); let err = InvalidPattern { span, non_sm_ty: cv.ty() }; - tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + tcx.sess.emit_err(err); PatKind::Wild } // `&str` is represented as `ConstValue::Slice`, let's keep using this @@ -407,8 +400,7 @@ impl<'tcx> ConstToPat<'tcx> { // to figure out how to get a reference again. ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => { if self.behind_reference.get() { - if self.include_lint_checks - && !self.saw_const_match_error.get() + if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); @@ -424,7 +416,7 @@ impl<'tcx> ConstToPat<'tcx> { if !self.saw_const_match_error.get() { self.saw_const_match_error.set(true); let err = TypeNotStructural { span, non_sm_ty: *pointee_ty }; - tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + tcx.sess.emit_err(err); } PatKind::Wild } @@ -438,7 +430,7 @@ impl<'tcx> ConstToPat<'tcx> { // (except slices, which are handled in a separate arm above). let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; - tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + tcx.sess.emit_err(err); PatKind::Wild } else { @@ -466,8 +458,7 @@ impl<'tcx> ConstToPat<'tcx> { // 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() + if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); @@ -483,13 +474,12 @@ impl<'tcx> ConstToPat<'tcx> { _ => { self.saw_const_match_error.set(true); let err = InvalidPattern { span, non_sm_ty: cv.ty() }; - tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + tcx.sess.emit_err(err); PatKind::Wild } }; - if self.include_lint_checks - && !self.saw_const_match_error.get() + if !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 diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index e5b7d685c..7c2919644 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -53,14 +53,14 @@ use smallvec::{smallvec, SmallVec}; use rustc_data_structures::captures::Captures; use rustc_hir::{HirId, RangeEnd}; use rustc_index::vec::Idx; -use rustc_middle::mir::{self, Field}; +use rustc_middle::mir; 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 rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx, FIRST_VARIANT}; use self::Constructor::*; use self::SliceKind::*; @@ -258,7 +258,7 @@ impl IntRange { pcx: &PatCtxt<'_, 'p, 'tcx>, pats: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>, column_count: usize, - hir_id: HirId, + lint_root: HirId, ) { if self.is_singleton() { return; @@ -290,7 +290,7 @@ impl IntRange { if !overlap.is_empty() { pcx.cx.tcx.emit_spanned_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, - hir_id, + lint_root, pcx.span, OverlappingRangeEndpoints { overlap, range: pcx.span }, ); @@ -706,7 +706,7 @@ impl<'tcx> Constructor<'tcx> { Variant(idx) => idx, Single => { assert!(!adt.is_enum()); - VariantIdx::new(0) + FIRST_VARIANT } _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } @@ -1126,7 +1126,7 @@ impl<'tcx> SplitWildcard<'tcx> { /// 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 +/// included in `fields`. For that reason, when you have a `FieldIdx` you must use /// `index_with_declared_idx`. #[derive(Debug, Clone, Copy)] pub(super) struct Fields<'p, 'tcx> { @@ -1154,8 +1154,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { fn wildcards_from_tys( cx: &MatchCheckCtxt<'p, 'tcx>, tys: impl IntoIterator<Item = Ty<'tcx>>, + span: Span, ) -> Self { - Fields::from_iter(cx, tys.into_iter().map(DeconstructedPat::wildcard)) + Fields::from_iter(cx, tys.into_iter().map(|ty| DeconstructedPat::wildcard(ty, span))) } // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide @@ -1165,7 +1166,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { cx: &'a MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>, variant: &'a VariantDef, - ) -> impl Iterator<Item = (Field, Ty<'tcx>)> + Captures<'a> + Captures<'p> { + ) -> impl Iterator<Item = (FieldIdx, 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(); @@ -1180,7 +1181,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { if is_uninhabited && (!is_visible || is_non_exhaustive) { None } else { - Some((Field::new(i), ty)) + Some((FieldIdx::new(i), ty)) } }) } @@ -1191,18 +1192,18 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { 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::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter(), pcx.span), + ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty), pcx.span), 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))) + Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0)), pcx.span) } 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) + Fields::wildcards_from_tys(pcx.cx, tys, pcx.span) } } _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx), @@ -1210,7 +1211,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { 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)) + Fields::wildcards_from_tys(pcx.cx, (0..arity).map(|_| ty), pcx.span) } _ => bug!("bad slice pattern {:?} {:?}", constructor, pcx), }, @@ -1251,8 +1252,8 @@ pub(crate) struct DeconstructedPat<'p, 'tcx> { } 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 wildcard(ty: Ty<'tcx>, span: Span) -> Self { + Self::new(Wildcard, Fields::empty(), ty, span) } pub(super) fn new( @@ -1269,7 +1270,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// `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) + DeconstructedPat::new(ctor, fields, pcx.ty, pcx.span) } /// Clone this value. This method emphasizes that cloning loses reachability information and @@ -1298,7 +1299,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { ty::Tuple(fs) => { ctor = Single; let mut wilds: SmallVec<[_; 2]> = - fs.iter().map(DeconstructedPat::wildcard).collect(); + fs.iter().map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect(); for pat in subpatterns { wilds[pat.field.index()] = mkpat(&pat.pattern); } @@ -1317,11 +1318,11 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { // 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 { + let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0); + let pat = if let Some(pat) = pattern { mkpat(&pat.pattern) } else { - DeconstructedPat::wildcard(substs.type_at(0)) + DeconstructedPat::wildcard(substs.type_at(0), pat.span) }; ctor = Single; fields = Fields::singleton(cx, pat); @@ -1343,7 +1344,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { ty }); let mut wilds: SmallVec<[_; 2]> = - tys.map(DeconstructedPat::wildcard).collect(); + tys.map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect(); for pat in subpatterns { if let Some(i) = field_id_to_id[pat.field.index()] { wilds[i] = mkpat(&pat.pattern); @@ -1438,7 +1439,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { ty::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns .enumerate() - .map(|(i, pattern)| FieldPat { field: Field::new(i), pattern }) + .map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern }) .collect(), }, ty::Adt(adt_def, _) if adt_def.is_box() => { @@ -1566,8 +1567,10 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { }; 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 wildcard: &_ = pcx + .cx + .pattern_arena + .alloc(DeconstructedPat::wildcard(inner_ty, pcx.span)); 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() diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 41306dd80..70d015a39 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -21,29 +21,20 @@ 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::mir::{BorrowKind, 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_middle::ty::{self, AdtDef, ConstKind, Region, Ty, TyCtxt, UserType}; use rustc_span::{Span, Symbol}; +use rustc_target::abi::FieldIdx; use std::cmp::Ordering; -#[derive(Clone, Debug)] -enum PatternError { - AssocConstInPattern(Span), - ConstParamInPattern(Span), - StaticInPattern(Span), - NonConstPath(Span), -} - struct PatCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, - errors: Vec<PatternError>, - include_lint_checks: bool, } pub(super) fn pat_from_hir<'a, 'tcx>( @@ -52,30 +43,13 @@ pub(super) fn pat_from_hir<'a, 'tcx>( typeck_results: &'a ty::TypeckResults<'tcx>, pat: &'tcx hir::Pat<'tcx>, ) -> Box<Pat<'tcx>> { - let mut pcx = PatCtxt::new(tcx, param_env, typeck_results); + let mut pcx = PatCtxt { 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> { - 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 } - } - - fn include_lint_checks(&mut self) -> &mut Self { - self.include_lint_checks = true; - self - } - fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<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: @@ -356,7 +330,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let subpatterns = fields .iter() .map(|field| FieldPat { - field: Field::new(self.typeck_results.field_index(field.hir_id)), + field: self.typeck_results.field_index(field.hir_id), pattern: self.lower_pattern(&field.pat), }) .collect(); @@ -379,7 +353,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { pats.iter() .enumerate_and_adjust(expected_len, gap_pos) .map(|(i, subpattern)| FieldPat { - field: Field::new(i), + field: FieldIdx::new(i), pattern: self.lower_pattern(subpattern), }) .collect() @@ -472,12 +446,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { | Res::SelfTyAlias { .. } | 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), + match res { + Res::Def(DefKind::ConstParam, _) => { + self.tcx.sess.emit_err(ConstParamInPattern { span }) + } + Res::Def(DefKind::Static(_), _) => { + self.tcx.sess.emit_err(StaticInPattern { span }) + } + _ => self.tcx.sess.emit_err(NonConstPath { span }), }; - self.errors.push(pattern_error); PatKind::Wild } }; @@ -530,7 +507,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // 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)); + self.tcx.sess.emit_err(AssocConstInPattern { span }); return pat_from_kind(PatKind::Wild); } @@ -608,7 +585,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { match value { mir::ConstantKind::Ty(c) => match c.kind() { ConstKind::Param(_) => { - self.errors.push(PatternError::ConstParamInPattern(span)); + self.tcx.sess.emit_err(ConstParamInPattern { span }); return PatKind::Wild; } ConstKind::Error(_) => { @@ -723,7 +700,7 @@ macro_rules! ClonePatternFoldableImpls { } ClonePatternFoldableImpls! { <'tcx> - Span, Field, Mutability, Symbol, LocalVarId, usize, + Span, FieldIdx, Mutability, Symbol, LocalVarId, usize, Region<'tcx>, Ty<'tcx>, BindingMode, AdtDef<'tcx>, SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, UserTypeProjection, CanonicalUserTypeAnnotation<'tcx> diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index be66d0d47..d8f66a175 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -318,6 +318,8 @@ pub(crate) struct MatchCheckCtxt<'p, 'tcx> { pub(crate) module: DefId, pub(crate) param_env: ty::ParamEnv<'tcx>, pub(crate) pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>, + /// Only produce `NON_EXHAUSTIVE_OMITTED_PATTERNS` lint on refutable patterns. + pub(crate) refutable: bool, } impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { @@ -603,7 +605,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { 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)] + vec![DeconstructedPat::wildcard(pcx.ty, pcx.span)] } else { let mut split_wildcard = SplitWildcard::new(pcx); split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); @@ -630,7 +632,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { .collect(); if hide_variant_show_wild { - new.push(DeconstructedPat::wildcard(pcx.ty)); + new.push(DeconstructedPat::wildcard(pcx.ty, pcx.span)); } new @@ -733,7 +735,7 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { 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) + DeconstructedPat::new(ctor.clone(), fields, pcx.ty, pcx.span) }; self.0.push(pat); @@ -764,13 +766,13 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { /// `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), ret)] +#[instrument(level = "debug", skip(cx, matrix, lint_root), ret)] fn is_useful<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'p, 'tcx>, witness_preference: ArmType, - hir_id: HirId, + lint_root: HirId, is_under_guard: bool, is_top_level: bool, ) -> Usefulness<'p, 'tcx> { @@ -803,7 +805,7 @@ fn is_useful<'p, 'tcx>( 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) + is_useful(cx, &matrix, &v, witness_preference, lint_root, is_under_guard, false) }); debug!(?usefulness); ret.extend(usefulness); @@ -836,7 +838,7 @@ fn is_useful<'p, 'tcx>( pcx, matrix.heads(), matrix.column_count().unwrap_or(0), - hir_id, + lint_root, ) } // We split the head constructor of `v`. @@ -851,7 +853,15 @@ fn is_useful<'p, 'tcx>( 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) + is_useful( + cx, + &spec_matrix, + &v, + witness_preference, + lint_root, + is_under_guard, + false, + ) }); let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor); @@ -859,6 +869,8 @@ fn is_useful<'p, 'tcx>( // 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 + // Only emit a lint on refutable patterns. + && cx.refutable // We check that the match has a wildcard pattern and 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 {}` @@ -893,7 +905,7 @@ fn is_useful<'p, 'tcx>( // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. cx.tcx.emit_spanned_lint( NON_EXHAUSTIVE_OMITTED_PATTERNS, - hir_id, + lint_root, pcx.span, NonExhaustiveOmittedPattern { scrut_ty: pcx.ty, @@ -951,7 +963,7 @@ pub(crate) struct UsefulnessReport<'p, 'tcx> { pub(crate) fn compute_match_usefulness<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], - scrut_hir_id: HirId, + lint_root: HirId, scrut_ty: Ty<'tcx>, ) -> UsefulnessReport<'p, 'tcx> { let mut matrix = Matrix::empty(); @@ -974,9 +986,9 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( }) .collect(); - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); + let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP)); let v = PatStack::from_pattern(wild_pattern); - let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true); + let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, lint_root, false, true); let non_exhaustiveness_witnesses = match usefulness { WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(), NoWitnesses { .. } => bug!(), diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 8028227aa..ed61d6ee7 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -151,6 +151,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { initializer, else_block, lint_level, + span, } => { print_indented!(self, "kind: Let {", depth_lvl + 1); print_indented!( @@ -181,6 +182,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { } print_indented!(self, format!("lint_level: {:?}", lint_level), depth_lvl + 2); + print_indented!(self, format!("span: {:?}", span), depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } } |