@@ -54,6 +54,25 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
5454 }
5555 import debugging .patmatDebug
5656
57+ // to govern how much time we spend analyzing matches for unreachability/exhaustivity
58+ object AnalysisBudget {
59+ import scala .tools .cmd .FromString .IntFromString
60+ val max = sys.props.get(" scalac.patmat.analysisBudget" ).collect(IntFromString .orElse{case " off" => Integer .MAX_VALUE }).getOrElse(256 )
61+
62+ abstract class Exception extends RuntimeException (" CNF budget exceeded" ) {
63+ val advice : String
64+ def warn (pos : Position , kind : String ) = currentUnit.uncheckedWarning(pos, s " Cannot check match for $kind. \n $advice" )
65+ }
66+
67+ object exceeded extends Exception {
68+ val advice = s " (The analysis required more space than allowed. Please try with scalac -Dscalac.patmat.analysisBudget= ${AnalysisBudget .max* 2 } or -Dscalac.patmat.analysisBudget=off.) "
69+ }
70+
71+ object stackOverflow extends Exception {
72+ val advice = " (There was a stack overflow. Please try increasing the stack available to the compiler using e.g., -Xss2m.)"
73+ }
74+ }
75+
5776 def newTransformer (unit : CompilationUnit ): Transformer =
5877 if (opt.virtPatmat) new MatchTransformer (unit)
5978 else noopTransformer
@@ -1946,14 +1965,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
19461965 type Formula
19471966 def andFormula (a : Formula , b : Formula ): Formula
19481967
1949- class CNFBudgetExceeded extends RuntimeException (" CNF budget exceeded" )
19501968
1951- // may throw an CNFBudgetExceeded
1952- def propToSolvable (p : Prop ) = {
1969+ // may throw an AnalysisBudget.Exception
1970+ def propToSolvable (p : Prop ): Formula = {
19531971 val (eqAxioms, pure :: Nil ) = removeVarEq(List (p), modelNull = false )
19541972 eqFreePropToSolvable(And (eqAxioms, pure))
19551973 }
19561974
1975+ // may throw an AnalysisBudget.Exception
19571976 def eqFreePropToSolvable (p : Prop ): Formula
19581977 def cnfString (f : Formula ): String
19591978
@@ -1979,7 +1998,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
19791998 type Lit
19801999 def Lit (sym : Sym , pos : Boolean = true ): Lit
19812000
1982- // throws an CNFBudgetExceeded when the prop results in a CNF that's too big
2001+ // throws an AnalysisBudget.Exception when the prop results in a CNF that's too big
19832002 def eqFreePropToSolvable (p : Prop ): Formula = {
19842003 // TODO: for now, reusing the normalization from DPLL
19852004 def negationNormalForm (p : Prop ): Prop = p match {
@@ -2001,9 +2020,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
20012020 def lit (s : Sym ) = formula(clause(Lit (s)))
20022021 def negLit (s : Sym ) = formula(clause(Lit (s, false )))
20032022
2004- def conjunctiveNormalForm (p : Prop , budget : Int = 256 ): Formula = {
2023+ def conjunctiveNormalForm (p : Prop , budget : Int = AnalysisBudget .max ): Formula = {
20052024 def distribute (a : Formula , b : Formula , budget : Int ): Formula =
2006- if (budget <= 0 ) throw new CNFBudgetExceeded
2025+ if (budget <= 0 ) throw AnalysisBudget .exceeded
20072026 else
20082027 (a, b) match {
20092028 // true \/ _ = true
@@ -2018,7 +2037,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
20182037 big flatMap (c => distribute(formula(c), small, budget - (big.size* small.size)))
20192038 }
20202039
2021- if (budget <= 0 ) throw new CNFBudgetExceeded
2040+ if (budget <= 0 ) throw AnalysisBudget .exceeded
20222041
20232042 p match {
20242043 case True => TrueF
@@ -2037,9 +2056,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
20372056 }
20382057
20392058 val start = Statistics .startTimer(patmatCNF)
2040- val res = conjunctiveNormalForm(negationNormalForm(p))
2059+ val res =
2060+ try {
2061+ conjunctiveNormalForm(negationNormalForm(p))
2062+ } catch { case ex : StackOverflowError =>
2063+ throw AnalysisBudget .stackOverflow
2064+ }
2065+
20412066 Statistics .stopTimer(patmatCNF, start)
2042- patmatCNFSizes(res.size).value += 1
2067+
2068+ //
2069+ if (Statistics .enabled) patmatCNFSizes(res.size).value += 1
20432070
20442071// patmatDebug("cnf for\n"+ p +"\nis:\n"+cnfString(res))
20452072 res
@@ -2440,6 +2467,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
24402467 // right now hackily implement this by pruning counter-examples
24412468 // unreachability would also benefit from a more faithful representation
24422469
2470+
24432471 // reachability (dead code)
24442472
24452473 // computes the first 0-based case index that is unreachable (if any)
@@ -2508,9 +2536,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
25082536
25092537 if (reachable) None else Some (caseIndex)
25102538 } catch {
2511- case e : CNFBudgetExceeded =>
2512- // debugWarn(util.Position.formatMessage(prevBinder.pos, "Cannot check match for reachability", false))
2513- // e.printStackTrace()
2539+ case ex : AnalysisBudget .Exception =>
2540+ ex.warn(prevBinder.pos, " unreachability" )
25142541 None // CNF budget exceeded
25152542 }
25162543 }
@@ -2651,9 +2678,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
26512678 Statistics .stopTimer(patmatAnaExhaust, start)
26522679 pruned
26532680 } catch {
2654- case e : CNFBudgetExceeded =>
2655- patmatDebug(util.Position .formatMessage(prevBinder.pos, " Cannot check match for exhaustivity" , false ))
2656- // e.printStackTrace()
2681+ case ex : AnalysisBudget .Exception =>
2682+ ex.warn(prevBinder.pos, " exhaustivity" )
26572683 Nil // CNF budget exceeded
26582684 }
26592685 }
0 commit comments