Skip to content

Commit

Permalink
Be more lazy when checking CaptureChecked annotation in TreeUnpickler
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Sep 25, 2022
1 parent b1b1dfd commit c3dd75d
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 9 deletions.
16 changes: 10 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ object Annotations {

def symbol(using Context): Symbol = annotClass(tree)

def hasSymbol(sym: Symbol)(using Context) = symbol == sym

def matches(cls: Symbol)(using Context): Boolean = symbol.derivesFrom(cls)

def appliesToModule: Boolean = true // for now; see remark in SymDenotations
Expand Down Expand Up @@ -127,6 +129,11 @@ object Annotations {
override def isEvaluated: Boolean = myTree.isInstanceOf[Tree @unchecked]
}

class DeferredSymAndTree(symFn: Context ?=> Symbol, treeFn: Context ?=> Tree)
extends LazyAnnotation:
protected var mySym: Symbol | (Context ?=> Symbol) | Null = ctx ?=> symFn(using ctx)
protected var myTree: Tree | (Context ?=> Tree) | Null = ctx ?=> treeFn(using ctx)

/** An annotation indicating the body of a right-hand side,
* typically of an inline method. Treated specially in
* pickling/unpickling and TypeTreeMaps
Expand Down Expand Up @@ -193,18 +200,15 @@ object Annotations {
apply(New(atp, args))

/** Create an annotation where the tree is computed lazily. */
def deferred(sym: Symbol)(treeFn: Context ?=> Tree)(using Context): Annotation =
def deferred(sym: Symbol)(treeFn: Context ?=> Tree): Annotation =
new LazyAnnotation {
protected var myTree: Tree | (Context ?=> Tree) | Null = ctx ?=> treeFn(using ctx)
protected var mySym: Symbol | (Context ?=> Symbol) | Null = sym
}

/** Create an annotation where the symbol and the tree are computed lazily. */
def deferredSymAndTree(symFn: Context ?=> Symbol)(treeFn: Context ?=> Tree)(using Context): Annotation =
new LazyAnnotation {
protected var mySym: Symbol | (Context ?=> Symbol) | Null = ctx ?=> symFn(using ctx)
protected var myTree: Tree | (Context ?=> Tree) | Null = ctx ?=> treeFn(using ctx)
}
def deferredSymAndTree(symFn: Context ?=> Symbol)(treeFn: Context ?=> Tree): Annotation =
DeferredSymAndTree(symFn, treeFn)

/** Extractor for child annotations */
object Child {
Expand Down
14 changes: 11 additions & 3 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ class TreeUnpickler(reader: TastyReader,
else
newSymbol(ctx.owner, name, flags, completer, privateWithin, coord)
}
val annots = annotFns.map(_(sym.owner))
val annots = annotFns.map(_(sym.owner))
sym.annotations = annots
if sym.isOpaqueAlias then sym.setFlag(Deferred)
val isScala2MacroDefinedInScala3 = flags.is(Macro, butNot = Inline) && flags.is(Erased)
Expand All @@ -642,7 +642,7 @@ class TreeUnpickler(reader: TastyReader,
}
registerSym(start, sym)
if (isClass) {
if sym.owner.is(Package) && annots.exists(_.symbol == defn.CaptureCheckedAnnot) then
if sym.owner.is(Package) && annots.exists(_.hasSymbol(defn.CaptureCheckedAnnot)) then
wasCaptureChecked = true
sym.completer.withDecls(newScope)
forkAt(templateStart).indexTemplateParams()(using localContext(sym))
Expand Down Expand Up @@ -737,7 +737,15 @@ class TreeUnpickler(reader: TastyReader,
val tp = readType()
val lazyAnnotTree = readLaterWithOwner(end, _.readTerm())
owner =>
Annotation.deferredSymAndTree(tp.typeSymbol)(lazyAnnotTree(owner).complete)
new DeferredSymAndTree(tp.typeSymbol, lazyAnnotTree(owner).complete):
// Only force computation of symbol if it has the right name. This added
// amount of laziness is sometimes necessary to avid cycles. Test case pos/i15980.
override def hasSymbol(sym: Symbol)(using Context) = tp match
case tp: TypeRef =>
tp.designator match
case name: Name => name == sym.name && tp.symbol == sym
case _ => tp.symbol == sym
case _ => this.symbol == sym

/** Create symbols for the definitions in the statement sequence between
* current address and `end`.
Expand Down
2 changes: 2 additions & 0 deletions tests/new/test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
object Test:
def test = 0
6 changes: 6 additions & 0 deletions tests/pos/i15980/scalaql/1_syntax_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package scalaql.syntax

import scalaql.*

@forbiddenInheritance
trait ScalaqlSyntax
5 changes: 5 additions & 0 deletions tests/pos/i15980/scalaql/2_annotations_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package scalaql

import scala.annotation.StaticAnnotation

class forbiddenInheritance extends StaticAnnotation
3 changes: 3 additions & 0 deletions tests/pos/i15980/scalaql/3_package_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import scalaql.syntax.ScalaqlSyntax

package object scalaql extends ScalaqlSyntax
4 changes: 4 additions & 0 deletions tests/pos/i15980/scalaql/usage_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package scalaql

sealed trait Foo extends Product

0 comments on commit c3dd75d

Please sign in to comment.