From 5aaea2f6e847be0881f2116cc206e2057d64b524 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 17 Aug 2024 12:01:47 +0100 Subject: [PATCH] Print failures in boundsViolations, via TypeComparer.explaining TypeComparer.explaining is like TypeComparer.explained, but instead of just returning the trace, returns the result, still allowing the trace to be accessed via .lastTrace, as exemplified by implementing TypeComparer.explained in terms of TypeComparer.explaining. Also add, but leave commented out the call of, a trace.dumpStack, which is like Thread.dumpStack(), but outputing to System.out, like all our tracing does - so the two don't interact when unbuffering onto the terminal. Also, we can do customisations like filtering out stack elements, limiting the stack. --- .../dotty/tools/dotc/core/TypeComparer.scala | 12 ++++++++---- .../src/dotty/tools/dotc/core/TypeOps.scala | 19 +++++++++++++++---- .../dotty/tools/dotc/reporting/trace.scala | 12 ++++++++++++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 0f74ca40843b..5aab2e277693 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3268,9 +3268,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling /** The trace of comparison operations when performing `op` */ def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:", short: Boolean)(using Context): String = - val cmp = explainingTypeComparer(short) - inSubComparer(cmp)(op) - cmp.lastTrace(header) + explaining(cmp => { op(cmp); cmp.lastTrace(header) }, short) + + def explaining[T](op: ExplainingTypeComparer => T, short: Boolean)(using Context): T = + inSubComparer(explainingTypeComparer(short))(op) def reduceMatchWith[T](op: MatchReducer => T)(using Context): T = inSubComparer(matchReducer)(op) @@ -3440,6 +3441,9 @@ object TypeComparer { def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:", short: Boolean = false)(using Context): String = comparing(_.explained(op, header, short)) + def explaining[T](op: ExplainingTypeComparer => T, short: Boolean = false)(using Context): T = + comparing(_.explaining(op, short)) + def reduceMatchWith[T](op: MatchReducer => T)(using Context): T = comparing(_.reduceMatchWith(op)) @@ -3873,7 +3877,7 @@ class ExplainingTypeComparer(initctx: Context, short: Boolean) extends TypeCompa override def recur(tp1: Type, tp2: Type): Boolean = def moreInfo = if Config.verboseExplainSubtype || ctx.settings.verbose.value - then s" ${tp1.getClass} ${tp2.getClass}" + then s" ${tp1.className} ${tp2.className}" else "" val approx = approxState def approxStr = if short then "" else approx.show diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 8089735bdb0f..0d8801b646ee 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -691,11 +691,22 @@ object TypeOps: val hiBound = instantiate(bounds.hi, skolemizedArgTypes) val loBound = instantiate(bounds.lo, skolemizedArgTypes) - def check(using Context) = { - if (!(lo <:< hiBound)) violations += ((arg, "upper", hiBound)) - if (!(loBound <:< hi)) violations += ((arg, "lower", loBound)) + def check(tp1: Type, tp2: Type, which: String, bound: Type)(using Context) = { + val isSub = TypeComparer.explaining { cmp => + val isSub = cmp.isSubType(tp1, tp2) + if !isSub then + if !ctx.typerState.constraint.domainLambdas.isEmpty then + typr.println(i"${ctx.typerState.constraint}") + if !ctx.gadt.symbols.isEmpty then + typr.println(i"${ctx.gadt}") + typr.println(cmp.lastTrace(i"checkOverlapsBounds($lo, $hi, $arg, $bounds)($which)")) + //trace.dumpStack() + isSub + }//(using ctx.fresh.setSetting(ctx.settings.verbose, true)) // uncomment to enable moreInfo in ExplainingTypeComparer recur + if !isSub then violations += ((arg, which, bound)) } - check(using checkCtx) + check(lo, hiBound, "upper", hiBound)(using checkCtx) + check(loBound, hi, "lower", loBound)(using checkCtx) } def loop(args: List[Tree], boundss: List[TypeBounds]): Unit = args match diff --git a/compiler/src/dotty/tools/dotc/reporting/trace.scala b/compiler/src/dotty/tools/dotc/reporting/trace.scala index fbbc3d990969..732e779e9bf7 100644 --- a/compiler/src/dotty/tools/dotc/reporting/trace.scala +++ b/compiler/src/dotty/tools/dotc/reporting/trace.scala @@ -27,6 +27,18 @@ object trace extends TraceSyntax: object log extends TraceSyntax: inline def isEnabled: true = true protected val isForced = false + + def dumpStack(limit: Int = -1): Unit = { + val out = Console.out + val exc = new Exception("Dump Stack") + var stack = exc.getStackTrace + .filter(e => !e.getClassName.startsWith("dotty.tools.dotc.reporting.TraceSyntax")) + .filter(e => !e.getClassName.startsWith("dotty.tools.dotc.reporting.trace")) + if limit >= 0 then + stack = stack.take(limit) + exc.setStackTrace(stack) + exc.printStackTrace(out) + } end trace /** This module is carefully optimized to give zero overhead if Config.tracingEnabled