Skip to content

Commit e0e0f36

Browse files
Fix scala#7499, fix scala#7174: prevent extending jl.Enum except from an enum
1 parent bb23fea commit e0e0f36

File tree

5 files changed

+28
-7
lines changed

5 files changed

+28
-7
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -585,13 +585,18 @@ class Definitions {
585585

586586
@tu lazy val JavaEnumClass: ClassSymbol = {
587587
val cls = requiredClass("java.lang.Enum")
588+
// jl.Enum has a single constructor protected(name: String, ordinal: Int).
589+
// We remove the arguments from the primary constructor, and enter
590+
// a new constructor symbol with 2 arguments, so that both
591+
// `X extends jl.Enum[X]` and `X extends jl.Enum[X](name, ordinal)`
592+
// pass typer and go through jl.Enum-specific checks in RefChecks.
588593
cls.infoOrCompleter match {
589594
case completer: ClassfileLoader =>
590595
cls.info = new ClassfileLoader(completer.classfile) {
591596
override def complete(root: SymDenotation)(using Context): Unit = {
592597
super.complete(root)
593598
val constr = cls.primaryConstructor
594-
val newInfo = constr.info match {
599+
val noArgInfo = constr.info match {
595600
case info: PolyType =>
596601
info.resType match {
597602
case meth: MethodType =>
@@ -600,7 +605,8 @@ class Definitions {
600605
paramNames = Nil, paramInfos = Nil))
601606
}
602607
}
603-
constr.info = newInfo
608+
val argConstr = constr.copy().entered
609+
constr.info = noArgInfo
604610
constr.termRef.recomputeDenot()
605611
}
606612
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
164164
UnexpectedPatternForSummonFromID,
165165
AnonymousInstanceCannotBeEmptyID,
166166
TypeSpliceInValPatternID,
167-
ModifierNotAllowedForDefinitionID
167+
ModifierNotAllowedForDefinitionID,
168+
CannotExtendJavaEnumID
168169

169170
def errorNumber = ordinal - 2
170171
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,12 @@ import ast.tpd
14901490
|"""
14911491
}
14921492

1493+
class CannotExtendJavaEnum(sym: Symbol)(using Context)
1494+
extends SyntaxMsg(CannotExtendJavaEnumID) {
1495+
def msg = em"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can"""
1496+
def explain = ""
1497+
}
1498+
14931499
class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(using Context)
14941500
extends SyntaxMsg(CannotHaveSameNameAsID) {
14951501
import CannotHaveSameNameAs._

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
7373
*/
7474
private def addEnumConstrArgs(targetCls: Symbol, parents: List[Tree], args: List[Tree])(using Context): List[Tree] =
7575
parents.map {
76-
case app @ Apply(fn, args0) if fn.symbol.owner == targetCls => cpy.Apply(app)(fn, args0 ++ args)
76+
case app @ Apply(fn, args0) if fn.symbol.owner == targetCls =>
77+
if args0.nonEmpty && targetCls == defn.JavaEnumClass then
78+
report.error("the constructor of java.lang.Enum cannot be called explicitly", app.sourcePos)
79+
cpy.Apply(app)(fn, args0 ++ args)
7780
case p => p
7881
}
7982

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ object RefChecks {
8888
cls.thisType
8989
}
9090

91-
/** Check that self type of this class conforms to self types of parents.
92-
* and required classes.
91+
/** Check that self type of this class conforms to self types of parents
92+
* and required classes. Also check that only `enum` constructs extend
93+
* `java.lang.Enum`.
9394
*/
9495
private def checkParents(cls: Symbol)(using Context): Unit = cls.info match {
9596
case cinfo: ClassInfo =>
@@ -99,10 +100,14 @@ object RefChecks {
99100
report.error(DoesNotConformToSelfType(category, cinfo.selfType, cls, otherSelf, relation, other.classSymbol),
100101
cls.sourcePos)
101102
}
102-
for (parent <- cinfo.classParents)
103+
val parents = cinfo.classParents
104+
for (parent <- parents)
103105
checkSelfConforms(parent.classSymbol.asClass, "illegal inheritance", "parent")
104106
for (reqd <- cinfo.cls.givenSelfType.classSymbols)
105107
checkSelfConforms(reqd, "missing requirement", "required")
108+
109+
if !cls.is(Enum) && parents.exists(_.classSymbol == defn.JavaEnumClass) then
110+
report.error(CannotExtendJavaEnum(cls), cls.sourcePos)
106111
case _ =>
107112
}
108113

0 commit comments

Comments
 (0)