Skip to content

Commit 8518709

Browse files
committed
SI-6308 Specialize methods that have some unspecialized params
This is a continuation of 1591c14, which didn't go far enough to handle method calls with a mix of specialized and unspecialized type parameters. This commit modifies `specSym` to calculate the residual type of the original method after specialized type parameters have been removed and the type environment of the candidate specialized variant has been subsituted. For example, here is trace of `specSym` when searcing for the specialized variant of `f4` in the enclosed test: tree = Main.this.f4[Nothing, Int] tree.tpe = (a: Int, b: List[(Int, Nothing)])String fun.tpe = [B, A](a: A, b: List[(A, B)])String residualTreeType = [B](a: Int, b: List[(Int, B)])String memberType = [B](a: Int, b: List[(Int, B)])String env = Map(type A -> Int) doesConform = true A few "todo" tests are included that highlight an endemic issue with the current specialization implementation: type parameters that show up after `uncurry` might be clones of the original symbols from typer, if they have been through a TypeMap (e.g. within a call to `uncurryTreeType`). So testing them for existence with the `typeEnv` map is fruitless. No amount of `atPhase` acrobatics can rescue us from this; we need to transport this information in a symbol-cloning resiliant manner. Maybe Symbol Attachments?
1 parent d70c0e3 commit 8518709

File tree

3 files changed

+77
-4
lines changed

3 files changed

+77
-4
lines changed

src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,10 +1388,26 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
13881388
/* The specialized symbol of 'tree.symbol' for tree.tpe, if there is one */
13891389
def specSym(qual: Tree): Symbol = {
13901390
val env = unify(symbol.tpe, tree.tpe, emptyEnv, false)
1391-
def isMatch(member: Symbol) = (
1392-
doesConform(symbol, tree.tpe, qual.tpe memberType member, env)
1393-
&& TypeEnv.includes(typeEnv(member), env)
1394-
)
1391+
def isMatch(member: Symbol) = {
1392+
val memberType = qual.tpe memberType member
1393+
1394+
val residualTreeType = tree match {
1395+
case TypeApply(fun, targs) if fun.symbol == symbol =>
1396+
// SI-6308 Handle methods with only some type parameters specialized.
1397+
// drop the specialized type parameters from the PolyType, and
1398+
// substitute in the type environment.
1399+
val GenPolyType(tparams, tpe) = fun.tpe
1400+
val (from, to) = env.toList.unzip
1401+
val residualTParams = tparams.filterNot(env.contains)
1402+
GenPolyType(residualTParams, tpe).substituteTypes(from, to)
1403+
case _ => tree.tpe
1404+
}
1405+
1406+
(
1407+
doesConform(symbol, residualTreeType, memberType, env)
1408+
&& TypeEnv.includes(typeEnv(member), env)
1409+
)
1410+
}
13951411
if (env.isEmpty) NoSymbol
13961412
else qual.tpe member specializedName(symbol, env) suchThat isMatch
13971413
}

test/files/run/t6308.check

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
- Unspecialized type args
2+
// Specialized
3+
f1 f1$mIc$sp
4+
f2 f2$mIc$sp
5+
f3 f3$mIc$sp
6+
f4 f4$mIc$sp
7+
f5 f5$mIc$sp
8+
9+
// Unspecialized type args
10+
f4(Boolean) f4
11+
f4(String) f4
12+
13+
// Ideally these would be specialized
14+
todo1 todo1
15+
todo2 todo2
16+
todo3 todo3

test/files/run/t6308.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import scala.{specialized => sp}
2+
3+
object Test {
4+
def caller = new Exception().getStackTrace()(1).getMethodName
5+
def f1[@sp(Int) A](a: A, b: Any) = caller
6+
def f2[@sp(Int) A, B](a: A, b: String) = caller
7+
def f3[B, @sp(Int) A](a: A, b: List[B]) = caller
8+
def f4[B, @sp(Int) A](a: A, b: List[(A, B)]) = caller
9+
10+
def f5[@sp(Int) A, B <: Object](a: A, b: B) = caller
11+
12+
// `uncurryTreeType` calls a TypeMap on the call to this method and we end up with new
13+
// type parameter symbols, which are not found in `TypeEnv.includes(typeEnv(member), env)`
14+
// in `specSym`. (One of `uncurry`'s tasks is to expand type aliases in signatures.)
15+
type T = Object
16+
def todo1[@sp(Int) A, B <: T](a: A, b: String) = caller
17+
def todo2[@sp(Int) A, B <: AnyRef](a: A, b: String) = caller
18+
def todo3[B <: List[A], @specialized(Int) A](a: A, b: B) = caller
19+
20+
def main(args: Array[String]) {
21+
val s = ""
22+
val result =
23+
s"""|- Unspecialized type args
24+
|// Specialized
25+
|f1 ${f1(1,"some ref")}
26+
|f2 ${f2(1,"some ref")}
27+
|f3 ${f3(1,List("some ref"))}
28+
|f4 ${f4(1,Nil)}
29+
|f5 ${f5(1,s)}
30+
|
31+
|// Unspecialized type args
32+
|f4(Boolean) ${f4(Boolean,Nil)}
33+
|f4(String) ${f4("",Nil)}
34+
|
35+
|// Ideally these would be specialized
36+
|todo1 ${todo1(1,s)}
37+
|todo2 ${todo2(1,s)}
38+
|todo3 ${todo3(1,List(0))}""".stripMargin
39+
println(result)
40+
}
41+
}

0 commit comments

Comments
 (0)