Skip to content

Commit fbc89a8

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 fe49539 commit fbc89a8

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
@@ -588,6 +588,9 @@ object CaptureSet:
588588
cs.mutability = Mutability.Reader
589589
cs
590590

591+
class EmptyOfBoxed(val tp1: Type, val tp2: Type) extends Const(emptyRefs):
592+
override def toString = "{} of boxed mismatch"
593+
591594
/** The universal capture set `{cap}` */
592595
def universal(using Context): Const =
593596
Const(SimpleIdentitySet(GlobalCap))
@@ -1341,15 +1344,19 @@ object CaptureSet:
13411344
case _ =>
13421345
false
13431346

1344-
/** An include failure F1 covers another include failure F2 unless F2
1345-
* strictly subsumes F1, which means they describe the same capture sets
1346-
* and the element in F2 is more specific than the element in F1.
1347+
/** An include failure F1 covers another include failure F2 unless one
1348+
* of the following two conditons holds:
1349+
* 1. F2 strictly subsumes F1, which means they describe the same capture sets
1350+
* and the element in F2 is more specific than the element in F1.
1351+
* 2. Both F1 and F2 are the empty set, but only F2 is an empty set synthesized
1352+
* when comparing types with different box status
13471353
*/
13481354
override def covers(other: Note)(using Context) = other match
13491355
case other @ IncludeFailure(cs1, elem1, _) =>
13501356
val strictlySubsumes =
13511357
cs.elems == cs1.elems
1352-
&& elem1.singletonCaptureSet.mightSubcapture(elem.singletonCaptureSet)
1358+
&& (elem1.singletonCaptureSet.mightSubcapture(elem.singletonCaptureSet)
1359+
|| cs1.isInstanceOf[EmptyOfBoxed] && !cs.isInstanceOf[EmptyOfBoxed])
13531360
!strictlySubsumes
13541361
case _ => false
13551362

@@ -1390,6 +1397,11 @@ object CaptureSet:
13901397
else
13911398
trailing:
13921399
i"capability ${elem.showAsCapability} cannot be included in capture set $cs"
1400+
case cs: EmptyOfBoxed =>
1401+
trailing:
1402+
val (boxed, unboxed) =
1403+
if cs.tp1.isBoxedCapturing then (cs.tp1, cs.tp2) else (cs.tp2, cs.tp1)
1404+
i"${cs.tp1} does not conform to ${cs.tp2} because $boxed is boxed but $unboxed is not"
13931405
case _ =>
13941406
def why =
13951407
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)