Skip to content

fix #9873: no longer use scala.Enum as parents of enums #9877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ object desugar {
case _ => false
}

def isRetractableCaseClassOrEnumMethodName(name: Name)(using Context): Boolean =
isRetractableCaseClassMethodName(name) || name == nme.ordinal

/** Is `name` the name of a method that is added unconditionally to case classes? */
def isDesugaredCaseClassMethodName(name: Name)(using Context): Boolean =
isRetractableCaseClassMethodName(name) || name.isSelectorName
Expand Down Expand Up @@ -403,6 +406,7 @@ object desugar {
val isCaseObject = mods.is(Case) && isObject
val isEnum = mods.isEnumClass && !mods.is(Module)
def isEnumCase = mods.isEnumCase
def isNonEnumCase = !isEnumCase && (isCaseClass || isCaseObject)
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.

Expand Down Expand Up @@ -483,7 +487,9 @@ object desugar {
val enumCompanionRef = TermRefTree()
val enumImport =
Import(enumCompanionRef, enumCases.flatMap(caseIds).map(ImportSelector(_)))
(enumImport :: enumStats, enumCases, enumCompanionRef)
val enumLabelDef = DesugarEnums.enumLabelMeth(EmptyTree)
val ordinalDef = DesugarEnums.ordinalMeth(EmptyTree).withAddedFlags(Synthetic)
(enumImport :: ordinalDef :: enumLabelDef :: enumStats, enumCases, enumCompanionRef)
}
else (stats, Nil, EmptyTree)
}
Expand Down Expand Up @@ -621,10 +627,8 @@ object desugar {
var parents1 = parents
if (isEnumCase && parents.isEmpty)
parents1 = enumClassTypeRef :: Nil
if (isCaseClass | isCaseObject)
if (isNonEnumCase || isEnum)
parents1 = parents1 :+ scalaDot(str.Product.toTypeName) :+ scalaDot(nme.Serializable.toTypeName)
if (isEnum)
parents1 = parents1 :+ ref(defn.EnumClass.typeRef)

// derived type classes of non-module classes go to their companions
val (clsDerived, companionDerived) =
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ object SymUtils {
self
}

def isEnum(using Context): Boolean = self.is(Enum, butNot=JavaDefined)
def isEnumClass(using Context): Boolean = isEnum && !self.is(Case)

/** Does this symbol refer to anonymous classes synthesized by enum desugaring? */
def isEnumAnonymClass(using Context): Boolean =
self.isAnonymousClass && (self.owner.name.eq(nme.DOLLAR_NEW) || self.owner.is(CaseVal))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
lazy val accessors =
if (isDerivedValueClass(clazz)) clazz.paramAccessors.take(1) // Tail parameters can only be `erased`
else clazz.caseAccessors
val isEnumCase = clazz.derivesFrom(defn.EnumClass) && clazz != defn.EnumClass
val isEnumValue = isEnumCase && clazz.isAnonymousClass && clazz.classParents.head.classSymbol.is(Enum)
val isEnumCase = clazz.isEnum || clazz.classParents.exists(_.classSymbol.isEnum)
val isEnumValue = isEnumCase && clazz.isAnonymousClass && clazz.classParents.head.classSymbol.isEnum
val isNonJavaEnumValue = isEnumValue && !clazz.derivesFrom(defn.JavaEnumClass)

val symbolsToSynthesize: List[Symbol] =
Expand Down
27 changes: 20 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -743,21 +743,34 @@ class Namer { typer: Typer =>
}

/** Invalidate `denot` by overwriting its info with `NoType` if
* `denot` is a compiler generated case class method that clashes
* with a user-defined method in the same scope with a matching type.
* one of the following holds:
* - `denot` is a compiler generated case class method that clashes
* with a user-defined method in the same scope with a matching type.
* - `denot` is a compiler generated `ordinal` method that would override
* `ordinal` declared in `java.lang.Enum`
*/
private def invalidateIfClashingSynthetic(denot: SymDenotation): Unit = {
def isCaseClass(owner: Symbol) =
owner.isClass && {
if (owner.is(Module)) owner.linkedClass.is(CaseClass)
else owner.is(CaseClass)
}
val isClashingSynthetic =
denot.is(Synthetic) &&
desugar.isRetractableCaseClassMethodName(denot.name) &&
isCaseClass(denot.owner) &&
denot.owner.info.decls.lookupAll(denot.name).exists(alt =>
def isJavaEnumBaseClass(owner: Symbol) =
owner.isClass && owner.isEnumClass && owner.derivesFrom(defn.JavaEnumClass)
def firstParentCls(owner: Symbol) =
owner.asClass.classParents.head.classSymbol
def findMatch(owner: Symbol) =
owner.info.decls.lookupAll(denot.name).exists(alt =>
alt != denot.symbol && alt.info.matchesLoosely(denot.info))
def clashingCaseClassMethod =
desugar.isRetractableCaseClassMethodName(denot.name)
&& isCaseClass(denot.owner)
&& findMatch(denot.owner)
def clashingEnumMethod =
denot.name == nme.ordinal
&& isJavaEnumBaseClass(denot.owner)
&& findMatch(firstParentCls(denot.owner))
val isClashingSynthetic = denot.is(Synthetic) && (clashingCaseClassMethod || clashingEnumMethod)
if (isClashingSynthetic) {
typr.println(i"invalidating clashing $denot in ${denot.owner}")
denot.markAbsent()
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1941,11 +1941,10 @@ class Typer extends Namer
}

def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(using Context): Tree = {
if (!sym.info.exists) { // it's a discarded synthetic case class method, drop it
assert(sym.is(Synthetic) && desugar.isRetractableCaseClassMethodName(sym.name))
if !sym.info.exists then // it's a discarded synthetic case class method, or ordinal method drop it
assert(sym.is(Synthetic) && desugar.isRetractableCaseClassOrEnumMethodName(sym.name))
sym.owner.info.decls.openForMutations.unlink(sym)
return EmptyTree
}
val DefDef(name, tparams, vparamss, tpt, _) = ddef
completeAnnotations(ddef, sym)
val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef])
Expand Down Expand Up @@ -2105,7 +2104,8 @@ class Typer extends Namer
val constr1 = typed(constr).asInstanceOf[DefDef]
val parentsWithClass = ensureFirstTreeIsClass(parents.mapconserve(typedParent).filterConserve(!_.isEmpty), cdef.nameSpan)
val parents1 = ensureConstrCall(cls, parentsWithClass)(using superCtx)
val firstParent = parents1.head.tpe.dealias.typeSymbol
val firstParentTpe = parents1.head.tpe.dealias
val firstParent = firstParentTpe.typeSymbol

checkEnumParent(cls, firstParent)

Expand All @@ -2122,7 +2122,7 @@ class Typer extends Namer
.withType(dummy.termRef)
if (!cls.isOneOf(AbstractOrTrait) && !ctx.isAfterTyper)
checkRealizableBounds(cls, cdef.sourcePos.withSpan(cdef.nameSpan))
if cls.derivesFrom(defn.EnumClass) then
if cls.isEnum || firstParentTpe.classSymbol.isEnum then
checkEnum(cdef, cls, firstParent)
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls)

Expand Down
3 changes: 2 additions & 1 deletion library/src-bootstrapped/scala/Enum.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala

/** A base trait of all enum classes */
/** A Product that also describes a label and ordinal */
@deprecated("scala.Enum is no longer supported", "3.0.0-M1")
trait Enum extends Product, Serializable:

/** A string uniquely identifying a case of an enum */
Expand Down
3 changes: 3 additions & 0 deletions library/src-non-bootstrapped/scala/Enum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ package scala
/** A base trait of all enum classes */
trait Enum extends Product, Serializable:

/** A string uniquely identifying a case of an enum */
def enumLabel: String

/** A number uniquely identifying a case of an enum */
def ordinal: Int

This file was deleted.

21 changes: 0 additions & 21 deletions library/src-non-bootstrapped/scala/runtime/EnumValues.scala

This file was deleted.

15 changes: 15 additions & 0 deletions tests/neg/enumsLabel-overrides.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait Mixin { def enumLabel: String = "mixin" }

enum Mixed extends Mixin {
case B // error: overriding method enumLabel in trait Mixin of type => String;
}

enum MixedAlso {
case C extends MixedAlso with Mixin // error: overriding method enumLabel in trait Mixin of type => String;
}

trait HasEnumLabel { def enumLabel: String }

enum MyEnum extends HasEnumLabel {
case D // ok
}
15 changes: 15 additions & 0 deletions tests/neg/enumsLabel-singleimpl.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
enum Labelled {

case A

def enumLabel: String = "nolabel" // error: double definition of method enumLabel: => String

}

enum Ordinalled {

case A

def ordinal: Int = -1 // error: double definition of method ordinal: => Int

}
22 changes: 0 additions & 22 deletions tests/neg/enumsLabelDef.scala

This file was deleted.

6 changes: 4 additions & 2 deletions tests/patmat/i7186.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import MIPS._

import deriving.Mirror.SumOf

object MIPS {
type Labels = Label | ControlLabel
type Src = Register | Constant
Expand Down Expand Up @@ -233,8 +235,8 @@ object printMips {
def getScopedLabel(s: Scoped): String =
"L" + getScopedId(s)

def printEnum[E](e: String => Enum, t: E, code: String) = {
val num = e(t.toString).ordinal
def printEnum[E: SumOf](e: String => E, t: E, code: String) = {
val num = summon[SumOf[E]].ordinal(e(t.toString))
s"$code$num"
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/run/generic/Enum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ object runtime {
}
def values: Iterable[E] = myMap.values
}
}
}
19 changes: 18 additions & 1 deletion tests/semanticdb/metac.expect
Original file line number Diff line number Diff line change
Expand Up @@ -641,14 +641,16 @@ Schema => SemanticDB v4
Uri => Enums.scala
Text => empty
Language => Scala
Symbols => 183 entries
Symbols => 200 entries
Occurrences => 203 entries

Symbols:
_empty_/Enums. => final object Enums
_empty_/Enums.Coin# => abstract sealed enum class Coin
_empty_/Enums.Coin#`<init>`(). => primary ctor <init>
_empty_/Enums.Coin#`<init>`().(value) => param value
_empty_/Enums.Coin#enumLabel(). => abstract method enumLabel
_empty_/Enums.Coin#ordinal(). => abstract method ordinal
_empty_/Enums.Coin#value. => val method value
_empty_/Enums.Coin. => final object Coin
_empty_/Enums.Coin.$fromOrdinal(). => method $fromOrdinal
Expand All @@ -664,6 +666,8 @@ _empty_/Enums.Coin.valueOf().($name) => param $name
_empty_/Enums.Coin.values(). => method values
_empty_/Enums.Colour# => abstract sealed enum class Colour
_empty_/Enums.Colour#`<init>`(). => primary ctor <init>
_empty_/Enums.Colour#enumLabel(). => abstract method enumLabel
_empty_/Enums.Colour#ordinal(). => abstract method ordinal
_empty_/Enums.Colour. => final object Colour
_empty_/Enums.Colour.$fromOrdinal(). => method $fromOrdinal
_empty_/Enums.Colour.$fromOrdinal().(_$ordinal) => param _$ordinal
Expand All @@ -679,6 +683,8 @@ _empty_/Enums.Colour.valueOf().($name) => param $name
_empty_/Enums.Colour.values(). => method values
_empty_/Enums.Directions# => abstract sealed enum class Directions
_empty_/Enums.Directions#`<init>`(). => primary ctor <init>
_empty_/Enums.Directions#enumLabel(). => abstract method enumLabel
_empty_/Enums.Directions#ordinal(). => abstract method ordinal
_empty_/Enums.Directions. => final object Directions
_empty_/Enums.Directions.$fromOrdinal(). => method $fromOrdinal
_empty_/Enums.Directions.$fromOrdinal().(_$ordinal) => param _$ordinal
Expand All @@ -696,6 +702,8 @@ _empty_/Enums.Directions.values(). => method values
_empty_/Enums.Maybe# => abstract sealed enum class Maybe
_empty_/Enums.Maybe#[A] => covariant typeparam A
_empty_/Enums.Maybe#`<init>`(). => primary ctor <init>
_empty_/Enums.Maybe#enumLabel(). => abstract method enumLabel
_empty_/Enums.Maybe#ordinal(). => abstract method ordinal
_empty_/Enums.Maybe. => final object Maybe
_empty_/Enums.Maybe.$fromOrdinal(). => method $fromOrdinal
_empty_/Enums.Maybe.$fromOrdinal().(_$ordinal) => param _$ordinal
Expand Down Expand Up @@ -730,6 +738,7 @@ _empty_/Enums.Planet#G. => final val method G
_empty_/Enums.Planet#`<init>`(). => primary ctor <init>
_empty_/Enums.Planet#`<init>`().(mass) => param mass
_empty_/Enums.Planet#`<init>`().(radius) => param radius
_empty_/Enums.Planet#enumLabel(). => abstract method enumLabel
_empty_/Enums.Planet#mass. => val method mass
_empty_/Enums.Planet#radius. => val method radius
_empty_/Enums.Planet#surfaceGravity(). => method surfaceGravity
Expand All @@ -750,6 +759,8 @@ _empty_/Enums.Planet.valueOf().($name) => param $name
_empty_/Enums.Planet.values(). => method values
_empty_/Enums.Suits# => abstract sealed enum class Suits
_empty_/Enums.Suits#`<init>`(). => primary ctor <init>
_empty_/Enums.Suits#enumLabel(). => abstract method enumLabel
_empty_/Enums.Suits#ordinal(). => abstract method ordinal
_empty_/Enums.Suits. => final object Suits
_empty_/Enums.Suits.$fromOrdinal(). => method $fromOrdinal
_empty_/Enums.Suits.$fromOrdinal().(_$ordinal) => param _$ordinal
Expand All @@ -772,6 +783,8 @@ _empty_/Enums.Suits.values(). => method values
_empty_/Enums.Tag# => abstract sealed enum class Tag
_empty_/Enums.Tag#[A] => typeparam A
_empty_/Enums.Tag#`<init>`(). => primary ctor <init>
_empty_/Enums.Tag#enumLabel(). => abstract method enumLabel
_empty_/Enums.Tag#ordinal(). => abstract method ordinal
_empty_/Enums.Tag. => final object Tag
_empty_/Enums.Tag.$fromOrdinal(). => method $fromOrdinal
_empty_/Enums.Tag.$fromOrdinal().(_$ordinal) => param _$ordinal
Expand All @@ -783,6 +796,8 @@ _empty_/Enums.Tag.valueOf().($name) => param $name
_empty_/Enums.Tag.values(). => method values
_empty_/Enums.WeekDays# => abstract sealed enum class WeekDays
_empty_/Enums.WeekDays#`<init>`(). => primary ctor <init>
_empty_/Enums.WeekDays#enumLabel(). => abstract method enumLabel
_empty_/Enums.WeekDays#ordinal(). => abstract method ordinal
_empty_/Enums.WeekDays. => final object WeekDays
_empty_/Enums.WeekDays.$fromOrdinal(). => method $fromOrdinal
_empty_/Enums.WeekDays.$fromOrdinal().(_$ordinal) => param _$ordinal
Expand All @@ -804,6 +819,8 @@ _empty_/Enums.`<:<`# => abstract sealed enum class <:<
_empty_/Enums.`<:<`#[A] => contravariant typeparam A
_empty_/Enums.`<:<`#[B] => typeparam B
_empty_/Enums.`<:<`#`<init>`(). => primary ctor <init>
_empty_/Enums.`<:<`#enumLabel(). => abstract method enumLabel
_empty_/Enums.`<:<`#ordinal(). => abstract method ordinal
_empty_/Enums.`<:<`. => final object <:<
_empty_/Enums.`<:<`.Refl# => final case enum class Refl
_empty_/Enums.`<:<`.Refl#[C] => typeparam C
Expand Down