Skip to content

Commit b2f30a8

Browse files
committed
Present aligned dependent functions as generic functions
Aligning an expected function type to be dependent gives the false expression that we could have the parameter in a local capture. To avoid that misinterpretation, convert an aligned dependent function as the expected type back to a generic functions before displaying it in the error message.
1 parent 7a3550f commit b2f30a8

20 files changed

+50
-42
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,7 @@ class CheckCaptures extends Recheck, SymTransformer:
13371337
inline def testAdapted(actual: Type, expected: Type, tree: Tree, addenda: Addenda)
13381338
(fail: (Tree, Type, Addenda) => Unit)(using Context): Type =
13391339
var expected1 = alignDependentFunction(expected, actual.stripCapturing)
1340+
val falseDeps = expected1 ne expected
13401341
val actualBoxed = adapt(actual, expected1, tree)
13411342
//println(i"check conforms $actualBoxed <<< $expected1")
13421343

@@ -1346,6 +1347,7 @@ class CheckCaptures extends Recheck, SymTransformer:
13461347
TypeComparer.compareResult(isCompatible(actualBoxed, expected1)) match
13471348
case TypeComparer.CompareResult.Fail(notes) =>
13481349
capt.println(i"conforms failed for ${tree}: $actual vs $expected")
1350+
if falseDeps then expected1 = unalignFunction(expected1)
13491351
fail(tree.withType(actualBoxed), expected1,
13501352
addApproxAddenda(addenda ++ errorNotes(notes), expected1))
13511353
actual
@@ -1360,10 +1362,8 @@ class CheckCaptures extends Recheck, SymTransformer:
13601362
/** Turn `expected` into a dependent function when `actual` is dependent. */
13611363
private def alignDependentFunction(expected: Type, actual: Type)(using Context): Type =
13621364
def recur(expected: Type): Type = expected.dealias match
1363-
case expected0 @ CapturingType(eparent, refs) =>
1364-
val eparent1 = recur(eparent)
1365-
if eparent1 eq eparent then expected
1366-
else CapturingType(eparent1, refs, boxed = expected0.isBoxed)
1365+
case expected @ CapturingType(eparent, refs) =>
1366+
expected.derivedCapturingType(recur(eparent), refs)
13671367
case expected @ defn.FunctionOf(args, resultType, isContextual)
13681368
if defn.isNonRefinedFunction(expected) =>
13691369
actual match
@@ -1381,6 +1381,14 @@ class CheckCaptures extends Recheck, SymTransformer:
13811381
case _ => expected
13821382
recur(expected)
13831383

