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 20d3b3f98ce31..d1ede5772e552 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -405,7 +405,10 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { scrut_ty: Ty<'tcx>, ) -> Result, ErrorGuaranteed> { let pattern_complexity_limit = - get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity); + get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity) + // Default value to emit the warning for "too complex" match. We picked it to warn + // after a second or two. + .or(Some(10_000_000)); let report = rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty, pattern_complexity_limit) .map_err(|err| { diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl index 41a1d958f1094..663f00f136ac4 100644 --- a/compiler/rustc_pattern_analysis/messages.ftl +++ b/compiler/rustc_pattern_analysis/messages.ftl @@ -19,6 +19,10 @@ pattern_analysis_overlapping_range_endpoints = multiple patterns overlap on thei .label = ... with this range .note = you likely meant to write mutually exclusive ranges +pattern_analysis_too_complex = this pattern-match expression is taking a long time to analyze + .help = to speed up checking, break it up into smaller `match`es and/or replace some patterns with guards + .note = match checking can take exponential time when many different fields of structs/tuples/arrays are involved + pattern_analysis_uncovered = {$count -> [1] pattern `{$witness_1}` [2] patterns `{$witness_1}` and `{$witness_2}` diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 75b7b7c8f6784..385266c5f807c 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -1,5 +1,5 @@ use rustc_errors::{Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic}; -use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::thir::Pat; use rustc_middle::ty::Ty; use rustc_span::Span; @@ -148,3 +148,12 @@ pub(crate) struct NonExhaustiveOmittedPatternLintOnArm { pub lint_level: &'static str, pub lint_name: &'static str, } + +#[derive(Diagnostic)] +#[diag(pattern_analysis_too_complex)] +#[help] +#[note] +pub(crate) struct TooComplex { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index fd51fbedeef9d..df99e6568bf79 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -913,7 +913,8 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { fn complexity_exceeded(&self) -> Result<(), Self::Error> { let span = self.whole_match_span.unwrap_or(self.scrut_span); - Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit")) + self.tcx.dcx().emit_warn(errors::TooComplex { span }); + Ok(()) } fn lint_non_contiguous_range_endpoints( diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 3760db8b68894..a1d3d4b8d7439 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -745,6 +745,8 @@ impl<'a, Cx: PatCx> UsefulnessCtxt<'a, Cx> { .complexity_limit .is_some_and(|complexity_limit| complexity_limit < self.complexity_level) { + // We change it to `None` to prevent it from being called more than once. + self.complexity_limit = None; return self.tycx.complexity_exceeded(); } Ok(()) diff --git a/tests/ui/pattern/complexity_limit.rs b/tests/ui/pattern/complexity_limit.rs index c9a3f99bccd14..c367141153f64 100644 --- a/tests/ui/pattern/complexity_limit.rs +++ b/tests/ui/pattern/complexity_limit.rs @@ -1,5 +1,9 @@ +//@ check-pass + #![feature(rustc_attrs)] -#![pattern_complexity = "10000"] +// By default the complexity is 10_000_000, but we reduce it so the test takes +// (much) less time. +#![pattern_complexity = "2000000"] #[derive(Default)] struct BaseCommand { @@ -21,22 +25,10 @@ struct BaseCommand { field16: bool, field17: bool, field18: bool, - field19: bool, - field20: bool, - field21: bool, - field22: bool, - field23: bool, - field24: bool, - field25: bool, - field26: bool, - field27: bool, - field28: bool, - field29: bool, - field30: bool, } fn request_key(command: BaseCommand) { - match command { //~ ERROR: reached pattern complexity limit + match command { //~ WARN: this pattern-match expression is taking a long time to analyze BaseCommand { field01: true, .. } => {} BaseCommand { field02: true, .. } => {} BaseCommand { field03: true, .. } => {} @@ -55,18 +47,6 @@ fn request_key(command: BaseCommand) { BaseCommand { field16: true, .. } => {} BaseCommand { field17: true, .. } => {} BaseCommand { field18: true, .. } => {} - BaseCommand { field19: true, .. } => {} - BaseCommand { field20: true, .. } => {} - BaseCommand { field21: true, .. } => {} - BaseCommand { field22: true, .. } => {} - BaseCommand { field23: true, .. } => {} - BaseCommand { field24: true, .. } => {} - BaseCommand { field25: true, .. } => {} - BaseCommand { field26: true, .. } => {} - BaseCommand { field27: true, .. } => {} - BaseCommand { field28: true, .. } => {} - BaseCommand { field29: true, .. } => {} - BaseCommand { field30: true, .. } => {} BaseCommand { field01: false, .. } => {} BaseCommand { field02: false, .. } => {} @@ -86,18 +66,6 @@ fn request_key(command: BaseCommand) { BaseCommand { field16: false, .. } => {} BaseCommand { field17: false, .. } => {} BaseCommand { field18: false, .. } => {} - BaseCommand { field19: false, .. } => {} - BaseCommand { field20: false, .. } => {} - BaseCommand { field21: false, .. } => {} - BaseCommand { field22: false, .. } => {} - BaseCommand { field23: false, .. } => {} - BaseCommand { field24: false, .. } => {} - BaseCommand { field25: false, .. } => {} - BaseCommand { field26: false, .. } => {} - BaseCommand { field27: false, .. } => {} - BaseCommand { field28: false, .. } => {} - BaseCommand { field29: false, .. } => {} - BaseCommand { field30: false, .. } => {} } } diff --git a/tests/ui/pattern/complexity_limit.stderr b/tests/ui/pattern/complexity_limit.stderr index 08d9d40fe4626..9ad198ce05542 100644 --- a/tests/ui/pattern/complexity_limit.stderr +++ b/tests/ui/pattern/complexity_limit.stderr @@ -1,14 +1,17 @@ -error: reached pattern complexity limit - --> $DIR/complexity_limit.rs:39:5 +warning: this pattern-match expression is taking a long time to analyze + --> $DIR/complexity_limit.rs:31:5 | LL | / match command { LL | | BaseCommand { field01: true, .. } => {} LL | | BaseCommand { field02: true, .. } => {} LL | | BaseCommand { field03: true, .. } => {} ... | -LL | | BaseCommand { field30: false, .. } => {} +LL | | BaseCommand { field18: false, .. } => {} LL | | } | |_____^ + | + = help: to speed up checking, break it up into smaller `match`es and/or replace some patterns with guards + = note: match checking can take exponential time when many different fields of structs/tuples/arrays are involved -error: aborting due to 1 previous error +warning: 1 warning emitted