Skip to content

Commit c95d942

Browse files
committed
Represent captures as annotations until phase cc.
New annotation @retains replaces the `retains` infix type. Prefix capture sets map to the annotation.
1 parent c07c60b commit c95d942

File tree

27 files changed

+93
-109
lines changed

27 files changed

+93
-109
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,9 @@ object desugar {
17421742
flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
17431743
case ext: ExtMethods =>
17441744
Block(List(ext), Literal(Constant(())).withSpan(ext.span))
1745+
case CapturingTypeTree(refs, parent) =>
1746+
val annot = New(scalaDot(tpnme.retains), List(refs))
1747+
Annotated(parent, annot)
17451748
}
17461749
desugared.withSpan(tree.span)
17471750
}
@@ -1880,6 +1883,8 @@ object desugar {
18801883
case _ => traverseChildren(tree)
18811884
}
18821885
}.traverse(expr)
1886+
case CapturingTypeTree(refs, parent) =>
1887+
collect(parent)
18831888
case _ =>
18841889
}
18851890
collect(tree)

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
147147
case Floating
148148
}
149149

150+
/** {x1, ..., xN} T (only relevant under -Ycc) */
151+
case class CapturingTypeTree(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree
152+
150153
/** Short-lived usage in typer, does not need copy/transform/fold infrastructure */
151154
case class DependentTypeTree(tp: List[Symbol] => Type)(implicit @constructorOnly src: SourceFile) extends Tree
152155

@@ -646,6 +649,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
646649
case tree: Number if (digits == tree.digits) && (kind == tree.kind) => tree
647650
case _ => finalize(tree, untpd.Number(digits, kind))
648651
}
652+
def CapturingTypeTree(tree: Tree)(refs: List[Tree], parent: Tree)(using Context): Tree = tree match
653+
case tree: CapturingTypeTree if (refs eq tree.refs) && (parent eq tree.parent) => tree
654+
case _ => finalize(tree, untpd.CapturingTypeTree(refs, parent))
655+
649656
def TypedSplice(tree: Tree)(splice: tpd.Tree)(using Context): ProxyTree = tree match {
650657
case tree: TypedSplice if splice `eq` tree.splice => tree
651658
case _ => finalize(tree, untpd.TypedSplice(splice)(using ctx))
@@ -711,6 +718,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
711718
tree
712719
case MacroTree(expr) =>
713720
cpy.MacroTree(tree)(transform(expr))
721+
case CapturingTypeTree(refs, parent) =>
722+
cpy.CapturingTypeTree(tree)(transform(refs), transform(parent))
714723
case _ =>
715724
super.transformMoreCases(tree)
716725
}
@@ -772,6 +781,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
772781
this(x, splice)
773782
case MacroTree(expr) =>
774783
this(x, expr)
784+
case CapturingTypeTree(refs, parent) =>
785+
this(this(x, refs), parent)
775786
case _ =>
776787
super.foldMoreCases(x, tree)
777788
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package core
44

55
import Types.*, Symbols.*, Flags.*, Contexts.*, Decorators.*
66
import config.Printers.capt
7+
import Annotations.Annotation
78
import annotation.threadUnsafe
89
import annotation.internal.sharable
910
import reporting.trace
@@ -351,8 +352,11 @@ object CaptureSet:
351352
recur(tp)
352353
.showing(i"capture set of $tp = $result", capt)
353354

354-
def fromRetainsTypeArg(tp: Type)(using Context): CaptureSet = tp match
355-
case tp: CaptureRef => tp.singletonCaptureSet
356-
case OrType(tp1, tp2) => fromRetainsTypeArg(tp1) ++ fromRetainsTypeArg(tp2)
355+
def fromAnnotation(annot: Annotation)(using Context): CaptureSet =
356+
import ast.Trees.*
357+
assert(annot.symbol == defn.RetainsAnnot)
358+
annot.tree match
359+
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) =>
360+
CaptureSet(elems.map(_.tpe.asInstanceOf[CaptureRef])*)
357361

