Skip to content

Commit 3bda057

Browse files
committed
New escape hatch: discardUses
1 parent 4689288 commit 3bda057

File tree

5 files changed

+24
-1
lines changed

5 files changed

+24
-1
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ class CCState:
8787

8888
private var collapseFresh: Boolean = false
8989

90+
private var discardUses: Boolean = false
91+
9092
object CCState:
9193

9294
/** If we are currently in capture checking or setup, and `mt` is a method
@@ -137,4 +139,16 @@ object CCState:
137139
/** Should all FreshCap instances be treated as equal to GlobalCap? */
138140
def collapseFresh(using Context): Boolean = ccState.collapseFresh
139141

142+
/** Run `op` but suppress all recording of uses in `markFree` */
143+
inline def withDiscardedUses[T](op: => T)(using Context): T =
144+
if isCaptureCheckingOrSetup then
145+
val ccs = ccState
146+
val saved = ccs.discardUses
147+
ccs.discardUses = true
148+
try op finally ccs.discardUses = saved
149+
else op
150+
151+
/** Should uses not be recorded in markFree? */
152+
def discardUses(using Context): Boolean = ccState.discardUses
153+
140154
end CCState

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ class CheckCaptures extends Recheck, SymTransformer:
574574
// Under deferredReaches, don't propagate out of methods inside terms.
575575
// The use set of these methods will be charged when that method is called.
576576

577-
if !cs.isAlwaysEmpty then
577+
if !cs.isAlwaysEmpty && !CCState.discardUses then
578578
recur(cs, curEnv, null)
579579
if addUseInfo then useInfos += ((tree, cs, curEnv))
580580
end markFree
@@ -783,6 +783,9 @@ class CheckCaptures extends Recheck, SymTransformer:
783783
else argType0.widen.stripCapturing
784784
capt.println(i"rechecking unsafeAssumePure of $arg with $pt: $argType")
785785
super.recheckFinish(argType, tree, pt)
786+
else if meth == defn.Caps_unsafeDiscardUses then
787+
val arg :: Nil = tree.args: @unchecked
788+
withDiscardedUses(recheck(arg, pt))
786789
else
787790
val res = super.recheckApply(tree, pt)
788791
includeCallCaptures(meth, res, tree)

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,7 @@ class Definitions {
10261026
@tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe")
10271027
@tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure")
10281028
@tu lazy val Caps_unsafeAssumeSeparate: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumeSeparate")
1029+
@tu lazy val Caps_unsafeDiscardUses: Symbol = CapsUnsafeModule.requiredMethod("unsafeDiscardUses")
10291030
@tu lazy val Caps_unsafeErasedValue: Symbol = CapsUnsafeModule.requiredMethod("unsafeErasedValue")
10301031
@tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains")
10311032
@tu lazy val Caps_ContainsModule: Symbol = requiredModule("scala.caps.Contains")

library/src/scala/caps/package.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ object unsafe:
208208
*/
209209
def unsafeAssumeSeparate(op: Any): op.type = op
210210

211+
/** A wrapper around code for which uses go unrecorded */
212+
def unsafeDiscardUses(op: Any): op.type = op
213+
211214
/** An unsafe variant of erasedValue that can be used as an escape hatch. Unlike the
212215
* user-accessible compiletime.erasedValue, this version is assumed
213216
* to be a pure expression, hence capability safe. But there is no proof
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test(c: Object^) =
2+
val x: () -> Unit = caps.unsafe.unsafeDiscardUses(() => println(c))

0 commit comments

Comments
 (0)