Skip to content

Add experimental capture checking #15877

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

Merged
merged 99 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
12131be
Merge pull request #12971 from dotty-staging/add-rechecker
odersky Aug 17, 2021
b64aa7e
Cleanups
odersky Aug 17, 2021
cbdb610
First version of capture checker.
odersky Sep 29, 2021
b3474f2
Include capture sets of methods in enclosing class
odersky Oct 2, 2021
e12648b
Update iterators example
odersky Nov 19, 2021
8f5ef66
Add capture checks for mutable variables
odersky Oct 5, 2021
cdaee68
Implement deep check for variables
odersky Oct 5, 2021
5b04553
Introduce @capability annotations
odersky Dec 12, 2021
e835dc0
Fix typo and update semanticdb.expect
odersky Dec 12, 2021
8cac9db
Special rule for {this} in capture sets of class members
odersky Dec 23, 2021
965635c
Pure function types -> and ?->
odersky Dec 25, 2021
bfeacfe
Updates for latest master
odersky Jan 21, 2022
0ae7911
Shorthand for curried functions
odersky Dec 23, 2021
62095a6
Syntax change: allow capture sets in infix types
odersky Jan 26, 2022
9c485e5
Test for contravariantly used class fields
odersky Jan 26, 2022
4074217
Handle captures in by-name parameters
odersky Jan 23, 2022
fd6b376
Treat exceptions as capabilities
odersky Jan 25, 2022
39b14f6
Mark classes compiled under -Ycc with a CaptureChecked annotation
odersky Jan 26, 2022
d4ffebb
Map regular function types to impure function types when unpickling
odersky Jan 26, 2022
57a6bc0
New scheme to reject root captures
odersky Jan 28, 2022
127a223
Three changes to typing rules
odersky Jan 30, 2022
1b4370b
Change result type of by-name closures
odersky Feb 1, 2022
0949618
Capture-check exception capabilties
odersky Feb 1, 2022
c361123
Drop restriction that tracked class parameters must be `val`s.
odersky Feb 2, 2022
5460a83
Handle constructors with multiple parameter lists
odersky Feb 2, 2022
ba8ae0f
Don't add class refinements for @constructorOnly parameters
odersky Feb 3, 2022
ebe081b
Check capture set conditions on self types
odersky Feb 4, 2022
27128c8
Handle local objects
odersky Feb 5, 2022
97b9519
Print boxes only under -Ydebug-cc
odersky Feb 6, 2022
c6a2e07
Refine setup of initial types for anonymous functions
odersky Feb 7, 2022
7db3a7c
Test from doc page
odersky Feb 9, 2022
77cf546
Update checkfiles after rebase
odersky Feb 25, 2022
161e8cd
Refinements to capture checking and a collection strawman
odersky Mar 2, 2022
75143cc
Make it compile under -Yexplicit-nulls
odersky Mar 6, 2022
8a1acde
Make it compile without language.postfixOps
odersky Mar 26, 2022
0486a79
Make derivedAnnotation of universal the identity
odersky Mar 26, 2022
acbab4b
add stack allocation tests from CC paper
olhotak Mar 29, 2022
152225d
udpate test to match paper
olhotak Mar 29, 2022
2f0bc12
Fix rebase breakage
odersky Apr 28, 2022
097b179
Fix universal check for inferred types
odersky Apr 28, 2022
64fcbcb
Allow references to local class parameters in capture sets
odersky Apr 30, 2022
94d0d53
Three capture checking fixes
odersky May 1, 2022
d61a779
Clean up mapped set handling
odersky May 2, 2022
ed69c36
Address review comments
odersky May 3, 2022
bfd5b20
Apply suggestions from code review
odersky May 3, 2022
f00dc5d
Fix locility condition for checking inferred types
odersky May 6, 2022
8e8b336
Fix rebase breakage
odersky May 18, 2022
65ee41a
Explicitly generate capturing types for synthetic methods
odersky May 18, 2022
33f9ea9
Better implementation of capture set intersection
odersky May 18, 2022
c83751d
Fix some issues with box handling
odersky May 18, 2022
71847a4
Use intersection to type selections
odersky May 13, 2022
ef00273
Update semanticDB expect file
odersky May 29, 2022
78e5fa4
Avoi intersections in typing selections and applications
odersky Jun 12, 2022
2724ac7
Generalize new rule for applications
odersky Jun 12, 2022
8c8d6e4
Generalize application rule to all methods
odersky Jun 12, 2022
c90f003
Fix rebase breakage
odersky Jul 26, 2022
768dba2
Don't elide capture sets when printing (empty) bounds
odersky Jul 27, 2022
275218d
Several fixes to boxing
odersky Jul 27, 2022
54c0ebf
Always print boxes (for now)
odersky Jul 27, 2022
0221744
Box arguments of type applications
odersky Jul 27, 2022
e08a59f
Make classes added by cc experimental
odersky Jul 27, 2022
8c2b6bf
Also print boxes for variable capture sets with no elems defined
odersky Jul 27, 2022
6d30fd2
More fixes related to boxing
odersky Jul 29, 2022
f12f221
Don't follow abstract in boxed capturing
odersky Jul 29, 2022
f6407e3
WIP: Turn on box-aware subtyping
odersky Aug 7, 2022
7066ef7
Cache boxed versions of CapturingTypes
odersky Aug 7, 2022
f73a73a
Auto-boxing of arguments of applied types
odersky Aug 7, 2022
ad9d8f9
Fuse CapturingTypes
odersky Aug 8, 2022
4c2bdc9
Add test
odersky Aug 8, 2022
0f3d3dd
Fix init checker error
odersky Aug 8, 2022
7006bea
Fix test
odersky Aug 9, 2022
b9a559e
Don't box type arguments in Setup
odersky Aug 9, 2022
5659aaf
Optimize boxedUnlessFun
odersky Aug 9, 2022
fa37358
Stabilize boxedType cache
odersky Aug 9, 2022
d2b9c9d
Drop checkBoxes switch
odersky Aug 9, 2022
77d08ef
Drop addResultBoxes
odersky Aug 9, 2022
3e2d6aa
Polishings
odersky Aug 9, 2022
dfe28e4
Test case 15772
odersky Aug 10, 2022
0230b5a
Another test
odersky Aug 10, 2022
2e7c363
Drop check file
odersky Aug 10, 2022
562f9d7
Recognize capture related syntax and names only under -Ycc
odersky Aug 10, 2022
01f4e0b
Recognize ImpureFunction only under -Ycc
odersky Aug 10, 2022
9f2a801
Drop widenSkolems
odersky Aug 10, 2022
8bab2ac
Polishings in dotc, core, ast, parsing
odersky Aug 11, 2022
0ce75e1
Polishings mostly in typer
odersky Aug 11, 2022
30a53ef
Reorganize retains annotation classes
odersky Aug 12, 2022
c091c33
Move some cc classes to their proper place in the file hierarchy
odersky Aug 12, 2022
a158840
Polishing some cc classes
odersky Aug 12, 2022
9ecfbc1
Polish and comment CaptureSet
odersky Aug 14, 2022
aeafb05
Work on rechecker
odersky Aug 15, 2022
b3eeb23
Don't box arguments of inferred applied types at Setup
odersky Aug 15, 2022
84c1b24
Polish Setup
odersky Aug 15, 2022
a0cbb87
Treat bounds of type variables as boxed
odersky Aug 16, 2022
acf8286
Reorganize the way capture sets are included in the current environme…
odersky Aug 16, 2022
e15d31c
Refactor and comment CheckCaptures
odersky Aug 17, 2022
cf55ddb
Polish Synthetics.scala
odersky Aug 17, 2022
39f035f
Change -Ycc setting explanations
odersky Aug 17, 2022
4bab1ed
Drop mention of cc-experiment in docs
odersky Aug 17, 2022
943d84a
Address review comment
odersky Aug 29, 2022
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
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package dotc
import core._
import Contexts._
import typer.{TyperPhase, RefChecks}
import cc.CheckCaptures
import parsing.Parser
import Phases.Phase
import transform._
Expand Down Expand Up @@ -78,6 +79,10 @@ class Compiler {
new SpecializeApplyMethods, // Adds specialized methods to FunctionN
new TryCatchPatterns, // Compile cases in try/catch
new PatternMatcher) :: // Compile pattern matches
List(new TestRecheck.Pre) :: // Test only: run rechecker, enabled under -Yrecheck-test
List(new TestRecheck) :: // Test only: run rechecker, enabled under -Yrecheck-test
List(new CheckCaptures.Pre) :: // Preparations for check captures phase, enabled under -Ycc
List(new CheckCaptures) :: // Check captures, enabled under -Ycc
List(new ElimOpaque, // Turn opaque into normal aliases
new sjs.ExplicitJSClasses, // Make all JS classes explicit (Scala.js only)
new ExplicitOuter, // Add accessors to outer classes from nested ones.
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import reporting.{Suppression, Action, Profile, ActiveProfile, NoProfile}
import reporting.Diagnostic
import reporting.Diagnostic.Warning
import rewrites.Rewrites

import profile.Profiler
import printing.XprintMode
import typer.ImplicitRunInfo
Expand Down Expand Up @@ -294,7 +293,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
val fusedPhase = ctx.phase.prevMega
val echoHeader = f"[[syntax trees at end of $fusedPhase%25s]] // ${unit.source}"
val tree = if ctx.isAfterTyper then unit.tpdTree else unit.untpdTree
val treeString = tree.show(using ctx.withProperty(XprintMode, Some(())))
val treeString = fusedPhase.show(tree)

last match {
case SomePrintedTree(phase, lastTreeString) if lastTreeString == treeString =>
Expand Down
17 changes: 14 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ object desugar {

if mods.is(Trait) then
for vparams <- originalVparamss; vparam <- vparams do
if vparam.tpt.isInstanceOf[ByNameTypeTree] then
if isByNameType(vparam.tpt) then
report.error(em"implementation restriction: traits cannot have by name parameters", vparam.srcPos)

// Annotations on class _type_ parameters are set on the derived parameters
Expand Down Expand Up @@ -576,9 +576,8 @@ object desugar {
appliedTypeTree(tycon, targs)
}

def isRepeated(tree: Tree): Boolean = tree match {
def isRepeated(tree: Tree): Boolean = stripByNameType(tree) match {
case PostfixOp(_, Ident(tpnme.raw.STAR)) => true
case ByNameTypeTree(tree1) => isRepeated(tree1)
case _ => false
}

Expand Down Expand Up @@ -1810,6 +1809,16 @@ object desugar {
flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
case ext: ExtMethods =>
Block(List(ext), Literal(Constant(())).withSpan(ext.span))
case CapturingTypeTree(refs, parent) =>
// convert `{refs} T` to `T @retains refs`
// `{refs}-> T` to `-> (T @retainsByName refs)`
def annotate(annotName: TypeName, tp: Tree) =
Annotated(tp, New(scalaAnnotationDot(annotName), List(refs)))
parent match
case ByNameTypeTree(restpt) =>
cpy.ByNameTypeTree(parent)(annotate(tpnme.retainsByName, restpt))
case _ =>
annotate(tpnme.retains, parent)
}
desugared.withSpan(tree.span)
}
Expand Down Expand Up @@ -1946,6 +1955,8 @@ object desugar {
case _ => traverseChildren(tree)
}
}.traverse(expr)
case CapturingTypeTree(refs, parent) =>
collect(parent)
case _ =>
}
collect(tree)
Expand Down
31 changes: 29 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
}

