Pre-SIP: Divergence Checking for Match Type Reduction
Issue: #22587
Implementation PR: #24661
Full report & proof: Lluc24/divergence
Situation
Match types can diverge during reduction — either cycling through a closed set of types or growing complexity without bound. The compiler currently lets reduction run until it hits a StackOverflowError, which it then catches to report a “recursion limit exceeded” error.
Catching StackOverflowError is fundamentally unsafe. The JVM specification does not guarantee correct cleanup after a stack overflow: documented OpenJDK bugs confirm that finally blocks can be bypassed (JDK-8177802), locks can remain held after recovery (JDK-8318888), and behavior is platform- and -Xss-dependent (JDK-8217475).
Proposed solution
Replace the runtime stack-overflow heuristic with a compile-time divergence check that proves termination before reduction proceeds.
The algorithm adapts the divergence check from SIP-31 (Byname Implicit Parameters) — which already handles this for implicit resolution — and extends it from single types to full argument lists. For each match type constructor the compiler tracks the sequence of argument lists seen during reduction and rejects a new argument list when it is dominated by a previously seen one.
Domination is defined as follows. Given an argument list T:
- Complexity vector
c(T): the size of a type measured using the structural AST of each argument. - Covering set
cs(T): the union of all type symbols mentioned in the arguments.
T is dominated by a history entry U when either:
TandUare type-equivalent (cycle detection), orcs(T) = cs(U)andc(T)is component-wise strictly larger thanc(U)(growth detection).
When domination is detected the compiler throws a MatchTypeDivergence exception — caught at the same site as RecursionOverflow — and emits a clear error with the full reduction trace, complexity vectors, and covering sets.
Termination proof
The algorithm is proven to terminate by three lemmas:
- Finite covering sets — only finitely many covering sets can arise.
- Non-decreasing subsequence — every infinite sequence in ℕⁿ contains a non-decreasing subsequence (generalized Dickson’s Lemma).
- Finite constructible types — a fixed covering set and fixed complexity vector admit only finitely many distinct types (Pigeonhole Principle).
By contradiction: any infinite reduction sequence would either trigger growth detection (Lemmas 1 + 2) or cycle detection (Lemma 3).
The full formal proof is available in the project report.
Implementation status
The implementation is complete and ready to be merged. All existing Scala 3 compiler tests pass without modification — the check is conservative enough to accept every match type that compiled before.
This project was directed by Martin Odersky and supervised by Matthieu Bovel. It was presented in person to the LAMP research group, who endorsed it for the SIP track.