Skip to content

Commit df3dc4f

Browse files
Backport "Shorten traces for TypeMismatch errors under -explain" to LTS (#20738)
Backports #18742 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents 6b1af5e + dbdc4da commit df3dc4f

File tree

7 files changed

+130
-53
lines changed

7 files changed

+130
-53
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

+37-19
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
246246
//}
247247
assert(!ctx.settings.YnoDeepSubtypes.value)
248248
if (Config.traceDeepSubTypeRecursions && !this.isInstanceOf[ExplainingTypeComparer])
249-
report.log(explained(_.isSubType(tp1, tp2, approx)))
249+
report.log(explained(_.isSubType(tp1, tp2, approx), short = false))
250250
}
251251
// Eliminate LazyRefs before checking whether we have seen a type before
252252
val normalize = new TypeMap {
@@ -2868,7 +2868,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
28682868
}
28692869
}
28702870

2871-
protected def explainingTypeComparer = ExplainingTypeComparer(comparerContext)
2871+
protected def explainingTypeComparer(short: Boolean) = ExplainingTypeComparer(comparerContext, short)
28722872
protected def trackingTypeComparer = TrackingTypeComparer(comparerContext)
28732873

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

28802880
/** The trace of comparison operations when performing `op` */
2881-
def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:")(using Context): String =
2882-
val cmp = explainingTypeComparer
2881+
def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:", short: Boolean)(using Context): String =
2882+
val cmp = explainingTypeComparer(short)
28832883
inSubComparer(cmp)(op)
28842884
cmp.lastTrace(header)
28852885

@@ -3038,8 +3038,8 @@ object TypeComparer {
30383038
def constrainPatternType(pat: Type, scrut: Type, forceInvariantRefinement: Boolean = false)(using Context): Boolean =
30393039
comparing(_.constrainPatternType(pat, scrut, forceInvariantRefinement))
30403040

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

30443044
def tracked[T](op: TrackingTypeComparer => T)(using Context): T =
30453045
comparing(_.tracked(op))
@@ -3207,30 +3207,47 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32073207
}
32083208
}
32093209

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

32143217
init(initctx)
32153218

3216-
override def explainingTypeComparer = this
3219+
override def explainingTypeComparer(short: Boolean) =
3220+
if short == this.short then this
3221+
else ExplainingTypeComparer(comparerContext, short)
32173222

32183223
private var indent = 0
32193224
private val b = new StringBuilder
3220-
3221-
private var skipped = false
3225+
private var lastForwardGoal: String | Null = null
32223226

32233227
override def traceIndented[T](str: String)(op: => T): T =
3224-
if (skipped) op
3225-
else {
3228+
val str1 = str.replace('\n', ' ')
3229+
if short && str1 == lastForwardGoal then
3230+
op // repeated goal, skip for clarity
3231+
else
3232+
lastForwardGoal = str1
3233+
val curLength = b.length
32263234
indent += 2
3227-
val str1 = str.replace('\n', ' ')
32283235
b.append("\n").append(" " * indent).append("==> ").append(str1)
32293236
val res = op
3230-
b.append("\n").append(" " * indent).append("<== ").append(str1).append(" = ").append(show(res))
3237+
if short then
3238+
if res == false then
3239+
if lastForwardGoal != null then // last was deepest goal that failed
3240+
b.append(" = false")
3241+
lastForwardGoal = null
3242+
else
3243+
b.length = curLength // don't show successful subtraces
3244+
else
3245+
b.append("\n").append(" " * indent).append("<== ").append(str1).append(" = ").append(show(res))
32313246
indent -= 2
32323247
res
3233-
}
3248+
3249+
private def traceIndentedIfNotShort[T](str: String)(op: => T): T =
3250+
if short then op else traceIndented(str)(op)
32343251

32353252
private def frozenNotice: String =
32363253
if frozenConstraint then " in frozen constraint" else ""
@@ -3241,7 +3258,8 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32413258
then s" ${tp1.getClass} ${tp2.getClass}"
32423259
else ""
32433260
val approx = approxState
3244-
traceIndented(s"${show(tp1)} <: ${show(tp2)}$moreInfo${approx.show}$frozenNotice") {
3261+
def approxStr = if short then "" else approx.show
3262+
traceIndented(s"${show(tp1)} <: ${show(tp2)}$moreInfo${approxStr}$frozenNotice") {
32453263
super.recur(tp1, tp2)
32463264
}
32473265

@@ -3251,12 +3269,12 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32513269
}
32523270

