Skip to content

Commit d1fd58f

Browse files
author
Adriaan Moors
committed
Merge pull request scala#953 from adriaanm/ticket-6031
SI-6031 customizable budget for patmat analyses
2 parents a3d122a + 93519ab commit d1fd58f

File tree

1 file changed

+41
-15
lines changed

1 file changed

+41
-15
lines changed

src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)