1384+
private def unalignFunction(tp: Type)(using Context): Type = tp match
1385+
case tp @ CapturingType(parent, refs) =>
1386+
tp.derivedCapturingType(unalignFunction(parent), refs)
1387+
case defn.RefinedFunctionOf(mt) =>
1388+
mt.toFunctionType(alwaysDependent = false)
1389+
case _ =>
1390+
tp
1391+
13841392
/** For the expected type, implement the rule outlined in #14390:
13851393
* - when checking an expression `a: Ta^Ca` against an expected type `Te^Ce`,
13861394
* - where the capture set `Ce` contains Cls.this,

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
8 | x.value(cap => cap.use()) // error, was OK
33
| ^^^^^^^^^^^^^^^^
44
| Found: (cap: box Cap^?) => Int
5-
| Required: (cap: box Cap^) =>² Int
5+
| Required: box Cap^ =>² Int
66
|
77
| where: => refers to the universal root capability
88
| =>² refers to a fresh root capability created in method test1
@@ -13,13 +13,13 @@
1313
15 | x.value(cap => cap.use()) // error
1414
| ^^^^^^^^^^^^^^^^
1515
| Found: (cap: box Cap^{io}) ->{io} Int
16-
| Required: (cap: box Cap^{io}) -> Int
16+
| Required: box Cap^{io} -> Int
1717
|
1818
| longer explanation available when compiling with `-explain`
1919
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/box-adapt-cases.scala:29:10 ------------------------------
2020
29 | x.value(cap => cap.use()) // error
2121
| ^^^^^^^^^^^^^^^^
2222
| Found: (cap: box Cap^?) ->{io, fs} Int
23-
| Required: (cap: box Cap^{io, fs}) ->{io} Int
23+
| Required: box Cap^{io, fs} ->{io} Int
2424
|
2525
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/depfun-reach.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
12 | List(() => op.foreach((f,g) => { f(); g() })) // error (???)
33
| ^^^^^^^^^^^^^^^^^^^^
44
|Found: (x$1: (box () ->? Unit, box () ->? Unit)^?) ->? Unit
5-
|Required: (x$1: (box () ->{op*} Unit, box () ->{op*} Unit)) => Unit
5+
|Required: ((box () ->{op*} Unit, box () ->{op*} Unit)) => Unit
66
|
77
|where: => refers to a fresh root capability created in anonymous function of type (): Unit when checking argument to parameter op of method foreach
88
|

tests/neg-custom-args/captures/effect-swaps-explicit.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@
1919
| ^
2020
|Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^) ?->{fr, async}
2121
| box Future[box T^?]^{fr, contextual$9}
22-
|Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]^) ?=> box Future[box T^?]^?
22+
|Required: (boundary.Label[Result[box Future[box T^?]^{fr, async}, box E^?]]^) ?=> box Future[box T^?]^{fr, async}
2323
|
2424
|where: ?=> refers to a fresh root capability created in method fail4 when checking argument to parameter body of method make
2525
| ^ refers to the universal root capability
2626
|
2727
|Note that reference contextual$9.type
28-
|cannot be included in outer capture set ?
28+
|cannot be included in outer capture set {fr, async}
2929
70 | fr.await.ok
3030
|
3131
| longer explanation available when compiling with `-explain`
3232
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:73:35 ------------------------
3333
73 | Result.make[Future[T], E]: lbl ?=> // error: type mismatch
3434
| ^
3535
|Found: (lbl: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^) ?->{fr, async} Future[box T^?]^{fr, lbl}
36-
|Required: (lbl: boundary.Label[Result[Future[T], E]]^) ?=> Future[T]
36+
|Required: (boundary.Label[Result[Future[T], E]]^) ?=> Future[T]
3737
|
3838
|where: ?=> refers to a fresh root capability created in method fail5 when checking argument to parameter body of method make
3939
| ^ refers to the universal root capability

tests/neg-custom-args/captures/effect-swaps.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@
1919
| ^
2020
|Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^{cap.rd}) ?->{fr, async}
2121
| box Future[box T^?]^{fr, contextual$9}
22-
|Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]^{cap.rd}) ?=> box Future[box T^?]^?
22+
|Required: (boundary.Label[Result[box Future[box T^?]^{fr, async}, box E^?]]^{cap.rd}) ?=> box Future[box T^?]^{fr, async}
2323
|
2424
|where: ?=> refers to a fresh root capability created in method fail4 when checking argument to parameter body of method make
2525
| cap is the universal root capability
2626
|
2727
|Note that reference contextual$9.type
28-
|cannot be included in outer capture set ?
28+
|cannot be included in outer capture set {fr, async}
2929
70 | fr.await.ok
3030
|
3131
| longer explanation available when compiling with `-explain`
3232
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:73:35 ---------------------------------
3333
73 | Result.make[Future[T], E]: lbl ?=> // error: type mismatch
3434
| ^
3535
|Found: (lbl: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^{cap.rd}) ?->{fr, async} Future[box T^?]^{fr, lbl}
36-
|Required: (lbl: boundary.Label[Result[Future[T], E]]^{cap.rd}) ?=> Future[T]
36+
|Required: (boundary.Label[Result[Future[T], E]]^{cap.rd}) ?=> Future[T]
3737
|
3838
|where: ?=> refers to a fresh root capability created in method fail5 when checking argument to parameter body of method make
3939
| cap is the universal root capability

tests/neg-custom-args/captures/heal-tparam-cs.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
44 | io => () => io.use() // error
5151
| ^^^^^^^^^^^^^^^^^^^^
5252
| Found: (io: Capp^) ->? () ->{io} Unit
53-
| Required: (io: Capp^) -> () -> Unit
53+
| Required: Capp^ -> () -> Unit
5454
|
5555
| where: ^ refers to the universal root capability
5656
|

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
13 | (ref1, f1) => map[A, B](ref1, f1) // error
33
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44
|Found: (ref1: LazyRef[box A^?]{val elem: () => A^?}^{io}, f1: A^? => B^?) ->? LazyRef[box B^?]{val elem: () =>² B^?}^{f1, ref1}
5-
|Required: (ref1: LazyRef[A]^{io}, f1: A => B) =>³ LazyRef[B]^
5+
|Required: (LazyRef[A]^{io}, A => B) =>³ LazyRef[B]^
66
|
77
|where: => refers to the universal root capability
88
| =>² refers to a root capability associated with the result type of (ref1: LazyRef[box A^?]{val elem: () => A^?}^{io}, f1: A^? => B^?): LazyRef[box B^?]{val elem: () =>² B^?}^{f1, ref1}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
15 | val a = usingIO[IO^](x => x) // error // error
1717
| ^^^^^^
1818
|Found: (x: IO^) ->? box IO^{x}
19-
|Required: (x: IO^) => box IO^²
19+
|Required: IO^ => box IO^²
2020
|
2121
|where: => refers to a fresh root capability created in value a when checking argument to parameter op of method usingIO
2222
| ^ refers to the universal root capability
@@ -48,7 +48,7 @@
4848
17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error // error
4949
| ^^^^^^
5050
| Found: (x: Boxed[box IO^]^?) ->? Boxed[box IO^{x*}]^?
51-
| Required: (x: Boxed[box IO^]) -> Boxed[box IO^²]
51+
| Required: Boxed[box IO^] -> Boxed[box IO^²]
5252
|
5353
| where: ^ refers to the universal root capability
5454
| ^² refers to a fresh root capability created in value x²

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
15 | files.map(new Logger(_)) // error, Q: can we improve the error message?
1212
| ^^^^^^^^^^^^^
1313
|Found: (_$1: box File^{files*}) ->{files*} box Logger{val f: File^{_$1}}^{cap.rd, _$1}
14-
|Required: (_$1: box File^{files*}) => box Logger{val f: File^?}^?
14+
|Required: box File^{files*} => box Logger{val f: File^?}^?
1515
|
1616
|where: => refers to a fresh root capability created in method mkLoggers2 when checking argument to parameter f of method map
1717
| cap is a root capability associated with the result type of (_$1: box File^{files*}): box Logger{val f: File^{_$1}}^{cap.rd, _$1}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
34 | val cell: Cell[File] = File.open(f => Cell(() => Seq(f))) // error
33
| ^^^^^^^^^^^^^^^^^^^^^^^
44
|Found: (f: File^?) ->? box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^?
5-
|Required: (f: File^) => box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^?
5+
|Required: File^ => box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^?
66
|
77
|where: => refers to a fresh root capability created in value cell when checking argument to parameter f of method open
88
| ^ refers to the universal root capability

0 commit comments

Comments
 (0)