358362
end CaptureSet

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,6 @@ class Definitions {
444444
@tu lazy val andType: TypeSymbol = enterBinaryAlias(tpnme.AND, AndType(_, _))
445445
@tu lazy val orType: TypeSymbol = enterBinaryAlias(tpnme.OR, OrType(_, _, soft = false))
446446
@tu lazy val captureRoot: TermSymbol = enterPermanentSymbol(nme.CAPTURE_ROOT, AnyType).asTerm
447-
@tu lazy val captureRootAlias: TypeSymbol = enterAliasType(tpnme.CAPTURE_ROOT, captureRoot.termRef)
448447

449448
/** Marker method to indicate an argument to a call-by-name parameter.
450449
* Created by byNameClosures and elimByName, eliminated by Erasure,
@@ -475,8 +474,6 @@ class Definitions {
475474
@tu lazy val Predef_classOf : Symbol = ScalaPredefModule.requiredMethod(nme.classOf)
476475
@tu lazy val Predef_identity : Symbol = ScalaPredefModule.requiredMethod(nme.identity)
477476
@tu lazy val Predef_undefined: Symbol = ScalaPredefModule.requiredMethod(nme.???)
478-
@tu lazy val Predef_retainsType: Symbol = ScalaPredefModule.requiredType(tpnme.retains)
479-
@tu lazy val Predef_capturing: Symbol = ScalaPredefModule.requiredType(tpnme.CAPTURING)
480477
@tu lazy val ScalaPredefModuleClass: ClassSymbol = ScalaPredefModule.moduleClass.asClass
481478

482479
@tu lazy val SubTypeClass: ClassSymbol = requiredClass("scala.<:<")
@@ -1796,7 +1793,7 @@ class Definitions {
17961793
this.initCtx = ctx
17971794
if (!isInitialized) {
17981795
// force initialization of every symbol that is synthesized or hijacked by the compiler
1799-
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() ++ List(JavaEnumClass, captureRoot, captureRootAlias)
1796+
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() ++ List(JavaEnumClass, captureRoot)
18001797

18011798
isInitialized = true
18021799
}

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,6 @@ class TreePickler(pickler: TastyPickler) {
277277
pickleType(tpe.scrutinee)
278278
tpe.cases.foreach(pickleType(_))
279279
}
280-
case tp: CapturingType =>
281-
writeByte(APPLIEDtype)
282-
withLength {
283-
pickleType(defn.Predef_retainsType.typeRef)
284-
pickleType(tp.parent)
285-
pickleType(tp.refs.toRetainsTypeArg)
286-
}
287280
case tpe: PolyType if richTypes =>
288281
pickleMethodic(POLYtype, tpe, EmptyFlags)
289282
case tpe: MethodType if richTypes =>

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -359,12 +359,7 @@ class TreeUnpickler(reader: TastyReader,
359359
case APPLIEDtype =>
360360
val tycon = readType()
361361
val args = until(end)(readType())
362-
tycon match
363-
case tycon: TypeRef if tycon.symbol == defn.Predef_retainsType =>
364-
if ctx.settings.Ycc.value then CapturingType(args(0), CaptureSet.fromRetainsTypeArg(args(1)))
365-
else args(0)
366-
case _ =>
367-
tycon.appliedTo(args)
362+
tycon.appliedTo(args)
368363
case TYPEBOUNDS =>
369364
val lo = readType()
370365
if nothingButMods(end) then

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,12 +1347,10 @@ object Parsers {
13471347
case _ => false
13481348
}
13491349

1350+
/** CaptureRef ::= ident | `this`
1351+
*/
13501352
def captureRef(): Tree =
1351-
atSpan(in.offset) {
1352-
val name = ident()
1353-
if name.isVarPattern then SingletonTypeTree(Ident(name))
1354-
else Ident(name.toTypeName)
1355-
}
1353+
if in.token == THIS then simpleRef() else termIdent()
13561354

13571355
/** Type ::= FunType
13581356
* | HkTypeParamClause ‘=>>’ Type
@@ -1476,8 +1474,7 @@ object Parsers {
14761474
else if in.token == LBRACE && followingIsCaptureSet() then
14771475
val refs = inBraces { commaSeparated(captureRef) }
14781476
val t = typ()
1479-
val captured = refs.reduce(InfixOp(_, Ident(tpnme.raw.BAR), _))
1480-
AppliedTypeTree(TypeTree(defn.Predef_capturing.typeRef), captured :: t :: Nil)
1477+
CapturingTypeTree(refs, t)
14811478
else if (in.token == INDENT) enclosed(INDENT, typ())
14821479
else infixType()
14831480

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -532,10 +532,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
532532
changePrec(OrTypePrec) { toText(args(0)) ~ " | " ~ atPrec(OrTypePrec + 1) { toText(args(1)) } }
533533
else if (tpt.symbol == defn.andType && args.length == 2)
534534
changePrec(AndTypePrec) { toText(args(0)) ~ " & " ~ atPrec(AndTypePrec + 1) { toText(args(1)) } }
535-
else if tpt.symbol == defn.Predef_retainsType && args.length == 2 then
536-
changePrec(InfixPrec) { toText(args(0)) ~ " retains " ~ toText(args(1)) }
537-
else if tpt.symbol == defn.Predef_capturing && args.length == 2 then
538-
changePrec(GlobalPrec) { "{" ~ toText(args(0)) ~ "}" ~ toText(args(1)) }
539535
else if defn.isFunctionClass(tpt.symbol)
540536
&& tpt.isInstanceOf[TypeTree] && tree.hasType && !printDebug
541537
then changePrec(GlobalPrec) { toText(tree.typeOpt) }
@@ -707,6 +703,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
707703
val (prefix, postfix) = if isTermHole then ("{{{ ", " }}}") else ("[[[ ", " ]]]")
708704
val argsText = toTextGlobal(args, ", ")
709705
prefix ~~ idx.toString ~~ "|" ~~ argsText ~~ postfix
706+
case CapturingTypeTree(refs, parent) =>
707+
changePrec(GlobalPrec)("{" ~ Text(refs.map(toText), ", ") ~ "} " ~ toText(parent))
710708
case _ =>
711709
tree.fallbackToText(this)
712710
}

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,11 @@ import transform.SymUtils._
280280
val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2)(using printCtx)
281281
s"""|Found: $foundStr
282282
|Required: $expectedStr""".stripMargin
283-
+ whereSuffix + postScript
283+
+ whereSuffix + postScript
284284

285-
override def explain =
285+
override def explain =
286286
val treeStr = inTree.map(x => s"\nTree: ${x.show}").getOrElse("")
287287
treeStr + "\n" + super.explain
288-
289288

290289
end TypeMismatch
291290

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ class CheckCaptures extends Recheck:
9292
val mapType = new TypeMap:
9393
def apply(t: Type) = mapOver(t) match
9494
case AnnotatedType(parent, annot) if annot.symbol == defn.RetainsAnnot =>
95-
annot.tree match
96-
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) =>
97-
CapturingType(parent, CaptureSet(elems.tpes.asInstanceOf[List[CaptureRef]]*))
95+
CapturingType(parent, CaptureSet.fromAnnotation(annot))
9896
case t1 =>
9997
t1
10098
mapType(tp)

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,6 @@ trait TypeAssigner {
214214
val constr = tycon.typeSymbol
215215
if constr == defn.andType then AndType(args(0), args(1))
216216
else if constr == defn.orType then OrType(args(0), args(1), soft = false)
217-
else if constr == defn.Predef_retainsType then
218-
if ctx.settings.Ycc.value
219-
then CapturingType(args(0), include(CaptureSet.empty, args(1)))
220-
else args(0)
221-
else if constr == defn.Predef_capturing then
222-
if ctx.settings.Ycc.value
223-
then CapturingType(args(1), include(CaptureSet.empty, args(0)))
224-
else args(1)
225217
else tp
226218
case _ => tp
227219
end processAppliedType

library/src/scala/runtime/stdLibPatches/Predef.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,4 @@ object Predef:
4848
extension [T](x: T | Null) inline def nn: x.type & T =
4949
scala.runtime.Scala3RunTime.nn(x)
5050

51-
/** type `A` with capture set `B` */
52-
infix type retains[A, B]
53-
54-
/** An alternative notation for capturing types. TODO: needed? Or maybe mangle the name? */
55-
infix type |> [A, B]
5651
end Predef

tests/disabled/neg-custom-args/captures/capt-wf.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
// No longer valid
22
class C
3-
type Cap = C retains *
4-
type Top = Any retains *
3+
type Cap = C @retains(*)
4+
type Top = Any @retains(*)
55

6-
type T = (x: Cap) => List[String retains x.type] => Unit // error
7-
val x: (x: Cap) => Array[String retains x.type] = ??? // error
6+
type T = (x: Cap) => List[String @retains(x)] => Unit // error
7+
val x: (x: Cap) => Array[String @retains(x)] = ??? // error
88
val y = x
99

1010
def test: Unit =
1111
def f(x: Cap) = // ok
12-
val g = (xs: List[String retains x.type]) => ()
12+
val g = (xs: List[String @retains(x)]) => ()
1313
g
14-
def f2(x: Cap)(xs: List[String retains x.type]) = ()
14+
def f2(x: Cap)(xs: List[String @retains(x)]) = ()
1515
val x = f // error
1616
val x2 = f2 // error
1717
val y = f(C()) // ok

tests/disabled/neg-custom-args/captures/try2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import annotation.ability
55
@ability erased val canThrow: * = ???
66

77
class CanThrow[E <: Exception] extends Retains[canThrow.type]
8-
type Top = Any retains *
8+
type Top = Any @retains(*)
99

1010
infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R
1111

tests/neg-custom-args/captures/boxmap.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
type Top = Any retains *
1+
type Top = Any @retains(*)
22

3-
infix type ==> [A, B] = (A => B) retains *
3+
infix type ==> [A, B] = (A => B) @retains(*)
44

55
type Box[+T <: Top] = ([K <: Top] => (T ==> K) => K)
66

tests/neg-custom-args/captures/capt1.check

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
1-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:4:2 ------------------------------------------
2-
4 | () => if x == null then y else y // error
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:3:2 ------------------------------------------
2+
3 | () => if x == null then y else y // error
33
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44
| Found: {x} () => C
55
| Required: () => C
66

77
longer explanation available when compiling with `-explain`
8-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:7:2 ------------------------------------------
9-
7 | () => if x == null then y else y // error
8+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:6:2 ------------------------------------------
9+
6 | () => if x == null then y else y // error
1010
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1111
| Found: {x} () => C
1212
| Required: Matchable
1313

1414
longer explanation available when compiling with `-explain`
15-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:15:2 -----------------------------------------
16-
15 | f // error
15+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:14:2 -----------------------------------------
16+
14 | f // error
1717
| ^
1818
| Found: {x} Int => Int
1919
| Required: Matchable
2020

2121
longer explanation available when compiling with `-explain`
22-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:23:2 -----------------------------------------
23-
23 | F(22) // error
22+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:22:2 -----------------------------------------
23+
22 | F(22) // error
2424
| ^^^^^
2525
| Found: {x} A
2626
| Required: A
2727

2828
longer explanation available when compiling with `-explain`
29-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:27:40 ----------------------------------------
30-
27 | def m() = if x == null then y else y // error
29+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:26:40 ----------------------------------------
30+
26 | def m() = if x == null then y else y // error
3131
| ^
3232
| Found: {x} A
3333
| Required: A
3434

3535
longer explanation available when compiling with `-explain`
36-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:32:24 ----------------------------------------
37-
32 | val z2 = h[() => Cap](() => x)(() => C()) // error
36+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:31:24 ----------------------------------------
37+
31 | val z2 = h[() => Cap](() => x)(() => C()) // error
3838
| ^^^^^^^
3939
| Found: {x} () => Cap
4040
| Required: () => Cap

tests/neg-custom-args/captures/capt1.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import scala.retains
21
class C
32
def f(x: C @retains(*), y: C): () => C =
43
() => if x == null then y else y // error
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
//import scala.retains
12
class C
2-
type Cap = C retains *
3+
type Cap = {*} C
34

4-
def f1(c: Cap): (() => C retains c.type) = () => c // ok
5-
def f2(c: Cap): (() => C) retains c.type = () => c // error
5+
def f1(c: Cap): (() => {c} C) = () => c // error, but would be OK under capture abbreciations for funciton types
6+
def f2(c: Cap): ({c} () => C) = () => c // error
67

78
def h5(x: Cap): () => C =
89
f1(x) // error

tests/neg-custom-args/captures/capt3.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class C
2-
type Cap = C retains *
2+
type Cap = C @retains(*)
33

44
def test1() =
55
val x: Cap = C()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
object Test:
22

3-
def f[A <: Matchable retains *](x: A): Matchable = x // error
3+
def f[A <: Matchable @retains(*)](x: A): Matchable = x // error
44

tests/neg-custom-args/captures/io.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ sealed trait IO:
22
def puts(msg: Any): Unit = println(msg)
33

44
def test1 =
5-
val IO : IO retains * = new IO {}
5+
val IO : IO @retains(*) = new IO {}
66
def foo = {IO; IO.puts("hello") }
77
val x : () => Unit = () => foo // error: Found: (() => Unit) retains IO; Required: () => Unit
88

99
def test2 =
10-
val IO : IO retains * = new IO {}
11-
def puts(msg: Any, io: IO retains *) = println(msg)
10+
val IO : IO @retains(*) = new IO {}
11+
def puts(msg: Any, io: IO @retains(*)) = println(msg)
1212
def foo() = puts("hello", IO)
1313
val x : () => Unit = () => foo() // error: Found: (() => Unit) retains IO; Required: () => Unit
1414

15-
type Capability[T] = T retains *
15+
type Capability[T] = T @retains(*)
1616

1717
def test3 =
1818
val IO : Capability[IO] = new IO {}

tests/neg-custom-args/captures/try.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import language.experimental.erasedDefinitions
22

33
class CT[E <: Exception]
4-
type CanThrow[E <: Exception] = CT[E] retains *
5-
type Top = Any retains *
4+
type CanThrow[E <: Exception] = CT[E] @retains(*)
5+
type Top = Any @retains(*)
66

77
infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R
88

0 commit comments

Comments
 (0)