summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-06-03 22:15:32 -0700
committerPaul Phillips <paulp@improving.org>2013-06-04 08:12:05 -0700
commit767a25fe2178b00f494d55bdaae53535cd7fb57d (patch)
tree1bae0553a051f6a8805391db9e75858fd297f3b6
parentfb06073844f388ec4c007b4c08e7d508fc88d5ab (diff)
parente317d0bcc9fa8f2a2e6d93c4ad5edf5326f5a617 (diff)
downloadscala-767a25fe2178b00f494d55bdaae53535cd7fb57d.tar.gz
scala-767a25fe2178b00f494d55bdaae53535cd7fb57d.tar.bz2
scala-767a25fe2178b00f494d55bdaae53535cd7fb57d.zip
Merge branch 'pr/merge-2.10.2' into master
* pr/merge-2.10.2: SI-7375 ClassTag for value class aliases SI-7507 Fix lookup of private[this] member in presence of self type. SI-7532 Fix regression in Java inner classfile reader SI-7517 Fix higher kinded type inference regression SI-7516 Revert "SI-7234 Make named args play nice w. depmet types" A test case for a recent LUB progression. SI-7421 remove unneeded extra-attachement in maven deploy SI-7486 Regressions in implicit search. SI-7509 Avoid crasher as erronous args flow through NamesDefaults SI-6138 Centralize and refine detection of `getClass` calls SI-7497 Fix scala.util.Properties.isMac SI-7473 Bad for expr crashes postfix Increase build.number to 2.10.3 SI-7391 Always use ForkJoin in Scala actors on ... ... Java 6 and above (except when the porperty actors.enableForkJoin says otherwise) Reimplementing much of the DefaultPromise methods Optimizations: 1) Avoiding to call 'synchronized' in tryComplete and in tryAwait 2) Implementing blocking by using an optimized latch so no blocking ops for non-blockers 3) Reducing method size of isCompleted to be cheaper to inline 4) 'result' to use Try.get instead of patmat c.typeCheck(silent = true) now suppresses ambiguous errors Conflicts: bincompat-backward.whitelist.conf bincompat-forward.whitelist.conf src/compiler/scala/reflect/macros/contexts/Typers.scala src/compiler/scala/reflect/reify/package.scala src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala src/compiler/scala/tools/nsc/typechecker/Typers.scala src/compiler/scala/tools/reflect/ToolBoxFactory.scala src/library/scala/concurrent/impl/Promise.scala src/reflect/scala/reflect/internal/Types.scala
-rw-r--r--src/actors/scala/actors/scheduler/ThreadPoolConfig.scala5
-rw-r--r--src/compiler/scala/reflect/reify/package.scala5
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala9
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala6
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala1060
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala17
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala21
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala6
-rw-r--r--src/library/scala/concurrent/impl/Promise.scala85
-rw-r--r--src/library/scala/util/Properties.scala12
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala12
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala19
-rw-r--r--test/files/neg/t6138.check7
-rw-r--r--test/files/neg/t6138.scala5
-rw-r--r--test/files/neg/t7473.check7
-rw-r--r--test/files/neg/t7473.scala7
-rw-r--r--test/files/neg/t7507.check4
-rw-r--r--test/files/neg/t7507.scala7
-rw-r--r--test/files/neg/t7509.check12
-rw-r--r--test/files/neg/t7509.scala4
-rw-r--r--test/files/pos/lub-dealias-widen.scala34
-rw-r--r--test/files/pos/t7516/A_1.scala9
-rw-r--r--test/files/pos/t7516/B_2.scala4
-rw-r--r--test/files/pos/t7517.scala22
-rw-r--r--test/files/pos/t7532/A_1.java6
-rw-r--r--test/files/pos/t7532/B_2.scala5
-rw-r--r--test/files/pos/t7532b/A_1.scala7
-rw-r--r--test/files/pos/t7532b/B_2.scala8
-rw-r--r--test/files/run/t7375a.check4
-rw-r--r--test/files/run/t7375a.scala16
-rw-r--r--test/files/run/t7375b.check4
-rw-r--r--test/files/run/t7375b/Macros_1.scala18
-rw-r--r--test/files/run/t7375b/Test_2.scala3
-rw-r--r--test/files/run/t7507.scala31
-rw-r--r--test/pending/neg/t7441.check (renamed from test/files/neg/t7441.check)0
-rw-r--r--test/pending/neg/t7441.scala (renamed from test/files/neg/t7441.scala)0
-rw-r--r--test/pending/pos/t7234.scala (renamed from test/files/pos/t7234.scala)0
-rw-r--r--test/pending/pos/t7234b.scala (renamed from test/files/pos/t7234b.scala)0
-rw-r--r--test/pending/pos/t7486.scala (renamed from test/files/pos/t7486.scala)0
41 files changed, 799 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
diff --git a/test/files/neg/t6138.check b/test/files/neg/t6138.check
new file mode 100644
index 0000000000..8fd9978248
--- /dev/null
+++ b/test/files/neg/t6138.check
@@ -0,0 +1,7 @@
+t6138.scala:4: error: ambiguous reference to overloaded definition,
+both method getClass in object definitions of type (s: Int)Any
+and method getClass in object definitions of type (s: String)Any
+match argument types (Nothing)
+ getClass(???): String
+ ^
+one error found
diff --git a/test/files/neg/t6138.scala b/test/files/neg/t6138.scala
new file mode 100644
index 0000000000..2f45a46b1c
--- /dev/null
+++ b/test/files/neg/t6138.scala
@@ -0,0 +1,5 @@
+object definitions {
+ def getClass(s: String): Any = ???
+ def getClass(s: Int): Any = ???
+ getClass(???): String
+}
diff --git a/test/files/neg/t7473.check b/test/files/neg/t7473.check
new file mode 100644
index 0000000000..bc8c29d463
--- /dev/null
+++ b/test/files/neg/t7473.check
@@ -0,0 +1,7 @@
+t7473.scala:6: error: '<-' expected but '=' found.
+ (for (x = Option(i); if x == j) yield 42) toList
+ ^
+t7473.scala:6: error: illegal start of simple expression
+ (for (x = Option(i); if x == j) yield 42) toList
+ ^
+two errors found
diff --git a/test/files/neg/t7473.scala b/test/files/neg/t7473.scala
new file mode 100644
index 0000000000..593231d5f2
--- /dev/null
+++ b/test/files/neg/t7473.scala
@@ -0,0 +1,7 @@
+
+object Foo {
+ val i,j = 3
+ //for (x = Option(i); if x == j) yield 42 //t7473.scala:4: error: '<-' expected but '=' found.
+ // evil postfix!
+ (for (x = Option(i); if x == j) yield 42) toList
+}
diff --git a/test/files/neg/t7507.check b/test/files/neg/t7507.check
new file mode 100644
index 0000000000..d402869fd4
--- /dev/null
+++ b/test/files/neg/t7507.check
@@ -0,0 +1,4 @@
+t7507.scala:6: error: value bippy in trait Cake cannot be accessed in Cake
+ locally(bippy)
+ ^
+one error found
diff --git a/test/files/neg/t7507.scala b/test/files/neg/t7507.scala
new file mode 100644
index 0000000000..1b4756d955
--- /dev/null
+++ b/test/files/neg/t7507.scala
@@ -0,0 +1,7 @@
+trait Cake extends Slice {
+ private[this] val bippy = ()
+}
+
+trait Slice { self: Cake =>
+ locally(bippy)
+}
diff --git a/test/files/neg/t7509.check b/test/files/neg/t7509.check
new file mode 100644
index 0000000000..eaa6303cf5
--- /dev/null
+++ b/test/files/neg/t7509.check
@@ -0,0 +1,12 @@
+t7509.scala:3: error: inferred type arguments [Int] do not conform to method crash's type parameter bounds [R <: AnyRef]
+ crash(42)
+ ^
+t7509.scala:3: error: type mismatch;
+ found : Int(42)
+ required: R
+ crash(42)
+ ^
+t7509.scala:3: error: could not find implicit value for parameter ev: R
+ crash(42)
+ ^
+three errors found
diff --git a/test/files/neg/t7509.scala b/test/files/neg/t7509.scala
new file mode 100644
index 0000000000..3cba801ea7
--- /dev/null
+++ b/test/files/neg/t7509.scala
@@ -0,0 +1,4 @@
+object NMWE {
+ def crash[R <: AnyRef](f: R)(implicit ev: R): Any = ???
+ crash(42)
+}
diff --git a/test/files/pos/lub-dealias-widen.scala b/test/files/pos/lub-dealias-widen.scala
new file mode 100644
index 0000000000..38854fbc5c
--- /dev/null
+++ b/test/files/pos/lub-dealias-widen.scala
@@ -0,0 +1,34 @@
+import scala.language.higherKinds
+
+sealed trait Path {
+ type EncodeFunc
+ type Route[R] = List[String] => R
+
+ def >>(f: Route[Int]): Sitelet[EncodeFunc] = ???
+}
+
+case object PAny extends Path {
+ type EncodeFunc = List[String] => String
+}
+
+case class PLit[Next <: Path]() extends Path {
+ type EncodeFunc = Next#EncodeFunc
+}
+
+trait Sitelet[EncodeFunc] { self =>
+ def &[G <: H, H >: EncodeFunc](that: Sitelet[G]): Sitelet[H] = ???
+}
+
+object Test {
+ val r: Sitelet[Int => (Int => String)] = ???
+
+ val p2: PLit[PAny.type] = ???
+ val r2 /*: Sitelet[List[String] => String] */ // annotate type and it compiles with 2.10.0
+ = p2 >> { (xs: List[String]) => 0 }
+
+ // This works after https://github.com/scala/scala/commit/a06d31f6a
+ // Before: error: inferred type arguments [List[String] => String,List[String] => String]
+ // do not conform to method &'s type parameter bounds
+ // [G <: H,H >: Int => (Int => String)]
+ val s = r & r2
+} \ No newline at end of file
diff --git a/test/files/pos/t7516/A_1.scala b/test/files/pos/t7516/A_1.scala
new file mode 100644
index 0000000000..3bba19966d
--- /dev/null
+++ b/test/files/pos/t7516/A_1.scala
@@ -0,0 +1,9 @@
+import scala.reflect._,macros._, scala.language.experimental.macros
+
+object A {
+ def impl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[List[T]] = {
+ val r = c.universe.reify { List(t.splice) }
+ c.Expr[List[T]]( c.resetLocalAttrs(r.tree) )
+ }
+ def demo[T](t: T): List[T] = macro impl[T]
+}
diff --git a/test/files/pos/t7516/B_2.scala b/test/files/pos/t7516/B_2.scala
new file mode 100644
index 0000000000..1b8531bc85
--- /dev/null
+++ b/test/files/pos/t7516/B_2.scala
@@ -0,0 +1,4 @@
+object B {
+ final case class CV(p: Int = 3, g: Int = 2)
+ A.demo { val d = 4; CV(g = d); "a" }
+}
diff --git a/test/files/pos/t7517.scala b/test/files/pos/t7517.scala
new file mode 100644
index 0000000000..7ce4c6b13e
--- /dev/null
+++ b/test/files/pos/t7517.scala
@@ -0,0 +1,22 @@
+trait Box[ K[A[x]] ]
+
+object Box {
+ // type constructor composition
+ sealed trait ∙[A[_], B[_]] { type l[T] = A[B[T]] }
+
+ // composes type constructors inside K
+ type SplitBox[K[A[x]], B[x]] = Box[ ({ type l[A[x]] = K[ (A ∙ B)#l] })#l ]
+
+ def split[ K[A[x]], B[x] ](base: Box[K]): SplitBox[K,B] = ???
+
+ class Composed[B[_], L[A[x]] ] {
+ val box: Box[L] = ???
+
+ type Split[ A[x] ] = L[ (A ∙ B)#l ]
+ val a: Box[Split] = Box.split(box)
+
+ //Either of these work:
+ val a1: Box[Split] = Box.split[L,B](box)
+ val a2: Box[ ({ type l[A[x]] = L[ (A ∙ B)#l ] })#l ] = Box.split(box)
+ }
+} \ No newline at end of file
diff --git a/test/files/pos/t7532/A_1.java b/test/files/pos/t7532/A_1.java
new file mode 100644
index 0000000000..1ade76cc70
--- /dev/null
+++ b/test/files/pos/t7532/A_1.java
@@ -0,0 +1,6 @@
+class R {
+ public class attr { // Will have the bytecode name `R$attr`, not to be confused with `R@tr`!
+ }
+ public static class attr1 {
+ }
+}
diff --git a/test/files/pos/t7532/B_2.scala b/test/files/pos/t7532/B_2.scala
new file mode 100644
index 0000000000..ee7ce7751f
--- /dev/null
+++ b/test/files/pos/t7532/B_2.scala
@@ -0,0 +1,5 @@
+object Test {
+ val r = new R
+ new r.attr() // Was: error while loading attr, class file '.../t7532-pos.obj/R$attr.class' has location not matching its contents: contains class
+ new R.attr1
+} \ No newline at end of file
diff --git a/test/files/pos/t7532b/A_1.scala b/test/files/pos/t7532b/A_1.scala
new file mode 100644
index 0000000000..e8f9540609
--- /dev/null
+++ b/test/files/pos/t7532b/A_1.scala
@@ -0,0 +1,7 @@
+package pack
+class R {
+ class attr // Will have the bytecode name `R$attr`, not to be confused with `R@tr`!
+ class `@`
+}
+
+class `@` \ No newline at end of file
diff --git a/test/files/pos/t7532b/B_2.scala b/test/files/pos/t7532b/B_2.scala
new file mode 100644
index 0000000000..1555a5daa7
--- /dev/null
+++ b/test/files/pos/t7532b/B_2.scala
@@ -0,0 +1,8 @@
+import pack._
+
+object Test {
+ val r = new R
+ new r.attr()
+ new r.`@`
+ new `@`
+} \ No newline at end of file
diff --git a/test/files/run/t7375a.check b/test/files/run/t7375a.check
new file mode 100644
index 0000000000..a0a15dfb2f
--- /dev/null
+++ b/test/files/run/t7375a.check
@@ -0,0 +1,4 @@
+C1
+C2
+C1
+C2
diff --git a/test/files/run/t7375a.scala b/test/files/run/t7375a.scala
new file mode 100644
index 0000000000..e46ad08f63
--- /dev/null
+++ b/test/files/run/t7375a.scala
@@ -0,0 +1,16 @@
+import scala.reflect.ClassTag
+
+class C1(val n: Int) extends AnyVal
+class C2(val n: Int) extends AnyRef
+
+object Test {
+ type F1 = C1
+ type F2 = C2
+
+ def main(args: Array[String]): Unit = {
+ println(implicitly[ClassTag[C1]])
+ println(implicitly[ClassTag[C2]])
+ println(implicitly[ClassTag[F1]])
+ println(implicitly[ClassTag[F2]])
+ }
+}
diff --git a/test/files/run/t7375b.check b/test/files/run/t7375b.check
new file mode 100644
index 0000000000..d7578e28ba
--- /dev/null
+++ b/test/files/run/t7375b.check
@@ -0,0 +1,4 @@
+Predef.this.classOf[C1]
+Predef.this.classOf[C2]
+Predef.this.classOf[C1]
+Predef.this.classOf[C2]
diff --git a/test/files/run/t7375b/Macros_1.scala b/test/files/run/t7375b/Macros_1.scala
new file mode 100644
index 0000000000..70e79cc2b4
--- /dev/null
+++ b/test/files/run/t7375b/Macros_1.scala
@@ -0,0 +1,18 @@
+import language.experimental.macros
+import scala.reflect.macros.Context
+
+class C1(val n: Int) extends AnyVal
+class C2(val n: Int) extends AnyRef
+
+object Macros {
+ type F1 = C1
+ type F2 = C2
+
+ def foo = macro impl
+ def impl(c: Context) = {
+ import c.universe._
+ def test[T: c.TypeTag] = reify(println(c.literal(c.reifyRuntimeClass(c.typeOf[T]).toString).splice)).tree
+ def tests = Block(List(test[C1], test[C2], test[F1], test[F2]), Literal(Constant(())))
+ c.Expr[Unit](tests)
+ }
+} \ No newline at end of file
diff --git a/test/files/run/t7375b/Test_2.scala b/test/files/run/t7375b/Test_2.scala
new file mode 100644
index 0000000000..acfddae942
--- /dev/null
+++ b/test/files/run/t7375b/Test_2.scala
@@ -0,0 +1,3 @@
+object Test extends App {
+ Macros.foo
+} \ No newline at end of file
diff --git a/test/files/run/t7507.scala b/test/files/run/t7507.scala
new file mode 100644
index 0000000000..6c1959ddac
--- /dev/null
+++ b/test/files/run/t7507.scala
@@ -0,0 +1,31 @@
+trait Cake extends Slice
+
+// Minimization
+trait Slice { self: Cake => // must have self type that extends `Slice`
+ private[this] val bippy = () // must be private[this]
+ locally(bippy)
+}
+
+// Originally reported bug:
+trait Cake1 extends Slice1
+trait Slice1 { self: Cake1 =>
+ import java.lang.String // any import will do!
+ val Tuple2(x, y) = ((1, 2))
+}
+
+
+// Nesting
+trait Cake3 extends Outer.Slice3
+
+// Minimization
+object Outer {
+ private[this] val bippy = ()
+ trait Slice3 { self: Cake3 =>
+ locally(bippy)
+ }
+}
+
+object Test extends App {
+ val s1 = new Cake1 {}
+ assert((s1.x, s1.y) == (1, 2), (s1.x, s1.y))
+}
diff --git a/test/files/neg/t7441.check b/test/pending/neg/t7441.check
index f259457197..f259457197 100644
--- a/test/files/neg/t7441.check
+++ b/test/pending/neg/t7441.check
diff --git a/test/files/neg/t7441.scala b/test/pending/neg/t7441.scala
index dad7421e3f..dad7421e3f 100644
--- a/test/files/neg/t7441.scala
+++ b/test/pending/neg/t7441.scala
diff --git a/test/files/pos/t7234.scala b/test/pending/pos/t7234.scala
index 59a233d835..59a233d835 100644
--- a/test/files/pos/t7234.scala
+++ b/test/pending/pos/t7234.scala
diff --git a/test/files/pos/t7234b.scala b/test/pending/pos/t7234b.scala
index fee98e87a8..fee98e87a8 100644
--- a/test/files/pos/t7234b.scala
+++ b/test/pending/pos/t7234b.scala
diff --git a/test/files/pos/t7486.scala b/test/pending/pos/t7486.scala
index 6dd7f4c4ac..6dd7f4c4ac 100644
--- a/test/files/pos/t7486.scala
+++ b/test/pending/pos/t7486.scala