Skip to content

Commit ee22f54

Browse files
committed
Don't internalize if result type depends on lambda params
This tends to give better error messages.
1 parent 9253f3b commit ee22f54

File tree

4 files changed

+31
-38
lines changed

4 files changed

+31
-38
lines changed

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

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

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

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +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 is not included in capture set {cap}.
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
10-
| cap is a fresh root capability in the type of variable file
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
1111
16 | val o = Service()
1212
17 | o.file = f
1313
18 | o.log
Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +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: (lcap: scala.caps.Capability^) ?->'s1 test2.Cap^{lcap} => [T] => (op: test2.Cap^{lcap} ->'s2 T) ->'s3 T
5-
|Required: (lcap: scala.caps.Capability^) ?-> test2.Cap^{lcap} =>² [T] => (op: test2.Cap^{lcap²} ->'s2 T) ->'s3 T
6-
|
7-
|Note that capability lcap cannot be included in outer capture set {lcap²}.
8-
|
9-
|where: => refers to a root capability associated with the result type of (using lcap: scala.caps.Capability^): test2.Cap^{lcap} => [T] => (op: test2.Cap^{lcap} ->'s2 T) ->'s3 T
10-
| =>² refers to a root capability associated with the result type of (using lcap: scala.caps.Capability^): test2.Cap^{lcap} =>² [T] => (op: test2.Cap^{lcap²} ->'s2 T) ->'s3 T
11-
| ^ refers to the universal root capability
12-
| lcap is a reference to a value parameter
13-
| lcap² is a parameter in an anonymous function in method bar
14-
|
15-
| longer explanation available when compiling with `-explain`
161
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15923.scala:12:21 ---------------------------------------
172
12 | val leak = withCap(cap => mkId(cap)) // error
183
| ^^^^^^^^^^^^^^^^
19-
|Found: (lcap: scala.caps.Capability^) ?->'s4 Cap^{lcap} => Id[Cap^{lcap}]^'s5
20-
|Required: (lcap: scala.caps.Capability^) ?-> Cap^{lcap} =>² Id[Cap^{lcap²}]^'s5
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
216
|
22-
|Note that capability lcap² is not included in capture set {lcap}.
7+
|Note that capability cap cannot be included in outer capture set 's6.
238
|
24-
|where: => refers to a root capability associated with the result type of (using lcap: scala.caps.Capability^): Cap^{lcap} => Id[Cap^{lcap}]^'s5
25-
| =>² refers to a root capability associated with the result type of (using lcap: scala.caps.Capability^): Cap^{lcap} =>² Id[Cap^{lcap²}]^'s5
26-
| ^ refers to the universal root capability
27-
| lcap is a reference to a value parameter
28-
| lcap² is a parameter in an anonymous function in method bar
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
2912
|
3013
| 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
}

0 commit comments

Comments
 (0)