32533271
override def lub(tp1: Type, tp2: Type, canConstrain: Boolean, isSoft: Boolean): Type =
3254-
traceIndented(s"lub(${show(tp1)}, ${show(tp2)}, canConstrain=$canConstrain, isSoft=$isSoft)") {
3272+
traceIndentedIfNotShort(s"lub(${show(tp1)}, ${show(tp2)}, canConstrain=$canConstrain, isSoft=$isSoft)") {
32553273
super.lub(tp1, tp2, canConstrain, isSoft)
32563274
}
32573275

32583276
override def glb(tp1: Type, tp2: Type): Type =
3259-
traceIndented(s"glb(${show(tp1)}, ${show(tp2)})") {
3277+
traceIndentedIfNotShort(s"glb(${show(tp1)}, ${show(tp2)})") {
32603278
super.glb(tp1, tp2)
32613279
}
32623280

compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ object ErrorReporting {
195195
| $found
196196
|conforms to
197197
| $expected
198-
|but the comparison trace ended with `false`:
198+
|but none of the attempts shown below succeeded:
199199
|"""
200200
val c = ctx.typerState.constraint
201201
val constraintText =
@@ -204,7 +204,7 @@ object ErrorReporting {
204204
else
205205
i"""a constraint with:
206206
|$c"""
207-
i"""${TypeComparer.explained(_.isSubType(found, expected), header)}
207+
i"""${TypeComparer.explained(_.isSubType(found, expected), header, short = !ctx.settings.Ydebug.value)}
208208
|
209209
|The tests were made under $constraintText"""
210210

tests/neg/hidden-type-errors.check

+2-5
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@
1212
| String
1313
| conforms to
1414
| Int
15-
| but the comparison trace ended with `false`:
15+
| but none of the attempts shown below succeeded:
1616
|
17-
| ==> String <: Int
18-
| ==> String <: Int
19-
| <== String <: Int = false
20-
| <== String <: Int = false
17+
| ==> String <: Int = false
2118
|
2219
| The tests were made under the empty constraint
2320
---------------------------------------------------------------------------------------------------------------------

tests/neg/i11637.check

+4-18
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,13 @@
99
| test2.FunctorImpl
1010
| conforms to
1111
| [Generic2[T <: String] <: Set[T]] =>> Any
12-
| but the comparison trace ended with `false`:
12+
| but none of the attempts shown below succeeded:
1313
|
1414
| ==> test2.FunctorImpl <: [Generic2[T <: String] <: Set[T]] =>> Any
1515
| ==> type bounds [[T <: String] <: Set[T]] <: type bounds [[T] <: Iterable[T]]
1616
| ==> [T <: String] =>> Set[T] <: Iterable
1717
| ==> type bounds [] <: type bounds [ <: String]
18-
| ==> Any <: String
19-
| ==> Any <: String
20-
| <== Any <: String = false
21-
| <== Any <: String = false
22-
| <== type bounds [] <: type bounds [ <: String] = false
23-
| <== [T <: String] =>> Set[T] <: Iterable = false
24-
| <== type bounds [[T <: String] <: Set[T]] <: type bounds [[T] <: Iterable[T]] = false
25-
| <== test2.FunctorImpl <: [Generic2[T <: String] <: Set[T]] =>> Any = false
18+
| ==> Any <: String = false
2619
|
2720
| The tests were made under the empty constraint
2821
--------------------------------------------------------------------------------------------------------------------
@@ -37,20 +30,13 @@
3730
| test2.FunctorImpl
3831
| conforms to
3932
| [Generic2[T <: String] <: Set[T]] =>> Any
40-
| but the comparison trace ended with `false`:
33+
| but none of the attempts shown below succeeded:
4134
|
4235
| ==> test2.FunctorImpl <: [Generic2[T <: String] <: Set[T]] =>> Any
4336
| ==> type bounds [[T <: String] <: Set[T]] <: type bounds [[T] <: Iterable[T]]
4437
| ==> [T <: String] =>> Set[T] <: Iterable
4538
| ==> type bounds [] <: type bounds [ <: String]
46-
| ==> Any <: String
47-
| ==> Any <: String
48-
| <== Any <: String = false
49-
| <== Any <: String = false
50-
| <== type bounds [] <: type bounds [ <: String] = false
51-
| <== [T <: String] =>> Set[T] <: Iterable = false
52-
| <== type bounds [[T <: String] <: Set[T]] <: type bounds [[T] <: Iterable[T]] = false
53-
| <== test2.FunctorImpl <: [Generic2[T <: String] <: Set[T]] =>> Any = false
39+
| ==> Any <: String = false
5440
|
5541
| The tests were made under the empty constraint
5642
--------------------------------------------------------------------------------------------------------------------

tests/neg/i15575.check

+4-9
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
| Any
1010
| conforms to
1111
| T & Any
12-
| but the comparison trace ended with `false`:
12+
| but none of the attempts shown below succeeded:
1313
|
1414
| ==> Any <: T & Any
15-
| ==> Any <: T
16-
| <== Any <: T = false
17-
| <== Any <: T & Any = false
15+
| ==> Any <: T = false
1816
|
1917
| The tests were made under the empty constraint
2018
---------------------------------------------------------------------------------------------------------------------
@@ -29,12 +27,9 @@
2927
| CharSequence
3028
| conforms to
3129
| String
32-
| but the comparison trace ended with `false`:
30+
| but none of the attempts shown below succeeded:
3331
|
34-
| ==> CharSequence <: String
35-
| ==> CharSequence <: String
36-
| <== CharSequence <: String = false
37-
| <== CharSequence <: String = false
32+
| ==> CharSequence <: String = false
3833
|
3934
| The tests were made under the empty constraint
4035
---------------------------------------------------------------------------------------------------------------------

tests/neg/i18737.check

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
-- [E007] Type Mismatch Error: tests/neg/i18737.scala:3:36 -------------------------------------------------------------
2+
3 |def test2(v: String & Long) = test1(v) // error
3+
| ^
4+
| Found: (v : String & Long)
5+
| Required: String & Integer & List[String]
6+
|---------------------------------------------------------------------------------------------------------------------
7+
| Explanation (enabled by `-explain`)
8+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9+
|
10+
| Tree: v
11+
| I tried to show that
12+
| (v : String & Long)
13+
| conforms to
14+
| String & Integer & List[String]
15+
| but none of the attempts shown below succeeded:
16+
|
17+
| ==> (v : String & Long) <: String & Integer & List[String]
18+
| ==> (v : String & Long) <: String & Integer
19+
| ==> (v : String & Long) <: Integer
20+
| ==> String & Long <: Integer
21+
| ==> String <: Integer = false
22+
| ==> Long <: Integer = false
23+
|
24+
| The tests were made under the empty constraint
25+
---------------------------------------------------------------------------------------------------------------------
26+
-- [E007] Type Mismatch Error: tests/neg/i18737.scala:6:36 -------------------------------------------------------------
27+
6 |def test4(v: String | Long) = test3(v) // error
28+
| ^
29+
| Found: (v : String | Long)
30+
| Required: String | Integer | List[String]
31+
|---------------------------------------------------------------------------------------------------------------------
32+
| Explanation (enabled by `-explain`)
33+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34+
|
35+
| Tree: v
36+
| I tried to show that
37+
| (v : String | Long)
38+
| conforms to
39+
| String | Integer | List[String]
40+
| but none of the attempts shown below succeeded:
41+
|
42+
| ==> (v : String | Long) <: String | Integer | List[String]
43+
| ==> String | Long <: String | Integer | List[String]
44+
| ==> Long <: String | Integer | List[String]
45+
| ==> Long <: String | Integer
46+
| ==> Long <: String = false
47+
| ==> Long <: Integer = false
48+
| ==> Long <: List[String] = false
49+
| ==> (v : String | Long) <: String | Integer
50+
| ==> String | Long <: String | Integer
51+
| ==> Long <: String | Integer
52+
| ==> Long <: String = false
53+
| ==> Long <: Integer = false
54+
| ==> (v : String | Long) <: String
55+
| ==> String | Long <: String
56+
| ==> Long <: String = false
57+
| ==> (v : String | Long) <: Integer
58+
| ==> String | Long <: Integer
59+
| ==> String <: Integer = false
60+
| ==> String | Long <: String | Integer
61+
| ==> Long <: String | Integer
62+
| ==> Long <: String = false
63+
| ==> Long <: Integer = false
64+
| ==> (v : String | Long) <: List[String]
65+
| ==> String | Long <: List[String]
66+
| ==> String <: List[String] = false
67+
| ==> String | Long <: String | Integer | List[String]
68+
| ==> Long <: String | Integer | List[String]
69+
| ==> Long <: String | Integer
70+
| ==> Long <: String = false
71+
| ==> Long <: Integer = false
72+
| ==> Long <: List[String] = false
73+
|
74+
| The tests were made under the empty constraint
75+
---------------------------------------------------------------------------------------------------------------------

tests/neg/i18737.scala

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//> using options -explain
2+
def test1(v: String & Integer & List[String]) = ()
3+
def test2(v: String & Long) = test1(v) // error
4+
5+
def test3(v: String | Integer | List[String]) = ()
6+
def test4(v: String | Long) = test3(v) // error

0 commit comments

Comments
 (0)