summaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala17
-rw-r--r--src/reflect/scala/reflect/api/Importers.scala2
-rw-r--r--src/reflect/scala/reflect/api/Trees.scala2
-rw-r--r--src/reflect/scala/reflect/api/TypeTags.scala2
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala181
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala19
-rw-r--r--src/reflect/scala/reflect/internal/FreshNames.scala8
-rw-r--r--src/reflect/scala/reflect/internal/Importers.scala3
-rw-r--r--src/reflect/scala/reflect/internal/Required.scala3
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala87
-rw-r--r--src/reflect/scala/reflect/internal/SymbolTable.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala147
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala89
-rw-r--r--src/reflect/scala/reflect/internal/pickling/UnPickler.scala12
-rw-r--r--src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala8
-rw-r--r--src/reflect/scala/reflect/internal/transform/PostErasure.scala19
-rw-r--r--src/reflect/scala/reflect/internal/transform/Transforms.scala11
-rw-r--r--src/reflect/scala/reflect/macros/Evals.scala6
-rw-r--r--src/reflect/scala/reflect/macros/Typers.scala6
-rw-r--r--src/reflect/scala/reflect/macros/Universe.scala5
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala284
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverse.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala3
-rw-r--r--src/reflect/scala/reflect/runtime/ReflectionUtils.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/SymbolLoaders.scala5
-rw-r--r--src/reflect/scala/reflect/runtime/SymbolTable.scala15
-rw-r--r--src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala117
-rw-r--r--src/reflect/scala/reflect/runtime/SynchronizedTypes.scala7
28 files changed, 744 insertions, 320 deletions
diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala
index 3bcf751ace..ec20a89a10 100644
--- a/src/reflect/scala/reflect/api/BuildUtils.scala
+++ b/src/reflect/scala/reflect/api/BuildUtils.scala
@@ -198,9 +198,9 @@ private[reflect] trait BuildUtils { self: Universe =>
val SyntacticFunction: SyntacticFunctionExtractor
trait SyntacticFunctionExtractor {
- def apply(params: List[Tree], body: Tree): Tree
+ def apply(params: List[Tree], body: Tree): Function
- def unapply(tree: Tree): Option[(List[ValDef], Tree)]
+ def unapply(tree: Function): Option[(List[ValDef], Tree)]
}
val SyntacticDefDef: SyntacticDefDefExtractor
@@ -248,6 +248,13 @@ private[reflect] trait BuildUtils { self: Universe =>
def unapply(tree: Tree): Option[(Tree)]
}
+ val SyntacticEmptyTypeTree: SyntacticEmptyTypeTreeExtractor
+
+ trait SyntacticEmptyTypeTreeExtractor {
+ def apply(): TypeTree
+ def unapply(tt: TypeTree): Boolean
+ }
+
val SyntacticFor: SyntacticForExtractor
val SyntacticForYield: SyntacticForExtractor
@@ -283,5 +290,11 @@ private[reflect] trait BuildUtils { self: Universe =>
def apply(name: Name, isBackquoted: Boolean = false): Ident
def unapply(tree: Ident): Option[(Name, Boolean)]
}
+
+ val SyntacticImport: SyntacticImportExtractor
+ trait SyntacticImportExtractor {
+ def apply(expr: Tree, selectors: List[Tree]): Import
+ def unapply(imp: Import): Some[(Tree, List[Tree])]
+ }
}
}
diff --git a/src/reflect/scala/reflect/api/Importers.scala b/src/reflect/scala/reflect/api/Importers.scala
index 5667d93e29..6539137cee 100644
--- a/src/reflect/scala/reflect/api/Importers.scala
+++ b/src/reflect/scala/reflect/api/Importers.scala
@@ -52,7 +52,7 @@ package api
* val imported = importer.importTree(tree)
*
* // after the tree is imported, it can be evaluated as usual
- * val tree = toolBox.resetAllAttrs(imported.duplicate)
+ * val tree = toolBox.untypecheck(imported.duplicate)
* val valueOfX = toolBox.eval(imported).asInstanceOf[T]
* ...
* }
diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala
index 83da5141b9..60e00ca5fd 100644
--- a/src/reflect/scala/reflect/api/Trees.scala
+++ b/src/reflect/scala/reflect/api/Trees.scala
@@ -1066,7 +1066,7 @@ trait Trees { self: Universe =>
* UnApply(
* // a dummy node that carries the type of unapplication to patmat
* // the <unapply-selector> here doesn't have an underlying symbol
- * // it only has a type assigned, therefore after `resetAllAttrs` this tree is no longer typeable
+ * // it only has a type assigned, therefore after `untypecheck` this tree is no longer typeable
* Apply(Select(Ident(Foo), newTermName("unapply")), List(Ident(newTermName("<unapply-selector>")))),
* // arguments of the unapply => nothing synthetic here
* List(Bind(newTermName("x"), Ident(nme.WILDCARD)))),
diff --git a/src/reflect/scala/reflect/api/TypeTags.scala b/src/reflect/scala/reflect/api/TypeTags.scala
index 7457910226..be76758224 100644
--- a/src/reflect/scala/reflect/api/TypeTags.scala
+++ b/src/reflect/scala/reflect/api/TypeTags.scala
@@ -134,7 +134,7 @@ import scala.language.implicitConversions
* reflection APIs provided by Java (for classes) and Scala (for types).</li>
*
* <li>'''Certain manifest operations(i.e., <:<, >:> and typeArguments) are not
- * supported.''' <br/>Instead, one culd use the reflection APIs provided by Java (for
+ * supported.''' <br/>Instead, one could use the reflection APIs provided by Java (for
* classes) and Scala (for types).</li>
*</ul>
*
diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala
index 6106339324..c5581601de 100644
--- a/src/reflect/scala/reflect/internal/BuildUtils.scala
+++ b/src/reflect/scala/reflect/internal/BuildUtils.scala
@@ -33,19 +33,19 @@ trait BuildUtils { self: SymbolTable =>
}
def newFreeTerm(name: String, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol =
- newFreeTermSymbol(newTermName(name), value, flags, origin)
+ newFreeTermSymbol(newTermName(name), value, flags, origin).markFlagsCompleted(mask = AllFlags)
def newFreeType(name: String, flags: Long = 0L, origin: String = null): FreeTypeSymbol =
- newFreeTypeSymbol(newTypeName(name), flags, origin)
+ newFreeTypeSymbol(newTypeName(name), flags, origin).markFlagsCompleted(mask = AllFlags)
def newNestedSymbol(owner: Symbol, name: Name, pos: Position, flags: Long, isClass: Boolean): Symbol =
- owner.newNestedSymbol(name, pos, flags, isClass)
+ owner.newNestedSymbol(name, pos, flags, isClass).markFlagsCompleted(mask = AllFlags)
def setAnnotations[S <: Symbol](sym: S, annots: List[AnnotationInfo]): S =
sym.setAnnotations(annots)
def setTypeSignature[S <: Symbol](sym: S, tpe: Type): S =
- sym.setTypeSignature(tpe)
+ sym.setTypeSignature(tpe).markAllCompleted()
def This(sym: Symbol): Tree = self.This(sym)
@@ -143,7 +143,7 @@ trait BuildUtils { self: SymbolTable =>
def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym
- def freshTermName(prefix: String): TermName = self.freshTermName(prefix)
+ def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX): TermName = self.freshTermName(prefix)
def freshTypeName(prefix: String): TypeName = self.freshTypeName(prefix)
@@ -435,16 +435,13 @@ trait BuildUtils { self: SymbolTable =>
}
object SyntacticFunction extends SyntacticFunctionExtractor {
- def apply(params: List[Tree], body: Tree): Tree = {
+ def apply(params: List[Tree], body: Tree): Function = {
val params0 :: Nil = mkParam(params :: Nil, PARAM)
require(params0.forall { _.rhs.isEmpty }, "anonymous functions don't support parameters with default values")
Function(params0, body)
}
- def unapply(tree: Tree): Option[(List[ValDef], Tree)] = tree match {
- case Function(params, body) => Some((params, body))
- case _ => None
- }
+ def unapply(tree: Function): Option[(List[ValDef], Tree)] = Function.unapply(tree)
}
object SyntacticNew extends SyntacticNewExtractor {
@@ -537,13 +534,15 @@ trait BuildUtils { self: SymbolTable =>
def unapply(tree: Tree): Option[Tree] = gen.Filter.unapply(tree)
}
- // abstract over possible alternative representations of no type in valdef
- protected object EmptyTypTree {
- def unapply(tree: Tree): Boolean = tree match {
- case EmptyTree => true
- case tt: TypeTree if (tt.original == null || tt.original.isEmpty) => true
- case _ => false
- }
+ // If a tree in type position isn't provided by the user (e.g. `tpt` fields of
+ // `ValDef` and `DefDef`, function params etc), then it's going to be parsed as
+ // TypeTree with empty original and empty tpe. This extractor matches such trees
+ // so that one can write q"val x = 2" to match typecheck(q"val x = 2"). Note that
+ // TypeTree() is the only possible representation for empty trees in type positions.
+ // We used to sometimes receive EmptyTree in such cases, but not anymore.
+ object SyntacticEmptyTypeTree extends SyntacticEmptyTypeTreeExtractor {
+ def apply(): TypeTree = self.TypeTree()
+ def unapply(tt: TypeTree): Boolean = tt.original == null || tt.original.isEmpty
}
// match a sequence of desugared `val $pat = $value`
@@ -561,8 +560,8 @@ trait BuildUtils { self: SymbolTable =>
case ValDef(_, name1, _, Match(MaybeUnchecked(value), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil)) :: UnPatSeq(rest)
if name1 == name2 =>
Some((pat, value) :: rest)
- // case q"${_} val $name: ${EmptyTypTree()} = $value" :: UnPatSeq(rest) =>
- case ValDef(_, name, EmptyTypTree(), value) :: UnPatSeq(rest) =>
+ // case q"${_} val $name: ${SyntacticEmptyTypeTree()} = $value" :: UnPatSeq(rest) =>
+ case ValDef(_, name, SyntacticEmptyTypeTree(), value) :: UnPatSeq(rest) =>
Some((Bind(name, self.Ident(nme.WILDCARD)), value) :: rest)
// case q"${_} val $name: $tpt = $value" :: UnPatSeq(rest) =>
case ValDef(_, name, tpt, value) :: UnPatSeq(rest) =>
@@ -604,8 +603,8 @@ trait BuildUtils { self: SymbolTable =>
def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
case Function(ValDef(Modifiers(PARAM, _, _), name, tpt, EmptyTree) :: Nil, body) =>
tpt match {
- case EmptyTypTree() => Some((Bind(name, self.Ident(nme.WILDCARD)), body))
- case _ => Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), body))
+ case SyntacticEmptyTypeTree() => Some((Bind(name, self.Ident(nme.WILDCARD)), body))
+ case _ => Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), body))
}
case UnVisitor(_, CaseDef(pat, EmptyTree, body) :: Nil) =>
Some((pat, body))
@@ -770,6 +769,146 @@ trait BuildUtils { self: SymbolTable =>
}
def unapply(tree: Ident): Some[(Name, Boolean)] = Some((tree.name, tree.hasAttachment[BackquotedIdentifierAttachment.type]))
}
+
+ /** Facade over Imports and ImportSelectors that lets to structurally
+ * deconstruct/reconstruct them.
+ *
+ * Selectors are represented in the following way:
+ * 1. q"import foo._" <==> q"import foo.${pq"_"}"
+ * 2. q"import foo.bar" <==> q"import foo.${pq"bar"}"
+ * 3. q"import foo.{bar => baz}" <==> q"import foo.${pq"bar -> baz"}"
+ * 4. q"import foo.{bar => _}" <==> q"import foo.${pq"bar -> _"}"
+ *
+ * All names in selectors are TermNames despite the fact ImportSelector
+ * can theoretically contain TypeNames too (but they never do in practice.)
+ */
+ object SyntacticImport extends SyntacticImportExtractor {
+ // construct/deconstruct {_} import selector
+ private object WildcardSelector {
+ def apply(offset: Int): ImportSelector = ImportSelector(nme.WILDCARD, offset, null, -1)
+ def unapply(sel: ImportSelector): Option[Int] = sel match {
+ case ImportSelector(nme.WILDCARD, offset, null, -1) => Some(offset)
+ case _ => None
+ }
+ }
+
+ // construct/deconstruct {foo} import selector
+ private object NameSelector {
+ def apply(name: TermName, offset: Int): ImportSelector = ImportSelector(name, offset, name, offset)
+ def unapply(sel: ImportSelector): Option[(TermName, Int)] = sel match {
+ case ImportSelector(name1, offset1, name2, offset2) if name1 == name2 && offset1 == offset2 =>
+ Some((name1.toTermName, offset1))
+ case _ =>
+ None
+ }
+ }
+
+ // construct/deconstruct {foo => bar} import selector
+ private object RenameSelector {
+ def apply(name1: TermName, offset1: Int, name2: TermName, offset2: Int): ImportSelector =
+ ImportSelector(name1, offset1, name2, offset2)
+ def unapply(sel: ImportSelector): Option[(TermName, Int, TermName, Int)] = sel match {
+ case ImportSelector(_, _, null | nme.WILDCARD, _) =>
+ None
+ case ImportSelector(name1, offset1, name2, offset2) if name1 != name2 =>
+ Some((name1.toTermName, offset1, name2.toTermName, offset2))
+ case _ =>
+ None
+ }
+ }
+
+ // construct/deconstruct {foo => _} import selector
+ private object UnimportSelector {
+ def apply(name: TermName, offset: Int): ImportSelector =
+ ImportSelector(name, offset, nme.WILDCARD, -1)
+ def unapply(sel: ImportSelector): Option[(TermName, Int)] = sel match {
+ case ImportSelector(name, offset, nme.WILDCARD, _) => Some((name.toTermName, offset))
+ case _ => None
+ }
+ }
+
+ // represent {_} import selector as pq"_"
+ private object WildcardSelectorRepr {
+ def apply(pos: Position): Tree = atPos(pos)(self.Ident(nme.WILDCARD))
+ def unapply(tree: Tree): Option[Position] = tree match {
+ case self.Ident(nme.WILDCARD) => Some(tree.pos)
+ case _ => None
+ }
+ }
+
+ // represent {foo} import selector as pq"foo"
+ private object NameSelectorRepr {
+ def apply(name: TermName, pos: Position): Tree = atPos(pos)(Bind(name, WildcardSelectorRepr(pos)))
+ def unapply(tree: Tree): Option[(TermName, Position)] = tree match {
+ case Bind(name, WildcardSelectorRepr(_)) => Some((name.toTermName, tree.pos))
+ case _ => None
+ }
+ }
+
+ // pq"left -> right"
+ private object Arrow {
+ def apply(left: Tree, right: Tree): Apply =
+ Apply(self.Ident(nme.MINGT), left :: right :: Nil)
+ def unapply(tree: Apply): Option[(Tree, Tree)] = tree match {
+ case Apply(self.Ident(nme.MINGT), left :: right :: Nil) => Some((left, right))
+ case _ => None
+ }
+ }
+
+ // represent {foo => bar} import selector as pq"foo -> bar"
+ private object RenameSelectorRepr {
+ def apply(name1: TermName, pos1: Position, name2: TermName, pos2: Position): Tree = {
+ val left = NameSelectorRepr(name1, pos1)
+ val right = NameSelectorRepr(name2, pos2)
+ atPos(wrappingPos(left :: right :: Nil))(Arrow(left, right))
+ }
+ def unapply(tree: Tree): Option[(TermName, Position, TermName, Position)] = tree match {
+ case Arrow(NameSelectorRepr(name1, pos1), NameSelectorRepr(name2, pos2)) =>
+ Some((name1.toTermName, pos1, name2.toTermName, pos2))
+ case _ =>
+ None
+ }
+ }
+
+ // represent {foo => _} import selector as pq"foo -> _"
+ private object UnimportSelectorRepr {
+ def apply(name: TermName, pos: Position): Tree =
+ atPos(pos)(Arrow(NameSelectorRepr(name, pos), WildcardSelectorRepr(pos)))
+ def unapply(tree: Tree): Option[(TermName, Position)] = tree match {
+ case Arrow(NameSelectorRepr(name, pos), WildcardSelectorRepr(_)) =>
+ Some((name, pos))
+ case _ =>
+ None
+ }
+ }
+
+ private def derivedPos(t: Tree, offset: Int): Position =
+ if (t.pos == NoPosition) NoPosition else t.pos.withPoint(offset)
+
+ private def derivedOffset(pos: Position): Int =
+ if (pos == NoPosition) -1 else pos.point
+
+ def apply(expr: Tree, selectors: List[Tree]): Import = {
+ val importSelectors = selectors.map {
+ case WildcardSelectorRepr(pos) => WildcardSelector(derivedOffset(pos))
+ case NameSelectorRepr(name, pos) => NameSelector(name, derivedOffset(pos))
+ case RenameSelectorRepr(name1, pos1, name2, pos2) => RenameSelector(name1, derivedOffset(pos1), name2, derivedOffset(pos2))
+ case UnimportSelectorRepr(name, pos) => UnimportSelector(name, derivedOffset(pos))
+ case tree => throw new IllegalArgumentException(s"${showRaw(tree)} doesn't correspond to import selector")
+ }
+ Import(expr, importSelectors)
+ }
+
+ def unapply(imp: Import): Some[(Tree, List[Tree])] = {
+ val selectors = imp.selectors.map {
+ case WildcardSelector(offset) => WildcardSelectorRepr(derivedPos(imp, offset))
+ case NameSelector(name, offset) => NameSelectorRepr(name, derivedPos(imp, offset))
+ case RenameSelector(name1, offset1, name2, offset2) => RenameSelectorRepr(name1, derivedPos(imp, offset1), name2, derivedPos(imp, offset2))
+ case UnimportSelector(name, offset) => UnimportSelectorRepr(name, derivedPos(imp, offset))
+ }
+ Some((imp.expr, selectors))
+ }
+ }
}
val build: BuildImpl = new BuildImpl
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 4d24f0b219..7a0c70caf6 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -30,12 +30,12 @@ trait Definitions extends api.StandardDefinitions {
private def enterNewClass(owner: Symbol, name: TypeName, parents: List[Type], flags: Long = 0L): ClassSymbol = {
val clazz = owner.newClassSymbol(name, NoPosition, flags)
- clazz setInfoAndEnter ClassInfoType(parents, newScope, clazz)
+ clazz setInfoAndEnter ClassInfoType(parents, newScope, clazz) markAllCompleted
}
private def newMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long): MethodSymbol = {
val msym = owner.newMethod(name.encode, NoPosition, flags)
val params = msym.newSyntheticValueParams(formals)
- msym setInfo MethodType(params, restpe)
+ msym setInfo MethodType(params, restpe) markAllCompleted
}
private def enterNewMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long = 0L): MethodSymbol =
owner.info.decls enter newMethod(owner, name, formals, restpe, flags)
@@ -251,8 +251,8 @@ trait Definitions extends api.StandardDefinitions {
}
// top types
- lazy val AnyClass = enterNewClass(ScalaPackageClass, tpnme.Any, Nil, ABSTRACT)
- lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe)
+ lazy val AnyClass = enterNewClass(ScalaPackageClass, tpnme.Any, Nil, ABSTRACT) markAllCompleted
+ lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe) markAllCompleted
lazy val ObjectClass = getRequiredClass(sn.Object.toString)
// Cached types for core monomorphic classes
@@ -275,7 +275,7 @@ trait Definitions extends api.StandardDefinitions {
val anyval = enterNewClass(ScalaPackageClass, tpnme.AnyVal, AnyTpe :: Nil, ABSTRACT)
val av_constr = anyval.newClassConstructor(NoPosition)
anyval.info.decls enter av_constr
- anyval
+ anyval markAllCompleted
}).asInstanceOf[ClassSymbol]
def AnyVal_getClass = getMemberMethod(AnyValClass, nme.getClass_)
@@ -287,8 +287,10 @@ trait Definitions extends api.StandardDefinitions {
locally {
this initFlags ABSTRACT | FINAL
this setInfoAndEnter ClassInfoType(List(parent.tpe), newScope, this)
+ this markAllCompleted
}
final override def isBottomClass = true
+ final override def isThreadsafe(purpose: SymbolOps): Boolean = true
}
final object NothingClass extends BottomClassSymbol(tpnme.Nothing, AnyClass) {
override def isSubClass(that: Symbol) = true
@@ -357,7 +359,7 @@ trait Definitions extends api.StandardDefinitions {
def delayedInitMethod = getMemberMethod(DelayedInitClass, nme.delayedInit)
lazy val TypeConstraintClass = requiredClass[scala.annotation.TypeConstraint]
- lazy val SingletonClass = enterNewClass(ScalaPackageClass, tpnme.Singleton, AnyTpe :: Nil, ABSTRACT | TRAIT | FINAL)
+ lazy val SingletonClass = enterNewClass(ScalaPackageClass, tpnme.Singleton, AnyTpe :: Nil, ABSTRACT | TRAIT | FINAL) markAllCompleted
lazy val SerializableClass = requiredClass[scala.Serializable]
lazy val JavaSerializableClass = requiredClass[java.io.Serializable] modifyInfo fixupAsAnyTrait
lazy val ComparableClass = requiredClass[java.lang.Comparable[_]] modifyInfo fixupAsAnyTrait
@@ -1127,6 +1129,7 @@ trait Definitions extends api.StandardDefinitions {
lazy val AnnotationDefaultAttr: ClassSymbol = {
val sym = RuntimePackageClass.newClassSymbol(tpnme.AnnotationDefaultATTR, NoPosition, 0L)
sym setInfo ClassInfoType(List(AnnotationClass.tpe), newScope, sym)
+ markAllCompleted(sym)
RuntimePackageClass.info.decls.toList.filter(_.name == sym.name) match {
case existing :: _ =>
existing.asInstanceOf[ClassSymbol]
@@ -1226,7 +1229,7 @@ trait Definitions extends api.StandardDefinitions {
val tparam = clazz.newSyntheticTypeParam("T0", flags)
val parents = List(AnyRefTpe, parentFn(tparam))
- clazz setInfo GenPolyType(List(tparam), ClassInfoType(parents, newScope, clazz))
+ clazz setInfo GenPolyType(List(tparam), ClassInfoType(parents, newScope, clazz)) markAllCompleted
}
def newPolyMethod(typeParamCount: Int, owner: Symbol, name: TermName, flags: Long)(createFn: PolyMethodCreator): MethodSymbol = {
@@ -1237,7 +1240,7 @@ trait Definitions extends api.StandardDefinitions {
case (_, restpe) => NullaryMethodType(restpe)
}
- msym setInfoAndEnter genPolyType(tparams, mtpe)
+ msym setInfoAndEnter genPolyType(tparams, mtpe) markAllCompleted
}
/** T1 means one type parameter.
diff --git a/src/reflect/scala/reflect/internal/FreshNames.scala b/src/reflect/scala/reflect/internal/FreshNames.scala
index 1de8d425ad..7e9a568266 100644
--- a/src/reflect/scala/reflect/internal/FreshNames.scala
+++ b/src/reflect/scala/reflect/internal/FreshNames.scala
@@ -8,7 +8,7 @@ package internal
import scala.reflect.internal.util.FreshNameCreator
-trait FreshNames { self: Names =>
+trait FreshNames { self: Names with StdNames =>
// SI-6879 Keeps track of counters that are supposed to be globally unique
// as opposed to traditional freshers that are unique to compilation units.
val globalFreshNameCreator = new FreshNameCreator
@@ -17,8 +17,8 @@ trait FreshNames { self: Names =>
def currentFreshNameCreator: FreshNameCreator
// create fresh term/type name using implicit fresh name creator
- def freshTermName(prefix: String = "x$")(implicit creator: FreshNameCreator): TermName = newTermName(creator.newName(prefix))
- def freshTypeName(prefix: String)(implicit creator: FreshNameCreator): TypeName = newTypeName(creator.newName(prefix))
+ def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX)(implicit creator: FreshNameCreator): TermName = newTermName(creator.newName(prefix))
+ def freshTypeName(prefix: String)(implicit creator: FreshNameCreator): TypeName = newTypeName(creator.newName(prefix))
// Extractor that matches names which were generated by some
// FreshNameCreator with known prefix. Extracts user-specified
@@ -36,4 +36,4 @@ trait FreshNames { self: Names =>
else Some(NameTransformer.decode(sname.replaceFirst(quotedCreatorPrefix, "").replaceAll("\\d*$", "")))
}
}
-} \ No newline at end of file
+}
diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala
index 483d0dd656..ff91b08ea1 100644
--- a/src/reflect/scala/reflect/internal/Importers.scala
+++ b/src/reflect/scala/reflect/internal/Importers.scala
@@ -4,6 +4,7 @@ package internal
import scala.collection.mutable.WeakHashMap
import scala.ref.WeakReference
+import scala.reflect.internal.Flags._
// SI-6241: move importers to a mirror
trait Importers extends api.Importers { to: SymbolTable =>
@@ -87,6 +88,7 @@ trait Importers extends api.Importers { to: SymbolTable =>
}
my setInfo GenPolyType(mytypeParams, importType(theirCore))
my setAnnotations (their.annotations map importAnnotationInfo)
+ markAllCompleted(my)
}
}
} finally {
@@ -142,6 +144,7 @@ trait Importers extends api.Importers { to: SymbolTable =>
myowner.newTypeSymbol(myname.toTypeName, mypos, myflags)
}
symMap.weakUpdate(their, my)
+ markFlagsCompleted(my)(mask = AllFlags)
my setInfo recreatedSymbolCompleter(my, their)
}
diff --git a/src/reflect/scala/reflect/internal/Required.scala b/src/reflect/scala/reflect/internal/Required.scala
index 14db252a16..009bc39d4c 100644
--- a/src/reflect/scala/reflect/internal/Required.scala
+++ b/src/reflect/scala/reflect/internal/Required.scala
@@ -6,6 +6,9 @@ import settings.MutableSettings
trait Required { self: SymbolTable =>
def picklerPhase: Phase
+
+ def erasurePhase: Phase
+
def settings: MutableSettings
@deprecated("Interactive is implemented with a custom Global; this flag is ignored", "2.11.0") def forInteractive = false
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 28d799ea0c..679186f938 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -295,22 +295,23 @@ trait StdNames {
protected implicit def createNameType(name: String): TermName = newTermNameCached(name)
/** Base strings from which synthetic names are derived. */
- val BITMAP_PREFIX = "bitmap$"
- val CHECK_IF_REFUTABLE_STRING = "check$ifrefutable$"
- val DEFAULT_GETTER_STRING = "$default$"
- val DEFAULT_GETTER_INIT_STRING = NameTransformer.encode("<init>") + DEFAULT_GETTER_STRING
- val DO_WHILE_PREFIX = "doWhile$"
- val EVIDENCE_PARAM_PREFIX = "evidence$"
- val EXCEPTION_RESULT_PREFIX = "exceptionResult"
- val EXPAND_SEPARATOR_STRING = "$$"
- val INTERPRETER_IMPORT_WRAPPER = "$iw"
- val LOCALDUMMY_PREFIX = "<local " // owner of local blocks
- val PROTECTED_PREFIX = "protected$"
- val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set"
- val SUPER_PREFIX_STRING = "super$"
- val WHILE_PREFIX = "while$"
- val FRESH_PREFIX = "fresh"
- val FRESH_SUFFIX = "macro$" // uses a keyword to avoid collisions with mangled names
+ val BITMAP_PREFIX = "bitmap$"
+ val CHECK_IF_REFUTABLE_STRING = "check$ifrefutable$"
+ val DEFAULT_GETTER_STRING = "$default$"
+ val DEFAULT_GETTER_INIT_STRING = NameTransformer.encode("<init>") + DEFAULT_GETTER_STRING
+ val DO_WHILE_PREFIX = "doWhile$"
+ val EVIDENCE_PARAM_PREFIX = "evidence$"
+ val EXCEPTION_RESULT_PREFIX = "exceptionResult"
+ val EXPAND_SEPARATOR_STRING = "$$"
+ val FRESH_TERM_NAME_PREFIX = "x$"
+ val INTERPRETER_IMPORT_WRAPPER = "$iw"
+ val LOCALDUMMY_PREFIX = "<local " // owner of local blocks
+ val PROTECTED_PREFIX = "protected$"
+ val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set"
+ val SUPER_PREFIX_STRING = "super$"
+ val WHILE_PREFIX = "while$"
+ val FRESH_PREFIX = "fresh"
+ val FRESH_SUFFIX = "macro$" // uses a keyword to avoid collisions with mangled names
// Compiler internal names
val ANYname: NameType = "<anyname>"
@@ -602,12 +603,14 @@ trait StdNames {
val SyntacticBlock: NameType = "SyntacticBlock"
val SyntacticClassDef: NameType = "SyntacticClassDef"
val SyntacticDefDef: NameType = "SyntacticDefDef"
+ val SyntacticEmptyTypeTree: NameType = "SyntacticEmptyTypeTree"
val SyntacticFilter: NameType = "SyntacticFilter"
val SyntacticFor: NameType = "SyntacticFor"
val SyntacticForYield: NameType = "SyntacticForYield"
val SyntacticFunction: NameType = "SyntacticFunction"
val SyntacticFunctionType: NameType = "SyntacticFunctionType"
val SyntacticIdent: NameType = "SyntacticIdent"
+ val SyntacticImport: NameType = "SyntacticImport"
val SyntacticMatch: NameType = "SyntacticMatch"
val SyntacticNew: NameType = "SyntacticNew"
val SyntacticObjectDef: NameType = "SyntacticObjectDef"
@@ -822,31 +825,33 @@ trait StdNames {
def newLazyValSlowComputeName(lzyValName: Name) = lzyValName append LAZY_SLOW_SUFFIX
// ASCII names for operators
- val ADD = encode("+")
- val AND = encode("&")
- val ASR = encode(">>")
- val CONS = encode("::")
- val DIV = encode("/")
- val EQ = encode("==")
- val EQL = encode("=")
- val GE = encode(">=")
- val GT = encode(">")
- val HASHHASH = encode("##")
- val LE = encode("<=")
- val LSL = encode("<<")
- val LSR = encode(">>>")
- val LT = encode("<")
- val MINUS = encode("-")
- val MOD = encode("%")
- val MUL = encode("*")
- val NE = encode("!=")
- val OR = encode("|")
- val PLUS = ADD // technically redundant, but ADD looks funny with MINUS
- val PLUSPLUS = encode("++")
- val SUB = MINUS // ... as does SUB with PLUS
- val XOR = encode("^")
- val ZAND = encode("&&")
- val ZOR = encode("||")
+ val ADD = encode("+")
+ val AND = encode("&")
+ val ASR = encode(">>")
+ val CONS = encode("::")
+ val COLONPLUS = encode(":+")
+ val DIV = encode("/")
+ val EQ = encode("==")
+ val EQL = encode("=")
+ val GE = encode(">=")
+ val GT = encode(">")
+ val HASHHASH = encode("##")
+ val LE = encode("<=")
+ val LSL = encode("<<")
+ val LSR = encode(">>>")
+ val LT = encode("<")
+ val MINUS = encode("-")
+ val MINGT = encode("->")
+ val MOD = encode("%")
+ val MUL = encode("*")
+ val NE = encode("!=")
+ val OR = encode("|")
+ val PLUS = ADD // technically redundant, but ADD looks funny with MINUS
+ val PLUSPLUS = encode("++")
+ val SUB = MINUS // ... as does SUB with PLUS
+ val XOR = encode("^")
+ val ZAND = encode("&&")
+ val ZOR = encode("||")
// unary operators
val UNARY_~ = encode("unary_~")
diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala
index 571c4cfa5d..802bd18a4e 100644
--- a/src/reflect/scala/reflect/internal/SymbolTable.scala
+++ b/src/reflect/scala/reflect/internal/SymbolTable.scala
@@ -371,6 +371,8 @@ abstract class SymbolTable extends macros.Universe
def newMap[K, V]() = recordCache(mutable.HashMap[K, V]())
def newSet[K]() = recordCache(mutable.HashSet[K]())
def newWeakSet[K <: AnyRef]() = recordCache(new WeakHashSet[K]())
+
+ def newAnyRefMap[K <: AnyRef, V]() = recordCache(mutable.AnyRefMap[K, V]())
def newGeneric[T](f: => T): () => T = {
val NoCached: T = null.asInstanceOf[T]
var cached: T = NoCached
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index b8cd1e86d3..2969bd92de 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -55,16 +55,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def newFreeTypeSymbol(name: TypeName, flags: Long = 0L, origin: String): FreeTypeSymbol =
new FreeTypeSymbol(name, origin) initFlags flags
- /** Determines whether the given information request should trigger the given symbol's completer.
- * See comments to `Symbol.needsInitialize` for details.
- */
- protected def shouldTriggerCompleter(symbol: Symbol, completer: Type, isFlagRelated: Boolean, mask: Long) =
- completer match {
- case null => false
- case _: FlagAgnosticCompleter => !isFlagRelated
- case _ => abort(s"unsupported completer: $completer of class ${if (completer != null) completer.getClass else null} for symbol ${symbol.fullName}")
- }
-
/** The original owner of a class. Used by the backend to generate
* EnclosingMethod attributes.
*/
@@ -106,18 +96,27 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
}
def knownDirectSubclasses = {
- if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
+ if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize
children
}
def selfType = {
- if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
+ if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize
typeOfThis
}
def baseClasses = info.baseClasses
def module = sourceModule
def thisPrefix: Type = thisType
+
+ // automatic full initialization on access to info from reflection API is a double-edged sword
+ // on the one hand, it's convenient so that the users don't have to deal with initialization themselves before printing out stuff
+ // (e.g. printing out a method's signature without fully initializing it would result in <_>'s for parameters
+ // on the other hand, this strategy can potentially cause unexpected effects due to being inconsistent with compiler's behavior
+ // so far I think user convenience outweighs the scariness, but we need to keep the tradeoff in mind
+ // NOTE: if you end up removing the call to fullyInitializeSymbol, consider that it would affect both runtime reflection and macros
def typeSignature: Type = { fullyInitializeSymbol(this); info }
def typeSignatureIn(site: Type): Type = { fullyInitializeSymbol(this); site memberInfo this }
@@ -140,6 +139,11 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
with Annotatable[Symbol]
with Attachable {
+ // makes sure that all symbols that runtime reflection deals with are synchronized
+ private def isSynchronized = this.isInstanceOf[scala.reflect.runtime.SynchronizedSymbols#SynchronizedSymbol]
+ private def isAprioriThreadsafe = isThreadsafe(AllOps)
+ assert(isCompilerUniverse || isSynchronized || isAprioriThreadsafe, s"unsafe symbol $initName (child of $initOwner) in runtime reflection universe")
+
type AccessBoundaryType = Symbol
type AnnotationType = AnnotationInfo
@@ -609,20 +613,55 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
&& isTopLevel
&& nme.isReplWrapperName(name)
)
+
+ /** In our current architecture, symbols for top-level classes and modules
+ * are created as dummies. Package symbols just call newClass(name) or newModule(name) and
+ * consider their job done.
+ *
+ * In order for such a dummy to provide meaningful info (e.g. a list of its members),
+ * it needs to go through unpickling. Unpickling is a process of reading Scala metadata
+ * from ScalaSignature annotations and assigning it to symbols and types.
+ *
+ * A single unpickling session takes a top-level class or module, parses the ScalaSignature annotation
+ * and then reads metadata for the unpicklee, its companion (if any) and all their members recursively
+ * (i.e. the pickle not only contains info about directly nested classes/modules, but also about
+ * classes/modules nested into those and so on).
+ *
+ * Unpickling is triggered automatically whenever typeSignature (info in compiler parlance) is called.
+ * This happens because package symbols assign completer thunks to the dummies they create.
+ * Therefore metadata loading happens lazily and transparently.
+ *
+ * Almost transparently. Unfortunately metadata isn't limited to just signatures (i.e. lists of members).
+ * It also includes flags (which determine e.g. whether a class is sealed or not), annotations and privateWithin.
+ * This gives rise to unpleasant effects like in SI-6277, when a flag test called on an uninitialize symbol
+ * produces incorrect results.
+ *
+ * One might think that the solution is simple: automatically call the completer
+ * whenever one needs flags, annotations and privateWithin - just like it's done for typeSignature.
+ * Unfortunately, this leads to weird crashes in scalac, and currently we can't attempt
+ * to fix the core of the compiler risk stability a few weeks before the final release.
+ * upd. Haha, "a few weeks before the final release". This surely sounds familiar :)
+ *
+ * However we do need to fix this for runtime reflection, since this idionsynchrazy is not something
+ * we'd like to expose to reflection users. Therefore a proposed solution is to check whether we're in a
+ * runtime reflection universe, and if yes and if we've not yet loaded the requested info, then to commence initialization.
+ */
final def getFlag(mask: Long): Long = {
- if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize
+ if (!isCompilerUniverse && !isThreadsafe(purpose = FlagOps(mask))) initialize
flags & mask
}
/** Does symbol have ANY flag in `mask` set? */
final def hasFlag(mask: Long): Boolean = {
- if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
+ if (!isCompilerUniverse && !isThreadsafe(purpose = FlagOps(mask))) initialize
(flags & mask) != 0
}
def hasFlag(mask: Int): Boolean = hasFlag(mask.toLong)
/** Does symbol have ALL the flags in `mask` set? */
final def hasAllFlags(mask: Long): Boolean = {
- if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
+ if (!isCompilerUniverse && !isThreadsafe(purpose = FlagOps(mask))) initialize
(flags & mask) == mask
}
@@ -789,7 +828,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
*/
- def isStable = isTerm && !isMutable && !(hasFlag(BYNAMEPARAM)) && (!isMethod || hasStableFlag)
+ final 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? */
@@ -943,12 +982,21 @@ 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.
+ /** We consider a symbol to be thread-safe, when multiple concurrent threads can call its methods
+ * (either directly or indirectly via public reflection or internal compiler infrastructure),
+ * without any locking and everything works as it should work.
+ *
+ * In its basic form, `isThreadsafe` always returns false. Runtime reflection augments reflection infrastructure
+ * with threadsafety-tracking mechanism implemented in `SynchronizedSymbol` that communicates with underlying completers
+ * and can sometimes return true if the symbol has been completed to the point of thread safety.
+ *
+ * The `purpose` parameter signifies whether we want to just check immutability of certain flags for the given mask.
+ * This is necessary to enable robust auto-initialization of `Symbol.flags` for runtime reflection, and is also quite handy
+ * in avoiding unnecessary initializations when requesting for flags that have already been set.
*/
- final def isFullyInitialized: Boolean = _validTo != NoPeriod && (flags & LOCKED) == 0
+ def isThreadsafe(purpose: SymbolOps): Boolean = false
+ def markFlagsCompleted(mask: Long): this.type = this
+ def markAllCompleted(): this.type = this
/** Can this symbol be loaded by a reflective mirror?
*
@@ -1225,7 +1273,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
*/
private[this] var _privateWithin: Symbol = _
def privateWithin = {
- if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
+ if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize
_privateWithin
}
def privateWithin_=(sym: Symbol) { _privateWithin = sym }
@@ -1483,46 +1532,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
catch { case _: CyclicReference => debuglog("Hit cycle in maybeInitialize of $this") ; false }
}
- /** Called when the programmer requests information that might require initialization of the underlying symbol.
- *
- * `isFlagRelated` and `mask` describe the nature of this information.
- * isFlagRelated = true means that the programmer needs particular bits in flags.
- * isFlagRelated = false means that the request is unrelated to flags (annotations or privateWithin).
- *
- * In our current architecture, symbols for top-level classes and modules
- * are created as dummies. Package symbols just call newClass(name) or newModule(name) and
- * consider their job done.
- *
- * In order for such a dummy to provide meaningful info (e.g. a list of its members),
- * it needs to go through unpickling. Unpickling is a process of reading Scala metadata
- * from ScalaSignature annotations and assigning it to symbols and types.
- *
- * A single unpickling session takes a top-level class or module, parses the ScalaSignature annotation
- * and then reads metadata for the unpicklee, its companion (if any) and all their members recursively
- * (i.e. the pickle not only contains info about directly nested classes/modules, but also about
- * classes/modules nested into those and so on).
- *
- * Unpickling is triggered automatically whenever typeSignature (info in compiler parlance) is called.
- * This happens because package symbols assign completer thunks to the dummies they create.
- * Therefore metadata loading happens lazily and transparently.
- *
- * Almost transparently. Unfortunately metadata isn't limited to just signatures (i.e. lists of members).
- * It also includes flags (which determine e.g. whether a class is sealed or not), annotations and privateWithin.
- * This gives rise to unpleasant effects like in SI-6277, when a flag test called on an uninitialize symbol
- * produces incorrect results.
- *
- * One might think that the solution is simple: automatically call the completer whenever one needs
- * flags, annotations and privateWithin - just like it's done for typeSignature. Unfortunately, this
- * leads to weird crashes in scalac, and currently we can't attempt to fix the core of the compiler
- * risk stability a few weeks before the final release.
- *
- * However we do need to fix this for runtime reflection, since it's not something we'd like to
- * expose to reflection users. Therefore a proposed solution is to check whether we're in a
- * runtime reflection universe and if yes then to commence initialization.
- */
- protected def needsInitialize(isFlagRelated: Boolean, mask: Long) =
- !isInitialized && (flags & LOCKED) == 0 && shouldTriggerCompleter(this, if (infos ne null) infos.info else null, isFlagRelated, mask)
-
/** Was symbol's type updated during given phase? */
final def hasTypeAt(pid: Phase#Id): Boolean = {
assert(isCompilerUniverse)
@@ -1681,7 +1690,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
* the annotations attached to member a definition (class, method, type, field).
*/
def annotations: List[AnnotationInfo] = {
- if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize
+ // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
+ if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize
_annotations
}
@@ -3510,6 +3520,17 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
Statistics.newView("#symbols")(ids)
+
+// -------------- Completion --------------------------------------------------------
+
+ // is used to differentiate levels of thread-safety in `Symbol.isThreadsafe`
+ case class SymbolOps(isFlagRelated: Boolean, mask: Long)
+ val AllOps = SymbolOps(isFlagRelated = false, mask = 0L)
+ def FlagOps(mask: Long) = SymbolOps(isFlagRelated = true, mask = mask)
+
+ private def relevantSymbols(syms: Seq[Symbol]) = syms.flatMap(sym => List(sym, sym.moduleClass, sym.sourceModule))
+ def markFlagsCompleted(syms: Symbol*)(mask: Long): Unit = relevantSymbols(syms).foreach(_.markFlagsCompleted(mask))
+ def markAllCompleted(syms: Symbol*): Unit = relevantSymbols(syms).foreach(_.markAllCompleted)
}
object SymbolsStats {
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 53c528a2bb..17a58d79f6 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -899,7 +899,7 @@ trait Types
-1
}
- /** If this is a poly- or methodtype, a copy with cloned type / value parameters
+ /** If this is a ExistentialType, PolyType or MethodType, a copy with cloned type / value parameters
* owned by `owner`. Identity for all other types.
*/
def cloneInfo(owner: Symbol) = this
@@ -1969,12 +1969,12 @@ trait Types
def apply(value: Constant) = unique(new UniqueConstantType(value))
}
- private var _volatileRecursions: Int = 0
- def volatileRecursions = _volatileRecursions
- def volatileRecursions_=(value: Int) = _volatileRecursions = value
-
- private val _pendingVolatiles = new mutable.HashSet[Symbol]
- def pendingVolatiles = _pendingVolatiles
+ /* 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]
class ArgsTypeRef(pre0: Type, sym0: Symbol, args0: List[Type]) extends TypeRef(pre0, sym0, args0) {
require(args0 ne Nil, this)
@@ -2683,8 +2683,57 @@ trait Types
override def baseTypeSeq = underlying.baseTypeSeq map maybeRewrap
override def isHigherKinded = false
- override def skolemizeExistential(owner: Symbol, origin: AnyRef) =
+ /** [SI-6169, SI-8197 -- companion to SI-1786]
+ *
+ * Approximation to improve the bounds of a Java-defined existential type,
+ * based on the bounds of the type parameters of the quantified type
+ * In Scala syntax, given a java-defined class C[T <: String], the existential type C[_]
+ * is improved to C[_ <: String] before skolemization, which captures (get it?) what Java does:
+ * enter the type paramers' bounds into the context when checking subtyping/type equality of existential types
+ *
+ * (Also tried doing this once during class file parsing or when creating the existential type,
+ * but that causes cyclic errors because it happens too early.)
+ *
+ * TODO: figure out how to do this earlier without running into cycles, so this can subsume the fix for SI-1786
+ */
+ private def sharpenQuantifierBounds(): Unit = {
+ /* Check that we're looking at rawToExistential's handiwork
+ * (`existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe)))`).
+ * We can't do this sharpening there because we'll run into cycles.
+ */
+ def rawToExistentialCreatedMe = (quantified corresponds underlying.typeArgs){ (q, a) => q.tpe =:= a }
+
+ if (underlying.typeSymbol.isJavaDefined && rawToExistentialCreatedMe) {
+ val tpars = underlying.typeSymbol.initialize.typeParams // TODO: is initialize needed?
+ debuglog(s"sharpen bounds: $this | ${underlying.typeArgs.map(_.typeSymbol)} <-- ${tpars.map(_.info)}")
+
+ foreach2(quantified, tpars) { (quant, tparam) =>
+ // TODO: check `tparam.info.substSym(tpars, quantified) <:< quant.info` instead (for some weird reason not working for test/t6169/ExistF)
+ // for now, crude approximation for the common case
+ if (quant.info.bounds.isEmptyBounds && !tparam.info.bounds.isEmptyBounds) {
+ // avoid creating cycles [pos/t2940] that consist of an existential quantifier's
+ // bounded by an existential type that unhygienically has that quantifier as its own quantifier
+ // (TODO: clone latter existential with fresh quantifiers -- not covering this case for now)
+ if ((existentialsInType(tparam.info) intersect quantified).isEmpty)
+ quant setInfo tparam.info.substSym(tpars, quantified)
+ }
+ }
+ }
+
+ _sharpenQuantifierBounds = false
+ }
+ private[this] var _sharpenQuantifierBounds = true
+
+ override def skolemizeExistential(owner: Symbol, origin: AnyRef) = {
+ // do this here because it's quite close to what Java does:
+ // when checking subtyping/type equality, enter constraints
+ // derived from the existentially quantified type into the typing environment
+ // (aka \Gamma, which tracks types for variables and constraints/kinds for types)
+ // as a nice bonus, delaying this until we need it avoids cyclic errors
+ if (_sharpenQuantifierBounds) sharpenQuantifierBounds
+
deriveType(quantified, tparam => (owner orElse tparam.owner).newExistentialSkolem(tparam, origin))(underlying)
+ }
private def wildcardArgsString(qset: Set[Symbol], args: List[Type]): List[String] = args map {
case TypeRef(_, sym, _) if (qset contains sym) =>
@@ -2905,6 +2954,8 @@ trait Types
override def params: List[Symbol] = zippedArgs map (_._1)
override def typeArgs: List[Type] = zippedArgs map (_._2)
+
+ override def safeToString: String = super.safeToString + typeArgs.map(_.safeToString).mkString("[", ", ", "]")
}
trait UntouchableTypeVar extends TypeVar {
@@ -3025,15 +3076,19 @@ trait Types
def addLoBound(tp: Type, isNumericBound: Boolean = false) {
assert(tp != this, tp) // implies there is a cycle somewhere (?)
//println("addLoBound: "+(safeToString, debugString(tp))) //DEBUG
- undoLog record this
- constr.addLoBound(tp, isNumericBound)
+ if (!sharesConstraints(tp)) {
+ undoLog record this
+ constr.addLoBound(tp, isNumericBound)
+ }
}
def addHiBound(tp: Type, isNumericBound: Boolean = false) {
// assert(tp != this)
//println("addHiBound: "+(safeToString, debugString(tp))) //DEBUG
- undoLog record this
- constr.addHiBound(tp, isNumericBound)
+ if (!sharesConstraints(tp)) {
+ undoLog record this
+ constr.addHiBound(tp, isNumericBound)
+ }
}
// </region>
@@ -3045,6 +3100,16 @@ trait Types
case ConstantTrue => true
case tv: TypeVar => tv.suspended
}
+
+ /** `AppliedTypeVar`s share the same `TypeConstraint` with the `HKTypeVar` that it was spawned from.
+ * A type inference session can also have more than one ATV.
+ * If we don't detect that, we end up with "cyclic constraint" when we try to instantiate type parameters
+ * after solving in, pos/t8237
+ */
+ protected final def sharesConstraints(other: Type): Boolean = other match {
+ case other: TypeVar => constr == other.constr // SI-8237 avoid cycles. Details in pos/t8237.scala
+ case _ => false
+ }
private[Types] def suspended_=(b: Boolean): Unit = _suspended = if (b) ConstantTrue else ConstantFalse
// SI-7785 Link the suspended attribute of a TypeVar created in, say, a TypeMap (e.g. AsSeenFrom) to its originator
private[Types] def linkSuspended(origin: TypeVar): Unit = _suspended = origin
diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
index 2a19441476..42f794736a 100644
--- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
+++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
@@ -275,6 +275,7 @@ abstract class UnPickler {
def pflags = flags & PickledFlags
def finishSym(sym: Symbol): Symbol = {
+ markFlagsCompleted(sym)(mask = AllFlags)
sym.privateWithin = privateWithin
sym.info = (
if (atEnd) {
@@ -663,7 +664,7 @@ abstract class UnPickler {
private class LazyTypeRef(i: Int) extends LazyType with FlagAgnosticCompleter {
private val definedAtRunId = currentRunId
private val p = phase
- override def complete(sym: Symbol) : Unit = try {
+ protected def completeInternal(sym: Symbol) : Unit = try {
val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType`
if (p ne null)
slowButSafeEnteringPhase(p) (sym setInfo tp)
@@ -673,6 +674,10 @@ abstract class UnPickler {
catch {
case e: MissingRequirementError => throw toTypeError(e)
}
+ override def complete(sym: Symbol) : Unit = {
+ completeInternal(sym)
+ if (!isCompilerUniverse) markAllCompleted(sym)
+ }
override def load(sym: Symbol) { complete(sym) }
}
@@ -680,8 +685,9 @@ abstract class UnPickler {
* of completed symbol to symbol at index `j`.
*/
private class LazyTypeRefAndAlias(i: Int, j: Int) extends LazyTypeRef(i) {
- override def complete(sym: Symbol) = try {
- super.complete(sym)
+ override def completeInternal(sym: Symbol) = try {
+ super.completeInternal(sym)
+
var alias = at(j, readSymbol)
if (alias.isOverloaded)
alias = slowButSafeEnteringPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt))))
diff --git a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala
index e2159d30f5..564cbb1ce3 100644
--- a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala
+++ b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala
@@ -257,6 +257,12 @@ private[internal] trait TypeConstraints {
// println("solving "+tvars+"/"+tparams+"/"+(tparams map (_.info)))
foreach3(tvars, tparams, variances)(solveOne)
- tvars forall (tv => tv.instWithinBounds || util.andFalse(log(s"Inferred type for ${tv.originString} does not conform to bounds: ${tv.constr}")))
+
+ def logBounds(tv: TypeVar) = log {
+ val what = if (!tv.instValid) "is invalid" else s"does not conform to bounds: ${tv.constr}"
+ s"Inferred type for ${tv.originString} (${tv.inst}) $what"
+ }
+
+ tvars forall (tv => tv.instWithinBounds || util.andFalse(logBounds(tv)))
}
}
diff --git a/src/reflect/scala/reflect/internal/transform/PostErasure.scala b/src/reflect/scala/reflect/internal/transform/PostErasure.scala
new file mode 100644
index 0000000000..f0c7d0f050
--- /dev/null
+++ b/src/reflect/scala/reflect/internal/transform/PostErasure.scala
@@ -0,0 +1,19 @@
+package scala.reflect
+package internal
+package transform
+
+trait PostErasure {
+ val global: SymbolTable
+ import global._
+ import definitions._
+
+ object elimErasedValueType extends TypeMap {
+ def apply(tp: Type) = tp match {
+ case ConstantType(Constant(tp: Type)) => ConstantType(Constant(apply(tp)))
+ case ErasedValueType(_, underlying) => underlying
+ case _ => mapOver(tp)
+ }
+ }
+
+ def transformInfo(sym: Symbol, tp: Type) = elimErasedValueType(tp)
+}
diff --git a/src/reflect/scala/reflect/internal/transform/Transforms.scala b/src/reflect/scala/reflect/internal/transform/Transforms.scala
index fa185db22f..296ccde443 100644
--- a/src/reflect/scala/reflect/internal/transform/Transforms.scala
+++ b/src/reflect/scala/reflect/internal/transform/Transforms.scala
@@ -26,17 +26,20 @@ trait Transforms { self: SymbolTable =>
private val refChecksLazy = new Lazy(new { val global: Transforms.this.type = self } with RefChecks)
private val uncurryLazy = new Lazy(new { val global: Transforms.this.type = self } with UnCurry)
private val erasureLazy = new Lazy(new { val global: Transforms.this.type = self } with Erasure)
+ private val postErasureLazy = new Lazy(new { val global: Transforms.this.type = self } with PostErasure)
def refChecks = refChecksLazy.force
def uncurry = uncurryLazy.force
def erasure = erasureLazy.force
+ def postErasure = postErasureLazy.force
def transformedType(sym: Symbol) =
- erasure.transformInfo(sym,
- uncurry.transformInfo(sym,
- refChecks.transformInfo(sym, sym.info)))
+ postErasure.transformInfo(sym,
+ erasure.transformInfo(sym,
+ uncurry.transformInfo(sym,
+ refChecks.transformInfo(sym, sym.info))))
def transformedType(tpe: Type) =
- erasure.scalaErasure(uncurry.uncurry(tpe))
+ postErasure.elimErasedValueType(erasure.scalaErasure(uncurry.uncurry(tpe)))
}
diff --git a/src/reflect/scala/reflect/macros/Evals.scala b/src/reflect/scala/reflect/macros/Evals.scala
index 222ae43d79..68e07dd319 100644
--- a/src/reflect/scala/reflect/macros/Evals.scala
+++ b/src/reflect/scala/reflect/macros/Evals.scala
@@ -17,13 +17,13 @@ trait Evals {
* permitted by the shape of the arguments.
*
* Known issues: because of [[https://issues.scala-lang.org/browse/SI-5748 https://issues.scala-lang.org/browse/SI-5748]]
- * trees being evaluated first need to undergo `resetAllAttrs`. Resetting symbols and types
+ * trees being evaluated first need to undergo `untypecheck`. Resetting symbols and types
* mutates the tree in place, therefore the conventional approach is to `duplicate` the tree first.
*
* {{{
* scala> def impl(c: Context)(x: c.Expr[String]) = {
- * | val x1 = c.Expr[String](c.resetAllAttrs(x.tree.duplicate))
- * | println(s"compile-time value is: \${c.eval(x1)}")
+ * | val x1 = c.Expr[String](c.untypecheck(x.tree.duplicate))
+ * | println(s"compile-time value is: ${c.eval(x1)}")
* | x
* | }
* impl: (c: Context)(x: c.Expr[String])c.Expr[String]
diff --git a/src/reflect/scala/reflect/macros/Typers.scala b/src/reflect/scala/reflect/macros/Typers.scala
index 87de442921..6c077de1d2 100644
--- a/src/reflect/scala/reflect/macros/Typers.scala
+++ b/src/reflect/scala/reflect/macros/Typers.scala
@@ -68,12 +68,6 @@ trait Typers {
*/
def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree
- /** Recursively resets symbols and types in a given tree.
- * WARNING: Don't use this API, go for [[untypecheck]] instead.
- */
- @deprecated("Use `c.untypecheck` instead", "2.11.0")
- def resetAllAttrs(tree: Tree): Tree
-
/** Recursively resets locally defined symbols and types in a given tree.
* WARNING: Don't use this API, go for [[untypecheck]] instead.
*/
diff --git a/src/reflect/scala/reflect/macros/Universe.scala b/src/reflect/scala/reflect/macros/Universe.scala
index d84e6aa737..bc5c8b2840 100644
--- a/src/reflect/scala/reflect/macros/Universe.scala
+++ b/src/reflect/scala/reflect/macros/Universe.scala
@@ -122,7 +122,7 @@ abstract class Universe extends scala.reflect.api.Universe {
def setType(tp: Type): Tree
/** Like `setType`, but if this is a previously empty TypeTree that
- * fact is remembered so that resetAllAttrs will snap back.
+ * fact is remembered so that `untypecheck` will snap back.
*
* \@PP: Attempting to elaborate on the above, I find: If defineType
* is called on a TypeTree whose type field is null or NoType,
@@ -130,7 +130,8 @@ abstract class Universe extends scala.reflect.api.Universe {
* ResetAttrsTraverser, which nulls out the type field of TypeTrees
* for which wasEmpty is true, leaving the others alone.
*
- * resetAllAttrs is used in situations where some speculative
+ * `untypecheck` (or `resetAttrs` in compiler parlance) is used
+ * in situations where some speculative
* typing of a tree takes place, fails, and the tree needs to be
* returned to its former state to try again. So according to me:
* using `defineType` instead of `setType` is how you communicate
diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
index 7cc5176507..bc95b839b7 100644
--- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala
+++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
@@ -16,7 +16,7 @@ import java.io.IOException
import scala.reflect.internal.{ MissingRequirementError, JavaAccFlags, JMethodOrConstructor }
import internal.pickling.ByteCodecs
import internal.pickling.UnPickler
-import scala.collection.mutable.{ HashMap, ListBuffer }
+import scala.collection.mutable.{ HashMap, ListBuffer, ArrayBuffer }
import internal.Flags._
import ReflectionUtils._
import scala.language.existentials
@@ -42,7 +42,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
// overriden by ReflectGlobal
def rootClassLoader: ClassLoader = this.getClass.getClassLoader
- trait JavaClassCompleter extends FlagAssigningCompleter
+ trait JavaClassCompleter
def runtimeMirror(cl: ClassLoader): Mirror = gilSynchronized {
mirrors get cl match {
@@ -63,10 +63,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
private[reflect] lazy val runDefinitions = new definitions.RunDefinitions // only one "run" in the reflection universe
import runDefinitions._
- 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
+ override lazy val RootPackage = (new RootPackage with SynchronizedTermSymbol).markFlagsCompleted(mask = AllFlags)
+ override lazy val RootClass = (new RootClass with SynchronizedModuleClassSymbol).markFlagsCompleted(mask = AllFlags)
+ override lazy val EmptyPackage = (new EmptyPackage with SynchronizedTermSymbol).markFlagsCompleted(mask = AllFlags)
+ override lazy val EmptyPackageClass = (new EmptyPackageClass with SynchronizedModuleClassSymbol).markFlagsCompleted(mask = AllFlags)
/** The lazy type for root.
*/
@@ -118,15 +118,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
private def abort(msg: String) = throw new ScalaReflectionException(msg)
- private def ErrorInnerClass(sym: Symbol) = abort(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror")
- private def ErrorInnerModule(sym: Symbol) = abort(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror")
- private def ErrorStaticClass(sym: Symbol) = abort(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror")
- private def ErrorStaticModule(sym: Symbol) = abort(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror")
- private def ErrorNotMember(sym: Symbol, owner: Symbol) = abort(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}")
- private def ErrorNotField(sym: Symbol) = abort(s"expected a field or an accessor method symbol, you provided $sym")
- private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = abort(s"expected a constructor of $owner, you provided $sym")
- private def ErrorFree(member: Symbol, freeType: Symbol) = abort(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}")
- private def ErrorNonExistentField(sym: Symbol) = abort(
+ private def ErrorInnerClass(sym: Symbol) = abort(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror")
+ private def ErrorInnerModule(sym: Symbol) = abort(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror")
+ private def ErrorStaticClass(sym: Symbol) = abort(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror")
+ private def ErrorStaticModule(sym: Symbol) = abort(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror")
+ private def ErrorNotMember(sym: Symbol, owner: Symbol) = abort(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}")
+ private def ErrorNotField(sym: Symbol) = abort(s"expected a field or an accessor method symbol, you provided $sym")
+ private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = abort(s"expected a constructor of $owner, you provided $sym")
+ private def ErrorArrayConstructor(sym: Symbol, owner: Symbol) = abort(s"Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead")
+ private def ErrorFree(member: Symbol, freeType: Symbol) = abort(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}")
+ private def ErrorNonExistentField(sym: Symbol) = abort(
sm"""Scala field ${sym.name} isn't represented as a Java field, neither it has a Java accessor method
|note that private parameters of class constructors don't get mapped onto fields and/or accessors,
|unless they are used outside of their declaring constructors.""")
@@ -221,6 +222,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
private def checkConstructorOf(sym: Symbol, owner: ClassSymbol) {
if (!sym.isClassConstructor) ErrorNotConstructor(sym, owner)
+ if (owner == ArrayClass) ErrorArrayConstructor(sym, owner)
ensuringNotFree(sym) {
if (!owner.info.decls.toList.contains(sym)) ErrorNotConstructor(sym, owner)
}
@@ -247,7 +249,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
}
def reflectMethod(method: MethodSymbol): MethodMirror = {
checkMemberOf(method, symbol)
- mkJavaMethodMirror(instance, method)
+ mkMethodMirror(instance, method)
}
def reflectClass(cls: ClassSymbol): ClassMirror = {
if (cls.isStatic) ErrorStaticClass(cls)
@@ -262,16 +264,35 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
override def toString = s"instance mirror for $instance"
}
- private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol)
+ // caches value class metadata, so that we minimize the work that needs to be done during Mirror.apply
+ private class DerivedValueClassMetadata(info: Type) {
+ val symbol = info.typeSymbol
+ val isDerivedValueClass = symbol.isDerivedValueClass
+ lazy val boxer = runtimeClass(symbol.toType).getDeclaredConstructors().head
+ lazy val unboxer = {
+ val fields @ (field :: _) = symbol.toType.declarations.collect{ case ts: TermSymbol if ts.isParamAccessor && ts.isMethod => ts }.toList
+ assert(fields.length == 1, s"$symbol: $fields")
+ runtimeClass(symbol.asClass).getDeclaredMethod(field.name.toString)
+ }
+ }
+
+ private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol, metadata: DerivedValueClassMetadata)
extends FieldMirror {
+ def this(receiver: Any, symbol: TermSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.info))
+ def bind(newReceiver: Any) = new JavaFieldMirror(newReceiver, symbol, metadata)
+ import metadata._
+
lazy val jfield = ensureAccessible(fieldToJava(symbol))
- def get = jfield get receiver
+ def get = {
+ val value = jfield get receiver
+ if (isDerivedValueClass) boxer.newInstance(value) else value
+ }
def set(value: Any) = {
// it appears useful to be able to set values of vals, therefore I'm disabling this check
// if (!symbol.isMutable) ErrorSetImmutableField(symbol)
- jfield.set(receiver, value)
+ jfield.set(receiver, if (isDerivedValueClass) unboxer.invoke(value) else value)
}
- def bind(newReceiver: Any) = new JavaFieldMirror(newReceiver, symbol)
+
override def toString = s"field mirror for ${symbol.fullName} (bound to $receiver)"
}
@@ -312,13 +333,17 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
bytecodelessMethodOwners(meth.owner) && !bytecodefulObjectMethods(meth)
}
+ private def isByNameParam(p: Type) = isByNameParamType(p)
+ private def isValueClassParam(p: Type) = p.typeSymbol.isDerivedValueClass
+
// unlike other mirrors, method mirrors are created by a factory
// that's because we want to have decent performance
// therefore we move special cases into separate subclasses
// rather than have them on a hot path them in a unified implementation of the `apply` method
- private def mkJavaMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): JavaMethodMirror = {
- if (isBytecodelessMethod(symbol)) new JavaBytecodelessMethodMirror(receiver, symbol)
- else if (symbol.paramss.flatten exists (p => isByNameParamType(p.info))) new JavaByNameMethodMirror(receiver, symbol)
+ private def mkMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): MethodMirror = {
+ def existsParam(pred: Type => Boolean) = symbol.paramss.flatten.map(_.info).exists(pred)
+ if (isBytecodelessMethod(symbol)) new BytecodelessMethodMirror(receiver, symbol)
+ else if (existsParam(isByNameParam) || existsParam(isValueClassParam)) new JavaTransformingMethodMirror(receiver, symbol)
else {
symbol.paramss.flatten.length match {
case 0 => new JavaVanillaMethodMirror0(receiver, symbol)
@@ -330,68 +355,123 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
}
}
}
- private abstract class JavaMethodMirror(val symbol: MethodSymbol) extends MethodMirror {
+
+ private abstract class JavaMethodMirror(val symbol: MethodSymbol, protected val ret: DerivedValueClassMetadata) extends MethodMirror {
lazy val jmeth = ensureAccessible(methodToJava(symbol))
- def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*)
+ lazy val jconstr = ensureAccessible(constructorToJava(symbol))
- def jinvoke(jmeth: jMethod, receiver: Any, args: Seq[Any]): Any = {
- val result = jinvokeraw(jmeth, receiver, args)
- if (jmeth.getReturnType == java.lang.Void.TYPE) ()
+ def jinvokeraw(args: Seq[Any]) =
+ if (!symbol.isConstructor) jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*)
+ else if (receiver == null) jconstr.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*)
+ else jconstr.newInstance((receiver +: args).asInstanceOf[Seq[AnyRef]]: _*)
+ def jinvoke(args: Seq[Any]): Any = {
+ val result = jinvokeraw(args)
+ if (!symbol.isConstructor && jmeth.getReturnType == java.lang.Void.TYPE) ()
+ else if (!symbol.isConstructor && ret.isDerivedValueClass) ret.boxer.newInstance(result.asInstanceOf[AnyRef])
else result
}
- override def toString = s"method mirror for ${showMethodSig(symbol)} (bound to $receiver)"
- }
-
- private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol)
- extends JavaMethodMirror(symbol) {
- def bind(newReceiver: Any) = new JavaVanillaMethodMirror(newReceiver, symbol)
- def apply(args: Any*): Any = jinvoke(jmeth, receiver, args)
- }
-
- private class JavaVanillaMethodMirror0(receiver: Any, symbol: MethodSymbol)
- extends JavaVanillaMethodMirror(receiver, symbol) {
- override def bind(newReceiver: Any) = new JavaVanillaMethodMirror0(newReceiver, symbol)
- override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver)
- }
-
- private class JavaVanillaMethodMirror1(receiver: Any, symbol: MethodSymbol)
- extends JavaVanillaMethodMirror(receiver, symbol) {
- override def bind(newReceiver: Any) = new JavaVanillaMethodMirror1(newReceiver, symbol)
- override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef])
- }
-
- private class JavaVanillaMethodMirror2(receiver: Any, symbol: MethodSymbol)
- extends JavaVanillaMethodMirror(receiver, symbol) {
- override def bind(newReceiver: Any) = new JavaVanillaMethodMirror2(newReceiver, symbol)
- override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef])
- }
-
- private class JavaVanillaMethodMirror3(receiver: Any, symbol: MethodSymbol)
- extends JavaVanillaMethodMirror(receiver, symbol) {
- override def bind(newReceiver: Any) = new JavaVanillaMethodMirror3(newReceiver, symbol)
- override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef])
+ override def toString = {
+ val what = if (symbol.isConstructor) "constructor mirror" else "method mirror"
+ s"$what for ${showMethodSig(symbol)} (bound to $receiver)"
+ }
}
- private class JavaVanillaMethodMirror4(receiver: Any, symbol: MethodSymbol)
- extends JavaVanillaMethodMirror(receiver, symbol) {
- override def bind(newReceiver: Any) = new JavaVanillaMethodMirror4(newReceiver, symbol)
- override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef])
- }
+ private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata)
+ extends JavaMethodMirror(symbol, ret) {
+ def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType))
+ def bind(newReceiver: Any) = new JavaVanillaMethodMirror(newReceiver, symbol, ret)
+ def apply(args: Any*): Any = jinvoke(args)
+ }
+
+ private class JavaVanillaMethodMirror0(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata)
+ extends JavaVanillaMethodMirror(receiver, symbol, ret) {
+ def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType))
+ override def bind(newReceiver: Any) = new JavaVanillaMethodMirror0(newReceiver, symbol, ret)
+ override def jinvokeraw(args: Seq[Any]) =
+ if (!symbol.isConstructor) jmeth.invoke(receiver)
+ else if (receiver == null) jconstr.newInstance()
+ else jconstr.newInstance(receiver.asInstanceOf[AnyRef])
+ }
+
+ private class JavaVanillaMethodMirror1(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata)
+ extends JavaVanillaMethodMirror(receiver, symbol, ret) {
+ def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType))
+ override def bind(newReceiver: Any) = new JavaVanillaMethodMirror1(newReceiver, symbol, ret)
+ override def jinvokeraw(args: Seq[Any]) =
+ if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef])
+ else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef])
+ else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef])
+ }
+
+ private class JavaVanillaMethodMirror2(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata)
+ extends JavaVanillaMethodMirror(receiver, symbol, ret) {
+ def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType))
+ override def bind(newReceiver: Any) = new JavaVanillaMethodMirror2(newReceiver, symbol, ret)
+ override def jinvokeraw(args: Seq[Any]) =
+ if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef])
+ else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef])
+ else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef])
+ }
+
+ private class JavaVanillaMethodMirror3(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata)
+ extends JavaVanillaMethodMirror(receiver, symbol, ret) {
+ def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType))
+ override def bind(newReceiver: Any) = new JavaVanillaMethodMirror3(newReceiver, symbol, ret)
+ override def jinvokeraw(args: Seq[Any]) =
+ if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef])
+ else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef])
+ else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef])
+ }
+
+ private class JavaVanillaMethodMirror4(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata)
+ extends JavaVanillaMethodMirror(receiver, symbol, ret) {
+ def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType))
+ override def bind(newReceiver: Any) = new JavaVanillaMethodMirror4(newReceiver, symbol, ret)
+ override def jinvokeraw(args: Seq[Any]) =
+ if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef])
+ else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef])
+ else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef])
+ }
+
+ // caches MethodSymbol metadata, so that we minimize the work that needs to be done during Mirror.apply
+ // TODO: vararg is only supported in the last parameter list (SI-6182), so we don't need to worry about the rest for now
+ private class MethodMetadata(symbol: MethodSymbol) {
+ private val params = symbol.paramss.flatten.toArray
+ private val vcMetadata = params.map(p => new DerivedValueClassMetadata(p.info))
+ val isByName = params.map(p => isByNameParam(p.info))
+ def isDerivedValueClass(i: Int) = vcMetadata(i).isDerivedValueClass
+ def paramUnboxers(i: Int) = vcMetadata(i).unboxer
+ val paramCount = params.length
+ val ret = new DerivedValueClassMetadata(symbol.returnType)
+ }
+
+ private class JavaTransformingMethodMirror(val receiver: Any, symbol: MethodSymbol, metadata: MethodMetadata)
+ extends JavaMethodMirror(symbol, metadata.ret) {
+ def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new MethodMetadata(symbol))
+ override def bind(newReceiver: Any) = new JavaTransformingMethodMirror(newReceiver, symbol, metadata)
+ import metadata._
- private class JavaByNameMethodMirror(val receiver: Any, symbol: MethodSymbol)
- extends JavaMethodMirror(symbol) {
- def bind(newReceiver: Any) = new JavaByNameMethodMirror(newReceiver, symbol)
def apply(args: Any*): Any = {
- val transformed = map2(args.toList, symbol.paramss.flatten)((arg, param) => if (isByNameParamType(param.info)) () => arg else arg)
- jinvoke(jmeth, receiver, transformed)
+ val args1 = new Array[Any](args.length)
+ var i = 0
+ while (i < args1.length) {
+ val arg = args(i)
+ if (i >= paramCount) args1(i) = arg // don't transform varargs
+ else if (isByName(i)) args1(i) = () => arg // don't transform by-name value class params
+ else if (isDerivedValueClass(i)) args1(i) = paramUnboxers(i).invoke(arg)
+ i += 1
+ }
+ jinvoke(args1)
}
}
- private class JavaBytecodelessMethodMirror[T: ClassTag](val receiver: T, symbol: MethodSymbol)
- extends JavaMethodMirror(symbol) {
- def bind(newReceiver: Any) = new JavaBytecodelessMethodMirror(newReceiver.asInstanceOf[T], symbol)
- def apply(args: Any*): Any = {
+ private class BytecodelessMethodMirror[T: ClassTag](val receiver: T, val symbol: MethodSymbol)
+ extends MethodMirror {
+ def bind(newReceiver: Any) = new BytecodelessMethodMirror(newReceiver.asInstanceOf[T], symbol)
+ override def toString = s"bytecodeless method mirror for ${showMethodSig(symbol)} (bound to $receiver)"
+
+ def apply(args: Any*): Any = {
// checking type conformance is too much of a hassle, so we don't do it here
// actually it's not even necessary, because we manually dispatch arguments below
val params = symbol.paramss.flatten
@@ -414,7 +494,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
def invokePrimitiveMethod = {
val jmeths = classOf[BoxesRunTime].getDeclaredMethods.filter(_.getName == nme.primitiveMethodName(symbol.name).toString)
assert(jmeths.length == 1, jmeths.toList)
- jinvoke(jmeths.head, null, objReceiver +: objArgs)
+ val jmeth = jmeths.head
+ val result = jmeth.invoke(null, (objReceiver +: objArgs).asInstanceOf[Seq[AnyRef]]: _*)
+ if (jmeth.getReturnType == java.lang.Void.TYPE) ()
+ else result
}
symbol match {
@@ -445,23 +528,6 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
}
}
- private class JavaConstructorMirror(val outer: AnyRef, val symbol: MethodSymbol)
- extends MethodMirror {
- def bind(newReceiver: Any) = new JavaConstructorMirror(newReceiver.asInstanceOf[AnyRef], symbol)
- override val receiver = outer
- lazy val jconstr = ensureAccessible(constructorToJava(symbol))
- def apply(args: Any*): Any = {
- if (symbol.owner == ArrayClass)
- abort("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead")
-
- val effectiveArgs =
- if (outer == null) args.asInstanceOf[Seq[AnyRef]]
- else outer +: args.asInstanceOf[Seq[AnyRef]]
- jconstr.newInstance(effectiveArgs: _*)
- }
- override def toString = s"constructor mirror for ${showMethodSig(symbol)} (bound to $outer)"
- }
-
private abstract class JavaTemplateMirror
extends TemplateMirror {
def outer: AnyRef
@@ -474,7 +540,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
def isStatic = false
def reflectConstructor(constructor: MethodSymbol) = {
checkConstructorOf(constructor, symbol)
- new JavaConstructorMirror(outer, constructor)
+ mkMethodMirror(outer, constructor)
}
override def toString = s"class mirror for ${symbol.fullName} (bound to $outer)"
}
@@ -575,6 +641,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
val bytes = ssig.getBytes
val len = ByteCodecs.decode(bytes)
unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName)
+ markAllCompleted(clazz, module)
case None =>
loadBytes[Array[String]]("scala.reflect.ScalaLongSignature") match {
case Some(slsig) =>
@@ -583,6 +650,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
val len = ByteCodecs.decode(encoded)
val decoded = encoded.take(len)
unpickler.unpickle(decoded, 0, clazz, module, jclazz.getName)
+ markAllCompleted(clazz, module)
case None =>
// class does not have a Scala signature; it's a Java class
info("translating reflection info for Java " + jclazz) //debug
@@ -605,6 +673,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
private def createTypeParameter(jtvar: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = {
val tparam = sOwner(jtvar).newTypeParameter(newTypeName(jtvar.getName))
.setInfo(new TypeParamCompleter(jtvar))
+ markFlagsCompleted(tparam)(mask = AllFlags)
tparamCache enter (jtvar, tparam)
tparam
}
@@ -617,6 +686,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
override def load(sym: Symbol) = complete(sym)
override def complete(sym: Symbol) = {
sym setInfo TypeBounds.upper(glb(jtvar.getBounds.toList map typeToScala map objToAny))
+ markAllCompleted(sym)
}
}
@@ -655,7 +725,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
* @param module The Scala companion object for which info is copied
* @param jclazz The Java class
*/
- private class FromJavaClassCompleter(clazz: Symbol, module: Symbol, jclazz: jClass[_]) extends LazyType with JavaClassCompleter with FlagAssigningCompleter {
+ private class FromJavaClassCompleter(clazz: Symbol, module: Symbol, jclazz: jClass[_]) extends LazyType with JavaClassCompleter with FlagAgnosticCompleter {
+ // one doesn't need to do non-trivial computations to assign flags for Java-based reflection artifacts
+ // therefore I'm moving flag-assigning logic from completion to construction
+ val flags = jclazz.scalaFlags
+ clazz setFlag (flags | JAVA)
+ if (module != NoSymbol) {
+ module setFlag (flags & PRIVATE | JAVA)
+ module.moduleClass setFlag (flags & PRIVATE | JAVA)
+ }
+ markFlagsCompleted(clazz, module)(mask = AllFlags)
/** used to avoid cycles while initializing classes */
private var parentsLevel = 0
@@ -665,12 +744,6 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
override def load(sym: Symbol): Unit = {
debugInfo("completing from Java " + sym + "/" + clazz.fullName)//debug
assert(sym == clazz || (module != NoSymbol && (sym == module || sym == module.moduleClass)), sym)
- val flags = jclazz.scalaFlags
- clazz setFlag (flags | JAVA)
- if (module != NoSymbol) {
- module setFlag (flags & PRIVATE | JAVA)
- module.moduleClass setFlag (flags & PRIVATE | JAVA)
- }
propagatePackageBoundary(jclazz, relatedSymbols: _*)
copyAnnotations(clazz, jclazz)
@@ -686,6 +759,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
override def complete(sym: Symbol): Unit = {
load(sym)
completeRest()
+ markAllCompleted(clazz, module)
}
def completeRest(): Unit = gilSynchronized {
@@ -738,6 +812,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
class LazyPolyType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter {
override def complete(sym: Symbol) {
completeRest()
+ markAllCompleted(clazz, module)
}
}
}
@@ -892,6 +967,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
val pkg = owner.newPackage(name)
pkg.moduleClass setInfo new LazyPackageType
pkg setInfoAndEnter pkg.moduleClass.tpe
+ markFlagsCompleted(pkg)(mask = AllFlags)
info("made Scala "+pkg)
pkg
} else
@@ -1074,6 +1150,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
fieldCache.enter(jfield, field)
propagatePackageBoundary(jfield, field)
copyAnnotations(field, jfield)
+ markAllCompleted(field)
field
}
@@ -1100,11 +1177,9 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
setMethType(meth, tparams, paramtpes, resulttpe)
propagatePackageBoundary(jmeth.javaFlags, meth)
copyAnnotations(meth, jmeth)
-
- if (jmeth.javaFlags.isVarargs)
- meth modifyInfo arrayToRepeated
- else
- meth
+ if (jmeth.javaFlags.isVarargs) meth modifyInfo arrayToRepeated
+ markAllCompleted(meth)
+ meth
}
/**
@@ -1127,6 +1202,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
constr setInfo GenPolyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe))
propagatePackageBoundary(jconstr.javaFlags, constr)
copyAnnotations(constr, jconstr)
+ markAllCompleted(constr)
constr
}
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala
index cfd66744ff..f6556a442d 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala
@@ -12,6 +12,8 @@ class JavaUniverse extends internal.SymbolTable with JavaUniverseForce with Refl
override def inform(msg: String): Unit = log(msg)
def picklerPhase = internal.SomePhase
+ def erasurePhase = internal.SomePhase
+
lazy val settings = new Settings
private val isLogging = sys.props contains "scala.debug.reflect"
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index 8811b5513e..fb893cbff1 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -32,8 +32,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
// inaccessible: this._skolemizationLevel
// inaccessible: this._undoLog
// inaccessible: this._intersectionWitness
- // inaccessible: this._volatileRecursions
- // inaccessible: this._pendingVolatiles
// inaccessible: this._subsametypeRecursions
// inaccessible: this._pendingSubTypes
// inaccessible: this._basetypeRecursions
@@ -206,6 +204,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.NoSymbol
this.CyclicReference
// inaccessible: this.TypeHistory
+ this.SymbolOps
this.TermName
this.TypeName
this.Liftable
diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala
index 813c0e1386..d642b25127 100644
--- a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala
+++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala
@@ -12,7 +12,7 @@ import scala.reflect.internal.util.AbstractFileClassLoader
/** A few java-reflection oriented utility functions useful during reflection bootstrapping.
*/
-private[scala] object ReflectionUtils {
+object ReflectionUtils {
// Unwraps some chained exceptions which arise during reflective calls.
def unwrapThrowable(x: Throwable): Throwable = x match {
case _: InvocationTargetException | // thrown by reflectively invoked method or constructor
diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
index 30a3855d70..c56bc28d90 100644
--- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
+++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
@@ -6,6 +6,7 @@ import internal.Flags
import java.lang.{Class => jClass, Package => jPackage}
import scala.collection.mutable
import scala.reflect.runtime.ReflectionUtils.scalacShouldntLoadClass
+import scala.reflect.internal.Flags._
private[reflect] trait SymbolLoaders { self: SymbolTable =>
@@ -17,6 +18,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable =>
* is found, a package is created instead.
*/
class TopClassCompleter(clazz: Symbol, module: Symbol) extends SymLoader with FlagAssigningCompleter {
+ markFlagsCompleted(clazz, module)(mask = ~TopLevelPickledFlags)
override def complete(sym: Symbol) = {
debugInfo("completing "+sym+"/"+clazz.fullName)
assert(sym == clazz || sym == module || sym == module.moduleClass)
@@ -24,6 +26,8 @@ private[reflect] trait SymbolLoaders { self: SymbolTable =>
val loadingMirror = mirrorThatLoaded(sym)
val javaClass = loadingMirror.javaClass(clazz.javaClassName)
loadingMirror.unpickleClass(clazz, module, javaClass)
+ // NOTE: can't mark as thread-safe here, because unpickleClass might decide to delegate to FromJavaClassCompleter
+ // if (!isCompilerUniverse) markAllCompleted(clazz, module)
}
}
override def load(sym: Symbol) = complete(sym)
@@ -64,6 +68,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable =>
sym setInfo new ClassInfoType(List(), new PackageScope(sym), sym)
// override def safeToString = pkgClass.toString
openPackageModule(sym)
+ markAllCompleted(sym)
}
}
diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala
index ddbf3bd629..02155578f8 100644
--- a/src/reflect/scala/reflect/runtime/SymbolTable.scala
+++ b/src/reflect/scala/reflect/runtime/SymbolTable.scala
@@ -28,19 +28,4 @@ private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors w
* in order to prevent memory leaks: http://groups.google.com/group/scala-internals/browse_thread/thread/eabcf3d406dab8b2.
*/
override def isCompilerUniverse = false
-
- /** Unlike compiler universes, reflective universes can auto-initialize symbols on flag requests.
- *
- * scalac wasn't designed with such auto-initialization in mind, and quite often it makes assumptions
- * that flag requests won't cause initialization. Therefore enabling auto-init leads to cyclic errors.
- * We could probably fix those, but at the moment it's too risky.
- *
- * Reflective universes share codebase with scalac, but their surface is much smaller, which means less assumptions.
- * These assumptions are taken care of in this overriden `shouldTriggerCompleter` method.
- */
- override protected def shouldTriggerCompleter(symbol: Symbol, completer: Type, isFlagRelated: Boolean, mask: Long) =
- completer match {
- case _: TopClassCompleter | _: JavaClassCompleter => !isFlagRelated || (mask & TopLevelPickledFlags) != 0
- case _ => super.shouldTriggerCompleter(symbol, completer, isFlagRelated, mask)
- }
}
diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala
index 1a232c8de1..f5e16c6640 100644
--- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala
+++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala
@@ -4,6 +4,7 @@ package runtime
import scala.reflect.io.AbstractFile
import scala.collection.{ immutable, mutable }
+import scala.reflect.internal.Flags._
private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable =>
@@ -31,23 +32,105 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb
trait SynchronizedSymbol extends Symbol {
- def gilSynchronizedIfNotInited[T](body: => T): T = {
- // TODO JZ desired, but prone to race conditions. We need the runtime reflection based
- // type completers to establish a memory barrier upon initialization. Maybe a volatile
- // write? We need to consult with the experts here. Until them, lock pessimistically.
- //
- // `run/reflection-sync-subtypes.scala` fails about 1/50 times otherwise.
- //
- // if (isFullyInitialized) body
- // else gilSynchronized { body }
+ /** (Things written in this comment only applies to runtime reflection. Compile-time reflection,
+ * especially across phases and runs, is somewhat more complicated, but we won't be touching it,
+ * because at the moment we only care about synchronizing runtime reflection).
+ *
+ * As it has been noted on multiple occasions, generally speaking, reflection artifacts aren't thread-safe.
+ * Reasons for that differ from artifact to artifact. In some cases it's quite bad (e.g. types use a number
+ * of non-concurrent compiler caches, so we need to serialize certain operations on types in order to make
+ * sure that things stay deterministic). However, in case of symbols there's hope, because it's only during
+ * initializaton that symbols are thread-unsafe. After everything's set up, symbols become immutable
+ * (sans a few deterministic caches that can be populated simultaneously by multiple threads) and therefore thread-safe.
+ *
+ * Note that by saying "symbols become immutable" I mean literally that. In a very common case of PackageClassSymbol's,
+ * even when a symbol finishes its initialization and becomes immutable, its info forever remains mutable.
+ * Therefore even if we no longer need to synchronize a PackageClassSymbol after it's initialized, we still have to take
+ * care of its ClassInfoType (or, more precisely, of the underlying Scope), but that's done elsewhere, and
+ * here we don't need to worry about that.
+ *
+ * Okay, so now we simply check `Symbol.isInitialized` and if it's true, then everything's fine? Haha, nope!
+ * The thing is that some completers call sym.setInfo when still in-flight and then proceed with initialization
+ * (e.g. see LazyPackageType). Consequently, 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 to somehow distinguish between initialized and really initialized symbol states.
+ * Okay, let's do it on per-completer basis. We have seven kinds of completers to worry about:
+ * 1) LazyPackageType that initializes packages and their underlying package classes
+ * 2) TopClassCompleter that initializes top-level Scala-based class-module companion pairs of static definitions
+ * 3) LazyTypeRef and LazyTypeRefAndAlias set up by TopClassCompleter that initialize (transitive members) of top-level classes/modules
+ * 4) FromJavaClassCompleter that does the same for both top-level and non-toplevel Java-based classes/modules
+ * 5) Fully-initialized signatures of non-class/module Java-based reflection artifacts
+ * 6) Importing completer that transfers metadata from one universe to another
+ * 7) Signatures of special symbols such as roots and symbolsNotPresentInBytecode
+ *
+ * The mechanisms underlying completion are quite complex, and it'd be only natural to suppose that over time we're going to overlook something.
+ * Wrt isThreadsafe we could have two wrong situations: false positives (isThreadsafe = true, but the symbol isn't actually threadsafe)
+ * and false negatives (isThreadsafe = false, but the symbol is actually threadsafe). However, even though both are wrong, only the former
+ * is actively malicious. Indeed, false positives might lead to races, inconsistent state and crashes, while the latter would only cause
+ * `initialize` to be called and a gil to be taken on every potentially auto-initializable operation. Unpleasant yes, but still robust.
+ *
+ * What makes me hopeful is that:
+ * 1) By default (e.g. if some new completion mechanism gets introduced for a special flavor of symbols and we forget to call markCompleted)
+ * isThreadsafe is always in false negative state, which is unpleasant but safe.
+ * 2) Calls to `markCompleted` which are the only potential source of erroneous behavior are few and are relatively easy to place:
+ * just put them just before your completer's `complete` returns, and you should be fine.
+ *
+ * upd. Actually, there's another problem of not keeping initialization mask up-to-date. If we're not careful enough,
+ * then it might so happen that getting a certain flag that the compiler assumes to be definitively set will spuriously
+ * return isThreadsafe(purpose = FlagsOp(<flag>)) = false and that will lead to spurious auto-initialization,
+ * which will cause an SO or a cyclic reference or some other crash. I've done my best to go through all possible completers
+ * and call `markFlagsCompleted` where appropriate, but again over time something might be overlooked, so to guard against that
+ * I'm only considering TopLevelPickledFlags to be sources of potential initialization. This ensures that such system flags as
+ * isMethod, isModule or isPackage are never going to auto-initialize.
+ */
+ override def isThreadsafe(purpose: SymbolOps) = {
+ if (isCompilerUniverse) false
+ else if (_initialized) true
+ else purpose.isFlagRelated && (_initializationMask & purpose.mask & TopLevelPickledFlags) == 0
+ }
+
+ /** Communicates with completers declared in scala.reflect.runtime.SymbolLoaders
+ * about the status of initialization of the underlying symbol.
+ *
+ * Unfortunately, it's not as easy as just introducing the `markThreadsafe` method that would be called
+ * by the completers when they are really done (as opposed to `setInfo` that, as mentioned above, doesn't mean anything).
+ *
+ * Since we also want to auto-initialize symbols when certain methods are being called (`Symbol.hasFlag` for example),
+ * we need to track the identity of the initializer, so as to block until initialization is complete if the caller
+ * comes from a different thread, but to skip auto-initialization if we're the initializing thread.
+ *
+ * Just a volatile var is fine, because:
+ * 1) Status can only be changed in a single-threaded fashion (this is enforced by gilSynchronized
+ * that effecively guards `Symbol.initialize`), which means that there can't be update conflicts.
+ * 2) If someone reads a stale value of status, then the worst thing that might happen is that this someone
+ * is going to spuriously call `initialize`, which is either a gil-protected operation (if the symbol isn't inited yet)
+ * or a no-op (if the symbol is already inited), and that is fine in both cases.
+ *
+ * upd. It looks like we also need to keep track of a mask of initialized flags to make sure
+ * that normal symbol initialization routines don't trigger auto-init in Symbol.flags-related routines (e.g. Symbol.getFlag).
+ * Due to the same reasoning as above, a single volatile var is enough for to store the mask.
+ */
+ @volatile private[this] var _initialized = false
+ @volatile private[this] var _initializationMask = TopLevelPickledFlags
+ override def markFlagsCompleted(mask: Long): this.type = { _initializationMask = _initializationMask & ~mask; this }
+ override def markAllCompleted(): this.type = { _initializationMask = 0L; _initialized = true; this }
+
+ def gilSynchronizedIfNotThreadsafe[T](body: => T): T = {
+ // TODO: debug and fix the race that doesn't allow us uncomment this optimization
+ // if (isCompilerUniverse || isThreadsafe(purpose = AllOps)) body
+ // else gilSynchronized { body }
gilSynchronized { body }
}
- override def validTo = gilSynchronizedIfNotInited { super.validTo }
- override def info = gilSynchronizedIfNotInited { super.info }
- override def rawInfo: Type = gilSynchronizedIfNotInited { super.rawInfo }
+ override def validTo = gilSynchronizedIfNotThreadsafe { super.validTo }
+ override def info = gilSynchronizedIfNotThreadsafe { super.info }
+ override def rawInfo: Type = gilSynchronizedIfNotThreadsafe { super.rawInfo }
+ override def typeSignature: Type = gilSynchronizedIfNotThreadsafe { super.typeSignature }
+ override def typeSignatureIn(site: Type): Type = gilSynchronizedIfNotThreadsafe { super.typeSignatureIn(site) }
- override def typeParams: List[Symbol] = gilSynchronizedIfNotInited {
+ override def typeParams: List[Symbol] = gilSynchronizedIfNotThreadsafe {
if (isCompilerUniverse) super.typeParams
else {
if (isMonomorphicType) Nil
@@ -63,7 +146,7 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb
}
}
}
- override def unsafeTypeParams: List[Symbol] = gilSynchronizedIfNotInited {
+ override def unsafeTypeParams: List[Symbol] = gilSynchronizedIfNotThreadsafe {
if (isCompilerUniverse) super.unsafeTypeParams
else {
if (isMonomorphicType) Nil
@@ -71,8 +154,6 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb
}
}
- override def isStable: Boolean = gilSynchronized { super.isStable }
-
// ------ creators -------------------------------------------------------------------
override protected def createAbstractTypeSymbol(name: TypeName, pos: Position, newFlags: Long): AbstractTypeSymbol =
@@ -126,7 +207,7 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb
// 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) } }
+ override def typeAsMemberOf(pre: Type): Type = gilSynchronizedIfNotThreadsafe { typeAsMemberOfLock.synchronized { super.typeAsMemberOf(pre) } }
}
trait SynchronizedModuleSymbol extends ModuleSymbol with SynchronizedTermSymbol
@@ -135,7 +216,7 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb
// 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_* } }
+ override def tpe_* : Type = gilSynchronizedIfNotThreadsafe { tpeLock.synchronized { super.tpe_* } }
}
trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol
diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala
index 83d471f91e..9bcf85dd6f 100644
--- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala
+++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala
@@ -50,13 +50,6 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa
private lazy val _intersectionWitness = mkThreadLocalStorage(perRunCaches.newWeakMap[List[Type], sWeakRef[Type]]())
override def intersectionWitness = _intersectionWitness.get
- private lazy val _volatileRecursions = mkThreadLocalStorage(0)
- override def volatileRecursions = _volatileRecursions.get
- override def volatileRecursions_=(value: Int) = _volatileRecursions.set(value)
-
- private lazy val _pendingVolatiles = mkThreadLocalStorage(new mutable.HashSet[Symbol])
- override def pendingVolatiles = _pendingVolatiles.get
-
private lazy val _subsametypeRecursions = mkThreadLocalStorage(0)
override def subsametypeRecursions = _subsametypeRecursions.get
override def subsametypeRecursions_=(value: Int) = _subsametypeRecursions.set(value)