From 85b1c0e740ed72490f15b7fc3f61c3e01bf0ea26 Mon Sep 17 00:00:00 2001
From: Matt Bovel <matthieu@bovel.net>
Date: Fri, 19 Apr 2024 17:03:51 +0200
Subject: [PATCH 1/4] Use `uninitialized` instead of `_` in lazyval benchmarks

---
 .../benchmarks/lazyvals/ContendedInitialization.scala      | 7 ++++---
 .../tools/benchmarks/lazyvals/InitializedAccess.scala      | 3 ++-
 .../tools/benchmarks/lazyvals/InitializedAccessAny.scala   | 3 ++-
 .../benchmarks/lazyvals/InitializedAccessGeneric.scala     | 3 ++-
 .../tools/benchmarks/lazyvals/InitializedAccessInt.scala   | 3 ++-
 .../benchmarks/lazyvals/InitializedAccessMultiple.scala    | 3 ++-
 .../benchmarks/lazyvals/InitializedAccessString.scala      | 3 ++-
 7 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/ContendedInitialization.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/ContendedInitialization.scala
index fb2cedbb7d41..12713b297759 100644
--- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/ContendedInitialization.scala
+++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/ContendedInitialization.scala
@@ -1,5 +1,6 @@
 package dotty.tools.benchmarks.lazyvals
 
+import compiletime.uninitialized
 import org.openjdk.jmh.annotations._
 import LazyVals.LazyHolder
 import org.openjdk.jmh.infra.Blackhole
