From 4d772b48eed63819e1f758f6d8a11818ec909c8b Mon Sep 17 00:00:00 2001 From: Arnout Engelen Date: Thu, 28 Apr 2022 17:06:36 +0200 Subject: [PATCH] Keep annotation order This change makes sure non-repeated annotations are kept in the order they were found in the source code. The motivation is not necessarily to have them in the original order, but to have them in an order that is deterministic across rebuilds (potentially even across different machines), for reasons discussed further in #7661 and the corresponding scala/scala-dev#405 An 'integration test' `Annotations.scala` was added in `tests/pos` to be picked up by `IdempotencyCheck.scala`, but unfortunately I haven't successfully reproduced the nondeterminism that way. I didn't see an obvious place for a 'unit test' of this code, I'd be happy to add one when someone can recommend a good place to put it. This is basically the dotty equivalent of https://github.com/scala/scala/commit/954c5d32d71a43b141be546877b01183a994a1b2 Fixes #14743 --- .../tools/dotc/transform/RepeatableAnnotations.scala | 12 +++++++++++- tests/pos/Annotations.scala | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/pos/Annotations.scala diff --git a/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala b/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala index 24c46cd98e4c..a9957815d5a5 100644 --- a/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala +++ b/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala @@ -11,6 +11,8 @@ import Constants._ import Types._ import Decorators._ +import scala.collection.mutable + class RepeatableAnnotations extends MiniPhase: override def phaseName: String = RepeatableAnnotations.name @@ -28,7 +30,7 @@ class RepeatableAnnotations extends MiniPhase: tree private def aggregateAnnotations(annotations: Seq[Annotation])(using Context): List[Annotation] = - val annsByType = annotations.groupBy(_.symbol) + val annsByType = stableGroupBy[Annotation, Symbols.Symbol](annotations, _.symbol) annsByType.flatMap { case (_, a :: Nil) => a :: Nil case (sym, anns) if sym.derivesFrom(defn.ClassfileAnnotationClass) => @@ -50,6 +52,14 @@ class RepeatableAnnotations extends MiniPhase: case (_, anns) => anns }.toList + private def stableGroupBy[A, K](ins: Seq[A], f: A => K): scala.collection.MapView[K, List[A]] = + val out = new mutable.LinkedHashMap[K, mutable.ListBuffer[A]]() + for (in <- ins) { + val buffer = out.getOrElseUpdate(f(in), new mutable.ListBuffer) + buffer += in + } + out.view.mapValues(_.toList) + object RepeatableAnnotations: val name: String = "repeatableAnnotations" val description: String = "aggregate repeatable annotations" diff --git a/tests/pos/Annotations.scala b/tests/pos/Annotations.scala new file mode 100644 index 000000000000..322cf2eafb06 --- /dev/null +++ b/tests/pos/Annotations.scala @@ -0,0 +1,8 @@ +package foo.bar + +import jdk.jfr.Enabled + +@Enabled +@Deprecated +final class Annotations { +}