/** Is tpt a vararg type of the form T* or => T*? */
def isRepeatedParamType(tpt: Tree)(using Context): Boolean = tpt match {
case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1)
def isRepeatedParamType(tpt: Tree)(using Context): Boolean = stripByNameType(tpt) match {
case tpt: TypeTree => tpt.typeOpt.isRepeatedParam
case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true
case _ => false
Expand All @@ -196,6 +195,18 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case arg => arg.typeOpt.widen.isRepeatedParam
}

/** Is tree a type tree of the form `=> T` or (under -Ycc) `{refs}-> T`? */
def isByNameType(tree: Tree)(using Context): Boolean =
stripByNameType(tree) ne tree

/** Strip `=> T` to `T` and (under -Ycc) `{refs}-> T` to `T` */
def stripByNameType(tree: Tree)(using Context): Tree = unsplice(tree) match
case ByNameTypeTree(t1) => t1
case untpd.CapturingTypeTree(_, parent) =>
val parent1 = stripByNameType(parent)
if parent1 eq parent then tree else parent1
case _ => tree

/** All type and value parameter symbols of this DefDef */
def allParamSyms(ddef: DefDef)(using Context): List[Symbol] =
ddef.paramss.flatten.map(_.symbol)
Expand Down Expand Up @@ -388,6 +399,22 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
case _ => None
}
}

