Skip to content

Commit 81d2c61

Browse files
jrudolphlrytz
authored andcommitted
SI-3236 constant types for literal final static java fields
Since we don't parse Java expressions, fields of Java classes coming from source files never have constant types. This prevents using static java fields in annotation arguments in mixed compilation This PR assigns constant types to final static java fields if the initializer is a simple literal.
1 parent b9a16c4 commit 81d2c61

15 files changed

+234
-2
lines changed

src/compiler/scala/tools/nsc/javac/JavaParsers.scala

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,10 +567,64 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
567567

568568
def varDecl(pos: Position, mods: Modifiers, tpt: Tree, name: TermName): ValDef = {
569569
val tpt1 = optArrayBrackets(tpt)
570-
if (in.token == EQUALS && !mods.isParameter) skipTo(COMMA, SEMI)
570+
571+
/** Tries to detect final static literals syntactically and returns a constant type replacement */
572+
def optConstantTpe(): Tree = {
573+
in.nextToken()
574+
575+
def constantTpe(lit: Any): Tree =
576+
try TypeTree(ConstantType(Constant(lit)))
577+
finally in.nextToken()
578+
579+
def byType(value: Long): Tree =
580+
tpt.tpe match {
581+
case ByteTpe => constantTpe(value.toByte)
582+
case CharTpe => constantTpe(value.toChar)
583+
case ShortTpe => constantTpe(value.toShort)
584+
case IntTpe => constantTpe(value.toInt)
585+
case LongTpe => constantTpe(value.toLong)
586+
case _ => tpt1
587+
}
588+
589+
if (mods.hasFlag(Flags.STATIC) && mods.isFinal) {
590+
def lit(negate: Boolean): Tree =
591+
if (in.lookaheadToken == SEMI)
592+
in.token match {
593+
case TRUE if tpt.tpe == BooleanTpe => constantTpe(!negate)
594+
case FALSE if tpt.tpe == BooleanTpe => constantTpe(negate)
595+
case CHARLIT => byType(in.name.charAt(0))
596+
case INTLIT => byType(in.intVal(negate))
597+
case LONGLIT if tpt.tpe == LongTpe => constantTpe(in.intVal(negate))
598+
case FLOATLIT if tpt.tpe == FloatTpe => constantTpe(in.floatVal(negate).toFloat)
599+
case DOUBLELIT if tpt.tpe == DoubleTpe => constantTpe(in.floatVal(negate))
600+
case STRINGLIT =>
601+
tpt match {
602+
case Ident(TypeName("String")) => constantTpe(in.name.toString)
603+
case _ => tpt1
604+
}
605+
case _ => tpt1
606+
}
607+
else tpt1
608+
609+
in.token match {
610+
case MINUS | BANG =>
611+
in.nextToken()
612+
lit(negate = true)
613+
case other => lit(negate = false)
614+
}
615+
} else tpt1
616+
}
617+
618+
val tpt2: Tree =
619+
if (in.token == EQUALS && !mods.isParameter) {
620+
val res = optConstantTpe()
621+
skipTo(COMMA, SEMI)
622+
res
623+
} else tpt1
624+
571625
val mods1 = if (mods.isFinal) mods &~ Flags.FINAL else mods | Flags.MUTABLE
572626
atPos(pos) {
573-
ValDef(mods1, name, tpt1, blankExpr)
627+
ValDef(mods1, name, tpt2, blankExpr)
574628
}
575629
}
576630

