Permalink
Browse files

Fixes SI-5629.

Adds an additional argument to the unify method in
specialization - `tparams`. When this parameter is set
to `true`, unification is done over type parameters in
poly types as well.

Additionally, the unification in specialization now
works over type bounds.

This ensures that in the below example:

trait Foo[@specialized(Int) A] {
  def bar[B <: A](b: B) {}
}

class IntFoo extends Foo[Int] {
  override def bar[B <: Int](b: B) {}
}

the method `bar` gets correctly determined
as a method that needs a special override
for its `bar$mcI$sp`.
  • Loading branch information...
axel22 committed May 7, 2012
1 parent ea25648 commit a11a57735e73d66147f9f6166687ad7bc04feca8
@@ -752,9 +752,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
}
val subclasses = specializations(clazz.info.typeParams) filter satisfiable
subclasses foreach { env =>
subclasses foreach {
env =>
val spc = specializedClass(env, decls1)
val existing = clazz.owner.info.decl(spc.name)
@@ -928,7 +929,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
if (currentRun compiles overriding)
checkOverriddenTParams(overridden)
val env = unify(overridden.info, overriding.info, emptyEnv, false)
val env = unify(overridden.info, overriding.info, emptyEnv, false, true)
def atNext = afterSpecialize(overridden.owner.info.decl(specializedName(overridden, env)))
if (TypeEnv.restrict(env, stvars).nonEmpty && TypeEnv.isValid(env, overridden) && atNext != NoSymbol) {
@@ -990,8 +991,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* Fails if such an environment cannot be found.
*
* If `strict` is true, a UnifyError is thrown if unification is impossible.
*
* If `tparams` is true, then the methods tries to unify over type params in polytypes as well.
*/
private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean): TypeEnv = (tp1, tp2) match {
private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean, tparams: Boolean = false): TypeEnv = (tp1, tp2) match {
case (TypeRef(_, sym1, _), _) if sym1.isSpecialized =>
debuglog("Unify " + tp1 + ", " + tp2)
if (isPrimitiveValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1))
@@ -1020,17 +1023,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
debuglog("Unify polytypes " + tp1 + " and " + tp2)
if (strict && tparams1.length != tparams2.length)
unifyError(tp1, tp2)
else if (tparams && tparams1.length == tparams2.length)
unify(res1 :: tparams1.map(_.info), res2 :: tparams2.map(_.info), env, strict)
else
unify(res1, res2, env, strict)
case (PolyType(_, res), other) => unify(res, other, env, strict)
case (ThisType(_), ThisType(_)) => env
case (_, SingleType(_, _)) => unify(tp1, tp2.underlying, env, strict)
case (SingleType(_, _), _) => unify(tp1.underlying, tp2, env, strict)
case (ThisType(_), _) => unify(tp1.widen, tp2, env, strict)
case (_, ThisType(_)) => unify(tp1, tp2.widen, env, strict)
case (RefinedType(_, _), RefinedType(_, _)) => env
case (AnnotatedType(_, tp1, _), tp2) => unify(tp2, tp1, env, strict)
case (ExistentialType(_, res1), _) => unify(tp2, res1, env, strict)
case (PolyType(_, res), other) => unify(res, other, env, strict)
case (ThisType(_), ThisType(_)) => env
case (_, SingleType(_, _)) => unify(tp1, tp2.underlying, env, strict)
case (SingleType(_, _), _) => unify(tp1.underlying, tp2, env, strict)
case (ThisType(_), _) => unify(tp1.widen, tp2, env, strict)
case (_, ThisType(_)) => unify(tp1, tp2.widen, env, strict)
case (RefinedType(_, _), RefinedType(_, _)) => env
case (AnnotatedType(_, tp1, _), tp2) => unify(tp2, tp1, env, strict)
case (ExistentialType(_, res1), _) => unify(tp2, res1, env, strict)
case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => unify(List(lo1, hi1), List(lo2, hi2), env, strict)
case _ =>
debuglog("don't know how to unify %s [%s] with %s [%s]".format(tp1, tp1.getClass, tp2, tp2.getClass))
env
@@ -0,0 +1,2 @@
int child got: 33
any child got: 33
View
@@ -0,0 +1,36 @@
import scala.{specialized => spec}
trait GrandParent[@spec(Int) -A] {
def foo(a: A): Unit
def bar[B <: A](b: B): Unit = println("grandparent got: %s" format b)
}
trait Parent[@spec(Int) -A] extends GrandParent[A] {
def foo(a: A) = bar(a)
}
class IntChild extends Parent[Int] {
override def bar[B <: Int](b: B): Unit = println("int child got: %s" format b)
}
class AnyChild extends Parent[Any] {
override def bar[B <: Any](b: B): Unit = println("any child got: %s" format b)
}
object Test {
def main(args: Array[String]) {
new IntChild().foo(33)
new AnyChild().foo(33)
}
}
@@ -0,0 +1,10 @@
=== pf(1):
MySmartPF.apply entered...
newPF.applyOrElse entered...
default
scala.MatchError: () (of class scala.runtime.BoxedUnit)
=== pf(42):
MySmartPF.apply entered...
newPF.applyOrElse entered...
ok
=== done
@@ -0,0 +1,41 @@
object Test extends App {
trait MyPF[@specialized(Int) -A] extends (A => Unit) {
def isDefinedAt(x: A): Boolean
def applyOrElse[A1 <: A](x: A1, default: A1 => Unit): Unit = {
println("MyPF.applyOrElse entered...")
if (isDefinedAt(x)) apply(x) else default(x)
}
}
trait MySmartPF[@specialized(Int) -A] extends MyPF[A] {
def apply(x: A): Unit = {
println("MySmartPF.apply entered...")
applyOrElse(x, { _: Any => throw new MatchError })
}
}
type T = Int
//type T = Any
def newPF(test: T): MyPF[T] = new MySmartPF[T] {
def isDefinedAt(x: T): Boolean = x != test
override def applyOrElse[A1 <: T](x: A1, default: A1 => Unit): Unit = {
println("newPF.applyOrElse entered...")
if (x != test) { println("ok"); () } else { println("default"); default(x) }
}
}
val pf = newPF(1)
println("=== pf(1):")
try { pf(1) } catch { case x => println(x) }
println("=== pf(42):")
pf(42)
println("=== done")
}