Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shorten traces for TypeMismatch errors under -explain #18742

Merged
merged 1 commit into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 37 additions & 19 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
//}
assert(!ctx.settings.YnoDeepSubtypes.value)
if (Config.traceDeepSubTypeRecursions && !this.isInstanceOf[ExplainingTypeComparer])
report.log(explained(_.isSubType(tp1, tp2, approx)))
report.log(explained(_.isSubType(tp1, tp2, approx), short = false))
}
// Eliminate LazyRefs before checking whether we have seen a type before
val normalize = new TypeMap {
Expand Down Expand Up @@ -2972,7 +2972,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
}
}

protected def explainingTypeComparer = ExplainingTypeComparer(comparerContext)
protected def explainingTypeComparer(short: Boolean) = ExplainingTypeComparer(comparerContext, short)
protected def trackingTypeComparer = TrackingTypeComparer(comparerContext)

private def inSubComparer[T, Cmp <: TypeComparer](comparer: Cmp)(op: Cmp => T): T =
Expand All @@ -2982,8 +2982,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
finally myInstance = saved

/** The trace of comparison operations when performing `op` */
def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:")(using Context): String =
val cmp = explainingTypeComparer
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)

Expand Down Expand Up @@ -3152,8 +3152,8 @@ object TypeComparer {
def constrainPatternType(pat: Type, scrut: Type, forceInvariantRefinement: Boolean = false)(using Context): Boolean =
comparing(_.constrainPatternType(pat, scrut, forceInvariantRefinement))

def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:")(using Context): String =
comparing(_.explained(op, header))
def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:", short: Boolean = false)(using Context): String =
comparing(_.explained(op, header, short))

def tracked[T](op: TrackingTypeComparer => T)(using Context): T =
comparing(_.tracked(op))
Expand Down Expand Up @@ -3350,30 +3350,47 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
}
}

