Skip to content

Be more lazy when checking CaptureChecked annotation in TreeUnpickler #16097

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 1 commit into from
Sep 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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