From b33b097d52e6b7da74bcd5bb81f05aed5a1d6ff6 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 1 Jan 2025 11:27:34 +0100 Subject: [PATCH] Revise handing of @uncheckedAnnotation The previous meaning did not do enough in the presence of fresh. The new meaning is that all universal capture sets under a @uncheckedCaptures become capture sets. --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 20 +++++++++++++++++-- .../captures/explain-under-approx.check | 17 ---------------- tests/pos-custom-args/captures/foreach2.scala | 7 +++++++ 3 files changed, 25 insertions(+), 19 deletions(-) delete mode 100644 tests/neg-custom-args/captures/explain-under-approx.check create mode 100644 tests/pos-custom-args/captures/foreach2.scala diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index ac720b7863dc..ede036e6a875 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -356,6 +356,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: catch case ex: IllegalCaptureRef => report.error(em"Illegal capture reference: ${ex.getMessage.nn}", tptToCheck.srcPos) parent2 + else if ann.symbol == defn.UncheckedCapturesAnnot then + makeUnchecked(apply(parent)) else t.derivedAnnotatedType(parent1, ann) case throwsAlias(res, exc) => @@ -439,7 +441,9 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: private val paramSigChange = util.EqHashSet[Tree]() - /** Transform type of tree, and remember the transformed type as the type the tree */ + /** Transform type of tree, and remember the transformed type as the type the tree + * @pre !(boxed && sym.exists) + */ private def transformTT(tree: TypeTree, sym: Symbol, boxed: Boolean)(using Context): Unit = if !tree.hasNuType then var transformed = @@ -450,7 +454,9 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: if sym.is(Param) && (transformed ne tree.tpe) then paramSigChange += tree tree.setNuType( - if boxed then transformed else Fresh.fromCap(transformed, sym)) + if boxed then transformed + else if sym.hasAnnotation(defn.UncheckedCapturesAnnot) then makeUnchecked(transformed) + else Fresh.fromCap(transformed, sym)) /** Transform the type of a val or var or the result type of a def */ def transformResultType(tpt: TypeTree, sym: Symbol)(using Context): Unit = @@ -818,6 +824,16 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: if variance > 0 then t1 else decorate(t1, Function.const(CaptureSet.Fluid)) + /** Replace all universal capture sets in this type by */ + private def makeUnchecked(using Context): TypeMap = new TypeMap with FollowAliasesMap: + def apply(t: Type) = t match + case t @ CapturingType(parent, refs) => + val parent1 = this(parent) + if refs.isUniversal then t.derivedCapturingType(parent1, CaptureSet.Fluid) + else t + case Existential(_) => t + case _ => mapFollowingAliases(t) + /** Pull out an embedded capture set from a part of `tp` */ def normalizeCaptures(tp: Type)(using Context): Type = tp match case tp @ RefinedType(parent @ CapturingType(parent1, refs), rname, rinfo) => diff --git a/tests/neg-custom-args/captures/explain-under-approx.check b/tests/neg-custom-args/captures/explain-under-approx.check deleted file mode 100644 index d4cafc7840b7..000000000000 --- a/tests/neg-custom-args/captures/explain-under-approx.check +++ /dev/null @@ -1,17 +0,0 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/explain-under-approx.scala:12:10 ------------------------- -12 | col.add(Future(() => 25)) // error - | ^^^^^^^^^^^^^^^^ - | Found: Future[Int]{val a: (async : Async)}^{async} - | Required: Future[Int]^{col.futs*} - | - | Note that reference ex$22.type - | cannot be included in outer capture set {cap} - | - | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/explain-under-approx.scala:15:11 ------------------------- -15 | col1.add(Future(() => 25)) // error - | ^^^^^^^^^^^^^^^^ - | Found: Future[Int]{val a: (async : Async)}^{async} - | Required: Future[Int]^{col1.futs*} - | - | longer explanation available when compiling with `-explain` diff --git a/tests/pos-custom-args/captures/foreach2.scala b/tests/pos-custom-args/captures/foreach2.scala new file mode 100644 index 000000000000..318bcb9cddfc --- /dev/null +++ b/tests/pos-custom-args/captures/foreach2.scala @@ -0,0 +1,7 @@ +import annotation.unchecked.uncheckedCaptures + +class ArrayBuffer[T]: + def foreach(op: T => Unit): Unit = ??? +def test = + val tasks = new ArrayBuffer[(() => Unit) @uncheckedCaptures] + val _: Unit = tasks.foreach(((task: () => Unit) => task()))