Skip to content

Commit 2043885

Browse files
committed
Better error messages in healTypeParam
Also: better parameter names for synthesized methods
1 parent 20a9b96 commit 2043885

File tree

9 files changed

+48
-62
lines changed

9 files changed

+48
-62
lines changed

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package cc
44

55
import core.*
66
import Types.*, Symbols.*, Contexts.*, Annotations.*, Flags.*
7+
import Names.TermName
78
import ast.{tpd, untpd}
89
import Decorators.*, NameOps.*
910
import config.SourceVersion
@@ -45,9 +46,12 @@ def isCaptureCheckingOrSetup(using Context): Boolean =
4546
/** A dependent function type with given arguments and result type
4647
* TODO Move somewhere else where we treat all function type related ops together.
4748
*/
48-
def depFun(args: List[Type], resultType: Type, isContextual: Boolean)(using Context): Type =
49-
MethodType.companion(isContextual = isContextual)(args, resultType)
50-
.toFunctionType(alwaysDependent = true)
49+
def depFun(args: List[Type], resultType: Type, isContextual: Boolean, paramNames: List[TermName] = Nil)(using Context): Type =
50+
val make = MethodType.companion(isContextual = isContextual)
51+
val mt =
52+
if paramNames.length == args.length then make(paramNames, args, resultType)
53+
else make(args, resultType)
54+
mt.toFunctionType(alwaysDependent = true)
5155

5256
/** An exception thrown if a @retains argument is not syntactically a CaptureRef */
5357
class IllegalCaptureRef(tpe: Type) extends Exception(tpe.toString)

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

+18-12
Original file line numberDiff line numberDiff line change
@@ -867,11 +867,13 @@ class CheckCaptures extends Recheck, SymTransformer:
867867
if eparent1 eq eparent then expected
868868
else CapturingType(eparent1, refs, boxed = expected0.isBoxed)
869869
case expected @ defn.FunctionOf(args, resultType, isContextual)
870-
if defn.isNonRefinedFunction(expected) && defn.isFunctionNType(actual) && !defn.isNonRefinedFunction(actual) =>
871-
val expected1 = depFun(args, resultType, isContextual)
872-
expected1
873-
case _ =>
874-
expected
870+
if defn.isNonRefinedFunction(expected) =>
871+
actual match
872+
case RefinedType(parent, nme.apply, rinfo: MethodType)
873+
if defn.isFunctionNType(actual) =>
874+
depFun(args, resultType, isContextual, rinfo.paramNames)
875+
case _ => expected
876+
case _ => expected
875877
recur(expected)
876878

877879
/** For the expected type, implement the rule outlined in #14390:
@@ -1239,7 +1241,7 @@ class CheckCaptures extends Recheck, SymTransformer:
12391241
* compensate this by pushing the widened capture set of `f` into ?1.
12401242
* This solves the soundness issue caused by the ill-formness of ?1.
12411243
*/
1242-
private def healTypeParam(tree: Tree)(using Context): Unit =
1244+
private def healTypeParam(tree: Tree, paramName: TypeName, meth: Symbol)(using Context): Unit =
12431245
val checker = new TypeTraverser:
12441246
private var allowed: SimpleIdentitySet[TermParamRef] = SimpleIdentitySet.empty
12451247

@@ -1261,8 +1263,14 @@ class CheckCaptures extends Recheck, SymTransformer:
12611263
val widened = ref.captureSetOfInfo
12621264
val added = widened.filter(isAllowed(_))
12631265
capt.println(i"heal $ref in $cs by widening to $added")
1264-
checkSubset(added, cs, tree.srcPos)
1265-
widened.elems.foreach(recur)
1266+
if !added.subCaptures(cs, frozen = false).isOK then
1267+
val location = if meth.exists then i" of $meth" else ""
1268+
val debugSetInfo = if ctx.settings.YccDebug.value then i" $cs" else ""
1269+
report.error(
1270+
i"local reference ${ref.paramName} leaks into outer capture set$debugSetInfo of type parameter $paramName$location",
1271+
tree.srcPos)
1272+
else
1273+
widened.elems.foreach(recur)
12661274
case _ =>
12671275
recur(elem)
12681276

@@ -1303,14 +1311,12 @@ class CheckCaptures extends Recheck, SymTransformer:
13031311
case t @ TypeApply(fun, args) =>
13041312
fun.knownType.widen match
13051313
case tl: PolyType =>
1306-
val normArgs = args.lazyZip(tl.paramInfos).map { (arg, bounds) =>
1314+
val normArgs = args.lazyZip(tl.paramInfos).map: (arg, bounds) =>
13071315
arg.withType(arg.knownType.forceBoxStatus(
13081316
bounds.hi.isBoxedCapturing | bounds.lo.isBoxedCapturing))
1309-
}
13101317
checkBounds(normArgs, tl)
1318+
args.lazyZip(tl.paramNames).foreach(healTypeParam(_, _, fun.symbol))
13111319
case _ =>
1312-
1313-
args.foreach(healTypeParam(_))
13141320
case _ =>
13151321
end check
13161322
end checker

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
27 | val boxed2 : Observe[C^] = box2(c) // error
2020
| ^^^^^^^
2121
| Found: (C{val arg: C^}^{c} => Unit) ->{c} Unit
22-
| Required: Observe[C^]
22+
| Required: (C^ => Unit) -> Unit
2323
|
2424
| longer explanation available when compiling with `-explain`
2525
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:33 ---------------------------------------
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Error: tests/neg-custom-args/captures/simple-using.scala:8:2 --------------------------------------------------------
2+
8 | usingLogFile { f => () => f.write(2) } // error
3+
| ^^^^^^^^^^^^
4+
| local reference f leaks into outer capture set of type parameter T of method usingLogFile
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import java.io.*
2+
def usingLogFile[sealed T](op: FileOutputStream^ => T): T =
3+
val logFile = FileOutputStream("log")
4+
val result = op(logFile)
5+
logFile.close()
6+
result
7+
def test() =
8+
usingLogFile { f => () => f.write(2) } // error

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

