Skip to content

Commit 5829928

Browse files
oderskymilessabin
authored andcommitted
Synthesis for mirror infrastructure
1 parent 42807da commit 5829928

File tree

2 files changed

+120
-20
lines changed

2 files changed

+120
-20
lines changed

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

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package dotty.tools.dotc
22
package transform
33

44
import core._
5-
import Symbols._, Types._, Contexts._, StdNames._, Constants._, SymUtils._
5+
import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._, SymUtils._
66
import Flags._
77
import DenotTransformers._
88
import Decorators._
99
import NameOps._
1010
import Annotations.Annotation
11+
import typer.ProtoTypes.constrained
12+
import ast.untpd
1113
import ValueClasses.isDerivedValueClass
14+
import SymUtils._
1215

1316
/** Synthetic method implementations for case classes, case objects,
1417
* and value classes.
@@ -51,6 +54,14 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
5154
def caseSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseSymbols }
5255
def caseModuleSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseModuleSymbols }
5356

57+
private def alreadyDefined(sym: Symbol, clazz: ClassSymbol)(implicit ctx: Context): Boolean = {
58+
val existing = sym.matchingMember(clazz.thisType)
59+
existing.exists && !(existing == sym || existing.is(Deferred))
60+
}
61+
62+
private def synthesizeDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Context => Tree)(implicit ctx: Context): Tree =
63+
DefDef(sym, rhsFn(_)(ctx.withOwner(sym))).withSpan(ctx.owner.span.focus)
64+
5465
/** If this is a case or value class, return the appropriate additional methods,
5566
* otherwise return nothing.
5667
*/
@@ -68,36 +79,34 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
6879
else if (isDerivedValueClass(clazz)) valueSymbols
6980
else Nil
7081

71-
def syntheticDefIfMissing(sym: Symbol): List[Tree] = {
72-
val existing = sym.matchingMember(clazz.thisType)
73-
if (existing == sym || existing.is(Deferred)) syntheticDef(sym) :: Nil
74-
else Nil
75-
}
82+
def syntheticDefIfMissing(sym: Symbol): List[Tree] =
83+
if (alreadyDefined(sym, clazz)) Nil else syntheticDef(sym) :: Nil
7684

7785
def syntheticDef(sym: Symbol): Tree = {
7886
val synthetic = sym.copy(
7987
owner = clazz,
8088
flags = sym.flags &~ Deferred | Synthetic | Override,
89+
info = clazz.thisType.memberInfo(sym),
8190
coord = clazz.coord).enteredAfter(thisPhase).asTerm
8291

83-
def forwardToRuntime(vrefss: List[List[Tree]]): Tree =
84-
ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head)
92+
def forwardToRuntime(vrefs: List[Tree]): Tree =
93+
ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefs)
8594

86-
def ownName(vrefss: List[List[Tree]]): Tree =
95+
def ownName: Tree =
8796
Literal(Constant(clazz.name.stripModuleClassSuffix.toString))
8897

89-
def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match {
90-
case nme.hashCode_ if isDerivedValueClass(clazz) => vrefss => valueHashCodeBody
91-
case nme.hashCode_ => vrefss => caseHashCodeBody
92-
case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime
93-
case nme.equals_ => vrefss => equalsBody(vrefss.head.head)
94-
case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head)
95-
case nme.productArity => vrefss => Literal(Constant(accessors.length))
98+
def syntheticRHS(vrefss: List[List[Tree]])(implicit ctx: Context): Tree = synthetic.name match {
99+
case nme.hashCode_ if isDerivedValueClass(clazz) => valueHashCodeBody
100+
case nme.hashCode_ => caseHashCodeBody
101+
case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime(vrefss.head)
102+
case nme.equals_ => equalsBody(vrefss.head.head)
103+
case nme.canEqual_ => canEqualBody(vrefss.head.head)
104+
case nme.productArity => Literal(Constant(accessors.length))
96105
case nme.productPrefix => ownName
97-
case nme.productElement => vrefss => productElementBody(accessors.length, vrefss.head.head)
106+
case nme.productElement => productElementBody(accessors.length, vrefss.head.head)
98107
}
99108
ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}")
100-
DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))).withSpan(ctx.owner.span.focus)
109+
synthesizeDef(synthetic, syntheticRHS)
101110
}
102111

