| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fixes non-determinism within the DPLL algorithm and disallows
infeasible counter examples directly in the formula.
The function to compute all solutions was flawed and thus only
returned a subset of the solutions. The algorithm would stop too soon
and thus depending on the ordering of the symbols return more or less
solutions. I also added printing a warning when the search was stopped
because the max recursion depth was reached. This is very useful as an
explanation of spuriously failing regression tests, since less counter
examples might be reported. In such a case the recursion depth should
be set to infinite by adding `-Ypatmat-exhaust-depth off`.
The mapping of the solutions of the DPLL algorithm to counter examples
has been adapted to take the additional solutions from the
solver into account:
Consider for example `t8430.scala`:
```Scala
sealed trait CL3Literal
case object IntLit extends CL3Literal
case object CharLit extends CL3Literal
case object BooleanLit extends CL3Literal
case object UnitLit extends CL3Literal
sealed trait Tree
case class LetL(value: CL3Literal) extends Tree
case object LetP extends Tree
case object LetC extends Tree
case object LetF extends Tree
object Test {
(tree: Tree) => tree match {case LetL(CharLit) => ??? }
}
```
This test contains 2 domains, `IntLit, CharLit, ...` and `LetL, LetP, ...`,
the corresponding formula to check exhaustivity looks like:
```
V1=LetC.type#13 \/ V1=LetF.type#14 \/ V1=LetL#11 \/ V1=LetP.type#15 /\
V2=BooleanLit.type#16 \/ V2=CharLit#12 \/ V2=IntLit.type#17 \/ V2=UnitLit.type#18 /\
-V1=LetL#11 \/ -V2=CharLit#12 \/ \/
```
The first two lines assign a value of the domain to the scrutinee (and
the correponding member in case of `LetL`) and prohibit the counter
example `LetL(CharLit)` since it's covered by the pattern match. The
used Boolean encoding allows that scrutinee `V1` can be equal to
`LetC` and `LetF` at the same time and thus, during enumeration of all
possible solutions of the formula, such a solution will be found,
since only one literal needs to be set to true, to satisfy that
clause. That means, if at least one of the literals of such a clause
was in the `unassigned` list of the DPLL procedure, we will get
solutions where the scrutinee is equal to more than one element of the
domain.
A remedy would be to add constraints that forbid both literals
to be true at the same time. His is infeasible for big domains (see
`pos/t8531.scala`), since we would have to add a quadratic number of
clauses (one clause for each pair in the domain). A much simpler
solution is to just filter the invalid results. Since both values for
`unassigned` literals are explored, we will eventually find a valid
counter example.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
One last flurry with the broom before I leave you slobs to code
in your own filth. Eliminated all the trailing whitespace I
could manage, with special prejudice reserved for the test cases
which depended on the preservation of trailing whitespace.
Was reminded I cannot figure out how to eliminate the trailing
space on the "scala> " prompt in repl transcripts. At least
reduced the number of such empty prompts by trimming transcript
code on the way in.
Routed ConsoleReporter's "printMessage" through a trailing
whitespace stripping method which might help futureproof
against the future of whitespace diseases. Deleted the up-to-40
lines of trailing whitespace found in various library files.
It seems like only yesterday we performed whitespace surgery
on the whole repo. Clearly it doesn't stick very well. I suggest
it would work better to enforce a few requirements on the way in.
|
|
|
|
|
|
|
| |
Instead of changing warnings to errors mid-stream, at the end of
a run I check for condition "no errors, some warnings, and fatal
warnings" and then generate an error at that point. This is
necessary to test for some warnings which come from later stages.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Analyze matches for unreachable cases.
A case is unreachable if it implies its preceding cases.
Call `C` the formula that is satisfiable if the considered case matches.
Call `P` the formula that is satisfiable if the cases preceding it match.
The case is reachable if there is a model for `-P /\ C`.
Thus, the case is unreachable if there is no model for `-(-P /\ C)`,
or, equivalently, `P \/ -C`, or `C => P`.
Unreachability needs a more precise model for type and value tests than exhaustivity.
Before, `{case _: Int => case 1 =>}` would be modeled as `X = Int \/ X = 1.type`,
and thus, the second case would be reachable if we can satisfy `X != Int /\ X = 1.type`.
Of course, the case isn't reachable, yet the formula is satisfiable, so we must augment
our model to take into account that `X = 1.type => X = Int`.
This is done by `removeVarEq`, which models the following axioms about equality.
It does so to retain the meaning of equality after replacing `V = C` (variable = constant)
by a literal (fresh symbol). For each variable:
1. a sealed type test must result in exactly one of its partitions being chosen
(the core of exhaustivity)
2. when a type test is true, tests of super types must also be true,
and unrelated type tests must be false
For example, `V : X ::= A | B | C`, and `A => B` (since `A extends B`).
Coverage (1) is formulated as: `A \/ B \/ C`, and the implications of (2) are simply
`V=A => V=B /\ V=X`, `V=B => V=X`, `V=C => V=X`.
Exclusion for unrelated types typically results from matches such as `{case SomeConst =>
case OtherConst => }`. Here, `V=SomeConst.type => !V=OtherConst.type`. This is a
conservative approximation. If these constants happen to be the same value dynamically
(but the types don't tell us this), the last case is actually unreachable. Of course we
must err on the safe side.
We simplify the equality axioms as follows (in principle this could be done by the
solver, but it's easy to do before solving). If we've already excluded a pair of
assignments of constants to a certain variable at some point, say `(-A \/ -B)`, then
don't exclude the symmetric one `(-B \/ -A)`. (Nor the positive implications `-B \/ A`,
or `-A \/ B`, which would entail the equality axioms falsifying the whole formula.)
TODO: We should also model dependencies between variables: if `V1` corresponds to
`x: List[_]` and `V2` is `x.hd`, `V2` cannot be assigned at all when `V1 = null` or
`V1 = Nil`. Right now this is implemented hackily by pruning counter-examples in exhaustivity.
Unreachability would also benefit from a more faithful representation.
I had to refactor some of the framework, but most of it is shared with exhaustivity. We
must allow approximating tree makers twice, sharing variables, but using different
approximations for values not statically known. When considering reachability of a case,
we must assume, for example, that its unknown guard succeeds (otherwise it would wrongly
be considered unreachable), whereas unknown guards in the preceding cases must be
considered to fail (otherwise we could never get to those case, and again, it would
falsely be considered unreachable).
Since this analysis is relatively expensive, you may opt-out using `-Xno-patmat-analysis`
(or annotating the selector with @unchecked). We hope to improve the performance in the
near future. -Ystatistics has also been extended to provide some numbers on time spent in
the equality-rewrite, solving and analyzing.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We check exhaustivity by representing a match as a formula in
finite-domain propositional logic (FDPL) that is false when
the match may fail. The variables in the formula represent
tested trees in the match (type tests/value equality tests).
The approximation uses the same framework as the CSE analysis.
A matrix of tree makers is turned into a DAG, where sharing
represents the same value/type being tested.
We reduce FDPL to Boolean PL as follows.
For all assignments, V_i = c_i_j, we introduce a proposition
P_i_j that is true iff V_i is equal to the constant c_i_j,
for a given i, and all j, P_i_j are mutually exclusive
(a variable cannot have multiple values).
If the variable's domain is closed, we assert that one of P_i_j
must be true for each i and some j. The conjunction of these
propositions constitutes the equality axioms.
After going through negational normal form to conjunctive normal
form, we use a small SAT solver (the DPLL algorithm) to find
a model under which the equational axioms hold but the match fails.
The formula: EqAxioms /\ -MatchSucceeds.
Note that match failure expresses nicely in CNF: the
negation of each case (which yields a disjunction) is anded.
We then turn this model into variable assignments
(what's the variable (not) equal to, along with recursive
assignments for its fields). Valid assignments (no ill-typed
field assignments) are then presented to the user as counter examples.
A counter example is a value, a type test, a constructor call
or a wildcard. We prune the example set and only report the
most general examples. (Finally, we sort the output to yield stable,
i.e. testable, warning messages.)
A match is only checked for exhaustivity when the type of the
selector is "checkable" (has a sealed type or is a tuple
with at least one component of sealed type).
We consider statically known guard outcomes, but generally back
off (don't check exhaustivity) when a match has guards
or user-defined extractor calls. (Sometimes constant folding
lets us statically decide a guard.)
We ignore possibly failing null checks (which are performed
before calling extractors, for example), though this could
be done easily in the current framework. The problem is false
positives. People don't usually put nulls in tuples or lists.
To improve the exhaustivity checks, we rewrite `List()` to Nil.
TODO: more general rewrite of List(a, b, ..., z) to `a :: b :: ... :: z`.
When presenting counter examples, we represent lists in the
user-friendly List(a,...,z) format. (Similarly for tuples.)
There are no exhaustivity checks for a match-defined PartialFunction.
misc notes:
- fix pure case of dpll solver
impure set (symbol that occurs both as a positive and negative literal)
was always empty since I was looking for literals (which are not equal if positivity is not equal)
but should have been looking for symbols
- FDPL -> BoolPL translation collects all syms in props
since propForEqualsTo generates an Or, must traverse the prop
rather than assuming only top-level Syms are relevant...
also, propForEqualsTo will not assume Or'ing a whole domain is equivalent to True
(which it isn't, since the type test may fail in general)
- improve counter example description
- treat as constructor call when we either have definite type information about a real class,
or we have no equality information at all, but the variable's type is a class
and we gathered constraints about its fields (typically when selector is a tuple)
- flatten a :: b :: ... :: Nil to List(a, b, ...)
- don't print tuple constructor names, so instead of "Tuple2(a, b)", say "(a, b)"
- filter out more statically impossible subtypes
the static types convey more information than is actually checkable at run time
this is good, as it allows us to narrow down the subtypes of a sealed type,
however, when modeling the corresponding run-time checks we need to "erase" the
uncheckable parts (using existentials so that the types are still well-kinded),
so that we can use static subtyping as a sound model for dynamic type checks
- experimental java enum handling seals enum class
before, we created a refinement class as the placeholder for the sealed children
it seems more direct to use the enum class for that
this way, the new pattern matcher's exhaustiveness checker just works for java enums
|
|
|
|
|
|
|
| |
Finally figured out what was going on with a certain class of
exhaustiveness checking bugs. Hey moors, you can put away your pins,
puppets, and magic sauces. Closes #3098, no review.
|
|
|
|
|
|
|
|
| |
Completely to my surprise, found that fixing all those sequence issues
revealed that the pattern matcher can catch a lot more inexhaustive
cases than it has been catching. Fixed most of the inexhaustive matches
in the compiler, which had become a bit warnier. No review.
|
| |
|
|
|
|
|
|
| |
Reverts r23174, which I believe will bring the build back to life. It
only chokes under -optimise. No review.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
patches for #3887 and #3888, but I determined that I could achieve the
same effect by deleting a bunch of code, so I did. This left only a few
lines in TransMatch, so I eliminated it, which led me to remember that
many places still reference non-existent phase transmatch, so those were
updated. Notes:
* This swaps equality tests on stable identifier patterns. They
have never conformed to the spec (as noted long ago in ticket #785)
which says "The pattern matches any value v such that r == v" whereas
until now the test being performed was v == r.
* An issue was introduced with specialization in that the implementation
of "isTupleType" in Definitions relied upon sym == TupleClass(elems.length).
This test is untrue for specialized tuples, causing mysterious behavior
because only some tuples are specialized. There is now "isTupleTypeOrSubtype"
although it seems likely the former implementation is unnecessary.
The issue is sidestepped if one uses "getProductArgs" to retrieve the element
types because it sifts through the base types for the Product symbol.
Closes #3887 and #3888, review by dmharrah.
|
|
|
|
|
|
|
| |
Reorganizes children a little so they always come back sorted the same
way the pickler does. Taking advantage of -Yfatal-warnings in the test
case. Review by community.
|
|
|
|
|
|
|
| |
flags on AnyVal from FINAL|SEALED to ABSTRACT|SEALED. This appears
correct and without ill effect, but if anyone spots new anyval oddness
you know where to look.
|
|
|
|
|
|
|
|
| |
Starting on improving the abstraction level of the pattern matcher (code
from paul)
Still needs a fair bit of work.
|
| |
|
|
|
|
|
| |
optimizing irrefutable tuple matches and removed -Ymatch-algo
|
|
|
|
|
|
|
| |
removed "removeoption"
changed SUnit and some tests
added useful debug msg in typer
|
| |
|
| |
|
|
Iterator: gets mkString method
Iterable: only whitespace
Definitions: value classes no longer SEALED
test cases for exhaustivity + unapply/array
|