diff options
author | Paul Phillips <paulp@improving.org> | 2012-02-17 09:52:40 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-02-17 11:10:23 -0800 |
commit | 1e648c386216d4c60121321a7ec40e2536bada9c (patch) | |
tree | 300e7095cf45ba476dd5717f028cd154f4e7f702 | |
parent | 91148376049a152edec12348ff9b7e9e93e6ebe1 (diff) | |
download | scala-1e648c386216d4c60121321a7ec40e2536bada9c.tar.gz scala-1e648c386216d4c60121321a7ec40e2536bada9c.tar.bz2 scala-1e648c386216d4c60121321a7ec40e2536bada9c.zip |
Fixed AnyRef specialization.
At least for the value of fix which means "better than it was in
2.9." I accidentally spent a long while trying to fix something I didn't
realize I hadn't broken. But then I lucked into partially fixing it, so
that's good news.
See run/t5488-fn.scala if you want to see what still doesn't work.
(It's covered at SI-4770, which is now reopened.) Closes SI-5488.
-rw-r--r-- | src/compiler/scala/reflect/internal/Definitions.scala | 7 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala | 58 | ||||
-rw-r--r-- | src/library/scala/package.scala | 5 | ||||
-rw-r--r-- | test/files/buildmanager/t2652/t2652.check | 2 | ||||
-rw-r--r-- | test/files/run/t4794.check | 2 | ||||
-rw-r--r-- | test/files/run/t5488-fn.check | 17 | ||||
-rw-r--r-- | test/files/run/t5488-fn.scala | 49 | ||||
-rw-r--r-- | test/files/run/t5488.check | 14 | ||||
-rw-r--r-- | test/files/run/t5488.scala | 26 | ||||
-rw-r--r-- | test/pending/run/t4770.check (renamed from test/files/run/t4770.check) | 0 | ||||
-rw-r--r-- | test/pending/run/t4770.scala (renamed from test/files/run/t4770.scala) | 0 |
11 files changed, 148 insertions, 32 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index b4b0a7335d..d043230e48 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -70,8 +70,7 @@ trait Definitions extends reflect.api.StandardDefinitions { tpnme.Float -> FLOAT_TAG, tpnme.Double -> DOUBLE_TAG, tpnme.Boolean -> BOOL_TAG, - tpnme.Unit -> VOID_TAG, - tpnme.Object -> OBJECT_TAG + tpnme.Unit -> VOID_TAG ) private def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f) @@ -80,7 +79,7 @@ trait Definitions extends reflect.api.StandardDefinitions { private def boxedName(name: Name) = sn.Boxed(name.toTypeName) - lazy val abbrvTag = symbolsMap(ObjectClass :: ScalaValueClasses, nameToTag) + lazy val abbrvTag = symbolsMap(ScalaValueClasses, nameToTag) withDefaultValue OBJECT_TAG lazy val numericWeight = symbolsMapFilt(ScalaValueClasses, nameToWeight.keySet, nameToWeight) lazy val boxedModule = classesMap(x => getModule(boxedName(x))) lazy val boxedClass = classesMap(x => getClass(boxedName(x))) @@ -213,7 +212,7 @@ trait Definitions extends reflect.api.StandardDefinitions { // Note: this is not the type alias AnyRef, it's a companion-like // object used by the @specialize annotation. - def AnyRefModule = getMember(ScalaPackageClass, nme.AnyRef) + lazy val AnyRefModule = getMember(ScalaPackageClass, nme.AnyRef) @deprecated("Use AnyRefModule", "2.10.0") def Predef_AnyRef = AnyRefModule diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 8c34a9139d..8b1d5b3295 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -121,9 +121,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // then pos/spec-List.scala fails - why? Does this kind of check fail // for similar reasons? Does `sym.isAbstractType` make a difference? private def isSpecializedAnyRefSubtype(tp: Type, sym: Symbol) = ( - (specializedOn(sym) contains AnyRefModule) + // !!! Come back to this, not sure it's recognizing AnyRefModule + (specializedOn(sym) exists (s => !isValueClass(s))) && !isValueClass(tp.typeSymbol) && isBoundedGeneric(tp) + // && (tp <:< AnyRefClass.tpe) ) private def isBoundedGeneric(tp: Type) = tp match { case TypeRef(_, sym, _) if sym.isAbstractType => (tp <:< AnyRefClass.tpe) @@ -279,10 +281,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val pre1 = this(pre) // when searching for a specialized class, take care to map all // type parameters that are subtypes of AnyRef to AnyRef - val args1 = map2(args, sym.typeParams) { - case (tp, orig) if isSpecializedAnyRefSubtype(tp, orig) => AnyRefClass.tpe - case (tp, _) => tp - } + val args1 = map2(args, sym.info.typeParams)((tp, orig) => + if (isSpecializedAnyRefSubtype(tp, orig)) AnyRefClass.tpe + else tp + ) specializedClass.get((sym, TypeEnv.fromSpecialization(sym, args1))) match { case Some(sym1) => typeRef(pre1, sym1, survivingArgs(sym, args)) case None => typeRef(pre1, sym, args) @@ -305,7 +307,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { else specializedTypeVars(sym).intersect(env.keySet) ) val (methparams, others) = tvars.toList sortBy ("" + _.name) partition (_.owner.isMethod) - debuglog("specName(" + sym + ") env: " + env + " tvars: " + tvars) + log("specName(" + sym + ") env: " + env + " tvars: " + tvars) specializedName(sym.name, methparams map env, others map env) } @@ -322,10 +324,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { nme.getterToLocal(specializedName(nme.localToGetter(name), types1, types2)) else { val (base, cs, ms) = nme.splitSpecializedName(name) - val abbrevs = definitions.abbrvTag withDefaultValue definitions.abbrvTag(ObjectClass) newTermName(base.toString + "$" - + "m" + ms + types1.map(t => abbrevs(t.typeSymbol)).mkString("", "", "") - + "c" + cs + types2.map(t => abbrevs(t.typeSymbol)).mkString("", "", "$sp")) + + "m" + ms + types1.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "") + + "c" + cs + types2.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "$sp")) } } @@ -370,8 +371,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case set :: Nil => set map (x => List(x)) case set :: sets => for (x <- set ; xs <- loop(sets)) yield x :: xs } - // zip the keys with each permutation to create a TypeEnv - loop(keys map concreteTypes) map (xss => Map(keys zip xss: _*)) + // zip the keys with each permutation to create a TypeEnv. + // If we don't exclude the "all AnyRef" specialization, we will + // incur duplicate members and crash during mixin. + loop(keys map concreteTypes) filterNot (_ forall (_ <:< AnyRefClass.tpe)) map (xss => Map(keys zip xss: _*)) } /** Does the given 'sym' need to be specialized in the environment 'env'? @@ -962,6 +965,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } case object UnifyError extends scala.util.control.ControlThrowable + private[this] def unifyError(tp1: Any, tp2: Any): Nothing = { + log("unifyError" + ((tp1, tp2))) + throw UnifyError + } /** Return the most general type environment that specializes tp1 to tp2. * It only allows binding of type parameters annotated with @specialized. @@ -972,29 +979,34 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean): TypeEnv = (tp1, tp2) match { case (TypeRef(_, sym1, _), _) if isSpecialized(sym1) => debuglog("Unify - basic case: " + tp1 + ", " + tp2) - if (isValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1)) + if (isValueClass(tp2.typeSymbol)) env + ((sym1, tp2)) + else if (isSpecializedAnyRefSubtype(tp2, sym1)) + env + ((sym1, tp2)) // env + ((sym1, AnyRefClass.tpe)) + else if (strict) + unifyError(tp1, tp2) else - if (strict) throw UnifyError else env + env case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) => debuglog("Unify TypeRefs: " + tp1 + " and " + tp2 + " with args " + (args1, args2) + " - ") - if (strict && args1.length != args2.length) throw UnifyError + if (strict && args1.length != args2.length) unifyError(tp1, tp2) val e = unify(args1, args2, env, strict) debuglog("unified to: " + e) e case (TypeRef(_, sym1, _), _) if sym1.isTypeParameterOrSkolem => env case (MethodType(params1, res1), MethodType(params2, res2)) => - if (strict && params1.length != params2.length) throw UnifyError + if (strict && params1.length != params2.length) unifyError(tp1, tp2) debuglog("Unify MethodTypes: " + tp1 + " and " + tp2) unify(res1 :: (params1 map (_.tpe)), res2 :: (params2 map (_.tpe)), env, strict) case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => - if (strict && tparams1.length != tparams2.length) throw UnifyError debuglog("Unify PolyTypes: " + tp1 + " and " + tp2) - unify(res1, res2, env, strict) - case (PolyType(_, res), other) => - unify(res, other, env, strict) - case (ThisType(_), ThisType(_)) => env + if (strict && tparams1.length != tparams2.length) + unifyError(tp1, tp2) + 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) @@ -1016,7 +1028,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (env.keySet intersect nenv.keySet isEmpty) env ++ nenv else { debuglog("could not unify: u(" + args._1 + ", " + args._2 + ") yields " + nenv + ", env: " + env) - throw UnifyError + unifyError(tp1, tp2) } } } @@ -1242,7 +1254,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } - def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) = + def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) = { (treeType =:= memberType) || { // anyref specialization memberType match { case PolyType(_, resTpe) => @@ -1259,6 +1271,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case _ => false } } + } override def transform(tree: Tree): Tree = { val symbol = tree.symbol @@ -1289,6 +1302,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { curTree = tree tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => + // log("Attempting to specialize new %s(%s)".format(tpt, args.mkString(", "))) if (findSpec(tpt.tpe).typeSymbol ne tpt.tpe.typeSymbol) { // the ctor can be specialized log("** instantiated specialized type: " + findSpec(tpt.tpe)) diff --git a/src/library/scala/package.scala b/src/library/scala/package.scala index 9425eba232..06d7f46d52 100644 --- a/src/library/scala/package.scala +++ b/src/library/scala/package.scala @@ -29,10 +29,7 @@ package object scala { type AbstractMethodError = java.lang.AbstractMethodError // A dummy used by the specialization annotation. - // Normally it's bad juju to place objects inside package objects, - // but there's no choice here as we'd have to be AnyRef's companion - // and defined in the same file - except there is no such file. - object AnyRef extends Specializable { + val AnyRef = new Specializable { override def toString = "object AnyRef" } diff --git a/test/files/buildmanager/t2652/t2652.check b/test/files/buildmanager/t2652/t2652.check index b84c80205e..071281c6ff 100644 --- a/test/files/buildmanager/t2652/t2652.check +++ b/test/files/buildmanager/t2652/t2652.check @@ -3,7 +3,7 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(class A -> List(Added(Definition(A.x$mBc$sp)), Added(Definition(A.x$mCc$sp)), Added(Definition(A.x$mDc$sp)), Added(Definition(A.x$mFc$sp)), Added(Definition(A.x$mIc$sp)), Added(Definition(A.x$mJc$sp)), Added(Definition(A.x$mLc$sp)), Added(Definition(A.x$mSc$sp)), Added(Definition(A.x$mVc$sp)), Added(Definition(A.x$mZc$sp)), Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: <method> <triedcooking>])) +Changes: Map(class A -> List(Added(Definition(A.x$mBc$sp)), Added(Definition(A.x$mCc$sp)), Added(Definition(A.x$mDc$sp)), Added(Definition(A.x$mFc$sp)), Added(Definition(A.x$mIc$sp)), Added(Definition(A.x$mJc$sp)), Added(Definition(A.x$mSc$sp)), Added(Definition(A.x$mVc$sp)), Added(Definition(A.x$mZc$sp)), Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: <method> <triedcooking>])) invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: <method> <triedcooking>]] compiling Set(B.scala) Changes: Map(object B -> List()) diff --git a/test/files/run/t4794.check b/test/files/run/t4794.check index b4de394767..f599e28b8a 100644 --- a/test/files/run/t4794.check +++ b/test/files/run/t4794.check @@ -1 +1 @@ -11 +10 diff --git a/test/files/run/t5488-fn.check b/test/files/run/t5488-fn.check new file mode 100644 index 0000000000..196f58d063 --- /dev/null +++ b/test/files/run/t5488-fn.check @@ -0,0 +1,17 @@ +B$mcII$sp +B +B$mcIV$sp +B$mcLI$sp +B +B$mcLV$sp +B$mcVI$sp +B +B$mcVV$sp +C$mcIII$sp +C +C$mcILI$sp +C +C$mcLII$sp +C +C$mcLLI$sp +C diff --git a/test/files/run/t5488-fn.scala b/test/files/run/t5488-fn.scala new file mode 100644 index 0000000000..9e4f60002b --- /dev/null +++ b/test/files/run/t5488-fn.scala @@ -0,0 +1,49 @@ +class B[@specialized(Int, AnyRef, Unit) A, @specialized(Int, Unit) B](f: A => B) +class C[@specialized(Int, AnyRef) A, @specialized(Int, AnyRef) B, @specialized(Int) C](f: (A, B) => C) +// Not yet: +// class B[@specialized(Int, AnyRef, Unit) A, @specialized(Int, AnyRef, Unit) B](f: A => B) +// class C[@specialized(Int, AnyRef) A, @specialized(Int, AnyRef) B, @specialized(Int, AnyRef) C](f: (A, B) => C) + +object Test { + def main(args:Array[String]) { + def show(x: Any) = println(x.getClass.getName) + + show(new B((x: Int) => 1)) + show(new B((x: Int) => "abc")) + show(new B((x: Int) => ())) + show(new B((x: AnyRef) => 1)) + show(new B((x: AnyRef) => "abc")) + show(new B((x: AnyRef) => ())) + show(new B((x: Unit) => 1)) + show(new B((x: Unit) => "abc")) + show(new B((x: Unit) => ())) + + show(new C((x: Int, y: Int) => 1)) + show(new C((x: Int, y: Int) => "abc")) + show(new C((x: Int, y: AnyRef) => 1)) + show(new C((x: Int, y: AnyRef) => "abc")) + show(new C((x: AnyRef, y: Int) => 1)) + show(new C((x: AnyRef, y: Int) => "abc")) + show(new C((x: AnyRef, y: AnyRef) => 1)) + show(new C((x: AnyRef, y: AnyRef) => "abc")) + } +} +/** If the return types are specialized on AnyRef as well: + +files/run/t5488-fn.scala:18: error: type mismatch; + found : Unit => String + required: Unit => B$sp + show(new B((x: Unit) => "abc")) + ^ +files/run/t5488-fn.scala:24: error: type mismatch; + found : (Int, Object) => String + required: (Int, B$sp) => C$sp + show(new C((x: Int, y: AnyRef) => "abc")) + ^ +files/run/t5488-fn.scala:26: error: type mismatch; + found : (Object, Int) => String + required: (A$sp, Int) => C$sp + show(new C((x: AnyRef, y: Int) => "abc")) + ^ +three errors found +**/ diff --git a/test/files/run/t5488.check b/test/files/run/t5488.check new file mode 100644 index 0000000000..ccd98c4dbc --- /dev/null +++ b/test/files/run/t5488.check @@ -0,0 +1,14 @@ +A0$mcI$sp +A0 +B0$mcII$sp +B0$mcIL$sp +B0$mcLI$sp +B0 +C0$mcIII$sp +C0$mcIIL$sp +C0$mcILI$sp +C0$mcILL$sp +C0$mcLII$sp +C0$mcLIL$sp +C0$mcLLI$sp +C0 diff --git a/test/files/run/t5488.scala b/test/files/run/t5488.scala new file mode 100644 index 0000000000..7bab0cdc3c --- /dev/null +++ b/test/files/run/t5488.scala @@ -0,0 +1,26 @@ +class A0[@specialized(Int, AnyRef) A]() +class B0[@specialized(Int, AnyRef) A, @specialized(Int, AnyRef) B]() +class C0[@specialized(Int, AnyRef) A, @specialized(Int, AnyRef) B, @specialized(Int, AnyRef) C]() + +object Test { + def main(args:Array[String]) { + def show(x: Any) = println(x.getClass.getName) + + show(new A0[Int]()) + show(new A0[AnyRef]()) + + show(new B0[Int, Int]()) + show(new B0[Int, AnyRef]()) + show(new B0[AnyRef, Int]()) + show(new B0[AnyRef, AnyRef]()) + + show(new C0[Int, Int, Int]()) + show(new C0[Int, Int, AnyRef]()) + show(new C0[Int, AnyRef, Int]()) + show(new C0[Int, AnyRef, AnyRef]()) + show(new C0[AnyRef, Int, Int]()) + show(new C0[AnyRef, Int, AnyRef]()) + show(new C0[AnyRef, AnyRef, Int]()) + show(new C0[AnyRef, AnyRef, AnyRef]()) + } +} diff --git a/test/files/run/t4770.check b/test/pending/run/t4770.check index 38e5a831fa..38e5a831fa 100644 --- a/test/files/run/t4770.check +++ b/test/pending/run/t4770.check diff --git a/test/files/run/t4770.scala b/test/pending/run/t4770.scala index 25bf3050c3..25bf3050c3 100644 --- a/test/files/run/t4770.scala +++ b/test/pending/run/t4770.scala |