Skip to content

Commit bd64de7

Browse files
authored
Merge pull request #10027 from dotty-staging/fix-9324
fix #9324: forbid no-arg java.lang.Enum ctor
2 parents 972ae73 + ba70024 commit bd64de7

File tree

5 files changed

+62
-8
lines changed

5 files changed

+62
-8
lines changed

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
167167
ModifierNotAllowedForDefinitionID,
168168
CannotExtendJavaEnumID,
169169
InvalidReferenceInImplicitNotFoundAnnotationID,
170-
TraitMayNotDefineNativeMethodID
170+
TraitMayNotDefineNativeMethodID,
171+
JavaEnumParentArgsID
171172

172173
def errorNumber = ordinal - 2
173174
}

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,12 @@ import transform.SymUtils._
15281528
def explain = ""
15291529
}
15301530

1531+
class JavaEnumParentArgs(parent: Type)(using Context)
1532+
extends TypeMsg(JavaEnumParentArgsID) {
1533+
def msg = em"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}"""
1534+
def explain = ""
1535+
}
1536+
15311537
class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(using Context)
15321538
extends SyntaxMsg(CannotHaveSameNameAsID) {
15331539
import CannotHaveSameNameAs._

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ object RefChecks {
9595
* and required classes. Also check that only `enum` constructs extend
9696
* `java.lang.Enum`.
9797
*/
98-
private def checkParents(cls: Symbol)(using Context): Unit = cls.info match {
98+
private def checkParents(cls: Symbol, parentTrees: List[Tree])(using Context): Unit = cls.info match {
9999
case cinfo: ClassInfo =>
100100
def checkSelfConforms(other: ClassSymbol, category: String, relation: String) = {
101101
val otherSelf = other.declaredSelfTypeAsSeenFrom(cls.thisType)
@@ -109,12 +109,26 @@ object RefChecks {
109109
for (reqd <- cinfo.cls.givenSelfType.classSymbols)
110110
checkSelfConforms(reqd, "missing requirement", "required")
111111

112+
def isClassExtendingJavaEnum =
113+
!cls.isOneOf(Enum | Trait) && parents.exists(_.classSymbol == defn.JavaEnumClass)
114+
112115
// Prevent wrong `extends` of java.lang.Enum
113-
if !migrateTo3 &&
114-
!cls.isOneOf(Enum | Trait) &&
115-
parents.exists(_.classSymbol == defn.JavaEnumClass)
116-
then
117-
report.error(CannotExtendJavaEnum(cls), cls.sourcePos)
116+
if isClassExtendingJavaEnum then
117+
if !migrateTo3 then // always error, only traits or enum-syntax is possible under scala 3.x
118+
report.error(CannotExtendJavaEnum(cls), cls.sourcePos)
119+
else
120+
// conditionally error, we allow classes to extend java.lang.Enum in scala 2 migration mode,
121+
// however the no-arg constructor is forbidden, we must look at the parent trees to see
122+
// which overload is called.
123+
val javaEnumCtor = defn.JavaEnumClass.primaryConstructor
124+
parentTrees.exists {
125+
case parent @ tpd.Apply(tpd.TypeApply(fn, _), _) if fn.tpe.termSymbol eq javaEnumCtor =>
126+
// here we are simulating the error for missing arguments to a constructor.
127+
report.error(JavaEnumParentArgs(parent.tpe), cls.sourcePos)
128+
true
129+
case _ =>
130+
false
131+
}
118132

119133
case _ =>
120134
}
@@ -1089,7 +1103,7 @@ class RefChecks extends MiniPhase { thisPhase =>
10891103
override def transformTemplate(tree: Template)(using Context): Tree = try {
10901104
val cls = ctx.owner.asClass
10911105
checkOverloadedRestrictions(cls)
1092-
checkParents(cls)
1106+
checkParents(cls, tree.parents)
10931107
if (cls.is(Trait)) tree.parents.foreach(checkParentPrefix(cls, _))
10941108
checkCompanionNameClashes(cls)
10951109
checkAllOverrides(cls)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- [E160] Type Error: tests/neg/extend-java-enum-migration.scala:9:12 --------------------------------------------------
2+
9 |final class C extends jl.Enum[C] // error
3+
| ^
4+
| not enough arguments for constructor Enum: (name: String, ordinal: Int): Enum[C]
5+
-- [E160] Type Error: tests/neg/extend-java-enum-migration.scala:11:7 --------------------------------------------------
6+
11 |object O extends jl.Enum[O.type] // error
7+
| ^
8+
| not enough arguments for constructor Enum: (name: String, ordinal: Int): Enum[O.type]
9+
-- [E160] Type Error: tests/neg/extend-java-enum-migration.scala:14:6 --------------------------------------------------
10+
14 |class Sub extends T // error
11+
| ^
12+
| not enough arguments for constructor Enum: (name: String, ordinal: Int): Enum[T]
13+
-- [E160] Type Error: tests/neg/extend-java-enum-migration.scala:17:10 -------------------------------------------------
14+
17 |val foo = new java.lang.Enum[Color] {} // error
15+
| ^
16+
| not enough arguments for constructor Enum: (name: String, ordinal: Int): Enum[Color]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import java.{lang => jl}
2+
3+
import language.`3.0-migration`
4+
5+
// This file is different from `tests/neg/extend-java-enum.scala` as we
6+
// are testing that it is illegal to *not* pass arguments to jl.Enum
7+
// in 3.0-migration
8+
9+
final class C extends jl.Enum[C] // error
10+
11+
object O extends jl.Enum[O.type] // error
12+
13+
trait T extends jl.Enum[T] // ok
14+
class Sub extends T // error
15+
16+
abstract class Color(name: String, ordinal: Int) extends java.lang.Enum[Color](name, ordinal) // ok
17+
val foo = new java.lang.Enum[Color] {} // error

0 commit comments

Comments
 (0)