about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs4
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_log/src/lib.rs19
-rw-r--r--compiler/rustc_middle/src/middle/limits.rs11
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs8
-rw-r--r--compiler/rustc_pattern_analysis/src/lib.rs7
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs5
-rw-r--r--compiler/rustc_pattern_analysis/src/usefulness.rs24
-rw-r--r--compiler/rustc_span/src/symbol.rs1
9 files changed, 74 insertions, 7 deletions
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 327c70ede28..10867ae230a 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -929,6 +929,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         omit_gdb_pretty_printer_section, Normal, template!(Word), WarnFollowing,
         "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite",
     ),
+    rustc_attr!(
+        TEST, pattern_complexity, CrateLevel, template!(NameValueStr: "N"),
+        ErrorFollowing, @only_local: true,
+    ),
 ];
 
 pub fn deprecated_attributes() -> Vec<&'static BuiltinAttribute> {
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 53254d567cc..17c4d81474e 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -213,6 +213,8 @@ declare_features! (
     (internal, negative_bounds, "1.71.0", None),
     /// Allows using `#[omit_gdb_pretty_printer_section]`.
     (internal, omit_gdb_pretty_printer_section, "1.5.0", None),
+    /// Set the maximum pattern complexity allowed (not limited by default).
+    (internal, pattern_complexity, "CURRENT_RUSTC_VERSION", None),
     /// Allows using `#[prelude_import]` on glob `use` items.
     (internal, prelude_import, "1.2.0", None),
     /// Used to identify crates that contain the profiler runtime.
diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs
index 1a78f9f0f86..e4b67cde244 100644
--- a/compiler/rustc_log/src/lib.rs
+++ b/compiler/rustc_log/src/lib.rs
@@ -57,6 +57,7 @@ pub struct LoggerConfig {
     pub verbose_entry_exit: Result<String, VarError>,
     pub verbose_thread_ids: Result<String, VarError>,
     pub backtrace: Result<String, VarError>,
+    pub wraptree: Result<String, VarError>,
 }
 
 impl LoggerConfig {
@@ -67,6 +68,7 @@ impl LoggerConfig {
             verbose_entry_exit: env::var(format!("{env}_ENTRY_EXIT")),
             verbose_thread_ids: env::var(format!("{env}_THREAD_IDS")),
             backtrace: env::var(format!("{env}_BACKTRACE")),
+            wraptree: env::var(format!("{env}_WRAPTREE")),
         }
     }
 }
@@ -99,7 +101,7 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
         Err(_) => false,
     };
 
-    let layer = tracing_tree::HierarchicalLayer::default()
+    let mut layer = tracing_tree::HierarchicalLayer::default()
         .with_writer(io::stderr)
         .with_indent_lines(true)
         .with_ansi(color_logs)
@@ -110,6 +112,16 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
         .with_thread_ids(verbose_thread_ids)
         .with_thread_names(verbose_thread_ids);
 
+    match cfg.wraptree {
+        Ok(v) => match v.parse::<usize>() {
+            Ok(v) => {
+                layer = layer.with_wraparound(v);
+            }
+            Err(_) => return Err(Error::InvalidWraptree(v)),
+        },
+        Err(_) => {} // no wraptree
+    }
+
     let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
     match cfg.backtrace {
         Ok(str) => {
@@ -164,6 +176,7 @@ pub fn stderr_isatty() -> bool {
 pub enum Error {
     InvalidColorValue(String),
     NonUnicodeColorValue,
+    InvalidWraptree(String),
 }
 
 impl std::error::Error for Error {}
@@ -179,6 +192,10 @@ impl Display for Error {
                 formatter,
                 "non-Unicode log color value: expected one of always, never, or auto",
             ),
+            Error::InvalidWraptree(value) => write!(
+                formatter,
+                "invalid log WRAPTREE value '{value}': expected a non-negative integer",
+            ),
         }
     }
 }
diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs
index 344ea38192e..4d698012749 100644
--- a/compiler/rustc_middle/src/middle/limits.rs
+++ b/compiler/rustc_middle/src/middle/limits.rs
@@ -40,6 +40,13 @@ pub fn get_recursion_limit(krate_attrs: &[Attribute], sess: &Session) -> Limit {
 }
 
 fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: usize) -> Limit {
+    match get_limit_size(krate_attrs, sess, name) {
+        Some(size) => Limit::new(size),
+        None => Limit::new(default),
+    }
+}
+
+pub fn get_limit_size(krate_attrs: &[Attribute], sess: &Session, name: Symbol) -> Option<usize> {
     for attr in krate_attrs {
         if !attr.has_name(name) {
             continue;
@@ -47,7 +54,7 @@ fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: u
 
         if let Some(s) = attr.value_str() {
             match s.as_str().parse() {
-                Ok(n) => return Limit::new(n),
+                Ok(n) => return Some(n),
                 Err(e) => {
                     let value_span = attr
                         .meta()
@@ -69,5 +76,5 @@ fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: u
             }
         }
     }
-    return Limit::new(default);
+    None
 }
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 cf12b4fb912..2685bae4d09 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -17,6 +17,7 @@ use rustc_hir as hir;
 use rustc_hir::def::*;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::HirId;
+use rustc_middle::middle::limits::get_limit_size;
 use rustc_middle::thir::visit::Visitor;
 use rustc_middle::thir::*;
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -26,7 +27,7 @@ use rustc_session::lint::builtin::{
 };
 use rustc_session::Session;
 use rustc_span::hygiene::DesugaringKind;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
 
 pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
     let typeck_results = tcx.typeck(def_id);
@@ -403,8 +404,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
         arms: &[MatchArm<'p, 'tcx>],
         scrut_ty: Ty<'tcx>,
     ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
+        let pattern_complexity_limit =
+            get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity);
         let report =
-            rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
+            rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty, pattern_complexity_limit)
+                .map_err(|err| {
                 self.error = Err(err);
                 err
             })?;
diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs
index d4b38d260e7..4b0955699fc 100644
--- a/compiler/rustc_pattern_analysis/src/lib.rs
+++ b/compiler/rustc_pattern_analysis/src/lib.rs
@@ -142,6 +142,9 @@ pub trait TypeCx: Sized + fmt::Debug {
         _overlaps_with: &[&DeconstructedPat<Self>],
     ) {
     }
+
+    /// The maximum pattern complexity limit was reached.
+    fn complexity_exceeded(&self) -> Result<(), Self::Error>;
 }
 
 /// The arm of a match expression.
@@ -167,10 +170,12 @@ pub fn analyze_match<'p, 'tcx>(
     tycx: &RustcMatchCheckCtxt<'p, 'tcx>,
     arms: &[rustc::MatchArm<'p, 'tcx>],
     scrut_ty: Ty<'tcx>,
+    pattern_complexity_limit: Option<usize>,
 ) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
     let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
     let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
-    let report = compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity)?;
+    let report =
+        compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity, pattern_complexity_limit)?;
 
     // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
     // `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index f8839b0b590..5f5bfa7154a 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -895,6 +895,11 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
             errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
         );
     }
+
+    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"))
+    }
 }
 
 /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index bbe02f94c0a..0261768d916 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -734,6 +734,21 @@ struct UsefulnessCtxt<'a, Cx: TypeCx> {
     /// Collect the patterns found useful during usefulness checking. This is used to lint
     /// unreachable (sub)patterns.
     useful_subpatterns: FxHashSet<PatId>,
+    complexity_limit: Option<usize>,
+    complexity_level: usize,
+}
+
+impl<'a, Cx: TypeCx> UsefulnessCtxt<'a, Cx> {
+    fn increase_complexity_level(&mut self, complexity_add: usize) -> Result<(), Cx::Error> {
+        self.complexity_level += complexity_add;
+        if self
+            .complexity_limit
+            .is_some_and(|complexity_limit| complexity_limit < self.complexity_level)
+        {
+            return self.tycx.complexity_exceeded();
+        }
+        Ok(())
+    }
 }
 
 /// Context that provides information local to a place under investigation.
@@ -1552,6 +1567,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
     }
 
     let Some(place) = matrix.head_place() else {
+        mcx.increase_complexity_level(matrix.rows().len())?;
         // The base case: there are no columns in the matrix. We are morally pattern-matching on ().
         // A row is useful iff it has no (unguarded) rows above it.
         let mut useful = true; // Whether the next row is useful.
@@ -1690,8 +1706,14 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
     arms: &[MatchArm<'p, Cx>],
     scrut_ty: Cx::Ty,
     scrut_validity: ValidityConstraint,
+    complexity_limit: Option<usize>,
 ) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
-    let mut cx = UsefulnessCtxt { tycx, useful_subpatterns: FxHashSet::default() };
+    let mut cx = UsefulnessCtxt {
+        tycx,
+        useful_subpatterns: FxHashSet::default(),
+        complexity_limit,
+        complexity_level: 0,
+    };
     let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity);
     let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(&mut cx, &mut matrix)?;
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index ee8d9ae9c53..7e31cfc0662 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1303,6 +1303,7 @@ symbols! {
         pat,
         pat_param,
         path,
+        pattern_complexity,
         pattern_parentheses,
         phantom_data,
         pic,