/** Under -Ycc: A builder and extractor for `=> T`, which is an alias for `{*}-> T`.
* Only trees of the form `=> T` are matched; trees written directly as `{*}-> T`
* are ignored by the extractor.
*/
object ImpureByNameTypeTree:

def apply(tp: ByNameTypeTree)(using Context): untpd.CapturingTypeTree =
untpd.CapturingTypeTree(
Ident(nme.CAPTURE_ROOT).withSpan(tp.span.startPos) :: Nil, tp)

def unapply(tp: Tree)(using Context): Option[ByNameTypeTree] = tp match
case untpd.CapturingTypeTree(id @ Ident(nme.CAPTURE_ROOT) :: Nil, bntp: ByNameTypeTree)
if id.span == bntp.span.startPos => Some(bntp)
case _ => None
end ImpureByNameTypeTree
}

trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
Expand Down
8 changes: 1 addition & 7 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,10 @@ object Trees {
/** Tree's denotation can be derived from its type */
abstract class DenotingTree[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends Tree[T] {
type ThisTree[-T >: Untyped] <: DenotingTree[T]
override def denot(using Context): Denotation = typeOpt match {
override def denot(using Context): Denotation = typeOpt.stripped match
case tpe: NamedType => tpe.denot
case tpe: ThisType => tpe.cls.denot
case tpe: AnnotatedType => tpe.stripAnnots match {
case tpe: NamedType => tpe.denot
case tpe: ThisType => tpe.cls.denot
case _ => NoDenotation
}
case _ => NoDenotation
}
}

/** Tree's denot/isType/isTerm properties come from a subtree
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined =
ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion)

def TypeTree(tp: Type)(using Context): TypeTree =
untpd.TypeTree().withType(tp)
def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree =
(if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp)

def SingletonTypeTree(ref: Tree)(using Context): SingletonTypeTree =
ta.assignType(untpd.SingletonTypeTree(ref), ref)
Expand Down Expand Up @@ -203,8 +203,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
ta.assignType(untpd.UnApply(fun, implicits, patterns), proto)
}

def ValDef(sym: TermSymbol, rhs: LazyTree = EmptyTree)(using Context): ValDef =
ta.assignType(untpd.ValDef(sym.name, TypeTree(sym.info), rhs), sym)
def ValDef(sym: TermSymbol, rhs: LazyTree = EmptyTree, inferred: Boolean = false)(using Context): ValDef =
ta.assignType(untpd.ValDef(sym.name, TypeTree(sym.info, inferred), rhs), sym)

def SyntheticValDef(name: TermName, rhs: Tree, flags: FlagSet = EmptyFlags)(using Context): ValDef =
ValDef(newSymbol(ctx.owner, name, Synthetic | flags, rhs.tpe.widen, coord = rhs.span), rhs)
Expand Down
19 changes: 17 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class InterpolatedString(id: TermName, segments: List[Tree])(implicit @constructorOnly src: SourceFile)
extends TermTree

/** A function type */
/** A function type or closure */
case class Function(args: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends Tree {
override def isTerm: Boolean = body.isTerm
override def isType: Boolean = body.isType
}

/** A function type with `implicit`, `erased`, or `given` modifiers */
/** A function type or closure with `implicit`, `erased`, or `given` modifiers */
class FunctionWithMods(args: List[Tree], body: Tree, val mods: Modifiers)(implicit @constructorOnly src: SourceFile)
extends Function(args, body)

Expand Down Expand Up @@ -145,6 +145,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case Floating
}

/** {x1, ..., xN} T (only relevant under -Ycc) */
case class CapturingTypeTree(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree

/** Short-lived usage in typer, does not need copy/transform/fold infrastructure */
case class DependentTypeTree(tp: List[Symbol] => Type)(implicit @constructorOnly src: SourceFile) extends Tree

Expand Down Expand Up @@ -213,6 +216,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class Transparent()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Transparent)

case class Infix()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Infix)

/** Used under -Ycc to mark impure function types `A => B` in `FunctionWithMods` */
case class Impure()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Impure)
}

