Skip to content

Commit 83788d9

Browse files
Merge pull request #6629 from dotty-staging/java-enums
Java enums
2 parents 76e954e + 229c451 commit 83788d9

File tree

12 files changed

+174
-11
lines changed

12 files changed

+174
-11
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes {
224224
if (sym.isVarargsMethod) ACC_VARARGS else 0,
225225
if (sym.isSynchronized) ACC_SYNCHRONIZED else 0,
226226
if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0,
227-
if (sym.isJavaEnum) asm.Opcodes.ACC_ENUM else 0
227+
if (sym.isEnum) asm.Opcodes.ACC_ENUM else 0
228228
)
229229
}
230230

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ abstract class BackendInterface extends BackendInterfaceDefinitions {
512512
def isJavaDefaultMethod: Boolean
513513
def isClassConstructor: Boolean
514514
def isSerializable: Boolean
515-
def isJavaEnum: Boolean
515+
def isEnum: Boolean
516516

517517
/**
518518
* True for module classes of modules that are top-level or owned only by objects. Module classes

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
703703
def shouldEmitForwarders: Boolean =
704704
(sym is Flags.Module) && sym.isStatic
705705
def isJavaEntryPoint: Boolean = CollectEntryPoints.isJavaEntryPoint(sym)
706-
def isJavaEnum = sym.derivesFromJavaEnum
706+
def isEnum = sym.is(Flags.Enum)
707707

708708
def isClassConstructor: Boolean = toDenot(sym).isClassConstructor
709709
def isSerializable: Boolean = toDenot(sym).isSerializable

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@ object DesugarEnums {
8484
/** The following lists of definitions for an enum type E:
8585
*
8686
* private val $values = new EnumValues[E]
87+
* def values = $values.values.toArray
8788
* def valueOf($name: String) =
8889
* try $values.fromName($name) catch
8990
* {
9091
* case ex$:NoSuchElementException =>
9192
* throw new IllegalArgumentException("key not found: ".concat(name))
9293
* }
93-
* def values = $values.values.toArray
9494
*/
9595
private def enumScaffolding(implicit ctx: Context): List[Tree] = {
9696
val valuesDef =
@@ -286,7 +286,7 @@ object DesugarEnums {
286286
val toStringDef = toStringMethLit(name.toString)
287287
val impl1 = cpy.Template(impl)(body = List(ordinalDef, toStringDef) ++ registerCall)
288288
.withAttachment(ExtendsSingletonMirror, ())
289-
val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final)
289+
val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | EnumValue)
290290
flatTree(scaffolding ::: vdef :: Nil).withSpan(span)
291291
}
292292
}
@@ -302,7 +302,7 @@ object DesugarEnums {
302302
else {
303303
val (tag, scaffolding) = nextOrdinal(CaseKind.Simple)
304304
val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString))))
305-
val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final)
305+
val vdef = ValDef(name, enumClassRef, creator).withMods(mods | EnumValue)
306306
flatTree(scaffolding ::: vdef :: Nil).withSpan(span)
307307
}
308308
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,9 @@ object Flags {
693693
/** A Java enum value */
694694
final val JavaEnumValue: FlagConjunction = allOf(StableRealizable, JavaStatic, JavaDefined, Enum)
695695

696+
/** An enum value */
697+
final val EnumValue: FlagConjunction = allOf(StableRealizable, JavaStatic, Enum)
698+
696699
/** Labeled private[this] */
697700
final val PrivateLocal: FlagConjunction = allOf(Private, Local)
698701

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
110110
else tree
111111
}
112112