/** A type comparer that can record traces of subtype operations */
class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
/** A type comparer that can record traces of subtype operations
* @param short if true print only failing forward traces; never print succesful
* subtraces; never print backtraces starting with `<==`.
*/
class ExplainingTypeComparer(initctx: Context, short: Boolean) extends TypeComparer(initctx) {
import TypeComparer._

init(initctx)

override def explainingTypeComparer = this
override def explainingTypeComparer(short: Boolean) =
if short == this.short then this
else ExplainingTypeComparer(comparerContext, short)

private var indent = 0
private val b = new StringBuilder

private var skipped = false
private var lastForwardGoal: String | Null = null

override def traceIndented[T](str: String)(op: => T): T =
if (skipped) op
else {
val str1 = str.replace('\n', ' ')
if short && str1 == lastForwardGoal then
op // repeated goal, skip for clarity
else
lastForwardGoal = str1
val curLength = b.length
indent += 2
val str1 = str.replace('\n', ' ')
b.append("\n").append(" " * indent).append("==> ").append(str1)
val res = op
b.append("\n").append(" " * indent).append("<== ").append(str1).append(" = ").append(show(res))
if short then
if res == false then
if lastForwardGoal != null then // last was deepest goal that failed
b.append(" = false")
lastForwardGoal = null
else
b.length = curLength // don't show successful subtraces
else
b.append("\n").append(" " * indent).append("<== ").append(str1).append(" = ").append(show(res))
indent -= 2
res
}

private def traceIndentedIfNotShort[T](str: String)(op: => T): T =
if short then op else traceIndented(str)(op)

private def frozenNotice: String =
if frozenConstraint then " in frozen constraint" else ""
Expand All @@ -3384,7 +3401,8 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
then s" ${tp1.getClass} ${tp2.getClass}"
else ""
val approx = approxState
traceIndented(s"${show(tp1)} <: ${show(tp2)}$moreInfo${approx.show}$frozenNotice") {
def approxStr = if short then "" else approx.show
traceIndented(s"${show(tp1)} <: ${show(tp2)}$moreInfo${approxStr}$frozenNotice") {
super.recur(tp1, tp2)
}

Expand All @@ -3394,12 +3412,12 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
}

override def lub(tp1: Type, tp2: Type, canConstrain: Boolean, isSoft: Boolean): Type =
traceIndented(s"lub(${show(tp1)}, ${show(tp2)}, canConstrain=$canConstrain, isSoft=$isSoft)") {
traceIndentedIfNotShort(s"lub(${show(tp1)}, ${show(tp2)}, canConstrain=$canConstrain, isSoft=$isSoft)") {
super.lub(tp1, tp2, canConstrain, isSoft)
}

override def glb(tp1: Type, tp2: Type): Type =
traceIndented(s"glb(${show(tp1)}, ${show(tp2)})") {
traceIndentedIfNotShort(s"glb(${show(tp1)}, ${show(tp2)})") {
super.glb(tp1, tp2)
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ object ErrorReporting {
| $found
|conforms to
| $expected
|but the comparison trace ended with `false`:
|but none of the attempts shown below succeeded:
|"""
val c = ctx.typerState.constraint
val constraintText =
Expand All @@ -213,7 +213,7 @@ object ErrorReporting {
else
i"""a constraint with:
|$c"""
i"""${TypeComparer.explained(_.isSubType(found, expected), header)}
i"""${TypeComparer.explained(_.isSubType(found, expected), header, short = !ctx.settings.Ydebug.value)}
|
|The tests were made under $constraintText"""

Expand Down
7 changes: 2 additions & 5 deletions tests/neg/hidden-type-errors.check
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@
| String
| conforms to
| Int
| but the comparison trace ended with `false`:
| but none of the attempts shown below succeeded:
|
| ==> String <: Int
| ==> String <: Int
| <== String <: Int = false
| <== String <: Int = false
| ==> String <: Int = false
|
| The tests were made under the empty constraint
---------------------------------------------------------------------------------------------------------------------
22 changes: 4 additions & 18 deletions tests/neg/i11637.check
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,13 @@
| test2.FunctorImpl
| conforms to
| [Generic2[T <: String] <: Set[T]] =>> Any
| but the comparison trace ended with `false`:
| but none of the attempts shown below succeeded:
|
| ==> test2.FunctorImpl <: [Generic2[T <: String] <: Set[T]] =>> Any
| ==> type bounds [[T <: String] <: Set[T]] <: type bounds [[T] <: Iterable[T]]
| ==> [T <: String] =>> Set[T] <: Iterable
| ==> type bounds [] <: type bounds [ <: String]
| ==> Any <: String
| ==> Any <: String
| <== Any <: String = false
| <== Any <: String = false
| <== type bounds [] <: type bounds [ <: String] = false
| <== [T <: String] =>> Set[T] <: Iterable = false
| <== type bounds [[T <: String] <: Set[T]] <: type bounds [[T] <: Iterable[T]] = false
| <== test2.FunctorImpl <: [Generic2[T <: String] <: Set[T]] =>> Any = false
| ==> Any <: String = false
|
| The tests were made under the empty constraint
--------------------------------------------------------------------------------------------------------------------
Expand All @@ -37,20 +30,13 @@
| test2.FunctorImpl
| conforms to
| [Generic2[T <: String] <: Set[T]] =>> Any
| but the comparison trace ended with `false`:
| but none of the attempts shown below succeeded:
|
| ==> test2.FunctorImpl <: [Generic2[T <: String] <: Set[T]] =>> Any
| ==> type bounds [[T <: String] <: Set[T]] <: type bounds [[T] <: Iterable[T]]
| ==> [T <: String] =>> Set[T] <: Iterable
| ==> type bounds [] <: type bounds [ <: String]
| ==> Any <: String
| ==> Any <: String
| <== Any <: String = false
| <== Any <: String = false
| <== type bounds [] <: type bounds [ <: String] = false
| <== [T <: String] =>> Set[T] <: Iterable = false
| <== type bounds [[T <: String] <: Set[T]] <: type bounds [[T] <: Iterable[T]] = false
| <== test2.FunctorImpl <: [Generic2[T <: String] <: Set[T]] =>> Any = false
| ==> Any <: String = false
|
| The tests were made under the empty constraint
--------------------------------------------------------------------------------------------------------------------
13 changes: 4 additions & 9 deletions tests/neg/i15575.check
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@
| Any
| conforms to
| T & Any
| but the comparison trace ended with `false`:
| but none of the attempts shown below succeeded:
|
| ==> Any <: T & Any
| ==> Any <: T
| <== Any <: T = false
| <== Any <: T & Any = false
| ==> Any <: T = false
|
| The tests were made under the empty constraint
---------------------------------------------------------------------------------------------------------------------
Expand All @@ -29,12 +27,9 @@
| CharSequence
| conforms to
| String
| but the comparison trace ended with `false`:
| but none of the attempts shown below succeeded:
|
| ==> CharSequence <: String
| ==> CharSequence <: String
| <== CharSequence <: String = false
| <== CharSequence <: String = false
| ==> CharSequence <: String = false
|
| The tests were made under the empty constraint
---------------------------------------------------------------------------------------------------------------------
75 changes: 75 additions & 0 deletions tests/neg/i18737.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
-- [E007] Type Mismatch Error: tests/neg/i18737.scala:3:36 -------------------------------------------------------------
3 |def test2(v: String & Long) = test1(v) // error
| ^
| Found: (v : String & Long)
| Required: String & Integer & List[String]
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| Tree: v
| I tried to show that
| (v : String & Long)
| conforms to
| String & Integer & List[String]
| but none of the attempts shown below succeeded:
|
| ==> (v : String & Long) <: String & Integer & List[String]
| ==> (v : String & Long) <: String & Integer
| ==> (v : String & Long) <: Integer
| ==> String & Long <: Integer
| ==> String <: Integer = false
| ==> Long <: Integer = false
|
| The tests were made under the empty constraint
---------------------------------------------------------------------------------------------------------------------
-- [E007] Type Mismatch Error: tests/neg/i18737.scala:6:36 -------------------------------------------------------------
6 |def test4(v: String | Long) = test3(v) // error
| ^
| Found: (v : String | Long)
| Required: String | Integer | List[String]
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| Tree: v
| I tried to show that
| (v : String | Long)
| conforms to
| String | Integer | List[String]
| but none of the attempts shown below succeeded:
|
| ==> (v : String | Long) <: String | Integer | List[String]
| ==> String | Long <: String | Integer | List[String]
| ==> Long <: String | Integer | List[String]
| ==> Long <: String | Integer
| ==> Long <: String = false
| ==> Long <: Integer = false
| ==> Long <: List[String] = false
| ==> (v : String | Long) <: String | Integer
| ==> String | Long <: String | Integer
| ==> Long <: String | Integer
| ==> Long <: String = false
| ==> Long <: Integer = false
| ==> (v : String | Long) <: String
| ==> String | Long <: String
| ==> Long <: String = false
| ==> (v : String | Long) <: Integer
| ==> String | Long <: Integer
| ==> String <: Integer = false
| ==> String | Long <: String | Integer
| ==> Long <: String | Integer
| ==> Long <: String = false
| ==> Long <: Integer = false
| ==> (v : String | Long) <: List[String]
| ==> String | Long <: List[String]
| ==> String <: List[String] = false
| ==> String | Long <: String | Integer | List[String]
| ==> Long <: String | Integer | List[String]
| ==> Long <: String | Integer
| ==> Long <: String = false
| ==> Long <: Integer = false
| ==> Long <: List[String] = false
|
| The tests were made under the empty constraint
---------------------------------------------------------------------------------------------------------------------
6 changes: 6 additions & 0 deletions tests/neg/i18737.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//> using options -explain
def test1(v: String & Integer & List[String]) = ()
def test2(v: String & Long) = test1(v) // error

def test3(v: String | Integer | List[String]) = ()
def test4(v: String | Long) = test3(v) // error
Loading