/** Modifiers and annotations for definitions
Expand Down Expand Up @@ -390,6 +396,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt)
def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion)
def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree()
def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree()
def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref)
def RefinedTypeTree(tpt: Tree, refinements: List[Tree])(implicit src: SourceFile): RefinedTypeTree = new RefinedTypeTree(tpt, refinements)
def AppliedTypeTree(tpt: Tree, args: List[Tree])(implicit src: SourceFile): AppliedTypeTree = new AppliedTypeTree(tpt, args)
Expand Down Expand Up @@ -647,6 +654,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case tree: Number if (digits == tree.digits) && (kind == tree.kind) => tree
case _ => finalize(tree, untpd.Number(digits, kind))
}
def CapturingTypeTree(tree: Tree)(refs: List[Tree], parent: Tree)(using Context): Tree = tree match
case tree: CapturingTypeTree if (refs eq tree.refs) && (parent eq tree.parent) => tree
case _ => finalize(tree, untpd.CapturingTypeTree(refs, parent))

def TypedSplice(tree: Tree)(splice: tpd.Tree)(using Context): ProxyTree = tree match {
case tree: TypedSplice if splice `eq` tree.splice => tree
case _ => finalize(tree, untpd.TypedSplice(splice)(using ctx))
Expand Down Expand Up @@ -710,6 +721,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
tree
case MacroTree(expr) =>
cpy.MacroTree(tree)(transform(expr))
case CapturingTypeTree(refs, parent) =>
cpy.CapturingTypeTree(tree)(transform(refs), transform(parent))
case _ =>
super.transformMoreCases(tree)
}
Expand Down Expand Up @@ -769,6 +782,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
this(x, splice)
case MacroTree(expr) =>
this(x, expr)
case CapturingTypeTree(refs, parent) =>
this(this(x, refs), parent)
case _ =>
super.foldMoreCases(x, tree)
}
Expand Down
19 changes: 19 additions & 0 deletions compiler/src/dotty/tools/dotc/cc/BoxedTypeCache.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dotty.tools
package dotc
package cc

import core.*
import Types.*, Symbols.*, Contexts.*

/** A one-element cache for the boxed version of an unboxed capturing type */
class BoxedTypeCache:
private var boxed: Type = compiletime.uninitialized
private var unboxed: Type = NoType