+4-10
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,16 @@
1818
51 | 22
1919
52 |} { // error
2020
| ^
21-
| Found: () ->{x$0, x$0²} Int
21+
| Found: () ->{x, x²} Int
2222
| Required: () -> Int
2323
|
24-
| where: x$0 is a reference to a value parameter
25-
| x$0² is a reference to a value parameter
24+
| where: x is a reference to a value parameter
25+
| x² is a reference to a value parameter
2626
53 | (ex: Exception) => () => 22
2727
54 |}
2828
|
2929
| longer explanation available when compiling with `-explain`
3030
-- Error: tests/neg-custom-args/captures/try.scala:35:11 ---------------------------------------------------------------
3131
35 | val xx = handle { // error
3232
| ^^^^^^
33-
| reference (caps.cap : caps.Cap) is not included in the allowed capture set {x$0, x$0²}
34-
|
35-
| Note that the universal capability `cap`
36-
| cannot be included in capture set {x$0, x$0}
37-
|
38-
| where: x$0 is a reference to a value parameter
39-
| x$0² is a reference to a value parameter
33+
| local reference x leaks into outer capture set of type parameter R of method handle

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

+4-28
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,16 @@
1313
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:23:14 ------------------------------------------------------
1414
23 | val later = usingLogFile { f => () => f.write(0) } // error
1515
| ^^^^^^^^^^^^
16-
| reference (caps.cap : caps.Cap) is not included in the allowed capture set {x$0, x$0²}
17-
|
18-
| Note that the universal capability `cap`
19-
| cannot be included in capture set {x$0, x$0}
20-
|
21-
| where: x$0 is a reference to a value parameter
22-
| x$0² is a reference to a value parameter
16+
| local reference f leaks into outer capture set of type parameter T of method usingLogFile
2317
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:28:23 ------------------------------------------------------
2418
28 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error
2519
| ^^^^^^^^^^^^
26-
| reference (caps.cap : caps.Cap) is not included in the allowed capture set {x$0, x$0²}
27-
|
28-
| Note that the universal capability `cap`
29-
| cannot be included in capture set {x$0, x$0}
30-
|
31-
| where: x$0 is a reference to a value parameter
32-
| x$0² is a reference to a value parameter
20+
| local reference f leaks into outer capture set of type parameter T of method usingLogFile
3321
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:52:16 ------------------------------------------------------
3422
52 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error
3523
| ^^^^^^^^^
36-
| reference (caps.cap : caps.Cap) is not included in the allowed capture set {x$0, x$0²}
37-
|
38-
| Note that the universal capability `cap`
39-
| cannot be included in capture set {x$0, x$0}
40-
|
41-
| where: x$0 is a reference to a value parameter
42-
| x$0² is a reference to a value parameter
24+
| local reference f leaks into outer capture set of type parameter T of method usingFile
4325
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:60:16 ------------------------------------------------------
4426
60 | val later = usingFile("logfile", // error !!! but should be ok, since we can widen `l` to `file` instead of to `cap`
4527
| ^^^^^^^^^
46-
| reference (_$1 : java.io.OutputStream^) is not included in the allowed capture set {x$0, x$0²}
47-
|
48-
| Note that reference (_$1 : java.io.OutputStream^), defined in method $anonfun
49-
| cannot be included in outer capture set {x$0, x$0} which is associated with method test
50-
|
51-
| where: x$0 is a reference to a value parameter
52-
| x$0² is a reference to a value parameter
28+
| local reference l leaks into outer capture set of type parameter T of method usingFile

tests/neg-custom-args/captures/vars-simple.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
16 | a = g // error
1010
| ^
1111
| Found: (x: String) ->{cap3} String
12-
| Required: (x$0: String) ->{cap[test]} String
12+
| Required: (x: String) ->{cap[test]} String
1313
|
1414
| longer explanation available when compiling with `-explain`
1515
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars-simple.scala:17:12 ----------------------------------

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

+1-7
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,4 @@
2525
-- Error: tests/neg-custom-args/captures/vars.scala:34:2 ---------------------------------------------------------------
2626
34 | local { cap3 => // error
2727
| ^^^^^
28-
| reference (caps.cap : caps.Cap) is not included in the allowed capture set {x$0, x$0²}
29-
|
30-
| Note that the universal capability `cap`
31-
| cannot be included in capture set {x$0, x$0}
32-
|
33-
| where: x$0 is a reference to a value parameter
34-
| x$0² is a reference to a value parameter
28+
| local reference cap3 leaks into outer capture set of type parameter T of method local

0 commit comments

Comments
 (0)