Skip to content

Commit 993c739

Browse files
committed
Fix specialised method dispatch to recursive calls
Fixes dispatching of specialised methods to recursive calls. Also takes care of mutually recursive calls. Adds a few tests for recursive specialisation, and `@specialized` annotations with parameter `AnyRef` or `Specializable.specializable_group`
1 parent 5e938dc commit 993c739

File tree

4 files changed

+46
-40
lines changed

4 files changed

+46
-40
lines changed

src/dotty/tools/dotc/transform/TypeSpecializer.scala

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
3131
defn.DoubleType -> "D",
3232
defn.CharType -> "C",
3333
defn.UnitType -> "V",
34-
defn.AnyRefType -> "A")
34+
defn.AnyRefType -> "L")
3535

3636
private def primitiveTypes(implicit ctx: Context) =
3737
List(defn.ByteType,
@@ -48,10 +48,10 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
4848
private def defn(implicit ctx:Context) = ctx.definitions
4949

5050
private val specializationRequests: mutable.HashMap[Symbols.Symbol, List[Type]] = mutable.HashMap.empty
51-
51+
private val genericToInstantiation: mutable.HashMap[Symbols.Symbol, Type] = mutable.HashMap.empty
5252
/**
5353
* A map that links symbols to their specialized variants.
54-
* Each symbol maps to another as map, from the list of specialization types to the specialized symbol.
54+
* Each symbol maps to another map, from the list of specialization types to the specialized symbol.
5555
*/
5656
private val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[List[Type], Symbols.Symbol]] = mutable.HashMap.empty
5757