def apply(tp: AnnotatedType)(using Context): Type =
if tp ne unboxed then
unboxed = tp
val CapturingType(parent, refs) = tp: @unchecked
boxed = CapturingType(parent, refs, boxed = true)
boxed
end BoxedTypeCache
77 changes: 77 additions & 0 deletions compiler/src/dotty/tools/dotc/cc/CaptureAnnotation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package dotty.tools
package dotc
package cc

import core.*
import Types.*, Symbols.*, Contexts.*, Annotations.*
import ast.Trees.*
import ast.{tpd, untpd}
import Decorators.*
import config.Printers.capt
import printing.Printer
import printing.Texts.Text

/** An annotation representing a capture set and whether it is boxed.
* It simulates a normal @retains annotation except that it is more efficient,
* supports variables as capture sets, and adds a `boxed` flag.
* These annotations are created during capture checking. Before that
* there are only regular @retains and @retainsByName annotations.
* @param refs the capture set
* @param boxed whether the type carrying the annotation is boxed
* @param cls the underlying class (either annotation.retains or annotation.retainsByName)
*/
case class CaptureAnnotation(refs: CaptureSet, boxed: Boolean)(cls: Symbol) extends Annotation:
import CaptureAnnotation.*
import tpd.*

/** A cache for boxed version of a capturing type with this annotation */
val boxedType = BoxedTypeCache()

/** Reconstitute annotation tree from capture set */
override def tree(using Context) =
val elems = refs.elems.toList.map {
case cr: TermRef => ref(cr)
case cr: TermParamRef => untpd.Ident(cr.paramName).withType(cr)
case cr: ThisType => This(cr.cls)
}
val arg = repeated(elems, TypeTree(defn.AnyType))
New(symbol.typeRef, arg :: Nil)

override def symbol(using Context) = cls

override def derivedAnnotation(tree: Tree)(using Context): Annotation =
unsupported(i"derivedAnnotation(Tree), $tree, $refs")

def derivedAnnotation(refs: CaptureSet, boxed: Boolean)(using Context): Annotation =
if (this.refs eq refs) && (this.boxed == boxed) then this
else CaptureAnnotation(refs, boxed)(cls)

override def sameAnnotation(that: Annotation)(using Context): Boolean = that match
case CaptureAnnotation(refs, boxed) =>
this.refs == refs && this.boxed == boxed && this.symbol == that.symbol
case _ => false

override def mapWith(tm: TypeMap)(using Context) =
val elems = refs.elems.toList
val elems1 = elems.mapConserve(tm)
if elems1 eq elems then this
else if elems1.forall(_.isInstanceOf[CaptureRef])
then derivedAnnotation(CaptureSet(elems1.asInstanceOf[List[CaptureRef]]*), boxed)
else EmptyAnnotation

override def refersToParamOf(tl: TermLambda)(using Context): Boolean =
refs.elems.exists {
case TermParamRef(tl1, _) => tl eq tl1
case _ => false
}

override def toText(printer: Printer): Text = refs.toText(printer)

override def hash: Int =
(refs.hashCode << 1) | (if boxed then 1 else 0)

override def eql(that: Annotation) = that match
case that: CaptureAnnotation => (this.refs eq that.refs) && (this.boxed == that.boxed)
case _ => false

end CaptureAnnotation
Loading