diff options
author | Paul Phillips <paulp@improving.org> | 2012-03-14 10:00:30 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-03-14 10:08:36 -0700 |
commit | 3ab383ae01a66208df4955bf2117dd2ea8eb2afe (patch) | |
tree | ac83e7e89310955ccbc317f57f34165f9781117c /src/compiler | |
parent | b6dde2b6cf5f66e96fc92c09c1fbe3b4a8dc348c (diff) | |
parent | 5dca64cefeed4bc3289e641949b103e5e806aa32 (diff) | |
download | scala-3ab383ae01a66208df4955bf2117dd2ea8eb2afe.tar.gz scala-3ab383ae01a66208df4955bf2117dd2ea8eb2afe.tar.bz2 scala-3ab383ae01a66208df4955bf2117dd2ea8eb2afe.zip |
Merge branch 'master' into merge-inline
Conflicts:
lib/scala-compiler.jar.desired.sha1
lib/scala-library-src.jar.desired.sha1
lib/scala-library.jar.desired.sha1
src/compiler/scala/reflect/internal/Definitions.scala
src/compiler/scala/reflect/internal/Importers.scala
src/compiler/scala/reflect/internal/Symbols.scala
src/compiler/scala/reflect/internal/Trees.scala
src/compiler/scala/reflect/internal/Types.scala
src/compiler/scala/tools/nsc/Global.scala
src/compiler/scala/tools/nsc/transform/Erasure.scala
src/compiler/scala/tools/nsc/transform/LiftCode.scala
src/compiler/scala/tools/nsc/transform/UnCurry.scala
src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
src/compiler/scala/tools/nsc/typechecker/Typers.scala
test/files/run/programmatic-main.check
test/files/speclib/instrumented.jar.desired.sha1
Diffstat (limited to 'src/compiler')
135 files changed, 5448 insertions, 3303 deletions
diff --git a/src/compiler/rootdoc.txt b/src/compiler/rootdoc.txt new file mode 100644 index 0000000000..173f604098 --- /dev/null +++ b/src/compiler/rootdoc.txt @@ -0,0 +1,6 @@ +The Scala compiler API. + +The following resources are useful for Scala plugin/compiler development: + - [[http://www.scala-lang.org/node/215 Scala development tutorials]] on [[http://www.scala-lang.org www.scala-lang.org]] + - [[https://wiki.scala-lang.org/display/SIW/ Scala Internals wiki]] + - [[http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ Scala compiler corner]], maintained by Miguel diff --git a/src/compiler/scala/reflect/internal/AnnotationInfos.scala b/src/compiler/scala/reflect/internal/AnnotationInfos.scala index c3dde3e6d1..9a7c79d856 100644 --- a/src/compiler/scala/reflect/internal/AnnotationInfos.scala +++ b/src/compiler/scala/reflect/internal/AnnotationInfos.scala @@ -116,6 +116,11 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => // Classfile annot: args empty. Scala annot: assocs empty. assert(args.isEmpty || assocs.isEmpty, atp) + // @xeno.by: necessary for reification, see Reifiers.scala for more info + private var orig: Tree = EmptyTree + def original = orig + def setOriginal(t: Tree): this.type = { orig = t; this } + override def toString = ( atp + (if (!args.isEmpty) args.mkString("(", ", ", ")") else "") + @@ -130,7 +135,7 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => private var forced = false private lazy val forcedInfo = try { - val result = lazyInfo + val result = lazyInfo if (result.pos == NoPosition) result setPos pos result } finally forced = true @@ -138,10 +143,12 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => def atp: Type = forcedInfo.atp def args: List[Tree] = forcedInfo.args def assocs: List[(Name, ClassfileAnnotArg)] = forcedInfo.assocs + def original: Tree = forcedInfo.original + def setOriginal(t: Tree): this.type = { forcedInfo.setOriginal(t); this } // We should always be able to print things without forcing them. override def toString = if (forced) forcedInfo.toString else "@<?>" - + override def pos: Position = if (forced) forcedInfo.pos else NoPosition } @@ -166,10 +173,16 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => def args: List[Tree] def assocs: List[(Name, ClassfileAnnotArg)] + // @xeno.by: necessary for reification, see Reifiers.scala for more info + def original: Tree + def setOriginal(t: Tree): this.type + /** Hand rolling Product. */ def _1 = atp def _2 = args def _3 = assocs + // @xeno.by: original hasn't become a product member for backward compatibility purposes + // def _4 = original def canEqual(other: Any) = other.isInstanceOf[AnnotationInfo] override def productPrefix = "AnnotationInfo" diff --git a/src/compiler/scala/reflect/internal/BaseTypeSeqs.scala b/src/compiler/scala/reflect/internal/BaseTypeSeqs.scala index 9e5c93753f..3753a45133 100644 --- a/src/compiler/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/compiler/scala/reflect/internal/BaseTypeSeqs.scala @@ -29,12 +29,12 @@ trait BaseTypeSeqs { this: SymbolTable => import definitions._ - protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = + protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = new BaseTypeSeq(parents, elems) /** Note: constructor is protected to force everyone to use the factory method newBaseTypeSeq instead. - * This is necessary because when run from reflection every base type sequence needs to have a - * SynchronizedBaseTypeSeq as mixin. + * This is necessary because when run from reflection every base type sequence needs to have a + * SynchronizedBaseTypeSeq as mixin. */ class BaseTypeSeq protected[BaseTypeSeqs] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { self => @@ -242,7 +242,7 @@ trait BaseTypeSeqs { // Console.println("computed baseTypeSeq of " + tsym.tpe + " " + parents + ": "+elems.toString)//DEBUG newBaseTypeSeq(parents, elems) } - + class MappedBaseTypeSeq(orig: BaseTypeSeq, f: Type => Type) extends BaseTypeSeq(orig.parents map f, orig.elems) { override def apply(i: Int) = f(orig.apply(i)) override def rawElem(i: Int) = f(orig.rawElem(i)) @@ -254,7 +254,7 @@ trait BaseTypeSeqs { override def exists(p: Type => Boolean) = elems exists (x => p(f(x))) override protected def maxDepthOfElems: Int = elems map (x => maxDpth(f(x))) max override def toString = elems.mkString("MBTS(", ",", ")") - } - + } + val CyclicInheritance = new Throwable } diff --git a/src/compiler/scala/reflect/internal/ClassfileConstants.scala b/src/compiler/scala/reflect/internal/ClassfileConstants.scala index f1bf41ede9..eec72d082d 100644 --- a/src/compiler/scala/reflect/internal/ClassfileConstants.scala +++ b/src/compiler/scala/reflect/internal/ClassfileConstants.scala @@ -88,6 +88,7 @@ object ClassfileConstants { final val ARRAY_TAG = '[' final val VOID_TAG = 'V' final val TVAR_TAG = 'T' + final val OBJECT_TAG = 'L' final val ANNOTATION_TAG = '@' final val SCALA_NOTHING = "scala.runtime.Nothing$" final val SCALA_NULL = "scala.runtime.Null$" @@ -359,7 +360,7 @@ object ClassfileConstants { res |= translateFlag(jflags & JAVA_ACC_INTERFACE) res } - + def classFlags(jflags: Int): Long = { initFields(jflags) isClass = true @@ -375,11 +376,11 @@ object ClassfileConstants { } } object FlagTranslation extends FlagTranslation { } - + def toScalaMethodFlags(flags: Int): Long = FlagTranslation methodFlags flags def toScalaClassFlags(flags: Int): Long = FlagTranslation classFlags flags def toScalaFieldFlags(flags: Int): Long = FlagTranslation fieldFlags flags - + @deprecated("Use another method in this object", "2.10.0") def toScalaFlags(flags: Int, isClass: Boolean = false, isField: Boolean = false): Long = ( if (isClass) toScalaClassFlags(flags) diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 2ca9c8bfd0..9114eb4b67 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -72,8 +72,7 @@ trait Definitions extends reflect.api.StandardDefinitions { tpnme.Float -> FLOAT_TAG, tpnme.Double -> DOUBLE_TAG, tpnme.Boolean -> BOOL_TAG, - tpnme.Unit -> VOID_TAG, - tpnme.Object -> TVAR_TAG + tpnme.Unit -> VOID_TAG ) private def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f) @@ -82,7 +81,7 @@ trait Definitions extends reflect.api.StandardDefinitions { private def boxedName(name: Name) = sn.Boxed(name.toTypeName) - lazy val abbrvTag = symbolsMap(ObjectClass :: ScalaValueClasses, nameToTag) + lazy val abbrvTag = symbolsMap(ScalaValueClasses, nameToTag) withDefaultValue OBJECT_TAG lazy val numericWeight = symbolsMapFilt(ScalaValueClasses, nameToWeight.keySet, nameToWeight) lazy val boxedModule = classesMap(x => getModule(boxedName(x))) lazy val boxedClass = classesMap(x => getClass(boxedName(x))) @@ -129,6 +128,7 @@ trait Definitions extends reflect.api.StandardDefinitions { FloatClass, DoubleClass ) + def ScalaValueClassCompanions: List[Symbol] = ScalaValueClasses map (_.companionSymbol) } object definitions extends AbsDefinitions with ValueClassDefinitions { @@ -222,8 +222,12 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val AnyClass = enterNewClass(ScalaPackageClass, tpnme.Any, Nil, ABSTRACT) lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectClass.typeConstructor) lazy val ObjectClass = getClass(sn.Object) - lazy val AnyCompanionClass = getRequiredClass("scala.AnyCompanion") initFlags (SEALED | ABSTRACT | TRAIT) - lazy val AnyValCompanionClass = getRequiredClass("scala.AnyValCompanion") initFlags (SEALED | ABSTRACT | TRAIT) + + // Note: this is not the type alias AnyRef, it's a companion-like + // object used by the @specialize annotation. + lazy val AnyRefModule = getMember(ScalaPackageClass, nme.AnyRef) + @deprecated("Use AnyRefModule", "2.10.0") + def Predef_AnyRef = AnyRefModule lazy val AnyValClass = ScalaPackageClass.info member tpnme.AnyVal orElse { val anyval = enterNewClass(ScalaPackageClass, tpnme.AnyVal, List(AnyClass.tpe, NotNullClass.tpe), 0L) @@ -285,13 +289,12 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val PredefModule: Symbol = getRequiredModule("scala.Predef") lazy val PredefModuleClass = PredefModule.moduleClass - // Note: this is not the type alias AnyRef, it's a val defined in Predef - // used by the @specialize annotation. - def Predef_AnyRef = getMember(PredefModule, nme.AnyRef) + def Predef_classOf = getMember(PredefModule, nme.classOf) def Predef_identity = getMember(PredefModule, nme.identity) def Predef_conforms = getMember(PredefModule, nme.conforms) def Predef_wrapRefArray = getMember(PredefModule, nme.wrapRefArray) + def Predef_??? = getMember(PredefModule, nme.???) /** Is `sym` a member of Predef with the given name? * Note: DON't replace this by sym == Predef_conforms/etc, as Predef_conforms is a `def` @@ -302,6 +305,11 @@ trait Definitions extends reflect.api.StandardDefinitions { (sym.name == name) && (sym.owner == PredefModule.moduleClass) ) + /** Specialization. + */ + lazy val SpecializableModule = getRequiredModule("scala.Specializable") + lazy val GroupOfSpecializable = SpecializableModule.info.member(newTypeName("Group")) + lazy val ConsoleModule: Symbol = getRequiredModule("scala.Console") lazy val ScalaRunTimeModule: Symbol = getRequiredModule("scala.runtime.ScalaRunTime") lazy val SymbolModule: Symbol = getRequiredModule("scala.Symbol") @@ -422,9 +430,6 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val FullManifestModule = getRequiredModule("scala.reflect.Manifest") lazy val OptManifestClass = getRequiredClass("scala.reflect.OptManifest") lazy val NoManifest = getRequiredModule("scala.reflect.NoManifest") - lazy val CodeClass = getClass(sn.Code) - lazy val CodeModule = getModule(sn.Code) - lazy val Code_lift = getMember(CodeModule, nme.lift_) lazy val ScalaSignatureAnnotation = getRequiredClass("scala.reflect.ScalaSignature") lazy val ScalaLongSignatureAnnotation = getRequiredClass("scala.reflect.ScalaLongSignature") @@ -609,14 +614,6 @@ trait Definitions extends reflect.api.StandardDefinitions { case _ => NoType } - /** To avoid unchecked warnings on polymorphic classes, translate - * a Foo[T] into a Foo[_] for use in the pattern matcher. - */ - def typeCaseType(clazz: Symbol) = clazz.tpe.normalize match { - case TypeRef(_, sym, args) if args.nonEmpty => newExistentialType(sym.typeParams, clazz.tpe) - case tp => tp - } - def seqType(arg: Type) = appliedType(SeqClass.typeConstructor, List(arg)) def arrayType(arg: Type) = appliedType(ArrayClass.typeConstructor, List(arg)) def byNameType(arg: Type) = appliedType(ByNameParamClass.typeConstructor, List(arg)) @@ -629,6 +626,29 @@ trait Definitions extends reflect.api.StandardDefinitions { if (phase.erasedTypes || forMSIL) ClassClass.tpe else appliedType(ClassClass.typeConstructor, List(arg)) + def vmClassType(arg: Type): Type = ClassType(arg) + def vmSignature(sym: Symbol, info: Type): String = signature(info) // !!! + + /** Given a class symbol C with type parameters T1, T2, ... Tn + * which have upper/lower bounds LB1/UB1, LB1/UB2, ..., LBn/UBn, + * returns an existential type of the form + * + * C[E1, ..., En] forSome { E1 >: LB1 <: UB1 ... en >: LBn <: UBn }. + */ + def classExistentialType(clazz: Symbol): Type = + newExistentialType(clazz.typeParams, clazz.tpe) + + /** Given type U, creates a Type representing Class[_ <: U]. + */ + def boundedClassType(upperBound: Type) = + appliedTypeAsUpperBounds(ClassClass.typeConstructor, List(upperBound)) + + /** To avoid unchecked warnings on polymorphic classes, translate + * a Foo[T] into a Foo[_] for use in the pattern matcher. + */ + @deprecated("Use classExistentialType", "2.10.0") + def typeCaseType(clazz: Symbol): Type = classExistentialType(clazz) + // // .NET backend // @@ -881,6 +901,9 @@ trait Definitions extends reflect.api.StandardDefinitions { try getModule(fullname.toTermName) catch { case _: MissingRequirementError => NoSymbol } + def termMember(owner: Symbol, name: String): Symbol = owner.info.member(newTermName(name)) + def typeMember(owner: Symbol, name: String): Symbol = owner.info.member(newTypeName(name)) + def getMember(owner: Symbol, name: Name): Symbol = { if (owner == NoSymbol) NoSymbol else owner.info.nonPrivateMember(name) match { @@ -964,8 +987,9 @@ trait Definitions extends reflect.api.StandardDefinitions { def isPrimitiveValueClass(sym: Symbol) = scalaValueClassesSet(sym) /** Is symbol a value class? */ - def isValueClass(sym: Symbol) = scalaValueClassesSet(sym) - def isNonUnitValueClass(sym: Symbol) = (sym != UnitClass) && isValueClass(sym) + def isValueClass(sym: Symbol) = scalaValueClassesSet(sym) + def isNonUnitValueClass(sym: Symbol) = isValueClass(sym) && (sym != UnitClass) + def isSpecializableClass(sym: Symbol) = isValueClass(sym) || (sym == AnyRefClass) def isScalaValueType(tp: Type) = scalaValueClassesSet(tp.typeSymbol) /** Is symbol a boxed value class, e.g. java.lang.Integer? */ diff --git a/src/compiler/scala/reflect/internal/ExistentialsAndSkolems.scala b/src/compiler/scala/reflect/internal/ExistentialsAndSkolems.scala new file mode 100644 index 0000000000..f1fe4fc118 --- /dev/null +++ b/src/compiler/scala/reflect/internal/ExistentialsAndSkolems.scala @@ -0,0 +1,50 @@ +/* NSC -- new scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import scala.collection.{ mutable, immutable } +import util._ + +/** The name of this trait defines the eventual intent better than + * it does the initial contents. + */ +trait ExistentialsAndSkolems { + self: SymbolTable => + + /** Map a list of type parameter symbols to skolemized symbols, which + * can be deskolemized to the original type parameter. (A skolem is a + * representation of a bound variable when viewed inside its scope.) + * !!!Adriaan: this does not work for hk types. + */ + def deriveFreshSkolems(tparams: List[Symbol]): List[Symbol] = { + class Deskolemizer extends LazyType { + override val typeParams = tparams + val typeSkolems = typeParams map (_.newTypeSkolem setInfo this) + override def complete(sym: Symbol) { + // The info of a skolem is the skolemized info of the + // actual type parameter of the skolem + sym setInfo sym.deSkolemize.info.substSym(typeParams, typeSkolems) + } + } + (new Deskolemizer).typeSkolems + } + + /** Convert to corresponding type parameters all skolems of method + * parameters which appear in `tparams`. + */ + def deskolemizeTypeParams(tparams: List[Symbol])(tp: Type): Type = { + class DeSkolemizeMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) if sym.isTypeSkolem && (tparams contains sym.deSkolemize) => + mapOver(typeRef(NoPrefix, sym.deSkolemize, args)) + case _ => + mapOver(tp) + } + } + new DeSkolemizeMap mapOver tp + } +} diff --git a/src/compiler/scala/reflect/internal/Flags.scala b/src/compiler/scala/reflect/internal/Flags.scala index 66af92be5f..270491d078 100644 --- a/src/compiler/scala/reflect/internal/Flags.scala +++ b/src/compiler/scala/reflect/internal/Flags.scala @@ -165,6 +165,7 @@ class Flags extends ModifierFlags { final val TRIEDCOOKING = 0x100000000000L // ``Cooking'' has been tried on this symbol // A Java method's type is ``cooked'' by transforming raw types to existentials + final val SYNCHRONIZED = 0x200000000000L // symbol is a method which should be marked ACC_SYNCHRONIZED // ------- shift definitions ------------------------------------------------------- final val InitialFlags = 0x0001FFFFFFFFFFFFL // flags that are enabled from phase 1. @@ -222,7 +223,7 @@ class Flags extends ModifierFlags { /** These modifiers appear in TreePrinter output. */ final val PrintableFlags: Long = ExplicitFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR | MACRO | - ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | BRIDGE | STATIC | VBRIDGE | SPECIALIZED + ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | BRIDGE | STATIC | VBRIDGE | SPECIALIZED | SYNCHRONIZED /** The two bridge flags */ final val BridgeFlags = BRIDGE | VBRIDGE @@ -384,7 +385,7 @@ class Flags extends ModifierFlags { case VBRIDGE => "<vbridge>" // (1L << 42) case VARARGS => "<varargs>" // (1L << 43) case TRIEDCOOKING => "<triedcooking>" // (1L << 44) - case 0x200000000000L => "" // (1L << 45) + case SYNCHRONIZED => "<synchronized>" // (1L << 45) case 0x400000000000L => "" // (1L << 46) case 0x800000000000L => "" // (1L << 47) case 0x1000000000000L => "" // (1L << 48) @@ -466,7 +467,7 @@ class Flags extends ModifierFlags { } protected final val rawFlagPickledOrder: Array[Long] = pickledListOrder.toArray - def flagOfModifier(mod: Modifier.Value): Long = mod match { + def flagOfModifier(mod: Modifier): Long = mod match { case Modifier.`protected` => PROTECTED case Modifier.`private` => PRIVATE case Modifier.`override` => OVERRIDE @@ -496,13 +497,13 @@ class Flags extends ModifierFlags { case Modifier.bynameParameter => BYNAMEPARAM } - def flagsOfModifiers(mods: List[Modifier.Value]): Long = + def flagsOfModifiers(mods: List[Modifier]): Long = (mods :\ 0L) { (mod, curr) => curr | flagOfModifier(mod) } - def modifierOfFlag(flag: Long): Option[Modifier.Value] = + def modifierOfFlag(flag: Long): Option[Modifier] = Modifier.values find { mod => flagOfModifier(mod) == flag } - def modifiersOfFlags(flags: Long): List[Modifier.Value] = + def modifiersOfFlags(flags: Long): List[Modifier] = pickledListOrder map (mask => modifierOfFlag(flags & mask)) flatMap { mod => mod } } diff --git a/src/compiler/scala/reflect/internal/HasFlags.scala b/src/compiler/scala/reflect/internal/HasFlags.scala index ec4e919bdc..8affd66cd5 100644 --- a/src/compiler/scala/reflect/internal/HasFlags.scala +++ b/src/compiler/scala/reflect/internal/HasFlags.scala @@ -136,7 +136,7 @@ trait HasFlags { /** Whether this entity has NONE of the flags in the given mask. */ def hasNoFlags(mask: Long): Boolean = !hasFlag(mask) - + protected def isSetting(f: Long, mask: Long) = !hasFlag(f) && ((mask & f) != 0L) protected def isClearing(f: Long, mask: Long) = hasFlag(f) && ((mask & f) != 0L) diff --git a/src/compiler/scala/reflect/internal/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala index b36191b025..1003fa804f 100644 --- a/src/compiler/scala/reflect/internal/Importers.scala +++ b/src/compiler/scala/reflect/internal/Importers.scala @@ -9,16 +9,34 @@ trait Importers { self: SymbolTable => val from: SymbolTable lazy val symMap: WeakHashMap[from.Symbol, Symbol] = new WeakHashMap + lazy val tpeMap: WeakHashMap[from.Type, Type] = new WeakHashMap + + // fixups and maps prevent stackoverflows in importer + var pendingSyms = 0 + var pendingTpes = 0 + lazy val fixups = collection.mutable.MutableList[Function0[Unit]]() + def addFixup(fixup: => Unit): Unit = fixups += (() => fixup) + def tryFixup(): Unit = { + if (pendingSyms == 0 && pendingTpes == 0) { + val fixups = this.fixups.toList + this.fixups.clear() + fixups foreach { _() } + } + } object reverse extends from.Importer { val from: self.type = self for ((fromsym, mysym) <- Importer.this.symMap) symMap += ((mysym, fromsym)) + for ((fromtpe, mytpe) <- Importer.this.tpeMap) tpeMap += ((mytpe, fromtpe)) } def importPosition(pos: from.Position): Position = NoPosition - def importSymbol(sym: from.Symbol): Symbol = { + def importSymbol(sym0: from.Symbol): Symbol = { def doImport(sym: from.Symbol): Symbol = { + if (symMap.contains(sym)) + return symMap(sym) + val myowner = importSymbol(sym.owner) val mypos = importPosition(sym.pos) val myname = importName(sym.name).toTermName @@ -32,7 +50,7 @@ trait Importers { self: SymbolTable => case x: from.MethodSymbol => linkReferenced(myowner.newMethod(myname, mypos, myflags), x, importSymbol) case x: from.ModuleSymbol => - linkReferenced(myowner.newModuleSymbol(myname, mypos, myflags), x, doImport) + linkReferenced(myowner.newModuleSymbol(myname, mypos, myflags), x, importSymbol) case x: from.FreeVar => newFreeVar(importName(x.name).toTermName, importType(x.tpe), x.value, myflags) case x: from.TermSymbol => @@ -44,14 +62,14 @@ trait Importers { self: SymbolTable => case y: from.Symbol => importSymbol(y) } myowner.newTypeSkolemSymbol(myname.toTypeName, origin, mypos, myflags) - /* - case x: from.ModuleClassSymbol => - val mysym = new ModuleClassSymbol(myowner, mypos, myname.toTypeName) - mysym.sourceModule = importSymbol(x.sourceModule) - mysym -*/ + case x: from.ModuleClassSymbol => + val mysym = myowner.newModuleClassSymbol(myname.toTypeName, mypos, myflags) + symMap(x) = mysym + mysym.sourceModule = importSymbol(x.sourceModule) + mysym case x: from.ClassSymbol => val mysym = myowner.newClassSymbol(myname.toTypeName, mypos, myflags) + symMap(x) = mysym if (sym.thisSym != sym) { mysym.typeOfThis = importType(sym.typeOfThis) mysym.thisSym.name = importName(sym.thisSym.name) @@ -63,7 +81,7 @@ trait Importers { self: SymbolTable => symMap(sym) = mysym mysym setFlag Flags.LOCKED mysym setInfo { - val mytypeParams = sym.typeParams map doImport + val mytypeParams = sym.typeParams map importSymbol new LazyPolyType(mytypeParams) { override def complete(s: Symbol) { val result = sym.info match { @@ -78,7 +96,8 @@ trait Importers { self: SymbolTable => mysym resetFlag Flags.LOCKED } // end doImport - def importOrRelink: Symbol = + def importOrRelink: Symbol = { + val sym = sym0 // makes sym visible in the debugger if (sym == null) null else if (sym == from.NoSymbol) @@ -86,112 +105,156 @@ trait Importers { self: SymbolTable => else if (sym.isRoot) definitions.RootClass else { - val myowner = importSymbol(sym.owner) - val myname = importName(sym.name) - if (sym.isModuleClass) { - assert(sym.sourceModule != NoSymbol, sym) - val mymodule = importSymbol(sym.sourceModule) - assert(mymodule != NoSymbol, sym) - assert(mymodule.moduleClass != NoSymbol, mymodule) - mymodule.moduleClass - } else if (myowner.isClass && !myowner.isRefinementClass && !(myowner hasFlag Flags.LOCKED) && sym.owner.info.decl(sym.name).exists) { - // symbol is in class scope, try to find equivalent one in local scope - if (sym.isOverloaded) - myowner.newOverloaded(myowner.thisType, sym.alternatives map importSymbol) - else { - var existing: Symbol = myowner.info.decl(myname) - if (existing.isOverloaded) { - existing = - if (sym.isMethod) { - val localCopy = doImport(sym) - existing filter (_.tpe matches localCopy.tpe) - } else { - existing filter (!_.isMethod) - } - assert(!existing.isOverloaded, - "import failure: cannot determine unique overloaded method alternative from\n "+ - (existing.alternatives map (_.defString) mkString "\n")+"\n that matches "+sym+":"+sym.tpe) + val name = sym.name + val owner = sym.owner + var scope = if (owner.isClass && !owner.isRefinementClass) owner.info else from.NoType + var existing = scope.decl(name) + if (sym.isPackageClass || sym.isModuleClass) existing = existing.moduleClass + if (!existing.exists) scope = from.NoType + + val myname = importName(name) + val myowner = importSymbol(owner) + val myscope = if (scope != from.NoType && !(myowner hasFlag Flags.LOCKED)) myowner.info else NoType + var myexisting = if (myscope != NoType) myowner.info.decl(myname) else NoSymbol // cannot load myexisting in general case, because it creates cycles for methods + if (sym.isPackageClass || sym.isModuleClass) myexisting = importSymbol(sym.sourceModule).moduleClass + if (!sym.isOverloaded && myexisting.isOverloaded) { + myexisting = + if (sym.isMethod) { + val localCopy = doImport(sym) + myexisting filter (_.tpe matches localCopy.tpe) + } else { + myexisting filter (!_.isMethod) } - if (existing != NoSymbol) existing - else { + assert(!myexisting.isOverloaded, + "import failure: cannot determine unique overloaded method alternative from\n "+ + (myexisting.alternatives map (_.defString) mkString "\n")+"\n that matches "+sym+":"+sym.tpe) + } + + val mysym = { + if (sym.isOverloaded) { + myowner.newOverloaded(myowner.thisType, sym.alternatives map importSymbol) + } else if (sym.isTypeParameter && sym.paramPos >= 0 && !(myowner hasFlag Flags.LOCKED)) { + assert(myowner.typeParams.length > sym.paramPos, + "import failure: cannot determine parameter "+sym+" (#"+sym.paramPos+") in "+ + myowner+typeParamsString(myowner.rawInfo)+"\n original symbol was: "+ + sym.owner+from.typeParamsString(sym.owner.info)) + myowner.typeParams(sym.paramPos) + } else { + if (myexisting != NoSymbol) { + myexisting + } else { val mysym = doImport(sym) - assert(myowner.info.decls.lookup(myname) == NoSymbol, myname+" "+myowner.info.decl(myname)+" "+existing) - myowner.info.decls enter mysym + + if (myscope != NoType) { + assert(myowner.info.decls.lookup(myname) == NoSymbol, myname+" "+myowner.info.decl(myname)+" "+myexisting) + myowner.info.decls enter mysym + } + mysym } } - } else if (sym.isTypeParameter && sym.paramPos >= 0 && !(myowner hasFlag Flags.LOCKED)) { - assert(myowner.typeParams.length > sym.paramPos, - "import failure: cannot determine parameter "+sym+" (#"+sym.paramPos+") in "+ - myowner+typeParamsString(myowner.rawInfo)+"\n original symbol was: "+ - sym.owner+from.typeParamsString(sym.owner.info)) - myowner.typeParams(sym.paramPos) - } else - doImport(sym) + } + + mysym } - symMap getOrElseUpdate (sym, importOrRelink) + } // end importOrRelink + + val sym = sym0 + if (symMap contains sym) { + symMap(sym) + } else { + pendingSyms += 1 + + try { + symMap getOrElseUpdate (sym, importOrRelink) + } finally { + pendingSyms -= 1 + tryFixup() + } + } } - def importType(tpe: from.Type): Type = tpe match { - case from.TypeRef(pre, sym, args) => - TypeRef(importType(pre), importSymbol(sym), args map importType) - case from.ThisType(clazz) => - ThisType(importSymbol(clazz)) - case from.SingleType(pre, sym) => - SingleType(importType(pre), importSymbol(sym)) - case from.MethodType(params, restpe) => - MethodType(params map importSymbol, importType(restpe)) - case from.PolyType(tparams, restpe) => - PolyType(tparams map importSymbol, importType(restpe)) - case from.NullaryMethodType(restpe) => - NullaryMethodType(importType(restpe)) - case from.ConstantType(constant @ from.Constant(_)) => - ConstantType(importConstant(constant)) - case from.SuperType(thistpe, supertpe) => - SuperType(importType(thistpe), importType(supertpe)) - case from.TypeBounds(lo, hi) => - TypeBounds(importType(lo), importType(hi)) - case from.BoundedWildcardType(bounds) => - BoundedWildcardType(importTypeBounds(bounds)) - case from.ClassInfoType(parents, decls, clazz) => - val myclazz = importSymbol(clazz) - val myscope = if (myclazz.isPackageClass) newPackageScope(myclazz) else newScope - val myclazzTpe = ClassInfoType(parents map importType, myscope, myclazz) - myclazz setInfo GenPolyType(myclazz.typeParams, myclazzTpe) // needed so that newly created symbols find their scope - decls foreach importSymbol // will enter itself into myclazz - myclazzTpe - case from.RefinedType(parents, decls) => - RefinedType(parents map importType, importScope(decls), importSymbol(tpe.typeSymbol)) - case from.ExistentialType(tparams, restpe) => - newExistentialType(tparams map importSymbol, importType(restpe)) - case from.OverloadedType(pre, alts) => - OverloadedType(importType(pre), alts map importSymbol) - case from.AntiPolyType(pre, targs) => - AntiPolyType(importType(pre), targs map importType) - case x: from.TypeVar => - TypeVar(importType(x.origin), importTypeConstraint(x.constr0), x.typeArgs map importType, x.params map importSymbol) - case from.NotNullType(tpe) => - NotNullType(importType(tpe)) - case from.AnnotatedType(annots, tpe, selfsym) => - AnnotatedType(annots map importAnnotationInfo, importType(tpe), importSymbol(selfsym)) - case from.ErrorType => - ErrorType - case from.WildcardType => - WildcardType - case from.NoType => - NoType - case from.NoPrefix => - NoPrefix - case null => - null + def importType(tpe: from.Type): Type = { + def doImport(tpe: from.Type): Type = tpe match { + case from.TypeRef(pre, sym, args) => + TypeRef(importType(pre), importSymbol(sym), args map importType) + case from.ThisType(clazz) => + ThisType(importSymbol(clazz)) + case from.SingleType(pre, sym) => + SingleType(importType(pre), importSymbol(sym)) + case from.MethodType(params, restpe) => + MethodType(params map importSymbol, importType(restpe)) + case from.PolyType(tparams, restpe) => + PolyType(tparams map importSymbol, importType(restpe)) + case from.NullaryMethodType(restpe) => + NullaryMethodType(importType(restpe)) + case from.ConstantType(constant @ from.Constant(_)) => + ConstantType(importConstant(constant)) + case from.SuperType(thistpe, supertpe) => + SuperType(importType(thistpe), importType(supertpe)) + case from.TypeBounds(lo, hi) => + TypeBounds(importType(lo), importType(hi)) + case from.BoundedWildcardType(bounds) => + BoundedWildcardType(importTypeBounds(bounds)) + case from.ClassInfoType(parents, decls, clazz) => + val myclazz = importSymbol(clazz) + val myscope = if (myclazz.isPackageClass) newPackageScope(myclazz) else newScope + val myclazzTpe = ClassInfoType(parents map importType, myscope, myclazz) + myclazz setInfo GenPolyType(myclazz.typeParams, myclazzTpe) // needed so that newly created symbols find their scope + decls foreach importSymbol // will enter itself into myclazz + myclazzTpe + case from.RefinedType(parents, decls) => + RefinedType(parents map importType, importScope(decls), importSymbol(tpe.typeSymbol)) + case from.ExistentialType(tparams, restpe) => + newExistentialType(tparams map importSymbol, importType(restpe)) + case from.OverloadedType(pre, alts) => + OverloadedType(importType(pre), alts map importSymbol) + case from.AntiPolyType(pre, targs) => + AntiPolyType(importType(pre), targs map importType) + case x: from.TypeVar => + TypeVar(importType(x.origin), importTypeConstraint(x.constr0), x.typeArgs map importType, x.params map importSymbol) + case from.NotNullType(tpe) => + NotNullType(importType(tpe)) + case from.AnnotatedType(annots, tpe, selfsym) => + AnnotatedType(annots map importAnnotationInfo, importType(tpe), importSymbol(selfsym)) + case from.ErrorType => + ErrorType + case from.WildcardType => + WildcardType + case from.NoType => + NoType + case from.NoPrefix => + NoPrefix + case null => + null + } // end doImport + + def importOrRelink: Type = + doImport(tpe) + + if (tpeMap contains tpe) { + tpeMap(tpe) + } else { + pendingTpes += 1 + + try { + tpeMap getOrElseUpdate (tpe, importOrRelink) + } finally { + pendingTpes -= 1 + tryFixup() + } + } } def importTypeBounds(bounds: from.TypeBounds) = importType(bounds).asInstanceOf[TypeBounds] - def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = - AnnotationInfo(importType(ann.atp), ann.args map importTree, ann.assocs map { - case (name, arg) => (importName(name), importAnnotArg(arg)) - }) + def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = { + val atp1 = importType(ann.atp) + val args1 = ann.args map importTree + val assocs1 = ann.assocs map { case (name, arg) => (importName(name), importAnnotArg(arg)) } + val original1 = importTree(ann.original) + AnnotationInfo(atp1, args1, assocs1) setOriginal original1 + } def importAnnotArg(arg: from.ClassfileAnnotArg): ClassfileAnnotArg = arg match { case from.LiteralAnnotArg(constant @ from.Constant(_)) => @@ -223,7 +286,7 @@ trait Importers { self: SymbolTable => new Modifiers(mods.flags, importName(mods.privateWithin), mods.annotations map importTree) def importImportSelector(sel: from.ImportSelector): ImportSelector = - new ImportSelector(importName(sel.name), sel.namePos, importName(sel.rename), sel.renamePos) + new ImportSelector(importName(sel.name), sel.namePos, if (sel.rename != null) importName(sel.rename) else null, sel.renamePos) def importTree(tree: from.Tree): Tree = { val mytree = tree match { @@ -265,6 +328,8 @@ trait Importers { self: SymbolTable => new Function(vparams map importValDef, importTree(body)) case from.Assign(lhs, rhs) => new Assign(importTree(lhs), importTree(rhs)) + case from.AssignOrNamedArg(lhs, rhs) => + new AssignOrNamedArg(importTree(lhs), importTree(rhs)) case from.If(cond, thenp, elsep) => new If(importTree(cond), importTree(thenp), importTree(elsep)) case from.Match(selector, cases) => @@ -326,21 +391,24 @@ trait Importers { self: SymbolTable => case null => null } - if (mytree != null) { - val mysym = if (tree hasSymbol) importSymbol(tree.symbol) else NoSymbol - val mytpe = importType(tree.tpe) - - mytree match { - case mytt: TypeTree => - val tt = tree.asInstanceOf[from.TypeTree] - if (mytree hasSymbol) mytt.symbol = mysym - if (tt.wasEmpty) mytt.defineType(mytpe) else mytt.setType(mytpe) - if (tt.original != null) mytt.setOriginal(importTree(tt.original)) - case _ => - if (mytree hasSymbol) mytree.symbol = importSymbol(tree.symbol) - mytree.tpe = importType(tree.tpe) + addFixup({ + if (mytree != null) { + val mysym = if (tree hasSymbol) importSymbol(tree.symbol) else NoSymbol + val mytpe = importType(tree.tpe) + + mytree match { + case mytt: TypeTree => + val tt = tree.asInstanceOf[from.TypeTree] + if (mytree hasSymbol) mytt.symbol = mysym + if (tt.wasEmpty) mytt.defineType(mytpe) else mytt.setType(mytpe) + if (tt.original != null) mytt.setOriginal(importTree(tt.original)) + case _ => + if (mytree hasSymbol) mytree.symbol = importSymbol(tree.symbol) + mytree.tpe = importType(tree.tpe) + } } - } + }) + tryFixup() mytree } @@ -356,4 +424,4 @@ trait Importers { self: SymbolTable => case _ => constant.value }) } -} +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/Kinds.scala b/src/compiler/scala/reflect/internal/Kinds.scala index e675be43dc..23bff950b8 100644 --- a/src/compiler/scala/reflect/internal/Kinds.scala +++ b/src/compiler/scala/reflect/internal/Kinds.scala @@ -128,7 +128,7 @@ trait Kinds { // @M sometimes hkargs != arg.typeParams, the symbol and the type may // have very different type parameters val hkparams = param.typeParams - + def kindCheck(cond: Boolean, f: KindErrors => KindErrors) { if (!cond) kindErrors = f(kindErrors) diff --git a/src/compiler/scala/reflect/internal/NameManglers.scala b/src/compiler/scala/reflect/internal/NameManglers.scala index 97a74c2383..12f56976c9 100644 --- a/src/compiler/scala/reflect/internal/NameManglers.scala +++ b/src/compiler/scala/reflect/internal/NameManglers.scala @@ -22,10 +22,10 @@ trait NameManglers { val MODULE_SUFFIX_STRING = NameTransformer.MODULE_SUFFIX_STRING val NAME_JOIN_STRING = NameTransformer.NAME_JOIN_STRING - + val MODULE_SUFFIX_NAME: TermName = newTermName(MODULE_SUFFIX_STRING) val NAME_JOIN_NAME: TermName = newTermName(NAME_JOIN_STRING) - + def flattenedName(segments: Name*): NameType = compactedString(segments mkString NAME_JOIN_STRING) /** @@ -76,12 +76,14 @@ trait NameManglers { val PROTECTED_PREFIX = "protected$" val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set" val SINGLETON_SUFFIX = ".type" - val SPECIALIZED_SUFFIX_STRING = "$sp" val SUPER_PREFIX_STRING = "super$" val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" - - val SETTER_SUFFIX: TermName = encode("_=") - val SPECIALIZED_SUFFIX_NAME: TermName = SPECIALIZED_SUFFIX_STRING + val SETTER_SUFFIX: TermName = encode("_=") + + @deprecated("2.10.0", "Use SPECIALIZED_SUFFIX") + def SPECIALIZED_SUFFIX_STRING = SPECIALIZED_SUFFIX.toString + @deprecated("2.10.0", "Use SPECIALIZED_SUFFIX") + def SPECIALIZED_SUFFIX_NAME: TermName = SPECIALIZED_SUFFIX.toTermName def isConstructorName(name: Name) = name == CONSTRUCTOR || name == MIXIN_CONSTRUCTOR def isExceptionResultName(name: Name) = name startsWith EXCEPTION_RESULT_PREFIX @@ -90,6 +92,7 @@ trait NameManglers { def isLocalName(name: Name) = name endsWith LOCAL_SUFFIX_STRING def isLoopHeaderLabel(name: Name) = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX) def isProtectedAccessorName(name: Name) = name startsWith PROTECTED_PREFIX + def isSuperAccessorName(name: Name) = name startsWith SUPER_PREFIX_STRING def isReplWrapperName(name: Name) = name containsName INTERPRETER_IMPORT_WRAPPER def isSetterName(name: Name) = name endsWith SETTER_SUFFIX def isTraitSetterName(name: Name) = isSetterName(name) && (name containsName TRAIT_SETTER_SEPARATOR_STRING) @@ -118,13 +121,13 @@ trait NameManglers { name.subName(i, name.length) } else name } - + def unspecializedName(name: Name): Name = ( - if (name endsWith SPECIALIZED_SUFFIX_NAME) + if (name endsWith SPECIALIZED_SUFFIX) name.subName(0, name.lastIndexOf('m') - 1) else name ) - + def macroMethodName(name: Name) = { val base = if (name.isTypeName) nme.TYPEkw else nme.DEFkw base append nme.MACRO append name @@ -140,8 +143,8 @@ trait NameManglers { * and another one belonging to the enclosing class, on Double. */ def splitSpecializedName(name: Name): (Name, String, String) = - if (name endsWith SPECIALIZED_SUFFIX_NAME) { - val name1 = name dropRight SPECIALIZED_SUFFIX_NAME.length + if (name endsWith SPECIALIZED_SUFFIX) { + val name1 = name dropRight SPECIALIZED_SUFFIX.length val idxC = name1 lastIndexOf 'c' val idxM = name1 lastIndexOf 'm' @@ -155,7 +158,7 @@ trait NameManglers { def getterToLocal(name: TermName): TermName = name append LOCAL_SUFFIX_STRING def getterToSetter(name: TermName): TermName = name append SETTER_SUFFIX def localToGetter(name: TermName): TermName = name dropRight LOCAL_SUFFIX_STRING.length - + def dropLocalSuffix(name: Name): Name = if (name endsWith ' ') name dropRight 1 else name def setterToGetter(name: TermName): TermName = { diff --git a/src/compiler/scala/reflect/internal/Names.scala b/src/compiler/scala/reflect/internal/Names.scala index 907b564d4c..5f38374f20 100644 --- a/src/compiler/scala/reflect/internal/Names.scala +++ b/src/compiler/scala/reflect/internal/Names.scala @@ -73,7 +73,7 @@ trait Names extends api.Names { /** Create a term name from the characters in cs[offset..offset+len-1]. */ def newTermName(cs: Array[Char], offset: Int, len: Int): TermName = newTermName(cs, offset, len, cachedString = null) - + def newTermName(cs: Array[Char]): TermName = newTermName(cs, 0, cs.length) def newTypeName(cs: Array[Char]): TypeName = newTypeName(cs, 0, cs.length) @@ -87,7 +87,7 @@ trait Names extends api.Names { var n = termHashtable(h) while ((n ne null) && (n.length != len || !equals(n.start, cs, offset, len))) n = n.next - + if (n ne null) n else { // The logic order here is future-proofing against the possibility @@ -135,7 +135,7 @@ trait Names extends api.Names { /** The name class. * TODO - resolve schizophrenia regarding whether to treat Names as Strings - * or Strings as Names. Give names the key functions the absence of which + * or Strings as Names. Give names the key functions the absence of which * make people want Strings all the time. */ sealed abstract class Name(protected val index: Int, protected val len: Int) extends AbsName with Function1[Int, Char] { @@ -166,7 +166,7 @@ trait Names extends api.Names { /** Return a new name of the same variety. */ def newName(str: String): ThisNameType - + /** Return a new name based on string transformation. */ def mapName(f: String => String): ThisNameType = newName(f(toString)) @@ -357,7 +357,7 @@ trait Names extends api.Names { def dropRight(n: Int) = subName(0, len - n) def drop(n: Int) = subName(n, len) - + def indexOf(ch: Char) = { val idx = pos(ch) if (idx == length) -1 else idx @@ -382,11 +382,18 @@ trait Names extends api.Names { } newTermName(cs, 0, len) } - + /** TODO - reconcile/fix that encode returns a Name but * decode returns a String. */ + /** !!! Duplicative but consistently named. + */ + def decoded: String = decode + def encoded: String = "" + encode + // def decodedName: ThisNameType = newName(decoded) + def encodedName: ThisNameType = encode + /** Replace operator symbols by corresponding $op_name. */ def encode: ThisNameType = { val str = toString @@ -418,7 +425,7 @@ trait Names extends api.Names { def longString: String = nameKind + " " + decode def debugString = { val s = decode ; if (isTypeName) s + "!" else s } } - + /** A name that contains no operator chars nor dollar signs. * TODO - see if it's any faster to do something along these lines. */ @@ -454,7 +461,7 @@ trait Names extends api.Names { sealed abstract class TermName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) { type ThisNameType = TermName protected[this] def thisName: TermName = this - + var next: TermName = termHashtable(hash) termHashtable(hash) = this def isTermName: Boolean = true @@ -481,7 +488,7 @@ trait Names extends api.Names { sealed abstract class TypeName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) { type ThisNameType = TypeName protected[this] def thisName: TypeName = this - + var next: TypeName = typeHashtable(hash) typeHashtable(hash) = this def isTermName: Boolean = false diff --git a/src/compiler/scala/reflect/internal/Phase.scala b/src/compiler/scala/reflect/internal/Phase.scala index acd3360c4f..89d643aacf 100644 --- a/src/compiler/scala/reflect/internal/Phase.scala +++ b/src/compiler/scala/reflect/internal/Phase.scala @@ -26,6 +26,8 @@ abstract class Phase(val prev: Phase) { if ((prev ne null) && (prev ne NoPhase)) prev.nx = this def next: Phase = nx + def hasNext = next != this + def iterator = Iterator.iterate(this)(_.next) takeWhile (p => p.next != p) def name: String def description: String = name @@ -37,7 +39,7 @@ abstract class Phase(val prev: Phase) { def refChecked: Boolean = false /** This is used only in unsafeTypeParams, and at this writing is - * overridden to false in namer, typer, and erasure. (And NoPhase.) + * overridden to false in parser, namer, typer, and erasure. (And NoPhase.) */ def keepsTypeParams = true def run(): Unit diff --git a/src/compiler/scala/reflect/internal/Scopes.scala b/src/compiler/scala/reflect/internal/Scopes.scala index 54d3de09cd..ef48d6102f 100644 --- a/src/compiler/scala/reflect/internal/Scopes.scala +++ b/src/compiler/scala/reflect/internal/Scopes.scala @@ -38,11 +38,11 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** Note: constructor is protected to force everyone to use the factory methods newScope or newNestedScope instead. - * This is necessary because when run from reflection every scope needs to have a - * SynchronizedScope as mixin. + * This is necessary because when run from reflection every scope needs to have a + * SynchronizedScope as mixin. */ class Scope protected[Scopes] (initElems: ScopeEntry = null) extends Iterable[Symbol] { - + protected[Scopes] def this(base: Scope) = { this(base.elems) nestinglevel = base.nestinglevel + 1 @@ -120,7 +120,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => * @param sym ... */ def enterUnique(sym: Symbol) { - assert(lookup(sym.name) == NoSymbol) + assert(lookup(sym.name) == NoSymbol, (sym.fullLocationString, lookup(sym.name).fullLocationString)) enter(sym) } @@ -319,7 +319,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** Create a new scope */ def newScope: Scope = new Scope() - + /** Create a new scope nested in another one with which it shares its elements */ def newNestedScope(outer: Scope): Scope = new Scope(outer) diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index c3a7dc23f3..84007425ed 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -94,11 +94,13 @@ trait StdNames extends NameManglers { self: SymbolTable => val EMPTY: NameType = "" val ANON_FUN_NAME: NameType = "$anonfun" + val ANON_CLASS_NAME: NameType = "$anon" val EMPTY_PACKAGE_NAME: NameType = "<empty>" val IMPORT: NameType = "<import>" val MODULE_VAR_SUFFIX: NameType = "$module" val ROOT: NameType = "<root>" val PACKAGE: NameType = "package" + val SPECIALIZED_SUFFIX: NameType = "$sp" // value types (and AnyRef) are all used as terms as well // as (at least) arguments to the @specialize annotation. @@ -152,6 +154,9 @@ trait StdNames extends NameManglers { self: SymbolTable => final val ClassfileAnnotation: NameType = "ClassfileAnnotation" final val Enum: NameType = "Enum" + final val Tree: NameType = "Tree" + final val TypeTree: NameType = "TypeTree" + // Annotation simple names, used in Namer final val BeanPropertyAnnot: NameType = "BeanProperty" final val BooleanBeanPropertyAnnot: NameType = "BooleanBeanProperty" @@ -240,7 +245,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val x_7 : NameType = "x$7" val x_8 : NameType = "x$8" val x_9 : NameType = "x$9" - + @switch def syntheticParamName(i: Int): TermName = i match { case 0 => nme.x_0 case 1 => nme.x_1 @@ -254,7 +259,9 @@ trait StdNames extends NameManglers { self: SymbolTable => case 9 => nme.x_9 case _ => newTermName("x$" + i) } - + + val ??? = encode("???") + val wrapRefArray: NameType = "wrapRefArray" val wrapByteArray: NameType = "wrapByteArray" val wrapShortArray: NameType = "wrapShortArray" @@ -270,8 +277,9 @@ trait StdNames extends NameManglers { self: SymbolTable => // Compiler utilized names // val productElementName: NameType = "productElementName" val Ident: NameType = "Ident" - val This: NameType = "This" val StringContext: NameType = "StringContext" + val This: NameType = "This" + val Tree : NameType = "Tree" val TYPE_ : NameType = "TYPE" val TypeTree: NameType = "TypeTree" val UNIT : NameType = "UNIT" @@ -302,8 +310,6 @@ trait StdNames extends NameManglers { self: SymbolTable => val classOf: NameType = "classOf" val clone_ : NameType = if (forMSIL) "MemberwiseClone" else "clone" // sn.OClone causes checkinit failure val conforms: NameType = "conforms" - val context : NameType = "_context" - val contextImplicit : NameType = "$context" val copy: NameType = "copy" val delayedInit: NameType = "delayedInit" val delayedInitArg: NameType = "delayedInit$body" @@ -327,6 +333,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val freeValue : NameType = "freeValue" val genericArrayOps: NameType = "genericArrayOps" val get: NameType = "get" + val getOrElse: NameType = "getOrElse" val hasNext: NameType = "hasNext" val hashCode_ : NameType = if (forMSIL) "GetHashCode" else "hashCode" val hash_ : NameType = "hash" @@ -339,11 +346,14 @@ trait StdNames extends NameManglers { self: SymbolTable => val isInstanceOf_ : NameType = "isInstanceOf" val isInstanceOf_Ob : NameType = "$isInstanceOf" val java: NameType = "java" + val key: NameType = "key" val lang: NameType = "lang" val length: NameType = "length" val lengthCompare: NameType = "lengthCompare" val lift_ : NameType = "lift" val macro_ : NameType = "macro" + val macroThis : NameType = "_this" + val macroContext : NameType = "_context" val main: NameType = "main" val map: NameType = "map" val mirror : NameType = "mirror" @@ -368,7 +378,9 @@ trait StdNames extends NameManglers { self: SymbolTable => val self: NameType = "self" val setAccessible: NameType = "setAccessible" val setAnnotations: NameType = "setAnnotations" - val setTypeSig: NameType = "setTypeSig" + val setSymbol: NameType = "setSymbol" + val setType: NameType = "setType" + val setTypeSignature: NameType = "setTypeSignature" val synchronized_ : NameType = "synchronized" val tail: NameType = "tail" val thisModuleType: NameType = "thisModuleType" @@ -426,12 +438,11 @@ trait StdNames extends NameManglers { self: SymbolTable => val toInteger: NameType = "toInteger" } - object tpnme extends TypeNames /*with LibraryTypeNames*/ with TypeNameMangling { + object tpnme extends AbsTypeNames with TypeNames /*with LibraryTypeNames*/ with TypeNameMangling { type NameType = TypeName protected implicit def createNameType(name: String): TypeName = newTypeNameCached(name) val REFINE_CLASS_NAME: NameType = "<refinement>" - val ANON_CLASS_NAME: NameType = "$anon" } /** For fully qualified type names. @@ -463,7 +474,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val javanme = nme.javaKeywords - object nme extends TermNames /*with LibraryTermNames*/ with TermNameMangling { + object nme extends AbsTermNames with TermNames /*with LibraryTermNames*/ with TermNameMangling { type NameType = TermName protected implicit def createNameType(name: String): TermName = newTermNameCached(name) @@ -606,7 +617,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val testLessOrEqualThan: NameType = "testLessOrEqualThan" val testLessThan: NameType = "testLessThan" val testNotEqual: NameType = "testNotEqual" - + val isBoxedNumberOrBoolean: NameType = "isBoxedNumberOrBoolean" val isBoxedNumber: NameType = "isBoxedNumber" @@ -658,7 +669,7 @@ trait StdNames extends NameManglers { self: SymbolTable => case `toDouble` => toDouble case _ => NO_NAME } - + val reflPolyCacheName: NameType = "reflPoly$Cache" val reflClassCacheName: NameType = "reflClass$Cache" val reflParamsCacheName: NameType = "reflParams$Cache" @@ -710,7 +721,6 @@ trait StdNames extends NameManglers { self: SymbolTable => val BoxedCharacter : TypeName val BoxedNumber : TypeName val Class : TypeName - val Code : TypeName val Delegate : TypeName val IOOBException : TypeName // IndexOutOfBoundsException val InvTargetException : TypeName // InvocationTargetException @@ -845,7 +855,6 @@ trait StdNames extends NameManglers { self: SymbolTable => final val BoxedCharacter: TypeName = "System.IConvertible" final val BoxedNumber: TypeName = "System.IConvertible" final val Class: TypeName = "System.Type" - final val Code: TypeName = tpnme.NO_NAME final val Delegate: TypeName = "System.MulticastDelegate" final val IOOBException: TypeName = "System.IndexOutOfRangeException" final val InvTargetException: TypeName = "System.Reflection.TargetInvocationException" @@ -879,7 +888,6 @@ trait StdNames extends NameManglers { self: SymbolTable => private class J2SENames extends JavaNames { final val BeanProperty: TypeName = "scala.beans.BeanProperty" final val BooleanBeanProperty: TypeName = "scala.beans.BooleanBeanProperty" - final val Code: TypeName = "scala.reflect.Code" final val JavaSerializable: TypeName = "java.io.Serializable" } diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index 2e799f914a..5ae8f5dbf4 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -8,6 +8,7 @@ package internal import scala.collection.{ mutable, immutable } import util._ +import scala.tools.nsc.util.WeakHashSet abstract class SymbolTable extends api.Universe with Collections @@ -15,6 +16,7 @@ abstract class SymbolTable extends api.Universe with Symbols with Types with Kinds + with ExistentialsAndSkolems with Scopes with Definitions with Constants @@ -41,7 +43,7 @@ abstract class SymbolTable extends api.Universe /** Override with final implementation for inlining. */ def debuglog(msg: => String): Unit = if (settings.debug.value) log(msg) def debugwarn(msg: => String): Unit = if (settings.debug.value) Console.err.println(msg) - + /** Overridden when we know more about what was happening during a failure. */ def supplementErrorMessage(msg: String): String = msg @@ -77,16 +79,29 @@ abstract class SymbolTable extends api.Universe type RunId = Int final val NoRunId = 0 + // sigh, this has to be public or atPhase doesn't inline. + var phStack: List[Phase] = Nil private var ph: Phase = NoPhase private var per = NoPeriod + final def atPhaseStack: List[Phase] = phStack final def phase: Phase = ph final def phase_=(p: Phase) { //System.out.println("setting phase to " + p) - assert((p ne null) && p != NoPhase) + assert((p ne null) && p != NoPhase, p) ph = p - per = (currentRunId << 8) + p.id + per = period(currentRunId, p.id) + } + final def pushPhase(ph: Phase): Phase = { + val current = phase + phase = ph + phStack ::= ph + current + } + final def popPhase(ph: Phase) { + phStack = phStack.tail + phase = ph } /** The current compiler run identifier. */ @@ -111,18 +126,23 @@ abstract class SymbolTable extends api.Universe final def phaseOf(period: Period): Phase = phaseWithId(phaseId(period)) final def period(rid: RunId, pid: Phase#Id): Period = - (currentRunId << 8) + pid + (rid << 8) + pid /** Perform given operation at given phase. */ @inline final def atPhase[T](ph: Phase)(op: => T): T = { - val current = phase - phase = ph + val saved = pushPhase(ph) try op - finally phase = current + finally popPhase(saved) } + - @inline final def afterPhase[T](ph: Phase)(op: => T): T = - atPhase(ph.next)(op) + /** Since when it is to be "at" a phase is inherently ambiguous, + * a couple unambiguously named methods. + */ + @inline final def beforePhase[T](ph: Phase)(op: => T): T = atPhase(ph)(op) + @inline final def afterPhase[T](ph: Phase)(op: => T): T = atPhase(ph.next)(op) + @inline final def afterCurrentPhase[T](op: => T): T = atPhase(phase.next)(op) + @inline final def beforePrevPhase[T](op: => T): T = atPhase(phase.prev)(op) @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = if (target != NoPhase && phase.id > target.id) atPhase(target)(op) else op @@ -257,9 +277,10 @@ abstract class SymbolTable extends api.Universe } } - def newWeakMap[K, V]() = recordCache(mutable.WeakHashMap[K, V]()) - def newMap[K, V]() = recordCache(mutable.HashMap[K, V]()) - def newSet[K]() = recordCache(mutable.HashSet[K]()) + def newWeakMap[K, V]() = recordCache(mutable.WeakHashMap[K, V]()) + def newMap[K, V]() = recordCache(mutable.HashMap[K, V]()) + def newSet[K]() = recordCache(mutable.HashSet[K]()) + def newWeakSet[K <: AnyRef]() = recordCache(new WeakHashSet[K]()) } /** Break into repl debugger if assertion is true. */ @@ -276,7 +297,7 @@ abstract class SymbolTable extends api.Universe /** The phase which has given index as identifier. */ val phaseWithId: Array[Phase] - + /** Is this symbol table part of reflexive mirror? In this case * operations need to be made thread safe. */ diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 415b32958d..853046e81a 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -17,7 +17,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => import definitions._ protected var ids = 0 - + val emptySymbolArray = new Array[Symbol](0) def symbolCount = ids // statistics @@ -38,14 +38,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => nextexid += 1 newTypeName("_" + nextexid + suffix) } - + // Set the fields which point companions at one another. Returns the module. def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = { moduleClass.sourceModule = m m setModuleClass moduleClass m } - + /** Create a new free variable. Its owner is NoSymbol. */ def newFreeVar(name: TermName, tpe: Type, value: Any, newFlags: Long = 0L): FreeVar = @@ -61,13 +61,18 @@ trait Symbols extends api.Symbols { self: SymbolTable => case n: TermName => newTermSymbol(n, pos, newFlags) case n: TypeName => newTypeSymbol(n, pos, newFlags) } - def typeSig: Type = info - def typeSigIn(site: Type): Type = site.memberInfo(this) + def enclosingClass: Symbol = enclClass + def enclosingMethod: Symbol = enclMethod + def thisPrefix: Type = thisType + def selfType: Type = typeOfThis + def typeSignature: Type = info + def typeSignatureIn(site: Type): Type = site memberInfo this + def asType: Type = tpe def asTypeIn(site: Type): Type = site.memberType(this) def asTypeConstructor: Type = typeConstructor def setInternalFlags(flag: Long): this.type = { setFlag(flag); this } - def setTypeSig(tpe: Type): this.type = { setInfo(tpe); this } + def setTypeSignature(tpe: Type): this.type = { setInfo(tpe); this } def setAnnotations(annots: AnnotationInfo*): this.type = { setAnnotations(annots.toList); this } } @@ -84,31 +89,36 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[this] var _rawowner = initOwner // Syncnote: need not be protected, as only assignment happens in owner_=, which is not exposed to api private[this] var _rawname = initName private[this] var _rawflags = 0L - + def rawowner = _rawowner def rawname = _rawname def rawflags = _rawflags - + protected def rawflags_=(x: FlagsType) { _rawflags = x } - + private var rawpos = initPos - + val id = nextId() // identity displayed when -uniqid private[this] var _validTo: Period = NoPeriod - + def validTo = _validTo def validTo_=(x: Period) { _validTo = x} def pos = rawpos def setPos(pos: Position): this.type = { this.rawpos = pos; this } - override def hasModifier(mod: Modifier.Value) = + /** !!! The logic after "hasFlag" is far too opaque to be unexplained. + * I'm guessing it's attempting to compensate for flag overloading, + * and embedding such logic in an undocumented island like this is a + * notarized guarantee of future breakage. + */ + override def hasModifier(mod: Modifier) = hasFlag(flagOfModifier(mod)) && (!(mod == Modifier.bynameParameter) || isTerm) && (!(mod == Modifier.covariant) || isType) - override def allModifiers: Set[Modifier.Value] = + override def modifiers: Set[Modifier] = Modifier.values filter hasModifier // ------ creators ------------------------------------------------------------------- @@ -169,10 +179,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def newTermSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): TermSymbol = new TermSymbol(this, pos, name) initFlags newFlags - + def newAbstractTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AbstractTypeSymbol = new AbstractTypeSymbol(this, pos, name) initFlags newFlags - + def newAliasTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AliasTypeSymbol = new AliasTypeSymbol(this, pos, name) initFlags newFlags @@ -184,10 +194,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = new ClassSymbol(this, pos, name) initFlags newFlags - + def newModuleClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleClassSymbol = new ModuleClassSymbol(this, pos, name) initFlags newFlags - + /** Derive whether it is an abstract type from the flags; after creation * the DEFERRED flag will be ignored. */ @@ -196,7 +206,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => newAliasTypeSymbol(name, pos, newFlags) else newAbstractTypeSymbol(name, pos, newFlags) - + def newTypeSkolemSymbol(name: TypeName, origin: AnyRef, pos: Position = NoPosition, newFlags: Long = 0L): TypeSkolem = if ((newFlags & DEFERRED) == 0L) new TypeSkolem(this, pos, name, origin) initFlags newFlags @@ -233,7 +243,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ final def newAliasType(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol = newAliasTypeSymbol(name, pos, newFlags) - + /** Symbol of an abstract type type T >: ... <: ... */ final def newAbstractType(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol = @@ -251,7 +261,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def freshName() = { cnt += 1; nme.syntheticParamName(cnt) } mmap(argtypess)(tp => newValueParameter(freshName(), focusPos(owner.pos), SYNTHETIC) setInfo tp) } - + def newSyntheticTypeParam(): Symbol = newSyntheticTypeParam("T0", 0L) def newSyntheticTypeParam(name: String, newFlags: Long): Symbol = newTypeParameter(newTypeName(name), NoPosition, newFlags) setInfo TypeBounds.empty def newSyntheticTypeParams(num: Int): List[Symbol] = (0 until num).toList map (n => newSyntheticTypeParam("T" + n, 0L)) @@ -259,9 +269,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Create a new existential type skolem with this symbol its owner, * based on the given symbol and origin. */ - def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = { - val skolem = newTypeSkolemSymbol(basis.name.toTypeName, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) - skolem setInfo (basis.info cloneInfo skolem) + def newExistentialSkolem(basis: Symbol, origin: AnyRef, name: TypeName = null, info: Type = null): TypeSkolem = { + val skolem = newTypeSkolemSymbol(if (name eq null) basis.name.toTypeName else name, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) + skolem setInfo (if (info eq null) basis.info cloneInfo skolem else info) } final def newExistential(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol = @@ -292,7 +302,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def newClass(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L) = newClassSymbol(name, pos, newFlags) - + /** A new class with its info set to a ClassInfoType with given scope and parents. */ def newClassWithInfo(name: TypeName, parents: List[Type], scope: Scope, pos: Position = NoPosition, newFlags: Long = 0L) = { val clazz = newClass(name, pos, newFlags) @@ -344,9 +354,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newAliasType(pos: Position, name: TypeName): Symbol = newAliasType(name, pos) @deprecated("Use the other signature", "2.10.0") def newAbstractType(pos: Position, name: TypeName): Symbol = newAbstractType(name, pos) - @deprecated("Use the other signature", "2.10.0") + @deprecated("Use the other signature", "2.10.0") def newExistential(pos: Position, name: TypeName): Symbol = newExistential(name, pos) - @deprecated("Use the other signature", "2.10.0") + @deprecated("Use the other signature", "2.10.0") def newMethod(pos: Position, name: TermName): MethodSymbol = newMethod(name, pos) // ----- locking and unlocking ------------------------------------------------------ @@ -830,7 +840,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => private def addModuleSuffix(n: Name): Name = if (needsModuleSuffix) n append nme.MODULE_SUFFIX_STRING else n - + def moduleSuffix: String = ( if (needsModuleSuffix) nme.MODULE_SUFFIX_STRING else "" @@ -838,15 +848,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Whether this symbol needs nme.MODULE_SUFFIX_STRING (aka $) appended on the java platform. */ def needsModuleSuffix = ( - hasModuleFlag + hasModuleFlag && !isMethod && !isImplClass && !isJavaDefined ) /** These should be moved somewhere like JavaPlatform. */ - def javaSimpleName: String = addModuleSuffix(nme.dropLocalSuffix(simpleName)).toString - def javaBinaryName: String = addModuleSuffix(fullNameInternal('/')).toString + def javaSimpleName: Name = addModuleSuffix(nme.dropLocalSuffix(simpleName)) + def javaBinaryName: Name = addModuleSuffix(fullNameInternal('/')) def javaClassName: String = addModuleSuffix(fullNameInternal('.')).toString /** The encoded full path name of this symbol, where outer names and inner names @@ -865,7 +875,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (owner.isEffectiveRoot) name else effectiveOwner.enclClass.fullNameAsName(separator) append separator append name ) - + def fullNameAsName(separator: Char): Name = nme.dropLocalSuffix(fullNameInternal(separator)) /** The encoded full path name of this symbol, where outer names and inner names @@ -1015,9 +1025,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Modifies this symbol's info in place. */ def modifyInfo(f: Type => Type): this.type = setInfo(f(info)) /** Substitute second list of symbols for first in current info. */ - def substInfo(syms0: List[Symbol], syms1: List[Symbol]) = modifyInfo(_.substSym(syms0, syms1)) - def setInfoOwnerAdjusted(info: Type): this.type = setInfo(info atOwner this) - + def substInfo(syms0: List[Symbol], syms1: List[Symbol]): this.type = + if (syms0.isEmpty) this + else modifyInfo(_.substSym(syms0, syms1)) + + def setInfoOwnerAdjusted(info: Type): this.type = setInfo(info atOwner this) + /** Set the info and enter this symbol into the owner's scope. */ def setInfoAndEnter(info: Type): this.type = { setInfo(info) @@ -1155,7 +1168,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => abort("typeConstructor inapplicable for " + this) /** The logic approximately boils down to finding the most recent phase - * which immediately follows any of namer, typer, or erasure. + * which immediately follows any of parser, namer, typer, or erasure. + * In effect that means this will return one of: + * + * - packageobjects (follows namer) + * - superaccessors (follows typer) + * - lazyvals (follows erasure) + * - null */ private def unsafeTypeParamPhase = { var ph = phase @@ -1273,14 +1292,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** After the typer phase (before, look at the definition's Modifiers), contains * the annotations attached to member a definition (class, method, type, field). */ - def annotations: List[AnnotationInfo] = _annotations + def annotations: List[AnnotationInfo] = { + // Necessary for reflection, see SI-5423 + if (inReflexiveMirror) + initialize - /** This getter is necessary for reflection, see https://issues.scala-lang.org/browse/SI-5423 - * We could auto-inject completion into `annotations' and `setAnnotations', but I'm not sure about that - * @odersky writes: I fear we can't do the forcing for all compiler symbols as that could introduce cycles - */ - def getAnnotations: List[AnnotationInfo] = { - initialize _annotations } @@ -1329,7 +1345,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ final def isNestedIn(that: Symbol): Boolean = owner == that || owner != NoSymbol && (owner isNestedIn that) - + /** Is this class symbol a subclass of that symbol, * and is this class symbol also different from Null or Nothing? */ def isNonBottomSubClass(that: Symbol): Boolean = false @@ -1375,15 +1391,25 @@ trait Symbols extends api.Symbols { self: SymbolTable => cloneSymbol(owner) /** A clone of this symbol, but with given owner. */ - final def cloneSymbol(owner: Symbol): Symbol = cloneSymbol(owner, this.rawflags) - final def cloneSymbol(owner: Symbol, newFlags: Long): Symbol = { - val newSym = cloneSymbolImpl(owner, newFlags) - ( newSym + final def cloneSymbol(newOwner: Symbol): Symbol = + cloneSymbol(newOwner, this.rawflags) + final def cloneSymbol(newOwner: Symbol, newFlags: Long): Symbol = + cloneSymbol(newOwner, newFlags, nme.NO_NAME) + final def cloneSymbol(newOwner: Symbol, newFlags: Long, newName: Name): Symbol = { + val clone = cloneSymbolImpl(newOwner, newFlags) + ( clone setPrivateWithin privateWithin - setInfo (info cloneInfo newSym) + setInfo (this.info cloneInfo clone) setAnnotations this.annotations ) + if (clone.thisSym != clone) + clone.typeOfThis = (clone.typeOfThis cloneInfo clone) + if (newName != nme.NO_NAME) + clone.name = newName + + clone } + /** Internal method to clone a symbol's implementation with the given flags and no info. */ def cloneSymbolImpl(owner: Symbol, newFlags: Long): Symbol def cloneSymbolImpl(owner: Symbol): Symbol = cloneSymbolImpl(owner, 0L) @@ -1573,11 +1599,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isMethod || isClass) this else owner.logicallyEnclosingMember + /** Kept for source compatibility with 2.9. Scala IDE for Eclipse relies on this. */ + @deprecated("Use enclosingTopLevelClass") + def toplevelClass: Symbol = enclosingTopLevelClass + /** The top-level class containing this symbol. */ - def toplevelClass: Symbol = + def enclosingTopLevelClass: Symbol = if (owner.isPackageClass) { if (isClass) this else moduleClass - } else owner.toplevelClass + } else owner.enclosingTopLevelClass /** Is this symbol defined in the same scope and compilation unit as `that` symbol? */ def isCoDefinedWith(that: Symbol) = ( @@ -1695,6 +1725,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * (which is always the interface, by convention) * - before erasure, it looks up the interface name in the scope of the owner of the class. * This only works for implementation classes owned by other classes or traits. + * !!! Why? */ final def toInterface: Symbol = if (isImplClass) { @@ -1847,7 +1878,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Remove any access boundary and clear flags PROTECTED | PRIVATE. */ def makePublic = this setPrivateWithin NoSymbol resetFlag AccessFlags - + /** The first parameter to the first argument list of this method, * or NoSymbol if inapplicable. */ @@ -1881,7 +1912,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def sourceFile: AbstractFileType = if (isModule) moduleClass.sourceFile - else toplevelClass.sourceFile + else enclosingTopLevelClass.sourceFile def sourceFile_=(f: AbstractFileType) { abort("sourceFile_= inapplicable for " + this) @@ -2071,6 +2102,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def infosString = infos.toString() + def debugLocationString = fullLocationString + " " + debugFlagString + def debugFlagString = hasFlagsToString(-1L) def hasFlagsToString(mask: Long): String = flagsToString( flags & mask, if (hasAccessBoundary) privateWithin.toString else "" @@ -2129,7 +2162,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def referenced: Symbol = _referenced def referenced_=(x: Symbol) { _referenced = x } - + def existentialBound = singletonBounds(this.tpe) def cloneSymbolImpl(owner: Symbol, newFlags: Long): Symbol = @@ -2169,7 +2202,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } def setLazyAccessor(sym: Symbol): TermSymbol = { - assert(isLazy && (referenced == NoSymbol || referenced == sym), (this, hasFlagsToString(-1L), referenced, sym)) + assert(isLazy && (referenced == NoSymbol || referenced == sym), (this, debugFlagString, referenced, sym)) referenced = sym this } @@ -2223,7 +2256,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!isMethod && needsFlatClasses) { if (flatname eq null) flatname = nme.flattenedName(rawowner.name, rawname) - + flatname } else rawname.toTermName @@ -2259,7 +2292,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => res } } - + class AliasTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) { // Temporary programmatic help tracking down who might do such a thing @@ -2274,13 +2307,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def cloneSymbolImpl(owner: Symbol, newFlags: Long): AliasTypeSymbol = owner.newAliasTypeSymbol(name, pos, newFlags) } - + class AbstractTypeSymbol(initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) with AbstractTypeMixin { override def cloneSymbolImpl(owner: Symbol, newFlags: Long): AbstractTypeSymbol = owner.newAbstractTypeSymbol(name, pos, newFlags) } - + /** Might be mixed into TypeSymbol or TypeSkolem. */ trait AbstractTypeMixin extends TypeSymbol { @@ -2310,15 +2343,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Overridden in subclasses for which it makes sense. */ - def existentialBound: Type = abort("unexpected type: "+this.getClass+ " "+this.fullLocationString+ " " + hasFlagsToString(-1L)) + def existentialBound: Type = abort("unexpected type: "+this.getClass+ " "+debugLocationString) - override def name: TypeName = super.name.asInstanceOf[TypeName] + override def name: TypeName = super.name.toTypeName final override def isType = true override def isNonClassType = true override def isAbstractType = { if (settings.debug.value) { if (isDeferred) { - println("TypeSymbol claims to be abstract type: " + this.getClass + " " + hasFlagsToString(-1L) + " at ") + println("TypeSymbol claims to be abstract type: " + this.getClass + " " + debugFlagString + " at ") (new Throwable).printStackTrace } } @@ -2506,19 +2539,19 @@ trait Symbols extends api.Symbols { self: SymbolTable => } thisTypeCache } - + override def owner: Symbol = if (needsFlatClasses) rawowner.owner else rawowner override def name: TypeName = ( if (needsFlatClasses) { if (flatname eq null) flatname = nme.flattenedName(rawowner.name, rawname).toTypeName - + flatname } else rawname.toTypeName ) - + /** A symbol carrying the self type of the class as its type */ override def thisSym: Symbol = thissym @@ -2590,7 +2623,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } class FreeVar(name0: TermName, val value: Any) extends TermSymbol(NoSymbol, NoPosition, name0) { - override def hashCode = value.hashCode + override def hashCode = if (value == null) 0 else value.hashCode override def equals(other: Any): Boolean = other match { case that: FreeVar => this.value.asInstanceOf[AnyRef] eq that.value.asInstanceOf[AnyRef] case _ => false @@ -2613,7 +2646,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def defString: String = toString override def locationString: String = "" override def enclClass: Symbol = this - override def toplevelClass: Symbol = this + override def enclosingTopLevelClass: Symbol = this override def enclMethod: Symbol = this override def sourceFile: AbstractFileType = null override def ownerChain: List[Symbol] = List() @@ -2689,6 +2722,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def cloneSymbolsAndModify(syms: List[Symbol], infoFn: Type => Type): List[Symbol] = cloneSymbols(syms) map (_ modifyInfo infoFn) + def cloneSymbolsAtOwnerAndModify(syms: List[Symbol], owner: Symbol, infoFn: Type => Type): List[Symbol] = + cloneSymbolsAtOwner(syms, owner) map (_ modifyInfo infoFn) /** Functions which perform the standard clone/substituting on the given symbols and type, * then call the creator function with the new symbols and type as arguments. @@ -2701,7 +2736,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => val syms1 = cloneSymbolsAtOwner(syms, owner) creator(syms1, tpe.substSym(syms, syms1)) } - + /** A deep map on a symbol's paramss. */ def mapParamss[T](sym: Symbol)(f: Symbol => T): List[List[T]] = mmap(sym.info.paramss)(f) @@ -2725,5 +2760,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert(validFrom != NoPeriod) override def toString() = "TypeHistory(" + phaseOf(validFrom)+":"+runId(validFrom) + "," + info + "," + prev + ")" + + def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) } } diff --git a/src/compiler/scala/reflect/internal/TreeGen.scala b/src/compiler/scala/reflect/internal/TreeGen.scala index def1350187..89585724f1 100644 --- a/src/compiler/scala/reflect/internal/TreeGen.scala +++ b/src/compiler/scala/reflect/internal/TreeGen.scala @@ -249,20 +249,22 @@ abstract class TreeGen { * var x: T = _ * which is appropriate to the given Type. */ - def mkZero(tp: Type): Tree = { - val tree = tp.typeSymbol match { - case UnitClass => Literal(Constant()) - case BooleanClass => Literal(Constant(false)) - case FloatClass => Literal(Constant(0.0f)) - case DoubleClass => Literal(Constant(0.0d)) - case ByteClass => Literal(Constant(0.toByte)) - case ShortClass => Literal(Constant(0.toShort)) - case IntClass => Literal(Constant(0)) - case LongClass => Literal(Constant(0L)) - case CharClass => Literal(Constant(0.toChar)) - case _ => Literal(Constant(null)) - } - tree setType tp + def mkZero(tp: Type): Tree = tp.typeSymbol match { + case NothingClass => mkMethodCall(Predef_???, Nil) setType NothingClass.tpe + case _ => Literal(mkConstantZero(tp)) setType tp + } + + def mkConstantZero(tp: Type): Constant = tp.typeSymbol match { + case UnitClass => Constant(()) + case BooleanClass => Constant(false) + case FloatClass => Constant(0.0f) + case DoubleClass => Constant(0.0d) + case ByteClass => Constant(0.toByte) + case ShortClass => Constant(0.toShort) + case IntClass => Constant(0) + case LongClass => Constant(0L) + case CharClass => Constant(0.toChar) + case _ => Constant(null) } def mkZeroContravariantAfterTyper(tp: Type): Tree = { diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index ecd31c784d..769d7a9ed1 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -146,7 +146,7 @@ abstract class TreeInfo { true } - + /** * Selects the correct parameter list when there are nested applications. * Given Apply(fn, args), args might correspond to any of fn.symbol's parameter @@ -175,7 +175,7 @@ abstract class TreeInfo { } def foreachMethodParamAndArg(t: Tree)(f: (Symbol, Tree) => Unit): Unit = t match { case Apply(fn, args) => foreachMethodParamAndArg(applyMethodParameters(fn), args)(f) - case _ => + case _ => } /** Is symbol potentially a getter of a variable? diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index e7ba0c793d..5845eda5ca 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -24,21 +24,30 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => } def quotedName(name: Name): String = quotedName(name, false) + private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { + val sym = tree.symbol + if (sym != null && sym != NoSymbol) { + val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" + var suffix = "" + if (settings.uniqid.value) suffix += ("#" + sym.id) + if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) + prefix + tree.symbol.decodedName + suffix + } else { + quotedName(name, decoded) + } + } + + def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) + def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) + /** Turns a path into a String, introducing backquotes * as necessary. */ def backquotedPath(t: Tree): String = { - def suffix(t: Tree) = { - var suffix = "" - if (t.hasSymbol && settings.uniqid.value) suffix += ("#" + t.symbol.id) - if (t.hasSymbol && settings.Yshowsymkinds.value) suffix += ("#" + t.symbol.abbreviatedKindString) - suffix - } - t match { - case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), quotedName(name)) + suffix(t) - case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), quotedName(name)) + suffix(t) - case Ident(name) => quotedName(name) + suffix(t) + case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), symName(t, name)) + case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), symName(t, name)) + case Ident(name) => symName(t, name) case _ => t.toString } } @@ -128,18 +137,6 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => } private def ifSym(tree: Tree, p: Symbol => Boolean) = symFn(tree, p, false) - private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { - def nameFn(sym: Symbol) = { - val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" - val suffix = if (uniqueIds) "#"+sym.id else "" - prefix + tree.symbol.decodedName + suffix - } - symFn(tree, nameFn, quotedName(name, decoded)) - } - - def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) - def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) - def printOpt(prefix: String, tree: Tree) { if (!tree.isEmpty) { print(prefix, tree) } } @@ -307,6 +304,9 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => case Assign(lhs, rhs) => print(lhs, " = ", rhs) + case AssignOrNamedArg(lhs, rhs) => + print(lhs, " = ", rhs) + case If(cond, thenp, elsep) => print("if (", cond, ")"); indent; println() print(thenp); undent @@ -427,7 +427,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => case name: Name => print(quotedName(name)) case arg => - out.print(arg.toString) + out.print(if (arg == null) "null" else arg.toString) } } diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index 0004311647..e576f09f56 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -84,9 +84,9 @@ trait Trees extends api.Trees { self: SymbolTable => def withPosition(flag: Long, position: Position) = copy() setPositions positions + (flag -> position) - override def hasModifier(mod: Modifier.Value) = + override def hasModifier(mod: Modifier) = hasFlag(flagOfModifier(mod)) - override def allModifiers: Set[Modifier.Value] = + override def modifiers: Set[Modifier] = Modifier.values filter hasModifier override def mapAnnotations(f: List[Tree] => List[Tree]): Modifiers = Modifiers(flags, privateWithin, f(annotations)) setPositions positions @@ -97,7 +97,7 @@ trait Trees extends api.Trees { self: SymbolTable => def Modifiers(flags: Long, privateWithin: Name): Modifiers = Modifiers(flags, privateWithin, List()) def Modifiers(flags: Long): Modifiers = Modifiers(flags, tpnme.EMPTY) - def Modifiers(mods: Set[Modifier.Value], + def Modifiers(mods: Set[Modifier], privateWithin: Name, annotations: List[Tree]): Modifiers = { val flagSet = mods map flagOfModifier @@ -204,7 +204,7 @@ trait Trees extends api.Trees { self: SymbolTable => def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = atPos(sym.pos) { assert(sym != NoSymbol) - DefDef(Modifiers(sym.flags), + DefDef(mods, sym.name.toTermName, sym.typeParams map TypeDef, vparamss, @@ -239,31 +239,24 @@ trait Trees extends api.Trees { self: SymbolTable => LabelDef(sym.name.toTermName, params map Ident, rhs) setSymbol sym } - /** casedef shorthand */ def CaseDef(pat: Tree, body: Tree): CaseDef = CaseDef(pat, EmptyTree, body) def Bind(sym: Symbol, body: Tree): Bind = Bind(sym.name, body) setSymbol sym + def Try(body: Tree, cases: (Tree, Tree)*): Try = + Try(body, cases.toList map { case (pat, rhs) => CaseDef(pat, EmptyTree, rhs) }, EmptyTree) - /** Factory method for object creation `new tpt(args_1)...(args_n)` - * A `New(t, as)` is expanded to: `(new t).<init>(as)` - */ - def New(tpt: Tree, argss: List[List[Tree]]): Tree = { - assert(!argss.isEmpty) - val superRef: Tree = Select(New(tpt), nme.CONSTRUCTOR) - (superRef /: argss) (Apply) - } - /** 0-1 argument list new, based on a symbol. - */ - def New(sym: Symbol, args: Tree*): Tree = - if (args.isEmpty) New(TypeTree(sym.tpe)) - else New(TypeTree(sym.tpe), List(args.toList)) + def Throw(tpe: Type, args: Tree*): Throw = + Throw(New(tpe, args: _*)) def Apply(sym: Symbol, args: Tree*): Tree = Apply(Ident(sym), args.toList) + def New(sym: Symbol, args: Tree*): Tree = + New(sym.tpe, args: _*) + def Super(sym: Symbol, mix: TypeName): Tree = Super(This(sym), mix) /** Block factory that flattens directly nested blocks. @@ -295,7 +288,18 @@ trait Trees extends api.Trees { self: SymbolTable => override def traverse(t: Tree) { if (t != EmptyTree && t.pos == NoPosition) { t.setPos(pos) - super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if? + super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if? + // @PP: it's pruning whenever it encounters a node with a + // position, which I interpret to mean that (in the author's + // mind at least) either the children of a positioned node will + // already be positioned, or the children of a positioned node + // do not merit positioning. + // + // Whatever the author's rationale, it does seem like a bad idea + // to press on through a positioned node to find unpositioned + // children beneath it and then to assign whatever happens to + // be in `pos` to such nodes. There are supposed to be some + // position invariants which I can't imagine surviving that. } } } diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 89664bad9f..94559aeacd 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -112,13 +112,13 @@ trait Types extends api.Types { self: SymbolTable => * to undo constraints in the case of isSubType/isSameType failure. */ lazy val undoLog = newUndoLog - + protected def newUndoLog = new UndoLog - + class UndoLog { private type UndoPairs = List[(TypeVar, TypeConstraint)] private var log: UndoPairs = List() - + // register with the auto-clearing cache manager perRunCaches.recordCache(this) @@ -138,7 +138,7 @@ trait Types extends api.Types { self: SymbolTable => private[reflect] def record(tv: TypeVar) = { log ::= ((tv, tv.constr.cloneInternal)) } - + private[scala] def clear() { if (settings.debug.value) self.log("Clearing " + log.size + " entries from the undoLog.") @@ -263,6 +263,7 @@ trait Types extends api.Types { self: SymbolTable => def declarations = decls def typeArguments = typeArgs def erasedType = transformedType(this) + def substituteTypes(from: List[Symbol], to: List[Type]): Type = subst(from, to) } /** The base class for all types */ @@ -434,7 +435,7 @@ trait Types extends api.Types { self: SymbolTable => /** For a typeref, its arguments. The empty list for all other types */ def typeArgs: List[Type] = List() - + /** A list of placeholder types derived from the type parameters. * Used by RefinedType and TypeRef. */ @@ -531,7 +532,7 @@ trait Types extends api.Types { self: SymbolTable => * Alternatives of overloaded symbol appear in the order they are declared. */ def decl(name: Name): Symbol = findDecl(name, 0) - + /** A list of all non-private members defined or declared in this type. */ def nonPrivateDecls: List[Symbol] = decls filter (x => !x.isPrivate) toList @@ -572,7 +573,7 @@ trait Types extends api.Types { self: SymbolTable => */ def nonPrivateMember(name: Name): Symbol = memberBasedOnName(name, BridgeAndPrivateFlags) - + /** All members with the given flags, excluding bridges. */ def membersWithFlags(requiredFlags: Long): List[Symbol] = @@ -597,7 +598,7 @@ trait Types extends api.Types { self: SymbolTable => * an OverloadedSymbol if several exist, NoSymbol if none exist */ def nonLocalMember(name: Name): Symbol = memberBasedOnName(name, BridgeFlags | LOCAL) - + /** Members excluding and requiring the given flags. * Note: unfortunately it doesn't work to exclude DEFERRED this way. */ @@ -686,7 +687,7 @@ trait Types extends api.Types { self: SymbolTable => * symbol. */ def substSym(from: List[Symbol], to: List[Symbol]): Type = - if (from eq to) this + if ((from eq to) || from.isEmpty) this else new SubstSymMap(from, to) apply this /** Substitute all occurrences of `ThisType(from)` in this type by `to`. @@ -899,7 +900,7 @@ trait Types extends api.Types { self: SymbolTable => def toLongString = { val str = toString if (str == "type") widen.toString - else if (str endsWith ".type") str + " (with underlying type " + widen + ")" + else if ((str endsWith ".type") && !typeSymbol.isModuleClass) str + " (with underlying type " + widen + ")" else str } @@ -1124,7 +1125,14 @@ trait Types extends api.Types { self: SymbolTable => underlying.baseTypeSeq prepend this } override def isHigherKinded = false // singleton type classifies objects, thus must be kind * - override def safeToString: String = prefixString + "type" + override def safeToString: String = { + // Avoiding printing Predef.type and scala.package.type as "type", + // since in all other cases we omit those prefixes. + val pre = underlying.typeSymbol.skipPackageObject + if (pre.isOmittablePrefix) pre.fullName + ".type" + else prefixString + "type" + } + /* override def typeOfThis: Type = typeSymbol.typeOfThis override def bounds: TypeBounds = TypeBounds(this, this) @@ -1243,7 +1251,7 @@ trait Types extends api.Types { self: SymbolTable => private[reflect] var underlyingPeriod = NoPeriod override def underlying: Type = { val cache = underlyingCache - if (underlyingPeriod == currentPeriod && cache != null) cache + if (underlyingPeriod == currentPeriod && cache != null) cache else { defineUnderlyingOfSingleType(this) underlyingCache @@ -1286,7 +1294,7 @@ trait Types extends api.Types { self: SymbolTable => unique(new UniqueSingleType(pre, sym)) } } - + protected def defineUnderlyingOfSingleType(tpe: SingleType) = { val period = tpe.underlyingPeriod if (period != currentPeriod) { @@ -1356,13 +1364,13 @@ trait Types extends api.Types { self: SymbolTable => override def baseTypeSeq: BaseTypeSeq = { val cached = baseTypeSeqCache - if (baseTypeSeqPeriod == currentPeriod && cached != null && cached != undetBaseTypeSeq) + if (baseTypeSeqPeriod == currentPeriod && cached != null && cached != undetBaseTypeSeq) cached else { defineBaseTypeSeqOfCompoundType(this) if (baseTypeSeqCache eq undetBaseTypeSeq) throw new RecoverableCyclicReference(typeSymbol) - + baseTypeSeqCache } } @@ -1376,7 +1384,7 @@ trait Types extends api.Types { self: SymbolTable => defineBaseClassesOfCompoundType(this) if (baseClassesCache eq null) throw new RecoverableCyclicReference(typeSymbol) - + baseClassesCache } } @@ -1422,7 +1430,7 @@ trait Types extends api.Types { self: SymbolTable => decls.mkString("{", "; ", "}") else "") ) } - + protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = { val period = tpe.baseTypeSeqPeriod if (period != currentPeriod) { @@ -1475,7 +1483,7 @@ trait Types extends api.Types { self: SymbolTable => if (tpe.baseTypeSeqCache eq undetBaseTypeSeq) throw new TypeError("illegal cyclic inheritance involving " + tpe.typeSymbol) } - + protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = { def computeBaseClasses: List[Symbol] = if (tpe.parents.isEmpty) List(tpe.typeSymbol) @@ -1757,7 +1765,7 @@ trait Types extends api.Types { self: SymbolTable => // override def isNonNull: Boolean = symbol == NonNullClass || super.isNonNull; override def kind = "ClassInfoType" - + override def safeToString = if (settings.debug.value || decls.size > 1) formattedToString @@ -1807,13 +1815,13 @@ trait Types extends api.Types { self: SymbolTable => } } - /* Syncnote: The `volatile` var and `pendingVolatiles` mutable set need not be protected + /* 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) with UniqueType { require(args0.nonEmpty, this) @@ -1831,7 +1839,7 @@ trait Types extends api.Types { self: SymbolTable => asSeenFromOwner(tp).instantiateTypeParams(sym.typeParams, args) } - + // note: does not go through typeRef. There's no need to because // neither `pre` nor `sym` changes. And there's a performance // advantage to call TypeRef directly. @@ -1846,7 +1854,7 @@ trait Types extends api.Types { self: SymbolTable => override def isHigherKinded = typeParams.nonEmpty override def typeParams = if (isDefinitionsInitialized) sym.typeParams else sym.unsafeTypeParams private def isRaw = !phase.erasedTypes && isRawIfWithoutArgs(sym) - + override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type = if (isHigherKinded) { if (sameLength(formals intersect typeParams, typeParams)) @@ -1866,9 +1874,9 @@ trait Types extends api.Types { self: SymbolTable => res } - override def transformInfo(tp: Type): Type = + override def transformInfo(tp: Type): Type = appliedType(asSeenFromOwner(tp), dummyArgs) - + override def narrow = if (sym.isModuleClass) singleType(pre, sym.sourceModule) else super.narrow @@ -1876,14 +1884,14 @@ trait Types extends api.Types { self: SymbolTable => override def typeConstructor = this // eta-expand, subtyping relies on eta-expansion of higher-kinded types - override protected def normalizeImpl: Type = + override protected def normalizeImpl: Type = if (isHigherKinded) etaExpand else super.normalizeImpl } - + trait ClassTypeRef extends TypeRef { // !!! There are scaladoc-created symbols arriving which violate this require. // require(sym.isClass, sym) - + override protected def normalizeImpl: Type = if (sym.isRefinementClass) sym.info.normalize // I think this is okay, but see #1241 (r12414), #2208, and typedTypeConstructor in Typers else super.normalizeImpl @@ -1892,7 +1900,7 @@ trait Types extends api.Types { self: SymbolTable => if (sym == clazz) this else transform(sym.info.baseType(clazz)) } - + trait NonClassTypeRef extends TypeRef { require(sym.isNonClassType, sym) @@ -1911,11 +1919,11 @@ trait Types extends api.Types { self: SymbolTable => } relativeInfoCache } - + override def baseType(clazz: Symbol): Type = if (sym == clazz) this else baseTypeOfNonClassTypeRef(this, clazz) } - + protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = try { basetypeRecursions += 1 if (basetypeRecursions < LogPendingBaseTypesThreshold) @@ -1932,7 +1940,7 @@ trait Types extends api.Types { self: SymbolTable => } finally { basetypeRecursions -= 1 } - + trait AliasTypeRef extends NonClassTypeRef { require(sym.isAliasType, sym) @@ -1950,7 +1958,7 @@ trait Types extends api.Types { self: SymbolTable => if (typeParamsMatchArgs) betaReduce.normalize else if (isHigherKinded) super.normalizeImpl else ErrorType - + // isHKSubType0 introduces synthetic type params so that // betaReduce can first apply sym.info to typeArgs before calling // asSeenFrom. asSeenFrom then skips synthetic type params, which @@ -1960,7 +1968,7 @@ trait Types extends api.Types { self: SymbolTable => // this crashes pos/depmet_implicit_tpbetareduce.scala // appliedType(sym.info, typeArgs).asSeenFrom(pre, sym.owner) def betaReduce = transform(sym.info.resultType) - + // #3731: return sym1 for which holds: pre bound sym.name to sym and // pre1 now binds sym.name to sym1, conceptually exactly the same // symbol as sym. The selection of sym on pre must be updated to the @@ -1974,12 +1982,12 @@ trait Types extends api.Types { self: SymbolTable => // TODO: is there another way a typeref's symbol can refer to a symbol defined in its pre? case _ => sym } - + } trait AbstractTypeRef extends NonClassTypeRef { require(sym.isAbstractType, sym) - + /** Syncnote: Pure performance caches; no need to synchronize in multi-threaded environment */ private var symInfoCache: Type = _ @@ -2008,7 +2016,7 @@ trait Types extends api.Types { self: SymbolTable => volatileRecursions -= 1 } } - + override def thisInfo = { val symInfo = sym.info if (thisInfoCache == null || (symInfo ne symInfoCache)) { @@ -2041,7 +2049,7 @@ trait Types extends api.Types { self: SymbolTable => private[reflect] var parentsPeriod = NoPeriod private[reflect] var baseTypeSeqCache: BaseTypeSeq = _ private[reflect] var baseTypeSeqPeriod = NoPeriod - private var normalized: Type = _ + private var normalized: Type = _ // @M: propagate actual type params (args) to `tp`, by replacing // formal type parameters with actual ones. If tp is higher kinded, @@ -2063,7 +2071,7 @@ trait Types extends api.Types { self: SymbolTable => normalized } } - + def etaExpand: Type = { // must initialise symbol, see test/files/pos/ticket0137.scala val tpars = initializedTypeParams @@ -2117,12 +2125,12 @@ trait Types extends api.Types { self: SymbolTable => } thisInfo.decls } - + protected[Types] def baseTypeSeqImpl: BaseTypeSeq = sym.info.baseTypeSeq map transform override def baseTypeSeq: BaseTypeSeq = { val cache = baseTypeSeqCache - if (baseTypeSeqPeriod == currentPeriod && cache != null && cache != undetBaseTypeSeq) + if (baseTypeSeqPeriod == currentPeriod && cache != null && cache != undetBaseTypeSeq) cache else { defineBaseTypeSeqOfTypeRef(this) @@ -2216,7 +2224,7 @@ trait Types extends api.Types { self: SymbolTable => } }) } - + protected def defineParentsOfTypeRef(tpe: TypeRef) = { val period = tpe.parentsPeriod if (period != currentPeriod) { @@ -2228,7 +2236,7 @@ trait Types extends api.Types { self: SymbolTable => } } } - + protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = { val period = tpe.baseTypeSeqPeriod if (period != currentPeriod) { @@ -2388,7 +2396,7 @@ trait Types extends api.Types { self: SymbolTable => } object PolyType extends PolyTypeExtractor - + /** A creator for existential types which flattens nested existentials. */ def newExistentialType(quantified: List[Symbol], underlying: Type): Type = @@ -2439,25 +2447,37 @@ trait Types extends api.Types { self: SymbolTable => case _ => List() } - + /** An existential can only be printed with wildcards if: + * - the underlying type is a typeref + * - where there is a 1-to-1 correspondence between underlying's typeargs and quantified + * - and none of the existential parameters is referenced from anywhere else in the type + * - and none of the existential parameters are singleton types + */ + private def isRepresentableWithWildcards = !settings.debug.value && { + val qset = quantified.toSet + !qset.exists(_.isSingletonExistential) && (underlying match { + case TypeRef(_, sym, args) => + sameLength(args, quantified) && { + args forall { arg => + qset(arg.typeSymbol) && !qset.exists(arg.typeSymbol.info.bounds contains _) + } + } + case _ => false + }) + } override def safeToString: String = { - if (!(quantified exists (_.isSingletonExistential)) && !settings.debug.value) - // try to represent with wildcards first - underlying match { - case TypeRef(pre, sym, args) if args.nonEmpty => - val wargs = wildcardArgsString(quantified.toSet, args) - if (sameLength(wargs, args)) - return TypeRef(pre, sym, List()) + wargs.mkString("[", ", ", "]") - case _ => - } - var ustr = underlying.toString + def clauses = { + val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") + if (settings.explaintypes.value) "(" + str + ")" else str + } underlying match { - case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => ustr = "("+ustr+")" + case TypeRef(pre, sym, args) if isRepresentableWithWildcards => + "" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]") + case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => + "(" + underlying + ")" + clauses case _ => + "" + underlying + clauses } - val str = - ustr+(quantified map (_.existentialToString) mkString(" forSome { ", "; ", " }")) - if (settings.explaintypes.value) "("+str+")" else str } override def cloneInfo(owner: Symbol) = @@ -2591,7 +2611,7 @@ trait Types extends api.Types { self: SymbolTable => else if (args.isEmpty) new HKTypeVar(origin, constr, params) else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params))) ) - + trace("create", "In " + tv.originLocation)(tv) } } @@ -2632,7 +2652,7 @@ trait Types extends api.Types { self: SymbolTable => override def isHigherKinded = true override protected def typeVarString = params.map(_.name).mkString("[", ", ", "]=>" + originName) } - + /** Precondition: zipped params/args nonEmpty. (Size equivalence enforced structurally.) */ class AppliedTypeVar( @@ -2640,17 +2660,17 @@ trait Types extends api.Types { self: SymbolTable => _constr: TypeConstraint, zippedArgs: List[(Symbol, Type)] ) extends TypeVar(_origin, _constr) { - + require(zippedArgs.nonEmpty, this) override def params: List[Symbol] = zippedArgs map (_._1) override def typeArgs: List[Type] = zippedArgs map (_._2) - + override protected def typeVarString = ( zippedArgs map { case (p, a) => p.name + "=" + a } mkString (origin + "[", ", ", "]") ) } - + /** A class representing a type variable: not used after phase `typer`. * * A higher-kinded TypeVar has params (Symbols) and typeArgs (Types). @@ -2668,7 +2688,7 @@ trait Types extends api.Types { self: SymbolTable => override def typeArgs: List[Type] = Nil override def isHigherKinded = false - /** The constraint associated with the variable + /** The constraint associated with the variable * Syncnote: Type variables are assumed to be used from only one * thread. They are not exposed in api.Types and are used only locally * in operations that are exposed from types. Hence, no syncing of `constr` @@ -2679,7 +2699,7 @@ trait Types extends api.Types { self: SymbolTable => /** The variable's skolemization level */ val level = skolemizationLevel - + /** Two occurrences of a higher-kinded typevar, e.g. `?CC[Int]` and `?CC[String]`, correspond to * ''two instances'' of `TypeVar` that share the ''same'' `TypeConstraint`. * @@ -2710,7 +2730,7 @@ trait Types extends api.Types { self: SymbolTable => // inference may generate several TypeVar's for a single type parameter that must be inferred, // only one of them is in the set of tvars that need to be solved, but // they share the same TypeConstraint instance - + // When comparing to types containing skolems, remember the highest level // of skolemization. If that highest level is higher than our initial // skolemizationLevel, we can't re-use those skolems as the solution of this @@ -2934,7 +2954,7 @@ trait Types extends api.Types { self: SymbolTable => def originLocation = { val sym = origin.typeSymbolDirect val encl = sym.owner.logicallyEnclosingMember - + // This should display somewhere between one and three // things which enclose the origin: at most, a class, a // a method, and a term. At least, a class. @@ -3278,6 +3298,25 @@ trait Types extends api.Types { self: SymbolTable => case _ => abort(debugString(tycon)) } + /** A creator for existential types where the type arguments, + * rather than being applied directly, are interpreted as the + * upper bounds of unknown types. For instance if the type argument + * list given is List(AnyRefClass), the resulting type would be + * e.g. Set[_ <: AnyRef] rather than Set[AnyRef] . + */ + def appliedTypeAsUpperBounds(tycon: Type, args: List[Type]): Type = { + tycon match { + case TypeRef(pre, sym, _) if sameLength(sym.typeParams, args) => + val eparams = typeParamsToExistentials(sym) + val bounds = args map (TypeBounds upper _) + (eparams, bounds).zipped foreach (_ setInfo _) + + newExistentialType(eparams, typeRef(pre, sym, eparams map (_.tpe))) + case _ => + appliedType(tycon, args) + } + } + /** A creator and extractor for type parameterizations that strips empty type parameter lists. * Use this factory method to indicate the type has kind * (it's a polymorphic value) * until we start tracking explicit kinds equivalent to typeFun (except that the latter requires tparams nonEmpty). @@ -3382,7 +3421,7 @@ trait Types extends api.Types { self: SymbolTable => mapOver(tp) } } - + /** Type with all top-level occurrences of abstract types replaced by their bounds */ def abstractTypesToBounds(tp: Type): Type = tp match { // @M don't normalize here (compiler loops on pos/bug1090.scala ) case TypeRef(_, sym, _) if sym.isAbstractType => @@ -3492,7 +3531,7 @@ trait Types extends api.Types { self: SymbolTable => def this(lo0: List[Type], hi0: List[Type]) = this(lo0, hi0, NoType, NoType) def this(bounds: TypeBounds) = this(List(bounds.lo), List(bounds.hi)) def this() = this(List(), List()) - + /* Syncnote: Type constraints are assumed to be used from only one * thread. They are not exposed in api.Types and are used only locally * in operations that are exposed from types. Hence, no syncing of any @@ -3566,7 +3605,7 @@ trait Types extends api.Types { self: SymbolTable => val hi = hiBounds filterNot (_.typeSymbolDirect eq AnyClass) val lostr = if (lo.isEmpty) Nil else List(lo.mkString(" >: (", ", ", ")")) val histr = if (hi.isEmpty) Nil else List(hi.mkString(" <: (", ", ", ")")) - + lostr ++ histr mkString ("[", " | ", "]") } if (inst eq NoType) boundsStr @@ -3592,7 +3631,7 @@ trait Types extends api.Types { self: SymbolTable => override def variance = _variance def variance_=(x: Int) = _variance = x - + override protected def noChangeToSymbols(origSyms: List[Symbol]) = { origSyms forall { sym => val v = variance @@ -3759,7 +3798,7 @@ trait Types extends api.Types { self: SymbolTable => protected def mapOverArgs(args: List[Type], tparams: List[Symbol]): List[Type] = args mapConserve this - + /** Called by mapOver to determine whether the original symbols can * be returned, or whether they must be cloned. Overridden in VariantTypeMap. */ @@ -3773,7 +3812,7 @@ trait Types extends api.Types { self: SymbolTable => if (elems1 eq elems) scope else newScopeWith(elems1: _*) } - + /** Map this function over given list of symbols */ def mapOver(origSyms: List[Symbol]): List[Symbol] = { // fast path in case nothing changes due to map @@ -3836,7 +3875,7 @@ trait Types extends api.Types { self: SymbolTable => def traverse(tp: Type): Unit def apply(tp: Type): Type = { traverse(tp); tp } } - + abstract class TypeTraverserWithResult[T] extends TypeTraverser { def result: T def clear(): Unit @@ -3856,13 +3895,13 @@ trait Types extends api.Types { self: SymbolTable => */ // class ContainsVariantExistentialCollector(v: Int) extends TypeCollector(false) with VariantTypeMap { // variance = v - // + // // def traverse(tp: Type) = tp match { // case ExistentialType(_, _) if (variance == v) => result = true // case _ => mapOver(tp) // } // } - // + // // val containsCovariantExistentialCollector = new ContainsVariantExistentialCollector(1) // val containsContravariantExistentialCollector = new ContainsVariantExistentialCollector(-1) @@ -3872,6 +3911,8 @@ trait Types extends api.Types { self: SymbolTable => eparams map (_ substInfo (tparams, eparams)) } + def typeParamsToExistentials(clazz: Symbol): List[Symbol] = + typeParamsToExistentials(clazz, clazz.typeParams) // note: it's important to write the two tests in this order, // as only typeParams forces the classfile to be read. See #400 @@ -3897,26 +3938,21 @@ trait Types extends api.Types { self: SymbolTable => */ def rawToExistential = new TypeMap { private var expanded = immutable.Set[Symbol]() - private var generated = immutable.Set[Type]() def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, List()) if isRawIfWithoutArgs(sym) => if (expanded contains sym) AnyRefClass.tpe else try { expanded += sym - val eparams = mapOver(typeParamsToExistentials(sym, sym.typeParams)) + val eparams = mapOver(typeParamsToExistentials(sym)) existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe))) } finally { expanded -= sym } - case ExistentialType(_, _) if !(generated contains tp) => // to avoid infinite expansions. todo: not sure whether this is needed - val result = mapOver(tp) - generated += result - result case _ => mapOver(tp) } } - + /** Used by existentialAbstraction. */ class ExistentialExtrapolation(tparams: List[Symbol]) extends VariantTypeMap { @@ -3934,10 +3970,10 @@ trait Types extends api.Types { self: SymbolTable => countOccs(tpe) for (tparam <- tparams) countOccs(tparam.info) - + apply(tpe) } - + def apply(tp: Type): Type = { val tp1 = mapOver(tp) if (variance == 0) tp1 @@ -4313,83 +4349,83 @@ trait Types extends api.Types { self: SymbolTable => else mapOver(tp) } - class InstantiateDependentMap(params: List[Symbol], actuals: List[Type]) extends TypeMap with KeepOnlyTypeConstraints { - private val actualsIndexed = actuals.toIndexedSeq + class InstantiateDependentMap(params: List[Symbol], actuals0: List[Type]) extends TypeMap with KeepOnlyTypeConstraints { + private val actuals = actuals0.toIndexedSeq + private val existentials = new Array[Symbol](actuals.size) + def existentialsNeeded: List[Symbol] = existentials.filter(_ ne null).toList - object ParamWithActual { - def unapply(sym: Symbol): Option[Type] = { - val pid = params indexOf sym - if(pid != -1) Some(actualsIndexed(pid)) else None - } + private object StableArg { + def unapply(param: Symbol) = Arg unapply param map actuals filter (tp => + tp.isStable && (tp.typeSymbol != NothingClass) + ) + } + private object Arg { + def unapply(param: Symbol) = Some(params indexOf param) filter (_ >= 0) } - def apply(tp: Type): Type = - mapOver(tp) match { - case SingleType(NoPrefix, ParamWithActual(arg)) if arg.isStable => arg // unsound to replace args by unstable actual #3873 - // (soundly) expand type alias selections on implicit arguments, see depmet_implicit_oopsla* test cases -- typically, `param.isImplicit` - case tp1@TypeRef(SingleType(NoPrefix, ParamWithActual(arg)), sym, targs) => - val res = typeRef(arg, sym, targs) - if(res.typeSymbolDirect isAliasType) res.dealias - else tp1 - case tp1 => tp1 // don't return the original `tp`, which may be different from `tp1`, due to dropping annotations - } - - def existentialsNeeded: List[Symbol] = existSyms.filter(_ ne null).toList - - private val existSyms: Array[Symbol] = new Array(actualsIndexed.size) - private def haveExistential(i: Int) = {assert((i >= 0) && (i <= actualsIndexed.size)); existSyms(i) ne null} + def apply(tp: Type): Type = mapOver(tp) match { + // unsound to replace args by unstable actual #3873 + case SingleType(NoPrefix, StableArg(arg)) => arg + // (soundly) expand type alias selections on implicit arguments, + // see depmet_implicit_oopsla* test cases -- typically, `param.isImplicit` + case tp1 @ TypeRef(SingleType(NoPrefix, Arg(pid)), sym, targs) => + val arg = actuals(pid) + val res = typeRef(arg, sym, targs) + if (res.typeSymbolDirect.isAliasType) res.dealias else tp1 + // don't return the original `tp`, which may be different from `tp1`, + // due to dropping annotations + case tp1 => tp1 + } /* Return the type symbol for referencing a parameter inside the existential quantifier. * (Only needed if the actual is unstable.) */ - def existSymFor(actualIdx: Int) = - if (haveExistential(actualIdx)) existSyms(actualIdx) - else { - val oldSym = params(actualIdx) - val symowner = oldSym.owner - val bound = singletonBounds(actualsIndexed(actualIdx)) - - val sym = symowner.newExistential(newTypeName(oldSym.name + ".type"), oldSym.pos) - sym.setInfo(bound) - sym.setFlag(oldSym.flags) - - existSyms(actualIdx) = sym - sym + private def existentialFor(pid: Int) = { + if (existentials(pid) eq null) { + val param = params(pid) + existentials(pid) = ( + param.owner.newExistential(newTypeName(param.name + ".type"), param.pos, param.flags) + setInfo singletonBounds(actuals(pid)) + ) } + existentials(pid) + } //AM propagate more info to annotations -- this seems a bit ad-hoc... (based on code by spoon) override def mapOver(arg: Tree, giveup: ()=>Nothing): Tree = { + // TODO: this should be simplified; in the stable case, one can + // probably just use an Ident to the tree.symbol. + // + // @PP: That leads to failure here, where stuff no longer has type + // 'String @Annot("stuff")' but 'String @Annot(x)'. + // + // def m(x: String): String @Annot(x) = x + // val stuff = m("stuff") + // + // (TODO cont.) Why an existential in the non-stable case? + // + // @PP: In the following: + // + // def m = { val x = "three" ; val y: String @Annot(x) = x; y } + // + // m is typed as 'String @Annot(x) forSome { val x: String }'. + // + // Both examples are from run/constrained-types.scala. object treeTrans extends Transformer { - override def transform(tree: Tree): Tree = { - tree match { - case RefParamAt(pid) => - // TODO: this should be simplified; in the stable case, one can probably - // just use an Ident to the tree.symbol. Why an existential in the non-stable case? - val actual = actualsIndexed(pid) - if (actual.isStable && actual.typeSymbol != NothingClass) { - gen.mkAttributedQualifier(actualsIndexed(pid), tree.symbol) - } else { - val sym = existSymFor(pid) - (Ident(sym.name) - copyAttrs tree - setType typeRef(NoPrefix, sym, Nil)) - } - case _ => super.transform(tree) - } - } - object RefParamAt { - def unapply(tree: Tree): Option[Int] = tree match { - case Ident(_) => Some(params indexOf tree.symbol) filterNot (_ == -1) - case _ => None - } + override def transform(tree: Tree): Tree = tree.symbol match { + case StableArg(actual) => + gen.mkAttributedQualifier(actual, tree.symbol) + case Arg(pid) => + val sym = existentialFor(pid) + Ident(sym) copyAttrs tree setType typeRef(NoPrefix, sym, Nil) + case _ => + super.transform(tree) } } - - treeTrans.transform(arg) + treeTrans transform arg } } - object StripAnnotationsMap extends TypeMap { def apply(tp: Type): Type = tp match { case AnnotatedType(_, atp, _) => @@ -4514,12 +4550,12 @@ trait Types extends api.Types { self: SymbolTable => if (commonOwnerMap.result ne null) commonOwnerMap.result else NoSymbol } } - + protected def commonOwnerMap: CommonOwnerMap = commonOwnerMapObj - + protected class CommonOwnerMap extends TypeTraverserWithResult[Symbol] { var result: Symbol = _ - + def clear() { result = null } private def register(sym: Symbol) { @@ -4537,7 +4573,7 @@ trait Types extends api.Types { self: SymbolTable => case _ => mapOver(tp) } } - + private lazy val commonOwnerMapObj = new CommonOwnerMap class MissingAliasControl extends ControlThrowable @@ -4545,7 +4581,7 @@ trait Types extends api.Types { self: SymbolTable => class MissingTypeControl extends ControlThrowable object adaptToNewRunMap extends TypeMap { - + private def adaptToNewRun(pre: Type, sym: Symbol): Symbol = { if (phase.flatClasses) { sym @@ -4569,7 +4605,7 @@ trait Types extends api.Types { self: SymbolTable => var rebind0 = pre.findMember(sym.name, BRIDGE, 0, true) if (rebind0 == NoSymbol) { if (sym.isAliasType) throw missingAliasException - if (settings.debug.value) println(pre+"."+sym+" does no longer exist, phase = "+phase) + debugwarn(pre+"."+sym+" does no longer exist, phase = "+phase) throw new MissingTypeControl // For build manager and presentation compiler purposes //assert(false, pre+"."+sym+" does no longer exist, phase = "+phase) } @@ -4625,7 +4661,7 @@ trait Types extends api.Types { self: SymbolTable => if ((pre1 eq pre) && (sym1 eq sym) && (args1 eq args)/* && sym.isExternal*/) { tp } else if (sym1 == NoSymbol) { - if (settings.debug.value) println("adapt fail: "+pre+" "+pre1+" "+sym) + debugwarn("adapt fail: "+pre+" "+pre1+" "+sym) tp } else { copyTypeRef(tp, pre1, sym1, args1) @@ -4712,7 +4748,7 @@ trait Types extends api.Types { self: SymbolTable => case (TypeRef(pre1, sym1, args1), TypeRef(pre2, sym2, args2)) => assert(sym1 == sym2) pre1 =:= pre2 && - forall3(args1, args2, sym1.typeParams) { (arg1, arg2, tparam) => + forall3(args1, args2, sym1.typeParams) { (arg1, arg2, tparam) => //if (tparam.variance == 0 && !(arg1 =:= arg2)) Console.println("inconsistent: "+arg1+"!="+arg2)//DEBUG if (tparam.variance == 0) arg1 =:= arg2 else if (arg1.isInstanceOf[TypeVar]) @@ -5373,9 +5409,9 @@ trait Types extends api.Types { self: SymbolTable => val params2 = mt2.params val res2 = mt2.resultType (sameLength(params1, params2) && + mt1.isImplicit == mt2.isImplicit && matchingParams(params1, params2, mt1.isJava, mt2.isJava) && - (res1 <:< res2.substSym(params2, params1)) && - mt1.isImplicit == mt2.isImplicit) + (res1 <:< res2.substSym(params2, params1))) // TODO: if mt1.params.isEmpty, consider NullaryMethodType? case _ => false @@ -5485,8 +5521,8 @@ trait Types extends api.Types { self: SymbolTable => matchesType(tp1, res2, true) case MethodType(_, _) => false - case PolyType(tparams2, res2) => - tparams2.isEmpty && matchesType(tp1, res2, alwaysMatchSimple) + case PolyType(_, _) => + false case _ => alwaysMatchSimple || tp1 =:= tp2 } @@ -5495,14 +5531,16 @@ trait Types extends api.Types { self: SymbolTable => tp2 match { case mt2 @ MethodType(params2, res2) => // sameLength(params1, params2) was used directly as pre-screening optimization (now done by matchesQuantified -- is that ok, performancewise?) - matchesQuantified(params1, params2, res1, res2) && + mt1.isImplicit == mt2.isImplicit && matchingParams(params1, params2, mt1.isJava, mt2.isJava) && - mt1.isImplicit == mt2.isImplicit + matchesQuantified(params1, params2, res1, res2) case NullaryMethodType(res2) => if (params1.isEmpty) matchesType(res1, res2, alwaysMatchSimple) else matchesType(tp1, res2, alwaysMatchSimple) case ExistentialType(_, res2) => alwaysMatchSimple && matchesType(tp1, res2, true) + case TypeRef(_, sym, Nil) => + params1.isEmpty && sym.isModuleClass && matchesType(res1, tp2, alwaysMatchSimple) case _ => false } @@ -5514,13 +5552,18 @@ trait Types extends api.Types { self: SymbolTable => matchesType(res1, res2, alwaysMatchSimple) case ExistentialType(_, res2) => alwaysMatchSimple && matchesType(tp1, res2, true) + case TypeRef(_, sym, Nil) if sym.isModuleClass => + matchesType(res1, tp2, alwaysMatchSimple) case _ => matchesType(res1, tp2, alwaysMatchSimple) } case PolyType(tparams1, res1) => tp2 match { case PolyType(tparams2, res2) => - matchesQuantified(tparams1, tparams2, res1, res2) + if ((tparams1 corresponds tparams2)(_ eq _)) + matchesType(res1, res2, alwaysMatchSimple) + else + matchesQuantified(tparams1, tparams2, res1, res2) case ExistentialType(_, res2) => alwaysMatchSimple && matchesType(tp1, res2, true) case _ => @@ -5534,6 +5577,12 @@ trait Types extends api.Types { self: SymbolTable => if (alwaysMatchSimple) matchesType(res1, tp2, true) else lastTry } + case TypeRef(_, sym, Nil) if sym.isModuleClass => + tp2 match { + case MethodType(Nil, res2) => matchesType(tp1, res2, alwaysMatchSimple) + case NullaryMethodType(res2) => matchesType(tp1, res2, alwaysMatchSimple) + case _ => lastTry + } case _ => lastTry } @@ -5722,8 +5771,8 @@ trait Types extends api.Types { self: SymbolTable => val formatted = tableDef.table(transposed) println("** Depth is " + depth + "\n" + formatted) } - - /** From a list of types, find any which take type parameters + + /** From a list of types, find any which take type parameters * where the type parameter bounds contain references to other * any types in the list (including itself.) * @@ -5823,7 +5872,7 @@ trait Types extends api.Types { self: SymbolTable => } } - val initialBTSes = ts map (_.baseTypeSeq.toList filter (_.typeSymbol.isPublic)) + val initialBTSes = ts map (_.baseTypeSeq.toList) if (printLubs) printLubMatrix(ts zip initialBTSes toMap, depth) @@ -6242,13 +6291,13 @@ trait Types extends api.Types { self: SymbolTable => if (ts exists (_.isNotNull)) res.notNull else res } - + /** A list of the typevars in a type. */ def typeVarsInType(tp: Type): List[TypeVar] = { var tvs: List[TypeVar] = Nil tp foreach { case t: TypeVar => tvs ::= t - case _ => + case _ => } tvs.reverse } @@ -6260,7 +6309,7 @@ trait Types extends api.Types { self: SymbolTable => // !!! Is it somehow guaranteed that this will not break under nesting? // In general one has to save and restore the contents of the field... tvs foreach (_.suspended = true) - tvs + tvs } /** Compute lub (if `variance == 1`) or glb (if `variance == -1`) of given list @@ -6490,5 +6539,5 @@ trait Types extends api.Types { self: SymbolTable => } finally { tostringRecursions -= 1 } - + } diff --git a/src/compiler/scala/reflect/internal/pickling/UnPickler.scala b/src/compiler/scala/reflect/internal/pickling/UnPickler.scala index b21b33e138..34163d54f8 100644 --- a/src/compiler/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/compiler/scala/reflect/internal/pickling/UnPickler.scala @@ -846,10 +846,11 @@ abstract class UnPickler /*extends reflect.generic.UnPickler*/ { private val p = phase override def complete(sym: Symbol) : Unit = try { val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` - if (p != phase) atPhase(p) (sym setInfo tp) - else sym setInfo tp - if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) - } catch { + atPhase(p) (sym setInfo tp) + if (currentRunId != definedAtRunId) + sym.setInfo(adaptToNewRunMap(tp)) + } + catch { case e: MissingRequirementError => throw toTypeError(e) } override def load(sym: Symbol) { complete(sym) } @@ -862,13 +863,12 @@ abstract class UnPickler /*extends reflect.generic.UnPickler*/ { override def complete(sym: Symbol) = try { super.complete(sym) var alias = at(j, readSymbol) - if (alias.isOverloaded) { - atPhase(picklerPhase) { - alias = alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)) - } - } + if (alias.isOverloaded) + alias = atPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) + sym.asInstanceOf[TermSymbol].setAlias(alias) - } catch { + } + catch { case e: MissingRequirementError => throw toTypeError(e) } } diff --git a/src/compiler/scala/reflect/internal/util/Collections.scala b/src/compiler/scala/reflect/internal/util/Collections.scala index 94672097c4..d26a1abadb 100644 --- a/src/compiler/scala/reflect/internal/util/Collections.scala +++ b/src/compiler/scala/reflect/internal/util/Collections.scala @@ -64,7 +64,21 @@ trait Collections { } lb.toList } + + final def foreachWithIndex[A, B](xs: List[A])(f: (A, Int) => Unit) { + var index = 0 + var ys = xs + while (!ys.isEmpty) { + f(ys.head, index) + ys = ys.tail + index += 1 + } + } + @inline final def findOrElse[A](xs: TraversableOnce[A])(p: A => Boolean)(orElse: => A): A = { + xs find p getOrElse orElse + } + final def mapWithIndex[A, B](xs: List[A])(f: (A, Int) => B): List[B] = { val lb = new ListBuffer[B] var index = 0 @@ -88,7 +102,7 @@ trait Collections { val x2 = ys2.head if (p(x1, x2)) buf += ((x1, x2)) - + ys1 = ys1.tail ys2 = ys2.tail } @@ -120,7 +134,7 @@ trait Collections { while (!ys1.isEmpty && !ys2.isEmpty) { if (f(ys1.head, ys2.head)) return true - + ys1 = ys1.tail ys2 = ys2.tail } @@ -132,7 +146,7 @@ trait Collections { while (!ys1.isEmpty && !ys2.isEmpty) { if (!f(ys1.head, ys2.head)) return false - + ys1 = ys1.tail ys2 = ys2.tail } @@ -145,7 +159,7 @@ trait Collections { while (!ys1.isEmpty && !ys2.isEmpty && !ys3.isEmpty) { if (!f(ys1.head, ys2.head, ys3.head)) return false - + ys1 = ys1.tail ys2 = ys2.tail ys3 = ys3.tail diff --git a/src/compiler/scala/reflect/runtime/ConversionUtil.scala b/src/compiler/scala/reflect/runtime/ConversionUtil.scala index e75fd78590..8c32026e37 100644 --- a/src/compiler/scala/reflect/runtime/ConversionUtil.scala +++ b/src/compiler/scala/reflect/runtime/ConversionUtil.scala @@ -23,7 +23,7 @@ trait ConversionUtil { self: SymbolTable => toJavaMap(s) = j } - def toScala(key: J)(body: => S): S = synchronized { + def toScala(key: J)(body: => S): S = synchronized { toScalaMap get key match { case Some(v) => v @@ -34,7 +34,7 @@ trait ConversionUtil { self: SymbolTable => } } - def toJava(key: S)(body: => J): J = synchronized { + def toJava(key: S)(body: => J): J = synchronized { toJavaMap get key match { case Some(v) => v diff --git a/src/compiler/scala/reflect/runtime/Mirror.scala b/src/compiler/scala/reflect/runtime/Mirror.scala index 4808326902..d3e4dd7619 100644 --- a/src/compiler/scala/reflect/runtime/Mirror.scala +++ b/src/compiler/scala/reflect/runtime/Mirror.scala @@ -12,28 +12,28 @@ class Mirror extends Universe with RuntimeTypes with TreeBuildUtil with ToolBoxe import definitions._ - def classWithName(name: String): Symbol = { + def symbolForName(name: String): Symbol = { val clazz = javaClass(name, defaultReflectiveClassLoader()) classToScala(clazz) } - - def getCompanionObject(clazz: Symbol): AnyRef = { + + def companionInstance(clazz: Symbol): AnyRef = { val singleton = ReflectionUtils.singletonInstance(clazz.fullName, defaultReflectiveClassLoader()) singleton } - - def getClass(obj: AnyRef): Symbol = classToScala(obj.getClass) - def getType(obj: AnyRef): Type = typeToScala(obj.getClass) + + def symbolOfInstance(obj: Any): Symbol = classToScala(obj.getClass) + def typeOfInstance(obj: Any): Type = typeToScala(obj.getClass) // to do add getClass/getType for instances of primitive types, probably like this: // def getClass[T <: AnyVal : Manifest](x: T): Symbol = manifest[T].getClass - def getValue(receiver: AnyRef, field: Symbol): Any = { + def getValueOfField(receiver: AnyRef, field: Symbol): Any = { fieldToJava(field).get(receiver) } - def setValue(receiver: AnyRef, field: Symbol, value: Any): Unit = { + def setValueOfField(receiver: AnyRef, field: Symbol, value: Any): Unit = { fieldToJava(field).set(receiver, value) } - def invoke(receiver: AnyRef, meth: Symbol, args: Any*): Any = { + def invoke(receiver: AnyRef, meth: Symbol)(args: Any*): Any = { if (meth.owner == ArrayClass) { meth.name match { case nme.length => return Array.getLength(receiver) @@ -41,8 +41,8 @@ class Mirror extends Universe with RuntimeTypes with TreeBuildUtil with ToolBoxe case nme.update => return Array.set(receiver, args(0).asInstanceOf[Int], args(1)) } } - - val jmeth = methodToJava(meth) + + val jmeth = methodToJava(meth) jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) } @@ -51,7 +51,7 @@ class Mirror extends Universe with RuntimeTypes with TreeBuildUtil with ToolBoxe override def typeToClass(tpe: Type): java.lang.Class[_] = typeToJavaClass(tpe) override def symbolToClass(sym: Symbol): java.lang.Class[_] = classToJava(sym) - + override def inReflexiveMirror = true } diff --git a/src/compiler/scala/reflect/runtime/SynchronizedOps.scala b/src/compiler/scala/reflect/runtime/SynchronizedOps.scala index 72adbd4004..dd806beb2a 100644 --- a/src/compiler/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/compiler/scala/reflect/runtime/SynchronizedOps.scala @@ -1,22 +1,22 @@ package scala.reflect package runtime -trait SynchronizedOps extends internal.SymbolTable +trait SynchronizedOps extends internal.SymbolTable with SynchronizedSymbols with SynchronizedTypes { self: SymbolTable => - + // Names - + private lazy val nameLock = new Object - + override def newTermName(s: String): TermName = nameLock.synchronized { super.newTermName(s) } override def newTypeName(s: String): TypeName = nameLock.synchronized { super.newTypeName(s) } - + // BaseTypeSeqs - - override protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = + + override protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq - + trait SynchronizedBaseTypeSeq extends BaseTypeSeq { override def apply(i: Int): Type = synchronized { super.apply(i) } override def rawElem(i: Int) = synchronized { super.rawElem(i) } @@ -30,9 +30,9 @@ trait SynchronizedOps extends internal.SymbolTable override def lateMap(f: Type => Type): BaseTypeSeq = new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq } - + // Scopes - + override def newScope = new Scope() with SynchronizedScope override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope diff --git a/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala b/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala index 9baf94f71d..3f2fa30be2 100644 --- a/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala @@ -6,61 +6,61 @@ import internal.Flags.DEFERRED trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => override protected def nextId() = synchronized { super.nextId() } - - override protected def freshExistentialName(suffix: String) = + + override protected def freshExistentialName(suffix: String) = synchronized { super.freshExistentialName(suffix) } // Set the fields which point companions at one another. Returns the module. override def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = synchronized { super.connectModuleToClass(m, moduleClass) } - + override def newFreeVar(name: TermName, tpe: Type, value: Any, newFlags: Long = 0L): FreeVar = new FreeVar(name, value) with SynchronizedTermSymbol initFlags newFlags setInfo tpe override protected def makeNoSymbol = new NoSymbol with SynchronizedSymbol - + trait SynchronizedSymbol extends Symbol { - + override def rawowner = synchronized { super.rawowner } override def rawname = synchronized { super.rawname } override def rawflags = synchronized { super.rawflags } - + override def rawflags_=(x: FlagsType) = synchronized { super.rawflags_=(x) } override def name_=(x: Name) = synchronized { super.name_=(x) } override def owner_=(owner: Symbol) = synchronized { super.owner_=(owner) } - + override def validTo = synchronized { super.validTo } override def validTo_=(x: Period) = synchronized { super.validTo_=(x) } - + override def pos = synchronized { super.pos } override def setPos(pos: Position): this.type = { synchronized { super.setPos(pos) }; this } override def privateWithin = synchronized { super.privateWithin } - override def privateWithin_=(sym: Symbol) = synchronized { super.privateWithin_=(sym) } + override def privateWithin_=(sym: Symbol) = synchronized { super.privateWithin_=(sym) } - override def info = synchronized { super.info } + override def info = synchronized { super.info } override def info_=(info: Type) = synchronized { super.info_=(info) } - override def updateInfo(info: Type): Symbol = synchronized { super.updateInfo(info) } + override def updateInfo(info: Type): Symbol = synchronized { super.updateInfo(info) } override def rawInfo: Type = synchronized { super.rawInfo } override def typeParams: List[Symbol] = synchronized { super.typeParams } - override def reset(completer: Type) = synchronized { super.reset(completer) } + override def reset(completer: Type) = synchronized { super.reset(completer) } - override def infosString: String = synchronized { super.infosString } + override def infosString: String = synchronized { super.infosString } override def annotations: List[AnnotationInfo] = synchronized { super.annotations } - override def setAnnotations(annots: List[AnnotationInfo]): this.type = { synchronized { super.setAnnotations(annots) }; this } + override def setAnnotations(annots: List[AnnotationInfo]): this.type = { synchronized { super.setAnnotations(annots) }; this } // ------ creators ------------------------------------------------------------------- override def newTermSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): TermSymbol = new TermSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags - + override def newAbstractTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AbstractTypeSymbol = new AbstractTypeSymbol(this, pos, name) with SynchronizedTypeSymbol initFlags newFlags - + override def newAliasTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AliasTypeSymbol = new AliasTypeSymbol(this, pos, name) with SynchronizedTypeSymbol initFlags newFlags @@ -72,10 +72,10 @@ trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => override def newClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = new ClassSymbol(this, pos, name) with SynchronizedClassSymbol initFlags newFlags - + override def newModuleClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleClassSymbol = new ModuleClassSymbol(this, pos, name) with SynchronizedModuleClassSymbol initFlags newFlags - + override def newTypeSkolemSymbol(name: TypeName, origin: AnyRef, pos: Position = NoPosition, newFlags: Long = 0L): TypeSkolem = if ((newFlags & DEFERRED) == 0L) new TypeSkolem(this, pos, name, origin) with SynchronizedTypeSymbol initFlags newFlags @@ -116,4 +116,4 @@ trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => override def implicitMembers: List[Symbol] = synchronized { super.implicitMembers } } } - + diff --git a/src/compiler/scala/reflect/runtime/SynchronizedTypes.scala b/src/compiler/scala/reflect/runtime/SynchronizedTypes.scala index c842d3dd01..e5a508f802 100644 --- a/src/compiler/scala/reflect/runtime/SynchronizedTypes.scala +++ b/src/compiler/scala/reflect/runtime/SynchronizedTypes.scala @@ -2,86 +2,86 @@ package scala.reflect package runtime /** This trait overrides methods in reflect.internal, bracketing - * them in synchronized { ... } to make them thread-safe + * them in synchronized { ... } to make them thread-safe */ trait SynchronizedTypes extends internal.Types { self: SymbolTable => - + // No sharing of map objects: override protected def commonOwnerMap = new CommonOwnerMap - + private val uniqueLock = new Object override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { super.unique(tp) } - + class SynchronizedUndoLog extends UndoLog { - - override def clear() = + + override def clear() = synchronized { super.clear() } - + override def undo[T](block: => T): T = synchronized { super.undo(block) } - + override def undoUnless(block: => Boolean): Boolean = synchronized { super.undoUnless(block) } } - + override protected def newUndoLog = new SynchronizedUndoLog - - override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = + + override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = synchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) } - - private val subsametypeLock = new Object - + + private val subsametypeLock = new Object + override def isSameType(tp1: Type, tp2: Type): Boolean = subsametypeLock.synchronized { super.isSameType(tp1, tp2) } - + override def isDifferentType(tp1: Type, tp2: Type): Boolean = subsametypeLock.synchronized { super.isDifferentType(tp1, tp2) } - + override def isSubType(tp1: Type, tp2: Type, depth: Int): Boolean = subsametypeLock.synchronized { super.isSubType(tp1, tp2, depth) } - + private val lubglbLock = new Object - + override def glb(ts: List[Type]): Type = lubglbLock.synchronized { super.glb(ts) } - + override def lub(ts: List[Type]): Type = lubglbLock.synchronized { super.lub(ts) } - + private val indentLock = new Object - + override protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { indentLock.synchronized { super.explain(op, p, tp1, arg2) } } - + private val toStringLock = new Object override protected def typeToString(tpe: Type): String = toStringLock.synchronized(super.typeToString(tpe)) - - /* The idea of caches is as follows. + + /* The idea of caches is as follows. * When in reflexive mode, a cache is either null, or one sentinal * value representing undefined or the final defined * value. Hence, we can ask in non-synchronized ode whether the cache field - * is non null and different from the sentinel (if a sentinel exists). + * is non null and different from the sentinel (if a sentinel exists). * If that's true, the cache value is current. * Otherwise we arrive in one of the defined... methods listed below * which go through all steps in synchronized mode. */ - + override protected def defineUnderlyingOfSingleType(tpe: SingleType) = tpe.synchronized { super.defineUnderlyingOfSingleType(tpe) } - - override protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = + + override protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = tpe.synchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } - override protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = + override protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = tpe.synchronized { super.defineBaseClassesOfCompoundType(tpe) } - - override protected def defineParentsOfTypeRef(tpe: TypeRef) = + + override protected def defineParentsOfTypeRef(tpe: TypeRef) = tpe.synchronized { super.defineParentsOfTypeRef(tpe) } - - override protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = + + override protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = tpe.synchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/ToolBoxes.scala b/src/compiler/scala/reflect/runtime/ToolBoxes.scala index 70a3061fc7..8cc4d5f788 100644 --- a/src/compiler/scala/reflect/runtime/ToolBoxes.scala +++ b/src/compiler/scala/reflect/runtime/ToolBoxes.scala @@ -44,11 +44,11 @@ trait ToolBoxes extends { self: Universe => // !!! Why is this is in the empty package? If it's only to make // it inaccessible then please put it somewhere designed for that // rather than polluting the empty package with synthetics. - trace("typing: ")(showAttributed(tree)) + trace("typing: ")(showAttributed(tree, true, true, settings.Yshowsymkinds.value)) val ownerClass = EmptyPackageClass.newClassWithInfo(newTypeName("<expression-owner>"), List(ObjectClass.tpe), newScope) val owner = ownerClass.newLocalDummy(tree.pos) val ttree = typer.atOwner(tree, owner).typed(tree, analyzer.EXPRmode, pt) - trace("typed: ")(showAttributed(ttree)) + trace("typed: ")(showAttributed(ttree, true, true, settings.Yshowsymkinds.value)) ttree } @@ -64,7 +64,7 @@ trait ToolBoxes extends { self: Universe => obj setInfo obj.moduleClass.tpe val meth = obj.moduleClass.newMethod(newTermName(wrapperMethodName)) def makeParam(fv: Symbol) = meth.newValueParameter(fv.name.toTermName) setInfo fv.tpe - meth setInfo MethodType(fvs map makeParam, expr.tpe) + meth setInfo MethodType(fvs map makeParam, AnyClass.tpe) minfo.decls enter meth trace("wrapping ")(defOwner(expr) -> meth) val methdef = DefDef(meth, expr changeOwner (defOwner(expr) -> meth)) @@ -78,9 +78,9 @@ trait ToolBoxes extends { self: Universe => List(List()), List(methdef), NoPosition)) - trace("wrapped: ")(showAttributed(moduledef)) + trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) val cleanedUp = resetLocalAttrs(moduledef) - trace("cleaned up: ")(showAttributed(cleanedUp)) + trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymkinds.value)) cleanedUp } @@ -94,6 +94,20 @@ trait ToolBoxes extends { self: Universe => } def compileExpr(expr: Tree, fvs: List[Symbol]): String = { + // Previously toolboxes used to typecheck their inputs before compiling. + // Actually, the initial demo by Martin first typechecked the reified tree, + // then ran it, which typechecked it again, and only then launched the + // reflective compiler. + // + // However, as observed in https://issues.scala-lang.org/browse/SI-5464 + // current implementation typechecking is not always idempotent. + // That's why we cannot allow inputs of toolboxes to be typechecked, + // at least not until the aforementioned issue is closed. + val typed = expr filter (t => t.tpe != null && t.tpe != NoType && !t.isInstanceOf[TypeTree]) + if (!typed.isEmpty) { + throw new Error("cannot compile trees that are already typed") + } + val mdef = wrapInObject(expr, fvs) val pdef = wrapInPackage(mdef) val unit = wrapInCompilationUnit(pdef) @@ -106,7 +120,6 @@ trait ToolBoxes extends { self: Universe => jclazz.getDeclaredMethods.find(_.getName == name).get def runExpr(expr: Tree): Any = { - val etpe = expr.tpe val fvs = (expr filter isFree map (_.symbol)).distinct reporter.reset() @@ -164,7 +177,13 @@ trait ToolBoxes extends { self: Universe => } command.settings.outputDirs setSingleOutput virtualDirectory - new ToolBoxGlobal(command.settings, reporter) + val instance = new ToolBoxGlobal(command.settings, reporter) + + // need to establish a run an phase because otherwise we run into an assertion in TypeHistory + // that states that the period must be different from NoPeriod + val run = new instance.Run + instance.phase = run.refchecksPhase + instance } lazy val importer = new compiler.Importer { @@ -175,22 +194,13 @@ trait ToolBoxes extends { self: Universe => lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, defaultReflectiveClassLoader) - private def importAndTypeCheck(tree: rm.Tree, expectedType: rm.Type): compiler.Tree = { - // need to establish a run an phase because otherwise we run into an assertion in TypeHistory - // that states that the period must be different from NoPeriod - val run = new compiler.Run - compiler.phase = run.refchecksPhase + def typeCheck(tree: rm.Tree, expectedType: rm.Type): rm.Tree = { + if (compiler.settings.verbose.value) println("typing "+tree+", pt = "+expectedType) val ctree: compiler.Tree = importer.importTree(tree.asInstanceOf[Tree]) val pt: compiler.Type = importer.importType(expectedType.asInstanceOf[Type]) -// val typer = compiler.typer.atOwner(ctree, if (owner.isModule) cowner.moduleClass else cowner) val ttree: compiler.Tree = compiler.typedTopLevelExpr(ctree, pt) - ttree - } - - def typeCheck(tree: rm.Tree, expectedType: rm.Type): rm.Tree = { - if (compiler.settings.verbose.value) println("typing "+tree+", pt = "+expectedType) - val ttree = importAndTypeCheck(tree, expectedType) - exporter.importTree(ttree).asInstanceOf[rm.Tree] + val rmttree = exporter.importTree(ttree).asInstanceOf[rm.Tree] + rmttree } def typeCheck(tree: rm.Tree): rm.Tree = @@ -199,11 +209,10 @@ trait ToolBoxes extends { self: Universe => def showAttributed(tree: rm.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = compiler.showAttributed(importer.importTree(tree.asInstanceOf[Tree]), printTypes, printIds, printKinds) - def runExpr(tree: rm.Tree, expectedType: rm.Type): Any = { - val ttree = importAndTypeCheck(tree, expectedType) - compiler.runExpr(ttree) + def runExpr(tree: rm.Tree): Any = { + if (compiler.settings.verbose.value) println("running "+tree) + val ctree: compiler.Tree = importer.importTree(tree.asInstanceOf[Tree]) + compiler.runExpr(ctree) } - - def runExpr(tree: rm.Tree): Any = runExpr(tree, WildcardType.asInstanceOf[rm.Type]) } } diff --git a/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala b/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala index fc4177e956..61001a4778 100644 --- a/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala +++ b/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala @@ -2,10 +2,12 @@ package scala.reflect package runtime trait TreeBuildUtil extends Universe with api.TreeBuildUtil { - - def staticClass(fullname: String): Symbol = definitions.getRequiredClass(fullname) - def staticModule(fullname: String): Symbol = definitions.getRequiredModule(fullname) - def thisModuleType(fullname: String) = staticModule(fullname).moduleClass.thisType + /** A comment to the effect of why initialize was added to all these + * would be appreciated. (We may as well start somewhere.) + */ + def staticClass(fullname: String) = definitions.getRequiredClass(fullname).initialize + def staticModule(fullname: String) = definitions.getRequiredModule(fullname).initialize + def thisModuleType(fullname: String) = staticModule(fullname).moduleClass.initialize.thisType /** Selects type symbol with given name from the defined members of prefix type */ @@ -39,7 +41,7 @@ trait TreeBuildUtil extends Universe with api.TreeBuildUtil { selectIn(owner.info, idx) } - def freeVar(name: String, info: Type, value: Any) = newFreeVar(newTermName(name), info, value) + def newFreeVar(name: String, info: Type, value: Any) = newFreeVar(newTermName(name), info, value) def modifiersFromInternalFlags(flags: Long, privateWithin: Name, annotations: List[Tree]): Modifiers = Modifiers(flags, privateWithin, annotations) diff --git a/src/compiler/scala/reflect/runtime/Universe.scala b/src/compiler/scala/reflect/runtime/Universe.scala index c786bb86c5..700f819226 100644 --- a/src/compiler/scala/reflect/runtime/Universe.scala +++ b/src/compiler/scala/reflect/runtime/Universe.scala @@ -16,7 +16,7 @@ class Universe extends SymbolTable { val gen = new TreeGen { val global: Universe.this.type = Universe.this } - def settings = new Settings + lazy val settings = new Settings def forInteractive = false def forScaladoc = false diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index 04ff0c440d..3c79fcd3fb 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -90,7 +90,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { /** Defines valid values for properties that refer to compiler phases. */ object CompilerPhase extends PermissibleValue { - val values = List("namer", "typer", "pickler", "refchecks", "liftcode", + val values = List("namer", "typer", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "lazyvals", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "icode", "inliner", diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala index 253d1dec5d..c92474b33e 100644 --- a/src/compiler/scala/tools/ant/Scaladoc.scala +++ b/src/compiler/scala/tools/ant/Scaladoc.scala @@ -43,7 +43,8 @@ import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} * - `deprecation`, * - `docgenerator`, * - `docrootcontent`, - * - `unchecked`. + * - `unchecked`, + * - `nofail`. * * It also takes the following parameters as nested elements: * - `src` (for srcdir), @@ -123,6 +124,9 @@ class Scaladoc extends ScalaMatchingTask { /** Instruct the compiler to generate unchecked information. */ private var unchecked: Boolean = false + /** Instruct the ant task not to fail in the event of errors */ + private var nofail: Boolean = false + /*============================================================================*\ ** Properties setters ** \*============================================================================*/ @@ -353,6 +357,17 @@ class Scaladoc extends ScalaMatchingTask { docUncompilable = Some(input) } + /** Set the `nofail` info attribute. + * + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. + */ + def setNoFail(input: String) { + if (Flag.isPermissible(input)) + nofail = "yes".equals(input) || "on".equals(input) + else + buildError("Unknown nofail flag '" + input + "'") + } + /*============================================================================*\ ** Properties getters ** \*============================================================================*/ @@ -553,6 +568,8 @@ class Scaladoc extends ScalaMatchingTask { Pair(docSettings, sourceFiles) } + def safeBuildError(message: String): Unit = if (nofail) log(message) else buildError(message) + /** Performs the compilation. */ override def execute() = { val Pair(docSettings, sourceFiles) = initialize @@ -561,7 +578,7 @@ class Scaladoc extends ScalaMatchingTask { val docProcessor = new scala.tools.nsc.doc.DocFactory(reporter, docSettings) docProcessor.document(sourceFiles.map (_.toString)) if (reporter.ERROR.count > 0) - buildError( + safeBuildError( "Document failed with " + reporter.ERROR.count + " error" + (if (reporter.ERROR.count > 1) "s" else "") + @@ -576,11 +593,11 @@ class Scaladoc extends ScalaMatchingTask { } catch { case exception: Throwable if exception.getMessage ne null => exception.printStackTrace() - buildError("Document failed because of an internal documenter error (" + + safeBuildError("Document failed because of an internal documenter error (" + exception.getMessage + "); see the error output for details.") case exception => exception.printStackTrace() - buildError("Document failed because of an internal documenter error " + + safeBuildError("Document failed because of an internal documenter error " + "(no error message provided); see the error output for details.") } } diff --git a/src/compiler/scala/tools/ant/templates/tool-unix.tmpl b/src/compiler/scala/tools/ant/templates/tool-unix.tmpl index 7e51930fa4..599936f6f8 100644 --- a/src/compiler/scala/tools/ant/templates/tool-unix.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-unix.tmpl @@ -128,9 +128,11 @@ if [[ -z "$cygwin$mingw" ]]; then usebootcp="true" fi +# If using the boot classpath, also pass an empty classpath +# to java to suppress "." from materializing. classpathArgs () { if [[ -n $usebootcp ]]; then - echo "-Xbootclasspath/a:$TOOL_CLASSPATH" + echo "-Xbootclasspath/a:$TOOL_CLASSPATH -classpath \"\"" else echo "-classpath $TOOL_CLASSPATH" fi diff --git a/src/compiler/scala/tools/cmd/gen/AnyVals.scala b/src/compiler/scala/tools/cmd/gen/AnyVals.scala index ab4a4a4402..0869350dd3 100644 --- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala +++ b/src/compiler/scala/tools/cmd/gen/AnyVals.scala @@ -30,7 +30,7 @@ trait AnyValReps { " * @return the bitwise negation of this value\n" + " * @example {{{\n" + " * ~5 == -6\n" + - " * // in binary: ~00000101 == \n" + + " * // in binary: ~00000101 ==\n" + " * // 11111010\n" + " * }}}\n" + " */") :: ops @@ -44,9 +44,9 @@ trait AnyValReps { " * @return the bitwise OR of this value and x\n" + " * @example {{{\n" + " * (0xf0 | 0xaa) == 0xfa\n" + - " * // in binary: 11110000 \n" + - " * // | 10101010 \n" + - " * // -------- \n" + + " * // in binary: 11110000\n" + + " * // | 10101010\n" + + " * // --------\n" + " * // 11111010\n" + " * }}}\n" + " */"), @@ -54,9 +54,9 @@ trait AnyValReps { " * @return the bitwise AND of this value and x\n" + " * @example {{{\n" + " * (0xf0 & 0xaa) == 0xa0\n" + - " * // in binary: 11110000 \n" + - " * // & 10101010 \n" + - " * // -------- \n" + + " * // in binary: 11110000\n" + + " * // & 10101010\n" + + " * // --------\n" + " * // 10100000\n" + " * }}}\n" + " */"), @@ -64,9 +64,9 @@ trait AnyValReps { " * @return the bitwise XOR of this value and x\n" + " * @example {{{\n" + " * (0xf0 ^ 0xaa) == 0x5a\n" + - " * // in binary: 11110000 \n" + - " * // ^ 10101010 \n" + - " * // -------- \n" + + " * // in binary: 11110000\n" + + " * // ^ 10101010\n" + + " * // --------\n" + " * // 01011010\n" + " * }}}\n" + " */")) @@ -83,11 +83,11 @@ trait AnyValReps { Op(">>>", "/**\n" + " * @return this value bit-shifted right by the specified number of bits,\n" + - " * filling the new left bits with zeroes. \n" + + " * filling the new left bits with zeroes.\n" + " * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}}\n" + " * @example {{{\n" + - " * -21 >>> 3 == 536870909 \n" + - " * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == \n" + + " * -21 >>> 3 == 536870909\n" + + " * // in binary: 11111111 11111111 11111111 11101011 >>> 3 ==\n" + " * // 00011111 11111111 11111111 11111101\n" + " * }}}\n" + " */"), @@ -97,8 +97,8 @@ trait AnyValReps { " * filling in the right bits with the same value as the left-most bit of this.\n" + " * The effect of this is to retain the sign of the value.\n" + " * @example {{{\n" + - " * -21 >> 3 == -3 \n" + - " * // in binary: 11111111 11111111 11111111 11101011 >> 3 == \n" + + " * -21 >> 3 == -3\n" + + " * // in binary: 11111111 11111111 11111111 11101011 >> 3 ==\n" + " * // 11111111 11111111 11111111 11111101\n" + " * }}}\n" + " */")) diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index 940d115b2f..d6f57801e7 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -74,7 +74,7 @@ trait CompilationUnits { self: Global => * It is empty up to phase 'icode'. */ val icode: LinkedHashSet[icodes.IClass] = new LinkedHashSet - + def echo(pos: Position, msg: String) = reporter.echo(pos, msg) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 8e5ca2156a..248d5d675d 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -37,6 +37,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb with Plugins with PhaseAssembly with Trees + with Reifiers with TreePrinters with DocComments with MacroContext @@ -58,7 +59,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb type AbstractFileType = scala.tools.nsc.io.AbstractFile def mkAttributedQualifier(tpe: Type, termSym: Symbol): Tree = gen.mkAttributedQualifier(tpe, termSym) - + def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase // platform specific elements @@ -124,7 +125,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Print tree in detailed form */ object nodePrinters extends { val global: Global.this.type = Global.this - } with NodePrinters { + } with NodePrinters with ReifyPrinters { infolevel = InfoLevel.Verbose } @@ -134,6 +135,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb } with TreeBrowsers val nodeToString = nodePrinters.nodeToString + val reifiedNodeToString = nodePrinters.reifiedNodeToString val treeBrowser = treeBrowsers.create() // ------------ Hooks for interactive mode------------------------- @@ -152,7 +154,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Register top level class (called on entering the class) */ def registerTopLevelSym(sym: Symbol) {} - + // ------------------ Reporting ------------------------------------- // not deprecated yet, but a method called "error" imported into @@ -191,10 +193,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb if (settings.debug.value) body } - @inline final override def debuglog(msg: => String) { - if (settings.debug.value && (settings.log containsPhase globalPhase)) - inform("[log " + phase + "] " + msg) - } // Warnings issued only under -Ydebug. For messages which should reach // developer ears, but are not adequately actionable by users. @inline final override def debugwarn(msg: => String) { @@ -211,10 +209,29 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) def logError(msg: String, t: Throwable): Unit = () + + def logAfterEveryPhase[T](msg: String)(op: => T) { + log("Running operation '%s' after every phase.\n".format(msg) + describeAfterEveryPhase(op)) + } + + def shouldLogAtThisPhase = ( + (settings.log.isSetByUser) + && ((settings.log containsPhase globalPhase) || (settings.log containsPhase phase)) + ) + def atPhaseStackMessage = atPhaseStack match { + case Nil => "" + case ps => ps.reverseMap("->" + _).mkString("(", " ", ")") + } // Over 200 closure objects are eliminated by inlining this. - @inline final def log(msg: => AnyRef): Unit = - if (settings.log containsPhase globalPhase) - inform("[log " + phase + "] " + msg) + @inline final def log(msg: => AnyRef) { + if (shouldLogAtThisPhase) + inform("[log %s%s] %s".format(globalPhase, atPhaseStackMessage, msg)) + } + + @inline final override def debuglog(msg: => String) { + if (settings.debug.value) + log(msg) + } def logThrowable(t: Throwable): Unit = globalError(throwableAsString(t)) def throwableAsString(t: Throwable): String = @@ -463,17 +480,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb val runsRightAfter = None } with RefChecks - // phaseName = "liftcode" - object liftcode extends { - val global: Global.this.type = Global.this - val runsAfter = List("refchecks") - val runsRightAfter = None - } with LiftCode - // phaseName = "uncurry" override object uncurry extends { val global: Global.this.type = Global.this - val runsAfter = List("refchecks", "liftcode") + val runsAfter = List[String]("refchecks") val runsRightAfter = None } with UnCurry @@ -659,7 +669,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb extensionMethods -> "add extension methods for inline classes", pickler -> "serialize symbol tables", refChecks -> "reference/override checking, translate nested objects", - liftcode -> "reify trees", uncurry -> "uncurry, translate function values to anonymous classes", tailCalls -> "replace tail calls by jumps", specializeTypes -> "@specialized-driven class and method specialization", @@ -709,18 +718,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb private lazy val unitTimings = mutable.HashMap[CompilationUnit, Long]() withDefaultValue 0L // tracking time spent per unit private def unitTimingsFormatted(): String = { def toMillis(nanos: Long) = "%.3f" format nanos / 1000000d - + val formatter = new util.TableDef[(String, String)] { >> ("ms" -> (_._1)) >+ " " << ("path" -> (_._2)) } "" + ( - new formatter.Table(unitTimings.toList sortBy (-_._2) map { + new formatter.Table(unitTimings.toList sortBy (-_._2) map { case (unit, nanos) => (toMillis(nanos), unit.source.path) }) ) } - + protected def addToPhasesSet(sub: SubComponent, descr: String) { phasesSet += sub phasesDescMap(sub) = descr @@ -767,6 +776,51 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb line1 :: line2 :: descs mkString } + /** Returns List of (phase, value) pairs, including only those + * where the value compares unequal to the previous phase's value. + */ + def afterEachPhase[T](op: => T): List[(Phase, T)] = { + phaseDescriptors.map(_.ownPhase).filterNot(_ eq NoPhase).foldLeft(List[(Phase, T)]()) { (res, ph) => + val value = afterPhase(ph)(op) + if (res.nonEmpty && res.head._2 == value) res + else ((ph, value)) :: res + } reverse + } + + /** Returns List of ChangeAfterPhase objects, encapsulating those + * phase transitions where the result of the operation gave a different + * list than it had when run during the previous phase. + */ + def changesAfterEachPhase[T](op: => List[T]): List[ChangeAfterPhase[T]] = { + val ops = ((NoPhase, Nil)) :: afterEachPhase(op) + + ops sliding 2 map { + case (_, before) :: (ph, after) :: Nil => + val lost = before filterNot (after contains _) + val gained = after filterNot (before contains _) + ChangeAfterPhase(ph, lost, gained) + case _ => ??? + } toList + } + private def numberedPhase(ph: Phase) = "%2d/%s".format(ph.id, ph.name) + + case class ChangeAfterPhase[+T](ph: Phase, lost: List[T], gained: List[T]) { + private def mkStr(what: String, xs: List[_]) = ( + if (xs.isEmpty) "" + else xs.mkString(what + " after " + numberedPhase(ph) + " {\n ", "\n ", "\n}\n") + ) + override def toString = mkStr("Lost", lost) + mkStr("Gained", gained) + } + + def describeAfterEachPhase[T](op: => T): List[String] = + afterEachPhase(op) map { case (ph, t) => "[after %-15s] %s".format(numberedPhase(ph), t) } + + def describeAfterEveryPhase[T](op: => T): String = + describeAfterEachPhase(op) map (" " + _ + "\n") mkString + + def printAfterEachPhase[T](op: => T): Unit = + describeAfterEachPhase(op) foreach (m => println(" " + m)) + // ----------- Runs --------------------------------------- private var curRun: Run = null @@ -821,6 +875,28 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def currentUnit: CompilationUnit = if (currentRun eq null) NoCompilationUnit else currentRun.currentUnit def currentSource: SourceFile = if (currentUnit.exists) currentUnit.source else lastSeenSourceFile + // TODO - trim these to the absolute minimum. + @inline final def afterErasure[T](op: => T): T = afterPhase(currentRun.erasurePhase)(op) + @inline final def afterExplicitOuter[T](op: => T): T = afterPhase(currentRun.explicitouterPhase)(op) + @inline final def afterFlatten[T](op: => T): T = afterPhase(currentRun.flattenPhase)(op) + @inline final def afterIcode[T](op: => T): T = afterPhase(currentRun.icodePhase)(op) + @inline final def afterMixin[T](op: => T): T = afterPhase(currentRun.mixinPhase)(op) + @inline final def afterPickler[T](op: => T): T = afterPhase(currentRun.picklerPhase)(op) + @inline final def afterRefchecks[T](op: => T): T = afterPhase(currentRun.refchecksPhase)(op) + @inline final def afterSpecialize[T](op: => T): T = afterPhase(currentRun.specializePhase)(op) + @inline final def afterTyper[T](op: => T): T = afterPhase(currentRun.typerPhase)(op) + @inline final def afterUncurry[T](op: => T): T = afterPhase(currentRun.uncurryPhase)(op) + @inline final def beforeErasure[T](op: => T): T = beforePhase(currentRun.erasurePhase)(op) + @inline final def beforeExplicitOuter[T](op: => T): T = beforePhase(currentRun.explicitouterPhase)(op) + @inline final def beforeFlatten[T](op: => T): T = beforePhase(currentRun.flattenPhase)(op) + @inline final def beforeIcode[T](op: => T): T = beforePhase(currentRun.icodePhase)(op) + @inline final def beforeMixin[T](op: => T): T = beforePhase(currentRun.mixinPhase)(op) + @inline final def beforePickler[T](op: => T): T = beforePhase(currentRun.picklerPhase)(op) + @inline final def beforeRefchecks[T](op: => T): T = beforePhase(currentRun.refchecksPhase)(op) + @inline final def beforeSpecialize[T](op: => T): T = beforePhase(currentRun.specializePhase)(op) + @inline final def beforeTyper[T](op: => T): T = beforePhase(currentRun.typerPhase)(op) + @inline final def beforeUncurry[T](op: => T): T = beforePhase(currentRun.uncurryPhase)(op) + /** Don't want to introduce new errors trying to report errors, * so swallow exceptions. */ @@ -867,7 +943,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Counts for certain classes of warnings during this run. */ var deprecationWarnings: List[(Position, String)] = Nil var uncheckedWarnings: List[(Position, String)] = Nil - + /** A flag whether macro expansions failed */ var macroExpansionFailed = false @@ -928,16 +1004,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb // Each subcomponent supplies a phase, which are chained together. // If -Ystop:phase is given, neither that phase nor any beyond it is added. // If -Yskip:phase is given, that phase will be skipped. - val lastPhase = phaseDescriptors.tail . - takeWhile (pd => !stopPhase(pd.phaseName)) . - filterNot (pd => skipPhase(pd.phaseName)) . - foldLeft (parserPhase) ((chain, ph) => ph newPhase chain) - - // Ensure there is a terminal phase at the end, since -Ystop may have limited the phases. - terminalPhase = - if (lastPhase.name == "terminal") lastPhase - else terminal newPhase lastPhase - + val phaseLinks = { + val phs = ( + phaseDescriptors.tail + takeWhile (pd => !stopPhase(pd.phaseName)) + filterNot (pd => skipPhase(pd.phaseName)) + ) + // Ensure there is a terminal phase at the end, since -Ystop may have limited the phases. + if (phs.isEmpty || (phs.last ne terminal)) phs :+ terminal + else phs + } + // Link them together. + phaseLinks.foldLeft(parserPhase)((chain, ph) => ph newPhase chain) parserPhase } @@ -1014,38 +1092,46 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb } def cancel() { reporter.cancelled = true } - + private def currentProgress = (phasec * size) + unitc private def totalProgress = (phaseDescriptors.size - 1) * size // -1: drops terminal phase private def refreshProgress() = if (size > 0) progress(currentProgress, totalProgress) // ----- finding phases -------------------------------------------- - def phaseNamed(name: String): Phase = { - var p: Phase = firstPhase - while (p.next != p && p.name != name) p = p.next - if (p.name != name) NoPhase else p - } - - val parserPhase = phaseNamed("parser") - val namerPhase = phaseNamed("namer") - // packageobjects - val typerPhase = phaseNamed("typer") - val inlineclassesPhase = phaseNamed("inlineclasses") - // superaccessors - val picklerPhase = phaseNamed("pickler") - val refchecksPhase = phaseNamed("refchecks") - val uncurryPhase = phaseNamed("uncurry") - // tailcalls, specialize - val explicitouterPhase = phaseNamed("explicitouter") - val erasurePhase = phaseNamed("erasure") - // lazyvals, lambdalift, constructors - val flattenPhase = phaseNamed("flatten") - val mixinPhase = phaseNamed("mixin") - val cleanupPhase = phaseNamed("cleanup") - val icodePhase = phaseNamed("icode") - // inliner, closelim, dce - val jvmPhase = phaseNamed("jvm") + def phaseNamed(name: String): Phase = + findOrElse(firstPhase.iterator)(_.name == name)(NoPhase) + + /** All phases as of 3/2012 here for handiness; the ones in + * active use uncommented. + */ + val parserPhase = phaseNamed("parser") + val namerPhase = phaseNamed("namer") + // val packageobjectsPhase = phaseNamed("packageobjects") + val typerPhase = phaseNamed("typer") + val inlineclassesPhase = phaseNamed("inlineclasses") + // val superaccessorsPhase = phaseNamed("superaccessors") + val picklerPhase = phaseNamed("pickler") + val refchecksPhase = phaseNamed("refchecks") + // val selectiveanfPhase = phaseNamed("selectiveanf") + // val selectivecpsPhase = phaseNamed("selectivecps") + val uncurryPhase = phaseNamed("uncurry") + // val tailcallsPhase = phaseNamed("tailcalls") + val specializePhase = phaseNamed("specialize") + val explicitouterPhase = phaseNamed("explicitouter") + val erasurePhase = phaseNamed("erasure") + // val lazyvalsPhase = phaseNamed("lazyvals") + val lambdaliftPhase = phaseNamed("lambdalift") + // val constructorsPhase = phaseNamed("constructors") + val flattenPhase = phaseNamed("flatten") + val mixinPhase = phaseNamed("mixin") + val cleanupPhase = phaseNamed("cleanup") + val icodePhase = phaseNamed("icode") + // val inlinerPhase = phaseNamed("inliner") + // val inlineExceptionHandlersPhase = phaseNamed("inlineExceptionHandlers") + // val closelimPhase = phaseNamed("closelim") + // val dcePhase = phaseNamed("dce") + val jvmPhase = phaseNamed("jvm") def runIsAt(ph: Phase) = globalPhase.id == ph.id def runIsPast(ph: Phase) = globalPhase.id > ph.id @@ -1090,7 +1176,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def compiles(sym: Symbol): Boolean = if (sym == NoSymbol) false else if (symSource.isDefinedAt(sym)) true - else if (!sym.owner.isPackageClass) compiles(sym.toplevelClass) + else if (!sym.owner.isPackageClass) compiles(sym.enclosingTopLevelClass) else if (sym.isModuleClass) compiles(sym.sourceModule) else false @@ -1128,7 +1214,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb lazy val trackers = currentRun.units.toList map (x => SymbolTracker(x)) def snapshot() = { inform("\n[[symbol layout at end of " + phase + "]]") - atPhase(phase.next) { + afterPhase(phase) { trackers foreach { t => t.snapshot() inform(t.show("Heading from " + phase.prev.name + " to " + phase.name)) @@ -1181,12 +1267,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb */ def compileUnits(units: List[CompilationUnit], fromPhase: Phase) { try compileUnitsInternal(units, fromPhase) - catch { case ex => + catch { case ex => globalError(supplementErrorMessage("uncaught exception during compilation: " + ex.getClass.getName)) throw ex } } - + private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) { units foreach addUnit if (opt.profileAll) { @@ -1199,7 +1285,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb checkDeprecatedSettings(unitbuf.head) globalPhase = fromPhase - while (globalPhase != terminalPhase && !reporter.hasErrors) { + while (globalPhase.hasNext && !reporter.hasErrors) { val startTime = currentTime phase = globalPhase @@ -1320,19 +1406,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Compile abstract file until `globalPhase`, but at least to phase "namer". */ def compileLate(unit: CompilationUnit) { - def stop(ph: Phase) = ph == null || ph.id >= (globalPhase.id max typerPhase.id) - def loop(ph: Phase) { - if (stop(ph)) refreshProgress - else { - atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit) - loop(ph.next match { - case `ph` => null // ph == ph.next implies terminal, and null ends processing - case x => x - }) - } - } + val maxId = math.max(globalPhase.id, typerPhase.id) addUnit(unit) - loop(firstPhase) + + firstPhase.iterator takeWhile (_.id < maxId) foreach (ph => + atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit) + ) + refreshProgress } /** @@ -1402,7 +1482,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def printAllUnits() { print("[[syntax trees at end of " + phase + "]]") - atPhase(phase.next) { currentRun.units foreach (treePrinter.print(_)) } + afterPhase(phase) { currentRun.units foreach (treePrinter.print(_)) } } private def findMemberFromRoot(fullName: Name): Symbol = { diff --git a/src/compiler/scala/tools/nsc/MacroContext.scala b/src/compiler/scala/tools/nsc/MacroContext.scala index 72662291f8..9ea1f87125 100644 --- a/src/compiler/scala/tools/nsc/MacroContext.scala +++ b/src/compiler/scala/tools/nsc/MacroContext.scala @@ -3,8 +3,8 @@ package scala.tools.nsc import symtab.Flags._ trait MacroContext extends reflect.macro.Context { self: Global => - + def captureVariable(vble: Symbol): Unit = vble setFlag CAPTURED - + def referenceCapturedVariable(id: Ident): Tree = ReferenceToBoxed(id) } diff --git a/src/compiler/scala/tools/nsc/SubComponent.scala b/src/compiler/scala/tools/nsc/SubComponent.scala index cd9fef117f..a3e451f32f 100644 --- a/src/compiler/scala/tools/nsc/SubComponent.scala +++ b/src/compiler/scala/tools/nsc/SubComponent.scala @@ -47,6 +47,9 @@ abstract class SubComponent { private var ownPhaseCache: WeakReference[Phase] = new WeakReference(null) private var ownPhaseRunId = global.NoRunId + @inline final def beforeOwnPhase[T](op: => T) = global.beforePhase(ownPhase)(op) + @inline final def afterOwnPhase[T](op: => T) = global.afterPhase(ownPhase)(op) + /** The phase corresponding to this subcomponent in the current compiler run */ def ownPhase: Phase = { ownPhaseCache.get match { diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index ea51fc0141..9466d1c1f2 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -71,34 +71,39 @@ abstract class NodePrinters { def nodeinfo(tree: Tree): String = if (infolevel == InfoLevel.Quiet) "" else { - val buf = new StringBuilder(" // sym=" + tree.symbol) - if (tree.hasSymbol) { - if (tree.symbol.isPrimaryConstructor) - buf.append(", isPrimaryConstructor") - else if (tree.symbol.isConstructor) - buf.append(", isConstructor") - if (tree.symbol != NoSymbol) - buf.append(", sym.owner=" + tree.symbol.owner) - buf.append(", sym.tpe=" + tree.symbol.tpe) - } - buf.append(", tpe=" + tree.tpe) - if (tree.tpe != null) { - var sym = tree.tpe.termSymbol - if (sym == NoSymbol) sym = tree.tpe.typeSymbol - buf.append(", tpe.sym=" + sym) - if (sym != NoSymbol) { - buf.append(", tpe.sym.owner=" + sym.owner) - if ((infolevel > InfoLevel.Normal) && - !(sym.owner eq definitions.ScalaPackageClass) && - !sym.isModuleClass && !sym.isPackageClass && - !sym.isJavaDefined) { - val members = for (m <- tree.tpe.decls) - yield m.toString() + ": " + m.tpe + ", " - buf.append(", tpe.decls=" + members) + try { + val buf = new StringBuilder(" // sym=" + tree.symbol) + if (tree.hasSymbol) { + if (tree.symbol.isPrimaryConstructor) + buf.append(", isPrimaryConstructor") + else if (tree.symbol.isConstructor) + buf.append(", isConstructor") + if (tree.symbol != NoSymbol) + buf.append(", sym.owner=" + tree.symbol.owner) + buf.append(", sym.tpe=" + tree.symbol.tpe) + } + buf.append(", tpe=" + tree.tpe) + if (tree.tpe != null) { + var sym = tree.tpe.termSymbol + if (sym == NoSymbol) sym = tree.tpe.typeSymbol + buf.append(", tpe.sym=" + sym) + if (sym != NoSymbol) { + buf.append(", tpe.sym.owner=" + sym.owner) + if ((infolevel > InfoLevel.Normal) && + !(sym.owner eq definitions.ScalaPackageClass) && + !sym.isModuleClass && !sym.isPackageClass && + !sym.isJavaDefined) { + val members = for (m <- tree.tpe.decls) + yield m.toString() + ": " + m.tpe + ", " + buf.append(", tpe.decls=" + members) + } } } + buf.toString + } catch { + case ex: Throwable => + return " // sym= <error> " + ex.getMessage } - buf.toString } def nodeinfo2(tree: Tree): String = (if (comma) "," else "") + nodeinfo(tree) diff --git a/src/compiler/scala/tools/nsc/ast/Reifiers.scala b/src/compiler/scala/tools/nsc/ast/Reifiers.scala new file mode 100644 index 0000000000..7ece8bbd31 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/Reifiers.scala @@ -0,0 +1,761 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Gilles Dubochet + */ + +package scala.tools.nsc +package ast + +import symtab._ +import Flags._ +import scala.reflect.api.Modifier._ +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.ListBuffer +import scala.tools.nsc.util.FreshNameCreator +import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple } + +/** Given a tree or type, generate a tree that when executed at runtime produces the original tree or type. + * See more info in the comments to `reify' in scala.reflect.macro.Context. + * + * @author Martin Odersky + * @version 2.10 + */ +trait Reifiers { self: Global => + + def reify(tree: Tree): Tree = { + class Reifier { + import definitions._ + import Reifier._ + + final val scalaPrefix = "scala." + final val localPrefix = "$local" + final val memoizerName = "$memo" + + val reifyDebug = settings.Yreifydebug.value + + private val reifiableSyms = mutable.ArrayBuffer[Symbol]() // the symbols that are reified with the tree + private val symIndex = mutable.HashMap[Symbol, Int]() // the index of a reifiable symbol in `reifiableSyms` + private var boundSyms = Set[Symbol]() // set of all symbols that are bound in tree to be reified + + private def definedInLiftedCode(tpe: Type) = + tpe exists (tp => boundSyms contains tp.typeSymbol) + + private def definedInLiftedCode(sym: Symbol) = + boundSyms contains sym + + /** + * Generate tree of the form + * + * { val $mr = scala.reflect.runtime.Mirror + * $local1 = new TypeSymbol(owner1, NoPosition, name1) + * ... + * $localN = new TermSymbol(ownerN, NoPositiion, nameN) + * $local1.setInfo(tpe1) + * ... + * $localN.setInfo(tpeN) + * $localN.setAnnotations(annotsN) + * rtree + * } + * + * where + * + * - `$localI` are free type symbols in the environment, as well as local symbols + * of refinement types. + * - `tpeI` are the info's of `symI` + * - `rtree` is code that generates `data` at runtime, maintaining all attributes. + * - `data` is typically a tree or a type. + */ + def reifyTopLevel(data: Any): Tree = { + val rtree = reify(data) + Block(mirrorAlias :: reifySymbolTableSetup, rtree) + } + + private def isLocatable(sym: Symbol) = + sym.isPackageClass || sym.owner.isClass || sym.isTypeParameter && sym.paramPos >= 0 + + private def registerReifiableSymbol(sym: Symbol): Unit = + if (!(symIndex contains sym)) { + sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol + symIndex(sym) = reifiableSyms.length + reifiableSyms += sym + } + + // helper methods + + private def localName(sym: Symbol): TermName = + newTermName(localPrefix + symIndex(sym)) + + private def call(fname: String, args: Tree*): Tree = + Apply(termPath(fname), args.toList) + + private def mirrorSelect(name: String): Tree = + termPath(nme.MIRROR_PREFIX + name) + + private def mirrorCall(name: TermName, args: Tree*): Tree = + call("" + (nme.MIRROR_PREFIX append name), args: _*) + + private def mirrorCall(name: String, args: Tree*): Tree = + call(nme.MIRROR_PREFIX + name, args: _*) + + private def mirrorFactoryCall(value: Product, args: Tree*): Tree = + mirrorFactoryCall(value.productPrefix, args: _*) + + private def mirrorFactoryCall(prefix: String, args: Tree*): Tree = + mirrorCall(prefix, args: _*) + + private def scalaFactoryCall(name: String, args: Tree*): Tree = + call(scalaPrefix + name + ".apply", args: _*) + + private def mkList(args: List[Tree]): Tree = + scalaFactoryCall("collection.immutable.List", args: _*) + + private def reifyModifiers(m: Modifiers) = + mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations)) + + private def reifyAggregate(name: String, args: Any*) = + scalaFactoryCall(name, (args map reify).toList: _*) + + /** + * Reify a list + */ + private def reifyList(xs: List[Any]): Tree = + mkList(xs map reify) + + /** + * Reify an array + */ + private def reifyArray(xs: Array[_]): Tree = + // @xeno.by: doesn't work for Array(LiteralAnnotArg(...)) + // because we cannot generate manifests for path-dependent types + scalaFactoryCall(nme.Array, xs map reify: _*) + + /** Reify a name */ + private def reifyName(name: Name) = + mirrorCall(if (name.isTypeName) "newTypeName" else "newTermName", Literal(Constant(name.toString))) + + private def isFree(sym: Symbol) = + !(symIndex contains sym) + + /** + * Reify a reference to a symbol + */ + private def reifySymRef(sym: Symbol): Tree = { + symIndex get sym match { + case Some(idx) => + Ident(localName(sym)) + case None => + if (sym == NoSymbol) + mirrorSelect("NoSymbol") + else if (sym == RootPackage) + mirrorSelect("definitions.RootPackage") + else if (sym == RootClass) + mirrorSelect("definitions.RootClass") + else if (sym == EmptyPackage) + mirrorSelect("definitions.EmptyPackage") + else if (sym.isModuleClass) + Select(reifySymRef(sym.sourceModule), "moduleClass") + else if (sym.isStatic && sym.isClass) + mirrorCall("staticClass", reify(sym.fullName)) + else if (sym.isStatic && sym.isModule) + mirrorCall("staticModule", reify(sym.fullName)) + else if (isLocatable(sym)) + if (sym.isTypeParameter) + mirrorCall("selectParam", reify(sym.owner), reify(sym.paramPos)) + else { + if (reifyDebug) println("locatable: " + sym + " " + sym.isPackageClass + " " + sym.owner + " " + sym.isTypeParameter) + val rowner = reify(sym.owner) + val rname = reify(sym.name.toString) + if (sym.isType) + mirrorCall("selectType", rowner, rname) + else if (sym.isMethod && sym.owner.isClass && sym.owner.info.decl(sym.name).isOverloaded) { + val index = sym.owner.info.decl(sym.name).alternatives indexOf sym + assert(index >= 0, sym) + mirrorCall("selectOverloadedMethod", rowner, rname, reify(index)) + } else + mirrorCall("selectTerm", rowner, rname) + } + else { + if (sym.isTerm) { + if (reifyDebug) println("Free: " + sym) + val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false) + def markIfCaptured(arg: Ident): Tree = + if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg + mirrorCall("newFreeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym))) + } else { + if (reifyDebug) println("Late local: " + sym) + registerReifiableSymbol(sym) + reifySymRef(sym) + } + } + } + } + + /** + * reify the creation of a symbol + */ + private def reifySymbolDef(sym: Symbol): Tree = { + if (reifyDebug) println("reify sym def " + sym) + + ValDef(NoMods, localName(sym), TypeTree(), + Apply( + Select(reify(sym.owner), "newNestedSymbol"), + List(reify(sym.name), reify(sym.pos), Literal(Constant(sym.flags))) + ) + ) + } + + /** + * Generate code to add type and annotation info to a reified symbol + */ + private def fillInSymbol(sym: Symbol): Tree = { + val rset = Apply(Select(reifySymRef(sym), nme.setTypeSignature), List(reifyType(sym.info))) + if (sym.annotations.isEmpty) rset + else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations))) + } + + /** Reify a scope */ + private def reifyScope(scope: Scope): Tree = { + scope foreach registerReifiableSymbol + mirrorCall(nme.newScopeWith, scope.toList map reifySymRef: _*) + } + + /** Reify a list of symbols that need to be created */ + private def reifySymbols(syms: List[Symbol]): Tree = { + syms foreach registerReifiableSymbol + mkList(syms map reifySymRef) + } + + /** Reify a type that defines some symbols */ + private def reifyTypeBinder(value: Product, bound: List[Symbol], underlying: Type): Tree = + mirrorFactoryCall(value, reifySymbols(bound), reify(underlying)) + + /** Reify a type */ + private def reifyType(tpe0: Type): Tree = { + val tpe = tpe0.normalize + + if (tpe.isErroneous) + CannotReifyErroneousType(tpe) + if (definedInLiftedCode(tpe)) + CannotReifyTypeInvolvingBoundType(tpe) + + val tsym = tpe.typeSymbol + if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic) + Select(reifySymRef(tpe.typeSymbol), nme.asTypeConstructor) + else tpe match { + case t @ NoType => + reifyMirrorObject(t) + case t @ NoPrefix => + reifyMirrorObject(t) + case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic => + mirrorCall(nme.thisModuleType, reify(clazz.fullName)) + case t @ RefinedType(parents, decls) => + registerReifiableSymbol(tpe.typeSymbol) + mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol)) + case t @ ClassInfoType(parents, decls, clazz) => + registerReifiableSymbol(clazz) + mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol)) + case t @ ExistentialType(tparams, underlying) => + reifyTypeBinder(t, tparams, underlying) + case t @ PolyType(tparams, underlying) => + reifyTypeBinder(t, tparams, underlying) + case t @ MethodType(params, restpe) => + reifyTypeBinder(t, params, restpe) + case t @ AnnotatedType(anns, underlying, selfsym) => + val saved1 = reifySymbols + val saved2 = reifyTypes + + try { + // one more quirk of reifying annotations + // + // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs + // that's because a lot of logic expects post-typer trees to have non-null tpes + // + // Q: reified trees are pre-typer, so there's shouldn't be a problem. + // reflective typechecker will fill in missing symbols and types, right? + // A: actually, no. annotation ASTs live inside AnnotatedTypes, + // and insides of the types is the place where typechecker doesn't look. + reifySymbols = true + reifyTypes = true + if (reifyDebug) println("reify AnnotatedType: " + tpe) + reifyProductUnsafe(tpe) + } finally { + reifySymbols = saved1 + reifyTypes = saved2 + } + case _ => + reifyProductUnsafe(tpe) + } + } + + var reifySymbols = false + var reifyTypes = false + + /** Preprocess a tree before reification */ + private def trimTree(tree: Tree): Tree = { + def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]) = { + var stats1 = stats filterNot (stat => stat.isDef && { + if (stat.symbol.isCaseAccessorMethod && reifyDebug) println("discarding case accessor method: " + stat) + stat.symbol.isCaseAccessorMethod + }) + stats1 = stats1 filterNot (memberDef => memberDef.isDef && { + val isSynthetic = memberDef.symbol.isSynthetic + // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass) + // that's why I replace the check with an assumption that all synthetic members are, in fact, generated of case classes +// val isCaseMember = deff.symbol.isCaseClass || deff.symbol.companionClass.isCaseClass + val isCaseMember = true + if (isSynthetic && isCaseMember && reifyDebug) println("discarding case class synthetic def: " + memberDef) + isSynthetic && isCaseMember + }) + stats1 = stats1 map { + case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isCaseAccessor => + if (reifyDebug) println("resetting visibility of case accessor field: " + valdef) + val Modifiers(flags, privateWithin, annotations) = mods + val flags1 = flags & ~Flags.LOCAL & ~Flags.PRIVATE + val mods1 = Modifiers(flags1, privateWithin, annotations) + ValDef(mods1, name, tpt, rhs).copyAttrs(valdef) + case stat => + stat + } + stats1 + } + + def trimSyntheticCaseClassCompanions(stats: List[Tree]) = + stats diff (stats collect { case moddef: ModuleDef => moddef } filter (moddef => { + val isSynthetic = moddef.symbol.isSynthetic + // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass) + // that's why I replace the check with an assumption that all synthetic modules are, in fact, companions of case classes +// val isCaseCompanion = moddef.symbol.companionClass.isCaseClass + val isCaseCompanion = true + // @xeno.by: we also have to do this ugly hack for the very same reason described above + // normally this sort of stuff is performed in reifyTree, which binds related symbols, however, local companions will be out of its reach + if (reifyDebug) println("boundSym: "+ moddef.symbol) + boundSyms += moddef.symbol + if (isSynthetic && isCaseCompanion && reifyDebug) println("discarding synthetic case class companion: " + moddef) + isSynthetic && isCaseCompanion + })) + + tree match { + case tree if tree.isErroneous => + tree + case ta @ TypeApply(hk, ts) => + def isErased(tt: TypeTree) = tt.tpe != null && definedInLiftedCode(tt.tpe) && tt.original == null + val discard = ts collect { case tt: TypeTree => tt } exists isErased + if (reifyDebug && discard) println("discarding TypeApply: " + tree) + if (discard) hk else ta + case classDef @ ClassDef(mods, name, params, impl) => + val Template(parents, self, body) = impl + val body1 = trimSyntheticCaseClassMembers(classDef, body) + var impl1 = Template(parents, self, body1).copyAttrs(impl) + ClassDef(mods, name, params, impl1).copyAttrs(classDef) + case moduledef @ ModuleDef(mods, name, impl) => + val Template(parents, self, body) = impl + val body1 = trimSyntheticCaseClassMembers(moduledef, body) + var impl1 = Template(parents, self, body1).copyAttrs(impl) + ModuleDef(mods, name, impl1).copyAttrs(moduledef) + case template @ Template(parents, self, body) => + val body1 = trimSyntheticCaseClassCompanions(body) + Template(parents, self, body1).copyAttrs(template) + case block @ Block(stats, expr) => + val stats1 = trimSyntheticCaseClassCompanions(stats) + Block(stats1, expr).copyAttrs(block) + case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isLazy => + if (reifyDebug) println("dropping $lzy in lazy val's name: " + tree) + val name1 = if (name endsWith nme.LAZY_LOCAL) name dropRight nme.LAZY_LOCAL.length else name + ValDef(mods, name1, tpt, rhs).copyAttrs(valdef) + case unapply @ UnApply(fun, args) => + def extractExtractor(tree: Tree): Tree = { + val Apply(fun, args) = tree + args match { + case List(Ident(special)) if special == nme.SELECTOR_DUMMY => + val Select(extractor, flavor) = fun + assert(flavor == nme.unapply || flavor == nme.unapplySeq) + extractor + case _ => + extractExtractor(fun) + } + } + + if (reifyDebug) println("unapplying unapply: " + tree) + val fun1 = extractExtractor(fun) + Apply(fun1, args).copyAttrs(unapply) + case _ => + tree + } + } + + /** Reify a tree */ + private def reifyTree(tree0: Tree): Tree = { + val tree = trimTree(tree0) + + var rtree = tree match { + case tree if tree.isErroneous => + CannotReifyErroneousTree(tree) + case self.EmptyTree => + reifyMirrorObject(EmptyTree) + case self.emptyValDef => + mirrorSelect(nme.emptyValDef) + case This(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) => + reifyFree(tree) + case Ident(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) => + if (tree.symbol.isVariable && tree.symbol.owner.isTerm) { + if (reifyDebug) println("captured variable: " + tree.symbol) + captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reifyTree here. + mirrorCall("Select", reifyFree(tree), reifyName(nme.elem)) + } else reifyFree(tree) + case tt: TypeTree if (tt.tpe != null) => + reifyTypeTree(tt) + case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) => + CannotReifyClassOfBoundType(tree, tpe) + case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym => + CannotReifyClassOfBoundEnum(tree, constant.tpe) + case tree if tree.isDef => + if (reifyDebug) println("boundSym: %s of type %s".format(tree.symbol, (tree.productIterator.toList collect { case tt: TypeTree => tt } headOption).getOrElse(TypeTree(tree.tpe)))) + boundSyms += tree.symbol + + bindRelatedSymbol(tree.symbol.sourceModule, "sourceModule") + bindRelatedSymbol(tree.symbol.moduleClass, "moduleClass") + bindRelatedSymbol(tree.symbol.companionClass, "companionClass") + bindRelatedSymbol(tree.symbol.companionModule, "companionModule") + Some(tree.symbol) collect { case termSymbol: TermSymbol => bindRelatedSymbol(termSymbol.referenced, "referenced") } + def bindRelatedSymbol(related: Symbol, name: String): Unit = + if (related != null && related != NoSymbol) { + if (reifyDebug) println("boundSym (" + name + "): " + related) + boundSyms += related + } + + val prefix = tree.productPrefix + val elements = (tree.productIterator map { + // annotations exist in two flavors: + // 1) pre-typer ones that populate: a) Modifiers, b) Annotated nodes (irrelevant in this context) + // 2) post-typer ones that dwell inside: a) sym.annotations, b) AnnotatedTypes (irrelevant in this context) + // + // here we process Modifiers that are involved in deftrees + // AnnotatedTypes get reified elsewhere (currently, in ``reifyTypeTree'') + case Modifiers(flags, privateWithin, annotations) => + assert(annotations.isEmpty) // should've been eliminated by the typer + val postTyper = tree.symbol.annotations filter (_.original != EmptyTree) + if (reifyDebug && !postTyper.isEmpty) println("reify symbol annotations for %s: %s".format(tree.symbol, tree.symbol.annotations)) + val preTyper = postTyper map toPreTyperAnnotation + Modifiers(flags, privateWithin, preTyper) + case x => + x + }).toList + reifyProduct(prefix, elements) + case _ => + reifyProduct(tree) + } + + // usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation + // however, reification of AnnotatedTypes is special. see ``reifyType'' to find out why. + if (reifySymbols && tree.hasSymbol) { + if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree)) + rtree = Apply(Select(rtree, nme.setSymbol), List(reifySymRef(tree.symbol))) + } + if (reifyTypes && tree.tpe != null) { + if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree)) + rtree = Apply(Select(rtree, nme.setType), List(reifyType(tree.tpe))) + } + + rtree + } + + /** Reify pre-typer representation of a type. + * + * NB: This is the trickiest part of reification! + * + * In most cases, we're perfectly fine to reify a Type itself (see ``reifyType''). + * However if the type involves a symbol declared inside the quasiquote (i.e. registered in ``boundSyms''), + * then we cannot reify it, or otherwise subsequent reflective compilation will fail. + * + * Why will it fail? Because reified deftrees (e.g. ClassDef(...)) will generate fresh symbols during that compilation, + * so naively reified symbols will become out of sync, which brings really funny compilation errors and/or crashes, e.g.: + * https://issues.scala-lang.org/browse/SI-5230 + * + * To deal with this unpleasant fact, we need to fall back from types to equivalent trees (after all, parser trees don't contain any types, just trees, so it should be possible). + * Luckily, these original trees get preserved for us in the ``original'' field when Trees get transformed into TypeTrees. + * And if an original of a type tree is empty, we can safely assume that this type is non-essential (e.g. was inferred/generated by the compiler). + * In that case the type can be omitted (e.g. reified as an empty TypeTree), since it will be inferred again later on. + * + * An important property of the original is that it isn't just a pre-typer tree. + * It's actually kind of a post-typer tree with symbols assigned to its Idents (e.g. Ident("List") will contain a symbol that points to immutable.this.List). + * This is very important, since subsequent reflective compilation won't have to resolve these symbols. + * In general case, such resolution cannot be performed, since reification doesn't preserve lexical context, + * which means that reflective compilation won't be aware of, say, imports that were provided when the reifee has been compiled. + * + * This workaround worked surprisingly well and allowed me to fix several important reification bugs, until the abstraction has leaked. + * Suddenly I found out that in certain contexts original trees do not contain symbols, but are just parser trees. + * To the moment I know only one such situation: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless. + * This is laboriously worked around in the code below. I hope this will be the only workaround in this department. + */ + private def reifyTypeTree(tt: TypeTree): Tree = { + if (definedInLiftedCode(tt.tpe)) { + if (reifyDebug) println("reifyTypeTree, defined in lifted code: " + tt.tpe) + if (tt.original != null) { + val annotations = tt.tpe filter { _.isInstanceOf[AnnotatedType] } collect { case atp: AnnotatedType => atp.annotations } flatten + val annmap = annotations map { ann => (ann.original, ann) } toMap + + // annotations exist in two flavors: + // 1) pre-typer ones that populate: a) Modifiers (irrelevant in this context), b) Annotated nodes + // 2) post-typer ones that dwell inside: a) sym.annotations (irrelevant in this context), b) AnnotatedTypes + // + // here we process AnnotatedTypes, since only they can be involved in TypeTrees + // Modifiers get reified elsewhere (currently, in the "isDef" case of ``reifyTree'') + // + // the problem with annotations is that their originals don't preserve any symbols at all + // read the comment to this method to find out why it's bad + // that's why we transplant typechecked, i.e. symful, annotations onto original trees + class AnnotationFixup extends self.Transformer { + override def transform(tree: Tree) = tree match { + case Annotated(ann0, args) => + assert(annmap contains ann0) + val ann1 = annmap(ann0) + val ann = toPreTyperAnnotation(ann1) + Annotated(ann, transform(args)) + case _ => + tree + } + } + + if (reifyDebug) println("verdict: essential, reify as original") + val patchedOriginal = new AnnotationFixup().transform(tt.original) + reifyTree(patchedOriginal) + } else { + // type is deemed to be non-essential + // erase it and hope that subsequent reflective compilation will be able to recreate it again + if (reifyDebug) println("verdict: non-essential, discard") + mirrorCall("TypeTree") + } + } else { + var rtt = mirrorCall(nme.TypeTree, reifyType(tt.tpe)) + // @xeno.by: temporarily disabling reification of originals + // subsequent reflective compilation will try to typecheck them + // and this means that the reifier has to do additional efforts to ensure that this will succeed + // additional efforts + no clear benefit = will be implemented later +// if (tt.original != null) { +// val setOriginal = Select(rtt, newTermName("setOriginal")) +// val reifiedOriginal = reify(tt.original) +// rtt = Apply(setOriginal, List(reifiedOriginal)) +// } + rtt + } + } + + /** Reify post-typer representation of an annotation */ + private def reifyAnnotation(ann: AnnotationInfo): Tree = + // @xeno.by: if you reify originals, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important + mirrorFactoryCall("AnnotationInfo", reifyType(ann.atp), reifyList(ann.args), reify(ann.assocs)) + + /** Reify pre-typer representation of an annotation. + * The trick here is to retain the symbols that have been populated during typechecking of the annotation. + * If we do not do that, subsequent reflective compilation will fail. + */ + private def toPreTyperAnnotation(ann: AnnotationInfo): Tree = { + if (definedInLiftedCode(ann.atp)) { + // todo. deconstruct reifiable tree from ann.original and ann.args+ann.assocs + // + // keep in mind that we can't simply use ann.original, because its args are symless + // which means that any imported symbol (e.g. List) will crash subsequent reflective compilation + // hint: if I had enough time, I'd try to extract reifiable annotation type from ann.original + // and to apply its constructor to ann.args (that are symful, i.e. suitable for reification) + // + // also, if we pursue the route of reifying annotations defined in lifted code + // we should think about how to provide types for all nodes of the return value + // this will be necessary for reifying AnnotatedTypes, since ASTs inside ATs must all have non-null tpes + // an alternative would be downgrading ATs to Annotated nodes, but this needs careful thinking + // for now I just leave this as an implementation restriction + CannotReifyAnnotationInvolvingBoundType(ann) + } else { + val args = if (ann.assocs.isEmpty) { + ann.args + } else { + def toScalaAnnotation(jann: ClassfileAnnotArg): Tree = jann match { + case LiteralAnnotArg(const) => + Literal(const) + case ArrayAnnotArg(arr) => + Apply(Ident(definitions.ArrayModule), arr.toList map toScalaAnnotation) + case NestedAnnotArg(ann) => + toPreTyperAnnotation(ann) + } + + ann.assocs map { case (nme, arg) => AssignOrNamedArg(Ident(nme), toScalaAnnotation(arg)) } + } + + New(ann.atp, args: _*) + } + } + + /** + * Reify a free reference. The result will be either a mirror reference + * to a global value, or else a mirror Literal. + */ + private def reifyFree(tree: Tree): Tree = tree match { + case This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass => + val sym = tree.symbol + if (reifyDebug) println("This for %s, reified as freeVar".format(sym)) + if (reifyDebug) println("Free: " + sym) + val freeVar = mirrorCall("newFreeVar", reify(sym.name.toString), reify(sym.tpe), This(sym)) + mirrorCall(nme.Ident, freeVar) + case This(_) => + if (reifyDebug) println("This for %s, reified as This".format(tree.symbol)) + mirrorCall(nme.This, reifySymRef(tree.symbol)) + case _ => + mirrorCall(nme.Ident, reifySymRef(tree.symbol)) + } + + // todo: consider whether we should also reify positions + private def reifyPosition(pos: Position): Tree = + reifyMirrorObject(NoPosition) + + // !!! we must eliminate these casts. + private def reifyProductUnsafe(x: Any): Tree = + if (x.isInstanceOf[Product]) reifyProduct(x.asInstanceOf[Product]) + else throw new Exception("%s of type %s cannot be cast to Product".format(x, x.getClass)) + private def reifyProduct(x: Product): Tree = + reifyProduct(x.productPrefix, x.productIterator.toList) + private def reifyProduct(prefix: String, elements: List[Any]): Tree = { + // @xeno.by: reflection would be more robust, but, hey, this is a hot path + if (prefix.startsWith("Tuple")) reifyAggregate(prefix, elements: _*) + else mirrorCall(prefix, (elements map reify): _*) + } + + /** + * Reify a case object defined in Mirror + */ + private def reifyMirrorObject(name: String): Tree = mirrorSelect(name) + private def reifyMirrorObject(x: Product): Tree = reifyMirrorObject(x.productPrefix) + + private def isReifiableConstant(value: Any) = value match { + case null => true // seems pretty reifable to me? + case _: String => true + case _ => isAnyVal(value) + } + + /** Reify an arbitary value */ + private def reify(value: Any): Tree = value match { + case tree: Tree => reifyTree(tree) + case sym: Symbol => reifySymRef(sym) + case tpe: Type => reifyType(tpe) + case xs: List[_] => reifyList(xs) + case xs: Array[_] => reifyArray(xs) + case scope: Scope => reifyScope(scope) + case x: Name => reifyName(x) + case x: Position => reifyPosition(x) + case x: Modifiers => reifyModifiers(x) + case x: AnnotationInfo => reifyAnnotation(x) + case _ => + if (isReifiableConstant(value)) Literal(Constant(value)) + else reifyProductUnsafe(value) + } + + /** + * An (unreified) path that refers to definition with given fully qualified name + * @param mkName Creator for last portion of name (either TermName or TypeName) + */ + private def path(fullname: String, mkName: String => Name): Tree = { + val parts = fullname split "\\." + val prefixParts = parts.init + val lastName = mkName(parts.last) + if (prefixParts.isEmpty) Ident(lastName) + else { + val prefixTree = ((Ident(prefixParts.head): Tree) /: prefixParts.tail)(Select(_, _)) + Select(prefixTree, lastName) + } + } + + /** An (unreified) path that refers to term definition with given fully qualified name */ + private def termPath(fullname: String): Tree = path(fullname, newTermName) + + /** An (unreified) path that refers to type definition with given fully qualified name */ + private def typePath(fullname: String): Tree = path(fullname, newTypeName) + + private def mirrorAlias = + ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(termPath(fullnme.MirrorPackage)), termPath(fullnme.MirrorPackage)) + + /** + * Generate code that generates a symbol table of all symbols registered in `reifiableSyms` + */ + private def reifySymbolTableSetup: List[Tree] = { + val symDefs, fillIns = new mutable.ArrayBuffer[Tree] + var i = 0 + while (i < reifiableSyms.length) { + // fillInSymbol might create new reifiableSyms, that's why this is done iteratively + symDefs += reifySymbolDef(reifiableSyms(i)) + fillIns += fillInSymbol(reifiableSyms(i)) + i += 1 + } + + symDefs.toList ++ fillIns.toList + } + } // end of Reifier + + object Reifier { + def CannotReifyPreTyperTree(tree: Tree) = { + val msg = "pre-typer trees are not supported, consider typechecking the tree before passing it to the reifier" + throw new ReifierError(tree.pos, msg) + } + + def CannotReifyErroneousTree(tree: Tree) = { + val msg = "erroneous trees are not supported, make sure that your tree typechecks successfully before passing it to the reifier" + throw new ReifierError(tree.pos, msg) + } + + def CannotReifyErroneousType(tpe: Type) = { + val msg = "erroneous types are not supported, make sure that your tree typechecks successfully before passing it to the reifier" + throw new ReifierError(NoPosition, msg) + } + + def CannotReifyClassOfBoundType(tree: Tree, tpe: Type) = { + val msg = "implementation restriction: cannot reify classOf[%s] which refers to a type declared inside the block being reified".format(tpe) + throw new ReifierError(tree.pos, msg) + } + + def CannotReifyClassOfBoundEnum(tree: Tree, tpe: Type) = { + val msg = "implementation restriction: cannot reify classOf[%s] which refers to an enum declared inside the block being reified".format(tpe) + throw new ReifierError(tree.pos, msg) + } + + def CannotReifyTypeInvolvingBoundType(tpe: Type) = { + val msg = "implementation restriction: cannot reify type %s which involves a symbol declared inside the block being reified".format(tpe) + throw new ReifierError(NoPosition, msg) + } + + def CannotReifyAnnotationInvolvingBoundType(ann: AnnotationInfo) = { + val msg = "implementation restriction: cannot reify annotation @%s which involves a symbol declared inside the block being reified".format(ann) + throw new ReifierError(ann.original.pos, msg) + } + } // end of Reifier + + // begin reify + import Reifier._ + if (tree.tpe != null) { + val saved = printTypings + try { + val reifyDebug = settings.Yreifydebug.value + val debugTrace = util.trace when reifyDebug + debugTrace("transforming = ")(if (settings.Xshowtrees.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) + debugTrace("transformed = ") { + val reifier = new Reifier() + val untyped = reifier.reifyTopLevel(tree) + + val reifyCopypaste = settings.Yreifycopypaste.value + if (reifyCopypaste) { + if (reifyDebug) println("=======================") + println(reifiedNodeToString(untyped)) + if (reifyDebug) println("=======================") + } + + untyped + } + } finally { + printTypings = saved + } + } else { + CannotReifyPreTyperTree(tree) + } + } + + /** A throwable signalling a reification error */ + class ReifierError(var pos: Position, val msg: String) extends Throwable(msg) { + def this(msg: String) = this(NoPosition, msg) + } +} diff --git a/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala new file mode 100644 index 0000000000..fce59bb099 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala @@ -0,0 +1,75 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package ast + +import compat.Platform.EOL +import symtab._ +import Flags._ + +trait ReifyPrinters { self: NodePrinters => + + val global: Global + import global._ + + object reifiedNodeToString extends Function1[Tree, String] { + def apply(tree: Tree): String = { + import scala.reflect.api.Modifier + + // @PP: I fervently hope this is a test case or something, not anything being + // depended upon. Of more fragile code I cannot conceive. + // @eb: This stuff is only needed to debug-print out reifications in human-readable format + // Rolling a full-fledged, robust TreePrinter would be several times more code. + (for (line <- (tree.toString.split(EOL) drop 2 dropRight 1)) yield { + var s = line.trim + s = s.replace("$mr.", "") + s = s.replace(".apply", "") + s = s.replace("scala.collection.immutable.", "") + s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List") + s = "List\\[.*?\\]".r.replaceAllIn(s, "List") + s = s.replace("immutable.this.Nil", "List()") + s = s.replace("modifiersFromInternalFlags", "Modifiers") + s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()") + s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => { + val buf = new collection.mutable.ListBuffer[String] + + val annotations = m.group(3) + if (buf.nonEmpty || annotations.nonEmpty) + buf.append("List(" + annotations + ")") + + val privateWithin = "" + m.group(2) + if (buf.nonEmpty || privateWithin != "") + buf.append("newTypeName(\"" + privateWithin + "\")") + + val flags = m.group(1).toLong + val s_flags = Flags.modifiersOfFlags(flags) map (_.sourceString) mkString ", " + if (buf.nonEmpty || s_flags != "") + buf.append("Set(" + s_flags + ")") + + "Modifiers(" + buf.reverse.mkString(", ") + ")" + }) + s = """setInternalFlags\((\d+)L\)""".r.replaceAllIn(s, m => { + val flags = m.group(1).toLong + val mods = Flags.modifiersOfFlags(flags) map (_.sourceString) + "setInternalFlags(flagsOfModifiers(List(" + mods.mkString(", ") + ")))" + }) + + s + }) mkString EOL + } + } + + + def printReifyCopypaste(tree: Tree) { + val reifyDebug = settings.Yreifydebug.value + if (reifyDebug) println("=======================") + printReifyCopypaste1(tree) + if (reifyDebug) println("=======================") + } + + def printReifyCopypaste1(tree: Tree) { + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index 2cfd21ecc8..0d19b781e2 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -253,13 +253,11 @@ trait TreeDSL { } /** Top level accessible. */ - def MATCHERROR(arg: Tree) = Throw(New(TypeTree(MatchErrorClass.tpe), List(List(arg)))) - /** !!! should generalize null guard from match error here. */ - def THROW(sym: Symbol): Throw = Throw(New(TypeTree(sym.tpe), List(Nil))) - def THROW(sym: Symbol, msg: Tree): Throw = Throw(New(TypeTree(sym.tpe), List(List(msg.TOSTRING())))) + def MATCHERROR(arg: Tree) = Throw(MatchErrorClass.tpe, arg) + def THROW(sym: Symbol, msg: Tree): Throw = Throw(sym.tpe, msg.TOSTRING()) def NEW(tpt: Tree, args: Tree*): Tree = New(tpt, List(args.toList)) - def NEW(sym: Symbol, args: Tree*): Tree = New(sym, args: _*) + def NEW(sym: Symbol, args: Tree*): Tree = New(sym.tpe, args: _*) def DEF(name: Name, tp: Type): DefTreeStart = DEF(name) withType tp def DEF(name: Name): DefTreeStart = new DefTreeStart(name) diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 265d017653..d7159c5fa8 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -13,7 +13,7 @@ import symtab.SymbolTable /** XXX to resolve: TreeGen only assumes global is a SymbolTable, but * TreeDSL at the moment expects a Global. Can we get by with SymbolTable? */ -abstract class TreeGen extends reflect.internal.TreeGen { +abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { val global: Global import global._ @@ -30,7 +30,7 @@ abstract class TreeGen extends reflect.internal.TreeGen { else tree } - + /** Builds a fully attributed wildcard import node. */ def mkWildcardImport(pkg: Symbol): Import = { @@ -51,13 +51,12 @@ abstract class TreeGen extends reflect.internal.TreeGen { } // wrap the given expression in a SoftReference so it can be gc-ed - def mkSoftRef(expr: Tree): Tree = atPos(expr.pos) { - New(SoftReferenceClass, expr) - } + def mkSoftRef(expr: Tree): Tree = atPos(expr.pos)(New(SoftReferenceClass.tpe, expr)) + // annotate the expression with @unchecked def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) { // This can't be "Annotated(New(UncheckedClass), expr)" because annotations - // are very pick about things and it crashes the compiler with "unexpected new". + // are very picky about things and it crashes the compiler with "unexpected new". Annotated(New(scalaDot(UncheckedClass.name), List(Nil)), expr) } // if it's a Match, mark the selector unchecked; otherwise nothing. @@ -66,18 +65,81 @@ abstract class TreeGen extends reflect.internal.TreeGen { case _ => tree } - def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = matchExpr match { - case Match(scrutinee, cases) => - if (cases exists treeInfo.isDefaultCase) matchExpr - else { - val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(scrutinee)) - Match(scrutinee, cases :+ defaultCase) + // must be kept in synch with the codegen in PatMatVirtualiser + object VirtualCaseDef { + def unapply(b: Block): Option[(Assign, Tree, Tree)] = b match { + case Block(List(assign@Assign(keepGoingLhs, falseLit), matchRes), zero) => Some((assign, matchRes, zero)) // TODO: check tree annotation + case _ => None + } + } + + // TODO: would be so much nicer if we would know during match-translation (i.e., type checking) + // whether we should emit missingCase-style apply (and isDefinedAt), instead of transforming trees post-factum + class MatchMatcher { + def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = unknownTree(orig) + def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = unknownTree(orig) + def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = unknownTree(orig) + + def apply(matchExpr: Tree): Tree = (matchExpr: @unchecked) match { + // old-style match or virtpatmat switch + case Match(selector, cases) => // println("simple match: "+ (selector, cases) + "for:\n"+ matchExpr ) + caseMatch(matchExpr, selector, cases, identity) + // old-style match or virtpatmat switch + case Block((vd: ValDef) :: Nil, orig@Match(selector, cases)) => // println("block match: "+ (selector, cases, vd) + "for:\n"+ matchExpr ) + caseMatch(matchExpr, selector, cases, m => copyBlock(matchExpr, List(vd), m)) + // virtpatmat + case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), List(scrut)), List(matcher)) if opt.virtPatmat => // println("virt match: "+ (tgt, targs, scrut, matcher) + "for:\n"+ matchExpr ) + caseVirtualizedMatch(matchExpr, tgt, targs, scrut, matcher) + // optimized version of virtpatmat + case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue) if opt.virtPatmat => // TODO: check tree annotation // println("virtopt match: "+ (zero, x, matchRes, keepGoing, stats) + "for:\n"+ matchExpr ) + caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, identity) + // optimized version of virtpatmat + case Block(outerStats, orig@Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue)) if opt.virtPatmat => // TODO: check tree annotation // println("virt opt block match: "+ (zero, x, matchRes, keepGoing, stats, outerStats) + "for:\n"+ matchExpr ) + caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, m => copyBlock(matchExpr, outerStats, m)) + case other => + unknownTree(other) + } + + def unknownTree(t: Tree): Tree = throw new MatchError(t) + def copyBlock(orig: Tree, stats: List[Tree], expr: Tree): Block = Block(stats, expr) + + def dropSyntheticCatchAll(cases: List[CaseDef]): List[CaseDef] = + if (!opt.virtPatmat) cases + else cases filter { + case CaseDef(pat, EmptyTree, Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _))) if (treeInfo.isWildcardArg(pat) && (exTpt.tpe.typeSymbol eq MatchErrorClass)) => false + case CaseDef(pat, guard, body) => true + } + } + + def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = { + object withDefaultTransformer extends MatchMatcher { + override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = { + val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) + if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) orig + else { + val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(selector.duplicate)) + wrap(Match(selector, casesNoSynthCatchAll :+ defaultCase)) + } + } + override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { import CODE._ + ((matcher APPLY (scrut)) DOT nme.getOrElse) APPLY (defaultAction(scrut.duplicate)) // TODO: pass targs } - case _ => - matchExpr - // [Martin] Adriaan: please fill in virtpatmat transformation here + override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = { import CODE._ + wrap(Block( + zero :: + x :: + matchRes :: + keepGoing :: + stats, + // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `if (keepGoing) ${defaultAction(`x`)} else matchRes` + (IF (REF(keepGoing.symbol)) THEN defaultAction(x.rhs.duplicate) ELSE REF(matchRes.symbol)) + )) + } + } + withDefaultTransformer(matchExpr) } + def mkCached(cvar: Symbol, expr: Tree): Tree = { val cvarRef = mkUnattributedRef(cvar) Block( @@ -98,7 +160,7 @@ abstract class TreeGen extends reflect.internal.TreeGen { def mkModuleVarDef(accessor: Symbol) = { val inClass = accessor.owner.isClass val extraFlags = if (inClass) PrivateLocal | SYNTHETIC else 0 - + val mval = ( accessor.owner.newVariable(nme.moduleVarName(accessor.name), accessor.pos.focus, MODULEVAR | extraFlags) setInfo accessor.tpe.finalResultType @@ -118,10 +180,11 @@ abstract class TreeGen extends reflect.internal.TreeGen { def mkModuleAccessDef(accessor: Symbol, msym: Symbol) = DefDef(accessor, Select(This(msym.owner), msym)) - def newModule(accessor: Symbol, tpe: Type) = - New(TypeTree(tpe), - List(for (pt <- tpe.typeSymbol.primaryConstructor.info.paramTypes) - yield This(accessor.owner.enclClass))) + def newModule(accessor: Symbol, tpe: Type) = { + val ps = tpe.typeSymbol.primaryConstructor.info.paramTypes + if (ps.isEmpty) New(tpe) + else New(tpe, This(accessor.owner.enclClass)) + } // def m: T; def mkModuleAccessDcl(accessor: Symbol) = @@ -156,6 +219,18 @@ abstract class TreeGen extends reflect.internal.TreeGen { def mkSynchronized(monitor: Tree, body: Tree): Tree = Apply(Select(monitor, Object_synchronized), List(body)) + def mkAppliedTypeForCase(clazz: Symbol): Tree = { + val numParams = clazz.typeParams.size + if (clazz.typeParams.isEmpty) Ident(clazz) + else AppliedTypeTree(Ident(clazz), 1 to numParams map (_ => Bind(tpnme.WILDCARD, EmptyTree)) toList) + } + def mkBindForCase(patVar: Symbol, clazz: Symbol, targs: List[Type]): Tree = { + Bind(patVar, Typed(Ident(nme.WILDCARD), + if (targs.isEmpty) mkAppliedTypeForCase(clazz) + else AppliedTypeTree(Ident(clazz), targs map TypeTree) + )) + } + def wildcardStar(tree: Tree) = atPos(tree.pos) { Typed(tree, Ident(tpnme.WILDCARD_STAR)) } diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala index 5c3071739c..3371353f25 100644 --- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala @@ -51,14 +51,11 @@ trait TreePrinters extends reflect.internal.TreePrinters { this: Global => treePrinter.println() treePrinter.print(definition) - case AssignOrNamedArg(lhs, rhs) => - treePrinter.print(lhs, " = ", rhs) - case TypeTreeWithDeferredRefCheck() => treePrinter.print("<tree with deferred refcheck>") case SelectFromArray(qualifier, name, _) => - treePrinter.print(qualifier, ".<arr>", treePrinter.symName(tree, name)) + treePrinter.print(qualifier, ".<arr>", symName(tree, name)) case _ => super.xprintTree(treePrinter, tree) diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 83b6252b26..ad87889145 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -30,12 +30,6 @@ trait Trees extends reflect.internal.Trees { self: Global => override def isType = definition.isType } - /** Either an assignment or a named argument. Only appears in argument lists, - * eliminated by typecheck (doTypedApply) - */ - case class AssignOrNamedArg(lhs: Tree, rhs: Tree) - extends TermTree - /** Array selection <qualifier> . <name> only used during erasure */ case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type) extends TermTree with RefTree @@ -85,16 +79,16 @@ trait Trees extends reflect.internal.Trees { self: Global => val (edefs, rest) = body span treeInfo.isEarlyDef val (evdefs, etdefs) = edefs partition treeInfo.isEarlyValDef val gvdefs = evdefs map { - case vdef @ ValDef(mods, name, tpt, rhs) => - treeCopy.ValDef( - vdef.duplicate, mods, name, - atPos(focusPos(vdef.pos)) { TypeTree() setOriginal tpt setPos focusPos(tpt.pos) }, // atPos in case - EmptyTree) - } - val lvdefs = evdefs map { - case vdef @ ValDef(mods, name, tpt, rhs) => - treeCopy.ValDef(vdef, Modifiers(PRESUPER), name, tpt, rhs) + case vdef @ ValDef(_, _, tpt, _) => copyValDef(vdef)( + // !!! I know "atPos in case" wasn't intentionally planted to + // add an air of mystery to this file, but it is the sort of + // comment which only its author could love. + tpt = atPos(focusPos(vdef.pos))(TypeTree() setOriginal tpt setPos focusPos(tpt.pos)), // atPos in case + rhs = EmptyTree + ) } + val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = Modifiers(PRESUPER)) } + val constrs = { if (constrMods hasFlag TRAIT) { if (body forall treeInfo.isInterfaceMember) List() @@ -114,13 +108,11 @@ trait Trees extends reflect.internal.Trees { self: Global => DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) } } - // println("typed template, gvdefs = "+gvdefs+", parents = "+parents+", constrs = "+constrs) constrs foreach (ensureNonOverlapping(_, parents ::: gvdefs)) - // vparamss2 are used as field definitions for the class. remove defaults - val vparamss2 = vparamss map (vps => vps map { vd => - treeCopy.ValDef(vd, vd.mods &~ DEFAULTPARAM, vd.name, vd.tpt, EmptyTree) - }) - Template(parents, self, gvdefs ::: vparamss2.flatten ::: constrs ::: etdefs ::: rest) + // Field definitions for the class - remove defaults. + val fieldDefs = vparamss.flatten map (vd => copyValDef(vd)(mods = vd.mods &~ DEFAULTPARAM, rhs = EmptyTree)) + + Template(parents, self, gvdefs ::: fieldDefs ::: constrs ::: etdefs ::: rest) } /** Construct class definition with given class symbol, value parameters, @@ -155,8 +147,6 @@ trait Trees extends reflect.internal.Trees { self: Global => traverser.traverseTrees(ts) case DocDef(comment, definition) => traverser.traverse(definition) - case AssignOrNamedArg(lhs, rhs) => - traverser.traverse(lhs); traverser.traverse(rhs) case SelectFromArray(qualifier, selector, erasure) => traverser.traverse(qualifier) case ReferenceToBoxed(idt) => @@ -168,7 +158,6 @@ trait Trees extends reflect.internal.Trees { self: Global => trait TreeCopier extends super.TreeCopierOps { def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef - def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree): AssignOrNamedArg def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray def ReferenceToBoxed(tree: Tree, idt: Ident): ReferenceToBoxed def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck @@ -180,8 +169,6 @@ trait Trees extends reflect.internal.Trees { self: Global => class StrictTreeCopier extends super.StrictTreeCopier with TreeCopier { def DocDef(tree: Tree, comment: DocComment, definition: Tree) = new DocDef(comment, definition).copyAttrs(tree) - def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = - new AssignOrNamedArg(lhs, rhs).copyAttrs(tree) def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree) def ReferenceToBoxed(tree: Tree, idt: Ident) = @@ -197,11 +184,6 @@ trait Trees extends reflect.internal.Trees { self: Global => if (comment0 == comment) && (definition0 == definition) => t case _ => this.treeCopy.DocDef(tree, comment, definition) } - def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = tree match { - case t @ AssignOrNamedArg(lhs0, rhs0) - if (lhs0 == lhs) && (rhs0 == rhs) => t - case _ => this.treeCopy.AssignOrNamedArg(tree, lhs, rhs) - } def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = tree match { case t @ SelectFromArray(qualifier0, selector0, _) if (qualifier0 == qualifier) && (selector0 == selector) => t @@ -232,8 +214,6 @@ trait Trees extends reflect.internal.Trees { self: Global => override protected def xtransform(transformer: super.Transformer, tree: Tree): Tree = tree match { case DocDef(comment, definition) => transformer.treeCopy.DocDef(tree, comment, transformer.transform(definition)) - case AssignOrNamedArg(lhs, rhs) => - transformer.treeCopy.AssignOrNamedArg(tree, transformer.transform(lhs), transformer.transform(rhs)) case SelectFromArray(qualifier, selector, erasure) => transformer.treeCopy.SelectFromArray( tree, transformer.transform(qualifier), selector, erasure) @@ -269,12 +249,27 @@ trait Trees extends reflect.internal.Trees { self: Global => * (bq:) This transformer has mutable state and should be discarded after use */ private class ResetAttrs(localOnly: Boolean) { + val debug = settings.debug.value + val trace = scala.tools.nsc.util.trace when debug + val locals = util.HashSet[Symbol](8) + val orderedLocals = collection.mutable.ListBuffer[Symbol]() + def registerLocal(sym: Symbol) { + if (sym != null && sym != NoSymbol) { + if (debug && !(locals contains sym)) orderedLocals append sym + locals addEntry sym + } + } class MarkLocals extends self.Traverser { - def markLocal(tree: Tree) = - if (tree.symbol != null && tree.symbol != NoSymbol) - locals addEntry tree.symbol + def markLocal(tree: Tree) { + if (tree.symbol != null && tree.symbol != NoSymbol) { + val sym = tree.symbol + registerLocal(sym) + registerLocal(sym.sourceModule) + registerLocal(sym.moduleClass) + } + } override def traverse(tree: Tree) = { tree match { @@ -319,9 +314,12 @@ trait Trees extends reflect.internal.Trees { self: Global => def transform[T <: Tree](x: T): T = { new MarkLocals().traverse(x) - val trace = scala.tools.nsc.util.trace when settings.debug.value - val eoln = System.getProperty("line.separator") - trace("locals (%d total): %n".format(locals.size))(locals.toList map {" " + _} mkString eoln) + if (debug) { + assert(locals.size == orderedLocals.size) + val eoln = System.getProperty("line.separator") + val msg = orderedLocals.toList filter {_ != NoSymbol} map {" " + _} mkString eoln + trace("locals (%d total): %n".format(orderedLocals.size))(msg) + } val x1 = new Transformer().transform(x) assert(x.getClass isInstance x1) @@ -333,7 +331,6 @@ trait Trees extends reflect.internal.Trees { self: Global => case Parens(expr) (only used during parsing) case DocDef(comment, defn) => (eliminated by typer) - case AssignOrNamedArg(lhs, rhs) => (eliminated by typer) case TypeTreeWithDeferredRefCheck() => (created and eliminated by typer) case SelectFromArray(_, _, _) => (created and eliminated by erasure) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index c2b4dc32b6..cd19fca0b0 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -658,7 +658,8 @@ self => DocDef(doc, t) setPos { if (t.pos.isDefined) { val pos = doc.pos.withEnd(t.pos.endOrPoint) - if (t.pos.isOpaqueRange) pos else pos.makeTransparent + // always make the position transparent + pos.makeTransparent } else { t.pos } @@ -1205,7 +1206,7 @@ self => */ def wildcardType(start: Int) = { val pname = freshTypeName("_$") - val t = atPos(start) { Ident(pname) } + val t = atPos(start)(Ident(pname)) val bounds = typeBounds() val param = atPos(t.pos union bounds.pos) { makeSyntheticTypeParam(pname, bounds) } placeholderTypes = param :: placeholderTypes @@ -1423,15 +1424,14 @@ self => def implicitClosure(start: Int, location: Int): Tree = { val param0 = convertToParam { atPos(in.offset) { - var paramexpr: Tree = Ident(ident()) - if (in.token == COLON) { - in.nextToken() - paramexpr = Typed(paramexpr, typeOrInfixType(location)) + Ident(ident()) match { + case expr if in.token == COLON => + in.nextToken() ; Typed(expr, typeOrInfixType(location)) + case expr => expr } - paramexpr } } - val param = treeCopy.ValDef(param0, param0.mods | Flags.IMPLICIT, param0.name, param0.tpt, param0.rhs) + val param = copyValDef(param0)(mods = param0.mods | Flags.IMPLICIT) atPos(start, in.offset) { accept(ARROW) Function(List(param), if (location != InBlock) expr() else block()) @@ -2689,8 +2689,8 @@ self => val (self, body) = templateBody(true) if (in.token == WITH && self.isEmpty) { val earlyDefs: List[Tree] = body flatMap { - case vdef @ ValDef(mods, name, tpt, rhs) if !mods.isDeferred => - List(treeCopy.ValDef(vdef, mods | Flags.PRESUPER, name, tpt, rhs)) + case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => + List(copyValDef(vdef)(mods = mods | Flags.PRESUPER)) case tdef @ TypeDef(mods, name, tparams, rhs) => List(treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs)) case stat if !stat.isEmpty => diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index dae264fffe..2895d02dfe 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -84,7 +84,7 @@ trait Scanners extends ScannersCommon { abstract class Scanner extends CharArrayReader with TokenData with ScannerCommon { private def isDigit(c: Char) = java.lang.Character isDigit c - + def isAtEnd = charOffset >= buf.length def flush = { charOffset = offset; nextChar(); this } @@ -164,7 +164,7 @@ trait Scanners extends ScannersCommon { * RBRACE if region starts with '{' * ARROW if region starts with `case' * STRINGLIT if region is a string interpolation expression starting with '${' - * (the STRINGLIT appears twice in succession on the stack iff the + * (the STRINGLIT appears twice in succession on the stack iff the * expression is a multiline string literal). */ var sepRegions: List[Int] = List() @@ -173,15 +173,15 @@ trait Scanners extends ScannersCommon { /** Are we directly in a string interpolation expression? */ - @inline private def inStringInterpolation = + @inline private def inStringInterpolation = sepRegions.nonEmpty && sepRegions.head == STRINGLIT - + /** Are we directly in a multiline string interpolation expression? * @pre: inStringInterpolation */ - @inline private def inMultiLineInterpolation = - sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART - + @inline private def inMultiLineInterpolation = + inStringInterpolation && sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART + /** read next token and return last offset */ def skipToken(): Offset = { @@ -205,7 +205,7 @@ trait Scanners extends ScannersCommon { case CASE => sepRegions = ARROW :: sepRegions case RBRACE => - while (!sepRegions.isEmpty && sepRegions.head != RBRACE) + while (!sepRegions.isEmpty && sepRegions.head != RBRACE) sepRegions = sepRegions.tail if (!sepRegions.isEmpty) sepRegions = sepRegions.tail docBuffer = null @@ -217,11 +217,13 @@ trait Scanners extends ScannersCommon { if (!sepRegions.isEmpty && sepRegions.head == lastToken) sepRegions = sepRegions.tail case STRINGLIT => - if (inStringInterpolation) + if (inMultiLineInterpolation) + sepRegions = sepRegions.tail.tail + else if (inStringInterpolation) sepRegions = sepRegions.tail case _ => } - + // Read a token or copy it from `next` tokenData if (next.token == EMPTY) { lastOffset = charOffset - 1 @@ -325,8 +327,8 @@ trait Scanners extends ScannersCommon { 'z' => putChar(ch) nextChar() - getIdentRest() - if (ch == '"' && token == IDENTIFIER && settings.Xexperimental.value) + getIdentRest() + if (ch == '"' && token == IDENTIFIER && settings.Xexperimental.value) token = INTERPOLATIONID case '<' => // is XMLSTART? val last = if (charOffset >= 2) buf(charOffset - 2) else ' ' @@ -386,7 +388,7 @@ trait Scanners extends ScannersCommon { if (ch == '\"') { nextRawChar() getStringPart(multiLine = true) - sepRegions = STRINGLIT :: sepRegions // indicate string part + sepRegions = STRINGPART :: sepRegions // indicate string part sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part } else { token = STRINGLIT @@ -407,7 +409,7 @@ trait Scanners extends ScannersCommon { token = STRINGLIT strVal = "" } - } else { + } else { getStringLit() } } @@ -630,8 +632,8 @@ trait Scanners extends ScannersCommon { else finishNamed() } } - - + + // Literals ----------------------------------------------------------------- private def getStringLit() = { @@ -659,21 +661,27 @@ trait Scanners extends ScannersCommon { getRawStringLit() } } - + @annotation.tailrec private def getStringPart(multiLine: Boolean): Unit = { def finishStringPart() = { setStrVal() token = STRINGPART next.lastOffset = charOffset - 1 next.offset = charOffset - 1 - } + } if (ch == '"') { - nextRawChar() - if (!multiLine || isTripleQuote()) { + if (multiLine) { + nextRawChar() + if (isTripleQuote()) { + setStrVal() + token = STRINGLIT + } else + getStringPart(multiLine) + } else { + nextChar() setStrVal() token = STRINGLIT - } else - getStringPart(multiLine) + } } else if (ch == '$') { nextRawChar() if (ch == '$') { @@ -696,20 +704,23 @@ trait Scanners extends ScannersCommon { } else { syntaxError("invalid string interpolation") } - } else if ((ch == CR || ch == LF || ch == SU) && !isUnicodeEscape) { - syntaxError("unclosed string literal") } else { - putChar(ch) - nextRawChar() - getStringPart(multiLine) + val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF))) + if (isUnclosedLiteral) { + syntaxError(if (!multiLine) "unclosed string literal" else "unclosed multi-line string literal") + } else { + putChar(ch) + nextRawChar() + getStringPart(multiLine) + } } } - + private def fetchStringPart() = { offset = charOffset - 1 getStringPart(multiLine = inMultiLineInterpolation) } - + private def isTripleQuote(): Boolean = if (ch == '"') { nextRawChar() @@ -730,7 +741,7 @@ trait Scanners extends ScannersCommon { false } - /** copy current character into cbuf, interpreting any escape sequences, + /** copy current character into cbuf, interpreting any escape sequences, * and advance to next character. */ protected def getLitChar(): Unit = diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala index 091f333c27..fb4daefd57 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -58,7 +58,7 @@ object Tokens extends Tokens { final val BACKQUOTED_IDENT = 11 def isIdentifier(code: Int) = code >= IDENTIFIER && code <= BACKQUOTED_IDENT - + @switch def canBeginExpression(code: Int) = code match { case IDENTIFIER|BACKQUOTED_IDENT|USCORE => true case LBRACE|LPAREN|LBRACKET|COMMENT => true diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 40389466e2..0d2fbc5372 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -471,15 +471,11 @@ abstract class TreeBuilder { def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean): Tree = makeVisitor(cases, checkExhaustive, "x$") - private def makeUnchecked(expr: Tree): Tree = atPos(expr.pos) { - Annotated(New(scalaDot(definitions.UncheckedClass.name), List(Nil)), expr) - } - /** Create visitor <x => x match cases> */ def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean, prefix: String): Tree = { - val x = freshTermName(prefix) - val id = Ident(x) - val sel = if (checkExhaustive) id else makeUnchecked(id) + val x = freshTermName(prefix) + val id = Ident(x) + val sel = if (checkExhaustive) id else gen.mkUnchecked(id) Function(List(makeSyntheticParam(x)), Match(sel, cases)) } @@ -564,7 +560,7 @@ abstract class TreeBuilder { val vars = getVariables(pat1) val matchExpr = atPos((pat1.pos union rhs.pos).makeTransparent) { Match( - makeUnchecked(rhs), + gen.mkUnchecked(rhs), List( atPos(pat1.pos) { CaseDef(pat1, EmptyTree, makeTupleTerm(vars map (_._1) map Ident, true)) diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala index 05571b2424..aab944f65a 100644 --- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala +++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala @@ -565,7 +565,7 @@ abstract class ScalaPrimitives { import definitions._ val code = getPrimitive(fun) - def elementType = atPhase(currentRun.typerPhase) { + def elementType = beforeTyper { val arrayParent = tpe :: tpe.parents collectFirst { case TypeRef(_, ArrayClass, elem :: Nil) => elem } diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index 4ab0eb0129..68c4ac03f6 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -18,7 +18,7 @@ trait BasicBlocks { import opcodes._ import global.{ ifDebug, settings, log, nme } import nme.isExceptionResultName - + object NoBasicBlock extends BasicBlock(-1, null) /** This class represents a basic block. Each @@ -182,7 +182,7 @@ trait BasicBlocks { final def foreach[U](f: Instruction => U) = { if (!closed) dumpMethodAndAbort(method, this) else instrs foreach f - + // !!! If I replace "instrs foreach f" with the following: // var i = 0 // val len = instrs.length diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 3baff7da9e..9e801e3ea8 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -94,7 +94,7 @@ abstract class GenICode extends SubComponent { // !! modules should be eliminated by refcheck... or not? case ModuleDef(mods, name, impl) => - abort("Modules should not reach backend!") + abort("Modules should not reach backend! " + tree) case ValDef(mods, name, tpt, rhs) => ctx // we use the symbol to add fields @@ -133,7 +133,7 @@ abstract class GenICode extends SubComponent { if (!ctx1.bb.closed) ctx1.bb.close prune(ctx1.method) } else - ctx1.method.setCode(null) + ctx1.method.setCode(NoCode) ctx1 case Template(_, _, body) => @@ -393,15 +393,15 @@ abstract class GenICode extends SubComponent { for (CaseDef(pat, _, body) <- catches.reverse) yield { def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) = (sym, kind, ctx => { - ctx.bb.emit(DROP(REFERENCE(sym))) + ctx.bb.emit(DROP(REFERENCE(sym))) // drop the loaded exception genLoad(body, ctx, kind) }) pat match { case Typed(Ident(nme.WILDCARD), tpt) => genWildcardHandler(tpt.tpe.typeSymbol) case Ident(nme.WILDCARD) => genWildcardHandler(ThrowableClass) - case Bind(name, _) => - val exception = ctx.method addLocal new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false) + case Bind(_, _) => + val exception = ctx.method addLocal new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false) // the exception will be loaded and stored into this local (pat.symbol.tpe.typeSymbol, kind, { ctx: Context => @@ -704,7 +704,8 @@ abstract class GenICode extends SubComponent { ctx1 case New(tpt) => - abort("Unexpected New") + abort("Unexpected New(" + tpt.summaryString + "/" + tpt + ") received in icode.\n" + + " Call was genLoad" + ((tree, ctx, expectedType))) case Apply(TypeApply(fun, targs), _) => val sym = fun.symbol @@ -1054,7 +1055,7 @@ abstract class GenICode extends SubComponent { case Match(selector, cases) => debuglog("Generating SWITCH statement."); - var ctx1 = genLoad(selector, ctx, INT) + var ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue) val afterCtx = ctx1.newBlock var caseCtx: Context = null generatedType = toTypeKind(tree.tpe) @@ -2086,12 +2087,12 @@ abstract class GenICode extends SubComponent { exh }) else None - val exhs = handlers.map { handler => - val exh = this.newExceptionHandler(handler._1, handler._2, tree.pos) + val exhs = handlers.map { case (sym, kind, handler) => // def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) = + val exh = this.newExceptionHandler(sym, kind, tree.pos) var ctx1 = outerCtx.enterExceptionHandler(exh) ctx1.addFinalizer(finalizer, finalizerCtx) loadException(ctx1, exh, tree.pos) - ctx1 = handler._3(ctx1) + ctx1 = handler(ctx1) // emit finalizer val ctx2 = emitFinalizer(ctx1) ctx2.bb.closeWith(JUMP(afterCtx.bb)) diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 2668e7f29f..36651541b2 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -21,7 +21,7 @@ trait Members { self: ICodes => import global._ - + object NoCode extends Code(null, "NoCode") { override def blocksList: List[BasicBlock] = Nil } @@ -138,7 +138,7 @@ trait Members { /** Represent a field in ICode */ class IField(val symbol: Symbol) extends IMember { } - + object NoIMethod extends IMethod(NoSymbol) { } /** @@ -177,19 +177,13 @@ trait Members { /** method parameters */ var params: List[Local] = Nil - // TODO - see how null is stil arriving here - def hasCode = (code ne NoCode) && (code ne null) + def hasCode = code ne NoCode def setCode(code: Code): IMethod = { this.code = code; this } - def addLocal(l: Local): Local = - locals find (_ == l) getOrElse { - locals ::= l - l - } - + def addLocal(l: Local): Local = findOrElse(locals)(_ == l) { locals ::= l ; l } def addParam(p: Local): Unit = if (params contains p) () @@ -214,6 +208,12 @@ trait Members { override def toString() = symbol.fullName + def matchesSignature(other: IMethod) = { + (symbol.name == other.symbol.name) && + (params corresponds other.params)(_.kind == _.kind) && + (returnType == other.returnType) + } + import opcodes._ def checkLocals(): Unit = { def localsSet = (code.blocks flatMap { bb => diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index 2bcfb9d4a9..ec6c631bd1 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -350,6 +350,7 @@ trait Opcodes { self: ICodes => } case class BOX(boxType: TypeKind) extends Instruction { + assert(boxType.isValueType && (boxType ne UNIT)) // documentation override def toString(): String = "BOX " + boxType override def consumed = 1 override def consumedTypes = boxType :: Nil @@ -357,6 +358,7 @@ trait Opcodes { self: ICodes => } case class UNBOX(boxType: TypeKind) extends Instruction { + assert(boxType.isValueType && (boxType ne UNIT)) // documentation override def toString(): String = "UNBOX " + boxType override def consumed = 1 override def consumedTypes = ObjectReference :: Nil diff --git a/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala b/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala index 37fff0e1e8..f99ac28e9d 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala @@ -120,47 +120,50 @@ trait Primitives { self: ICodes => /** This class represents a test operation. */ - class TestOp { + sealed abstract class TestOp { /** Returns the negation of this operation. */ - def negate(): TestOp = this match { - case EQ => NE - case NE => EQ - case LT => GE - case GE => LT - case LE => GT - case GT => LE - case _ => throw new RuntimeException("TestOp unknown case") - } + def negate(): TestOp /** Returns a string representation of this operation. */ - override def toString(): String = this match { - case EQ => "EQ" - case NE => "NE" - case LT => "LT" - case GE => "GE" - case LE => "LE" - case GT => "GT" - case _ => throw new RuntimeException("TestOp unknown case") - } + override def toString(): String } + /** An equality test */ - case object EQ extends TestOp + case object EQ extends TestOp { + def negate() = NE + override def toString() = "EQ" + } /** A non-equality test */ - case object NE extends TestOp + case object NE extends TestOp { + def negate() = EQ + override def toString() = "NE" + } /** A less-than test */ - case object LT extends TestOp + case object LT extends TestOp { + def negate() = GE + override def toString() = "LT" + } /** A greater-than-or-equal test */ - case object GE extends TestOp + case object GE extends TestOp { + def negate() = LT + override def toString() = "GE" + } /** A less-than-or-equal test */ - case object LE extends TestOp + case object LE extends TestOp { + def negate() = GT + override def toString() = "LE" + } /** A greater-than test */ - case object GT extends TestOp + case object GT extends TestOp { + def negate() = LE + override def toString() = "GT" + } /** This class represents an arithmetic operation. */ class ArithmeticOp { diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index a485272ca6..2ff0c1926c 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -74,22 +74,19 @@ trait TypeKinds { self: ICodes => case _ => false } - /** On the JVM, these types are like Ints for the - * purposes of calculating the lub. + /** On the JVM, + * BOOL, BYTE, CHAR, SHORT, and INT + * are like Ints for the purposes of calculating the lub. */ - def isIntSizedType: Boolean = this match { - case BOOL | CHAR | BYTE | SHORT | INT => true - case _ => false - } - def isIntegralType: Boolean = this match { - case BYTE | SHORT | INT | LONG | CHAR => true - case _ => false - } - def isRealType: Boolean = this match { - case FLOAT | DOUBLE => true - case _ => false - } - def isNumericType: Boolean = isIntegralType | isRealType + def isIntSizedType: Boolean = false + + /** On the JVM, similar to isIntSizedType except that BOOL isn't integral while LONG is. */ + def isIntegralType: Boolean = false + + /** On the JVM, FLOAT and DOUBLE. */ + def isRealType: Boolean = false + + final def isNumericType: Boolean = isIntegralType | isRealType /** Simple subtyping check */ def <:<(other: TypeKind): Boolean = (this eq other) || (this match { @@ -97,11 +94,8 @@ trait TypeKinds { self: ICodes => case _ => this eq other }) - /** Is this type a category 2 type in JVM terms? */ - def isWideType: Boolean = this match { - case DOUBLE | LONG => true - case _ => false - } + /** Is this type a category 2 type in JVM terms? (ie, is it LONG or DOUBLE?) */ + def isWideType: Boolean = false /** The number of dimensions for array types. */ def dimensions: Int = 0 @@ -145,7 +139,7 @@ trait TypeKinds { self: ICodes => * Here we make the adjustment by rewinding to a pre-erasure state and * sifting through the parents for a class type. */ - def lub0(tk1: TypeKind, tk2: TypeKind): Type = atPhase(currentRun.uncurryPhase) { + def lub0(tk1: TypeKind, tk2: TypeKind): Type = beforeUncurry { import definitions._ val tp = global.lub(List(tk1.toType, tk2.toType)) val (front, rest) = tp.parents span (_.typeSymbol.hasTraitFlag) @@ -182,6 +176,7 @@ trait TypeKinds { self: ICodes => /** A boolean value */ case object BOOL extends ValueTypeKind { + override def isIntSizedType = true def maxType(other: TypeKind) = other match { case BOOL | REFERENCE(NothingClass) => BOOL case _ => uncomparable(other) @@ -195,6 +190,8 @@ trait TypeKinds { self: ICodes => /** A 1-byte signed integer */ case object BYTE extends ValueTypeKind { + override def isIntSizedType = true + override def isIntegralType = true def maxType(other: TypeKind) = { if (other == BYTE || other.isNothingType) BYTE else if (other == CHAR) INT @@ -205,6 +202,8 @@ trait TypeKinds { self: ICodes => /** A 2-byte signed integer */ case object SHORT extends ValueTypeKind { + override def isIntSizedType = true + override def isIntegralType = true override def maxType(other: TypeKind) = other match { case BYTE | SHORT | REFERENCE(NothingClass) => SHORT case CHAR => INT @@ -215,6 +214,8 @@ trait TypeKinds { self: ICodes => /** A 2-byte UNSIGNED integer */ case object CHAR extends ValueTypeKind { + override def isIntSizedType = true + override def isIntegralType = true override def maxType(other: TypeKind) = other match { case CHAR | REFERENCE(NothingClass) => CHAR case BYTE | SHORT => INT @@ -225,6 +226,8 @@ trait TypeKinds { self: ICodes => /** A 4-byte signed integer */ case object INT extends ValueTypeKind { + override def isIntSizedType = true + override def isIntegralType = true override def maxType(other: TypeKind) = other match { case BYTE | SHORT | CHAR | INT | REFERENCE(NothingClass) => INT case LONG | FLOAT | DOUBLE => other @@ -234,6 +237,8 @@ trait TypeKinds { self: ICodes => /** An 8-byte signed integer */ case object LONG extends ValueTypeKind { + override def isIntegralType = true + override def isWideType = true override def maxType(other: TypeKind): TypeKind = if (other.isIntegralType || other.isNothingType) LONG else if (other.isRealType) DOUBLE @@ -242,6 +247,7 @@ trait TypeKinds { self: ICodes => /** A 4-byte floating point number */ case object FLOAT extends ValueTypeKind { + override def isRealType = true override def maxType(other: TypeKind): TypeKind = if (other == DOUBLE) DOUBLE else if (other.isNumericType || other.isNothingType) FLOAT @@ -250,6 +256,8 @@ trait TypeKinds { self: ICodes => /** An 8-byte floating point number */ case object DOUBLE extends ValueTypeKind { + override def isRealType = true + override def isWideType = true override def maxType(other: TypeKind): TypeKind = if (other.isNumericType || other.isNothingType) DOUBLE else uncomparable(other) diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala index ba4b250303..8a2ec9a191 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala @@ -21,7 +21,7 @@ trait TypeStacks { * stack of the ICode. */ type Rep = List[TypeKind] - + object NoTypeStack extends TypeStack(Nil) { } class TypeStack(var types: Rep) { diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala index 60cb679782..9f43e1b84c 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala @@ -60,20 +60,17 @@ trait DataFlowAnalysis[L <: SemiLattice] { val output = f(point, in(point)) if ((lattice.bottom == out(point)) || output != out(point)) { -// Console.println("Output changed at " + point -// + " from: " + out(point) + " to: " + output -// + " for input: " + in(point) + " and they are different: " + (output != out(point))) + // Console.println("Output changed at " + point + // + " from: " + out(point) + " to: " + output + // + " for input: " + in(point) + " and they are different: " + (output != out(point))) out(point) = output val succs = point.successors succs foreach { p => - if (!worklist(p)) - worklist += p; - if (!in.isDefinedAt(p)) - assert(false, "Invalid successor for: " + point + " successor " + p + " does not exist") -// if (!p.exceptionHandlerHeader) { -// println("lubbing " + p.predecessors + " outs: " + p.predecessors.map(out.apply).mkString("\n", "\n", "")) - in(p) = lattice.lub(in(p) :: (p.predecessors map out.apply), p.exceptionHandlerStart) -// } + val updated = lattice.lub(in(p) :: (p.predecessors map out.apply), p.exceptionHandlerStart) + if(updated != in(p)) { + in(p) = updated + if (!worklist(p)) { worklist += p; } + } } } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala index c06bd2e097..69de0dfa90 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala @@ -105,11 +105,9 @@ abstract class ReachingDefinitions { def genAndKill(b: BasicBlock): (ListSet[Definition], ListSet[Local]) = { var genSet = ListSet[Definition]() var killSet = ListSet[Local]() - for ((i, idx) <- b.toList.zipWithIndex) i match { - case STORE_LOCAL(local) => - killSet = killSet + local - genSet = updateReachingDefinition(b, idx, genSet) - case _ => () + for ((STORE_LOCAL(local), idx) <- b.toList.zipWithIndex) { + killSet = killSet + local + genSet = updateReachingDefinition(b, idx, genSet) } (genSet, killSet) } diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala index 6421d6c8ef..877c51ebc1 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala @@ -127,34 +127,6 @@ abstract class TypeFlowAnalysis { } } - /** reinitialize the analysis, keeping around solutions from a previous run. */ - def reinit(m: icodes.IMethod) { - if (this.method == null || this.method.symbol != m.symbol) - init(m) - else reinit { - m foreachBlock { b => - if (!in.contains(b)) { - for (p <- b.predecessors) { - if (out.isDefinedAt(p)) { - in(b) = out(p) - worklist += p - } - /* else - in(b) = typeFlowLattice.bottom - */ } - out(b) = typeFlowLattice.bottom - } - } - for (handler <- m.exh) { - val start = handler.startBlock - if (!in.contains(start)) { - worklist += start - in(start) = lattice.IState(in(start).vars, typeStackLattice.exceptionHandlerStack) - } - } - } - } - def this(m: icodes.IMethod) { this() init(m) @@ -162,7 +134,7 @@ abstract class TypeFlowAnalysis { def run = { timer.start -// icodes.lubs0 = 0 + // icodes.lubs0 = 0 forwardAnalysis(blockTransfer) val t = timer.stop if (settings.debug.value) { @@ -170,216 +142,35 @@ abstract class TypeFlowAnalysis { assert(visited.contains(b), "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited)); } -// log("" + method.symbol.fullName + " [" + method.code.blocks.size + " blocks] " -// + "\n\t" + iterations + " iterations: " + t + " ms." -// + "\n\tlubs: " + typeFlowLattice.lubs + " out of which " + icodes.lubs0 + " typer lubs") + // log("" + method.symbol.fullName + " [" + method.code.blocks.size + " blocks] " + // + "\n\t" + iterations + " iterations: " + t + " ms." + // + "\n\tlubs: " + typeFlowLattice.lubs + " out of which " + icodes.lubs0 + " typer lubs") } def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = { - b.iterator.foldLeft(in)(interpret) - } - /** The flow function of a given basic block. */ - /* var flowFun: immutable.Map[BasicBlock, TransferFunction] = new immutable.HashMap */ - - /** Fill flowFun with a transfer function per basic block. */ -/* - private def buildFlowFunctions(blocks: List[BasicBlock]) { - def transfer(b: BasicBlock): TransferFunction = { - var gens: List[Gen] = Nil - var consumed: Int = 0 - val stack = new SimulatedStack - - for (instr <- b) instr match { - case THIS(clasz) => - stack push toTypeKind(clasz.tpe) - - case CONSTANT(const) => - stack push toTypeKind(const.tpe) - - case LOAD_ARRAY_ITEM(kind) => - stack.pop2 - stack.push(kind) - - case LOAD_LOCAL(local) => - val t = bindings(local) - stack push (if (t == typeLattice.bottom) local.kind else t) - - case LOAD_FIELD(field, isStatic) => - if (!isStatic) - stack.pop - stack push toTypeKind(field.tpe) - - case LOAD_MODULE(module) => - stack push toTypeKind(module.tpe) - - case STORE_ARRAY_ITEM(kind) => - stack.pop3 - - case STORE_LOCAL(local) => - val t = stack.pop - bindings += (local -> t) - - case STORE_THIS(_) => - stack.pop - - case STORE_FIELD(field, isStatic) => - if (isStatic) - stack.pop - else - stack.pop2 - - case CALL_PRIMITIVE(primitive) => - primitive match { - case Negation(kind) => - stack.pop; stack.push(kind) - case Test(_, kind, zero) => - stack.pop - if (!zero) stack.pop - stack push BOOL; - case Comparison(_, _) => - stack.pop2 - stack push INT - - case Arithmetic(op, kind) => - stack.pop - if (op != NOT) - stack.pop - val k = kind match { - case BYTE | SHORT | CHAR => INT - case _ => kind - } - stack push k - - case Logical(op, kind) => - stack.pop2 - stack push kind - - case Shift(op, kind) => - stack.pop2 - stack push kind - - case Conversion(src, dst) => - stack.pop - stack push dst - - case ArrayLength(kind) => - stack.pop - stack push INT - - case StartConcat => - stack.push(ConcatClass) - - case EndConcat => - stack.pop - stack.push(STRING) - - case StringConcat(el) => - stack.pop2 - stack push ConcatClass - } - - case CALL_METHOD(method, style) => style match { - case Dynamic => - stack.pop(1 + method.info.paramTypes.length) - stack.push(toTypeKind(method.info.resultType)) - - case Static(onInstance) => - if (onInstance) { - stack.pop(1 + method.info.paramTypes.length) - if (!method.isConstructor) - stack.push(toTypeKind(method.info.resultType)); - } else { - stack.pop(method.info.paramTypes.length) - stack.push(toTypeKind(method.info.resultType)) - } - - case SuperCall(mix) => - stack.pop(1 + method.info.paramTypes.length) - stack.push(toTypeKind(method.info.resultType)) - } - - case BOX(kind) => - stack.pop - stack.push(BOXED(kind)) - - case UNBOX(kind) => - stack.pop - stack.push(kind) - - case NEW(kind) => - stack.push(kind) - - case CREATE_ARRAY(elem, dims) => - stack.pop(dims) - stack.push(ARRAY(elem)) - - case IS_INSTANCE(tpe) => - stack.pop - stack.push(BOOL) - - case CHECK_CAST(tpe) => - stack.pop - stack.push(tpe) - - case SWITCH(tags, labels) => - stack.pop - - case JUMP(whereto) => - () - - case CJUMP(success, failure, cond, kind) => - stack.pop2 - - case CZJUMP(success, failure, cond, kind) => - stack.pop - - case RETURN(kind) => - if (kind != UNIT) - stack.pop; - - case THROW() => - stack.pop - - case DROP(kind) => - stack.pop - - case DUP(kind) => - stack.push(stack.head) - - case MONITOR_ENTER() => - stack.pop - - case MONITOR_EXIT() => - stack.pop - - case SCOPE_ENTER(_) | SCOPE_EXIT(_) => - () - - case LOAD_EXCEPTION(_) => - stack.pop(stack.length) - stack.push(typeLattice.Object) - - case _ => - dumpClassesAndAbort("Unknown instruction: " + i) - } - - new TransferFunction(consumed, gens) - } - - for (b <- blocks) { - flowFun = flowFun + (b -> transfer(b)) + var result = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack)) + var instrs = b.toList + while(!instrs.isEmpty) { + val i = instrs.head + result = mutatingInterpret(result, i) + instrs = instrs.tail } + result } -*/ + /** Abstract interpretation for one instruction. */ def interpret(in: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = { val out = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack)) + mutatingInterpret(out, i) + } + + def mutatingInterpret(out: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = { val bindings = out.vars val stack = out.stack if (settings.debug.value) { -// Console.println("[before] Stack: " + stack); -// Console.println(i); + // Console.println("[before] Stack: " + stack); + // Console.println(i); } i match { @@ -619,11 +410,292 @@ abstract class TypeFlowAnalysis { } } + case class CallsiteInfo(bb: icodes.BasicBlock, receiver: Symbol, stackLength: Int, concreteMethod: Symbol) + + /** + + A full type-flow analysis on a method computes in- and out-flows for each basic block (that's what MethodTFA does). + + For the purposes of Inliner, doing so guarantees that an abstract typestack-slot is available by the time an inlining candidate (a CALL_METHOD instruction) is visited. + This subclass (MTFAGrowable) of MethodTFA also aims at performing such analysis on CALL_METHOD instructions, with some differences: + + (a) early screening is performed while the type-flow is being computed (in an override of `blockTransfer`) by testing a subset of the conditions that Inliner checks later. + The reasoning here is: if the early check fails at some iteration, there's no chance a follow-up iteration (with a yet more lub-ed typestack-slot) will succeed. + Failure is sufficient to remove that particular CALL_METHOD from the typeflow's `remainingCALLs`. + A forward note: in case inlining occurs at some basic block B, all blocks reachable from B get their CALL_METHOD instructions considered again as candidates + (because of the more precise types that -- perhaps -- can be computed). + + (b) in case the early check does not fail, no conclusive decision can be made, thus the CALL_METHOD stays `isOnwatchlist`. + + In other words, `remainingCALLs` tracks those callsites that still remain as candidates for inlining, so that Inliner can focus on those. + `remainingCALLs` also caches info about the typestack just before the callsite, so as to spare computing them again at inlining time. + + Besides caching, a further optimization involves skipping those basic blocks whose in-flow and out-flow isn't needed anyway (as explained next). + A basic block lacking a callsite in `remainingCALLs`, when visisted by the standard algorithm, won't cause any inlining. + But as we know from the way type-flows are computed, computing the in- and out-flow for a basic block relies in general on those of other basic blocks. + In detail, we want to focus on that sub-graph of the CFG such that control flow may reach a remaining candidate callsite. + Those basic blocks not in that subgraph can be skipped altogether. That's why: + - `forwardAnalysis()` in `MTFAGrowable` now checks for inclusion of a basic block in `relevantBBs` + - same check is performed before adding a block to the worklist, and as part of choosing successors. + The bookkeeping supporting on-the-fly pruning of irrelevant blocks requires overridding most methods of the dataflow-analysis. + + The rest of the story takes place in Inliner, which does not visit all of the method's basic blocks but only on those represented in `remainingCALLs`. + + @author Miguel Garcia, http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ + + */ class MTFAGrowable extends MethodTFA { import icodes._ - /** discards what must be discarded, blanks what needs to be blanked out, and keeps the rest. */ + val remainingCALLs = mutable.Map.empty[opcodes.CALL_METHOD, CallsiteInfo] + + val preCandidates = mutable.Set.empty[BasicBlock] + + var callerLin: Traversable[BasicBlock] = null + + override def run { + + timer.start + forwardAnalysis(blockTransfer) + val t = timer.stop + + /* Now that `forwardAnalysis(blockTransfer)` has finished, all inlining candidates can be found in `remainingCALLs`, + whose keys are callsites and whose values are pieces of information about the typestack just before the callsite in question. + In order to keep `analyzeMethod()` simple, we collect in `preCandidates` those basic blocks containing at least one candidate. */ + preCandidates.clear() + for(rc <- remainingCALLs) { + preCandidates += rc._2.bb + } + + if (settings.debug.value) { + for(b <- callerLin; if (b != method.startBlock) && preCandidates(b)) { + assert(visited.contains(b), + "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited) + } + } + + } + + var shrinkedWatchlist = false + + /* + This is the method where information cached elsewhere is put to use. References are given those other places that populate those caches. + + The goal is avoiding computing type-flows for blocks we don't need (ie blocks not tracked in `relevantBBs`). The method used to add to `relevantBBs` is `putOnRadar`. + + Moreover, it's often the case that the last CALL_METHOD of interest ("of interest" equates to "being tracked in `isOnWatchlist`) isn't the last instruction on the block. + There are cases where the typeflows computed past this `lastInstruction` are needed, and cases when they aren't. + The reasoning behind this decsision is described in `populatePerimeter()`. All `blockTransfer()` needs to do (in order to know at which instruction it can stop) + is querying `isOnPerimeter`. + + Upon visiting a CALL_METHOD that's an inlining candidate, the relevant pieces of information about the pre-instruction typestack are collected for future use. + That is, unless the candidacy test fails. The reasoning here is: if such early check fails at some iteration, there's no chance a follow-up iteration + (with a yet more lub-ed typestack-slot) will succeed. In case of failure we can safely remove the CALL_METHOD from both `isOnWatchlist` and `remainingCALLs`. + + */ + override def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = { + var result = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack)) + + val stopAt = if(isOnPerimeter(b)) lastInstruction(b) else null; + var isPastLast = false + + var instrs = b.toList + while(!isPastLast && !instrs.isEmpty) { + val i = instrs.head + + if(isOnWatchlist(i)) { + val cm = i.asInstanceOf[opcodes.CALL_METHOD] + val msym = cm.method + val paramsLength = msym.info.paramTypes.size + val receiver = result.stack.types.drop(paramsLength).head match { + case REFERENCE(s) => s + case _ => NoSymbol // e.g. the scrutinee is BOX(s) or ARRAY + } + val concreteMethod = inliner.lookupImplFor(msym, receiver) + val isCandidate = { + ( inliner.isClosureClass(receiver) || concreteMethod.isEffectivelyFinal || receiver.isEffectivelyFinal ) && + !blackballed(concreteMethod) + } + if(isCandidate) { + remainingCALLs += Pair(cm, CallsiteInfo(b, receiver, result.stack.length, concreteMethod)) + } else { + remainingCALLs.remove(cm) + isOnWatchlist.remove(cm) + shrinkedWatchlist = true + } + } + + isPastLast = (i eq stopAt) + + if(!isPastLast) { + result = mutatingInterpret(result, i) + instrs = instrs.tail + } + } + + result + } // end of method blockTransfer + + val isOnWatchlist = mutable.Set.empty[Instruction] + + /* Each time CallerCalleeInfo.isSafeToInline determines a concrete callee is unsafe to inline in the current caller, + the fact is recorded in this TFA instance for the purpose of avoiding devoting processing to that callsite next time. + The condition of "being unsafe to inline in the current caller" sticks across inlinings and TFA re-inits + because it depends on the instructions of the callee, which stay unchanged during the course of `analyzeInc(caller)` + (with the caveat of the side-effecting `makePublic` in `helperIsSafeToInline`).*/ + val knownUnsafe = mutable.Set.empty[Symbol] + val knownSafe = mutable.Set.empty[Symbol] + val knownNever = mutable.Set.empty[Symbol] // `knownNever` needs be cleared only at the very end of the inlining phase (unlike `knownUnsafe` and `knownSafe`) + @inline final def blackballed(msym: Symbol): Boolean = { knownUnsafe(msym) || knownNever(msym) } + + val relevantBBs = mutable.Set.empty[BasicBlock] + + private def isPreCandidate(cm: opcodes.CALL_METHOD): Boolean = { + val msym = cm.method + val style = cm.style + // Dynamic == normal invocations + // Static(true) == calls to private members + !msym.isConstructor && !blackballed(msym) && + (style.isDynamic || (style.hasInstance && style.isStatic)) + // && !(msym hasAnnotation definitions.ScalaNoInlineClass) + } + + override def init(m: icodes.IMethod) { + super.init(m) + remainingCALLs.clear() + knownUnsafe.clear() + knownSafe.clear() + // initially populate the watchlist with all callsites standing a chance of being inlined + isOnWatchlist.clear() + relevantBBs.clear() + /* TODO Do we want to perform inlining in non-finally exception handlers? + * Seems counterproductive (the larger the method the less likely it will be JITed. + * It's not that putting on radar only `linearizer linearizeAt (m, m.startBlock)` makes for much shorter inlining times (a minor speedup nonetheless) + * but the effect on method size could be explored. */ + putOnRadar(m.linearizedBlocks(linearizer)) + populatePerimeter() + assert(relevantBBs.isEmpty || relevantBBs.contains(m.startBlock), "you gave me dead code") + } + + def conclusives(b: BasicBlock): List[opcodes.CALL_METHOD] = { + knownBeforehand(b) filter { cm => inliner.isMonadicMethod(cm.method) || inliner.hasInline(cm.method) } + } + + def knownBeforehand(b: BasicBlock): List[opcodes.CALL_METHOD] = { + b.toList collect { case c : opcodes.CALL_METHOD => c } filter { cm => isPreCandidate(cm) && isReceiverKnown(cm) } + } + + private def isReceiverKnown(cm: opcodes.CALL_METHOD): Boolean = { + cm.method.isEffectivelyFinal && cm.method.owner.isEffectivelyFinal + } + + private def putOnRadar(blocks: Traversable[BasicBlock]) { + for(bb <- blocks) { + val preCands = bb.toList collect { + case cm : opcodes.CALL_METHOD + if isPreCandidate(cm) /* && !isReceiverKnown(cm) */ + => cm + } + isOnWatchlist ++= preCands + } + relevantBBs ++= blocks + } + + /* the argument is also included in the result */ + private def transitivePreds(b: BasicBlock): Set[BasicBlock] = { transitivePreds(List(b)) } + + /* those BBs in the argument are also included in the result */ + private def transitivePreds(starters: Traversable[BasicBlock]): Set[BasicBlock] = { + val result = mutable.Set.empty[BasicBlock] + var toVisit: List[BasicBlock] = starters.toList.distinct + while(toVisit.nonEmpty) { + val h = toVisit.head + toVisit = toVisit.tail + result += h + for(p <- h.predecessors; if !result(p) && !toVisit.contains(p)) { toVisit = p :: toVisit } + } + result.toSet + } + + /* those BBs in the argument are also included in the result */ + private def transitiveSuccs(starters: Traversable[BasicBlock]): Set[BasicBlock] = { + val result = mutable.Set.empty[BasicBlock] + var toVisit: List[BasicBlock] = starters.toList.distinct + while(toVisit.nonEmpty) { + val h = toVisit.head + toVisit = toVisit.tail + result += h + for(p <- h.successors; if !result(p) && !toVisit.contains(p)) { toVisit = p :: toVisit } + } + result.toSet + } + + /* A basic block B is "on the perimeter" of the current control-flow subgraph if none of its successors belongs to that subgraph. + * In that case, for the purposes of inlining, we're interested in the typestack right before the last inline candidate in B, not in those afterwards. + * In particular we can do without computing the outflow at B. */ + private def populatePerimeter() { + isOnPerimeter.clear() + var done = true + do { + val (frontier, toPrune) = (relevantBBs filter hasNoRelevantSuccs) partition isWatching + isOnPerimeter ++= frontier + relevantBBs --= toPrune + done = toPrune.isEmpty + } while(!done) + + lastInstruction.clear() + for(b <- isOnPerimeter; val lastIns = b.toList.reverse find isOnWatchlist) { + lastInstruction += (b -> lastIns.get.asInstanceOf[opcodes.CALL_METHOD]) + } + + // assertion: "no relevant block can have a predecessor that is on perimeter" + assert((for (b <- relevantBBs; if transitivePreds(b.predecessors) exists isOnPerimeter) yield b).isEmpty) + } + + private val isOnPerimeter = mutable.Set.empty[BasicBlock] + private val lastInstruction = mutable.Map.empty[BasicBlock, opcodes.CALL_METHOD] + + def hasNoRelevantSuccs(x: BasicBlock): Boolean = { !(x.successors exists relevantBBs) } + + def isWatching(x: BasicBlock): Boolean = (x.toList exists isOnWatchlist) + + + + + /** + + This method is invoked after one or more inlinings have been performed in basic blocks whose in-flow is non-bottom (this makes a difference later). + What we know about those inlinings is given by: + + - `staleOut`: These are the blocks where a callsite was inlined. + For each callsite, all instructions in that block before the callsite were left in the block, and the rest moved to an `afterBlock`. + The out-flow of these basic blocks is thus in general stale, that's why we'll add them to the TFA worklist. + + - `inlined` : These blocks were spliced into the method's CFG as part of inlining. Being new blocks, they haven't been visited yet by the typeflow analysis. + + - `staleIn` : These blocks are what `doInline()` calls `afterBlock`s, ie the new home for instructions that previously appearead + after a callsite in a `staleOut` block. + + Based on the above information, we have to bring up-to-date the caches that `forwardAnalysis` and `blockTransfer` use to skip blocks and instructions. + Those caches are `relevantBBs` and `isOnPerimeter` (for blocks) and `isOnWatchlist` and `lastInstruction` (for CALL_METHODs). + Please notice that all `inlined` and `staleIn` blocks are reachable from `staleOut` blocks. + + The update takes place in two steps: + + (1) `staleOut foreach { so => putOnRadar(linearizer linearizeAt (m, so)) }` + This results in initial populations for `relevantBBs` and `isOnWatchlist`. + Because of the way `isPreCandidate` reuses previous decision-outcomes that are still valid, + this already prunes some candidates standing no chance of being inlined. + + (2) `populatePerimeter()` + Based on the CFG-subgraph determined in (1) as reflected in `relevantBBs`, + this method detects some blocks whose typeflows aren't needed past a certain CALL_METHOD + (not needed because none of its successors is relevant for the purposes of inlining, see `hasNoRelevantSuccs`). + The blocks thus chosen are said to be "on the perimeter" of the CFG-subgraph. + For each of them, its `lastInstruction` (after which no more typeflows are needed) is found. + + */ def reinit(m: icodes.IMethod, staleOut: List[BasicBlock], inlined: collection.Set[BasicBlock], staleIn: collection.Set[BasicBlock]) { if (this.method == null || this.method.symbol != m.symbol) { init(m) @@ -633,31 +705,102 @@ abstract class TypeFlowAnalysis { return; } - reinit { - // asserts conveying an idea what CFG shapes arrive here. - // staleIn foreach (p => assert( !in.isDefinedAt(p), p)) - // staleIn foreach (p => assert(!out.isDefinedAt(p), p)) - // inlined foreach (p => assert( !in.isDefinedAt(p), p)) - // inlined foreach (p => assert(!out.isDefinedAt(p), p)) - // inlined foreach (p => assert(!p.successors.isEmpty || p.lastInstruction.isInstanceOf[icodes.opcodes.THROW], p)) - // staleOut foreach (p => assert( in.isDefinedAt(p), p)) - - // never rewrite in(m.startBlock) - staleOut foreach { b => - if(!inlined.contains(b)) { worklist += b } - out(b) = typeFlowLattice.bottom - } - // nothing else is added to the worklist, bb's reachable via succs will be tfa'ed - blankOut(inlined) - blankOut(staleIn) - // no need to add startBlocks from m.exh + worklist.clear // calling reinit(f: => Unit) would also clear visited, thus forgetting about blocks visited before reinit. + + // asserts conveying an idea what CFG shapes arrive here: + // staleIn foreach (p => assert( !in.isDefinedAt(p), p)) + // staleIn foreach (p => assert(!out.isDefinedAt(p), p)) + // inlined foreach (p => assert( !in.isDefinedAt(p), p)) + // inlined foreach (p => assert(!out.isDefinedAt(p), p)) + // inlined foreach (p => assert(!p.successors.isEmpty || p.lastInstruction.isInstanceOf[icodes.opcodes.THROW], p)) + // staleOut foreach (p => assert( in.isDefinedAt(p), p)) + + // remainingCALLs.clear() + isOnWatchlist.clear() + relevantBBs.clear() + + // never rewrite in(m.startBlock) + staleOut foreach { b => + enqueue(b) + out(b) = typeFlowLattice.bottom } + // nothing else is added to the worklist, bb's reachable via succs will be tfa'ed + blankOut(inlined) + blankOut(staleIn) + // no need to add startBlocks from m.exh + + staleOut foreach { so => putOnRadar(linearizer linearizeAt (m, so)) } + populatePerimeter() + + } // end of method reinit + + /* this is not a general purpose method to add to the worklist, + * because the assert is expected to hold only when called from MTFAGrowable.reinit() */ + private def enqueue(b: BasicBlock) { + assert(in(b) ne typeFlowLattice.bottom) + if(!worklist.contains(b)) { worklist += b } + } + + /* this is not a general purpose method to add to the worklist, + * because the assert is expected to hold only when called from MTFAGrowable.reinit() */ + private def enqueue(bs: Traversable[BasicBlock]) { + bs foreach enqueue } private def blankOut(blocks: collection.Set[BasicBlock]) { blocks foreach { b => - in(b) = typeFlowLattice.bottom - out(b) = typeFlowLattice.bottom + in(b) = typeFlowLattice.bottom + out(b) = typeFlowLattice.bottom + } + } + + /* + This is basically the plain-old forward-analysis part of a dataflow algorithm, + adapted to skip non-relevant blocks (as determined by `reinit()` via `populatePerimeter()`). + + The adaptations are: + + - only relevant blocks dequeued from the worklist move on to have the transfer function applied + + - `visited` now means the transfer function was applied to the block, + but please notice that this does not imply anymore its out-flow to be different from bottom, + because a block on the perimeter will have per-instruction typeflows computed only up to its `lastInstruction`. + In case you need to know whether a visted block `v` has been "fully visited", evaluate `out(v) ne typeflowLattice.bottom` + + - given that the transfer function may remove callsite-candidates from the watchlist (thus, they are not candidates anymore) + there's an opportunity to detect whether a previously relevant block has been left without candidates. + That's what `shrinkedWatchlist` detects. Provided the block was on the perimeter, we know we can skip it from now now, + and we can also constrain the CFG-subgraph by finding a new perimeter (thus the invocation to `populatePerimeter()`). + */ + override def forwardAnalysis(f: (P, lattice.Elem) => lattice.Elem): Unit = { + while (!worklist.isEmpty && relevantBBs.nonEmpty) { + if (stat) iterations += 1 + val point = worklist.iterator.next; worklist -= point; + if(relevantBBs(point)) { + shrinkedWatchlist = false + val output = f(point, in(point)) + visited += point; + if(isOnPerimeter(point)) { + if(shrinkedWatchlist && !isWatching(point)) { + relevantBBs -= point; + populatePerimeter() + } + } else { + val propagate = ((lattice.bottom == out(point)) || output != out(point)) + if (propagate) { + out(point) = output + val succs = point.successors filter relevantBBs + succs foreach { p => + assert((p.predecessors filter isOnPerimeter).isEmpty) + val updated = lattice.lub(List(output, in(p)), p.exceptionHandlerStart) + if(updated != in(p)) { + in(p) = updated + enqueue(p) + } + } + } + } + } } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index 865bacffaa..c217869a48 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -23,9 +23,7 @@ trait BytecodeWriters { import global._ private def outputDirectory(sym: Symbol): AbstractFile = ( - settings.outputDirs.outputDirFor { - atPhase(currentRun.flattenPhase.prev)(sym.sourceFile) - } + settings.outputDirs.outputDirFor(beforeFlatten(sym.sourceFile)) ) private def getFile(base: AbstractFile, cls: JClass, suffix: String): AbstractFile = { var dir = base @@ -85,7 +83,7 @@ trait BytecodeWriters { emitJavap(bytes, javapFile) } } - + trait ClassBytecodeWriter extends BytecodeWriter { def writeClass(label: String, jclass: JClass, sym: Symbol) { val outfile = getFile(sym, jclass, ".class") @@ -96,18 +94,18 @@ trait BytecodeWriters { informProgress("wrote '" + label + "' to " + outfile) } } - + trait DumpBytecodeWriter extends BytecodeWriter { val baseDir = Directory(settings.Ydumpclasses.value).createDirectory() - + abstract override def writeClass(label: String, jclass: JClass, sym: Symbol) { super.writeClass(label, jclass, sym) - + val pathName = jclass.getName() var dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile; dumpFile.parent.createDirectory() val outstream = new DataOutputStream(new FileOutputStream(dumpFile.path)) - + try jclass writeTo outstream finally outstream.close() } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index b5232fff09..c609f126d3 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -37,21 +37,19 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with /** Create a new phase */ override def newPhase(p: Phase): Phase = new JvmPhase(p) - private def outputDirectory(sym: Symbol): AbstractFile = ( - settings.outputDirs.outputDirFor { - atPhase(currentRun.flattenPhase.prev)(sym.sourceFile) - } - ) - private def getFile(base: AbstractFile, cls: JClass, suffix: String): AbstractFile = { + private def outputDirectory(sym: Symbol): AbstractFile = + settings.outputDirs outputDirFor beforeFlatten(sym.sourceFile) + + private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { var dir = base - val pathParts = cls.getName().split("[./]").toList + val pathParts = clsName.split("[./]").toList for (part <- pathParts.init) { dir = dir.subdirectoryNamed(part) } dir.fileNamed(pathParts.last + suffix) } - private def getFile(sym: Symbol, cls: JClass, suffix: String): AbstractFile = - getFile(outputDirectory(sym), cls, suffix) + private def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = + getFile(outputDirectory(sym), clsName, suffix) /** JVM code generation phase */ @@ -87,7 +85,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with // succeed or warn that it isn't. hasApproximate && { // Before erasure so we can identify generic mains. - atPhase(currentRun.erasurePhase) { + beforeErasure { val companion = sym.linkedClassOfClass val companionMain = companion.tpe.member(nme.main) @@ -154,14 +152,14 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with if (settings.Ygenjavap.isDefault) { if(settings.Ydumpclasses.isDefault) new ClassBytecodeWriter { } - else + else new ClassBytecodeWriter with DumpBytecodeWriter { } } else new ClassBytecodeWriter with JavapBytecodeWriter { } } val codeGenerator = new BytecodeGenerator(bytecodeWriter) - log("Created new bytecode generator for " + classes.size + " classes.") + debuglog("Created new bytecode generator for " + classes.size + " classes.") sortedClasses foreach { c => try codeGenerator.genClass(c) @@ -197,8 +195,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with val StringBuilderClassName = javaName(definitions.StringBuilderClass) val BoxesRunTime = "scala.runtime.BoxesRunTime" - val StringBuilderType = new JObjectType(StringBuilderClassName) - val toStringType = new JMethodType(JAVA_LANG_STRING, JType.EMPTY_ARRAY) + val StringBuilderType = new JObjectType(StringBuilderClassName) // TODO use ASMType.getObjectType + val toStringType = new JMethodType(JAVA_LANG_STRING, JType.EMPTY_ARRAY) // TODO use ASMType.getMethodType val arrayCloneType = new JMethodType(JAVA_LANG_OBJECT, JType.EMPTY_ARRAY) val MethodTypeType = new JObjectType("java.dyn.MethodType") val JavaLangClassType = new JObjectType("java.lang.Class") @@ -209,17 +207,18 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with val BeanInfoSkipAttr = definitions.getRequiredClass("scala.beans.BeanInfoSkip") val BeanDisplayNameAttr = definitions.getRequiredClass("scala.beans.BeanDisplayName") val BeanDescriptionAttr = definitions.getRequiredClass("scala.beans.BeanDescription") - + final val ExcludedForwarderFlags = { import Flags._ - ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | BridgeAndPrivateFlags ) + // Should include DEFERRED but this breaks findMember. + ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags ) } // Additional interface parents based on annotations and other cues - def newParentForAttr(attr: Symbol): Option[Type] = attr match { - case SerializableAttr => Some(SerializableClass.tpe) - case CloneableAttr => Some(JavaCloneableClass.tpe) - case RemoteAttr => Some(RemoteInterfaceClass.tpe) + def newParentForAttr(attr: Symbol): Option[Symbol] = attr match { + case SerializableAttr => Some(SerializableClass) + case CloneableAttr => Some(JavaCloneableClass) + case RemoteAttr => Some(RemoteInterfaceClass) case _ => None } @@ -232,11 +231,47 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with vp } + private def helperBoxTo(kind: ValueTypeKind): Tuple2[String, JMethodType] = { + val boxedType = definitions.boxedClass(kind.toType.typeSymbol) + val mtype = new JMethodType(javaType(boxedType), Array(javaType(kind))) + + Pair("boxTo" + boxedType.decodedName, mtype) + } + + private val jBoxTo: Map[TypeKind, Tuple2[String, JMethodType]] = Map( + BOOL -> helperBoxTo(BOOL) , + BYTE -> helperBoxTo(BYTE) , + CHAR -> helperBoxTo(CHAR) , + SHORT -> helperBoxTo(SHORT) , + INT -> helperBoxTo(INT) , + LONG -> helperBoxTo(LONG) , + FLOAT -> helperBoxTo(FLOAT) , + DOUBLE -> helperBoxTo(DOUBLE) + ) + + private def helperUnboxTo(kind: ValueTypeKind): Tuple2[String, JMethodType] = { + val mtype = new JMethodType(javaType(kind), Array(JAVA_LANG_OBJECT)) + val mname = "unboxTo" + kind.toType.typeSymbol.decodedName + + Pair(mname, mtype) + } + + private val jUnboxTo: Map[TypeKind, Tuple2[String, JMethodType]] = Map( + BOOL -> helperUnboxTo(BOOL) , + BYTE -> helperUnboxTo(BYTE) , + CHAR -> helperUnboxTo(CHAR) , + SHORT -> helperUnboxTo(SHORT) , + INT -> helperUnboxTo(INT) , + LONG -> helperUnboxTo(LONG) , + FLOAT -> helperUnboxTo(FLOAT) , + DOUBLE -> helperUnboxTo(DOUBLE) + ) + var clasz: IClass = _ var method: IMethod = _ var jclass: JClass = _ var jmethod: JMethod = _ -// var jcode: JExtendedCode = _ + // var jcode: JExtendedCode = _ def isParcelableClass = isAndroidParcelableClass(clasz.symbol) def isRemoteClass = clasz.symbol hasAnnotation RemoteAttr @@ -264,7 +299,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with private def innerClassSymbolFor(s: Symbol): Symbol = if (s.isClass) s else if (s.isModule) s.moduleClass else NoSymbol - override def javaName(sym: Symbol): String = { + override def javaName(sym: Symbol): String = { // TODO Miguel says: check whether a single pass over `icodes.classes` can populate `innerClassBuffer` faster. /** * Checks if given symbol corresponds to inner class/object and add it to innerClassBuffer * @@ -272,13 +307,16 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with * of inner class all until root class. */ def collectInnerClass(s: Symbol): Unit = { - // TODO: something atPhase(currentRun.flattenPhase.prev) which accounts for + // TODO: some beforeFlatten { ... } which accounts for // being nested in parameterized classes (if we're going to selectively flatten.) val x = innerClassSymbolFor(s) - val isInner = x.isClass && !x.rawowner.isPackageClass - if (isInner) { - innerClassBuffer += x - collectInnerClass(x.rawowner) + if(x ne NoSymbol) { + assert(x.isClass, "not an inner-class symbol") + val isInner = !x.rawowner.isPackageClass + if (isInner) { + innerClassBuffer += x + collectInnerClass(x.rawowner) + } } } collectInnerClass(sym) @@ -340,38 +378,44 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with private var innerClassBuffer = mutable.LinkedHashSet[Symbol]() - /** Drop redundant interfaces (ones which are implemented by some - * other parent) from the immediate parents. This is important on - * android because there is otherwise an interface explosion. + /** Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents. + * This is important on Android because there is otherwise an interface explosion. */ - private def minimizeInterfaces(interfaces: List[Symbol]): List[Symbol] = ( - interfaces filterNot (int1 => - interfaces exists (int2 => - (int1 ne int2) && (int2 isSubClass int1) - ) - ) - ) + private def minimizeInterfaces(interfaces: List[Symbol]): List[Symbol] = { + var rest = interfaces + var leaves = List.empty[Symbol] + while(!rest.isEmpty) { + val candidate = rest.head + val nonLeaf = leaves exists { lsym => lsym isSubClass candidate } + if(!nonLeaf) { + leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym }) + } + rest = rest.tail + } + + leaves + } def genClass(c: IClass) { clasz = c innerClassBuffer.clear() val name = javaName(c.symbol) - val superClass :: superInterfaces = { - val parents0 = c.symbol.info.parents match { - case Nil => List(ObjectClass.tpe) - case ps => ps - } - parents0 ++ c.symbol.annotations.flatMap(ann => newParentForAttr(ann.symbol)) distinct - } - val ifaces = superInterfaces match { - case Nil => JClass.NO_INTERFACES - case _ => mkArray(minimizeInterfaces(superInterfaces map (_.typeSymbol)) map javaName) - } + + val ps = c.symbol.info.parents + + val superClass: Symbol = if(ps.isEmpty) ObjectClass else ps.head.typeSymbol; + + val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses; + val superInterfaces = superInterfaces0 ++ c.symbol.annotations.flatMap(ann => newParentForAttr(ann.symbol)) distinct + + val ifaces = + if(superInterfaces.isEmpty) JClass.NO_INTERFACES + else mkArray(minimizeInterfaces(superInterfaces) map javaName) jclass = fjbgContext.JClass(javaFlags(c.symbol), name, - javaName(superClass.typeSymbol), + javaName(superClass), ifaces, c.cunit.source.toString) @@ -393,7 +437,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with // it must be a top level class (name contains no $s) def isCandidateForForwarders(sym: Symbol): Boolean = - atPhase(currentRun.picklerPhase.next) { + afterPickler { !(sym.name.toString contains '$') && sym.hasModuleFlag && !sym.isImplClass && !sym.isNestedClass } @@ -433,7 +477,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with private def addEnclosingMethodAttribute(jclass: JClass, clazz: Symbol) { val sym = clazz.originalEnclosingMethod if (sym.isMethod) { - log("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass)) + debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass)) jclass addAttribute fjbgContext.JEnclosingMethodAttribute( jclass, javaName(sym.enclClass), @@ -449,7 +493,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with enclClass, clazz) ) else { - log("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass)) + debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass)) jclass addAttribute fjbgContext.JEnclosingMethodAttribute( jclass, javaName(enclClass), @@ -681,7 +725,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with ) def addGenericSignature(jmember: JMember, sym: Symbol, owner: Symbol) { if (needsGenericSignature(sym)) { - val memberTpe = atPhase(currentRun.erasurePhase)(owner.thisType.memberInfo(sym)) + val memberTpe = beforeErasure(owner.thisType.memberInfo(sym)) // println("addGenericSignature sym: " + sym.fullName + " : " + memberTpe + " sym.info: " + sym.info) // println("addGenericSignature: "+ (sym.ownerChain map (x => (x.name, x.isImplClass)))) erasure.javaSig(sym, memberTpe) foreach { sig => @@ -700,7 +744,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with return } if ((settings.check.value contains "genjvm")) { - val normalizedTpe = atPhase(currentRun.erasurePhase)(erasure.prepareSigMap(memberTpe)) + val normalizedTpe = beforeErasure(erasure.prepareSigMap(memberTpe)) val bytecodeTpe = owner.thisType.memberInfo(sym) if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym, normalizedTpe) =:= bytecodeTpe)) { clasz.cunit.warning(sym.pos, @@ -716,9 +760,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with } val index = jmember.getConstantPool.addUtf8(sig).toShort if (opt.verboseDebug) - atPhase(currentRun.erasurePhase) { - println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index) - } + beforeErasure(println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index)) + val buf = ByteBuffer.allocate(2) buf putShort index addAttribute(jmember, tpnme.SignatureATTR, buf) @@ -793,14 +836,14 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with innerSym.rawname + innerSym.moduleSuffix // add inner classes which might not have been referenced yet - atPhase(currentRun.erasurePhase.next) { + afterErasure { for (sym <- List(clasz.symbol, clasz.symbol.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass) innerClassBuffer += m } val allInners = innerClassBuffer.toList if (allInners.nonEmpty) { - log(clasz.symbol.fullName('.') + " contains " + allInners.size + " inner classes.") + debuglog(clasz.symbol.fullName('.') + " contains " + allInners.size + " inner classes.") val innerClassesAttr = jclass.getInnerClasses() // sort them so inner classes succeed their enclosing class // to satisfy the Eclipse Java compiler @@ -826,6 +869,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with def genField(f: IField) { debuglog("Adding field: " + f.symbol.fullName) + val jfield = jclass.addNewField( javaFlags(f.symbol) | javaFieldFlags(f.symbol), javaName(f.symbol), @@ -1128,8 +1172,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with linearization = linearizer.linearize(m) val labels = makeLabels(linearization) - /** local variables whose scope appears in this block. */ - val varsInBlock: mutable.Set[Local] = new mutable.HashSet var nextBlock: BasicBlock = linearization.head @@ -1139,302 +1181,298 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with case x :: y :: ys => nextBlock = y; genBlock(x); genBlocks(y :: ys) } - /** Generate exception handlers for the current method. */ - def genExceptionHandlers() { + /** Generate exception handlers for the current method. */ + def genExceptionHandlers() { - /** Return a list of pairs of intervals where the handler is active. - * The intervals in the list have to be inclusive in the beginning and - * exclusive in the end: [start, end). - */ - def ranges(e: ExceptionHandler): List[(Int, Int)] = { - var covered = e.covered - var ranges: List[(Int, Int)] = Nil - var start = -1 - var end = -1 - - linearization foreach { b => - if (! (covered contains b) ) { - if (start >= 0) { // we're inside a handler range - end = labels(b).getAnchor() - ranges ::= ((start, end)) - start = -1 + /** Return a list of pairs of intervals where the handler is active. + * The intervals in the list have to be inclusive in the beginning and + * exclusive in the end: [start, end). + */ + def ranges(e: ExceptionHandler): List[(Int, Int)] = { + var covered = e.covered + var ranges: List[(Int, Int)] = Nil + var start = -1 + var end = -1 + + linearization foreach { b => + if (! (covered contains b) ) { + if (start >= 0) { // we're inside a handler range + end = labels(b).getAnchor() + ranges ::= ((start, end)) + start = -1 + } + } else { + if (start < 0) // we're not inside a handler range + start = labels(b).getAnchor() + + end = endPC(b) + covered -= b } - } else { - if (start < 0) // we're not inside a handler range - start = labels(b).getAnchor() + } - end = endPC(b) - covered -= b + /* Add the last interval. Note that since the intervals are + * open-ended to the right, we have to give a number past the actual + * code! + */ + if (start >= 0) { + ranges ::= ((start, jcode.getPC())) } - } - /* Add the last interval. Note that since the intervals are - * open-ended to the right, we have to give a number past the actual - * code! - */ - if (start >= 0) { - ranges ::= ((start, jcode.getPC())) + if (!covered.isEmpty) + debuglog("Some covered blocks were not found in method: " + method + + " covered: " + covered + " not in " + linearization) + ranges } - if (!covered.isEmpty) - debuglog("Some covered blocks were not found in method: " + method + - " covered: " + covered + " not in " + linearization) - ranges + for (e <- this.method.exh ; p <- ranges(e).sortBy(_._1)) { + if (p._1 < p._2) { + debuglog("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method + + " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls); + val cls = if (e.cls == NoSymbol || e.cls == ThrowableClass) null + else javaName(e.cls) + jcode.addExceptionHandler(p._1, p._2, + labels(e.startBlock).getAnchor(), + cls) + } else + log("Empty exception range: " + p) + } } - for (e <- this.method.exh ; p <- ranges(e).sortBy(_._1)) { - if (p._1 < p._2) { - debuglog("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method + - " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls); - val cls = if (e.cls == NoSymbol || e.cls == ThrowableClass) null - else javaName(e.cls) - jcode.addExceptionHandler(p._1, p._2, - labels(e.startBlock).getAnchor(), - cls) - } else - log("Empty exception range: " + p) + def isAccessibleFrom(target: Symbol, site: Symbol): Boolean = { + target.isPublic || target.isProtected && { + (site.enclClass isSubClass target.enclClass) || + (site.enclosingPackage == target.privateWithin) + } } - } - def isAccessibleFrom(target: Symbol, site: Symbol): Boolean = { - target.isPublic || target.isProtected && { - (site.enclClass isSubClass target.enclClass) || - (site.enclosingPackage == target.privateWithin) - } - } + def genCallMethod(call: CALL_METHOD) { + val CALL_METHOD(method, style) = call + val siteSymbol = clasz.symbol + val hostSymbol = call.hostClass + val methodOwner = method.owner + // info calls so that types are up to date; erasure may add lateINTERFACE to traits + hostSymbol.info ; methodOwner.info + + def isInterfaceCall(sym: Symbol) = ( + sym.isInterface && methodOwner != ObjectClass + || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass) + ) + // whether to reference the type of the receiver or + // the type of the method owner (if not an interface!) + val useMethodOwner = ( + style != Dynamic + || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol) + || hostSymbol.isBottomClass + ) + val receiver = if (useMethodOwner) methodOwner else hostSymbol + val jowner = javaName(receiver) + val jname = javaName(method) + val jtype = javaType(method).asInstanceOf[JMethodType] - def genCallMethod(call: CALL_METHOD) { - val CALL_METHOD(method, style) = call - val siteSymbol = clasz.symbol - val hostSymbol = call.hostClass - val methodOwner = method.owner - // info calls so that types are up to date; erasure may add lateINTERFACE to traits - hostSymbol.info ; methodOwner.info - - def isInterfaceCall(sym: Symbol) = ( - sym.isInterface && methodOwner != ObjectClass - || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass) - ) - // whether to reference the type of the receiver or - // the type of the method owner (if not an interface!) - val useMethodOwner = ( - style != Dynamic - || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol) - || hostSymbol.isBottomClass - ) - val receiver = if (useMethodOwner) methodOwner else hostSymbol - val jowner = javaName(receiver) - val jname = javaName(method) - val jtype = javaType(method).asInstanceOf[JMethodType] - - def emit(invoke: String) { - log("%s %s %s.%s:%s".format(invoke, receiver.accessString, jowner, jname, jtype)) - invoke match { - case "invokeinterface" => jcode.emitINVOKEINTERFACE(jowner, jname, jtype) - case "invokevirtual" => jcode.emitINVOKEVIRTUAL(jowner, jname, jtype) - case "invokespecial" => jcode.emitINVOKESPECIAL(jowner, jname, jtype) - case "invokestatic" => jcode.emitINVOKESTATIC(jowner, jname, jtype) + def debugMsg(invoke: String) { + debuglog("%s %s %s.%s:%s".format(invoke, receiver.accessString, jowner, jname, jtype)) } - } - def initModule() { - // we initialize the MODULE$ field immediately after the super ctor - if (isStaticModule(siteSymbol) && !isModuleInitialized && - jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME && - jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) { - isModuleInitialized = true - jcode.emitALOAD_0() - jcode.emitPUTSTATIC(jclass.getName(), - nme.MODULE_INSTANCE_FIELD.toString, - jclass.getType()) + + def initModule() { + // we initialize the MODULE$ field immediately after the super ctor + if (isStaticModule(siteSymbol) && !isModuleInitialized && + jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME && + jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) { + isModuleInitialized = true + jcode.emitALOAD_0() + jcode.emitPUTSTATIC(jclass.getName(), + nme.MODULE_INSTANCE_FIELD.toString, + jclass.getType()) + } } - } - style match { - case Static(true) => emit("invokespecial") - case Static(false) => emit("invokestatic") - case Dynamic if isInterfaceCall(receiver) => emit("invokeinterface") - case Dynamic => emit("invokevirtual") - case SuperCall(_) => emit("invokespecial") ; initModule() + style match { + case Static(true) => jcode.emitINVOKESPECIAL (jowner, jname, jtype) ; debugMsg("invokespecial") + case Static(false) => jcode.emitINVOKESTATIC (jowner, jname, jtype) ; debugMsg("invokestatic") + case Dynamic if isInterfaceCall(receiver) => jcode.emitINVOKEINTERFACE(jowner, jname, jtype) ; debugMsg("invokinterface") + case Dynamic => jcode.emitINVOKEVIRTUAL (jowner, jname, jtype) ; debugMsg("invokevirtual") + case SuperCall(_) => + jcode.emitINVOKESPECIAL(jowner, jname, jtype) + initModule() + debugMsg("invokespecial") + } } - } - def genBlock(b: BasicBlock) { - labels(b).anchorToNext() + def genBlock(b: BasicBlock) { + labels(b).anchorToNext() - debuglog("Generating code for block: " + b + " at pc: " + labels(b).getAnchor()) - var lastMappedPC = 0 - var lastLineNr = 0 - var crtPC = 0 - varsInBlock.clear() + debuglog("Generating code for block: " + b + " at pc: " + labels(b).getAnchor()) + var lastMappedPC = 0 + var lastLineNr = 0 + var crtPC = 0 - for (instr <- b) { + /** local variables whose scope appears in this block. */ + val varsInBlock: mutable.Set[Local] = new mutable.HashSet + val lastInstr = b.lastInstruction - instr match { - case THIS(clasz) => - jcode.emitALOAD_0() + for (instr <- b) { - case CONSTANT(const) => - genConstant(jcode, const) + instr match { + case THIS(clasz) => jcode.emitALOAD_0() - case LOAD_ARRAY_ITEM(kind) => - jcode.emitALOAD(javaType(kind)) + case CONSTANT(const) => genConstant(jcode, const) - case LOAD_LOCAL(local) => - jcode.emitLOAD(indexOf(local), javaType(local.kind)) + case LOAD_ARRAY_ITEM(kind) => + if(kind.isRefOrArrayType) { jcode.emitAALOAD() } + else { + (kind: @unchecked) match { + case UNIT => throw new IllegalArgumentException("invalid type for aload " + kind) + case BOOL | BYTE => jcode.emitBALOAD() + case SHORT => jcode.emitSALOAD() + case CHAR => jcode.emitCALOAD() + case INT => jcode.emitIALOAD() + case LONG => jcode.emitLALOAD() + case FLOAT => jcode.emitFALOAD() + case DOUBLE => jcode.emitDALOAD() + } + } - case lf @ LOAD_FIELD(field, isStatic) => - var owner = javaName(lf.hostClass) - debuglog("LOAD_FIELD with owner: " + owner + - " flags: " + Flags.flagsToString(field.owner.flags)) - if (isStatic) - jcode.emitGETSTATIC(owner, - javaName(field), - javaType(field)) - else - jcode.emitGETFIELD(owner, - javaName(field), - javaType(field)) - - case LOAD_MODULE(module) => -// assert(module.isModule, "Expected module: " + module) - debuglog("generating LOAD_MODULE for: " + module + " flags: " + - Flags.flagsToString(module.flags)); - if (clasz.symbol == module.moduleClass && jmethod.getName() != nme.readResolve.toString) - jcode.emitALOAD_0() - else - jcode.emitGETSTATIC(javaName(module) /* + "$" */ , - nme.MODULE_INSTANCE_FIELD.toString, - javaType(module)) - - case STORE_ARRAY_ITEM(kind) => - jcode emitASTORE javaType(kind) - - case STORE_LOCAL(local) => - jcode.emitSTORE(indexOf(local), javaType(local.kind)) - - case STORE_THIS(_) => - // this only works for impl classes because the self parameter comes first - // in the method signature. If that changes, this code has to be revisited. - jcode.emitASTORE_0() - - case STORE_FIELD(field, isStatic) => - val owner = javaName(field.owner) - if (isStatic) - jcode.emitPUTSTATIC(owner, - javaName(field), - javaType(field)) - else - jcode.emitPUTFIELD(owner, - javaName(field), - javaType(field)) - - case CALL_PRIMITIVE(primitive) => - genPrimitive(primitive, instr.pos) - - /** Special handling to access native Array.clone() */ - case call @ CALL_METHOD(definitions.Array_clone, Dynamic) => - val target: String = javaType(call.targetTypeKind).getSignature() - jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType) - - case call @ CALL_METHOD(method, style) => - genCallMethod(call) - - case BOX(kind) => - val boxedType = definitions.boxedClass(kind.toType.typeSymbol) - val mtype = new JMethodType(javaType(boxedType), Array(javaType(kind))) - jcode.emitINVOKESTATIC(BoxesRunTime, "boxTo" + boxedType.decodedName, mtype) - - case UNBOX(kind) => - val mtype = new JMethodType(javaType(kind), Array(JAVA_LANG_OBJECT)) - jcode.emitINVOKESTATIC(BoxesRunTime, "unboxTo" + kind.toType.typeSymbol.decodedName, mtype) - - case NEW(REFERENCE(cls)) => - val className = javaName(cls) - jcode emitNEW className - - case CREATE_ARRAY(elem, 1) => elem match { - case REFERENCE(_) | ARRAY(_) => - jcode emitANEWARRAY javaType(elem).asInstanceOf[JReferenceType] - case _ => - jcode emitNEWARRAY javaType(elem) - } + case LOAD_LOCAL(local) => jcode.emitLOAD(indexOf(local), javaType(local.kind)) + + case lf @ LOAD_FIELD(field, isStatic) => + var owner = javaName(lf.hostClass) + debuglog("LOAD_FIELD with owner: " + owner + + " flags: " + Flags.flagsToString(field.owner.flags)) + val fieldJName = javaName(field) + val fieldJType = javaType(field) + if (isStatic) jcode.emitGETSTATIC(owner, fieldJName, fieldJType) + else jcode.emitGETFIELD( owner, fieldJName, fieldJType) + + case LOAD_MODULE(module) => + // assert(module.isModule, "Expected module: " + module) + debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags)); + if (clasz.symbol == module.moduleClass && jmethod.getName() != nme.readResolve.toString) + jcode.emitALOAD_0() + else + jcode.emitGETSTATIC(javaName(module) /* + "$" */ , + nme.MODULE_INSTANCE_FIELD.toString, + javaType(module)) + + case STORE_ARRAY_ITEM(kind) => + if(kind.isRefOrArrayType) { jcode.emitAASTORE() } + else { + (kind: @unchecked) match { + case UNIT => throw new IllegalArgumentException("invalid type for astore " + kind) + case BOOL | BYTE => jcode.emitBASTORE() + case SHORT => jcode.emitSASTORE() + case CHAR => jcode.emitCASTORE() + case INT => jcode.emitIASTORE() + case LONG => jcode.emitLASTORE() + case FLOAT => jcode.emitFASTORE() + case DOUBLE => jcode.emitDASTORE() + } + } - case CREATE_ARRAY(elem, dims) => - jcode.emitMULTIANEWARRAY(javaType(ArrayN(elem, dims)).asInstanceOf[JReferenceType], dims) + case STORE_LOCAL(local) => + jcode.emitSTORE(indexOf(local), javaType(local.kind)) - case IS_INSTANCE(tpe) => - tpe match { - case REFERENCE(cls) => - jcode emitINSTANCEOF new JObjectType(javaName(cls)) - case ARRAY(elem) => - jcode emitINSTANCEOF new JArrayType(javaType(elem)) - case _ => - abort("Unknown reference type in IS_INSTANCE: " + tpe) - } + case STORE_THIS(_) => + // this only works for impl classes because the self parameter comes first + // in the method signature. If that changes, this code has to be revisited. + jcode.emitASTORE_0() - case CHECK_CAST(tpe) => - tpe match { - case REFERENCE(cls) => - // No need to checkcast for Objects - if (cls != ObjectClass) - jcode emitCHECKCAST new JObjectType(javaName(cls)) - case ARRAY(elem) => - jcode emitCHECKCAST new JArrayType(javaType(elem)) - case _ => - abort("Unknown reference type in IS_INSTANCE: " + tpe) - } + case STORE_FIELD(field, isStatic) => + val owner = javaName(field.owner) + val fieldJName = javaName(field) + val fieldJType = javaType(field) + if (isStatic) jcode.emitPUTSTATIC(owner, fieldJName, fieldJType) + else jcode.emitPUTFIELD( owner, fieldJName, fieldJType) - case SWITCH(tags, branches) => - val tagArray = new Array[Array[Int]](tags.length) - var caze = tags - var i = 0 + case CALL_PRIMITIVE(primitive) => genPrimitive(primitive, instr.pos) - while (i < tagArray.length) { - tagArray(i) = new Array[Int](caze.head.length) - caze.head.copyToArray(tagArray(i), 0) - i += 1 - caze = caze.tail - } - val branchArray = jcode.newLabels(tagArray.length) - i = 0 - while (i < branchArray.length) { - branchArray(i) = labels(branches(i)) - i += 1 - } - debuglog("Emitting SWITCH:\ntags: " + tags + "\nbranches: " + branches) - jcode.emitSWITCH(tagArray, - branchArray, - labels(branches.last), - MIN_SWITCH_DENSITY) - () - - case JUMP(whereto) => - if (nextBlock != whereto) - jcode.emitGOTO_maybe_W(labels(whereto), false) // default to short jumps - - case CJUMP(success, failure, cond, kind) => - kind match { - case BOOL | BYTE | CHAR | SHORT | INT => + /** Special handling to access native Array.clone() */ + case call @ CALL_METHOD(definitions.Array_clone, Dynamic) => + val target: String = javaType(call.targetTypeKind).getSignature() + jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType) + + case call @ CALL_METHOD(method, style) => genCallMethod(call) + + case BOX(kind) => + val Pair(mname, mtype) = jBoxTo(kind) + jcode.emitINVOKESTATIC(BoxesRunTime, mname, mtype) + + case UNBOX(kind) => + val Pair(mname, mtype) = jUnboxTo(kind) + jcode.emitINVOKESTATIC(BoxesRunTime, mname, mtype) + + case NEW(REFERENCE(cls)) => + val className = javaName(cls) + jcode emitNEW className + + case CREATE_ARRAY(elem, 1) => + if(elem.isRefOrArrayType) { jcode emitANEWARRAY javaType(elem).asInstanceOf[JReferenceType] } + else { jcode emitNEWARRAY javaType(elem) } + + case CREATE_ARRAY(elem, dims) => + jcode.emitMULTIANEWARRAY(javaType(ArrayN(elem, dims)).asInstanceOf[JReferenceType], dims) + + case IS_INSTANCE(tpe) => + tpe match { + case REFERENCE(cls) => jcode emitINSTANCEOF new JObjectType(javaName(cls)) + case ARRAY(elem) => jcode emitINSTANCEOF new JArrayType(javaType(elem)) + case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) + } + + case CHECK_CAST(tpe) => + tpe match { + case REFERENCE(cls) => if (cls != ObjectClass) { jcode emitCHECKCAST new JObjectType(javaName(cls)) } // No need to checkcast for Objects + case ARRAY(elem) => jcode emitCHECKCAST new JArrayType(javaType(elem)) + case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) + } + + case SWITCH(tags, branches) => + val tagArray = new Array[Array[Int]](tags.length) + var caze = tags + var i = 0 + + while (i < tagArray.length) { + tagArray(i) = new Array[Int](caze.head.length) + caze.head.copyToArray(tagArray(i), 0) + i += 1 + caze = caze.tail + } + val branchArray = jcode.newLabels(tagArray.length) + i = 0 + while (i < branchArray.length) { + branchArray(i) = labels(branches(i)) + i += 1 + } + debuglog("Emitting SWITCH:\ntags: " + tags + "\nbranches: " + branches) + jcode.emitSWITCH(tagArray, + branchArray, + labels(branches.last), + MIN_SWITCH_DENSITY) + () + + case JUMP(whereto) => + if (nextBlock != whereto) + jcode.emitGOTO_maybe_W(labels(whereto), false) // default to short jumps + + case CJUMP(success, failure, cond, kind) => + if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT if (nextBlock == success) { - jcode.emitIF_ICMP(conds(negate(cond)), labels(failure)) + jcode.emitIF_ICMP(conds(cond.negate()), labels(failure)) // .. and fall through to success label } else { jcode.emitIF_ICMP(conds(cond), labels(success)) if (nextBlock != failure) jcode.emitGOTO_maybe_W(labels(failure), false) } - - case REFERENCE(_) | ARRAY(_) => + } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) if (nextBlock == success) { - jcode.emitIF_ACMP(conds(negate(cond)), labels(failure)) + jcode.emitIF_ACMP(conds(cond.negate()), labels(failure)) // .. and fall through to success label } else { jcode.emitIF_ACMP(conds(cond), labels(success)) if (nextBlock != failure) jcode.emitGOTO_maybe_W(labels(failure), false) } - - case _ => + } else { (kind: @unchecked) match { case LONG => jcode.emitLCMP() case FLOAT => @@ -1445,38 +1483,32 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with else jcode.emitDCMPL() } if (nextBlock == success) { - jcode.emitIF(conds(negate(cond)), labels(failure)) + jcode.emitIF(conds(cond.negate()), labels(failure)) // .. and fall through to success label } else { jcode.emitIF(conds(cond), labels(success)); if (nextBlock != failure) jcode.emitGOTO_maybe_W(labels(failure), false) } - } + } - case CZJUMP(success, failure, cond, kind) => - kind match { - case BOOL | BYTE | CHAR | SHORT | INT => + case CZJUMP(success, failure, cond, kind) => + if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT if (nextBlock == success) { - jcode.emitIF(conds(negate(cond)), labels(failure)) + jcode.emitIF(conds(cond.negate()), labels(failure)) } else { jcode.emitIF(conds(cond), labels(success)) if (nextBlock != failure) jcode.emitGOTO_maybe_W(labels(failure), false) } - - case REFERENCE(_) | ARRAY(_) => + } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) val Success = success val Failure = failure (cond, nextBlock) match { - case (EQ, Success) => - jcode emitIFNONNULL labels(failure) - case (NE, Failure) => - jcode emitIFNONNULL labels(success) - case (EQ, Failure) => - jcode emitIFNULL labels(success) - case (NE, Success) => - jcode emitIFNULL labels(failure) + case (EQ, Success) => jcode emitIFNONNULL labels(failure) + case (NE, Failure) => jcode emitIFNONNULL labels(success) + case (EQ, Failure) => jcode emitIFNULL labels(success) + case (NE, Success) => jcode emitIFNULL labels(failure) case (EQ, _) => jcode emitIFNULL labels(success) jcode.emitGOTO_maybe_W(labels(failure), false) @@ -1484,11 +1516,11 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with jcode emitIFNONNULL labels(success) jcode.emitGOTO_maybe_W(labels(failure), false) } - - case _ => + } else { (kind: @unchecked) match { case LONG => - jcode.emitLCONST_0(); jcode.emitLCMP() + jcode.emitLCONST_0() + jcode.emitLCMP() case FLOAT => jcode.emitFCONST_0() if (cond == LT || cond == LE) jcode.emitFCMPG() @@ -1499,263 +1531,254 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with else jcode.emitDCMPL() } if (nextBlock == success) { - jcode.emitIF(conds(negate(cond)), labels(failure)) + jcode.emitIF(conds(cond.negate()), labels(failure)) } else { jcode.emitIF(conds(cond), labels(success)) if (nextBlock != failure) jcode.emitGOTO_maybe_W(labels(failure), false) } - } + } - case RETURN(kind) => - jcode emitRETURN javaType(kind) + case RETURN(kind) => jcode emitRETURN javaType(kind) - case THROW(_) => - jcode.emitATHROW() + case THROW(_) => jcode.emitATHROW() - case DROP(kind) => - kind match { - case LONG | DOUBLE => jcode.emitPOP2() - case _ => jcode.emitPOP() - } + case DROP(kind) => + if(kind.isWideType) jcode.emitPOP2() + else jcode.emitPOP() - case DUP(kind) => - kind match { - case LONG | DOUBLE => jcode.emitDUP2() - case _ => jcode.emitDUP() - } + case DUP(kind) => + if(kind.isWideType) jcode.emitDUP2() + else jcode.emitDUP() - case MONITOR_ENTER() => - jcode.emitMONITORENTER() + case MONITOR_ENTER() => jcode.emitMONITORENTER() - case MONITOR_EXIT() => - jcode.emitMONITOREXIT() + case MONITOR_EXIT() => jcode.emitMONITOREXIT() - case SCOPE_ENTER(lv) => - varsInBlock += lv - lv.start = jcode.getPC() + case SCOPE_ENTER(lv) => + varsInBlock += lv + lv.start = jcode.getPC() - case SCOPE_EXIT(lv) => - if (varsInBlock(lv)) { - lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges - varsInBlock -= lv - } - else if (b.varsInScope(lv)) { - lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges - b.varsInScope -= lv - } - else dumpMethodAndAbort(method, "Illegal local var nesting") + case SCOPE_EXIT(lv) => + if (varsInBlock(lv)) { + lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges + varsInBlock -= lv + } + else if (b.varsInScope(lv)) { + lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges + b.varsInScope -= lv + } + else dumpMethodAndAbort(method, "Illegal local var nesting") - case LOAD_EXCEPTION(_) => - () - } + case LOAD_EXCEPTION(_) => + () + } - crtPC = jcode.getPC() + crtPC = jcode.getPC() -// assert(instr.pos.source.isEmpty || instr.pos.source.get == (clasz.cunit.source), "sources don't match") -// val crtLine = instr.pos.line.get(lastLineNr); + // assert(instr.pos.source.isEmpty || instr.pos.source.get == (clasz.cunit.source), "sources don't match") + // val crtLine = instr.pos.line.get(lastLineNr); - val crtLine = try { - if (instr.pos == NoPosition) lastLineNr else (instr.pos).line // check NoPosition to avoid costly exception - } catch { - case _: UnsupportedOperationException => - log("Warning: wrong position in: " + method) - lastLineNr - } + val crtLine = try { + if (instr.pos == NoPosition) lastLineNr else (instr.pos).line // check NoPosition to avoid costly exception + } catch { + case _: UnsupportedOperationException => + log("Warning: wrong position in: " + method) + lastLineNr + } - if (b.lastInstruction == instr) - endPC(b) = jcode.getPC() + if (instr eq lastInstr) { endPC(b) = jcode.getPC() } - //System.err.println("CRTLINE: " + instr.pos + " " + - // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine); + //System.err.println("CRTLINE: " + instr.pos + " " + + // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine); - if (crtPC > lastMappedPC) { - jcode.completeLineNumber(lastMappedPC, crtPC, crtLine) - lastMappedPC = crtPC - lastLineNr = crtLine + if (crtPC > lastMappedPC) { + jcode.completeLineNumber(lastMappedPC, crtPC, crtLine) + lastMappedPC = crtPC + lastLineNr = crtLine + } } - } - // local vars that survived this basic block - for (lv <- varsInBlock) { - lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges - } - for (lv <- b.varsInScope) { - lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges + // local vars that survived this basic block + for (lv <- varsInBlock) { + lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges + } + for (lv <- b.varsInScope) { + lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges + } } - } - /** - * @param primitive ... - * @param pos ... - */ - def genPrimitive(primitive: Primitive, pos: Position) { - primitive match { - case Negation(kind) => - kind match { - case BOOL | BYTE | CHAR | SHORT | INT => - jcode.emitINEG() - case LONG => jcode.emitLNEG() - case FLOAT => jcode.emitFNEG() - case DOUBLE => jcode.emitDNEG() - case _ => abort("Impossible to negate a " + kind) - } - - case Arithmetic(op, kind) => - op match { - case ADD => jcode.emitADD(javaType(kind)) - case SUB => - (kind: @unchecked) match { - case BOOL | BYTE | CHAR | SHORT | INT => - jcode.emitISUB() - case LONG => jcode.emitLSUB() - case FLOAT => jcode.emitFSUB() - case DOUBLE => jcode.emitDSUB() + /** + * @param primitive ... + * @param pos ... + */ + def genPrimitive(primitive: Primitive, pos: Position) { + primitive match { + case Negation(kind) => + if(kind.isIntSizedType) { jcode.emitINEG() } + else { + kind match { + case LONG => jcode.emitLNEG() + case FLOAT => jcode.emitFNEG() + case DOUBLE => jcode.emitDNEG() + case _ => abort("Impossible to negate a " + kind) } + } - case MUL => - (kind: @unchecked) match { - case BOOL | BYTE | CHAR | SHORT | INT => - jcode.emitIMUL() - case LONG => jcode.emitLMUL() - case FLOAT => jcode.emitFMUL() - case DOUBLE => jcode.emitDMUL() - } + case Arithmetic(op, kind) => + op match { + case ADD => + if(kind.isIntSizedType) { jcode.emitIADD() } + else { + (kind: @unchecked) match { + case LONG => jcode.emitLADD() + case FLOAT => jcode.emitFADD() + case DOUBLE => jcode.emitDADD() + } + } - case DIV => - (kind: @unchecked) match { - case BOOL | BYTE | CHAR | SHORT | INT => - jcode.emitIDIV() - case LONG => jcode.emitLDIV() - case FLOAT => jcode.emitFDIV() - case DOUBLE => jcode.emitDDIV() - } + case SUB => + if(kind.isIntSizedType) { jcode.emitISUB() } + else { + (kind: @unchecked) match { + case LONG => jcode.emitLSUB() + case FLOAT => jcode.emitFSUB() + case DOUBLE => jcode.emitDSUB() + } + } - case REM => - (kind: @unchecked) match { - case BOOL | BYTE | CHAR | SHORT | INT => - jcode.emitIREM() - case LONG => jcode.emitLREM() - case FLOAT => jcode.emitFREM() - case DOUBLE => jcode.emitDREM() - } + case MUL => + if(kind.isIntSizedType) { jcode.emitIMUL() } + else { + (kind: @unchecked) match { + case LONG => jcode.emitLMUL() + case FLOAT => jcode.emitFMUL() + case DOUBLE => jcode.emitDMUL() + } + } - case NOT => - kind match { - case BOOL | BYTE | CHAR | SHORT | INT => + case DIV => + if(kind.isIntSizedType) { jcode.emitIDIV() } + else { + (kind: @unchecked) match { + case LONG => jcode.emitLDIV() + case FLOAT => jcode.emitFDIV() + case DOUBLE => jcode.emitDDIV() + } + } + + case REM => + if(kind.isIntSizedType) { jcode.emitIREM() } + else { + (kind: @unchecked) match { + case LONG => jcode.emitLREM() + case FLOAT => jcode.emitFREM() + case DOUBLE => jcode.emitDREM() + } + } + + case NOT => + if(kind.isIntSizedType) { jcode.emitPUSH(-1) jcode.emitIXOR() - case LONG => + } else if(kind == LONG) { jcode.emitPUSH(-1l) jcode.emitLXOR() - case _ => + } else { abort("Impossible to negate an " + kind) - } - - case _ => - abort("Unknown arithmetic primitive " + primitive) - } - - case Logical(op, kind) => (op, kind) match { - case (AND, LONG) => - jcode.emitLAND() - case (AND, INT) => - jcode.emitIAND() - case (AND, _) => - jcode.emitIAND() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - - case (OR, LONG) => - jcode.emitLOR() - case (OR, INT) => - jcode.emitIOR() - case (OR, _) => - jcode.emitIOR() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - - case (XOR, LONG) => - jcode.emitLXOR() - case (XOR, INT) => - jcode.emitIXOR() - case (XOR, _) => - jcode.emitIXOR() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - } - - case Shift(op, kind) => (op, kind) match { - case (LSL, LONG) => - jcode.emitLSHL() - case (LSL, INT) => - jcode.emitISHL() - case (LSL, _) => - jcode.emitISHL() - jcode.emitT2T(javaType(INT), javaType(kind)) - - case (ASR, LONG) => - jcode.emitLSHR() - case (ASR, INT) => - jcode.emitISHR() - case (ASR, _) => - jcode.emitISHR() - jcode.emitT2T(javaType(INT), javaType(kind)) - - case (LSR, LONG) => - jcode.emitLUSHR() - case (LSR, INT) => - jcode.emitIUSHR() - case (LSR, _) => - jcode.emitIUSHR() - jcode.emitT2T(javaType(INT), javaType(kind)) - } + } - case Comparison(op, kind) => ((op, kind): @unchecked) match { - case (CMP, LONG) => jcode.emitLCMP() - case (CMPL, FLOAT) => jcode.emitFCMPL() - case (CMPG, FLOAT) => jcode.emitFCMPG() - case (CMPL, DOUBLE) => jcode.emitDCMPL() - case (CMPG, DOUBLE) => jcode.emitDCMPL() - } + case _ => + abort("Unknown arithmetic primitive " + primitive) + } - case Conversion(src, dst) => - debuglog("Converting from: " + src + " to: " + dst) - if (dst == BOOL) { - println("Illegal conversion at: " + clasz + - " at: " + pos.source + ":" + pos.line) - } else - jcode.emitT2T(javaType(src), javaType(dst)) + case Logical(op, kind) => (op, kind) match { + case (AND, LONG) => jcode.emitLAND() + case (AND, INT) => jcode.emitIAND() + case (AND, _) => + jcode.emitIAND() + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + + case (OR, LONG) => jcode.emitLOR() + case (OR, INT) => jcode.emitIOR() + case (OR, _) => + jcode.emitIOR() + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + + case (XOR, LONG) => jcode.emitLXOR() + case (XOR, INT) => jcode.emitIXOR() + case (XOR, _) => + jcode.emitIXOR() + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + } - case ArrayLength(_) => - jcode.emitARRAYLENGTH() + case Shift(op, kind) => (op, kind) match { + case (LSL, LONG) => jcode.emitLSHL() + case (LSL, INT) => jcode.emitISHL() + case (LSL, _) => + jcode.emitISHL() + jcode.emitT2T(javaType(INT), javaType(kind)) + + case (ASR, LONG) => jcode.emitLSHR() + case (ASR, INT) => jcode.emitISHR() + case (ASR, _) => + jcode.emitISHR() + jcode.emitT2T(javaType(INT), javaType(kind)) + + case (LSR, LONG) => jcode.emitLUSHR() + case (LSR, INT) => jcode.emitIUSHR() + case (LSR, _) => + jcode.emitIUSHR() + jcode.emitT2T(javaType(INT), javaType(kind)) + } - case StartConcat => - jcode emitNEW StringBuilderClassName - jcode.emitDUP() - jcode.emitINVOKESPECIAL(StringBuilderClassName, - JMethod.INSTANCE_CONSTRUCTOR_NAME, - JMethodType.ARGLESS_VOID_FUNCTION) - - case StringConcat(el) => - val jtype = el match { - case REFERENCE(_) | ARRAY(_) => JAVA_LANG_OBJECT - case _ => javaType(el) + case Comparison(op, kind) => ((op, kind): @unchecked) match { + case (CMP, LONG) => jcode.emitLCMP() + case (CMPL, FLOAT) => jcode.emitFCMPL() + case (CMPG, FLOAT) => jcode.emitFCMPG() + case (CMPL, DOUBLE) => jcode.emitDCMPL() + case (CMPG, DOUBLE) => jcode.emitDCMPL() } - jcode.emitINVOKEVIRTUAL(StringBuilderClassName, - "append", - new JMethodType(StringBuilderType, - Array(jtype))) - case EndConcat => - jcode.emitINVOKEVIRTUAL(StringBuilderClassName, - "toString", - toStringType) - case _ => - abort("Unimplemented primitive " + primitive) + case Conversion(src, dst) => + debuglog("Converting from: " + src + " to: " + dst) + if (dst == BOOL) { + println("Illegal conversion at: " + clasz + " at: " + pos.source + ":" + pos.line) + } else + jcode.emitT2T(javaType(src), javaType(dst)) + + case ArrayLength(_) => + jcode.emitARRAYLENGTH() + + case StartConcat => + jcode emitNEW StringBuilderClassName + jcode.emitDUP() + jcode.emitINVOKESPECIAL(StringBuilderClassName, + JMethod.INSTANCE_CONSTRUCTOR_NAME, + JMethodType.ARGLESS_VOID_FUNCTION) + + case StringConcat(el) => + val jtype = el match { + case REFERENCE(_) | ARRAY(_) => JAVA_LANG_OBJECT + case _ => javaType(el) + } + jcode.emitINVOKEVIRTUAL(StringBuilderClassName, + "append", + new JMethodType(StringBuilderType, + Array(jtype))) + case EndConcat => + jcode.emitINVOKEVIRTUAL(StringBuilderClassName, + "toString", + toStringType) + + case _ => + abort("Unimplemented primitive " + primitive) + } } - } // genCode starts here genBlocks(linearization) @@ -1825,10 +1848,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe)) - def sizeOf(k: TypeKind): Int = k match { - case DOUBLE | LONG => 2 - case _ => 1 - } + def sizeOf(k: TypeKind): Int = if(k.isWideType) 2 else 1 def indexOf(m: IMethod, sym: Symbol): Int = { val Some(local) = m lookupLocal sym @@ -1845,9 +1865,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with * method. *Does not assume the parameters come first!* */ def computeLocalVarsIndex(m: IMethod) { - var idx = 1 - if (m.symbol.isStaticMember) - idx = 0; + var idx = if (m.symbol.isStaticMember) 0 else 1; for (l <- m.params) { debuglog("Index value for " + l + "{" + l.## + "}: " + idx) @@ -1906,6 +1924,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with ((sym.rawflags & (Flags.FINAL | Flags.MODULE)) != 0) && !sym.enclClass.isInterface && !sym.isClassConstructor + && !sym.isMutable // fix for SI-3569, it is too broad? ) mkFlags( @@ -1914,9 +1933,10 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with if (sym.isInterface) ACC_INTERFACE else 0, if (finalFlag) ACC_FINAL else 0, if (sym.isStaticMember) ACC_STATIC else 0, - if (sym.isBridge) ACC_BRIDGE else 0, + if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0, if (sym.isClass && !sym.isInterface) ACC_SUPER else 0, - if (sym.isVarargsMethod) ACC_VARARGS else 0 + if (sym.isVarargsMethod) ACC_VARARGS else 0, + if (sym.hasFlag(Flags.SYNCHRONIZED)) JAVA_ACC_SYNCHRONIZED else 0 ) } def javaFieldFlags(sym: Symbol) = { @@ -1928,9 +1948,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with } def isTopLevelModule(sym: Symbol): Boolean = - atPhase (currentRun.picklerPhase.next) { - sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass - } + afterPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } def isStaticModule(sym: Symbol): Boolean = { sym.isModuleClass && !sym.isImplClass && !sym.isLifted diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala index 93d3d19ac8..b74981b999 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala @@ -54,14 +54,6 @@ trait GenJVMUtil { LE -> JExtendedCode.COND_LE, GE -> JExtendedCode.COND_GE ) - val negate = immutable.Map[TestOp, TestOp]( - EQ -> NE, - NE -> EQ, - LT -> GE, - GT -> LE, - LE -> GT, - GE -> LT - ) /** Specialized array conversion to prevent calling * java.lang.reflect.Array.newInstance via TraversableOnce.toArray @@ -85,12 +77,10 @@ trait GenJVMUtil { */ def javaName(sym: Symbol): String = javaNameCache.getOrElseUpdate(sym, { - sym.name.newName( - if (sym.isClass || (sym.isModule && !sym.isMethod)) - sym.javaBinaryName - else - sym.javaSimpleName - ) + if (sym.isClass || (sym.isModule && !sym.isMethod)) + sym.javaBinaryName + else + sym.javaSimpleName }).toString def javaType(t: TypeKind): JType = (t: @unchecked) match { diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index d2e54ff3f1..2fb615f893 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -1125,7 +1125,7 @@ abstract class GenMSIL extends SubComponent { } // method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType - val (isDelegateView, paramType, resType) = atPhase(currentRun.typerPhase) { + val (isDelegateView, paramType, resType) = beforeTyper { msym.tpe match { case MethodType(params, resultType) if (params.length == 1 && msym.name == nme.view_) => @@ -1954,7 +1954,7 @@ abstract class GenMSIL extends SubComponent { } // createClassMembers0 private def isTopLevelModule(sym: Symbol): Boolean = - atPhase (currentRun.refchecksPhase) { + beforeRefchecks { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index e8abee7d06..ff45bb8fd1 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -108,7 +108,7 @@ abstract class ClosureElimination extends SubComponent { case LOAD_LOCAL(l) if info.bindings isDefinedAt LocalVar(l) => val t = info.getBinding(l) t match { - case Deref(LocalVar(_)) | Deref(This) | Const(_) => + case Deref(This) | Const(_) => bb.replaceInstruction(i, valueToInstruction(t)); log("replaced " + i + " with " + t) diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 5fc7329955..95c371fa8b 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -225,9 +225,9 @@ abstract class DeadCodeElimination extends SubComponent { m foreachBlock { bb => assert(bb.closed, "Open block in computeCompensations") - for ((i, idx) <- bb.toList.zipWithIndex) { + foreachWithIndex(bb.toList) { (i, idx) => if (!useful(bb)(idx)) { - for ((consumedType, depth) <- i.consumedTypes.reverse.zipWithIndex) { + foreachWithIndex(i.consumedTypes.reverse) { (consumedType, depth) => log("Finding definitions of: " + i + "\n\t" + consumedType + " at depth: " + depth) val defs = rdef.findDefs(bb, idx, 1, depth) for (d <- defs) { diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 66f802f74f..a734b2b92b 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -38,6 +38,33 @@ abstract class Inliners extends SubComponent { res } + /** Look up implementation of method 'sym in 'clazz'. + */ + def lookupImplFor(sym: Symbol, clazz: Symbol): Symbol = { + // TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info) + def needsLookup = ( + (clazz != NoSymbol) + && (clazz != sym.owner) + && !sym.isEffectivelyFinal + && clazz.isEffectivelyFinal + ) + def lookup(clazz: Symbol): Symbol = { + // println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner) + if (sym.owner == clazz || isBottomType(clazz)) sym + else sym.overridingSymbol(clazz) match { + case NoSymbol => if (sym.owner.isTrait) sym else lookup(clazz.superClass) + case imp => imp + } + } + if (needsLookup) { + val concreteMethod = lookup(clazz) + debuglog("\tlooked up method: " + concreteMethod.fullName) + + concreteMethod + } + else sym + } + /* A warning threshold */ private final val MAX_INLINE_MILLIS = 2000 @@ -67,8 +94,7 @@ abstract class Inliners extends SubComponent { try { super.run() } finally { - inliner.NonPublicRefs.usesNonPublics.clear() - inliner.recentTFAs.clear + inliner.clearCaches() } } } @@ -80,6 +106,21 @@ abstract class Inliners extends SubComponent { def isClosureClass(cls: Symbol): Boolean = cls.isFinal && cls.isSynthetic && !cls.isModuleClass && cls.isAnonymousFunction + /* + TODO now that Inliner runs faster we could consider additional "monadic methods" (in the limit, all those taking a closure as last arg) + Any "monadic method" occurring in a given caller C that is not `isMonadicMethod()` will prevent CloseElim from eliminating + any anonymous-closure-class any whose instances are given as argument to C invocations. + */ + def isMonadicMethod(sym: Symbol) = { + nme.unspecializedName(sym.name) match { + case nme.foreach | nme.filter | nme.withFilter | nme.map | nme.flatMap => true + case _ => false + } + } + + def hasInline(sym: Symbol) = sym hasAnnotation ScalaInlineClass + def hasNoInline(sym: Symbol) = sym hasAnnotation ScalaNoInlineClass + /** * Simple inliner. */ @@ -92,9 +133,6 @@ abstract class Inliners extends SubComponent { } import NonPublicRefs._ - private def hasInline(sym: Symbol) = sym hasAnnotation ScalaInlineClass - private def hasNoInline(sym: Symbol) = sym hasAnnotation ScalaNoInlineClass - /** The current iclass */ private var currentIClazz: IClass = _ private def warn(pos: Position, msg: String) = currentIClazz.cunit.warning(pos, msg) @@ -121,6 +159,21 @@ abstract class Inliners extends SubComponent { (hasRETURN, a) } + def clearCaches() { + // methods + NonPublicRefs.usesNonPublics.clear() + recentTFAs.clear + tfa.knownUnsafe.clear() + tfa.knownSafe.clear() + tfa.knownNever.clear() + // basic blocks + tfa.preCandidates.clear() + tfa.relevantBBs.clear() + // callsites + tfa.remainingCALLs.clear() + tfa.isOnWatchlist.clear() + } + def analyzeClass(cls: IClass): Unit = if (settings.inline.value) { debuglog("Analyzing " + cls) @@ -142,7 +195,38 @@ abstract class Inliners extends SubComponent { val splicedBlocks = mutable.Set.empty[BasicBlock] val staleIn = mutable.Set.empty[BasicBlock] + /** + * A transformation local to the body of the argument. + * An linining decision consists in replacing a callsite with the body of the callee. + * Please notice that, because `analyzeMethod()` itself may modify a method body, + * the particular callee bodies that end up being inlined depend on the particular order in which methods are visited + * (no topological ordering over the call-graph is attempted). + * + * Making an inlining decision requires type-flow information for both caller and callee. + * Regarding the caller, such information is needed only for basic blocks containing inlining candidates + * (and their transitive predecessors). This observation leads to using a custom type-flow analysis (MTFAGrowable) + * that can be re-inited, i.e. that reuses lattice elements (type-flow information) computed in a previous iteration + * as starting point for faster convergence in a new iteration. + * + * The mechanics of inlining are iterative for a given invocation of `analyzeMethod(m)`, + * thus considering the basic blocks that successful inlining added in a previous iteration: + * + * (1) before the iterations proper start, so-called preinlining is performed. + * Those callsites whose (receiver, concreteMethod) are both known statically + * can be analyzed for inlining before computing a type-flow. Details in `preInline()` + * + * (2) the first iteration computes type-flow information for basic blocks containing inlining candidates + * (and their transitive predecessors), so called `relevantBBs`. + * The ensuing analysis of each candidate (performed by `analyzeInc()`) + * may result in a CFG isomorphic to that of the callee being inserted where the callsite was + * (i.e. a CALL_METHOD instruction is replaced with a single-entry single-exit CFG, which we call "successful inlining"). + * + * (3) following iterations have their relevant basic blocks updated to focus + * on the inlined basic blocks and their successors only. Details in `MTFAGrowable.reinit()` + * */ def analyzeMethod(m: IMethod): Unit = { + // m.normalize + var sizeBeforeInlining = m.code.blockCount var instrBeforeInlining = m.code.instructionCount var retry = false @@ -154,17 +238,53 @@ abstract class Inliners extends SubComponent { val inlinedMethodCount = mutable.HashMap.empty[Symbol, Int] withDefaultValue 0 val caller = new IMethodInfo(m) - var info: tfa.lattice.Elem = null - def analyzeInc(msym: Symbol, i: Instruction, bb: BasicBlock): Boolean = { - var inlined = false - def paramTypes = msym.info.paramTypes - val receiver = (info.stack.types drop paramTypes.length) match { - case Nil => log("analyzeInc(" + msym + "), no type on the stack!") ; NoSymbol - case REFERENCE(s) :: _ => s - case _ => NoSymbol + def preInline(isFirstRound: Boolean): Int = { + val inputBlocks = caller.m.linearizedBlocks() + val callsites: Function1[BasicBlock, List[opcodes.CALL_METHOD]] = { + if(isFirstRound) tfa.conclusives else tfa.knownBeforehand } - val concreteMethod = lookupImplFor(msym, receiver) + inlineWithoutTFA(inputBlocks, callsites) + } + + /** + * Inline straightforward callsites (those that can be inlined without a TFA). + * + * To perform inlining, all we need to know is listed as formal params in `analyzeInc()`: + * - callsite and block containing it + * - actual (ie runtime) class of the receiver + * - actual (ie runtime) method being invoked + * - stack length just before the callsite (to check whether enough arguments have been pushed). + * The assert below lists the conditions under which "no TFA is needed" + * (the statically known receiver and method are both final, thus, at runtime they can't be any others than those). + * + */ + def inlineWithoutTFA(inputBlocks: Traversable[BasicBlock], callsites: Function1[BasicBlock, List[opcodes.CALL_METHOD]]): Int = { + var inlineCount = 0 + import scala.util.control.Breaks._ + for(x <- inputBlocks; val easyCake = callsites(x); if easyCake.nonEmpty) { + breakable { + for(ocm <- easyCake) { + assert(ocm.method.isEffectivelyFinal && ocm.method.owner.isEffectivelyFinal) + if(analyzeInc(ocm, x, ocm.method.owner, -1, ocm.method)) { + inlineCount += 1 + break + } + } + } + } + + inlineCount + } + + /** + Decides whether it's feasible and desirable to inline the body of the method given by `concreteMethod` + at the program point given by `i` (a callsite). The boolean result indicates whether inlining was performed. + + */ + def analyzeInc(i: CALL_METHOD, bb: BasicBlock, receiver: Symbol, stackLength: Int, concreteMethod: Symbol): Boolean = { + var inlined = false + val msym = i.method def warnNoInline(reason: String) = { if (hasInline(msym) && !caller.isBridge) @@ -209,7 +329,7 @@ abstract class Inliners extends SubComponent { val inc = new IMethodInfo(callee) val pair = new CallerCalleeInfo(caller, inc, fresh, inlinedMethodCount) - if (pair isStampedForInlining info.stack) { + if (pair isStampedForInlining stackLength) { retry = true inlined = true if (isCountable) @@ -228,9 +348,9 @@ abstract class Inliners extends SubComponent { } else { if (settings.debug.value) - pair logFailure info.stack + pair logFailure stackLength - warnNoInline(pair failureReason info.stack) + warnNoInline(pair failureReason stackLength) } case None => warnNoInline("bytecode was not available") @@ -241,38 +361,96 @@ abstract class Inliners extends SubComponent { if (!isAvailable) "bytecode was not available" else "it can be overridden" ) + inlined } - import scala.util.control.Breaks._ + /* Pre-inlining consists in invoking the usual inlining subroutine with (receiver class, concrete method) pairs as input + * where both method and receiver are final, which implies that the receiver computed via TFA will always match `concreteMethod.owner`. + * + * As with any invocation of `analyzeInc()` the inlining outcome is based on heuristics which favor inlining an isMonadicMethod before other methods. + * That's why preInline() is invoked twice: any inlinings downplayed by the heuristics during the first round get an opportunity to rank higher during the second. + * + * As a whole, both `preInline()` invocations amount to priming the inlining process, + * so that the first TFA run afterwards is able to gain more information as compared to a cold-start. + */ + val totalPreInlines = { + val firstRound = preInline(true) + if(firstRound == 0) 0 else (firstRound + preInline(false)) + } + staleOut.clear() + splicedBlocks.clear() + staleIn.clear() + do { retry = false log("Analyzing " + m + " count " + count + " with " + caller.length + " blocks") + + /* it's important not to inline in unreachable basic blocks. linearizedBlocks() returns only reachable ones. */ + tfa.callerLin = caller.m.linearizedBlocks() + /* TODO Do we want to perform inlining in non-finally exception handlers? + * Seems counterproductive (the larger the method the less likely it will be JITed). + * The alternative above would be `linearizer.linearizeAt(caller.m, caller.m.startBlock)`. + * See also comment on the same topic in TypeFlowAnalysis. */ + tfa.reinit(m, staleOut.toList, splicedBlocks, staleIn) tfa.run staleOut.clear() splicedBlocks.clear() staleIn.clear() - caller.m.linearizedBlocks() foreach { bb => - info = tfa in bb - + import scala.util.control.Breaks._ + for(bb <- tfa.callerLin; if tfa.preCandidates(bb)) { + val cms = bb.toList collect { case cm : CALL_METHOD => cm } breakable { - for (i <- bb) { - i match { - // Dynamic == normal invocations - // Static(true) == calls to private members - case CALL_METHOD(msym, Dynamic | Static(true)) if !msym.isConstructor => - if (analyzeInc(msym, i, bb)) { - break - } - case _ => () + for (cm <- cms; if tfa.remainingCALLs.isDefinedAt(cm)) { + val analysis.CallsiteInfo(_, receiver, stackLength, concreteMethod) = tfa.remainingCALLs(cm) + if (analyzeInc(cm, bb, receiver, stackLength, concreteMethod)) { + break } - info = tfa.interpret(info, i) } } + } + + /* As part of inlining, some instructions are moved to a new block. + * In detail: the instructions moved to a new block originally appeared after a (by now inlined) callsite. + * Their new home is an `afterBlock` created by `doInline()` to that effect. + * Each block in staleIn is one such `afterBlock`. + * + * Some of those instructions may be CALL_METHOD possibly tracked in `remainingCALLs` + * (with an entry still noting the old containing block). However, that causes no problem: + * + * (1) such callsites won't be analyzed for inlining by `analyzeInc()` (*in this iteration*) + * because of the `break` that abandons the original basic block where it was contained. + * + * (2) Additionally, its new containing block won't be visited either (*in this iteration*) + * because the new blocks don't show up in the linearization computed before inlinings started: + * `for(bb <- tfa.callerLin; if tfa.preCandidates(bb)) {` + * + * For a next iteration, the new home of any instructions that have moved + * will be tracked properly in `remainingCALLs` after `MTFAGrowable.reinit()` puts on radar their new homes. + * + */ + if(retry) { + for(afterBlock <- staleIn) { + val justCALLsAfter = afterBlock.toList collect { case c : opcodes.CALL_METHOD => c } + for(ia <- justCALLsAfter) { tfa.remainingCALLs.remove(ia) } + } + } + /* + if(splicedBlocks.nonEmpty) { // TODO explore (saves time but leads to slightly different inlining decisions) + // opportunistically perform straightforward inlinings before the next typeflow round + val savedRetry = retry + val savedStaleOut = staleOut.toSet; staleOut.clear() + val savedStaleIn = staleIn.toSet ; staleIn.clear() + val howmany = inlineWithoutTFA(splicedBlocks, tfa.knownBeforehand) + splicedBlocks ++= staleIn + staleOut.clear(); staleOut ++= savedStaleOut; + staleIn.clear(); staleIn ++= savedStaleIn; + retry = savedRetry } + */ if (tfa.stat) log(m.symbol.fullName + " iterations: " + tfa.iterations + " (size: " + caller.length + ")") @@ -288,15 +466,10 @@ abstract class Inliners extends SubComponent { } } - private def isMonadicMethod(sym: Symbol) = { - nme.unspecializedName(sym.name) match { - case nme.foreach | nme.filter | nme.withFilter | nme.map | nme.flatMap => true - case _ => false - } - } - - private def isHigherOrderMethod(sym: Symbol) = - sym.isMethod && atPhase(currentRun.erasurePhase.prev)(sym.info.paramTypes exists isFunctionType) + private def isHigherOrderMethod(sym: Symbol) = ( + sym.isMethod + && beforeExplicitOuter(sym.info.paramTypes exists isFunctionType) // was "at erasurePhase.prev" + ) /** Should method 'sym' being called in 'receiver' be loaded from disk? */ def shouldLoadImplFor(sym: Symbol, receiver: Symbol): Boolean = { @@ -308,33 +481,6 @@ abstract class Inliners extends SubComponent { res } - /** Look up implementation of method 'sym in 'clazz'. - */ - def lookupImplFor(sym: Symbol, clazz: Symbol): Symbol = { - // TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info) - def needsLookup = ( - (clazz != NoSymbol) - && (clazz != sym.owner) - && !sym.isEffectivelyFinal - && clazz.isEffectivelyFinal - ) - def lookup(clazz: Symbol): Symbol = { - // println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner) - if (sym.owner == clazz || isBottomType(clazz)) sym - else sym.overridingSymbol(clazz) match { - case NoSymbol => if (sym.owner.isTrait) sym else lookup(clazz.superClass) - case imp => imp - } - } - if (needsLookup) { - val concreteMethod = lookup(clazz) - debuglog("\tlooked up method: " + concreteMethod.fullName) - - concreteMethod - } - else sym - } - class IMethodInfo(val m: IMethod) { val sym = m.symbol val name = sym.name @@ -386,10 +532,13 @@ abstract class Inliners extends SubComponent { /** Inline 'inc' into 'caller' at the given block and instruction. * The instruction must be a CALL_METHOD. */ - def doInline(block: BasicBlock, instr: Instruction) { + def doInline(block: BasicBlock, instr: CALL_METHOD) { staleOut += block + tfa.remainingCALLs.remove(instr) // this bookkpeeping is done here and not in MTFAGrowable.reinit due to (1st) convenience and (2nd) necessity. + tfa.isOnWatchlist.remove(instr) // ditto + val targetPos = instr.pos log("Inlining " + inc.m + " in " + caller.m + " at pos: " + posToStr(targetPos)) @@ -403,9 +552,9 @@ abstract class Inliners extends SubComponent { val activeHandlers = caller.handlers filter (_ covered block) /* Map 'original' blocks to the ones inlined in the caller. */ - val inlinedBlock: mutable.Map[BasicBlock, BasicBlock] = new mutable.HashMap + val inlinedBlock = mutable.Map[BasicBlock, BasicBlock]() - val varsInScope: mutable.Set[Local] = mutable.HashSet() ++= block.varsInScope + val varsInScope = mutable.HashSet[Local]() ++= block.varsInScope /** Side effects varsInScope when it sees SCOPE_ENTERs. */ def instrBeforeFilter(i: Instruction): Boolean = { @@ -557,10 +706,11 @@ abstract class Inliners extends SubComponent { if (settings.debug.value) icodes.checkValid(caller.m) } - def isStampedForInlining(stack: TypeStack) = - !sameSymbols && inc.m.hasCode && shouldInline && isSafeToInline(stack) + def isStampedForInlining(stackLength: Int) = + !sameSymbols && inc.m.hasCode && shouldInline && + isSafeToInline(stackLength) // `isSafeToInline()` must be invoked last in this AND expr bc it mutates the `knownSafe` and `knownUnsafe` maps for good. - def logFailure(stack: TypeStack) = log( + def logFailure(stackLength: Int) = log( """|inline failed for %s: | pair.sameSymbols: %s | inc.numInlined < 2: %s @@ -569,13 +719,14 @@ abstract class Inliners extends SubComponent { | shouldInline: %s """.stripMargin.format( inc.m, sameSymbols, inlinedMethodCount(inc.sym) < 2, - inc.m.hasCode, isSafeToInline(stack), shouldInline + inc.m.hasCode, isSafeToInline(stackLength), shouldInline ) ) - def failureReason(stack: TypeStack) = + def failureReason(stackLength: Int) = if (!inc.m.hasCode) "bytecode was unavailable" - else if (!isSafeToInline(stack)) "it is unsafe (target may reference private fields)" + else if (inc.m.symbol.hasFlag(Flags.SYNCHRONIZED)) "method is synchronized" + else if (!isSafeToInline(stackLength)) "it is unsafe (target may reference private fields)" else "of a bug (run with -Ylog:inline -Ydebug for more information)" def canAccess(level: NonPublicRefs.Value) = level match { @@ -587,15 +738,26 @@ abstract class Inliners extends SubComponent { private def sameOwner = caller.owner == inc.owner /** A method is safe to inline when: - * - it does not contain calls to private methods when - * called from another class + * - it does not contain calls to private methods when called from another class * - it is not inlined into a position with non-empty stack, * while having a top-level finalizer (see liftedTry problem) * - it is not recursive * Note: * - synthetic private members are made public in this pass. */ - def isSafeToInline(stack: TypeStack): Boolean = { + def isSafeToInline(stackLength: Int): Boolean = { + + if(tfa.blackballed(inc.sym)) { return false } + if(tfa.knownSafe(inc.sym)) { return true } + + if(helperIsSafeToInline(stackLength)) { + tfa.knownSafe += inc.sym; true + } else { + tfa.knownUnsafe += inc.sym; false + } + } + + private def helperIsSafeToInline(stackLength: Int): Boolean = { def makePublic(f: Symbol): Boolean = (inc.m.sourceFile ne NoSourceFile) && (f.isSynthetic || f.isParamAccessor) && { debuglog("Making not-private symbol out of synthetic: " + f) @@ -604,8 +766,8 @@ abstract class Inliners extends SubComponent { true } - if (!inc.m.hasCode || inc.isRecursive) - return false + if (!inc.m.hasCode || inc.isRecursive) { return false } + if (inc.m.symbol.hasFlag(Flags.SYNCHRONIZED)) { return false } val accessNeeded = usesNonPublics.getOrElseUpdate(inc.m, { // Avoiding crashing the compiler if there are open blocks. @@ -642,9 +804,10 @@ abstract class Inliners extends SubComponent { }) canAccess(accessNeeded) && { - val isIllegalStack = (stack.length > inc.minimumStack && inc.hasNonFinalizerHandler) + val isIllegalStack = (stackLength > inc.minimumStack && inc.hasNonFinalizerHandler) + !isIllegalStack || { - debuglog("method " + inc.sym + " is used on a non-empty stack with finalizer. Stack: " + stack) + debuglog("method " + inc.sym + " is used on a non-empty stack with finalizer.") false } } diff --git a/src/compiler/scala/tools/nsc/dependencies/Changes.scala b/src/compiler/scala/tools/nsc/dependencies/Changes.scala index 089ef9cf35..176c00c025 100644 --- a/src/compiler/scala/tools/nsc/dependencies/Changes.scala +++ b/src/compiler/scala/tools/nsc/dependencies/Changes.scala @@ -18,7 +18,7 @@ abstract class Changes { abstract class Change - private lazy val annotationsChecked = + private lazy val annotationsChecked = List(definitions.SpecializedClass) // Any others that should be checked? private val flagsToCheck = IMPLICIT | FINAL | PRIVATE | PROTECTED | SEALED | diff --git a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala index bd890b7194..02be916f59 100644 --- a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala +++ b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala @@ -145,10 +145,8 @@ trait DependencyAnalysis extends SubComponent with Files { val name = d.toString d.symbol match { case s : ModuleClassSymbol => - val isTopLevelModule = - atPhase (currentRun.picklerPhase.next) { - !s.isImplClass && !s.isNestedClass - } + val isTopLevelModule = afterPickler { !s.isImplClass && !s.isNestedClass } + if (isTopLevelModule && (s.companionModule != NoSymbol)) { dependencies.emits(source, nameToFile(unit.source.file, name)) } @@ -182,16 +180,18 @@ trait DependencyAnalysis extends SubComponent with Files { || (tree.symbol.sourceFile.path != file.path)) && (!tree.symbol.isClassConstructor)) { updateReferences(tree.symbol.fullName) - atPhase(currentRun.uncurryPhase.prev) { - checkType(tree.symbol.tpe) - } + // was "at uncurryPhase.prev", which is actually non-deterministic + // because the continuations plugin may or may not supply uncurry's + // immediately preceding phase. + beforeRefchecks(checkType(tree.symbol.tpe)) } tree match { case cdef: ClassDef if !cdef.symbol.hasPackageFlag && !cdef.symbol.isAnonymousFunction => if (cdef.symbol != NoSymbol) buf += cdef.symbol - atPhase(currentRun.erasurePhase.prev) { + // was "at erasurePhase.prev" + beforeExplicitOuter { for (s <- cdef.symbol.info.decls) s match { case ts: TypeSymbol if !ts.isClass => @@ -202,9 +202,8 @@ trait DependencyAnalysis extends SubComponent with Files { super.traverse(tree) case ddef: DefDef => - atPhase(currentRun.typerPhase.prev) { - checkType(ddef.symbol.tpe) - } + // was "at typer.prev" + beforeTyper { checkType(ddef.symbol.tpe) } super.traverse(tree) case a @ Select(q, n) if ((a.symbol != NoSymbol) && (q.symbol != null)) => // #2556 if (!a.symbol.isConstructor && diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala index c21507ef45..4f05678d85 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -80,9 +80,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { "selected.png", "selected2-right.png", "selected2.png", - "unselected.png", - - "rootdoc.txt" + "unselected.png" ) /** Generates the Scaladoc site for a model into the site root. diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt b/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt deleted file mode 100644 index 6145429f1e..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt +++ /dev/null @@ -1,27 +0,0 @@ -This is the documentation for the Scala standard library. - -== Package structure == - -The [[scala]] package contains core types. - -scala.[[scala.collection]] and its subpackages contain a collections framework with higher-order functions for manipulation. Both [[scala.collection.immutable]] and [[scala.collection.mutable]] data structures are available, with immutable as the default. The [[scala.collection.parallel]] collections provide automatic parallel operation. - -Other important packages include: - - - scala.[[scala.actors]] - Concurrency framework inspired by Erlang. - - scala.[[scala.io]] - Input and output. - - scala.[[scala.math]] - Basic math functions and additional numeric types. - - scala.[[scala.sys]] - Interaction with other processes and the operating system. - - scala.util.[[scala.util.matching]] - Pattern matching in text using regular expressions. - - scala.util.parsing.[[scala.util.parsing.combinator]] - Composable combinators for parsing. - - scala.[[scala.xml]] - XML parsing, manipulation, and serialization. - -Many other packages exist. See the complete list on the left. - -== Automatic imports == - -Identifiers in the scala package and the [[scala.Predef]] object are always in scope by default. - -Some of these identifiers are type aliases provided as shortcuts to commonly used classes. For example, List is an alias for scala.collection.immutable.[[scala.collection.immutable.List]]. - -Other aliases refer to classes providing by the underlying platform. For example, on the JVM, String is an alias for java.lang.String. diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index ec9f705f5a..496d004fd8 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -104,7 +104,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case mb: NonTemplateMemberEntity if (mb.useCaseOf.isDefined) => mb.useCaseOf.get.inDefinitionTemplates case _ => - if (inTpl == null) + if (inTpl == null) makeRootPackage.toList else makeTemplate(sym.owner) :: (sym.allOverriddenSymbols map { inhSym => makeTemplate(inhSym.owner) }) @@ -123,14 +123,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { else Public() } } - def flags = { + def flags = { val fgs = mutable.ListBuffer.empty[Paragraph] if (sym.isImplicit) fgs += Paragraph(Text("implicit")) if (sym.isSealed) fgs += Paragraph(Text("sealed")) if (!sym.isTrait && (sym hasFlag Flags.ABSTRACT)) fgs += Paragraph(Text("abstract")) if (!sym.isTrait && (sym hasFlag Flags.DEFERRED)) fgs += Paragraph(Text("abstract")) if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final")) - fgs.toList + fgs.toList } def deprecation = if (sym.isDeprecated) diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index f2d59206e0..1b91b06942 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -10,6 +10,8 @@ import scala.tools.nsc.io.AbstractFile import scala.tools.nsc.util.{SourceFile, Position, WorkScheduler} import scala.tools.nsc.symtab._ import scala.tools.nsc.ast._ +import scala.tools.nsc.util.FailedInterrupt +import scala.tools.nsc.util.EmptyAction /** Interface of interactive compiler to a client such as an IDE * The model the presentation compiler consists of the following parts: @@ -48,7 +50,7 @@ trait CompilerControl { self: Global => /** The scheduler by which client and compiler communicate * Must be initialized before starting compilerRunner */ - protected[interactive] val scheduler = new WorkScheduler + @volatile protected[interactive] var scheduler = new WorkScheduler /** Return the compilation unit attached to a source file, or None * if source is not loaded. @@ -374,6 +376,25 @@ trait CompilerControl { self: Global => response raise new MissingResponse } + /** A do-nothing work scheduler that responds immediately with MissingResponse. + * + * Used during compiler shutdown. + */ + class NoWorkScheduler extends WorkScheduler { + + override def postWorkItem(action: Action) = synchronized { + action match { + case w: WorkItem => w.raiseMissing() + case e: EmptyAction => // do nothing + case _ => println("don't know what to do with this " + action.getClass) + } + } + + override def doQuickly[A](op: () => A): A = { + throw new FailedInterrupt(new Exception("Posted a work item to a compiler that's shutting down")) + } + } + } // ---------------- Interpreted exceptions ------------------- diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 477cec8c8e..166b38f503 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -357,6 +357,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") checkNoResponsesOutstanding() log.flush(); + scheduler = new NoWorkScheduler throw ShutdownReq } @@ -609,6 +610,15 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } response raise ex throw ex + + case ex @ ShutdownReq => + if (debugIDE) { + println("ShutdownReq thrown during response") + ex.printStackTrace() + } + response raise ex + throw ex + case ex => if (debugIDE) { println("exception thrown during response: "+ex) diff --git a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala index f251fd83fb..bad181eb76 100644 --- a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala @@ -22,6 +22,7 @@ import scala.tools.util.PathResolver * changes require a compilation. It repeats this process until * a fixpoint is reached. */ +@deprecated("Use sbt incremental compilation mechanism", "2.10.0") class RefinedBuildManager(val settings: Settings) extends Changes with BuildManager { class BuilderGlobal(settings: Settings, reporter : Reporter) extends scala.tools.nsc.Global(settings, reporter) { @@ -47,7 +48,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana protected def newCompiler(settings: Settings) = new BuilderGlobal(settings) val compiler = newCompiler(settings) - import compiler.{Symbol, Type, atPhase, currentRun} + import compiler.{ Symbol, Type, beforeErasure } import compiler.dependencyAnalysis.Inherited private case class SymWithHistory(sym: Symbol, befErasure: Type) @@ -159,10 +160,8 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana isCorrespondingSym(s.sym, sym)) match { case Some(SymWithHistory(oldSym, info)) => val changes = changeSet(oldSym.info, sym) - val changesErasure = - atPhase(currentRun.erasurePhase.prev) { - changeSet(info, sym) - } + val changesErasure = beforeErasure(changeSet(info, sym)) + changesOf(oldSym) = (changes ++ changesErasure).distinct case _ => // a new top level definition @@ -332,11 +331,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana for (src <- files; localDefs = compiler.dependencyAnalysis.definitions(src)) { definitions(src) = (localDefs map (s => { this.classes += s.fullName -> src - SymWithHistory( - s.cloneSymbol, - atPhase(currentRun.erasurePhase.prev) { - s.info.cloneInfo(s) - }) + SymWithHistory(s.cloneSymbol, beforeErasure(s.info.cloneInfo(s))) })) } this.references = compiler.dependencyAnalysis.references diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala index 9f5fde70d8..68c8f2fdb8 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala @@ -62,7 +62,7 @@ trait ExprTyper { else Some(trees) } } - def tokens(line: String) = beSilentDuring(codeParser.tokens(line)) + def tokens(line: String) = beQuietDuring(codeParser.tokens(line)) // TODO: integrate these into a CodeHandler[Type]. @@ -92,7 +92,7 @@ trait ExprTyper { case _ => NoType } } - + def evaluate(): Type = { typeOfExpressionDepth += 1 try typeOfTerm(expr) orElse asModule orElse asExpr orElse asQualifiedImport diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index 7c71438b98..e1ea69842f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -324,7 +324,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) private def implicitsCommand(line: String): Result = { val intp = ILoop.this.intp import intp._ - import global.Symbol + import global.{ Symbol, afterTyper } def p(x: Any) = intp.reporter.printMessage("" + x) @@ -348,7 +348,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) // This groups the members by where the symbol is defined val byOwner = syms groupBy (_.owner) - val sortedOwners = byOwner.toList sortBy { case (owner, _) => intp.afterTyper(source.info.baseClasses indexOf owner) } + val sortedOwners = byOwner.toList sortBy { case (owner, _) => afterTyper(source.info.baseClasses indexOf owner) } sortedOwners foreach { case (owner, members) => @@ -382,7 +382,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) private def findToolsJar() = { val jdkPath = Directory(jdkHome) val jar = jdkPath / "lib" / "tools.jar" toFile; - + if (jar isFile) Some(jar) else if (jdkPath.isDirectory) @@ -440,7 +440,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) else { val tp = intp.typeOfExpression(line, false) if (tp == NoType) "" // the error message was already printed - else intp.afterTyper(tp.toString) + else intp.global.afterTyper(tp.toString) } } private def warningsCommand(): Result = { diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 6ae8d0e7d0..9a12bc1471 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -230,9 +230,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } with MemberHandlers import memberHandlers._ - def atPickler[T](op: => T): T = atPhase(currentRun.picklerPhase)(op) - def afterTyper[T](op: => T): T = atPhase(currentRun.typerPhase.next)(op) - /** Temporarily be quiet */ def beQuietDuring[T](body: => T): T = { val saved = printResults @@ -787,10 +784,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } def compile(source: String): Boolean = compileAndSaveRun("<console>", source) - def lineAfterTyper[T](op: => T): T = { - assert(lastRun != null, "Internal error: trying to use atPhase, but Run is null." + this) - atPhase(lastRun.typerPhase.next)(op) - } /** The innermost object inside the wrapper, found by * following accessPath into the outer one. @@ -799,7 +792,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val readRoot = getRequiredModule(readPath) // the outermost wrapper (accessPath split '.').foldLeft(readRoot) { (sym, name) => if (name == "") sym else - lineAfterTyper(sym.info member newTermName(name)) + afterTyper(sym.info member newTermName(name)) } } /** We get a bunch of repeated warnings for reasons I haven't @@ -842,7 +835,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends // private class Request(val line: String, val trees: List[Tree]) { val lineRep = new ReadEvalPrint() - import lineRep.lineAfterTyper private var _originalLine: String = null def withOriginalLine(s: String): this.type = { _originalLine = s ; this } @@ -906,11 +898,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends if (!handlers.last.definesValue) "" else handlers.last.definesTerm match { case Some(vname) if typeOf contains vname => - """ - |lazy val %s = { - | %s - | %s - |}""".stripMargin.format(lineRep.resultName, lineRep.printName, fullPath(vname)) + "lazy val %s = %s".format(lineRep.resultName, fullPath(vname)) case _ => "" } // first line evaluates object to make sure constructor is run @@ -956,7 +944,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends typesOfDefinedTerms // compile the result-extraction object - beSilentDuring { + beQuietDuring { savingSettings(_.nowarn.value = true) { lineRep compile ResultObjectSourceCode(handlers) } @@ -965,7 +953,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } lazy val resultSymbol = lineRep.resolvePathToSymbol(accessPath) - def applyToResultMember[T](name: Name, f: Symbol => T) = lineAfterTyper(f(resultSymbol.info.nonPrivateDecl(name))) + def applyToResultMember[T](name: Name, f: Symbol => T) = afterTyper(f(resultSymbol.info.nonPrivateDecl(name))) /* typeOf lookup with encoding */ def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(global.encode(name.toString))) diff --git a/src/compiler/scala/tools/nsc/interpreter/Imports.scala b/src/compiler/scala/tools/nsc/interpreter/Imports.scala index d34ca8bbca..d579e0369e 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Imports.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Imports.scala @@ -61,7 +61,7 @@ trait Imports { def importedTypeSymbols = importedSymbols collect { case x: TypeSymbol => x } def implicitSymbols = importedSymbols filter (_.isImplicit) - def importedTermNamed(name: String): Symbol = + def importedTermNamed(name: String): Symbol = importedTermSymbols find (_.name.toString == name) getOrElse NoSymbol /** Tuples of (source, imported symbols) in the order they were imported. @@ -191,5 +191,5 @@ trait Imports { prevRequestList flatMap (req => req.handlers map (req -> _)) private def membersAtPickler(sym: Symbol): List[Symbol] = - atPickler(sym.info.nonPrivateMembers) + beforePickler(sym.info.nonPrivateMembers) }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala index d96e8b07fc..f9c1907696 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala @@ -18,7 +18,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput import global._ import definitions.{ PredefModule, RootClass, AnyClass, AnyRefClass, ScalaPackage, JavaLangPackage, getModuleIfDefined } type ExecResult = Any - import intp.{ debugging, afterTyper } + import intp.{ debugging } // verbosity goes up with consecutive tabs private var verbosity: Int = 0 @@ -61,7 +61,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput def packageNames = packages map tos def aliasNames = aliases map tos } - + object NoTypeCompletion extends TypeMemberCompletion(NoType) { override def memberNamed(s: String) = NoSymbol override def members = Nil @@ -165,11 +165,11 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput override def follow(id: String): Option[CompletionAware] = { if (!completions(0).contains(id)) return None - + val tpe = intp typeOfExpression id if (tpe == NoType) return None - + def default = Some(TypeMemberCompletion(tpe)) // only rebinding vals in power mode for now. diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index 37dd032135..68bfeafbc6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -13,7 +13,7 @@ import scala.reflect.internal.Chars trait MemberHandlers { val intp: IMain - import intp.{ Request, global, naming, atPickler } + import intp.{ Request, global, naming } import global._ import naming._ @@ -118,8 +118,9 @@ trait MemberHandlers { class DefHandler(member: DefDef) extends MemberDefHandler(member) { private def vparamss = member.vparamss - // true if 0-arity - override def definesValue = vparamss.isEmpty || vparamss.head.isEmpty + private def isMacro = member.mods.hasFlag(scala.reflect.internal.Flags.MACRO) + // true if not a macro and 0-arity + override def definesValue = !isMacro && (vparamss.isEmpty || vparamss.head.isEmpty) override def resultExtractionCode(req: Request) = if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else "" } @@ -199,10 +200,10 @@ trait MemberHandlers { def importedSymbols = individualSymbols ++ wildcardSymbols lazy val individualSymbols: List[Symbol] = - atPickler(individualNames map (targetType nonPrivateMember _)) + beforePickler(individualNames map (targetType nonPrivateMember _)) lazy val wildcardSymbols: List[Symbol] = - if (importsWildcard) atPickler(targetType.nonPrivateMembers) + if (importsWildcard) beforePickler(targetType.nonPrivateMembers) else Nil /** Complete list of names imported by a wildcard */ diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index 835fbb5638..14876425f4 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -15,6 +15,31 @@ import scala.io.Codec import java.net.{ URL, MalformedURLException } import io.{ Path } +/** Collecting some power mode examples. + +scala> trait F[@specialized(Int) T] { def f: T = ??? } +defined trait F + +scala> trait G[@specialized(Long, Int) T] extends F[T] { override def f: T = super.f } +defined trait G + +scala> changesAfterEachPhase(intp("G").info.members filter (_.name.toString contains "super")) > +Gained after 1/parser { + method super$f +} + +Gained after 12/specialize { + method super$f$mcJ$sp + method super$f$mcI$sp +} + +Lost after 18/flatten { + method super$f$mcJ$sp + method super$f$mcI$sp + method super$f +} +*/ + /** A class for methods to be injected into the intp in power mode. */ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: ReplValsImpl) { @@ -130,7 +155,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl ( rutil.info[ReplValsImpl].membersDeclared filter (m => m.isPublic && !m.hasModuleFlag && !m.isConstructor) - sortBy (_.decodedName) + sortBy (_.decodedName) map to_str mkString ("Name and type of values imported into the repl in power mode.\n\n", "\n", "") ) @@ -140,7 +165,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl implicit def apply[T: Manifest] : InternalInfo[T] = new InternalInfo[T](None) } object InternalInfo extends LowPriorityInternalInfo { } - + /** Now dealing with the problem of acidentally calling a method on Type * when you're holding a Symbol and seeing the Symbol converted to the * type of Symbol rather than the type of the thing represented by the @@ -151,7 +176,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl implicit def apply[T: Manifest] : InternalInfoWrapper[T] = new InternalInfoWrapper[T](None) } object InternalInfoWrapper extends LowPriorityInternalInfoWrapper { - + } class InternalInfoWrapper[T: Manifest](value: Option[T] = None) { def ? : InternalInfo[T] = new InternalInfo[T](value) @@ -165,7 +190,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl private def newInfo[U: Manifest](value: U): InternalInfo[U] = new InternalInfo[U](Some(value)) private def isSpecialized(s: Symbol) = s.name.toString contains "$mc" private def isImplClass(s: Symbol) = s.name.toString endsWith "$class" - + /** Standard noise reduction filter. */ def excludeMember(s: Symbol) = ( isSpecialized(s) @@ -193,7 +218,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl def membersInherited = members filterNot (membersDeclared contains _) def memberTypes = members filter (_.name.isTypeName) def memberMethods = members filter (_.isMethod) - + def pkg = symbol.enclosingPackage def pkgName = pkg.fullName def pkgClass = symbol.enclosingPackageClass @@ -318,12 +343,12 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl def sigs = syms map (_.defString) def infos = syms map (_.info) } - + trait Implicits1 { // fallback implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = new SinglePrettifierClass[T](x) - + implicit def liftToTypeName(s: String): TypeName = newTypeName(s) } trait Implicits2 extends Implicits1 { @@ -350,7 +375,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in) implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec) - + implicit def liftToTermName(s: String): TermName = newTermName(s) implicit def replListOfSymbols(xs: List[Symbol]) = new RichSymbolList(xs) } diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala index dac20ad348..130af990ad 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala @@ -14,7 +14,10 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.i // Avoiding deadlock if the compiler starts logging before // the lazy val is complete. if (intp.isInitializeComplete) { - if (intp.totalSilence) () + if (intp.totalSilence) { + if (isReplTrace) + super.printMessage("[silent] " + msg) + } else super.printMessage(msg) } else Console.println("[init] " + msg) diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala index 6e5dec4205..a68392f0fb 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala @@ -50,7 +50,7 @@ object ReplVals { def mkManifestToType[T <: Global](global: T) = { import global._ import definitions._ - + /** We can't use definitions.manifestToType directly because we're passing * it to map and the compiler refuses to perform eta expansion on a method * with a dependent return type. (Can this be relaxed?) To get around this @@ -59,15 +59,17 @@ object ReplVals { */ def manifestToType(m: OptManifest[_]): Global#Type = definitions.manifestToType(m) - + class AppliedTypeFromManifests(sym: Symbol) { def apply[M](implicit m1: Manifest[M]): Type = - appliedType(sym.typeConstructor, List(m1) map (x => manifestToType(x).asInstanceOf[Type])) + if (sym eq NoSymbol) NoType + else appliedType(sym.typeConstructor, List(m1) map (x => manifestToType(x).asInstanceOf[Type])) def apply[M1, M2](implicit m1: Manifest[M1], m2: Manifest[M2]): Type = - appliedType(sym.typeConstructor, List(m1, m2) map (x => manifestToType(x).asInstanceOf[Type])) + if (sym eq NoSymbol) NoType + else appliedType(sym.typeConstructor, List(m1, m2) map (x => manifestToType(x).asInstanceOf[Type])) } - + (sym: Symbol) => new AppliedTypeFromManifests(sym) } } diff --git a/src/compiler/scala/tools/nsc/io/Pickler.scala b/src/compiler/scala/tools/nsc/io/Pickler.scala index 5bb8bdda35..80b6e086da 100644 --- a/src/compiler/scala/tools/nsc/io/Pickler.scala +++ b/src/compiler/scala/tools/nsc/io/Pickler.scala @@ -165,7 +165,7 @@ object Pickler { def pkl[T: Pickler] = implicitly[Pickler[T]] /** A class represenenting `~`-pairs */ - case class ~[S, T](fst: S, snd: T) + case class ~[+S, +T](fst: S, snd: T) /** A wrapper class to be able to use `~` s an infix method */ class TildeDecorator[S](x: S) { diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 0c94e40d68..06b06c50a6 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -393,8 +393,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { // assumed true unless we see public/private/protected var isPackageAccess = true var annots: List[Tree] = Nil - def addAnnot(sym: Symbol) = - annots :+= New(TypeTree(sym.tpe), List(Nil)) + def addAnnot(sym: Symbol) = annots :+= New(sym.tpe) while (true) { in.token match { @@ -654,15 +653,12 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { // leaves auxiliary constructors unable to access members of the companion object // as unqualified identifiers. def addCompanionObject(statics: List[Tree], cdef: ClassDef): List[Tree] = { - def implWithImport(importStmt: Tree) = { - import cdef.impl._ - treeCopy.Template(cdef.impl, parents, self, importStmt :: body) - } + def implWithImport(importStmt: Tree) = deriveTemplate(cdef.impl)(importStmt :: _) // if there are no statics we can use the original cdef, but we always // create the companion so import A._ is not an error (see ticket #1700) val cdefNew = if (statics.isEmpty) cdef - else treeCopy.ClassDef(cdef, cdef.mods, cdef.name, cdef.tparams, implWithImport(importCompanionObject(cdef))) + else deriveClassDef(cdef)(_ => implWithImport(importCompanionObject(cdef))) List(makeCompanionObject(cdefNew, statics), cdefNew) } diff --git a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala index 5e46960d04..371f4bc4d8 100644 --- a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala +++ b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala @@ -115,6 +115,10 @@ trait MatchSupport extends ast.TreeDSL { self: ParallelMatching => println(fmt.format(xs: _*) + " == " + x) x } + private[nsc] def debugging[T](fmt: String, xs: Any*)(x: T): T = { + if (settings.debug.value) printing(fmt, xs: _*)(x) + else x + } def indent(s: Any) = s.toString() split "\n" map (" " + _) mkString "\n" def indentAll(s: Seq[Any]) = s map (" " + _.toString() + "\n") mkString diff --git a/src/compiler/scala/tools/nsc/matching/Matrix.scala b/src/compiler/scala/tools/nsc/matching/Matrix.scala index d81f05cd51..e1ff88557e 100644 --- a/src/compiler/scala/tools/nsc/matching/Matrix.scala +++ b/src/compiler/scala/tools/nsc/matching/Matrix.scala @@ -198,6 +198,10 @@ trait Matrix extends MatrixAdditions { class PatternVar(val lhs: Symbol, val rhs: Tree, val checked: Boolean) { def sym = lhs def tpe = lhs.tpe + if (checked) + lhs resetFlag NO_EXHAUSTIVE + else + lhs setFlag NO_EXHAUSTIVE // See #1427 for an example of a crash which occurs unless we retype: // in that instance there is an existential in the pattern. @@ -207,11 +211,6 @@ trait Matrix extends MatrixAdditions { override def toString() = "%s: %s = %s".format(lhs, tpe, rhs) } - /** Sets the rhs to EmptyTree, which makes the valDef ignored in Scrutinee. - */ - def specialVar(lhs: Symbol, checked: Boolean) = - new PatternVar(lhs, EmptyTree, checked) - /** Given a tree, creates a new synthetic variable of the same type * and assigns the tree to it. */ diff --git a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala index 24d3c38e74..e72a0007a0 100644 --- a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala +++ b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala @@ -131,23 +131,11 @@ trait MatrixAdditions extends ast.TreeDSL { import Flags.{ MUTABLE, ABSTRACT, SEALED } - private case class Combo(index: Int, sym: Symbol) { - val isBaseClass = sym.tpe.baseClasses.toSet - - // is this combination covered by the given pattern? - def isCovered(p: Pattern) = { - def coversSym = isBaseClass(decodedEqualsType(p.tpe).typeSymbol) - - cond(p.tree) { - case _: UnApply | _: ArrayValue => true - case x => p.isDefault || coversSym - } - } - } + private case class Combo(index: Int, sym: Symbol) { } /* True if the patterns in 'row' cover the given type symbol combination, and has no guard. */ private def rowCoversCombo(row: Row, combos: List[Combo]) = - row.guard.isEmpty && (combos forall (c => c isCovered row.pats(c.index))) + row.guard.isEmpty && combos.forall(c => row.pats(c.index) covers c.sym) private def requiresExhaustive(sym: Symbol) = { (sym.isMutable) && // indicates that have not yet checked exhaustivity diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index 9d4c9b4411..1285e29d4a 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -745,7 +745,7 @@ trait ParallelMatching extends ast.TreeDSL (others.head :: _column.tail, make(_tvars, _rows)) def mix() = { - val newScrut = new Scrutinee(specialVar(_pv.sym, _pv.checked)) + val newScrut = new Scrutinee(new PatternVar(_pv.sym, EmptyTree, _pv.checked)) PatternMatch(newScrut, _ncol) mkRule _nrep } } diff --git a/src/compiler/scala/tools/nsc/matching/PatternBindings.scala b/src/compiler/scala/tools/nsc/matching/PatternBindings.scala index 5dd7d8f3ee..56297f0195 100644 --- a/src/compiler/scala/tools/nsc/matching/PatternBindings.scala +++ b/src/compiler/scala/tools/nsc/matching/PatternBindings.scala @@ -19,9 +19,10 @@ trait PatternBindings extends ast.TreeDSL import Debug._ /** EqualsPattern **/ - def isEquals(tpe: Type) = cond(tpe) { case TypeRef(_, EqualsPatternClass, _) => true } + def isEquals(tpe: Type) = tpe.typeSymbol == EqualsPatternClass def mkEqualsRef(tpe: Type) = typeRef(NoPrefix, EqualsPatternClass, List(tpe)) - def decodedEqualsType(tpe: Type) = condOpt(tpe) { case TypeRef(_, EqualsPatternClass, List(arg)) => arg } getOrElse (tpe) + def decodedEqualsType(tpe: Type) = + if (tpe.typeSymbol == EqualsPatternClass) tpe.typeArgs.head else tpe // A subtype test which creates fresh existentials for type // parameters on the right hand side. diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala index 18409cfffe..8bdf83fda4 100644 --- a/src/compiler/scala/tools/nsc/matching/Patterns.scala +++ b/src/compiler/scala/tools/nsc/matching/Patterns.scala @@ -26,19 +26,6 @@ trait Patterns extends ast.TreeDSL { type PatternMatch = MatchMatrix#PatternMatch private type PatternVar = MatrixContext#PatternVar - // private def unapplyArgs(x: Any) = x match { - // case UnApply(Apply(TypeApply(_, targs), args), _) => (targs map (_.symbol), args map (_.symbol)) - // case _ => (Nil, Nil) - // } - // - // private def unapplyCall(x: Any) = x match { - // case UnApply(t, _) => treeInfo.methPart(t).symbol - // case _ => NoSymbol - // } - - private lazy val dummyMethod = - NoSymbol.newTermSymbol(newTermName("matching$dummy")) - // Fresh patterns def emptyPatterns(i: Int): List[Pattern] = List.fill(i)(NoPattern) def emptyTrees(i: Int): List[Tree] = List.fill(i)(EmptyTree) @@ -56,13 +43,14 @@ trait Patterns extends ast.TreeDSL { case class VariablePattern(tree: Ident) extends NamePattern { lazy val Ident(name) = tree require(isVarPattern(tree) && name != nme.WILDCARD) - + override def covers(sym: Symbol) = true override def description = "%s".format(name) } // 8.1.1 (b) case class WildcardPattern() extends Pattern { def tree = EmptyTree + override def covers(sym: Symbol) = true override def isDefault = true override def description = "_" } @@ -71,6 +59,8 @@ trait Patterns extends ast.TreeDSL { case class TypedPattern(tree: Typed) extends Pattern { lazy val Typed(expr, tpt) = tree + override def covers(sym: Symbol) = newMatchesPattern(sym, tpt.tpe) + override def sufficientType = tpt.tpe override def subpatternsForVars: List[Pattern] = List(Pattern(expr)) override def simplify(pv: PatternVar) = Pattern(expr) match { case ExtractorPattern(ua) if pv.sym.tpe <:< tpt.tpe => this rebindTo expr @@ -115,6 +105,7 @@ trait Patterns extends ast.TreeDSL { } } + override def covers(sym: Symbol) = newMatchesPattern(sym, sufficientType) override def simplify(pv: PatternVar) = this.rebindToObjectCheck() override def description = backticked match { case Some(s) => "this." + s @@ -133,13 +124,15 @@ trait Patterns extends ast.TreeDSL { case class ObjectPattern(tree: Apply) extends ApplyPattern { // NamePattern? require(!fn.isType && isModule) + override def covers(sym: Symbol) = newMatchesPattern(sym, sufficientType) override def sufficientType = tpe.narrow override def simplify(pv: PatternVar) = this.rebindToObjectCheck() override def description = "Obj(%s)".format(fn) } // 8.1.4 (e) case class SimpleIdPattern(tree: Ident) extends NamePattern { - lazy val Ident(name) = tree + val Ident(name) = tree + override def covers(sym: Symbol) = newMatchesPattern(sym, tpe.narrow) override def description = "Id(%s)".format(name) } @@ -163,6 +156,11 @@ trait Patterns extends ast.TreeDSL { if (args.isEmpty) this rebindToEmpty tree.tpe else this + override def covers(sym: Symbol) = { + debugging("[constructor] Does " + this + " cover " + sym + " ? ") { + sym.tpe.typeSymbol == this.tpe.typeSymbol + } + } override def description = { if (isColonColon) "%s :: %s".format(Pattern(args(0)), Pattern(args(1))) else "%s(%s)".format(name, toPats(args).mkString(", ")) @@ -175,17 +173,12 @@ trait Patterns extends ast.TreeDSL { // 8.1.7 / 8.1.8 (unapply and unapplySeq calls) case class ExtractorPattern(tree: UnApply) extends UnapplyPattern { - override def simplify(pv: PatternVar) = { - if (pv.sym hasFlag NO_EXHAUSTIVE) () - else { - TRACE("Setting NO_EXHAUSTIVE on " + pv.sym + " due to extractor " + tree) - pv.sym setFlag NO_EXHAUSTIVE - } + private def uaTyped = Typed(tree, TypeTree(arg.tpe)) setType arg.tpe + override def simplify(pv: PatternVar) = { if (pv.tpe <:< arg.tpe) this else this rebindTo uaTyped } - override def description = "Unapply(%s => %s)".format(necessaryType, resTypesString) } @@ -208,6 +201,7 @@ trait Patterns extends ast.TreeDSL { private def listFolder(hd: Tree, tl: Tree): Tree = unbind(hd) match { case t @ Star(_) => moveBindings(hd, WILD(t.tpe)) case _ => + val dummyMethod = NoSymbol.newTermSymbol(newTermName("matching$dummy")) val consType = MethodType(dummyMethod newSyntheticValueParams List(packedType, listRef), consRef) Apply(TypeTree(consType), List(hd, tl)) setType consRef @@ -376,7 +370,7 @@ trait Patterns extends ast.TreeDSL { case _: This if isVariableName(name) => Some("`%s`".format(name)) case _ => None } - + override def covers(sym: Symbol) = newMatchesPattern(sym, tree.tpe) protected def getPathSegments(t: Tree): List[Name] = t match { case Select(q, name) => name :: getPathSegments(q) case Apply(f, Nil) => getPathSegments(f) @@ -395,7 +389,13 @@ trait Patterns extends ast.TreeDSL { lazy val UnApply(unfn, args) = tree lazy val Apply(fn, _) = unfn lazy val MethodType(List(arg, _*), _) = fn.tpe - protected def uaTyped = Typed(tree, TypeTree(arg.tpe)) setType arg.tpe + + // Covers if the symbol matches the unapply method's argument type, + // and the return type of the unapply is Some. + override def covers(sym: Symbol) = newMatchesPattern(sym, arg.tpe) + + // TODO: for alwaysCovers: + // fn.tpe.finalResultType.typeSymbol == SomeClass override def necessaryType = arg.tpe override def subpatternsForVars = args match { @@ -419,6 +419,7 @@ trait Patterns extends ast.TreeDSL { else emptyPatterns(sufficientType.typeSymbol.caseFieldAccessors.size) def isConstructorPattern = fn.isType + override def covers(sym: Symbol) = newMatchesPattern(sym, fn.tpe) } sealed abstract class Pattern extends PatternBindingLogic { @@ -443,6 +444,15 @@ trait Patterns extends ast.TreeDSL { // the subpatterns for this pattern (at the moment, that means constructor arguments) def subpatterns(pm: MatchMatrix#PatternMatch): List[Pattern] = pm.dummies + // if this pattern should be considered to cover the given symbol + def covers(sym: Symbol): Boolean = newMatchesPattern(sym, sufficientType) + def newMatchesPattern(sym: Symbol, pattp: Type) = { + debugging("[" + kindString + "] Does " + pattp + " cover " + sym + " ? ") { + (sym.isModuleClass && (sym.tpe.typeSymbol eq pattp.typeSymbol)) || + (sym.tpe.baseTypeSeq exists (_ matchesPattern pattp)) + } + } + def sym = tree.symbol def tpe = tree.tpe def isEmpty = tree.isEmpty @@ -475,6 +485,7 @@ trait Patterns extends ast.TreeDSL { final override def toString = description def toTypeString() = "%s <: x <: %s".format(necessaryType, sufficientType) + def kindString = "" } /*** Extractors ***/ diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala index f19a285d7c..309fc5733f 100644 --- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -56,10 +56,10 @@ abstract class Reporter { */ def echo(msg: String): Unit = info(NoPosition, msg, true) def echo(pos: Position, msg: String): Unit = info(pos, msg, true) - + /** Informational messages, suppressed unless -verbose or force=true. */ def info(pos: Position, msg: String, force: Boolean): Unit = info0(pos, msg, INFO, force) - + /** Warnings and errors. */ def warning(pos: Position, msg: String): Unit = withoutTruncating(info0(pos, msg, WARNING, false)) def error(pos: Position, msg: String): Unit = withoutTruncating(info0(pos, msg, ERROR, false)) diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index b468e7c0af..e7959f36b2 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -95,8 +95,7 @@ class MutableSettings(val errorFn: String => Unit) */ def copy(): Settings = { val s = new Settings() - val xs = userSetSettings flatMap (_.unparse) - s.processArguments(xs.toList, true) + s.processArguments(recreateArgs, true) s } @@ -534,7 +533,7 @@ class MutableSettings(val errorFn: String => Unit) Some(rest) } override def tryToSetColon(args: List[String]) = tryToSet(args) - override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(" +").toList) + override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) def unparse: List[String] = value map { name + ":" + _ } withHelpSyntax(name + ":<" + arg + ">") diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index d1ce460eb9..e949cb3eb2 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -167,8 +167,7 @@ trait ScalaSettings extends AbsScalaSettings val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") val Yreifydebug = BooleanSetting ("-Yreify-debug", "Trace reification.") - val Yreifytyperdebug - = BooleanSetting ("-Yreifytyper-debug", "Trace typings of reified trees.") + val Ymacrodebug = BooleanSetting ("-Ymacro-debug", "Trace macro-related activities: generation of synthetics, expansion, exceptions.") val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") val Yrepldebug = BooleanSetting ("-Yrepl-debug", "Trace all repl activity.") . withPostSetHook(_ => interpreter.replProps.debug setValue true) diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala index a47bfda8c1..fb85ebeeb0 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala @@ -9,4 +9,4 @@ package symtab import ast.{Trees, TreePrinters, DocComments} import util._ -abstract class SymbolTable extends reflect.internal.SymbolTable
\ No newline at end of file +abstract class SymbolTable extends reflect.internal.SymbolTable
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index d8db62a408..1cd4ab21ea 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -368,7 +368,7 @@ abstract class ClassfileParser { case arr: Type => Constant(arr) } } - + private def getSubArray(bytes: Array[Byte]): Array[Byte] = { val decodedLength = ByteCodecs.decode(bytes) val arr = new Array[Byte](decodedLength) @@ -424,19 +424,21 @@ abstract class ClassfileParser { def forceMangledName(name: Name, module: Boolean): Symbol = { val parts = name.decode.toString.split(Array('.', '$')) var sym: Symbol = definitions.RootClass - atPhase(currentRun.flattenPhase.prev) { + + // was "at flatten.prev" + beforeFlatten { for (part0 <- parts; if !(part0 == ""); part = newTermName(part0)) { - val sym1 = atPhase(currentRun.icodePhase) { + val sym1 = beforeIcode { sym.linkedClassOfClass.info sym.info.decl(part.encode) }//.suchThat(module == _.isModule) - if (sym1 == NoSymbol) - sym = sym.info.decl(part.encode.toTypeName) - else - sym = sym1 + + sym = ( + if (sym1 ne NoSymbol) sym1 + else sym.info.decl(part.encode.toTypeName) + ) } } -// println("found: " + sym) sym } @@ -719,7 +721,7 @@ abstract class ClassfileParser { index += 1 val bounds = variance match { case '+' => TypeBounds.upper(objToAny(sig2type(tparams, skiptvs))) - case '-' => + case '-' => val tp = sig2type(tparams, skiptvs) // sig2type seems to return AnyClass regardless of the situation: // we don't want Any as a LOWER bound. @@ -1205,11 +1207,11 @@ abstract class ClassfileParser { // if loading during initialization of `definitions` typerPhase is not yet set. // in that case we simply load the member at the current phase if (currentRun.typerPhase != null) - atPhase(currentRun.typerPhase)(getMember(sym, innerName.toTypeName)) + beforeTyper(getMember(sym, innerName.toTypeName)) else getMember(sym, innerName.toTypeName) - assert(s ne NoSymbol, + assert(s ne NoSymbol, "" + ((externalName, outerName, innerName, sym.fullLocationString)) + " / " + " while parsing " + ((in.file, busy)) + sym + "." + innerName + " linkedModule: " + sym.companionModule + sym.companionModule.info.members @@ -1269,13 +1271,13 @@ abstract class ClassfileParser { if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC)) == 0) // See ticket #1687 for an example of when topLevelClass is NoSymbol: it // apparently occurs when processing v45.3 bytecode. - if (sym.toplevelClass != NoSymbol) - sym.privateWithin = sym.toplevelClass.owner + if (sym.enclosingTopLevelClass != NoSymbol) + sym.privateWithin = sym.enclosingTopLevelClass.owner // protected in java means package protected. #3946 if ((jflags & JAVA_ACC_PROTECTED) != 0) - if (sym.toplevelClass != NoSymbol) - sym.privateWithin = sym.toplevelClass.owner + if (sym.enclosingTopLevelClass != NoSymbol) + sym.privateWithin = sym.enclosingTopLevelClass.owner } @inline private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0 diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 7d42dabc08..68af518d3a 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -179,7 +179,7 @@ abstract class ICodeReader extends ClassfileParser { } else { forceMangledName(name, false) - atPhase(currentRun.flattenPhase.next)(definitions.getClass(name)) + afterFlatten(definitions.getClass(name.toTypeName)) } if (sym.isModule) sym.moduleClass diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 25ae6f33d2..758f870d6b 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -77,7 +77,7 @@ abstract class Pickler extends SubComponent { private var entries = new Array[AnyRef](256) private var ep = 0 private val index = new LinkedHashMap[AnyRef, Int] - private lazy val nonClassRoot = root.ownersIterator.find(! _.isClass) getOrElse NoSymbol + private lazy val nonClassRoot = findOrElse(root.ownersIterator)(!_.isClass)(NoSymbol) private def isRootSym(sym: Symbol) = sym.name.toTermName == rootName && sym.owner == rootOwner diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index e7759f1d7e..71d595c9c4 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -82,7 +82,9 @@ abstract class AddInterfaces extends InfoTransform { implClassMap.getOrElse(iface, { atPhase(implClassPhase) { - log("%s.implClass == %s".format(iface, iface.implClass)) + if (iface.implClass ne NoSymbol) + log("%s.implClass == %s".format(iface, iface.implClass)) + val implName = nme.implClassName(iface.name) var impl = if (iface.owner.isClass) iface.owner.info.decl(implName) else NoSymbol @@ -94,7 +96,6 @@ abstract class AddInterfaces extends InfoTransform { // error: java.lang.AssertionError: assertion failed: (scala.tools.nsc.typechecker.Contexts$NoContext$,scala.tools.nsc.typechecker.Contexts,NoContext$,trait Contexts in package typechecker) / while parsing (/scala/trunk/build/pack/lib/scala-compiler.jar(scala/tools/nsc/interactive/ContextTrees$class.class),Some(class ContextTrees$class))trait Contexts.NoContext$ linkedModule: <none>List() val originalImpl = impl - val originalImplString = originalImpl.hasFlagsToString(-1L) if (impl != NoSymbol) { // Unlink a pre-existing symbol only if the implementation class is // visible on the compilation classpath. In general this is true under @@ -120,8 +121,8 @@ abstract class AddInterfaces extends InfoTransform { impl setInfo new LazyImplClassType(iface) implClassMap(iface) = impl debuglog( - "generating impl class " + impl + " " + impl.hasFlagsToString(-1L) + " in " + iface.owner + ( - if (originalImpl == NoSymbol) "" else " (cloned from " + originalImpl.fullLocationString + " " + originalImplString + ")" + "generating impl class " + impl.debugLocationString + " in " + iface.owner + ( + if (originalImpl == NoSymbol) "" else " (cloned from " + originalImpl.debugLocationString + ")" ) ) impl @@ -194,7 +195,7 @@ abstract class AddInterfaces extends InfoTransform { case PolyType(_, restpe) => implType(restpe) } - sym setInfo implType(atPhase(currentRun.erasurePhase)(iface.info)) + sym setInfo implType(beforeErasure(iface.info)) } override def load(clazz: Symbol) { complete(clazz) } @@ -327,13 +328,11 @@ abstract class AddInterfaces extends InfoTransform { override def transform(tree: Tree): Tree = { val sym = tree.symbol val tree1 = tree match { - case ClassDef(mods, name, tparams, impl) if (sym.needsImplClass) => + case ClassDef(mods, _, _, impl) if sym.needsImplClass => implClass(sym).initialize // to force lateDEFERRED flags - treeCopy.ClassDef(tree, mods | INTERFACE, name, tparams, ifaceTemplate(impl)) - case DefDef(mods, name, tparams, vparamss, tpt, rhs) - if (sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass) => - treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, - addMixinConstructorCalls(rhs, sym.owner)) // (3) + copyClassDef(tree)(mods = mods | INTERFACE, impl = ifaceTemplate(impl)) + case DefDef(_,_,_,_,_,_) if sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass => + deriveDefDef(tree)(addMixinConstructorCalls(_, sym.owner)) // (3) case Template(parents, self, body) => val parents1 = sym.owner.info.parents map (t => TypeTree(t) setPos tree.pos) treeCopy.Template(tree, parents1, emptyValDef, body) @@ -350,7 +349,7 @@ abstract class AddInterfaces extends InfoTransform { val mix1 = mix if (mix == tpnme.EMPTY) mix else { - val ps = atPhase(currentRun.erasurePhase) { + val ps = beforeErasure { sym.info.parents dropWhile (p => p.symbol.name != mix) } assert(!ps.isEmpty, tree); diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 50e6139e65..d04c6115ca 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -33,21 +33,21 @@ abstract class CleanUp extends Transform with ast.TreeDSL { private def savingStatics[T](body: => T): T = { val savedNewStaticMembers : mutable.Buffer[Tree] = newStaticMembers.clone() val savedNewStaticInits : mutable.Buffer[Tree] = newStaticInits.clone() - val savedSymbolsStoredAsStatic : mutable.Map[String, Symbol] = symbolsStoredAsStatic.clone() + val savedSymbolsStoredAsStatic : mutable.Map[String, Symbol] = symbolsStoredAsStatic.clone() val result = body clearStatics() newStaticMembers ++= savedNewStaticMembers newStaticInits ++= savedNewStaticInits symbolsStoredAsStatic ++= savedSymbolsStoredAsStatic - + result } private def transformTemplate(tree: Tree) = { val Template(parents, self, body) = tree clearStatics() val newBody = transformTrees(body) - val templ = treeCopy.Template(tree, parents, self, transformTrees(newStaticMembers.toList) ::: newBody) + val templ = deriveTemplate(tree)(_ => transformTrees(newStaticMembers.toList) ::: newBody) try addStaticInits(templ) // postprocess to include static ctors finally clearStatics() } @@ -85,6 +85,11 @@ abstract class CleanUp extends Transform with ast.TreeDSL { case "poly-cache" => POLY_CACHE } + def shouldRewriteTry(tree: Try) = { + val sym = tree.tpe.typeSymbol + forMSIL && (sym != UnitClass) && (sym != NothingClass) + } + private def typedWithPos(pos: Position)(tree: Tree) = localTyper.typedPos(pos)(tree) @@ -97,7 +102,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { /** The boxed type if it's a primitive; identity otherwise. */ def toBoxedType(tp: Type) = if (isJavaValueType(tp)) boxedClass(tp.typeSymbol).tpe else tp - + override def transform(tree: Tree): Tree = tree match { /* Transforms dynamic calls (i.e. calls to methods that are undefined @@ -134,7 +139,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { case ad@ApplyDynamic(qual0, params) => if (settings.logReflectiveCalls.value) unit.echo(ad.pos, "method invocation uses reflection") - + val typedPos = typedWithPos(ad.pos) _ assert(ad.symbol.isPublic) @@ -146,7 +151,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { val flags = PRIVATE | STATIC | SYNTHETIC | ( if (isFinal) FINAL else 0 ) - + val varSym = currentClass.newVariable(mkTerm("" + forName), ad.pos, flags) setInfoAndEnter forType if (!isFinal) varSym.addAnnotation(VolatileAttr) @@ -488,7 +493,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { val t: Tree = ad.symbol.tpe match { case MethodType(mparams, resType) => assert(params.length == mparams.length, mparams) - + typedPos { val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe qual = safeREF(sym) @@ -560,8 +565,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { * Hence, we here rewrite all try blocks with a result != {Unit, All} such that they * store their result in a local variable. The catch blocks are adjusted as well. * The try tree is subsituted by a block whose result expression is read of that variable. */ - case theTry @ Try(block, catches, finalizer) - if theTry.tpe.typeSymbol != definitions.UnitClass && theTry.tpe.typeSymbol != definitions.NothingClass => + case theTry @ Try(block, catches, finalizer) if shouldRewriteTry(theTry) => val tpe = theTry.tpe.widen val tempVar = currentOwner.newVariable(mkTerm(nme.EXCEPTION_RESULT_PREFIX), theTry.pos).setInfo(tpe) def assignBlock(rhs: Tree) = super.transform(BLOCK(Ident(tempVar) === transform(rhs))) @@ -669,9 +673,9 @@ abstract class CleanUp extends Transform with ast.TreeDSL { val newCtor = findStaticCtor(template) match { // in case there already were static ctors - augment existing ones // currently, however, static ctors aren't being generated anywhere else - case Some(ctor @ DefDef(mods, name, tparams, vparamss, tpt, rhs)) => + case Some(ctor @ DefDef(_,_,_,_,_,_)) => // modify existing static ctor - val newBlock = rhs match { + deriveDefDef(ctor) { case block @ Block(stats, expr) => // need to add inits to existing block treeCopy.Block(block, newStaticInits.toList ::: stats, expr) @@ -679,15 +683,14 @@ abstract class CleanUp extends Transform with ast.TreeDSL { // need to create a new block with inits and the old term treeCopy.Block(term, newStaticInits.toList, term) } - treeCopy.DefDef(ctor, mods, name, tparams, vparamss, tpt, newBlock) case None => // create new static ctor val staticCtorSym = currentClass.newStaticConstructor(template.pos) - val rhs = Block(newStaticInits.toList, Literal(Constant())) + val rhs = Block(newStaticInits.toList, Literal(Constant(()))) localTyper.typedPos(template.pos)(DefDef(staticCtorSym, rhs)) } - treeCopy.Template(template, template.parents, template.self, newCtor :: template.body) + deriveTemplate(template)(newCtor :: _) } } diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index b60b411f47..445b21c7ad 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -24,8 +24,8 @@ abstract class Constructors extends Transform with ast.TreeDSL { protected def newTransformer(unit: CompilationUnit): Transformer = new ConstructorTransformer(unit) - private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = new mutable.HashMap[Symbol, List[Tree]] - private val ctorParams: mutable.Map[Symbol, List[Symbol]] = new mutable.HashMap[Symbol, List[Symbol]] + private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = perRunCaches.newMap[Symbol, List[Tree]] + private val ctorParams: mutable.Map[Symbol, List[Symbol]] = perRunCaches.newMap[Symbol, List[Symbol]] class ConstructorTransformer(unit: CompilationUnit) extends Transformer { @@ -129,7 +129,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { if (from.name != nme.OUTER) result else localTyper.typedPos(to.pos) { - IF (from OBJ_EQ NULL) THEN THROW(NullPointerExceptionClass) ELSE result + IF (from OBJ_EQ NULL) THEN Throw(NullPointerExceptionClass.tpe) ELSE result } } @@ -167,20 +167,18 @@ abstract class Constructors extends Transform with ast.TreeDSL { // Triage all template definitions to go into defBuf/auxConstructorBuf, constrStatBuf, or constrPrefixBuf. for (stat <- stats) stat match { - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case DefDef(_,_,_,_,_,rhs) => // methods with constant result type get literals as their body // all methods except the primary constructor go into template stat.symbol.tpe match { case MethodType(List(), tp @ ConstantType(c)) => - defBuf += treeCopy.DefDef( - stat, mods, name, tparams, vparamss, tpt, - Literal(c) setPos rhs.pos setType tp) + defBuf += deriveDefDef(stat)(Literal(c) setPos _.pos setType tp) case _ => if (stat.symbol.isPrimaryConstructor) () else if (stat.symbol.isConstructor) auxConstructorBuf += stat else defBuf += stat } - case ValDef(mods, name, tpt, rhs) => + case ValDef(_, _, _, rhs) => // val defs with constant right-hand sides are eliminated. // for all other val defs, an empty valdef goes into the template and // the initializer goes as an assignment into the constructor @@ -193,7 +191,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { (if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign( stat.symbol, rhs1) } - defBuf += treeCopy.ValDef(stat, mods, name, tpt, EmptyTree) + defBuf += deriveValDef(stat)(_ => EmptyTree) } case ClassDef(_, _, _, _) => // classes are treated recursively, and left in the template @@ -231,11 +229,11 @@ abstract class Constructors extends Transform with ast.TreeDSL { tree match { case DefDef(_, _, _, _, _, body) if (tree.symbol.isOuterAccessor && tree.symbol.owner == clazz && clazz.isEffectivelyFinal) => - log("outerAccessors += " + tree.symbol.fullName) + debuglog("outerAccessors += " + tree.symbol.fullName) outerAccessors ::= ((tree.symbol, body)) case Select(_, _) => if (!mustbeKept(tree.symbol)) { - log("accessedSyms += " + tree.symbol.fullName) + debuglog("accessedSyms += " + tree.symbol.fullName) accessedSyms addEntry tree.symbol } super.traverse(tree) @@ -519,14 +517,9 @@ abstract class Constructors extends Transform with ast.TreeDSL { } } - def delayedInitCall(closure: Tree) = - localTyper.typed { - atPos(impl.pos) { - Apply( - Select(This(clazz), delayedInitMethod), - List(New(TypeTree(closure.symbol.tpe), List(List(This(clazz)))))) - } - } + def delayedInitCall(closure: Tree) = localTyper.typedPos(impl.pos) { + gen.mkMethodCall(This(clazz), delayedInitMethod, Nil, List(New(closure.symbol.tpe, This(clazz)))) + } /** Return a pair consisting of (all statements up to and including superclass and trait constr calls, rest) */ def splitAtSuper(stats: List[Tree]) = { @@ -555,13 +548,12 @@ abstract class Constructors extends Transform with ast.TreeDSL { } // Assemble final constructor - defBuf += treeCopy.DefDef( - constr, constr.mods, constr.name, constr.tparams, constr.vparamss, constr.tpt, + defBuf += deriveDefDef(constr)(_ => treeCopy.Block( constrBody, paramInits ::: constrPrefixBuf.toList ::: uptoSuperStats ::: guardSpecializedInitializer(remainingConstrStats), - constrBody.expr)); + constrBody.expr)) // Followed by any auxiliary constructors defBuf ++= auxConstructorBuf @@ -571,14 +563,13 @@ abstract class Constructors extends Transform with ast.TreeDSL { clazz.info.decls unlink sym // Eliminate all field definitions that can be dropped from template - treeCopy.Template(impl, impl.parents, impl.self, - defBuf.toList filter (stat => mustbeKept(stat.symbol))) + deriveTemplate(impl)(_ => defBuf.toList filter (stat => mustbeKept(stat.symbol))) } // transformClassTemplate override def transform(tree: Tree): Tree = tree match { - case ClassDef(mods, name, tparams, impl) if !tree.symbol.isInterface && !isValueClass(tree.symbol) => - treeCopy.ClassDef(tree, mods, name, tparams, transformClassTemplate(impl)) + case ClassDef(_,_,_,_) if !tree.symbol.isInterface && !isValueClass(tree.symbol) => + deriveClassDef(tree)(transformClassTemplate) case _ => super.transform(tree) } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index efbfe4da41..70364070ff 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -31,6 +31,33 @@ abstract class Erasure extends AddInterfaces // -------- erasure on types -------------------------------------------------------- + // A type function from T => Class[U], used to determine the return + // type of getClass calls. The returned type is: + // + // 1. If T is a value type, Class[T]. + // 2. If T is a phantom type (Any or AnyVal), Class[_]. + // 3. If T is a local class, Class[_ <: |T|]. + // 4. Otherwise, Class[_ <: T]. + // + // Note: AnyVal cannot be Class[_ <: AnyVal] because if the static type of the + // receiver is AnyVal, it implies the receiver is boxed, so the correct + // class object is that of java.lang.Integer, not Int. + // + // TODO: If T is final, return type could be Class[T]. Should it? + def getClassReturnType(tpe: Type): Type = { + if (phase.erasedTypes) ClassClass.tpe else { + val tp = tpe.widen.normalize + val sym = tp.typeSymbol + + if (isValueClass(sym)) ClassType(tp) + else boundedClassType( + if (isPhantomClass(sym)) ObjectClass.tpe + else if (sym.isLocalClass) intersectionDominator(tp.parents) + else tp + ) + } + } + // convert a numeric with a toXXX method def numericConversion(tree: Tree, numericSym: Symbol): Tree = { val mname = newTermName("to" + numericSym.name) @@ -196,7 +223,7 @@ abstract class Erasure extends AddInterfaces /** The Java signature of type 'info', for symbol sym. The symbol is used to give the right return * type for constructors. */ - def javaSig(sym0: Symbol, info: Type): Option[String] = atPhase(currentRun.erasurePhase) { + def javaSig(sym0: Symbol, info: Type): Option[String] = beforeErasure { val isTraitSignature = sym0.enclClass.isTrait def superSig(parents: List[Type]) = traceSig("superSig", parents) { @@ -230,7 +257,7 @@ abstract class Erasure extends AddInterfaces // Anything which could conceivably be a module (i.e. isn't known to be // a type parameter or similar) must go through here or the signature is // likely to end up with Foo<T>.Empty where it needs Foo<T>.Empty$. - def fullNameInSig(sym: Symbol) = "L" + atPhase(currentRun.icodePhase)(sym.javaBinaryName) + def fullNameInSig(sym: Symbol) = "L" + beforeIcode(sym.javaBinaryName.toString) def jsig(tp0: Type, existentiallyBound: List[Symbol] = Nil, toplevel: Boolean = false, primitiveOK: Boolean = true): String = { val tp = tp0.dealias @@ -394,9 +421,9 @@ abstract class Erasure extends AddInterfaces /** Box `tree` of unboxed type */ private def box(tree: Tree): Tree = tree match { - case LabelDef(name, params, rhs) => - val rhs1 = box(rhs) - treeCopy.LabelDef(tree, name, params, rhs1) setType rhs1.tpe + case LabelDef(_, _, _) => + val ldef = deriveLabelDef(tree)(box) + ldef setType ldef.rhs.tpe case _ => typedPos(tree.pos)(tree.tpe.typeSymbol match { case UnitClass => @@ -413,7 +440,7 @@ abstract class Erasure extends AddInterfaces * fields (see TupleX). (ID) */ case Apply(boxFun, List(arg)) if isUnbox(tree.symbol) && safeToRemoveUnbox(arg.tpe.typeSymbol) => - log("boxing an unbox: " + tree + " and replying with " + arg) + log("boxing an unbox: " + tree + "/" + tree.symbol + " and replying with " + arg + " of type " + arg.tpe) arg case _ => (REF(boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectClass.tpe @@ -433,9 +460,9 @@ abstract class Erasure extends AddInterfaces println("unbox shorten: "+tree) // this never seems to kick in during build and test; therefore disabled. adaptToType(unboxed, pt) */ - case LabelDef(name, params, rhs) => - val rhs1 = unbox(rhs, pt) - treeCopy.LabelDef(tree, name, params, rhs1) setType rhs1.tpe + case LabelDef(_, _, _) => + val ldef = deriveLabelDef(tree)(unbox(_, pt)) + ldef setType ldef.rhs.tpe case _ => typedPos(tree.pos)(pt.typeSymbol match { case UnitClass => @@ -577,8 +604,8 @@ abstract class Erasure extends AddInterfaces throw ex } def adaptCase(cdef: CaseDef): CaseDef = { - val body1 = adaptToType(cdef.body, tree1.tpe) - treeCopy.CaseDef(cdef, cdef.pat, cdef.guard, body1) setType body1.tpe + val newCdef = deriveCaseDef(cdef)(adaptToType(_, tree1.tpe)) + newCdef setType newCdef.body.tpe } def adaptBranch(branch: Tree): Tree = if (branch == EmptyTree) branch else adaptToType(branch, tree1.tpe); @@ -621,21 +648,20 @@ abstract class Erasure extends AddInterfaces private def checkNoDoubleDefs(root: Symbol) { def doubleDefError(sym1: Symbol, sym2: Symbol) { // the .toString must also be computed at the earlier phase - def atRefc[T](op: => T) = atPhase[T](currentRun.refchecksPhase.next)(op) - val tpe1 = atRefc(root.thisType.memberType(sym1)) - val tpe2 = atRefc(root.thisType.memberType(sym2)) + val tpe1 = afterRefchecks(root.thisType.memberType(sym1)) + val tpe2 = afterRefchecks(root.thisType.memberType(sym2)) if (!tpe1.isErroneous && !tpe2.isErroneous) unit.error( if (sym1.owner == root) sym1.pos else root.pos, (if (sym1.owner == sym2.owner) "double definition:\n" else if (sym1.owner == root) "name clash between defined and inherited member:\n" else "name clash between inherited members:\n") + - sym1 + ":" + atRefc(tpe1.toString) + + sym1 + ":" + afterRefchecks(tpe1.toString) + (if (sym1.owner == root) "" else sym1.locationString) + " and\n" + - sym2 + ":" + atRefc(tpe2.toString) + + sym2 + ":" + afterRefchecks(tpe2.toString) + (if (sym2.owner == root) " at line " + (sym2.pos).line else sym2.locationString) + "\nhave same type" + - (if (atRefc(tpe1 =:= tpe2)) "" else " after erasure: " + atPhase(phase.next)(sym1.tpe))) + (if (afterRefchecks(tpe1 =:= tpe2)) "" else " after erasure: " + afterErasure(sym1.tpe))) sym1.setInfo(ErrorType) } @@ -645,7 +671,7 @@ abstract class Erasure extends AddInterfaces if (e.sym.isTerm) { var e1 = decls.lookupNextEntry(e) while (e1 ne null) { - if (atPhase(phase.next)(e1.sym.info =:= e.sym.info)) doubleDefError(e.sym, e1.sym) + if (afterErasure(e1.sym.info =:= e.sym.info)) doubleDefError(e.sym, e1.sym) e1 = decls.lookupNextEntry(e1) } } @@ -659,10 +685,10 @@ abstract class Erasure extends AddInterfaces || !sym.hasTypeAt(currentRun.refchecksPhase.id)) override def matches(sym1: Symbol, sym2: Symbol): Boolean = - atPhase(phase.next)(sym1.tpe =:= sym2.tpe) + afterErasure(sym1.tpe =:= sym2.tpe) } while (opc.hasNext) { - if (!atPhase(currentRun.refchecksPhase.next)( + if (!afterRefchecks( root.thisType.memberType(opc.overriding) matches root.thisType.memberType(opc.overridden))) { debuglog("" + opc.overriding.locationString + " " + @@ -681,8 +707,8 @@ abstract class Erasure extends AddInterfaces for (member <- root.info.nonPrivateMember(other.name).alternatives) { if (member != other && !(member hasFlag BRIDGE) && - atPhase(phase.next)(member.tpe =:= other.tpe) && - !atPhase(refchecksPhase.next)( + afterErasure(member.tpe =:= other.tpe) && + !afterRefchecks( root.thisType.memberType(member) matches root.thisType.memberType(other))) { debuglog("" + member.locationString + " " + member.infosString + other.locationString + " " + other.infosString); doubleDefError(member, other) @@ -706,13 +732,13 @@ abstract class Erasure extends AddInterfaces */ private def bridgeDefs(owner: Symbol): (List[Tree], immutable.Set[Symbol]) = { var toBeRemoved: immutable.Set[Symbol] = immutable.Set() - //println("computing bridges for " + owner)//DEBUG - assert(phase == currentRun.erasurePhase) + debuglog("computing bridges for " + owner)//DEBUG + assert(phase == currentRun.erasurePhase, phase) val site = owner.thisType val bridgesScope = newScope val bridgeTarget = new mutable.HashMap[Symbol, Symbol] var bridges: List[Tree] = List() - val opc = atPhase(currentRun.explicitouterPhase) { + val opc = beforeExplicitOuter { new overridingPairs.Cursor(owner) { override def parents: List[Type] = List(owner.info.firstParent) override def exclude(sym: Symbol): Boolean = @@ -723,9 +749,9 @@ abstract class Erasure extends AddInterfaces val member = opc.overriding val other = opc.overridden //println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString)//DEBUG - if (atPhase(currentRun.explicitouterPhase)(!member.isDeferred)) { + if (beforeExplicitOuter(!member.isDeferred)) { val otpe = erasure(owner, other.tpe) - val bridgeNeeded = atPhase(phase.next) ( + val bridgeNeeded = afterErasure ( !(other.tpe =:= member.tpe) && !(deconstMap(other.tpe) =:= deconstMap(member.tpe)) && { var e = bridgesScope.lookupEntry(member.name) @@ -740,15 +766,15 @@ abstract class Erasure extends AddInterfaces // the parameter symbols need to have the new owner bridge.setInfo(otpe.cloneInfo(bridge)) bridgeTarget(bridge) = member - atPhase(phase.next) { owner.info.decls.enter(bridge) } + afterErasure { owner.info.decls.enter(bridge) } if (other.owner == owner) { //println("bridge to same: "+other+other.locationString)//DEBUG - atPhase(phase.next) { owner.info.decls.unlink(other) } + afterErasure { owner.info.decls.unlink(other) } toBeRemoved += other } bridgesScope enter bridge bridges = - atPhase(phase.next) { + afterErasure { atPos(bridge.pos) { val bridgeDef = DefDef(bridge, @@ -762,7 +788,7 @@ abstract class Erasure extends AddInterfaces if ( member.isSynthetic // TODO: should we do this for user-defined unapplies as well? && ((member.name == nme.unapply) || (member.name == nme.unapplySeq)) // && (bridge.paramss.nonEmpty && bridge.paramss.head.nonEmpty && bridge.paramss.head.tail.isEmpty) // does the first argument list has exactly one argument -- for user-defined unapplies we can't be sure - && !(atPhase(phase.next)(member.tpe <:< other.tpe))) { // no static guarantees (TODO: is the subtype test ever true?) + && !(afterErasure(member.tpe <:< other.tpe))) { // no static guarantees (TODO: is the subtype test ever true?) import CODE._ val typeTest = gen.mkIsInstanceOf(REF(bridge.firstParam), member.tpe.params.head.tpe, any = true, wrapInApply = true) // any = true since we're before erasure (?), wrapInapply is true since we're after uncurry // println("unapp type test: "+ typeTest) @@ -819,11 +845,11 @@ abstract class Erasure extends AddInterfaces */ private val preTransformer = new TypingTransformer(unit) { def preErase(tree: Tree): Tree = tree match { - case ClassDef(mods, name, tparams, impl) => + case ClassDef(_,_,_,_) => debuglog("defs of " + tree.symbol + " = " + tree.symbol.info.decls) - treeCopy.ClassDef(tree, mods, name, List(), impl) - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - treeCopy.DefDef(tree, mods, name, List(), vparamss, tpt, rhs) + copyClassDef(tree)(tparams = Nil) + case DefDef(_,_,_,_,_,_) => + copyDefDef(tree)(tparams = Nil) case TypeDef(_, _, _, _) => EmptyTree case Apply(instanceOf @ TypeApply(fun @ Select(qual, name), args @ List(arg)), List()) // !!! todo: simplify by having GenericArray also extract trees @@ -1025,7 +1051,7 @@ abstract class Erasure extends AddInterfaces */ override def transform(tree: Tree): Tree = { val tree1 = preTransformer.transform(tree) - atPhase(phase.next) { + afterErasure { val tree2 = mixinTransformer.transform(tree1) debuglog("tree after addinterfaces: \n" + tree2) diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 7f7f7e7b65..595c1486b6 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -68,7 +68,7 @@ abstract class ExplicitOuter extends InfoTransform result } - + private val innerClassConstructorParamName: TermName = newTermName("arg" + nme.OUTER) class RemoveBindingsTransformer(toRemove: Set[Symbol]) extends Transformer { @@ -89,13 +89,13 @@ abstract class ExplicitOuter extends InfoTransform def outerAccessor(clazz: Symbol): Symbol = { val firstTry = clazz.info.decl(nme.expandedName(nme.OUTER, clazz)) if (firstTry != NoSymbol && firstTry.outerSource == clazz) firstTry - else clazz.info.decls find (_.outerSource == clazz) getOrElse NoSymbol - } + else findOrElse(clazz.info.decls)(_.outerSource == clazz)(NoSymbol) + } def newOuterAccessor(clazz: Symbol) = { val accFlags = SYNTHETIC | METHOD | STABLE | ( if (clazz.isTrait) DEFERRED else 0 ) val sym = clazz.newMethodSymbol(nme.OUTER, clazz.pos, accFlags) val restpe = if (clazz.isTrait) clazz.outerClass.tpe else clazz.outerClass.thisType - + sym expandName clazz sym.referenced = clazz sym setInfo MethodType(Nil, restpe) @@ -163,14 +163,14 @@ abstract class ExplicitOuter extends InfoTransform decls1 = decls.cloneScope val outerAcc = clazz.newMethod(nme.OUTER, clazz.pos) // 3 outerAcc expandName clazz - + decls1 enter newOuterAccessor(clazz) if (hasOuterField(clazz)) //2 decls1 enter newOuterField(clazz) } if (!clazz.isTrait && !parents.isEmpty) { for (mc <- clazz.mixinClasses) { - val mixinOuterAcc: Symbol = atPhase(phase.next)(outerAccessor(mc)) + val mixinOuterAcc: Symbol = afterExplicitOuter(outerAccessor(mc)) if (mixinOuterAcc != NoSymbol) { if (decls1 eq decls) decls1 = decls.cloneScope val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED) @@ -468,10 +468,12 @@ abstract class ExplicitOuter extends InfoTransform } } super.transform( - treeCopy.Template(tree, parents, self, - if (newDefs.isEmpty) decls else decls ::: newDefs.toList) + deriveTemplate(tree)(decls => + if (newDefs.isEmpty) decls + else decls ::: newDefs.toList + ) ) - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case DefDef(_, _, _, vparamss, _, rhs) => if (sym.isClassConstructor) { rhs match { case Literal(_) => @@ -484,7 +486,7 @@ abstract class ExplicitOuter extends InfoTransform sym.newValueParameter(nme.OUTER, sym.pos) setInfo outerField(clazz).info ((ValDef(outerParam) setType NoType) :: vparamss.head) :: vparamss.tail } else vparamss - super.transform(treeCopy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs)) + super.transform(copyDefDef(tree)(vparamss = vparamss1)) } } else super.transform(tree) @@ -517,7 +519,7 @@ abstract class ExplicitOuter extends InfoTransform super.transform(treeCopy.Apply(tree, sel, outerVal :: args)) // entry point for pattern matcher translation - case mch: Match => + case mch: Match if (!opt.virtPatmat) => // don't use old pattern matcher as fallback when the user wants the virtualizing one matchTranslation(mch) case _ => @@ -559,7 +561,7 @@ abstract class ExplicitOuter extends InfoTransform /** The transformation method for whole compilation units */ override def transformUnit(unit: CompilationUnit) { - atPhase(phase.next)(super.transformUnit(unit)) + afterExplicitOuter(super.transformUnit(unit)) } } diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index 4fa5b52de3..8856024a30 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -20,16 +20,14 @@ abstract class Flatten extends InfoTransform { /** Updates the owning scope with the given symbol; returns the old symbol. */ - private def replaceSymbolInCurrentScope(sym: Symbol): Symbol = { - atPhase(phase.next) { - val scope = sym.owner.info.decls - val old = scope lookup sym.name - if (old ne NoSymbol) - scope unlink old + private def replaceSymbolInCurrentScope(sym: Symbol): Symbol = afterFlatten { + val scope = sym.owner.info.decls + val old = scope lookup sym.name + if (old ne NoSymbol) + scope unlink old - scope enter sym - old - } + scope enter sym + old } private def liftClass(sym: Symbol) { @@ -53,24 +51,26 @@ abstract class Flatten extends InfoTransform { val clazz = pre.typeSymbol clazz.isClass && !clazz.isPackageClass && { // Cannot flatten here: class A[T] { object B } - atPhase(currentRun.erasurePhase.prev)(clazz.typeParams.isEmpty) + // was "at erasurePhase.prev" + beforeErasure(clazz.typeParams.isEmpty) } } private val flattened = new TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, args) if isFlattenablePrefix(pre) => - assert(args.isEmpty && sym.toplevelClass != NoSymbol, sym.ownerChain) - typeRef(sym.toplevelClass.owner.thisType, sym, Nil) + assert(args.isEmpty && sym.enclosingTopLevelClass != NoSymbol, sym.ownerChain) + typeRef(sym.enclosingTopLevelClass.owner.thisType, sym, Nil) case ClassInfoType(parents, decls, clazz) => var parents1 = parents val decls1 = scopeTransform(clazz) { val decls1 = newScope if (clazz.isPackageClass) { - atPhase(phase.next)(decls foreach (decls1 enter _)) - } else { + afterFlatten { decls foreach (decls1 enter _) } + } + else { val oldowner = clazz.owner - atPhase(phase.next)(oldowner.info) + afterFlatten { oldowner.info } parents1 = parents mapConserve (this) for (sym <- decls) { @@ -102,7 +102,7 @@ abstract class Flatten extends InfoTransform { class Flattener extends Transformer { /** Buffers for lifted out classes */ - private val liftedDefs = new mutable.HashMap[Symbol, ListBuffer[Tree]] + private val liftedDefs = perRunCaches.newMap[Symbol, ListBuffer[Tree]]() override def transform(tree: Tree): Tree = { tree match { @@ -119,14 +119,10 @@ abstract class Flatten extends InfoTransform { val sym = tree.symbol val tree1 = tree match { case ClassDef(_, _, _, _) if sym.isNestedClass => - liftedDefs(sym.toplevelClass.owner) += tree + liftedDefs(sym.enclosingTopLevelClass.owner) += tree EmptyTree case Select(qual, name) if (sym.isStaticModule && !sym.owner.isPackageClass) => - atPhase(phase.next) { - atPos(tree.pos) { - gen.mkAttributedRef(sym) - } - } + afterFlatten(atPos(tree.pos)(gen.mkAttributedRef(sym))) case _ => tree } diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 712298bd89..570eaba3a9 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -18,7 +18,7 @@ abstract class LambdaLift extends InfoTransform { /** the following two members override abstract members in Transform */ val phaseName: String = "lambdalift" - + /** Converts types of captured variables to *Ref types. */ def boxIfCaptured(sym: Symbol, tpe: Type, erasedTypes: Boolean) = @@ -65,17 +65,20 @@ abstract class LambdaLift extends InfoTransform { /** The set of symbols that need to be renamed. */ private val renamable = newSymSet - private val renamableImplClasses = mutable.HashMap[Name, Symbol]() withDefaultValue NoSymbol + // (trait, name) -> owner + private val localTraits = mutable.HashMap[(Symbol, Name), Symbol]() + // (owner, name) -> implClass + private val localImplClasses = mutable.HashMap[(Symbol, Name), Symbol]() /** A flag to indicate whether new free variables have been found */ private var changedFreeVars: Boolean = _ /** Buffers for lifted out classes and methods */ private val liftedDefs = new LinkedHashMap[Symbol, List[Tree]] - + /** True if we are transforming under a ReferenceToBoxed node */ private var isBoxedRef = false - + private type SymSet = TreeSet[Symbol] private def newSymSet = new TreeSet[Symbol](_ isLess _) @@ -125,7 +128,7 @@ abstract class LambdaLift extends InfoTransform { if (!ss(sym)) { ss addEntry sym renamable addEntry sym - atPhase(currentRun.picklerPhase) { + beforePickler { // The param symbol in the MethodType should not be renamed, only the symbol in scope. This way, // parameter names for named arguments are not changed. Example: without cloning the MethodType, // def closure(x: Int) = { () => x } @@ -167,8 +170,13 @@ abstract class LambdaLift extends InfoTransform { // arrangements, and then have separate methods which attempt to compensate // for that failure. There should be exactly one method for any given // entity which always gives the right answer. - if (sym.isImplClass) renamableImplClasses(nme.interfaceName(sym.name)) = sym - else renamable addEntry sym + if (sym.isImplClass) + localImplClasses((sym.owner, nme.interfaceName(sym.name))) = sym + else { + renamable addEntry sym + if (sym.isTrait) + localTraits((sym, sym.name)) = sym.owner + } } case DefDef(_, _, _, _, _, _) => if (sym.isLocal) { @@ -213,7 +221,7 @@ abstract class LambdaLift extends InfoTransform { for (caller <- called.keys ; callee <- called(caller) ; fvs <- free get callee ; fv <- fvs) markFree(fv, caller) } while (changedFreeVars) - + def renameSym(sym: Symbol) { val originalName = sym.name val base = sym.name + nme.NAME_JOIN_STRING + ( @@ -241,13 +249,20 @@ abstract class LambdaLift extends InfoTransform { for (sym <- renamable) { // If we renamed a trait from Foo to Foo$1, we must rename the implementation // class from Foo$class to Foo$1$class. (Without special consideration it would - // become Foo$class$1 instead.) - val implClass = if (sym.isTrait) renamableImplClasses(sym.name) else NoSymbol - if ((implClass ne NoSymbol) && (sym.owner == implClass.owner)) renameTrait(sym, implClass) - else renameSym(sym) + // become Foo$class$1 instead.) Since the symbols are being renamed out from + // under us, and there's no reliable link between trait symbol and impl symbol, + // we have maps from ((trait, name)) -> owner and ((owner, name)) -> impl. + localTraits remove ((sym, sym.name)) match { + case None => renameSym(sym) + case Some(owner) => + localImplClasses remove ((owner, sym.name)) match { + case Some(implSym) => renameTrait(sym, implSym) + case _ => renameSym(sym) // pure interface, no impl class + } + } } - atPhase(phase.next) { + afterOwnPhase { for ((owner, freeValues) <- free.toList) { val newFlags = SYNTHETIC | ( if (owner.isClass) PARAMACCESSOR | PrivateLocal else PARAM ) debuglog("free var proxy: %s, %s".format(owner.fullLocationString, freeValues.toList.mkString(", "))) @@ -305,12 +320,13 @@ abstract class LambdaLift extends InfoTransform { case Some(ps) => val freeParams = ps map (p => ValDef(p) setPos tree.pos setType NoType) tree match { - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case DefDef(_, _, _, vparams :: _, _, _) => val addParams = cloneSymbols(ps).map(_.setFlag(PARAM)) sym.updateInfo( lifted(MethodType(sym.info.params ::: addParams, sym.info.resultType))) - treeCopy.DefDef(tree, mods, name, tparams, List(vparamss.head ++ freeParams), tpt, rhs) - case ClassDef(mods, name, tparams, impl @ Template(parents, self, body)) => + + copyDefDef(tree)(vparamss = List(vparams ++ freeParams)) + case ClassDef(_, _, _, _) => // Disabled attempt to to add getters to freeParams // this does not work yet. Problem is that local symbols need local names // and references to local symbols need to be transformed into @@ -322,8 +338,7 @@ abstract class LambdaLift extends InfoTransform { // DefDef(getter, rhs) setPos tree.pos setType NoType // } // val newDefs = if (sym.isTrait) freeParams ::: (ps map paramGetter) else freeParams - treeCopy.ClassDef(tree, mods, name, tparams, - treeCopy.Template(impl, parents, self, body ::: freeParams)) + deriveClassDef(tree)(impl => deriveTemplate(impl)(_ ::: freeParams)) } case None => tree @@ -404,10 +419,10 @@ abstract class LambdaLift extends InfoTransform { def refConstr(expr: Tree): Tree = expr match { case Try(block, catches, finalizer) => Try(refConstr(block), catches map refConstrCase, finalizer) - case _ => - Apply(Select(New(TypeTree(sym.tpe)), nme.CONSTRUCTOR), List(expr)) + case _ => + New(sym.tpe, expr) } - def refConstrCase(cdef: CaseDef): CaseDef = + def refConstrCase(cdef: CaseDef): CaseDef = CaseDef(cdef.pat, cdef.guard, refConstr(cdef.body)) treeCopy.ValDef(tree, mods, name, tpt1, typer.typedPos(rhs.pos) { refConstr(constructorArg) @@ -452,7 +467,7 @@ abstract class LambdaLift extends InfoTransform { tree } } - + private def preTransform(tree: Tree) = super.transform(tree) setType lifted(tree.tpe) override def transform(tree: Tree): Tree = tree match { @@ -461,21 +476,20 @@ abstract class LambdaLift extends InfoTransform { case _ => postTransform(preTransform(tree)) } - + /** Transform statements and add lifted definitions to them. */ override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { def addLifted(stat: Tree): Tree = stat match { - case ClassDef(mods, name, tparams, impl @ Template(parents, self, body)) => + case ClassDef(_, _, _, _) => val lifted = liftedDefs get stat.symbol match { case Some(xs) => xs reverseMap addLifted case _ => log("unexpectedly no lifted defs for " + stat.symbol) ; Nil } - val result = treeCopy.ClassDef( - stat, mods, name, tparams, treeCopy.Template(impl, parents, self, body ::: lifted)) - liftedDefs -= stat.symbol - result - case DefDef(mods, name, tp, vp, tpt, Block(Nil, expr)) if !stat.symbol.isConstructor => - treeCopy.DefDef(stat, mods, name, tp, vp, tpt, expr) + try deriveClassDef(stat)(impl => deriveTemplate(impl)(_ ::: lifted)) + finally liftedDefs -= stat.symbol + + case DefDef(_, _, _, _, _, Block(Nil, expr)) if !stat.symbol.isConstructor => + deriveDefDef(stat)(_ => expr) case _ => stat } @@ -484,7 +498,7 @@ abstract class LambdaLift extends InfoTransform { override def transformUnit(unit: CompilationUnit) { computeFreeVars - atPhase(phase.next)(super.transformUnit(unit)) + afterOwnPhase(super.transformUnit(unit)) assert(liftedDefs.isEmpty, liftedDefs.keys mkString ", ") } } // class LambdaLifter diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index f8c5f5bfc6..85ba539993 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -50,9 +50,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD */ class LazyValues(unit: CompilationUnit) extends TypingTransformer(unit) { /** map from method symbols to the number of lazy values it defines. */ - private val lazyVals = new mutable.HashMap[Symbol, Int] { - override def default(meth: Symbol) = 0 - } + private val lazyVals = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0 import symtab.Flags._ import lazyVals._ @@ -70,7 +68,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD curTree = tree tree match { - case DefDef(mods, name, tparams, vparams, tpt, rhs) => atOwner(tree.symbol) { + case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) { val res = if (!sym.owner.isClass && sym.isLazy) { val enclosingClassOrDummyOrMethod = { val enclMethod = sym.enclMethod @@ -92,11 +90,10 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD } else super.transform(rhs) - treeCopy.DefDef(tree, mods, name, tparams, vparams, tpt, - if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res) + deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res) } - case Template(parents, self, body) => atOwner(currentOwner) { + case Template(_, _, body) => atOwner(currentOwner) { val body1 = super.transformTrees(body) var added = false val stats = @@ -108,8 +105,8 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD added = true typed(addBitmapDefs(sym, stat)) } else stat - case ValDef(mods, name, tpt, rhs) => - typed(treeCopy.ValDef(stat, mods, name, tpt, addBitmapDefs(stat.symbol, rhs))) + case ValDef(_, _, _, _) => + typed(deriveValDef(stat)(addBitmapDefs(stat.symbol, _))) case _ => stat } @@ -124,29 +121,29 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD }) toAdd0 } else List() - treeCopy.Template(tree, parents, self, innerClassBitmaps ++ stats) + deriveTemplate(tree)(_ => innerClassBitmaps ++ stats) } - case ValDef(mods, name, tpt, rhs0) if (!sym.owner.isModule && !sym.owner.isClass) => - val rhs = super.transform(rhs0) - treeCopy.ValDef(tree, mods, name, tpt, - if (LocalLazyValFinder.find(rhs)) typed(addBitmapDefs(sym, rhs)) else rhs) + case ValDef(_, _, _, _) if !sym.owner.isModule && !sym.owner.isClass => + deriveValDef(tree) { rhs0 => + val rhs = super.transform(rhs0) + if (LocalLazyValFinder.find(rhs)) typed(addBitmapDefs(sym, rhs)) else rhs + } case l@LabelDef(name0, params0, ifp0@If(_, _, _)) if name0.startsWith(nme.WHILE_PREFIX) => val ifp1 = super.transform(ifp0) val If(cond0, thenp0, elsep0) = ifp1 + if (LocalLazyValFinder.find(thenp0)) - treeCopy.LabelDef(l, name0, params0, - treeCopy.If(ifp1, cond0, typed(addBitmapDefs(sym.owner, thenp0)), elsep0)) + deriveLabelDef(l)(_ => treeCopy.If(ifp1, cond0, typed(addBitmapDefs(sym.owner, thenp0)), elsep0)) else l - case l@LabelDef(name0, params0, block@Block(stats0, _)) + case l@LabelDef(name0, params0, block@Block(stats0, expr)) if name0.startsWith(nme.WHILE_PREFIX) || name0.startsWith(nme.DO_WHILE_PREFIX) => val stats1 = super.transformTrees(stats0) if (LocalLazyValFinder.find(stats1)) - treeCopy.LabelDef(l, name0, params0, - treeCopy.Block(block, typed(addBitmapDefs(sym.owner, stats1.head))::stats1.tail, block.expr)) + deriveLabelDef(l)(_ => treeCopy.Block(block, typed(addBitmapDefs(sym.owner, stats1.head))::stats1.tail, expr)) else l @@ -171,9 +168,9 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD def isMatch(params: List[Ident]) = (params.tail corresponds methSym.tpe.params)(_.tpe == _.tpe) if (bmps.isEmpty) rhs else rhs match { - case Block(assign, l @ LabelDef(name, params, rhs1)) + case Block(assign, l @ LabelDef(name, params, _)) if name.toString == ("_" + methSym.name) && isMatch(params) => - Block(assign, treeCopy.LabelDef(l, name, params, typed(prependStats(bmps, rhs1)))) + Block(assign, deriveLabelDef(l)(rhs => typed(prependStats(bmps, rhs)))) case _ => prependStats(bmps, rhs) } @@ -233,9 +230,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD private def mkSetFlag(bmp: Symbol, mask: Tree, bmpRef: Tree): Tree = bmpRef === (bmpRef INT_| mask) - val bitmaps = new mutable.HashMap[Symbol, List[Symbol]] { - override def default(meth: Symbol) = Nil - } + val bitmaps = mutable.Map[Symbol, List[Symbol]]() withDefaultValue Nil /** Return the symbol corresponding of the right bitmap int inside meth, * given offset. @@ -247,7 +242,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD bmps(n) else { val sym = meth.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), meth.pos).setInfo(IntClass.tpe) - atPhase(currentRun.typerPhase) { + beforeTyper { sym addAnnotation VolatileAttr } diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index b3b7596f9a..c9794cc20f 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -71,7 +71,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * maps all other types to themselves. */ private def toInterface(tp: Type): Type = - atPhase(currentRun.mixinPhase)(tp.typeSymbol.toInterface).tpe + beforeMixin(tp.typeSymbol.toInterface).tpe private def isFieldWithBitmap(field: Symbol) = { field.info // ensure that nested objects are transformed @@ -103,7 +103,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { private val toInterfaceMap = new TypeMap { def apply(tp: Type): Type = mapOver( tp match { case TypeRef(pre, sym, args) if (sym.isImplClass) => - typeRef(pre, atPhase(currentRun.mixinPhase)(sym.toInterface), args) + typeRef(pre, beforeMixin(sym.toInterface), args) case _ => tp }) } @@ -123,7 +123,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * @param mixinClass The mixin class that produced the superaccessor */ private def rebindSuper(base: Symbol, member: Symbol, mixinClass: Symbol): Symbol = - atPhase(currentRun.picklerPhase.next) { + afterPickler { var bcs = base.info.baseClasses.dropWhile(mixinClass !=).tail var sym: Symbol = NoSymbol debuglog("starting rebindsuper " + base + " " + member + ":" + member.tpe + @@ -131,7 +131,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { while (!bcs.isEmpty && sym == NoSymbol) { if (settings.debug.value) { val other = bcs.head.info.nonPrivateDecl(member.name); - log("rebindsuper " + bcs.head + " " + other + " " + other.tpe + + debuglog("rebindsuper " + bcs.head + " " + other + " " + other.tpe + " " + other.isDeferred) } sym = member.matchingSymbol(bcs.head, base.thisType).suchThat(sym => !sym.hasFlag(DEFERRED | BRIDGE)) @@ -147,7 +147,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { member.hasAccessorFlag && (!member.isDeferred || (member hasFlag lateDEFERRED)) /** Is member overridden (either directly or via a bridge) in base class sequence `bcs`? */ - def isOverriddenAccessor(member: Symbol, bcs: List[Symbol]): Boolean = atPhase(ownPhase) { + def isOverriddenAccessor(member: Symbol, bcs: List[Symbol]): Boolean = beforeOwnPhase { def hasOverridingAccessor(clazz: Symbol) = { clazz.info.nonPrivateDecl(member.name).alternatives.exists( sym => @@ -155,8 +155,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { !sym.hasFlag(MIXEDIN) && matchesType(sym.tpe, member.tpe, true)) } - bcs.head != member.owner && - (hasOverridingAccessor(bcs.head) || isOverriddenAccessor(member, bcs.tail)) + ( bcs.head != member.owner + && (hasOverridingAccessor(bcs.head) || isOverriddenAccessor(member, bcs.tail)) + ) } /** Add given member to given class, and mark member as mixed-in. @@ -202,7 +203,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { setter setInfo MethodType(setter.newSyntheticValueParams(List(field.info)), UnitClass.tpe) if (needsExpandedSetterName(field)) setter.name = nme.expandedSetterName(setter.name, clazz) - + setter } @@ -241,7 +242,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ def addMixedinMembers(clazz: Symbol, unit : CompilationUnit) { def cloneBeforeErasure(iface: Symbol, clazz: Symbol, imember: Symbol): Symbol = { - val newSym = atPhase(currentRun.erasurePhase) { + val newSym = beforeErasure { val res = imember.cloneSymbol(clazz) // since we used the member (imember) from the interface that represents the trait that's being mixed in, // have to instantiate the interface type params (that may occur in imember's info) as they are seen from the class @@ -337,8 +338,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case _ => // otherwise mixin a field as well // atPhase: the private field is moved to the implementation class by erasure, // so it can no longer be found in the member's owner (the trait) - val accessed = atPhase(currentRun.picklerPhase)(member.accessed) - val sym = atPhase(currentRun.erasurePhase){ // #3857, need to retain info before erasure when cloning (since cloning only carries over the current entry in the type history) + val accessed = beforePickler(member.accessed) + val sym = beforeErasure { // #3857, need to retain info before erasure when cloning (since cloning only carries over the current entry in the type history) clazz.newValue(nme.getterToLocal(member.name), member.pos).setInfo(member.tpe.resultType) // so we have a type history entry before erasure } sym.updateInfo(member.tpe.resultType) // info at current phase @@ -349,13 +350,15 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { setAnnotations accessed.annotations) } } - } else if (member.isSuperAccessor) { // mixin super accessors + } + else if (member.isSuperAccessor) { // mixin super accessors val member1 = addMember(clazz, member.cloneSymbol(clazz)) setPos clazz.pos assert(member1.alias != NoSymbol, member1) val alias1 = rebindSuper(clazz, member.alias, mixinClass) member1.asInstanceOf[TermSymbol] setAlias alias1 - } else if (member.isMethod && member.isModule && member.hasNoFlags(LIFTED | BRIDGE)) { + } + else if (member.isMethod && member.isModule && member.hasNoFlags(LIFTED | BRIDGE)) { // mixin objects: todo what happens with abstract objects? addMember(clazz, member.cloneSymbol(clazz, member.flags & ~(DEFERRED | lateDEFERRED)) setPos clazz.pos) } @@ -383,7 +386,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { var parents1 = parents var decls1 = decls if (!clazz.isPackageClass) { - atPhase(phase.next)(clazz.owner.info) + afterMixin(clazz.owner.info) if (clazz.isImplClass) { clazz setFlag lateMODULE var sourceModule = clazz.owner.info.decls.lookup(sym.name.toTermName) @@ -449,7 +452,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { && sym.owner == templ.symbol.owner && !sym.isLazy && !tree.isDef) { - log("added use in: " + currentOwner + " -- " + tree) + debuglog("added use in: " + currentOwner + " -- " + tree) usedIn(sym) ::= currentOwner } @@ -459,7 +462,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } } SingleUseTraverser(templ) - log("usedIn: " + usedIn) + debuglog("usedIn: " + usedIn) usedIn filter { case (_, member :: Nil) => member.isValue && member.isLazy case _ => false @@ -515,7 +518,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { tree match { case Template(parents, self, body) => localTyper = erasure.newTyper(rootContext.make(tree, currentOwner)) - atPhase(phase.next)(currentOwner.owner.info)//todo: needed? + afterMixin(currentOwner.owner.info)//todo: needed? if (!currentOwner.isTrait && !isValueClass(currentOwner)) addMixedinMembers(currentOwner, unit) @@ -523,18 +526,18 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { addLateInterfaceMembers(currentOwner) tree - case DefDef(mods, name, tparams, List(vparams), tpt, rhs) => + case DefDef(_, _, _, vparams :: Nil, _, _) => if (currentOwner.isImplClass) { if (isImplementedStatically(sym)) { sym setFlag notOVERRIDE self = sym.newValueParameter(nme.SELF, sym.pos) setInfo toInterface(currentOwner.typeOfThis) val selfdef = ValDef(self) setType NoType - treeCopy.DefDef(tree, mods, name, tparams, List(selfdef :: vparams), tpt, rhs) - } else { - EmptyTree + copyDefDef(tree)(vparamss = List(selfdef :: vparams)) } - } else { - if (currentOwner.isTrait && sym.isSetter && !atPhase(currentRun.picklerPhase)(sym.isDeferred)) { + else EmptyTree + } + else { + if (currentOwner.isTrait && sym.isSetter && !beforePickler(sym.isDeferred)) { sym.addAnnotation(TraitSetterAnnotationClass) } tree @@ -699,15 +702,11 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * This rhs is typed and then mixin transformed. */ def completeSuperAccessor(stat: Tree) = stat match { - case DefDef(mods, name, tparams, List(vparams), tpt, EmptyTree) if stat.symbol.isSuperAccessor => + case DefDef(_, _, _, vparams :: Nil, _, EmptyTree) if stat.symbol.isSuperAccessor => val rhs0 = (Super(clazz, tpnme.EMPTY) DOT stat.symbol.alias)(vparams map (v => Ident(v.symbol)): _*) val rhs1 = localTyped(stat.pos, rhs0, stat.symbol.tpe.resultType) - val rhs2 = atPhase(currentRun.mixinPhase)(transform(rhs1)) - debuglog("complete super acc " + stat.symbol.fullLocationString + - " " + rhs1 + " " + stat.symbol.alias.fullLocationString + - "/" + stat.symbol.alias.owner.hasFlag(lateINTERFACE))//debug - treeCopy.DefDef(stat, mods, name, tparams, List(vparams), tpt, rhs2) + deriveDefDef(stat)(_ => beforeMixin(transform(rhs1))) case _ => stat } @@ -738,7 +737,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def createBitmap: Symbol = { val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo IntClass.tpe - atPhase(currentRun.typerPhase)(sym addAnnotation VolatileAttr) + beforeTyper(sym addAnnotation VolatileAttr) category match { case nme.BITMAP_TRANSIENT | nme.BITMAP_CHECKINIT_TRANSIENT => sym addAnnotation TransientAttr @@ -846,7 +845,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val nulls = lazyValNullables(lzyVal).toList sortBy (_.id) map nullify def syncBody = init ::: List(mkSetFlag(clazz, offset, lzyVal), UNIT) - log("nulling fields inside " + lzyVal + ": " + nulls) + if (nulls.nonEmpty) + log("nulling fields inside " + lzyVal + ": " + nulls) + val result = gen.mkDoubleCheckedLocking(clazz, cond, syncBody, nulls) typedPos(init.head.pos)(BLOCK(result, retVal)) } @@ -883,14 +884,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ def addCheckedGetters(clazz: Symbol, stats: List[Tree]): List[Tree] = { def dd(stat: DefDef) = { - val DefDef(mods, name, tp, vp, tpt, rhs) = stat - val sym = stat.symbol - def isUnit = sym.tpe.resultType.typeSymbol == UnitClass - def isEmpty = rhs == EmptyTree + val sym = stat.symbol + def isUnit = sym.tpe.resultType.typeSymbol == UnitClass + def isEmpty = stat.rhs == EmptyTree if (sym.isLazy && !isEmpty && !clazz.isImplClass) { assert(fieldOffset contains sym, sym) - treeCopy.DefDef(stat, mods, name, tp, vp, tpt, + deriveDefDef(stat)(rhs => if (isUnit) mkLazyDef(clazz, sym, List(rhs), UNIT, fieldOffset(sym)) else { @@ -901,7 +901,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } else if (needsInitFlag(sym) && !isEmpty && !clazz.hasFlag(IMPLCLASS | TRAIT)) { assert(fieldOffset contains sym, sym) - treeCopy.DefDef(stat, mods, name, tp, vp, tpt, + deriveDefDef(stat)(rhs => (mkCheckedAccessor(clazz, _: Tree, fieldOffset(sym), stat.pos, sym))( if (sym.tpe.resultType.typeSymbol == UnitClass) UNIT else rhs @@ -909,26 +909,24 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { ) } else if (sym.isConstructor) { - treeCopy.DefDef(stat, mods, name, tp, vp, tpt, addInitBits(clazz, rhs)) + deriveDefDef(stat)(addInitBits(clazz, _)) } else if (settings.checkInit.value && !clazz.isTrait && sym.isSetter) { val getter = sym.getter(clazz) if (needsInitFlag(getter) && fieldOffset.isDefinedAt(getter)) - treeCopy.DefDef(stat, mods, name, tp, vp, tpt, - Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter))), UNIT) - ) + deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter))), UNIT)) else stat } else if (sym.isModule && (!clazz.isTrait || clazz.isImplClass) && !sym.isBridge) { - treeCopy.DefDef(stat, mods, name, tp, vp, tpt, - typedPos(stat.pos) { + deriveDefDef(stat)(rhs => + typedPos(stat.pos)( mkInnerClassAccessorDoubleChecked( // Martin to Hubert: I think this can be replaced by selfRef(tree.pos) // @PP: It does not seem so, it crashes for me trying to bootstrap. - if (clazz.isImplClass) gen.mkAttributedIdent(vp.head.head.symbol) else gen.mkAttributedThis(clazz), + if (clazz.isImplClass) gen.mkAttributedIdent(stat.vparamss.head.head.symbol) else gen.mkAttributedThis(clazz), rhs ) - } + ) ) } else stat @@ -943,7 +941,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { private def checkedGetter(lhs: Tree) = { val sym = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter) if (needsInitAndHasOffset(sym)) { - log("adding checked getter for: " + sym + " " + lhs.symbol.defaultFlagString) + debuglog("adding checked getter for: " + sym + " " + lhs.symbol.defaultFlagString) List(localTyper typed mkSetFlag(clazz, fieldOffset(sym), sym)) } else Nil @@ -1130,7 +1128,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ private def postTransform(tree: Tree): Tree = { val sym = tree.symbol - // assert(tree.tpe ne null, tree.getClass +" : "+ tree +" in "+ localTyper.context.tree) // change every node type that refers to an implementation class to its // corresponding interface, unless the node's symbol is an implementation class. if (tree.tpe.typeSymbol.isImplClass && ((sym eq null) || !sym.isImplClass)) @@ -1163,7 +1160,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def implSym = implClass(sym.owner).info.member(sym.name) assert(target ne NoSymbol, List(sym + ":", sym.tpe, sym.owner, implClass(sym.owner), implSym, - atPhase(phase.prev)(implSym.tpe), phase) mkString " " + beforePrevPhase(implSym.tpe), phase) mkString " " ) typedPos(tree.pos)(Apply(staticRef(target), transformSuper(qual) :: args)) } @@ -1188,7 +1185,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val sym1 = sym.overridingSymbol(currentOwner.enclClass) typedPos(tree.pos)((transformSuper(qual) DOT sym1)()) } else { - staticCall(atPhase(phase.prev)(sym.overridingSymbol(implClass(sym.owner)))) + staticCall(beforePrevPhase(sym.overridingSymbol(implClass(sym.owner)))) } } else { assert(!currentOwner.enclClass.isImplClass, currentOwner.enclClass) @@ -1237,7 +1234,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val tree1 = super.transform(preTransform(tree)) // localTyper needed when not flattening inner classes. parts after an // inner class will otherwise be typechecked with a wrong scope - try atPhase(phase.next)(postTransform(tree1)) + try afterMixin(postTransform(tree1)) finally localTyper = saved } } diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index 1200e973c5..d8c18c2d50 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -45,8 +45,14 @@ abstract class OverridingPairs { * Types always match. Term symbols match if their membertypes * relative to <base>.this do */ - protected def matches(sym1: Symbol, sym2: Symbol): Boolean = - sym1.isType || (self.memberType(sym1) matches self.memberType(sym2)) + protected def matches(sym1: Symbol, sym2: Symbol): Boolean = { + def tp_s(s: Symbol) = self.memberType(s) + "/" + self.memberType(s).getClass + val result = sym1.isType || (self.memberType(sym1) matches self.memberType(sym2)) + debuglog("overriding-pairs? %s matches %s (%s vs. %s) == %s".format( + sym1.fullLocationString, sym2.fullLocationString, tp_s(sym1), tp_s(sym2), result)) + + result + } /** An implementation of BitSets as arrays (maybe consider collection.BitSet * for that?) The main purpose of this is to implement diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index c1265b39d7..323fecfd0a 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -6,12 +6,9 @@ package scala.tools.nsc package transform - import scala.tools.nsc.symtab.Flags import scala.collection.{ mutable, immutable } - - /** Specialize code on types. * * Make sure you've read the thesis: @@ -71,10 +68,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { RootClass, BooleanClass, UnitClass, ArrayClass, ScalaValueClasses, isValueClass, isScalaValueType, SpecializedClass, RepeatedParamClass, JavaRepeatedParamClass, - AnyRefClass, ObjectClass, Predef_AnyRef, - uncheckedVarianceClass + AnyRefClass, ObjectClass, AnyRefModule, + GroupOfSpecializable, uncheckedVarianceClass, ScalaInlineClass } - + /** TODO - this is a lot of maps. */ @@ -82,17 +79,17 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val specializedClass: mutable.Map[(Symbol, TypeEnv), Symbol] = new mutable.LinkedHashMap /** Map a method symbol to a list of its specialized overloads in the same class. */ - private val overloads: mutable.Map[Symbol, List[Overload]] = mutable.HashMap[Symbol, List[Overload]]() withDefaultValue Nil + private val overloads = perRunCaches.newMap[Symbol, List[Overload]]() withDefaultValue Nil /** Map a symbol to additional information on specialization. */ - private val info: mutable.Map[Symbol, SpecializedInfo] = perRunCaches.newMap[Symbol, SpecializedInfo]() + private val info = perRunCaches.newMap[Symbol, SpecializedInfo]() /** Map class symbols to the type environments where they were created. */ - private val typeEnv = mutable.HashMap[Symbol, TypeEnv]() withDefaultValue emptyEnv + private val typeEnv = perRunCaches.newMap[Symbol, TypeEnv]() withDefaultValue emptyEnv - // holds mappings from regular type parameter symbols to symbols of - // specialized type parameters which are subtypes of AnyRef - private val anyrefSpecCache = perRunCaches.newMap[Symbol, Symbol]() + // Key: a specialized class or method + // Value: a map from tparams in the original class to tparams in the specialized class. + private val anyrefSpecCache = perRunCaches.newMap[Symbol, mutable.Map[Symbol, Symbol]]() // holds mappings from members to the type variables in the class // that they were already specialized for, so that they don't get @@ -100,30 +97,53 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { private val wasSpecializedForTypeVars = perRunCaches.newMap[Symbol, Set[Symbol]]() withDefaultValue Set() /** Concrete methods that use a specialized type, or override such methods. */ - private val concreteSpecMethods = new mutable.HashSet[Symbol]() + private val concreteSpecMethods = perRunCaches.newWeakSet[Symbol]() private def isSpecialized(sym: Symbol) = sym hasAnnotation SpecializedClass private def hasSpecializedFlag(sym: Symbol) = sym hasFlag SPECIALIZED private def specializedTypes(tps: List[Symbol]) = tps filter isSpecialized - private def specializedOn(sym: Symbol) = sym getAnnotation SpecializedClass match { - case Some(AnnotationInfo(_, args, _)) => args - case _ => Nil + private def specializedOn(sym: Symbol): List[Symbol] = { + sym getAnnotation SpecializedClass match { + case Some(AnnotationInfo(_, Nil, _)) => specializableTypes.map(_.typeSymbol) + case Some(ann @ AnnotationInfo(_, args, _)) => { + args map (_.tpe) flatMap { tp => + tp baseType GroupOfSpecializable match { + case TypeRef(_, GroupOfSpecializable, arg :: Nil) => + arg.typeArgs map (_.typeSymbol) + case _ => + List(tp.typeSymbol) + } + } + } + case _ => Nil + } } // If we replace `isBoundedGeneric` with (tp <:< AnyRefClass.tpe), // then pos/spec-List.scala fails - why? Does this kind of check fail // for similar reasons? Does `sym.isAbstractType` make a difference? - private def isSpecializedAnyRefSubtype(tp: Type, sym: Symbol) = ( - specializedOn(sym).exists(_.symbol == Predef_AnyRef) // specialized on AnyRef - && !isValueClass(tp.typeSymbol) - && isBoundedGeneric(tp) - ) + private def isSpecializedAnyRefSubtype(tp: Type, sym: Symbol) = { + specializedOn(sym).exists(s => !isValueClass(s)) && + !isValueClass(tp.typeSymbol) && + isBoundedGeneric(tp) + //(tp <:< AnyRefClass.tpe) + } private def isBoundedGeneric(tp: Type) = tp match { case TypeRef(_, sym, _) if sym.isAbstractType => (tp <:< AnyRefClass.tpe) case TypeRef(_, sym, _) => !isValueClass(sym) case _ => false } + def unspecializedSymbol(sym: Symbol): Symbol = { + if (sym hasFlag SPECIALIZED) { + // add initialization from its generic class constructor + val genericName = nme.unspecializedName(sym.name) + val member = sym.owner.info.decl(genericName.toTypeName) + member + } + else NoSymbol + } + object TypeEnv { /** Return a new type environment binding specialized type parameters of sym to * the given args. Expects the lists to have the same length. @@ -241,7 +261,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val stvTypeParams = specializedTypeVars(target.info.typeParams map (_.info)) val stvResult = specializedTypeVars(target.info.resultType) - log("degenerate: " + target + " stv tparams: " + stvTypeParams + " stv info: " + stvResult) + debuglog("degenerate: " + target + " stv tparams: " + stvTypeParams + " stv info: " + stvResult) (stvTypeParams -- stvResult).nonEmpty } @@ -272,10 +292,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val pre1 = this(pre) // when searching for a specialized class, take care to map all // type parameters that are subtypes of AnyRef to AnyRef - val args1 = map2(args, sym.typeParams) { - case (tp, orig) if isSpecializedAnyRefSubtype(tp, orig) => AnyRefClass.tpe - case (tp, _) => tp - } + val args1 = map2(args, sym.info.typeParams)((tp, orig) => + if (isSpecializedAnyRefSubtype(tp, orig)) AnyRefClass.tpe + else tp + ) specializedClass.get((sym, TypeEnv.fromSpecialization(sym, args1))) match { case Some(sym1) => typeRef(pre1, sym1, survivingArgs(sym, args)) case None => typeRef(pre1, sym, args) @@ -315,35 +335,38 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { nme.getterToLocal(specializedName(nme.localToGetter(name), types1, types2)) else { val (base, cs, ms) = nme.splitSpecializedName(name) - val abbrevs = definitions.abbrvTag withDefaultValue definitions.abbrvTag(ObjectClass) newTermName(base.toString + "$" - + "m" + ms + types1.map(t => abbrevs(t.typeSymbol)).mkString("", "", "") - + "c" + cs + types2.map(t => abbrevs(t.typeSymbol)).mkString("", "", "$sp")) + + "m" + ms + types1.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "") + + "c" + cs + types2.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "$sp")) } } - lazy val primitiveTypes = ScalaValueClasses map (_.tpe) + lazy val specializableTypes = (ScalaValueClasses :+ AnyRefClass) map (_.tpe) sorted + + /** If the symbol is the companion of a value class, the value class. + * Otherwise, AnyRef. + */ + def specializesClass(sym: Symbol): Symbol = { + val c = sym.companionClass + if (isValueClass(c)) c else AnyRefClass + } /** Return the types `sym` should be specialized at. This may be some of the primitive types * or AnyRef. AnyRef means that a new type parameter T will be generated later, known to be a * subtype of AnyRef (T <: AnyRef). * These are in a meaningful order for stability purposes. */ - def concreteTypes(sym: Symbol): List[Type] = ( - if (!isSpecialized(sym)) Nil // no @specialized Annotation - else specializedOn(sym) match { - case Nil => primitiveTypes // specialized on everything - case args => // specialized on args - (args map { tp => - if (tp.symbol == Predef_AnyRef) { - if (isBoundedGeneric(sym.tpe)) - reporter.warning(sym.pos, sym + " is always a subtype of " + AnyRefClass.tpe + ".") - AnyRefClass.tpe - } - else tp.symbol.companionClass.tpe - }).sorted - } - ) + def concreteTypes(sym: Symbol): List[Type] = { + val types = if (!isSpecialized(sym)) + Nil // no @specialized Annotation + else + specializedOn(sym) map (s => specializesClass(s).tpe) sorted + + if (isBoundedGeneric(sym.tpe) && (types contains AnyRefClass)) + reporter.warning(sym.pos, sym + " is always a subtype of " + AnyRefClass.tpe + ".") + + types + } /** Return a list of all type environments for all specializations * of @specialized types in `tps`. @@ -357,8 +380,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case set :: Nil => set map (x => List(x)) case set :: sets => for (x <- set ; xs <- loop(sets)) yield x :: xs } - // zip the keys with each permutation to create a TypeEnv - loop(keys map concreteTypes) map (xss => Map(keys zip xss: _*)) + // zip the keys with each permutation to create a TypeEnv. + // If we don't exclude the "all AnyRef" specialization, we will + // incur duplicate members and crash during mixin. + loop(keys map concreteTypes) filterNot (_ forall (_ <:< AnyRefClass.tpe)) map (xss => Map(keys zip xss: _*)) } /** Does the given 'sym' need to be specialized in the environment 'env'? @@ -382,8 +407,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { tpes foreach (tp => buf ++= specializedTypeVars(tp)) buf.result } - def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = - atPhase(currentRun.typerPhase)(specializedTypeVars(sym.info)) + def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = beforeTyper(specializedTypeVars(sym.info)) /** Return the set of @specialized type variables mentioned by the given type. * It only counts type variables that appear: @@ -415,27 +439,24 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case _ => Set() } - /** Returns the type parameter in the specialized class `clazz` that corresponds to type parameter - * `sym` in the original class. It will create it if needed or use the one from the cache. + /** Returns the type parameter in the specialized class `sClass` that corresponds to type parameter + * `tparam` in the original class. It will create it if needed or use the one from the cache. */ - private def typeParamSubAnyRef(sym: Symbol, clazz: Symbol) = ( - anyrefSpecCache.getOrElseUpdate(sym, - clazz.newTypeParameter(sym.name append nme.SPECIALIZED_SUFFIX_NAME toTypeName, sym.pos) - setInfo TypeBounds(sym.info.bounds.lo, AnyRefClass.tpe) + private def typeParamSubAnyRef(tparam: Symbol, sClass: Symbol): Type = { + val sClassMap = anyrefSpecCache.getOrElseUpdate(sClass, mutable.Map[Symbol, Symbol]()) + + sClassMap.getOrElseUpdate(tparam, + tparam.cloneSymbol(sClass, tparam.flags, tparam.name append tpnme.SPECIALIZED_SUFFIX) + modifyInfo (info => TypeBounds(info.bounds.lo, AnyRefClass.tpe)) ).tpe - ) + } /** Cleans the anyrefSpecCache of all type parameter symbols of a class. */ - private def cleanAnyRefSpecCache(clazz: Symbol, decls: List[Symbol]) = ( + private def cleanAnyRefSpecCache(clazz: Symbol, decls: List[Symbol]) { // remove class type parameters and those of normalized members. - clazz :: decls foreach { - _.tpe match { - case PolyType(tparams, _) => anyrefSpecCache --= tparams - case _ => () - } - } - ) + clazz :: decls foreach (anyrefSpecCache remove _) + } /** Type parameters that survive when specializing in the specified environment. */ def survivingParams(params: List[Symbol], env: TypeEnv) = @@ -490,11 +511,21 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * was both already used for a map and mucho long. So "sClass" is the * specialized subclass of "clazz" throughout this file. */ - val sClass = clazz.owner.newClass(specializedName(clazz, env0).toTypeName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE) + + // SI-5545: Eliminate classes with the same name loaded from the bytecode already present - all we need to do is + // to force .info on them, as their lazy type will be evaluated and the symbols will be eliminated. Unfortunately + // evaluating the info after creating the specialized class will mess the specialized class signature, so we'd + // better evaluate it before creating the new class symbol + val clazzName = specializedName(clazz, env0).toTypeName + val bytecodeClazz = clazz.owner.info.decl(clazzName) + debuglog("Specializing " + clazz + " found " + bytecodeClazz + " already there") + bytecodeClazz.info + + val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE) def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long) = member.cloneSymbol(sClass, flagFn(member.flags | SPECIALIZED)) - + sClass.sourceFile = clazz.sourceFile currentRun.symSource(sClass) = clazz.sourceFile // needed later on by mixin @@ -527,7 +558,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { var res: List[Type] = Nil // log(specializedClass + ": seeking specialized parents of class with parents: " + parents.map(_.typeSymbol)) for (p <- parents) { - val stp = atPhase(phase.next)(specializedType(p)) + val stp = afterSpecialize(specializedType(p)) if (stp != p) if (p.typeSymbol.isTrait) res ::= stp else if (currentRun.compiles(clazz)) @@ -537,7 +568,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { res } - var parents = List(applyContext(atPhase(currentRun.typerPhase)(clazz.tpe))) + var parents = List(applyContext(beforeTyper(clazz.tpe))) // log("!!! Parents: " + parents + ", sym: " + parents.map(_.typeSymbol)) if (parents.head.typeSymbol.isTrait) parents = parents.head.parents.head :: parents @@ -551,7 +582,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // as with the parents and assign it to typeOfThis. if (clazz.typeOfThis.typeConstructor ne clazz.typeConstructor) { sClass.typeOfThis = applyContext(clazz.typeOfThis) - log("Rewriting self-type for specialized class:\n" + + debuglog("Rewriting self-type for specialized class:\n" + " " + clazz.defStringSeenAs(clazz.typeOfThis) + "\n" + " => " + sClass.defStringSeenAs(sClass.typeOfThis) ) @@ -559,7 +590,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { GenPolyType(newClassTParams, ClassInfoType(parents ::: extraSpecializedMixins, decls1, sClass)) } - atPhase(phase.next)(sClass setInfo specializedInfoType) + afterSpecialize(sClass setInfo specializedInfoType) val fullEnv = outerEnv ++ env /** Enter 'sym' in the scope of the current specialized class. It's type is @@ -633,7 +664,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { }) } else - log("conflicting env for " + m + " env: " + env) + debuglog("conflicting env for " + m + " env: " + env) } else if (m.isDeferred) { // abstract methods val specMember = enterMember(cloneInSpecializedClass(m, _ | DEFERRED)) @@ -702,7 +733,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { typeEnv(specClass) = fullEnv specClass.name = specializedName(specClass, fullEnv).toTypeName enterMember(specClass) - log("entered specialized class " + specClass.fullName) + debuglog("entered specialized class " + specClass.fullName) info(specClass) = SpecializedInnerClass(m, fullEnv) } } @@ -739,7 +770,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (existing != NoSymbol) clazz.owner.info.decls.unlink(existing) - atPhase(phase.next)(clazz.owner.info.decls enter spc) //!!! assumes fully specialized classes + afterSpecialize(clazz.owner.info.decls enter spc) //!!! assumes fully specialized classes } if (subclasses.nonEmpty) clazz.resetFlag(FINAL) cleanAnyRefSpecCache(clazz, decls1) @@ -758,7 +789,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { private def normalizeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv): List[Symbol] = { debuglog("normalizeMember: " + sym.fullName) sym :: ( - if (!sym.isMethod || atPhase(currentRun.typerPhase)(sym.typeParams.isEmpty)) Nil + if (!sym.isMethod || beforeTyper(sym.typeParams.isEmpty)) Nil else { var specializingOn = specializedParams(sym) val unusedStvars = specializingOn filterNot specializedTypeVars(sym.info) @@ -816,10 +847,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (sym.isPrivate/* || sym.isProtected*/) { //sym.privateWithin = sym.enclosingPackage sym.resetFlag(PRIVATE).setFlag(PROTECTED) - log("-->d SETTING PRIVATE WITHIN TO " + sym.enclosingPackage + " for " + sym) + debuglog("-->d SETTING PRIVATE WITHIN TO " + sym.enclosingPackage + " for " + sym) } - sym.resetFlag(FINAL) val specMember = subst(outerEnv)(specializedOverload(owner, sym, spec)) typeEnv(specMember) = typeEnv(sym) ++ outerEnv ++ spec wasSpecializedForTypeVars(specMember) ++= spec collect { case (s, tp) if s.tpe == tp => s } @@ -900,7 +930,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { checkOverriddenTParams(overridden) val env = unify(overridden.info, overriding.info, emptyEnv, false) - def atNext = atPhase(phase.next)(overridden.owner.info.decl(specializedName(overridden, env))) + def atNext = afterSpecialize(overridden.owner.info.decl(specializedName(overridden, env))) debuglog("\t\tenv: " + env + "isValid: " + TypeEnv.isValid(env, overridden) + "found: " + atNext) if (TypeEnv.restrict(env, stvars).nonEmpty && TypeEnv.isValid(env, overridden) && atNext != NoSymbol) @@ -915,7 +945,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case (NoSymbol, _) => None case (overridden, env) => val om = specializedOverload(clazz, overridden, env) - log("Added specialized overload %s for %s in env: %s with type: %s".format(om, overriding.fullName, env, om.info)) + debuglog("Added specialized overload %s for %s in env: %s with type: %s".format(om, overriding.fullName, env, om.info)) typeEnv(om) = env addConcreteSpecMethod(overriding) info(om) = ( @@ -940,7 +970,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } ) overloads(overriding) ::= Overload(om, env) - ifDebug(atPhase(phase.next)(assert( + ifDebug(afterSpecialize(assert( overridden.owner.info.decl(om.name) != NoSymbol, "Could not find " + om.name + " in " + overridden.owner.info.decls)) ) @@ -950,6 +980,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } case object UnifyError extends scala.util.control.ControlThrowable + private[this] def unifyError(tp1: Any, tp2: Any): Nothing = { + log("unifyError" + ((tp1, tp2))) + throw UnifyError + } /** Return the most general type environment that specializes tp1 to tp2. * It only allows binding of type parameters annotated with @specialized. @@ -960,29 +994,34 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean): TypeEnv = (tp1, tp2) match { case (TypeRef(_, sym1, _), _) if isSpecialized(sym1) => debuglog("Unify - basic case: " + tp1 + ", " + tp2) - if (isValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1)) + if (isValueClass(tp2.typeSymbol)) env + ((sym1, tp2)) + else if (isSpecializedAnyRefSubtype(tp2, sym1)) + env + ((sym1, tp2)) // env + ((sym1, AnyRefClass.tpe)) + else if (strict) + unifyError(tp1, tp2) else - if (strict) throw UnifyError else env + env case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) => debuglog("Unify TypeRefs: " + tp1 + " and " + tp2 + " with args " + (args1, args2) + " - ") - if (strict && args1.length != args2.length) throw UnifyError + if (strict && args1.length != args2.length) unifyError(tp1, tp2) val e = unify(args1, args2, env, strict) debuglog("unified to: " + e) e case (TypeRef(_, sym1, _), _) if sym1.isTypeParameterOrSkolem => env case (MethodType(params1, res1), MethodType(params2, res2)) => - if (strict && params1.length != params2.length) throw UnifyError + if (strict && params1.length != params2.length) unifyError(tp1, tp2) debuglog("Unify MethodTypes: " + tp1 + " and " + tp2) unify(res1 :: (params1 map (_.tpe)), res2 :: (params2 map (_.tpe)), env, strict) case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => - if (strict && tparams1.length != tparams2.length) throw UnifyError debuglog("Unify PolyTypes: " + tp1 + " and " + tp2) - unify(res1, res2, env, strict) - case (PolyType(_, res), other) => - unify(res, other, env, strict) - case (ThisType(_), ThisType(_)) => env + if (strict && tparams1.length != tparams2.length) + unifyError(tp1, tp2) + else + unify(res1, res2, env, strict) + case (PolyType(_, res), other) => unify(res, other, env, strict) + case (ThisType(_), ThisType(_)) => env case (_, SingleType(_, _)) => unify(tp1, tp2.underlying, env, strict) case (SingleType(_, _), _) => unify(tp1.underlying, tp2, env, strict) case (ThisType(_), _) => unify(tp1.widen, tp2, env, strict) @@ -1004,7 +1043,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (env.keySet intersect nenv.keySet isEmpty) env ++ nenv else { debuglog("could not unify: u(" + args._1 + ", " + args._2 + ") yields " + nenv + ", env: " + env) - throw UnifyError + unifyError(tp1, tp2) } } } @@ -1082,7 +1121,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case cinfo @ ClassInfoType(parents, decls, clazz) if !unspecializableClass(cinfo) => val tparams = tpe.typeParams if (tparams.isEmpty) - atPhase(phase.next)(parents map (_.typeSymbol.info)) + afterSpecialize(parents map (_.typeSymbol.info)) val parents1 = parents map specializedType debuglog("transformInfo %s %s with parents1 %s ph: %s".format( @@ -1128,7 +1167,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (warnings) reporter.warning(tvar.pos, "Bounds prevent specialization of " + tvar) - log("specvars: " + + debuglog("specvars: " + tvar.info.bounds.lo + ": " + specializedTypeVars(tvar.info.bounds.lo) + " " + subst(env, tvar.info.bounds.hi) + ": " + @@ -1197,27 +1236,27 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } else NoSymbol def illegalSpecializedInheritance(clazz: Symbol): Boolean = ( - hasSpecializedFlag(clazz) + hasSpecializedFlag(clazz) && originalClass(clazz).parentSymbols.exists(p => hasSpecializedParams(p) && !p.isTrait) ) def specializeCalls(unit: CompilationUnit) = new TypingTransformer(unit) { /** Map a specializable method to it's rhs, when not deferred. */ - val body: mutable.Map[Symbol, Tree] = new mutable.HashMap + val body = perRunCaches.newMap[Symbol, Tree]() /** Map a specializable method to its value parameter symbols. */ - val parameters: mutable.Map[Symbol, List[List[Symbol]]] = new mutable.HashMap + val parameters = perRunCaches.newMap[Symbol, List[Symbol]]() /** Collect method bodies that are concrete specialized methods. */ class CollectMethodBodies extends Traverser { override def traverse(tree: Tree) = tree match { - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case DefDef(_, _, _, vparams :: Nil, _, rhs) => if (concreteSpecMethods(tree.symbol) || tree.symbol.isConstructor) { debuglog("!!! adding body of a defdef %s, symbol %s: %s".format(tree, tree.symbol, rhs)) body(tree.symbol) = rhs // body(tree.symbol) = tree // whole method - parameters(tree.symbol) = mmap(vparamss)(_.symbol) + parameters(tree.symbol) = vparams.map(_.symbol) concreteSpecMethods -= tree.symbol } // no need to descend further down inside method bodies @@ -1230,7 +1269,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } - def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) = + def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) = { (treeType =:= memberType) || { // anyref specialization memberType match { case PolyType(_, resTpe) => @@ -1247,6 +1286,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case _ => false } } + } override def transform(tree: Tree): Tree = { val symbol = tree.symbol @@ -1254,20 +1294,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** The specialized symbol of 'tree.symbol' for tree.tpe, if there is one */ def specSym(qual: Tree): Option[Symbol] = { val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) - log("[specSym] checking for rerouting: %s with \n\tsym.tpe: %s, \n\ttree.tpe: %s \n\tenv: %s \n\tname: %s" + debuglog("[specSym] checking for rerouting: %s with \n\tsym.tpe: %s, \n\ttree.tpe: %s \n\tenv: %s \n\tname: %s" .format(tree, symbol.tpe, tree.tpe, env, specializedName(symbol, env))) if (!env.isEmpty) { // a method? val specCandidates = qual.tpe.member(specializedName(symbol, env)) val specMember = specCandidates suchThat { s => doesConform(symbol, tree.tpe, qual.tpe.memberType(s), env) } - - log("[specSym] found: " + specCandidates.tpe + ", instantiated as: " + tree.tpe) - log("[specSym] found specMember: " + specMember) + + debuglog("[specSym] found: " + specCandidates.tpe + ", instantiated as: " + tree.tpe) + debuglog("[specSym] found specMember: " + specMember) if (specMember ne NoSymbol) if (TypeEnv.includes(typeEnv(specMember), env)) Some(specMember) else { - log("wrong environments for specialized member: \n\ttypeEnv(%s) = %s\n\tenv = %s".format(specMember, typeEnv(specMember), env)) + debuglog("wrong environments for specialized member: \n\ttypeEnv(%s) = %s\n\tenv = %s".format(specMember, typeEnv(specMember), env)) None } else None @@ -1277,16 +1317,13 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { curTree = tree tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => - if (findSpec(tpt.tpe).typeSymbol ne tpt.tpe.typeSymbol) { + debuglog("Attempting to specialize new %s(%s)".format(tpt, args.mkString(", "))) + val found = findSpec(tpt.tpe) + if (found.typeSymbol ne tpt.tpe.typeSymbol) { // the ctor can be specialized - log("** instantiated specialized type: " + findSpec(tpt.tpe)) - try { - atPos(tree.pos)( - localTyper.typed( - Apply( - Select(New(TypeTree(findSpec(tpt.tpe))), nme.CONSTRUCTOR), - transformTrees(args)))) - } catch { + debuglog("** instantiated specialized type: " + found) + try localTyper.typedPos(tree.pos)(New(found, transformTrees(args): _*)) + catch { case te: TypeError => reporter.error(tree.pos, te.msg) super.transform(tree) @@ -1313,7 +1350,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { ) val tree1 = gen.mkTypeApply(Select(qual1, specMember), residualTargs) - log("rewrote " + tree + " to " + tree1) + debuglog("rewrote " + tree + " to " + tree1) localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method case None => super.transform(tree) @@ -1321,8 +1358,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case Select(Super(_, _), name) if illegalSpecializedInheritance(currentClass) => val pos = tree.pos - log(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.") - log(pos.lineContent) + debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.") + debuglog(pos.lineContent) tree case Select(qual, name) => @@ -1378,13 +1415,13 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { (new CollectMethodBodies)(tree) val parents1 = map2(currentOwner.info.parents, parents)((tpe, parent) => TypeTree(tpe) setPos parent.pos) - + treeCopy.Template(tree, parents1 /*currentOwner.info.parents.map(tpe => TypeTree(tpe) setPos parents.head.pos)*/ , self, atOwner(currentOwner)(transformTrees(body ::: specMembers))) - case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) if info.isDefinedAt(symbol) => + case ddef @ DefDef(_, _, _, vparamss, _, _) if info.isDefinedAt(symbol) => // log("--> method: " + ddef + " in " + ddef.symbol.owner + ", " + info(symbol)) if (symbol.isConstructor) { @@ -1392,116 +1429,96 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val superRef: Tree = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) forwardCtorCall(tree.pos, superRef, vparamss, symbol.owner) } - if (symbol.isPrimaryConstructor) localTyper typed { - atPos(symbol.pos)(treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, Block(List(t), Literal(Constant())))) - } else { - // duplicate the original constructor + if (symbol.isPrimaryConstructor) + localTyper.typedPos(symbol.pos)(deriveDefDef(tree)(_ => Block(List(t), Literal(Constant())))) + else // duplicate the original constructor duplicateBody(ddef, info(symbol).target) - } - } else info(symbol) match { - + } + else info(symbol) match { case Implementation(target) => assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName) // we have an rhs, specialize it val tree1 = duplicateBody(ddef, target) debuglog("implementation: " + tree1) - val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree1 - treeCopy.DefDef(tree1, mods, name, tparams, vparamss, tpt, transform(rhs)) + deriveDefDef(tree1)(transform) case NormalizedMember(target) => - log("Normalized member: " + symbol + ", target: " + target) + debuglog("Normalized member: " + symbol + ", target: " + target) if (target.isDeferred || conflicting(typeEnv(symbol))) { - treeCopy.DefDef( - tree, mods, name, tparams, vparamss, tpt, - localTyper typed gen.mkSysErrorCall("boom! you stepped on a bug. This method should never be called.") - ) + deriveDefDef(tree)(_ => localTyper typed gen.mkSysErrorCall("Fatal error in code generation: this should never be called.")) } else { // we have an rhs, specialize it val tree1 = duplicateBody(ddef, target) debuglog("implementation: " + tree1) - val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree1 - treeCopy.DefDef(tree1, mods, name, tparams, vparamss, tpt, transform(rhs)) + deriveDefDef(tree1)(transform) } case SpecialOverride(target) => assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName) //debuglog("moving implementation, body of target " + target + ": " + body(target)) - log("%s is param accessor? %b".format(ddef.symbol, ddef.symbol.isParamAccessor)) + debuglog("%s is param accessor? %b".format(ddef.symbol, ddef.symbol.isParamAccessor)) // we have an rhs, specialize it val tree1 = addBody(ddef, target) (new ChangeOwnerTraverser(target, tree1.symbol))(tree1.rhs) debuglog("changed owners, now: " + tree1) - val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree1 - treeCopy.DefDef(tree1, mods, name, tparams, vparamss, tpt, transform(rhs)) - + deriveDefDef(tree1)(transform) case SpecialOverload(original, env) => debuglog("completing specialized " + symbol.fullName + " calling " + original) - log("special overload " + original + " -> " + env) + debuglog("special overload " + original + " -> " + env) val t = DefDef(symbol, { vparamss => val fun = Apply(Select(This(symbol.owner), original), makeArguments(original, vparamss.head)) - log("inside defdef: " + symbol + "; type: " + symbol.tpe + "; owner: " + symbol.owner) + debuglog("inside defdef: " + symbol + "; type: " + symbol.tpe + "; owner: " + symbol.owner) gen.maybeMkAsInstanceOf(fun, symbol.owner.thisType.memberType(symbol).finalResultType, symbol.owner.thisType.memberType(original).finalResultType) }) - log("created special overload tree " + t) + debuglog("created special overload tree " + t) debuglog("created " + t) localTyper.typed(t) case fwd @ Forward(_) => - log("forward: " + fwd + ", " + ddef) + debuglog("forward: " + fwd + ", " + ddef) val rhs1 = forwardCall(tree.pos, gen.mkAttributedRef(symbol.owner.thisType, fwd.target), vparamss) - log("-->d completed forwarder to specialized overload: " + fwd.target + ": " + rhs1) - localTyper.typed(treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1)) + debuglog("-->d completed forwarder to specialized overload: " + fwd.target + ": " + rhs1) + localTyper.typed(deriveDefDef(tree)(_ => rhs1)) case SpecializedAccessor(target) => val rhs1 = if (symbol.isGetter) gen.mkAttributedRef(target) else Assign(gen.mkAttributedRef(target), Ident(vparamss.head.head.symbol)) - log("specialized accessor: " + target + " -> " + rhs1) - localTyper.typed(treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1)) + debuglog("specialized accessor: " + target + " -> " + rhs1) + localTyper.typed(deriveDefDef(tree)(_ => rhs1)) case Abstract(targ) => - log("abstract: " + targ) - val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree - val t = treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs) - localTyper.typed(t) + debuglog("abstract: " + targ) + localTyper.typed(deriveDefDef(tree)(rhs => rhs)) } - case ValDef(mods, name, tpt, rhs) if symbol.hasFlag(SPECIALIZED) && !symbol.isParamAccessor => + case ValDef(_, _, _, _) if symbol.hasFlag(SPECIALIZED) && !symbol.isParamAccessor => assert(body.isDefinedAt(symbol.alias), body) - val tree1 = treeCopy.ValDef(tree, mods, name, tpt, body(symbol.alias).duplicate) + val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate) debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName) + val d = new Duplicator - val ValDef(mods1, name1, tpt1, rhs1) = d.retyped( + val newValDef = d.retyped( localTyper.context1.asInstanceOf[d.Context], tree1, symbol.alias.enclClass, symbol.enclClass, typeEnv(symbol.alias) ++ typeEnv(tree.symbol) ) - val t = treeCopy.ValDef(tree1, mods1, name1, tpt1, transform(rhs1)) - log("valdef " + tree + " -> " + t) - t - -// val tree1 = -// treeCopy.ValDef(tree, mods, name, tpt, -// localTyper.typed( -// Apply(Select(Super(currentClass, nme.EMPTY), symbol.alias.getter(symbol.alias.owner)), -// List()))) -// debuglog("replaced ValDef: " + tree1 + " in " + tree.symbol.owner.fullName) -// tree1 + deriveValDef(newValDef)(transform) case Apply(sel @ Select(sup @ Super(qual, name), name1), args) - if (sup.symbol.info.parents != atPhase(phase.prev)(sup.symbol.info.parents)) => + if (sup.symbol.info.parents != beforePrevPhase(sup.symbol.info.parents)) => def parents = sup.symbol.info.parents - debuglog(tree + " parents changed from: " + atPhase(phase.prev)(parents) + " to: " + parents) + debuglog(tree + " parents changed from: " + beforePrevPhase(parents) + " to: " + parents) val res = localTyper.typed( Apply(Select(Super(qual, name) setPos sup.pos, name1) setPos sel.pos, transformTrees(args)) setPos tree.pos) @@ -1513,18 +1530,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } - private def reskolemize(tparams: List[TypeDef]): (List[Symbol], List[Symbol]) = { - val saved = tparams map (_.symbol) - localTyper skolemizeTypeParams tparams - (saved, tparams map (_.symbol)) - } - private def duplicateBody(tree: DefDef, source: Symbol) = { val symbol = tree.symbol val meth = addBody(tree, source) val d = new Duplicator - log("-->d DUPLICATING: " + meth) + debuglog("-->d DUPLICATING: " + meth) d.retyped( localTyper.context1.asInstanceOf[d.Context], meth, @@ -1542,8 +1553,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ private def addBody(tree: DefDef, source: Symbol): DefDef = { val symbol = tree.symbol - debuglog("specializing body of" + symbol.fullName + ": " + symbol.info) - val DefDef(mods, name, tparams, vparamss, tpt, _) = tree + debuglog("specializing body of" + symbol.defString) + val DefDef(_, _, tparams, vparams :: Nil, tpt, _) = tree // val (_, origtparams) = splitParams(source.typeParams) val env = typeEnv(symbol) val boundTvars = env.keySet @@ -1551,12 +1562,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { debuglog("substituting " + origtparams + " for " + symbol.typeParams) // skolemize type parameters - val (oldtparams, newtparams) = reskolemize(tparams) + val oldtparams = tparams map (_.symbol) + val newtparams = deriveFreshSkolems(oldtparams) + map2(tparams, newtparams)(_ setSymbol _) // create fresh symbols for value parameters to hold the skolem types - val vparamss1 = List(for (vdef <- vparamss.head; param = vdef.symbol) yield { - ValDef(param cloneSymbol symbol substInfo (oldtparams, newtparams)) - }) + val newSyms = cloneSymbolsAtOwnerAndModify(vparams map (_.symbol), symbol, _.substSym(oldtparams, newtparams)) // replace value and type parameters of the old method with the new ones // log("Adding body for " + tree.symbol + " - origtparams: " + origtparams + "; tparams: " + tparams) @@ -1564,14 +1575,15 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // log("Type env of: " + tree.symbol + ": " + boundTvars) // log("newtparams: " + newtparams) val symSubstituter = new ImplementationAdapter( - parameters(source).flatten ::: origtparams, - vparamss1.flatten.map(_.symbol) ::: newtparams, + parameters(source) ::: origtparams, + newSyms ::: newtparams, source.enclClass, false) // don't make private fields public - val tmp = symSubstituter(body(source).duplicate) + + val newBody = symSubstituter(body(source).duplicate) tpt.tpe = tpt.tpe.substSym(oldtparams, newtparams) - treeCopy.DefDef(tree, mods, name, tparams, vparamss1, tpt, tmp) + copyDefDef(tree)(vparamss = List(newSyms map ValDef), rhs = newBody) } /** Create trees for specialized members of 'sClass', based on the @@ -1588,13 +1600,13 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if m.hasFlag(SPECIALIZED) && (m.sourceFile ne null) && satisfiable(typeEnv(m), !sClass.hasFlag(SPECIALIZED))) { - log("creating tree for " + m.fullName) + debuglog("creating tree for " + m.fullName) if (m.isMethod) { if (info(m).target.hasAccessorFlag) hasSpecializedFields = true if (m.isClassConstructor) { - val origParamss = parameters(info(m).target) + val origParams = parameters(info(m).target) val vparams = ( - map2(m.info.paramTypes, origParamss(0))((tp, sym) => + map2(m.info.paramTypes, origParams)((tp, sym) => m.newValue(specializedName(sym, typeEnv(sClass)), sym.pos, sym.flags) setInfo tp ) ) @@ -1644,7 +1656,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { buf += ClassDef(specCls, atPos(impl.pos)(Template(parents, emptyValDef, List())) .setSymbol(specCls.newLocalDummy(sym1.pos))) setPos tree.pos - log("created synthetic class: " + specCls + " of " + sym1 + " in env: " + env) + debuglog("created synthetic class: " + specCls + " of " + sym1 + " in env: " + env) } case _ => } @@ -1685,7 +1697,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * - there is a getter for the specialized field in the same class */ def initializesSpecializedField(f: Symbol) = ( - (f.name endsWith nme.SPECIALIZED_SUFFIX_NAME) + (f.name endsWith nme.SPECIALIZED_SUFFIX) && clazz.info.member(nme.originalName(f.name)).isPublic && clazz.info.decl(f.name).suchThat(_.isGetter) != NoSymbol ) @@ -1720,9 +1732,27 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { class SpecializationTransformer(unit: CompilationUnit) extends Transformer { informProgress("specializing " + unit) - override def transform(tree: Tree) = - if (settings.nospecialization.value) tree - else atPhase(phase.next)(specializeCalls(unit).transform(tree)) + override def transform(tree: Tree) = { + val resultTree = if (settings.nospecialization.value) tree + else afterSpecialize(specializeCalls(unit).transform(tree)) + + // Remove the final modifier and @inline annotation from anything in the + // original class (since it's being overridden in at least onesubclass). + // + // We do this here so that the specialized subclasses will correctly copy + // final and @inline. + info.foreach { + case (sym, SpecialOverload(target, _)) => { + sym.resetFlag(FINAL) + target.resetFlag(FINAL) + sym.removeAnnotation(ScalaInlineClass) + target.removeAnnotation(ScalaInlineClass) + } + case _ => {} + } + + resultTree + } } def printSpecStats() { diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 1655ad09c4..fdb5c7e52e 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -204,7 +204,7 @@ abstract class TailCalls extends Transform { fail(reason) } def rewriteTailCall(recv: Tree): Tree = { - log("Rewriting tail recursive call: " + fun.pos.lineContent.trim) + debuglog("Rewriting tail recursive call: " + fun.pos.lineContent.trim) ctx.accessed = true typedPos(fun.pos)(Apply(Ident(ctx.label), recv :: transformArgs)) @@ -223,13 +223,29 @@ abstract class TailCalls extends Transform { } tree match { - case dd @ DefDef(mods, name, tparams, vparams, tpt, rhs) => - val newCtx = new Context(dd) + case ValDef(_, _, _, _) => + if (tree.symbol.isLazy && tree.symbol.hasAnnotation(TailrecClass)) + unit.error(tree.pos, "lazy vals are not tailcall transformed") + + super.transform(tree) - debuglog("Considering " + name + " for tailcalls") - val newRHS = transform(rhs, newCtx) + case dd @ DefDef(_, _, _, vparamss0, _, rhs0) if !dd.symbol.hasAccessorFlag => + val newCtx = new Context(dd) + def isRecursiveCall(t: Tree) = { + val sym = t.symbol + (sym != null) && { + sym.isMethod && (dd.symbol.name == sym.name) && (dd.symbol.enclClass isSubClass sym.enclClass) + } + } + if (newCtx.isMandatory) { + if (!rhs0.exists(isRecursiveCall)) { + unit.error(tree.pos, "@tailrec annotated method contains no recursive calls") + } + } + debuglog("Considering " + dd.name + " for tailcalls") + val newRHS = transform(rhs0, newCtx) - treeCopy.DefDef(tree, mods, name, tparams, vparams, tpt, { + deriveDefDef(tree)(rhs => if (newCtx.isTransformed) { /** We have rewritten the tree, but there may be nested recursive calls remaining. * If @tailrec is given we need to fail those now. @@ -241,7 +257,7 @@ abstract class TailCalls extends Transform { } } val newThis = newCtx.newThis(tree.pos) - val vpSyms = vparams.flatten map (_.symbol) + val vpSyms = vparamss0.flatten map (_.symbol) typedPos(tree.pos)(Block( List(ValDef(newThis, This(currentClass))), @@ -249,12 +265,12 @@ abstract class TailCalls extends Transform { )) } else { - if (newCtx.isMandatory) + if (newCtx.isMandatory && newRHS.exists(isRecursiveCall)) newCtx.tailrecFailure() newRHS } - }) + ) case Block(stats, expr) => treeCopy.Block(tree, @@ -263,11 +279,7 @@ abstract class TailCalls extends Transform { ) case CaseDef(pat, guard, body) => - treeCopy.CaseDef(tree, - pat, - guard, - transform(body) - ) + deriveCaseDef(tree)(transform) case If(cond, thenp, elsep) => treeCopy.If(tree, diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index adbb7bc7f1..ee565530b7 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -129,7 +129,7 @@ abstract class UnCurry extends InfoTransform appliedType(NonLocalReturnControlClass.typeConstructor, List(argtype)) /** A hashmap from method symbols to non-local return keys */ - private val nonLocalReturnKeys = new mutable.HashMap[Symbol, Symbol] + private val nonLocalReturnKeys = perRunCaches.newMap[Symbol, Symbol]() /** Return non-local return key for given method */ private def nonLocalReturnKey(meth: Symbol) = @@ -144,13 +144,13 @@ abstract class UnCurry extends InfoTransform * todo: maybe clone a pre-existing exception instead? * (but what to do about exceptions that miss their targets?) */ - private def nonLocalReturnThrow(expr: Tree, meth: Symbol) = - localTyper.typed { - Throw( - New( - TypeTree(nonLocalReturnExceptionType(expr.tpe)), - List(List(Ident(nonLocalReturnKey(meth)), expr)))) - } + private def nonLocalReturnThrow(expr: Tree, meth: Symbol) = localTyper typed { + Throw( + nonLocalReturnExceptionType(expr.tpe.widen), + Ident(nonLocalReturnKey(meth)), + expr + ) + } /** Transform (body, key) to: * @@ -166,31 +166,18 @@ abstract class UnCurry extends InfoTransform * } */ private def nonLocalReturnTry(body: Tree, key: Symbol, meth: Symbol) = { - localTyper.typed { - val extpe = nonLocalReturnExceptionType(meth.tpe.finalResultType) - val ex = meth.newValue(nme.ex, body.pos) setInfo extpe - val pat = Bind(ex, - Typed(Ident(nme.WILDCARD), - AppliedTypeTree(Ident(NonLocalReturnControlClass), - List(Bind(tpnme.WILDCARD, - EmptyTree))))) - val rhs = - If( - Apply( - Select( - Apply(Select(Ident(ex), "key"), List()), - Object_eq), - List(Ident(key))), - Apply( - TypeApply( - Select( - Apply(Select(Ident(ex), "value"), List()), - Any_asInstanceOf), - List(TypeTree(meth.tpe.finalResultType))), - List()), - Throw(Ident(ex))) - val keyDef = ValDef(key, New(TypeTree(ObjectClass.tpe), List(List()))) - val tryCatch = Try(body, List(CaseDef(pat, EmptyTree, rhs)), EmptyTree) + localTyper typed { + val extpe = nonLocalReturnExceptionType(meth.tpe.finalResultType) + val ex = meth.newValue(body.pos, nme.ex) setInfo extpe + val pat = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(meth.tpe.finalResultType)) + val rhs = ( + IF ((ex DOT nme.key)() OBJ_EQ Ident(key)) + THEN ((ex DOT nme.value)()) + ELSE (Throw(Ident(ex))) + ) + val keyDef = ValDef(key, New(ObjectClass.tpe)) + val tryCatch = Try(body, pat -> rhs) + Block(List(keyDef), tryCatch) } } @@ -228,9 +215,9 @@ abstract class UnCurry extends InfoTransform * case P_1 if G_1 => E_1 * ... * case P_n if G_n => true - * case _ => this.missingCase(x) + * case _ => this.missingCase(expr) * } - * def isDefinedAtCurrent(x: T): boolean = (x: @unchecked) match { + * def _isDefinedAt(x: T): boolean = (x: @unchecked) match { * case P_1 if G_1 => true * ... * case P_n if G_n => true @@ -240,7 +227,7 @@ abstract class UnCurry extends InfoTransform * new $anon() * * However, if one of the patterns P_i if G_i is a default pattern, - * drop the last default clause in tghe definition of `apply` and generate for `isDefinedAtCurrent` instead + * drop the last default clause in the definition of `apply` and generate for `_isDefinedAt` instead * * def isDefinedAtCurrent(x: T): boolean = true */ @@ -260,7 +247,7 @@ abstract class UnCurry extends InfoTransform else List(ObjectClass.tpe, fun.tpe, SerializableClass.tpe) anonClass setInfo ClassInfoType(parents, newScope, anonClass) - val applyMethod = anonClass.newMethod(nme.apply, fun.pos, FINAL) + val applyMethod = anonClass.newMethod(nme.apply, fun.pos, FINAL) applyMethod setInfoAndEnter MethodType(applyMethod newSyntheticValueParams formals, restpe) anonClass addAnnotation serialVersionUIDAnnotation @@ -291,73 +278,26 @@ abstract class UnCurry extends InfoTransform val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params) def substTree[T <: Tree](t: T): T = substParam(resetLocalAttrs(t)) - // waiting here until we can mix case classes and extractors reliably (i.e., when virtpatmat becomes the default) - // object VirtPatmatOpt { - // object Last { - // def unapply[T](xs: List[T]) = xs.lastOption - // } - // // keep this in synch by what's generated by combineCases/runOrElse - // object MatcherBlock { - // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree])] = matcher match { // TODO: BUG the unapplySeq version of the case below does not seem to work in virtpatmat?? - // case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) => Some(zero, x, matchRes, keepGoing, stats) - // case _ => None - // } - // } - // // TODO: virtpatmat use case: would be nice if could abstract over the repeated pattern more easily - // // case Block(Last(P)) => - // // case P => - // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree], Tree => Tree)] = matcher match { - // case MatcherBlock(zero, x, matchRes, keepGoing, stats) => Some(zero, x, matchRes, keepGoing, stats, identity[Tree]) - // case Block(outerStats, MatcherBlock(zero, x, matchRes, keepGoing, stats)) => Some(zero, x, matchRes, keepGoing, stats, inner => Block(outerStats, inner)) - // case b => treeBrowser browse b; None - // } - // } - - // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly - def dupMatch(selector: Tree, cases: List[CaseDef], wrap: Match => Tree = identity) = { - def transformCase(cdef: CaseDef): CaseDef = - CaseDef(cdef.pat, cdef.guard, Literal(Constant(true))) - def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) - - gen.mkUncheckedMatch( - if (cases exists treeInfo.isDefaultCase) Literal(Constant(true)) - else substTree(wrap(Match(selector, (cases map transformCase) :+ defaultCase)).duplicate) - ) - } + object isDefinedAtTransformer extends gen.MatchMatcher { + // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly + override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = { + def transformCase(cdef: CaseDef): CaseDef = + CaseDef(cdef.pat, cdef.guard, Literal(Constant(true))) - def dupVirtMatch(zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], wrap: Block => Tree = identity) = { - object dropMatchResAssign extends Transformer { - // override val treeCopy = newStrictTreeCopier // will duplicate below - override def transform(tree: Tree): Tree = tree match { - // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing - case Block(List(matchRes, ass@Assign(keepGoingLhs, falseLit)), zero) if keepGoingLhs.symbol eq keepGoing.symbol => - Block(List(ass), zero) - case _ => - super.transform(tree) - } + def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) + + val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) + + gen.mkUncheckedMatch( + if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) Literal(Constant(true)) + else substTree(wrap(Match(selector, (casesNoSynthCatchAll map transformCase) :+ defaultCase)).duplicate) + ) } - val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList - val idaBlock = wrap(Block( - zero :: - x :: - /* drop matchRes def */ - keepGoing :: - statsNoMatchRes, - NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `!keepGoing` - )) - substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed - } - DefDef(m, (fun.body: @unchecked) match { - case Match(selector, cases) => - dupMatch(selector, cases) - case Block((vd: ValDef) :: Nil, Match(selector, cases)) => // can't factor this out using an extractor due to bugs in the old pattern matcher - dupMatch(selector, cases, m => Block(List(vd), m)) - // virtpatmat -- TODO: find a better way to keep this in synch with the code generated by patmatvirtualizer - case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), args_scrut), args_pm) if opt.virtPatmat => + override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { object noOne extends Transformer { override val treeCopy = newStrictTreeCopier // must duplicate everything - val one = tgt.tpe member newTermName("one") + val one = _match.tpe member newTermName("one") override def transform(tree: Tree): Tree = tree match { case Apply(fun, List(a)) if fun.symbol == one => // blow one's argument away since all we want to know is whether the match succeeds or not @@ -367,15 +307,34 @@ abstract class UnCurry extends InfoTransform super.transform(tree) } } - substTree(Apply(Apply(TypeApply(Select(tgt.duplicate, tgt.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), args_scrut map (_.duplicate)), args_pm map (noOne.transform))) - // for the optimized version of virtpatmat - case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) if opt.virtPatmat => - dupVirtMatch(zero, x, matchRes, keepGoing, stats) - case Block(outerStats, Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _)) if opt.virtPatmat => // can't factor this out using an extractor due to bugs in the old pattern matcher - dupVirtMatch(zero, x, matchRes, keepGoing, stats, m => Block(outerStats, m)) - // case other => - // treeBrowser browse other - }) + substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher)))) + } + + override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = { + object dropMatchResAssign extends Transformer { + // override val treeCopy = newStrictTreeCopier // will duplicate below + override def transform(tree: Tree): Tree = tree match { + // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing + case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol => + Block(List(assignKeepGoing), zero) + case _ => + super.transform(tree) + } + } + val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList + val idaBlock = wrap(Block( + zero :: + x :: + /* drop matchRes def */ + keepGoing :: + statsNoMatchRes, + NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing` + )) + substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed + } + } + + DefDef(m, isDefinedAtTransformer(fun.body)) } val members = @@ -385,9 +344,7 @@ abstract class UnCurry extends InfoTransform localTyper.typedPos(fun.pos) { Block( List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, fun.pos)), - Typed( - New(TypeTree(anonClass.tpe), List(List())), - TypeTree(fun.tpe))) + Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } } } @@ -400,7 +357,7 @@ abstract class UnCurry extends InfoTransform // when calling into scala varargs, make sure it's a sequence. def arrayToSequence(tree: Tree, elemtp: Type) = { - atPhase(phase.next) { + afterUncurry { localTyper.typedPos(pos) { val pt = arrayType(elemtp) val adaptedTree = // might need to cast to Array[elemtp], as arrays are not covariant @@ -424,7 +381,7 @@ abstract class UnCurry extends InfoTransform else if (tp.bounds.hi ne tp) getManifest(tp.bounds.hi) else localTyper.getManifestTree(tree, tp, false) } - atPhase(phase.next) { + afterUncurry { localTyper.typedPos(pos) { Apply(gen.mkAttributedSelect(tree, toArraySym), List(getManifest(tree.tpe.baseType(TraversableClass).typeArgs.head))) @@ -449,7 +406,7 @@ abstract class UnCurry extends InfoTransform else arrayToSequence(mkArray, varargsElemType) } - atPhase(phase.next) { + afterUncurry { if (isJava && isPrimitiveArray(suffix.tpe) && isArrayOfSymbol(fun.tpe.params.last.tpe, ObjectClass)) { suffix = localTyper.typedPos(pos) { gen.mkRuntimeCall(nme.toObjectArray, List(suffix)) @@ -482,19 +439,38 @@ abstract class UnCurry extends InfoTransform } } - /** For removing calls to specially designated methods. + /** Called if a tree's symbol is elidable. If it's a DefDef, + * replace only the body/rhs with 0/false/()/null; otherwise replace + * the whole tree with it. */ - def elideIntoUnit(tree: Tree): Tree = Literal(Constant()) setPos tree.pos setType UnitClass.tpe - def isElidable(tree: Tree) = { - val sym = treeInfo.methPart(tree).symbol - // XXX settings.noassertions.value temporarily retained to avoid - // breakage until a reasonable interface is settled upon. - sym != null && sym.elisionLevel.exists(x => x < settings.elidebelow.value || settings.noassertions.value) && { - log("Eliding call from " + tree.symbol.owner + " to " + sym + " based on its elision threshold of " + sym.elisionLevel.get) - true + private def replaceElidableTree(tree: Tree): Tree = { + tree match { + case DefDef(_,_,_,_,_,_) => + deriveDefDef(tree)(rhs => Block(Nil, gen.mkZero(rhs.tpe)) setType rhs.tpe) setSymbol tree.symbol setType tree.tpe + case _ => + gen.mkZero(tree.tpe) setType tree.tpe } } + private def isSelfSynchronized(ddef: DefDef) = ddef.rhs match { + case Apply(fn @ TypeApply(Select(sel, _), _), _) => + fn.symbol == Object_synchronized && sel.symbol == ddef.symbol.enclClass && !ddef.symbol.enclClass.isTrait + case _ => false + } + + /** If an eligible method is entirely wrapped in a call to synchronized + * locked on the same instance, remove the synchronized scaffolding and + * mark the method symbol SYNCHRONIZED for bytecode generation. + */ + private def translateSynchronized(tree: Tree) = tree match { + case dd @ DefDef(_, _, _, _, _, Apply(fn, body :: Nil)) if isSelfSynchronized(dd) => + log("Translating " + dd.symbol.defString + " into synchronized method") + dd.symbol setFlag SYNCHRONIZED + deriveDefDef(dd)(_ => body) + case _ => tree + } + def isNonLocalReturn(ret: Return) = ret.symbol != currentOwner.enclMethod || currentOwner.isLazy + // ------ The tree transformers -------------------------------------------------------- def mainTransform(tree: Tree): Tree = { @@ -532,112 +508,103 @@ abstract class UnCurry extends InfoTransform finally this.inConstructorFlag = saved } - if (isElidable(tree)) elideIntoUnit(tree) - else tree match { - case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => - if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd) - withNeedLift(false) { - if (tree.symbol.isClassConstructor) { - atOwner(tree.symbol) { - val rhs1 = (rhs: @unchecked) match { - case Block(stats, expr) => - def transformInConstructor(stat: Tree) = - withInConstructorFlag(INCONSTRUCTOR) { transform(stat) } - val presupers = treeInfo.preSuperFields(stats) map transformInConstructor - val rest = stats drop presupers.length - val supercalls = rest take 1 map transformInConstructor - val others = rest drop 1 map transform - treeCopy.Block(rhs, presupers ::: supercalls ::: others, transform(expr)) + val sym = tree.symbol + val result = ( + // TODO - settings.noassertions.value temporarily retained to avoid + // breakage until a reasonable interface is settled upon. + if ((sym ne null) && (sym.elisionLevel.exists (_ < settings.elidebelow.value || settings.noassertions.value))) + replaceElidableTree(tree) + else translateSynchronized(tree) match { + case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd) + + withNeedLift(false) { + if (dd.symbol.isClassConstructor) { + atOwner(sym) { + val rhs1 = (rhs: @unchecked) match { + case Block(stats, expr) => + def transformInConstructor(stat: Tree) = + withInConstructorFlag(INCONSTRUCTOR) { transform(stat) } + val presupers = treeInfo.preSuperFields(stats) map transformInConstructor + val rest = stats drop presupers.length + val supercalls = rest take 1 map transformInConstructor + val others = rest drop 1 map transform + treeCopy.Block(rhs, presupers ::: supercalls ::: others, transform(expr)) + } + treeCopy.DefDef( + dd, mods, name, transformTypeDefs(tparams), + transformValDefss(vparamss), transform(tpt), rhs1) } - treeCopy.DefDef( - tree, mods, name, transformTypeDefs(tparams), - transformValDefss(vparamss), transform(tpt), rhs1) + } else { + super.transform(dd) } - } else { - super.transform(tree) } - } - case ValDef(_, _, _, rhs) => - val sym = tree.symbol - if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit) - // a local variable that is mutable and free somewhere later should be lifted - // as lambda lifting (coming later) will wrap 'rhs' in an Ref object. - if (!sym.owner.isSourceMethod) + case ValDef(_, _, _, rhs) => + if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit) + // a local variable that is mutable and free somewhere later should be lifted + // as lambda lifting (coming later) will wrap 'rhs' in an Ref object. + if (!sym.owner.isSourceMethod) + withNeedLift(true) { super.transform(tree) } + else + super.transform(tree) + case UnApply(fn, args) => + val fn1 = withInPattern(false)(transform(fn)) + val args1 = transformTrees(fn.symbol.name match { + case nme.unapply => args + case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeListFromReturnTypeSeq(fn.tpe)) + case _ => sys.error("internal error: UnApply node has wrong symbol") + }) + treeCopy.UnApply(tree, fn1, args1) + + case Apply(fn, args) => + if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head)) + transform(treeCopy.Apply(tree, fn, List(liftTree(args.head)))) + else + withNeedLift(true) { + val formals = fn.tpe.paramTypes + treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) + } + + case Assign(Select(_, _), _) => withNeedLift(true) { super.transform(tree) } - else - super.transform(tree) -/* - case Apply(Select(Block(List(), Function(vparams, body)), nme.apply), args) => - // perform beta-reduction; this helps keep view applications small - println("beta-reduce1: "+tree) - withNeedLift(true) { - mainTransform(new TreeSubstituter(vparams map (_.symbol), args).transform(body)) - } - case Apply(Select(Function(vparams, body), nme.apply), args) => -// if (List.forall2(vparams, args)((vparam, arg) => treeInfo.isAffineIn(body) || -// treeInfo.isExprSafeToInline(arg))) => - // perform beta-reduction; this helps keep view applications small - println("beta-reduce2: "+tree) - withNeedLift(true) { - mainTransform(new TreeSubstituter(vparams map (_.symbol), args).transform(body)) - } -*/ - case UnApply(fn, args) => - val fn1 = withInPattern(false)(transform(fn)) - val args1 = transformTrees(fn.symbol.name match { - case nme.unapply => args - case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeListFromReturnTypeSeq(fn.tpe)) - case _ => sys.error("internal error: UnApply node has wrong symbol") - }) - treeCopy.UnApply(tree, fn1, args1) - - case Apply(fn, args) => - if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head)) - transform(treeCopy.Apply(tree, fn, List(liftTree(args.head)))) - else - withNeedLift(true) { - val formals = fn.tpe.paramTypes - treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) - } + case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => + withNeedLift(true) { super.transform(tree) } - case Assign(Select(_, _), _) => - withNeedLift(true) { super.transform(tree) } + case ret @ Return(_) if (isNonLocalReturn(ret)) => + withNeedLift(true) { super.transform(ret) } - case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => - withNeedLift(true) { super.transform(tree) } + case Try(block, catches, finalizer) => + if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) + else super.transform(tree) - case Try(block, catches, finalizer) => - if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) - else super.transform(tree) + case CaseDef(pat, guard, body) => + val pat1 = withInPattern(true)(transform(pat)) + treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) - case CaseDef(pat, guard, body) => - val pat1 = withInPattern(true)(transform(pat)) - treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) + case fun @ Function(_, _) => + mainTransform(transformFunction(fun)) - case fun @ Function(_, _) => - mainTransform(transformFunction(fun)) + case Template(_, _, _) => + withInConstructorFlag(0) { super.transform(tree) } - case Template(_, _, _) => - withInConstructorFlag(0) { super.transform(tree) } - - case _ => - val tree1 = super.transform(tree) - if (isByNameRef(tree1)) { - val tree2 = tree1 setType functionType(Nil, tree1.tpe) - return { - if (noApply contains tree2) tree2 - else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil)) + case _ => + val tree1 = super.transform(tree) + if (isByNameRef(tree1)) { + val tree2 = tree1 setType functionType(Nil, tree1.tpe) + return { + if (noApply contains tree2) tree2 + else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil)) + } } - } - tree1 - } - } setType { - assert(tree.tpe != null, "tpe is null at " + tree.pos + " for " + tree.summaryString + " / " + tree) - uncurryTreeType(tree.tpe) + tree1 + } + ) + assert(result.tpe != null, result + " tpe is null") + result setType uncurryTreeType(result.tpe) } - def postTransform(tree: Tree): Tree = atPhase(phase.next) { + def postTransform(tree: Tree): Tree = afterUncurry { def applyUnary(): Tree = { // TODO_NMT: verify that the inner tree of a type-apply also gets parens if the // whole tree is a polymorphic nullary method application @@ -663,23 +630,24 @@ abstract class UnCurry extends InfoTransform * In particular, this case will add: * - synthetic Java varargs forwarders for repeated parameters */ - case Template(parents, self, body) => + case Template(_, _, _) => localTyper = typer.atOwner(tree, currentClass) - val tmpl = if (!forMSIL || forMSIL) { - treeCopy.Template(tree, parents, self, transformTrees(newMembers.toList) ::: body) - } else super.transform(tree).asInstanceOf[Template] - newMembers.clear - tmpl - case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => - val rhs1 = nonLocalReturnKeys.get(tree.symbol) match { - case None => rhs - case Some(k) => atPos(rhs.pos)(nonLocalReturnTry(rhs, k, tree.symbol)) - } - val flatdd = treeCopy.DefDef(tree, mods, name, tparams, List(vparamss.flatten), tpt, rhs1) - if (dd.symbol hasAnnotation VarargsClass) addJavaVarargsForwarders(dd, flatdd, tree) - flatdd + try deriveTemplate(tree)(transformTrees(newMembers.toList) ::: _) + finally newMembers.clear() + + case dd @ DefDef(_, _, _, vparamss0, _, rhs0) => + val flatdd = copyDefDef(dd)( + vparamss = List(vparamss0.flatten), + rhs = nonLocalReturnKeys get dd.symbol match { + case Some(k) => atPos(rhs0.pos)(nonLocalReturnTry(rhs0, k, dd.symbol)) + case None => rhs0 + } + ) + addJavaVarargsForwarders(dd, flatdd) + case Try(body, catches, finalizer) => - if (catches forall treeInfo.isCatchCase) tree + if (opt.virtPatmat) { if(catches exists (cd => !treeInfo.isCatchCase(cd))) debugwarn("VPM BUG! illegal try/catch "+ catches); tree } + else if (catches forall treeInfo.isCatchCase) tree else { val exname = unit.freshTermName("ex$") val cases = @@ -701,7 +669,7 @@ abstract class UnCurry extends InfoTransform } debuglog("rewrote try: " + catches + " ==> " + catchall); val catches1 = localTyper.typedCases( - tree, List(catchall), ThrowableClass.tpe, WildcardType) + List(catchall), ThrowableClass.tpe, WildcardType) treeCopy.Try(tree, body, catches1, finalizer) } case Apply(Apply(fn, args), args1) => @@ -711,9 +679,9 @@ abstract class UnCurry extends InfoTransform applyUnary() case Select(_, _) | TypeApply(_, _) => applyUnary() - case Return(expr) if (tree.symbol != currentOwner.enclMethod || currentOwner.isLazy) => - debuglog("non local return in "+tree.symbol+" from "+currentOwner.enclMethod) - atPos(tree.pos)(nonLocalReturnThrow(expr, tree.symbol)) + case ret @ Return(expr) if (isNonLocalReturn(ret)) => + debuglog("non local return in "+ret.symbol+" from "+currentOwner.enclMethod) + atPos(ret.pos)(nonLocalReturnThrow(expr, ret.symbol)) case TypeTree() => tree case _ => @@ -739,9 +707,9 @@ abstract class UnCurry extends InfoTransform * It looks for the method in the `repeatedParams` map, and generates a Java-style * varargs forwarder. It then adds the forwarder to the `newMembers` sequence. */ - private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef, tree: Tree): Unit = { - if (!repeatedParams.contains(dd.symbol)) - return + private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef): DefDef = { + if (!dd.symbol.hasAnnotation(VarargsClass) || !repeatedParams.contains(dd.symbol)) + return flatdd def toSeqType(tp: Type): Type = { val arg = elementType(ArrayClass, tp) @@ -762,7 +730,7 @@ abstract class UnCurry extends InfoTransform val reps = repeatedParams(dd.symbol) val rpsymbols = reps.map(_.symbol).toSet - val theTyper = typer.atOwner(tree, currentClass) + val theTyper = typer.atOwner(dd, currentClass) val flatparams = flatdd.vparamss.head // create the type @@ -814,10 +782,11 @@ abstract class UnCurry extends InfoTransform case None => // enter symbol into scope currentClass.info.decls enter forwsym - // add the method to `newMembers` newMembers += forwtree } + + flatdd } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 68bc80ffc4..b7a22c6ac1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -31,14 +31,14 @@ trait ContextErrors { case class NormalTypeError(underlyingTree: Tree, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) extends AbsTypeError { - + def errPos:Position = underlyingTree.pos override def toString() = "[Type error at:" + underlyingTree.pos + "] " + errMsg } case class SymbolTypeError(underlyingSym: Symbol, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) extends AbsTypeError { - + def errPos = underlyingSym.pos } @@ -76,7 +76,7 @@ trait ContextErrors { } def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) } - + def typeErrorMsg(found: Type, req: Type, possiblyMissingArgs: Boolean) = { def missingArgsMsg = if (possiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else "" "type mismatch" + foundReqMsg(found, req) + missingArgsMsg @@ -143,12 +143,12 @@ trait ContextErrors { found } assert(!found.isErroneous && !req.isErroneous, (found, req)) - + issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req))) ) if (settings.explaintypes.value) explainTypes(found, req) } - + def WithFilterError(tree: Tree, ex: AbsTypeError) = { issueTypeError(ex) setError(tree) @@ -177,13 +177,13 @@ trait ContextErrors { val calcSimilar = ( name.length > 2 && ( startingIdentCx.reportErrors - || startingIdentCx.enclClassOrMethod.reportErrors + || startingIdentCx.enclClassOrMethod.reportErrors ) ) - // avoid calculating if we're in "silent" mode. - // name length check to limit unhelpful suggestions for e.g. "x" and "b1" + // avoid calculating if we're in "silent" mode. + // name length check to limit unhelpful suggestions for e.g. "x" and "b1" val similar = { - if (!calcSimilar) "" + if (!calcSimilar) "" else { val allowed = ( startingIdentCx.enclosingContextChain @@ -624,11 +624,21 @@ trait ContextErrors { setError(tree) } - // checkNoDoubleDefs... - def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) = - issueSymbolTypeError(sym0, sym1+" is defined twice"+ - {if(!settings.debug.value) "" else " in "+context0.unit}+ - {if (sym0.isMacro && sym1.isMacro) " \n(note that macros cannot be overloaded)" else ""}) + // checkNoDoubleDefs... + // @PP: I hacked the filename in (context0.unit) to work around SI-4893. It would be + // much better if every symbol could offer some idea of where it came from, else + // the obviously untrue claim that something has been defined twice can only frustrate. + // There's no direct test because partest doesn't work, but to reproduce, separately + // compile the next two lines: + // package object foo { val x: Class[_] = null } + // package foo + def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) = { + val isBug = sym0.isAbstractType && sym1.isAbstractType && (sym0.name startsWith "_$") + issueSymbolTypeError(sym0, sym1+" is defined twice in " + context0.unit + + ( if (sym0.isMacro && sym1.isMacro) "\n(note that macros cannot be overloaded)" else "" ) + + ( if (isBug) "\n(this error is likely due to a bug in the scala compiler involving wildcards in package objects)" else "" ) + ) + } // cyclic errors def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) = @@ -636,11 +646,6 @@ trait ContextErrors { def CyclicReferenceError(errPos: Position, lockedSym: Symbol) = issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym)) - - def MacroExpandError(tree: Tree, t: Any) = { - issueNormalTypeError(tree, "macros must return a compiler-specific tree; returned class is: " + t.getClass) - setError(tree) - } } } @@ -667,7 +672,7 @@ trait ContextErrors { type ErrorType = Value val WrongNumber, NoParams, ArgsDoNotConform = Value } - + private def ambiguousErrorMsgPos(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) = if (sym1.hasDefaultFlag && sym2.hasDefaultFlag && sym1.enclClass == sym2.enclClass) { val methodName = nme.defaultGetterToMethod(sym1.name) @@ -714,9 +719,15 @@ trait ContextErrors { setError(tree) } - def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) = + def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) = { issueNormalTypeError(tree, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) + // since inferMethodAlternative modifies the state of the tree + // we have to set the type of tree to ErrorType only in the very last + // fallback action that is done in the inference (tracking it manually is error prone). + // This avoids entering infinite loop in doTypeApply. + if (implicitly[Context].reportErrors) setError(tree) + } def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, argtpes: List[Type], pt: Type) = { @@ -724,6 +735,8 @@ trait ContextErrors { "argument types " + argtpes.mkString("(", ",", ")") + (if (pt == WildcardType) "" else " and expected result type " + pt) val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) + // discover last attempt in a similar way as for NoBestMethodAlternativeError + if (implicitly[Context].ambiguousErrors) setError(tree) issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) } @@ -830,14 +843,14 @@ trait ContextErrors { object NamerErrorGen { implicit val context0 = context - + object SymValidateErrors extends Enumeration { val ImplicitConstr, ImplicitNotTerm, ImplicitTopObject, OverrideClass, SealedNonClass, AbstractNonClass, OverrideConstr, AbstractOverride, LazyAndEarlyInit, ByNameParameter, AbstractVar = Value } - + object DuplicatesErrorKinds extends Enumeration { val RenamedTwice, AppearsTwice = Value } @@ -845,7 +858,7 @@ trait ContextErrors { import SymValidateErrors._ import DuplicatesErrorKinds._ import symtab.Flags - + def TypeSigError(tree: Tree, ex: TypeError) = { ex match { case CyclicReference(sym, info: TypeCompleter) => @@ -854,7 +867,7 @@ trait ContextErrors { context0.issue(TypeErrorWithUnderlyingTree(tree, ex)) } } - + def GetterDefinedTwiceError(getter: Symbol) = issueSymbolTypeError(getter, getter+" is defined twice") @@ -897,37 +910,37 @@ trait ContextErrors { val msg = errKind match { case ImplicitConstr => "`implicit' modifier not allowed for constructors" - + case ImplicitNotTerm => "`implicit' modifier can be used only for values, variables and methods" - + case ImplicitTopObject => "`implicit' modifier cannot be used for top-level objects" - + case OverrideClass => "`override' modifier not allowed for classes" - + case SealedNonClass => "`sealed' modifier can be used only for classes" - + case AbstractNonClass => "`abstract' modifier can be used only for classes; it should be omitted for abstract members" - + case OverrideConstr => "`override' modifier not allowed for constructors" - + case AbstractOverride => "`abstract override' modifier only allowed for members of traits" - + case LazyAndEarlyInit => "`lazy' definitions may not be initialized early" - + case ByNameParameter => "pass-by-name arguments not allowed for case class parameters" - + case AbstractVar => "only classes can have declared but undefined members" + abstractVarMessage(sym) - + } issueSymbolTypeError(sym, msg) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index b5afd681d2..a1ba8a2982 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -21,7 +21,7 @@ trait Contexts { self: Analyzer => outer = this enclClass = this enclMethod = this - + override def nextEnclosing(p: Context => Boolean): Context = this override def enclosingContextChain: List[Context] = Nil override def implicitss: List[List[ImplicitInfo]] = Nil @@ -128,6 +128,8 @@ trait Contexts { self: Analyzer => var typingIndentLevel: Int = 0 def typingIndent = " " * typingIndentLevel + var buffer: Set[AbsTypeError] = _ + def enclClassOrMethod: Context = if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this else outer.enclClassOrMethod @@ -145,7 +147,6 @@ trait Contexts { self: Analyzer => } private[this] var mode = 0 - private[this] val buffer = LinkedHashSet[AbsTypeError]() def errBuffer = buffer def hasErrors = buffer.nonEmpty @@ -160,7 +161,7 @@ trait Contexts { self: Analyzer => def setReportErrors() = mode = (ReportErrors | AmbiguousErrors) def setBufferErrors() = { - assert(bufferErrors || !hasErrors, "When entering the buffer state, context has to be clean. Current buffer: " + buffer) + //assert(bufferErrors || !hasErrors, "When entering the buffer state, context has to be clean. Current buffer: " + buffer) mode = BufferErrors } def setThrowErrors() = mode &= (~AllMask) @@ -177,7 +178,7 @@ trait Contexts { self: Analyzer => buffer.clear() current } - + def logError(err: AbsTypeError) = buffer += err def withImplicitsDisabled[T](op: => T): T = { @@ -225,6 +226,7 @@ trait Contexts { self: Analyzer => c.checking = this.checking c.retyping = this.retyping c.openImplicits = this.openImplicits + c.buffer = if (this.buffer == null) LinkedHashSet[AbsTypeError]() else this.buffer // need to initialize registerContext(c.asInstanceOf[analyzer.Context]) debuglog("[context] ++ " + c.unit + " / " + tree.summaryString) c @@ -237,7 +239,7 @@ trait Contexts { self: Analyzer => c.implicitsEnabled = true c } - + def makeNewImport(sym: Symbol): Context = makeNewImport(gen.mkWildcardImport(sym)) @@ -265,6 +267,7 @@ trait Contexts { self: Analyzer => val c = make(newtree) c.setBufferErrors() c.setAmbiguousErrors(reportAmbiguousErrors) + c.buffer = new LinkedHashSet[AbsTypeError]() c } @@ -308,12 +311,14 @@ trait Contexts { self: Analyzer => unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg) def issue(err: AbsTypeError) { + debugwarn("issue error: " + err.errMsg) if (reportErrors) unitError(err.errPos, addDiagString(err.errMsg)) else if (bufferErrors) { buffer += err } else throw new TypeError(err.errPos, err.errMsg) } def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { + debugwarn("issue ambiguous error: " + err.errMsg) if (ambiguousErrors) { if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) unitError(err.errPos, err.errMsg) @@ -322,6 +327,7 @@ trait Contexts { self: Analyzer => } def issueAmbiguousError(err: AbsTypeError) { + debugwarn("issue ambiguous error: " + err.errMsg) if (ambiguousErrors) unitError(err.errPos, addDiagString(err.errMsg)) else if (bufferErrors) { buffer += err } diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 3536608efd..29831c8469 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package typechecker import scala.tools.nsc.symtab.Flags - import scala.collection.{ mutable, immutable } /** Duplicate trees and re-type check them, taking care to replace @@ -18,6 +17,7 @@ import scala.collection.{ mutable, immutable } */ abstract class Duplicators extends Analyzer { import global._ + import definitions.{ AnyRefClass, AnyValClass } def retyped(context: Context, tree: Tree): Tree = { resetClassOwners @@ -36,7 +36,7 @@ abstract class Duplicators extends Analyzer { } else resetClassOwners envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList) - log("retyped with env: " + env) + debuglog("retyped with env: " + env) (new BodyDuplicator(context)).typed(tree) } @@ -82,14 +82,14 @@ abstract class Duplicators extends Analyzer { val sym1 = context.scope.lookup(sym.name) // assert(sym1 ne NoSymbol, tpe) if ((sym1 ne NoSymbol) && (sym1 ne sym)) { - log("fixing " + sym + " -> " + sym1) + debuglog("fixing " + sym + " -> " + sym1) typeRef(NoPrefix, sym1, mapOverArgs(args, sym1.typeParams)) } else super.mapOver(tpe) case TypeRef(pre, sym, args) => val newsym = updateSym(sym) if (newsym ne sym) { - log("fixing " + sym + " -> " + newsym) + debuglog("fixing " + sym + " -> " + newsym) typeRef(mapOver(pre), newsym, mapOverArgs(args, newsym.typeParams)) } else super.mapOver(tpe) @@ -97,7 +97,7 @@ abstract class Duplicators extends Analyzer { case SingleType(pre, sym) => val sym1 = updateSym(sym) if (sym1 ne sym) { - log("fixing " + sym + " -> " + sym1) + debuglog("fixing " + sym + " -> " + sym1) singleType(mapOver(pre), sym1) } else super.mapOver(tpe) @@ -105,7 +105,7 @@ abstract class Duplicators extends Analyzer { case ThisType(sym) => val sym1 = updateSym(sym) if (sym1 ne sym) { - log("fixing " + sym + " -> " + sym1) + debuglog("fixing " + sym + " -> " + sym1) ThisType(sym1) } else super.mapOver(tpe) @@ -136,26 +136,26 @@ abstract class Duplicators extends Analyzer { private def invalidate(tree: Tree) { debuglog("attempting to invalidate " + tree.symbol + ", owner - " + (if (tree.symbol ne null) tree.symbol.owner else "<NULL>")) if (tree.isDef && tree.symbol != NoSymbol) { - log("invalid " + tree.symbol) + debuglog("invalid " + tree.symbol) invalidSyms(tree.symbol) = tree tree match { case ldef @ LabelDef(name, params, rhs) => - log("LabelDef " + name + " sym.info: " + ldef.symbol.info) + debuglog("LabelDef " + name + " sym.info: " + ldef.symbol.info) invalidSyms(ldef.symbol) = ldef // breakIf(true, this, ldef, context) val newsym = ldef.symbol.cloneSymbol(context.owner) newsym.setInfo(fixType(ldef.symbol.info)) ldef.symbol = newsym - log("newsym: " + newsym + " info: " + newsym.info) + debuglog("newsym: " + newsym + " info: " + newsym.info) case vdef @ ValDef(mods, name, _, rhs) if mods.hasFlag(Flags.LAZY) => - log("ValDef " + name + " sym.info: " + vdef.symbol.info) + debuglog("ValDef " + name + " sym.info: " + vdef.symbol.info) invalidSyms(vdef.symbol) = vdef val newsym = vdef.symbol.cloneSymbol(context.owner) newsym.setInfo(fixType(vdef.symbol.info)) vdef.symbol = newsym - log("newsym: " + newsym + " info: " + newsym.info) + debuglog("newsym: " + newsym + " info: " + newsym.info) case DefDef(_, name, tparams, vparamss, _, rhs) => // invalidate parameters @@ -182,7 +182,7 @@ abstract class Duplicators extends Analyzer { } ddef.symbol = NoSymbol enterSym(context, ddef) - log("remapping this of " + oldClassOwner + " to " + newClassOwner) + debuglog("remapping this of " + oldClassOwner + " to " + newClassOwner) typed(ddef) } @@ -228,7 +228,7 @@ abstract class Duplicators extends Analyzer { ttree case Block(stats, res) => - log("invalidating block") + debuglog("invalidating block") invalidate(stats) invalidate(res) tree.tpe = null @@ -256,7 +256,7 @@ abstract class Duplicators extends Analyzer { case ldef @ LabelDef(name, params, rhs) => // log("label def: " + ldef) ldef.tpe = null - val params1 = params map { p => Ident(updateSym(p.symbol)) } + val params1 = params map (p => Ident(updateSym(p.symbol))) super.typed(treeCopy.LabelDef(tree, name, params1, rhs), mode, pt) case Bind(name, _) => @@ -266,13 +266,13 @@ abstract class Duplicators extends Analyzer { super.typed(tree, mode, pt) case Ident(_) if tree.symbol.isLabel => - log("Ident to labeldef " + tree + " switched to ") + debuglog("Ident to labeldef " + tree + " switched to ") tree.symbol = updateSym(tree.symbol) tree.tpe = null super.typed(tree, mode, pt) case Ident(_) if (origtreesym ne null) && origtreesym.isLazy => - log("Ident to a lazy val " + tree + ", " + tree.symbol + " updated to " + origtreesym) + debuglog("Ident to a lazy val " + tree + ", " + tree.symbol + " updated to " + origtreesym) tree.symbol = updateSym(origtreesym) tree.tpe = null super.typed(tree, mode, pt) @@ -308,17 +308,26 @@ abstract class Duplicators extends Analyzer { super.typed(atPos(tree.pos)(tree1)) */ case Match(scrut, cases) => - val scrut1 = typed(scrut, EXPRmode | BYVALmode, WildcardType) + val scrut1 = typed(scrut, EXPRmode | BYVALmode, WildcardType) val scrutTpe = scrut1.tpe.widen - val cases1 = if (scrutTpe.isFinalType) cases filter { - case CaseDef(Bind(_, pat @ Typed(_, tpt)), EmptyTree, body) => - // the typed pattern is not incompatible with the scrutinee type - scrutTpe.matchesPattern(fixType(tpt.tpe)) - case CaseDef(Typed(_, tpt), EmptyTree, body) => - // the typed pattern is not incompatible with the scrutinee type - scrutTpe.matchesPattern(fixType(tpt.tpe)) - case _ => true - } else cases + val cases1 = { + if (scrutTpe.isFinalType) cases filter { + case CaseDef(Bind(_, pat @ Typed(_, tpt)), EmptyTree, body) => + // the typed pattern is not incompatible with the scrutinee type + scrutTpe matchesPattern fixType(tpt.tpe) + case CaseDef(Typed(_, tpt), EmptyTree, body) => + // the typed pattern is not incompatible with the scrutinee type + scrutTpe matchesPattern fixType(tpt.tpe) + case _ => true + } + // Without this, AnyRef specializations crash on patterns like + // case _: Boolean => ... + // Not at all sure this is safe. + else if (scrutTpe <:< AnyRefClass.tpe) + cases filterNot (_.pat.tpe <:< AnyValClass.tpe) + else + cases + } super.typed(atPos(tree.pos)(Match(scrut, cases1)), mode, pt) @@ -327,7 +336,7 @@ abstract class Duplicators extends Analyzer { tree case _ => - log("default: " + tree) + debuglog("Duplicators default case: " + tree.summaryString) if (tree.hasSymbol && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 036e7fc750..0ddacf7d36 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -215,7 +215,7 @@ trait Implicits { object HasMethodMatching { val dummyMethod = NoSymbol.newTermSymbol(newTermName("typer$dummy")) def templateArgType(argtpe: Type) = new BoundedWildcardType(TypeBounds.lower(argtpe)) - + def apply(name: Name, argtpes: List[Type], restpe: Type): Type = { val mtpe = MethodType(dummyMethod.newSyntheticValueParams(argtpes map templateArgType), restpe) memberWildcardType(name, mtpe) @@ -571,7 +571,7 @@ trait Implicits { else { val tvars = undetParams map freshVar def ptInstantiated = pt.instantiateTypeParams(undetParams, tvars) - + printInference("[search] considering %s (pt contains %s) trying %s against pt=%s".format( if (undetParams.isEmpty) "no tparams" else undetParams.map(_.name).mkString(", "), typeVarsInType(ptInstantiated) filterNot (_.isGround) match { case Nil => "no tvars" ; case tvs => tvs.mkString(", ") }, @@ -594,7 +594,7 @@ trait Implicits { // we must be conservative in leaving type params in undetparams // prototype == WildcardType: want to remove all inferred Nothings val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, tvars, targs) - + val subst: TreeTypeSubstituter = if (okParams.isEmpty) EmptyTreeTypeSubstituter else { @@ -621,7 +621,7 @@ trait Implicits { case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c case t => t } - + if (context.hasErrors) fail("typing TypeApply reported errors for the implicit tree") else { @@ -780,13 +780,13 @@ trait Implicits { val newPending = undoLog undo { is filterNot (alt => alt == i || { try improves(i, alt) - catch { - case e: CyclicReference => + catch { + case e: CyclicReference => if (printInfers) { println(i+" discarded because cyclic reference occurred") e.printStackTrace() } - true + true } }) } @@ -1163,7 +1163,7 @@ trait Implicits { /* !!! the following is almost right, but we have to splice nested manifest * !!! types into this type. This requires a substantial extension of * !!! reifiers. - val reifier = new liftcode.Reifier() + val reifier = new Reifier() val rtree = reifier.reifyTopLevel(tp1) manifestFactoryCall("apply", tp, rtree) */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index eac657da19..c0c801910c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -210,9 +210,9 @@ trait Infer { def getContext = context def issue(err: AbsTypeError): Unit = context.issue(err) - - def isPossiblyMissingArgs(found: Type, req: Type) = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req) - + + def isPossiblyMissingArgs(found: Type, req: Type) = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req) + def explainTypes(tp1: Type, tp2: Type) = withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) @@ -228,9 +228,9 @@ trait Infer { if (sym.isError) { tree setSymbol sym setType ErrorType } else { - val topClass = context.owner.toplevelClass + val topClass = context.owner.enclosingTopLevelClass if (context.unit.exists) - context.unit.depends += sym.toplevelClass + context.unit.depends += sym.enclosingTopLevelClass var sym1 = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])) // Console.println("check acc " + (sym, sym1) + ":" + (sym.tpe, sym1.tpe) + " from " + pre);//DEBUG @@ -465,7 +465,7 @@ trait Infer { */ def adjustTypeArgs(tparams: List[Symbol], tvars: List[TypeVar], targs: List[Type], restpe: Type = WildcardType): AdjustedTypeArgs.Result = { val buf = AdjustedTypeArgs.Result.newBuilder[Symbol, Option[Type]] - + foreach3(tparams, tvars, targs) { (tparam, tvar, targ) => val retract = ( targ.typeSymbol == NothingClass // only retract Nothings @@ -640,13 +640,7 @@ trait Infer { case ExistentialType(tparams, qtpe) => isApplicable(undetparams, qtpe, argtpes0, pt) case MethodType(params, _) => - val formals0 = params map { param => - param.tpe match { - case TypeRef(_, sym, List(tpe)) if sym isNonBottomSubClass CodeClass => tpe - case tpe => tpe - } - } - val formals = formalTypes(formals0, argtpes0.length) + val formals = formalTypes(params map { _.tpe }, argtpes0.length) def tryTupleApply: Boolean = { // if 1 formal, 1 argtpe (a tuple), otherwise unmodified argtpes0 @@ -1057,49 +1051,74 @@ trait Infer { * @param pt the expected result type of the instance */ def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type) { - val pt = widen(pt0) - //println("infer constr inst "+tree+"/"+undetparams+"/"+pt0) - var restpe = tree.tpe.finalResultType - var tvars = undetparams map freshVar + val pt = widen(pt0) + val ptparams = freeTypeParamsOfTerms.collect(pt) + val ctorTp = tree.tpe + val resTp = ctorTp.finalResultType - /** Compute type arguments for undetermined params and substitute them in given tree. + debuglog("infer constr inst "+ tree +"/"+ undetparams +"/ pt= "+ pt +" pt0= "+ pt0 +" resTp: "+ resTp) + + /** Compute type arguments for undetermined params */ - def computeArgs = - try { - val targs = solvedTypes(tvars, undetparams, undetparams map varianceInType(restpe), - true, lubDepth(List(restpe, pt))) -// checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ") -// no checkBounds here. If we enable it, test bug602 fails. - new TreeTypeSubstituter(undetparams, targs).traverse(tree) - } catch ifNoInstance{ msg => - NoConstructorInstanceError(tree, restpe, pt, msg) + def inferFor(pt: Type): Option[List[Type]] = { + val tvars = undetparams map freshVar + val resTpV = resTp.instantiateTypeParams(undetparams, tvars) + + if (resTpV <:< pt) { + try { + // debuglog("TVARS "+ (tvars map (_.constr))) + // look at the argument types of the primary constructor corresponding to the pattern + val variances = undetparams map varianceInType(ctorTp.paramTypes.headOption getOrElse ctorTp) + val targs = solvedTypes(tvars, undetparams, variances, true, lubDepth(List(resTp, pt))) + // checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ") + // no checkBounds here. If we enable it, test bug602 fails. + // TODO: reinstate checkBounds, return params that fail to meet their bounds to undetparams + Some(targs) + } catch ifNoInstance { msg => + debuglog("NO INST "+ (tvars, tvars map (_.constr))) + NoConstructorInstanceError(tree, resTp, pt, msg) + None + } + } else { + debuglog("not a subtype: "+ resTpV +" </:< "+ pt) + None } - def instError = { - if (settings.debug.value) Console.println("ici " + tree + " " + undetparams + " " + pt) - if (settings.explaintypes.value) explainTypes(restpe.instantiateTypeParams(undetparams, tvars), pt) - ConstrInstantiationError(tree, restpe, pt) } - if (restpe.instantiateTypeParams(undetparams, tvars) <:< pt) { - computeArgs - } else if (isFullyDefined(pt)) { - debuglog("infer constr " + tree + ":" + restpe + ", pt = " + pt) - var ptparams = freeTypeParamsOfTerms.collect(pt) - debuglog("free type params = " + ptparams) - val ptWithWildcards = pt.instantiateTypeParams(ptparams, ptparams map (ptparam => WildcardType)) - tvars = undetparams map freshVar - if (restpe.instantiateTypeParams(undetparams, tvars) <:< ptWithWildcards) { - computeArgs - restpe = skipImplicit(tree.tpe.resultType) - debuglog("new tree = " + tree + ":" + restpe) - val ptvars = ptparams map freshVar - val pt1 = pt.instantiateTypeParams(ptparams, ptvars) - if (isPopulated(restpe, pt1)) { - ptvars foreach instantiateTypeVar - } else { if (settings.debug.value) Console.println("no instance: "); instError } - } else { if (settings.debug.value) Console.println("not a subtype " + restpe.instantiateTypeParams(undetparams, tvars) + " of " + ptWithWildcards); instError } - } else { if (settings.debug.value) Console.println("not fully defined: " + pt); instError } + + def inferForApproxPt = + if (isFullyDefined(pt)) { + inferFor(pt.instantiateTypeParams(ptparams, ptparams map (x => WildcardType))) flatMap { targs => + val ctorTpInst = tree.tpe.instantiateTypeParams(undetparams, targs) + val resTpInst = skipImplicit(ctorTpInst.finalResultType) + val ptvars = + ptparams map { + // since instantiateTypeVar wants to modify the skolem that corresponds to the method's type parameter, + // and it uses the TypeVar's origin to locate it, deskolemize the existential skolem to the method tparam skolem + // (the existential skolem was created by adaptConstrPattern to introduce the type slack necessary to soundly deal with variant type parameters) + case skolem if skolem.isExistentialSkolem => freshVar(skolem.deSkolemize.asInstanceOf[TypeSymbol]) + case p => freshVar(p) + } + + val ptV = pt.instantiateTypeParams(ptparams, ptvars) + + if (isPopulated(resTpInst, ptV)) { + ptvars foreach instantiateTypeVar + debuglog("isPopulated "+ resTpInst +", "+ ptV +" vars= "+ ptvars) + Some(targs) + } else None + } + } else None + + (inferFor(pt) orElse inferForApproxPt) map { targs => + new TreeTypeSubstituter(undetparams, targs).traverse(tree) + } getOrElse { + debugwarn("failed inferConstructorInstance for "+ tree +" : "+ tree.tpe +" under "+ undetparams +" pt = "+ pt +(if(isFullyDefined(pt)) " (fully defined)" else " (not fully defined)")) + // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) + ConstrInstantiationError(tree, resTp, pt) + } } + def instBounds(tvar: TypeVar): (Type, Type) = { val tparam = tvar.origin.typeSymbol val instType = toOrigin(tvar.constr.inst) @@ -1391,10 +1410,11 @@ trait Infer { case _ => } } + // todo: missing test case NoBestExprAlternativeError(tree, pt) } else if (!competing.isEmpty) { - if (secondTry) NoBestExprAlternativeError(tree, pt) - else { if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) } + if (secondTry) { NoBestExprAlternativeError(tree, pt); setError(tree) } + else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) } else { // val applicable = alts1 filter (alt => // global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt)) @@ -1404,10 +1424,14 @@ trait Infer { } } - @inline private def inSilentMode(expr: Typer => Boolean): Boolean = { - val silentContext = context.makeSilent(context.ambiguousErrors) - val res = expr(newTyper(silentContext)) - if (silentContext.hasErrors) false else res + @inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = { + val oldState = context.state + context.setBufferErrors() + val res = expr + val contextWithErrors = context.hasErrors + context.flushBuffer() + context.restoreState(oldState) + res && !contextWithErrors } // Checks against the name of the parameter and also any @deprecatedName. @@ -1478,7 +1502,7 @@ trait Infer { val applicable = resolveOverloadedMethod(argtpes, { alts filter { alt => - inSilentMode(typer0 => typer0.infer.isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) && + inSilentMode(context)(isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) && (!varArgsOnly || isVarArgsList(alt.tpe.params)) } }) @@ -1494,14 +1518,12 @@ trait Infer { if (improves(alt, best)) alt else best) val competing = applicable.dropWhile(alt => best == alt || improves(best, alt)) if (best == NoSymbol) { - if (pt == WildcardType) - NoBestMethodAlternativeError(tree, argtpes, pt) - else - inferMethodAlternative(tree, undetparams, argtpes, WildcardType) + if (pt == WildcardType) NoBestMethodAlternativeError(tree, argtpes, pt) + else inferMethodAlternative(tree, undetparams, argtpes, WildcardType) } else if (!competing.isEmpty) { if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt) - setError(tree) + else setError(tree) () } else { // checkNotShadowed(tree.pos, pre, best, applicable) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index c63ae90ef6..e43b1fab0b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -20,50 +20,50 @@ trait Macros { self: Analyzer => macroArgs(fn) :+ args case TypeApply(fn, args) => macroArgs(fn) :+ args - case Select(qual, name) if !isStaticMacro(tree.symbol) => + case Select(qual, name) => List(List(qual)) case _ => List(List()) } - private def isStaticMacro(mac: Symbol): Boolean = - mac.owner.isModuleClass - /** - * The definition of the method implementing a macro. Example: + * The definition of the method implementing a macro. Example: * Say we have in a class C * * def macro foo[T](xs: List[T]): T = expr * * Then the following macro method is generated for `foo`: * - * def defmacro$foo(glob: scala.reflect.api.Universe) - * (_this: glob.Tree) - * (T: glob.Type) - * (xs: glob.Tree): glob.Tree = { - * implicit val $glob = glob + * def defmacro$foo + * (_context: scala.reflect.macro.Context) + * (_this: _context.Tree) + * (T: _context.TypeTree) + * (xs: _context.Tree): _context.Tree = { + * import _context._ // this means that all methods of Context can be used unqualified in macro's body * expr * } * - * If `foo` is declared in an object, the second parameter list is () instead of (_this: glob.Tree). + * If macro has no type arguments, the third parameter list is omitted (it's not empty, but omitted altogether). + * + * To find out the desugared representation of your particular macro, compile it with -Ymacro-debug. */ def macroMethDef(mdef: DefDef): Tree = { def paramDef(name: Name, tpt: Tree) = ValDef(Modifiers(PARAM), name, tpt, EmptyTree) val contextType = TypeTree(ReflectMacroContext.tpe) - val globParamSec = List(paramDef(nme.context, contextType)) - def globSelect(name: Name) = Select(Ident(nme.context), name) - def globTree = globSelect(newTypeName("Tree")) - def globType = globSelect(newTypeName("Type")) - val thisParamSec = if (isStaticMacro(mdef.symbol)) List() else List(paramDef(newTermName("_this"), globTree)) - def tparamInMacro(tdef: TypeDef) = paramDef(tdef.name.toTermName, globType) + val globParamSec = List(paramDef(nme.macroContext, contextType)) + def globSelect(name: Name) = Select(Ident(nme.macroContext), name) + def globTree = globSelect(tpnme.Tree) + def globTypeTree = globSelect(tpnme.TypeTree) + val thisParamSec = List(paramDef(newTermName(nme.macroThis), globTree)) + def tparamInMacro(tdef: TypeDef) = paramDef(tdef.name.toTermName, globTypeTree) def vparamInMacro(vdef: ValDef): ValDef = paramDef(vdef.name, vdef.tpt match { case tpt @ AppliedTypeTree(hk, _) if treeInfo.isRepeatedParamType(tpt) => AppliedTypeTree(hk, List(globTree)) case _ => globTree }) def wrapImplicit(tree: Tree) = atPos(tree.pos) { // implicit hasn't proven useful so far, so I'm disabling it - //val implicitDecl = ValDef(Modifiers(IMPLICIT), nme.contextImplicit, SingletonTypeTree(Ident(nme.context)), Ident(nme.context)) - val importGlob = Import(Ident(nme.context), List(ImportSelector(nme.WILDCARD, -1, null, -1))) + //val implicitDecl = ValDef(Modifiers(IMPLICIT), nme.macroContextImplicit, SingletonTypeTree(Ident(nme.macroContext)), Ident(nme.macroContext)) + val importGlob = Import(Ident(nme.macroContext), List(ImportSelector(nme.WILDCARD, -1, null, -1))) Block(List(importGlob), tree) } var formals = (mdef.vparamss map (_ map vparamInMacro)) @@ -82,7 +82,7 @@ trait Macros { self: Analyzer => def addMacroMethods(templ: Template, namer: Namer): Unit = { for (ddef @ DefDef(mods, _, _, _, _, _) <- templ.body if mods hasFlag MACRO) { - val trace = scala.tools.nsc.util.trace when settings.debug.value + val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value val sym = namer.enterSyntheticSym(trace("macro def: ")(macroMethDef(ddef))) trace("added to "+namer.context.owner.enclClass+": ")(sym) } @@ -90,33 +90,102 @@ trait Macros { self: Analyzer => lazy val mirror = new scala.reflect.runtime.Mirror { lazy val libraryClassLoader = { + // todo. this is more or less okay, but not completely correct + // see https://issues.scala-lang.org/browse/SI-5433 for more info val classpath = global.classPath.asURLs - ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + + // an heuristic to detect REPL + if (global.settings.exposeEmptyPackage.value) { + import scala.tools.nsc.interpreter._ + val virtualDirectory = global.settings.outputDirs.getSingleOutput.get + loader = new AbstractFileClassLoader(virtualDirectory, loader) {} + } + + loader } override def defaultReflectiveClassLoader() = libraryClassLoader } /** Return optionally address of companion object and implementation method symbol - * of given macro; or None if implementation classfile cannot be loaded or does + * of given macro; or None if implementation classfile cannot be loaded or does * not contain the macro implementation. */ def macroImpl(mac: Symbol): Option[(AnyRef, mirror.Symbol)] = { + val debug = settings.Ymacrodebug.value + val trace = scala.tools.nsc.util.trace when debug + trace("looking for macro implementation: ")(mac.fullNameString) + try { val mmeth = macroMeth(mac) + trace("found implementation at: ")(mmeth.fullNameString) + if (mmeth == NoSymbol) None else { - val receiverClass: mirror.Symbol = mirror.classWithName(mmeth.owner.fullName) + trace("loading implementation class: ")(mmeth.owner.fullName) + trace("classloader is: ")("%s of type %s".format(mirror.libraryClassLoader, mirror.libraryClassLoader.getClass)) + def inferClasspath(cl: ClassLoader) = cl match { + case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" + case _ => "<unknown>" + } + trace("classpath is: ")(inferClasspath(mirror.libraryClassLoader)) + + // @xeno.by: relies on the fact that macros can only be defined in static classes + def classfile(sym: Symbol): String = { + def recur(sym: Symbol): String = sym match { + case sym if sym.owner.isPackageClass => + val suffix = if (sym.isModuleClass) "$" else "" + sym.fullName + suffix + case sym => + val separator = if (sym.owner.isModuleClass) "" else "$" + recur(sym.owner) + separator + sym.javaSimpleName.toString + } + + if (sym.isClass || sym.isModule) recur(sym) + else recur(sym.enclClass) + } + + // @xeno.by: this doesn't work for inner classes + // neither does mmeth.owner.javaClassName, so I had to roll my own implementation + //val receiverName = mmeth.owner.fullName + val receiverName = classfile(mmeth.owner) + val receiverClass: mirror.Symbol = mirror.symbolForName(receiverName) + + if (debug) { + println("receiverClass is: " + receiverClass.fullNameString) + + val jreceiverClass = mirror.classToJava(receiverClass) + val jreceiverSource = jreceiverClass.getProtectionDomain.getCodeSource + println("jreceiverClass is %s from %s".format(jreceiverClass, jreceiverSource)) + println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, inferClasspath(jreceiverClass.getClassLoader))) + } + val receiverObj = receiverClass.companionModule - if (receiverObj == NoSymbol) None + trace("receiverObj is: ")(receiverObj.fullNameString) + + if (receiverObj == mirror.NoSymbol) None else { - val receiver = mirror.getCompanionObject(receiverClass) + // @xeno.by: yet another reflection method that doesn't work for inner classes + //val receiver = mirror.companionInstance(receiverClass) + val clazz = java.lang.Class.forName(receiverName, true, mirror.libraryClassLoader) + val receiver = clazz getField "MODULE$" get null + val rmeth = receiverObj.info.member(mirror.newTermName(mmeth.name.toString)) - Some((receiver, rmeth)) + if (debug) { + println("rmeth is: " + rmeth.fullNameString) + println("jrmeth is: " + mirror.methodToJava(rmeth)) + } + + if (rmeth == mirror.NoSymbol) None + else { + Some((receiver, rmeth)) + } } } } catch { case ex: ClassNotFoundException => + trace("implementation class failed to load: ")(ex.toString) None } } @@ -125,32 +194,62 @@ trait Macros { self: Analyzer => * Or, if that fails, and the macro overrides a method return * tree that calls this method instead of the macro. */ - def macroExpand(tree: Tree, context: Context): Option[Any] = { + def macroExpand(tree: Tree, typer: Typer): Option[Any] = { + val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value + trace("macroExpand: ")(tree) + val macroDef = tree.symbol macroImpl(macroDef) match { case Some((receiver, rmeth)) => val argss = List(global) :: macroArgs(tree) val paramss = macroMeth(macroDef).paramss + trace("paramss: ")(paramss) val rawArgss = for ((as, ps) <- argss zip paramss) yield { if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) else as } val rawArgs: Seq[Any] = rawArgss.flatten + trace("rawArgs: ")(rawArgs) + val savedInfolevel = nodePrinters.infolevel try { - Some(mirror.invoke(receiver, rmeth, rawArgs: _*)) + // @xeno.by: InfoLevel.Verbose examines and prints out infos of symbols + // by the means of this'es these symbols can climb up the lexical scope + // when these symbols will be examined by a node printer + // they will enumerate and analyze their children (ask for infos and tpes) + // if one of those children involves macro expansion, things might get nasty + // that's why I'm temporarily turning this behavior off + nodePrinters.infolevel = nodePrinters.InfoLevel.Quiet + val expanded = mirror.invoke(receiver, rmeth)(rawArgs: _*) + expanded match { + case expanded: Tree => + val expectedTpe = tree.tpe + val typed = typer.typed(expanded, EXPRmode, expectedTpe) + Some(typed) + case expanded if expanded.isInstanceOf[Tree] => + typer.context.unit.error(tree.pos, "macro must return a compiler-specific tree; returned value is Tree, but it doesn't belong to this compiler's universe") + None + case expanded => + typer.context.unit.error(tree.pos, "macro must return a compiler-specific tree; returned value is of class: " + expanded.getClass) + None + } } catch { case ex => val realex = ReflectionUtils.unwrapThrowable(ex) - val stacktrace = new java.io.StringWriter() - realex.printStackTrace(new java.io.PrintWriter(stacktrace)) - val msg = System.getProperty("line.separator") + stacktrace - context.unit.error(tree.pos, "exception during macro expansion: " + msg) + val msg = if (settings.Ymacrodebug.value) { + val stacktrace = new java.io.StringWriter() + realex.printStackTrace(new java.io.PrintWriter(stacktrace)) + System.getProperty("line.separator") + stacktrace + } else { + realex.getMessage + } + typer.context.unit.error(tree.pos, "exception during macro expansion: " + msg) None + } finally { + nodePrinters.infolevel = savedInfolevel } case None => - val trace = scala.tools.nsc.util.trace when settings.debug.value def notFound() = { - context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name) + typer.context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name) None } def fallBackToOverridden(tree: Tree): Option[Tree] = { @@ -159,7 +258,7 @@ trait Macros { self: Analyzer => macroDef.allOverriddenSymbols match { case first :: _ => Some(Select(qual, name) setPos tree.pos setSymbol first) - case _ => + case _ => trace("macro is not overridden: ")(tree) notFound() } diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 0c32ff32c0..088a56cd7b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -18,7 +18,7 @@ trait MethodSynthesis { import global._ import definitions._ import CODE._ - + object synthesisUtil { type M[T] = Manifest[T] type CM[T] = ClassManifest[T] @@ -39,7 +39,7 @@ trait MethodSynthesis { typeRef(container.typeConstructor.prefix, container, args map (_.tpe)) } - + def companionType[T](implicit m: M[T]) = getRequiredModule(m.erasure.getName).tpe @@ -71,7 +71,7 @@ trait MethodSynthesis { class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { private def isOverride(name: TermName) = clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz)) - + def newMethodFlags(name: TermName) = { val overrideFlag = if (isOverride(name)) OVERRIDE else 0L overrideFlag | SYNTHETIC @@ -82,7 +82,7 @@ trait MethodSynthesis { } private def finishMethod(method: Symbol, f: Symbol => Tree): Tree = - logResult("finishMethod")(localTyper typed ValOrDefDef(method, f(method))) + localTyper typed ValOrDefDef(method, f(method)) private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = { val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) @@ -200,7 +200,7 @@ trait MethodSynthesis { map (acc => atPos(vd.pos.focus)(acc derive annotations)) filterNot (_ eq EmptyTree) ) - log(trees.mkString("Accessor trees:\n ", "\n ", "\n")) + // log(trees.mkString("Accessor trees:\n ", "\n ", "\n")) if (vd.symbol.isLazy) List(stat) else trees case _ => @@ -282,7 +282,7 @@ trait MethodSynthesis { } } private def logDerived(result: Tree): Tree = { - log("[+derived] " + ojoin(mods.defaultFlagString, basisSym.accurateKindString, basisSym.getterName.decode) + debuglog("[+derived] " + ojoin(mods.defaultFlagString, basisSym.accurateKindString, basisSym.getterName.decode) + " (" + derivedSym + ")\n " + result) result @@ -326,22 +326,35 @@ trait MethodSynthesis { super.validate() } - // keep type tree of original abstract field - private def fixTypeTree(dd: DefDef): DefDef = { - dd.tpt match { - case tt: TypeTree if dd.rhs == EmptyTree => - tt setOriginal tree.tpt - case tpt => - tpt setPos tree.tpt.pos.focus - } - dd - } override def derivedTree: DefDef = { - fixTypeTree { - DefDef(derivedSym, - if (mods.isDeferred) EmptyTree - else gen.mkCheckInit(fieldSelection) - ) + // For existentials, don't specify a type for the getter, even one derived + // from the symbol! This leads to incompatible existentials for the field and + // the getter. Let the typer do all the work. You might think "why only for + // existentials, why not always," and you would be right, except: a single test + // fails, but it looked like some work to deal with it. Test neg/t0606.scala + // starts compiling (instead of failing like it's supposed to) because the typer + // expects to be able to identify escaping locals in typedDefDef, and fails to + // spot that brand of them. In other words it's an artifact of the implementation. + val tpt = derivedSym.tpe.finalResultType match { + case ExistentialType(_, _) => TypeTree() + case tp => TypeTree(tp) + } + tpt setPos focusPos(derivedSym.pos) + // keep type tree of original abstract field + if (mods.isDeferred) + tpt setOriginal tree.tpt + + // TODO - reconcile this with the DefDef creator in Trees (which + // at this writing presented no way to pass a tree in for tpt.) + atPos(derivedSym.pos) { + DefDef( + Modifiers(derivedSym.flags), + derivedSym.name.toTermName, + Nil, + Nil, + tpt, + if (mods.isDeferred) EmptyTree else gen.mkCheckInit(fieldSelection) + ) setSymbol derivedSym } } } @@ -363,7 +376,7 @@ trait MethodSynthesis { override def keepClean = !mods.isParamAccessor override def derivedTree = ( if (mods.isDeferred) EmptyTree - else treeCopy.ValDef(tree, mods | flagsExtra, name, tree.tpt, tree.rhs) + else copyValDef(tree)(mods = mods | flagsExtra, name = this.name) ) } case class Param(tree: ValDef) extends DerivedFromValDef { diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 35ee46363c..1566897dab 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -277,12 +277,16 @@ trait Namers extends MethodSynthesis { def assignAndEnterFinishedSymbol(tree: MemberDef): Symbol = { val sym = assignAndEnterSymbol(tree) sym setInfo completerOf(tree) - log("[+info] " + sym.fullLocationString) + // log("[+info] " + sym.fullLocationString) sym } private def logAssignSymbol(tree: Tree, sym: Symbol): Symbol = { - log("[+symbol] " + sym.hasFlagsToString(-1L) + " " + sym) + sym.name.toTermName match { + case nme.IMPORT | nme.OUTER | nme.ANON_CLASS_NAME | nme.ANON_FUN_NAME | nme.CONSTRUCTOR => () + case _ => + log("[+symbol] " + sym.debugLocationString) + } tree.symbol = sym sym } @@ -1315,7 +1319,7 @@ trait Namers extends MethodSynthesis { catch typeErrorHandler(tree, ErrorType) result match { - case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => typer.deskolemizeTypeParams(tparams)(result) + case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => deskolemizeTypeParams(tparams)(result) case _ => result } } @@ -1478,8 +1482,11 @@ trait Namers extends MethodSynthesis { private val ownerSym = owner.symbol override val typeParams = tparams map (_.symbol) //@M override val tree = restp.tree - if (ownerSym.isTerm) - typer skolemizeTypeParams tparams + + if (ownerSym.isTerm) { + val skolems = deriveFreshSkolems(tparams map (_.symbol)) + map2(tparams, skolems)(_ setSymbol _) + } def completeImpl(sym: Symbol) = { // @M an abstract type's type parameters are entered. diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 359e72e3e4..c621497618 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -37,21 +37,17 @@ trait NamesDefaults { self: Analyzer => } def isNamed(arg: Tree) = nameOf(arg).isDefined - /** @param pos maps indicies from old to new */ + /** @param pos maps indices from old to new */ def reorderArgs[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = { val res = new Array[T](args.length) - // (hopefully) faster than zipWithIndex - (0 /: args) { case (index, arg) => res(pos(index)) = arg; index + 1 } + foreachWithIndex(args)((arg, index) => res(pos(index)) = arg) res.toList } - /** @param pos maps indicies from new to old (!) */ + /** @param pos maps indices from new to old (!) */ def reorderArgsInv[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = { val argsArray = args.toArray - val res = new mutable.ListBuffer[T] - for (i <- 0 until argsArray.length) - res += argsArray(pos(i)) - res.toList + argsArray.indices map (i => argsArray(pos(i))) toList } /** returns `true` if every element is equal to its index */ @@ -432,11 +428,11 @@ trait NamesDefaults { self: Analyzer => } } else NoSymbol } - + private def savingUndeterminedTParams[T](context: Context)(fn: List[Symbol] => T): T = { val savedParams = context.extractUndetparams() val savedReporting = context.ambiguousErrors - + context.setAmbiguousErrors(false) try fn(savedParams) finally { @@ -455,7 +451,7 @@ trait NamesDefaults { self: Analyzer => || (ctx.owner.rawInfo.member(name) != NoSymbol) ) ) - + /** A full type check is very expensive; let's make sure there's a name * somewhere which could potentially be ambiguous before we go that route. */ @@ -507,7 +503,7 @@ trait NamesDefaults { self: Analyzer => /** * Removes name assignments from args. Additionally, returns an array mapping - * argument indicies from call-site-order to definition-site-order. + * argument indices from call-site-order to definition-site-order. * * Verifies that names are not specified twice, positional args don't appear * after named ones. @@ -523,7 +519,7 @@ trait NamesDefaults { self: Analyzer => def matchesName(param: Symbol) = !param.isSynthetic && ( (param.name == name) || (param.deprecatedParamName match { case Some(`name`) => - context0.unit.deprecationWarning(arg.pos, + context0.unit.deprecationWarning(arg.pos, "the parameter name "+ name +" has been deprecated. Use "+ param.name +" instead.") true case _ => false diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 6d31243fd0..b060fd7121 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -43,7 +43,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val outer = newTermName("<outer>") val runOrElse = newTermName("runOrElse") val zero = newTermName("zero") - val __match = newTermName("__match") + val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching... def counted(str: String, i: Int) = newTermName(str+i) } @@ -51,8 +51,8 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => object MatchTranslator { def apply(typer: Typer): MatchTranslation = { import typer._ - // typing `__match` to decide which MatchTranslator to create adds 4% to quick.comp.timer - newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName.__match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { + // typing `_match` to decide which MatchTranslator to create adds 4% to quick.comp.timer + newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { case SilentResultValue(ms) => new PureMatchTranslator(typer, ms) case _ => new OptimizingMatchTranslator(typer) } @@ -116,6 +116,10 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore => import typer.{typed, context, silent, reallyExists} + private def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { + case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) + case _ => tp + } /** Implement a pattern match by turning its cases (including the implicit failure case) * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. @@ -133,11 +137,6 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => // and the only place that emits Matches after typers is for exception handling anyway) assert(phase.id <= currentRun.typerPhase.id, phase) - def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) - case _ => tp - } - val scrutType = repeatedToSeq(elimAnonymousClass(scrut.tpe.widen)) val scrutSym = freshSym(scrut.pos, pureType(scrutType)) @@ -146,6 +145,47 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, matchOwner) } + // return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard) + // we don't have a global scrutinee -- the caught exception must be bound in each of the casedefs + // there's no need to check the scrutinee for null -- "throw null" becomes "throw new NullPointerException" + // try to simplify to a type-based switch, or fall back to a catch-all case that runs a normal pattern match + // unlike translateMatch, we type our result before returning it + def translateTry(caseDefs: List[CaseDef], pt: Type, pos: Position): List[CaseDef] = + // if they're already simple enough to be handled by the back-end, we're done + if (caseDefs forall treeInfo.isCatchCase) caseDefs + else { + val okPt = repeatedToSeq(pt) + val switch = { + val bindersAndCases = caseDefs map { caseDef => + // generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there) + // if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this) + val caseScrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) + (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, okPt)(caseDef), EmptySubstitution)) + } + + (emitTypeSwitch(bindersAndCases, pt) map (_.map(fixerUpper(matchOwner, pos).apply(_).asInstanceOf[CaseDef]))) + } + + val catches = switch getOrElse { + val scrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) + val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, okPt)(caseDef), EmptySubstitution))} + + val exSym = freshSym(pos, pureType(ThrowableClass.tpe), "ex") + + List( + atPos(pos) { + CaseDef( + Bind(exSym, Ident(nme.WILDCARD)), // TODO: does this need fixing upping? + EmptyTree, + combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, scrut => Throw(CODE.REF(exSym))) + ) + }) + } + + typer.typedCases(catches, ThrowableClass.tpe, WildcardType) + } + + /** The translation of `pat if guard => body` has two aspects: * 1) the substitution due to the variables bound by patterns @@ -213,13 +253,14 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => withSubPats(typeTestTreeMaker :+ extractor.treeMaker(patBinderOrCasted, pos), extractor.subBindersAndPatterns: _*) } - /** Decompose the pattern in `tree`, of shape C(p_1, ..., p_N), into a list of N symbols, and a list of its N sub-trees - * The list of N symbols contains symbols for every bound name as well as the un-named sub-patterns (fresh symbols are generated here for these) - * - * @arg patBinder symbol used to refer to the result of the previous pattern's extractor (will later be replaced by the outer tree with the correct tree to refer to that patterns result) - */ + object MaybeBoundTyped { - // the returned type is the one inferred by inferTypedPattern (`owntype`) + /** Decompose the pattern in `tree`, of shape C(p_1, ..., p_N), into a list of N symbols, and a list of its N sub-trees + * The list of N symbols contains symbols for every bound name as well as the un-named sub-patterns (fresh symbols are generated here for these). + * The returned type is the one inferred by inferTypedPattern (`owntype`) + * + * @arg patBinder symbol used to refer to the result of the previous pattern's extractor (will later be replaced by the outer tree with the correct tree to refer to that patterns result) + */ def unapply(tree: Tree): Option[(Symbol, Type)] = tree match { case Bound(subpatBinder, typed@Typed(expr, tpt)) => Some((subpatBinder, typed.tpe)) case Bind(_, typed@Typed(expr, tpt)) => Some((patBinder, typed.tpe)) @@ -668,6 +709,10 @@ class Foo(x: Other) { x._1 } // no error in this order def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = None + // for catch + def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = + None + abstract class TreeMaker { /** captures the scope and the value of the bindings in patterns * important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed) @@ -788,6 +833,7 @@ class Foo(x: Other) { x._1 } // no error in this order } // implements the run-time aspects of (§8.2) (typedPattern has already done the necessary type transformations) + // TODO: normalize construction, which yields a combination of a EqualityTestTreeMaker (when necessary) and a TypeTestTreeMaker case class TypeAndEqualityTestTreeMaker(prevBinder: Symbol, patBinder: Symbol, pt: Type, pos: Position) extends CondTreeMaker { val nextBinderTp = glb(List(patBinder.info.widen, pt)) @@ -843,6 +889,10 @@ class Foo(x: Other) { x._1 } // no error in this order val cond = typeAndEqualityTest(patBinder, pt) val res = codegen._asInstanceOf(patBinder, nextBinderTp) + + // TODO: remove this + def isStraightTypeTest = cond match { case TypeApply(_, _) => cond.symbol == Any_isInstanceOf case _ => false } + override def toString = "TET"+(patBinder, pt) } @@ -926,25 +976,30 @@ class Foo(x: Other) { x._1 } // no error in this order } // calls propagateSubstitution on the treemakers - def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){ - val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them + def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = { + val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them + combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, CODE.MATCHERROR(_)) + } - emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{ + def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFail: Tree => Tree): Tree = fixerUpper(owner, scrut.pos){ + emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt).getOrElse{ val (matcher, hasDefault, toHoist) = - if (casesUnOpt nonEmpty) { + if (casesNoSubstOnly nonEmpty) { // when specified, need to propagate pt explicitly (type inferencer can't handle it) val optPt = if (isFullyDefined(pt)) inMatchMonad(pt) else NoType - // do this check on casesUnOpt, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one + // do this check on casesNoSubstOnly, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one // exhaustivity and reachability must be checked before optimization as well - val hasDefault = casesUnOpt.nonEmpty && { - val nonTrivLast = casesUnOpt.last + // TODO: improve, a trivial type test before the body still makes for a default case + // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) + val hasDefault = casesNoSubstOnly.nonEmpty && { + val nonTrivLast = casesNoSubstOnly.last nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] } - val (cases, toHoist) = optimizeCases(scrutSym, casesUnOpt, pt) + val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) val combinedCases = cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt)) @@ -952,7 +1007,11 @@ class Foo(x: Other) { x._1 } // no error in this order (combinedCases, hasDefault, toHoist) } else (codegen.zero, false, Nil) - val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault) + // catch-all + val catchAll = + if (hasDefault) None // no need for a catch-all when there's already a default + else Some(matchFail) + val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, catchAll) if (toHoist isEmpty) expr else Block(toHoist, expr) } @@ -966,7 +1025,7 @@ class Foo(x: Other) { x._1 } // no error in this order // TODO: do this during tree construction, but that will require tracking the current owner in treemakers // TODO: assign more fine-grained positions // fixes symbol nesting, assigns positions - private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser { + protected def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser { currentOwner = origOwner override def traverse(t: Tree) { @@ -1019,7 +1078,7 @@ class Foo(x: Other) { x._1 } // no error in this order // codegen relevant to the structure of the translation (how extractors are combined) trait AbsCodegen { - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree def one(res: Tree, bodyPt: Type, matchPt: Type): Tree def zero: Tree def flatMap(prev: Tree, b: Symbol, next: Tree): Tree @@ -1098,10 +1157,10 @@ class Foo(x: Other) { x._1 } // no error in this order protected def matchMonadSym = oneSig.finalResultType.typeSymbol import CODE._ - def __match(n: Name): SelectStart = matchStrategy DOT n + def _match(n: Name): SelectStart = matchStrategy DOT n private lazy val oneSig: Type = - typer.typed(__match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message + typer.typed(_match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message } trait PureCodegen extends CodegenCore with PureMatchMonadInterface { @@ -1110,14 +1169,15 @@ class Foo(x: Other) { x._1 } // no error in this order object pureCodegen extends CommonCodegen { import CODE._ //// methods in MatchingStrategy (the monad companion) -- used directly in translation // __match.runOrElse(`scrut`)(`scrutSym` => `matcher`) - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree - = __match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher)) + // TODO: consider catchAll, or virtualized matching will break in exception handlers + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree + = _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher)) // __match.one(`res`) - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (__match(vpmName.one)) (res) + def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (_match(vpmName.one)) (res) // __match.zero - def zero: Tree = __match(vpmName.zero) + def zero: Tree = _match(vpmName.zero) // __match.guard(`c`, `then`) - def guard(c: Tree, then: Tree, tp: Type): Tree = __match(vpmName.guard) APPLY (c, then) + def guard(c: Tree, then: Tree, tp: Type): Tree = _match(vpmName.guard) APPLY (c, then) //// methods in the monad instance -- used directly in translation // `prev`.flatMap(`b` => `next`) @@ -1437,94 +1497,145 @@ class Foo(x: Other) { x._1 } // no error in this order } } - //// SWITCHES + //// SWITCHES -- TODO: operate on Tests rather than TreeMakers trait SwitchEmission extends TreeMakers with OptimizedMatchMonadInterface { self: CodegenCore => - object SwitchablePattern { def unapply(pat: Tree) = pat match { - case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches - case _ => false - }} - - // def isSwitchable(cases: List[(List[TreeMaker], Tree)]): Boolean = { - // def isSwitchableTreeMaker(tm: TreeMaker) = tm match { - // case tm@EqualityTestTreeMaker(_, SwitchablePattern(), _) => true - // case SubstOnlyTreeMaker(_) => true - // case AlternativesTreeMaker(_, altss, _) => altss forall (_.forall(isSwitchableTreeMaker)) - // case _ => false - // } - // } + abstract class SwitchMaker { + abstract class SwitchableTreeMakerExtractor { def unapply(x: TreeMaker): Option[Tree] } + val SwitchableTreeMaker: SwitchableTreeMakerExtractor - private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) + def alternativesSupported: Boolean - override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { - def sequence[T](xs: List[Option[T]]): Option[List[T]] = + def isDefault(x: CaseDef): Boolean + def defaultSym: Symbol + def defaultBody: Tree + def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef + + private def sequence[T](xs: List[Option[T]]): Option[List[T]] = if (xs exists (_.isEmpty)) None else Some(xs.flatten) - def isSwitchableTpe(tpe: Type): Boolean = - switchableTpes contains tpe - def switchableConstToInt(x: Tree): Tree = { - val Literal(const) = x - const.tag match { - case IntTag => x - case ByteTag | ShortTag | CharTag => Literal(Constant(const.intValue)) + // empty list ==> failure + def apply(cases: List[(Symbol, List[TreeMaker])], pt: Type): List[CaseDef] = { + val caseDefs = cases map { case (scrutSym, makers) => + makers match { + // default case + case (btm@BodyTreeMaker(body, _)) :: Nil => + Some(defaultCase(scrutSym, btm.substitution(body))) + // constant (or typetest for typeSwitch) + case SwitchableTreeMaker(pattern) :: (btm@BodyTreeMaker(body, _)) :: Nil => + Some(CaseDef(pattern, EmptyTree, btm.substitution(body))) + // alternatives + case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil if alternativesSupported => + val casePatterns = altss map { + case SwitchableTreeMaker(pattern) :: Nil => + Some(pattern) + case _ => + None + } + + sequence(casePatterns) map { patterns => + val substedBody = btm.substitution(body) + CaseDef(Alternative(patterns), EmptyTree, substedBody) + } + case _ => //println("can't emit switch for "+ makers) + None //failure (can't translate pattern to a switch) + } } - } - val caseDefs = cases map { makers => - removeSubstOnly(makers) match { - // default case (don't move this to unfold, as it may only occur on the top level, not as an alternative -- well, except in degenerate matches) - case (btm@BodyTreeMaker(body, _)) :: Nil => - Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body))) - // constant - case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => - Some(CaseDef(switchableConstToInt(const), EmptyTree, btm.substitution(body))) - // alternatives - case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty) - val caseConstants = altss map { - case EqualityTestTreeMaker(_, const@SwitchablePattern(), _) :: Nil => - Some(switchableConstToInt(const)) - case _ => - None + (for( + caseDefs <- sequence(caseDefs)) yield + if (caseDefs exists isDefault) caseDefs + else { + caseDefs :+ defaultCase() } + ) getOrElse Nil + } + } - sequence(caseConstants) map { contants => - val substedBody = btm.substitution(body) - CaseDef(Alternative(contants), EmptyTree, substedBody) - } - case _ => - None //failure (can't translate pattern to a switch) + class RegularSwitchMaker(scrutSym: Symbol) extends SwitchMaker { + val switchableTpe = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) + val alternativesSupported = true + + object SwitchablePattern { def unapply(pat: Tree): Option[Tree] = pat match { + case Literal(const@Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => + Some(Literal(Constant(const.intValue))) // TODO: Java 7 allows strings in switches + case _ => None + }} + + object SwitchableTreeMaker extends SwitchableTreeMakerExtractor { + def unapply(x: TreeMaker): Option[Tree] = x match { + case EqualityTestTreeMaker(_, SwitchablePattern(const), _) => Some(const) + case _ => None } } - if (!isSwitchableTpe(scrut.tpe)) - None // TODO: emit a cast of the scrutinee and a switch on the cast scrutinee if patterns allow switch but the type of the scrutinee doesn't - else { - sequence(caseDefs) map { caseDefs => - import CODE._ - val caseDefsWithDefault = { - def isDefault(x: CaseDef): Boolean = x match { - case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true - case _ => false - } - val hasDefault = caseDefs exists isDefault - if (hasDefault) caseDefs else { - val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } - caseDefs :+ default - } - } - val matcher = BLOCK( - if (scrut.tpe != IntClass.tpe) { - scrutSym setInfo IntClass.tpe - VAL(scrutSym) === (scrut DOT nme.toInt) - } else { - VAL(scrutSym) === scrut - }, - Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut - ) - // matcher filter (tree => tree.tpe == null) foreach println - // treeBrowser browse matcher - matcher // set type to avoid recursion in typedMatch + def isDefault(x: CaseDef): Boolean = x match { + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case _ => false + } + + def defaultSym: Symbol = scrutSym + def defaultBody: Tree = { import CODE._; MATCHERROR(REF(scrutSym)) } + def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) { + DEFAULT ==> body + }} + } + + override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { import CODE._ + val regularSwitchMaker = new RegularSwitchMaker(scrutSym) + // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result + if (regularSwitchMaker.switchableTpe(scrutSym.tpe)) { + val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) + if (caseDefsWithDefault isEmpty) None + else { + // match on scrutSym -- converted to an int if necessary -- not on scrut directly (to avoid duplicating scrut) + val scrutToInt: Tree = + if(scrutSym.tpe =:= IntClass.tpe) REF(scrutSym) + else (REF(scrutSym) DOT (nme.toInt)) + Some(BLOCK( + VAL(scrutSym) === scrut, + Match(scrutToInt, caseDefsWithDefault) + )) } + } else None + } + + // for the catch-cases in a try/catch + private object typeSwitchMaker extends SwitchMaker { + def switchableTpe(tp: Type) = true + val alternativesSupported = false // TODO: needs either back-end support of flattening of alternatives during typers + + // TODO: there are more treemaker-sequences that can be handled by type tests + // analyze the result of approximateTreeMaker rather than the TreeMaker itself + object SwitchableTreeMaker extends SwitchableTreeMakerExtractor { + def unapply(x: TreeMaker): Option[Tree] = x match { + case tm@TypeTestTreeMaker(_, _, _) => + Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */)) // -- TODO: use this if binder does not occur in the body + case tm@TypeAndEqualityTestTreeMaker(_, patBinder, pt, _) if tm.isStraightTypeTest => + Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */)) + case _ => + None + } + } + + def isDefault(x: CaseDef): Boolean = x match { + case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => true + case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => true + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case _ => false } + + lazy val defaultSym: Symbol = freshSym(NoPosition, ThrowableClass.tpe) + def defaultBody: Tree = Throw(CODE.REF(defaultSym)) + def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) { + CASE (Bind(scrutSym, Typed(Ident(nme.WILDCARD), TypeTree(ThrowableClass.tpe)))) ==> body + }} + } + + // TODO: drop null checks + override def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = { + val caseDefsWithDefault = typeSwitchMaker(bindersAndCases, pt) + if (caseDefsWithDefault isEmpty) None + else Some(caseDefsWithDefault) } } @@ -1551,33 +1662,31 @@ class Foo(x: Other) { x._1 } // no error in this order /** Inline runOrElse and get rid of Option allocations * - * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse (throw new MatchError(x)) + * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse ${catchAll(`scrut`)} * the matcher's optional result is encoded as a flag, keepGoing, where keepGoing == true encodes result.isEmpty, * if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x */ @inline private def dontStore(tp: Type) = (tp.typeSymbol eq UnitClass) || (tp.typeSymbol eq NothingClass) lazy val keepGoing = freshSym(NoPosition, BooleanClass.tpe, "keepGoing") setFlag MUTABLE lazy val matchRes = freshSym(NoPosition, AnyClass.tpe, "matchRes") setFlag MUTABLE - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean) = { + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]) = { matchRes.info = if (resTp ne NoType) resTp.widen else AnyClass.tpe // we don't always know resTp, and it might be AnyVal, in which case we can't assign NULL if (dontStore(resTp)) matchRes resetFlag MUTABLE // don't assign to Unit-typed var's, in fact, make it a val -- conveniently also works around SI-5245 BLOCK( VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite... - VAL(scrutSym) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for scrutSym in `matcher` -- the owner structure is repaired by fixerUpper + VAL(scrutSym) === scrut, VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above VAL(keepGoing) === TRUE, matcher, - if(hasDefault) REF(matchRes) - else (IF (REF(keepGoing)) THEN MATCHERROR(REF(scrutSym)) ELSE REF(matchRes)) + catchAll map { catchAllGen => (IF (REF(keepGoing)) THEN catchAllGen(REF(scrutSym)) ELSE REF(matchRes)) } getOrElse REF(matchRes) ) } // only used to wrap the RHS of a body def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = { BLOCK( - if (dontStore(matchPt)) res // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse... - else (REF(matchRes) === res), // _asInstanceOf(res, tp.widen, force = true) - REF(keepGoing) === FALSE, + REF(keepGoing) === FALSE, // comes before assignment to matchRes, so the latter is in tail positions (can ignore the trailing zero -- will disappear when we flatten blocks, which is TODO) + if (dontStore(matchPt)) res else (REF(matchRes) === res), // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse... zero // to have a nice lub for lubs -- otherwise we'll get a boxed unit here -- TODO: get rid of all those dangling else zero's ) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 04213cfda7..1e17cb2e3f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -150,7 +150,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R } // Override checking ------------------------------------------------------------ - + def isJavaVarargsAncestor(clazz: Symbol) = ( clazz.isClass && clazz.isJavaDefined @@ -167,14 +167,14 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R log("Found java varargs ancestor in " + clazz.fullLocationString + ".") val self = clazz.thisType val bridges = new ListBuffer[Tree] - + def varargBridge(member: Symbol, bridgetpe: Type): Tree = { log("Generating varargs bridge for " + member.fullLocationString + " of type " + bridgetpe) - + val bridge = member.cloneSymbolImpl(clazz, member.flags | VBRIDGE) setPos clazz.pos bridge.setInfo(bridgetpe.cloneInfo(bridge)) clazz.info.decls enter bridge - + val params = bridge.paramss.head val elemtp = params.last.tpe.typeArgs.head val idents = params map Ident @@ -183,7 +183,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R localTyper typed DefDef(bridge, body) } - + // For all concrete non-private members that have a (Scala) repeated parameter: // compute the corresponding method type `jtpe` with a Java repeated parameter // if a method with type `jtpe` exists and that method is not a varargs bridge @@ -203,7 +203,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R } } } - + bridges.toList } else Nil @@ -276,10 +276,13 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R * of class `clazz` are met. */ def checkOverride(member: Symbol, other: Symbol) { + debuglog("Checking validity of %s overriding %s".format(member.fullLocationString, other.fullLocationString)) + def memberTp = self.memberType(member) def otherTp = self.memberType(other) def noErrorType = other.tpe != ErrorType && member.tpe != ErrorType def isRootOrNone(sym: Symbol) = sym == RootClass || sym == NoSymbol + def isNeitherInClass = (member.owner != clazz) && (other.owner != clazz) def objectOverrideErrorMsg = ( "overriding " + other.fullLocationString + " with " + member.fullLocationString + ":\n" + "an overriding object must conform to the overridden object's class bound" + @@ -334,7 +337,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R def deferredCheck = member.isDeferred || !other.isDeferred def subOther(s: Symbol) = s isSubClass other.owner def subMember(s: Symbol) = s isSubClass member.owner - + if (subOther(member.owner) && deferredCheck) { //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG return @@ -381,7 +384,14 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R overrideError("cannot override final member"); // synthetic exclusion needed for (at least) default getters. } else if (!other.isDeferred && !member.isAnyOverride && !member.isSynthetic) { - overrideError("needs `override' modifier"); + if (isNeitherInClass && !(other.owner isSubClass member.owner)) + emitOverrideError( + clazz + " inherits conflicting members:\n " + + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member) + + "\n(Note: this can be resolved by declaring an override in " + clazz + ".)" + ) + else + overrideError("needs `override' modifier") } else if (other.isAbstractOverride && other.isIncompleteIn(clazz) && !member.isAbstractOverride) { overrideError("needs `abstract override' modifiers") } else if (member.isAnyOverride && (other hasFlag ACCESSOR) && other.accessed.isVariable && !other.accessed.isLazy) { @@ -420,12 +430,12 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R if( !(sameLength(member.typeParams, other.typeParams) && (memberTp.substSym(member.typeParams, other.typeParams) =:= otherTp)) ) // (1.6) overrideTypeError(); - } + } else if (other.isAbstractType) { //if (!member.typeParams.isEmpty) // (1.7) @MAT // overrideError("may not be parameterized"); val otherTp = self.memberInfo(other) - + if (!(otherTp.bounds containsType memberTp)) { // (1.7.1) overrideTypeError(); // todo: do an explaintypes with bounds here explainTypes(_.bounds containsType _, otherTp, memberTp) @@ -515,16 +525,17 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R !other.isDeferred && other.isJavaDefined && { // #3622: erasure operates on uncurried types -- // note on passing sym in both cases: only sym.isType is relevant for uncurry.transformInfo - def uncurryAndErase(tp: Type) = erasure.erasure(sym, uncurry.transformInfo(sym, tp)) + // !!! erasure.erasure(sym, uncurry.transformInfo(sym, tp)) gives erreneous of inaccessible type - check whether that's still the case! + def uncurryAndErase(tp: Type) = erasure.erasure(sym)(uncurry.transformInfo(sym, tp)) val tp1 = uncurryAndErase(clazz.thisType.memberType(sym)) val tp2 = uncurryAndErase(clazz.thisType.memberType(other)) - atPhase(currentRun.erasurePhase.next)(tp1 matches tp2) + afterErasure(tp1 matches tp2) }) def ignoreDeferred(member: Symbol) = ( (member.isAbstractType && !member.isFBounded) || ( member.isJavaDefined && - // the test requires atPhase(erasurePhase.next) so shouldn't be + // the test requires afterErasure so shouldn't be // done if the compiler has no erasure phase available (currentRun.erasurePhase == NoPhase || javaErasedOverridingSym(member) != NoSymbol) ) @@ -1053,7 +1064,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass) - def isSpecial(s: Symbol) = isValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass) || isMaybeValue(s) + def isSpecial(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass) || isMaybeValue(s) def possibleNumericCount = onSyms(_ filter (x => isNumeric(x) || isMaybeValue(x)) size) val nullCount = onSyms(_ filter (_ == NullClass) size) @@ -1074,7 +1085,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R if (nullCount == 2) nonSensible("", true) // null == null else if (nullCount == 1) { - if (onSyms(_ exists isValueClass)) // null == 5 + if (onSyms(_ exists isPrimitiveValueClass)) // null == 5 nonSensible("", false) else if (onTrees( _ exists isNew)) // null == new AnyRef nonSensibleWarning("a fresh object", false) @@ -1167,7 +1178,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case vsym => ValDef(vsym) } } - def createStaticModuleAccessor() = atPhase(phase.next) { + def createStaticModuleAccessor() = afterRefchecks { val method = ( sym.owner.newMethod(sym.name.toTermName, sym.pos, (sym.flags | STABLE) & ~MODULE) setInfoAndEnter NullaryMethodType(sym.moduleClass.tpe) @@ -1178,7 +1189,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R vdef, localTyper.typedPos(tree.pos) { val vsym = vdef.symbol - atPhase(phase.next) { + afterRefchecks { val rhs = gen.newModule(sym, vsym.tpe) val body = if (sym.owner.isTrait) rhs else gen.mkAssignAndReturn(vsym, rhs) DefDef(sym, body.changeOwner(vsym -> sym)) @@ -1214,12 +1225,12 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R else gen.mkAssignAndReturn(vsym, rhs) ) val lazyDef = atPos(tree.pos)(DefDef(lazySym, body.changeOwner(vsym -> lazySym))) - log("Made lazy def: " + lazyDef) + debuglog("Created lazy accessor: " + lazyDef) if (hasUnitType) List(typed(lazyDef)) else List( typed(ValDef(vsym)), - atPhase(phase.next)(typed(lazyDef)) + afterRefchecks(typed(lazyDef)) ) } @@ -1443,26 +1454,6 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R transform(qual) - case Apply(Select(New(tpt), name), args) - if (tpt.tpe.typeSymbol == ArrayClass && args.length >= 2) => - unit.deprecationWarning(tree.pos, - "new Array(...) with multiple dimensions has been deprecated; use Array.ofDim(...) instead") - val manif = { - var etpe = tpt.tpe - for (_ <- args) { etpe = etpe.typeArgs.headOption.getOrElse(NoType) } - if (etpe == NoType) { - unit.error(tree.pos, "too many dimensions for array creation") - Literal(Constant(null)) - } else { - localTyper.getManifestTree(tree, etpe, false) - } - } - val newResult = localTyper.typedPos(tree.pos) { - new ApplyToImplicitArgs(gen.mkMethodCall(ArrayModule, nme.ofDim, args), List(manif)) - } - currentApplication = tree - newResult - case Apply(fn, args) => checkSensible(tree.pos, fn, args) currentApplication = tree @@ -1531,17 +1522,17 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R ) case _ => () } - + // verify classes extending AnyVal meet the requirements // (whatever those are to be, but at least: @inline annotation) private def checkAnyValSubclass(clazz: Symbol) = { if ((clazz isSubClass AnyValClass) && (clazz ne AnyValClass) && !isPrimitiveValueClass(clazz)) { - if (!clazz.hasAnnotation(ScalaInlineClass)) - unit.error(clazz.pos, "Only @inline classes are allowed to extend AnyVal") if (clazz.isTrait) - unit.error(clazz.pos, "Only @inline classes (not traits) are allowed to extend AnyVal") + unit.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal") + /* [Martin] That one is already taken care of by Typers if (clazz.tpe <:< AnyRefClass.tpe) unit.error(clazz.pos, "Classes which extend AnyVal may not have an ancestor which inherits AnyRef") + */ } } @@ -1556,12 +1547,9 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R // inside annotations. applyRefchecksToAnnotations(tree) var result: Tree = tree match { - case DefDef(mods, name, tparams, vparams, tpt, EmptyTree) if tree.symbol.hasAnnotation(NativeAttr) => - tree.symbol.resetFlag(DEFERRED) - transform(treeCopy.DefDef( - tree, mods, name, tparams, vparams, tpt, - typed(gen.mkSysErrorCall("native method stub")) - )) + case DefDef(_, _, _, _, _, EmptyTree) if sym hasAnnotation NativeAttr => + sym resetFlag DEFERRED + transform(deriveDefDef(tree)(_ => typed(gen.mkSysErrorCall("native method stub")))) case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) => checkDeprecatedOvers(tree) @@ -1580,9 +1568,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R val bridges = addVarargBridges(currentOwner) checkAllOverrides(currentOwner) checkAnyValSubclass(currentOwner) - - if (bridges.nonEmpty) treeCopy.Template(tree, parents, self, body ::: bridges) - else tree + if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree case dc@TypeTreeWithDeferredRefCheck() => assert(false, "adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc"); dc case tpt@TypeTree() => diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 0ab09b4fec..243e685b13 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -24,7 +24,7 @@ import symtab.Flags._ */ abstract class SuperAccessors extends transform.Transform with transform.TypingTransformers { import global._ - import definitions.{ UnitClass, isRepeatedParamType, isByNameParamType, Any_asInstanceOf } + import definitions.{ UnitClass, ObjectClass, isRepeatedParamType, isByNameParamType, Any_asInstanceOf } import analyzer.{ restrictionError } /** the following two members override abstract members in Transform */ @@ -34,6 +34,12 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT new SuperAccTransformer(unit) class SuperAccTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + /** validCurrentOwner arrives undocumented, but I reverse engineer it to be + * a flag for needsProtectedAccessor which is false while transforming either + * a by-name argument block or a closure. This excludes them from being + * considered able to access protected members via subclassing (why?) which in turn + * increases the frequency with which needsProtectedAccessor will be true. + */ private var validCurrentOwner = true private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]() @@ -41,6 +47,25 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val buf = accDefs.getOrElse(clazz, sys.error("no acc def buf for "+clazz)) buf += typers(clazz) typed tree } + private def ensureAccessor(sel: Select) = { + val Select(qual, name) = sel + val sym = sel.symbol + val clazz = qual.symbol + val supername = nme.superName(name) + val superAcc = clazz.info.decl(supername).suchThat(_.alias == sym) orElse { + debuglog("add super acc " + sym + sym.locationString + " to `" + clazz);//debug + val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE) setAlias sym + val tpe = clazz.thisType memberType sym match { + case t if sym.isModule && !sym.isMethod => NullaryMethodType(t) + case t => t + } + acc setInfoAndEnter (tpe cloneInfo acc) + storeAccessorDefinition(clazz, DefDef(acc, EmptyTree)) + acc + } + + atPos(sel.pos)(Select(gen.mkAttributedThis(clazz), superAcc) setType sel.tpe) + } private def transformArgs(params: List[Symbol], args: List[Tree]) = { treeInfo.mapMethodParamsAndArgs(params, args) { (param, arg) => @@ -88,42 +113,21 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } } - private def transformSuperSelect(tree: Tree): Tree = tree match { - case Select(sup @ Super(_, mix), name) => - val sym = tree.symbol - val clazz = sup.symbol - - if (sym.isDeferred) { - val member = sym.overridingSymbol(clazz); - if (mix != tpnme.EMPTY || member == NoSymbol || - !((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz))) - unit.error(tree.pos, ""+sym+sym.locationString+" is accessed from super. It may not be abstract "+ - "unless it is overridden by a member declared `abstract' and `override'"); - } - if (tree.isTerm && mix == tpnme.EMPTY && - (clazz.isTrait || clazz != currentOwner.enclClass || !validCurrentOwner)) { - val supername = nme.superName(sym.name) - var superAcc = clazz.info.decl(supername).suchThat(_.alias == sym) - if (superAcc == NoSymbol) { - debuglog("add super acc " + sym + sym.locationString + " to `" + clazz);//debug - superAcc = clazz.newMethod(supername, tree.pos, SUPERACCESSOR | PRIVATE) setAlias sym - var superAccTpe = clazz.thisType.memberType(sym) - if (sym.isModule && !sym.isMethod) { - // the super accessor always needs to be a method. See #231 - superAccTpe = NullaryMethodType(superAccTpe) - } - superAcc setInfoAndEnter (superAccTpe cloneInfo superAcc) - storeAccessorDefinition(clazz, DefDef(superAcc, EmptyTree)) - } - atPos(sup.pos) { - Select(gen.mkAttributedThis(clazz), superAcc) setType tree.tpe; - } - } else { - tree - } - case _ => - assert(tree.tpe.isError, tree) - tree + private def transformSuperSelect(sel: Select): Tree = { + val Select(sup @ Super(_, mix), name) = sel + val sym = sel.symbol + val clazz = sup.symbol + + if (sym.isDeferred) { + val member = sym.overridingSymbol(clazz); + if (mix != tpnme.EMPTY || member == NoSymbol || + !((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz))) + unit.error(sel.pos, ""+sym.fullLocationString+" is accessed from super. It may not be abstract "+ + "unless it is overridden by a member declared `abstract' and `override'"); + } + if (name.isTermName && mix == tpnme.EMPTY && (clazz.isTrait || clazz != currentClass || !validCurrentOwner)) + ensureAccessor(sel) + else sel } // Disallow some super.XX calls targeting Any methods which would @@ -156,9 +160,11 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT for (s <- decls) { if (s.privateWithin.isClass && !s.isProtected && !s.privateWithin.isModuleClass && !s.hasFlag(EXPANDEDNAME) && !s.isConstructor) { + val savedName = s.name decls.unlink(s) s.expandName(s.privateWithin) decls.enter(s) + log("Expanded '%s' to '%s' in %s".format(savedName, s.name, sym)) } } if (settings.verbose.value && forScaladoc && !sym.isAnonymousClass) { @@ -177,7 +183,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case ModuleDef(_, _, _) => checkCompanionNameClashes(sym) super.transform(tree) - case Template(parents, self, body) => + case Template(_, _, body) => val ownAccDefs = new ListBuffer[Tree] accDefs(currentOwner) = ownAccDefs @@ -189,7 +195,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val body1 = atOwner(currentOwner)(transformTrees(body)) accDefs -= currentOwner ownAccDefs ++= body1 - treeCopy.Template(tree, parents, self, ownAccDefs.toList) + deriveTemplate(tree)(_ => ownAccDefs.toList) case TypeApply(sel @ Select(This(_), name), args) => mayNeedProtectedAccessor(sel, args, false) @@ -218,24 +224,47 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT // direct calls to aliases of param accessors to the superclass in order to avoid // duplicating fields. if (sym.isParamAccessor && sym.alias != NoSymbol) { - val result = localTyper.typed { - Select( - Super(qual, tpnme.EMPTY/*qual.symbol.info.parents.head.symbol.name*/) setPos qual.pos, - sym.alias) setPos tree.pos - } + val result = (localTyper.typedPos(tree.pos) { + Select(Super(qual, tpnme.EMPTY) setPos qual.pos, sym.alias) + }).asInstanceOf[Select] debuglog("alias replacement: " + tree + " ==> " + result);//debug localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, true)) } - else mayNeedProtectedAccessor(sel, List(EmptyTree), false) + else { + /** A trait which extends a class and accesses a protected member + * of that class cannot implement the necessary accessor method + * because its implementation is in an implementation class (e.g. + * Foo$class) which inherits nothing, and jvm access restrictions + * require the call site to be in an actual subclass. So non-trait + * classes inspect their ancestors for any such situations and + * generate the accessors. See SI-2296. + */ + // FIXME - this should be unified with needsProtectedAccessor, but some + // subtlety which presently eludes me is foiling my attempts. + val shouldEnsureAccessor = ( + currentClass.isTrait + && sym.isProtected + && sym.enclClass != currentClass + && !sym.owner.isTrait + && (sym.owner.enclosingPackageClass != currentPackage) + && (qual.symbol.info.member(sym.name) ne NoSymbol) + ) + if (shouldEnsureAccessor) { + log("Ensuring accessor for call to protected " + sym.fullLocationString + " from " + currentClass) + ensureAccessor(sel) + } + else + mayNeedProtectedAccessor(sel, List(EmptyTree), false) + } - case Select(Super(_, mix), name) => + case sel @ Select(Super(_, mix), name) => if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) { unit.error(tree.pos, "super may be not be used on "+ sym.accessedOrSelf) } else if (isDisallowed(sym)) { unit.error(tree.pos, "super not allowed here: use this." + name.decode + " instead") } - transformSuperSelect(tree) + transformSuperSelect(sel) case TypeApply(sel @ Select(qual, name), args) => mayNeedProtectedAccessor(sel, args, true) @@ -280,11 +309,10 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } private def withInvalidOwner[A](trans: => A): A = { - val prevValidCurrentOwner = validCurrentOwner + val saved = validCurrentOwner validCurrentOwner = false - val result = trans - validCurrentOwner = prevValidCurrentOwner - result + try trans + finally validCurrentOwner = saved } /** Add a protected accessor, if needed, and return a tree that calls @@ -294,7 +322,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT private def makeAccessor(tree: Select, targs: List[Tree]): Tree = { val Select(qual, name) = tree val sym = tree.symbol - val clazz = hostForAccessorOf(sym, currentOwner.enclClass) + val clazz = hostForAccessorOf(sym, currentClass) assert(clazz != NoSymbol, sym) debuglog("Decided for host class: " + clazz) @@ -334,7 +362,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } val selection = Select(This(clazz), protAcc) def mkApply(fn: Tree) = Apply(fn, qual :: Nil) - val res = atPos(tree.pos) { + val res = atPos(tree.pos) { targs.head match { case EmptyTree => mkApply(selection) case _ => mkApply(TypeApply(selection, targs)) @@ -373,27 +401,27 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT */ private def makeSetter(tree: Select): Tree = { val field = tree.symbol - val clazz = hostForAccessorOf(field, currentOwner.enclClass) + val clazz = hostForAccessorOf(field, currentClass) assert(clazz != NoSymbol, field) debuglog("Decided for host class: " + clazz) - + val accName = nme.protSetterName(field.originalName) val protectedAccessor = clazz.info decl accName orElse { val protAcc = clazz.newMethod(accName, field.pos) val paramTypes = List(clazz.typeOfThis, field.tpe) val params = protAcc newSyntheticValueParams paramTypes val accessorType = MethodType(params, UnitClass.tpe) - + protAcc setInfoAndEnter accessorType val obj :: value :: Nil = params storeAccessorDefinition(clazz, DefDef(protAcc, Assign(Select(Ident(obj), field.name), Ident(value)))) - + protAcc } atPos(tree.pos)(Select(This(clazz), protectedAccessor)) } - /** Does `sym` need an accessor when accessed from `currentOwner`? + /** Does `sym` need an accessor when accessed from `currentClass`? * A special case arises for classes with explicit self-types. If the * self type is a Java class, and a protected accessor is needed, we issue * an error. If the self type is a Scala class, we don't add an accessor. @@ -407,23 +435,20 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT * classes, this has to be signaled as error. */ private def needsProtectedAccessor(sym: Symbol, pos: Position): Boolean = { - val clazz = currentOwner.enclClass + val clazz = currentClass def accessibleThroughSubclassing = validCurrentOwner && clazz.thisSym.isSubClass(sym.owner) && !clazz.isTrait - def packageAccessBoundry(sym: Symbol) = { - val b = sym.accessBoundary(sym.owner) - if (b.isPackageClass) b - else b.enclosingPackageClass - } + def packageAccessBoundry(sym: Symbol) = + sym.accessBoundary(sym.enclosingPackageClass) val isCandidate = ( sym.isProtected && sym.isJavaDefined && !sym.isDefinedInPackage && !accessibleThroughSubclassing - && (sym.owner.enclosingPackageClass != currentOwner.enclosingPackageClass) - && (sym.owner.enclosingPackageClass == packageAccessBoundry(sym)) + && (sym.enclosingPackageClass != currentPackage) + && (sym.enclosingPackageClass == sym.accessBoundary(sym.enclosingPackageClass)) ) val host = hostForAccessorOf(sym, clazz) def isSelfType = !(host.tpe <:< host.typeOfThis) && { @@ -433,15 +458,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT ) true } - def isJavaProtected = host.isTrait && sym.isJavaDefined && { - restrictionError(pos, unit, - """|%s accesses protected %s inside a concrete trait method. - |Add an accessor in a class extending %s as a workaround.""".stripMargin.format( - clazz, sym, sym.enclClass) - ) - true - } - isCandidate && !host.isPackageClass && !isSelfType && !isJavaProtected + isCandidate && !host.isPackageClass && !isSelfType } /** Return the innermost enclosing class C of referencingClass for which either diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 3ee5bf601d..def6475221 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -126,8 +126,8 @@ trait SyntheticMethods extends ast.TreeDSL { * def canEqual(that: Any) = that.isInstanceOf[This] */ def canEqualMethod: Tree = ( - createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m => - Ident(m.firstParam) IS_OBJ typeCaseType(clazz)) + createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m => + Ident(m.firstParam) IS_OBJ classExistentialType(clazz)) ) /** The equality method for case classes. @@ -143,7 +143,7 @@ trait SyntheticMethods extends ast.TreeDSL { */ def equalsClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => val arg0 = Ident(m.firstParam) - val thatTest = gen.mkIsInstanceOf(arg0, typeCaseType(clazz), true, false) + val thatTest = gen.mkIsInstanceOf(arg0, classExistentialType(clazz), true, false) val thatCast = gen.mkCast(arg0, clazz.tpe) def argsBody: Tree = { @@ -259,11 +259,11 @@ trait SyntheticMethods extends ast.TreeDSL { } if (phase.id > currentRun.typerPhase.id) templ - else treeCopy.Template(templ, templ.parents, templ.self, + else deriveTemplate(templ)(body => if (clazz.isCase) caseTemplateBody() else synthesize() match { - case Nil => templ.body // avoiding unnecessary copy - case ms => templ.body ++ ms + case Nil => body // avoiding unnecessary copy + case ms => body ++ ms } ) } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 4f4087a953..1434002121 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -415,7 +415,7 @@ trait TypeDiagnostics { "\nIf applicable, you may wish to try moving some members into another object." ) } - + /** Report a type error. * * @param pos0 The position where to report the error diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 66330d4321..24ec0c8028 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -604,6 +604,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * 1. Check that non-function pattern expressions are stable * 2. Check that packages and static modules are not used as values * 3. Turn tree type into stable type if possible and required by context. + * 4. Give getClass calls a more precise type based on the type of the target of the call. */ private def stabilize(tree: Tree, pre: Type, mode: Int, pt: Type): Tree = { if (tree.symbol.isOverloaded && !inFunMode(mode)) @@ -614,7 +615,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (tree.isErrorTyped) tree else if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1) - if (sym.isValue) checkStable(tree) + if (sym.isValue) { + val tree1 = checkStable(tree) + // A module reference in a pattern has type Foo.type, not "object Foo" + if (sym.isModule && !sym.isMethod) tree1 setType singleType(pre, sym) + else tree1 + } else fail() } else if ((mode & (EXPRmode | QUALmode)) == EXPRmode && !sym.isValue && !phase.erasedTypes) { // (2) fail() @@ -622,7 +628,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (sym.isStable && pre.isStable && !isByNameParamType(tree.tpe) && (isStableContext(tree, mode, pt) || sym.isModule && !sym.isMethod)) tree.setType(singleType(pre, sym)) - else tree + // To fully benefit from special casing the return type of + // getClass, we have to catch it immediately so expressions + // like x.getClass().newInstance() are typed with the type of x. + else if ( tree.symbol.name == nme.getClass_ + && tree.tpe.params.isEmpty + // TODO: If the type of the qualifier is inaccessible, we can cause private types + // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this + // so for now it requires the type symbol be public. + && pre.typeSymbol.isPublic) + tree setType MethodType(Nil, erasure.getClassReturnType(pre)) + else + tree } } @@ -708,8 +725,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def isCodeType(tpe: Type) = tpe.typeSymbol isNonBottomSubClass CodeClass - /** Perform the following adaptations of expression, pattern or type `tree` wrt to * given mode `mode` and given prototype `pt`: * (-1) For expressions with annotated types, let AnnotationCheckers decide what to do @@ -849,6 +864,33 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } + /** + * To deal with the type slack between actual (run-time) types and statically known types, for each abstract type T, + * reflect its variance as a skolem that is upper-bounded by T (covariant position), or lower-bounded by T (contravariant). + * + * Consider the following example: + * + * class AbsWrapperCov[+A] + * case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B] + * + * def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match { + * case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, it's *upper-bounded* by it + * wrapped // : Wrapped[_ <: T] + * } + * + * this method should type check if and only if Wrapped is covariant in its type parameter + * + * when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T], + * we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T} + * as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound + * + * since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?), + * we can simply replace skolems that represent method type parameters as seen from the method's body + * by other skolems that are (upper/lower)-bounded by that type-parameter skolem + * (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt) + * + * see test/files/../t5189*.scala + */ def adaptConstrPattern(): Tree = { // (5) val extractor = tree.symbol.filter(sym => reallyExists(unapplyMember(sym.tpe))) if (extractor != NoSymbol) { @@ -862,7 +904,32 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) .setOriginal(tree) - inferConstructorInstance(tree1, clazz.typeParams, pt) + val skolems = new mutable.ListBuffer[TypeSymbol] + object variantToSkolem extends VariantTypeMap { + def apply(tp: Type) = mapOver(tp) match { + case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => + val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) + val skolem = context.owner.newExistentialSkolem(tpSym, tpSym, unit.freshTypeName("?"+tpSym.name), bounds) + // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt) + skolems += skolem + skolem.tpe + case tp1 => tp1 + } + } + + // have to open up the existential and put the skolems in scope + // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) + val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? + val freeVars = skolems.toList + + // use "tree" for the context, not context.tree: don't make another CaseDef context, + // as instantiateTypeVar's bounds would end up there + val ctorContext = context.makeNewScope(tree, context.owner) + freeVars foreach ctorContext.scope.enter + newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) + + // tree1's type-slack skolems will be deskolemized (to the method type parameter skolems) + // once the containing CaseDef has been type checked (see typedCase) tree1 } else { tree @@ -943,10 +1010,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (tree.isType) adaptType() - else if (inExprModeButNot(mode, FUNmode) && tree.symbol != null && tree.symbol.isMacro && !tree.isDef) { - val tree1 = expandMacro(tree) - if (tree1.isErroneous) tree1 else typed(tree1, mode, pt) - } else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) + else if (inExprModeButNot(mode, FUNmode) && tree.symbol != null && tree.symbol.isMacro && !tree.isDef && !(tree exists (_.isErroneous))) + macroExpand(tree, this) match { + case Some(expanded: Tree) => + typed(expanded, mode, pt) + case None => + setError(tree) // error already reported + } + else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) adaptConstrPattern() else if (inAllModes(mode, EXPRmode | FUNmode) && !tree.tpe.isInstanceOf[MethodType] && @@ -1410,7 +1481,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedClassDef(cdef: ClassDef): Tree = { // attributes(cdef) val clazz = cdef.symbol - val typedMods = removeAnnotations(cdef.mods) + val typedMods = typedModifiers(cdef.mods) assert(clazz != NoSymbol, cdef) reenterTypeParams(cdef.tparams) val tparams1 = cdef.tparams mapConserve (typedTypeDef) @@ -1451,7 +1522,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { linkedClass.info.decl(nme.CONSTRUCTOR).alternatives foreach (_.initialize) val clazz = mdef.symbol.moduleClass - val typedMods = removeAnnotations(mdef.mods) + val typedMods = typedModifiers(mdef.mods) assert(clazz != NoSymbol, mdef) val impl1 = typerReportAnyContextErrors(context.make(mdef.impl, clazz, newScope)) { _.typedTemplate(mdef.impl, { @@ -1509,12 +1580,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (templ.symbol == NoSymbol) templ setSymbol clazz.newLocalDummy(templ.pos) val self1 = templ.self match { - case vd @ ValDef(mods, name, tpt, EmptyTree) => + case vd @ ValDef(_, _, tpt, EmptyTree) => val tpt1 = checkNoEscaping.privates( clazz.thisSym, treeCopy.TypeTree(tpt).setOriginal(tpt) setType vd.symbol.tpe ) - treeCopy.ValDef(vd, mods, name, tpt1, EmptyTree) setType NoType + copyValDef(vd)(tpt = tpt1, rhs = EmptyTree) setType NoType } // was: // val tpt1 = checkNoEscaping.privates(clazz.thisSym, typedType(tpt)) @@ -1551,8 +1622,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { /** Remove definition annotations from modifiers (they have been saved * into the symbol's ``annotations'' in the type completer / namer) + * + * However reification does need annotation definitions to proceed. + * Unfortunately, AnnotationInfo doesn't provide enough info to reify it in general case. + * The biggest problem is with the "atp: Type" field, which cannot be reified in some situations + * that involve locally defined annotations. See more about that in Reifiers.scala. + * + * That's why the original tree gets saved into ``original'' field of AnnotationInfo (happens elsewhere). + * The field doesn't get pickled/unpickled and exists only during a single compilation run. + * This simultaneously allows us to reify annotations and to preserve backward compatibility. */ - def removeAnnotations(mods: Modifiers): Modifiers = + def typedModifiers(mods: Modifiers): Modifiers = mods.copy(annotations = Nil) setPositions mods.positions /** @@ -1563,7 +1643,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // attributes(vdef) val sym = vdef.symbol.initialize val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor) - val typedMods = removeAnnotations(vdef.mods) + val typedMods = typedModifiers(vdef.mods) // complete lazy annotations val annots = sym.annotations @@ -1774,7 +1854,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt)) checkNonCyclic(ddef, tpt1) ddef.tpt.setType(tpt1.tpe) - val typedMods = removeAnnotations(ddef.mods) + val typedMods = typedModifiers(ddef.mods) var rhs1 = if (ddef.name == nme.CONSTRUCTOR && !ddef.symbol.hasStaticFlag) { // need this to make it possible to generate static ctors if (!meth.isPrimaryConstructor && @@ -1827,7 +1907,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { tdef.symbol.initialize reenterTypeParams(tdef.tparams) val tparams1 = tdef.tparams mapConserve typedTypeDef - val typedMods = removeAnnotations(tdef.mods) + val typedMods = typedModifiers(tdef.mods) // complete lazy annotations val annots = tdef.symbol.annotations @@ -1862,8 +1942,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val restpe = ldef.symbol.tpe.resultType val rhs1 = typed(ldef.rhs, restpe) ldef.params foreach (param => param.tpe = param.symbol.tpe) - treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs1) setType restpe - } else { + deriveLabelDef(ldef)(_ => rhs1) setType restpe + } + else { val initpe = ldef.symbol.tpe.resultType val rhs1 = typed(ldef.rhs) val restpe = rhs1.tpe @@ -1876,7 +1957,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(List(), restpe)) val rhs2 = typed(resetAllAttrs(ldef.rhs), restpe) ldef.params foreach (param => param.tpe = param.symbol.tpe) - treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs2) setSymbol sym2 setType restpe + deriveLabelDef(ldef)(_ => rhs2) setSymbol sym2 setType restpe } } } @@ -1981,18 +2062,38 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree else typed(cdef.guard, BooleanClass.tpe) var body1: Tree = typed(cdef.body, pt) - if (!context.savedTypeBounds.isEmpty) { - body1.tpe = context.restoreTypeBounds(body1.tpe) - if (isFullyDefined(pt) && !(body1.tpe <:< pt)) { - // @M no need for pt.normalize here, is done in erasure + + val contextWithTypeBounds = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) + if (contextWithTypeBounds.savedTypeBounds nonEmpty) { + body1.tpe = contextWithTypeBounds restoreTypeBounds body1.tpe + + // insert a cast if something typechecked under the GADT constraints, + // but not in real life (i.e., now that's we've reset the method's type skolems' + // infos back to their pre-GADT-constraint state) + if (isFullyDefined(pt) && !(body1.tpe <:< pt)) body1 = typedPos(body1.pos)(gen.mkCast(body1, pt)) - } + } + // body1 = checkNoEscaping.locals(context.scope, pt, body1) - treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe + val treeWithSkolems = treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe + + // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher + // TODO: Paul, can we do the deskolemization lazily in the old pattern matcher + object deskolemizeOnce extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case TypeRef(pre, sym, args) if sym.isExistentialSkolem && sym.deSkolemize.isSkolem && sym.deSkolemize.owner.isTerm => + typeRef(NoPrefix, sym.deSkolemize, args) + case tp1 => tp1 + } + } + + new TypeMapTreeSubstituter(deskolemizeOnce).traverse(treeWithSkolems) + + treeWithSkolems // now without skolems, actually } - def typedCases(tree: Tree, cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = + def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = cases mapConserve { cdef => newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) } @@ -2005,8 +2106,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { */ def typedFunction(fun: Function, mode: Int, pt: Type): Tree = { val numVparams = fun.vparams.length - val codeExpected = !forMSIL && (pt.typeSymbol isNonBottomSubClass CodeClass) - if (numVparams > definitions.MaxFunctionArity) return MaxFunctionArityError(fun) @@ -2023,7 +2122,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) - val (clazz, argpts, respt) = decompose(if (codeExpected) pt.normalize.typeArgs.head else pt) + val (clazz, argpts, respt) = decompose(pt) if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts) else { @@ -2033,7 +2132,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (isFullyDefined(argpt)) argpt else { fun match { - case etaExpansion(vparams, fn, args) if !codeExpected => + case etaExpansion(vparams, fn, args) => silent(_.typed(fn, forFunMode(mode), pt)) match { case SilentResultValue(fn1) if context.undetparams.isEmpty => // if context,undetparams is not empty, the function was polymorphic, @@ -2065,13 +2164,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val restpe = packedType(body1, fun.symbol).deconst.resultType val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) // body = checkNoEscaping.locals(context.scope, restpe, body) - val fun1 = treeCopy.Function(fun, vparams, body1).setType(funtpe) - if (codeExpected) lifted(fun1) else fun1 - } + treeCopy.Function(fun, vparams, body1).setType(funtpe) } - - def lifted(tree: Tree): Tree = typedPos(tree.pos) { - Apply(Select(Ident(CodeModule), nme.lift_), List(tree)) } def typedRefinement(stats: List[Tree]) { @@ -2181,6 +2275,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // error for this is issued in RefChecks.checkDefaultsInOverloaded if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag && !e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) { + log("Double definition detected:\n " + + ((e.sym.getClass, e.sym.info, e.sym.ownerChain)) + "\n " + + ((e1.sym.getClass, e1.sym.info, e1.sym.ownerChain))) + DefDefinedTwiceError(e.sym, e1.sym) scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779 } @@ -2794,7 +2892,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (hasError) annotationError - else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setPos(ann.pos) + else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(ann).setPos(ann.pos) } } else if (requireJava) { reportAnnotationError(NestedAnnotationError(ann, annType)) @@ -2834,7 +2932,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def annInfo(t: Tree): AnnotationInfo = t match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => - AnnotationInfo(annType, args, List()).setPos(t.pos) + AnnotationInfo(annType, args, List()).setOriginal(ann).setPos(t.pos) case Block(stats, expr) => context.warning(t.pos, "Usage of named or default arguments transformed this annotation\n"+ @@ -2869,6 +2967,33 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type? sym.isTypeParameter && sym.owner.isJavaDefined + /** If we map a set of hidden symbols to their existential bounds, we + * have a problem: the bounds may themselves contain references to the + * hidden symbols. So this recursively calls existentialBound until + * the typeSymbol is not amongst the symbols being hidden. + */ + def existentialBoundsExcludingHidden(hidden: List[Symbol]): Map[Symbol, Type] = { + def safeBound(t: Type): Type = + if (hidden contains t.typeSymbol) safeBound(t.typeSymbol.existentialBound.bounds.hi) else t + + def hiBound(s: Symbol): Type = safeBound(s.existentialBound.bounds.hi) match { + case tp @ RefinedType(parents, decls) => + val parents1 = parents mapConserve safeBound + if (parents eq parents1) tp + else copyRefinedType(tp, parents1, decls) + case tp => tp + } + + (hidden map { s => + // Hanging onto lower bound in case anything interesting + // happens with it. + (s, s.existentialBound match { + case TypeBounds(lo, hi) => TypeBounds(lo, hiBound(s)) + case _ => hiBound(s) + }) + }).toMap + } + /** Given a set `rawSyms` of term- and type-symbols, and a type * `tp`, produce a set of fresh type parameters and a type so that * it can be abstracted to an existential type. Every type symbol @@ -2886,12 +3011,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * only the type of the Ident is changed. */ protected def existentialTransform[T](rawSyms: List[Symbol], tp: Type)(creator: (List[Symbol], Type) => T): T = { + val allBounds = existentialBoundsExcludingHidden(rawSyms) val typeParams: List[Symbol] = rawSyms map { sym => val name = sym.name match { case x: TypeName => x - case x => newTypeName(x + ".type") + case x => nme.singletonName(x) } - val bound = sym.existentialBound + val bound = allBounds(sym) val sowner = if (isRawParameter(sym)) context.owner else sym.owner val quantified = sowner.newExistential(name, sym.pos) @@ -3015,40 +3141,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { packSymbols(localSyms.toList, normalizedTpe) } - /** Replace type parameters with their TypeSkolems, which can later - * be deskolemized to the original type param. (A skolem is a - * representation of a bound variable when viewed inside its scope) - * !!!Adriaan: this does not work for hk types. - */ - def skolemizeTypeParams(tparams: List[TypeDef]): List[TypeDef] = { - class Deskolemizer extends LazyType { - override val typeParams = tparams map (_.symbol) - val typeSkolems = typeParams map (_.newTypeSkolem setInfo this) - // Replace the symbols - def substitute() = map2(tparams, typeSkolems)(_ setSymbol _) - override def complete(sym: Symbol) { - // The info of a skolem is the skolemized info of the - // actual type parameter of the skolem - sym setInfo sym.deSkolemize.info.substSym(typeParams, typeSkolems) - } - } - (new Deskolemizer).substitute() - } - /** Convert to corresponding type parameters all skolems of method - * parameters which appear in `tparams`. - */ - def deskolemizeTypeParams(tparams: List[Symbol])(tp: Type): Type = { - class DeSkolemizeMap extends TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) if sym.isTypeSkolem && (tparams contains sym.deSkolemize) => - mapOver(typeRef(NoPrefix, sym.deSkolemize, args)) - case _ => - mapOver(tp) - } - } - new DeSkolemizeMap mapOver tp - } - def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) = if (!checkClassType(tpt, true, false) && noGen) tpt else atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) @@ -3305,7 +3397,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { typed1(atPos(tree.pos) { Function(params, body) }, mode, pt) } else { val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt) + var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) if (isPastTyper || !opt.virtPatmat) { val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) @@ -3321,7 +3413,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match { case Block(vd :: Nil, tree@Match(selector, cases)) => val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt) + var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) if (needAdapt) cases1 = cases1 map (adaptCase(_, owntype)) @@ -3657,8 +3749,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (ps.isEmpty) ps = site.parents filter (_.typeSymbol.toInterface.name == mix) if (ps.isEmpty) { - if (settings.debug.value) - Console.println(site.parents map (_.typeSymbol.name))//debug + debuglog("Fatal: couldn't find site " + site + " in " + site.parents.map(_.typeSymbol.name)) if (phase.erasedTypes && context.enclClass.owner.isImplClass) { // println(qual1) // println(clazz) @@ -3733,7 +3824,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { return typed(treeCopy.Select(tree, qual1, name), mode, pt) } if (!reallyExists(sym)) { - if (context.owner.toplevelClass.isJavaDefined && name.isTypeName) { + if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } if (tree1 != EmptyTree) return typed1(tree1, mode, pt) } @@ -3805,7 +3896,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (settings.warnSelectNullable.value && isPotentialNullDeference && unit != null) unit.warning(tree.pos, "potential null pointer dereference: "+tree) - val selection = result match { + result match { // could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual? case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs nonEmpty => // TODO: somehow the new qual is not checked in refchecks treeCopy.SelectFromTypeTree( @@ -3827,22 +3918,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case _ => result } - // To fully benefit from special casing the return type of - // getClass, we have to catch it immediately so expressions - // like x.getClass().newInstance() are typed with the type of x. - val isRefinableGetClass = ( - !selection.isErrorTyped - && selection.symbol.name == nme.getClass_ - && selection.tpe.params.isEmpty - // TODO: If the type of the qualifier is inaccessible, we can cause private types - // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this - // so for now it requires the type symbol be public. - && qual.tpe.typeSymbol.isPublic - ) - if (isRefinableGetClass) - selection setType MethodType(Nil, getClassReturnType(qual.tpe)) - else - selection } } @@ -4120,7 +4195,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def adaptCase(cdef: CaseDef, tpe: Type): CaseDef = - treeCopy.CaseDef(cdef, cdef.pat, cdef.guard, adapt(cdef.body, mode, tpe)) + deriveCaseDef(cdef)(adapt(_, mode, tpe)) // begin typed1 val sym: Symbol = tree.symbol @@ -4224,7 +4299,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Try(block, catches, finalizer) => var block1 = typed(block, pt) - var catches1 = typedCases(tree, catches, ThrowableClass.tpe, pt) + var catches1 = typedCases(catches, ThrowableClass.tpe, pt) val finalizer1 = if (finalizer.isEmpty) finalizer else typed(finalizer, UnitClass.tpe) val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe))) @@ -4232,6 +4307,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { block1 = adapt(block1, mode, owntype) catches1 = catches1 map (adaptCase(_, owntype)) } + + if(!isPastTyper && opt.virtPatmat) { + catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos) + } + treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype case Throw(expr) => @@ -4518,13 +4598,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def expandMacro(tree: Tree): Tree = - macroExpand(tree, context) match { - case Some(t: Tree) => t - case Some(t) => MacroExpandError(tree, t) - case None => setError(tree) // error already reported - } - def atOwner(owner: Symbol): Typer = newTyper(context.make(context.tree, owner)) @@ -4641,7 +4714,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case None => typed(tree, mode, pt) } - def findManifest(tp: Type, full: Boolean) = atPhase(currentRun.typerPhase) { + def findManifest(tp: Type, full: Boolean) = beforeTyper { inferImplicit( EmptyTree, appliedType((if (full) FullManifestClass else PartialManifestClass).typeConstructor, List(tp)), diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index b8bc0946c1..cc272b7b8d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -22,7 +22,7 @@ trait Unapplies extends ast.TreeDSL import treeInfo.{ isRepeatedParamType, isByNameParamType } private val unapplyParamName = nme.x_0 - + /** returns type list for return type of the extraction */ def unapplyTypeList(ufn: Symbol, ufntpe: Type) = { assert(ufn.isMethod, ufn) diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala index 1db6c38b4d..fbe92e5d84 100755 --- a/src/compiler/scala/tools/nsc/util/DocStrings.scala +++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala @@ -71,13 +71,35 @@ object DocStrings { * Every section starts with a `@` and extends to the next `@`, or * to the end of the comment string, but excluding the final two * characters which terminate the comment. + * + * Also take usecases into account - they need to expand until the next + * usecase or the end of the string, as they might include other sections + * of their own */ def tagIndex(str: String, p: Int => Boolean = (idx => true)): List[(Int, Int)] = findAll(str, 0) (idx => str(idx) == '@' && p(idx)) match { case List() => List() - case idxs => idxs zip (idxs.tail ::: List(str.length - 2)) + case idxs => { + val idxs2 = mergeUsecaseSections(str, idxs) + idxs2 zip (idxs2.tail ::: List(str.length - 2)) + } } + /** + * Merge sections following an usecase into the usecase comment, so they + * can override the parent symbol's sections + */ + def mergeUsecaseSections(str: String, idxs: List[Int]): List[Int] = { + idxs.find(str.substring(_).startsWith("@usecase")) match { + case Some(firstUC) => + val commentSections = idxs.take(idxs.indexOf(firstUC)) + val usecaseSections = idxs.drop(idxs.indexOf(firstUC)).filter(str.substring(_).startsWith("@usecase")) + commentSections ::: usecaseSections + case None => + idxs + } + } + /** Does interval `iv` start with given `tag`? */ def startsWithTag(str: String, section: (Int, Int), tag: String): Boolean = diff --git a/src/compiler/scala/tools/nsc/util/ProxyReport.scala b/src/compiler/scala/tools/nsc/util/ProxyReport.scala index 2f4f029308..4fc86c3a32 100644 --- a/src/compiler/scala/tools/nsc/util/ProxyReport.scala +++ b/src/compiler/scala/tools/nsc/util/ProxyReport.scala @@ -141,6 +141,6 @@ object ProxyReportRunner { s.processArguments(args.toList.tail, true) val g = new ProxyGlobal(s) val run = new g.Run() - g.atPhase(run.typerPhase.next)(g.proxyReport.generate(dir)) + g.afterTyper(g.proxyReport.generate(dir)) } } diff --git a/src/compiler/scala/tools/nsc/util/Statistics.scala b/src/compiler/scala/tools/nsc/util/Statistics.scala index f7c27dceb5..d1cdd30dd8 100644 --- a/src/compiler/scala/tools/nsc/util/Statistics.scala +++ b/src/compiler/scala/tools/nsc/util/Statistics.scala @@ -20,7 +20,7 @@ class Statistics extends scala.reflect.internal.util.Statistics { val typedSelectCount = new Counter val typerNanos = new Timer val classReadNanos = new Timer - + val failedApplyNanos = new Timer val failedOpEqNanos = new Timer val failedSilentNanos = new Timer diff --git a/src/compiler/scala/tools/nsc/util/WeakHashSet.scala b/src/compiler/scala/tools/nsc/util/WeakHashSet.scala new file mode 100644 index 0000000000..6a10422b00 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/WeakHashSet.scala @@ -0,0 +1,60 @@ +package scala.tools.nsc.util + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.Builder +import scala.collection.mutable.SetBuilder +import scala.runtime.AbstractFunction1 + +/** A bare-bones implementation of a mutable `Set` that uses weak references + * to hold the elements. + * + * This implementation offers only add/remove/test operations, + * therefore it does not fulfill the contract of Scala collection sets. + */ +class WeakHashSet[T <: AnyRef] extends AbstractFunction1[T, Boolean] { + private val underlying = mutable.HashSet[WeakReferenceWithEquals[T]]() + + /** Add the given element to this set. */ + def +=(elem: T): this.type = { + underlying += new WeakReferenceWithEquals(elem) + this + } + + /** Remove the given element from this set. */ + def -=(elem: T): this.type = { + underlying -= new WeakReferenceWithEquals(elem) + this + } + + /** Does the given element belong to this set? */ + def contains(elem: T): Boolean = + underlying.contains(new WeakReferenceWithEquals(elem)) + + /** Does the given element belong to this set? */ + def apply(elem: T): Boolean = contains(elem) + + /** Return the number of elements in this set, including reclaimed elements. */ + def size = underlying.size + + /** Remove all elements in this set. */ + def clear() = underlying.clear() +} + +/** A WeakReference implementation that implements equals and hashCode by + * delegating to the referent. + */ +class WeakReferenceWithEquals[T <: AnyRef](ref: T) { + def get(): T = underlying.get() + + override val hashCode = ref.hashCode + + override def equals(other: Any): Boolean = other match { + case wf: WeakReferenceWithEquals[_] => + underlying.get() == wf.get() + case _ => + false + } + + private val underlying = new java.lang.ref.WeakReference(ref) +} diff --git a/src/compiler/scala/tools/util/EditDistance.scala b/src/compiler/scala/tools/util/EditDistance.scala index 5f152ecabb..0af34020a8 100644 --- a/src/compiler/scala/tools/util/EditDistance.scala +++ b/src/compiler/scala/tools/util/EditDistance.scala @@ -8,7 +8,7 @@ package util object EditDistance { import java.lang.Character.{ toLowerCase => lower } - + def similarString(name: String, allowed: TraversableOnce[String]): String = { val suggested = suggestions(name, allowed.toSeq, maxDistance = 1, maxSuggestions = 2) if (suggested.isEmpty) "" |