113+
override def transformValDef(tree: ValDef)(implicit ctx: Context): ValDef = {
114+
val sym = tree.symbol
115+
if ((sym.is(EnumValue) || sym.name == nme.DOLLAR_VALUES) && sym.owner.linkedClass.derivesFromJavaEnum)
116+
sym.addAnnotation(Annotations.Annotation(defn.ScalaStaticAnnot))
117+
tree
118+
}
119+
113120
/** 1. If this is an enum class, add $name and $ordinal parameters to its
114121
* parameter accessors and pass them on to the java.lang.Enum constructor.
115122
*

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ mixin-forwarder-overload
1717
t8905
1818
t10889
1919
i5257.scala
20+
enum-java

docs/docs/reference/enums/desugarEnums.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ Companion objects of enumerations that contain at least one simple case define i
170170
ordinal number and name. This method can be thought as being defined as
171171
follows.
172172

173-
private def $new(\_$ordinal: Int, $name: String) = new E {
174-
def $ordinal = $tag
173+
private def $new(_$ordinal: Int, $name: String) = new E {
174+
def $ordinal = $_ordinal
175175
override def toString = $name
176176
$values.register(this) // register enum value so that `valueOf` and `values` can return it.
177177
}
@@ -186,6 +186,14 @@ identifiers.
186186
Even though translated enum cases are located in the enum's companion object, referencing
187187
this object or its members via `this` or a simple identifier is also illegal. The compiler typechecks enum cases in the scope of the enclosing companion object but flags any such illegal accesses as errors.
188188

189+
### Translation of Java-compatible enums
190+
A Java-compatible enum is an enum that extends `java.lang.Enum`. The translation rules are the same as above, with the reservations defined in this section.
191+
192+
It is a compile-time error for a Java-compatible enum to have class cases.
193+
194+
Cases such as `case C` expand to a `@static val` as opposed to a `val`. This allows them to be generated as static fields of the enum type, thus ensuring they are represented the same way as Java enums.
195+
196+
189197
### Other Rules
190198

191199
A normal case class which is not produced from an enum case is not allowed to extend

docs/docs/reference/enums/enums.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,13 @@ object Planet {
8989
```
9090

9191
### Compatibility with Java Enums
92-
If you want to use the Scala-defined enums as Java enums, you can do so by extending `compat.JEnum` class as follows:
92+
If you want to use the Scala-defined enums as Java enums, you can do so by extending `java.lang.Enum` class as follows:
9393

9494
```scala
95-
enum Color extends compat.JEnum[Color] { case Red, Green, Blue }
95+
enum Color extends java.lang.Enum[Color] { case Red, Green, Blue }
9696
```
9797

98-
The type parameter comes from the Java enum [definition](https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/Enum.html) and should me the same as the type of the enum. The compiler will transform the definition above so that `Color` extends `java.lang.Enum`.
98+
The type parameter comes from the Java enum [definition](https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/Enum.html) and should be the same as the type of the enum. There is no need to provide constructor arguments (as defined in the API docs) to `java.lang.Enum` when extending it – the compiler will generate them automatically.
9999

100100
After defining `Color` like that, you can use like you would a Java enum:
101101

@@ -104,6 +104,8 @@ scala> Color.Red.compareTo(Color.Green)
104104
val res15: Int = -1
105105
```
106106

107+
For a more in-depth example of using Scala 3 enums from Java, see [this test](https://github.com/lampepfl/dotty/tree/master/tests/run/enum-java). In the test, the enums are defined in the `MainScala.scala` file and used from a Java source, `Test.java`.
108+
107109
### Implementation
108110

109111
Enums are represented as `sealed` classes that extend the `scala.Enum` trait.

tests/run/enum-java.check

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
API Test A
2+
compareTo greater:-1
3+
compareTo lesser: 1
4+
compareTo self: 0
5+
equals other: false
6+
equals self: true
7+
getDeclaringClass:class A
8+
name: MONDAY
9+
ordinal: 0
10+
toString: MONDAY
11+
12+
API Test B
13+
compareTo greater:-1
14+
compareTo lesser: 1
15+
compareTo self: 0
16+
equals other: false
17+
equals self: true
18+
getDeclaringClass:class B
19+
name: EARTH
20+
ordinal: 0
21+
toString: EARTH
22+
23+
Module Test
24+
Values class: class [LA;
25+
MONDAY : 0
26+
TUESDAY : 1
27+
SATURDAY : 2
28+
By-name value: MONDAY
29+
Correctly failed to retrieve illegal name, message: key not found: stuff
30+
31+
Collections Test
32+
Retrieving Monday: workday
33+
Contains Tuesday: false
34+
All days:
35+
MONDAY
36+
TUESDAY
37+
SATURDAY
38+
39+
Switch Test
40+
Saw tuesday
41+
Jup
42+
43+
Misc Tests
44+
Gravity on Earth: 9.8

0 commit comments

Comments
 (0)