From 8a6c719f7b435bf833616da6b6a3d9b65ad86277 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 10 Oct 2014 16:25:26 +0200 Subject: [PATCH] Add the right constructor to Java annotations --- src/dotty/tools/dotc/core/Flags.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 2 +- .../dotc/core/pickling/ClassfileParser.scala | 40 +++++++++++++++++++ tests/pos/annot.scala | 11 +++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tests/pos/annot.scala diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index c527cef62209..08b8fb87dd6e 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -318,7 +318,7 @@ object Flags { /** An unpickled Scala 2.x class */ final val Scala2x = typeFlag(27, "") - /** A method that has default params */ // TODO: drop + /** A method that has default params */ final val DefaultParameterized = termFlag(28, "") /** Symbol is initialized to the default value, e.g. var x: T = _ */ diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 474958b86575..592a767c139d 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1804,7 +1804,7 @@ object Types { if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (restpe eq this.resultType)) this else copy(paramNames, paramBounds, restpe) - def copy(paramNames: List[TypeName], paramBounds: List[TypeBounds], restpe: Type)(implicit ctx: Context) = + def copy(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, restpe: Type)(implicit ctx: Context) = PolyType(paramNames)( x => paramBounds mapConserve (_.subst(this, x).bounds), x => restpe.subst(this, x)) diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 59658c9c14f0..c9c6fa5ec5f3 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -127,6 +127,7 @@ class ClassfileParser( for (i <- 0 until in.nextChar) parseMember(method = false) for (i <- 0 until in.nextChar) parseMember(method = true) classInfo = parseAttributes(classRoot.symbol, classInfo) + if (isAnnotation) addAnnotationConstructor(classInfo) setClassInfo(classRoot, classInfo) setClassInfo(moduleRoot, staticInfo) } @@ -539,6 +540,45 @@ class ClassfileParser( newType } + /** Add a synthetic constructor and potentially also default getters which + * reflects the fields of the annotation with given `classInfo`. + * Annotations in Scala are assumed to get all their arguments as constructor + * parameters. For Java annotations we need to fake it by making up the constructor. + * Note that default getters have type Nothing. That's OK because we need + * them only to signal that the corresponding parameter is optional. + */ + def addAnnotationConstructor(classInfo: Type, tparams: List[Symbol] = Nil)(implicit ctx: Context): Unit = { + def addDefaultGetter(attr: Symbol, n: Int) = + ctx.newSymbol( + owner = moduleRoot.symbol, + name = nme.CONSTRUCTOR.defaultGetterName(n), + flags = attr.flags & Flags.AccessFlags, + info = defn.NothingType).entered + + classInfo match { + case classInfo @ TempPolyType(tparams, restpe) if tparams.isEmpty => + addAnnotationConstructor(restpe, tparams) + case classInfo: TempClassInfoType => + val attrs = classInfo.decls.toList.filter(_.isTerm) + val targs = tparams.map(_.typeRef) + val methType = MethodType( + attrs.map(_.name.asTermName), + attrs.map(_.info.resultType), + classRoot.typeRef.appliedTo(targs)) + val constr = ctx.newSymbol( + owner = classRoot.symbol, + name = nme.CONSTRUCTOR, + flags = Flags.Synthetic, + info = if (tparams.isEmpty) methType else TempPolyType(tparams, methType) + ).entered + for ((attr, i) <- attrs.zipWithIndex) + if (attr.hasAnnotation(defn.AnnotationDefaultAnnot)) { + constr.setFlag(Flags.HasDefaultParams) + addDefaultGetter(attr, i) + } + } + } + /** Enter own inner classes in the right scope. It needs the scopes to be set up, * and implicitly current class' superclasses. */ diff --git a/tests/pos/annot.scala b/tests/pos/annot.scala new file mode 100644 index 000000000000..cda24e05daaf --- /dev/null +++ b/tests/pos/annot.scala @@ -0,0 +1,11 @@ +import java.beans.Transient + +class Test { + + @SuppressWarnings(Array("hi")) def foo() = ??? + + @Transient(false) def bar = ??? + + @Transient() def baz = ??? +} +