Skip to content

Commit ad0b86e

Browse files
authored
Fix typing closure blocks where result type refers to a closure parameter. (#24299)
Fix typing closure blocks where the expected result type refers to a closure parameter. A substitution was missing in this case. Fixes #23727
2 parents 04389c6 + 1209716 commit ad0b86e

File tree

5 files changed

+34
-29
lines changed

5 files changed

+34
-29
lines changed

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,18 +1045,28 @@ class CheckCaptures extends Recheck, SymTransformer:
10451045
checkConformsExpr(argType, paramType, param)
10461046
.showing(i"compared expected closure formal $argType against $param with ${paramTpt.nuType}", capt)
10471047
if resType.isValueType && isFullyDefined(resType, ForceDegree.none) then
1048-
val localResType = pt match
1048+
1049+
def updateTpt(localResType: Type) =
1050+
mdef.tpt.updNuType(localResType)
1051+
// Make sure we affect the info of the anonfun by the previous updNuType
1052+
// unless the info is already defined in a previous phase and does not change.
1053+
assert(!anonfun.isCompleted || anonfun.denot.validFor.firstPhaseId != thisPhase.id)
1054+
1055+
pt match
10491056
case RefinedType(_, _, mt: MethodType) =>
1050-
inContext(ctx.withOwner(anonfun)):
1051-
Internalize(mt)(resType)
1052-
case _ => resType
1053-
mdef.tpt.updNuType(localResType)
1054-
// Make sure we affect the info of the anonfun by the previous updNuType
1055-
// unless the info is already defined in a previous phase and does not change.
1056-
assert(!anonfun.isCompleted || anonfun.denot.validFor.firstPhaseId != thisPhase.id)
1057-
//println(i"updating ${mdef.tpt} to $localResType/${mdef.tpt.nuType}")
1057+
if !mt.isResultDependent then
1058+
// If mt is result dependent we could compensate this by
1059+
// internalizing `resType.substParams(mt, params.tpes)`.
1060+
// But this tends to give worse error messages, so we refrain
1061+
// from doing that and don't update the local result type instead.
1062+
val localResType = inContext(ctx.withOwner(anonfun)):
1063+
Internalize(mt)(resType)
1064+
updateTpt(localResType)
1065+
case _ =>
1066+
updateTpt(resType)
10581067
case _ =>
10591068
case Nil =>
1069+
end matchParamsAndResult
10601070

10611071
openClosures = (anonfun, pt) :: openClosures
10621072
// openClosures is needed for errors but currently makes no difference

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/filevar.scala:15:12 --------------------------------------
22
15 | withFile: f => // error with level checking, was OK under both schemes before
33
| ^
4-
|Found: (f: File^'s1) ->'s2 Unit
5-
|Required: (f: File^{l}) => Unit
4+
|Found: (l: scala.caps.Capability^) ?->'s1 File^'s2 ->'s3 Unit
5+
|Required: (l: scala.caps.Capability^) ?-> (f: File^{l}) => Unit
66
|
7-
|Note that capability l cannot be included in outer capture set 's1 of parameter f.
7+
|Note that capability l cannot be included in outer capture set 's4 of parameter f.
88
|
9-
|where: => refers to a fresh root capability created in anonymous function of type (using l: scala.caps.Capability): File^{l} -> Unit when instantiating expected result type (f: File^{l}) ->{cap} Unit of function literal
9+
|where: => refers to a root capability associated with the result type of (using l: scala.caps.Capability^): (f: File^{l}) => Unit
10+
| ^ refers to the universal root capability
1011
16 | val o = Service()
1112
17 | o.file = f
1213
18 | o.log
Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,13 @@
1-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15923.scala:27:23 ---------------------------------------
2-
27 | val leak = withCap(cap => mkId(cap)) // error (was: no error here since type aliases don't box)
3-
| ^^^^^^^^^^^^^^^^
4-
|Found: (cap: test2.Cap^'s1) ->'s2 [T] => (op: test2.Cap^'s3 ->'s4 T) ->'s5 T
5-
|Required: test2.Cap^{lcap} => [T] => (op: test2.Cap^'s6 ->'s7 T) ->'s8 T
6-
|
7-
|Note that capability lcap cannot be included in outer capture set 's1 of parameter cap.
8-
|
9-
|where: => refers to a fresh root capability created in anonymous function of type (using lcap: scala.caps.Capability): test2.Cap^{lcap} -> [T] => (op: test2.Cap^{lcap} => T) -> T when instantiating expected result type test2.Cap^{lcap} ->{cap²} [T] => (op: test2.Cap^'s6 ->'s7 T) ->'s8 T of function literal
10-
|
11-
| longer explanation available when compiling with `-explain`
121
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15923.scala:12:21 ---------------------------------------
132
12 | val leak = withCap(cap => mkId(cap)) // error
143
| ^^^^^^^^^^^^^^^^
15-
|Found: (cap: Cap^'s9) ->'s10 Id[Cap^'s11]^'s12
16-
|Required: Cap^{lcap} => Id[Cap^'s13]^'s14
4+
|Found: (lcap: scala.caps.Capability^) ?->'s1 Cap^'s2 ->'s3 Id[Cap^'s4]^'s5
5+
|Required: (lcap: scala.caps.Capability^) ?-> Cap^{lcap} => Id[Cap^'s6]^'s7
176
|
18-
|Note that capability lcap cannot be included in outer capture set 's9 of parameter cap.
7+
|Note that capability cap cannot be included in outer capture set 's6.
198
|
20-
|where: => refers to a fresh root capability created in anonymous function of type (using lcap: scala.caps.Capability): Cap^{lcap} -> Id[Cap] when instantiating expected result type Cap^{lcap} ->{cap²} Id[Cap^'s13]^'s14 of function literal
9+
|where: => refers to a root capability associated with the result type of (using lcap: scala.caps.Capability^): Cap^{lcap} => Id[Cap^'s6]^'s7
10+
| ^ refers to the universal root capability
11+
| cap is a root capability associated with the result type of (x$0: Cap^'s2): Id[Cap^'s4]^'s5
2112
|
2213
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/i15923.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ object test2:
2424
result
2525
}
2626

27-
val leak = withCap(cap => mkId(cap)) // error (was: no error here since type aliases don't box)
27+
val leak = withCap(cap => mkId(cap)) // no error here since type aliases don't box
2828
leak { cap => cap.use() }
2929
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def test1(): Unit =
2+
val t1: (x: () => Unit) -> (y: () ->{x} Unit) -> Unit =
3+
x => y => () // should ok, but error

0 commit comments

Comments
 (0)