Skip to content

Commit ab257d6

Browse files
committed
Don't use a special representation for Java enum values
In Java, annotation arguments must be constants, so enum values are treated specially. In Scala, annotation arguments can be whatever tree we want, so we don't really need to represent them as `Literal(Constant(enumValueSymbol))` and can just use a `TermRef` to the enum value instead. This commit implements this change, this has several advantages compared to the status quo: - The handling of Java enum values and Scala enum values is now less different. - We can drop the handling of symbols in `Constant` - ... and therefore remove one tag from Tasty. - In turn, this means we don't have to worry about how to expose this in tasty-reflect. This commit breaks the Tasty format and therefore bump its major version to 24.
1 parent 94970a0 commit ab257d6

File tree

11 files changed

+35
-66
lines changed

11 files changed

+35
-66
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
488488
// ---------------- emitting constant values ----------------
489489

490490
/*
491-
* For const.tag in {ClazzTag, EnumTag}
491+
* For ClazzTag:
492492
* must-single-thread
493493
* Otherwise it's safe to call from multiple threads.
494494
*/
@@ -527,22 +527,6 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
527527
else
528528
mnode.visitLdcInsn(tp.toASMType)
529529

530-
case EnumTag =>
531-
val sym = const.symbolValue
532-
val ownerName = internalName(sym.owner)
533-
val fieldName = sym.javaSimpleName
534-
val underlying = sym.info match { // TODO: Is this actually necessary? Could it be replaced by a call to widen?
535-
case t: TypeProxy => t.underlying
536-
case t => t
537-
}
538-
val fieldDesc = toTypeKind(underlying).descriptor
539-
mnode.visitFieldInsn(
540-
asm.Opcodes.GETSTATIC,
541-
ownerName,
542-
fieldName,
543-
fieldDesc
544-
)
545-
546530
case _ => abort(s"Unknown constant value: $const")
547531
}
548532
}

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -377,14 +377,10 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
377377
assert(const.value != null, const) // TODO this invariant isn't documented in `case class Constant`
378378
av.visit(name, const.stringValue) // `stringValue` special-cases null, but that execution path isn't exercised for a const with StringTag
379379
case ClazzTag => av.visit(name, typeToTypeKind(TypeErasure.erasure(const.typeValue))(bcodeStore)(innerClasesStore).toASMType)
380-
case EnumTag =>
381-
val edesc = innerClasesStore.typeDescriptor(const.tpe) // the class descriptor of the enumeration class.
382-
val evalue = const.symbolValue.javaSimpleName // value the actual enumeration value.
383-
av.visitEnum(name, edesc, evalue)
384380
}
385381
case Ident(nme.WILDCARD) =>
386382
// An underscore argument indicates that we want to use the default value for this parameter, so do not emit anything
387-
case t: tpd.RefTree if t.symbol.denot.owner.isAllOf(JavaEnumTrait) =>
383+
case t: tpd.RefTree if t.symbol.owner.linkedClass.isAllOf(JavaEnumTrait) =>
388384
val edesc = innerClasesStore.typeDescriptor(t.tpe) // the class descriptor of the enumeration class.
389385
val evalue = t.symbol.javaSimpleName // value the actual enumeration value.
390386
av.visitEnum(name, edesc, evalue)
@@ -454,7 +450,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
454450

455451
private def retentionPolicyOf(annot: Annotation): Symbol =
456452
annot.tree.tpe.typeSymbol.getAnnotation(AnnotationRetentionAttr).
457-
flatMap(_.argumentConstant(0).map(_.symbolValue)).getOrElse(AnnotationRetentionClassAttr)
453+
flatMap(_.argument(0).map(_.tpe.termSymbol)).getOrElse(AnnotationRetentionClassAttr)
458454

