Skip to content

Commit 39b904d

Browse files
committed
Somewhat better error notes for box failures
At least we don't get Note that cap is not included in {} anymore. To make this even better we'd have to trace back a box failure to the original types we were trying to compare. Right now the box failure error notes are too disconnected from the rest. But they become clearer when one compiles with -explain.
1 parent 48f6cca commit 39b904d

File tree

5 files changed

+55
-7
lines changed

5 files changed

+55
-7
lines changed

compiler/src/dotty/tools/dotc/cc/CaptureSet.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,9 @@ object CaptureSet:
576576
cs.mutability = Mutability.Reader
577577
cs
578578

579+
class EmptyOfBoxed(val tp1: Type, val tp2: Type) extends Const(emptyRefs):
580+
override def toString = "{} of boxed mismatch"
581+
579582
/** The universal capture set `{cap}` */
580583
def universal(using Context): Const =
581584
Const(SimpleIdentitySet(GlobalCap))
@@ -1328,15 +1331,19 @@ object CaptureSet:
13281331
case _ =>
13291332
false
13301333

1331-
/** An include failure F1 covers another include failure F2 unless F2
1332-
* strictly subsumes F1, which means they describe the same capture sets
1333-
* and the element in F2 is more specific than the element in F1.
1334+
/** An include failure F1 covers another include failure F2 unless one
1335+
* of the following two conditons holds:
1336+
* 1. F2 strictly subsumes F1, which means they describe the same capture sets
1337+
* and the element in F2 is more specific than the element in F1.
1338+
* 2. Both F1 and F2 are the empty set, but only F2 is an empty set synthesized
1339+
* when comparing types with different box status
13341340
*/
13351341
override def covers(other: Note)(using Context) = other match
13361342
case other @ IncludeFailure(cs1, elem1, _) =>
13371343
val strictlySubsumes =
13381344
cs.elems == cs1.elems
1339-
&& elem1.singletonCaptureSet.mightSubcapture(elem.singletonCaptureSet)
1345+
&& (elem1.singletonCaptureSet.mightSubcapture(elem.singletonCaptureSet)
1346+
|| cs1.isInstanceOf[EmptyOfBoxed] && !cs.isInstanceOf[EmptyOfBoxed])
13401347
!strictlySubsumes
13411348
case _ => false
13421349

@@ -1377,6 +1384,11 @@ object CaptureSet:
13771384
else
13781385
trailing:
13791386
i"capability ${elem.showAsCapability} cannot be included in capture set $cs"
1387+
case cs: EmptyOfBoxed =>
1388+
trailing:
1389+
val (boxed, unboxed) =
1390+
if cs.tp1.isBoxedCapturing then (cs.tp1, cs.tp2) else (cs.tp2, cs.tp1)
1391+
i"${cs.tp1} does not conform to ${cs.tp2} because $boxed is boxed but $unboxed is not"
13801392
case _ =>
13811393
def why =
13821394
val reasons = cs.elems.toList.collect:

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2915,8 +2915,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
29152915
case _ =>
29162916
subc
29172917
&& (tp1.isBoxedCapturing == tp2.isBoxedCapturing
2918-
|| refs1.subCaptures(CaptureSet.empty, makeVarState()))
2919-
2918+
|| refs1.subCaptures(CaptureSet.EmptyOfBoxed(tp1, tp2), makeVarState()))
2919+
29202920
protected def logUndoAction(action: () => Unit) =
29212921
undoLog += action
29222922

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
| Found: (g : () -> A)
55
| Required: () -> Proc^{f}
66
|
7-
| Note that capability f is not included in capture set {}.
7+
| Note that () ->{f} Unit does not conform to Proc^{f} because () ->{f} Unit is boxed but Proc^{f} is not.
88
|
99
| longer explanation available when compiling with `-explain`
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i24543.scala:7:23 ----------------------------------------
2+
7 | val y: (Ref^, Int) = (x.elem, 1) // error
3+
| ^^^^^^^^^^^
4+
| Found: (T^'s1, Int)
5+
| Required: (Ref^, Int)
6+
|
7+
| Note that Ref^² does not conform to Ref^ because Ref^² is boxed but Ref^ is not.
8+
|
9+
| where: ^ refers to a fresh root capability in the type of value y
10+
| ^² refers to a fresh root capability in the type of type T
11+
|
12+
| longer explanation available when compiling with `-explain`
13+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i24543.scala:9:45 ----------------------------------------
14+
9 |def h2[T <: Ref^](x: List[T]): (Ref^, Int) = (x.head, 1) // error
15+
| ^^^^^^^^^^^
16+
| Found: (T^'s2, Int)
17+
| Required: (Ref^, Int)
18+
|
19+
| Note that Ref^² does not conform to Ref^ because Ref^² is boxed but Ref^ is not.
20+
|
21+
| where: ^ refers to a fresh root capability in the result type of method h2
22+
| ^² refers to a fresh root capability in the type of type T
23+
|
24+
| longer explanation available when compiling with `-explain`
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.language.experimental.captureChecking
2+
3+
class Ref
4+
case class Box[T](elem: T)
5+
6+
def h1[T <: Ref^](x: Box[T]): Unit =
7+
val y: (Ref^, Int) = (x.elem, 1) // error
8+
9+
def h2[T <: Ref^](x: List[T]): (Ref^, Int) = (x.head, 1) // error
10+
11+
12+

0 commit comments

Comments
 (0)