src/compiler/scala/tools/nsc/javac/JavaScanners.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ trait JavaScanners extends ast.parser.ScannersCommon {
8989
javanme.ELSEkw -> ELSE,
9090
javanme.ENUMkw -> ENUM,
9191
javanme.EXTENDSkw -> EXTENDS,
92+
javanme.FALSEkw -> FALSE,
9293
javanme.FINALkw -> FINAL,
9394
javanme.FINALLYkw -> FINALLY,
9495
javanme.FLOATkw -> FLOAT,
@@ -118,6 +119,7 @@ trait JavaScanners extends ast.parser.ScannersCommon {
118119
javanme.THROWkw -> THROW,
119120
javanme.THROWSkw -> THROWS,
120121
javanme.TRANSIENTkw -> TRANSIENT,
122+
javanme.TRUEkw -> TRUE,
121123
javanme.TRYkw -> TRY,
122124
javanme.VOIDkw -> VOID,
123125
javanme.VOLATILEkw -> VOLATILE,

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ trait StdNames {
11031103
final val ELSEkw: TermName = kw("else")
11041104
final val ENUMkw: TermName = kw("enum")
11051105
final val EXTENDSkw: TermName = kw("extends")
1106+
final val FALSEkw: TermName = kw("false")
11061107
final val FINALkw: TermName = kw("final")
11071108
final val FINALLYkw: TermName = kw("finally")
11081109
final val FLOATkw: TermName = kw("float")
@@ -1132,6 +1133,7 @@ trait StdNames {
11321133
final val THROWkw: TermName = kw("throw")
11331134
final val THROWSkw: TermName = kw("throws")
11341135
final val TRANSIENTkw: TermName = kw("transient")
1136+
final val TRUEkw: TermName = kw("true")
11351137
final val TRYkw: TermName = kw("try")
11361138
final val VOIDkw: TermName = kw("void")
11371139
final val VOLATILEkw: TermName = kw("volatile")
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
trait AnnotationTest {
2+
@BooleanAnnotation(Constants.BooleanTrue)
3+
@ByteAnnotation(Constants.Byte)
4+
@CharAnnotation(Constants.Char)
5+
@ShortAnnotation(Constants.Short)
6+
@IntAnnotation(Constants.Int)
7+
@LongAnnotation(Constants.Long)
8+
@FloatAnnotation(Constants.Float)
9+
@DoubleAnnotation(Constants.Double)
10+
@StringAnnotation(Constants.String)
11+
def test1: Unit
12+
13+
@BooleanAnnotation(Constants.InvertedBoolean)
14+
@ByteAnnotation(Constants.NegativeByte)
15+
@ShortAnnotation(Constants.NegativeShort)
16+
@IntAnnotation(Constants.NegativeInt)
17+
@LongAnnotation(Constants.NegativeLong)
18+
@FloatAnnotation(Constants.NegativeFloat)
19+
@DoubleAnnotation(Constants.NegativeDouble)
20+
@StringAnnotation(Constants.NegativeString)
21+
def test2: Unit
22+
23+
@BooleanAnnotation(Constants.BooleanFalse)
24+
@ByteAnnotation(Constants.LiteralCharAsByte)
25+
@CharAnnotation(Constants.LiteralChar)
26+
@ShortAnnotation(Constants.LiteralCharAsShort)
27+
@IntAnnotation(Constants.LiteralCharAsInt)
28+
@LongAnnotation(Constants.LiteralCharAsLong)
29+
def test3: Unit
30+
31+
@LongAnnotation(Constants.LiteralIntAsLong)
32+
def test4: Unit
33+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface BooleanAnnotation {
6+
boolean value();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface ByteAnnotation {
6+
byte value();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface CharAnnotation {
6+
char value();
7+
}

test/files/run/t3236/Constants.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
public class Constants {
2+
public static final boolean BooleanTrue = true;
3+
public static final boolean BooleanFalse = false;
4+
public static final boolean InvertedBoolean = !true;
5+
6+
public static final byte Byte = 23;
7+
public static final byte NegativeByte = -42;
8+
public static final byte LiteralCharAsByte = 'a';
9+
10+
public static final char Char = 33;
11+
public static final char LiteralChar = 'b';
12+
13+
public static final short Short = 0x1234;
14+
public static final short NegativeShort= -0x5678;
15+
public static final short LiteralCharAsShort = 'c';
16+
17+
public static final int Int = 0xabcdef;
18+
public static final int NegativeInt = -12345678;
19+
public static final int LiteralCharAsInt = 'd';
20+
21+
public static final long Long = 0x1234567890abcdefL;
22+
public static final long NegativeLong = -0xfedcba09876L;
23+
public static final long LiteralCharAsLong = 'e';
24+
public static final long LiteralIntAsLong = 0x12345678;
25+
26+
public static final float Float = 42.232323f;
27+
public static final float NegativeFloat = -3.1415f;
28+
29+
public static final double Double = 23.4243598374594d;
30+
public static final double NegativeDouble = -42.2324358934589734859d;
31+
32+
public static final String String = "testConstant";
33+
public static final String NegativeString = "!#!$!grml%!%!$#@@@";
34+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface DoubleAnnotation {
6+
double value();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface FloatAnnotation {
6+
float value();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface IntAnnotation {
6+
int value();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface LongAnnotation {
6+
long value();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface ShortAnnotation {
6+
short value();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.lang.annotation.Retention;
2+
import java.lang.annotation.RetentionPolicy;
3+
4+
@Retention(RetentionPolicy.RUNTIME)
5+
public @interface StringAnnotation {
6+
String value();
7+
}

test/files/run/t3236/Test.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import scala.language.reflectiveCalls
2+
3+
object Test extends App {
4+
val theClass = classOf[AnnotationTest]
5+
6+
def annotation[T <: java.lang.annotation.Annotation](annotationClass: Class[T], methodName: String): T =
7+
theClass.getDeclaredMethod(methodName)
8+
.getAnnotation[T](annotationClass)
9+
10+
def check[T, U <: java.lang.annotation.Annotation { def value(): T } ](annotationClass: Class[U], methodName: String, expected: T): Unit = {
11+
val a = annotation(annotationClass, methodName)
12+
assert(a != null, s"No annotation of type $annotationClass found on method $methodName")
13+
assert(a.value() == expected, s"Actual value of annotation $a on $methodName was not of expected value $expected")
14+
}
15+
16+
check(classOf[BooleanAnnotation], "test1", Constants.BooleanTrue)
17+
check(classOf[ByteAnnotation], "test1", Constants.Byte)
18+
check(classOf[CharAnnotation], "test1", Constants.Char)
19+
check(classOf[ShortAnnotation], "test1", Constants.Short)
20+
check(classOf[IntAnnotation], "test1", Constants.Int)
21+
check(classOf[LongAnnotation], "test1", Constants.Long)
22+
check(classOf[FloatAnnotation], "test1", Constants.Float)
23+
check(classOf[DoubleAnnotation], "test1", Constants.Double)
24+
check(classOf[StringAnnotation], "test1", Constants.String)
25+
26+
check(classOf[BooleanAnnotation], "test2", Constants.InvertedBoolean)
27+
check(classOf[ByteAnnotation], "test2", Constants.NegativeByte)
28+
// no negative char possible
29+
check(classOf[ShortAnnotation], "test2", Constants.NegativeShort)
30+
check(classOf[IntAnnotation], "test2", Constants.NegativeInt)
31+
check(classOf[LongAnnotation], "test2", Constants.NegativeLong)
32+
check(classOf[FloatAnnotation], "test2", Constants.NegativeFloat)
33+
check(classOf[DoubleAnnotation], "test2", Constants.NegativeDouble)
34+
check(classOf[StringAnnotation], "test2", Constants.NegativeString)
35+
36+
check(classOf[BooleanAnnotation], "test3", Constants.BooleanFalse)
37+
check(classOf[ByteAnnotation], "test3", Constants.LiteralCharAsByte)
38+
check(classOf[CharAnnotation], "test3", Constants.LiteralChar)
39+
check(classOf[ShortAnnotation], "test3", Constants.LiteralCharAsShort)
40+
check(classOf[IntAnnotation], "test3", Constants.LiteralCharAsInt)
41+
check(classOf[LongAnnotation], "test3", Constants.LiteralCharAsLong)
42+
43+
check(classOf[LongAnnotation], "test4", Constants.LiteralIntAsLong)
44+
}

0 commit comments

Comments
 (0)