Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit a2f73d3

Browse files
committed
Auto merge of rust-lang#16879 - Nadrieril:fuel, r=Veykril
Add fuel to match checking Exhaustiveness checking is NP-hard hence can take extremely long to check some specific matches. This PR makes ehxaustiveness bail after a set number of steps. I chose a bound that takes ~100ms on my machine, which should be more than enough for normal matches. I'd like someone with less recent hardware to run the test to see if that limit is low enough for them. Also curious if the r-a team thinks this is a good ballpark or if we should go lower/higher. I don't have much data on how complex real-life matches get, but we can definitely go lower than `500 000` steps. The second commit is a drive-by soundness fix which doesn't matter much today but will matter once `min_exhaustive_patterns` is stabilized. Fixes rust-lang/rust-analyzer#9528 cc `@matklad`
2 parents e03df77 + 08a5f1e commit a2f73d3

File tree

3 files changed

+42
-16
lines changed

3 files changed

+42
-16
lines changed

crates/hir-ty/src/diagnostics/expr.rs

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use hir_def::{ItemContainerId, Lookup};
1111
use hir_expand::name;
1212
use itertools::Itertools;
1313
use rustc_hash::FxHashSet;
14-
use rustc_pattern_analysis::usefulness::{compute_match_usefulness, PlaceValidity};
1514
use syntax::{ast, AstNode};
1615
use tracing::debug;
1716
use triomphe::Arc;
@@ -234,13 +233,7 @@ impl ExprValidator {
234233
return;
235234
}
236235

237-
let report = match compute_match_usefulness(
238-
&cx,
239-
m_arms.as_slice(),
240-
scrut_ty.clone(),
241-
PlaceValidity::ValidOnly,
242-
None,
243-
) {
236+
let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) {
244237
Ok(report) => report,
245238
Err(()) => return,
246239
};
@@ -282,13 +275,7 @@ impl ExprValidator {
282275
continue;
283276
}
284277

285-
let report = match compute_match_usefulness(
286-
&cx,
287-
&[match_arm],
288-
ty.clone(),
289-
PlaceValidity::ValidOnly,
290-
None,
291-
) {
278+
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
292279
Ok(v) => v,
293280
Err(e) => {
294281
debug!(?e, "match usefulness error");

crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_hash::FxHashMap;
88
use rustc_pattern_analysis::{
99
constructor::{Constructor, ConstructorSet, VariantVisibility},
1010
index::IdxContainer,
11+
usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport},
1112
Captures, PatCx, PrivateUninhabitedField,
1213
};
1314
use smallvec::{smallvec, SmallVec};
@@ -59,6 +60,18 @@ impl<'p> MatchCheckCtx<'p> {
5960
Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns }
6061
}
6162

63+
pub(crate) fn compute_match_usefulness(
64+
&self,
65+
arms: &[MatchArm<'p>],
66+
scrut_ty: Ty,
67+
) -> Result<UsefulnessReport<'p, Self>, ()> {
68+
// FIXME: Determine place validity correctly. For now, err on the safe side.
69+
let place_validity = PlaceValidity::MaybeInvalid;
70+
// Measured to take ~100ms on modern hardware.
71+
let complexity_limit = Some(500000);
72+
compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
73+
}
74+
6275
fn is_uninhabited(&self, ty: &Ty) -> bool {
6376
is_ty_uninhabited_from(ty, self.module, self.db)
6477
}
@@ -465,7 +478,6 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
465478
}
466479

467480
fn complexity_exceeded(&self) -> Result<(), Self::Error> {
468-
// FIXME(Nadrieril): make use of the complexity counter.
469481
Err(())
470482
}
471483
}

crates/ide-diagnostics/src/handlers/missing_match_arms.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod tests {
2323
},
2424
DiagnosticsConfig,
2525
};
26+
use test_utils::skip_slow_tests;
2627

2728
#[track_caller]
2829
fn check_diagnostics_no_bails(ra_fixture: &str) {
@@ -1004,6 +1005,32 @@ fn f() {
10041005
);
10051006
}
10061007

1008+
#[test]
1009+
fn exponential_match() {
1010+
if skip_slow_tests() {
1011+
return;
1012+
}
1013+
// Constructs a match where match checking takes exponential time. Ensures we bail early.
1014+
use std::fmt::Write;
1015+
let struct_arity = 50;
1016+
let mut code = String::new();
1017+
write!(code, "struct BigStruct {{").unwrap();
1018+
for i in 0..struct_arity {
1019+
write!(code, " field{i}: bool,").unwrap();
1020+
}
1021+
write!(code, "}}").unwrap();
1022+
write!(code, "fn big_match(s: BigStruct) {{").unwrap();
1023+
write!(code, " match s {{").unwrap();
1024+
for i in 0..struct_arity {
1025+
write!(code, " BigStruct {{ field{i}: true, ..}} => {{}},").unwrap();
1026+
write!(code, " BigStruct {{ field{i}: false, ..}} => {{}},").unwrap();
1027+
}
1028+
write!(code, " _ => {{}},").unwrap();
1029+
write!(code, " }}").unwrap();
1030+
write!(code, "}}").unwrap();
1031+
check_diagnostics_no_bails(&code);
1032+
}
1033+
10071034
mod rust_unstable {
10081035
use super::*;
10091036

0 commit comments

Comments
 (0)