103112
/** The class
@@ -289,9 +298,97 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
289298
Nil
290299
}
291300

292-
def addSyntheticMethods(impl: Template)(implicit ctx: Context): Template = {
301+
/** The class
302+
*
303+
* ```
304+
* case class C[T <: U](x: T, y: String*)
305+
* ```
306+
*
307+
* gets the `fromProduct` method:
308+
*
309+
* ```
310+
* def fromProduct(x$0: Product): MonoType =
311+
* new C[U](
312+
* x$0.productElement(0).asInstanceOf[U],
313+
* x$0.productElement(1).asInstanceOf[Seq[String]]: _*)
314+
* ```
315+
* where
316+
* ```
317+
* type MonoType = C[_]
318+
* ```
319+
*/
320+
def fromProductBody(caseClass: Symbol, param: Tree)(implicit ctx: Context): Tree = {
321+
val (classRef, methTpe) =
322+
caseClass.primaryConstructor.info match {
323+
case tl: PolyType =>
324+
val (tl1, tpts) = constrained(tl, untpd.EmptyTree, alwaysAddTypeVars = true)
325+
val targs =
326+
for (tpt <- tpts) yield
327+
tpt.tpe match {
328+
case tvar: TypeVar => tvar.instantiate(fromBelow = false)
329+
}
330+
(caseClass.typeRef.appliedTo(targs), tl.instantiate(targs))
331+
case methTpe =>
332+
(caseClass.typeRef, methTpe)
333+
}
334+
methTpe match {
335+
case methTpe: MethodType =>
336+
val elems =
337+
for ((formal, idx) <- methTpe.paramInfos.zipWithIndex) yield {
338+
val elem =
339+
param.select(defn.Product_productElement).appliedTo(Literal(Constant(idx)))
340+
.ensureConforms(formal.underlyingIfRepeated(isJava = false))
341+
if (formal.isRepeatedParam) ctx.typer.seqToRepeated(elem) else elem
342+
}
343+
New(classRef, elems)
344+
}
345+
}
346+
347+
def addMirrorSupport(impl: Template)(implicit ctx: Context): Template = {
293348
val clazz = ctx.owner.asClass
294-
cpy.Template(impl)(body = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body)
349+
var newBody = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body
350+
var newParents = impl.parents
351+
def addParent(parent: Type) = {
352+
newParents = newParents :+ TypeTree(parent)
353+
val oldClassInfo = clazz.classInfo
354+
val newClassInfo = oldClassInfo.derivedClassInfo(
355+
classParents = oldClassInfo.classParents :+ parent)
356+
clazz.copySymDenotation(info = newClassInfo).installAfter(thisPhase)
357+
}
358+
if (clazz.is(Module)) {
359+
if (clazz.is(Case)) addParent(defn.Mirror_SingletonType)
360+
else {
361+
val linked = clazz.linkedClass
362+
if (linked.isGenericProduct) {
363+
addParent(defn.Mirror_ProductType)
364+
val rawClassType =
365+
linked.typeRef.appliedTo(linked.typeParams.map(_ => TypeBounds.empty))
366+
val monoType =
367+
ctx.newSymbol(clazz, tpnme.MonoType, Synthetic, TypeAlias(rawClassType), coord = clazz.coord)
368+
if (!alreadyDefined(monoType, clazz)) {
369+
monoType.entered
370+
newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus)
371+
}
372+
val fromProduct =
373+
ctx.newSymbol(clazz, nme.fromProduct, Synthetic | Method,
374+
info = MethodType(defn.ProductType :: Nil, monoType.typeRef), coord = clazz.coord)
375+
if (!alreadyDefined(fromProduct, clazz)) {
376+
fromProduct.entered
377+
newBody = newBody :+
378+
synthesizeDef(fromProduct, vrefss => ctx =>
379+
fromProductBody(linked, vrefss.head.head)(ctx)
380+
.ensureConforms(rawClassType)) // t4758.scala or i3381.scala are examples where a cast is needed
381+
}
382+
}
383+
}
384+
}
385+
386+
cpy.Template(impl)(parents = newParents, body = newBody)
295387
}
296388

389+
def addSyntheticMethods(impl: Template)(implicit ctx: Context): Template = {
390+
val clazz = ctx.owner.asClass
391+
addMirrorSupport(
392+
cpy.Template(impl)(body = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body))
393+
}
297394
}

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ t3486
1010
t3612.scala
1111
reference
1212

13+
# type of super reference changes due to late addition of Mirror.Singleton
14+
i939.scala
15+
1316
# Match types
1417
typelevel0.scala
1518
matchtype.scala

0 commit comments

Comments
 (0)