Skip to content

Commit bd6f24c

Browse files
authored
Merge pull request #4613 from dotty-staging/fix/denot-validity
Fix #1895: Use proper validity period for extension methods
2 parents dc5c343 + 44a305f commit bd6f24c

File tree

4 files changed

+35
-538
lines changed

4 files changed

+35
-538
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ object DenotTransformers {
2424
/** The last phase during which the transformed denotations are valid */
2525
def lastPhaseId(implicit ctx: Context) = ctx.nextDenotTransformerId(id + 1)
2626

27-
/** The validity period of the transformer in the given context */
27+
/** The validity period of the transformed denotations in the given context */
2828
def validFor(implicit ctx: Context): Period =
29-
Period(ctx.runId, id, lastPhaseId)
29+
Period(ctx.runId, id + 1, lastPhaseId)
3030

3131
/** The transformation method */
3232
def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,9 @@ trait SymDenotations { this: Context =>
5151
else {
5252
val initial = denot.initial
5353
val firstPhaseId = initial.validFor.firstPhaseId.max(ctx.typerPhase.id)
54-
if ((initial ne denot) || ctx.phaseId != firstPhaseId) {
55-
ctx.withPhase(firstPhaseId).stillValidInOwner(initial) ||
56-
// Workaround #1895: A symbol might not be entered into an owner
57-
// until the second phase where it exists
58-
(denot.validFor.containsPhaseId(firstPhaseId + 1)) &&
59-
ctx.withPhase(firstPhaseId + 1).stillValidInOwner(initial)
60-
} else
54+
if ((initial ne denot) || ctx.phaseId != firstPhaseId)
55+
ctx.withPhase(firstPhaseId).stillValidInOwner(initial)
56+
else
6157
stillValidInOwner(denot)
6258
}
6359

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

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -62,51 +62,38 @@ class ExtensionMethods extends MiniPhase with DenotTransformer with FullParamete
6262
val decls1 = cinfo.decls.cloneScope
6363
val moduleSym = moduleClassSym.symbol.asClass
6464

65-
var newSuperClass: Type = null
66-
67-
ctx.atPhase(thisPhase.next) { implicit ctx =>
68-
// In Scala 2, extension methods are added before pickling so we should
69-
// not generate them again.
70-
if (!(valueClass is Scala2x)) ctx.atPhase(thisPhase) { implicit ctx =>
71-
for (decl <- valueClass.classInfo.decls) {
72-
if (isMethodWithExtension(decl)) {
73-
val meth = createExtensionMethod(decl, moduleClassSym.symbol)
74-
decls1.enter(meth)
75-
// Workaround #1895: force denotation of `meth` to be
76-
// at phase where `meth` is entered into the decls of a class
77-
meth.denot(ctx.withPhase(thisPhase.next))
78-
}
79-
}
80-
}
81-
82-
val underlying = valueErasure(underlyingOfValueClass(valueClass))
83-
val evt = ErasedValueType(valueClass.typeRef, underlying)
84-
val u2evtSym = ctx.newSymbol(moduleSym, nme.U2EVT, Synthetic | Method,
85-
MethodType(List(nme.x_0), List(underlying), evt))
86-
val evt2uSym = ctx.newSymbol(moduleSym, nme.EVT2U, Synthetic | Method,
87-
MethodType(List(nme.x_0), List(evt), underlying))
88-
89-
val defn = ctx.definitions
90-
91-
val underlyingCls = underlying.classSymbol
92-
val underlyingClsName =
93-
if (underlyingCls.isNumericValueClass || underlyingCls == defn.BooleanClass) underlyingCls.name
94-
else nme.Object
95-
96-
val syp = ctx.requiredClass(s"dotty.runtime.vc.VC${underlyingClsName}Companion").asClass
97-
98-
newSuperClass = tpd.ref(syp).select(nme.CONSTRUCTOR).appliedToType(valueClass.typeRef).tpe.resultType
99-
100-
decls1.enter(u2evtSym)
101-
decls1.enter(evt2uSym)
65+
def enterInModuleClass(sym: Symbol): Unit = {
66+
decls1.enter(sym)
67+
// This is tricky: in this denotation transformer, we transform
68+
// companion modules of value classes by adding methods to them.
69+
// Running the transformer will create these methods, but they're
70+
// only valid once it has finished running. This means we cannot use
71+
// `ctx.withPhase(thisPhase.next)` here without potentially running
72+
// into cycles. Instead, we manually set their validity after having
73+
// created them to match the validity of the owner transformed
74+
// denotation.
75+
sym.validFor = thisPhase.validFor
10276
}
10377

104-
// Add the extension methods, the cast methods u2evt$ and evt2u$, and a VC*Companion superclass
105-
moduleClassSym.copySymDenotation(info =
106-
cinfo.derivedClassInfo(
107-
// FIXME: use of VC*Companion superclasses is disabled until the conflicts with SyntheticMethods are solved.
108-
//classParents = List(newSuperClass)
109-
decls = decls1))
78+
// Create extension methods, except if the class comes from Scala 2
79+
// because it adds extension methods before pickling.
80+
if (!(valueClass.is(Scala2x)))
81+
for (decl <- valueClass.classInfo.decls)
82+
if (isMethodWithExtension(decl))
83+
enterInModuleClass(createExtensionMethod(decl, moduleClassSym.symbol))
84+
85+
// Create synthetic methods to cast values between the underlying type
86+
// and the ErasedValueType. These methods are removed in ElimErasedValueType.
87+
val underlying = valueErasure(underlyingOfValueClass(valueClass))
88+
val evt = ErasedValueType(valueClass.typeRef, underlying)
89+
val u2evtSym = ctx.newSymbol(moduleSym, nme.U2EVT, Synthetic | Method,
90+
MethodType(List(nme.x_0), List(underlying), evt))
91+
val evt2uSym = ctx.newSymbol(moduleSym, nme.EVT2U, Synthetic | Method,
92+
MethodType(List(nme.x_0), List(evt), underlying))
93+
enterInModuleClass(u2evtSym)
94+
enterInModuleClass(evt2uSym)
95+
96+
moduleClassSym.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1))
11097
case _ =>
11198
moduleClassSym
11299
}

0 commit comments

Comments
 (0)