459455
private def assocsFromApply(tree: Tree): List[(Name, Tree)] = {
460456
tree match {

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,8 +1223,6 @@ class JSCodeGen()(using genCtx: Context) {
12231223
js.Null()
12241224
case ClazzTag =>
12251225
genClassConstant(value.typeValue)
1226-
case EnumTag =>
1227-
genLoadStaticField(value.symbolValue)
12281226
}
12291227

12301228
case Block(stats, expr) =>

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
460460
def ref(tp: NamedType)(using Context): Tree =
461461
TypedSplice(tpd.ref(tp))
462462

463+
def ref(sym: Symbol)(using Context): Tree =
464+
TypedSplice(tpd.ref(sym))
465+
463466
def rawRef(tp: NamedType)(using Context): Tree =
464467
if tp.typeParams.isEmpty then ref(tp)
465468
else AppliedTypeTree(ref(tp), tp.typeParams.map(_ => WildcardTypeBoundsTree()))

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ object Constants {
2020
final val StringTag = 10
2121
final val NullTag = 11
2222
final val ClazzTag = 12
23-
// For supporting java enumerations inside java annotations (see ClassfileParser)
24-
final val EnumTag = 13
2523

2624
class Constant(val value: Any, val tag: Int) extends printing.Showable with Product1[Any] {
2725
import java.lang.Double.doubleToRawLongBits
@@ -50,7 +48,6 @@ object Constants {
5048
case StringTag => defn.StringType
5149
case NullTag => defn.NullType
5250
case ClazzTag => defn.ClassType(typeValue)
53-
case EnumTag => defn.EnumType(symbolValue)
5451
}
5552

5653
/** We need the equals method to take account of tags as well as values.
@@ -190,7 +187,6 @@ object Constants {
190187
def toText(printer: Printer): Text = printer.toText(this)
191188

192189
def typeValue: Type = value.asInstanceOf[Type]
193-
def symbolValue: Symbol = value.asInstanceOf[Symbol]
194190

195191
/**
196192
* Consider two `NaN`s to be identical, despite non-equality
@@ -237,7 +233,6 @@ object Constants {
237233
def apply(x: String): Constant = new Constant(x, StringTag)
238234
def apply(x: Char): Constant = new Constant(x, CharTag)
239235
def apply(x: Type): Constant = new Constant(x, ClazzTag)
240-
def apply(x: Symbol): Constant = new Constant(x, EnumTag)
241236
def apply(value: Any): Constant =
242237
new Constant(value,
243238
value match {
@@ -253,7 +248,6 @@ object Constants {
253248
case x: String => StringTag
254249
case x: Char => CharTag
255250
case x: Type => ClazzTag
256-
case x: Symbol => EnumTag
257251
}
258252
)
259253

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ class ClassfileParser(
282282

283283
val isVarargs = denot.is(Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0
284284
denot.info = pool.getType(in.nextChar, isVarargs)
285-
if (isEnum) denot.info = ConstantType(Constant(sym))
286285
if (isConstructor) normalizeConstructorParams()
287286
denot.info = translateTempPoly(parseAttributes(sym, denot.info, isVarargs))
288287
if (isConstructor) normalizeConstructorInfo()
@@ -525,17 +524,13 @@ class ClassfileParser(
525524
case CLASS_TAG =>
526525
if (skip) None else Some(lit(Constant(pool.getType(index))))
527526
case ENUM_TAG =>
528-
val t = pool.getType(index)
529-
val n = pool.getName(in.nextChar)
530-
val module = t.typeSymbol.companionModule
531-
val s = module.info.decls.lookup(n)
527+
val enumClassTp = pool.getType(index)
528+
val enumCaseName = pool.getName(in.nextChar)
532529
if (skip)
533530
None
534-
else if (s != NoSymbol)
535-
Some(lit(Constant(s)))
536531
else {
537-
report.warning(s"""While parsing annotations in ${in.file}, could not find $n in enum $module.\nThis is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (SI-7014).""")
538-
None
532+
val enumModuleClass = enumClassTp.classSymbol.companionModule
533+
Some(Select(ref(enumModuleClass), enumCaseName))
539534
}
540535
case ARRAY_TAG =>
541536
val arr = new ArrayBuffer[Tree]()

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,6 @@ class TreePickler(pickler: TastyPickler) {
152152
case ClazzTag =>
153153
writeByte(CLASSconst)
154154
pickleType(c.typeValue)
155-
case EnumTag =>
156-
writeByte(ENUMconst)
157-
pickleType(c.symbolValue.termRef)
158155
}
159156

160157
def pickleVariances(tp: Type)(using Context): Unit = tp match

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,6 @@ class TreeUnpickler(reader: TastyReader,
286286
Constant(null)
287287
case CLASSconst =>
288288
Constant(readType())
289-
case ENUMconst =>
290-
Constant(readTermRef().termSymbol)
291289
}
292290

293291
/** Read a type */

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
735735
val supertpe = readTypeRef()
736736
SuperType(thistpe, supertpe)
737737
case CONSTANTtpe =>
738-
ConstantType(readConstantRef())
738+
readConstantRef() match
739+
case c: Constant => ConstantType(c)
740+
case tp: TermRef => tp
739741
case TYPEREFtpe =>
740742
var pre = readPrefix()
741743
val sym = readSymbolRef()
@@ -822,7 +824,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
822824
errorBadSignature("bad type tag: " + tag)
823825

824826
/** Read a constant */
825-
protected def readConstant()(using Context): Constant = {
827+
protected def readConstant()(using Context): Constant | TermRef = {
826828
val tag = readByte().toInt
827829
val len = readNat()
828830
(tag: @switch) match {
@@ -838,7 +840,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
838840
case LITERALstring => Constant(readNameRef().toString)
839841
case LITERALnull => Constant(null)
840842
case LITERALclass => Constant(readTypeRef())
841-
case LITERALenum => Constant(readSymbolRef())
843+
case LITERALenum => readSymbolRef().termRef
842844
case _ => noSuchConstantTag(tag, len)
843845
}
844846
}
@@ -881,7 +883,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
881883

882884
protected def readNameRef()(using Context): Name = at(readNat(), () => readName())
883885
protected def readTypeRef()(using Context): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... ()
884-
protected def readConstantRef()(using Context): Constant = at(readNat(), () => readConstant())
886+
protected def readConstantRef()(using Context): Constant | TermRef = at(readNat(), () => readConstant())
885887

886888
protected def readTypeNameRef()(using Context): TypeName = readNameRef().toTypeName
887889
protected def readTermNameRef()(using Context): TermName = readNameRef().toTermName
@@ -896,7 +898,11 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
896898
*/
897899
protected def readAnnotArg(i: Int)(using Context): Tree = bytes(index(i)) match {
898900
case TREE => at(i, () => readTree())
899-
case _ => Literal(at(i, () => readConstant()))
901+
case _ => at(i, () =>
902+
readConstant() match
903+
case c: Constant => Literal(c)
904+
case tp: TermRef => ref(tp)
905+
)
900906
}
901907

902908
/** Read a ClassfileAnnotArg (argument to a classfile annotation)
@@ -1208,7 +1214,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
12081214
Ident(symbol.namedType)
12091215

12101216
case LITERALtree =>
1211-
Literal(readConstantRef())
1217+
readConstantRef() match
1218+
case c: Constant => Literal(c)
1219+
case tp: TermRef => ref(tp)
12121220

12131221
case TYPEtree =>
12141222
TypeTree(tpe)

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ class PlainPrinter(_ctx: Context) extends Printer {
528528
case ClazzTag => "classOf[" ~ toText(const.typeValue) ~ "]"
529529
case CharTag => literalText(s"'${escapedChar(const.charValue)}'")
530530
case LongTag => literalText(const.longValue.toString + "L")
531-
case EnumTag => literalText(const.symbolValue.name.toString)
532531
case _ => literalText(String.valueOf(const.value))
533532
}
534533

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ Standard-Section: "ASTs" TopLevelStat*
141141
STRINGconst NameRef -- A string literal
142142
NULLconst -- null
143143
CLASSconst Type -- classOf[Type]
144-
ENUMconst Path -- An enum constant
145144
146145
Type = Path -- Paths represent both types and terms
147146
TYPEREFdirect sym_ASTRef -- A reference to a local symbol (without a prefix). Reference is to definition node of symbol.
@@ -254,7 +253,7 @@ Standard Section: "Comments" Comment*
254253
object TastyFormat {
255254

256255
final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F)
257-
val MajorVersion: Int = 23
256+
val MajorVersion: Int = 24
258257
val MinorVersion: Int = 0
259258

260259
/** Tags used to serialize names, should update [[nameTagToString]] if a new constant is added */
@@ -387,17 +386,16 @@ object TastyFormat {
387386
final val THIS = 80
388387
final val QUALTHIS = 81
389388
final val CLASSconst = 82
390-
final val ENUMconst = 83
391-
final val BYNAMEtype = 84
392-
final val BYNAMEtpt = 85
393-
final val NEW = 86
394-
final val THROW = 87
395-
final val IMPLICITarg = 88
396-
final val PRIVATEqualified = 89
397-
final val PROTECTEDqualified = 90
398-
final val RECtype = 91
399-
final val SINGLETONtpt = 92
400-
final val BOUNDED = 93
389+
final val BYNAMEtype = 83
390+
final val BYNAMEtpt = 84
391+
final val NEW = 85
392+
final val THROW = 86
393+
final val IMPLICITarg = 87
394+
final val PRIVATEqualified = 88
395+
final val PROTECTEDqualified = 89
396+
final val RECtype = 90
397+
final val SINGLETONtpt = 91
398+
final val BOUNDED = 92
401399

402400
// Cat. 4: tag Nat AST
403401

@@ -651,7 +649,6 @@ object TastyFormat {
651649
case QUALTHIS => "QUALTHIS"
652650
case SUPER => "SUPER"
653651
case CLASSconst => "CLASSconst"
654-
case ENUMconst => "ENUMconst"
655652
case SINGLETONtpt => "SINGLETONtpt"
656653
case SUPERtype => "SUPERtype"
657654
case TERMREFin => "TERMREFin"

0 commit comments

Comments
 (0)