@@ -16,12 +17,12 @@ import java.util.concurrent.{Executors, ExecutorService}
 class ContendedInitialization {
 
   @Param(Array("2000000", "5000000"))
-  var size: Int = _
+  var size: Int = uninitialized
 
   @Param(Array("2", "4", "8"))
-  var nThreads: Int = _
+  var nThreads: Int = uninitialized
 
-  var executor: ExecutorService = _
+  var executor: ExecutorService = uninitialized
 
   @Setup
   def prepare: Unit = {
diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala
index d413458d0049..34bd652cbd2d 100644
--- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala
+++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala
@@ -1,5 +1,6 @@
 package dotty.tools.benchmarks.lazyvals
 
+import compiletime.uninitialized
 import org.openjdk.jmh.annotations._
 import LazyVals.LazyHolder
 import org.openjdk.jmh.infra.Blackhole
@@ -14,7 +15,7 @@ import java.util.concurrent.TimeUnit
 @State(Scope.Benchmark)
 class InitializedAccess {
 
-  var holder: LazyHolder = _
+  var holder: LazyHolder = uninitialized
 
   @Setup
   def prepare: Unit = {
diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala
index 8c75f6bb11a2..4e044dcaee52 100644
--- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala
+++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala
@@ -1,5 +1,6 @@
 package dotty.tools.benchmarks.lazyvals
 
+import compiletime.uninitialized
 import org.openjdk.jmh.annotations._
 import LazyVals.LazyAnyHolder
 import org.openjdk.jmh.infra.Blackhole
@@ -14,7 +15,7 @@ import java.util.concurrent.TimeUnit
 @State(Scope.Benchmark)
 class InitializedAccessAny {
 
-  var holder: LazyAnyHolder = _
+  var holder: LazyAnyHolder = uninitialized
 
   @Setup
   def prepare: Unit = {
diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala
index a9fecae6281e..4c1a0c6d7417 100644
--- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala
+++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala
@@ -1,5 +1,6 @@
 package dotty.tools.benchmarks.lazyvals
 
+import compiletime.uninitialized
 import org.openjdk.jmh.annotations._
 import LazyVals.LazyGenericHolder
 import org.openjdk.jmh.infra.Blackhole
@@ -14,7 +15,7 @@ import java.util.concurrent.TimeUnit
 @State(Scope.Benchmark)
 class InitializedAccessGeneric {
 
-  var holder: LazyGenericHolder[String] = _
+  var holder: LazyGenericHolder[String] = uninitialized
 
   @Setup
   def prepare: Unit = {
diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala
index 2a115ad63496..6ff8622a82e8 100644
--- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala
+++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala
@@ -1,5 +1,6 @@
 package dotty.tools.benchmarks.lazyvals
 
+import compiletime.uninitialized
 import org.openjdk.jmh.annotations.*
 import org.openjdk.jmh.infra.Blackhole
 import LazyVals.LazyIntHolder
@@ -14,7 +15,7 @@ import java.util.concurrent.TimeUnit
 @State(Scope.Benchmark)
 class InitializedAccessInt {
 
-  var holder: LazyIntHolder = _
+  var holder: LazyIntHolder = uninitialized
 
   @Setup
   def prepare: Unit = {
diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessMultiple.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessMultiple.scala
index 4f3c75fd920b..9416bac36c33 100644
--- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessMultiple.scala
+++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessMultiple.scala
@@ -1,5 +1,6 @@
 package dotty.tools.benchmarks.lazyvals
 
+import compiletime.uninitialized
 import org.openjdk.jmh.annotations._
 import LazyVals.LazyHolder
 import org.openjdk.jmh.infra.Blackhole
@@ -14,7 +15,7 @@ import java.util.concurrent.TimeUnit
 @State(Scope.Benchmark)
 class InitializedAccessMultiple {
 
-  var holders: Array[LazyHolder] = _
+  var holders: Array[LazyHolder] = uninitialized
 
   @Setup
   def prepare: Unit = {
diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala
index e6c6cd5eb2e3..af751d782010 100644
--- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala
+++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala
@@ -1,5 +1,6 @@
 package dotty.tools.benchmarks.lazyvals
 
+import compiletime.uninitialized
 import org.openjdk.jmh.annotations._
 import LazyVals.LazyStringHolder
 import org.openjdk.jmh.infra.Blackhole
@@ -14,7 +15,7 @@ import java.util.concurrent.TimeUnit
 @State(Scope.Benchmark)
 class InitializedAccessString {
 
-  var holder: LazyStringHolder = _
+  var holder: LazyStringHolder = uninitialized
 
   @Setup
   def prepare: Unit = {

From 90d99c36e8e9260b9f81dbe3e4eec1289e17160a Mon Sep 17 00:00:00 2001
From: Matt Bovel <matthieu@bovel.net>
Date: Fri, 19 Apr 2024 17:17:08 +0200
Subject: [PATCH 2/4] Add AnnotationsMappingBenchmark

---
 .../AnnotationsMappingBenchmark.scala         | 71 +++++++++++++++++++
 bench-micro/tests/someAnnotatedTypes.scala    | 28 ++++++++
 2 files changed, 99 insertions(+)
 create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/AnnotationsMappingBenchmark.scala
 create mode 100644 bench-micro/tests/someAnnotatedTypes.scala

diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/AnnotationsMappingBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/AnnotationsMappingBenchmark.scala
new file mode 100644
index 000000000000..310a1745171f
--- /dev/null
+++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/AnnotationsMappingBenchmark.scala
@@ -0,0 +1,71 @@
+package dotty.tools.benchmarks
+
+import org.openjdk.jmh.annotations.{Benchmark, BenchmarkMode, Fork, Level, Measurement, Mode as JMHMode, Param, Scope, Setup, State, Warmup}
+import java.util.concurrent.TimeUnit.SECONDS
+
+import dotty.tools.dotc.{Driver, Run, Compiler}
+import dotty.tools.dotc.ast.{tpd, TreeTypeMap}, tpd.{Apply, Block, Tree, TreeAccumulator, TypeApply}
+import dotty.tools.dotc.core.Annotations.{Annotation, ConcreteAnnotation, EmptyAnnotation}
+import dotty.tools.dotc.core.Contexts.{ContextBase, Context, ctx, withMode}
+import dotty.tools.dotc.core.Mode
+import dotty.tools.dotc.core.Phases.Phase
+import dotty.tools.dotc.core.Symbols.{defn, mapSymbols, Symbol}
+import dotty.tools.dotc.core.Types.{AnnotatedType, NoType, SkolemType, TermRef, Type, TypeMap}
+import dotty.tools.dotc.parsing.Parser
+import dotty.tools.dotc.typer.TyperPhase
+
+/** Measures the performance of mapping over annotated types.
+  *
+  * Run with: scala3-bench-micro / Jmh / run AnnotationsMappingBenchmark
+  */
+@Fork(value = 4)
+@Warmup(iterations = 4, time = 1, timeUnit = SECONDS)
+@Measurement(iterations = 4, time = 1, timeUnit = SECONDS)
+@BenchmarkMode(Array(JMHMode.Throughput))
+@State(Scope.Thread)
+class AnnotationsMappingBenchmark:
+  var tp: Type = null
+  var specialIntTp: Type = null
+  var context: Context = null
+  var typeFunction: Context ?=> Type => Type = null
+  var typeMap: TypeMap = null
+
+  @Param(Array("v1", "v2", "v3", "v4"))
+  var valName: String = null
+
+  @Param(Array("id", "mapInts"))
+  var typeFunctionName: String = null
+
+  @Setup(Level.Iteration)
+  def setup(): Unit =
+    val testPhase =
+      new Phase:
+        final override def phaseName = "testPhase"
+        final override def run(using ctx: Context): Unit =
+          val pkg = ctx.compilationUnit.tpdTree.symbol
+          tp = pkg.requiredClass("Test").requiredValueRef(valName).underlying
+          specialIntTp = pkg.requiredClass("Test").requiredType("SpecialInt").typeRef
+          context = ctx
+
+    val compiler =
+      new Compiler:
+        private final val baseCompiler = new Compiler()
+        final override def phases = List(List(Parser()), List(TyperPhase()), List(testPhase))
+
+    val driver =
+      new Driver:
+        final override def newCompiler(using Context): Compiler = compiler
+
+    driver.process(Array("-classpath", System.getProperty("BENCH_CLASS_PATH"), "tests/someAnnotatedTypes.scala"))
+
+    typeFunction =
+      typeFunctionName match
+        case "id"      => tp => tp
+        case "mapInts" => tp => (if tp frozen_=:= defn.IntType then specialIntTp else tp)
+        case _         => throw new IllegalArgumentException(s"Unknown type function: $typeFunctionName")
+
+    typeMap =
+      new TypeMap(using context):
+        final override def apply(tp: Type): Type = typeFunction(mapOver(tp))
+
+  @Benchmark def applyTypeMap() = typeMap.apply(tp)
diff --git a/bench-micro/tests/someAnnotatedTypes.scala b/bench-micro/tests/someAnnotatedTypes.scala
new file mode 100644
index 000000000000..8b12d4f7c2c6
--- /dev/null
+++ b/bench-micro/tests/someAnnotatedTypes.scala
@@ -0,0 +1,28 @@
+class Test:
+  class FlagAnnot extends annotation.StaticAnnotation
+  class StringAnnot(val s: String) extends annotation.StaticAnnotation
+  class LambdaAnnot(val f: Int => Boolean) extends annotation.StaticAnnotation
+
+  type SpecialInt <: Int
+
+  val v1: Int @FlagAnnot = 42
+
+  val v2: Int @StringAnnot("hello") = 42
+
+  val v3: Int @LambdaAnnot(it => it == 42) = 42
+
+  val v4: Int @LambdaAnnot(it => {
+    def g(x: Int, y: Int) = x - y + 5
+    g(it, 7) * 2 == 80
+  }) = 42
+
+  /*val v5: Int @LambdaAnnot(it => {
+    class Foo(x: Int):
+      def xPlus10 = x + 10
+      def xPlus20 = x + 20
+      def xPlus(y: Int) = x + y
+    val foo = Foo(it)
+    foo.xPlus10 - foo.xPlus20 + foo.xPlus(30) == 62
+  }) = 42*/
+
+  def main(args: Array[String]): Unit = ???

From 56fe7cfeb065bee24722985a7fba1ff64328a533 Mon Sep 17 00:00:00 2001
From: Matt Bovel <matthieu@bovel.net>
Date: Tue, 7 May 2024 10:24:50 +0200
Subject: [PATCH 3/4] Run printing tests in a separate directory

---
 compiler/test/dotty/tools/dotc/printing/PrintingTest.scala | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala b/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala
index 382c029c86e0..8a80a6978bdb 100644
--- a/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala
+++ b/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala
@@ -6,7 +6,7 @@ import scala.language.unsafeNulls
 
 import vulpix.FileDiff
 import vulpix.TestConfiguration
-import vulpix.TestConfiguration
+import vulpix.ParallelTesting
 import reporting.TestReporter
 
 import java.io._
@@ -25,7 +25,9 @@ import java.io.File
 class PrintingTest {
 
   def options(phase: String, flags: List[String]) =
-    List(s"-Xprint:$phase", "-color:never", "-nowarn", "-classpath", TestConfiguration.basicClasspath) ::: flags
+    val outDir = ParallelTesting.defaultOutputDir + "printing" + File.pathSeparator
+    File(outDir).mkdirs()
+    List(s"-Xprint:$phase", "-color:never", "-nowarn", "-d", outDir, "-classpath", TestConfiguration.basicClasspath) ::: flags
 
   private def compileFile(path: JPath, phase: String): Boolean = {
     val baseFilePath  = path.toString.stripSuffix(".scala")

From ac76938c1e596efcaffa0f5d64e224bdc8be72f0 Mon Sep 17 00:00:00 2001
From: Matt Bovel <matthieu@bovel.net>
Date: Tue, 7 May 2024 10:24:32 +0200
Subject: [PATCH 4/4] Improve mapping and pickling of annotated types
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

`Annotation.mapWith` maps an `Annotation` with a type map `tm`. As an optimization, this function first checks if `tm` would result in any change (by traversing the annotation’s argument trees with a `TreeAccumulator`) before applying `tm` to the whole annotation tree. This optimization had two problems: 1. it didn’t include type parameters, and  2. it used `frozen_=:=` to compare types, which didn’t work as expected with `NoType`. This commit fixes these issues.

Additionally, positions of trees that appear only inside `AnnotatedType` were not pickled. This commit also fixes this.
---
 .../src/dotty/tools/dotc/ast/TreeInfo.scala   | 10 +++++-
 .../dotty/tools/dotc/core/Annotations.scala   | 11 ++++---
 .../dotc/core/tasty/PositionPickler.scala     |  4 +++
 .../tools/dotc/core/tasty/TreePickler.scala   |  7 ++++
 .../tools/dotc/quoted/PickledQuotes.scala     |  2 +-
 .../dotty/tools/dotc/transform/Pickler.scala  |  2 +-
 tests/pos/annot-17939b.scala                  | 10 ++++++
 tests/pos/annot-18064.scala                   |  9 +++++
 tests/pos/annot-5789.scala                    | 10 ++++++
 tests/printing/annot-18064.check              | 16 +++++++++
 tests/printing/annot-18064.scala              |  9 +++++
 tests/printing/annot-19846b.check             | 33 +++++++++++++++++++
 tests/printing/annot-19846b.scala             |  7 ++++
 13 files changed, 123 insertions(+), 7 deletions(-)
 create mode 100644 tests/pos/annot-17939b.scala
 create mode 100644 tests/pos/annot-18064.scala
 create mode 100644 tests/pos/annot-5789.scala
 create mode 100644 tests/printing/annot-18064.check
 create mode 100644 tests/printing/annot-18064.scala
 create mode 100644 tests/printing/annot-19846b.check
 create mode 100644 tests/printing/annot-19846b.scala

diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
index 385917f9b368..5b89c9bbacd1 100644
--- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -141,9 +141,17 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
     loop(tree, Nil)
 
   /** All term arguments of an application in a single flattened list */
+  def allTermArguments(tree: Tree): List[Tree] = unsplice(tree) match {
+    case Apply(fn, args) => allArguments(fn) ::: args
+    case TypeApply(fn, args) => allArguments(fn)
+    case Block(_, expr) => allArguments(expr)
+    case _ => Nil
+  }
+
+  /** All type and term arguments of an application in a single flattened list */
   def allArguments(tree: Tree): List[Tree] = unsplice(tree) match {
     case Apply(fn, args) => allArguments(fn) ::: args
-    case TypeApply(fn, _) => allArguments(fn)
+    case TypeApply(fn, args) => allArguments(fn) ::: args
     case Block(_, expr) => allArguments(expr)
     case _ => Nil
   }
diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala
index b4cdeba4600b..d6a99b12e3b3 100644
--- a/compiler/src/dotty/tools/dotc/core/Annotations.scala
+++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala
@@ -30,8 +30,8 @@ object Annotations {
     def derivedAnnotation(tree: Tree)(using Context): Annotation =
       if (tree eq this.tree) this else Annotation(tree)
 
-    /** All arguments to this annotation in a single flat list */
-    def arguments(using Context): List[Tree] = tpd.allArguments(tree)
+    /** All term arguments of this annotation in a single flat list */
+    def arguments(using Context): List[Tree] = tpd.allTermArguments(tree)
 
     def argument(i: Int)(using Context): Option[Tree] = {
       val args = arguments
@@ -54,15 +54,18 @@ object Annotations {
      *  type, since ranges cannot be types of trees.
      */
     def mapWith(tm: TypeMap)(using Context) =
-      val args = arguments
+      val args = tpd.allArguments(tree)
       if args.isEmpty then this
       else
+        // Checks if `tm` would result in any change by applying it to types
+        // inside the annotations' arguments and checking if the resulting types
+        // are different.
         val findDiff = new TreeAccumulator[Type]:
           def apply(x: Type, tree: Tree)(using Context): Type =
             if tm.isRange(x) then x
             else
               val tp1 = tm(tree.tpe)
-              foldOver(if tp1 frozen_=:= tree.tpe then x else tp1, tree)
+              foldOver(if !tp1.exists || (tp1 frozen_=:= tree.tpe) then x else tp1, tree)
         val diff = findDiff(NoType, args)
         if tm.isRange(diff) then EmptyAnnotation
         else if diff.exists then derivedAnnotation(tm.mapOver(tree))
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala
index 86076517021a..3d8080e72a29 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala
@@ -33,6 +33,7 @@ object PositionPickler:
       pickler: TastyPickler,
       addrOfTree: TreeToAddr,
       treeAnnots: untpd.MemberDef => List[tpd.Tree],
+      typeAnnots: List[tpd.Tree],
       relativePathReference: String,
       source: SourceFile,
       roots: List[Tree],
@@ -136,6 +137,9 @@ object PositionPickler:
     }
     for (root <- roots)
       traverse(root, NoSource)
+
+    for annotTree <- typeAnnots do
+      traverse(annotTree, NoSource)
   end picklePositions
 end PositionPickler
 
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
index 6659348fb5de..7fd6444746ce 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
@@ -41,6 +41,10 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
    */
   private val annotTrees = util.EqHashMap[untpd.MemberDef, mutable.ListBuffer[Tree]]()
 
+  /** A set of annotation trees appearing in annotated types.
+   */
+  private val annotatedTypeTrees = mutable.ListBuffer[Tree]()
+
   /** A map from member definitions to their doc comments, so that later
    *  parallel comment pickling does not need to access symbols of trees (which
    *  would involve accessing symbols of named types and possibly changing phases
@@ -57,6 +61,8 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
     val ts = annotTrees.lookup(tree)
     if ts == null then Nil else ts.toList
 
+  def typeAnnots: List[Tree] = annotatedTypeTrees.toList
+
   def docString(tree: untpd.MemberDef): Option[Comment] =
     Option(docStrings.lookup(tree))
 
@@ -278,6 +284,7 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
     case tpe: AnnotatedType =>
       writeByte(ANNOTATEDtype)
       withLength { pickleType(tpe.parent, richTypes); pickleTree(tpe.annot.tree) }
+      annotatedTypeTrees += tpe.annot.tree
     case tpe: AndType =>
       writeByte(ANDtype)
       withLength { pickleType(tpe.tp1, richTypes); pickleType(tpe.tp2, richTypes) }
diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
index 6d6e2ff01ad4..67a354919d5b 100644
--- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
+++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
@@ -224,7 +224,7 @@ object PickledQuotes {
     if tree.span.exists then
       val positionWarnings = new mutable.ListBuffer[Message]()
       val reference = ctx.settings.sourceroot.value
-      PositionPickler.picklePositions(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, reference,
+      PositionPickler.picklePositions(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, treePkl.typeAnnots, reference,
         ctx.compilationUnit.source, tree :: Nil, positionWarnings)
       positionWarnings.foreach(report.warning(_))
 
diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala
index dd24f38990df..c8c071064ab8 100644
--- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala
@@ -322,7 +322,7 @@ class Pickler extends Phase {
           if tree.span.exists then
             val reference = ctx.settings.sourceroot.value
             PositionPickler.picklePositions(
-                pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, reference,
+                pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, treePkl.typeAnnots, reference,
                 unit.source, tree :: Nil, positionWarnings,
                 scratch.positionBuffer, scratch.pickledIndices)
 
diff --git a/tests/pos/annot-17939b.scala b/tests/pos/annot-17939b.scala
new file mode 100644
index 000000000000..a48f4690d0b2
--- /dev/null
+++ b/tests/pos/annot-17939b.scala
@@ -0,0 +1,10 @@
+import scala.annotation.Annotation
+class myRefined(f: ? => Boolean) extends Annotation
+
+def test(axes: Int) = true
+
+trait Tensor:
+  def mean(axes: Int): Int @myRefined(_ => test(axes))
+
+class TensorImpl() extends Tensor:
+  def mean(axes: Int) = ???
diff --git a/tests/pos/annot-18064.scala b/tests/pos/annot-18064.scala
new file mode 100644
index 000000000000..b6a67ea9ebe7
--- /dev/null
+++ b/tests/pos/annot-18064.scala
@@ -0,0 +1,9 @@
+//> using options "-Xprint:typer"
+
+class myAnnot[T]() extends annotation.Annotation
+
+trait Tensor[T]:
+  def add: Tensor[T] @myAnnot[T]()
+
+class TensorImpl[A]() extends Tensor[A]:
+  def add /* : Tensor[A] @myAnnot[A] */ = this
diff --git a/tests/pos/annot-5789.scala b/tests/pos/annot-5789.scala
new file mode 100644
index 000000000000..bdf4438c9d5d
--- /dev/null
+++ b/tests/pos/annot-5789.scala
@@ -0,0 +1,10 @@
+class Annot[T] extends scala.annotation.Annotation
+
+class D[T](val f: Int@Annot[T])
+
+object A{
+  def main(a:Array[String]) = {
+    val c = new D[Int](1)
+    c.f
+  }
+}
diff --git a/tests/printing/annot-18064.check b/tests/printing/annot-18064.check
new file mode 100644
index 000000000000..d93ddb95afee
--- /dev/null
+++ b/tests/printing/annot-18064.check
@@ -0,0 +1,16 @@
+[[syntax trees at end of                     typer]] // tests/printing/annot-18064.scala
+package <empty> {
+  class myAnnot[T >: Nothing <: Any]() extends annotation.Annotation() {
+    T
+  }
+  trait Tensor[T >: Nothing <: Any]() extends Object {
+    T
+    def add: Tensor[Tensor.this.T] @myAnnot[T]
+  }
+  class TensorImpl[A >: Nothing <: Any]() extends Object(), Tensor[
+    TensorImpl.this.A] {
+    A
+    def add: Tensor[A] @myAnnot[A] = this
+  }
+}
+
diff --git a/tests/printing/annot-18064.scala b/tests/printing/annot-18064.scala
new file mode 100644
index 000000000000..b6a67ea9ebe7
--- /dev/null
+++ b/tests/printing/annot-18064.scala
@@ -0,0 +1,9 @@
+//> using options "-Xprint:typer"
+
+class myAnnot[T]() extends annotation.Annotation
+
+trait Tensor[T]:
+  def add: Tensor[T] @myAnnot[T]()
+
+class TensorImpl[A]() extends Tensor[A]:
+  def add /* : Tensor[A] @myAnnot[A] */ = this
diff --git a/tests/printing/annot-19846b.check b/tests/printing/annot-19846b.check
new file mode 100644
index 000000000000..3f63a46c4286
--- /dev/null
+++ b/tests/printing/annot-19846b.check
@@ -0,0 +1,33 @@
+[[syntax trees at end of                     typer]] // tests/printing/annot-19846b.scala
+package <empty> {
+  class lambdaAnnot(g: () => Int) extends scala.annotation.Annotation(),
+    annotation.StaticAnnotation {
+    private[this] val g: () => Int
+  }
+  final lazy module val Test: Test = new Test()
+  final module class Test() extends Object() { this: Test.type =>
+    val y: Int = ???
+    val z:
+      Int @lambdaAnnot(
+        {
+          def $anonfun(): Int = Test.y
+          closure($anonfun)
+        }
+      )
+     = f(Test.y)
+  }
+  final lazy module val annot-19846b$package: annot-19846b$package =
+    new annot-19846b$package()
+  final module class annot-19846b$package() extends Object() {
+    this: annot-19846b$package.type =>
+    def f(x: Int):
+      Int @lambdaAnnot(
+        {
+          def $anonfun(): Int = x
+          closure($anonfun)
+        }
+      )
+     = x
+  }
+}
+
diff --git a/tests/printing/annot-19846b.scala b/tests/printing/annot-19846b.scala
new file mode 100644
index 000000000000..951a3c8116ff
--- /dev/null
+++ b/tests/printing/annot-19846b.scala
@@ -0,0 +1,7 @@
+class lambdaAnnot(g: () => Int) extends annotation.StaticAnnotation
+
+def f(x: Int): Int @lambdaAnnot(() => x) = x
+
+object Test:
+  val y: Int = ???
+  val z /* : Int @lambdaAnnot(() => y) */ = f(y)