summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_build/src/thir
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
commit218caa410aa38c29984be31a5229b9fa717560ee (patch)
treec54bd55eeb6e4c508940a30e94c0032fbd45d677 /compiler/rustc_mir_build/src/thir
parentReleasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz
rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_mir_build/src/thir')
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/mod.rs11
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs445
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs190
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs67
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs99
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs44
7 files changed, 317 insertions, 543 deletions
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index a9ed945d4..57ae6a365 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -33,13 +33,13 @@ pub(crate) fn lit_to_const<'tcx>(
let str_bytes = s.as_str().as_bytes();
ty::ValTree::from_raw_bytes(tcx, str_bytes)
}
- (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _))
+ (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() => {
+ (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)
}
diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
index b5c4b7b13..a355e1bda 100644
--- a/compiler/rustc_mir_build/src/thir/cx/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -18,10 +18,10 @@ use rustc_middle::thir::*;
use rustc_middle::ty::{self, RvalueScopes, TyCtxt};
use rustc_span::Span;
-pub(crate) fn thir_body<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub(crate) fn thir_body(
+ tcx: TyCtxt<'_>,
owner_def: ty::WithOptConstParam<LocalDefId>,
-) -> Result<(&'tcx Steal<Thir<'tcx>>, ExprId), ErrorGuaranteed> {
+) -> Result<(&Steal<Thir<'_>>, 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);
@@ -52,10 +52,7 @@ pub(crate) fn thir_body<'tcx>(
Ok((tcx.alloc_steal_thir(cx.thir), expr))
}
-pub(crate) fn thir_tree<'tcx>(
- tcx: TyCtxt<'tcx>,
- owner_def: ty::WithOptConstParam<LocalDefId>,
-) -> String {
+pub(crate) fn thir_tree(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam<LocalDefId>) -> String {
match thir_body(tcx, owner_def) {
Ok((thir, _)) => format!("{:#?}", thir.steal()),
Err(_) => "error".into(),
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 e369dba55..34e637f59 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -4,18 +4,22 @@ use super::usefulness::{
};
use super::{PatCtxt, PatternError};
+use crate::errors::*;
+
+use hir::{ExprKind, PatKind};
use rustc_arena::TypedArena;
-use rustc_ast::Mutability;
+use rustc_ast::{LitKind, Mutability};
use rustc_errors::{
- error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder,
- ErrorGuaranteed, MultiSpan,
+ 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::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,
};
@@ -107,28 +111,20 @@ impl PatCtxt<'_, '_> {
for error in &self.errors {
match *error {
PatternError::StaticInPattern(span) => {
- self.span_e0158(span, "statics cannot be referenced in patterns")
+ self.tcx.sess.emit_err(StaticInPattern { span });
}
PatternError::AssocConstInPattern(span) => {
- self.span_e0158(span, "associated consts cannot be referenced in patterns")
+ self.tcx.sess.emit_err(AssocConstInPattern { span });
}
PatternError::ConstParamInPattern(span) => {
- self.span_e0158(span, "const parameters cannot be referenced in patterns")
+ self.tcx.sess.emit_err(ConstParamInPattern { span });
}
PatternError::NonConstPath(span) => {
- rustc_middle::mir::interpret::struct_error(
- self.tcx.at(span),
- "runtime values cannot be referenced in patterns",
- )
- .emit();
+ self.tcx.sess.emit_err(NonConstPath { span });
}
}
}
}
-
- fn span_e0158(&self, span: Span, text: &str) {
- struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit();
- }
}
impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
@@ -251,14 +247,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
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);
+ let parent = hir.parent_id(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);
+ 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),
@@ -345,29 +341,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
);
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();
- let s = pluralize!(cnt);
- cx.tcx.struct_span_lint_hir(
- IRREFUTABLE_LET_PATTERNS,
- top,
- span,
- format!("{kind} irrefutable pattern{s} in let chain"),
- |lint| {
- lint.note(format!(
- "{these} pattern{s} will always match",
- these = pluralize!("this", cnt),
- ))
- .help(format!(
- "consider moving {} {suggestion}",
- if cnt > 1 { "them" } else { "it" }
- ))
- },
- );
- };
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.
@@ -381,13 +354,21 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
// Emit the lint
let prefix = &chain_refutabilities[..until];
- lint_affix(prefix, "leading", "outside of the construct");
+ let span_start = prefix[0].unwrap().0;
+ 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 });
}
}
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");
+ let span_start = suffix[0].unwrap().0;
+ 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 });
}
true
}
@@ -397,8 +378,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
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);
+ 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);
// Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
// only care about exhaustiveness here.
@@ -409,145 +390,84 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
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() =>
+ 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
{
- 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);
+ (
+ 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);
}
- 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() {
- err.span_suggestion_verbose(
- semi_span.shrink_to_lo(),
- &format!(
- "alternatively, you might want to use \
- let else to handle the variant{} that {} matched",
- pluralize!(witnesses.len()),
- match witnesses.len() {
- 1 => "isn't",
- _ => "aren't",
- },
- ),
- " else { todo!() }",
- 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();
- }
-}
+ 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)
+ };
-/// 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,),
- );
+ let adt_defined_here = try {
+ let ty = pattern_ty.peel_refs();
+ let ty::Adt(def, _) = ty.kind() else { None? };
+ let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span;
+ let mut variants = vec![];
- 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,
- );
+ for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) {
+ variants.push(Variant { span });
+ }
+ AdtDefinedHere { adt_def_span, ty, variants }
+ };
- if let Some(span) = tcx.hir().res_span(path.res) {
- err.span_label(span, format!("{} defined here", descr));
+ self.tcx.sess.emit_err(PatternNotCovered {
+ span: pat.span,
+ origin,
+ uncovered: Uncovered::new(pat.span, &cx, witnesses),
+ inform,
+ interpreted_as_const,
+ _p: (),
+ pattern_ty,
+ let_suggestion,
+ misc_suggestion,
+ res_defined_here,
+ adt_defined_here,
+ });
}
}
@@ -568,32 +488,22 @@ fn check_for_bindings_named_same_as_variants(
})
{
let variant_count = edef.variants().len();
- cx.tcx.struct_span_lint_hir(
+ let ty_path = with_no_trimmed_paths!({
+ cx.tcx.def_path_str(edef.did())
+ });
+ cx.tcx.emit_spanned_lint(
BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
- DelayDm(|| format!(
- "pattern binding `{}` is named the same as one \
- of the variants of the type `{}`",
- ident, cx.tcx.def_path_str(edef.did())
- )),
- |lint| {
- let ty_path = cx.tcx.def_path_str(edef.did());
- lint.code(error_code!(E0170));
-
+ BindingsWithVariantName {
// 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 {
- lint.span_suggestion(
- p.span,
- "to match on the variant, qualify the path",
- format!("{}::{}", ty_path, ident),
- Applicability::MachineApplicable,
- );
- }
-
- lint
+ suggestion: if rf == Refutable || variant_count == 1 {
+ Some(p.span)
+ } else { None },
+ ty_path,
+ ident,
},
)
}
@@ -611,14 +521,12 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
}
fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
- tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern", |lint| {
- if let Some(catchall) = catchall {
- // We had a catchall pattern, hint at that.
- lint.span_label(span, "unreachable pattern");
- lint.span_label(catchall, "matches any value");
- }
- lint
- });
+ tcx.emit_spanned_lint(
+ UNREACHABLE_PATTERNS,
+ id,
+ span,
+ UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
+ );
}
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
@@ -634,67 +542,18 @@ fn irrefutable_let_patterns(
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);
- tcx.struct_span_lint_hir(
- IRREFUTABLE_LET_PATTERNS,
- id,
- span,
- format!("irrefutable {} pattern{s}", $source_name),
- |lint| {
- lint.note(&format!(
- "{these} pattern{s} will always match, so the {}",
- $note_sufix
- ))
- .help(concat!("consider ", $help_sufix))
- },
- )
+ ($lint:tt) => {{
+ tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
}};
}
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"
- );
- }
- };
+ LetSource::GenericLet => emit_diag!(IrrefutableLetPatternsGenericLet),
+ LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
+ LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
+ LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
+ LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
+ }
}
fn is_let_irrefutable<'p, 'tcx>(
@@ -760,15 +619,17 @@ fn non_exhaustive_match<'p, 'tcx>(
// informative.
let mut err;
let pattern;
- let mut patterns_len = 0;
+ let patterns_len;
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();
+ cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
+ cx,
+ expr_span,
+ span: sp,
+ ty: scrut_ty,
+ });
+ return;
} else {
+ // FIXME: migration of this diagnostic will require list support
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
err = create_e0004(
cx.tcx.sess,
@@ -1039,24 +900,17 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa
}
});
if !conflicts_ref.is_empty() {
- let occurs_because = format!(
- "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
+ sess.emit_err(BorrowOfMovedValue {
+ span: pat.span,
+ binding_span,
+ conflicts_ref,
name,
- typeck_results.node_type(pat.hir_id),
- );
- let mut err = sess.struct_span_err(pat.span, "borrow of moved value");
- err.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");
- if pat.span.contains(binding_span) {
- err.span_suggestion_verbose(
- binding_span.shrink_to_lo(),
- "borrow this binding in the pattern to avoid moving the value",
- "ref ".to_string(),
- Applicability::MachineApplicable,
- );
- }
- err.emit();
+ ty: typeck_results.node_type(pat.hir_id),
+ suggest_borrowing: pat
+ .span
+ .contains(binding_span)
+ .then(|| binding_span.shrink_to_lo()),
+ });
}
return;
}
@@ -1086,19 +940,18 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa
// 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));
+ let mut occurences = vec![];
+
+ for (span, name_mut) in conflicts_mut_mut {
+ occurences.push(MultipleMutBorrowOccurence::Mutable { span, name_mut });
}
- for (span, name) in conflicts_mut_ref {
- err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name));
+ for (span, name_immut) in conflicts_mut_ref {
+ occurences.push(MultipleMutBorrowOccurence::Immutable { span, name_immut });
}
- for (span, name) in conflicts_move {
- err.span_label(span, format!("also moved into `{}` here", name));
+ for (span, name_moved) in conflicts_move {
+ occurences.push(MultipleMutBorrowOccurence::Moved { span, name_moved });
}
- err.emit();
+ sess.emit_err(MultipleMutBorrows { span: pat.span, binding_span, occurences, name });
} 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 {
@@ -1140,7 +993,7 @@ pub enum LetSource {
fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
let hir = tcx.hir();
- let parent = hir.get_parent_node(pat_id);
+ let parent = hir.parent_id(pat_id);
let_source_parent(tcx, parent, Some(pat_id))
}
@@ -1159,7 +1012,7 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
_ => {}
}
- let parent_parent = hir.get_parent_node(parent);
+ 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(_), .. }) => {
@@ -1171,8 +1024,8 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
_ => {}
}
- 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 = 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 {
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 a21f6cd39..7f3519945 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,11 +1,9 @@
-use rustc_errors::DelayDm;
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_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint;
use rustc_span::Span;
use rustc_trait_selection::traits::predicate_for_trait_def;
@@ -15,6 +13,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
use std::cell::Cell;
use super::PatCtxt;
+use crate::errors::{
+ FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch,
+ PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
+};
impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
/// Converts an evaluated constant to a pattern (if possible).
@@ -70,7 +72,7 @@ mod fallback_to_const_ref {
/// hoops to get a reference to the value.
pub(super) struct FallbackToConstRef(());
- pub(super) fn fallback_to_const_ref<'tcx>(c2p: &super::ConstToPat<'tcx>) -> FallbackToConstRef {
+ pub(super) fn fallback_to_const_ref(c2p: &super::ConstToPat<'_>) -> FallbackToConstRef {
assert!(c2p.behind_reference.get());
FallbackToConstRef(())
}
@@ -105,47 +107,6 @@ impl<'tcx> ConstToPat<'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)
}
@@ -176,7 +137,8 @@ impl<'tcx> ConstToPat<'tcx> {
// 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());
+ let structural =
+ traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
debug!(
"search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
cv.ty(),
@@ -194,17 +156,18 @@ impl<'tcx> ConstToPat<'tcx> {
return inlined_const_as_pat;
}
- if let Some(msg) = structural {
+ if let Some(non_sm_ty) = 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);
+ // fatal avoids ICE from resolution of non-existent method (rare case).
+ self.tcx()
+ .sess
+ .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty });
} else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
- self.tcx().struct_span_lint_hir(
+ self.tcx().emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
- msg,
- |lint| lint,
+ IndirectStructuralMatch { non_sm_ty },
);
} else {
debug!(
@@ -278,12 +241,11 @@ impl<'tcx> ConstToPat<'tcx> {
let kind = match cv.ty().kind() {
ty::Float(_) => {
if self.include_lint_checks {
- tcx.struct_span_lint_hir(
+ tcx.emit_spanned_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
span,
- "floating-point types cannot be used in patterns",
- |lint| lint,
+ FloatPattern,
);
}
PatKind::Constant { value: cv }
@@ -291,29 +253,22 @@ impl<'tcx> ConstToPat<'tcx> {
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);
- }
+ let err = UnionPattern { span };
+ tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
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 Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) =>
{
- // 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);
- }
+ let err = TypeNotStructural { span, non_sm_ty };
+ tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
// If the type is not structurally comparable, just emit the constant directly,
@@ -331,19 +286,11 @@ impl<'tcx> ConstToPat<'tcx> {
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
- tcx.struct_span_lint_hir(
+ tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
id,
span,
- DelayDm(|| {
- format!(
- "to use a constant of type `{}` in a pattern, \
- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
- cv.ty(),
- cv.ty(),
- )
- }),
- |lint| lint,
+ IndirectStructuralMatch { non_sm_ty: cv.ty() },
);
}
// Since we are behind a reference, we can just bubble the error up so we get a
@@ -357,18 +304,9 @@ impl<'tcx> ConstToPat<'tcx> {
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);
- }
+ let err = TypeNotStructural { span, non_sm_ty: cv.ty() };
+ tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
@@ -401,12 +339,8 @@ impl<'tcx> ConstToPat<'tcx> {
// 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);
- }
+ let err = InvalidPattern { span, non_sm_ty: cv.ty() };
+ tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
// `&str` is represented as `ConstValue::Slice`, let's keep using this
@@ -471,32 +405,26 @@ impl<'tcx> ConstToPat<'tcx> {
// 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) => {
+ 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()
&& !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(
+ self.saw_const_match_lint.set(true);
+ tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
- self.span,
- msg,
- |lint| lint,
+ span,
+ IndirectStructuralMatch { non_sm_ty: *pointee_ty },
);
}
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);
- }
+ let err = TypeNotStructural { span, non_sm_ty: *pointee_ty };
+ tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
}
PatKind::Wild
}
@@ -508,12 +436,10 @@ impl<'tcx> ConstToPat<'tcx> {
if !pointee_ty.is_sized(tcx, 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);
- }
+
+ let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
+ tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
+
PatKind::Wild
} else {
let old = self.behind_reference.replace(true);
@@ -545,27 +471,19 @@ impl<'tcx> ConstToPat<'tcx> {
&& !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(
+ tcx.emit_spanned_lint(
lint::builtin::POINTER_STRUCTURAL_MATCH,
id,
span,
- msg,
- |lint| lint,
+ PointerPattern
);
}
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);
- }
+ let err = InvalidPattern { span, non_sm_ty: cv.ty() };
+ tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
};
@@ -576,21 +494,17 @@ impl<'tcx> ConstToPat<'tcx> {
&& 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(
+ && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty())
+ {
+ self.saw_const_match_lint.set(true);
+ tcx.emit_spanned_lint(
lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
id,
span,
- msg,
- |lint| lint,
+ NontrivialStructuralMatch {non_sm_ty}
);
}
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 d60e8722c..aba5429da 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -45,7 +45,7 @@
use std::cell::Cell;
use std::cmp::{self, max, min, Ordering};
use std::fmt;
-use std::iter::{once, IntoIterator};
+use std::iter::once;
use std::ops::RangeInclusive;
use smallvec::{smallvec, SmallVec};
@@ -67,6 +67,7 @@ use self::SliceKind::*;
use super::compare_const_vals;
use super::usefulness::{MatchCheckCtxt, PatCtxt};
+use crate::errors::{Overlap, OverlappingRangeEndpoints};
/// 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>> {
@@ -96,7 +97,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
/// `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 {
+pub(crate) 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
@@ -140,27 +141,22 @@ impl IntRange {
) -> 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.)
- return scalar.to_bits_or_ptr_internal(target_size).unwrap().left();
- }
- 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"
- ),
- _ => {}
- },
- _ => {}
+ let val = if let mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) = value {
+ // 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.)
+ scalar.to_bits_or_ptr_internal(target_size).unwrap().left()?
+ } else {
+ if let mir::ConstantKind::Ty(c) = value
+ && let ty::ConstKind::Value(_) = c.kind()
+ {
+ 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)
- })()?;
+ value.try_eval_bits(tcx, param_env, ty)?
+ };
let val = val ^ bias;
Some(IntRange { range: val..=val, bias })
} else {
@@ -284,32 +280,21 @@ impl IntRange {
return;
}
- let overlaps: Vec<_> = pats
+ let overlap: 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))
+ .map(|(range, span)| Overlap {
+ range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty),
+ span,
+ })
.collect();
- if !overlaps.is_empty() {
- pcx.cx.tcx.struct_span_lint_hir(
+ if !overlap.is_empty() {
+ pcx.cx.tcx.emit_spanned_lint(
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
hir_id,
pcx.span,
- "multiple patterns overlap on their endpoints",
- |lint| {
- for (int_range, span) in overlaps {
- lint.span_label(
- span,
- &format!(
- "this range overlaps on `{}`...",
- int_range.to_pat(pcx.cx.tcx, pcx.ty)
- ),
- );
- }
- lint.span_label(pcx.span, "... with this range");
- lint.note("you likely meant to write mutually exclusive ranges");
- lint
- },
+ OverlappingRangeEndpoints { overlap, range: pcx.span },
);
}
}
@@ -404,7 +389,7 @@ impl SplitIntRange {
}
/// Iterate over the contained ranges.
- fn iter<'a>(&'a self) -> impl Iterator<Item = IntRange> + Captures<'a> {
+ fn iter(&self) -> impl Iterator<Item = IntRange> + Captures<'_> {
use IntBorder::*;
let self_range = Self::to_borders(self.range.clone());
@@ -612,7 +597,7 @@ impl SplitVarLenSlice {
}
/// Iterate over the partition of this slice.
- fn iter<'a>(&'a self) -> impl Iterator<Item = Slice> + Captures<'a> {
+ fn iter(&self) -> impl Iterator<Item = Slice> + Captures<'_> {
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
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 48a231a6c..3a6ef87c9 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -2,14 +2,16 @@
mod check_match;
mod const_to_pat;
-mod deconstruct_pat;
+pub(crate) mod deconstruct_pat;
mod usefulness;
pub(crate) use self::check_match::check_match;
+pub(crate) use self::usefulness::MatchCheckCtxt;
+use crate::errors::*;
use crate::thir::util::UserAnnotatedTyHelpers;
-use rustc_errors::struct_span_err;
+use rustc_errors::error_code;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
@@ -127,10 +129,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
hi: mir::ConstantKind<'tcx>,
end: RangeEnd,
span: Span,
+ lo_expr: Option<&hir::Expr<'tcx>>,
+ hi_expr: Option<&hir::Expr<'tcx>>,
) -> PatKind<'tcx> {
assert_eq!(lo.ty(), ty);
assert_eq!(hi.ty(), ty);
let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env);
+ let max = || {
+ self.tcx
+ .layout_of(self.param_env.with_reveal_all_normalized(self.tcx).and(ty))
+ .ok()
+ .unwrap()
+ .size
+ .unsigned_int_max()
+ };
match (end, cmp) {
// `x..y` where `x < y`.
// Non-empty because the range includes at least `x`.
@@ -139,13 +151,27 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}
// `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();
+ let mut lower_overflow = false;
+ let mut higher_overflow = false;
+ if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr
+ && let rustc_ast::ast::LitKind::Int(val, _) = lit.node
+ {
+ if lo.eval_bits(self.tcx, self.param_env, ty) != val {
+ lower_overflow = true;
+ self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
+ }
+ }
+ if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr
+ && let rustc_ast::ast::LitKind::Int(val, _) = lit.node
+ {
+ if hi.eval_bits(self.tcx, self.param_env, ty) != val {
+ higher_overflow = true;
+ self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
+ }
+ }
+ if !lower_overflow && !higher_overflow {
+ self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span });
+ }
PatKind::Wild
}
// `x..=y` where `x == y`.
@@ -156,23 +182,34 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}
// `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.",
- );
+ let mut lower_overflow = false;
+ let mut higher_overflow = false;
+ if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr
+ && let rustc_ast::ast::LitKind::Int(val, _) = lit.node
+ {
+ if lo.eval_bits(self.tcx, self.param_env, ty) != val {
+ lower_overflow = true;
+ self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
+ }
+ }
+ if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr
+ && let rustc_ast::ast::LitKind::Int(val, _) = lit.node
+ {
+ if hi.eval_bits(self.tcx, self.param_env, ty) != val {
+ higher_overflow = true;
+ self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
+ }
+ }
+ if !lower_overflow && !higher_overflow {
+ self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
+ span,
+ teach: if self.tcx.sess.teach(&error_code!(E0030)) {
+ Some(())
+ } else {
+ None
+ },
+ });
}
- err.emit();
PatKind::Wild
}
}
@@ -218,7 +255,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
let (lp, hp) = (lo.as_ref().map(|(x, _)| x), hi.as_ref().map(|(x, _)| x));
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),
+ Some((lc, hc)) => {
+ self.lower_pattern_range(ty, lc, hc, end, lo_span, lo_expr, hi_expr)
+ }
None => {
let msg = &format!(
"found bad range pattern `{:?}` outside of error recovery",
@@ -501,7 +540,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}
Err(_) => {
- self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+ self.tcx.sess.emit_err(CouldNotEvalConstPattern { span });
return pat_from_kind(PatKind::Wild);
}
};
@@ -548,11 +587,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
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");
+ self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
pat_from_kind(PatKind::Wild)
}
Err(_) => {
- self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+ self.tcx.sess.emit_err(CouldNotEvalConstPattern { span });
pat_from_kind(PatKind::Wild)
}
}
@@ -584,7 +623,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind,
mir::ConstantKind::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");
+ self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
return PatKind::Wild;
}
}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index 3e370a053..be66d0d47 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -291,9 +291,8 @@
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 crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
use rustc_data_structures::captures::Captures;
@@ -743,31 +742,6 @@ impl<'p, 'tcx> Witness<'p, 'tcx> {
}
}
-/// 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_hir_analysis/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>>,
-) {
- cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| {
- let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
- 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
- });
-}
-
/// 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:
@@ -845,7 +819,7 @@ fn is_useful<'p, 'tcx>(
// Opaque types can't get destructured/split, but the patterns can
// actually hint at hidden types, so we use the patterns' types instead.
- if let ty::Opaque(..) = ty.kind() {
+ if let ty::Alias(ty::Opaque, ..) = ty.kind() {
if let Some(row) = rows.first() {
ty = row.head().ty();
}
@@ -913,7 +887,19 @@ fn is_useful<'p, 'tcx>(
.collect::<Vec<_>>()
};
- lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns);
+ // 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_hir_analysis/src/check/pat.rs`.
+ cx.tcx.emit_spanned_lint(
+ NON_EXHAUSTIVE_OMITTED_PATTERNS,
+ hir_id,
+ pcx.span,
+ NonExhaustiveOmittedPattern {
+ scrut_ty: pcx.ty,
+ uncovered: Uncovered::new(pcx.span, pcx.cx, patterns),
+ },
+ );
}
ret.extend(usefulness);