-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12053 from dotty-staging/fix-12049
Explain match type reduction failures in error messages
- Loading branch information
Showing
14 changed files
with
779 additions
and
22 deletions.
There are no files selected for viewing
103 changes: 103 additions & 0 deletions
103
compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package dotty.tools | ||
package dotc | ||
package core | ||
|
||
import Types._, Contexts._, Symbols._, Decorators._ | ||
import util.Property | ||
|
||
/** A utility module to produce match type reduction traces in error messages. | ||
*/ | ||
object MatchTypeTrace: | ||
|
||
private enum TraceEntry: | ||
case TryReduce(scrut: Type) | ||
case NoMatches(scrut: Type, cases: List[Type]) | ||
case Stuck(scrut: Type, stuckCase: Type, otherCases: List[Type]) | ||
import TraceEntry._ | ||
|
||
private class MatchTrace: | ||
var entries: List[TraceEntry] = Nil | ||
|
||
private val MatchTrace = new Property.Key[MatchTrace] | ||
|
||
/** Execute `op` and if it involves a failed match type reduction | ||
* return the trace of that reduction. Otherwise return the empty string. | ||
*/ | ||
def record(op: Context ?=> Any)(using Context): String = | ||
val trace = new MatchTrace | ||
inContext(ctx.fresh.setProperty(MatchTrace, trace)) { | ||
op | ||
if trace.entries.isEmpty then "" | ||
else | ||
i""" | ||
| | ||
|Note: a match type could not be fully reduced: | ||
| | ||
|${trace.entries.reverse.map(explainEntry)}%\n%""" | ||
} | ||
|
||
/** Are we running an operation that records a match type trace? */ | ||
def isRecording(using Context): Boolean = | ||
ctx.property(MatchTrace).isDefined | ||
|
||
private def matchTypeFail(entry: TraceEntry)(using Context) = | ||
ctx.property(MatchTrace) match | ||
case Some(trace) => | ||
trace.entries match | ||
case (e: TryReduce) :: es => trace.entries = entry :: trace.entries | ||
case _ => | ||
case _ => | ||
|
||
/** Record a failure that scrutinee `scrut` does not match any case in `cases`. | ||
* Only the first failure is recorded. | ||
*/ | ||
def noMatches(scrut: Type, cases: List[Type])(using Context) = | ||
matchTypeFail(NoMatches(scrut, cases)) | ||
|
||
/** Record a failure that scrutinee `scrut` does not match `stuckCase` but is | ||
* not disjoint from it either, which means that the remaining cases `otherCases` | ||
* cannot be visited. Only the first failure is recorded. | ||
*/ | ||
def stuck(scrut: Type, stuckCase: Type, otherCases: List[Type])(using Context) = | ||
matchTypeFail(Stuck(scrut, stuckCase, otherCases)) | ||
|
||
/** Record in the trace that we are trying to reduce `scrut` when performing `op` | ||
* If `op` succeeds the entry is removed after exit. If `op` fails, it stays. | ||
*/ | ||
def recurseWith(scrut: Type)(op: => Type)(using Context): Type = | ||
ctx.property(MatchTrace) match | ||
case Some(trace) => | ||
val prev = trace.entries | ||
trace.entries = TryReduce(scrut) :: prev | ||
val res = op | ||
if res.exists then trace.entries = prev | ||
res | ||
case _ => | ||
op | ||
|
||
private def caseText(tp: Type)(using Context): String = tp match | ||
case tp: HKTypeLambda => caseText(tp.resultType) | ||
case defn.MatchCase(pat, body) => i"case $pat => $body" | ||
case _ => i"case $tp" | ||
|
||
private def casesText(cases: List[Type])(using Context) = | ||
i"${cases.map(caseText)}%\n %" | ||
|
||
private def explainEntry(entry: TraceEntry)(using Context): String = entry match | ||
case TryReduce(scrut: Type) => | ||
i" trying to reduce $scrut" | ||
case NoMatches(scrut, cases) => | ||
i""" failed since selector $scrut | ||
| matches none of the cases | ||
| | ||
| ${casesText(cases)}""" | ||
case Stuck(scrut, stuckCase, otherCases) => | ||
i""" failed since selector $scrut | ||
| does not match ${caseText(stuckCase)} | ||
| and cannot be shown to be disjoint from it either. | ||
| Therefore, reduction cannot advance to the remaining case${if otherCases.length == 1 then "" else "s"} | ||
| | ||
| ${casesText(otherCases)}""" | ||
|
||
end MatchTypeTrace | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.