diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 31745cae3c064..65bfc7d362b61 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -832,18 +832,18 @@ pub enum PatKind<'tcx> { value: ty::Value<'tcx>, }, - /// Pattern obtained by converting a constant (inline or named) to its pattern - /// representation using `const_to_pat`. This is used for unsafety checking. + /// Wrapper node representing a named constant that was lowered to a pattern + /// using `const_to_pat`. + /// + /// This is used by some diagnostics for non-exhaustive matches, to map + /// the pattern node back to the `DefId` of its original constant. + /// + /// FIXME(#150498): Can we make this an `Option` field on `Pat` + /// instead, so that non-diagnostic code can ignore it more easily? ExpandedConstant { /// [DefId] of the constant item. def_id: DefId, /// The pattern that the constant lowered to. - /// - /// HACK: we need to keep the `DefId` of inline constants around for unsafety checking; - /// therefore when a range pattern contains inline constants, we re-wrap the range pattern - /// with the `ExpandedConstant` nodes that correspond to the range endpoints. Hence - /// `subpattern` may actually be a range pattern, and `def_id` be the constant for one of - /// its endpoints. subpattern: Box>, }, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index a06c030b103b2..4f03e3d965c6c 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -410,14 +410,6 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_pat(self, pat); self.inside_adt = old_inside_adt; } - PatKind::ExpandedConstant { def_id, .. } => { - if let Some(def) = def_id.as_local() - && matches!(self.tcx.def_kind(def_id), DefKind::InlineConst) - { - self.visit_inner_body(def); - } - visit::walk_pat(self, pat); - } _ => { visit::walk_pat(self, pat); } 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 c4479c402ab2f..bf480cf601ee3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -5,7 +5,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, struct_span_code_err}; use rustc_hir::def::*; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::Level; @@ -687,12 +687,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { unpeeled_pat = subpattern; } - if let PatKind::ExpandedConstant { def_id, .. } = unpeeled_pat.kind - && let DefKind::Const = self.tcx.def_kind(def_id) - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) - // We filter out paths with multiple path::segments. - && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') - { + if let Some(def_id) = is_const_pat_that_looks_like_binding(self.tcx, unpeeled_pat) { let span = self.tcx.def_span(def_id); let variable = self.tcx.item_name(def_id).to_string(); // When we encounter a constant as the binding name, point at the `const` definition. @@ -1209,6 +1204,26 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { } } +/// If the given pattern is a named constant that looks like it could have been +/// intended to be a binding, returns the `DefId` of the named constant. +/// +/// Diagnostics use this to give more detailed suggestions for non-exhaustive +/// matches. +fn is_const_pat_that_looks_like_binding<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat<'tcx>) -> Option { + // The pattern must be a named constant, and the name that appears in + // the pattern's source text must resemble a plain identifier without any + // `::` namespace separators or other non-identifier characters. + if let PatKind::ExpandedConstant { def_id, .. } = pat.kind + && matches!(tcx.def_kind(def_id), DefKind::Const) + && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(pat.span) + && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') + { + Some(def_id) + } else { + None + } +} + /// Report that a match is not exhaustive. fn report_non_exhaustive_match<'p, 'tcx>( cx: &PatCtxt<'p, 'tcx>, @@ -1303,12 +1318,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( for &arm in arms { let arm = &thir.arms[arm]; - if let PatKind::ExpandedConstant { def_id, .. } = arm.pattern.kind - && !matches!(cx.tcx.def_kind(def_id), DefKind::InlineConst) - && let Ok(snippet) = cx.tcx.sess.source_map().span_to_snippet(arm.pattern.span) - // We filter out paths with multiple path::segments. - && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') - { + if let Some(def_id) = is_const_pat_that_looks_like_binding(cx.tcx, &arm.pattern) { let const_name = cx.tcx.item_name(def_id); err.span_label( arm.pattern.span, 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 ce4c89a8eb2e7..2da9a43f71d86 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 @@ -187,7 +187,7 @@ impl<'tcx> ConstToPat<'tcx> { } // Wrap the pattern in a marker node to indicate that it is the result of lowering a - // constant. This is used for diagnostics, and for unsafety checking of inline const blocks. + // constant. This is used for diagnostics. let kind = PatKind::ExpandedConstant { subpattern: inlined_const_as_pat, def_id: uv.def }; Box::new(Pat { kind, ty, span: self.span }) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index d62faae44d67f..063bb4a3d461a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -21,7 +21,6 @@ use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_span::def_id::DefId; use rustc_span::{ErrorGuaranteed, Span}; use tracing::{debug, instrument}; @@ -131,9 +130,8 @@ impl<'tcx> PatCtxt<'tcx> { fn lower_pattern_range_endpoint( &mut self, expr: Option<&'tcx hir::PatExpr<'tcx>>, - // Out-parameters collecting extra data to be reapplied by the caller + // Out-parameter collecting extra data to be reapplied by the caller ascriptions: &mut Vec>, - expanded_consts: &mut Vec, ) -> Result>, ErrorGuaranteed> { let Some(expr) = expr else { return Ok(None) }; @@ -148,8 +146,10 @@ impl<'tcx> PatCtxt<'tcx> { ascriptions.push(ascription); kind = subpattern.kind; } - PatKind::ExpandedConstant { def_id, subpattern } => { - expanded_consts.push(def_id); + PatKind::ExpandedConstant { def_id: _, subpattern } => { + // Expanded-constant nodes are currently only needed by + // diagnostics that don't apply to range patterns, so we + // can just discard them here. kind = subpattern.kind; } _ => break, @@ -227,10 +227,7 @@ impl<'tcx> PatCtxt<'tcx> { // Collect extra data while lowering the endpoints, to be reapplied later. let mut ascriptions = vec![]; - let mut expanded_consts = vec![]; - - let mut lower_endpoint = - |expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions, &mut expanded_consts); + let mut lower_endpoint = |expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions); let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity); let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity); @@ -282,10 +279,11 @@ impl<'tcx> PatCtxt<'tcx> { let subpattern = Box::new(Pat { span, ty, kind }); kind = PatKind::AscribeUserType { ascription, subpattern }; } - for def_id in expanded_consts { - let subpattern = Box::new(Pat { span, ty, kind }); - kind = PatKind::ExpandedConstant { def_id, subpattern }; - } + // `PatKind::ExpandedConstant` wrappers from range endpoints used to + // also be preserved here, but that was only needed for unsafeck of + // inline `const { .. }` patterns, which were removed by + // . + Ok(kind) }