Skip to content

Commit 0bddba1

Browse files
committed
Better grouping of explanations in error messages
Group explanations that refer to the same Recorded entry with multiple strings together.
1 parent 0b639c9 commit 0bddba1

35 files changed

+304
-402
lines changed

compiler/src/dotty/tools/dotc/reporting/Message.scala

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ object Message:
142142
end record
143143

144144
/** Create explanation for single `Recorded` type or symbol */
145-
private def explanation(entry: AnyRef, key: String)(using Context): String =
145+
private def explanation(entry: AnyRef, keys: List[String])(using Context): String =
146146
def boundStr(bound: Type, default: ClassSymbol, cmp: String) =
147147
if (bound.isRef(default)) "" else i"$cmp $bound"
148148

@@ -178,7 +178,8 @@ object Message:
178178
s"is an unknown value of type ${tp.widen.show}"
179179
case ref: RootCapability =>
180180
val relation =
181-
if List("^", "=>", "?=>").exists(key.startsWith) then "refers to"
181+
if keys.length > 1 then "refer to"
182+
else if List("^", "=>", "?=>").exists(keys(0).startsWith) then "refers to"
182183
else "is"
183184
s"$relation ${ref.descr}"
184185
end explanation
@@ -207,16 +208,20 @@ object Message:
207208
res // help the inferencer out
208209
}.sortBy(_._1)
209210

210-
def columnar(parts: List[(String, String)]): List[String] = {
211+
def columnar(parts: List[(String, String)]): List[String] =
211212
lazy val maxLen = parts.map(_._1.length).max
212-
parts.map {
213-
case (leader, trailer) =>
214-
val variable = hl(leader)
215-
s"""$variable${" " * (maxLen - leader.length)} $trailer"""
216-
}
217-
}
218-
219-
val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry, str)) }
213+
parts.map: (leader, trailer) =>
214+
val variable = hl(leader)
215+
s"""$variable${" " * (maxLen - leader.length)} $trailer"""
216+
217+
// Group keys with the same Recorded entry together. We can't use groupBy here
218+
// since we want to maintain the order in which entries first appear in the
219+
// original list.
220+
val toExplainGrouped: List[(Recorded, List[String])] =
221+
for entry <- toExplain.map(_._2).distinct
222+
yield (entry, for (key, e) <- toExplain if e == entry yield key)
223+
val explainParts = toExplainGrouped.map:
224+
(entry, keys) => (keys.mkString(" and "), explanation(entry, keys))
220225
val explainLines = columnar(explainParts)
221226
if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n"
222227
end explanations

