1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
package scala
package reflect
package internal
package transform
import Flags._
import scala.collection.mutable
trait UnCurry {
val global: SymbolTable
import global._
import definitions._
/**
* The synthetic Java vararg method symbol corresponding to a Scala vararg method
* annotated with @varargs.
*/
case class VarargsSymbolAttachment(varargMethod: Symbol)
/** Note: changing tp.normalize to tp.dealias in this method leads to a single
* test failure: run/t5688.scala, where instead of the expected output
* Vector(ta, tb, tab)
* we instead get
* Vector(tab, tb, tab)
* I think that difference is not the product of sentience but of randomness.
* Let us figure out why it is and then change this method.
*/
private def expandAlias(tp: Type): Type = if (!tp.isHigherKinded) tp.normalize else tp
val uncurry: TypeMap = new TypeMap {
def apply(tp0: Type): Type = {
val tp = expandAlias(tp0)
tp match {
case MethodType(params, MethodType(params1, restpe)) =>
// This transformation is described in UnCurryTransformer.dependentParamTypeErasure
val packSymbolsMap = new TypeMap {
// Wrapping in a TypeMap to reuse the code that opts for a fast path if the function is an identity.
def apply(tp: Type): Type = packSymbols(params, tp)
}
val existentiallyAbstractedParam1s = packSymbolsMap.mapOver(params1)
val substitutedResult = restpe.substSym(params1, existentiallyAbstractedParam1s)
apply(MethodType(params ::: existentiallyAbstractedParam1s, substitutedResult))
case MethodType(params, ExistentialType(tparams, restpe @ MethodType(_, _))) =>
abort("unexpected curried method types with intervening existential")
case MethodType(h :: t, restpe) if h.isImplicit =>
apply(MethodType(h.cloneSymbol.resetFlag(IMPLICIT) :: t, restpe))
case NullaryMethodType(restpe) =>
apply(MethodType(List(), restpe))
case DesugaredParameterType(desugaredTpe) =>
apply(desugaredTpe)
case _ =>
expandAlias(mapOver(tp))
}
}
}
object DesugaredParameterType {
def unapply(tpe: Type): Option[Type] = tpe match {
case TypeRef(pre, ByNameParamClass, arg :: Nil) =>
Some(functionType(List(), arg))
case TypeRef(pre, RepeatedParamClass, arg :: Nil) =>
Some(seqType(arg))
case TypeRef(pre, JavaRepeatedParamClass, arg :: Nil) =>
Some(arrayType(if (isUnboundedGeneric(arg)) ObjectTpe else arg))
case _ =>
None
}
}
private val uncurryType = new TypeMap {
def apply(tp0: Type): Type = {
val tp = expandAlias(tp0)
tp match {
case ClassInfoType(parents, decls, clazz) if !clazz.isJavaDefined =>
val parents1 = parents mapConserve uncurry
val varargOverloads = mutable.ListBuffer.empty[Symbol]
// Not using `hasAnnotation` here because of dreaded cyclic reference errors:
// it may happen that VarargsClass has not been initialized yet and we get here
// while processing one of its superclasses (such as java.lang.Object). Since we
// don't need the more precise `matches` semantics, we only check the symbol, which
// is anyway faster and safer
for (decl <- decls if decl.annotations.exists(_.symbol == VarargsClass)) {
if (mexists(decl.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))) {
varargOverloads += varargForwarderSym(clazz, decl, exitingPhase(phase)(decl.info))
}
}
if ((parents1 eq parents) && varargOverloads.isEmpty) tp
else {
val newDecls = decls.cloneScope
varargOverloads.foreach(newDecls.enter)
ClassInfoType(parents1, newDecls, clazz)
} // @MAT normalize in decls??
case PolyType(_, _) =>
mapOver(tp)
case _ =>
tp
}
}
}
private def varargForwarderSym(currentClass: Symbol, origSym: Symbol, newInfo: Type): Symbol = {
val forwSym = origSym.cloneSymbol(currentClass, VARARGS | SYNTHETIC | origSym.flags & ~DEFERRED, origSym.name.toTermName).withoutAnnotations
// we are using `origSym.info`, which contains the type *before* the transformation
// so we still see repeated parameter types (uncurry replaces them with Seq)
val isRepeated = origSym.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe))
val oldPs = newInfo.paramss.head
def toArrayType(tp: Type, newParam: Symbol): Type = {
val arg = elementType(SeqClass, tp)
val elem = if (arg.typeSymbol.isTypeParameterOrSkolem && !(arg <:< AnyRefTpe)) {
// To prevent generation of an `Object` parameter from `Array[T]` parameter later
// as this would crash the Java compiler which expects an `Object[]` array for varargs
// e.g. def foo[T](a: Int, b: T*)
// becomes def foo[T](a: Int, b: Array[Object])
// instead of def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object)
//
// In order for the forwarder method to type check we need to insert a cast:
// def foo'[T'](a: Int, b: Array[Object]) = foo[T'](a, wrapRefArray(b).asInstanceOf[Seq[T']])
// The target element type for that cast (T') is stored in the TypeParamVarargsAttachment
// val originalArg = arg.substSym(oldTps, tps)
// Store the type parameter that was replaced by Object to emit the correct generic signature
newParam.updateAttachment(new TypeParamVarargsAttachment(arg))
ObjectTpe
} else
arg
arrayType(elem)
}
foreach2(forwSym.paramss.flatten, isRepeated)((p, isRep) =>
if (isRep) {
p.setInfo(toArrayType(p.info, p))
}
)
origSym.updateAttachment(VarargsSymbolAttachment(forwSym))
forwSym
}
/** - return symbol's transformed type,
* - if symbol is a def parameter with transformed type T, return () => T
*
* @MAT: starting with this phase, the info of every symbol will be normalized
*/
def transformInfo(sym: Symbol, tp: Type): Type =
if (sym.isType) uncurryType(tp)
else if ((sym hasFlag MODULE) && !sym.isStatic) { // see Fields::nonStaticModuleToMethod
sym setFlag METHOD | STABLE
MethodType(Nil, uncurry(tp))
}
else uncurry(tp)
}
|