@@ -90,7 +90,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
9090
(instantiations: List[Type], names: List[String], poly: PolyType, decl: Symbol)
9191
(implicit ctx: Context): List[Symbol] = {
9292
if (remainingTParams.nonEmpty) {
93-
(for (tpe <- specTypes) yield {
93+
specTypes.map(tpe => {
9494
generateSpecializations(remainingTParams.tail, specTypes)(tpe :: instantiations, specialisedTypeToSuffix(ctx)(tpe) :: names, poly, decl)
9595
}).flatten
9696
}
@@ -134,7 +134,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
134134
case classInfo: ClassInfo =>
135135
val newDecls = classInfo.decls
136136
.filter(_.symbol.isCompleted) // we do not want to force symbols here.
137-
// if there's unforced symbol it means its not used in the source
137+
// if there's unforced symbol it means its not used in the source
138138
.filterNot(_.isConstructor)
139139
.filter(requestedSpecialization)
140140
.flatMap(decl => {
@@ -150,7 +150,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
150150
case poly: PolyType if !newSymbolMap.contains(sym) &&
151151
requestedSpecialization(sym) &&
152152
allowedToSpecialize(sym, poly.paramNames.length)=>
153-
generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, List.empty, poly, sym)
153+
generateSpecializations(poly.paramNames, getSpecTypes(sym, poly))(List.empty, List.empty, poly, sym)
154154
case nil =>
155155
}
156156
tp
@@ -173,14 +173,20 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
173173
val newSyms = declSpecs.values.toList
174174
val instantiations = declSpecs.keys.toArray
175175
var index = -1
176-
println(s"specializing ${tree.symbol} for $origTParams")
176+
ctx.debuglog(s"specializing ${tree.symbol} for $origTParams")
177177
newSyms.map { newSym =>
178178
index += 1
179179
polyDefDef(newSym.asTerm, { tparams => vparams => {
180180
val tmap: (Tree => Tree) = _ match {
181181
case Return(t, from) if from.symbol == tree.symbol => Return(t, ref(newSym))
182-
case t: TypeApply => transformTypeApply(t)
183-
case t: Apply => transformApply(t)
182+
case t: TypeApply => {
183+
(origTParams zip instantiations(index)).foreach(x => genericToInstantiation.put(x._1, x._2))
184+
transformTypeApply(t)
185+
}
186+
case t: Apply => {
187+
(origTParams zip instantiations(index)).foreach(x => genericToInstantiation.put(x._1, x._2))
188+
transformApply(t)
189+
}
184190
case t => t
185191
}
186192

@@ -195,7 +201,7 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
195201

196202
val tp = new TreeMap() {
197203
// needed to workaround https://github.com/lampepfl/dotty/issues/592
198-
override def transform(t: Tree)(implicit ctx: Context) = super.transform(t) match {
204+
override def transform(tree1: Tree)(implicit ctx: Context) = super.transform(tree1) match {
199205
case t @ Apply(fun, args) =>
200206
assert(sameLength(args, fun.tpe.widen.firstParamTypes))
201207
val newArgs = (args zip fun.tpe.widen.firstParamTypes).map{case(t, tpe) => t.ensureConforms(tpe)}
@@ -220,48 +226,25 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
220226
}
221227
}
222228

223-
override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = {
224-
val TypeApply(fun, _) = tree
225-
if (fun.tpe.isParameterless) rewireTree(tree)
226-
tree
227-
}
228-
229-
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
230-
val Apply(fun, args) = tree
231-
fun match {
232-
case fun: TypeApply => {
233-
val newFun = rewireTree(fun)
234-
if (fun ne newFun) {
235-
val as = (args zip newFun.tpe.widen.firstParamTypes).map{
236-
case (arg, tpe) =>
237-
arg.ensureConforms(tpe)
238-
}
239-
Apply(newFun,as)
240-
} else tree
241-
}
242-
case _ => tree
243-
}
244-
}
245-
246229
def rewireTree(tree: Tree)(implicit ctx: Context): Tree = {
247230
assert(tree.isInstanceOf[TypeApply])
248231
val TypeApply(fun,args) = tree
249232
if (newSymbolMap.contains(fun.symbol)){
250233
val newSymInfos = newSymbolMap(fun.symbol)
251234
val betterDefs = newSymInfos.filter(x => (x._1 zip args).forall{a =>
252235
val specializedType = a._1
253-
val argType = a._2
254-
argType.tpe <:< specializedType
236+
val argType = genericToInstantiation.getOrElse(a._2.tpe.typeSymbol, a._2.tpe)
237+
argType <:< specializedType
255238
}).toList
256239

257240
if (betterDefs.length > 1) {
258-
ctx.debuglog("Several specialized variants fit.")
241+
ctx.debuglog(s"Several specialized variants fit for method ${fun.symbol.name} of ${fun.symbol.owner}. Defaulting to no specialization.")
259242
tree
260243
}
261244

262245
else if (betterDefs.nonEmpty) {
263-
val best = betterDefs.head
264-
println(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type(s) : ${best._1.map{case TypeRef(_, name) => name}.mkString(", ")}")
246+
val bestDef = betterDefs.head
247+
ctx.debuglog(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant with type(s) : ${bestDef._1.map{case TypeRef(_, name) => name}.mkString(", ")}")
265248
val prefix = fun match {
266249
case Select(pre, name) =>
267250
pre
@@ -272,9 +255,32 @@ class TypeSpecializer extends MiniPhaseTransform with InfoTransformer {
272255
else EmptyTree
273256
}
274257
if (prefix ne EmptyTree)
275-
prefix.select(best._2)
276-
else ref(best._2)
258+
prefix.select(bestDef._2)
259+
else ref(bestDef._2)
277260
} else tree
278261
} else tree
279262
}
263+
264+
override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = {
265+
val TypeApply(fun, _) = tree
266+
if (fun.tpe.isParameterless) rewireTree(tree)
267+
tree
268+
}
269+
270+
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
271+
val Apply(fun, args) = tree
272+
fun match {
273+
case fun: TypeApply =>
274+
val newFun = rewireTree(fun)
275+
if (fun ne newFun) {
276+
val as = (args zip newFun.tpe.widen.firstParamTypes).map{
277+
case (arg, tpe) => arg.ensureConforms(tpe)
278+
}
279+
Apply(newFun,as)
280+
} else tree
281+
case fun : Apply =>
282+
Apply(transformApply(fun), args)
283+
case _ => tree
284+
}
285+
}
280286
}

tests/pos/specialization/anyRef_specialization.scala

Whitespace-only changes.

tests/pos/specialization/recursive_specialization.scala

Whitespace-only changes.

tests/pos/specialization/specializable_specialization.scala

Whitespace-only changes.

0 commit comments

Comments
 (0)