@@ -2,13 +2,16 @@ package dotty.tools.dotc
2
2
package transform
3
3
4
4
import core ._
5
- import Symbols ._ , Types ._ , Contexts ._ , StdNames ._ , Constants ._ , SymUtils ._
5
+ import Symbols ._ , Types ._ , Contexts ._ , Names . _ , StdNames ._ , Constants ._ , SymUtils ._
6
6
import Flags ._
7
7
import DenotTransformers ._
8
8
import Decorators ._
9
9
import NameOps ._
10
10
import Annotations .Annotation
11
+ import typer .ProtoTypes .constrained
12
+ import ast .untpd
11
13
import ValueClasses .isDerivedValueClass
14
+ import SymUtils ._
12
15
13
16
/** Synthetic method implementations for case classes, case objects,
14
17
* and value classes.
@@ -51,6 +54,14 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
51
54
def caseSymbols (implicit ctx : Context ): List [Symbol ] = { initSymbols; myCaseSymbols }
52
55
def caseModuleSymbols (implicit ctx : Context ): List [Symbol ] = { initSymbols; myCaseModuleSymbols }
53
56
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
+
54
65
/** If this is a case or value class, return the appropriate additional methods,
55
66
* otherwise return nothing.
56
67
*/
@@ -68,36 +79,34 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
68
79
else if (isDerivedValueClass(clazz)) valueSymbols
69
80
else Nil
70
81
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
76
84
77
85
def syntheticDef (sym : Symbol ): Tree = {
78
86
val synthetic = sym.copy(
79
87
owner = clazz,
80
88
flags = sym.flags &~ Deferred | Synthetic | Override ,
89
+ info = clazz.thisType.memberInfo(sym),
81
90
coord = clazz.coord).enteredAfter(thisPhase).asTerm
82
91
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 )
85
94
86
- def ownName ( vrefss : List [ List [ Tree ]]) : Tree =
95
+ def ownName : Tree =
87
96
Literal (Constant (clazz.name.stripModuleClassSuffix.toString))
88
97
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))
96
105
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)
98
107
}
99
108
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)
101
110
}
102
111
103
112
/** The class
@@ -289,9 +298,97 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
289
298
Nil
290
299
}
291
300
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 = {
293
348
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)
295
387
}
296
388
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
+ }
297
394
}
0 commit comments