110 lines
3.4 KiB
Rust
110 lines
3.4 KiB
Rust
|
//! Test the pattern complexity limit.
|
||
|
use common::*;
|
||
|
use rustc_pattern_analysis::{pat::DeconstructedPat, usefulness::PlaceValidity, MatchArm};
|
||
|
|
||
|
#[macro_use]
|
||
|
mod common;
|
||
|
|
||
|
/// Analyze a match made of these patterns. Ignore the report; we only care whether we exceeded the
|
||
|
/// limit or not.
|
||
|
fn check(patterns: &[DeconstructedPat<Cx>], complexity_limit: usize) -> Result<(), ()> {
|
||
|
let ty = *patterns[0].ty();
|
||
|
let arms: Vec<_> =
|
||
|
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
|
||
|
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, Some(complexity_limit))
|
||
|
.map(|_report| ())
|
||
|
}
|
||
|
|
||
|
/// Asserts that analyzing this match takes exactly `complexity` steps.
|
||
|
#[track_caller]
|
||
|
fn assert_complexity(patterns: Vec<DeconstructedPat<Cx>>, complexity: usize) {
|
||
|
assert!(check(&patterns, complexity).is_ok());
|
||
|
assert!(check(&patterns, complexity - 1).is_err());
|
||
|
}
|
||
|
|
||
|
/// Construct a match like:
|
||
|
/// ```ignore(illustrative)
|
||
|
/// match ... {
|
||
|
/// BigStruct { field01: true, .. } => {}
|
||
|
/// BigStruct { field02: true, .. } => {}
|
||
|
/// BigStruct { field03: true, .. } => {}
|
||
|
/// BigStruct { field04: true, .. } => {}
|
||
|
/// ...
|
||
|
/// _ => {}
|
||
|
/// }
|
||
|
/// ```
|
||
|
fn diagonal_match(arity: usize) -> Vec<DeconstructedPat<Cx>> {
|
||
|
let struct_ty = Ty::BigStruct { arity, ty: &Ty::Bool };
|
||
|
let mut patterns = vec![];
|
||
|
for i in 0..arity {
|
||
|
patterns.push(pat!(struct_ty; Struct { .i: true }));
|
||
|
}
|
||
|
patterns.push(pat!(struct_ty; _));
|
||
|
patterns
|
||
|
}
|
||
|
|
||
|
/// Construct a match like:
|
||
|
/// ```ignore(illustrative)
|
||
|
/// match ... {
|
||
|
/// BigStruct { field01: true, .. } => {}
|
||
|
/// BigStruct { field02: true, .. } => {}
|
||
|
/// BigStruct { field03: true, .. } => {}
|
||
|
/// BigStruct { field04: true, .. } => {}
|
||
|
/// ...
|
||
|
/// BigStruct { field01: false, .. } => {}
|
||
|
/// BigStruct { field02: false, .. } => {}
|
||
|
/// BigStruct { field03: false, .. } => {}
|
||
|
/// BigStruct { field04: false, .. } => {}
|
||
|
/// ...
|
||
|
/// _ => {}
|
||
|
/// }
|
||
|
/// ```
|
||
|
fn diagonal_exponential_match(arity: usize) -> Vec<DeconstructedPat<Cx>> {
|
||
|
let struct_ty = Ty::BigStruct { arity, ty: &Ty::Bool };
|
||
|
let mut patterns = vec![];
|
||
|
for i in 0..arity {
|
||
|
patterns.push(pat!(struct_ty; Struct { .i: true }));
|
||
|
}
|
||
|
for i in 0..arity {
|
||
|
patterns.push(pat!(struct_ty; Struct { .i: false }));
|
||
|
}
|
||
|
patterns.push(pat!(struct_ty; _));
|
||
|
patterns
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn test_diagonal_struct_match() {
|
||
|
// These cases are nicely linear: we check `arity` patterns with exactly one `true`, matching
|
||
|
// in 2 branches each, and a final pattern with all `false`, matching only the `_` branch.
|
||
|
assert_complexity(diagonal_match(20), 41);
|
||
|
assert_complexity(diagonal_match(30), 61);
|
||
|
// This case goes exponential.
|
||
|
assert!(check(&diagonal_exponential_match(10), 10000).is_err());
|
||
|
}
|
||
|
|
||
|
/// Construct a match like:
|
||
|
/// ```ignore(illustrative)
|
||
|
/// match ... {
|
||
|
/// BigEnum::Variant1(_) => {}
|
||
|
/// BigEnum::Variant2(_) => {}
|
||
|
/// BigEnum::Variant3(_) => {}
|
||
|
/// ...
|
||
|
/// _ => {}
|
||
|
/// }
|
||
|
/// ```
|
||
|
fn big_enum(arity: usize) -> Vec<DeconstructedPat<Cx>> {
|
||
|
let enum_ty = Ty::BigEnum { arity, ty: &Ty::Bool };
|
||
|
let mut patterns = vec![];
|
||
|
for i in 0..arity {
|
||
|
patterns.push(pat!(enum_ty; Variant.i));
|
||
|
}
|
||
|
patterns.push(pat!(enum_ty; _));
|
||
|
patterns
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn test_big_enum() {
|
||
|
// We try 2 branches per variant.
|
||
|
assert_complexity(big_enum(20), 40);
|
||
|
}
|