summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala21
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala107
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala47
-rw-r--r--test/files/run/spec-early.check4
-rw-r--r--test/files/run/spec-early.scala15
-rw-r--r--test/files/run/spec-init.check9
-rw-r--r--test/files/run/spec-init.scala41
7 files changed, 223 insertions, 21 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
index 5c7e7925ea..9133228768 100644
--- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala
+++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
@@ -95,6 +95,7 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable =>
val SELECTOR_DUMMY = newTermName("<unapply-selector>")
val MODULE_INSTANCE_FIELD = newTermName("MODULE$")
+ val SPECIALIZED_INSTANCE = newTermName("specInstance$")
def isLocalName(name: Name) = name.endsWith(LOCAL_SUFFIX)
def isSetterName(name: Name) = name.endsWith(SETTER_SUFFIX)
@@ -122,6 +123,26 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable =>
} else name
}
+ /** Return the original name and the types on which this name
+ * is specialized. For example,
+ * {{{
+ * splitSpecializedName("foo$mIcD$sp") == ('foo', "I", "D")
+ * }}}
+ * `foo$mIcD$sp` is the name of a method specialized on two type
+ * parameters, the first one belonging to the method itself, on Int,
+ * and another one belonging to the enclosing class, on Double.
+ */
+ def splitSpecializedName(name: Name): (Name, String, String) =
+ if (name.endsWith("$sp")) {
+ val name1 = name.subName(0, name.length - 3)
+ val idxC = name1.lastPos('c')
+ val idxM = name1.lastPos('m', idxC)
+ (name1.subName(0, idxM - 1).toString,
+ name1.subName(idxC + 1, name1.length).toString,
+ name1.subName(idxM + 1, idxC).toString)
+ } else
+ (name, "", "")
+
def localToGetter(name: Name): Name = {
assert(isLocalName(name))//debug
name.subName(0, name.length - LOCAL_SUFFIX.length)
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index 7169516560..ad88b783b4 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -1,4 +1,4 @@
-/* NSC -- new Scala compiler
+/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author
*/
@@ -25,12 +25,18 @@ abstract class Constructors extends Transform with ast.TreeDSL {
new ConstructorTransformer(unit)
class ConstructorTransformer(unit: CompilationUnit) extends Transformer {
+ import collection.mutable
+
+ private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = new mutable.HashMap[Symbol, List[Tree]]
def transformClassTemplate(impl: Template): Template = {
val clazz = impl.symbol.owner // the transformed class
val stats = impl.body // the transformed template body
val localTyper = typer.atOwner(impl, clazz)
+ val specializedFlag: Symbol = clazz.info.decl(nme.SPECIALIZED_INSTANCE)
+ val shouldGuard = (specializedFlag != NoSymbol) && !clazz.hasFlag(SPECIALIZED)
+
var constr: DefDef = null // The primary constructor
var constrParams: List[Symbol] = null // ... and its parameters
var constrBody: Block = null // ... and its body
@@ -68,6 +74,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
}
var thisRefSeen: Boolean = false
+ var usesSpecializedField: Boolean = false
// A transformer for expressions that go into the constructor
val intoConstructorTransformer = new Transformer {
@@ -87,6 +94,8 @@ abstract class Constructors extends Transform with ast.TreeDSL {
gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos
case Select(_, _) =>
thisRefSeen = true
+ if (specializeTypes.specializedTypeVars(tree.symbol).nonEmpty)
+ usesSpecializedField = true
super.transform(tree)
case This(_) =>
thisRefSeen = true
@@ -275,12 +284,106 @@ abstract class Constructors extends Transform with ast.TreeDSL {
copyParam(acc, parameter(acc))
}
+ /** Return a single list of statements, merging the generic class constructor with the
+ * specialized stats. The original statements are retyped in the current class, and
+ * assignments to generic fields that have a corresponding specialized assignment in
+ * `specializedStats` are replaced by the specialized assignment.
+ */
+ def mergeConstructors(genericClazz: Symbol, originalStats: List[Tree], specializedStats: List[Tree]): List[Tree] = {
+ val specBuf = new ListBuffer[Tree]
+ specBuf ++= specializedStats
+
+ def specializedAssignFor(sym: Symbol): Option[Tree] =
+ specializedStats.find {
+ case Assign(sel @ Select(This(_), _), rhs) if sel.symbol.hasFlag(SPECIALIZED) =>
+ val (generic, _, _) = nme.splitSpecializedName(nme.localToGetter(sel.symbol.name))
+ generic == nme.localToGetter(sym.name)
+ case _ => false
+ }
+
+ log("merging: " + originalStats.mkString("\n") + " : " + specializedStats.mkString("\n"))
+ val res = for (s <- originalStats; val stat = s.duplicate) yield {
+ log("merge: looking at " + stat)
+ val stat1 = stat match {
+ case Assign(sel @ Select(This(_), field), _) =>
+ specializedAssignFor(sel.symbol).getOrElse(stat)
+ case _ => stat
+ }
+ if (stat1 ne stat) {
+ log("replaced " + stat + " with " + stat1)
+ specBuf -= stat1
+ }
+
+ if (stat1 eq stat) {
+ // statements coming from the original class need retyping in the current context
+ if (settings.debug.value) log("retyping " + stat1)
+ val d = new specializeTypes.Duplicator
+ d.retyped(localTyper.context1.asInstanceOf[d.Context],
+ stat1,
+ genericClazz,
+ clazz,
+ Map.empty)
+ } else
+ stat1
+ }
+ if (specBuf.nonEmpty)
+ println("residual specialized constructor statements: " + specBuf)
+ res
+ }
+
+ /** Add an 'if' around the statements coming after the super constructor. This
+ * guard is necessary if the code uses specialized fields. A specialized field is
+ * initialized in the subclass constructor, but the accessors are (already) overridden
+ * and pointing to the (empty) fields. To fix this, a class with specialized fields
+ * will not run its constructor statements if the instance is specialized. The specialized
+ * subclass includes a copy of those constructor statements, and runs them. To flag that a class
+ * has specialized fields, and their initialization should be deferred to the subclass, method
+ * 'specInstance$' is added in phase specialize.
+ */
+ def guardSpecializedInitializer(stats0: List[Tree]): List[Tree] = if (settings.nospecialization.value) stats0 else {
+ // split the statements in presuper and postsuper
+ var (prefix, postfix) = stats0.span(tree => !((tree.symbol ne null) && tree.symbol.isConstructor))
+ if (postfix.nonEmpty) {
+ prefix = prefix :+ postfix.head
+ postfix = postfix.tail
+ }
+
+ if (usesSpecializedField && shouldGuard && postfix.nonEmpty) {
+ // save them for duplication in the specialized subclass
+ guardedCtorStats(clazz) = postfix
+
+ val tree =
+ If(
+ Apply(
+ Select(
+ Apply(gen.mkAttributedRef(specializedFlag), List()),
+ definitions.getMember(definitions.BooleanClass, nme.UNARY_!)),
+ List()),
+ Block(postfix, Literal(())),
+ EmptyTree)
+
+ prefix ::: List(localTyper.typed(tree))
+ } else if (clazz.hasFlag(SPECIALIZED)) {
+ // add initialization from its generic class constructor
+ val (genericName, _, _) = nme.splitSpecializedName(clazz.name)
+ val genericClazz = clazz.owner.info.decl(genericName.toTypeName)
+ assert(genericClazz != NoSymbol)
+
+ guardedCtorStats.get(genericClazz) match {
+ case Some(stats1) =>
+ val merged = mergeConstructors(genericClazz, stats1, postfix)
+ prefix ::: merged
+ case None => stats0
+ }
+ } else stats0
+ }
+
// Assemble final constructor
defBuf += treeCopy.DefDef(
constr, constr.mods, constr.name, constr.tparams, constr.vparamss, constr.tpt,
treeCopy.Block(
constrBody,
- paramInits ::: constrPrefixBuf.toList ::: constrStatBuf.toList,
+ paramInits ::: constrPrefixBuf.toList ::: guardSpecializedInitializer(constrStatBuf.toList),
constrBody.expr));
// Unlink all fields that can be dropped from class scope
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index 2b45062c44..1ab310282d 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -119,6 +119,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* type bounds of other @specialized type parameters (and not in its result type).
*/
def degenerate = false
+
+ def isAccessor = false
}
/** Symbol is a special overloaded method of 'original', in the environment env. */
@@ -132,7 +134,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
/** Symbol is a specialized accessor for the `target' field. */
- case class SpecializedAccessor(target: Symbol) extends SpecializedInfo
+ case class SpecializedAccessor(target: Symbol) extends SpecializedInfo {
+ override def isAccessor = true
+ }
/** Symbol is a specialized method whose body should be the target's method body. */
case class Implementation(target: Symbol) extends SpecializedInfo
@@ -220,18 +224,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* specialization on method type parameters, the second on outer environment.
*/
private def specializedName(name: Name, types1: List[Type], types2: List[Type]): Name = {
- def split: (String, String, String) = {
- if (name.endsWith("$sp")) {
- val name1 = name.subName(0, name.length - 3)
- val idxC = name1.lastPos('c')
- val idxM = name1.lastPos('m', idxC)
- (name1.subName(0, idxM - 1).toString,
- name1.subName(idxC + 1, name1.length).toString,
- name1.subName(idxM + 1, idxC).toString)
- } else
- (name.toString, "", "")
- }
-
if (nme.INITIALIZER == name || (types1.isEmpty && types2.isEmpty))
name
else if (nme.isSetterName(name))
@@ -239,8 +231,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
else if (nme.isLocalName(name))
nme.getterToLocal(specializedName(nme.localToGetter(name), types1, types2))
else {
- val (base, cs, ms) = split
- newTermName(base + "$"
+ val (base, cs, ms) = nme.splitSpecializedName(name)
+ newTermName(base.toString + "$"
+ "m" + ms + types1.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "")
+ "c" + cs + types2.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "$sp"))
}
@@ -322,16 +314,16 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}))
- private def specializedTypeVars(tpe: List[Type]): immutable.Set[Symbol] =
+ def specializedTypeVars(tpe: List[Type]): immutable.Set[Symbol] =
tpe.foldLeft(immutable.ListSet.empty[Symbol]: immutable.Set[Symbol]) {
(s, tp) => s ++ specializedTypeVars(tp)
}
- private def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] =
+ def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] =
specializedTypeVars(atPhase(currentRun.typerPhase)(sym.info))
/** Return the set of @specialized type variables mentioned by the given type. */
- private def specializedTypeVars(tpe: Type): immutable.Set[Symbol] = tpe match {
+ def specializedTypeVars(tpe: Type): immutable.Set[Symbol] = tpe match {
case TypeRef(pre, sym, args) =>
if (sym.isTypeParameter && sym.hasAnnotation(SpecializedClass))
specializedTypeVars(args) + sym
@@ -1135,7 +1127,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
localTyper.typed(treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1))
}
- case ValDef(mods, name, tpt, rhs) if symbol.hasFlag(SPECIALIZED) =>
+ case ValDef(mods, name, tpt, rhs) if symbol.hasFlag(SPECIALIZED) && !symbol.hasFlag(PARAMACCESSOR) =>
assert(body.isDefinedAt(symbol.alias))
val tree1 = treeCopy.ValDef(tree, mods, name, tpt, body(symbol.alias).duplicate)
if (settings.debug.value) log("now typing: " + tree1 + " in " + tree.symbol.owner.fullName)
@@ -1145,6 +1137,13 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
symbol.alias.enclClass,
symbol.enclClass,
typeEnv(symbol.alias) ++ typeEnv(tree.symbol))
+// val tree1 =
+// treeCopy.ValDef(tree, mods, name, tpt,
+// localTyper.typed(
+// Apply(Select(Super(currentClass, nme.EMPTY), symbol.alias.getter(symbol.alias.owner)),
+// List())))
+// if (settings.debug.value) log("replaced ValDef: " + tree1 + " in " + tree.symbol.owner.fullName)
+// tree1
case Apply(sel @ Select(sup @ Super(qual, name), name1), args)
if (sup.symbol.info.parents != atPhase(phase.prev)(sup.symbol.info.parents)) =>
@@ -1262,6 +1261,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
// if (!cls.hasFlag(SPECIALIZED))
// for (m <- specialOverrides(cls)) cls.info.decls.enter(m)
val mbrs = new mutable.ListBuffer[Tree]
+ var hasSpecializedFields = false
for (m <- cls.info.decls.toList
if m.hasFlag(SPECIALIZED)
@@ -1269,6 +1269,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
&& satisfiable(typeEnv(m), warn(cls))) {
log("creating tree for " + m.fullName)
if (m.isMethod) {
+ if (info(m).target.isGetterOrSetter) hasSpecializedFields = true
if (m.isClassConstructor) {
val origParamss = parameters(info(m).target)
assert(origParamss.length == 1) // we are after uncurry
@@ -1299,6 +1300,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
// log("created synthetic class: " + m.fullName)
}
}
+ if (hasSpecializedFields) {
+ val sym = cls.newMethod(nme.SPECIALIZED_INSTANCE, cls.pos)
+ .setInfo(MethodType(Nil, definitions.BooleanClass.tpe))
+ cls.info.decls.enter(sym)
+ mbrs += atPos(sym.pos) {
+ DefDef(sym, Literal(cls.hasFlag(SPECIALIZED)).setType(sym.tpe.finalResultType)).setType(NoType)
+ }
+ }
mbrs.toList
}
diff --git a/test/files/run/spec-early.check b/test/files/run/spec-early.check
new file mode 100644
index 0000000000..414aacc419
--- /dev/null
+++ b/test/files/run/spec-early.check
@@ -0,0 +1,4 @@
+a
+abc
+42
+abc
diff --git a/test/files/run/spec-early.scala b/test/files/run/spec-early.scala
new file mode 100644
index 0000000000..84a8983f8c
--- /dev/null
+++ b/test/files/run/spec-early.scala
@@ -0,0 +1,15 @@
+trait Tr
+
+class Foo[@specialized(Int) T](_x: T) extends {
+ val bar = "abc"
+ val baz = "bbc"
+} with Tr {
+ val x = _x
+ println(x)
+ println(bar)
+}
+
+object Test extends Application {
+ new Foo("a")
+ new Foo(42)
+}
diff --git a/test/files/run/spec-init.check b/test/files/run/spec-init.check
new file mode 100644
index 0000000000..c9d2f1b6cc
--- /dev/null
+++ b/test/files/run/spec-init.check
@@ -0,0 +1,9 @@
+java.lang.Object@6b359c1b
+java.lang.Object@6b359c1b
+null
+shouldn't see two initialized values and one uninitialized
+42
+42
+0
+ok
+ok
diff --git a/test/files/run/spec-init.scala b/test/files/run/spec-init.scala
new file mode 100644
index 0000000000..630f03ac7a
--- /dev/null
+++ b/test/files/run/spec-init.scala
@@ -0,0 +1,41 @@
+class Foo[@specialized(Int) T](_x: T) {
+ val x = _x
+ def bar {}
+
+ val y = x
+ println(x)
+ println(y)
+ println(z)
+
+ def baz {}
+ val z = y
+
+}
+
+class Bar[@specialized(Int) T] {
+ def foo(x: T) = print(x)
+}
+
+object Global {
+ var msg = "ok"
+}
+
+class TouchGlobal[@specialized(Int) T](_x: T) {
+ println(Global.msg)
+ val x = {
+ Global.msg = "not ok"
+ _x
+ }
+}
+
+object Test {
+ def main(args: Array[String]) {
+ (new Foo(new Object))
+ println("shouldn't see two initialized values and one uninitialized")
+ (new Foo(42))
+
+ (new TouchGlobal(new Object))
+ Global.msg = "ok" // reset the value
+ (new TouchGlobal(42))
+ }
+}