Skip to content

Commit f66fb55

Browse files
committed
port constant types for literal final static java fields
1 parent a923e6a commit f66fb55

File tree

3 files changed

+115
-2
lines changed

3 files changed

+115
-2
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ object StdNames {
807807
final val ELSEkw: N = kw("else")
808808
final val ENUMkw: N = kw("enum")
809809
final val EXTENDSkw: N = kw("extends")
810+
final val FALSEkw: N = kw("false")
810811
final val FINALkw: N = kw("final")
811812
final val FINALLYkw: N = kw("finally")
812813
final val FLOATkw: N = kw("float")
@@ -836,6 +837,7 @@ object StdNames {
836837
final val THROWkw: N = kw("throw")
837838
final val THROWSkw: N = kw("throws")
838839
final val TRANSIENTkw: N = kw("transient")
840+
final val TRUEkw: N = kw("true")
839841
final val TRYkw: N = kw("try")
840842
final val VOIDkw: N = kw("void")
841843
final val VOLATILEkw: N = kw("volatile")

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,9 +601,53 @@ object JavaParsers {
601601

602602
def varDecl(mods: Modifiers, tpt: Tree, name: TermName): ValDef = {
603603
val tpt1 = optArrayBrackets(tpt)
604-
if (in.token == EQUALS && !mods.is(Flags.Param)) skipTo(COMMA, SEMI)
604+
/** Tries to detect final static literals syntactically and returns a constant type replacement */
605+
def optConstantTpe(): Tree = {
606+
def constantTpe(const: Constant): Tree = TypeTree(ConstantType(const))
607+
608+
def forConst(const: Constant): Tree = {
609+
if (in.token != SEMI) tpt1
610+
else {
611+
def isStringTyped = tpt1 match {
612+
case Ident(n: TypeName) => "String" == n.toString
613+
case _ => false
614+
}
615+
if (const.tag == Constants.StringTag && isStringTyped) constantTpe(const)
616+
else tpt1 match {
617+
case TypedSplice(tpt2) =>
618+
if (const.tag == Constants.BooleanTag || const.isNumeric) {
619+
//for example, literal 'a' is ok for float. 127 is ok for byte, but 128 is not.
620+
val converted = const.convertTo(tpt2.tpe)
621+
if (converted == null) tpt1
622+
else constantTpe(converted)
623+
}
624+
else tpt1
625+
case _ => tpt1
626+
}
627+
}
628+
}
629+
630+
in.nextToken() // EQUALS
631+
if (mods.is(Flags.JavaStatic) && mods.is(Flags.Final)) {
632+
val neg = in.token match {
633+
case MINUS | BANG => in.nextToken(); true
634+
case _ => false
635+
}
636+
tryLiteral(neg).map(forConst).getOrElse(tpt1)
637+
}
638+
else tpt1
639+
}
640+
641+
val tpt2: Tree =
642+
if (in.token == EQUALS && !mods.is(Flags.Param)) {
643+
val res = optConstantTpe()
644+
skipTo(COMMA, SEMI)
645+
res
646+
}
647+
else tpt1
648+
605649
val mods1 = if (mods.is(Flags.Final)) mods else mods | Flags.Mutable
606-
ValDef(name, tpt1, if (mods.is(Flags.Param)) EmptyTree else unimplementedExpr).withMods(mods1)
650+
ValDef(name, tpt2, if (mods.is(Flags.Param)) EmptyTree else unimplementedExpr).withMods(mods1)
607651
}
608652

609653
def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match {
@@ -881,6 +925,25 @@ object JavaParsers {
881925
case _ => in.nextToken(); syntaxError("illegal start of type declaration", skipIt = true); List(errorTypeTree)
882926
}
883927

928+
def tryLiteral(negate: Boolean = false): Option[Constant] = {
929+
val l = in.token match {
930+
case TRUE => !negate
931+
case FALSE => negate
932+
case CHARLIT => in.strVal.charAt(0)
933+
case INTLIT => in.intVal(negate).toInt
934+
case LONGLIT => in.intVal(negate)
935+
case FLOATLIT => in.floatVal(negate).toFloat
936+
case DOUBLELIT => in.floatVal(negate)
937+
case STRINGLIT => in.strVal
938+
case _ => null
939+
}
940+
if (l == null) None
941+
else {
942+
in.nextToken()
943+
Some(Constant(l))
944+
}
945+
}
946+
884947
/** CompilationUnit ::= [package QualId semi] TopStatSeq
885948
*/
886949
def compilationUnit(): Tree = {

compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,54 @@ object JavaScanners {
479479
setStrVal()
480480
}
481481

482+
/** convert name to long value
483+
*/
484+
def intVal(negated: Boolean): Long =
485+
if (token == CHARLIT && !negated)
486+
if (strVal.length > 0) strVal.charAt(0).toLong else 0
487+
else {
488+
var value: Long = 0
489+
val divider = if (base == 10) 1 else 2
490+
val limit: Long =
491+
if (token == LONGLIT) Long.MaxValue else Int.MaxValue
492+
var i = 0
493+
val len = strVal.length
494+
while (i < len) {
495+
val d = digit2int(strVal.charAt(i), base)
496+
if (d < 0) {
497+
error("malformed integer number")
498+
return 0
499+
}
500+
if (value < 0 ||
501+
limit / (base / divider) < value ||
502+
limit - (d / divider) < value * (base / divider) &&
503+
!(negated && limit == value * base - 1 + d)) {
504+
error("integer number too large")
505+
return 0
506+
}
507+
value = value * base + d
508+
i += 1
509+
}
510+
if (negated) -value else value
511+
}
512+
513+
/** convert name, base to double value
514+
*/
515+
def floatVal(negated: Boolean): Double = {
516+
val limit: Double =
517+
if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue
518+
try {
519+
val value: Double = java.lang.Double.valueOf(strVal.toString).doubleValue()
520+
if (value > limit)
521+
error("floating point number too large")
522+
if (negated) -value else value
523+
} catch {
524+
case _: NumberFormatException =>
525+
error("malformed floating point number")
526+
0.0
527+
}
528+
}
529+
482530
/** read a number into name and set base
483531
*/
484532
protected def getNumber(): Unit = {

0 commit comments

Comments
 (0)