diff options
Diffstat (limited to 'src')
44 files changed, 2053 insertions, 1507 deletions
diff --git a/src/build/maven/maven-deploy.xml b/src/build/maven/maven-deploy.xml index 946b712b6c..fbd6d4cd51 100644 --- a/src/build/maven/maven-deploy.xml +++ b/src/build/maven/maven-deploy.xml @@ -74,12 +74,21 @@ <artifact:attach type="jar" file="${path}-docs.jar" classifier="javadoc" /> </artifact:deploy> </then><else> - <artifact:install file="${path}.jar"> - <artifact:localRepository path="${local.repository}" id="${repository.credentials.id}" /> - <artifact:pom refid="@{name}.pom" /> - <artifact:attach type="jar" file="${path}-src.jar" classifier="sources" /> - <artifact:attach type="jar" file="${path}-docs.jar" classifier="javadoc" /> - </artifact:install> + <if><isset property="docs.skip"/><then> + <artifact:install file="${path}.jar"> + <artifact:localRepository path="${local.repository}" id="${repository.credentials.id}" /> + <artifact:pom refid="@{name}.pom" /> + <artifact:attach type="jar" file="${path}-src.jar" classifier="sources" /> + </artifact:install> + </then> + <else> + <artifact:install file="${path}.jar"> + <artifact:localRepository path="${local.repository}" id="${repository.credentials.id}" /> + <artifact:pom refid="@{name}.pom" /> + <artifact:attach type="jar" file="${path}-src.jar" classifier="sources" /> + </artifact:install> + </else> + </if> </else></if> </then><else> <local name="repo"/> diff --git a/src/build/pack.xml b/src/build/pack.xml index ed628726fb..4c5ba228fc 100644 --- a/src/build/pack.xml +++ b/src/build/pack.xml @@ -56,7 +56,7 @@ MAIN DISTRIBUTION PACKAGING <checksum file="${dists.dir}/archives/${dist.name}.zip" fileext=".md5"/> </target> - <target name="pack-devel-docs.tar" depends="pack-archives.zip"> + <target name="pack-devel-docs.tar" depends="pack-archives.zip" unless="docs.skip"> <tar destfile="${dists.dir}/archives/${dist.name}-devel-docs.tar" compression="none" longfile="gnu"> <tarfileset dir="${dist.dir}/doc/scala-devel-docs" prefix="${dist.name}-devel-docs"/> @@ -119,11 +119,11 @@ MAIN DISTRIBUTION PACKAGING `resource` is relative to directory of `link` --> <symlink link="${dists.dir}/archives/scala-latest-sources.tgz" resource="scala-${version.number}-sources.tgz" - overwrite="yes"/> + overwrite="true"/> </target> <target name="pack-archives.latest.win" depends="pack-archives.src" if="os.win"> - <copy tofile="${dists.dir}/archives/scala-latest-sources.tgz"> + <copy tofile="${dists.dir}/archives/scala-latest-sources.tgz" overwrite="true"> <fileset dir="${dists.dir}/archives"> <include name="scala-${version.number}-sources.tgz"/> </fileset> @@ -141,7 +141,7 @@ MAIN DISTRIBUTION PACKAGING <attribute name="mvn.artifact.name"/> <sequential> <mkdir dir="${dists.dir}/maven/${version.number}/@{mvn.artifact.name}"/> - <copy todir="${dists.dir}/maven/${version.number}/@{mvn.artifact.name}"> + <copy verbose="true" overwrite="true" todir="${dists.dir}/maven/${version.number}/@{mvn.artifact.name}"> <fileset dir="${dist.dir}/lib/"> <filename name="@{mvn.artifact.name}.jar"/> </fileset> @@ -167,7 +167,7 @@ MAIN DISTRIBUTION PACKAGING <attribute name="mvn.artifact.name"/> <sequential> <mkdir dir="${dists.dir}/maven/${version.number}/plugins/@{mvn.artifact.name}"/> - <copy todir="${dists.dir}/maven/${version.number}/plugins/@{mvn.artifact.name}"> + <copy todir="${dists.dir}/maven/${version.number}/plugins/@{mvn.artifact.name}" overwrite="true"> <fileset dir="${dist.dir}/misc/scala-devel/plugins/"> <filename name="@{mvn.artifact.name}.jar"/> </fileset> @@ -189,7 +189,7 @@ MAIN DISTRIBUTION PACKAGING </jar> </target> - <target name="pack-maven.docs" depends="pack-maven.libs, pack-maven.plugins"> + <target name="pack-maven.docs" depends="pack-maven.libs, pack-maven.plugins" unless="docs.skip"> <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar" basedir="${build-docs.dir}/library"> <include name="**/*"/> @@ -209,30 +209,30 @@ MAIN DISTRIBUTION PACKAGING <!-- TODO - Scala swing and actors should maybe have thier own jar, but creating it is SLOW. --> <copy tofile="${dists.dir}/maven/${version.number}/scala-swing/scala-swing-docs.jar" - file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar"/> + file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar" overwrite="true"/> <copy tofile="${dists.dir}/maven/${version.number}/scala-actors/scala-actors-docs.jar" - file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar"/> + file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar" overwrite="true"/> <copy tofile="${dists.dir}/maven/${version.number}/scala-reflect/scala-reflect-docs.jar" - file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar"/> + file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar" overwrite="true"/> </target> <target name="pack-maven.latest.unix" depends="pack-maven.docs" unless="os.win"> <symlink link="${dists.dir}/maven/latest" resource="${version.number}" - overwrite="yes"/> + overwrite="true"/> </target> <target name="pack-maven.latest.win" depends="pack-maven.docs" if="os.win"> - <copy todir="${dists.dir}/maven/latest"> + <copy todir="${dists.dir}/maven/latest" overwrite="true"> <fileset dir="${dists.dir}/maven/${version.number}"/> </copy> </target> <target name="pack-maven.scripts" depends="pack-maven.latest.unix,pack-maven.latest.win,pack-maven.srcs"> <copy todir="${dists.dir}/maven/${version.number}" - file="${lib-ant.dir}/ant-contrib.jar"/> + file="${lib-ant.dir}/ant-contrib.jar" overwrite="true"/> <copy todir="${dists.dir}/maven/${version.number}" - file="${lib-ant.dir}/maven-ant-tasks-2.1.1.jar"/> + file="${lib-ant.dir}/maven-ant-tasks-2.1.1.jar" overwrite="true"/> <copy tofile="${dists.dir}/maven/${version.number}/build.xml" file="${src.dir}/build/maven/maven-deploy.xml"/> <!-- export properties for use when deploying --> diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 1f80aa4b6f..01d5791f60 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -176,8 +176,8 @@ trait ScalaSettings extends AbsScalaSettings val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") val Yreploutdir = StringSetting ("-Yrepl-outdir", "path", "Write repl-generated classfiles to given output directory (use \"\" to generate a temporary dir)" , "") val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.") - val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition."). - withDeprecationMessage("This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug.") + val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.").withDeprecationMessage(removalIn212) + val inferByName = BooleanSetting ("-Yinfer-by-name", "Allow inference of by-name types. This is a temporary option to ease transition. See SI-7899.").withDeprecationMessage(removalIn212) val Yinvalidate = StringSetting ("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "") val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") val YdisableUnreachablePrevention = BooleanSetting("-Ydisable-unreachable-prevention", "Disable the prevention of unreachable blocks in code generation.") @@ -185,6 +185,8 @@ trait ScalaSettings extends AbsScalaSettings val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() + private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug." + /** Area-specific debug output. */ val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.") diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 9ac1ce1b9c..ce3e7b1bb5 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -11,6 +11,7 @@ import java.lang.Float.floatToIntBits import java.lang.Double.doubleToLongBits import scala.io.Codec import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } +import scala.reflect.internal.util.shortClassOfInstance import scala.collection.mutable.LinkedHashMap import PickleFormat._ import Flags._ @@ -80,7 +81,7 @@ abstract class Pickler extends SubComponent { private var entries = new Array[AnyRef](256) private var ep = 0 private val index = new LinkedHashMap[AnyRef, Int] - private lazy val nonClassRoot = findOrElse(root.ownersIterator)(!_.isClass)(NoSymbol) + private lazy val nonClassRoot = findSymbol(root.ownersIterator)(!_.isClass) private def isRootSym(sym: Symbol) = sym.name.toTermName == rootName && sym.owner == rootOwner @@ -106,13 +107,14 @@ abstract class Pickler extends SubComponent { * anyway? This is the case if symbol is a refinement class, * an existentially bound variable, or a higher-order type parameter. */ - private def isLocal(sym: Symbol): Boolean = - !sym.isPackageClass && sym != NoSymbol && - (isRootSym(sym) || - sym.isRefinementClass || - sym.isAbstractType && sym.hasFlag(EXISTENTIAL) || // existential param - sym.isParameter || - isLocal(sym.owner)) + private def isLocal(sym: Symbol): Boolean = (sym != NoSymbol) && !sym.isPackageClass && ( + isRootSym(sym) + || sym.isRefinementClass + || sym.isAbstractType && sym.hasFlag(EXISTENTIAL) // existential param + || sym.isParameter + || isLocal(sym.owner) + ) + private def isExternalSymbol(sym: Symbol): Boolean = (sym != NoSymbol) && !isLocal(sym) // Phase 1 methods: Populate entries/index ------------------------------------ @@ -135,13 +137,18 @@ abstract class Pickler extends SubComponent { true } + private def deskolemizeTypeSymbols(ref: AnyRef): AnyRef = ref match { + case sym: Symbol => deskolemize(sym) + case _ => ref + } + /** If the symbol is a type skolem, deskolemize and log it. * If we fail to deskolemize, in a method like * trait Trait[+A] { def f[CC[X]] : CC[A] } * the applied type CC[A] will hold a different CC symbol * than the type-constructor type-parameter CC. */ - private def deskolemize(sym: Symbol) = { + private def deskolemize(sym: Symbol): Symbol = { if (sym.isTypeSkolem) { val sym1 = sym.deSkolemize log({ @@ -169,7 +176,7 @@ abstract class Pickler extends SubComponent { putSymbol(sym.owner) putSymbol(sym.privateWithin) putType(sym.info) - if (sym.thisSym.tpeHK != sym.tpeHK) + if (sym.hasSelfType) putType(sym.typeOfThis) putSymbol(sym.alias) if (!sym.children.isEmpty) { @@ -202,252 +209,65 @@ abstract class Pickler extends SubComponent { case ThisType(sym) => putSymbol(sym) case SingleType(pre, sym) => - putType(pre); putSymbol(sym) + putType(pre) + putSymbol(sym) case SuperType(thistpe, supertpe) => putType(thistpe) putType(supertpe) case ConstantType(value) => putConstant(value) case TypeRef(pre, sym, args) => -// if (sym.isAbstractType && (sym hasFlag EXISTENTIAL)) -// if (!(boundSyms contains sym)) -// println("unbound existential: "+sym+sym.locationString) - putType(pre); putSymbol(sym); putTypes(args) + putType(pre) + putSymbol(sym) + putTypes(args) case TypeBounds(lo, hi) => - putType(lo); putType(hi) - case RefinedType(parents, decls) => - val rclazz = tp.typeSymbol - for (m <- decls.iterator) - if (m.owner != rclazz) abort("bad refinement member "+m+" of "+tp+", owner = "+m.owner) - putSymbol(rclazz); putTypes(parents); putSymbols(decls.toList) - case ClassInfoType(parents, decls, clazz) => - putSymbol(clazz); putTypes(parents); putSymbols(decls.toList) + putType(lo) + putType(hi) + case tp: CompoundType => + putSymbol(tp.typeSymbol) + putTypes(tp.parents) + putSymbols(tp.decls.toList) case MethodType(params, restpe) => - putType(restpe); putSymbols(params) + putType(restpe) + putSymbols(params) case NullaryMethodType(restpe) => putType(restpe) case PolyType(tparams, restpe) => - /* no longer needed since all params are now local - tparams foreach { tparam => - if (!isLocal(tparam)) locals += tparam // similar to existential types, these tparams are local - } - */ - putType(restpe); putSymbols(tparams) + putType(restpe) + putSymbols(tparams) case ExistentialType(tparams, restpe) => -// val savedBoundSyms = boundSyms // boundSyms are known to be local based on the EXISTENTIAL flag (see isLocal) -// boundSyms = tparams ::: boundSyms -// try { - putType(restpe) - // } finally { -// boundSyms = savedBoundSyms -// } + putType(restpe) putSymbols(tparams) - case AnnotatedType(annotations, underlying, selfsym) => + case AnnotatedType(_, underlying, selfsym) => putType(underlying) if (settings.selfInAnnots) putSymbol(selfsym) - putAnnotations(annotations filter (_.isStatic)) + tp.staticAnnotations foreach putAnnotation case _ => throw new FatalError("bad type: " + tp + "(" + tp.getClass + ")") } } private def putTypes(tps: List[Type]) { tps foreach putType } - private def putTree(tree: Tree): Unit = if (putEntry(tree)) { - if (tree != EmptyTree) - putType(tree.tpe) - if (tree.hasSymbolField) - putSymbol(tree.symbol) - - tree match { - case EmptyTree => - - case tree@PackageDef(pid, stats) => - putTree(pid) - putTrees(stats) - - case ClassDef(mods, name, tparams, impl) => - putMods(mods) - putEntry(name) - putTree(impl) - putTrees(tparams) - - case ModuleDef(mods, name, impl) => - putMods(mods) - putEntry(name) - putTree(impl) - - case ValDef(mods, name, tpt, rhs) => - putMods(mods) - putEntry(name) - putTree(tpt) - putTree(rhs) - - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - putMods(mods) - putEntry(name) - putTrees(tparams) - putTreess(vparamss) - putTree(tpt) - putTree(rhs) - - case TypeDef(mods, name, tparams, rhs) => - putMods(mods) - putEntry(name) - putTree(rhs) - putTrees(tparams) - - case LabelDef(name, params, rhs) => - putEntry(name) - putTree(rhs) - putTrees(params) - - case Import(expr, selectors) => - putTree(expr) - for (ImportSelector(from, _, to, _) <- selectors) { - putEntry(from) - putEntry(to) - } -/* - case DocDef(comment, definition) => should not be needed - putConstant(Constant(comment)) - putTree(definition) -*/ - case Template(parents, self, body) => - putTrees(parents) - putTree(self) - putTrees(body) - - case Block(stats, expr) => - putTree(expr) - putTrees(stats) - - case CaseDef(pat, guard, body) => - putTree(pat) - putTree(guard) - putTree(body) - - case Alternative(trees) => - putTrees(trees) - - case Star(elem) => - putTree(elem) - - case Bind(name, body) => - putEntry(name) - putTree(body) - - case UnApply(fun: Tree, args) => - putTree(fun) - putTrees(args) - - case ArrayValue(elemtpt, trees) => - putTree(elemtpt) - putTrees(trees) - - - case Function(vparams, body) => - putTree(body) - putTrees(vparams) - - case Assign(lhs, rhs) => - putTree(lhs) - putTree(rhs) - - case If(cond, thenp, elsep) => - putTree(cond) - putTree(thenp) - putTree(elsep) - - case Match(selector, cases) => - putTree(selector) - putTrees(cases) - - case Return(expr) => - putTree(expr) - - case Try(block, catches, finalizer) => - putTree(block) - putTree(finalizer) - putTrees(catches) - - case Throw(expr) => - putTree(expr) - - case New(tpt) => - putTree(tpt) - - case Typed(expr, tpt) => - putTree(expr) - putTree(tpt) - - case TypeApply(fun, args) => - putTree(fun) - putTrees(args) - - case Apply(fun, args) => - putTree(fun) - putTrees(args) - - case ApplyDynamic(qual, args) => - putTree(qual) - putTrees(args) - - case Super(qual, mix) => - putTree(qual) - putEntry(mix:Name) - - case This(qual) => - putEntry(qual) - - case Select(qualifier, selector) => - putTree(qualifier) - putEntry(selector) - - case Ident(name) => - putEntry(name) - - case Literal(value) => - putEntry(value) - - case TypeTree() => - - case Annotated(annot, arg) => - putTree(annot) - putTree(arg) - - case SingletonTypeTree(ref) => - putTree(ref) - - case SelectFromTypeTree(qualifier, selector) => - putTree(qualifier) - putEntry(selector) - - case CompoundTypeTree(templ: Template) => - putTree(templ) - - case AppliedTypeTree(tpt, args) => - putTree(tpt) - putTrees(args) - - case TypeBoundsTree(lo, hi) => - putTree(lo) - putTree(hi) - - case ExistentialTypeTree(tpt, whereClauses) => - putTree(tpt) - putTrees(whereClauses) + private object putTreeTraverser extends Traverser { + // Only used when pickling trees, i.e. in an argument of some Annotation + // annotations in Modifiers are removed by the typechecker + override def traverseModifiers(mods: Modifiers): Unit = if (putEntry(mods)) putEntry(mods.privateWithin) + override def traverseName(name: Name): Unit = putEntry(name) + override def traverseConstant(const: Constant): Unit = putEntry(const) + override def traverse(tree: Tree): Unit = putTree(tree) + + def put(tree: Tree): Unit = { + if (tree.canHaveAttrs) + putType(tree.tpe) + if (tree.hasSymbolField) + putSymbol(tree.symbol) + + super.traverse(tree) } } - - private def putTrees(trees: List[Tree]) = trees foreach putTree - private def putTreess(treess: List[List[Tree]]) = treess foreach putTrees - - /** only used when pickling trees, i.e. in an - * argument of some Annotation */ - private def putMods(mods: Modifiers) = if (putEntry(mods)) { - // annotations in Modifiers are removed by the typechecker - val Modifiers(_, privateWithin, Nil) = mods - putEntry(privateWithin) + private def putTree(tree: Tree) { + if (putEntry(tree)) + putTreeTraverser put tree } /** Store a constant in map index, along with anything it references. @@ -461,7 +281,7 @@ abstract class Pickler extends SubComponent { } private def putChildren(sym: Symbol, children: List[Symbol]) { - assert(putEntry((sym, children))) + putEntry(sym -> children) children foreach putSymbol } @@ -469,14 +289,10 @@ abstract class Pickler extends SubComponent { private def putAnnotation(sym: Symbol, annot: AnnotationInfo) { // if an annotation with the same arguments is applied to the // same symbol multiple times, it's only pickled once. - if (putEntry((sym, annot))) + if (putEntry(sym -> annot)) putAnnotationBody(annot) } - /** used in AnnotatedType only, i.e. annotations on types */ - private def putAnnotations(annots: List[AnnotationInfo]) { - annots foreach putAnnotation - } private def putAnnotation(annot: AnnotationInfo) { if (putEntry(annot)) putAnnotationBody(annot) @@ -510,14 +326,11 @@ abstract class Pickler extends SubComponent { /** Write a reference to object, i.e., the object's number in the map index. */ - private def writeRef(ref0: AnyRef) { - val ref = ref0 match { - case sym: Symbol => deskolemize(sym) - case _ => ref0 - } - writeNat(index(ref)) + private def writeRef(ref: AnyRef) { + writeNat(index(deskolemizeTypeSymbols(ref))) } - private def writeRefs(refs: List[AnyRef]) { refs foreach writeRef } + private def writeRefs(refs: List[AnyRef]): Unit = refs foreach writeRef + private def writeRefsWithLength(refs: List[AnyRef]) { writeNat(refs.length) writeRefs(refs) @@ -567,446 +380,137 @@ abstract class Pickler extends SubComponent { } } - /** Write an entry */ - private def writeEntry(entry: AnyRef) { - def writeBody(entry: AnyRef): Int = entry match { - case name: Name => - writeName(name) - if (name.isTermName) TERMname else TYPEname - case NoSymbol => - NONEsym - case sym: Symbol if !isLocal(sym) => - val tag = - if (sym.isModuleClass) { - writeRef(sym.name.toTermName); EXTMODCLASSref - } else { - writeRef(sym.name); EXTref - } - if (!sym.owner.isRoot) writeRef(sym.owner) - tag - case sym: ClassSymbol => - writeSymInfo(sym) - if (sym.thisSym.tpe_* != sym.tpe_*) writeRef(sym.typeOfThis) - CLASSsym - case sym: TypeSymbol => - writeSymInfo(sym) - if (sym.isAbstractType) TYPEsym else ALIASsym - case sym: TermSymbol => - writeSymInfo(sym) - if (sym.alias != NoSymbol) writeRef(sym.alias) - if (sym.isModule) MODULEsym else VALsym - case NoType => - NOtpe - case NoPrefix => - NOPREFIXtpe - case ThisType(sym) => - writeRef(sym); THIStpe - case SingleType(pre, sym) => - writeRef(pre); writeRef(sym); SINGLEtpe - case SuperType(thistpe, supertpe) => - writeRef(thistpe); writeRef(supertpe); SUPERtpe - case ConstantType(value) => - writeRef(value); CONSTANTtpe - case TypeRef(pre, sym, args) => - writeRef(pre); writeRef(sym); writeRefs(args); TYPEREFtpe - case TypeBounds(lo, hi) => - writeRef(lo); writeRef(hi); TYPEBOUNDStpe - case tp @ RefinedType(parents, decls) => - writeRef(tp.typeSymbol); writeRefs(parents); REFINEDtpe - case ClassInfoType(parents, decls, clazz) => - writeRef(clazz); writeRefs(parents); CLASSINFOtpe - case mt @ MethodType(formals, restpe) => - writeRef(restpe); writeRefs(formals) ; METHODtpe - case mt @ NullaryMethodType(restpe) => - // reuse POLYtpe since those can never have an empty list of tparams. - // TODO: is there any way this can come back and bite us in the bottom? - // ugliness and thrift aside, this should make this somewhat more backward compatible - // (I'm not sure how old scalac's would deal with nested PolyTypes, as these used to be folded into one) - writeRef(restpe); writeRefs(Nil); POLYtpe - case PolyType(tparams, restpe) => // invar: tparams nonEmpty - writeRef(restpe); writeRefs(tparams); POLYtpe - case ExistentialType(tparams, restpe) => - writeRef(restpe); writeRefs(tparams); EXISTENTIALtpe - case c @ Constant(_) => - if (c.tag == BooleanTag) writeLong(if (c.booleanValue) 1 else 0) - else if (ByteTag <= c.tag && c.tag <= LongTag) writeLong(c.longValue) - else if (c.tag == FloatTag) writeLong(floatToIntBits(c.floatValue).toLong) - else if (c.tag == DoubleTag) writeLong(doubleToLongBits(c.doubleValue)) - else if (c.tag == StringTag) writeRef(newTermName(c.stringValue)) - else if (c.tag == ClazzTag) writeRef(c.typeValue) - else if (c.tag == EnumTag) writeRef(c.symbolValue) - LITERAL + c.tag // also treats UnitTag, NullTag; no value required - case AnnotatedType(annotations, tp, selfsym) => - annotations filter (_.isStatic) match { - case Nil => writeBody(tp) // write the underlying type if there are no annotations - case staticAnnots => - if (settings.selfInAnnots && selfsym != NoSymbol) - writeRef(selfsym) - writeRef(tp) - writeRefs(staticAnnots) - ANNOTATEDtpe - } - // annotations attached to a symbol (i.e. annots on terms) - case (target: Symbol, annot@AnnotationInfo(_, _, _)) => - writeRef(target) - writeAnnotation(annot) - SYMANNOT - - case ArrayAnnotArg(args) => - args foreach writeClassfileAnnotArg - ANNOTARGARRAY - - case (target: Symbol, children: List[_]) => - writeRef(target) - writeRefs(children.asInstanceOf[List[Symbol]]) - CHILDREN - - case EmptyTree => - writeNat(EMPTYtree) - TREE - - case tree@PackageDef(pid, stats) => - writeNat(PACKAGEtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(tree.mods) - writeRef(pid) - writeRefs(stats) - TREE - - case tree@ClassDef(mods, name, tparams, impl) => - writeNat(CLASStree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(mods) - writeRef(name) - writeRef(impl) - writeRefs(tparams) - TREE - - case tree@ModuleDef(mods, name, impl) => - writeNat(MODULEtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(mods) - writeRef(name) - writeRef(impl) - TREE - - case tree@ValDef(mods, name, tpt, rhs) => - writeNat(VALDEFtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(mods) - writeRef(name) - writeRef(tpt) - writeRef(rhs) - TREE - - case tree@DefDef(mods, name, tparams, vparamss, tpt, rhs) => - writeNat(DEFDEFtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(mods) - writeRef(name) - writeRefsWithLength(tparams) - writeNat(vparamss.length) - vparamss foreach writeRefsWithLength - writeRef(tpt) - writeRef(rhs) - TREE - - case tree@TypeDef(mods, name, tparams, rhs) => - writeNat(TYPEDEFtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(mods) - writeRef(name) - writeRef(rhs) - writeRefs(tparams) - TREE - - case tree@LabelDef(name, params, rhs) => - writeNat(LABELtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(name) - writeRef(rhs) - writeRefs(params) - TREE - - case tree@Import(expr, selectors) => - writeNat(IMPORTtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(expr) - for (ImportSelector(from, _, to, _) <- selectors) { - writeRef(from) - writeRef(to) - } - TREE - - case tree@DocDef(comment, definition) => - writeNat(DOCDEFtree) - writeRef(tree.tpe) - writeRef(Constant(comment)) - writeRef(definition) - TREE - - case tree@Template(parents, self, body) => - writeNat(TEMPLATEtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRefsWithLength(parents) - writeRef(self) - writeRefs(body) - TREE - - case tree@Block(stats, expr) => - writeNat(BLOCKtree) - writeRef(tree.tpe) - writeRef(expr) - writeRefs(stats) - TREE - - case tree@CaseDef(pat, guard, body) => - writeNat(CASEtree) - writeRef(tree.tpe) - writeRef(pat) - writeRef(guard) - writeRef(body) - TREE - - case tree@Alternative(trees) => - writeNat(ALTERNATIVEtree) - writeRef(tree.tpe) - writeRefs(trees) - TREE - - case tree@Star(elem) => - writeNat(STARtree) - writeRef(tree.tpe) - writeRef(elem) - TREE - - case tree@Bind(name, body) => - writeNat(BINDtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(name) - writeRef(body) - TREE - - case tree@UnApply(fun: Tree, args) => - writeNat(UNAPPLYtree) - writeRef(tree.tpe) - writeRef(fun) - writeRefs(args) - TREE - - case tree@ArrayValue(elemtpt, trees) => - writeNat(ARRAYVALUEtree) - writeRef(tree.tpe) - writeRef(elemtpt) - writeRefs(trees) - TREE - - case tree@Function(vparams, body) => - writeNat(FUNCTIONtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(body) - writeRefs(vparams) - TREE - - case tree@Assign(lhs, rhs) => - writeNat(ASSIGNtree) - writeRef(tree.tpe) - writeRef(lhs) - writeRef(rhs) - TREE - - case tree@If(cond, thenp, elsep) => - writeNat(IFtree) - writeRef(tree.tpe) - writeRef(cond) - writeRef(thenp) - writeRef(elsep) - TREE - - case tree@Match(selector, cases) => - writeNat(MATCHtree) - writeRef(tree.tpe) - writeRef(selector) - writeRefs(cases) - TREE - - case tree@Return(expr) => - writeNat(RETURNtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(expr) - TREE - - case tree@Try(block, catches, finalizer) => - writeNat(TREtree) - writeRef(tree.tpe) - writeRef(block) - writeRef(finalizer) - writeRefs(catches) - TREE - - case tree@Throw(expr) => - writeNat(THROWtree) - writeRef(tree.tpe) - writeRef(expr) - TREE - - case tree@New(tpt) => - writeNat(NEWtree) - writeRef(tree.tpe) - writeRef(tpt) - TREE - - case tree@Typed(expr, tpt) => - writeNat(TYPEDtree) - writeRef(tree.tpe) - writeRef(expr) - writeRef(tpt) - TREE - - case tree@TypeApply(fun, args) => - writeNat(TYPEAPPLYtree) - writeRef(tree.tpe) - writeRef(fun) - writeRefs(args) - TREE - - case tree@Apply(fun, args) => - writeNat(APPLYtree) - writeRef(tree.tpe) - writeRef(fun) - writeRefs(args) - TREE - - case tree@ApplyDynamic(qual, args) => - writeNat(APPLYDYNAMICtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(qual) - writeRefs(args) - TREE - - case tree@Super(qual, mix) => - writeNat(SUPERtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(qual) - writeRef(mix) - TREE - - case tree@This(qual) => - writeNat(THIStree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(qual) - TREE - - case tree@Select(qualifier, selector) => - writeNat(SELECTtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(qualifier) - writeRef(selector) - TREE - - case tree@Ident(name) => - writeNat(IDENTtree) - writeRef(tree.tpe) - writeRef(tree.symbol) - writeRef(name) - TREE - - case tree@Literal(value) => - writeNat(LITERALtree) - writeRef(tree.tpe) - writeRef(value) - TREE - - case tree@TypeTree() => - writeNat(TYPEtree) - writeRef(tree.tpe) - TREE - - case tree@Annotated(annot, arg) => - writeNat(ANNOTATEDtree) - writeRef(tree.tpe) - writeRef(annot) - writeRef(arg) - TREE - - case tree@SingletonTypeTree(ref) => - writeNat(SINGLETONTYPEtree) + private object writeTreeBodyTraverser extends Traverser { + private var refs = false + @inline private def asRefs[T](body: => T): T = { + val saved = refs + refs = true + try body finally refs = saved + } + override def traverseModifiers(mods: Modifiers): Unit = if (refs) writeRef(mods) else super.traverseModifiers(mods) + override def traverseName(name: Name): Unit = writeRef(name) + override def traverseConstant(const: Constant): Unit = writeRef(const) + override def traverseParams(params: List[Tree]): Unit = writeRefsWithLength(params) + override def traverseParamss(vparamss: List[List[Tree]]): Unit = { + writeNat(vparamss.length) + super.traverseParamss(vparamss) + } + override def traverse(tree: Tree): Unit = { + if (refs) + writeRef(tree) + else { writeRef(tree.tpe) - writeRef(ref) - TREE + if (tree.hasSymbolField) + writeRef(tree.symbol) - case tree@SelectFromTypeTree(qualifier, selector) => - writeNat(SELECTFROMTYPEtree) - writeRef(tree.tpe) - writeRef(qualifier) - writeRef(selector) - TREE + asRefs(super.traverse(tree)) + } + } + } - case tree@CompoundTypeTree(templ: Template) => - writeNat(COMPOUNDTYPEtree) - writeRef(tree.tpe) - writeRef(templ) - TREE + /** Write an entry */ + private def writeEntry(entry: AnyRef) { + def writeLocalSymbolBody(sym: Symbol) { + writeSymInfo(sym) + sym match { + case _: ClassSymbol if sym.hasSelfType => writeRef(sym.typeOfThis) + case _: TermSymbol if sym.alias.exists => writeRef(sym.alias) + case _ => + } + } + def writeExtSymbolBody(sym: Symbol) { + val name = if (sym.isModuleClass) sym.name.toTermName else sym.name + writeRef(name) + if (!sym.owner.isRoot) + writeRef(sym.owner) + } + def writeSymbolBody(sym: Symbol) { + if (sym ne NoSymbol) { + if (isLocal(sym)) + writeLocalSymbolBody(sym) + else + writeExtSymbolBody(sym) + } + } - case tree@AppliedTypeTree(tpt, args) => - writeNat(APPLIEDTYPEtree) - writeRef(tree.tpe) - writeRef(tpt) - writeRefs(args) - TREE + // NullaryMethodType reuses POLYtpe since those can never have an empty list of tparams. + // TODO: is there any way this can come back and bite us in the bottom? + // ugliness and thrift aside, this should make this somewhat more backward compatible + // (I'm not sure how old scalac's would deal with nested PolyTypes, as these used to be folded into one) + def writeTypeBody(tpe: Type): Unit = tpe match { + case NoType | NoPrefix => + case ThisType(sym) => writeRef(sym) + case SingleType(pre, sym) => writeRef(pre) ; writeRef(sym) + case SuperType(thistpe, supertpe) => writeRef(thistpe) ; writeRef(supertpe) + case ConstantType(value) => writeRef(value) + case TypeBounds(lo, hi) => writeRef(lo) ; writeRef(hi) + case TypeRef(pre, sym, args) => writeRef(pre) ; writeRef(sym); writeRefs(args) + case MethodType(formals, restpe) => writeRef(restpe) ; writeRefs(formals) + case NullaryMethodType(restpe) => writeRef(restpe); writeRefs(Nil) + case PolyType(tparams, restpe) => writeRef(restpe); writeRefs(tparams) + case ExistentialType(tparams, restpe) => writeRef(restpe); writeRefs(tparams) + case StaticallyAnnotatedType(annots, tp) => writeRef(tp) ; writeRefs(annots) + case AnnotatedType(_, tp, _) => writeTypeBody(tp) // write the underlying type if there are no static annotations + case CompoundType(parents, _, clazz) => writeRef(clazz); writeRefs(parents) + } - case tree@TypeBoundsTree(lo, hi) => - writeNat(TYPEBOUNDStree) - writeRef(tree.tpe) - writeRef(lo) - writeRef(hi) - TREE + def writeTreeBody(tree: Tree) { + writeNat(picklerSubTag(tree)) + if (!tree.isEmpty) + writeTreeBodyTraverser traverse tree + } - case tree@ExistentialTypeTree(tpt, whereClauses) => - writeNat(EXISTENTIALTYPEtree) - writeRef(tree.tpe) - writeRef(tpt) - writeRefs(whereClauses) - TREE + def writeConstant(c: Constant): Unit = c.tag match { + case BooleanTag => writeLong(if (c.booleanValue) 1 else 0) + case FloatTag => writeLong(floatToIntBits(c.floatValue).toLong) + case DoubleTag => writeLong(doubleToLongBits(c.doubleValue)) + case StringTag => writeRef(newTermName(c.stringValue)) + case ClazzTag => writeRef(c.typeValue) + case EnumTag => writeRef(c.symbolValue) + case tag => if (ByteTag <= tag && tag <= LongTag) writeLong(c.longValue) + } - case Modifiers(flags, privateWithin, _) => - val pflags = rawToPickledFlags(flags) - writeNat((pflags >> 32).toInt) - writeNat((pflags & 0xFFFFFFFF).toInt) - writeRef(privateWithin) - MODIFIERS + def writeModifiers(mods: Modifiers) { + val pflags = rawToPickledFlags(mods.flags) + writeNat((pflags >> 32).toInt) + writeNat((pflags & 0xFFFFFFFF).toInt) + writeRef(mods.privateWithin) + } - // annotations on types (not linked to a symbol) - case annot@AnnotationInfo(_, _, _) => - writeAnnotation(annot) - ANNOTINFO + def writeSymbolTuple(target: Symbol, other: Any) { + writeRef(target) + other match { + case annot: AnnotationInfo => writeAnnotation(annot) + case children: List[Symbol @unchecked] => writeRefs(children) + case _ => + } + } - case _ => - throw new FatalError("bad entry: " + entry + " " + entry.getClass) + def writeBody(entry: AnyRef): Unit = entry match { + case tree: Tree => writeTreeBody(tree) + case sym: Symbol => writeSymbolBody(sym) + case tpe: Type => writeTypeBody(tpe) + case name: Name => writeName(name) + case const: Constant => writeConstant(const) + case mods: Modifiers => writeModifiers(mods) + case annot: AnnotationInfo => writeAnnotation(annot) + case (target: Symbol, other) => writeSymbolTuple(target, other) + case ArrayAnnotArg(args) => args foreach writeClassfileAnnotArg + case _ => devWarning(s"Unexpected entry to pickler ${shortClassOfInstance(entry)} $entry") } // begin writeEntry - val startpos = writeIndex - // reserve some space so that the patchNat's most likely won't need to shift - writeByte(0); writeByte(0) - patchNat(startpos, writeBody(entry)) - patchNat(startpos + 1, writeIndex - (startpos + 2)) + // The picklerTag method can't determine if it's an external symbol reference + val tag = entry match { + case sym: Symbol if isExternalSymbol(sym) => if (sym.isModuleClass) EXTMODCLASSref else EXTref + case _ => picklerTag(entry) + } + writeNat(tag) + writeByte(0) // reserve a place to record the number of bytes written + val start = writeIndex + writeBody(entry) + val length = writeIndex - start + patchNat(start - 1, length) // patch bytes written over the placeholder } /** Write byte array */ diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index d97f62d5d6..ccf2266540 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -85,9 +85,6 @@ abstract class UnCurry extends InfoTransform transformFunction(result) } - private lazy val serialVersionUIDAnnotation = - AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List()) - // I don't have a clue why I'm catching TypeErrors here, but it's better // than spewing stack traces at end users for internal errors. Examples // which hit at this point should not be hard to come by, but the immediate @@ -220,7 +217,7 @@ abstract class UnCurry extends InfoTransform case fun1 if fun1 ne fun => fun1 case _ => val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe)) - val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation + val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation anonClass setInfo ClassInfoType(parents, newScope, anonClass) val targs = fun.tpe.typeArgs diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index b30ae917d9..c19d861d23 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -579,10 +579,10 @@ trait Implicits { private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = { if (Statistics.canEnable) Statistics.incCounter(matchingImplicits) - val itree = atPos(pos.focus) { - // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints - val isScalaDoc = context.tree == EmptyTree + // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints + val isScalaDoc = context.tree == EmptyTree + val itree = atPos(pos.focus) { if (isLocal && !isScalaDoc) { // SI-4270 SI-5376 Always use an unattributed Ident for implicits in the local scope, // rather than an attributed Select, to detect shadowing. @@ -605,7 +605,23 @@ trait Implicits { atPos(itree.pos)(Apply(itree, List(Ident("<argument>") setType approximate(arg1)))), EXPRmode, approximate(arg2) - ) + ) match { + // try to infer implicit parameters immediately in order to: + // 1) guide type inference for implicit views + // 2) discard ineligible views right away instead of risking spurious ambiguous implicits + // + // this is an improvement of the state of the art that brings consistency to implicit resolution rules + // (and also helps fundep materialization to be applicable to implicit views) + // + // there's one caveat though. we need to turn this behavior off for scaladoc + // because scaladoc usually doesn't know the entire story + // and is just interested in views that are potentially applicable + // for instance, if we have `class C[T]` and `implicit def conv[T: Numeric](c: C[T]) = ???` + // then Scaladoc will give us something of type `C[T]`, and it would like to know + // that `conv` is potentially available under such and such conditions + case tree if isImplicitMethodType(tree.tpe) && !isScalaDoc => applyImplicitArgs(tree) + case tree => tree + } case _ => fallback } context.firstError match { // using match rather than foreach to avoid non local return. @@ -617,7 +633,7 @@ trait Implicits { if (Statistics.canEnable) Statistics.incCounter(typedImplicits) - val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun } + val itree2 = if (isView) treeInfo.dissectApplied(itree1).callee else adapt(itree1, EXPRmode, wildPt) typingStack.showAdapt(itree, itree2, pt, context) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index ccf4a5e46f..dd0923a696 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -913,7 +913,11 @@ trait Infer extends Checkable { val targs = exprTypeArgs(tvars, tparams, treeTp, pt, useWeaklyCompatible) def infer_s = map3(tparams, tvars, targs)((tparam, tvar, targ) => s"$tparam=$tvar/$targ") mkString "," printTyping(tree, s"infer expr instance from pt=$pt, $infer_s") - def targsStrict = if (targs eq null) null else targs mapConserve dropByName + + // SI-7899 infering by-name types is unsound. The correct behaviour is conditional because the hole is + // exploited in Scalaz (Free.scala), as seen in: run/t7899-regression. + def dropByNameIfStrict(tp: Type): Type = if (settings.inferByName) tp else dropByName(tp) + def targsStrict = if (targs eq null) null else targs mapConserve dropByNameIfStrict if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226 substExpr(tree, tparams, targsStrict, pt) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 599969598e..e3d7bfd4f8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -425,11 +425,10 @@ trait Namers extends MethodSynthesis { sym } - /** Enter a module symbol. The tree parameter can be either - * a module definition or a class definition. + /** Enter a module symbol. */ def enterModuleSymbol(tree : ModuleDef): Symbol = { - var m: Symbol = context.scope lookupAll tree.name find (_.isModule) getOrElse NoSymbol + var m: Symbol = context.scope lookupModule tree.name val moduleFlags = tree.mods.flags | MODULE if (m.isModule && !m.isPackage && inCurrentScope(m) && (currentRun.canRedefine(m) || m.isSynthetic)) { updatePosFlags(m, tree.pos, moduleFlags) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index a9a7f6a954..36f889f8a4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1414,7 +1414,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean) = tp match { case TypeRef(pre, sym, args) => - checkDeprecated(sym, tree.pos) + tree match { + case tt: TypeTree if tt.original == null => // SI-7783 don't warn about inferred types + case _ => + checkDeprecated(sym, tree.pos) + } if(sym.isJavaDefined) sym.typeParams foreach (_.cookJavaRawInfo()) if (!tp.isHigherKinded && !skipBounds) diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 1b726c37b9..fd8f9bebba 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -283,6 +283,9 @@ abstract class TreeCheckers extends Analyzer { } private def traverseInternal(tree: Tree) { + if (!tree.canHaveAttrs) + return + checkSymbolRefsRespectScope(enclosingMemberDefs takeWhile (md => !md.symbol.hasPackageFlag), tree) checkReturnReferencesDirectlyEnclosingDef(tree) @@ -329,10 +332,9 @@ abstract class TreeCheckers extends Analyzer { return case _ => } - - if (tree.canHaveAttrs && tree.pos == NoPosition) + if (tree.pos == NoPosition) noPos(tree) - else if (tree.tpe == null && phase.id > currentRun.typerPhase.id) + else if (tree.tpe == null && isPastTyper) noType(tree) else if (tree.isDef) { checkSym(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 3cd937313f..c385e7533a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -13,7 +13,7 @@ package scala package tools.nsc package typechecker -import scala.collection.mutable +import scala.collection.{ mutable, immutable } import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance } import mutable.ListBuffer import symtab.Flags._ @@ -2503,7 +2503,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper assert(pt.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt.") val targs = pt.dealiasWiden.typeArgs - // if targs.head isn't fully defined, we can translate --> error + // if targs.head isn't fully defined, we can't translate --> error targs match { case argTp :: _ if isFullyDefined(argTp) => // ok case _ => // uh-oh @@ -2517,9 +2517,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // targs must conform to Any for us to synthesize an applyOrElse (fallback to apply otherwise -- typically for @cps annotated targs) val targsValidParams = targs forall (_ <:< AnyTpe) - val anonClass = (context.owner - newAnonymousFunctionClass tree.pos - addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())) + val anonClass = context.owner newAnonymousFunctionClass tree.pos addAnnotation SerialVersionUIDAnnotation import CODE._ @@ -2712,17 +2710,197 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } + /** Synthesize and type check the implementation of a type with a Single Abstract Method + * + * `{ (p1: T1, ..., pN: TN) => body } : S` + * + * expands to (where `S` is the expected type that defines a single abstract method named `apply`) + * + * `{ + * def apply$body(p1: T1, ..., pN: TN): T = body + * new S { + * def apply(p1: T1, ..., pN: TN): T = apply$body(p1,..., pN) + * } + * }` + * + * If 'T' is not fully defined, it is inferred by type checking + * `apply$body` without a result type before type checking the block. + * The method's inferred result type is used instead of T`. [See test/files/pos/sammy_poly.scala] + * + * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `samClassTp`, + * and `resPt` is derived from `samClassTp` -- it may be fully defined, or not... + * + * The function's body is put in a method outside of the class definition to enforce scoping. + * S's members should not be in scope in `body`. + * + * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list), + * is largely to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple. + * + * NOTE: it would be nicer to not have to type check `apply$body` separately when `T` is not fully defined. + * However T must be fully defined before we type the instantiation, as it'll end up as a parent type, + * which must be fully defined. Would be nice to have some kind of mechanism to insert type vars in a block of code, + * and have the instantiation of the first occurrence propagate to the rest of the block. + */ + def synthesizeSAMFunction(sam: Symbol, fun: Function, resPt: Type, samClassTp: Type, mode: Mode): Tree = { + // assert(fun.vparams forall (vp => isFullyDefined(vp.tpt.tpe))) -- by construction, as we take them from sam's info + val sampos = fun.pos + + // if the expected sam type is fully defined, use it for the method's result type + // otherwise, NoType, so that type inference will determine the method's result type + // resPt is syntactically contained in samClassTp, so if the latter is fully defined, so is the former + // ultimately, we want to fully define samClassTp as it is used as the superclass of our anonymous class + val samDefTp = if (isFullyDefined(resPt)) resPt else NoType + val bodyName = newTermName(sam.name + "$body") + + // `def '${sam.name}\$body'($p1: $T1, ..., $pN: $TN): $resPt = $body` + val samBodyDef = + DefDef(NoMods, + bodyName, + Nil, + List(fun.vparams.map(_.duplicate)), // must duplicate as we're also using them for `samDef` + TypeTree(samDefTp) setPos sampos.focus, + fun.body) + + // If we need to enter the sym for the body def before type checking the block, + // we'll create a nested context, as explained below. + var nestedTyper = this + + // Type check body def before classdef to fully determine samClassTp (if necessary). + // As `samClassTp` determines a parent type for the class, + // we can't type check `block` in one go unless `samClassTp` is fully defined. + val samClassTpFullyDefined = + if (isFullyDefined(samClassTp)) samClassTp + else try { + // This creates a symbol for samBodyDef with a type completer that'll be triggered immediately below. + // The symbol is entered in the same scope used for the block below, and won't thus be reentered later. + // It has to be a new scope, though, or we'll "get ambiguous reference to overloaded definition" [pos/sammy_twice.scala] + // makeSilent: [pos/nonlocal-unchecked.scala -- when translation all functions to sams] + val nestedCtx = enterSym(context.makeNewScope(context.tree, context.owner).makeSilent(), samBodyDef) + nestedTyper = newTyper(nestedCtx) + + // NOTE: this `samBodyDef.symbol.info` runs the type completer set up by the enterSym above + val actualSamType = samBodyDef.symbol.info + + // we're trying to fully define the type arguments for this type constructor + val samTyCon = samClassTp.typeSymbol.typeConstructor + + // the unknowns + val tparams = samClassTp.typeSymbol.typeParams + // ... as typevars + val tvars = tparams map freshVar + + // 1. Recover partial information: + // - derive a type from samClassTp that has the corresponding tparams for type arguments that aren't fully defined + // - constrain typevars to be equal to type args that are fully defined + val samClassTpMoreDefined = appliedType(samTyCon, + (samClassTp.typeArgs, tparams, tvars).zipped map { + case (a, _, tv) if isFullyDefined(a) => tv =:= a; a + case (_, p, _) => p.typeConstructor + }) + + // the method type we're expecting the synthesized sam to have, based on the expected sam type, + // where fully defined type args to samClassTp have been preserved, + // with the unknown args replaced by their corresponding type param + val expectedSamType = samClassTpMoreDefined.memberInfo(sam) + + // 2. make sure the body def's actual type (formals and result) conforms to + // sam's expected type (in terms of the typevars that represent the sam's class's type params) + actualSamType <:< expectedSamType.substituteTypes(tparams, tvars) + + // solve constraints tracked by tvars + val targs = solvedTypes(tvars, tparams, tparams map varianceInType(sam.info), upper = false, lubDepth(sam.info :: Nil)) + + debuglog(s"sam infer: $samClassTp --> ${appliedType(samTyCon, targs)} by $actualSamType <:< $expectedSamType --> $targs for $tparams") + + // a fully defined samClassTp + appliedType(samTyCon, targs) + } catch { + case _: NoInstance | _: TypeError => + devWarning(sampos, s"Could not define type $samClassTp using ${samBodyDef.symbol.rawInfo} <:< ${samClassTp memberInfo sam} (for $sam)") + samClassTp + } + + // `final override def ${sam.name}($p1: $T1, ..., $pN: $TN): $resPt = ${sam.name}\$body'($p1, ..., $pN)` + val samDef = + DefDef(Modifiers(FINAL | OVERRIDE | SYNTHETIC), + sam.name.toTermName, + Nil, + List(fun.vparams), + TypeTree(samBodyDef.tpt.tpe) setPos sampos.focus, + Apply(Ident(bodyName), fun.vparams map (p => Ident(p.name))) + ) + + val serializableParentAddendum = + if (typeIsSubTypeOfSerializable(samClassTp)) Nil + else List(TypeTree(SerializableTpe)) + + val classDef = + ClassDef(Modifiers(FINAL), tpnme.ANON_FUN_NAME, tparams = Nil, + gen.mkTemplate( + parents = TypeTree(samClassTpFullyDefined) :: serializableParentAddendum, + self = emptyValDef, + constrMods = NoMods, + vparamss = ListOfNil, + body = List(samDef), + superPos = sampos.focus + ) + ) + + // type checking the whole block, so that everything is packaged together nicely + // and we don't have to create any symbols by hand + val block = + nestedTyper.typedPos(sampos, mode, samClassTpFullyDefined) { + Block( + samBodyDef, + classDef, + Apply(Select(New(Ident(tpnme.ANON_FUN_NAME)), nme.CONSTRUCTOR), Nil) + ) + } + + classDef.symbol addAnnotation SerialVersionUIDAnnotation + block + } + + /** Type check a function literal. + * + * Based on the expected type pt, potentially synthesize an instance of + * - PartialFunction, + * - a type with a Single Abstract Method (under -Xexperimental for now). + */ private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = { val numVparams = fun.vparams.length - if (numVparams > definitions.MaxFunctionArity) - return MaxFunctionArityError(fun) + val FunctionSymbol = + if (numVparams > definitions.MaxFunctionArity) NoSymbol + else FunctionClass(numVparams) - val FunctionSymbol = FunctionClass(numVparams) - val (argpts, respt) = pt baseType FunctionSymbol match { - case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) - case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType) - } - if (argpts.lengthCompare(numVparams) != 0) + /* The Single Abstract Member of pt, unless pt is the built-in function type of the expected arity, + * as `(a => a): Int => Int` should not (yet) get the sam treatment. + */ + val sam = + if (!settings.Xexperimental || pt.typeSymbol == FunctionSymbol) NoSymbol + else samOf(pt) + + /* The SAM case comes first so that this works: + * abstract class MyFun extends (Int => Int) + * (a => a): MyFun + * + * Note that the arity of the sam must correspond to the arity of the function. + */ + val samViable = sam.exists && sameLength(sam.info.params, fun.vparams) + val (argpts, respt) = + if (samViable) { + val samInfo = pt memberInfo sam + (samInfo.paramTypes, samInfo.resultType) + } else { + pt baseType FunctionSymbol match { + case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) + case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType) + } + } + + if (!FunctionSymbol.exists) + MaxFunctionArityError(fun) + else if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts) else { foreach2(fun.vparams, argpts) { (vparam, argpt) => @@ -2733,7 +2911,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper fun match { case etaExpansion(vparams, fn, args) => silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 => - // if context,undetparams is not empty, the function was polymorphic, + // if context.undetparams is not empty, the function was polymorphic, // so we need the missing arguments to infer its type. See #871 //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams) @@ -2761,6 +2939,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe outerTyper.synthesizePartialFunction(p.name, p.pos, fun.body, mode, pt) + + // Use synthesizeSAMFunction to expand `(p1: T1, ..., pN: TN) => body` + // to an instance of the corresponding anonymous subclass of `pt`. + case _ if samViable => + newTyper(context.outer).synthesizeSAMFunction(sam, fun, respt, pt, mode) + + // regular Function case _ => val vparamSyms = fun.vparams map { vparam => enterSym(context, vparam) @@ -3404,7 +3589,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper else { val annScope = annType.decls .filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined) - val names = new scala.collection.mutable.HashSet[Symbol] + val names = mutable.Set[Symbol]() names ++= (if (isJava) annScope.iterator else typedFun.tpe.params.iterator) @@ -3417,7 +3602,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val nvPairs = args map { case arg @ AssignOrNamedArg(Ident(name), rhs) => val sym = if (isJava) annScope.lookup(name) - else typedFun.tpe.params.find(p => p.name == name).getOrElse(NoSymbol) + else findSymbol(typedFun.tpe.params)(_.name == name) if (sym == NoSymbol) { reportAnnotationError(UnknownAnnotationNameError(arg, name)) (nme.ERROR, None) @@ -3544,8 +3729,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper while (o != owner && o != NoSymbol && !o.hasPackageFlag) o = o.owner o == owner && !isVisibleParameter(sym) } - var localSyms = scala.collection.immutable.Set[Symbol]() - var boundSyms = scala.collection.immutable.Set[Symbol]() + var localSyms = immutable.Set[Symbol]() + var boundSyms = immutable.Set[Symbol]() def isLocal(sym: Symbol): Boolean = if (sym == NoSymbol || sym.isRefinementClass || sym.isLocalDummy) false else if (owner == NoSymbol) tree exists (defines(_, sym)) @@ -4125,7 +4310,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper NotAMemberError(tpt, TypeTree(tp), nme.CONSTRUCTOR) setError(tpt) } - else if (!( tp == sym.thisSym.tpe_* // when there's no explicit self type -- with (#3612) or without self variable + else if (!( tp == sym.typeOfThis // when there's no explicit self type -- with (#3612) or without self variable // sym.thisSym.tpe == tp.typeOfThis (except for objects) || narrowRhs(tp) <:< tp.typeOfThis || phase.erasedTypes @@ -4561,7 +4746,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def sym = tree1.symbol if (tree.isInstanceOf[PostfixSelect]) checkFeature(tree.pos, PostfixOpsFeature, name.decode) - if (sym != null && sym.isOnlyRefinementMember) + if (sym != null && sym.isOnlyRefinementMember && !sym.isMacro) checkFeature(tree1.pos, ReflectiveCallsFeature, sym.toString) qualStableOrError.symbol match { diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 9e5a97270d..e94b7725cd 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -340,7 +340,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lazy val exporter = importer.reverse } - def apply[T](f: CompilerApi => T): T = { + private val toolBoxLock = new Object + def apply[T](f: CompilerApi => T): T = toolBoxLock.synchronized { try f(api) catch { case ex: FatalError => throw ToolBoxError(s"fatal compiler error", ex) } finally api.compiler.cleanupCaches() diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index fa7d41f0fc..241747e6d8 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -2544,18 +2544,32 @@ trait Trees { self: Universe => class Traverser { protected[scala] var currentOwner: Symbol = rootMirror.RootClass + /** Traverse something which Trees contain, but which isn't a Tree itself. */ + def traverseName(name: Name): Unit = () + def traverseConstant(c: Constant): Unit = () + def traverseImportSelector(sel: ImportSelector): Unit = () + def traverseModifiers(mods: Modifiers): Unit = traverseAnnotations(mods.annotations) + /** Traverses a single tree. */ - def traverse(tree: Tree): Unit = itraverse(this, tree) + def traverse(tree: Tree): Unit = itraverse(this, tree) + def traversePattern(pat: Tree): Unit = traverse(pat) + def traverseGuard(guard: Tree): Unit = traverse(guard) + def traverseTypeAscription(tpt: Tree): Unit = traverse(tpt) + // Special handling of noSelfType necessary for backward compat: existing + // traversers break down when they see the unexpected tree. + def traverseSelfType(self: ValDef): Unit = if (self ne noSelfType) traverse(self) /** Traverses a list of trees. */ - def traverseTrees(trees: List[Tree]) { - trees foreach traverse - } + def traverseTrees(trees: List[Tree]): Unit = trees foreach traverse + def traverseTypeArgs(args: List[Tree]): Unit = traverseTrees(args) + def traverseParents(parents: List[Tree]): Unit = traverseTrees(parents) + def traverseCases(cases: List[CaseDef]): Unit = traverseTrees(cases) + def traverseAnnotations(annots: List[Tree]): Unit = traverseTrees(annots) /** Traverses a list of lists of trees. */ - def traverseTreess(treess: List[List[Tree]]) { - treess foreach traverseTrees - } + def traverseTreess(treess: List[List[Tree]]): Unit = treess foreach traverseTrees + def traverseParams(params: List[Tree]): Unit = traverseTrees(params) + def traverseParamss(vparamss: List[List[Tree]]): Unit = vparamss foreach traverseParams /** Traverses a list of trees with a given owner symbol. */ def traverseStats(stats: List[Tree], exprOwner: Symbol) { diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 215ab6abd6..f45fa40f89 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -27,6 +27,8 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => def filterAnnotations(p: AnnotationInfo => Boolean): Self // Retain only annotations meeting the condition. def withoutAnnotations: Self // Remove all annotations from this type. + def staticAnnotations = annotations filter (_.isStatic) + /** Symbols of any @throws annotations on this symbol. */ def throwsAnnotations(): List[Symbol] = annotations collect { diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index 05aaa462c4..19c67879f5 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -38,7 +38,7 @@ trait BaseTypeSeqs { * This is necessary because when run from reflection every base type sequence needs to have a * SynchronizedBaseTypeSeq as mixin. */ - class BaseTypeSeq protected[BaseTypeSeqs] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { + class BaseTypeSeq protected[reflect] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { self => if (Statistics.canEnable) Statistics.incCounter(baseTypeSeqCount) if (Statistics.canEnable) Statistics.incCounter(baseTypeSeqLenTotal, elems.length) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 4d8ae73bcc..7b88514429 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -289,12 +289,6 @@ trait Definitions extends api.StandardDefinitions { lazy val ConstantFalse = ConstantType(Constant(false)) lazy val ConstantNull = ConstantType(Constant(null)) - // Note: this is not the type alias AnyRef, it's a companion-like - // object used by the @specialize annotation. - lazy val AnyRefModule = getMemberModule(ScalaPackageClass, nme.AnyRef) - @deprecated("Use AnyRefModule", "2.10.0") - def Predef_AnyRef = AnyRefModule - lazy val AnyValClass: ClassSymbol = (ScalaPackageClass.info member tpnme.AnyVal orElse { val anyval = enterNewClass(ScalaPackageClass, tpnme.AnyVal, AnyTpe :: Nil, ABSTRACT) val av_constr = anyval.newClassConstructor(NoPosition) @@ -799,6 +793,44 @@ trait Definitions extends api.StandardDefinitions { (sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass) } + /** The single abstract method declared by type `tp` (or `NoSymbol` if it cannot be found). + * + * The method must be monomorphic and have exactly one parameter list. + * The class defining the method is a supertype of `tp` that + * has a public no-arg primary constructor. + */ + def samOf(tp: Type): Symbol = { + // if tp has a constructor, it must be public and must not take any arguments + // (not even an implicit argument list -- to keep it simple for now) + val tpSym = tp.typeSymbol + val ctor = tpSym.primaryConstructor + val ctorOk = !ctor.exists || (!ctor.isOverloaded && ctor.isPublic && ctor.info.params.isEmpty && ctor.info.paramSectionCount <= 1) + + if (tpSym.exists && ctorOk) { + // find the single abstract member, if there is one + // don't go out requiring DEFERRED members, as you will get them even if there's a concrete override: + // scala> abstract class X { def m: Int } + // scala> class Y extends X { def m: Int = 1} + // scala> typeOf[Y].deferredMembers + // Scopes(method m, method getClass) + // + // scala> typeOf[Y].members.filter(_.isDeferred) + // Scopes() + // must filter out "universal" members (getClass is deferred for some reason) + val deferredMembers = ( + tp membersBasedOnFlags (excludedFlags = BridgeAndPrivateFlags, requiredFlags = METHOD) + filter (mem => mem.isDeferredNotDefault && !isUniversalMember(mem)) // TODO: test + ) + + // if there is only one, it's monomorphic and has a single argument list + if (deferredMembers.size == 1 && + deferredMembers.head.typeParams.isEmpty && + deferredMembers.head.info.paramSectionCount == 1) + deferredMembers.head + else NoSymbol + } else NoSymbol + } + def arrayType(arg: Type) = appliedType(ArrayClass, arg) def byNameType(arg: Type) = appliedType(ByNameParamClass, arg) def iteratorOfType(tp: Type) = appliedType(IteratorClass, tp) @@ -1089,6 +1121,7 @@ trait Definitions extends api.StandardDefinitions { lazy val ScalaInlineClass = requiredClass[scala.inline] lazy val ScalaNoInlineClass = requiredClass[scala.noinline] lazy val SerialVersionUIDAttr = requiredClass[scala.SerialVersionUID] + lazy val SerialVersionUIDAnnotation = AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List()) lazy val SpecializedClass = requiredClass[scala.specialized] lazy val ThrowsClass = requiredClass[scala.throws[_]] lazy val TransientAttr = requiredClass[scala.transient] @@ -1141,14 +1174,21 @@ trait Definitions extends api.StandardDefinitions { } lazy val AnnotationDefaultAttr: ClassSymbol = { - val attr = enterNewClass(RuntimePackageClass, tpnme.AnnotationDefaultATTR, List(AnnotationClass.tpe)) - // This attribute needs a constructor so that modifiers in parsed Java code make sense - attr.info.decls enter attr.newClassConstructor(NoPosition) - attr + val sym = RuntimePackageClass.newClassSymbol(tpnme.AnnotationDefaultATTR, NoPosition, 0L) + sym setInfo ClassInfoType(List(AnnotationClass.tpe), newScope, sym) + RuntimePackageClass.info.decls.toList.filter(_.name == sym.name) match { + case existing :: _ => + existing.asInstanceOf[ClassSymbol] + case _ => + RuntimePackageClass.info.decls enter sym + // This attribute needs a constructor so that modifiers in parsed Java code make sense + sym.info.decls enter sym.newClassConstructor(NoPosition) + sym + } } - private def fatalMissingSymbol(owner: Symbol, name: Name, what: String = "member") = { - throw new FatalError(owner + " does not have a " + what + " " + name) + private def fatalMissingSymbol(owner: Symbol, name: Name, what: String = "member", addendum: String = "") = { + throw new FatalError(owner + " does not have a " + what + " " + name + addendum) } def getLanguageFeature(name: String, owner: Symbol = languageFeatureModule): Symbol = getMember(owner, newTypeName(name)) @@ -1183,7 +1223,8 @@ trait Definitions extends api.StandardDefinitions { def getMemberModule(owner: Symbol, name: Name): ModuleSymbol = { getMember(owner, name.toTermName) match { case x: ModuleSymbol => x - case _ => fatalMissingSymbol(owner, name, "member object") + case NoSymbol => fatalMissingSymbol(owner, name, "member object") + case other => fatalMissingSymbol(owner, name, "member object", addendum = s". A symbol ${other} of kind ${other.accurateKindString} already exists.") } } def getTypeMember(owner: Symbol, name: Name): TypeSymbol = { @@ -1349,10 +1390,13 @@ trait Definitions extends api.StandardDefinitions { else flatNameString(etp.typeSymbol, '.') } + // documented in JavaUniverse.init def init() { if (isInitialized) return - // force initialization of every symbol that is synthesized or hijacked by the compiler - val _ = symbolsNotPresentInBytecode + ObjectClass.initialize + ScalaPackageClass.initialize + val forced1 = symbolsNotPresentInBytecode + val forced2 = NoSymbol isInitialized = true } //init diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index 6ed9de8e20..e122fa498b 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -250,6 +250,19 @@ trait Mirrors extends api.Mirrors { RootClass.info.decls enter EmptyPackage RootClass.info.decls enter RootPackage + if (rootOwner != NoSymbol) { + // synthetic core classes are only present in root mirrors + // because Definitions.scala, which initializes and enters them, only affects rootMirror + // therefore we need to enter them manually for non-root mirrors + definitions.syntheticCoreClasses foreach (theirSym => { + val theirOwner = theirSym.owner + assert(theirOwner.isPackageClass, s"theirSym = $theirSym, theirOwner = $theirOwner") + val ourOwner = staticPackage(theirOwner.fullName).moduleClass + val ourSym = theirSym // just copy the symbol into our branch of the symbol table + ourOwner.info.decls enterIfNew ourSym + }) + } + initialized = true } } @@ -274,34 +287,45 @@ trait Mirrors extends api.Mirrors { def mirror = thisMirror.asInstanceOf[Mirror] } - // This is the package _root_. The actual root cannot be referenced at - // the source level, but _root_ is essentially a function => <root>. - final object RootPackage extends ModuleSymbol(rootOwner, NoPosition, nme.ROOTPKG) with RootSymbol { + class RootPackage extends ModuleSymbol(rootOwner, NoPosition, nme.ROOTPKG) with RootSymbol { this setInfo NullaryMethodType(RootClass.tpe) RootClass.sourceModule = this override def isRootPackage = true } + + // This is the package _root_. The actual root cannot be referenced at + // the source level, but _root_ is essentially a function => <root>. + lazy val RootPackage = new RootPackage + + class RootClass extends PackageClassSymbol(rootOwner, NoPosition, tpnme.ROOT) with RootSymbol { + this setInfo rootLoader + + override def isRoot = true + override def isEffectiveRoot = true + override def isNestedClass = false + } + // This is <root>, the actual root of everything except the package _root_. // <root> and _root_ (RootPackage and RootClass) should be the only "well known" // symbols owned by NoSymbol. All owner chains should go through RootClass, // although it is probable that some symbols are created as direct children // of NoSymbol to ensure they will not be stumbled upon. (We should designate // a better encapsulated place for that.) - final object RootClass extends PackageClassSymbol(rootOwner, NoPosition, tpnme.ROOT) with RootSymbol { - this setInfo rootLoader + lazy val RootClass = new RootClass - override def isRoot = true - override def isEffectiveRoot = true - override def isNestedClass = false - } - // The empty package, which holds all top level types without given packages. - final object EmptyPackage extends ModuleSymbol(RootClass, NoPosition, nme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { + class EmptyPackage extends ModuleSymbol(RootClass, NoPosition, nme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { override def isEmptyPackage = true } - final object EmptyPackageClass extends PackageClassSymbol(RootClass, NoPosition, tpnme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { + + // The empty package, which holds all top level types without given packages. + lazy val EmptyPackage = new EmptyPackage + + class EmptyPackageClass extends PackageClassSymbol(RootClass, NoPosition, tpnme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { override def isEffectiveRoot = true override def isEmptyPackageClass = true } + + lazy val EmptyPackageClass = new EmptyPackageClass } } diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index 485d4d5ddd..b7a1681838 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -139,6 +139,12 @@ trait Scopes extends api.Scopes { self: SymbolTable => enter(sym) } + def enterIfNew[T <: Symbol](sym: T): T = { + val existing = lookupEntry(sym.name) + if (existing == null) enter(sym) + else existing.sym.asInstanceOf[T] + } + private def createHash() { hashtable = new Array[ScopeEntry](HASHSIZE) enterAllInHash(elems) @@ -221,8 +227,8 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** Lookup a module or a class, filtering out matching names in scope * which do not match that requirement. */ - def lookupModule(name: Name): Symbol = lookupAll(name.toTermName) find (_.isModule) getOrElse NoSymbol - def lookupClass(name: Name): Symbol = lookupAll(name.toTypeName) find (_.isClass) getOrElse NoSymbol + def lookupModule(name: Name): Symbol = findSymbol(lookupAll(name.toTermName))(_.isModule) + def lookupClass(name: Name): Symbol = findSymbol(lookupAll(name.toTypeName))(_.isClass) /** True if the name exists in this scope, false otherwise. */ def containsName(name: Name) = lookupEntry(name) != null diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 8a0e1e4768..9f56e78059 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -274,6 +274,8 @@ trait StdNames { final val SourceFileATTR: NameType = "SourceFile" final val SyntheticATTR: NameType = "Synthetic" + final val scala_ : NameType = "scala" + def dropSingletonName(name: Name): TypeName = (name dropRight SINGLETON_SUFFIX.length).toTypeName def singletonName(name: Name): TypeName = (name append SINGLETON_SUFFIX).toTypeName def implClassName(name: Name): TypeName = (name append IMPL_CLASS_SUFFIX).toTypeName diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 8f954e4095..8386d02b7c 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -41,6 +41,7 @@ abstract class SymbolTable extends macros.Universe with StdCreators with BuildUtils with PrivateWithin + with pickling.Translations { val gen = new TreeGen { val global: SymbolTable.this.type = SymbolTable.this } @@ -124,6 +125,10 @@ abstract class SymbolTable extends macros.Universe result } + @inline final def findSymbol(xs: TraversableOnce[Symbol])(p: Symbol => Boolean): Symbol = { + xs find p getOrElse NoSymbol + } + // For too long have we suffered in order to sort NAMES. // I'm pretty sure there's a reasonable default for that. // Notice challenge created by Ordering's invariance. @@ -237,12 +242,20 @@ abstract class SymbolTable extends macros.Universe finally popPhase(saved) } + def slowButSafeEnteringPhase[T](ph: Phase)(op: => T): T = { + if (isCompilerUniverse) enteringPhase(ph)(op) + else op + } + @inline final def exitingPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph.next)(op) @inline final def enteringPrevPhase[T](op: => T): T = enteringPhase(phase.prev)(op) @inline final def enteringPhaseNotLaterThan[T](target: Phase)(op: => T): T = if (isAtPhaseAfter(target)) enteringPhase(target)(op) else op + def slowButSafeEnteringPhaseNotLaterThan[T](target: Phase)(op: => T): T = + if (isCompilerUniverse) enteringPhaseNotLaterThan(target)(op) else op + final def isValid(period: Period): Boolean = period != 0 && runId(period) == currentRunId && { val pid = phaseId(period) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index efdc8f7435..ba785c14bd 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -30,13 +30,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => //protected var lockedSyms = scala.collection.immutable.Set[Symbol]() /** Used to keep track of the recursion depth on locked symbols */ - private var recursionTable = immutable.Map.empty[Symbol, Int] + private var _recursionTable = immutable.Map.empty[Symbol, Int] + def recursionTable = _recursionTable + def recursionTable_=(value: immutable.Map[Symbol, Int]) = _recursionTable = value - private var nextexid = 0 - protected def freshExistentialName(suffix: String) = { - nextexid += 1 - newTypeName("_" + nextexid + suffix) - } + private var existentialIds = 0 + protected def nextExistentialId() = { existentialIds += 1; existentialIds } + protected def freshExistentialName(suffix: String) = newTypeName("_" + nextExistentialId() + suffix) // Set the fields which point companions at one another. Returns the module. def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = { @@ -110,10 +110,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => children } + def selfType = { + if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize + typeOfThis + } + def baseClasses = info.baseClasses def module = sourceModule def thisPrefix: Type = thisType - def selfType: Type = typeOfThis def typeSignature: Type = { fullyInitializeSymbol(this); info } def typeSignatureIn(site: Type): Type = { fullyInitializeSymbol(this); site memberInfo this } @@ -127,6 +131,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def setter: Symbol = setter(owner) } + private[reflect] case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) + /** The class for all symbols */ abstract class Symbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: Name) extends SymbolContextApiImpl @@ -800,7 +806,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * * Stability and volatility are checked separately to allow volatile paths in patterns that amount to equality checks. SI-6815 */ - final def isStable = isTerm && !isMutable && !(hasFlag(BYNAMEPARAM)) && (!isMethod || hasStableFlag) + def isStable = isTerm && !isMutable && !(hasFlag(BYNAMEPARAM)) && (!isMethod || hasStableFlag) final def hasVolatileType = tpe.isVolatile && !hasAnnotation(uncheckedStableClass) /** Does this symbol denote the primary constructor of its enclosing class? */ @@ -949,6 +955,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isInitialized: Boolean = validTo != NoPeriod + /** Some completers call sym.setInfo when still in-flight and then proceed with initialization (e.g. see LazyPackageType) + * setInfo sets _validTo to current period, which means that after a call to setInfo isInitialized will start returning true. + * Unfortunately, this doesn't mean that info becomes ready to be used, because subsequent initialization might change the info. + * Therefore we need this method to distinguish between initialized and really initialized symbol states. + */ + final def isFullyInitialized: Boolean = _validTo != NoPeriod && (flags & LOCKED) == 0 + /** Can this symbol be loaded by a reflective mirror? * * Scalac relies on `ScalaSignature' annotation to retain symbols across compilation runs. @@ -1563,6 +1576,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => * assumption: if a type starts out as monomorphic, it will not acquire * type parameters later. */ + // NOTE: overridden in SynchronizedSymbols with the code copy/pasted + // don't forget to modify the code over there if you modify this method def unsafeTypeParams: List[Symbol] = if (isMonomorphicType) Nil else enteringPhase(unsafeTypeParamPhase)(rawInfo.typeParams) @@ -1571,6 +1586,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => * assumption: if a type starts out as monomorphic, it will not acquire * type parameters later. */ + // NOTE: overridden in SynchronizedSymbols with the code copy/pasted + // don't forget to modify the code over there if you modify this method def typeParams: List[Symbol] = if (isMonomorphicType) Nil else { @@ -1859,6 +1876,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def thisSym: Symbol = this + def hasSelfType = thisSym.tpeHK != this.tpeHK + /** The type of `this` in a class, or else the type of the symbol itself. */ def typeOfThis = thisSym.tpe_* @@ -2391,7 +2410,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isTerm && (!isParameter || isParamAccessor)) "val" else "" - private case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) private def symbolKind: SymbolKind = { var kind = if (isTermMacro) ("term macro", "macro method", "MACM") @@ -3122,8 +3140,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def thisType: Type = { val period = thisTypePeriod if (period != currentPeriod) { - thisTypePeriod = currentPeriod if (!isValid(period)) thisTypeCache = ThisType(this) + thisTypePeriod = currentPeriod } thisTypeCache } @@ -3211,9 +3229,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def typeOfThis = { val period = typeOfThisPeriod if (period != currentPeriod) { - typeOfThisPeriod = currentPeriod if (!isValid(period)) typeOfThisCache = singleType(owner.thisType, sourceModule) + typeOfThisPeriod = currentPeriod } typeOfThisCache } @@ -3224,9 +3242,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => // Skip a package object class, because the members are also in // the package and we wish to avoid spurious ambiguities as in pos/t3999. if (!isPackageObjectClass) { + implicitMembersCacheValue = tp.implicitMembers implicitMembersCacheKey1 = tp implicitMembersCacheKey2 = tp.decls.elems - implicitMembersCacheValue = tp.implicitMembers } } implicitMembersCacheValue @@ -3334,10 +3352,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => def name = nme.NO_NAME override def name_=(n: Name) = abort("Cannot set NoSymbol's name to " + n) - synchronized { - setInfo(NoType) - privateWithin = this - } + // Syncnote: no need to synchronize this, because NoSymbol's initialization is triggered by JavaUniverse.init + // which is called in universe's constructor - something that's inherently single-threaded + setInfo(NoType) + privateWithin = this + override def info_=(info: Type) = { infos = TypeHistory(1, NoType, null) unlock() diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 54a1913b96..9adddeed50 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -8,10 +8,12 @@ package reflect package internal import Flags._ -import scala.collection.mutable.{ListBuffer, LinkedHashSet} +import pickling.PickleFormat._ +import scala.collection.{ mutable, immutable } import util.Statistics -trait Trees extends api.Trees { self: SymbolTable => +trait Trees extends api.Trees { + self: SymbolTable => private[scala] var nodeCount = 0 @@ -160,7 +162,7 @@ trait Trees extends api.Trees { self: SymbolTable => override def freeTypes: List[FreeTypeSymbol] = freeSyms[FreeTypeSymbol](_.isFreeType, _.typeSymbol) private def freeSyms[S <: Symbol](isFree: Symbol => Boolean, symOfType: Type => Symbol): List[S] = { - val s = scala.collection.mutable.LinkedHashSet[S]() + val s = mutable.LinkedHashSet[S]() def addIfFree(sym: Symbol): Unit = if (sym != null && isFree(sym)) s += sym.asInstanceOf[S] for (t <- this) { addIfFree(t.symbol) @@ -1019,18 +1021,22 @@ trait Trees extends api.Trees { self: SymbolTable => } trait CannotHaveAttrs extends Tree { - override def canHaveAttrs = false - - private def requireLegal(value: Any, allowed: Any, what: String) = - require(value == allowed, s"can't set $what for $self to value other than $allowed") - super.setPos(NoPosition) + super.setType(NoType) + + override def canHaveAttrs = false override def setPos(pos: Position) = { requireLegal(pos, NoPosition, "pos"); this } override def pos_=(pos: Position) = setPos(pos) - - super.setType(NoType) override def setType(t: Type) = { requireLegal(t, NoType, "tpe"); this } override def tpe_=(t: Type) = setType(t) + + private def requireLegal(value: Any, allowed: Any, what: String) = ( + if (value != allowed) { + log(s"can't set $what for $self to value other than $allowed") + if (settings.debug && settings.developer) + (new Throwable).printStackTrace + } + ) } case object EmptyTree extends TermTree with CannotHaveAttrs { override def isEmpty = true; val asList = List(this) } @@ -1159,113 +1165,136 @@ trait Trees extends api.Trees { self: SymbolTable => override protected def itraverse(traverser: Traverser, tree: Tree): Unit = { import traverser._ - tree match { - case EmptyTree => - ; - case PackageDef(pid, stats) => - traverse(pid) - atOwner(mclass(tree.symbol)) { - traverseTrees(stats) - } - case ClassDef(mods, name, tparams, impl) => - atOwner(tree.symbol) { - traverseTrees(mods.annotations); traverseTrees(tparams); traverse(impl) - } - case ModuleDef(mods, name, impl) => - atOwner(mclass(tree.symbol)) { - traverseTrees(mods.annotations); traverse(impl) - } - case ValDef(mods, name, tpt, rhs) => - atOwner(tree.symbol) { - traverseTrees(mods.annotations); traverse(tpt); traverse(rhs) - } - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - atOwner(tree.symbol) { - traverseTrees(mods.annotations); traverseTrees(tparams); traverseTreess(vparamss); traverse(tpt); traverse(rhs) - } - case TypeDef(mods, name, tparams, rhs) => - atOwner(tree.symbol) { - traverseTrees(mods.annotations); traverseTrees(tparams); traverse(rhs) - } + + def traverseMemberDef(md: MemberDef, owner: Symbol): Unit = atOwner(owner) { + traverseModifiers(md.mods) + traverseName(md.name) + md match { + case ClassDef(_, _, tparams, impl) => traverseParams(tparams) ; traverse(impl) + case ModuleDef(_, _, impl) => traverse(impl) + case ValDef(_, _, tpt, rhs) => traverseTypeAscription(tpt) ; traverse(rhs) + case TypeDef(_, _, tparams, rhs) => traverseParams(tparams) ; traverse(rhs) + case DefDef(_, _, tparams, vparamss, tpt, rhs) => + traverseParams(tparams) + traverseParamss(vparamss) + traverseTypeAscription(tpt) + traverse(rhs) + } + } + def traverseComponents(): Unit = tree match { case LabelDef(name, params, rhs) => - traverseTrees(params); traverse(rhs) + traverseName(name) + traverseParams(params) + traverse(rhs) case Import(expr, selectors) => traverse(expr) + selectors foreach traverseImportSelector case Annotated(annot, arg) => - traverse(annot); traverse(arg) + traverse(annot) + traverse(arg) case Template(parents, self, body) => - traverseTrees(parents) - if (self ne noSelfType) traverse(self) + traverseParents(parents) + traverseSelfType(self) traverseStats(body, tree.symbol) case Block(stats, expr) => - traverseTrees(stats); traverse(expr) + traverseTrees(stats) + traverse(expr) case CaseDef(pat, guard, body) => - traverse(pat); traverse(guard); traverse(body) + traversePattern(pat) + traverseGuard(guard) + traverse(body) case Alternative(trees) => traverseTrees(trees) case Star(elem) => traverse(elem) case Bind(name, body) => + traverseName(name) traverse(body) case UnApply(fun, args) => - traverse(fun); traverseTrees(args) + traverse(fun) + traverseTrees(args) case ArrayValue(elemtpt, trees) => - traverse(elemtpt); traverseTrees(trees) - case Function(vparams, body) => - atOwner(tree.symbol) { - traverseTrees(vparams); traverse(body) - } + traverse(elemtpt) + traverseTrees(trees) case Assign(lhs, rhs) => - traverse(lhs); traverse(rhs) + traverse(lhs) + traverse(rhs) case AssignOrNamedArg(lhs, rhs) => - traverse(lhs); traverse(rhs) + traverse(lhs) + traverse(rhs) case If(cond, thenp, elsep) => - traverse(cond); traverse(thenp); traverse(elsep) + traverse(cond) + traverse(thenp) + traverse(elsep) case Match(selector, cases) => - traverse(selector); traverseTrees(cases) + traverse(selector) + traverseCases(cases) case Return(expr) => traverse(expr) case Try(block, catches, finalizer) => - traverse(block); traverseTrees(catches); traverse(finalizer) + traverse(block) + traverseCases(catches) + traverse(finalizer) case Throw(expr) => traverse(expr) case New(tpt) => traverse(tpt) case Typed(expr, tpt) => - traverse(expr); traverse(tpt) + traverse(expr) + traverseTypeAscription(tpt) case TypeApply(fun, args) => - traverse(fun); traverseTrees(args) + traverse(fun) + traverseTypeArgs(args) case Apply(fun, args) => - traverse(fun); traverseTrees(args) + traverse(fun) + traverseTrees(args) case ApplyDynamic(qual, args) => - traverse(qual); traverseTrees(args) - case Super(qual, _) => traverse(qual) - case This(_) => - ; + traverseTrees(args) + case Super(qual, mix) => + traverse(qual) + traverseName(mix) + case This(qual) => + traverseName(qual) case Select(qualifier, selector) => traverse(qualifier) - case Ident(_) => - ; + traverseName(selector) + case Ident(name) => + traverseName(name) case ReferenceToBoxed(idt) => traverse(idt) - case Literal(_) => - ; + case Literal(const) => + traverseConstant(const) case TypeTree() => ; case SingletonTypeTree(ref) => traverse(ref) case SelectFromTypeTree(qualifier, selector) => traverse(qualifier) + traverseName(selector) case CompoundTypeTree(templ) => traverse(templ) case AppliedTypeTree(tpt, args) => - traverse(tpt); traverseTrees(args) + traverse(tpt) + traverseTypeArgs(args) case TypeBoundsTree(lo, hi) => - traverse(lo); traverse(hi) + traverse(lo) + traverse(hi) case ExistentialTypeTree(tpt, whereClauses) => - traverse(tpt); traverseTrees(whereClauses) - case _ => xtraverse(traverser, tree) + traverse(tpt) + traverseTrees(whereClauses) + case _ => + xtraverse(traverser, tree) + } + + if (tree.canHaveAttrs) { + tree match { + case PackageDef(pid, stats) => traverse(pid) ; traverseStats(stats, mclass(tree.symbol)) + case md: ModuleDef => traverseMemberDef(md, mclass(tree.symbol)) + case md: MemberDef => traverseMemberDef(md, tree.symbol) + case Function(vparams, body) => atOwner(tree.symbol) { traverseParams(vparams) ; traverse(body) } + case _ => traverseComponents() + } } } @@ -1563,7 +1592,7 @@ trait Trees extends api.Trees { self: SymbolTable => } class FilterTreeTraverser(p: Tree => Boolean) extends Traverser { - val hits = new ListBuffer[Tree] + val hits = mutable.ListBuffer[Tree]() override def traverse(t: Tree) { if (p(t)) hits += t super.traverse(t) @@ -1571,7 +1600,7 @@ trait Trees extends api.Trees { self: SymbolTable => } class CollectTreeTraverser[T](pf: PartialFunction[Tree, T]) extends Traverser { - val results = new ListBuffer[T] + val results = mutable.ListBuffer[T]() override def traverse(t: Tree) { if (pf.isDefinedAt(t)) results += pf(t) super.traverse(t) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 85dfa037ec..204a2e7088 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -115,14 +115,17 @@ trait Types /** The current skolemization level, needed for the algorithms * in isSameType, isSubType that do constraint solving under a prefix. */ - var skolemizationLevel = 0 + private var _skolemizationLevel = 0 + def skolemizationLevel = _skolemizationLevel + def skolemizationLevel_=(value: Int) = _skolemizationLevel = value /** A map from lists to compound types that have the given list as parents. * This is used to avoid duplication in the computation of base type sequences and baseClasses. * It makes use of the fact that these two operations depend only on the parents, * not on the refinement. */ - val intersectionWitness = perRunCaches.newWeakMap[List[Type], WeakReference[Type]]() + private val _intersectionWitness = perRunCaches.newWeakMap[List[Type], WeakReference[Type]]() + def intersectionWitness = _intersectionWitness /** A proxy for a type (identified by field `underlying`) that forwards most * operations to it (for exceptions, see WrappingProxy, which forwards even more operations). @@ -974,6 +977,18 @@ trait Types else (baseClasses.head.newOverloaded(this, alts)) } + /** Find all members meeting the flag requirements. + * + * If you require a DEFERRED member, you will get it if it exists -- even if there's an overriding concrete member. + * If you exclude DEFERRED members, or don't specify any requirements, + * you won't get deferred members (whether they have an overriding concrete member or not) + * + * Thus, findMember requiring DEFERRED flags yields deferred members, + * while `findMember(excludedFlags = 0, requiredFlags = 0).filter(_.isDeferred)` may not (if there's a corresponding concrete member) + * + * Requirements take precedence over exclusions, so requiring and excluding DEFERRED will yield a DEFERRED member (if there is one). + * + */ def findMembers(excludedFlags: Long, requiredFlags: Long): Scope = { def findMembersInternal: Scope = { var members: Scope = null @@ -983,10 +998,10 @@ trait Types //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG var required = requiredFlags var excluded = excludedFlags | DEFERRED - var continue = true + var retryForDeferred = true var self: Type = null - while (continue) { - continue = false + while (retryForDeferred) { + retryForDeferred = false val bcs0 = baseClasses var bcs = bcs0 while (!bcs.isEmpty) { @@ -1018,7 +1033,7 @@ trait Types } if (others eq null) members enter sym } else if (excl == DEFERRED) { - continue = true + retryForDeferred = (excludedFlags & DEFERRED) == 0 } } entry = entry.next @@ -1028,7 +1043,7 @@ trait Types } // while (!bcs.isEmpty) required |= DEFERRED excluded &= ~(DEFERRED.toLong) - } // while (continue) + } // while (retryForDeferred) if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) if (members eq null) EmptyScope else members } @@ -1471,6 +1486,14 @@ trait Types } } + object CompoundType { + def unapply(tp: Type): Option[(List[Type], Scope, Symbol)] = tp match { + case ClassInfoType(parents, decls, clazz) => Some((parents, decls, clazz)) + case RefinedType(parents, decls) => Some((parents, decls, tp.typeSymbol)) + case _ => None + } + } + /** A common base class for intersection types and class types */ abstract class CompoundType extends Type { @@ -1954,12 +1977,12 @@ trait Types def apply(value: Constant) = unique(new UniqueConstantType(value)) } - /* Syncnote: The `volatile` var and `pendingVolatiles` mutable set need not be protected - * with synchronized, because they are accessed only from isVolatile, which is called only from - * Typer. - */ - private var volatileRecursions: Int = 0 - private val pendingVolatiles = new mutable.HashSet[Symbol] + private var _volatileRecursions: Int = 0 + def volatileRecursions = _volatileRecursions + def volatileRecursions_=(value: Int) = _volatileRecursions = value + + private val _pendingVolatiles = new mutable.HashSet[Symbol] + def pendingVolatiles = _pendingVolatiles class ArgsTypeRef(pre0: Type, sym0: Symbol, args0: List[Type]) extends TypeRef(pre0, sym0, args0) { require(args0.nonEmpty, this) @@ -3293,6 +3316,13 @@ trait Types object AnnotatedType extends AnnotatedTypeExtractor + object StaticallyAnnotatedType { + def unapply(tp: Type): Option[(List[AnnotationInfo], Type)] = tp.staticAnnotations match { + case Nil => None + case annots => Some((annots, tp.withoutAnnotations)) + } + } + /** A class representing types with a name. When an application uses * named arguments, the named argument types for calling isApplicable * are represented as NamedType. @@ -3920,9 +3950,12 @@ trait Types */ final def hasLength(xs: List[_], len: Int) = xs.lengthCompare(len) == 0 - private var basetypeRecursions: Int = 0 - private val pendingBaseTypes = new mutable.HashSet[Type] + private var _basetypeRecursions: Int = 0 + def basetypeRecursions = _basetypeRecursions + def basetypeRecursions_=(value: Int) = _basetypeRecursions = value + private val _pendingBaseTypes = new mutable.HashSet[Type] + def pendingBaseTypes = _pendingBaseTypes /** Does this type have a prefix that begins with a type variable, * or is it a refinement type? For type prefixes that fulfil this condition, @@ -4422,7 +4455,9 @@ trait Types } /** The current indentation string for traces */ - protected[internal] var indent: String = "" + private var _indent: String = "" + protected def indent = _indent + protected def indent_=(value: String) = _indent = value /** Perform operation `p` on arguments `tp1`, `arg2` and print trace of computation. */ protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala index bf00a7ac87..5280467055 100644 --- a/src/reflect/scala/reflect/internal/Variances.scala +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -142,7 +142,8 @@ trait Variances { // No variance check for object-private/protected methods/values. // Or constructors, or case class factory or extractor. def skip = ( - sym.hasLocalFlag + sym == NoSymbol + || sym.hasLocalFlag || sym.owner.isConstructor || sym.owner.isCaseApplyOrUnapply ) diff --git a/src/reflect/scala/reflect/internal/pickling/Translations.scala b/src/reflect/scala/reflect/internal/pickling/Translations.scala new file mode 100644 index 0000000000..e56cf796cb --- /dev/null +++ b/src/reflect/scala/reflect/internal/pickling/Translations.scala @@ -0,0 +1,128 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package reflect +package internal +package pickling + +import PickleFormat._ +import util.shortClassOfInstance + +trait Translations { + self: SymbolTable => + + def isTreeSymbolPickled(code: Int): Boolean = (code: @annotation.switch) match { + case PACKAGEtree | CLASStree | MODULEtree | VALDEFtree | DEFDEFtree | TYPEDEFtree | LABELtree => true + case IMPORTtree | TEMPLATEtree | BINDtree | FUNCTIONtree | RETURNtree => true + case APPLYDYNAMICtree | SUPERtree | THIStree | SELECTtree | IDENTtree => true + case _ => false + } + /** This method should be equivalent to tree.hasSymbolField, but that method + * doesn't do us any good when we're unpickling because we need to know based + * on the Int tag - the tree doesn't exist yet. Thus, this method is documentation only. + */ + def isTreeSymbolPickled(tree: Tree): Boolean = isTreeSymbolPickled(picklerSubTag(tree)) + + // The ad hoc pattern matching of tuples out of AnyRefs is a + // truly terrible idea. It reaches the height of its powers in + // combination with scala's insistence on helpfully tupling + // multiple arguments passed to a single-arg AnyRef. + def picklerTag(ref: AnyRef): Int = ref match { + case tp: Type => picklerTag(tp) + case sym: Symbol => picklerTag(sym) + case const: Constant => LITERAL + const.tag + case _: Tree => TREE // its sub tag more precisely identifies it + case _: TermName => TERMname + case _: TypeName => TYPEname + case _: ArrayAnnotArg => ANNOTARGARRAY // an array of annotation arguments + case _: AnnotationInfo => ANNOTINFO // annotations on types (not linked to a symbol) + case (_: Symbol, _: AnnotationInfo) => SYMANNOT // symbol annotations, i.e. on terms + case (_: Symbol, _: List[_]) => CHILDREN // the direct subclasses of a sealed symbol + case _: Modifiers => MODIFIERS + case _ => sys.error(s"unpicklable entry ${shortClassOfInstance(ref)} $ref") + } + + /** Local symbols only. The assessment of locality depends + * on convoluted conditions which depends in part on the root + * symbol being pickled, so it cannot be reproduced here. + * The pickler tags at stake are EXTMODCLASSref and EXTref. + * Those tags are never produced here - such symbols must be + * excluded prior to calling this method. + */ + def picklerTag(sym: Symbol): Int = sym match { + case NoSymbol => NONEsym + case _: ClassSymbol => CLASSsym + case _: TypeSymbol if sym.isAbstractType => TYPEsym + case _: TypeSymbol => ALIASsym + case _: TermSymbol if sym.isModule => MODULEsym + case _: TermSymbol => VALsym + } + + def picklerTag(tpe: Type): Int = tpe match { + case NoType => NOtpe + case NoPrefix => NOPREFIXtpe + case _: ThisType => THIStpe + case _: SingleType => SINGLEtpe + case _: SuperType => SUPERtpe + case _: ConstantType => CONSTANTtpe + case _: TypeBounds => TYPEBOUNDStpe + case _: TypeRef => TYPEREFtpe + case _: RefinedType => REFINEDtpe + case _: ClassInfoType => CLASSINFOtpe + case _: MethodType => METHODtpe + case _: PolyType => POLYtpe + case _: NullaryMethodType => POLYtpe // bad juju, distinct ints are not at a premium! + case _: ExistentialType => EXISTENTIALtpe + case _: AnnotatedType => ANNOTATEDtpe + } + + def picklerSubTag(tree: Tree): Int = tree match { + case EmptyTree => EMPTYtree + case _: PackageDef => PACKAGEtree + case _: ClassDef => CLASStree + case _: ModuleDef => MODULEtree + case _: ValDef => VALDEFtree + case _: DefDef => DEFDEFtree + case _: TypeDef => TYPEDEFtree + case _: LabelDef => LABELtree + case _: Import => IMPORTtree + // case _: DocDef => DOCDEFtree + case _: Template => TEMPLATEtree + case _: Block => BLOCKtree + case _: CaseDef => CASEtree + case _: Alternative => ALTERNATIVEtree + case _: Star => STARtree + case _: Bind => BINDtree + case _: UnApply => UNAPPLYtree + case _: ArrayValue => ARRAYVALUEtree + case _: Function => FUNCTIONtree + case _: Assign => ASSIGNtree + case _: If => IFtree + case _: Match => MATCHtree + case _: Return => RETURNtree + case _: Try => TREtree // TREtree? + case _: Throw => THROWtree + case _: New => NEWtree + case _: Typed => TYPEDtree + case _: TypeApply => TYPEAPPLYtree + case _: Apply => APPLYtree + case _: ApplyDynamic => APPLYDYNAMICtree + case _: Super => SUPERtree + case _: This => THIStree + case _: Select => SELECTtree + case _: Ident => IDENTtree + case _: Literal => LITERALtree + case _: TypeTree => TYPEtree + case _: Annotated => ANNOTATEDtree + case _: SingletonTypeTree => SINGLETONTYPEtree + case _: SelectFromTypeTree => SELECTFROMTYPEtree + case _: CompoundTypeTree => COMPOUNDTYPEtree + case _: AppliedTypeTree => APPLIEDTYPEtree + case _: TypeBoundsTree => TYPEBOUNDStree + case _: ExistentialTypeTree => EXISTENTIALTYPEtree + } +} + diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index f42dbf56e1..a6c34935ad 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -65,36 +65,38 @@ abstract class UnPickler { /** A map from symbols to their associated `decls` scopes */ private val symScopes = mutable.HashMap[Symbol, Scope]() + private def expect(expected: Int, msg: => String) { + val tag = readByte() + if (tag != expected) + errorBadSignature(s"$msg ($tag)") + } + //println("unpickled " + classRoot + ":" + classRoot.rawInfo + ", " + moduleRoot + ":" + moduleRoot.rawInfo);//debug + @inline private def runAtIndex[T](i: Int)(body: => T): T = { + val saved = readIndex + readIndex = index(i) + try body finally readIndex = saved + } + // Laboriously unrolled for performance. def run() { var i = 0 while (i < index.length) { - if (entries(i) == null && isSymbolEntry(i)) { - val savedIndex = readIndex - readIndex = index(i) - entries(i) = readSymbol() - readIndex = savedIndex - } + if (entries(i) == null && isSymbolEntry(i)) + runAtIndex(i)(entries(i) = readSymbol()) + i += 1 } + // read children last, fix for #3951 i = 0 while (i < index.length) { if (entries(i) == null) { - if (isSymbolAnnotationEntry(i)) { - val savedIndex = readIndex - readIndex = index(i) - readSymbolAnnotation() - readIndex = savedIndex - } - else if (isChildrenEntry(i)) { - val savedIndex = readIndex - readIndex = index(i) - readChildren() - readIndex = savedIndex - } + if (isSymbolAnnotationEntry(i)) + runAtIndex(i)(readSymbolAnnotation()) + else if (isChildrenEntry(i)) + runAtIndex(i)(readChildren()) } i += 1 } @@ -145,6 +147,11 @@ abstract class UnPickler { tag == CHILDREN } + private def maybeReadSymbol(): Either[Int, Symbol] = readNat() match { + case index if isSymbolRef(index) => Right(at(index, readSymbol)) + case index => Left(index) + } + /** Does entry represent a refinement symbol? * pre: Entry is a class symbol */ @@ -256,14 +263,11 @@ abstract class UnPickler { val name = at(nameref, readName) val owner = readSymbolRef() val flags = pickledToRawFlags(readLongNat()) - var inforef = readNat() - val privateWithin = - if (!isSymbolRef(inforef)) NoSymbol - else { - val pw = at(inforef, readSymbol) - inforef = readNat() - pw - } + + val (privateWithin, inforef) = maybeReadSymbol() match { + case Left(index) => NoSymbol -> index + case Right(sym) => sym -> readNat() + } def isModuleFlag = (flags & MODULE) != 0L def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) @@ -305,7 +309,7 @@ abstract class UnPickler { sym case MODULEsym => - val clazz = at(inforef, () => readType()).typeSymbol // after the NMT_TRANSITION period, we can leave off the () => ... () + val clazz = at(inforef, () => readType()).typeSymbol // after NMT_TRANSITION, we can leave off the () => ... () if (isModuleRoot) moduleRoot setFlag pflags else owner.newLinkedModule(clazz, pflags) case VALsym => @@ -317,84 +321,48 @@ abstract class UnPickler { }) } - /** Read a type - * - * @param forceProperType is used to ease the transition to NullaryMethodTypes (commentmarker: NMT_TRANSITION) - * the flag say that a type of kind * is expected, so that PolyType(tps, restpe) can be disambiguated to PolyType(tps, NullaryMethodType(restpe)) - * (if restpe is not a ClassInfoType, a MethodType or a NullaryMethodType, which leaves TypeRef/SingletonType -- the latter would make the polytype a type constructor) - */ protected def readType(forceProperType: Boolean = false): Type = { val tag = readByte() val end = readEnd() + @inline def all[T](body: => T): List[T] = until(end, () => body) + + def readTypes() = all(readTypeRef) + def readSymbols() = all(readSymbolRef) + def readAnnots() = all(readAnnotationRef) + + // if the method is overloaded, the params cannot be determined (see readSymbol) => return NoType. + // Only happen for trees, "case Apply" in readTree() takes care of selecting the correct + // alternative after parsing the arguments. + def MethodTypeRef(restpe: Type, params: List[Symbol]): Type = ( + if (restpe == NoType || (params contains NoSymbol)) NoType + else MethodType(params, restpe) + ) + def PolyOrNullaryType(restpe: Type, tparams: List[Symbol]): Type = tparams match { + case Nil => NullaryMethodType(restpe) + case _ => PolyType(tparams, restpe) + } + def CompoundType(clazz: Symbol, parents: List[Type]): Type = tag match { + case REFINEDtpe => RefinedType(parents, symScope(clazz), clazz) + case CLASSINFOtpe => ClassInfoType(parents, symScope(clazz), clazz) + } + + // We're stuck with the order types are pickled in, but with judicious use + // of named parameters we can recapture a declarative flavor in a few cases. + // But it's still a rat's nest of adhockery. (tag: @switch) match { - case NOtpe => - NoType - case NOPREFIXtpe => - NoPrefix - case THIStpe => - ThisType(readSymbolRef()) - case SINGLEtpe => - SingleType(readTypeRef(), readSymbolRef()) // !!! was singleType - case SUPERtpe => - val thistpe = readTypeRef() - val supertpe = readTypeRef() - SuperType(thistpe, supertpe) - case CONSTANTtpe => - ConstantType(readConstantRef()) - case TYPEREFtpe => - val pre = readTypeRef() - val sym = readSymbolRef() - val args = until(end, readTypeRef) - TypeRef(pre, sym, args) - case TYPEBOUNDStpe => - TypeBounds(readTypeRef(), readTypeRef()) - case REFINEDtpe => - val clazz = readSymbolRef() - RefinedType(until(end, readTypeRef), symScope(clazz), clazz) - case CLASSINFOtpe => - val clazz = readSymbolRef() - ClassInfoType(until(end, readTypeRef), symScope(clazz), clazz) - case METHODtpe | IMPLICITMETHODtpe => - val restpe = readTypeRef() - val params = until(end, readSymbolRef) - // if the method is overloaded, the params cannot be determined (see readSymbol) => return NoType. - // Only happen for trees, "case Apply" in readTree() takes care of selecting the correct - // alternative after parsing the arguments. - if (params.contains(NoSymbol) || restpe == NoType) NoType - else MethodType(params, restpe) - case POLYtpe => - val restpe = readTypeRef() - val typeParams = until(end, readSymbolRef) - if (typeParams.nonEmpty) { - // NMT_TRANSITION: old class files denoted a polymorphic nullary method as PolyType(tps, restpe), we now require PolyType(tps, NullaryMethodType(restpe)) - // when a type of kind * is expected (forceProperType is true), we know restpe should be wrapped in a NullaryMethodType (if it wasn't suitably wrapped yet) - def transitionNMT(restpe: Type) = { - val resTpeCls = restpe.getClass.toString // what's uglier than isInstanceOf? right! -- isInstanceOf does not work since the concrete types are defined in the compiler (not in scope here) - if(forceProperType /*&& pickleformat < 2.9 */ && !(resTpeCls.endsWith("MethodType"))) { assert(!resTpeCls.contains("ClassInfoType")) - NullaryMethodType(restpe) } - else restpe - } - PolyType(typeParams, transitionNMT(restpe)) - } - else - NullaryMethodType(restpe) - case EXISTENTIALtpe => - val restpe = readTypeRef() - newExistentialType(until(end, readSymbolRef), restpe) - - case ANNOTATEDtpe => - var typeRef = readNat() - val selfsym = if (isSymbolRef(typeRef)) { - val s = at(typeRef, readSymbol) - typeRef = readNat() - s - } else NoSymbol // selfsym can go. - val tp = at(typeRef, () => readType(forceProperType)) // NMT_TRANSITION - val annots = until(end, readAnnotationRef) - if (selfsym == NoSymbol) AnnotatedType(annots, tp, selfsym) - else tp - case _ => - noSuchTypeTag(tag, end) + case NOtpe => NoType + case NOPREFIXtpe => NoPrefix + case THIStpe => ThisType(readSymbolRef()) + case SINGLEtpe => SingleType(readTypeRef(), readSymbolRef()) + case SUPERtpe => SuperType(readTypeRef(), readTypeRef()) + case CONSTANTtpe => ConstantType(readConstantRef()) + case TYPEREFtpe => TypeRef(readTypeRef(), readSymbolRef(), readTypes()) + case TYPEBOUNDStpe => TypeBounds(readTypeRef(), readTypeRef()) + case REFINEDtpe | CLASSINFOtpe => CompoundType(readSymbolRef(), readTypes()) + case METHODtpe => MethodTypeRef(readTypeRef(), readSymbols()) + case POLYtpe => PolyOrNullaryType(readTypeRef(), readSymbols()) + case EXISTENTIALtpe => ExistentialType(underlying = readTypeRef(), quantified = readSymbols()) + case ANNOTATEDtpe => AnnotatedType(underlying = readTypeRef(), annotations = readAnnots(), selfsym = NoSymbol) } } @@ -483,9 +451,7 @@ abstract class UnPickler { * the symbol it requests. Called at top-level, for all * (symbol, annotInfo) entries. */ protected def readSymbolAnnotation() { - val tag = readByte() - if (tag != SYMANNOT) - errorBadSignature("symbol annotation expected ("+ tag +")") + expect(SYMANNOT, "symbol annotation expected") val end = readEnd() val target = readSymbolRef() target.addAnnotation(readAnnotationInfo(end)) @@ -501,260 +467,100 @@ abstract class UnPickler { readAnnotationInfo(end) } - /* Read an abstract syntax tree */ - protected def readTree(): Tree = { - val outerTag = readByte() - if (outerTag != TREE) - errorBadSignature("tree expected (" + outerTag + ")") - val end = readEnd() - val tag = readByte() - val tpe = if (tag == EMPTYtree) NoType else readTypeRef() - - // Set by the three functions to follow. If symbol is non-null - // after the new tree 't' has been created, t has its Symbol - // set to symbol; and it always has its Type set to tpe. - var symbol: Symbol = null - var mods: Modifiers = null - var name: Name = null - - /* Read a Symbol, Modifiers, and a Name */ - def setSymModsName() { - symbol = readSymbolRef() - mods = readModifiersRef() - name = readNameRef() + private def readNonEmptyTree(tag: Int, end: Int): Tree = { + @inline def all[T](body: => T): List[T] = until(end, () => body) + @inline def rep[T](body: => T): List[T] = times(readNat(), () => body) + + // !!! What is this doing here? + def fixApply(tree: Apply, tpe: Type): Apply = { + val Apply(fun, args) = tree + if (fun.symbol.isOverloaded) { + fun setType fun.symbol.info + inferMethodAlternative(fun, args map (_.tpe), tpe) + } + tree } - /* Read a Symbol and a Name */ - def setSymName() { - symbol = readSymbolRef() - name = readNameRef() + def ref() = readTreeRef() + def caseRef() = readCaseDefRef() + def modsRef() = readModifiersRef() + def implRef() = readTemplateRef() + def nameRef() = readNameRef() + def tparamRef() = readTypeDefRef() + def vparamRef() = readValDefRef() + def constRef() = readConstantRef() + def idRef() = readIdentRef() + def termNameRef() = readNameRef().toTermName + def typeNameRef() = readNameRef().toTypeName + def refTreeRef() = ref() match { + case t: RefTree => t + case t => errorBadSignature("RefTree expected, found " + t.shortClass) } - /* Read a Symbol */ - def setSym() { - symbol = readSymbolRef() + def selectorsRef() = all(ImportSelector(nameRef(), -1, nameRef(), -1)) + + /** A few of the most popular trees have been pulled to the top for + * switch efficiency purposes. + */ + def readTree(tpe: Type): Tree = (tag: @switch) match { + case IDENTtree => Ident(nameRef) + case SELECTtree => Select(ref, nameRef) + case APPLYtree => fixApply(Apply(ref, all(ref)), tpe) // !!! + case BINDtree => Bind(nameRef, ref) + case BLOCKtree => all(ref) match { case stats :+ expr => Block(stats, expr) } + case IFtree => If(ref, ref, ref) + case LITERALtree => Literal(constRef) + case TYPEAPPLYtree => TypeApply(ref, all(ref)) + case TYPEDtree => Typed(ref, ref) + case ALTERNATIVEtree => Alternative(all(ref)) + case ANNOTATEDtree => Annotated(ref, ref) + case APPLIEDTYPEtree => AppliedTypeTree(ref, all(ref)) + case APPLYDYNAMICtree => ApplyDynamic(ref, all(ref)) + case ARRAYVALUEtree => ArrayValue(ref, all(ref)) + case ASSIGNtree => Assign(ref, ref) + case CASEtree => CaseDef(ref, ref, ref) + case CLASStree => ClassDef(modsRef, typeNameRef, rep(tparamRef), implRef) + case COMPOUNDTYPEtree => CompoundTypeTree(implRef) + case DEFDEFtree => DefDef(modsRef, termNameRef, rep(tparamRef), rep(rep(vparamRef)), ref, ref) + case EXISTENTIALTYPEtree => ExistentialTypeTree(ref, all(ref)) + case FUNCTIONtree => Function(rep(vparamRef), ref) + case IMPORTtree => Import(ref, selectorsRef) + case LABELtree => LabelDef(termNameRef, rep(idRef), ref) + case MATCHtree => Match(ref, all(caseRef)) + case MODULEtree => ModuleDef(modsRef, termNameRef, implRef) + case NEWtree => New(ref) + case PACKAGEtree => PackageDef(refTreeRef, all(ref)) + case RETURNtree => Return(ref) + case SELECTFROMTYPEtree => SelectFromTypeTree(ref, typeNameRef) + case SINGLETONTYPEtree => SingletonTypeTree(ref) + case STARtree => Star(ref) + case SUPERtree => Super(ref, typeNameRef) + case TEMPLATEtree => Template(rep(ref), vparamRef, all(ref)) + case THIStree => This(typeNameRef) + case THROWtree => Throw(ref) + case TREtree => Try(ref, rep(caseRef), ref) + case TYPEBOUNDStree => TypeBoundsTree(ref, ref) + case TYPEDEFtree => TypeDef(modsRef, typeNameRef, rep(tparamRef), ref) + case TYPEtree => TypeTree() + case UNAPPLYtree => UnApply(ref, all(ref)) + case VALDEFtree => ValDef(modsRef, termNameRef, ref, ref) + case _ => noSuchTreeTag(tag, end) } - val t = tag match { - case EMPTYtree => - EmptyTree - - case PACKAGEtree => - setSym() - val pid = readTreeRef().asInstanceOf[RefTree] - val stats = until(end, readTreeRef) - PackageDef(pid, stats) - - case CLASStree => - setSymModsName() - val impl = readTemplateRef() - val tparams = until(end, readTypeDefRef) - ClassDef(mods, name.toTypeName, tparams, impl) - - case MODULEtree => - setSymModsName() - ModuleDef(mods, name.toTermName, readTemplateRef()) - - case VALDEFtree => - setSymModsName() - val tpt = readTreeRef() - val rhs = readTreeRef() - ValDef(mods, name.toTermName, tpt, rhs) - - case DEFDEFtree => - setSymModsName() - val tparams = times(readNat(), readTypeDefRef) - val vparamss = times(readNat(), () => times(readNat(), readValDefRef)) - val tpt = readTreeRef() - val rhs = readTreeRef() - DefDef(mods, name.toTermName, tparams, vparamss, tpt, rhs) - - case TYPEDEFtree => - setSymModsName() - val rhs = readTreeRef() - val tparams = until(end, readTypeDefRef) - TypeDef(mods, name.toTypeName, tparams, rhs) - - case LABELtree => - setSymName() - val rhs = readTreeRef() - val params = until(end, readIdentRef) - LabelDef(name.toTermName, params, rhs) - - case IMPORTtree => - setSym() - val expr = readTreeRef() - val selectors = until(end, () => { - val from = readNameRef() - val to = readNameRef() - ImportSelector(from, -1, to, -1) - }) - - Import(expr, selectors) - - case TEMPLATEtree => - setSym() - val parents = times(readNat(), readTreeRef) - val self = readValDefRef() - val body = until(end, readTreeRef) - - Template(parents, self, body) - - case BLOCKtree => - val expr = readTreeRef() - val stats = until(end, readTreeRef) - Block(stats, expr) - - case CASEtree => - val pat = readTreeRef() - val guard = readTreeRef() - val body = readTreeRef() - CaseDef(pat, guard, body) - - case ALTERNATIVEtree => - Alternative(until(end, readTreeRef)) - - case STARtree => - Star(readTreeRef()) - - case BINDtree => - setSymName() - Bind(name, readTreeRef()) - - case UNAPPLYtree => - val fun = readTreeRef() - val args = until(end, readTreeRef) - UnApply(fun, args) - - case ARRAYVALUEtree => - val elemtpt = readTreeRef() - val trees = until(end, readTreeRef) - ArrayValue(elemtpt, trees) - - case FUNCTIONtree => - setSym() - val body = readTreeRef() - val vparams = until(end, readValDefRef) - Function(vparams, body) - - case ASSIGNtree => - val lhs = readTreeRef() - val rhs = readTreeRef() - Assign(lhs, rhs) - - case IFtree => - val cond = readTreeRef() - val thenp = readTreeRef() - val elsep = readTreeRef() - If(cond, thenp, elsep) - - case MATCHtree => - val selector = readTreeRef() - val cases = until(end, readCaseDefRef) - Match(selector, cases) - - case RETURNtree => - setSym() - Return(readTreeRef()) - - case TREtree => - val block = readTreeRef() - val finalizer = readTreeRef() - val catches = until(end, readCaseDefRef) - Try(block, catches, finalizer) - - case THROWtree => - Throw(readTreeRef()) - - case NEWtree => - New(readTreeRef()) - - case TYPEDtree => - val expr = readTreeRef() - val tpt = readTreeRef() - Typed(expr, tpt) - - case TYPEAPPLYtree => - val fun = readTreeRef() - val args = until(end, readTreeRef) - TypeApply(fun, args) - - case APPLYtree => - val fun = readTreeRef() - val args = until(end, readTreeRef) - if (fun.symbol.isOverloaded) { - fun.setType(fun.symbol.info) - inferMethodAlternative(fun, args map (_.tpe), tpe) - } - Apply(fun, args) - - case APPLYDYNAMICtree => - setSym() - val qual = readTreeRef() - val args = until(end, readTreeRef) - ApplyDynamic(qual, args) - - case SUPERtree => - setSym() - val qual = readTreeRef() - val mix = readTypeNameRef() - Super(qual, mix) - - case THIStree => - setSym() - This(readTypeNameRef()) - - case SELECTtree => - setSym() - val qualifier = readTreeRef() - val selector = readNameRef() - Select(qualifier, selector) - - case IDENTtree => - setSymName() - Ident(name) - - case LITERALtree => - Literal(readConstantRef()) - - case TYPEtree => - TypeTree() - - case ANNOTATEDtree => - val annot = readTreeRef() - val arg = readTreeRef() - Annotated(annot, arg) - - case SINGLETONTYPEtree => - SingletonTypeTree(readTreeRef()) - - case SELECTFROMTYPEtree => - val qualifier = readTreeRef() - val selector = readTypeNameRef() - SelectFromTypeTree(qualifier, selector) - - case COMPOUNDTYPEtree => - CompoundTypeTree(readTemplateRef()) - - case APPLIEDTYPEtree => - val tpt = readTreeRef() - val args = until(end, readTreeRef) - AppliedTypeTree(tpt, args) - - case TYPEBOUNDStree => - val lo = readTreeRef() - val hi = readTreeRef() - TypeBoundsTree(lo, hi) - - case EXISTENTIALTYPEtree => - val tpt = readTreeRef() - val whereClauses = until(end, readTreeRef) - ExistentialTypeTree(tpt, whereClauses) + val tpe = readTypeRef() + val sym = if (isTreeSymbolPickled(tag)) readSymbolRef() else null + val result = readTree(tpe) - case _ => - noSuchTreeTag(tag, end) - } + if (sym ne null) result setSymbol sym + result setType tpe + } - if (symbol == null) t setType tpe - else t setSymbol symbol setType tpe + /* Read an abstract syntax tree */ + protected def readTree(): Tree = { + expect(TREE, "tree expected") + val end = readEnd() + readByte() match { + case EMPTYtree => EmptyTree + case tag => readNonEmptyTree(tag, end) + } } def noSuchTreeTag(tag: Int, end: Int) = @@ -853,7 +659,7 @@ abstract class UnPickler { override def complete(sym: Symbol) : Unit = try { val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` if (p ne null) - enteringPhase(p) (sym setInfo tp) + slowButSafeEnteringPhase(p) (sym setInfo tp) if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) } @@ -871,7 +677,7 @@ abstract class UnPickler { super.complete(sym) var alias = at(j, readSymbol) if (alias.isOverloaded) - alias = enteringPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) + alias = slowButSafeEnteringPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) sym.asInstanceOf[TermSymbol].setAlias(alias) } diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index e21e95903b..28afd18fe0 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -33,23 +33,26 @@ abstract class MutableSettings extends AbsSettings { } } - def overrideObjects: BooleanSetting - def printtypes: BooleanSetting + def Xexperimental: BooleanSetting + def XfullLubs: BooleanSetting + def XnoPatmatAnalysis: BooleanSetting + def Xprintpos: BooleanSetting + def Yposdebug: BooleanSetting + def Yrangepos: BooleanSetting + def Yshowsymkinds: BooleanSetting + def breakCycles: BooleanSetting def debug: BooleanSetting + def developer: BooleanSetting def explaintypes: BooleanSetting - def verbose: BooleanSetting + def overrideObjects: BooleanSetting + def printtypes: BooleanSetting def uniqid: BooleanSetting - def Yshowsymkinds: BooleanSetting - def Yposdebug: BooleanSetting - def Yrangepos: BooleanSetting - def Xprintpos: BooleanSetting + def verbose: BooleanSetting + def Yrecursion: IntSetting def maxClassfileName: IntSetting - def Xexperimental: BooleanSetting - def XnoPatmatAnalysis: BooleanSetting - def XfullLubs: BooleanSetting - def breakCycles: BooleanSetting } + object MutableSettings { import scala.language.implicitConversions /** Support the common use case, `if (settings.debug) println("Hello, martin.")` */ diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala index 6fa536d84c..6b33aca025 100644 --- a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala +++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala @@ -251,8 +251,11 @@ private[internal] trait GlbLubs { else if (isNumericSubType(t2, t1)) t1 else IntTpe) - private val lubResults = new mutable.HashMap[(Depth, List[Type]), Type] - private val glbResults = new mutable.HashMap[(Depth, List[Type]), Type] + private val _lubResults = new mutable.HashMap[(Depth, List[Type]), Type] + def lubResults = _lubResults + + private val _glbResults = new mutable.HashMap[(Depth, List[Type]), Type] + def glbResults = _glbResults /** Given a list of types, finds all the base classes they have in * common, then returns a list of type constructors derived directly diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index 6532bce9f0..b60fecd66e 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -14,7 +14,8 @@ trait TypeComparers { private final val LogPendingSubTypesThreshold = TypeConstants.DefaultLogThreshhold - private val pendingSubTypes = new mutable.HashSet[SubTypePair] + private val _pendingSubTypes = new mutable.HashSet[SubTypePair] + def pendingSubTypes = _pendingSubTypes class SubTypePair(val tp1: Type, val tp2: Type) { override def hashCode = tp1.hashCode * 41 + tp2.hashCode @@ -33,7 +34,9 @@ trait TypeComparers { override def toString = tp1+" <:<? "+tp2 } - private var subsametypeRecursions: Int = 0 + private var _subsametypeRecursions: Int = 0 + def subsametypeRecursions = _subsametypeRecursions + def subsametypeRecursions_=(value: Int) = _subsametypeRecursions = value private def isUnifiable(pre1: Type, pre2: Type) = ( (isEligibleForPrefixUnification(pre1) || isEligibleForPrefixUnification(pre2)) @@ -100,17 +103,13 @@ trait TypeComparers { // isSameType1(tp1, tp2) // } - undoLog.lock() + val before = undoLog.log + var result = false try { - val before = undoLog.log - var result = false - try { - result = isSameType1(tp1, tp2) - } - finally if (!result) undoLog.undoTo(before) - result + result = isSameType1(tp1, tp2) } - finally undoLog.unlock() + finally if (!result) undoLog.undoTo(before) + result } finally { subsametypeRecursions -= 1 @@ -256,30 +255,27 @@ trait TypeComparers { // } // } - undoLog.lock() - try { - val before = undoLog.log - var result = false - - try result = { // if subtype test fails, it should not affect constraints on typevars - if (subsametypeRecursions >= LogPendingSubTypesThreshold) { - val p = new SubTypePair(tp1, tp2) - if (pendingSubTypes(p)) - false - else - try { - pendingSubTypes += p - isSubType1(tp1, tp2, depth) - } finally { - pendingSubTypes -= p - } - } else { - isSubType1(tp1, tp2, depth) - } - } finally if (!result) undoLog.undoTo(before) + val before = undoLog.log + var result = false + + try result = { // if subtype test fails, it should not affect constraints on typevars + if (subsametypeRecursions >= LogPendingSubTypesThreshold) { + val p = new SubTypePair(tp1, tp2) + if (pendingSubTypes(p)) + false + else + try { + pendingSubTypes += p + isSubType1(tp1, tp2, depth) + } finally { + pendingSubTypes -= p + } + } else { + isSubType1(tp1, tp2, depth) + } + } finally if (!result) undoLog.undoTo(before) - result - } finally undoLog.unlock() + result } finally { subsametypeRecursions -= 1 // XXX AM TODO: figure out when it is safe and needed to clear the log -- the commented approach below is too eager (it breaks #3281, #3866) diff --git a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala index fdfe376c18..e2159d30f5 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala @@ -13,34 +13,14 @@ private[internal] trait TypeConstraints { /** A log of type variable with their original constraints. Used in order * to undo constraints in the case of isSubType/isSameType failure. */ - lazy val undoLog = newUndoLog - - protected def newUndoLog = new UndoLog + private lazy val _undoLog = new UndoLog + def undoLog = _undoLog class UndoLog extends Clearable { private type UndoPairs = List[(TypeVar, TypeConstraint)] //OPT this method is public so we can do `manual inlining` var log: UndoPairs = List() - /* - * These two methods provide explicit locking mechanism that is overridden in SynchronizedUndoLog. - * - * The idea behind explicit locking mechanism is that all public methods that access mutable state - * will have to obtain the lock for their entire execution so both reads and writes can be kept in - * right order. Originally, that was achieved by overriding those public methods in - * `SynchronizedUndoLog` which was fine but expensive. The reason is that those public methods take - * thunk as argument and if we keep them non-final there's no way to make them inlined so thunks - * can go away. - * - * By using explicit locking we can achieve inlining. - * - * NOTE: They are made public for now so we can apply 'manual inlining' (copy&pasting into hot - * places implementation of `undo` or `undoUnless`). This should be changed back to protected - * once inliner is fixed. - */ - def lock(): Unit = () - def unlock(): Unit = () - // register with the auto-clearing cache manager perRunCaches.recordCache(this) @@ -64,23 +44,16 @@ private[internal] trait TypeConstraints { } def clear() { - lock() - try { - if (settings.debug) - self.log("Clearing " + log.size + " entries from the undoLog.") - log = Nil - } finally unlock() + if (settings.debug) + self.log("Clearing " + log.size + " entries from the undoLog.") + log = Nil } // `block` should not affect constraints on typevars def undo[T](block: => T): T = { - lock() - try { - val before = log - - try block - finally undoTo(before) - } finally unlock() + val before = log + try block + finally undoTo(before) } } diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index 7e98ac03d5..9a54ad8217 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -581,6 +581,7 @@ private[internal] trait TypeMaps { else if (!matchesPrefixAndClass(pre, clazz)(tparam.owner)) loop(nextBase.prefix, clazz.owner) else nextBase match { + case NoType => loop(NoType, clazz.owner) // backstop for SI-2797, must remove `SingletonType#isHigherKinded` and run pos/t2797.scala to get here. case applied @ TypeRef(_, _, _) => correspondingTypeArgument(classParam, applied) case ExistentialType(eparams, qtpe) => captureSkolems(eparams) ; loop(qtpe, clazz) case t => abort(s"$tparam in ${tparam.owner} cannot be instantiated from ${seenFromPrefix.widen}") @@ -593,10 +594,8 @@ private[internal] trait TypeMaps { // Since pre may be something like ThisType(A) where trait A { self: B => }, // we have to test the typeSymbol of the widened type, not pre.typeSymbol, or // B will not be considered. - private def matchesPrefixAndClass(pre: Type, clazz: Symbol)(candidate: Symbol) = pre.widen match { - case _: TypeVar => false - case wide => (clazz == candidate) && (wide.typeSymbol isSubClass clazz) - } + private def matchesPrefixAndClass(pre: Type, clazz: Symbol)(candidate: Symbol) = + (clazz == candidate) && (pre.widen.typeSymbol isSubClass clazz) // Whether the annotation tree currently being mapped over has had a This(_) node rewritten. private[this] var wroteAnnotation = false diff --git a/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala index 16929cca0f..ebc4394d25 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala @@ -10,7 +10,9 @@ private[internal] trait TypeToStrings { */ final val maxTostringRecursions = 50 - private var tostringRecursions = 0 + private var _tostringRecursions = 0 + def tostringRecursions = _tostringRecursions + def tostringRecursions_=(value: Int) = _tostringRecursions = value protected def typeToString(tpe: Type): String = if (tostringRecursions >= maxTostringRecursions) { diff --git a/src/reflect/scala/reflect/runtime/Gil.scala b/src/reflect/scala/reflect/runtime/Gil.scala new file mode 100644 index 0000000000..0edb1e5748 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/Gil.scala @@ -0,0 +1,25 @@ +package scala.reflect +package runtime + +private[reflect] trait Gil { + self: SymbolTable => + + // fixme... please... + // there are the following avenues of optimization we discussed with Roland: + // 1) replace PackageScope locks with ConcurrentHashMap, because PackageScope materializers seem to be idempotent + // 2) unlock unpickling completers by verifying that they are idempotent or moving non-idempotent parts + // 3) remove the necessity in global state for isSubType + private lazy val gil = new java.util.concurrent.locks.ReentrantLock + + @inline final def gilSynchronized[T](body: => T): T = { + if (isCompilerUniverse) body + else { + try { + gil.lock() + body + } finally { + gil.unlock() + } + } + } +} diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 1a15454500..1e2dd6b7d3 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -22,7 +22,7 @@ import ReflectionUtils.{staticSingletonInstance, innerSingletonInstance, scalacS import scala.language.existentials import scala.runtime.{ScalaRunTime, BoxesRunTime} -private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { thisUniverse: SymbolTable => +private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse with TwoWayCaches { thisUniverse: SymbolTable => private lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]() @@ -44,19 +44,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni trait JavaClassCompleter extends FlagAssigningCompleter - def init() = { - definitions.AnyValClass // force it. - - // establish root association to avoid cyclic dependency errors later - rootMirror.classToScala(classOf[java.lang.Object]).initialize - - // println("initializing definitions") - definitions.init() - } - - def runtimeMirror(cl: ClassLoader): Mirror = mirrors get cl match { - case Some(WeakReference(m)) => m - case _ => createMirror(rootMirror.RootClass, cl) + def runtimeMirror(cl: ClassLoader): Mirror = gilSynchronized { + mirrors get cl match { + case Some(WeakReference(m)) => m + case _ => createMirror(rootMirror.RootClass, cl) + } } /** The API of a mirror for a reflective universe */ @@ -69,6 +61,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni import definitions._ + override lazy val RootPackage = new RootPackage with SynchronizedTermSymbol + override lazy val RootClass = new RootClass with SynchronizedModuleClassSymbol + override lazy val EmptyPackage = new EmptyPackage with SynchronizedTermSymbol + override lazy val EmptyPackageClass = new EmptyPackageClass with SynchronizedModuleClassSymbol + /** The lazy type for root. */ override lazy val rootLoader = new LazyType with FlagAgnosticCompleter { @@ -689,7 +686,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni completeRest() } - def completeRest(): Unit = thisUniverse.synchronized { + def completeRest(): Unit = gilSynchronized { val tparams = clazz.rawInfo.typeParams val parents = try { @@ -894,7 +891,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * The Scala package with given fully qualified name. Unlike `packageNameToScala`, * this one bypasses the cache. */ - private[JavaMirrors] def makeScalaPackage(fullname: String): ModuleSymbol = { + private[JavaMirrors] def makeScalaPackage(fullname: String): ModuleSymbol = gilSynchronized { val split = fullname lastIndexOf '.' val ownerModule: ModuleSymbol = if (split > 0) packageNameToScala(fullname take split) else this.RootPackage @@ -1275,11 +1272,6 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case _ => abort(s"${sym}.enclosingRootClass = ${sym.enclosingRootClass}, which is not a RootSymbol") } - private lazy val syntheticCoreClasses: Map[(String, Name), Symbol] = { - def mapEntry(sym: Symbol): ((String, Name), Symbol) = (sym.owner.fullName, sym.name) -> sym - Map() ++ (definitions.syntheticCoreClasses map mapEntry) - } - /** 1. If `owner` is a package class (but not the empty package) and `name` is a term name, make a new package * <owner>.<name>, otherwise return NoSymbol. * Exception: If owner is root and a java class with given name exists, create symbol in empty package instead @@ -1289,20 +1281,20 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def missingHook(owner: Symbol, name: Name): Symbol = { if (owner.hasPackageFlag) { val mirror = mirrorThatLoaded(owner) - // todo. this makes toolbox tests pass, but it's a mere workaround for SI-5865 -// assert((owner.info decl name) == NoSymbol, s"already exists: $owner . $name") if (owner.isRootSymbol && mirror.tryJavaClass(name.toString).isDefined) return mirror.EmptyPackageClass.info decl name if (name.isTermName && !owner.isEmptyPackageClass) return mirror.makeScalaPackage( if (owner.isRootSymbol) name.toString else owner.fullName+"."+name) - syntheticCoreClasses get ((owner.fullName, name)) foreach { tsym => - // synthetic core classes are only present in root mirrors - // because Definitions.scala, which initializes and enters them, only affects rootMirror - // therefore we need to enter them manually for non-root mirrors - if (mirror ne thisUniverse.rootMirror) owner.info.decls enter tsym - return tsym - } + if (name == tpnme.AnyRef && owner.owner.isRoot && owner.name == tpnme.scala_) + // when we synthesize the scala.AnyRef symbol, we need to add it to the scope of the scala package + // the problem is that adding to the scope implies doing something like `owner.info.decls enter anyRef` + // which entails running a completer for the scala package + // which will try to unpickle the stuff in scala/package.class + // which will transitively load scala.AnyRef + // which doesn't exist yet, because it hasn't been added to the scope yet + // this missing hook ties the knot without introducing synchronization problems like before + return definitions.AnyRefClass } info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass) super.missingHook(owner, name) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index 9d37edbacd..54b75b8e5b 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -8,7 +8,7 @@ package runtime * * @contentDiagram hideNodes "*Api" "*Extractor" */ -class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.SymbolTable { self => +class JavaUniverse extends internal.SymbolTable with JavaUniverseForce with ReflectSetup with runtime.SymbolTable { self => override def inform(msg: String): Unit = log(msg) def picklerPhase = internal.SomePhase @@ -29,4 +29,74 @@ class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.S } with internal.TreeInfo init() + + // ======= Initialization of runtime reflection ======= + // + // This doc describes the carefully laid out sequence of actions used to initialize reflective universes. + // + // Before reading the text below, read up the section Mirrors in the reflection pre-SIP + // https://docs.google.com/document/d/1nAwSw4TmMplsIlzh2shYLUJ5mVh3wndDa1Zm1H6an9A/edit. + // Take an especially good look at Figure 2, because it illustrates fundamental principles underlying runtime reflection: + // 1) For each universe we have one mirror per classloader + // 2) Package symbols are per-mirror + // 3) Other symbols are per-universe, which means that a symbol (e.g. Seq on the picture) might be shared between multiple owners + // + // Main challenges that runtime reflection presents wrt initialization are: + // 1) Extravagant completion scheme that enters package members on-demand rather than a result of scanning a directory with class files. + // (That's a direct consequence of the fact that in general case we can't enumerate all classes in a classloader. + // As Paul rightfully mentioned, we could specialcase classloaders that point to filesystems, but that is left for future work). + // 2) Presence of synthetic symbols that aren't loaded by normal means (from classfiles) but are synthesized on-the-fly, + // and the necessity to propagate these synthetic symbols from rootMirror to other mirrors, + // complicated by the fact that such symbols depend on normal symbols (e.g. AnyRef depends on Object). + // 3) Necessity to remain thread-safe, which limits our options related to lazy initialization + // (E.g. we cannot use missingHook to enter synthetic symbols, because that's thread-unsafe). + // + // Directly addressing the challenge #3, we create all synthetic symbols fully in advance during init(). + // However, it's not that simple as just calling definitions.symbolsNotPresentInBytecode. + // Before doing that, we need to first initialize ObjectClass, then ScalaPackageClass, and only then deal with synthetics. + // Below you can find a detailed explanation for that. + // + // ### Why ScalaPackageClass? ### + // + // Forcing ScalaPackageClass first thing during startup is important, because syntheticCoreClasses such as AnyRefClass + // need to be entered into ScalaPackageClass, which entails calling ScalaPackageClass.info.decls.enter. + // If ScalaPackageClass isn't initialized by that moment, the following will happen for runtime reflection: + // 1) Initialization of ScalaPackageClass will trigger unpickling. + // 2) Unpickling will need to load some auxiliary types such as, for example, String. + // 3) To load String, runtime reflection will call mirrorDefining(classOf[String]). + // 4) This, in turn, will call runtimeMirror(classOf[String].getClassLoader). + // 5) For some classloader configurations, the resulting mirror will be different from rootMirror. + // 6) In that case, initialization of the resulting mirror will try to import definitions.syntheticCoreClasses into the mirror. + // 7) This will force all the lazy vals corresponding to syntheticCoreClasses. + // 8) By that time, the completer of ScalaPackageClass will have already called setInfo on ScalaPackageClass, so there won't be any stack overflow. + // + // So far so good, no crashes, no problems, right? Not quite. + // If forcing of ScalaPackageClass was called by a syntheticCoreClasses lazy val, + // then this lazy val will be entered twice: once during step 7 and once when returning from the original call. + // To avoid this we need to initialize ScalaPackageClass prior to other synthetics. + // + // ### Why ObjectClass? ### + // + // 1) As explained in JavaMirrors.missingHook, initialization of ScalaPackageClass critically depends on AnyRefClass. + // 2) AnyRefClass is defined as "lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe)", + // which means that initialization of AnyRefClass depends on ObjectClass. + // 3) ObjectClass is defined as "lazy val ObjectClass = getRequiredClass(sn.Object.toString)", + // which means that under some classloader configurations (see JavaMirrors.missingHook for more details) + // dereferencing ObjectClass might trigger an avalanche of initializations calling back into AnyRefClass + // while another AnyRefClass initializer is still on stack. + // 4) That will lead to AnyRefClass being entered two times (once when the recursive call returns and once when the original one returns) + // 5) That will crash PackageScope.enter that helpfully detects double-enters. + // + // Therefore, before initializing ScalaPackageClass, we must pre-initialize ObjectClass + def init() { + definitions.init() + + // workaround for http://groups.google.com/group/scala-internals/browse_thread/thread/97840ba4fd37b52e + // constructors are by definition single-threaded, so we initialize all lazy vals (and local object) in advance + // in order to avoid deadlocks later (e.g. one thread holds a global reflection lock and waits for definitions.Something to initialize, + // whereas another thread holds a definitions.Something initialization lock and needs a global reflection lock to complete the initialization) + + // TODO Convert this into a macro + force() + } } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala new file mode 100644 index 0000000000..8fd58c42be --- /dev/null +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -0,0 +1,496 @@ +// Generated Code, validated by run/t6240-universe-code-gen.scala +package scala.reflect +package runtime + +trait JavaUniverseForce { self: runtime.JavaUniverse => + def force() { + Literal(Constant(42)).duplicate + nme.flattenedName() + nme.raw + WeakTypeTag + TypeTag + TypeTag.Byte.tpe + TypeTag.Short.tpe + TypeTag.Char.tpe + TypeTag.Int.tpe + TypeTag.Long.tpe + TypeTag.Float.tpe + TypeTag.Double.tpe + TypeTag.Boolean.tpe + TypeTag.Unit.tpe + TypeTag.Any.tpe + TypeTag.AnyVal.tpe + TypeTag.AnyRef.tpe + TypeTag.Object.tpe + TypeTag.Nothing.tpe + TypeTag.Null.tpe + + this.settings + this.treeInfo + // inaccessible: this.scala$reflect$runtime$Gil$$gil + // inaccessible: this.uniqueLock + // inaccessible: this._skolemizationLevel + // inaccessible: this._undoLog + // inaccessible: this._intersectionWitness + // inaccessible: this._volatileRecursions + // inaccessible: this._pendingVolatiles + // inaccessible: this._subsametypeRecursions + // inaccessible: this._pendingSubTypes + // inaccessible: this._basetypeRecursions + // inaccessible: this._pendingBaseTypes + // inaccessible: this._lubResults + // inaccessible: this._glbResults + // inaccessible: this._indent + // inaccessible: this._tostringRecursions + // inaccessible: this.atomicIds + // inaccessible: this.atomicExistentialIds + // inaccessible: this._recursionTable + // inaccessible: this.mirrors + this.rootMirror + this.treeBuild + // inaccessible: this.SimpleNameOrdering + this.traceSymbols + this.perRunCaches + this.FixedMirrorTreeCreator + this.FixedMirrorTypeCreator + this.BackquotedIdentifierAttachment + this.CompoundTypeTreeOriginalAttachment + this.noPrint + this.typeDebug + // inaccessible: this.maxFree + this.Range + // inaccessible: this.posAssigner + this.ConsoleWriter + this.RefTree + this.PackageDef + this.ClassDef + this.ModuleDef + this.ValOrDefDef + this.ValDef + this.DefDef + this.TypeDef + this.LabelDef + this.ImportSelector + this.Import + this.Template + this.Block + this.CaseDef + this.Alternative + this.Star + this.Bind + this.UnApply + this.ArrayValue + this.Function + this.Assign + this.AssignOrNamedArg + this.If + this.Match + this.Return + this.Try + this.Throw + this.New + this.Typed + this.TypeApply + this.Apply + this.ApplyDynamic + this.Super + this.This + this.Select + this.Ident + this.ReferenceToBoxed + this.Literal + this.Annotated + this.SingletonTypeTree + this.SelectFromTypeTree + this.CompoundTypeTree + this.AppliedTypeTree + this.TypeBoundsTree + this.ExistentialTypeTree + this.TypeTree + this.Modifiers + this.EmptyTree + this.noSelfType + this.pendingSuperCall + this.emptyValDef + this.EmptyTreeTypeSubstituter + // inaccessible: this.duplicator + this.UnmappableAnnotArg + this.LiteralAnnotArg + this.ArrayAnnotArg + this.NestedAnnotArg + this.ScalaSigBytes + this.AnnotationInfo + this.Annotation + this.UnmappableAnnotation + this.ErroneousAnnotation + this.ThrownException + // inaccessible: this.compactify + this.tpnme + this.fulltpnme + this.binarynme + this.nme + this.sn + this.Constant + this.definitions + this.LookupSucceeded + this.LookupAmbiguous + this.LookupInaccessible + this.LookupNotFound + this.Scope + this.EmptyScope + this.Flag + this.KindErrors + this.Kind + this.ProperTypeKind + this.TypeConKind + this.inferKind + // inaccessible: this.substTypeMapCache + this.UnmappableTree + this.ErrorType + this.WildcardType + this.BoundedWildcardType + this.NoType + this.NoPrefix + this.ThisType + this.SingleType + this.SuperType + this.TypeBounds + this.CompoundType + this.baseClassesCycleMonitor + this.RefinedType + this.ClassInfoType + this.ConstantType + this.TypeRef + this.MethodType + this.NullaryMethodType + this.PolyType + this.ExistentialType + this.OverloadedType + this.AntiPolyType + this.HasTypeMember + this.ArrayTypeRef + this.TypeVar + this.AnnotatedType + this.StaticallyAnnotatedType + this.NamedType + this.RepeatedType + this.ErasedValueType + this.GenPolyType + this.unwrapToClass + this.unwrapToStableClass + this.unwrapWrapperTypes + this.RecoverableCyclicReference + // inaccessible: this._undoLog + // inaccessible: this.numericLoBound + // inaccessible: this.numericHiBound + this.TypeConstraint + this.normalizeAliases + this.dropSingletonType + this.abstractTypesToBounds + this.dropIllegalStarTypes + this.IsDependentCollector + this.ApproximateDependentMap + this.wildcardToTypeVarMap + this.typeVarToOriginMap + this.ErroneousCollector + this.adaptToNewRunMap + // inaccessible: this.commonOwnerMapObj + this.SymbolKind + this.NoSymbol + this.CyclicReference + // inaccessible: this.TypeHistory + this.TermName + this.TypeName + this.BooleanFlag + this.WeakTypeTag + this.TypeTag + this.Expr + this.NoMods + definitions.JavaLangPackage + definitions.JavaLangPackageClass + definitions.ScalaPackage + definitions.ScalaPackageClass + definitions.RuntimePackage + definitions.RuntimePackageClass + definitions.AnyClass + definitions.AnyRefClass + definitions.ObjectClass + definitions.AnyRefTpe + definitions.AnyTpe + definitions.AnyValTpe + definitions.BoxedUnitTpe + definitions.NothingTpe + definitions.NullTpe + definitions.ObjectTpe + definitions.SerializableTpe + definitions.StringTpe + definitions.ThrowableTpe + definitions.ConstantTrue + definitions.ConstantFalse + definitions.ConstantNull + definitions.AnyValClass + definitions.RuntimeNothingClass + definitions.RuntimeNullClass + definitions.NothingClass + definitions.NullClass + definitions.ClassCastExceptionClass + definitions.IndexOutOfBoundsExceptionClass + definitions.InvocationTargetExceptionClass + definitions.MatchErrorClass + definitions.NonLocalReturnControlClass + definitions.NullPointerExceptionClass + definitions.ThrowableClass + definitions.UninitializedErrorClass + definitions.UninitializedFieldConstructor + definitions.PartialFunctionClass + definitions.AbstractPartialFunctionClass + definitions.SymbolClass + definitions.StringClass + definitions.StringModule + definitions.ClassClass + definitions.DynamicClass + definitions.SysPackage + definitions.UnqualifiedModules + definitions.UnqualifiedOwners + definitions.PredefModule + definitions.SpecializableModule + definitions.GroupOfSpecializable + definitions.ScalaRunTimeModule + definitions.SymbolModule + definitions.Symbol_apply + definitions.StringAddClass + definitions.ArrowAssocClass + definitions.StringAdd_$plus + definitions.ScalaNumberClass + definitions.TraitSetterAnnotationClass + definitions.DelayedInitClass + definitions.TypeConstraintClass + definitions.SingletonClass + definitions.SerializableClass + definitions.JavaSerializableClass + definitions.ComparableClass + definitions.JavaCloneableClass + definitions.JavaNumberClass + definitions.RemoteInterfaceClass + definitions.RemoteExceptionClass + definitions.ByNameParamClass + definitions.JavaRepeatedParamClass + definitions.RepeatedParamClass + definitions.ExprClassOf + definitions.ConsClass + definitions.IteratorClass + definitions.IterableClass + definitions.ListClass + definitions.SeqClass + definitions.StringBuilderClass + definitions.TraversableClass + definitions.ListModule + definitions.List_apply + definitions.NilModule + definitions.SeqModule + definitions.ArrayModule + definitions.ArrayModule_overloadedApply + definitions.ArrayClass + definitions.Array_apply + definitions.Array_update + definitions.Array_length + definitions.Array_clone + definitions.SoftReferenceClass + definitions.MethodClass + definitions.EmptyMethodCacheClass + definitions.MethodCacheClass + definitions.ScalaXmlTopScope + definitions.ScalaXmlPackage + definitions.ReflectPackage + definitions.ReflectApiPackage + definitions.ReflectRuntimePackage + definitions.PartialManifestClass + definitions.PartialManifestModule + definitions.FullManifestClass + definitions.FullManifestModule + definitions.OptManifestClass + definitions.NoManifest + definitions.TreesClass + definitions.TreesTreeType + definitions.TreeType + definitions.SubtreeType + definitions.ExprsClass + definitions.ExprClass + definitions.ClassTagModule + definitions.ClassTagClass + definitions.TypeTagsClass + definitions.WeakTypeTagClass + definitions.WeakTypeTagModule + definitions.TypeTagClass + definitions.TypeTagModule + definitions.ApiUniverseClass + definitions.JavaUniverseClass + definitions.MirrorClass + definitions.TypeCreatorClass + definitions.TreeCreatorClass + definitions.LiftableClass + definitions.MacroClass + definitions.MacroContextClass + definitions.MacroImplAnnotation + definitions.StringContextClass + definitions.QuasiquoteClass + definitions.QuasiquoteClass_api + definitions.QuasiquoteClass_api_apply + definitions.QuasiquoteClass_api_unapply + definitions.ScalaSignatureAnnotation + definitions.ScalaLongSignatureAnnotation + definitions.OptionClass + definitions.OptionModule + definitions.Option_apply + definitions.SomeClass + definitions.NoneModule + definitions.SomeModule + definitions.VarArityClass + definitions.ProductClass + definitions.TupleClass + definitions.FunctionClass + definitions.AbstractFunctionClass + definitions.ProductRootClass + definitions.Any_$eq$eq + definitions.Any_$bang$eq + definitions.Any_equals + definitions.Any_hashCode + definitions.Any_toString + definitions.Any_$hash$hash + definitions.Any_getClass + definitions.Any_isInstanceOf + definitions.Any_asInstanceOf + definitions.primitiveGetClassMethods + definitions.getClassMethods + definitions.Object_$hash$hash + definitions.Object_$eq$eq + definitions.Object_$bang$eq + definitions.Object_eq + definitions.Object_ne + definitions.Object_isInstanceOf + definitions.Object_asInstanceOf + definitions.Object_synchronized + definitions.String_$plus + definitions.ObjectRefClass + definitions.VolatileObjectRefClass + definitions.RuntimeStaticsModule + definitions.BoxesRunTimeModule + definitions.BoxesRunTimeClass + definitions.BoxedNumberClass + definitions.BoxedCharacterClass + definitions.BoxedBooleanClass + definitions.BoxedByteClass + definitions.BoxedShortClass + definitions.BoxedIntClass + definitions.BoxedLongClass + definitions.BoxedFloatClass + definitions.BoxedDoubleClass + definitions.Boxes_isNumberOrBool + definitions.Boxes_isNumber + definitions.BoxedUnitClass + definitions.BoxedUnitModule + definitions.AnnotationClass + definitions.ClassfileAnnotationClass + definitions.StaticAnnotationClass + definitions.BridgeClass + definitions.ElidableMethodClass + definitions.ImplicitNotFoundClass + definitions.MigrationAnnotationClass + definitions.ScalaStrictFPAttr + definitions.SwitchClass + definitions.TailrecClass + definitions.VarargsClass + definitions.uncheckedStableClass + definitions.uncheckedVarianceClass + definitions.BeanPropertyAttr + definitions.BooleanBeanPropertyAttr + definitions.CompileTimeOnlyAttr + definitions.DeprecatedAttr + definitions.DeprecatedNameAttr + definitions.DeprecatedInheritanceAttr + definitions.DeprecatedOverridingAttr + definitions.NativeAttr + definitions.RemoteAttr + definitions.ScalaInlineClass + definitions.ScalaNoInlineClass + definitions.SerialVersionUIDAttr + definitions.SerialVersionUIDAnnotation + definitions.SpecializedClass + definitions.ThrowsClass + definitions.TransientAttr + definitions.UncheckedClass + definitions.UncheckedBoundsClass + definitions.UnspecializedClass + definitions.VolatileAttr + definitions.BeanGetterTargetClass + definitions.BeanSetterTargetClass + definitions.FieldTargetClass + definitions.GetterTargetClass + definitions.ParamTargetClass + definitions.SetterTargetClass + definitions.ObjectTargetClass + definitions.ClassTargetClass + definitions.MethodTargetClass + definitions.LanguageFeatureAnnot + definitions.languageFeatureModule + definitions.experimentalModule + definitions.MacrosFeature + definitions.DynamicsFeature + definitions.PostfixOpsFeature + definitions.ReflectiveCallsFeature + definitions.ImplicitConversionsFeature + definitions.HigherKindsFeature + definitions.ExistentialsFeature + definitions.metaAnnotations + definitions.AnnotationDefaultAttr + definitions.isUnbox + definitions.isBox + definitions.isPhantomClass + definitions.syntheticCoreClasses + definitions.syntheticCoreMethods + definitions.hijackedCoreClasses + definitions.symbolsNotPresentInBytecode + definitions.isPossibleSyntheticParent + // inaccessible: definitions.boxedValueClassesSet + definitions.abbrvTag + definitions.numericWeight + definitions.boxedModule + definitions.boxedClass + definitions.refClass + definitions.volatileRefClass + definitions.boxMethod + definitions.unboxMethod + definitions.UnitClass + definitions.ByteClass + definitions.ShortClass + definitions.CharClass + definitions.IntClass + definitions.LongClass + definitions.FloatClass + definitions.DoubleClass + definitions.BooleanClass + definitions.Boolean_and + definitions.Boolean_or + definitions.Boolean_not + definitions.UnitTpe + definitions.ByteTpe + definitions.ShortTpe + definitions.CharTpe + definitions.IntTpe + definitions.LongTpe + definitions.FloatTpe + definitions.DoubleTpe + definitions.BooleanTpe + definitions.ScalaNumericValueClasses + definitions.ScalaValueClassesNoUnit + definitions.ScalaValueClasses + + + erasure.GenericArray + erasure.scalaErasure + erasure.specialScalaErasure + erasure.javaErasure + erasure.verifiedJavaErasure + erasure.boxingErasure + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index d65e9329ed..11db83d7d5 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -34,16 +34,17 @@ private[reflect] class Settings extends MutableSettings { val XfullLubs = new BooleanSetting(false) val XnoPatmatAnalysis = new BooleanSetting(false) val Xprintpos = new BooleanSetting(false) - val Yshowsymkinds = new BooleanSetting(false) val Yposdebug = new BooleanSetting(false) val Yrangepos = new BooleanSetting(false) + val Yshowsymkinds = new BooleanSetting(false) + val breakCycles = new BooleanSetting(false) val debug = new BooleanSetting(false) + val developer = new BooleanSetting(false) val explaintypes = new BooleanSetting(false) val overrideObjects = new BooleanSetting(false) val printtypes = new BooleanSetting(false) val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) - val breakCycles = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 3e01a6df02..c6059ac402 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -17,37 +17,13 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => * is found, a package is created instead. */ class TopClassCompleter(clazz: Symbol, module: Symbol) extends SymLoader with FlagAssigningCompleter { -// def makePackage() { -// println("wrong guess; making package "+clazz) -// val ptpe = newPackageType(module.moduleClass) -// for (sym <- List(clazz, module, module.moduleClass)) { -// sym setFlag Flags.PACKAGE -// sym setInfo ptpe -// } -// } - override def complete(sym: Symbol) = { debugInfo("completing "+sym+"/"+clazz.fullName) assert(sym == clazz || sym == module || sym == module.moduleClass) -// try { - enteringPhaseNotLaterThan(picklerPhase) { + slowButSafeEnteringPhaseNotLaterThan(picklerPhase) { val loadingMirror = mirrorThatLoaded(sym) val javaClass = loadingMirror.javaClass(clazz.javaClassName) loadingMirror.unpickleClass(clazz, module, javaClass) -// } catch { -// case ex: ClassNotFoundException => makePackage() -// case ex: NoClassDefFoundError => makePackage() - // Note: We catch NoClassDefFoundError because there are situations - // where a package and a class have the same name except for capitalization. - // It seems in this case the class is loaded even if capitalization differs - // but then a NoClassDefFound error is issued with a ("wrong name: ...") - // reason. (I guess this is a concession to Windows). - // The present behavior is a bit too forgiving, in that it masks - // all class load errors, not just wrong name errors. We should try - // to be more discriminating. To get on the right track simply delete - // the clause above and load a collection class such as collection.Iterable. - // You'll see an error that class `parallel` has the wrong name. -// } } } override def load(sym: Symbol) = complete(sym) @@ -91,12 +67,54 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => } } + + // Since runtime reflection doesn't have a luxury of enumerating all classes + // on the classpath, it has to materialize symbols for top-level definitions + // (packages, classes, objects) on demand. + // + // Someone asks us for a class named `foo.Bar`? Easy. Let's speculatively create + // a package named `foo` and then look up `newTypeName("bar")` in its decls. + // This lookup, implemented in `SymbolLoaders.PackageScope` tests the waters by + // trying to to `Class.forName("foo.Bar")` and then creates a ClassSymbol upon + // success (the whole story is a bit longer, but the rest is irrelevant here). + // + // That's all neat, but these non-deterministic mutations of the global symbol + // table give a lot of trouble in multi-threaded setting. One of the popular + // reflection crashes happens when multiple threads happen to trigger symbol + // materialization multiple times for the same symbol, making subsequent + // reflective operations stumble upon outrageous stuff like overloaded packages. + // + // Short of significantly changing SymbolLoaders I see no other way than just + // to slap a global lock on materialization in runtime reflection. class PackageScope(pkgClass: Symbol) extends Scope(initFingerPrints = -1L) // disable fingerprinting as we do not know entries beforehand with SynchronizedScope { assert(pkgClass.isType) - // disable fingerprinting as we do not know entries beforehand - private val negatives = mutable.Set[Name]() // Syncnote: Performance only, so need not be protected. - override def lookupEntry(name: Name): ScopeEntry = { + + // materializing multiple copies of the same symbol in PackageScope is a very popular bug + // this override does its best to guard against it + override def enter[T <: Symbol](sym: T): T = { + // workaround for SI-7728 + if (isCompilerUniverse) super.enter(sym) + else { + val existing = super.lookupEntry(sym.name) + // commented out to provide a hotfix for strange class files that javac sometimes emits + // see more details at: https://groups.google.com/forum/#!topic/scala-internals/hcnUFk75MgQ + // assert(existing == null || existing.sym.isMethod, s"pkgClass = $pkgClass, sym = $sym, existing = $existing") + super.enter(sym) + } + } + + override def enterIfNew[T <: Symbol](sym: T): T = { + val existing = super.lookupEntry(sym.name) + if (existing == null) enter(sym) + else existing.sym.asInstanceOf[T] + } + + // package scopes need to synchronize on the GIL + // because lookupEntry might cause changes to the global symbol table + override def syncLockSynchronized[T](body: => T): T = gilSynchronized(body) + private val negatives = new mutable.HashSet[Name] + override def lookupEntry(name: Name): ScopeEntry = syncLockSynchronized { val e = super.lookupEntry(name) if (e != null) e @@ -119,8 +137,21 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => val module = origOwner.info decl name.toTermName assert(clazz != NoSymbol) assert(module != NoSymbol) - pkgClass.info.decls enter clazz - pkgClass.info.decls enter module + // currentMirror.mirrorDefining(cls) might side effect by entering symbols into pkgClass.info.decls + // therefore, even though in the beginning of this method, super.lookupEntry(name) returned null + // entering clazz/module now will result in a double-enter assertion in PackageScope.enter + // here's how it might happen + // 1) we are the rootMirror + // 2) cls.getClassLoader is different from our classloader + // 3) mirrorDefining(cls) looks up a mirror corresponding to that classloader and cannot find it + // 4) mirrorDefining creates a new mirror + // 5) that triggers Mirror.init() of the new mirror + // 6) that triggers definitions.syntheticCoreClasses + // 7) that might materialize symbols and enter them into our scope (because syntheticCoreClasses live in rootMirror) + // 8) now we come back here and try to enter one of the now entered symbols => BAM! + // therefore we use enterIfNew rather than just enter + enterIfNew(clazz) + enterIfNew(module) (clazz, module) } debugInfo(s"created $module/${module.moduleClass} in $pkgClass") diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala index bcd4d16cde..ddbf3bd629 100644 --- a/src/reflect/scala/reflect/runtime/SymbolTable.scala +++ b/src/reflect/scala/reflect/runtime/SymbolTable.scala @@ -9,7 +9,7 @@ import scala.reflect.internal.Flags._ * It can be used either from a reflexive universe (class scala.reflect.runtime.JavaUniverse), or else from * a runtime compiler that uses reflection to get a class information (class scala.tools.reflect.ReflectGlobal) */ -private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps { +private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps with Gil with ThreadLocalStorage { def info(msg: => String) = if (settings.verbose) println("[reflect-compiler] "+msg) diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index 6aa47a0405..c90901410a 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -14,20 +14,25 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable // BaseTypeSeqs override protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = - new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq + // only need to synchronize BaseTypeSeqs if they contain refined types + if (elems.filter(_.isInstanceOf[RefinedType]).nonEmpty) new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq + else new BaseTypeSeq(parents, elems) trait SynchronizedBaseTypeSeq extends BaseTypeSeq { - override def apply(i: Int): Type = synchronized { super.apply(i) } - override def rawElem(i: Int) = synchronized { super.rawElem(i) } - override def typeSymbol(i: Int): Symbol = synchronized { super.typeSymbol(i) } - override def toList: List[Type] = synchronized { super.toList } - override def copy(head: Type, offset: Int): BaseTypeSeq = synchronized { super.copy(head, offset) } - override def map(f: Type => Type): BaseTypeSeq = synchronized { super.map(f) } - override def exists(p: Type => Boolean): Boolean = synchronized { super.exists(p) } - override lazy val maxDepth = synchronized { maxDepthOfElems } - override def toString = synchronized { super.toString } - - override def lateMap(f: Type => Type): BaseTypeSeq = new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq + override def apply(i: Int): Type = gilSynchronized { super.apply(i) } + override def rawElem(i: Int) = gilSynchronized { super.rawElem(i) } + override def typeSymbol(i: Int): Symbol = gilSynchronized { super.typeSymbol(i) } + override def toList: List[Type] = gilSynchronized { super.toList } + override def copy(head: Type, offset: Int): BaseTypeSeq = gilSynchronized { super.copy(head, offset) } + override def map(f: Type => Type): BaseTypeSeq = gilSynchronized { super.map(f) } + override def exists(p: Type => Boolean): Boolean = gilSynchronized { super.exists(p) } + override lazy val maxDepth = gilSynchronized { maxDepthOfElems } + override def toString = gilSynchronized { super.toString } + + override def lateMap(f: Type => Type): BaseTypeSeq = + // only need to synchronize BaseTypeSeqs if they contain refined types + if (map(f).toList.filter(_.isInstanceOf[RefinedType]).nonEmpty) new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq + else new MappedBaseTypeSeq(this, f) } // Scopes @@ -36,15 +41,19 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope trait SynchronizedScope extends Scope { - override def isEmpty: Boolean = synchronized { super.isEmpty } - override def size: Int = synchronized { super.size } - override def enter[T <: Symbol](sym: T): T = synchronized { super.enter(sym) } - override def rehash(sym: Symbol, newname: Name) = synchronized { super.rehash(sym, newname) } - override def unlink(e: ScopeEntry) = synchronized { super.unlink(e) } - override def unlink(sym: Symbol) = synchronized { super.unlink(sym) } - override def lookupAll(name: Name) = synchronized { super.lookupAll(name) } - override def lookupEntry(name: Name) = synchronized { super.lookupEntry(name) } - override def lookupNextEntry(entry: ScopeEntry) = synchronized { super.lookupNextEntry(entry) } - override def toList: List[Symbol] = synchronized { super.toList } + // we can keep this lock fine-grained, because methods of Scope don't do anything extraordinary, which makes deadlocks impossible + // fancy subclasses of internal.Scopes#Scope should do synchronization themselves (e.g. see PackageScope for an example) + private lazy val syncLock = new Object + def syncLockSynchronized[T](body: => T): T = if (isCompilerUniverse) body else syncLock.synchronized { body } + override def isEmpty: Boolean = syncLockSynchronized { super.isEmpty } + override def size: Int = syncLockSynchronized { super.size } + override def enter[T <: Symbol](sym: T): T = syncLockSynchronized { super.enter(sym) } + override def rehash(sym: Symbol, newname: Name) = syncLockSynchronized { super.rehash(sym, newname) } + override def unlink(e: ScopeEntry) = syncLockSynchronized { super.unlink(e) } + override def unlink(sym: Symbol) = syncLockSynchronized { super.unlink(sym) } + override def lookupAll(name: Name) = syncLockSynchronized { super.lookupAll(name) } + override def lookupEntry(name: Name) = syncLockSynchronized { super.lookupEntry(name) } + override def lookupNextEntry(entry: ScopeEntry) = syncLockSynchronized { super.lookupNextEntry(entry) } + override def toList: List[Symbol] = syncLockSynchronized { super.toList } } } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index 98cad45db1..298d0ffebd 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -3,17 +3,23 @@ package reflect package runtime import scala.reflect.io.AbstractFile +import scala.collection.{ immutable, mutable } private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => - override protected def nextId() = synchronized { super.nextId() } + private lazy val atomicIds = new java.util.concurrent.atomic.AtomicInteger(0) + override protected def nextId() = atomicIds.incrementAndGet() - override protected def freshExistentialName(suffix: String) = - synchronized { super.freshExistentialName(suffix) } + private lazy val atomicExistentialIds = new java.util.concurrent.atomic.AtomicInteger(0) + override protected def nextExistentialId() = atomicExistentialIds.incrementAndGet() + + private lazy val _recursionTable = mkThreadLocalStorage(immutable.Map.empty[Symbol, Int]) + override def recursionTable = _recursionTable.get + override def recursionTable_=(value: immutable.Map[Symbol, Int]) = _recursionTable.set(value) // Set the fields which point companions at one another. Returns the module. override def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = - synchronized { super.connectModuleToClass(m, moduleClass) } + gilSynchronized { super.connectModuleToClass(m, moduleClass) } override def newFreeTermSymbol(name: TermName, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol = new FreeTermSymbol(name, value, origin) with SynchronizedTermSymbol initFlags flags @@ -25,35 +31,40 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb trait SynchronizedSymbol extends Symbol { - override def rawflags = synchronized { super.rawflags } - override def rawflags_=(x: Long) = synchronized { super.rawflags_=(x) } - - override def rawowner = synchronized { super.rawowner } - override def owner_=(owner: Symbol) = synchronized { super.owner_=(owner) } - - override def validTo = synchronized { super.validTo } - override def validTo_=(x: Period) = synchronized { super.validTo_=(x) } - - override def pos = synchronized { super.pos } - override def setPos(pos: Position): this.type = { synchronized { super.setPos(pos) }; this } - - override def privateWithin = synchronized { super.privateWithin } - override def privateWithin_=(sym: Symbol) = synchronized { super.privateWithin_=(sym) } - - override def info = synchronized { super.info } - override def info_=(info: Type) = synchronized { super.info_=(info) } - override def updateInfo(info: Type): Symbol = synchronized { super.updateInfo(info) } - override def rawInfo: Type = synchronized { super.rawInfo } - - override def typeParams: List[Symbol] = synchronized { super.typeParams } - - override def reset(completer: Type): this.type = synchronized { super.reset(completer) } - - override def infosString: String = synchronized { super.infosString } - - override def annotations: List[AnnotationInfo] = synchronized { super.annotations } - override def setAnnotations(annots: List[AnnotationInfo]): this.type = { synchronized { super.setAnnotations(annots) }; this } - + def gilSynchronizedIfNotInited[T](body: => T): T = { + if (isFullyInitialized) body + else gilSynchronized { body } + } + + override def validTo = gilSynchronizedIfNotInited { super.validTo } + override def info = gilSynchronizedIfNotInited { super.info } + override def rawInfo: Type = gilSynchronizedIfNotInited { super.rawInfo } + + override def typeParams: List[Symbol] = gilSynchronizedIfNotInited { + if (isCompilerUniverse) super.typeParams + else { + if (isMonomorphicType) Nil + else { + // analogously to the "info" getter, here we allow for two completions: + // one: sourceCompleter to LazyType, two: LazyType to completed type + if (validTo == NoPeriod) + rawInfo load this + if (validTo == NoPeriod) + rawInfo load this + + rawInfo.typeParams + } + } + } + override def unsafeTypeParams: List[Symbol] = gilSynchronizedIfNotInited { + if (isCompilerUniverse) super.unsafeTypeParams + else { + if (isMonomorphicType) Nil + else rawInfo.typeParams + } + } + + override def isStable: Boolean = gilSynchronized { super.isStable } // ------ creators ------------------------------------------------------------------- @@ -90,50 +101,38 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb override protected def createModuleSymbol(name: TermName, pos: Position, newFlags: Long): ModuleSymbol = new ModuleSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags - override protected def createPackageSymbol(name: TermName, pos: Position, newFlags: Long): ModuleSymbol = createModuleSymbol(name, pos, newFlags) + override protected def createPackageSymbol(name: TermName, pos: Position, newFlags: Long): ModuleSymbol = + createModuleSymbol(name, pos, newFlags) + + override protected def createValueParameterSymbol(name: TermName, pos: Position, newFlags: Long) = + new TermSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags - // TODO - // override protected def createValueParameterSymbol(name: TermName, pos: Position, newFlags: Long) - // override protected def createValueMemberSymbol(name: TermName, pos: Position, newFlags: Long) + override protected def createValueMemberSymbol(name: TermName, pos: Position, newFlags: Long) = + new TermSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags } // ------- subclasses --------------------------------------------------------------------- - trait SynchronizedTermSymbol extends TermSymbol with SynchronizedSymbol { - override def name_=(x: Name) = synchronized { super.name_=(x) } - override def rawname = synchronized { super.rawname } - override def referenced: Symbol = synchronized { super.referenced } - override def referenced_=(x: Symbol) = synchronized { super.referenced_=(x) } - } + trait SynchronizedTermSymbol extends SynchronizedSymbol trait SynchronizedMethodSymbol extends MethodSymbol with SynchronizedTermSymbol { - override def typeAsMemberOf(pre: Type): Type = synchronized { super.typeAsMemberOf(pre) } - override def paramss: List[List[Symbol]] = synchronized { super.paramss } - override def returnType: Type = synchronized { super.returnType } + // we can keep this lock fine-grained, because it's just a cache over asSeenFrom, which makes deadlocks impossible + // unfortunately we cannot elide this lock, because the cache depends on `pre` + private lazy val typeAsMemberOfLock = new Object + override def typeAsMemberOf(pre: Type): Type = gilSynchronizedIfNotInited { typeAsMemberOfLock.synchronized { super.typeAsMemberOf(pre) } } } + trait SynchronizedModuleSymbol extends ModuleSymbol with SynchronizedTermSymbol + trait SynchronizedTypeSymbol extends TypeSymbol with SynchronizedSymbol { - override def name_=(x: Name) = synchronized { super.name_=(x) } - override def rawname = synchronized { super.rawname } - override def typeConstructor: Type = synchronized { super.typeConstructor } - override def tpe_* : Type = synchronized { super.tpe_* } - override def tpeHK : Type = synchronized { super.tpeHK } + // unlike with typeConstructor, a lock is necessary here, because tpe calculation relies on + // temporarily assigning NoType to tpeCache to detect cyclic reference errors + private lazy val tpeLock = new Object + override def tpe_* : Type = gilSynchronizedIfNotInited { tpeLock.synchronized { super.tpe_* } } } - trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol { - override def associatedFile = synchronized { super.associatedFile } - override def associatedFile_=(f: AbstractFile) = synchronized { super.associatedFile_=(f) } - override def thisSym: Symbol = synchronized { super.thisSym } - override def thisType: Type = synchronized { super.thisType } - override def typeOfThis: Type = synchronized { super.typeOfThis } - override def typeOfThis_=(tp: Type) = synchronized { super.typeOfThis_=(tp) } - override def children = synchronized { super.children } - override def addChild(sym: Symbol) = synchronized { super.addChild(sym) } - } + trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol - trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol { - override def sourceModule = synchronized { super.sourceModule } - override def implicitMembers: Scope = synchronized { super.implicitMembers } - } + trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala index c0146167df..de78e527a7 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala @@ -2,8 +2,9 @@ package scala package reflect package runtime -import scala.collection.mutable.WeakHashMap -import java.lang.ref.WeakReference +import scala.collection.mutable +import java.lang.ref.{WeakReference => jWeakRef} +import scala.ref.{WeakReference => sWeakRef} import scala.reflect.internal.Depth /** This trait overrides methods in reflect.internal, bracketing @@ -14,9 +15,10 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa // No sharing of map objects: override protected def commonOwnerMap = new CommonOwnerMap - private object uniqueLock - - private val uniques = WeakHashMap[Type, WeakReference[Type]]() + // we can keep this lock fine-grained, because super.unique just updates the cache + // and, in particular, doesn't call any reflection APIs which makes deadlocks impossible + private lazy val uniqueLock = new Object + private val uniques = mutable.WeakHashMap[Type, jWeakRef[Type]]() override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { // we need to have weak uniques for runtime reflection // because unlike the normal compiler universe, reflective universe isn't organized in runs @@ -30,7 +32,7 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa val result = if (inCache.isDefined) inCache.get.get else null if (result ne null) result.asInstanceOf[T] else { - uniques(tp) = new WeakReference(tp) + uniques(tp) = new jWeakRef(tp) tp } } else { @@ -38,47 +40,50 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa } } - class SynchronizedUndoLog extends UndoLog { - private val actualLock = new java.util.concurrent.locks.ReentrantLock - - final override def lock(): Unit = actualLock.lock() - final override def unlock(): Unit = actualLock.unlock() - } + private lazy val _skolemizationLevel = mkThreadLocalStorage(0) + override def skolemizationLevel = _skolemizationLevel.get + override def skolemizationLevel_=(value: Int) = _skolemizationLevel.set(value) - override protected def newUndoLog = new SynchronizedUndoLog + private lazy val _undoLog = mkThreadLocalStorage(new UndoLog) + override def undoLog = _undoLog.get - override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = - synchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) } + private lazy val _intersectionWitness = mkThreadLocalStorage(perRunCaches.newWeakMap[List[Type], sWeakRef[Type]]()) + override def intersectionWitness = _intersectionWitness.get - private object subsametypeLock + private lazy val _volatileRecursions = mkThreadLocalStorage(0) + override def volatileRecursions = _volatileRecursions.get + override def volatileRecursions_=(value: Int) = _volatileRecursions.set(value) - override def isSameType(tp1: Type, tp2: Type): Boolean = - subsametypeLock.synchronized { super.isSameType(tp1, tp2) } + private lazy val _pendingVolatiles = mkThreadLocalStorage(new mutable.HashSet[Symbol]) + override def pendingVolatiles = _pendingVolatiles.get - override def isDifferentType(tp1: Type, tp2: Type): Boolean = - subsametypeLock.synchronized { super.isDifferentType(tp1, tp2) } + private lazy val _subsametypeRecursions = mkThreadLocalStorage(0) + override def subsametypeRecursions = _subsametypeRecursions.get + override def subsametypeRecursions_=(value: Int) = _subsametypeRecursions.set(value) - override def isSubType(tp1: Type, tp2: Type, depth: Depth): Boolean = - subsametypeLock.synchronized { super.isSubType(tp1, tp2, depth) } + private lazy val _pendingSubTypes = mkThreadLocalStorage(new mutable.HashSet[SubTypePair]) + override def pendingSubTypes = _pendingSubTypes.get - private object lubglbLock + private lazy val _basetypeRecursions = mkThreadLocalStorage(0) + override def basetypeRecursions = _basetypeRecursions.get + override def basetypeRecursions_=(value: Int) = _basetypeRecursions.set(value) - override def glb(ts: List[Type]): Type = - lubglbLock.synchronized { super.glb(ts) } + private lazy val _pendingBaseTypes = mkThreadLocalStorage(new mutable.HashSet[Type]) + override def pendingBaseTypes = _pendingBaseTypes.get - override def lub(ts: List[Type]): Type = - lubglbLock.synchronized { super.lub(ts) } + private lazy val _lubResults = mkThreadLocalStorage(new mutable.HashMap[(Depth, List[Type]), Type]) + override def lubResults = _lubResults.get - private object indentLock - - override protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { - indentLock.synchronized { super.explain(op, p, tp1, arg2) } - } + private lazy val _glbResults = mkThreadLocalStorage(new mutable.HashMap[(Depth, List[Type]), Type]) + override def glbResults = _glbResults.get - private object toStringLock + private lazy val _indent = mkThreadLocalStorage("") + override def indent = _indent.get + override def indent_=(value: String) = _indent.set(value) - override protected def typeToString(tpe: Type): String = - toStringLock.synchronized(super.typeToString(tpe)) + private lazy val _tostringRecursions = mkThreadLocalStorage(0) + override def tostringRecursions = _tostringRecursions.get + override def tostringRecursions_=(value: Int) = _tostringRecursions.set(value) /* The idea of caches is as follows. * When in reflexive mode, a cache is either null, or one sentinal @@ -91,18 +96,18 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa */ override protected def defineUnderlyingOfSingleType(tpe: SingleType) = - tpe.synchronized { super.defineUnderlyingOfSingleType(tpe) } + gilSynchronized { super.defineUnderlyingOfSingleType(tpe) } override protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = - tpe.synchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } + gilSynchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } override protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = - tpe.synchronized { super.defineBaseClassesOfCompoundType(tpe) } + gilSynchronized { super.defineBaseClassesOfCompoundType(tpe) } override protected def defineParentsOfTypeRef(tpe: TypeRef) = - tpe.synchronized { super.defineParentsOfTypeRef(tpe) } + gilSynchronized { super.defineParentsOfTypeRef(tpe) } override protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = - tpe.synchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } + gilSynchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } } diff --git a/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala b/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala new file mode 100644 index 0000000000..5edc051461 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala @@ -0,0 +1,28 @@ +package scala.reflect +package runtime + +import java.lang.Thread._ + +private[reflect] trait ThreadLocalStorage { + self: SymbolTable => + + // see a discussion at scala-internals for more information: + // http://groups.google.com/group/scala-internals/browse_thread/thread/337ce68aa5e51f79 + trait ThreadLocalStorage[T] { def get: T; def set(newValue: T): Unit } + private class MyThreadLocalStorage[T](initialValue: => T) extends ThreadLocalStorage[T] { + // TODO: how do we use org.cliffc.high_scale_lib.NonBlockingHashMap here? + val values = new java.util.concurrent.ConcurrentHashMap[Thread, T]() + def get: T = { + if (values containsKey currentThread) values.get(currentThread) + else { + val value = initialValue + values.putIfAbsent(currentThread, value) + value + } + } + def set(newValue: T): Unit = { + values.put(currentThread, newValue) + } + } + @inline final def mkThreadLocalStorage[T](x: => T): ThreadLocalStorage[T] = new MyThreadLocalStorage(x) +} diff --git a/src/reflect/scala/reflect/runtime/TwoWayCaches.scala b/src/reflect/scala/reflect/runtime/TwoWayCaches.scala new file mode 100644 index 0000000000..6e2890e536 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/TwoWayCaches.scala @@ -0,0 +1,68 @@ +package scala.reflect +package runtime + +import scala.collection.mutable.WeakHashMap +import java.lang.ref.WeakReference + +/** A cache that maintains a bijection between Java reflection type `J` + * and Scala reflection type `S`. + * + * The cache is two-way weak (i.e. is powered by weak references), + * so that neither Java artifacts prevent Scala artifacts from being garbage collected, + * nor the other way around. + */ +private[runtime] trait TwoWayCaches { self: SymbolTable => + class TwoWayCache[J, S] { + + private val toScalaMap = new WeakHashMap[J, WeakReference[S]] + private val toJavaMap = new WeakHashMap[S, WeakReference[J]] + + def enter(j: J, s: S) = gilSynchronized { + // debugInfo("cached: "+j+"/"+s) + toScalaMap(j) = new WeakReference(s) + toJavaMap(s) = new WeakReference(j) + } + + private object SomeRef { + def unapply[T](optRef: Option[WeakReference[T]]): Option[T] = + if (optRef.nonEmpty) { + val result = optRef.get.get + if (result != null) Some(result) else None + } else None + } + + def toScala(key: J)(body: => S): S = gilSynchronized { + toScalaMap get key match { + case SomeRef(v) => + v + case _ => + val result = body + enter(key, result) + result + } + } + + def toJava(key: S)(body: => J): J = gilSynchronized { + toJavaMap get key match { + case SomeRef(v) => + v + case _ => + val result = body + enter(result, key) + result + } + } + + def toJavaOption(key: S)(body: => Option[J]): Option[J] = gilSynchronized { + toJavaMap get key match { + case SomeRef(v) => + Some(v) + case _ => + val result = body + for (value <- result) enter(value, key) + result + } + } + } +} + |