Skip to content

[do not merge] Add capture checking annotation to Scala 3 Standard Library #23688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
03af9f5
Add capture checking to boundary and NonLocalReturns
natsukagami Apr 1, 2025
4acac00
Add capture-checking test for boundary
natsukagami Mar 11, 2025
a4eb3fa
Patch DropBreaks to correctly detect boundary blocks
natsukagami Mar 12, 2025
f1f099d
Add capture checking to CommandLineParser
natsukagami Mar 31, 2025
c0c79fe
Add capture checking to scala.util
natsukagami Mar 31, 2025
418f1fa
Add capture checking to scala.runtime
natsukagami Mar 31, 2025
81e68a8
Add capture checking to rest of scala.runtime (except TupledFunctions)
natsukagami Apr 1, 2025
4765107
Add capture checking to IArray
natsukagami Aug 7, 2025
fd07f14
[TODO INVESTIGATE] IArray: `Array[AnyRef^{xs*}]` not working, revert …
natsukagami Aug 5, 2025
f2187b5
Add capture checking to various scala.* base package files
natsukagami Aug 7, 2025
338bee0
[TODO FIX] Temporarily drop upper bound of higher-kinded type `F` in …
natsukagami Apr 7, 2025
0d1b1c9
Add capture checking to scala.reflect
natsukagami Apr 7, 2025
4326889
[TODO REMOVE] Add TODO list
natsukagami Apr 7, 2025
a7e0f59
[TODO EXPLAIN] Add capture checking to Mirror and TupleMirror
natsukagami Apr 8, 2025
e2353b5
Disable CC on Mirror for now, it's interferring with case classes
natsukagami Aug 11, 2025
3406c19
Add capture checking to scala.caps
natsukagami Apr 8, 2025
fd5c2a7
Add capture checking to compiletime.Ops
natsukagami Apr 9, 2025
56d846c
Add capture checking to stdLibPatches
natsukagami Apr 9, 2025
2a849f3
Add capture checking to annotations
natsukagami Apr 10, 2025
6f09030
Attempt to capture-check quotes
natsukagami Apr 10, 2025
aaceb12
Track sun.misc.Unsafe in LazyVal implementation
natsukagami Apr 14, 2025
4ed2444
Require Conversion to be pure
natsukagami Jul 31, 2025
27b3ce7
Remove CC from tuples for now, Map is not compiling
natsukagami Aug 7, 2025
b698ce4
Make sure that DropBreaks catch also constant folded statements
natsukagami Aug 8, 2025
13a2ea4
Update test outputs
natsukagami Aug 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
- [x] library/src/scala/CanEqual.scala
- [x] library/src/scala/CanThrow.scala
- [x] library/src/scala/Conversion.scala
- [x] library/src/scala/IArray.scala
- [x] library/src/scala/NamedTuple.scala
- [x] library/src/scala/PolyFunction.scala
- [x] library/src/scala/Precise.scala
- [x] library/src/scala/Pure.scala
- [x] library/src/scala/Selectable.scala
- [x] library/src/scala/Tuple.scala
- [x] library/src/scala/annotation/MacroAnnotation.scala
- [x] library/src/scala/annotation/RefiningAnnotation.scala
- [x] library/src/scala/annotation/alpha.scala
- [x] library/src/scala/annotation/capability.scala
- [x] library/src/scala/annotation/constructorOnly.scala
- [x] library/src/scala/annotation/experimental.scala
- [x] library/src/scala/annotation/init.scala
- [x] library/src/scala/annotation/internal/$into.scala
- [x] library/src/scala/annotation/internal/Alias.scala
- [x] library/src/scala/annotation/internal/AnnotationDefault.scala
- [x] library/src/scala/annotation/internal/AssignedNonLocally.scala
- [x] library/src/scala/annotation/internal/Body.scala
- [x] library/src/scala/annotation/internal/CaptureChecked.scala
- [x] library/src/scala/annotation/internal/Child.scala
- [x] library/src/scala/annotation/internal/ContextResultCount.scala
- [x] library/src/scala/annotation/internal/ErasedParam.scala
- [x] library/src/scala/annotation/internal/InlineParam.scala
- [x] library/src/scala/annotation/internal/MappedAlternative.scala
- [x] library/src/scala/annotation/internal/ProvisionalSuperClass.scala
- [x] library/src/scala/annotation/internal/Repeated.scala
- [x] library/src/scala/annotation/internal/RuntimeChecked.scala
- [x] library/src/scala/annotation/internal/SourceFile.scala
- [x] library/src/scala/annotation/internal/WithPureFuns.scala
- [x] library/src/scala/annotation/internal/WitnessNames.scala
- [x] library/src/scala/annotation/internal/preview.scala
- [x] library/src/scala/annotation/internal/reachCapability.scala
- [x] library/src/scala/annotation/internal/readOnlyCapability.scala
- [x] library/src/scala/annotation/internal/requiresCapability.scala
- [x] library/src/scala/annotation/internal/sharable.scala
- [x] library/src/scala/annotation/internal/unshared.scala
- [x] library/src/scala/annotation/into.scala
- [x] library/src/scala/annotation/publicInBinary.scala
- [x] library/src/scala/annotation/retains.scala
- [x] library/src/scala/annotation/retainsByName.scala
- [x] library/src/scala/annotation/static.scala
- [x] library/src/scala/annotation/targetName.scala
- [x] library/src/scala/annotation/threadUnsafe.scala
- [x] library/src/scala/annotation/transparentTrait.scala
- [ ] library/src/scala/annotation/unchecked/uncheckedCapabilityLeaks.scala
- [x] library/src/scala/annotation/unchecked/uncheckedCaptures.scala
- [x] library/src/scala/annotation/unroll.scala
- [x] library/src/scala/caps/package.scala
- [x] library/src/scala/compiletime/ops/any.scala
- [x] library/src/scala/compiletime/ops/boolean.scala
- [x] library/src/scala/compiletime/ops/double.scala
- [x] library/src/scala/compiletime/ops/float.scala
- [x] library/src/scala/compiletime/ops/int.scala
- [x] library/src/scala/compiletime/ops/long.scala
- [x] library/src/scala/compiletime/ops/string.scala
- [x] library/src/scala/compiletime/package.scala
- [x] library/src/scala/compiletime/testing/Error.scala
- [x] library/src/scala/compiletime/testing/ErrorKind.scala
- [x] library/src/scala/compiletime/testing/package.scala
- [x] library/src/scala/main.scala
- [x] library/src/scala/quoted/Expr.scala
- [x] library/src/scala/quoted/ExprMap.scala
- [x] library/src/scala/quoted/Exprs.scala
- [x] library/src/scala/quoted/FromExpr.scala
- [x] library/src/scala/quoted/Quotes.scala
- [x] library/src/scala/quoted/ToExpr.scala
- [x] library/src/scala/quoted/Type.scala
- [x] library/src/scala/quoted/Varargs.scala
- [x] library/src/scala/quoted/runtime/Expr.scala
- [x] library/src/scala/quoted/runtime/Patterns.scala
- [x] library/src/scala/quoted/runtime/QuoteMatching.scala
- [x] library/src/scala/quoted/runtime/QuoteUnpickler.scala
- [x] library/src/scala/quoted/runtime/SplicedType.scala
- [x] library/src/scala/quoted/runtime/StopMacroExpansion.scala
- [x] library/src/scala/reflect/Enum.scala
- [x] library/src/scala/reflect/Selectable.scala
- [x] library/src/scala/reflect/TypeTest.scala
- [x] library/src/scala/reflect/Typeable.scala
- [x] library/src/scala/runtime/$throws.scala
- [x] library/src/scala/runtime/Arrays.scala
- [x] library/src/scala/runtime/EnumValue.scala
- [x] library/src/scala/runtime/FunctionXXL.scala
- [x] library/src/scala/runtime/LazyVals.scala
- [x] library/src/scala/runtime/MatchCase.scala
- [x] library/src/scala/runtime/Scala3RunTime.scala
- [x] library/src/scala/runtime/TupleMirror.scala
- [x] library/src/scala/runtime/TupleXXL.scala
- [x] library/src/scala/runtime/TupledFunctions.scala
- [x] library/src/scala/runtime/Tuples.scala
- [x] library/src/scala/runtime/TypeBox.scala
- [x] library/src/scala/runtime/coverage/Invoker.scala
- [x] library/src/scala/runtime/stdLibPatches/Predef.scala
- [x] library/src/scala/runtime/stdLibPatches/language.scala
- [x] library/src/scala/util/CommandLineParser.scala
- [x] library/src/scala/util/FromDigits.scala
- [x] library/src/scala/util/NotGiven.scala
- [x] library/src/scala/util/TupledFunction.scala
- [x] library/src/scala/util/boundary.scala
- [x] library/src/scala/util/control/NonLocalReturns.scala
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/cc/Synthetics.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,4 @@ object Synthetics:
transformCompareCaptures)
end transform