tests/neg-custom-args/captures/boundary.check

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
|
88
|Note that capability cap cannot be included in outer capture set 's1.
99
|
10-
|where: ^ refers to a fresh root capability classified as Control created in value local when constructing Capability instance scala.util.boundary.Label[Object^'s1]
11-
| ^² refers to the universal root capability
12-
| ^³ refers to a fresh root capability classified as Control in the type of value local
13-
| cap is the universal root capability
10+
|where: ^ refers to a fresh root capability classified as Control created in value local when constructing Capability instance scala.util.boundary.Label[Object^'s1]
11+
| ^² and cap refer to the universal root capability
12+
| ^³ refers to a fresh root capability classified as Control in the type of value local
1413
6 | boundary[Unit]: l2 ?=>
1514
7 | boundary.break(l2)(using l1) // error
1615
8 | ???
@@ -31,11 +30,10 @@
3130
|
3231
|Note that capability cap is not included in capture set {cap²}.
3332
|
34-
|where: ^ refers to the universal root capability
35-
| ^² refers to a fresh root capability classified as Control in the type of value local²
36-
| ^³ refers to a fresh root capability classified as Control created in package <empty> when checking argument to parameter label of method break
37-
| cap is a fresh root capability classified as Control in the type of value local
38-
| cap² is a fresh root capability classified as Control created in package <empty> when checking argument to parameter label of method break
33+
|where: ^ refers to the universal root capability
34+
| ^² refers to a fresh root capability classified as Control in the type of value local²
35+
| ^³ and cap² refer to a fresh root capability classified as Control created in package <empty> when checking argument to parameter label of method break
36+
| cap is a fresh root capability classified as Control in the type of value local
3937
|
4038
| longer explanation available when compiling with `-explain`
4139
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boundary.scala:5:4 ---------------------------------------
@@ -47,10 +45,8 @@
4745
|
4846
| Note that capability cap is not included in capture set {cap²}.
4947
|
50-
| where: ^ refers to the universal root capability
51-
| ^² refers to a fresh root capability created in package <empty>
52-
| cap is a fresh root capability created in package <empty>
53-
| cap² is the universal root capability
48+
| where: ^ and cap² refer to the universal root capability
49+
| ^² and cap refer to a fresh root capability created in package <empty>
5450
6 | boundary[Unit]: l2 ?=>
5551
7 | boundary.break(l2)(using l1) // error
5652
8 | ???

tests/neg-custom-args/captures/box-adapt-contra.check

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
| Cap^{c} => Unit cannot be box-converted to Cap^{c} ->{cap, c} Unit
1010
| since the additional capture set {c} resulting from box conversion is not allowed in Cap^{c} => Unit
1111
|
12-
| where: => refers to the universal root capability
13-
| cap is the universal root capability
12+
| where: => and cap refer to the universal root capability
1413
-- Error: tests/neg-custom-args/captures/box-adapt-contra.scala:19:54 --------------------------------------------------
1514
19 | val f3: (Cap^{c} -> Unit) => Unit = useCap3[Cap^{c}](c) // error
1615
| ^^^^^^^^^^^^^^^^^^^

tests/neg-custom-args/captures/capt1.check

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,8 @@
5959
|Note that capability cap is not included in capture set {cap²}
6060
|because cap is not visible from cap² in value z2.
6161
|
62-
|where: ^ refers to a root capability associated with the result type of (): C^
63-
| ^² refers to a fresh root capability created in value z2 when checking argument to parameter a of method h
64-
| cap is a root capability associated with the result type of (): C^
65-
| cap² is a fresh root capability created in value z2 when checking argument to parameter a of method h
62+
|where: ^ and cap refer to a root capability associated with the result type of (): C^
63+
| ^² and cap² refer to a fresh root capability created in value z2 when checking argument to parameter a of method h
6664
|
6765
| longer explanation available when compiling with `-explain`
6866
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:37:5 -----------------------------------------
@@ -74,10 +72,8 @@
7472
|Note that capability cap is not included in capture set {cap²}
7573
|because cap is not visible from cap² in value z2.
7674
|
77-
|where: ^ refers to a root capability associated with the result type of (): C^
78-
| ^² refers to a fresh root capability created in value z2 when checking argument to parameter b of method h
79-
| cap is a root capability associated with the result type of (): C^
80-
| cap² is a fresh root capability created in value z2 when checking argument to parameter b of method h
75+
|where: ^ and cap refer to a root capability associated with the result type of (): C^
76+
| ^² and cap² refer to a fresh root capability created in value z2 when checking argument to parameter b of method h
8177
|
8278
| longer explanation available when compiling with `-explain`
8379
-- Error: tests/neg-custom-args/captures/capt1.scala:38:13 -------------------------------------------------------------
Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,60 @@
11
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-existential-conformance.scala:8:24 --------------------
22
8 | val y: A -> Fun[B^] = x // error
33
| ^
4-
| Found: (x : A -> (x²: A) -> B^)
5-
| Required: A -> A -> B^²
4+
| Found: (x : A -> (x²: A) -> B^)
5+
| Required: A -> A -> B^²
66
|
7-
| Note that capability cap is not included in capture set {cap²}
8-
| because cap is not visible from cap² in value y.
7+
| Note that capability cap is not included in capture set {cap²}
8+
| because cap is not visible from cap² in value y.
99
|
10-
| where: ^ refers to a root capability associated with the result type of (x²: A): B^
11-
| ^² refers to a fresh root capability in the type of value y
12-
| cap is a root capability associated with the result type of (x²: A): B^
13-
| cap² is a fresh root capability in the type of value y
14-
| x is a value in method test
15-
| x² is a reference to a value parameter
10+
| where: ^ and cap refer to a root capability associated with the result type of (x²: A): B^
11+
| ^² and cap² refer to a fresh root capability in the type of value y
12+
| x is a value in method test
13+
| x² is a reference to a value parameter
1614
|
1715
| longer explanation available when compiling with `-explain`
1816
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-existential-conformance.scala:9:29 --------------------
1917
9 | val z: A -> (x: A) -> B^ = y // error
2018
| ^
21-
| Found: A -> A -> B^{y*}
22-
| Required: A -> (x: A) -> B^
19+
| Found: A -> A -> B^{y*}
20+
| Required: A -> (x: A) -> B^
2321
|
24-
| Note that capability y* is not included in capture set {cap}.
22+
| Note that capability y* is not included in capture set {cap}.
2523
|
26-
| Note that the existential capture root in B^²
27-
| cannot subsume the capability y* since that capability is not a `Sharable` capability..
24+
| Note that the existential capture root in B^²
25+
| cannot subsume the capability y* since that capability is not a `Sharable` capability..
2826
|
29-
| where: ^ refers to a root capability associated with the result type of (x: A): B^
30-
| ^² refers to the universal root capability
31-
| cap is a root capability associated with the result type of (x: A): B^
27+
| where: ^ and cap refer to a root capability associated with the result type of (x: A): B^
28+
| ^² refers to the universal root capability
3229
|
3330
| longer explanation available when compiling with `-explain`
3431
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-existential-conformance.scala:13:19 -------------------
3532
13 | val y: Fun[B^] = x // error
3633
| ^
37-
| Found: (x : (x²: A) -> B^)
38-
| Required: A -> B^²
34+
| Found: (x : (x²: A) -> B^)
35+
| Required: A -> B^²
3936
|
40-
| Note that capability cap is not included in capture set {cap²}
41-
| because cap is not visible from cap² in value y.
37+
| Note that capability cap is not included in capture set {cap²}
38+
| because cap is not visible from cap² in value y.
4239
|
43-
| where: ^ refers to a root capability associated with the result type of (x²: A): B^
44-
| ^² refers to a fresh root capability in the type of value y
45-
| cap is a root capability associated with the result type of (x²: A): B^
46-
| cap² is a fresh root capability in the type of value y
47-
| x is a value in method test2
48-
| x² is a reference to a value parameter
40+
| where: ^ and cap refer to a root capability associated with the result type of (x²: A): B^
41+
| ^² and cap² refer to a fresh root capability in the type of value y
42+
| x is a value in method test2
43+
| x² is a reference to a value parameter
4944
|
5045
| longer explanation available when compiling with `-explain`
5146
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-existential-conformance.scala:14:24 -------------------
5247
14 | val z: (x: A) -> B^ = y // error
5348
| ^
54-
| Found: A -> B^{y*}
55-
| Required: (x: A) -> B^
49+
| Found: A -> B^{y*}
50+
| Required: (x: A) -> B^
5651
|
57-
| Note that capability y* is not included in capture set {cap}.
52+
| Note that capability y* is not included in capture set {cap}.
5853
|
59-
| Note that the existential capture root in B^²
60-
| cannot subsume the capability y* since that capability is not a `Sharable` capability..
54+
| Note that the existential capture root in B^²
55+
| cannot subsume the capability y* since that capability is not a `Sharable` capability..
6156
|
62-
| where: ^ refers to a root capability associated with the result type of (x: A): B^
63-
| ^² refers to the universal root capability
64-
| cap is a root capability associated with the result type of (x: A): B^
57+
| where: ^ and cap refer to a root capability associated with the result type of (x: A): B^
58+
| ^² refers to the universal root capability
6559
|
6660
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/cc-poly-2.check

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
|
77
| Note that capability cap is not included in capture set {c1}.
88
|
9-
| where: ^ refers to a fresh root capability in the type of value d
10-
| cap is a fresh root capability in the type of value d
9+
| where: ^ and cap refer to a fresh root capability in the type of value d
1110
|
1211
| longer explanation available when compiling with `-explain`
1312
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-poly-2.scala:16:20 ------------------------------------

tests/neg-custom-args/captures/cc-this4.check

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
|
77
| Note that capability cap is not included in capture set {}.
88
|
9-
| where: ^ refers to the universal root capability
10-
| cap is the universal root capability
9+
| where: ^ and cap refer to the universal root capability
1110
|
1211
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/class-level-attack.check

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
| Note that capability x is not included in capture set {cap}
1515
| because (x : IO^²) in method set is not visible from cap in value r.
1616
|
17-
| where: ^ refers to a fresh root capability in the type of value r
18-
| ^² refers to a fresh root capability in the type of parameter x
19-
| cap is a fresh root capability in the type of value r
17+
| where: ^ and cap refer to a fresh root capability in the type of value r
18+
| ^² refers to a fresh root capability in the type of parameter x
2019
|
2120
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/closure-result-typing.check

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
|
77
| Note that capability cap is not included in capture set {}.
88
|
9-
| where: ^ refers to a fresh root capability in the type of parameter c
10-
| cap is a fresh root capability in the type of parameter c
9+
| where: ^ and cap refer to a fresh root capability in the type of parameter c
1110
|
1211
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/contracap.check

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
|
77
| Note that capability a is not included in capture set {cap}.
88
|
9-
| where: ^ refers to the universal root capability
10-
| cap is the universal root capability
9+
| where: ^ and cap refer to the universal root capability
1110
|
1211
| longer explanation available when compiling with `-explain`

0 commit comments

Comments
 (0)