From 885f70a02ee002c6b4d0fded9a6a0a10cc268e26 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 29 Apr 2025 10:50:00 +0200 Subject: [PATCH 1/2] Fix hasCap test to establish PrintFresh criterion --- compiler/src/dotty/tools/dotc/cc/root.scala | 7 +++++-- tests/neg-custom-args/captures/capt-depfun.check | 2 +- .../captures/cc-existential-conformance.check | 8 ++++---- tests/neg-custom-args/captures/i19330.check | 2 +- tests/neg-custom-args/captures/i21614.check | 4 ++-- tests/neg-custom-args/captures/i23027.scala | 12 ++++++++++++ 6 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 tests/neg-custom-args/captures/i23027.scala diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index ee668efa8378..472dd37aa543 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -340,6 +340,7 @@ object root: x || t.dealiasKeepAnnots.match case Fresh(_) => false case t: TermRef => t.isCap || this(x, t.widen) + case CapturingType(t1, refs) => refs.containsCap || this(x, t1) case x: ThisType => false case _ => foldOver(x, t) @@ -349,7 +350,9 @@ object root: case refs: CaptureSet => refs.elems.exists(_.stripReadOnly.isCap) - if refs.exists(containsCap) then ctx.withProperty(PrintFresh, Some(())) - else ctx + if refs.exists(containsCap) then + ctx.withProperty(PrintFresh, Some(())) + else + ctx end printContext end root \ No newline at end of file diff --git a/tests/neg-custom-args/captures/capt-depfun.check b/tests/neg-custom-args/captures/capt-depfun.check index 7cd838d72dc0..d7ad819fb423 100644 --- a/tests/neg-custom-args/captures/capt-depfun.check +++ b/tests/neg-custom-args/captures/capt-depfun.check @@ -2,7 +2,7 @@ 11 | val dc: ((Str^{y, z}) => Str^{y, z}) = ac(g()) // error // error: separatioon | ^^^^^^^ | Found: Str^{} ->{ac, y, z} Str^{y, z} - | Required: Str^{y, z} ->{fresh} Str^{y, z} + | Required: Str^{y, z} => Str^{y, z} | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/capt-depfun.scala:11:24 ------------------------------------------------------- diff --git a/tests/neg-custom-args/captures/cc-existential-conformance.check b/tests/neg-custom-args/captures/cc-existential-conformance.check index bc94b97fc327..65367fad1df1 100644 --- a/tests/neg-custom-args/captures/cc-existential-conformance.check +++ b/tests/neg-custom-args/captures/cc-existential-conformance.check @@ -1,8 +1,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-existential-conformance.scala:8:24 -------------------- 8 | val y: A -> Fun[B^] = x // error | ^ - | Found: (x : A -> (x²: A) -> B^{localcap}) - | Required: A -> A -> B^{fresh} + | Found: (x : A -> (x²: A) -> B^) + | Required: A -> A -> B^ | | where: x is a value in method test | x² is a reference to a value parameter @@ -21,8 +21,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-existential-conformance.scala:13:19 ------------------- 13 | val y: Fun[B^] = x // error | ^ - | Found: (x : (x²: A) -> B^{localcap}) - | Required: A -> B^{fresh} + | Found: (x : (x²: A) -> B^) + | Required: A -> B^ | | where: x is a value in method test2 | x² is a reference to a value parameter diff --git a/tests/neg-custom-args/captures/i19330.check b/tests/neg-custom-args/captures/i19330.check index 06a074c40a10..91ba7f234845 100644 --- a/tests/neg-custom-args/captures/i19330.check +++ b/tests/neg-custom-args/captures/i19330.check @@ -14,7 +14,7 @@ 22 | val bad: bar.T = foo(bar) // error | ^^^^^^^^ | Found: bar.T - | Required: () => Logger^ + | Required: () ->{fresh} Logger^{fresh} | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/i19330.scala:16:14 ------------------------------------------------------------ diff --git a/tests/neg-custom-args/captures/i21614.check b/tests/neg-custom-args/captures/i21614.check index 6f1a2a4e23fa..e8d4c9f03102 100644 --- a/tests/neg-custom-args/captures/i21614.check +++ b/tests/neg-custom-args/captures/i21614.check @@ -8,8 +8,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21614.scala:15:12 --------------------------------------- 15 | files.map(new Logger(_)) // error, Q: can we improve the error message? | ^^^^^^^^^^^^^ - | Found: (_$1: box File^{files*}) ->{files*} box Logger{val f: File^{_$1}}^{localcap.rd, _$1} - | Required: (_$1: box File^{files*}) ->{fresh} box Logger{val f: File^?}^? + | Found: (_$1: box File^{files*}) ->{files*} box Logger{val f: File^{_$1}}^{cap.rd, _$1} + | Required: (_$1: box File^{files*}) => box Logger{val f: File^?}^? | | Note that reference .rd | cannot be included in outer capture set ? diff --git a/tests/neg-custom-args/captures/i23027.scala b/tests/neg-custom-args/captures/i23027.scala new file mode 100644 index 000000000000..b7fc06d2bf27 --- /dev/null +++ b/tests/neg-custom-args/captures/i23027.scala @@ -0,0 +1,12 @@ +import language.experimental.captureChecking + +trait Test { + def foo(x: Test): Test = + Test.bar + ??? +} + +object Test { + val _bar: Any => Any = identity + def bar[T] = _bar.asInstanceOf[T => T] // error +} From 627b6e5b0dea21008f6a9aee5e865d2a5a6f4fce Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 29 Apr 2025 10:56:11 +0200 Subject: [PATCH 2/2] Loosen the "dxoes not match previously inferred" criterion The expected type here is still the one using cap, not fresh. So it's OK if the actual type uses fresh where the expected uses cap. --- compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala | 9 ++++++--- .../captures/i23027.scala | 0 2 files changed, 6 insertions(+), 3 deletions(-) rename tests/{neg-custom-args => pos-custom-args}/captures/i23027.scala (100%) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index a4551fa2b86c..dec1a381e94c 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -1076,9 +1076,12 @@ class CheckCaptures extends Recheck, SymTransformer: tree.tpt match case tpt: InferredTypeTree if !canUseInferred => val expected = tpt.tpe.dropAllRetains - todoAtPostCheck += (() => checkConformsExpr(tp, expected, tree.rhs, addenda(expected))) - // The check that inferred <: expected is done after recheck so that it - // does not interfere with normal rechecking by constraining capture set variables. + todoAtPostCheck += { () => + withCapAsRoot: + checkConformsExpr(tp, expected, tree.rhs, addenda(expected)) + // The check that inferred <: expected is done after recheck so that it + // does not interfere with normal rechecking by constraining capture set variables. + } case _ => tp end checkInferredResult diff --git a/tests/neg-custom-args/captures/i23027.scala b/tests/pos-custom-args/captures/i23027.scala similarity index 100% rename from tests/neg-custom-args/captures/i23027.scala rename to tests/pos-custom-args/captures/i23027.scala