From 842240f358e30bb88cd821ce12fbf913f734b3d5 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 24 Oct 2024 23:41:22 +0200 Subject: [PATCH] More tests --- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 4 ++++ .../captures/delayedRunops1.scala | 19 +++++++++++++++++ .../captures/delayedRunops5.scala | 21 +++++++++++++++++++ .../captures/delayedRunops6.scala | 17 +++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 tests/neg-custom-args/captures/delayedRunops1.scala create mode 100644 tests/neg-custom-args/captures/delayedRunops5.scala create mode 100644 tests/neg-custom-args/captures/delayedRunops6.scala diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 51d1328d2ad6..f04c5ce18e41 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -821,6 +821,10 @@ object CaptureSet: // If this is the union of a constant and a variable, // propagate `elem` to the variable part to avoid slack // between the operands and the union. + // TODO: This means there's no back-propagation to the operands + // of a union of two variables. That could be a source of unsoundness. + // Altermative would be to be conservatibe and back-propagate to one + // of the operands arbitrarily or even to both of them. if res.isOK && (origin ne cs1) && (origin ne cs2) then if cs1.isConst then cs2.tryInclude(elem, origin) else if cs2.isConst then cs1.tryInclude(elem, origin) diff --git a/tests/neg-custom-args/captures/delayedRunops1.scala b/tests/neg-custom-args/captures/delayedRunops1.scala new file mode 100644 index 000000000000..17b34e1d01eb --- /dev/null +++ b/tests/neg-custom-args/captures/delayedRunops1.scala @@ -0,0 +1,19 @@ +import language.experimental.captureChecking + +def runOps(ops: List[() => Unit]): () ->{ops*} Unit = + () => ops.foreach(op => op()) + +def app[T, U](x: T, op: T => U): () ->{op} U = + () => op(x) + +def unsafeRunOps2(ops2: List[() => Unit]): () -> () -> Unit = + val x = app[List[() ->{ops2*} Unit], () ->{ops2*} Unit](ops2, runOps) // error + x + +def unsafeRunOps3(ops2: List[() => Unit]): () -> () -> Unit = + val x = app(ops2, runOps) // error + x + + + + diff --git a/tests/neg-custom-args/captures/delayedRunops5.scala b/tests/neg-custom-args/captures/delayedRunops5.scala new file mode 100644 index 000000000000..3b100ed8f179 --- /dev/null +++ b/tests/neg-custom-args/captures/delayedRunops5.scala @@ -0,0 +1,21 @@ +import language.experimental.captureChecking + +def runOps(ops: List[() => Unit]): Unit = + ops.foreach(op => op()) + +def app[T, U](x: T, op: T => U): () ->{op} U = + () => op(x) + +def test(c: Object^) = + + def unsafeRunOps1(ops: List[() ->{c} Unit]): () -> Unit = + app[List[() ->{c} Unit], Unit](ops, runOps) // !!! ok, but should be error + + def unsafeRunOps2(ops: List[() ->{c} Unit]): () -> Unit = + app(ops, runOps) // error + + () + + + + diff --git a/tests/neg-custom-args/captures/delayedRunops6.scala b/tests/neg-custom-args/captures/delayedRunops6.scala new file mode 100644 index 000000000000..ab9ac43a8835 --- /dev/null +++ b/tests/neg-custom-args/captures/delayedRunops6.scala @@ -0,0 +1,17 @@ +import language.experimental.captureChecking + +val runOps: [C^] -> () -> (ops: List[() ->{C^} Unit]) ->{C^} Unit = ??? + +def app[T, U](x: T, op: T => U): () ->{op} U = + () => op(x) + +def unsafeRunOps(ops: List[() => Unit]): () ->{} Unit = + app[List[() ->{ops*} Unit], Unit](ops, runOps()) // error + +def unsafeRunOps2(ops: List[() => Unit]): () ->{} Unit = + app(ops, runOps()) // error + +def test(c: Object^) = + def f = (ops: List[() ->{c} Unit]) => ops.foreach(_()) + val _: List[() ->{c} Unit] ->{c} Unit = f + ()