diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index fccf43a3e834..aa8ead280bbf 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -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 @@ -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 @@ -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 { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 6887937ed6fe..f5a1a5826bfb 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -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) @@ -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)) @@ -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`. diff --git a/tests/new/test.scala b/tests/new/test.scala new file mode 100644 index 000000000000..8aa8f42ac787 --- /dev/null +++ b/tests/new/test.scala @@ -0,0 +1,2 @@ +object Test: + def test = 0 diff --git a/tests/pos/i15980/scalaql/1_syntax_1.scala b/tests/pos/i15980/scalaql/1_syntax_1.scala new file mode 100644 index 000000000000..747f3f61fd16 --- /dev/null +++ b/tests/pos/i15980/scalaql/1_syntax_1.scala @@ -0,0 +1,6 @@ +package scalaql.syntax + +import scalaql.* + +@forbiddenInheritance +trait ScalaqlSyntax diff --git a/tests/pos/i15980/scalaql/2_annotations_1.scala b/tests/pos/i15980/scalaql/2_annotations_1.scala new file mode 100644 index 000000000000..3e1a040e589e --- /dev/null +++ b/tests/pos/i15980/scalaql/2_annotations_1.scala @@ -0,0 +1,5 @@ +package scalaql + +import scala.annotation.StaticAnnotation + +class forbiddenInheritance extends StaticAnnotation diff --git a/tests/pos/i15980/scalaql/3_package_1.scala b/tests/pos/i15980/scalaql/3_package_1.scala new file mode 100644 index 000000000000..46ada0b9bb94 --- /dev/null +++ b/tests/pos/i15980/scalaql/3_package_1.scala @@ -0,0 +1,3 @@ +import scalaql.syntax.ScalaqlSyntax + +package object scalaql extends ScalaqlSyntax diff --git a/tests/pos/i15980/scalaql/usage_2.scala b/tests/pos/i15980/scalaql/usage_2.scala new file mode 100644 index 000000000000..a1573603db95 --- /dev/null +++ b/tests/pos/i15980/scalaql/usage_2.scala @@ -0,0 +1,4 @@ +package scalaql + +sealed trait Foo extends Product +