Skip to content

Fix #1895: Use proper validity period for extension methods #4613

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

Merged
merged 3 commits into from
Jun 4, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/DenotTransformers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ object DenotTransformers {
/** The last phase during which the transformed denotations are valid */
def lastPhaseId(implicit ctx: Context) = ctx.nextDenotTransformerId(id + 1)

/** The validity period of the transformer in the given context */
/** The validity period of the transformed denotations in the given context */
def validFor(implicit ctx: Context): Period =
Period(ctx.runId, id, lastPhaseId)
Period(ctx.runId, id + 1, lastPhaseId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first I was scared, because it looks really hard to see all the ramifications of this change. But then I found out that the method is in fact not used at all! So do we still want to keep it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's in fact used by the commit just after it as explained in the commit message: 7aa1ee2, see https://github.com/lampepfl/dotty/pull/4613/files#diff-093136d16584c89857e7cb09ec4f6518R75


/** The transformation method */
def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation
Expand Down
10 changes: 3 additions & 7 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,9 @@ trait SymDenotations { this: Context =>
else {
val initial = denot.initial
val firstPhaseId = initial.validFor.firstPhaseId.max(ctx.typerPhase.id)
if ((initial ne denot) || ctx.phaseId != firstPhaseId) {
ctx.withPhase(firstPhaseId).stillValidInOwner(initial) ||
// Workaround #1895: A symbol might not be entered into an owner
// until the second phase where it exists
(denot.validFor.containsPhaseId(firstPhaseId + 1)) &&
ctx.withPhase(firstPhaseId + 1).stillValidInOwner(initial)
} else
if ((initial ne denot) || ctx.phaseId != firstPhaseId)
ctx.withPhase(firstPhaseId).stillValidInOwner(initial)
else
stillValidInOwner(denot)
}

Expand Down
73 changes: 30 additions & 43 deletions compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,51 +62,38 @@ class ExtensionMethods extends MiniPhase with DenotTransformer with FullParamete
val decls1 = cinfo.decls.cloneScope
val moduleSym = moduleClassSym.symbol.asClass

var newSuperClass: Type = null

ctx.atPhase(thisPhase.next) { implicit ctx =>
// In Scala 2, extension methods are added before pickling so we should
// not generate them again.
if (!(valueClass is Scala2x)) ctx.atPhase(thisPhase) { implicit ctx =>
for (decl <- valueClass.classInfo.decls) {
if (isMethodWithExtension(decl)) {
val meth = createExtensionMethod(decl, moduleClassSym.symbol)
decls1.enter(meth)
// Workaround #1895: force denotation of `meth` to be
// at phase where `meth` is entered into the decls of a class
meth.denot(ctx.withPhase(thisPhase.next))
}
}
}

val underlying = valueErasure(underlyingOfValueClass(valueClass))
val evt = ErasedValueType(valueClass.typeRef, underlying)
val u2evtSym = ctx.newSymbol(moduleSym, nme.U2EVT, Synthetic | Method,
MethodType(List(nme.x_0), List(underlying), evt))
val evt2uSym = ctx.newSymbol(moduleSym, nme.EVT2U, Synthetic | Method,
MethodType(List(nme.x_0), List(evt), underlying))

val defn = ctx.definitions

val underlyingCls = underlying.classSymbol
val underlyingClsName =
if (underlyingCls.isNumericValueClass || underlyingCls == defn.BooleanClass) underlyingCls.name
else nme.Object

val syp = ctx.requiredClass(s"dotty.runtime.vc.VC${underlyingClsName}Companion").asClass

newSuperClass = tpd.ref(syp).select(nme.CONSTRUCTOR).appliedToType(valueClass.typeRef).tpe.resultType

decls1.enter(u2evtSym)
decls1.enter(evt2uSym)
def enterInModuleClass(sym: Symbol): Unit = {
decls1.enter(sym)
// This is tricky: in this denotation transformer, we transform
// companion modules of value classes by adding methods to them.
// Running the transformer will create these methods, but they're
// only valid once it has finished running. This means we cannot use
// `ctx.withPhase(thisPhase.next)` here without potentially running
// into cycles. Instead, we manually set their validity after having
// created them to match the validity of the owner transformed
// denotation.
sym.validFor = thisPhase.validFor
}

// Add the extension methods, the cast methods u2evt$ and evt2u$, and a VC*Companion superclass
moduleClassSym.copySymDenotation(info =
cinfo.derivedClassInfo(
// FIXME: use of VC*Companion superclasses is disabled until the conflicts with SyntheticMethods are solved.
//classParents = List(newSuperClass)
decls = decls1))
// Create extension methods, except if the class comes from Scala 2
// because it adds extension methods before pickling.
if (!(valueClass.is(Scala2x)))
for (decl <- valueClass.classInfo.decls)
if (isMethodWithExtension(decl))
enterInModuleClass(createExtensionMethod(decl, moduleClassSym.symbol))

// Create synthetic methods to cast values between the underlying type
// and the ErasedValueType. These methods are removed in ElimErasedValueType.
val underlying = valueErasure(underlyingOfValueClass(valueClass))
val evt = ErasedValueType(valueClass.typeRef, underlying)
val u2evtSym = ctx.newSymbol(moduleSym, nme.U2EVT, Synthetic | Method,
MethodType(List(nme.x_0), List(underlying), evt))
val evt2uSym = ctx.newSymbol(moduleSym, nme.EVT2U, Synthetic | Method,
MethodType(List(nme.x_0), List(evt), underlying))
enterInModuleClass(u2evtSym)
enterInModuleClass(evt2uSym)

moduleClassSym.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1))
case _ =>
moduleClassSym
}
Expand Down
Loading