Skip to content

Commit 9a1b5e1

Browse files
committed
Correctly handle meta-annotations for ctor param val/vars.
As a consequence, this fixes #9881.
1 parent 2788c99 commit 9a1b5e1

File tree

4 files changed

+48
-4
lines changed

4 files changed

+48
-4
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -793,10 +793,8 @@ object desugar {
793793
val originalVparamsIt = originalVparamss.iterator.flatten
794794
derivedVparamss match {
795795
case first :: rest =>
796-
// Annotations on the class _value_ parameters are not set on the parameter accessors
797-
def mods(vdef: ValDef) = vdef.mods.withAnnotations(Nil)
798-
first.map(_.withMods(mods(originalVparamsIt.next()) | caseAccessor)) ++
799-
rest.flatten.map(_.withMods(mods(originalVparamsIt.next())))
796+
first.map(_.withMods(originalVparamsIt.next().mods | caseAccessor)) ++
797+
rest.flatten.map(_.withMods(originalVparamsIt.next().mods))
800798
case _ =>
801799
Nil
802800
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,13 +933,18 @@ class Definitions {
933933
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")
934934
@tu lazy val FieldMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.field")
935935
@tu lazy val GetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.getter")
936+
@tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param")
936937
@tu lazy val SetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.setter")
937938
@tu lazy val ShowAsInfixAnnot: ClassSymbol = requiredClass("scala.annotation.showAsInfix")
938939
@tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface")
939940
@tu lazy val InfixAnnot: ClassSymbol = requiredClass("scala.annotation.infix")
940941
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
941942
@tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs")
942943

944+
// A list of meta-annotations that are relevant for fields and accessors
945+
@tu lazy val FieldAccessorMetaAnnots: Set[Symbol] =
946+
Set(FieldMetaAnnot, GetterMetaAnnot, ParamMetaAnnot, SetterMetaAnnot)
947+
943948
// A list of annotations that are commonly used to indicate that a field/method argument or return
944949
// type is not null. These annotations are used by the nullification logic in JavaNullInterop to
945950
// improve the precision of type nullification.

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
147147
case tree: ValOrDefDef if !tree.symbol.is(Synthetic) =>
148148
checkInferredWellFormed(tree.tpt)
149149
val sym = tree.symbol
150+
if sym.is(Method) then
151+
if sym.isSetter then
152+
removeUnwantedAnnotations(sym, defn.SetterMetaAnnot, NoSymbol, keepIfNoRelevantAnnot = false)
153+
else
154+
if sym.is(Param) then
155+
removeUnwantedAnnotations(sym, defn.ParamMetaAnnot, NoSymbol, keepIfNoRelevantAnnot = true)
156+
else
157+
removeUnwantedAnnotations(sym, defn.GetterMetaAnnot, defn.FieldMetaAnnot, keepIfNoRelevantAnnot = !sym.is(ParamAccessor))
150158
if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then
151159
if !sym.owner.unforcedDecls.exists(p => !p.isScala2Macro && p.name == sym.name && p.signature == sym.signature)
152160
// Allow scala.reflect.materializeClassTag to be able to compile scala/reflect/package.scala
@@ -168,6 +176,18 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
168176
=> Checking.checkAppliedTypesIn(tree)
169177
case _ =>
170178

179+
private def removeUnwantedAnnotations(sym: Symbol, metaAnnotSym: Symbol,
180+
metaAnnotSymBackup: Symbol, keepIfNoRelevantAnnot: Boolean)(using Context): Unit =
181+
def shouldKeep(annot: Annotation): Boolean =
182+
val annotSym = annot.symbol
183+
annotSym.hasAnnotation(metaAnnotSym)
184+
|| annotSym.hasAnnotation(metaAnnotSymBackup)
185+
|| (keepIfNoRelevantAnnot && {
186+
!annotSym.annotations.exists(metaAnnot => defn.FieldAccessorMetaAnnots.contains(metaAnnot.symbol))
187+
})
188+
if sym.annotations.nonEmpty then
189+
sym.filterAnnotations(shouldKeep(_))
190+
171191
private def transformSelect(tree: Select, targs: List[Tree])(using Context): Tree = {
172192
val qual = tree.qualifier
173193
qual.symbol.moduleClass.denot match {

tests/run/i9881.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import java.io._
2+
3+
class Config(s: String)
4+
5+
class ConfigException(@transient val config: Config = null)
6+
extends java.io.Serializable
7+
8+
object Test {
9+
def main(args: Array[String]): Unit = {
10+
val e = new ConfigException(new Config("not serializable"))
11+
val byteStream = new ByteArrayOutputStream()
12+
val objectStream = new ObjectOutputStream(byteStream)
13+
objectStream.writeObject(e)
14+
objectStream.close()
15+
val bytes = byteStream.toByteArray()
16+
val inStream = new ByteArrayInputStream(bytes)
17+
val inObjectStream = new ObjectInputStream(inStream)
18+
val copy = inObjectStream.readObject()
19+
inObjectStream.close()
20+
}
21+
}

0 commit comments

Comments
 (0)