diff options
author | Iulian Dragos <jaguarul@gmail.com> | 2010-04-27 15:09:00 +0000 |
---|---|---|
committer | Iulian Dragos <jaguarul@gmail.com> | 2010-04-27 15:09:00 +0000 |
commit | 622c15815f8cfa93ab6c3b0e3ab49095988dd51f (patch) | |
tree | c668650caa98bbcb6c3e694713452d4437c329a8 /src | |
parent | f99b3ceac6465c630374e5bd5b680f4b99abc9b7 (diff) | |
download | scala-622c15815f8cfa93ab6c3b0e3ab49095988dd51f.tar.gz scala-622c15815f8cfa93ab6c3b0e3ab49095988dd51f.tar.bz2 scala-622c15815f8cfa93ab6c3b0e3ab49095988dd51f.zip |
Fixed construction of specialized classes in th...
Fixed construction of specialized classes in the presence of
side-effects and non-trivial initializers. Review by odersky.
Diffstat (limited to 'src')
3 files changed, 154 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 } |