Skip to content

Commit be11253

Browse files
committed
delay enter ordinal symbol
1 parent 4e3d6e4 commit be11253

File tree

1 file changed

+56
-22
lines changed

1 file changed

+56
-22
lines changed

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

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -377,13 +377,13 @@ class Namer { typer: Typer =>
377377
/** Expand tree and create top-level symbols for statement and enter them into symbol table */
378378
def index(stat: Tree)(using Context): Context = {
379379
expand(stat)
380-
indexExpanded(stat)
380+
indexExpanded(stat, mutable.Buffer.empty) // here we do not expect to populate the buffer
381381
}
382382

383383
/** Create top-level symbols for all statements in the expansion of this statement and
384384
* enter them into symbol table
385385
*/
386-
def indexExpanded(origStat: Tree)(using Context): Context = {
386+
def indexExpanded(origStat: Tree, delayedEntry: mutable.Buffer[Context ?=> Context])(using Context): Context = {
387387
def recur(stat: Tree): Context = stat match {
388388
case pcl: PackageDef =>
389389
val pkg = createPackageSymbol(pcl.pid)
@@ -395,10 +395,23 @@ class Namer { typer: Typer =>
395395
ctx.importContext(imp, createSymbol(imp))
396396
case mdef: DefTree =>
397397
val sym = createSymbol(mdef)
398-
enterSymbol(sym)
399-
setDocstring(sym, origStat)
400-
addEnumConstants(mdef, sym)
401-
ctx
398+
def enterDefTree(sym: Symbol): Context =
399+
enterSymbol(sym)
400+
setDocstring(sym, origStat)
401+
addEnumConstants(mdef, sym)
402+
ctx
403+
if sym.is(Synthetic) && isEnumOrdinal(sym) then
404+
delayedEntry += { ctx ?=>
405+
// Here we delay entering into scope of an enum ordinal method until after
406+
// the parents of `sym.owner` are known. This is because it is illegal to override ordinal
407+
// in java.lang.Enum
408+
if !clashingJavaEnumOrdinal(sym) then
409+
enterDefTree(sym)
410+
ctx
411+
}
412+
ctx
413+
else
414+
enterDefTree(sym)
402415
case stats: Thicket =>
403416
stats.toList.foreach(recur)
404417
ctx
@@ -456,7 +469,10 @@ class Namer { typer: Typer =>
456469
/** Create top-level symbols for statements and enter them into symbol table
457470
* @return A context that reflects all imports in `stats`.
458471
*/
459-
def index(stats: List[Tree])(using Context): Context = {
472+
def index(stats: List[Tree])(using Context): Context =
473+
index(stats, mutable.Buffer.empty) // here we do not expect to fill the buffer
474+
475+
def index(stats: List[Tree], delayedEntry: mutable.Buffer[Context ?=> Context])(using Context): Context = {
460476

461477
// module name -> (stat, moduleCls | moduleVal)
462478
val moduleClsDef = mutable.Map[TypeName, (Tree, TypeDef)]()
@@ -631,7 +647,7 @@ class Namer { typer: Typer =>
631647

632648
stats.foreach(expand)
633649
mergeCompanionDefs()
634-
val ctxWithStats = stats.foldLeft(ctx)((ctx, stat) => indexExpanded(stat)(using ctx))
650+
val ctxWithStats = stats.foldLeft(ctx)((ctx, stat) => indexExpanded(stat, delayedEntry)(using ctx))
635651
createCompanionLinks(using ctxWithStats)
636652
ctxWithStats
637653
}
@@ -663,6 +679,23 @@ class Namer { typer: Typer =>
663679
sym.resetFlag(GivenOrImplicit)
664680
}
665681

682+
private def findMatch(denot: SymDenotation, owner: Symbol)(using Context) =
683+
owner.info.decls.lookupAll(denot.name).exists(alt =>
684+
alt != denot.symbol && alt.info.matchesLoosely(denot.info))
685+
686+
private def isEnumOrdinal(denot: SymDenotation)(using Context) =
687+
denot.name == nme.ordinal
688+
&& denot.owner.isClass && denot.owner.isEnumClass
689+
690+
private def clashingJavaEnumOrdinal(denot: SymDenotation)(using Context) =
691+
def firstParentCls(owner: Symbol) =
692+
owner.asClass.classParents.head.classSymbol
693+
def isJavaEnumBaseClass(owner: Symbol) =
694+
owner.isClass && owner.isEnumClass && owner.derivesFrom(defn.JavaEnumClass)
695+
denot.name == nme.ordinal
696+
&& isJavaEnumBaseClass(denot.owner)
697+
&& findMatch(denot, firstParentCls(denot.owner))
698+
666699
/** The completer of a symbol defined by a member def or import (except ClassSymbols) */
667700
class Completer(val original: Tree)(ictx: Context) extends LazyType with SymbolLoaders.SecondCompleter {
668701

@@ -755,22 +788,11 @@ class Namer { typer: Typer =>
755788
if (owner.is(Module)) owner.linkedClass.is(CaseClass)
756789
else owner.is(CaseClass)
757790
}
758-
def isJavaEnumBaseClass(owner: Symbol) =
759-
owner.isClass && owner.isEnumClass && owner.derivesFrom(defn.JavaEnumClass)
760-
def firstParentCls(owner: Symbol) =
761-
owner.asClass.classParents.head.classSymbol
762-
def findMatch(owner: Symbol) =
763-
owner.info.decls.lookupAll(denot.name).exists(alt =>
764-
alt != denot.symbol && alt.info.matchesLoosely(denot.info))
765791
def clashingCaseClassMethod =
766792
desugar.isRetractableCaseClassMethodName(denot.name)
767793
&& isCaseClass(denot.owner)
768-
&& findMatch(denot.owner)
769-
def clashingEnumMethod =
770-
denot.name == nme.ordinal
771-
&& isJavaEnumBaseClass(denot.owner)
772-
&& findMatch(firstParentCls(denot.owner))
773-
val isClashingSynthetic = denot.is(Synthetic) && (clashingCaseClassMethod || clashingEnumMethod)
794+
&& findMatch(denot, denot.owner)
795+
val isClashingSynthetic = denot.is(Synthetic) && (clashingCaseClassMethod || clashingJavaEnumOrdinal(denot))
774796
if (isClashingSynthetic) {
775797
typr.println(i"invalidating clashing $denot in ${denot.owner}")
776798
denot.markAbsent()
@@ -936,6 +958,11 @@ class Namer { typer: Typer =>
936958
/** info to be used temporarily while completing the class, to avoid cyclic references. */
937959
private var tempInfo: TempClassInfo = _
938960

961+
/** we delay entry of the following symbols (already created) until after parents are known:
962+
* - def ordinal: Int (if parent is not java.lang.Enum)
963+
*/
964+
private var delayedEntry: List[Context ?=> Context] = _
965+
939966
val TypeDef(name, impl @ Template(constr, _, self, _)) = original
940967

941968
private val (params, rest): (List[Tree], List[Tree]) = impl.body.span {
@@ -1096,8 +1123,11 @@ class Namer { typer: Typer =>
10961123

10971124
localCtx = completerCtx.inClassContext(selfInfo)
10981125

1126+
val delayedEntryBuf = collection.mutable.ListBuffer.empty[Context ?=> Context]
1127+
10991128
index(constr)
1100-
index(rest)(using localCtx)
1129+
index(rest, delayedEntryBuf)(using localCtx)
1130+
delayedEntry = delayedEntryBuf.toList
11011131

11021132
symbolOfTree(constr).info.stripPoly match // Completes constr symbol as a side effect
11031133
case mt: MethodType if cls.is(Case) && mt.isParamDependent =>
@@ -1202,6 +1232,10 @@ class Namer { typer: Typer =>
12021232
denot.info = tempInfo.finalized(parentTypes)
12031233
tempInfo = null // The temporary info can now be garbage-collected
12041234

1235+
// now we know the parents we can decide if we enter the symbols or not
1236+
delayedEntry.foldLeft(localCtx) { (localCtx, op) => op(using localCtx) }
1237+
delayedEntry = null
1238+
12051239
Checking.checkWellFormed(cls)
12061240
if (isDerivedValueClass(cls)) cls.setFlag(Final)
12071241
cls.info = avoidPrivateLeaks(cls)

0 commit comments

Comments
 (0)