Skip to content

Commit c3dd75d

Browse files
committed
Be more lazy when checking CaptureChecked annotation in TreeUnpickler
Fixes #15980
1 parent b1b1dfd commit c3dd75d

File tree

7 files changed

+41
-9
lines changed

7 files changed

+41
-9
lines changed

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

+10-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ object Annotations {
2020

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

23+
def hasSymbol(sym: Symbol)(using Context) = symbol == sym
24+
2325
def matches(cls: Symbol)(using Context): Boolean = symbol.derivesFrom(cls)
2426

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

132+
class DeferredSymAndTree(symFn: Context ?=> Symbol, treeFn: Context ?=> Tree)
133+
extends LazyAnnotation:
134+
protected var mySym: Symbol | (Context ?=> Symbol) | Null = ctx ?=> symFn(using ctx)
135+
protected var myTree: Tree | (Context ?=> Tree) | Null = ctx ?=> treeFn(using ctx)
136+
130137
/** An annotation indicating the body of a right-hand side,
131138
* typically of an inline method. Treated specially in
132139
* pickling/unpickling and TypeTreeMaps
@@ -193,18 +200,15 @@ object Annotations {
193200
apply(New(atp, args))
194201

195202
/** Create an annotation where the tree is computed lazily. */
196-
def deferred(sym: Symbol)(treeFn: Context ?=> Tree)(using Context): Annotation =
203+
def deferred(sym: Symbol)(treeFn: Context ?=> Tree): Annotation =
197204
new LazyAnnotation {
198205
protected var myTree: Tree | (Context ?=> Tree) | Null = ctx ?=> treeFn(using ctx)
199206
protected var mySym: Symbol | (Context ?=> Symbol) | Null = sym
200207
}
201208

202209
/** Create an annotation where the symbol and the tree are computed lazily. */
203-
def deferredSymAndTree(symFn: Context ?=> Symbol)(treeFn: Context ?=> Tree)(using Context): Annotation =
204-
new LazyAnnotation {
205-
protected var mySym: Symbol | (Context ?=> Symbol) | Null = ctx ?=> symFn(using ctx)
206-
protected var myTree: Tree | (Context ?=> Tree) | Null = ctx ?=> treeFn(using ctx)
207-
}
210+
def deferredSymAndTree(symFn: Context ?=> Symbol)(treeFn: Context ?=> Tree): Annotation =
211+
DeferredSymAndTree(symFn, treeFn)
208212

209213
/** Extractor for child annotations */
210214
object Child {

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

+11-3
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ class TreeUnpickler(reader: TastyReader,
624624
else
625625
newSymbol(ctx.owner, name, flags, completer, privateWithin, coord)
626626
}
627-
val annots = annotFns.map(_(sym.owner))
627+
val annots = annotFns.map(_(sym.owner))
628628
sym.annotations = annots
629629
if sym.isOpaqueAlias then sym.setFlag(Deferred)
630630
val isScala2MacroDefinedInScala3 = flags.is(Macro, butNot = Inline) && flags.is(Erased)
@@ -642,7 +642,7 @@ class TreeUnpickler(reader: TastyReader,
642642
}
643643
registerSym(start, sym)
644644
if (isClass) {
645-
if sym.owner.is(Package) && annots.exists(_.symbol == defn.CaptureCheckedAnnot) then
645+
if sym.owner.is(Package) && annots.exists(_.hasSymbol(defn.CaptureCheckedAnnot)) then
646646
wasCaptureChecked = true
647647
sym.completer.withDecls(newScope)
648648
forkAt(templateStart).indexTemplateParams()(using localContext(sym))
@@ -737,7 +737,15 @@ class TreeUnpickler(reader: TastyReader,
737737
val tp = readType()
738738
val lazyAnnotTree = readLaterWithOwner(end, _.readTerm())
739739
owner =>
740-
Annotation.deferredSymAndTree(tp.typeSymbol)(lazyAnnotTree(owner).complete)
740+
new DeferredSymAndTree(tp.typeSymbol, lazyAnnotTree(owner).complete):
741+
// Only force computation of symbol if it has the right name. This added
742+
// amount of laziness is sometimes necessary to avid cycles. Test case pos/i15980.
743+
override def hasSymbol(sym: Symbol)(using Context) = tp match
744+
case tp: TypeRef =>
745+
tp.designator match
746+
case name: Name => name == sym.name && tp.symbol == sym
747+
case _ => tp.symbol == sym
748+
case _ => this.symbol == sym
741749

742750
/** Create symbols for the definitions in the statement sequence between
743751
* current address and `end`.

tests/new/test.scala

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
object Test:
2+
def test = 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package scalaql.syntax
2+
3+
import scalaql.*
4+
5+
@forbiddenInheritance
6+
trait ScalaqlSyntax
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scalaql
2+
3+
import scala.annotation.StaticAnnotation
4+
5+
class forbiddenInheritance extends StaticAnnotation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import scalaql.syntax.ScalaqlSyntax
2+
3+
package object scalaql extends ScalaqlSyntax
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package scalaql
2+
3+
sealed trait Foo extends Product
4+

0 commit comments

Comments
 (0)