diff options
Diffstat (limited to 'src')
14 files changed, 575 insertions, 693 deletions
diff --git a/src/actors/scala/actors/scheduler/ThreadPoolConfig.scala b/src/actors/scala/actors/scheduler/ThreadPoolConfig.scala index ee8776f397..bfd4e7ac40 100644 --- a/src/actors/scala/actors/scheduler/ThreadPoolConfig.scala +++ b/src/actors/scala/actors/scheduler/ThreadPoolConfig.scala @@ -42,10 +42,7 @@ private[actors] object ThreadPoolConfig { (propIsSetTo("actors.enableForkJoin", "true") || { Debug.info(this+": java.version = "+javaVersion) Debug.info(this+": java.vm.vendor = "+javaVmVendor) - - // on IBM J9 1.6 do not use ForkJoinPool - // XXX this all needs to go into Properties. - isJavaAtLeast("1.6") && ((javaVmVendor contains "Oracle") || (javaVmVendor contains "Sun") || (javaVmVendor contains "Apple")) + isJavaAtLeast("1.6") }) catch { case _: SecurityException => false diff --git a/src/compiler/scala/reflect/reify/package.scala b/src/compiler/scala/reflect/reify/package.scala index ae0288186b..d3cae3d123 100644 --- a/src/compiler/scala/reflect/reify/package.scala +++ b/src/compiler/scala/reflect/reify/package.scala @@ -46,11 +46,14 @@ package object reify { def reifyType(global: Global)(typer: global.analyzer.Typer,universe: global.Tree, mirror: global.Tree, tpe: global.Type, concrete: Boolean = false): global.Tree = mkReifier(global)(typer, universe, mirror, tpe, concrete = concrete).reification.asInstanceOf[global.Tree] - def reifyRuntimeClass(global: Global)(typer0: global.analyzer.Typer, tpe: global.Type, concrete: Boolean = true): global.Tree = { + def reifyRuntimeClass(global: Global)(typer0: global.analyzer.Typer, tpe0: global.Type, concrete: Boolean = true): global.Tree = { import global._ import definitions._ import analyzer.enclosingMacroPosition + // SI-7375 + val tpe = tpe0.dealiasWiden + if (tpe.isSpliceable) { val classTagInScope = typer0.resolveClassTag(enclosingMacroPosition, tpe, allowMaterialization = false) if (!classTagInScope.isEmpty) return Select(classTagInScope, nme.runtimeClass) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 89f933f577..fc532f5d44 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1423,8 +1423,9 @@ self => * }}} */ def postfixExpr(): Tree = { - val base = opstack - var top = prefixExpr() + val start = in.offset + val base = opstack + var top = prefixExpr() while (isIdent) { top = reduceStack(isExpr = true, base, top, precedence(in.name), leftAssoc = treeInfo.isLeftAssoc(in.name)) @@ -1442,9 +1443,7 @@ self => val topinfo = opstack.head opstack = opstack.tail val od = stripParens(reduceStack(isExpr = true, base, topinfo.operand, 0, leftAssoc = true)) - return atPos(od.pos.startOrPoint, topinfo.offset) { - new PostfixSelect(od, topinfo.operator.encode) - } + return makePostfixSelect(start, topinfo.offset, od, topinfo.operator) } } reduceStack(isExpr = true, base, top, 0, leftAssoc = true) diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index f737fcc635..ec4e932aae 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -242,6 +242,12 @@ abstract class TreeBuilder { Assign(lhs, rhs) } + /** Tree for `od op`, start is start0 if od.pos is borked. */ + def makePostfixSelect(start0: Int, end: Int, od: Tree, op: Name): Tree = { + val start = if (od.pos.isDefined) od.pos.startOrPoint else start0 + atPos(r2p(start, end, end)) { new PostfixSelect(od, op.encode) } + } + /** A type tree corresponding to (possibly unary) intersection type */ def makeIntersectionTypeTree(tps: List[Tree]): Tree = if (tps.tail.isEmpty) tps.head diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 6948d02fd5..fb927d15d3 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -491,8 +491,10 @@ abstract class ClassfileParser { } } - val c = if (currentIsTopLevel) pool.getClassSymbol(nameIdx) else clazz - if (currentIsTopLevel) { + val isTopLevel = !(currentClass containsChar '$') // Java class name; *don't* try to to use Scala name decoding (SI-7532) + + val c = if (isTopLevel) pool.getClassSymbol(nameIdx) else clazz + if (isTopLevel) { if (c != clazz) { if ((clazz eq NoSymbol) && (c ne NoSymbol)) clazz = c else mismatchError(c) diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 256bc4bae0..385bf2dade 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -9,6 +9,7 @@ package transform import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer import symtab.Flags._ +import util.TreeSet /** This phase converts classes with parameters into Java-like classes with * fields, which are assigned to from constructors. @@ -28,14 +29,13 @@ abstract class Constructors extends Transform with ast.TreeDSL { class ConstructorTransformer(unit: CompilationUnit) extends Transformer { - /* - * Inspect for obvious out-of-order initialization; concrete, eager vals or vars, declared in this class, - * for which a reference to the member precedes its definition. - */ - private def checkUninitializedReads(cd: ClassDef) { - val stats = cd.impl.body - val clazz = cd.symbol + 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) + // Inspect for obvious out-of-order initialization; concrete, eager vals or vars, + // declared in this class, for which a reference to the member precedes its definition. def checkableForInit(sym: Symbol) = ( (sym ne null) && (sym.isVal || sym.isVar) @@ -69,655 +69,527 @@ abstract class Constructors extends Transform with ast.TreeDSL { case t => check(t) // constructor body statement } } + val specializedFlag: Symbol = clazz.info.decl(nme.SPECIALIZED_INSTANCE) + val shouldGuard = (specializedFlag != NoSymbol) && !clazz.hasFlag(SPECIALIZED) - } // end of checkUninitializedReads() - - override def transform(tree: Tree): Tree = { - tree match { - case cd @ ClassDef(mods0, name0, tparams0, impl0) if !cd.symbol.isInterface && !isPrimitiveValueClass(cd.symbol) => - if(cd.symbol eq AnyValClass) { - cd - } - else { - checkUninitializedReads(cd) - val tplTransformer = new TemplateTransformer(unit, impl0) - treeCopy.ClassDef(cd, mods0, name0, tparams0, tplTransformer.transformed) - } - case _ => - super.transform(tree) - } - } - - } // ConstructorTransformer - - /* - * Summary - * ------- - * - * The following gets elided unless they're actually needed: - * (a) parameter-accessor fields for non-val, non-var, constructor-param-symbols, as well as - * (b) outer accessors of a final class which don't override anything. - * - * - * Gory details - * ------------ - * - * The constructors phase elides - * - * (a) parameter-accessor fields for non-val, non-var, constructor-param-symbols - * provided they're only accessed within the primary constructor; - * - * as well as - * - * (b) outer accessors directly owned by the class of interest, - * provided that class is final, they don't override anything, and moreover they aren't accessed anywhere. - * An outer accessor is backed by a param-accessor field. - * If an outer-accessor can be elided then its supporting field can be elided as well. - * - * Once the potential candidates for elision are known (as described above) it remains to visit - * those program locations where they might be accessed, and only those. - * - * What trees can be visited at this point? - * To recap, by the time the constructors phase runs, local definitions have been hoisted out of their original owner. - * Moreover, by the time elision is about to happen, the `intoConstructors` rewriting - * of template-level statements has taken place (the resulting trees can be found in `constrStatBuf`). - * - * That means: - * - * - nested classes are to be found in `defBuf` - * - * - value and method definitions are also in `defBuf` and none of them contains local methods or classes. - * - * - auxiliary constructors are to be found in `auxConstructorBuf` - * - * Coming back to the question which trees may contain accesses: - * - * (c) regarding parameter-accessor fields, all candidates in (a) are necessarily private-local, - * and thus may only be accessed from value or method definitions owned by the current class - * (ie there's no point drilling down into nested classes). - * - * (d) regarding candidates in (b), they are accesible from all places listed in (c) and in addition - * from nested classes (nested at any number of levels). - * - * In all cases, we're done with traversing as soon as all candidates have been ruled out. - * - * Finally, the whole affair of eliding is avoided for DelayedInit subclasses, - * given that for them usually nothing gets elided anyway. - * That's a consequence from re-locating the post-super-calls statements from their original location - * (the primary constructor) into a dedicated synthetic method that an anon-closure may invoke, as required by DelayedInit. - * - */ - private trait OmittablesHelper { self: TemplateTransformer => - - /* - * Initially populated with all elision candidates. - * Trees are traversed, and those candidates are removed which are actually needed. - * After that, `omittables` doesn't shrink anymore: each symbol it contains can be unlinked from clazz.info.decls. - */ - val omittables = mutable.Set.empty[Symbol] - - def populateOmittables() { - - omittables.clear() - - if(isDelayedInitSubclass) { - return + case class ConstrInfo( + constr: DefDef, // The primary constructor + constrParams: List[Symbol], // ... and its parameters + constrBody: Block // ... and its body + ) + // decompose primary constructor into the three entities above. + val constrInfo: ConstrInfo = { + stats find (_.symbol.isPrimaryConstructor) match { + case Some(ddef @ DefDef(_, _, _, List(vparams), _, rhs @ Block(_, _))) => + ConstrInfo(ddef, vparams map (_.symbol), rhs) + case x => + // AnyVal constructor is OK + assert(clazz eq AnyValClass, "no constructor in template: impl = " + impl) + return impl + } } + import constrInfo._ - def isParamCandidateForElision(sym: Symbol) = (sym.isParamAccessor && sym.isPrivateLocal) - def isOuterCandidateForElision(sym: Symbol) = (sym.isOuterAccessor && sym.owner.isEffectivelyFinal && !sym.isOverridingSymbol) - - val paramCandidatesForElision: Set[ /*Field*/ Symbol] = (clazz.info.decls.toSet filter isParamCandidateForElision) - val outerCandidatesForElision: Set[ /*Method*/ Symbol] = (clazz.info.decls.toSet filter isOuterCandidateForElision) + // The parameter accessor fields which are members of the class + val paramAccessors = clazz.constrParamAccessors - omittables ++= paramCandidatesForElision - omittables ++= outerCandidatesForElision + // The constructor parameter corresponding to an accessor + def parameter(acc: Symbol): Symbol = parameterNamed(acc.unexpandedName.getterName) - val bodyOfOuterAccessor: Map[Symbol, DefDef] = - defBuf collect { case dd: DefDef if outerCandidatesForElision(dd.symbol) => dd.symbol -> dd } toMap + // The constructor parameter with given name. This means the parameter + // has given name, or starts with given name, and continues with a `$` afterwards. + def parameterNamed(name: Name): Symbol = { + def matchesName(param: Symbol) = param.name == name || param.name.startsWith(name + nme.NAME_JOIN_STRING) - // no point traversing further once omittables is empty, all candidates ruled out already. - object detectUsages extends Traverser { - private def markUsage(sym: Symbol) { - omittables -= debuglogResult("omittables -= ")(sym) - // recursive call to mark as needed the field supporting the outer-accessor-method. - bodyOfOuterAccessor get sym foreach (this traverse _.rhs) + (constrParams filter matchesName) match { + case Nil => abort(name + " not in " + constrParams) + case p :: _ => p } - override def traverse(tree: Tree): Unit = if (omittables.nonEmpty) { - def sym = tree.symbol - tree match { - // don't mark as "needed" the field supporting this outer-accessor, ie not just yet. - case _: DefDef if outerCandidatesForElision(sym) => () - case _: Select if omittables(sym) => markUsage(sym) ; super.traverse(tree) - case _ => super.traverse(tree) - } - } - def walk(xs: Seq[Tree]) = xs.iterator foreach traverse } - if (omittables.nonEmpty) { - detectUsages walk defBuf - detectUsages walk auxConstructorBuf - } - } - def mustbeKept(sym: Symbol) = !omittables(sym) - - } // OmittablesHelper - - /* - * TemplateTransformer rewrites DelayedInit subclasses. - * The list of statements that will end up in the primary constructor can be split into: - * - * (a) up to and including the super-constructor call. - * These statements can occur only in the (bytecode-level) primary constructor. - * - * (b) remaining statements - * - * The purpose of DelayedInit is leaving (b) out of the primary constructor and have their execution "delayed". - * - * The rewriting to achieve "delayed initialization" involves: - * (c) an additional, synthetic, public method encapsulating (b) - * (d) an additional, synthetic closure whose argless apply() just invokes (c) - * (e) after executing the statements in (a), - * the primary constructor instantiates (d) and passes it as argument - * to a `delayedInit()` invocation on the current instance. - * In turn, `delayedInit()` is a method defined as abstract in the `DelayedInit` trait - * so that it can be overridden (for an example see `scala.App`) - * - * The following helper methods prepare Trees as part of this rewriting: - * - * (f) `delayedEndpointDef()` prepares (c). - * A transformer, `constrStatTransformer`, is used to re-locate statements (b) from template-level - * to become statements in method (c). The main task here is re-formulating accesses to params - * of the primary constructors (to recap, (c) has zero-params) in terms of param-accessor fields. - * In a Delayed-Init subclass, each class-constructor gets a param-accessor field because `mustbeKept()` forces it. - * - * (g) `delayedInitClosure()` prepares (d) - * - * (h) `delayedInitCall()` prepares the `delayedInit()` invocation referred to in (e) - * - * Both (c) and (d) are added to the Template returned by `transformClassTemplate()` - * - * A note of historic interest: Previously the rewriting for DelayedInit would include in the closure body - * all of the delayed initialization sequence, which in turn required: - * - reformulating "accesses-on-this" into "accesses-on-outer", and - * - adding public getters and setters. - * - * @param stats the statements in (b) above - * - * @return the DefDef for (c) above - * - * */ - private trait DelayedInitHelper { self: TemplateTransformer => - - private def delayedEndpointDef(stats: List[Tree]): DefDef = { - - val methodName = currentUnit.freshTermName("delayedEndpoint$" + clazz.fullNameAsName('$').toString + "$") - val methodSym = clazz.newMethod(methodName, impl.pos, SYNTHETIC | FINAL) - methodSym setInfoAndEnter MethodType(Nil, UnitTpe) - - // changeOwner needed because the `stats` contained in the DefDef were owned by the template, not long ago. - val blk = Block(stats, gen.mkZero(UnitTpe)).changeOwner(impl.symbol -> methodSym) - val delayedDD = localTyper typed { DefDef(methodSym, Nil, blk) } - - delayedDD.asInstanceOf[DefDef] - } - private def delayedInitClosure(delayedEndPointSym: MethodSymbol): ClassDef = { - val satelliteClass = localTyper.typed { - atPos(impl.pos) { - val closureClass = clazz.newClass(nme.delayedInitArg.toTypeName, impl.pos, SYNTHETIC | FINAL) - val closureParents = List(AbstractFunctionClass(0).tpe) - - closureClass setInfoAndEnter new ClassInfoType(closureParents, newScope, closureClass) - - val outerField: TermSymbol = ( - closureClass - newValue(nme.OUTER, impl.pos, PrivateLocal | PARAMACCESSOR) - setInfoAndEnter clazz.tpe - ) - val applyMethod: MethodSymbol = ( - closureClass - newMethod(nme.apply, impl.pos, FINAL) - setInfoAndEnter MethodType(Nil, ObjectTpe) - ) - val outerFieldDef = ValDef(outerField) - val closureClassTyper = localTyper.atOwner(closureClass) - val applyMethodTyper = closureClassTyper.atOwner(applyMethod) - - def applyMethodStat = - applyMethodTyper.typed { - atPos(impl.pos) { - val receiver = Select(This(closureClass), outerField) - Apply(Select(receiver, delayedEndPointSym), Nil) - } - } - - val applyMethodDef = DefDef( - sym = applyMethod, - vparamss = ListOfNil, - rhs = Block(applyMethodStat, gen.mkAttributedRef(BoxedUnit_UNIT))) - - ClassDef( - sym = closureClass, - constrMods = Modifiers(0), - vparamss = List(List(outerFieldDef)), - body = applyMethodDef :: Nil, - superPos = impl.pos) + var usesSpecializedField: Boolean = false + + // A transformer for expressions that go into the constructor + val intoConstructorTransformer = new Transformer { + def isParamRef(sym: Symbol) = + sym.isParamAccessor && + sym.owner == clazz && + !(clazz isSubClass DelayedInitClass) && + !(sym.isGetter && sym.accessed.isVariable) && + !sym.isSetter + private def possiblySpecialized(s: Symbol) = specializeTypes.specializedTypeVars(s).nonEmpty + override def transform(tree: Tree): Tree = tree match { + case Apply(Select(This(_), _), List()) => + // references to parameter accessor methods of own class become references to parameters + // outer accessors become references to $outer parameter + if (isParamRef(tree.symbol) && !possiblySpecialized(tree.symbol)) + gen.mkAttributedIdent(parameter(tree.symbol.accessed)) setPos tree.pos + else if (tree.symbol.outerSource == clazz && !clazz.isImplClass) + gen.mkAttributedIdent(parameterNamed(nme.OUTER)) setPos tree.pos + else + super.transform(tree) + case Select(This(_), _) if (isParamRef(tree.symbol) && !possiblySpecialized(tree.symbol)) => + // references to parameter accessor field of own class become references to parameters + gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos + case Select(_, _) => + if (specializeTypes.specializedTypeVars(tree.symbol).nonEmpty) + usesSpecializedField = true + super.transform(tree) + case _ => + super.transform(tree) } } - satelliteClass.asInstanceOf[ClassDef] - } - - private def delayedInitCall(closure: Tree) = localTyper.typedPos(impl.pos) { - gen.mkMethodCall(This(clazz), delayedInitMethod, Nil, List(New(closure.symbol.tpe, This(clazz)))) - } + // Move tree into constructor, take care of changing owner from `oldowner` to constructor symbol + def intoConstructor(oldowner: Symbol, tree: Tree) = + intoConstructorTransformer transform tree.changeOwner(oldowner -> constr.symbol) - def rewriteDelayedInit() { - - /* XXX This is not corect: remainingConstrStats.nonEmpty excludes too much, - * but excluding it includes too much. The constructor sequence being mimicked - * needs to be reproduced with total fidelity. - * - * See test case files/run/bug4680.scala, the output of which is wrong in many - * particulars. - */ - val needsDelayedInit = (isDelayedInitSubclass && remainingConstrStats.nonEmpty) + // Should tree be moved in front of super constructor call? + def canBeMoved(tree: Tree) = tree match { + case ValDef(mods, _, _, _) => (mods hasFlag PRESUPER | PARAMACCESSOR) + case _ => false + } - if (needsDelayedInit) { - val delayedHook: DefDef = delayedEndpointDef(remainingConstrStats) - defBuf += delayedHook - val hookCallerClass = { - // transform to make the closure-class' default constructor assign the the outer instance to its param-accessor field. - val drillDown = new ConstructorTransformer(unit) - drillDown transform delayedInitClosure(delayedHook.symbol.asInstanceOf[MethodSymbol]) + // Create an assignment to class field `to` with rhs `from` + def mkAssign(to: Symbol, from: Tree): Tree = + localTyper.typedPos(to.pos) { Assign(Select(This(clazz), to), from) } + + // Create code to copy parameter to parameter accessor field. + // If parameter is $outer, check that it is not null so that we NPE + // here instead of at some unknown future $outer access. + def copyParam(to: Symbol, from: Symbol): Tree = { + import CODE._ + val result = mkAssign(to, Ident(from)) + + if (from.name != nme.OUTER || + from.tpe.typeSymbol.isPrimitiveValueClass) result + else localTyper.typedPos(to.pos) { + // `throw null` has the same effect as `throw new NullPointerException`, see JVM spec on instruction `athrow` + IF (from OBJ_EQ NULL) THEN Throw(gen.mkZero(ThrowableTpe)) ELSE result } - defBuf += hookCallerClass - remainingConstrStats = delayedInitCall(hookCallerClass) :: Nil } - } + // The list of definitions that go into class + val defBuf = new ListBuffer[Tree] - } // DelayedInitHelper + // The auxiliary constructors, separate from the defBuf since they should + // follow the primary constructor + val auxConstructorBuf = new ListBuffer[Tree] - private trait GuardianOfCtorStmts { self: TemplateTransformer => + // The list of statements that go into constructor after and including the superclass constructor call + val constrStatBuf = new ListBuffer[Tree] - /* 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. - */ - private def mergeConstructors(genericClazz: Symbol, originalStats: List[Tree], specializedStats: List[Tree]): List[Tree] = { - val specBuf = new ListBuffer[Tree] - specBuf ++= specializedStats + // The list of early initializer statements that go into constructor before the superclass constructor call + val constrPrefixBuf = new ListBuffer[Tree] - def specializedAssignFor(sym: Symbol): Option[Tree] = - specializedStats find { - case Assign(sel @ Select(This(_), _), _) => - sel.symbol.isSpecialized && (nme.unspecializedName(sel.symbol.getterName) == sym.getterName) - case _ => false - } + // The early initialized field definitions of the class (these are the class members) + val presupers = treeInfo.preSuperFields(stats) - /* Rewrite calls to ScalaRunTime.array_update to the proper apply method in scala.Array. - * Erasure transforms Array.update to ScalaRunTime.update when the element type is a type - * variable, but after specialization this is a concrete primitive type, so it would - * be an error to pass it to array_update(.., .., Object). - */ - def rewriteArrayUpdate(tree: Tree): Tree = { - val adapter = new Transformer { - override def transform(t: Tree): Tree = t match { - case Apply(fun @ Select(receiver, method), List(xs, idx, v)) if fun.symbol == arrayUpdateMethod => - localTyper.typed(Apply(gen.mkAttributedSelect(xs, arrayUpdateMethod), List(idx, v))) - case _ => super.transform(t) - } - } - adapter.transform(tree) - } - - log("merging: " + originalStats.mkString("\n") + "\nwith\n" + specializedStats.mkString("\n")) - val res = for (s <- originalStats; 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 + // generate code to copy pre-initialized fields + for (stat <- constrBody.stats) { + constrStatBuf += stat + stat match { + case ValDef(mods, name, _, _) if (mods hasFlag PRESUPER) => + // stat is the constructor-local definition of the field value + val fields = presupers filter (_.getterName == name) + assert(fields.length == 1) + val to = fields.head.symbol + if (!to.tpe.isInstanceOf[ConstantType]) + constrStatBuf += mkAssign(to, Ident(stat.symbol)) + case _ => } - - if (stat1 eq stat) { - assert(ctorParams(genericClazz).length == constrInfo.constrParams.length) - // this is just to make private fields public - (new specializeTypes.ImplementationAdapter(ctorParams(genericClazz), constrInfo.constrParams, null, true))(stat1) - - val stat2 = rewriteArrayUpdate(stat1) - // statements coming from the original class need retyping in the current context - debuglog("retyping " + stat2) - - val d = new specializeTypes.Duplicator(Map[Symbol, Type]()) - d.retyped(localTyper.context1.asInstanceOf[d.Context], - stat2, - 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(stats: List[Tree]): List[Tree] = if (settings.nospecialization.value) stats 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 (shouldGuard && usesSpecializedField && stats.nonEmpty) { - // save them for duplication in the specialized subclass - guardedCtorStats(clazz) = stats - ctorParams(clazz) = constrInfo.constrParams - - val tree = - If( - Apply( - CODE.NOT ( - Apply(gen.mkAttributedRef(specializedFlag), List())), - List()), - Block(stats, Literal(Constant(()))), - EmptyTree) - - List(localTyper.typed(tree)) } - else if (clazz.hasFlag(SPECIALIZED)) { - // add initialization from its generic class constructor - val genericName = nme.unspecializedName(clazz.name) - val genericClazz = clazz.owner.info.decl(genericName.toTypeName) - assert(genericClazz != NoSymbol, clazz) - - guardedCtorStats.get(genericClazz) match { - case Some(stats1) => mergeConstructors(genericClazz, stats1, stats) - case None => stats - } - } else stats - } - } // GuardianOfCtorStmts - - private class TemplateTransformer(val unit: CompilationUnit, val impl: Template) - extends Transformer - with DelayedInitHelper - with OmittablesHelper - with GuardianOfCtorStmts { - - 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) - - val isDelayedInitSubclass = (clazz isSubClass DelayedInitClass) - - case class ConstrInfo( - constr: DefDef, // The primary constructor - constrParams: List[Symbol], // ... and its parameters - constrBody: Block // ... and its body - ) - // decompose primary constructor into the three entities above. - val constrInfo: ConstrInfo = { - val ddef = (stats find (_.symbol.isPrimaryConstructor)) - ddef match { - case Some(ddef @ DefDef(_, _, _, List(vparams), _, rhs @ Block(_, _))) => - ConstrInfo(ddef, vparams map (_.symbol), rhs) - case x => - abort("no constructor in template: impl = " + impl) + // Triage all template definitions to go into defBuf/auxConstructorBuf, constrStatBuf, or constrPrefixBuf. + for (stat <- stats) stat match { + case DefDef(_,_,_,_,_,rhs) => + // methods with constant result type get literals as their body + // all methods except the primary constructor go into template + stat.symbol.tpe match { + case MethodType(List(), tp @ ConstantType(c)) => + defBuf += deriveDefDef(stat)(Literal(c) setPos _.pos setType tp) + case _ => + if (stat.symbol.isPrimaryConstructor) () + else if (stat.symbol.isConstructor) auxConstructorBuf += stat + else defBuf += stat + } + case ValDef(_, _, _, rhs) => + // val defs with constant right-hand sides are eliminated. + // for all other val defs, an empty valdef goes into the template and + // the initializer goes as an assignment into the constructor + // if the val def is an early initialized or a parameter accessor, it goes + // before the superclass constructor call, otherwise it goes after. + // Lazy vals don't get the assignment in the constructor. + if (!stat.symbol.tpe.isInstanceOf[ConstantType]) { + if (rhs != EmptyTree && !stat.symbol.isLazy) { + val rhs1 = intoConstructor(stat.symbol, rhs) + (if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign( + stat.symbol, rhs1) + } + defBuf += deriveValDef(stat)(_ => EmptyTree) + } + case ClassDef(_, _, _, _) => + // classes are treated recursively, and left in the template + defBuf += new ConstructorTransformer(unit).transform(stat) + case _ => + // all other statements go into the constructor + constrStatBuf += intoConstructor(impl.symbol, stat) } - } - import constrInfo._ - // The parameter accessor fields which are members of the class - val paramAccessors = clazz.constrParamAccessors + // ----------- avoid making parameter-accessor fields for symbols accessed only within the primary constructor -------------- - // The constructor parameter corresponding to an accessor - def parameter(acc: Symbol): Symbol = parameterNamed(acc.unexpandedName.getterName) + // A sorted set of symbols that are known to be accessed outside the primary constructor. + val accessedSyms = new TreeSet[Symbol]((x, y) => x isLess y) - // The constructor parameter with given name. This means the parameter - // has given name, or starts with given name, and continues with a `$` afterwards. - def parameterNamed(name: Name): Symbol = { - def matchesName(param: Symbol) = param.name == name || param.name.startsWith(name + nme.NAME_JOIN_STRING) + // a list of outer accessor symbols and their bodies + var outerAccessors: List[(Symbol, Tree)] = List() - (constrParams filter matchesName) match { - case Nil => abort(name + " not in " + constrParams) - case p :: _ => p - } - } + val isDelayedInitSubclass = (clazz isSubClass DelayedInitClass) - /* - * `usesSpecializedField` makes a difference in deciding whether constructor-statements - * should be guarded in a `shouldGuard` class, ie in a class that's the generic super-class of - * one or more specialized sub-classes. - * - * Given that `usesSpecializedField` isn't read for any other purpose than the one described above, - * we skip setting `usesSpecializedField` in case the current class isn't `shouldGuard` to start with. - * That way, trips to a map in `specializeTypes` are saved. - */ - var usesSpecializedField: Boolean = false - - /* - * A transformer for expressions that go into the constructor. - */ - private class IntoCtorTransformer extends Transformer { + // Could symbol's definition be omitted, provided it is not accessed? + // This is the case if the symbol is defined in the current class, and + // ( the symbol is an object private parameter accessor field, or + // the symbol is an outer accessor of a final class which does not override another outer accessor. ) + def maybeOmittable(sym: Symbol) = sym.owner == clazz && ( + sym.isParamAccessor && sym.isPrivateLocal || + sym.isOuterAccessor && sym.owner.isEffectivelyFinal && !sym.isOverridingSymbol && + !isDelayedInitSubclass + ) - private def isParamRef(sym: Symbol) = { sym.isParamAccessor && sym.owner == clazz } + // Is symbol known to be accessed outside of the primary constructor, + // or is it a symbol whose definition cannot be omitted anyway? + def mustbeKept(sym: Symbol) = isDelayedInitSubclass || !maybeOmittable(sym) || (accessedSyms contains sym) - /* - * Terminology: a stationary location is never written after being read. - */ - private def isStationaryParamRef(sym: Symbol) = { - isParamRef(sym) && - !(sym.isGetter && sym.accessed.isVariable) && - !sym.isSetter + // A traverser to set accessedSyms and outerAccessors + val accessTraverser = new Traverser { + override def traverse(tree: Tree) = { + tree match { + case DefDef(_, _, _, _, _, body) + if (tree.symbol.isOuterAccessor && tree.symbol.owner == clazz && clazz.isEffectivelyFinal) => + debuglog("outerAccessors += " + tree.symbol.fullName) + outerAccessors ::= ((tree.symbol, body)) + case Select(_, _) => + if (!mustbeKept(tree.symbol)) { + debuglog("accessedSyms += " + tree.symbol.fullName) + accessedSyms addEntry tree.symbol + } + super.traverse(tree) + case _ => + super.traverse(tree) + } + } } - private def possiblySpecialized(s: Symbol) = specializeTypes.specializedTypeVars(s).nonEmpty + // first traverse all definitions except outeraccesors + // (outeraccessors are avoided in accessTraverser) + for (stat <- defBuf.iterator ++ auxConstructorBuf.iterator) + accessTraverser.traverse(stat) + + // then traverse all bodies of outeraccessors which are accessed themselves + // note: this relies on the fact that an outer accessor never calls another + // outer accessor in the same class. + for ((accSym, accBody) <- outerAccessors) + if (mustbeKept(accSym)) accessTraverser.traverse(accBody) + + // Initialize all parameters fields that must be kept. + val paramInits = paramAccessors filter mustbeKept map { acc => + // Check for conflicting symbol amongst parents: see bug #1960. + // It would be better to mangle the constructor parameter name since + // it can only be used internally, but I think we need more robust name + // mangling before we introduce more of it. + val conflict = clazz.info.nonPrivateMember(acc.name) filter (s => s.isGetter && !s.isOuterField && s.enclClass.isTrait) + if (conflict ne NoSymbol) + unit.error(acc.pos, "parameter '%s' requires field but conflicts with %s".format(acc.name, conflict.fullLocationString)) + + copyParam(acc, parameter(acc)) + } - /* - * whether `sym` denotes a param-accessor (ie a field) that fulfills all of: - * (a) has stationary value, ie the same value provided via the corresponding ctor-arg; and - * (b) isn't subject to specialization. We might be processing statements for: - * (b.1) the constructur in the generic (super-)class; or - * (b.2) the constructor in the specialized (sub-)class. - * (c) isn't part of a DelayedInit subclass. + /* 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. */ - private def canBeSupplanted(sym: Symbol) = { !isDelayedInitSubclass && isStationaryParamRef(sym) && !possiblySpecialized(sym) } - - override def transform(tree: Tree): Tree = tree match { - - case Apply(Select(This(_), _), List()) => - // references to parameter accessor methods of own class become references to parameters - // outer accessors become references to $outer parameter - if (canBeSupplanted(tree.symbol)) - gen.mkAttributedIdent(parameter(tree.symbol.accessed)) setPos tree.pos - else if (tree.symbol.outerSource == clazz && !clazz.isImplClass) - gen.mkAttributedIdent(parameterNamed(nme.OUTER)) setPos tree.pos - else - super.transform(tree) + 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(_), _), _) => + sel.symbol.isSpecialized && (nme.unspecializedName(sel.symbol.getterName) == sym.getterName) + case _ => false + } - case Select(This(_), _) if (canBeSupplanted(tree.symbol)) => - // references to parameter accessor field of own class become references to parameters - gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos + /* Rewrite calls to ScalaRunTime.array_update to the proper apply method in scala.Array. + * Erasure transforms Array.update to ScalaRunTime.update when the element type is a type + * variable, but after specialization this is a concrete primitive type, so it would + * be an error to pass it to array_update(.., .., Object). + */ + def rewriteArrayUpdate(tree: Tree): Tree = { + val adapter = new Transformer { + override def transform(t: Tree): Tree = t match { + case Apply(fun @ Select(receiver, method), List(xs, idx, v)) if fun.symbol == arrayUpdateMethod => + localTyper.typed(Apply(gen.mkAttributedSelect(xs, arrayUpdateMethod), List(idx, v))) + case _ => super.transform(t) + } + } + adapter.transform(tree) + } - case Select(_, _) if shouldGuard => // reasoning behind this guard in the docu of `usesSpecializedField` - if (possiblySpecialized(tree.symbol)) { - usesSpecializedField = true + log("merging: " + originalStats.mkString("\n") + "\nwith\n" + specializedStats.mkString("\n")) + val res = for (s <- originalStats; 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 } - super.transform(tree) - case _ => - super.transform(tree) + if (stat1 eq stat) { + assert(ctorParams(genericClazz).length == constrParams.length) + // this is just to make private fields public + (new specializeTypes.ImplementationAdapter(ctorParams(genericClazz), constrParams, null, true))(stat1) + + val stat2 = rewriteArrayUpdate(stat1) + // statements coming from the original class need retyping in the current context + debuglog("retyping " + stat2) + + val d = new specializeTypes.Duplicator(Map[Symbol, Type]()) + d.retyped(localTyper.context1.asInstanceOf[d.Context], + stat2, + 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(stats: List[Tree]): List[Tree] = if (settings.nospecialization.value) stats 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 && stats.nonEmpty) { + // save them for duplication in the specialized subclass + guardedCtorStats(clazz) = stats + ctorParams(clazz) = constrParams + + val tree = + If( + Apply( + CODE.NOT ( + Apply(gen.mkAttributedRef(specializedFlag), List())), + List()), + Block(stats, Literal(Constant(()))), + EmptyTree) + + List(localTyper.typed(tree)) + } + else if (clazz.hasFlag(SPECIALIZED)) { + // add initialization from its generic class constructor + val genericName = nme.unspecializedName(clazz.name) + val genericClazz = clazz.owner.info.decl(genericName.toTypeName) + assert(genericClazz != NoSymbol, clazz) + + guardedCtorStats.get(genericClazz) match { + case Some(stats1) => mergeConstructors(genericClazz, stats1, stats) + case None => stats + } + } else stats + } - private val intoConstructorTransformer = new IntoCtorTransformer + /* + * Translation scheme for DelayedInit + * ---------------------------------- + * + * Before returning, transformClassTemplate() rewrites DelayedInit subclasses. + * The list of statements that will end up in the primary constructor can be split into: + * + * (a) up to and including the super-constructor call. + * These statements can occur only in the (bytecode-level) primary constructor. + * + * (b) remaining statements + * + * The purpose of DelayedInit is leaving (b) out of the primary constructor and have their execution "delayed". + * + * The rewriting to achieve "delayed initialization" involves: + * (c) an additional, synthetic, public method encapsulating (b) + * (d) an additional, synthetic closure whose argless apply() just invokes (c) + * (e) after executing the statements in (a), + * the primary constructor instantiates (d) and passes it as argument + * to a `delayedInit()` invocation on the current instance. + * In turn, `delayedInit()` is a method defined as abstract in the `DelayedInit` trait + * so that it can be overridden (for an example see `scala.App`) + * + * The following helper methods prepare Trees as part of this rewriting: + * + * (f) `delayedEndpointDef()` prepares (c). + * A transformer, `constrStatTransformer`, is used to re-locate statements (b) from template-level + * to become statements in method (c). The main task here is re-formulating accesses to params + * of the primary constructors (to recap, (c) has zero-params) in terms of param-accessor fields. + * In a Delayed-Init subclass, each class-constructor gets a param-accessor field because `mustbeKept()` forces it. + * + * (g) `delayedInitClosure()` prepares (d) + * + * (h) `delayedInitCall()` prepares the `delayedInit()` invocation referred to in (e) + * + * Both (c) and (d) are added to the Template returned by `transformClassTemplate()` + * + * A note of historic interest: Previously the rewriting for DelayedInit would include in the closure body + * all of the delayed initialization sequence, which in turn required: + * - reformulating "accesses-on-this" into "accesses-on-outer", and + * - adding public getters and setters. + * + * @param stats the statements in (b) above + * + * @return the DefDef for (c) above + * + * */ + def delayedEndpointDef(stats: List[Tree]): DefDef = { - // Move tree into constructor, take care of changing owner from `oldowner` to constructor symbol - def intoConstructor(oldowner: Symbol, tree: Tree) = - intoConstructorTransformer transform tree.changeOwner(oldowner -> constr.symbol) + val methodName = currentUnit.freshTermName("delayedEndpoint$" + clazz.fullNameAsName('$').toString + "$") + val methodSym = clazz.newMethod(methodName, impl.pos, SYNTHETIC | FINAL) + methodSym setInfoAndEnter MethodType(Nil, UnitTpe) - // Should tree be moved in front of super constructor call? - def canBeMoved(tree: Tree) = tree match { - case ValDef(mods, _, _, _) => (mods hasFlag PRESUPER | PARAMACCESSOR) - case _ => false - } + // changeOwner needed because the `stats` contained in the DefDef were owned by the template, not long ago. + val blk = Block(stats, gen.mkZero(UnitTpe)).changeOwner(impl.symbol -> methodSym) + val delayedDD = localTyper typed { DefDef(methodSym, Nil, blk) } - // Create an assignment to class field `to` with rhs `from` - def mkAssign(to: Symbol, from: Tree): Tree = - localTyper.typedPos(to.pos) { Assign(Select(This(clazz), to), from) } - - // Create code to copy parameter to parameter accessor field. - // If parameter is $outer, check that it is not null so that we NPE - // here instead of at some unknown future $outer access. - def copyParam(to: Symbol, from: Symbol): Tree = { - import CODE._ - val result = mkAssign(to, Ident(from)) - - if (from.name != nme.OUTER || - from.tpe.typeSymbol.isPrimitiveValueClass) result - else localTyper.typedPos(to.pos) { - // `throw null` has the same effect as `throw new NullPointerException`, see JVM spec on instruction `athrow` - IF (from OBJ_EQ NULL) THEN Throw(gen.mkZero(ThrowableTpe)) ELSE result + delayedDD.asInstanceOf[DefDef] } - } - // The list of definitions that go into class - val defBuf = new ListBuffer[Tree] - - // The auxiliary constructors, separate from the defBuf since they should - // follow the primary constructor - val auxConstructorBuf = new ListBuffer[Tree] - - // The list of statements that go into constructor after and including the superclass constructor call - val constrStatBuf = new ListBuffer[Tree] - - // The list of early initializer statements that go into constructor before the superclass constructor call - val constrPrefixBuf = new ListBuffer[Tree] - - // The early initialized field definitions of the class (these are the class members) - val presupers = treeInfo.preSuperFields(stats) - - // generate code to copy pre-initialized fields - for (stat <- constrBody.stats) { - constrStatBuf += stat - stat match { - case ValDef(mods, name, _, _) if (mods hasFlag PRESUPER) => - // stat is the constructor-local definition of the field value - val fields = presupers filter (_.getterName == name) - assert(fields.length == 1) - val to = fields.head.symbol - if (!to.tpe.isInstanceOf[ConstantType]) - constrStatBuf += mkAssign(to, Ident(stat.symbol)) - case _ => - } - } + /* @see overview at `delayedEndpointDef()` of the translation scheme for DelayedInit */ + def delayedInitClosure(delayedEndPointSym: MethodSymbol): ClassDef = { + val satelliteClass = localTyper.typed { + atPos(impl.pos) { + val closureClass = clazz.newClass(nme.delayedInitArg.toTypeName, impl.pos, SYNTHETIC | FINAL) + val closureParents = List(AbstractFunctionClass(0).tpe) + + closureClass setInfoAndEnter new ClassInfoType(closureParents, newScope, closureClass) + + val outerField: TermSymbol = ( + closureClass + newValue(nme.OUTER, impl.pos, PrivateLocal | PARAMACCESSOR) + setInfoAndEnter clazz.tpe + ) + val applyMethod: MethodSymbol = ( + closureClass + newMethod(nme.apply, impl.pos, FINAL) + setInfoAndEnter MethodType(Nil, ObjectTpe) + ) + val outerFieldDef = ValDef(outerField) + val closureClassTyper = localTyper.atOwner(closureClass) + val applyMethodTyper = closureClassTyper.atOwner(applyMethod) + + def applyMethodStat = + applyMethodTyper.typed { + atPos(impl.pos) { + val receiver = Select(This(closureClass), outerField) + Apply(Select(receiver, delayedEndPointSym), Nil) + } + } - // Triage all template definitions to go into defBuf/auxConstructorBuf, constrStatBuf, or constrPrefixBuf. - for (stat <- stats) stat match { - case DefDef(_,_,_,_,_,rhs) => - // methods with constant result type get literals as their body - // all methods except the primary constructor go into template - stat.symbol.tpe match { - case MethodType(List(), tp @ ConstantType(c)) => - defBuf += deriveDefDef(stat)(Literal(c) setPos _.pos setType tp) - case _ => - if (stat.symbol.isPrimaryConstructor) () - else if (stat.symbol.isConstructor) auxConstructorBuf += stat - else defBuf += stat - } - case ValDef(_, _, _, rhs) => - // val defs with constant right-hand sides are eliminated. - // for all other val defs, an empty valdef goes into the template and - // the initializer goes as an assignment into the constructor - // if the val def is an early initialized or a parameter accessor, it goes - // before the superclass constructor call, otherwise it goes after. - // Lazy vals don't get the assignment in the constructor. - if (!stat.symbol.tpe.isInstanceOf[ConstantType]) { - if (rhs != EmptyTree && !stat.symbol.isLazy) { - val rhs1 = intoConstructor(stat.symbol, rhs) - (if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign( - stat.symbol, rhs1) + val applyMethodDef = DefDef( + sym = applyMethod, + vparamss = ListOfNil, + rhs = Block(applyMethodStat, gen.mkAttributedRef(BoxedUnit_UNIT))) + + ClassDef( + sym = closureClass, + constrMods = Modifiers(0), + vparamss = List(List(outerFieldDef)), + body = applyMethodDef :: Nil, + superPos = impl.pos) } - defBuf += deriveValDef(stat)(_ => EmptyTree) } - case ClassDef(_, _, _, _) => - // classes are treated recursively, and left in the template - defBuf += new ConstructorTransformer(unit).transform(stat) - case _ => - // all other statements go into the constructor - constrStatBuf += intoConstructor(impl.symbol, stat) - } - populateOmittables() + satelliteClass.asInstanceOf[ClassDef] + } - // Initialize all parameters fields that must be kept. - val paramInits = paramAccessors filter mustbeKept map { acc => - // Check for conflicting symbol amongst parents: see bug #1960. - // It would be better to mangle the constructor parameter name since - // it can only be used internally, but I think we need more robust name - // mangling before we introduce more of it. - val conflict = clazz.info.nonPrivateMember(acc.name) filter (s => s.isGetter && !s.isOuterField && s.enclClass.isTrait) - if (conflict ne NoSymbol) - unit.error(acc.pos, "parameter '%s' requires field but conflicts with %s".format(acc.name, conflict.fullLocationString)) + /* @see overview at `delayedEndpointDef()` of the translation scheme for DelayedInit */ + def delayedInitCall(closure: Tree) = localTyper.typedPos(impl.pos) { + gen.mkMethodCall(This(clazz), delayedInitMethod, Nil, List(New(closure.symbol.tpe, This(clazz)))) + } - copyParam(acc, parameter(acc)) - } + /* Return a pair consisting of (all statements up to and including superclass and trait constr calls, rest) */ + def splitAtSuper(stats: List[Tree]) = { + def isConstr(tree: Tree) = (tree.symbol ne null) && tree.symbol.isConstructor + val (pre, rest0) = stats span (!isConstr(_)) + val (supercalls, rest) = rest0 span (isConstr(_)) + (pre ::: supercalls, rest) + } - /* Return a pair consisting of (all statements up to and including superclass and trait constr calls, rest) */ - def splitAtSuper(stats: List[Tree]) = { - def isConstr(tree: Tree) = (tree.symbol ne null) && tree.symbol.isConstructor - val (pre, rest0) = stats span (!isConstr(_)) - val (supercalls, rest) = rest0 span (isConstr(_)) - (pre ::: supercalls, rest) - } + val (uptoSuperStats, remainingConstrStats0) = splitAtSuper(constrStatBuf.toList) + var remainingConstrStats = remainingConstrStats0 - val (uptoSuperStats, remainingConstrStats0) = splitAtSuper(constrStatBuf.toList) - var remainingConstrStats = remainingConstrStats0 + /* XXX This is not corect: remainingConstrStats.nonEmpty excludes too much, + * but excluding it includes too much. The constructor sequence being mimicked + * needs to be reproduced with total fidelity. + * + * See test case files/run/bug4680.scala, the output of which is wrong in many + * particulars. + */ + val needsDelayedInit = (isDelayedInitSubclass && remainingConstrStats.nonEmpty) - rewriteDelayedInit() + if (needsDelayedInit) { + val delayedHook: DefDef = delayedEndpointDef(remainingConstrStats) + defBuf += delayedHook + val hookCallerClass = { + // transform to make the closure-class' default constructor assign the the outer instance to its param-accessor field. + val drillDown = new ConstructorTransformer(unit) + drillDown transform delayedInitClosure(delayedHook.symbol.asInstanceOf[MethodSymbol]) + } + defBuf += hookCallerClass + remainingConstrStats = delayedInitCall(hookCallerClass) :: Nil + } - // Assemble final constructor - defBuf += deriveDefDef(constr)(_ => - treeCopy.Block( - constrBody, - paramInits ::: constrPrefixBuf.toList ::: uptoSuperStats ::: - guardSpecializedInitializer(remainingConstrStats), - constrBody.expr)) + // Assemble final constructor + defBuf += deriveDefDef(constr)(_ => + treeCopy.Block( + constrBody, + paramInits ::: constrPrefixBuf.toList ::: uptoSuperStats ::: + guardSpecializedInitializer(remainingConstrStats), + constrBody.expr)) - // Followed by any auxiliary constructors - defBuf ++= auxConstructorBuf + // Followed by any auxiliary constructors + defBuf ++= auxConstructorBuf - // Unlink all fields that can be dropped from class scope - for (sym <- clazz.info.decls ; if !mustbeKept(sym)) - clazz.info.decls unlink sym + // Unlink all fields that can be dropped from class scope + for (sym <- clazz.info.decls ; if !mustbeKept(sym)) + clazz.info.decls unlink sym - // Eliminate all field definitions that can be dropped from template - val transformed: Template = deriveTemplate(impl)(_ => defBuf.toList filter (stat => mustbeKept(stat.symbol))) + // Eliminate all field definitions that can be dropped from template + deriveTemplate(impl)(_ => defBuf.toList filter (stat => mustbeKept(stat.symbol))) + } // transformClassTemplate - } // TemplateTransformer + override def transform(tree: Tree): Tree = { + tree match { + case ClassDef(_,_,_,_) if !tree.symbol.isInterface && !isPrimitiveValueClass(tree.symbol) => + deriveClassDef(tree)(transformClassTemplate) + case _ => + super.transform(tree) + } + } + } // ConstructorTransformer } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index c9a7cc2e3f..0f65b11e9b 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -342,11 +342,6 @@ abstract class Erasure extends AddInterfaces } } - // Each primitive value class has its own getClass for ultra-precise class object typing. - private lazy val primitiveGetClassMethods = Set[Symbol](Any_getClass, AnyVal_getClass) ++ ( - ScalaValueClasses map (_.tpe member nme.getClass_) - ) - // ## requires a little translation private lazy val poundPoundMethods = Set[Symbol](Any_##, Object_##) // Methods on Any/Object which we rewrite here while we still know what diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 5622f67459..b53efafdd4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -931,22 +931,11 @@ trait Implicits { if (DivergentImplicitRecovery.sym != null) { DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym)(context) } - else if (invalidImplicits.nonEmpty) { - val sym = invalidImplicits.head - // We don't even dare look if errors are being buffered - // !sym.hasFlag(LOCKED) is a hail mary between SI-2206 and SI-7486 - def isSensibleAddendum = !sym.hasFlag(LOCKED) && (pt match { - case Function1(_, out) => out <:< sym.tpe.finalResultType - case _ => pt <:< sym.tpe.finalResultType - }) - // Don't pitch in with this theory unless it looks plausible that the - // implicit would have helped + + if (invalidImplicits.nonEmpty) setAddendum(pos, () => - if (isSensibleAddendum) - s"\n Note: implicit $sym is not applicable here because it comes after the application point and it lacks an explicit result type" - else "" + s"\n Note: implicit ${invalidImplicits.head} is not applicable here because it comes after the application point and it lacks an explicit result type" ) - } } best diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 03ad127498..8e9933f734 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -282,11 +282,13 @@ trait NamesDefaults { self: Analyzer => case WildcardStarArg(expr) => expr.tpe case _ => seqType(arg.tpe) } - else - // Note stabilizing can lead to a non-conformant argument when existentials are involved, e.g. neg/t3507-old.scala, hence the filter. - gen.stableTypeFor(arg).filter(_ <:< paramTpe).getOrElse(arg.tpe) - // We have to deconst or types inferred from literal arguments will be Constant(_), e.g. pos/z1730.scala. - ).deconst + else { + // TODO In 83c9c764b, we tried to a stable type here to fix SI-7234. But the resulting TypeTree over a + // singleton type without an original TypeTree fails to retypecheck after a resetLocalAttrs (SI-7516), + // which is important for (at least) macros. + arg.tpe + } + ).widen // have to widen or types inferred from literal defaults will be singletons val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos, newFlags = ARTIFACT) setInfo ( if (byName) functionType(Nil, argTpe) else argTpe ) @@ -328,9 +330,12 @@ trait NamesDefaults { self: Analyzer => // type the application without names; put the arguments in definition-site order val typedApp = doTypedApply(tree, funOnly, reorderArgs(namelessArgs, argPos), mode, pt) typedApp match { - // Extract the typed arguments, restore the call-site evaluation order (using - // ValDef's in the block), change the arguments to these local values. - case Apply(expr, typedArgs) if !(typedApp :: typedArgs).exists(_.isErrorTyped) => // bail out with erroneous args, see SI-7238 + case Apply(expr, typedArgs) if (typedApp :: typedArgs).exists(_.isErrorTyped) => + setError(tree) // bail out with and erroneous Apply *or* erroneous arguments, see SI-7238, SI-7509 + case Apply(expr, typedArgs) => + // Extract the typed arguments, restore the call-site evaluation order (using + // ValDef's in the block), change the arguments to these local values. + // typedArgs: definition-site order val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, removeByName = false, removeRepeated = false) // valDefs: call-site order diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index c9aa27cf49..ec6b78f2de 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -602,10 +602,8 @@ trait Typers extends Adaptations with Tags { // type of the qualifier is inaccessible, we can cause private types to // escape scope here, e.g. pos/t1107. I'm not sure how to properly handle // this so for now it requires the type symbol be public. - def isGetClassCall = ( - (sym == Any_getClass || sym == Object_getClass) - && pre.typeSymbol.isPublic - ) + def isGetClassCall = isGetClass(sym) && pre.typeSymbol.isPublic + def narrowIf(tree: Tree, condition: Boolean) = if (condition) tree setType singleType(pre, sym) else tree diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 7af70400ef..ffaea8de96 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -8,11 +8,14 @@ package scala.concurrent.impl -import scala.concurrent.{ ExecutionContext, CanAwait, OnCompleteRunnable, TimeoutException, ExecutionException } +import scala.concurrent.{ ExecutionContext, CanAwait, OnCompleteRunnable, TimeoutException, ExecutionException, blocking } +import scala.concurrent.Future.InternalCallbackExecutor import scala.concurrent.duration.{ Duration, Deadline, FiniteDuration, NANOSECONDS } import scala.annotation.tailrec import scala.util.control.NonFatal import scala.util.{ Try, Success, Failure } +import java.io.ObjectInputStream +import java.util.concurrent.locks.AbstractQueuedSynchronizer private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] { def future: this.type = this @@ -53,69 +56,68 @@ private[concurrent] object Promise { case t => Failure(t) } + /* + * Inspired by: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/locks/AbstractQueuedSynchronizer.java + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + private final class CompletionLatch[T] extends AbstractQueuedSynchronizer with (Try[T] => Unit) { + override protected def tryAcquireShared(ignored: Int): Int = if (getState != 0) 1 else -1 + override protected def tryReleaseShared(ignore: Int): Boolean = { + setState(1) + true + } + override def apply(ignored: Try[T]): Unit = releaseShared(1) + } + + /** Default promise implementation. */ class DefaultPromise[T] extends AbstractPromise with Promise[T] { self => updateState(null, Nil) // Start at "No callbacks" - protected final def tryAwait(atMost: Duration): Boolean = { - @tailrec - def awaitUnsafe(deadline: Deadline, nextWait: FiniteDuration): Boolean = { - if (!isCompleted && nextWait > Duration.Zero) { - val ms = nextWait.toMillis - val ns = (nextWait.toNanos % 1000000l).toInt // as per object.wait spec - - synchronized { if (!isCompleted) wait(ms, ns) } - - awaitUnsafe(deadline, deadline.timeLeft) - } else - isCompleted - } - @tailrec - def awaitUnbounded(): Boolean = { - if (isCompleted) true - else { - synchronized { if (!isCompleted) wait() } - awaitUnbounded() - } - } - + protected final def tryAwait(atMost: Duration): Boolean = if (!isCompleted) { import Duration.Undefined + import scala.concurrent.Future.InternalCallbackExecutor atMost match { - case u if u eq Undefined => throw new IllegalArgumentException("cannot wait for Undefined period") - case Duration.Inf => awaitUnbounded() - case Duration.MinusInf => isCompleted - case f: FiniteDuration => if (f > Duration.Zero) awaitUnsafe(f.fromNow, f) else isCompleted + case e if e eq Undefined => throw new IllegalArgumentException("cannot wait for Undefined period") + case Duration.Inf => + val l = new CompletionLatch[T]() + onComplete(l)(InternalCallbackExecutor) + l.acquireSharedInterruptibly(1) + case Duration.MinusInf => // Drop out + case f: FiniteDuration => + if (f > Duration.Zero) { + val l = new CompletionLatch[T]() + onComplete(l)(InternalCallbackExecutor) + l.tryAcquireSharedNanos(1, f.toNanos) + } } - } + + isCompleted + } else true // Already completed @throws(classOf[TimeoutException]) @throws(classOf[InterruptedException]) def ready(atMost: Duration)(implicit permit: CanAwait): this.type = - if (isCompleted || tryAwait(atMost)) this + if (tryAwait(atMost)) this else throw new TimeoutException("Futures timed out after [" + atMost + "]") @throws(classOf[Exception]) def result(atMost: Duration)(implicit permit: CanAwait): T = - ready(atMost).value.get match { - case Failure(e) => throw e - case Success(r) => r - } + ready(atMost).value.get.get // ready throws TimeoutException if timeout so value.get is safe here def value: Option[Try[T]] = getState match { case c: Try[_] => Some(c.asInstanceOf[Try[T]]) case _ => None } - override def isCompleted: Boolean = getState match { // Cheaper than boxing result into Option due to "def value" - case _: Try[_] => true - case _ => false - } + override def isCompleted: Boolean = getState.isInstanceOf[Try[_]] def tryComplete(value: Try[T]): Boolean = { val resolved = resolveTry(value) - (try { - @tailrec + @tailrec def tryComplete(v: Try[T]): List[CallbackRunnable[T]] = { getState match { case raw: List[_] => @@ -124,10 +126,7 @@ private[concurrent] object Promise { case _ => null } } - tryComplete(resolved) - } finally { - synchronized { notifyAll() } //Notify any evil blockers - }) match { + tryComplete(resolved) match { case null => false case rs if rs.isEmpty => true case rs => rs.foreach(r => r.executeWithValue(resolved)); true diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index 73469a955d..2240dde360 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -122,8 +122,7 @@ private[scala] trait PropertiesTrait { */ def lineSeparator = propOrElse("line.separator", "\n") - /** Various well-known properties. - */ + /* Various well-known properties. */ def javaClassPath = propOrEmpty("java.class.path") def javaHome = propOrEmpty("java.home") def javaVendor = propOrEmpty("java.vendor") @@ -139,10 +138,13 @@ private[scala] trait PropertiesTrait { def userHome = propOrEmpty("user.home") def userName = propOrEmpty("user.name") - /** Some derived values. - */ + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ def isWin = osName startsWith "Windows" - def isMac = javaVendor startsWith "Apple" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" // This is looking for javac, tools.jar, etc. // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 1b46d9e00b..851fc98a32 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -111,8 +111,10 @@ trait Definitions extends api.StandardDefinitions { /** Is symbol a numeric value class? */ def isNumericValueClass(sym: Symbol) = ScalaNumericValueClasses contains sym - def isGetClass(sym: Symbol) = - (sym.name == nme.getClass_) && flattensToEmpty(sym.paramss) + def isGetClass(sym: Symbol) = ( + sym.name == nme.getClass_ // this condition is for performance only, this is called from `Typer#stabliize`. + && getClassMethods(sym) + ) lazy val UnitClass = valueClassSymbol(tpnme.Unit) lazy val ByteClass = valueClassSymbol(tpnme.Byte) @@ -777,6 +779,12 @@ trait Definitions extends api.StandardDefinitions { lazy val Any_isInstanceOf = newT1NullaryMethod(AnyClass, nme.isInstanceOf_, FINAL)(_ => BooleanTpe) lazy val Any_asInstanceOf = newT1NullaryMethod(AnyClass, nme.asInstanceOf_, FINAL)(_.typeConstructor) + lazy val primitiveGetClassMethods = Set[Symbol](Any_getClass, AnyVal_getClass) ++ ( + ScalaValueClasses map (_.tpe member nme.getClass_) + ) + + lazy val getClassMethods: Set[Symbol] = primitiveGetClassMethods + Object_getClass + // A type function from T => Class[U], used to determine the return // type of getClass calls. The returned type is: // diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 00a929003e..3725f570a8 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1066,6 +1066,14 @@ trait Types continue = false val bcs0 = baseClasses var bcs = bcs0 + // omit PRIVATE LOCALS unless selector class is contained in class owning the def. + def admitPrivateLocal(owner: Symbol): Boolean = { + val selectorClass = this match { + case tt: ThisType => tt.sym // SI-7507 the first base class is not necessarily the selector class. + case _ => bcs0.head + } + selectorClass.hasTransOwner(owner) + } while (!bcs.isEmpty) { val decls = bcs.head.info.decls var entry = decls.lookupEntry(name) @@ -1075,10 +1083,10 @@ trait Types if ((flags & required) == required) { val excl = flags & excluded if (excl == 0L && - (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. + ( (bcs eq bcs0) || (flags & PrivateLocal) != PrivateLocal || - (bcs0.head.hasTransOwner(bcs.head)))) { + admitPrivateLocal(bcs.head))) { if (name.isTypeName || (stableOnly && sym.isStable && !sym.hasVolatileType)) { if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) return sym @@ -3118,10 +3126,9 @@ trait Types sameLength(typeArgs, tp.typeArgs) && { val lhs = if (isLowerBound) tp.typeArgs else typeArgs val rhs = if (isLowerBound) typeArgs else tp.typeArgs - // this is a higher-kinded type var with same arity as tp. - // side effect: adds the type constructor itself as a bound - addBound(tp.typeConstructor) - isSubArgs(lhs, rhs, params, AnyDepth) + // This is a higher-kinded type var with same arity as tp. + // If so (see SI-7517), side effect: adds the type constructor itself as a bound. + isSubArgs(lhs, rhs, params, AnyDepth) && { addBound(tp.typeConstructor); true } } } // The type with which we can successfully unify can be hidden |