end Synthetics
end Synthetics
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,7 @@ class Definitions {
"reactor.util.annotation.NonNullApi" ::
"io.reactivex.annotations.NonNull" :: Nil)


// convenient one-parameter method types
def methOfAny(tp: Type): MethodType = MethodType(List(AnyType), tp)
def methOfAnyVal(tp: Type): MethodType = MethodType(List(AnyValType), tp)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ object StdNames {
val isEmpty: N = "isEmpty"
val isInstanceOf_ : N = "isInstanceOf"
val isInstanceOfPM: N = "$isInstanceOf$"
val isSameLabelAs : N = "isSameLabelAs"
val java: N = "java"
val key: N = "key"
val label: N = "label"
Expand Down
15 changes: 13 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/DropBreaks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ object DropBreaks:
/** The number of other references to associated label */
var otherRefs: Int = 0

override def toString() = s"LabelUsage($goto, $enclMeth, returnRefs = $returnRefs, otherRefs = $otherRefs)"

private val LabelUsages = new Property.Key[Map[Symbol, LabelUsage]]
private val ShadowedLabels = new Property.Key[Set[Symbol]]

Expand Down Expand Up @@ -63,13 +65,22 @@ class DropBreaks extends MiniPhase:
*/
def unapply(expr: Tree)(using Context): Option[(Symbol, Symbol)] = stripTyped(expr) match
case If(
Apply(Select(Select(ex: Ident, label), eq), (lbl @ Ident(local)) :: Nil),
Apply(Select(ex: Ident, isSameLabelAs), (lbl @ Ident(local)) :: Nil),
Select(ex2: Ident, value),
Apply(throww, (ex3: Ident) :: Nil))
if label == nme.label && eq == nme.eq && local == nme.local && value == nme.value
if isSameLabelAs == nme.isSameLabelAs && local == nme.local && value == nme.value
&& throww.symbol == defn.throwMethod
&& ex.symbol == ex2.symbol && ex.symbol == ex3.symbol =>
Some((ex.symbol, lbl.symbol))
case If(
Apply(Select(ex: Ident, isSameLabelAs), (lbl @ Ident(local)) :: Nil),
Literal(_), // in the case where the value is constant folded
Apply(throww, (ex3: Ident) :: Nil))
if isSameLabelAs == nme.isSameLabelAs && local == nme.local
&& throww.symbol == defn.throwMethod
&& ex.symbol == ex3.symbol
&& expr.tpe.isSingleton =>
Some((ex.symbol, lbl.symbol))
case _ =>
None
end GuardedThrow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import util.Spans.Span
import config.Printers.derive
import NullOpsDecorator.*
import scala.runtime.Statics
import dotty.tools.dotc.config.Feature
import dotty.tools.dotc.cc.CapturingType

object SyntheticMembers {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class LabelBytecodeTests extends DottyBytecodeTest {
"""val local = boundary.Label[Long]()
|try break(5L)(using local)
|catch case ex: boundary.Break[Long] @unchecked =>
| if ex.label eq local then ex.value
| if ex.isSameLabelAs(local) then ex.value
| else throw ex
""".stripMargin,
"Long",
Expand Down
2 changes: 2 additions & 0 deletions library/src/scala/CanEqual.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package scala

import language.experimental.captureChecking

import annotation.implicitNotFound
import scala.collection.{Seq, Set, Map}

Expand Down
2 changes: 2 additions & 0 deletions library/src/scala/CanThrow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package scala
import language.experimental.erasedDefinitions
import annotation.{implicitNotFound, experimental, capability}

import language.experimental.captureChecking

/** A capability class that allows to throw exception `E`. When used with the
* experimental.saferExceptions feature, a `throw Ex()` expression will require
* a given of class `CanThrow[Ex]` to be available.
Expand Down
16 changes: 9 additions & 7 deletions library/src/scala/Conversion.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package scala

import language.experimental.captureChecking

/** A class for implicit values that can serve as implicit conversions.
* The implicit resolution algorithm will act as if there existed
* the additional implicit definition:
Expand All @@ -23,12 +25,13 @@ package scala
*/
@java.lang.FunctionalInterface
abstract class Conversion[-T, +U] extends Function1[T, U]:
/** Convert value `x` of type `T` to type `U` */
def apply(x: T): U
self =>
/** Convert value `x` of type `T` to type `U` */
def apply(x: T): U

extension (x: T)
/** `x.convert` converts a value `x` of type `T` to type `U` */
def convert = this(x)
extension (x: T)
/** `x.convert` converts a value `x` of type `T` to type `U` */
def convert = this(x)

object Conversion:
import annotation.experimental
Expand All @@ -44,5 +47,4 @@ object Conversion:
/** Unwrap an `into` */
extension [T](x: into[T])
@experimental def underlying: T = x

end Conversion
end Conversion
Loading
Loading