diff options
Diffstat (limited to 'src/compiler')
199 files changed, 7767 insertions, 2807 deletions
diff --git a/src/compiler/scala/reflect/internal/AnnotationInfos.scala b/src/compiler/scala/reflect/internal/AnnotationInfos.scala index 9a7c79d856..b86c62661a 100644 --- a/src/compiler/scala/reflect/internal/AnnotationInfos.scala +++ b/src/compiler/scala/reflect/internal/AnnotationInfos.scala @@ -116,7 +116,7 @@ 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 + // 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 } @@ -168,24 +168,15 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => * * `assocs` stores arguments to classfile annotations as name-value pairs. */ - sealed abstract class AnnotationInfo extends Product3[Type, List[Tree], List[(Name, ClassfileAnnotArg)]] { + sealed abstract class AnnotationInfo { def atp: Type def args: List[Tree] def assocs: List[(Name, ClassfileAnnotArg)] - // @xeno.by: necessary for reification, see Reifiers.scala for more info + // 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" - // see annotationArgRewriter lazy val isTrivial = atp.isTrivial && !hasArgWhich(_.isInstanceOf[This]) @@ -270,7 +261,7 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => } lazy val classfileAnnotArgManifest: ClassManifest[ClassfileAnnotArg] = - reflect.ClassManifest.classType(classOf[ClassfileAnnotArg]) + reflect.ClassManifest[ClassfileAnnotArg](classOf[ClassfileAnnotArg]) object UnmappableAnnotation extends CompleteAnnotationInfo(NoType, Nil, Nil) } diff --git a/src/compiler/scala/reflect/internal/CapturedVariables.scala b/src/compiler/scala/reflect/internal/CapturedVariables.scala new file mode 100644 index 0000000000..77909d9157 --- /dev/null +++ b/src/compiler/scala/reflect/internal/CapturedVariables.scala @@ -0,0 +1,36 @@ +package scala.reflect +package internal + +import Flags._ + +trait CapturedVariables { self: SymbolTable => + + import definitions._ + + /** Mark a variable as captured; i.e. force boxing in a *Ref type. + */ + def captureVariable(vble: Symbol): Unit = vble setFlag CAPTURED + + /** Mark given identifier as a reference to a captured variable itself + * suppressing dereferencing with the `elem` field. + */ + def referenceCapturedVariable(vble: Symbol): Tree = ReferenceToBoxed(Ident(vble)) + + /** Convert type of a captured variable to *Ref type. + */ + def capturedVariableType(vble: Symbol): Type = + capturedVariableType(vble, NoType, false) + + /** Convert type of a captured variable to *Ref type. + */ + def capturedVariableType(vble: Symbol, tpe: Type = NoType, erasedTypes: Boolean = false): Type = { + val tpe1 = if (tpe == NoType) vble.tpe else tpe + val symClass = tpe1.typeSymbol + def refType(valueRef: Map[Symbol, Symbol], objectRefClass: Symbol) = + if (isPrimitiveValueClass(symClass) && symClass != UnitClass) valueRef(symClass).tpe + else if (erasedTypes) objectRefClass.tpe + else appliedType(objectRefClass, tpe) + if (vble.hasAnnotation(VolatileAttr)) refType(volatileRefClass, VolatileObjectRefClass) + else refType(refClass, ObjectRefClass) + } +} diff --git a/src/compiler/scala/reflect/internal/Constants.scala b/src/compiler/scala/reflect/internal/Constants.scala index c328cc49cb..135d18d5ad 100644 --- a/src/compiler/scala/reflect/internal/Constants.scala +++ b/src/compiler/scala/reflect/internal/Constants.scala @@ -26,7 +26,7 @@ trait Constants extends api.Constants { final val DoubleTag = 9 final val StringTag = 10 final val NullTag = 11 - final val ClassTag = 12 + final val ClazzTag = 12 // For supporting java enumerations inside java annotations (see ClassfileParser) final val EnumTag = 13 @@ -43,7 +43,7 @@ trait Constants extends api.Constants { case x: Double => DoubleTag case x: String => StringTag case x: Char => CharTag - case x: Type => ClassTag + case x: Type => ClazzTag case x: Symbol => EnumTag case _ => throw new Error("bad constant value: " + value + " of class " + value.getClass) } @@ -70,7 +70,7 @@ trait Constants extends api.Constants { case DoubleTag => DoubleClass.tpe case StringTag => StringClass.tpe case NullTag => NullClass.tpe - case ClassTag => ClassType(value.asInstanceOf[Type]) + case ClazzTag => ClassType(value.asInstanceOf[Type]) case EnumTag => // given (in java): "class A { enum E { VAL1 } }" // - symbolValue: the symbol of the actual enumeration value (VAL1) @@ -201,7 +201,7 @@ trait Constants extends api.Constants { def stringValue: String = if (value == null) "null" - else if (tag == ClassTag) signature(typeValue) + else if (tag == ClazzTag) signature(typeValue) else value.toString() @switch def escapedChar(ch: Char): String = ch match { @@ -221,7 +221,7 @@ trait Constants extends api.Constants { tag match { case NullTag => "null" case StringTag => "\"" + escape(stringValue) + "\"" - case ClassTag => "classOf[" + signature(typeValue) + "]" + case ClazzTag => "classOf[" + signature(typeValue) + "]" case CharTag => "'" + escapedChar(charValue) + "'" case LongTag => longValue.toString() + "L" case _ => String.valueOf(value) diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 6ef6751720..72fca5da12 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -10,10 +10,29 @@ import annotation.{ switch } import scala.collection.{ mutable, immutable } import Flags._ import PartialFunction._ +import scala.reflect.{ mirror => rm } trait Definitions extends reflect.api.StandardDefinitions { self: SymbolTable => + // [Eugene] find a way to make these non-lazy + lazy val ByteTpe = definitions.ByteClass.asType + lazy val ShortTpe = definitions.ShortClass.asType + lazy val CharTpe = definitions.CharClass.asType + lazy val IntTpe = definitions.IntClass.asType + lazy val LongTpe = definitions.LongClass.asType + lazy val FloatTpe = definitions.FloatClass.asType + lazy val DoubleTpe = definitions.DoubleClass.asType + lazy val BooleanTpe = definitions.BooleanClass.asType + lazy val UnitTpe = definitions.UnitClass.asType + lazy val AnyTpe = definitions.AnyClass.asType + lazy val ObjectTpe = definitions.ObjectClass.asType + lazy val AnyValTpe = definitions.AnyValClass.asType + lazy val AnyRefTpe = definitions.AnyRefClass.asType + lazy val NothingTpe = definitions.NothingClass.asType + lazy val NullTpe = definitions.NullClass.asType + lazy val StringTpe = definitions.StringClass.asType + /** Since both the value parameter types and the result type may * require access to the type parameter symbols, we model polymorphic * creation as a function from those symbols to (formal types, result type). @@ -129,6 +148,7 @@ trait Definitions extends reflect.api.StandardDefinitions { DoubleClass ) def ScalaValueClassCompanions: List[Symbol] = ScalaValueClasses map (_.companionSymbol) + def ScalaPrimitiveValueClasses: List[Symbol] = ScalaValueClasses } object definitions extends AbsDefinitions with ValueClassDefinitions { @@ -446,19 +466,38 @@ trait Definitions extends reflect.api.StandardDefinitions { def methodCache_add = getMember(MethodCacheClass, nme.add_) // scala.reflect - lazy val ReflectApiUniverse = getRequiredClass("scala.reflect.api.Universe") - lazy val ReflectMacroContext = getRequiredClass("scala.reflect.macro.Context") - lazy val ReflectRuntimeMirror = getRequiredModule("scala.reflect.runtime.Mirror") - def freeValueMethod = getMember(ReflectRuntimeMirror, nme.freeValue) + lazy val ReflectPackageClass = getMember(ScalaPackageClass, nme.reflect) lazy val ReflectPackage = getPackageObject("scala.reflect") def Reflect_mirror = getMember(ReflectPackage, nme.mirror) - lazy val PartialManifestClass = getRequiredClass("scala.reflect.ClassManifest") - lazy val PartialManifestModule = getRequiredModule("scala.reflect.ClassManifest") - lazy val FullManifestClass = getRequiredClass("scala.reflect.Manifest") - lazy val FullManifestModule = getRequiredModule("scala.reflect.Manifest") - lazy val OptManifestClass = getRequiredClass("scala.reflect.OptManifest") - lazy val NoManifest = getRequiredModule("scala.reflect.NoManifest") + lazy val ExprClass = getMember(getRequiredClass("scala.reflect.api.Exprs"), tpnme.Expr) + def ExprTree = getMember(ExprClass, nme.tree) + def ExprTpe = getMember(ExprClass, nme.tpe) + def ExprEval = getMember(ExprClass, nme.eval) + def ExprValue = getMember(ExprClass, nme.value) + lazy val ExprModule = getMember(getRequiredClass("scala.reflect.api.Exprs"), nme.Expr) + + lazy val ClassTagClass = getRequiredClass("scala.reflect.ClassTag") + def ClassTagErasure = getMember(ClassTagClass, nme.erasure) + def ClassTagTpe = getMember(ClassTagClass, nme.tpe) + lazy val ClassTagModule = getRequiredModule("scala.reflect.ClassTag") + lazy val TypeTagsClass = getRequiredClass("scala.reflect.api.TypeTags") + lazy val TypeTagClass = getMember(TypeTagsClass, tpnme.TypeTag) + def TypeTagTpe = getMember(TypeTagClass, nme.tpe) + lazy val TypeTagModule = getMember(TypeTagsClass, nme.TypeTag) + lazy val ConcreteTypeTagClass = getMember(TypeTagsClass, tpnme.ConcreteTypeTag) + lazy val ConcreteTypeTagModule = getMember(TypeTagsClass, nme.ConcreteTypeTag) + + lazy val MacroContextClass = getRequiredClass("scala.reflect.makro.Context") + def MacroContextPrefix = getMember(MacroContextClass, nme.prefix) + def MacroContextPrefixType = getMember(MacroContextClass, tpnme.PrefixType) + def MacroContextMirror = getMember(MacroContextClass, nme.mirror) + def MacroContextReify = getMember(MacroContextClass, nme.reify) + lazy val MacroImplAnnotation = getRequiredClass("scala.reflect.makro.internal.macroImpl") + lazy val MacroInternalPackage = getPackageObject("scala.reflect.makro.internal") + def MacroInternal_materializeClassTag = getMember(MacroInternalPackage, nme.materializeClassTag) + def MacroInternal_materializeTypeTag = getMember(MacroInternalPackage, nme.materializeTypeTag) + def MacroInternal_materializeConcreteTypeTag = getMember(MacroInternalPackage, nme.materializeConcreteTypeTag) lazy val ScalaSignatureAnnotation = getRequiredClass("scala.reflect.ScalaSignature") lazy val ScalaLongSignatureAnnotation = getRequiredClass("scala.reflect.ScalaLongSignature") @@ -467,33 +506,15 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val OptionClass: Symbol = getRequiredClass("scala.Option") lazy val SomeClass: Symbol = getRequiredClass("scala.Some") lazy val NoneModule: Symbol = getRequiredModule("scala.None") + lazy val SomeModule: Symbol = getRequiredModule("scala.Some") - /** Note: don't use this manifest/type function for anything important, - * as it is incomplete. Would love to have things like existential types - * working, but very unfortunately the manifests just stuff the relevant - * information into the toString method. - */ - def manifestToType(m: OptManifest[_]): Type = m match { - case m: ClassManifest[_] => - val sym = manifestToSymbol(m) - val args = m.typeArguments + // [Eugene] how do I make this work without casts? + // private lazy val importerFromRm = self.mkImporter(rm) + private lazy val importerFromRm = self.mkImporter(rm).asInstanceOf[self.Importer { val from: rm.type }] - if ((sym eq NoSymbol) || args.isEmpty) sym.tpe - else appliedType(sym, args map manifestToType: _*) - case _ => - NoType - } + def manifestToType(m: Manifest[_]): Type = importerFromRm.importType(m.tpe) - def manifestToSymbol(m: ClassManifest[_]): Symbol = m match { - case x: scala.reflect.AnyValManifest[_] => - getMember(ScalaPackageClass, newTypeName("" + x)) - case _ => - val name = m.erasure.getName - if (name endsWith nme.MODULE_SUFFIX_STRING) - getModuleIfDefined(name stripSuffix nme.MODULE_SUFFIX_STRING) - else - getClassIfDefined(name) - } + def manifestToSymbol(m: Manifest[_]): Symbol = importerFromRm.importSymbol(m.tpe.typeSymbol) // The given symbol represents either String.+ or StringAdd.+ def isStringAddition(sym: Symbol) = sym == String_+ || sym == StringAdd_+ @@ -527,11 +548,6 @@ trait Definitions extends reflect.api.StandardDefinitions { } val MaxTupleArity, MaxProductArity, MaxFunctionArity = 22 - /** The maximal dimensions of a generic array creation. - * I.e. new Array[Array[Array[Array[Array[T]]]]] creates a 5 times - * nested array. More is not allowed. - */ - val MaxArrayDims = 5 lazy val ProductClass = { val arr = mkArityArray("Product", MaxProductArity) ; arr(0) = UnitClass ; arr } lazy val TupleClass = mkArityArray("Tuple", MaxTupleArity) lazy val FunctionClass = mkArityArray("Function", MaxFunctionArity, 0) @@ -905,7 +921,21 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val GetterTargetClass = getMetaAnnotation("getter") lazy val ParamTargetClass = getMetaAnnotation("param") lazy val SetterTargetClass = getMetaAnnotation("setter") - // TODO: module, moduleClass? package, packageObject? + lazy val ClassTargetClass = getMetaAnnotation("companionClass") + lazy val ObjectTargetClass = getMetaAnnotation("companionObject") + lazy val MethodTargetClass = getMetaAnnotation("companionMethod") // TODO: module, moduleClass? package, packageObject? + lazy val LanguageFeatureAnnot = getMetaAnnotation("languageFeature") + + // Language features + lazy val languageFeatureModule = getRequiredModule("scala.languageFeature") + lazy val experimentalModule = getMember(languageFeatureModule, newTermName("experimental")) + lazy val MacrosFeature = getLanguageFeature("macros", experimentalModule) + lazy val DynamicsFeature = getLanguageFeature("dynamics") + lazy val PostfixOpsFeature = getLanguageFeature("postfixOps") + lazy val ReflectiveCallsFeature = getLanguageFeature("reflectiveCalls") + lazy val ImplicitConversionsFeature = getLanguageFeature("implicitConversions") + lazy val HigherKindsFeature = getLanguageFeature("higherKinds") + lazy val ExistentialsFeature = getLanguageFeature("existentials") private def getMetaAnnotation(name: String) = getRequiredClass("scala.annotation.meta." + name) def isMetaAnnotation(sym: Symbol): Boolean = metaAnnotations(sym) || ( @@ -957,6 +987,9 @@ trait Definitions extends reflect.api.StandardDefinitions { try getModule(fullname.toTermName) catch { case _: MissingRequirementError => NoSymbol } + def getLanguageFeature(name: String, owner: Symbol = languageFeatureModule) = + getMember(owner, newTypeName(name)) + def termMember(owner: Symbol, name: String): Symbol = owner.info.member(newTermName(name)) def typeMember(owner: Symbol, name: String): Symbol = owner.info.member(newTypeName(name)) @@ -976,7 +1009,13 @@ trait Definitions extends reflect.api.StandardDefinitions { def getMember(owner: Symbol, name: Name): Symbol = { getMemberIfDefined(owner, name) orElse { - throw new FatalError(owner + " does not have a member " + name) + if (phase.flatClasses && name.isTypeName && !owner.isPackageObjectOrClass) { + val pkg = owner.owner + val flatname = nme.flattenedName(owner.name, name) + getMember(pkg, flatname) + } else { + throw new FatalError(owner + " does not have a member " + name) + } } } def getMemberIfDefined(owner: Symbol, name: Name): Symbol = @@ -993,7 +1032,7 @@ trait Definitions extends reflect.api.StandardDefinitions { } def getDeclIfDefined(owner: Symbol, name: Name): Symbol = owner.info.nonPrivateDecl(name) - + def packageExists(packageName: String): Boolean = getModuleIfDefined(packageName).isPackage diff --git a/src/compiler/scala/reflect/internal/FreeVars.scala b/src/compiler/scala/reflect/internal/FreeVars.scala new file mode 100644 index 0000000000..8b6e8b61f3 --- /dev/null +++ b/src/compiler/scala/reflect/internal/FreeVars.scala @@ -0,0 +1,60 @@ +package scala.reflect +package internal + +trait FreeVars extends api.FreeVars { + self: SymbolTable => + + object FreeTerm extends FreeTermExtractor { + def unapply(freeTerm: FreeTerm): Option[(TermName, Type, Any, String)] = + Some(freeTerm.name, freeTerm.info, freeTerm.value, freeTerm.origin) + } + + object FreeType extends FreeTypeExtractor { + def unapply(freeType: FreeType): Option[(TypeName, Type, String)] = + Some(freeType.name, freeType.info, freeType.origin) + } + + // [Eugene] am I doing this right? + def freeTerms(tree: Tree): List[FreeTerm] = { + def isFreeTermSym(sym: Symbol) = sym != null && sym.isFreeTerm + def isFreeTermTpe(t: Type) = t != null && isFreeTermSym(t.termSymbol) + + val buf = collection.mutable.Set[Symbol]() + tree foreach (sub => { + if (sub.tpe != null) buf ++= (sub.tpe collect { case tpe if isFreeTermTpe(tpe) => tpe.typeSymbol }) + if (sub.symbol != null && isFreeTermSym(sub.symbol)) buf += sub.symbol + }) + + buf.toList.collect{ case fty: FreeTerm => fty } + } + + // [Eugene] am I doing this right? + def freeTypes(tree: Tree): List[FreeType] = { + def isFreeTypeSym(sym: Symbol) = sym != null && sym.isFreeType + def isFreeTypeTpe(t: Type) = t != null && isFreeTypeSym(t.typeSymbol) + + val buf = collection.mutable.Set[Symbol]() + tree foreach (sub => { + if (sub.tpe != null) buf ++= (sub.tpe collect { case tpe if isFreeTypeTpe(tpe) => tpe.typeSymbol }) + if (sub.symbol != null && isFreeTypeSym(sub.symbol)) buf += sub.symbol + }) + + buf.toList.collect{ case fty: FreeType => fty } + } + + // todo. also update tpe's of dependent free vars + // e.g. if we substitute free$C, then free$C$this should have its info updated + // todo. should also transform typetags of types dependent on that free type? + // [Eugene] how do I check that the substitution is legal w.r.t fty.info? + def substituteFreeTypes(tree0: Tree, subs: Map[FreeType, Type]): Tree = { + val tree = tree0.duplicate + new TreeTypeSubstituter(subs.keys.toList, subs.values.toList).traverse(tree) + tree + } + + // [Eugene] how do I check that the substitution is legal w.r.t fty.info? + def substituteFreeTypes(tpe0: Type, subs: Map[FreeType, Type]): Type = { + val tpe = tpe0 // [Eugene] tpe0.duplicate? + tpe.subst(subs.keys.toList, subs.values.toList) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala index c9336e8cf1..ab5e19fca9 100644 --- a/src/compiler/scala/reflect/internal/Importers.scala +++ b/src/compiler/scala/reflect/internal/Importers.scala @@ -4,7 +4,24 @@ import scala.collection.mutable.WeakHashMap trait Importers { self: SymbolTable => - abstract class Importer { + // [Eugene] possible to make this less cast-heavy? + def mkImporter(from0: api.Universe): Importer { val from: from0.type } = ( + if (self eq from0) { + new Importer { + val from = from0 + val reverse = this.asInstanceOf[from.Importer{ val from: self.type }] + def importSymbol(sym: from.Symbol) = sym.asInstanceOf[self.Symbol] + def importType(tpe: from.Type) = tpe.asInstanceOf[self.Type] + def importTree(tree: from.Tree) = tree.asInstanceOf[self.Tree] + } + } else { + // todo. fix this loophole + assert(from0.isInstanceOf[SymbolTable], "`from` should be an instance of scala.reflect.internal.SymbolTable") + new StandardImporter { val from = from0.asInstanceOf[SymbolTable] } + } + ).asInstanceOf[Importer { val from: from0.type }] + + abstract class StandardImporter extends Importer { val from: SymbolTable @@ -24,13 +41,15 @@ trait Importers { self: SymbolTable => } } - object reverse extends from.Importer { + object reverse extends from.StandardImporter { val from: self.type = self - for ((fromsym, mysym) <- Importer.this.symMap) symMap += ((mysym, fromsym)) - for ((fromtpe, mytpe) <- Importer.this.tpeMap) tpeMap += ((mytpe, fromtpe)) + for ((fromsym, mysym) <- StandardImporter.this.symMap) symMap += ((mysym, fromsym)) + for ((fromtpe, mytpe) <- StandardImporter.this.tpeMap) tpeMap += ((mytpe, fromtpe)) } - def importPosition(pos: from.Position): Position = NoPosition + // todo. careful import of positions + def importPosition(pos: from.Position): Position = + pos.asInstanceOf[Position] def importSymbol(sym0: from.Symbol): Symbol = { def doImport(sym: from.Symbol): Symbol = { @@ -51,8 +70,10 @@ trait Importers { self: SymbolTable => linkReferenced(myowner.newMethod(myname, mypos, myflags), x, importSymbol) case x: from.ModuleSymbol => 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.FreeTerm => + newFreeTerm(importName(x.name).toTermName, importType(x.info), x.value, x.origin, myflags) + case x: from.FreeType => + newFreeType(importName(x.name).toTypeName, importType(x.info), x.value, x.origin, myflags) case x: from.TermSymbol => linkReferenced(myowner.newValue(myname, mypos, myflags), x, importSymbol) case x: from.TypeSkolem => @@ -374,6 +395,8 @@ trait Importers { self: SymbolTable => case _ => new Ident(importName(name)) } + case from.ReferenceToBoxed(ident) => + new ReferenceToBoxed(importTree(ident) match { case ident: Ident => ident }) case from.Literal(constant @ from.Constant(_)) => new Literal(importConstant(constant)) case from.TypeTree() => @@ -425,7 +448,7 @@ trait Importers { self: SymbolTable => def importIdent(tree: from.Ident): Ident = importTree(tree).asInstanceOf[Ident] def importCaseDef(tree: from.CaseDef): CaseDef = importTree(tree).asInstanceOf[CaseDef] def importConstant(constant: from.Constant): Constant = new Constant(constant.tag match { - case ClassTag => importType(constant.value.asInstanceOf[from.Type]) + case ClazzTag => importType(constant.value.asInstanceOf[from.Type]) case EnumTag => importSymbol(constant.value.asInstanceOf[from.Symbol]) case _ => constant.value }) diff --git a/src/compiler/scala/reflect/internal/NameManglers.scala b/src/compiler/scala/reflect/internal/NameManglers.scala index 05578a2042..ac22017569 100644 --- a/src/compiler/scala/reflect/internal/NameManglers.scala +++ b/src/compiler/scala/reflect/internal/NameManglers.scala @@ -128,12 +128,7 @@ trait NameManglers { else name ) - def macroMethodName(name: Name) = { - val base = if (name.isTypeName) nme.TYPEkw else nme.DEFkw - base append nme.MACRO append name - } - - /** Return the original name and the types on which this name + /** Return the original name and the types on which this name * is specialized. For example, * {{{ * splitSpecializedName("foo$mIcD$sp") == ('foo', "I", "D") diff --git a/src/compiler/scala/reflect/internal/Names.scala b/src/compiler/scala/reflect/internal/Names.scala index 5f38374f20..d2b55d9d39 100644 --- a/src/compiler/scala/reflect/internal/Names.scala +++ b/src/compiler/scala/reflect/internal/Names.scala @@ -8,6 +8,7 @@ package internal import scala.io.Codec import java.security.MessageDigest +import language.implicitConversions /** The class Names ... * diff --git a/src/compiler/scala/reflect/internal/Positions.scala b/src/compiler/scala/reflect/internal/Positions.scala index 78de8d0ff2..5ec2659098 100644 --- a/src/compiler/scala/reflect/internal/Positions.scala +++ b/src/compiler/scala/reflect/internal/Positions.scala @@ -3,9 +3,8 @@ package internal trait Positions extends api.Positions { self: SymbolTable => - def focusPos(pos: Position): Position - def isRangePos(pos: Position): Boolean - def showPos(pos: Position): String + type Position = scala.tools.nsc.util.Position + val NoPosition = scala.tools.nsc.util.NoPosition /** A position that wraps a set of trees. * The point of the wrapping position is the point of the default position. @@ -27,4 +26,37 @@ trait Positions extends api.Positions { self: SymbolTable => * to some of the nodes in `tree`. */ def ensureNonOverlapping(tree: Tree, others: List[Tree]) {} + + trait PosAssigner extends Traverser { + var pos: Position + } + protected[this] lazy val posAssigner: PosAssigner = new DefaultPosAssigner + + protected class DefaultPosAssigner extends PosAssigner { + var pos: Position = _ + override def traverse(t: Tree) { + if (t eq EmptyTree) () + else if (t.pos == NoPosition) { + t.setPos(pos) + 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. + } + } + } + + def atPos[T <: Tree](pos: Position)(tree: T): T = { + posAssigner.pos = pos + posAssigner.traverse(tree) + tree + } }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/Reporters.scala b/src/compiler/scala/reflect/internal/Reporters.scala new file mode 100644 index 0000000000..20d4a1d026 --- /dev/null +++ b/src/compiler/scala/reflect/internal/Reporters.scala @@ -0,0 +1,74 @@ +package scala.reflect +package internal + +trait Reporters { self: SymbolTable => + + import self.{Reporter => ApiReporter} + import scala.tools.nsc.reporters._ + import scala.tools.nsc.reporters.{Reporter => NscReporter} + import scala.tools.nsc.Settings + + def mkConsoleReporter(minSeverity: Int = 1): ApiReporter = { + val settings = new Settings() + if (minSeverity <= 0) settings.verbose.value = true + if (minSeverity > 1) settings.nowarn.value = true + wrapNscReporter(new ConsoleReporter(settings)) + } + + abstract class ApiToNscReporterProxy(val apiReporter: ApiReporter) extends AbstractReporter { + import apiReporter.{Severity => ApiSeverity} + val API_INFO = apiReporter.INFO + val API_WARNING = apiReporter.WARNING + val API_ERROR = apiReporter.ERROR + + type NscSeverity = Severity + val NSC_INFO = INFO + val NSC_WARNING = WARNING + val NSC_ERROR = ERROR + + def display(pos: Position, msg: String, nscSeverity: NscSeverity): Unit = + apiReporter.log(pos, msg, nscSeverity match { + case NSC_INFO => API_INFO + case NSC_WARNING => API_WARNING + case NSC_ERROR => API_ERROR + }) + + def displayPrompt(): Unit = + apiReporter.interactive() + } + + def wrapApiReporter(apiReporter: ApiReporter): NscReporter = new ApiToNscReporterProxy(apiReporter) { + val settings = new Settings() + settings.verbose.value = true + settings.nowarn.value = false + } + + class NscToApiReporterProxy(val nscReporter: NscReporter) extends ApiReporter { + val API_INFO = INFO + val API_WARNING = WARNING + val API_ERROR = ERROR + + def display(info: Info): Unit = info.severity match { + case API_INFO => nscReporter.info(info.pos, info.msg, false) + case API_WARNING => nscReporter.warning(info.pos, info.msg) + case API_ERROR => nscReporter.error(info.pos, info.msg) + } + + def interactive(): Unit = nscReporter match { + case nscReporter: AbstractReporter => nscReporter.displayPrompt() + case _ => // do nothing + } + + override def flush(): Unit = { + super.flush() + nscReporter.flush() + } + + override def reset(): Unit = { + super.reset() + nscReporter.reset() + } + } + + def wrapNscReporter(nscReporter: NscReporter): ApiReporter = new NscToApiReporterProxy(nscReporter) +} diff --git a/src/compiler/scala/reflect/internal/Required.scala b/src/compiler/scala/reflect/internal/Required.scala index 1bf1a2e97e..ba6d65a306 100644 --- a/src/compiler/scala/reflect/internal/Required.scala +++ b/src/compiler/scala/reflect/internal/Required.scala @@ -12,8 +12,6 @@ trait Required { self: SymbolTable => def picklerPhase: Phase - val gen: TreeGen { val global: Required.this.type } - def settings: MutableSettings def forInteractive: Boolean diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index 0cd3616ba9..ac2cf178bf 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -9,12 +9,13 @@ package internal import scala.collection.immutable import NameTransformer.MODULE_SUFFIX_STRING import annotation.switch +import language.implicitConversions trait StdNames extends NameManglers { self: SymbolTable => def encode(str: String): TermName = newTermNameCached(NameTransformer.encode(str)) - implicit def lowerTermNames(n: TermName): String = "" + n + implicit def lowerTermNames(n: TermName): String = n.toString // implicit def stringToTermName(s: String): TermName = newTermName(s) @@ -44,6 +45,7 @@ trait StdNames extends NameManglers { self: SymbolTable => final val IMPLICITkw: TermName = kw("implicit") final val IMPORTkw: TermName = kw("import") final val LAZYkw: TermName = kw("lazy") + final val MACROkw: TermName = kw("macro") final val MATCHkw: TermName = kw("match") final val NEWkw: TermName = kw("new") final val NULLkw: TermName = kw("null") @@ -55,6 +57,7 @@ trait StdNames extends NameManglers { self: SymbolTable => final val RETURNkw: TermName = kw("return") final val SEALEDkw: TermName = kw("sealed") final val SUPERkw: TermName = kw("super") + final val THENkw: TermName = kw("then") final val THISkw: TermName = kw("this") final val THROWkw: TermName = kw("throw") final val TRAITkw: TermName = kw("trait") @@ -123,6 +126,9 @@ trait StdNames extends NameManglers { self: SymbolTable => final val List: NameType = "List" final val Seq: NameType = "Seq" final val Symbol: NameType = "Symbol" + final val ClassTag: NameType = "ClassTag" + final val TypeTag : NameType = "TypeTag" + final val ConcreteTypeTag: NameType = "ConcreteTypeTag" // fictions we use as both types and terms final val ERROR: NameType = "<error>" @@ -140,10 +146,12 @@ trait StdNames extends NameManglers { self: SymbolTable => final val Any: NameType = "Any" final val AnyVal: NameType = "AnyVal" + final val Expr: NameType = "Expr" final val Nothing: NameType = "Nothing" final val Null: NameType = "Null" final val Object: NameType = "Object" final val PartialFunction: NameType = "PartialFunction" + final val PrefixType: NameType = "PrefixType" final val Product: NameType = "Product" final val Serializable: NameType = "Serializable" final val Singleton: NameType = "Singleton" @@ -185,32 +193,34 @@ trait StdNames extends NameManglers { self: SymbolTable => trait TermNames extends Keywords with CommonNames { // Compiler internal names - val EXPAND_SEPARATOR_STRING = "$$" - - val ANYNAME: NameType = "<anyname>" - val CONSTRUCTOR: NameType = "<init>" - val FAKE_LOCAL_THIS: NameType = "this$" - val INITIALIZER: NameType = CONSTRUCTOR // Is this buying us something? - val LAZY_LOCAL: NameType = "$lzy" - val LOCAL_SUFFIX_STRING = " " - val MACRO: NameType = "macro$" - val MIRROR_PREFIX: NameType = "$mr." - val MIRROR_SHORT: NameType = "$mr" - val MIXIN_CONSTRUCTOR: NameType = "$init$" - val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" - val OUTER: NameType = "$outer" - val OUTER_LOCAL: NameType = OUTER + LOCAL_SUFFIX_STRING // "$outer ", note the space - val OUTER_SYNTH: NameType = "<outer>" // emitted by virtual pattern matcher, replaced by outer accessor in explicitouter - val SELECTOR_DUMMY: NameType = "<unapply-selector>" - val SELF: NameType = "$this" - val SPECIALIZED_INSTANCE: NameType = "specInstance$" - val STAR: NameType = "*" - val THIS: NameType = "_$this" - - final val Nil: NameType = "Nil" - final val Predef: NameType = "Predef" - final val ScalaRunTime: NameType = "ScalaRunTime" - final val Some: NameType = "Some" + val EXPAND_SEPARATOR_STRING = "$$" + + val ANYNAME: NameType = "<anyname>" + val CONSTRUCTOR: NameType = "<init>" + val FAKE_LOCAL_THIS: NameType = "this$" + val INITIALIZER: NameType = CONSTRUCTOR // Is this buying us something? + val LAZY_LOCAL: NameType = "$lzy" + val LOCAL_SUFFIX_STRING = " " + val MIRROR_PREFIX: NameType = "$mr." + val MIRROR_SHORT: NameType = "$mr" + val MIRROR_FREE_PREFIX: NameType = "free$" + val MIRROR_FREE_THIS_SUFFIX: NameType = "$this" + val MIRROR_FREE_VALUE_SUFFIX: NameType = "$value" + val MIXIN_CONSTRUCTOR: NameType = "$init$" + val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" + val OUTER: NameType = "$outer" + val OUTER_LOCAL: NameType = OUTER + LOCAL_SUFFIX_STRING // "$outer ", note the space + val OUTER_SYNTH: NameType = "<outer>" // emitted by virtual pattern matcher, replaced by outer accessor in explicitouter + val SELECTOR_DUMMY: NameType = "<unapply-selector>" + val SELF: NameType = "$this" + val SPECIALIZED_INSTANCE: NameType = "specInstance$" + val STAR: NameType = "*" + val THIS: NameType = "_$this" + + final val Nil: NameType = "Nil" + final val Predef: NameType = "Predef" + final val ScalaRunTime: NameType = "ScalaRunTime" + final val Some: NameType = "Some" val _1 : NameType = "_1" val _2 : NameType = "_2" @@ -275,12 +285,38 @@ trait StdNames extends NameManglers { self: SymbolTable => val genericWrapArray: NameType = "genericWrapArray" // Compiler utilized names - // val productElementName: NameType = "productElementName" + + val AnnotatedType: NameType = "AnnotatedType" + val AnnotationInfo: NameType = "AnnotationInfo" + val Any: NameType = "Any" + val AnyVal: NameType = "AnyVal" + val Apply: NameType = "Apply" + val ArrayAnnotArg: NameType = "ArrayAnnotArg" + val ConstantType: NameType = "ConstantType" + val EmptyPackage: NameType = "EmptyPackage" + val EmptyPackageClass: NameType = "EmptyPackageClass" + val Expr: NameType = "Expr" val Ident: NameType = "Ident" + val Import: NameType = "Import" + val Literal: NameType = "Literal" + val LiteralAnnotArg: NameType = "LiteralAnnotArg" + val NestedAnnotArg: NameType = "NestedAnnotArg" + val NoPrefix: NameType = "NoPrefix" + val NoSymbol: NameType = "NoSymbol" + val Nothing: NameType = "Nothing" + val NoType: NameType = "NoType" + val Null: NameType = "Null" + val Object: NameType = "Object" + val RootPackage: NameType = "RootPackage" + val RootClass: NameType = "RootClass" + val Select: NameType = "Select" val StringContext: NameType = "StringContext" val This: NameType = "This" val Tree : NameType = "Tree" + val Tuple2: NameType = "Tuple2" val TYPE_ : NameType = "TYPE" + val TypeApply: NameType = "TypeApply" + val TypeRef: NameType = "TypeRef" val TypeTree: NameType = "TypeTree" val UNIT : NameType = "UNIT" val add_ : NameType = "add" @@ -289,6 +325,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val append: NameType = "append" val apply: NameType = "apply" val applyDynamic: NameType = "applyDynamic" + val applyDynamicNamed: NameType = "applyDynamicNamed" val applyOrElse: NameType = "applyOrElse" val args : NameType = "args" val argv : NameType = "argv" @@ -311,6 +348,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val clone_ : NameType = if (forMSIL) "MemberwiseClone" else "clone" // sn.OClone causes checkinit failure val conforms: NameType = "conforms" val copy: NameType = "copy" + val definitions: NameType = "definitions" val delayedInit: NameType = "delayedInit" val delayedInitArg: NameType = "delayedInit$body" val drop: NameType = "drop" @@ -322,7 +360,9 @@ trait StdNames extends NameManglers { self: SymbolTable => val equalsNumNum : NameType = "equalsNumNum" val equalsNumObject : NameType = "equalsNumObject" val equals_ : NameType = if (forMSIL) "Equals" else "equals" + val erasure: NameType = "erasure" val error: NameType = "error" + val eval: NameType = "eval" val ex: NameType = "ex" val false_ : NameType = "false" val filter: NameType = "filter" @@ -330,7 +370,6 @@ trait StdNames extends NameManglers { self: SymbolTable => val find_ : NameType = "find" val flatMap: NameType = "flatMap" val foreach: NameType = "foreach" - val freeValue : NameType = "freeValue" val genericArrayOps: NameType = "genericArrayOps" val get: NameType = "get" val getOrElse: NameType = "getOrElse" @@ -339,6 +378,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val hash_ : NameType = "hash" val head: NameType = "head" val identity: NameType = "identity" + val info: NameType = "info" val inlinedEquals: NameType = "inlinedEquals" val isArray: NameType = "isArray" val isDefinedAt: NameType = "isDefinedAt" @@ -350,36 +390,55 @@ trait StdNames extends NameManglers { self: SymbolTable => 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 macroContext : NameType = "c" val main: NameType = "main" + val manifest: NameType = "manifest" val map: NameType = "map" + val materializeClassTag: NameType = "materializeClassTag" + val materializeTypeTag: NameType = "materializeTypeTag" + val materializeConcreteTypeTag: NameType = "materializeConcreteTypeTag" val mirror : NameType = "mirror" + val moduleClass : NameType = "moduleClass" + val name: NameType = "name" val ne: NameType = "ne" val newArray: NameType = "newArray" + val newFreeTerm: NameType = "newFreeTerm" + val newFreeType: NameType = "newFreeType" + val newNestedSymbol: NameType = "newNestedSymbol" val newScopeWith: NameType = "newScopeWith" + val nmeNewTermName: NameType = "newTermName" + val nmeNewTypeName: NameType = "newTypeName" val next: NameType = "next" val notifyAll_ : NameType = "notifyAll" val notify_ : NameType = "notify" val null_ : NameType = "null" val ofDim: NameType = "ofDim" + val origin: NameType = "origin" + val prefix : NameType = "prefix" val productArity: NameType = "productArity" val productElement: NameType = "productElement" val productIterator: NameType = "productIterator" val productPrefix: NameType = "productPrefix" val readResolve: NameType = "readResolve" + val reflect : NameType = "reflect" + val reify : NameType = "reify" val runOrElse: NameType = "runOrElse" val runtime: NameType = "runtime" val sameElements: NameType = "sameElements" val scala_ : NameType = "scala" + val selectDynamic: NameType = "selectDynamic" + val selectOverloadedMethod: NameType = "selectOverloadedMethod" + val selectTerm: NameType = "selectTerm" + val selectType: NameType = "selectType" val self: NameType = "self" val setAccessible: NameType = "setAccessible" val setAnnotations: NameType = "setAnnotations" val setSymbol: NameType = "setSymbol" val setType: NameType = "setType" val setTypeSignature: NameType = "setTypeSignature" + val staticClass : NameType = "staticClass" + val staticModule : NameType = "staticModule" val synchronized_ : NameType = "synchronized" val tail: NameType = "tail" val thisModuleType: NameType = "thisModuleType" @@ -390,12 +449,15 @@ trait StdNames extends NameManglers { self: SymbolTable => val toObjectArray : NameType = "toObjectArray" val toSeq: NameType = "toSeq" val toString_ : NameType = if (forMSIL) "ToString" else "toString" + val tpe : NameType = "tpe" + val tree : NameType = "tree" val true_ : NameType = "true" val typedProductIterator: NameType = "typedProductIterator" val unapply: NameType = "unapply" val unapplySeq: NameType = "unapplySeq" val unbox: NameType = "unbox" val update: NameType = "update" + val updateDynamic: NameType = "updateDynamic" val value: NameType = "value" val valueOf : NameType = "valueOf" val values : NameType = "values" diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index 83a24dce68..0268339ed0 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -16,6 +16,7 @@ abstract class SymbolTable extends api.Universe with SymbolCreations with Symbols with SymbolFlags + with FreeVars with Types with Kinds with ExistentialsAndSkolems @@ -34,6 +35,9 @@ abstract class SymbolTable extends api.Universe with TypeDebugging with Importers with Required + with TreeBuildUtil + with Reporters + with CapturedVariables { def rootLoader: LazyType def log(msg: => AnyRef): Unit @@ -48,6 +52,13 @@ abstract class SymbolTable extends api.Universe /** Overridden when we know more about what was happening during a failure. */ def supplementErrorMessage(msg: String): String = msg + + private[scala] def printCaller[T](msg: String)(result: T) = { + Console.err.println(msg + ": " + result) + Console.err.println("Called from:") + (new Throwable).getStackTrace.drop(2).take(15).foreach(Console.err.println) + result + } private[scala] def printResult[T](msg: String)(result: T) = { Console.err.println(msg + ": " + result) @@ -158,7 +169,7 @@ abstract class SymbolTable extends api.Universe try op finally popPhase(saved) } - + /** Since when it is to be "at" a phase is inherently ambiguous, * a couple unambiguously named methods. @@ -256,6 +267,8 @@ abstract class SymbolTable extends api.Universe import java.lang.ref.WeakReference import scala.runtime.ScalaRunTime.stringOf + import language.reflectiveCalls + // We can allow ourselves a structural type, these methods // amount to a few calls per run at most. This does suggest // a "Clearable" trait may be useful. diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 04bdb0f4ad..3bb57cea04 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -45,10 +45,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => m } - /** Create a new free variable. Its owner is NoSymbol. + /** Create a new free term. Its owner is NoSymbol. */ - def newFreeVar(name: TermName, tpe: Type, value: Any, newFlags: Long = 0L): FreeVar = - new FreeVar(name, value) initFlags newFlags setInfo tpe + def newFreeTerm(name: TermName, info: Type, value: => Any, origin: String, newFlags: Long = 0L): FreeTerm = + new FreeTerm(name, value, origin) initFlags newFlags setInfo info + + /** Create a new free type. Its owner is NoSymbol. + */ + def newFreeType(name: TypeName, info: Type, value: => Any, origin: String, newFlags: Long = 0L): FreeType = + new FreeType(name, value, origin) initFlags newFlags setInfo info /** The original owner of a class. Used by the backend to generate * EnclosingMethod attributes. @@ -58,6 +63,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => abstract class AbsSymbolImpl extends AbsSymbol { this: Symbol => + def kind: String = kindString + def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { case n: TermName => newTermSymbol(n, pos, newFlags) case n: TypeName => if (isClass) newClassSymbol(n, pos, newFlags) else newNonClassSymbol(n, pos, newFlags) @@ -324,7 +331,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def newSyntheticValueParamss(argtypess: List[List[Type]]): List[List[Symbol]] = { var cnt = 0 def freshName() = { cnt += 1; nme.syntheticParamName(cnt) } - mmap(argtypess)(tp => newValueParameter(freshName(), focusPos(owner.pos), SYNTHETIC) setInfo tp) + mmap(argtypess)(tp => newValueParameter(freshName(), owner.pos.focus, SYNTHETIC) setInfo tp) } def newSyntheticTypeParam(): Symbol = newSyntheticTypeParam("T0", 0L) @@ -543,6 +550,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isTypeParameter = false def isTypeParameterOrSkolem = false def isTypeSkolem = false + def isTypeMacro = false + def isFreeType = false /** Qualities of Terms, always false for TypeSymbols. */ @@ -563,13 +572,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isValueParameter = false def isVariable = false override def hasDefault = false + def isTermMacro = false + def isFreeTerm = false /** Qualities of MethodSymbols, always false for TypeSymbols * and other TermSymbols. */ def isCaseAccessorMethod = false def isLiftedMethod = false - def isMacro = false def isMethod = false def isSourceMethod = false def isVarargsMethod = false @@ -613,11 +623,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => @inline final override def hasFlag(mask: Long): Boolean = (flags & mask) != 0 /** Does symbol have ALL the flags in `mask` set? */ @inline final override def hasAllFlags(mask: Long): Boolean = (flags & mask) == mask - + override def setFlag(mask: Long): this.type = { _rawflags |= mask ; this } override def resetFlag(mask: Long): this.type = { _rawflags &= ~mask ; this } override def resetFlags() { rawflags &= (TopLevelCreationFlags | alwaysHasFlags) } - + /** Default implementation calls the generic string function, which * will print overloaded flags as <flag1/flag2/flag3>. Subclasses * of Symbol refine. @@ -632,7 +642,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => _rawflags = mask this } - + final def flags: Long = { val fs = _rawflags & phase.flagMask (fs | ((fs & LateFlags) >>> LateShift)) & ~(fs >>> AntiShift) @@ -780,7 +790,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) final def isModuleVar = hasFlag(MODULEVAR) - /** Is this symbol static (i.e. with no outer instance)? */ + /** Is this symbol static (i.e. with no outer instance)? + * Q: When exactly is a sym marked as STATIC? + * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep. + * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 + */ def isStatic = (this hasFlag STATIC) || owner.isStaticOwner /** Is this symbol a static constructor? */ @@ -836,6 +850,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isStructuralRefinement: Boolean = (isClass || isType || isModule) && info.normalize/*.underlying*/.isStructuralRefinement + /** Is this a term symbol only defined in a refinement (so that it needs + * to be accessed by reflection)? + */ + def isOnlyRefinementMember: Boolean = + isTerm && // type members are not affected + owner.isRefinementClass && // owner must be a refinement class + (owner.info decl name) == this && // symbol must be explicitly declared in the refinement (not synthesized from glb) + allOverriddenSymbols.isEmpty && // symbol must not override a symbol in a base class + !isConstant // symbol must not be a constant. Question: Can we exclude @inline methods as well? + final def isStructuralRefinementMember = owner.isStructuralRefinement && isPossibleInRefinement && isPublic final def isPossibleInRefinement = !isConstructor && !isOverridingSymbol @@ -865,6 +889,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isInitialized: Boolean = validTo != NoPeriod + // [Eugene] is this correct? + /** Determines whether this symbol can be loaded by subsequent reflective compilation */ + final def isLocatable: Boolean = { + if (this == NoSymbol) return false + if (isRoot || isRootPackage) return true + + if (!owner.isLocatable) return false + if (owner.isTerm) return false + + if (isType && isNonClassType) return false + return true + } + + // [Eugene] is it a good idea to add ``dealias'' to Symbol? + /** Expands type aliases */ + def dealias: Symbol = this + /** The variance of this symbol as an integer */ final def variance: Int = if (isCovariant) 1 @@ -1292,7 +1333,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * which immediately follows any of parser, namer, typer, or erasure. * In effect that means this will return one of: * - * - packageobjects (follows namer) + * - packageobjects (follows namer) * - superaccessors (follows typer) * - lazyvals (follows erasure) * - null @@ -1720,7 +1761,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => } else owner.enclosingTopLevelClass /** Is this symbol defined in the same scope and compilation unit as `that` symbol? */ - def isCoDefinedWith(that: Symbol) = ( + def isCoDefinedWith(that: Symbol) = { + import language.reflectiveCalls (this.rawInfo ne NoType) && (this.effectiveOwner == that.effectiveOwner) && { !this.effectiveOwner.isPackageClass || @@ -1739,7 +1781,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => false } } - ) + } /** The internal representation of classes and objects: * @@ -1843,7 +1885,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => site.nonPrivateMemberAdmitting(name, admit).filter(sym => !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) - /** The symbol overridden by this symbol in given class `ofclazz`. + /** The symbol, in class `ofclazz`, that is overridden by this symbol. * * @param ofclazz is a base class of this symbol's owner. */ @@ -1946,7 +1988,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Remove private modifier from symbol `sym`s definition. If `sym` is a * is not a constructor nor a static module rename it by expanding its name to avoid name clashes - * @param base the fully qualified name of this class will be appended if name expansion is needed + * @param base the fully qualified name of this class will be appended if name expansion is needed */ final def makeNotPrivate(base: Symbol) { if (this.isPrivate) { @@ -2032,8 +2074,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => private case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) private def symbolKind: SymbolKind = { - val kind = - if (isInstanceOf[FreeVar]) ("free variable", "free variable", "FV") + var kind = + if (isTermMacro) ("macro method", "macro method", "MAC") + else if (isInstanceOf[FreeTerm]) ("free term", "free term", "FTE") + else if (isInstanceOf[FreeType]) ("free type", "free type", "FTY") else if (isPackage) ("package", "package", "PK") else if (isPackageClass) ("package class", "package", "PKC") else if (isPackageObject) ("package object", "package", "PKO") @@ -2054,6 +2098,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isSourceMethod) ("method", "method", "METH") else if (isTerm) ("value", "value", "VAL") else ("", "", "???") + if (isSkolem) kind = (kind._1, kind._2, kind._3 + "#SKO") SymbolKind(kind._1, kind._2, kind._3) } @@ -2216,8 +2261,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Term symbols with the exception of static parts of Java classes and packages. */ - override def isValue = !(isModule && hasFlag(PACKAGE | JAVA)) - override def isVariable = isMutable && !isMethod + override def isValue = !(isModule && hasFlag(PACKAGE | JAVA)) + override def isVariable = isMutable && !isMethod + override def isTermMacro = hasFlag(MACRO) // interesting only for lambda lift. Captured variables are accessed from inner lambdas. override def isCapturedVariable = hasAllFlags(MUTABLE | CAPTURED) && !hasFlag(METHOD) @@ -2406,7 +2452,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isMethod = true override def isLabel = this hasFlag LABEL - override def isMacro = this hasFlag MACRO override def isVarargsMethod = this hasFlag VARARGS override def isLiftedMethod = this hasFlag LIFTED @@ -2438,6 +2483,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => extends TypeSymbol(initOwner, initPos, initName) { type TypeOfClonedSymbol = TypeSymbol final override def isAliasType = true + final override def dealias = info.typeSymbol.dealias override def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeSymbol = owner.newNonClassSymbol(name, pos, newFlags) } @@ -2469,7 +2515,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => final override def isType = true override def isNonClassType = true - + override def isTypeMacro = hasFlag(MACRO) + override def resolveOverloadedFlag(flag: Long) = flag match { case TRAIT => "<trait>" // DEFAULTPARAM case EXISTENTIAL => "<existential>" // MIXEDIN @@ -2877,7 +2924,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => abort("Can't rename a package object to " + name) } } - + trait ImplClassSymbol extends ClassSymbol { override def sourceModule = companionModule // override def isImplClass = true @@ -2913,12 +2960,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) } - class FreeVar(name0: TermName, val value: Any) extends TermSymbol(NoSymbol, NoPosition, name0) { - 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 - } + class FreeTerm(name0: TermName, value0: => Any, val origin: String) extends TermSymbol(NoSymbol, NoPosition, name0) { + def value = value0 + override def isFreeTerm = true + } + + // [Eugene] the NoSymbol origin works for type parameters. what about existential free types? + class FreeType(name0: TypeName, value0: => Any, val origin: String) extends TypeSkolem(NoSymbol, NoPosition, name0, NoSymbol) { + def value = value0 + override def isFreeType = true } /** An object representing a missing symbol */ @@ -3054,10 +3104,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (settings.debug.value) printStackTrace() } - case class InvalidCompanions(sym1: Symbol, sym2: Symbol) extends Throwable( + case class InvalidCompanions(sym1: Symbol, sym2: Symbol) extends Throwable({ + import language.reflectiveCalls "Companions '" + sym1 + "' and '" + sym2 + "' must be defined in same file:\n" + " Found in " + sym1.sourceFile.canonicalPath + " and " + sym2.sourceFile.canonicalPath - ) { + }) { override def toString = getMessage } @@ -3068,7 +3119,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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/TreeBuildUtil.scala b/src/compiler/scala/reflect/internal/TreeBuildUtil.scala new file mode 100644 index 0000000000..fbcd5043bc --- /dev/null +++ b/src/compiler/scala/reflect/internal/TreeBuildUtil.scala @@ -0,0 +1,62 @@ +package scala.reflect +package internal + +trait TreeBuildUtil extends api.TreeBuildUtil { self: SymbolTable => + + // ``staticClass'' and ``staticModule'' rely on ClassLoaders + // which are implementation-specific for different Universes + + def staticClassIfDefined(fullName: String): Symbol = + try staticClass(fullName) + catch { case _: MissingRequirementError => NoSymbol } + + def staticModuleIfDefined(fullName: String): Symbol = + try staticModule(fullName) + catch { case _: MissingRequirementError => NoSymbol } + + def thisModuleType(fullname: String) = staticModule(fullname).moduleClass.thisType + + def selectType(owner: Symbol, name: String): Symbol = + owner.info.decl(newTypeName(name)) orElse { + MissingRequirementError.notFound("type %s in %s".format(name, owner.fullName)) + } + + def selectTypeIfDefined(owner: Symbol, name: String): Symbol = + try selectType(owner, name) + catch { case _: MissingRequirementError => NoSymbol } + +// try getModule(fullname.toTermName) +// catch { case _: MissingRequirementError => NoSymbol } + + def selectTerm(owner: Symbol, name: String): Symbol = { + val sym = owner.info.decl(newTermName(name)) + val result = + if (sym.isOverloaded) sym suchThat (!_.isMethod) + else sym + result orElse { + MissingRequirementError.notFound("term %s in %s".format(name, owner.fullName)) + } + } + + def selectTermIfDefined(owner: Symbol, name: String): Symbol = + try selectTerm(owner, name) + catch { case _: MissingRequirementError => NoSymbol } + + def selectOverloadedMethod(owner: Symbol, name: String, index: Int): Symbol = + owner.info.decl(newTermName(name)).alternatives(index) orElse { + MissingRequirementError.notFound("overloaded method %s #%d in %s".format(name, index, owner.fullName)) + } + + def selectOverloadedMethodIfDefined(owner: Symbol, name: String, index: Int): Symbol = + try selectOverloadedMethod(owner, name, index) + catch { case _: MissingRequirementError => NoSymbol } + + def newFreeTerm(name: String, info: Type, value: => Any, origin: String) = newFreeTerm(newTermName(name), info, value, origin) + + def newFreeType(name: String, info: Type, value: => Any, origin: String) = newFreeType(newTypeName(name), info, value, origin) + + def modifiersFromInternalFlags(flags: Long, privateWithin: Name, annotations: List[Tree]): Modifiers = + Modifiers(flags, privateWithin, annotations) + + val gen: TreeGen { val global: TreeBuildUtil.this.type } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/TreeGen.scala b/src/compiler/scala/reflect/internal/TreeGen.scala index 141ff12f8a..1a374b6e59 100644 --- a/src/compiler/scala/reflect/internal/TreeGen.scala +++ b/src/compiler/scala/reflect/internal/TreeGen.scala @@ -1,7 +1,7 @@ package scala.reflect package internal -abstract class TreeGen { +abstract class TreeGen extends api.AbsTreeGen { val global: SymbolTable import global._ diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index ce3de94335..a8cca1625f 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -531,4 +531,213 @@ abstract class TreeInfo { case _ => None } } + + def isApplyDynamicName(name: Name) = (name == nme.updateDynamic) || (name == nme.selectDynamic) || (name == nme.applyDynamic) || (name == nme.applyDynamicNamed) + + class DynamicApplicationExtractor(nameTest: Name => Boolean) { + def unapply(tree: Tree) = tree match { + case Apply(TypeApply(Select(qual, oper), _), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) + case Apply(Select(qual, oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) + case Apply(Ident(oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((EmptyTree, name)) + case _ => None + } + } + object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic) + object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName) + object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed) + + + // domain-specific extractors for reification + + import definitions._ + + object TypedOrAnnotated { + def unapply(tree: Tree): Option[Tree] = tree match { + case ty @ Typed(_, _) => + Some(ty) + case at @ Annotated(_, _) => + Some(at) + case _ => + None + } + } + + object TreeSplice { + def unapply(tree: Tree): Option[Tree] = tree match { + case Select(splicee, _) if tree.symbol == ExprEval || tree.symbol == ExprValue => + Some(splicee) + case _ => + None + } + } + + object EvalSplice { + def unapply(tree: Tree): Option[Tree] = tree match { + case Select(splicee, _) if tree.symbol == ExprEval => + Some(splicee) + case _ => + None + } + } + + object ValueSplice { + def unapply(tree: Tree): Option[Tree] = tree match { + case Select(splicee, _) if tree.symbol == ExprValue => + Some(splicee) + case _ => + None + } + } + + object Reified { + def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + case ReifiedTree(reifee, symbolTable, reified, _) => + Some(reifee, symbolTable, reified) + case ReifiedType(reifee, symbolTable, reified) => + Some(reifee, symbolTable, reified) + case _ => + None + } + } + + object ReifiedTree { + def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree)] = tree match { + case reifee @ Block((mrDef @ ValDef(_, _, _, _)) :: symbolTable, Apply(Apply(_, List(tree)), List(Apply(_, List(tpe))))) if mrDef.name == nme.MIRROR_SHORT => + Some(reifee, symbolTable map (_.asInstanceOf[ValDef]), tree, tpe) + case _ => + None + } + } + + object InlineableTreeSplice { + def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree, Symbol)] = tree match { + case select @ Select(ReifiedTree(splicee, symbolTable, tree, tpe), _) if select.symbol == ExprEval || select.symbol == ExprValue => + Some(splicee, symbolTable, tree, tpe, select.symbol) + case _ => + None + } + } + + object InlinedTreeSplice { + def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree)] = tree match { + case Select(ReifiedTree(splicee, symbolTable, tree, tpe), name) if name == ExprTree.name => + Some(splicee, symbolTable, tree, tpe) + case _ => + None + } + } + + object ReifiedType { + def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + case reifee @ Block((mrDef @ ValDef(_, _, _, _)) :: symbolTable, Apply(_, List(tpe))) if mrDef.name == nme.MIRROR_SHORT => + Some(reifee, symbolTable map (_.asInstanceOf[ValDef]), tpe) + case _ => + None + } + } + + object InlinedTypeSplice { + def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + case Select(ReifiedType(splicee, symbolTable, tpe), name) if name == TypeTagTpe.name => + Some(splicee, symbolTable, tpe) + case _ => + None + } + } + + object FreeDef { + def unapply(tree: Tree): Option[(Tree, TermName, Tree, String)] = tree match { + case FreeTermDef(mrRef, name, binding, origin) => + Some(mrRef, name, binding, origin) + case FreeTypeDef(mrRef, name, binding, origin) => + Some(mrRef, name, binding, origin) + case _ => + None + } + } + + object FreeTermDef { + lazy val newFreeTermMethod = getMember(getRequiredClass("scala.reflect.api.TreeBuildUtil"), nme.newFreeTerm) + + def unapply(tree: Tree): Option[(Tree, TermName, Tree, String)] = tree match { + case ValDef(_, name, _, Apply(Select(mrRef @ Ident(_), newFreeTerm), List(_, _, binding, Literal(Constant(origin: String))))) + if mrRef.name == nme.MIRROR_SHORT && newFreeTerm == newFreeTermMethod.name => + Some(mrRef, name, binding, origin) + case _ => + None + } + } + + object FreeTypeDef { + lazy val newFreeTypeMethod = getMember(getRequiredClass("scala.reflect.api.TreeBuildUtil"), nme.newFreeType) + + def unapply(tree: Tree): Option[(Tree, TermName, Tree, String)] = tree match { + case ValDef(_, name, _, Apply(Select(mrRef1 @ Ident(_), newFreeType), List(_, _, value, Literal(Constant(origin: String))))) + if mrRef1.name == nme.MIRROR_SHORT && newFreeType == newFreeTypeMethod.name => + value match { + case Apply(TypeApply(Select(Select(mrRef2 @ Ident(_), typeTag), apply), List(binding)), List(Literal(Constant(null)))) + if mrRef2.name == nme.MIRROR_SHORT && typeTag == nme.TypeTag && apply == nme.apply => + Some(mrRef1, name, binding, origin) + case Apply(TypeApply(Select(mrRef2 @ Ident(_), typeTag), List(binding)), List(Literal(Constant(null)))) + if mrRef2.name == nme.MIRROR_SHORT && typeTag == nme.TypeTag => + Some(mrRef1, name, binding, origin) + case _ => + throw new Error("unsupported free type def: " + showRaw(tree)) + } + case _ => + None + } + } + + object FreeRef { + def unapply(tree: Tree): Option[(Tree, TermName)] = tree match { + case Apply(Select(mrRef @ Ident(_), ident), List(Ident(name: TermName))) if ident == nme.Ident && name.startsWith(nme.MIRROR_FREE_PREFIX) => + Some(mrRef, name) + case _ => + None + } + } + + object TypeRefToFreeType { + def unapply(tree: Tree): Option[TermName] = tree match { + case Apply(Select(Select(mrRef @ Ident(_), typeRef), apply), List(Select(_, noSymbol), Ident(freeType: TermName), nil)) + if (mrRef.name == nme.MIRROR_SHORT && typeRef == nme.TypeRef && noSymbol == nme.NoSymbol && freeType.startsWith(nme.MIRROR_FREE_PREFIX)) => + Some(freeType) + case _ => + None + } + } + + object NestedExpr { + def unapply(tree: Tree): Option[(Tree, Tree, Tree)] = tree match { + case Apply(Apply(factory @ Select(expr, apply), List(tree)), List(typetag)) if expr.symbol == ExprModule && apply == nme.apply => + Some(factory, tree, typetag) + case _ => + None + } + } + + object BoundTerm { + def unapply(tree: Tree): Option[Tree] = tree match { + case Ident(name) if name.isTermName => + Some(tree) + case This(_) => + Some(tree) + case _ => + None + } + } + + object BoundType { + def unapply(tree: Tree): Option[Tree] = tree match { + case Select(_, name) if name.isTypeName => + Some(tree) + case SelectFromTypeTree(_, name) if name.isTypeName => + Some(tree) + case Ident(name) if name.isTypeName => + Some(tree) + case _ => + None + } + } } diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index 7a084304a8..486a3d3567 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -23,6 +23,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => else s } def quotedName(name: Name): String = quotedName(name, false) + def quotedName(name: String): String = quotedName(newTermName(name), false) private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { val sym = tree.symbol @@ -31,7 +32,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => var suffix = "" if (settings.uniqid.value) suffix += ("#" + sym.id) if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) - prefix + tree.symbol.decodedName + suffix + prefix + quotedName(tree.symbol.decodedName) + suffix } else { quotedName(name, decoded) } @@ -64,7 +65,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => def indent() = indentMargin += indentStep def undent() = indentMargin -= indentStep - def printPosition(tree: Tree) = if (doPrintPositions) print(showPos(tree.pos)) + def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show) def println() { out.println() @@ -102,6 +103,16 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => } } + def printLabelParams(ps: List[Ident]) { + print("(") + printSeq(ps){printLabelParam}{print(", ")} + print(")") + } + + def printLabelParam(p: Ident) { + print(symName(p, p.name)); printOpt(": ", TypeTree() setType p.tpe) + } + def printValueParams(ts: List[ValDef]) { print("(") if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, "") @@ -218,7 +229,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => } case LabelDef(name, params, rhs) => - print(symName(tree, name)); printRow(params, "(", ",", ")"); printBlock(rhs) + print(symName(tree, name)); printLabelParams(params); printBlock(rhs) case Import(expr, selectors) => // Is this selector remapping a name (i.e, {name1 => name2}) diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index c0d6f54b1a..5f1a8f3fbe 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -103,9 +103,7 @@ trait Trees extends api.Trees { self: SymbolTable => // --- extension methods -------------------------------------------------------- - implicit def treeOps(tree: Tree): TreeOps = new TreeOps(tree) - - class TreeOps(tree: Tree) { + implicit class TreeOps(tree: Tree) { def isErroneous = (tree.tpe ne null) && tree.tpe.isErroneous def isTyped = (tree.tpe ne null) && !tree.tpe.isErroneous @@ -146,11 +144,9 @@ trait Trees extends api.Trees { self: SymbolTable => * less than the whole tree. */ def summaryString: String = tree match { - case Select(qual, name) => qual.summaryString + "." + name.decode - case Ident(name) => name.longString case Literal(const) => "Literal(" + const + ")" - case t: DefTree => t.shortClass + " `" + t.name.decode + "`" - case t: RefTree => t.shortClass + " `" + t.name.longString + "`" + case Select(qual, name) => qual.summaryString + "." + name.decode + case t: NameTree => t.name.longString case t => t.shortClass + ( if (t.symbol != null && t.symbol != NoSymbol) " " + t.symbol @@ -184,7 +180,7 @@ trait Trees extends api.Trees { self: SymbolTable => def ValDef(sym: Symbol, rhs: Tree): ValDef = atPos(sym.pos) { ValDef(Modifiers(sym.flags), sym.name.toTermName, - TypeTree(sym.tpe) setPos focusPos(sym.pos), + TypeTree(sym.tpe) setPos sym.pos.focus, rhs) setSymbol sym } @@ -203,7 +199,7 @@ trait Trees extends api.Trees { self: SymbolTable => sym.name.toTermName, sym.typeParams map TypeDef, vparamss, - TypeTree(sym.tpe.finalResultType) setPos focusPos(sym.pos), + TypeTree(sym.tpe.finalResultType) setPos sym.pos.focus, rhs) setSymbol sym } @@ -235,7 +231,8 @@ trait Trees extends api.Trees { self: SymbolTable => } /** casedef shorthand */ - def CaseDef(pat: Tree, body: Tree): CaseDef = CaseDef(pat, EmptyTree, body) + def CaseDef(pat: Tree, body: Tree): CaseDef = + CaseDef(pat, EmptyTree, body) def Bind(sym: Symbol, body: Tree): Bind = Bind(sym.name, body) setSymbol sym @@ -249,10 +246,39 @@ trait Trees extends api.Trees { self: SymbolTable => def Apply(sym: Symbol, args: Tree*): Tree = Apply(Ident(sym), args.toList) + /** 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 = argss match { + case Nil => new ApplyConstructor(tpt, Nil) + case xs :: rest => rest.foldLeft(new ApplyConstructor(tpt, xs): Tree)(Apply) + } + + /** 0-1 argument list new, based on a type. + */ + def New(tpe: Type, args: Tree*): Tree = + new ApplyConstructor(TypeTree(tpe), args.toList) + def New(sym: Symbol, args: Tree*): Tree = New(sym.tpe, args: _*) - def Super(sym: Symbol, mix: TypeName): Tree = Super(This(sym), mix) + def Super(sym: Symbol, mix: TypeName): Tree = + Super(This(sym), mix) + + def This(sym: Symbol): Tree = + This(sym.name.toTypeName) setSymbol sym + + def Select(qualifier: Tree, name: String): Select = + Select(qualifier, newTermName(name)) + + def Select(qualifier: Tree, sym: Symbol): Select = + Select(qualifier, sym.name) setSymbol sym + + def Ident(name: String): Ident = + Ident(newTermName(name)) + + def Ident(sym: Symbol): Ident = + Ident(sym.name) setSymbol sym /** Block factory that flattens directly nested blocks. */ @@ -266,6 +292,7 @@ trait Trees extends api.Trees { self: SymbolTable => } // --- specific traversers and transformers + // todo. move these into scala.reflect.api protected[scala] def duplicateTree(tree: Tree): Tree = duplicator transform tree @@ -273,44 +300,11 @@ trait Trees extends api.Trees { self: SymbolTable => override val treeCopy = newStrictTreeCopier override def transform(t: Tree) = { val t1 = super.transform(t) - if ((t1 ne t) && isRangePos(t1.pos)) t1 setPos focusPos(t.pos) + if ((t1 ne t) && t1.pos.isRange) t1 setPos t.pos.focus t1 } } - trait PosAssigner extends Traverser { - var pos: Position - } - protected[this] lazy val posAssigner: PosAssigner = new DefaultPosAssigner - - protected class DefaultPosAssigner extends PosAssigner { - var pos: Position = _ - override def traverse(t: Tree) { - if (t eq EmptyTree) () - else if (t.pos == NoPosition) { - t.setPos(pos) - 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. - } - } - } - - def atPos[T <: Tree](pos: Position)(tree: T): T = { - posAssigner.pos = pos - posAssigner.traverse(tree) - tree - } - class ForeachPartialTreeTraverser(pf: PartialFunction[Tree, Tree]) extends Traverser { override def traverse(tree: Tree) { val t = if (pf isDefinedAt tree) pf(tree) else tree @@ -363,7 +357,7 @@ trait Trees extends api.Trees { self: SymbolTable => override def toString = substituterString("Symbol", "Tree", from, to) } - /** Substitute clazz.this with `to`. `to` must be an attributed tree. + /** Substitute clazz.this with `to`. `to` must be an attributed tree. */ class ThisSubstituter(clazz: Symbol, to: => Tree) extends Transformer { val newtpe = to.tpe @@ -430,4 +424,3 @@ trait Trees extends api.Trees { self: SymbolTable => override def toString() = "TreeSymSubstituter/" + substituterString("Symbol", "Symbol", from, to) } } - diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 575d84eab4..17c9b955ca 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -97,7 +97,7 @@ trait Types extends api.Types { self: SymbolTable => */ private final val propagateParameterBoundsToTypeVars = sys.props contains "scalac.debug.prop-constraints" - protected val enableTypeVarExperimentals = settings.Xexperimental.value || settings.YvirtPatmat.value + protected val enableTypeVarExperimentals = settings.Xexperimental.value || !settings.XoldPatmat.value /** Empty immutable maps to avoid allocations. */ private val emptySymMap = immutable.Map[Symbol, Symbol]() @@ -254,7 +254,9 @@ trait Types extends api.Types { self: SymbolTable => case object UnmappableTree extends TermTree { override def toString = "<unmappable>" super.tpe_=(NoType) - override def tpe_=(t: Type) = if (t != NoType) throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for <empty>") + override def tpe_=(t: Type) = if (t != NoType) { + throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for <empty>") + } } abstract class AbsTypeImpl extends AbsType { this: Type => @@ -262,7 +264,7 @@ trait Types extends api.Types { self: SymbolTable => def nonPrivateDeclaration(name: Name): Symbol = nonPrivateDecl(name) def declarations = decls def typeArguments = typeArgs - def erasedType = transformedType(this) + def erasure = transformedType(this) def substituteTypes(from: List[Symbol], to: List[Type]): Type = subst(from, to) } @@ -710,7 +712,9 @@ trait Types extends api.Types { self: SymbolTable => /** Returns all parts of this type which satisfy predicate `p` */ def filter(p: Type => Boolean): List[Type] = new FilterTypeCollector(p) collect this - def withFilter(p: Type => Boolean) = new FilterTypeCollector(p) { + def withFilter(p: Type => Boolean) = new FilterMapForeach(p) + + class FilterMapForeach(p: Type => Boolean) extends FilterTypeCollector(p){ def foreach[U](f: Type => U): Unit = collect(Type.this) foreach f def map[T](f: Type => T): List[T] = collect(Type.this) map f } @@ -723,6 +727,9 @@ trait Types extends api.Types { self: SymbolTable => /** Apply `f` to each part of this type */ def foreach(f: Type => Unit) { new ForEachTypeTraverser(f).traverse(this) } + /** Apply `pf' to each part of this type on which the function is defined */ + def collect[T](pf: PartialFunction[Type, T]): List[T] = new CollectTypeCollector(pf).collect(this) + /** Apply `f` to each part of this type; children get mapped before their parents */ def map(f: Type => Type): Type = new TypeMap { def apply(x: Type) = f(mapOver(x)) @@ -910,14 +917,10 @@ trait Types extends api.Types { self: SymbolTable => */ def directObjectString = safeToString - /** A test whether a type contains any unification type variables. */ + /** A test whether a type contains any unification type variables. + * Overridden with custom logic except where trivially true. + */ def isGround: Boolean = this match { - case TypeVar(_, constr) => - constr.instValid && constr.inst.isGround - case TypeRef(pre, sym, args) => - sym.isPackageClass || pre.isGround && (args forall (_.isGround)) - case SingleType(pre, sym) => - sym.isPackageClass || pre.isGround case ThisType(_) | NoPrefix | WildcardType | NoType | ErrorType | ConstantType(_) => true case _ => @@ -1194,6 +1197,8 @@ trait Types extends api.Types { self: SymbolTable => override def kind = "BoundedWildcardType" } + object BoundedWildcardType extends BoundedWildcardTypeExtractor + /** An object representing a non-existing type */ case object NoType extends Type { override def isTrivial: Boolean = true @@ -1251,6 +1256,8 @@ trait Types extends api.Types { self: SymbolTable => */ abstract case class SingleType(pre: Type, sym: Symbol) extends SingletonType { override val isTrivial: Boolean = pre.isTrivial + override def isGround = sym.isPackageClass || pre.isGround + // override def isNullable = underlying.isNullable override def isNotNull = underlying.isNotNull private[reflect] var underlyingCache: Type = NoType @@ -1822,7 +1829,35 @@ trait Types extends api.Types { self: SymbolTable => object ConstantType extends ConstantTypeExtractor { def apply(value: Constant): ConstantType = { - unique(new UniqueConstantType(value)).asInstanceOf[ConstantType] + val tpe = new UniqueConstantType(value) + if (value.tag == ClazzTag) { + // if we carry a classOf, we might be in trouble + // http://groups.google.com/group/scala-internals/browse_thread/thread/45185b341aeb6a30 + // I don't have time for a thorough fix, so I put a hacky workaround here + val alreadyThere = uniques findEntry tpe + if ((alreadyThere ne null) && (alreadyThere ne tpe) && (alreadyThere.toString != tpe.toString)) { + // we need to remove a stale type that has the same hashcode as we do + // HashSet doesn't support removal, and this makes our task non-trivial + // also we cannot simply recreate it, because that'd skew hashcodes (that change over time, omg!) + // the only solution I can see is getting into the underlying array and sneakily manipulating it + val ftable = uniques.getClass.getDeclaredFields().find(f => f.getName endsWith "table").get + ftable.setAccessible(true) + val table = ftable.get(uniques).asInstanceOf[Array[AnyRef]] + def overwrite(hc: Int, x: Type) { + def index(x: Int): Int = math.abs(x % table.length) + var h = index(hc) + var entry = table(h) + while (entry ne null) { + if (x == entry) + table(h) = x + h = index(h + 1) + entry = table(h) + } + } + overwrite(tpe.##, tpe) + } + } + unique(tpe).asInstanceOf[ConstantType] } } @@ -2108,6 +2143,11 @@ trait Types extends api.Types { self: SymbolTable => } } + override def isGround = ( + sym.isPackageClass + || pre.isGround && args.forall(_.isGround) + ) + def etaExpand: Type = { // must initialise symbol, see test/files/pos/ticket0137.scala val tpars = initializedTypeParams @@ -2200,10 +2240,10 @@ trait Types extends api.Types { self: SymbolTable => parentsString(thisInfo.parents) + refinementString else rest ) - private def customToString = this match { - case TypeRef(_, RepeatedParamClass, arg :: _) => arg + "*" - case TypeRef(_, ByNameParamClass, arg :: _) => "=> " + arg - case _ => + private def customToString = sym match { + case RepeatedParamClass => args.head + "*" + case ByNameParamClass => "=> " + args.head + case _ => def targs = normalize.typeArgs if (isFunctionType(this)) { @@ -2480,39 +2520,46 @@ trait Types extends api.Types { self: SymbolTable => override def skolemizeExistential(owner: Symbol, origin: AnyRef) = deriveType(quantified, tparam => (owner orElse tparam.owner).newExistentialSkolem(tparam, origin))(underlying) - private def wildcardArgsString(available: Set[Symbol], args: List[Type]): List[String] = args match { - case TypeRef(_, sym, _) :: args1 if (available contains sym) => - ("_"+sym.infoString(sym.info)) :: wildcardArgsString(available - sym, args1) - case arg :: args1 if !(quantified exists (arg contains _)) => - arg.toString :: wildcardArgsString(available, args1) - case _ => - List() + private def wildcardArgsString(qset: Set[Symbol], args: List[Type]): List[String] = args map { + case TypeRef(_, sym, _) if (qset contains sym) => + "_"+sym.infoString(sym.info) + case arg => + arg.toString } + /** 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 + * - every quantified variable appears at most once as a type argument and + * nowhere inside a type argument + * - no quantified type argument contains a quantified variable in its bound + * - the typeref's symbol is not itself quantified + * - the prefix is not quanitified */ - private def isRepresentableWithWildcards = !settings.debug.value && { + def isRepresentableWithWildcards = { 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 _) - } + underlying match { + case TypeRef(pre, sym, args) => + def isQuantified(tpe: Type): Boolean = { + (tpe exists (t => qset contains t.typeSymbol)) || + tpe.typeSymbol.isRefinementClass && (tpe.parents exists isQuantified) } + val (wildcardArgs, otherArgs) = args partition (arg => qset contains arg.typeSymbol) + wildcardArgs.distinct == wildcardArgs && + !(otherArgs exists (arg => isQuantified(arg))) && + !(wildcardArgs exists (arg => isQuantified(arg.typeSymbol.info.bounds))) && + !(qset contains sym) && + !isQuantified(pre) case _ => false - }) + } } + override def safeToString: String = { def clauses = { val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") if (settings.explaintypes.value) "(" + str + ")" else str } underlying match { - case TypeRef(pre, sym, args) if isRepresentableWithWildcards => + case TypeRef(pre, sym, args) if !settings.debug.value && isRepresentableWithWildcards => "" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]") case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => "(" + underlying + ")" + clauses @@ -2640,21 +2687,35 @@ trait Types extends api.Types { self: SymbolTable => else new TypeConstraint } def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr)) + def untouchable(tparam: Symbol): TypeVar = createTypeVar(tparam, untouchable = true) + def apply(tparam: Symbol): TypeVar = createTypeVar(tparam, untouchable = false) def apply(origin: Type, constr: TypeConstraint): TypeVar = apply(origin, constr, Nil, Nil) - def apply(tparam: Symbol): TypeVar = apply(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams) + def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]): TypeVar = + createTypeVar(origin, constr, args, params, untouchable = false) /** This is the only place TypeVars should be instantiated. */ - def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]): TypeVar = { + private def createTypeVar(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol], untouchable: Boolean): TypeVar = { val tv = ( - if (args.isEmpty && params.isEmpty) new TypeVar(origin, constr) - else if (args.size == params.size) new AppliedTypeVar(origin, constr, params zip args) - else if (args.isEmpty) new HKTypeVar(origin, constr, params) + if (args.isEmpty && params.isEmpty) { + if (untouchable) new TypeVar(origin, constr) with UntouchableTypeVar + else new TypeVar(origin, constr) + } + else if (args.size == params.size) { + if (untouchable) new AppliedTypeVar(origin, constr, params zip args) with UntouchableTypeVar + else new AppliedTypeVar(origin, constr, params zip args) + } + else if (args.isEmpty) { + if (untouchable) new HKTypeVar(origin, constr, params) with UntouchableTypeVar + else new HKTypeVar(origin, constr, params) + } else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params))) ) trace("create", "In " + tv.originLocation)(tv) } + private def createTypeVar(tparam: Symbol, untouchable: Boolean): TypeVar = + createTypeVar(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable) } /** Repack existential types, otherwise they sometimes get unpacked in the @@ -2702,6 +2763,23 @@ trait Types extends api.Types { self: SymbolTable => zippedArgs map { case (p, a) => p.name + "=" + a } mkString (origin + "[", ", ", "]") ) } + + trait UntouchableTypeVar extends TypeVar { + override def untouchable = true + override def isGround = true + override def registerTypeEquality(tp: Type, typeVarLHS: Boolean) = tp match { + case t: TypeVar if !t.untouchable => + t.registerTypeEquality(this, !typeVarLHS) + case _ => + super.registerTypeEquality(tp, typeVarLHS) + } + override def registerBound(tp: Type, isLowerBound: Boolean, isNumericBound: Boolean = false): Boolean = tp match { + case t: TypeVar if !t.untouchable => + t.registerBound(this, !isLowerBound, isNumericBound) + case _ => + super.registerBound(tp, isLowerBound, isNumericBound) + } + } /** A class representing a type variable: not used after phase `typer`. * @@ -2716,6 +2794,7 @@ trait Types extends api.Types { self: SymbolTable => val origin: Type, val constr0: TypeConstraint ) extends Type { + def untouchable = false // by other typevars override def params: List[Symbol] = Nil override def typeArgs: List[Type] = Nil override def isHigherKinded = false @@ -2728,6 +2807,7 @@ trait Types extends api.Types { self: SymbolTable => */ var constr = constr0 def instValid = constr.instValid + override def isGround = instValid && constr.inst.isGround /** The variable's skolemization level */ val level = skolemizationLevel @@ -2896,14 +2976,13 @@ trait Types extends api.Types { self: SymbolTable => // would be pointless. In this case, each check we perform causes us to lose specificity: in // the end the best we'll do is the least specific type we tested against, since the typevar // does not see these checks as "probes" but as requirements to fulfill. - // TODO: the `suspended` flag can be used to poke around with leaving a trace + // TODO: can the `suspended` flag be used to poke around without leaving a trace? // // So the strategy used here is to test first the type, then the direct parents, and finally // to fall back on the individual base types. This warrants eventual re-examination. // AM: I think we could use the `suspended` flag to avoid side-effecting during unification - - if (suspended) // constraint accumulation is disabled + if (suspended) // constraint accumulation is disabled checkSubtype(tp, origin) else if (constr.instValid) // type var is already set checkSubtype(tp, constr.inst) @@ -3001,7 +3080,7 @@ trait Types extends api.Types { self: SymbolTable => override def safeToString = ( if ((constr eq null) || (constr.inst eq null)) "TVar<" + originName + "=null>" else if (constr.inst ne NoType) "" + constr.inst - else "?" + levelString + originName + else (if(untouchable) "!?" else "?") + levelString + originName ) override def kind = "TypeVar" @@ -3751,6 +3830,8 @@ trait Types extends api.Types { self: SymbolTable => } } + // todo. move these into scala.reflect.api + /** A prototype for mapping a function over all possible types */ abstract class TypeMap extends (Type => Type) { @@ -4563,6 +4644,16 @@ trait Types extends api.Types { self: SymbolTable => } } + /** A map to implement the `collect` method. */ + class CollectTypeCollector[T](pf: PartialFunction[Type, T]) extends TypeCollector[List[T]](Nil) { + override def collect(tp: Type) = super.collect(tp).reverse + + def traverse(tp: Type) { + if (pf.isDefinedAt(tp)) result ::= pf(tp) + mapOver(tp) + } + } + class ForEachTypeTraverser(f: Type => Unit) extends TypeTraverser { def traverse(tp: Type) { f(tp) @@ -4686,7 +4777,7 @@ trait Types extends api.Types { self: SymbolTable => val sym1 = adaptToNewRun(sym.owner.thisType, sym) if (sym1 == sym) tp else ThisType(sym1) } catch { - case ex: MissingTypeControl => + case ex: MissingTypeControl => tp } case SingleType(pre, sym) => @@ -5997,8 +6088,9 @@ trait Types extends api.Types { self: SymbolTable => def stripType(tp: Type) = tp match { case ExistentialType(_, res) => res - case TypeVar(_, constr) => - if (constr.instValid) constr.inst + case tv@TypeVar(_, constr) => + if (tv.instValid) constr.inst + else if (tv.untouchable) tv else abort("trying to do lub/glb of typevar "+tp) case t => t } diff --git a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala index b556c33aba..45ba4ed3e6 100644 --- a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala @@ -43,5 +43,5 @@ abstract class MutableSettings extends AbsSettings { def Yrecursion: IntSetting def maxClassfileName: IntSetting def Xexperimental: BooleanSetting - def YvirtPatmat: BooleanSetting + def XoldPatmat: BooleanSetting }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/AbortMacroException.scala b/src/compiler/scala/reflect/makro/runtime/AbortMacroException.scala new file mode 100644 index 0000000000..d78eae9237 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/AbortMacroException.scala @@ -0,0 +1,6 @@ +package scala.reflect.makro +package runtime + +import scala.reflect.api.Position + +class AbortMacroException(val pos: Position, val msg: String) extends Throwable(msg) diff --git a/src/compiler/scala/reflect/makro/runtime/Aliases.scala b/src/compiler/scala/reflect/makro/runtime/Aliases.scala new file mode 100644 index 0000000000..a4f208ca34 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Aliases.scala @@ -0,0 +1,21 @@ +package scala.reflect.makro +package runtime + +trait Aliases { + self: Context => + + /** Aliases of mirror types */ + override type Symbol = mirror.Symbol + override type Type = mirror.Type + override type Name = mirror.Name + override type Tree = mirror.Tree + override type Position = mirror.Position + override type Scope = mirror.Scope + override type Modifiers = mirror.Modifiers + override type Expr[+T] = mirror.Expr[T] + override type TypeTag[T] = mirror.TypeTag[T] + + /** Creator/extractor objects for Expr and TypeTag values */ + override val TypeTag = mirror.TypeTag + override val Expr = mirror.Expr +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/CapturedVariables.scala b/src/compiler/scala/reflect/makro/runtime/CapturedVariables.scala new file mode 100644 index 0000000000..4e93d4e06d --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/CapturedVariables.scala @@ -0,0 +1,14 @@ +package scala.reflect.makro +package runtime + +trait CapturedVariables { + self: Context => + + import mirror._ + + def captureVariable(vble: Symbol): Unit = mirror.captureVariable(vble) + + def referenceCapturedVariable(vble: Symbol): Tree = mirror.referenceCapturedVariable(vble) + + def capturedVariableType(vble: Symbol): Type = mirror.capturedVariableType(vble) +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Context.scala b/src/compiler/scala/reflect/makro/runtime/Context.scala new file mode 100644 index 0000000000..184008658e --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Context.scala @@ -0,0 +1,26 @@ +package scala.reflect.makro +package runtime + +import scala.tools.nsc.Global + +abstract class Context extends scala.reflect.makro.Context + with Aliases + with CapturedVariables + with Infrastructure + with Enclosures + with Names + with Reifiers + with Reporters + with Settings + with Symbols + with Typers + with Util { + + val mirror: Global + + val callsiteTyper: mirror.analyzer.Typer + + val prefix: Expr[PrefixType] + + val expandee: Tree +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Enclosures.scala b/src/compiler/scala/reflect/makro/runtime/Enclosures.scala new file mode 100644 index 0000000000..f9a6987e48 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Enclosures.scala @@ -0,0 +1,36 @@ +package scala.reflect.makro +package runtime + +trait Enclosures { + self: Context => + + import mirror._ + + // vals are eager to simplify debugging + // after all we wouldn't save that much time by making them lazy + + val macroApplication: Tree = expandee + + val enclosingMacros: List[Context] = this :: mirror.analyzer.openMacros + + val enclosingImplicits: List[(Type, Tree)] = callsiteTyper.context.openImplicits + + val enclosingPosition: Position = enclosingMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication.pos).getOrElse(NoPosition) + + val enclosingApplication: Tree = { + def loop(context: analyzer.Context): Tree = context match { + case analyzer.NoContext => EmptyTree + case context if context.tree.isInstanceOf[Apply] => context.tree + case context => loop(context.outer) + } + + val context = callsiteTyper.context + loop(context) + } + + val enclosingMethod: Tree = callsiteTyper.context.enclMethod.tree + + val enclosingClass: Tree = callsiteTyper.context.enclClass.tree + + val enclosingUnit: CompilationUnit = currentRun.currentUnit +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Infrastructure.scala b/src/compiler/scala/reflect/makro/runtime/Infrastructure.scala new file mode 100644 index 0000000000..6d8e55cc35 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Infrastructure.scala @@ -0,0 +1,34 @@ +package scala.reflect.makro +package runtime + +trait Infrastructure { + self: Context => + + val forJVM: Boolean = mirror.forJVM + + val forMSIL: Boolean = mirror.forMSIL + + val forInteractive: Boolean = mirror.forInteractive + + val forScaladoc: Boolean = mirror.forScaladoc + + val currentRun: Run = mirror.currentRun + + type Run = mirror.Run + + object Run extends RunExtractor { + def unapply(run: Run): Option[(CompilationUnit, List[CompilationUnit])] = Some(run.currentUnit, run.units.toList) + } + + type CompilationUnit = mirror.CompilationUnit + + object CompilationUnit extends CompilationUnitExtractor { + def unapply(compilationUnit: CompilationUnit): Option[(java.io.File, Array[Char], Tree)] = Some(compilationUnit.source.file.file, compilationUnit.source.content, compilationUnit.body) + } + + val currentMacro: Symbol = expandee.symbol + + val globalCache: collection.mutable.Map[Any, Any] = mirror.analyzer.globalMacroCache + + val cache: collection.mutable.Map[Any, Any] = mirror.analyzer.perRunMacroCache.getOrElseUpdate(currentMacro, collection.mutable.Map[Any, Any]()) +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Names.scala b/src/compiler/scala/reflect/makro/runtime/Names.scala new file mode 100644 index 0000000000..d8ecc2b89e --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Names.scala @@ -0,0 +1,20 @@ +package scala.reflect.makro +package runtime + +trait Names { + self: Context => + + lazy val freshNameCreator = callsiteTyper.context.unit.fresh + + def fresh(): String = { + freshNameCreator.newName() + } + + def fresh(name: String): String = { + freshNameCreator.newName(name) + } + + def fresh(name: Name): Name = { + name.mapName(freshNameCreator.newName(_)) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Reifiers.scala b/src/compiler/scala/reflect/makro/runtime/Reifiers.scala new file mode 100644 index 0000000000..2488b06d6c --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Reifiers.scala @@ -0,0 +1,69 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Gilles Dubochet + */ + +package scala.reflect.makro +package runtime + +trait Reifiers { + self: Context => + + import mirror._ + + lazy val reflectMirrorPrefix: Tree = { + // [Eugene] how do I typecheck this without undergoing this tiresome (and, in general, incorrect) procedure? + val prefix: Tree = Select(Select(Ident(definitions.ScalaPackage), newTermName("reflect")), newTermName("mirror")) + val prefixTpe = typeCheck(TypeApply(Select(prefix, newTermName("asInstanceOf")), List(SingletonTypeTree(prefix)))).tpe + typeCheck(prefix) setType prefixTpe + } + + def reifyTree(prefix: Tree, tree: Tree): Tree = + reifyTopLevel(prefix, tree) + + def reifyType(prefix: Tree, tpe: Type, dontSpliceAtTopLevel: Boolean = false, requireConcreteTypeTag: Boolean = false): Tree = + reifyTopLevel(prefix, tpe, dontSpliceAtTopLevel, requireConcreteTypeTag) + + def unreifyTree(tree: Tree): Tree = + Select(tree, definitions.ExprEval) + + def reifyTopLevel(prefix: Tree, reifee: Any, dontSpliceAtTopLevel: Boolean = false, requireConcreteTypeTag: Boolean = false): Tree = { + // [Eugene] the plumbing is not very pretty, but anyways factoring out the reifier seems like a necessary step to me + import scala.reflect.reify._ + val reifier = mkReifier(mirror)(callsiteTyper, prefix, reifee, dontSpliceAtTopLevel, requireConcreteTypeTag) + + try { + val result = reifier.reified + logFreeVars(expandee.pos, result) + result + } catch { + case ex: reifier.ReificationError => +// // this is a "soft" exception - it will normally be caught by the macro +// // consequently, we need to log the stack trace here, so that it doesn't get lost +// if (settings.Yreifydebug.value) { +// val message = new java.io.StringWriter() +// ex.printStackTrace(new java.io.PrintWriter(message)) +// println(scala.compat.Platform.EOL + message) +// } + val xlated = new ReificationError(ex.pos, ex.msg) + xlated.setStackTrace(ex.getStackTrace) + throw xlated + case ex: reifier.UnexpectedReificationError => + val xlated = new UnexpectedReificationError(ex.pos, ex.msg, ex.cause) + xlated.setStackTrace(ex.getStackTrace) + throw xlated + } + } + + class ReificationError(var pos: Position, val msg: String) extends Throwable(msg) + + object ReificationError extends ReificationErrorExtractor { + def unapply(error: ReificationError): Option[(Position, String)] = Some((error.pos, error.msg)) + } + + class UnexpectedReificationError(val pos: Position, val msg: String, val cause: Throwable = null) extends Throwable(msg, cause) + + object UnexpectedReificationError extends UnexpectedReificationErrorExtractor { + def unapply(error: UnexpectedReificationError): Option[(Position, String, Throwable)] = Some((error.pos, error.msg, error.cause)) + } +} diff --git a/src/compiler/scala/reflect/makro/runtime/Reporters.scala b/src/compiler/scala/reflect/makro/runtime/Reporters.scala new file mode 100644 index 0000000000..0fd037bdd2 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Reporters.scala @@ -0,0 +1,44 @@ +package scala.reflect.makro +package runtime + +trait Reporters { + self: Context => + + import mirror._ + + def reporter: mirror.Reporter = wrapNscReporter(mirror.reporter) + + def setReporter(reporter: mirror.Reporter): this.type = { + mirror.reporter = wrapApiReporter(reporter) + this + } + + def withReporter[T](reporter: Reporter)(op: => T): T = { + val old = mirror.reporter + setReporter(reporter) + try op + finally mirror.reporter = old + } + + def echo(pos: Position, msg: String): Unit = mirror.reporter.echo(pos, msg) + + def info(pos: Position, msg: String, force: Boolean): Unit = mirror.reporter.info(pos, msg, force) + + def hasWarnings: Boolean = mirror.reporter.hasErrors + + def hasErrors: Boolean = mirror.reporter.hasErrors + + def warning(pos: Position, msg: String): Unit = callsiteTyper.context.warning(pos, msg) + + def error(pos: Position, msg: String): Unit = callsiteTyper.context.error(pos, msg) + + def abort(pos: Position, msg: String): Nothing = { + callsiteTyper.context.error(pos, msg) + throw new AbortMacroException(pos, msg) + } + + def interactive(): Unit = mirror.reporter match { + case reporter: tools.nsc.reporters.AbstractReporter => reporter.displayPrompt() + case _ => () + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Settings.scala b/src/compiler/scala/reflect/makro/runtime/Settings.scala new file mode 100644 index 0000000000..32f7115db8 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Settings.scala @@ -0,0 +1,36 @@ +package scala.reflect.makro +package runtime + +trait Settings { + self: Context => + + def settings: List[String] = { + val optionName = mirror.settings.XmacroSettings.name + val settings = compilerSettings.find(opt => opt.startsWith(optionName)).map(opt => opt.substring(optionName.length + 1)).getOrElse("") + settings.split(",").toList + } + + def compilerSettings: List[String] = mirror.settings.recreateArgs + + def setCompilerSettings(options: String): this.type = + // todo. is not going to work with quoted arguments with embedded whitespaces + setCompilerSettings(options.split(" ").toList) + + def setCompilerSettings(options: List[String]): this.type = { + val settings = new tools.nsc.Settings(_ => ()) + // [Eugene] what settings should we exclude? + settings.copyInto(mirror.settings) + this + } + + def withCompilerSettings[T](options: String)(op: => T): T = + // todo. is not going to work with quoted arguments with embedded whitespaces + withCompilerSettings(options.split(" ").toList)(op) + + def withCompilerSettings[T](options: List[String])(op: => T): T = { + val old = options + setCompilerSettings(options) + try op + finally setCompilerSettings(old) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Symbols.scala b/src/compiler/scala/reflect/makro/runtime/Symbols.scala new file mode 100644 index 0000000000..552ad2a303 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Symbols.scala @@ -0,0 +1,8 @@ +package scala.reflect.makro +package runtime + +trait Symbols { + self: Context => + + def isLocatable(sym: Symbol) = sym.isLocatable +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Typers.scala b/src/compiler/scala/reflect/makro/runtime/Typers.scala new file mode 100644 index 0000000000..38e819746d --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Typers.scala @@ -0,0 +1,78 @@ +package scala.reflect.makro +package runtime + +trait Typers { + self: Context => + + val openMacros: List[Context] = this :: mirror.analyzer.openMacros + + val openImplicits: List[(Type, Tree)] = callsiteTyper.context.openImplicits + + def typeCheck(tree: Tree, pt: Type = mirror.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { + def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg) + trace("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) + val wrapper1 = if (!withImplicitViewsDisabled) (callsiteTyper.context.withImplicitsEnabled[Tree] _) else (callsiteTyper.context.withImplicitsDisabled[Tree] _) + val wrapper2 = if (!withMacrosDisabled) (callsiteTyper.context.withMacrosEnabled[Tree] _) else (callsiteTyper.context.withMacrosDisabled[Tree] _) + def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) + // if you get a "silent mode is not available past typer" here + // don't rush to change the typecheck not to use the silent method when the silent parameter is false + // typechecking uses silent anyways (e.g. in typedSelect), so you'll only waste your time + // I'd advise fixing the root cause: finding why the context is not set to report errors + // (also see reflect.runtime.ToolBoxes.typeCheckExpr for a workaround that might work for you) + wrapper(callsiteTyper.silent(_.typed(tree, mirror.analyzer.EXPRmode, pt)) match { + case mirror.analyzer.SilentResultValue(result) => + trace(result) + result + case error @ mirror.analyzer.SilentTypeError(_) => + trace(error.err.errMsg) + if (!silent) throw new mirror.TypeError(error.err.errPos, error.err.errMsg) + mirror.EmptyTree + }) + } + + def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { + def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg) + trace("inferring implicit value of type %s, macros = %s".format(pt, !withMacrosDisabled)) + import mirror.analyzer.SearchResult + val wrapper1 = if (!withMacrosDisabled) (callsiteTyper.context.withMacrosEnabled[SearchResult] _) else (callsiteTyper.context.withMacrosDisabled[SearchResult] _) + def wrapper (inference: => SearchResult) = wrapper1(inference) + val context = callsiteTyper.context.makeImplicit(true) + wrapper(mirror.analyzer.inferImplicit(mirror.EmptyTree, pt, true, false, context, !silent, pos)) match { + case failure if failure.tree.isEmpty => + trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits") + if (context.hasErrors) throw new mirror.TypeError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg) + mirror.EmptyTree + case success => + success.tree + } + } + + def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, reportAmbiguous: Boolean = true, pos: Position = enclosingPosition): Tree = { + def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg) + trace("inferring implicit view from %s to %s for %s, macros = %s, reportAmbiguous = %s".format(from, to, tree, !withMacrosDisabled, reportAmbiguous)) + import mirror.analyzer.SearchResult + val wrapper1 = if (!withMacrosDisabled) (callsiteTyper.context.withMacrosEnabled[SearchResult] _) else (callsiteTyper.context.withMacrosDisabled[SearchResult] _) + def wrapper (inference: => SearchResult) = wrapper1(inference) + val fun1 = mirror.definitions.FunctionClass(1) + val viewTpe = mirror.TypeRef(fun1.typeConstructor.prefix, fun1, List(from, to)) + val context = callsiteTyper.context.makeImplicit(reportAmbiguous) + wrapper(mirror.analyzer.inferImplicit(mirror.EmptyTree, viewTpe, reportAmbiguous, true, context, !silent, pos)) match { + case failure if failure.tree.isEmpty => + trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits") + if (context.hasErrors) throw new mirror.TypeError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg) + mirror.EmptyTree + case success => + success.tree + } + } + + type TypeError = mirror.TypeError + + object TypeError extends TypeErrorExtractor { + def unapply(error: TypeError): Option[(Position, String)] = Some((error.pos, error.msg)) + } + + def resetAllAttrs[T <: Tree](tree: T): T = mirror.resetAllAttrs(tree) + + def resetLocalAttrs[T <: Tree](tree: T): T = mirror.resetLocalAttrs(tree) +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Util.scala b/src/compiler/scala/reflect/makro/runtime/Util.scala new file mode 100644 index 0000000000..2671155721 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Util.scala @@ -0,0 +1,34 @@ +package scala.reflect.makro +package runtime + +trait Util { + self: Context => + + import mirror._ + + def literalNull = Expr[Null](Literal(Constant(null)))(TypeTag.Null) + + def literalUnit = Expr[Unit](Literal(Constant(())))(TypeTag.Unit) + + def literalTrue = Expr[Boolean](Literal(Constant(true)))(TypeTag.Boolean) + + def literalFalse = Expr[Boolean](Literal(Constant(false)))(TypeTag.Boolean) + + def literal(x: Boolean) = Expr[Boolean](Literal(Constant(x)))(TypeTag.Boolean) + + def literal(x: Byte) = Expr[Byte](Literal(Constant(x)))(TypeTag.Byte) + + def literal(x: Short) = Expr[Short](Literal(Constant(x)))(TypeTag.Short) + + def literal(x: Int) = Expr[Int](Literal(Constant(x)))(TypeTag.Int) + + def literal(x: Long) = Expr[Long](Literal(Constant(x)))(TypeTag.Long) + + def literal(x: Float) = Expr[Float](Literal(Constant(x)))(TypeTag.Float) + + def literal(x: Double) = Expr[Double](Literal(Constant(x)))(TypeTag.Double) + + def literal(x: String) = Expr[String](Literal(Constant(x)))(TypeTag.String) + + def literal(x: Char) = Expr[Char](Literal(Constant(x)))(TypeTag.Char) +} diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala new file mode 100644 index 0000000000..30c6c06c7b --- /dev/null +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -0,0 +1,63 @@ +package scala.reflect +package reify + +import scala.tools.nsc.Global + +trait Errors { + self: Reifier => + + import mirror._ + import definitions._ + + class ReificationError(var pos: Position, val msg: String) extends Throwable(msg) + class UnexpectedReificationError(val pos: Position, val msg: String, val cause: Throwable = null) extends Throwable(msg) + + lazy val defaultErrorPosition: Position = + mirror.analyzer.openMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication.pos).getOrElse(NoPosition) + + // expected errors: these can happen if the user casually writes whatever.reify(...) + // hence we don't crash here, but nicely report a typechecking error and bail out asap + + def CannotReifyReifeeThatHasTypeLocalToReifee(tree: Tree) = { + val msg = "implementation restriction: cannot reify block of type %s that involves a type declared inside the block being reified. consider casting the return value to a suitable type".format(tree.tpe) + throw new ReificationError(tree.pos, msg) + } + + def CannotReifyType(tpe: Type) = { + val msg = "implementation restriction: cannot reify type %s (%s)".format(tpe, tpe.kind) + throw new ReificationError(defaultErrorPosition, msg) + } + + def CannotReifySymbol(sym: Symbol) = { + val msg = "implementation restriction: cannot reify symbol %s (%s)".format(sym, sym.accurateKindString) + throw new ReificationError(defaultErrorPosition, msg) + } + + def CannotReifyConcreteTypeTagHavingUnresolvedTypeParameters(tpe: Type) = { + val msg = "cannot reify ConcreteTypeTag having unresolved type parameter %s".format(tpe) + throw new ReificationError(defaultErrorPosition, msg) + } + + // unexpected errors: these can never happen under normal conditions unless there's a bug in the compiler (or in a compiler plugin or in a macro) + // hence, we fail fast and loudly and don't care about being nice - in this situation noone will appreciate our quiet nicety + + def CannotReifyUntypedPrefix(prefix: Tree) = { + val msg = "internal error: untyped prefixes are not supported, consider typechecking the prefix before passing it to the reifier" + throw new UnexpectedReificationError(defaultErrorPosition, msg) + } + + def CannotReifyUntypedReifee(reifee: Any) = { + val msg = "internal error: untyped trees are not supported, consider typechecking the reifee before passing it to the reifier" + throw new UnexpectedReificationError(defaultErrorPosition, msg) + } + + def CannotReifyErroneousPrefix(prefix: Tree) = { + val msg = "internal error: erroneous prefixes are not supported, make sure that your prefix has typechecked successfully before passing it to the reifier" + throw new UnexpectedReificationError(defaultErrorPosition, msg) + } + + def CannotReifyErroneousReifee(reifee: Any) = { + val msg = "internal error: erroneous reifees are not supported, make sure that your reifee has typechecked successfully before passing it to the reifier" + throw new UnexpectedReificationError(defaultErrorPosition, msg) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/NodePrinters.scala b/src/compiler/scala/reflect/reify/NodePrinters.scala new file mode 100644 index 0000000000..eaca9a4968 --- /dev/null +++ b/src/compiler/scala/reflect/reify/NodePrinters.scala @@ -0,0 +1,111 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package reify + +import scala.Array.canBuildFrom +import scala.compat.Platform.EOL +import scala.tools.nsc.symtab.Flags +import scala.tools.nsc.Global + +trait NodePrinters { self: scala.tools.nsc.ast.NodePrinters => + + val global: Global + import global._ + + object reifiedNodeToString extends Function2[Tree, Tree, String] { + def apply(prefix: Tree, tree: Tree): String = { + import scala.reflect.api.Modifier + var modifierIsUsed = false + var flagsAreUsed = false + + // @PP: I fervently hope this is a test case or something, not anything being + // depended upon. Of more fragile code I cannot conceive. + // @Eugene: 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. + val (List(mirror), reified) = (for (line <- (tree.toString.split(EOL).toList drop 1 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 != "") { + modifierIsUsed = true + buf.append("Set(" + s_flags + ")") + } + + "Modifiers(" + buf.reverse.mkString(", ") + ")" + }) + s = """setInternalFlags\((\d+)L\)""".r.replaceAllIn(s, m => { + flagsAreUsed = true + val flags = m.group(1).toLong + val mods = Flags.modifiersOfFlags(flags) map (_.sourceString) + "setInternalFlags(flagsOfModifiers(List(" + mods.mkString(", ") + ")))" + }) + + s + }) splitAt 1 + + val printout = collection.mutable.ListBuffer(mirror); + printout += "import " + nme.MIRROR_SHORT + "._" + if (modifierIsUsed) printout += "import scala.reflect.api.Modifier._" + if (flagsAreUsed) printout += "import scala.reflect.internal.Flags._" + val body = reified dropWhile (_.startsWith("val")) + if (body.length > 0 && body(0).startsWith("Expr[")) { + if (reified(0) startsWith "val") { + printout += "val code = {" + printout ++= (reified map (" " + _)) + printout += "}" + printout += "mkToolBox().runExpr(code)" + } else { + printout += "val code = " + reified(0) + printout ++= reified drop 1 + printout += "mkToolBox().runExpr(code)" + } + try { + val prefix = Select(Select(Ident(definitions.ScalaPackage), newTermName("reflect")), newTermName("mirror")) + val tree1 = new global.Transformer { + override def transform(tree: Tree) = super.transform(tree match { + case Block(ValDef(_, mr, _, _) :: Nil, expr) if mr == nme.MIRROR_SHORT => transform(expr) + case Block(ValDef(_, mr, _, _) :: symbolTable, expr) if mr == nme.MIRROR_SHORT => transform(Block(symbolTable, expr)) + case Select(Ident(mr), name) if mr == nme.MIRROR_SHORT => Select(prefix, name) + case tree => tree + }) + }.transform(tree) + val stringified = mkToolBox().runExpr(tree1).toString + if (settings.Yreifydebug.value) printout += "*****************************" + printout += stringified + } catch { + case ex: Throwable => +// val realex = ReflectionUtils.unwrapThrowable(ex) +// val message = new java.io.StringWriter() +// realex.printStackTrace(new java.io.PrintWriter(message)) +// println(message) + } + } else { + printout ++= reified + } + printout mkString EOL + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/Phases.scala b/src/compiler/scala/reflect/reify/Phases.scala new file mode 100644 index 0000000000..49d5a45e8e --- /dev/null +++ b/src/compiler/scala/reflect/reify/Phases.scala @@ -0,0 +1,42 @@ +package scala.reflect +package reify + +import scala.reflect.reify.phases._ + +trait Phases extends Calculate + with Reshape + with Metalevels + with Reify { + + self: Reifier => + + import mirror._ + import definitions._ + + private var alreadyRun = false + + lazy val mkReificationPipeline: Tree => Tree = tree0 => { + assert(!alreadyRun, "reifier instance cannot be used more than once") + alreadyRun = true + + var tree = tree0 + + if (reifyDebug) println("[calculate phase]") + calculate.traverse(tree) + + if (reifyDebug) println("[reshape phase]") + tree = reshape.transform(tree) + + if (reifyDebug) println("[metalevels phase]") + tree = metalevels.transform(tree) + + if (reifyDebug) println("[interlude]") + if (reifyDebug) println("symbol table = " + (if (symbolTable.length == 0) "<empty>" else "")) + if (reifyDebug) symbolTable foreach (println(_)) + if (reifyDebug) println("reifee = " + (if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)) + if (reifyDebug) println("[reify phase]") + var result = reify(tree) + + result + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/Reifier.scala b/src/compiler/scala/reflect/reify/Reifier.scala new file mode 100644 index 0000000000..16c26734b2 --- /dev/null +++ b/src/compiler/scala/reflect/reify/Reifier.scala @@ -0,0 +1,154 @@ +package scala.reflect +package reify + +import scala.tools.nsc.Global + +/** Given a tree or a 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.api.Universe. + * + * @author Martin Odersky + * @version 2.10 + */ +abstract class Reifier extends Phases + with Errors { + + val mirror: Global + import mirror._ + import definitions._ + import treeInfo._ + + val typer: mirror.analyzer.Typer + val prefix: Tree + val reifee: Any + val dontSpliceAtTopLevel: Boolean + val requireConcreteTypeTag: Boolean + + /** + * For ``reifee'' and other reification parameters, generate a tree of the form + * + * { + * val $mr = <[ prefix ]> + * $mr.Expr[T](rtree) // if data is a Tree + * $mr.TypeTag[T](rtree) // if data is a Type + * } + * + * where + * + * - `prefix` is the tree that represents the universe + * the result will be bound to + * - `rtree` is code that generates `reifee` at runtime. + * - `T` is the type that corresponds to `data`. + * + * This is not a method, but a value to indicate the fact that Reifier instances are a one-off. + */ + lazy val reified: Tree = { + try { + // [Eugene] conventional way of doing this? + if (prefix exists (_.isErroneous)) CannotReifyErroneousPrefix(prefix) + if (prefix.tpe == null) CannotReifyUntypedPrefix(prefix) + + val rtree = reifee match { + case tree: Tree => + reifyTrace("reifying = ")(if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) + reifyTrace("reifee is located at: ")(tree.pos) + reifyTrace("prefix = ")(prefix) + // [Eugene] conventional way of doing this? + if (tree exists (_.isErroneous)) CannotReifyErroneousReifee(prefix) + if (tree.tpe == null) CannotReifyUntypedReifee(tree) + val pipeline = mkReificationPipeline + val rtree = pipeline(tree) + + // consider the following code snippet + // + // val x = reify { class C; new C } + // + // inferred type for x will be C + // but C ceases to exist after reification so this type is clearly incorrect + // however, reify is "just" a library function, so it cannot affect type inference + // + // hence we crash here even though the reification itself goes well + // fortunately, all that it takes to fix the error is to cast "new C" to Object + // so I'm not very much worried about introducing this restriction + if (tree.tpe exists (sub => sub.typeSymbol.isLocalToReifee)) + CannotReifyReifeeThatHasTypeLocalToReifee(tree) + + val manifestedType = typer.packedType(tree, NoSymbol) + val manifestedRtype = reifyType(manifestedType) + val tagModule = if (definitelyConcrete) ConcreteTypeTagModule else TypeTagModule + var typeTagCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(manifestedType))) + var exprCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), ExprModule.name), List(TypeTree(manifestedType))) + Apply(Apply(exprCtor, List(rtree)), List(Apply(typeTagCtor, List(manifestedRtype)))) + + case tpe: Type => + reifyTrace("reifying = ")(tpe.toString) + reifyTrace("prefix = ")(prefix) + val rtree = reify(tpe) + + val manifestedType = tpe + var tagModule = if (definitelyConcrete) ConcreteTypeTagModule else TypeTagModule + var ctor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(manifestedType))) + Apply(ctor, List(rtree)) + + case _ => + throw new Error("reifee %s of type %s is not supported".format(reifee, if (reifee == null) "null" else reifee.getClass.toString)) + } + + val mirrorAlias = ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(prefix), prefix) + val wrapped = Block(mirrorAlias :: symbolTable, rtree) + + // todo. why do we resetAllAttrs? + // + // typically we do some preprocessing before reification and + // the code emitted/moved around during preprocessing is very hard to typecheck, so we leave it as it is + // however this "as it is" sometimes doesn't make any sense + // + // ===example 1=== + // we move a freevar from a nested symbol table to a top-level symbol table, + // and then the reference to mr$ becomes screwed up, because nested symbol tables are already typechecked, + // so we have an mr$ symbol that points to the nested mr$ rather than to the top-level one. + // + // ===example 2=== + // we inline a freevar by replacing a reference to it, e.g. $mr.Apply($mr.Select($mr.Ident($mr.newTermName("$mr")), $mr.newTermName("Ident")), List($mr.Ident($mr.newTermName("free$x")))) + // with its original binding (e.g. $mr.Ident("x")) + // we'd love to typecheck the result, but we cannot do this easily, because $mr is external to this tree + // what's even worse, sometimes $mr can point to the top-level symbol table's $mr, which doesn't have any symbol/type yet - + // it's just a ValDef that will be emitted only after the reification is completed + // + // hence, the simplest solution is to erase all attrs so that invalid (as well as non-existent) bindings get rebound correctly + // this is ugly, but it's the best we can do + // + // todo. this is a common problem with non-trivial macros in our current macro system + // needs to be solved some day + // + // list of non-hygienic transformations: + // 1) local freetype inlining in Nested + // 2) external freevar moving in Nested + // 3) local freeterm inlining in Metalevels + // 4) trivial tree splice inlining in Reify (Trees.scala) + // 5) trivial type splice inlining in Reify (Types.scala) + val freevarBindings = symbolTable collect { case freedef @ FreeDef(_, _, binding, _) => binding.symbol } toSet + val untyped = resetAllAttrs(wrapped, leaveAlone = { + case ValDef(_, mr, _, _) if mr == nme.MIRROR_SHORT => true + case tree if freevarBindings contains tree.symbol => true + case _ => false + }) + + if (reifyCopypaste) { + if (reifyDebug) println("=============================") + println(reifiedNodeToString(prefix, untyped)) + if (reifyDebug) println("=============================") + } else { + reifyTrace("reified = ")(untyped) + } + + untyped + } catch { + case ex: ReificationError => + throw ex + case ex: UnexpectedReificationError => + throw ex + case ex: Throwable => + throw new UnexpectedReificationError(defaultErrorPosition, "reification crashed", ex) + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/Names.scala b/src/compiler/scala/reflect/reify/codegen/Names.scala new file mode 100644 index 0000000000..589f6355d0 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/Names.scala @@ -0,0 +1,15 @@ +package scala.reflect.reify +package codegen + +trait Names { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + def reifyName(name: Name) = { + val factory = if (name.isTypeName) nme.nmeNewTypeName else nme.nmeNewTermName + mirrorCall(factory, Literal(Constant(name.toString))) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/Positions.scala b/src/compiler/scala/reflect/reify/codegen/Positions.scala new file mode 100644 index 0000000000..ac9195ef31 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/Positions.scala @@ -0,0 +1,18 @@ +package scala.reflect.reify +package codegen + +trait Positions { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + // we do not reify positions because this inflates resulting trees, but doesn't buy as anything + // where would one use positions? right, in error messages + // but I can hardly imagine when one would need a position that points to the reified code + // usually reified trees are used to compose macro expansions or to be fed to the runtime compiler + // however both macros and toolboxes have their own means to report errors in synthetic trees + def reifyPosition(pos: Position): Tree = + reifyMirrorObject(NoPosition) +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/Symbols.scala b/src/compiler/scala/reflect/reify/codegen/Symbols.scala new file mode 100644 index 0000000000..3328f5e402 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/Symbols.scala @@ -0,0 +1,111 @@ +package scala.reflect.reify +package codegen + +trait Symbols { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + /** Reify a reference to a symbol */ + def reifySymRef(sym0: Symbol): Tree = { + assert(sym0 != null, "sym is null") + val sym = sym0.dealias + + if (sym == NoSymbol) + mirrorSelect(nme.NoSymbol) + else if (sym == RootPackage) + Select(mirrorSelect(nme.definitions), nme.RootPackage) + else if (sym == RootClass) + Select(mirrorSelect(nme.definitions), nme.RootClass) + else if (sym == EmptyPackage) + Select(mirrorSelect(nme.definitions), nme.EmptyPackage) + else if (sym == EmptyPackageClass) + Select(mirrorSelect(nme.definitions), nme.EmptyPackageClass) + else if (sym.isModuleClass) + Select(reify(sym.sourceModule), nme.moduleClass) + else if (sym.isLocatable) { + // [Eugene] am I doing this right? +// if (sym.isStaticOwner) { // no good for us, because it returns false for packages + if (sym.isStatic && (sym.isClass || sym.isModule)) { + val resolver = if (sym.isType) nme.staticClass else nme.staticModule + mirrorCall(resolver, reify(sym.fullName)) + } else { + if (reifyDebug) println("Locatable: %s (%s) owned by %s (%s) at %s".format(sym, sym.accurateKindString, sym.owner, sym.owner.accurateKindString, sym.owner.fullNameString)) + val rowner = reify(sym.owner) + val rname = reify(sym.name.toString) + if (sym.isType) + mirrorCall(nme.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(nme.selectOverloadedMethod, rowner, rname, reify(index)) + } else + mirrorCall(nme.selectTerm, rowner, rname) + } + } else { + // todo. make sure that free methods and free local defs work correctly + if (sym.isTerm) { + if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym) + reifyFreeTerm(sym, Ident(sym)) + } else { + if (reifyDebug) println("Free type: " + sym) + reifyFreeType(sym, Ident(sym)) + } + } + } + + def reifyFreeTerm(sym: Symbol, value: Tree): Tree = + locallyReified get sym match { + case Some(reified) => + reified + case None => + if (sym.isCapturedVariable) { + assert(value.isInstanceOf[Ident], showRaw(value)) + val capturedTpe = capturedVariableType(sym) + val capturedValue = referenceCapturedVariable(sym) + locallyReify(sym, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(capturedTpe), capturedValue, reify(origin(sym)))) + } else { + locallyReify(sym, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(sym.tpe), value, reify(origin(sym)))) + } + } + + def reifyFreeType(sym: Symbol, value: Tree): Tree = + locallyReified get sym match { + case Some(reified) => + reified + case None => + val phantomTypeTag = Apply(TypeApply(Select(Ident(nme.MIRROR_SHORT), nme.TypeTag), List(value)), List(Literal(Constant(null)))) + // todo. implement info reification for free types: type bounds, HK-arity, whatever else that can be useful + locallyReify(sym, mirrorCall(nme.newFreeType, reify(sym.name.toString), reify(sym.info), phantomTypeTag, reify(origin(sym)))) + } + + import scala.collection.mutable._ + private val localReifications = ArrayBuffer[ValDef]() + private val locallyReified = Map[Symbol, Tree]() + def symbolTable: List[ValDef] = localReifications.toList + def symbolTable_=(newSymbolTable: List[ValDef]): Unit = { + localReifications.clear() + locallyReified.clear() + newSymbolTable foreach { + case freedef @ FreeDef(_, name, binding, _) => + if (!(locallyReified contains binding.symbol)) { + localReifications += freedef + locallyReified(binding.symbol) = Ident(name) + } + } + } + + private def locallyReify(sym: Symbol, reificode: => Tree): Tree = { + val reified = reificode + val Apply(Select(_, flavor), _) = reified + // [Eugene] name clashes are impossible, right? + var name = newTermName(nme.MIRROR_FREE_PREFIX + sym.name) + if (flavor == nme.newFreeTerm && sym.isType) name = name.append(nme.MIRROR_FREE_THIS_SUFFIX); + // todo. also reify annotations for free vars + localReifications += ValDef(NoMods, name, TypeTree(), reified) + locallyReified(sym) = Ident(name) + locallyReified(sym) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/Trees.scala b/src/compiler/scala/reflect/reify/codegen/Trees.scala new file mode 100644 index 0000000000..22f42aea49 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/Trees.scala @@ -0,0 +1,220 @@ +package scala.reflect.reify +package codegen + +trait Trees { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + /** + * Reify a tree. + * For internal use only, use ``reified'' instead. + */ + def reifyTree(tree: Tree): Tree = { + assert(tree != null, "tree is null") + + if (tree.isErroneous) + CannotReifyErroneousReifee(tree) + + val splicedTree = spliceTree(tree) + if (splicedTree != EmptyTree) + return splicedTree + + // the idea behind the new reincarnation of reifier is a simple maxim: + // + // never call ``reifyType'' to reify a tree + // + // this works because the stuff we are reifying was once represented with trees only + // and lexical scope information can be fully captured by reifying symbols + // + // to enable this idyll, we work hard in the ``Reshape'' phase + // which replaces all types with equivalent trees and works around non-idempotencies of the typechecker + // + // why bother? because this brings method to the madness + // the first prototype of reification reified all types and symbols for all trees => this quickly became unyieldy + // the second prototype reified external types, but avoided reifying local ones => this created an ugly irregularity + // current approach is uniform and compact + var rtree = tree match { + case mirror.EmptyTree => + reifyMirrorObject(EmptyTree) + case mirror.emptyValDef => + mirrorSelect(nme.emptyValDef) + case FreeDef(_, _, _, _) => + reifyNestedFreeDef(tree) + case FreeRef(_, _) => + reifyNestedFreeRef(tree) + case BoundTerm(tree) => + reifyBoundTerm(tree) + case BoundType(tree) => + reifyBoundType(tree) + case NestedExpr(_, _, _) => + reifyNestedExpr(tree) + case Literal(const @ Constant(_)) => + mirrorCall(nme.Literal, reifyProduct(const)) + case Import(expr, selectors) => + mirrorCall(nme.Import, reify(expr), mkList(selectors map reifyProduct)) + case _ => + reifyProduct(tree) + } + + rtree + } + + def reifyModifiers(m: mirror.Modifiers) = + mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations)) + + private def spliceTree(tree: Tree): Tree = { + tree match { + case EvalSplice(splicee) => + if (reifyDebug) println("splicing eval " + tree) + + // see ``Metalevels'' for more info about metalevel breaches + // and about how we deal with splices that contain them + if (splicee exists (sub => sub.hasSymbol && sub.symbol != NoSymbol && sub.symbol.metalevel > 0)) { + if (reifyDebug) println("splicing has failed: cannot splice when facing a metalevel breach") + EmptyTree + } else { + if (reifyDebug) println("splicing has succeeded") + var splice = Select(splicee, nme.tree) + splice match { + case InlinedTreeSplice(_, inlinedSymbolTable, tree, _) => + if (reifyDebug) println("inlining the splicee") + // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels'' + inlinedSymbolTable foreach { case freedef @ FreeDef(_, _, binding, _) => assert(!binding.symbol.isLocalToReifee, freedef) } + symbolTable ++= inlinedSymbolTable + tree + case tree => + // we need to preserve types of exprs, because oftentimes they cannot be inferred later + // this circumvents regular reification scheme, therefore we go the extra mile here + new Transformer { + override def transform(tree: Tree) = super.transform(tree match { + case NestedExpr(factory, tree, typetag) => + val typedFactory = TypeApply(factory, List(TypeTree(typetag.tpe.typeArgs(0)))) + Apply(Apply(typedFactory, List(tree)), List(typetag)) + case _ => + tree + }) + }.transform(tree) + } + } + case ValueSplice(splicee) => + // todo. implement this + ??? + case _ => + EmptyTree + } + } + + private def reifyBoundTerm(tree: Tree): Tree = tree match { + case tree @ This(_) if tree.symbol == NoSymbol => + throw new Error("unexpected: bound term that doesn't have a symbol: " + showRaw(tree)) + case tree @ This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass && !tree.symbol.isLocalToReifee => + val sym = tree.symbol + if (reifyDebug) println("This for %s, reified as freeVar".format(sym)) + if (reifyDebug) println("Free: " + sym) + mirrorCall(nme.Ident, reifyFreeTerm(sym, This(sym))) + case tree @ This(_) if !tree.symbol.isLocalToReifee => + if (reifyDebug) println("This for %s, reified as This".format(tree.symbol)) + mirrorCall(nme.This, reify(tree.symbol)) + case tree @ This(_) if tree.symbol.isLocalToReifee => + mirrorCall(nme.This, reify(tree.qual)) + case tree @ Ident(_) if tree.symbol == NoSymbol => + // this sometimes happens, e.g. for binds that don't have a body + // or for untyped code generated during previous phases + // (see a comment in Reifiers about the latter, starting with "why do we resetAllAttrs?") + mirrorCall(nme.Ident, reify(tree.name)) + case tree @ Ident(_) if !tree.symbol.isLocalToReifee => + if (tree.symbol.isVariable && tree.symbol.owner.isTerm) { + captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reification here. + mirrorCall(nme.Select, mirrorCall(nme.Ident, reify(tree.symbol)), reify(nme.elem)) + } else { + mirrorCall(nme.Ident, reify(tree.symbol)) + } + case tree @ Ident(_) if tree.symbol.isLocalToReifee => + mirrorCall(nme.Ident, reify(tree.name)) + case _ => + throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + } + + private def reifyBoundType(tree: Tree): Tree = { + def reifyBoundType(tree: Tree): Tree = { + if (tree.tpe == null) + throw new Error("unexpected: bound type that doesn't have a tpe: " + showRaw(tree)) + + if (tree.symbol.isLocalToReifee) + reifyProduct(tree) + else { + val sym0 = tree.symbol + val sym = sym0.dealias + val tpe0 = tree.tpe + val tpe = tpe0.dealias + if (reifyDebug) println("reifying bound type %s (underlying type is %s, dealiased is %s)".format(sym0, tpe0, tpe)) + + if (eligibleForSplicing(tpe)) { + val spliced = spliceType(tpe) + if (spliced == EmptyTree) { + if (reifyDebug) println("splicing failed: reify as is") + mirrorCall(nme.TypeTree, reifyType(tpe)) + } else { + spliced match { + case TypeRefToFreeType(freeType) => + if (reifyDebug) println("splicing returned a free type: " + freeType) + Ident(freeType) + case _ => + if (reifyDebug) println("splicing succeeded: " + spliced) + mirrorCall(nme.TypeTree, spliced) + } + } + } else { + if (sym.isLocatable) { + if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym)) + mirrorCall(nme.Ident, reify(sym)) + } else { + if (reifyDebug) println("tpe is an alias, but not a locatable: reify as TypeTree(%s)".format(tpe)) + mirrorCall(nme.TypeTree, reifyType(tpe)) + } + } + } + } + + tree match { + case Select(_, _) => + reifyBoundType(tree) + case SelectFromTypeTree(_, _) => + reifyBoundType(tree) + case Ident(_) => + reifyBoundType(tree) + case _ => + throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + } + } + + private def reifyNestedFreeDef(tree: Tree): Tree = { + if (reifyDebug) println("nested free def: %s".format(showRaw(tree))) + reifyProduct(tree) + } + + private def reifyNestedFreeRef(tree: Tree): Tree = tree match { + case Apply(Select(mrRef @ Ident(_), ident), List(Ident(name: TermName))) if ident == nme.Ident && name.startsWith(nme.MIRROR_FREE_PREFIX) => + if (reifyDebug) println("nested free ref: %s".format(showRaw(tree))) + reifyProduct(tree) + case _ => + throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + } + + private def reifyNestedExpr(tree: Tree): Tree = tree match { + case NestedExpr(factory, tree, typetag) => + // we need to preserve types of exprs, because oftentimes they cannot be inferred later + // this circumvents regular reification scheme, therefore we go through this crazy dance + if (reifyDebug) println("nested expr: %s".format(showRaw(tree))) + val rtype = mirrorCall(nme.TypeTree, reify(typetag.tpe.typeArgs(0))) + val rfactory = mirrorCall(nme.TypeApply, reify(factory), mkList(List(rtype))) + val rexpr = mirrorCall(nme.Apply, rfactory, reify(List(tree))) + val rwrapped = mirrorCall(nme.Apply, rexpr, reify(List(typetag))) + rwrapped + case _ => + throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/Types.scala b/src/compiler/scala/reflect/reify/codegen/Types.scala new file mode 100644 index 0000000000..9bc113e8a4 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/Types.scala @@ -0,0 +1,226 @@ +package scala.reflect.reify +package codegen + +trait Types { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + /** + * Reify a type. + * For internal use only, use ``reified'' instead. + */ + def reifyType(tpe0: Type): Tree = { + assert(tpe0 != null, "tpe is null") + val tpe = tpe0.dealias + + if (tpe.isErroneous) + CannotReifyErroneousReifee(tpe) + if (tpe.isLocalToReifee) + CannotReifyType(tpe) + + // [Eugene] how do I check that the substitution is legal w.r.t tpe.info? + val spliced = spliceType(tpe) + if (spliced != EmptyTree) + return spliced + + val tsym = tpe.typeSymbol + if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic) + Select(reify(tpe.typeSymbol), nme.asTypeConstructor) + else tpe match { + case tpe @ NoType => + reifyMirrorObject(tpe) + case tpe @ NoPrefix => + reifyMirrorObject(tpe) + case tpe @ ThisType(root) if root == RootClass => + mirrorSelect("definitions.RootClass.thisPrefix") + case tpe @ ThisType(empty) if empty == EmptyPackageClass => + mirrorSelect("definitions.EmptyPackageClass.thisPrefix") + case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic => + mirrorCall(nme.thisModuleType, reify(clazz.fullName)) + case tpe @ ThisType(_) => + reifyProduct(tpe) + case tpe @ SuperType(thistpe, supertpe) => + reifyProduct(tpe) + case tpe @ SingleType(pre, sym) => + reifyProduct(tpe) + case tpe @ ConstantType(value) => + mirrorFactoryCall(nme.ConstantType, reifyProduct(value)) + case tpe @ TypeRef(pre, sym, args) => + reifyProduct(tpe) + case tpe @ TypeBounds(lo, hi) => + reifyProduct(tpe) + case tpe @ NullaryMethodType(restpe) => + reifyProduct(tpe) + case tpe @ AnnotatedType(anns, underlying, selfsym) => +// reifyAnnotatedType(tpe) + CannotReifyType(tpe) + case _ => +// reifyToughType(tpe) + CannotReifyType(tpe) + } + } + + /** An obscure flag necessary for implicit TypeTag generation */ + private var spliceTypesEnabled = !dontSpliceAtTopLevel + + /** Keeps track of whether this reification contains abstract type parameters */ + var maybeConcrete = true + var definitelyConcrete = true + + def eligibleForSplicing(tpe: Type): Boolean = { + // [Eugene] is this comprehensive? + // the only thingies that we want to splice are: 1) type parameters, 2) type members + // this check seems to cover them all, right? + tpe.isInstanceOf[TypeRef] && tpe.typeSymbol.isAbstractType + } + + private type SpliceCacheKey = (Symbol, Symbol) + private lazy val spliceCache: collection.mutable.Map[SpliceCacheKey, Tree] = { + val cache = analyzer.perRunMacroCache.getOrElseUpdate(MacroContextReify, collection.mutable.Map[Any, Any]()) + cache.getOrElseUpdate("spliceCache", collection.mutable.Map[SpliceCacheKey, Tree]()).asInstanceOf[collection.mutable.Map[SpliceCacheKey, Tree]] + } + + def spliceType(tpe: Type): Tree = { + if (eligibleForSplicing(tpe)) { + if (reifyDebug) println("splicing " + tpe) + + if (spliceTypesEnabled) { + var tagClass = if (requireConcreteTypeTag) ConcreteTypeTagClass else TypeTagClass + val tagTpe = singleType(prefix.tpe, prefix.tpe member tagClass.name) + + // [Eugene] this should be enough for an abstract type, right? + val key = (tagClass, tpe.typeSymbol) + if (reifyDebug && spliceCache.contains(key)) println("cache hit: " + spliceCache(key)) + val result = spliceCache.getOrElseUpdate(key, { + // if this fails, it might produce the dreaded "erroneous or inaccessible type" error + // to find out the whereabouts of the error run scalac with -Ydebug + if (reifyDebug) println("launching implicit search for %s.%s[%s]".format(prefix, tagClass.name, tpe)) + val positionBearer = mirror.analyzer.openMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication).getOrElse(EmptyTree).asInstanceOf[Tree] + typer.resolveTypeTag(positionBearer, prefix.tpe, tpe, requireConcreteTypeTag) match { + case failure if failure.isEmpty => + if (reifyDebug) println("implicit search was fruitless") + definitelyConcrete &= false + maybeConcrete &= false + EmptyTree + case success => + if (reifyDebug) println("implicit search has produced a result: " + success) + definitelyConcrete |= requireConcreteTypeTag + maybeConcrete |= true + var splice = Select(success, nme.tpe) + splice match { + case InlinedTypeSplice(_, inlinedSymbolTable, tpe) => + // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels'' + inlinedSymbolTable foreach { case freedef @ FreeDef(_, _, binding, _) => assert(!binding.symbol.isLocalToReifee, freedef) } + symbolTable ++= inlinedSymbolTable + reifyTrace("inlined the splicee: ")(tpe) + case tpe => + tpe + } + } + }) + if (result != EmptyTree) return result.duplicate + } else { + if (reifyDebug) println("splicing has been cancelled: spliceTypesEnabled = false") + } + + if (requireConcreteTypeTag) + CannotReifyConcreteTypeTagHavingUnresolvedTypeParameters(tpe) + } + + spliceTypesEnabled = true + EmptyTree + } + + // yet another thingie disabled for simplicity + // in principle, we could retain and reify AnnotatedTypes + // but that'd require reifying every type and symbol inside ann.args + // however, since we've given up on tough types for the moment, the former would be problematic +// private def reifyAnnotatedType(tpe: AnnotatedType): Tree = { +// // ``Reshaper'' transforms annotation infos from symbols back into Modifier.annotations, which are trees +// // so the only place on Earth that can lead to reification of AnnotationInfos is the Ay Tee Land +// // therefore this function is as local as possible, don't move it out of this scope +// def reifyAnnotationInfo(ann: AnnotationInfo): Tree = { +// val reifiedArgs = ann.args map { arg => +// val saved1 = reifyTreeSymbols +// val saved2 = reifyTreeTypes +// +// 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. +// reifyTreeSymbols = true +// reifyTreeTypes = true +// +// // todo. every AnnotationInfo is an island, entire of itself +// // no regular Traverser or Transformer can reach it +// // hence we need to run its contents through the entire reification pipeline +// // e.g. to apply reshaping or to check metalevels +// reify(arg) +// } finally { +// reifyTreeSymbols = saved1 +// reifyTreeTypes = saved2 +// } +// } +// +// def reifyClassfileAnnotArg(arg: ClassfileAnnotArg): Tree = arg match { +// case LiteralAnnotArg(const) => +// mirrorFactoryCall(nme.LiteralAnnotArg, reifyProduct(const)) +// case ArrayAnnotArg(args) => +// mirrorFactoryCall(nme.ArrayAnnotArg, scalaFactoryCall(nme.Array, args map reifyClassfileAnnotArg: _*)) +// case NestedAnnotArg(ann) => +// mirrorFactoryCall(nme.NestedAnnotArg, reifyAnnotationInfo(ann)) +// } +// +// // if you reify originals of anns, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important +// val reifiedAssocs = ann.assocs map (assoc => scalaFactoryCall(nme.Tuple2, reify(assoc._1), reifyClassfileAnnotArg(assoc._2))) +// mirrorFactoryCall(nme.AnnotationInfo, reify(ann.atp), mkList(reifiedArgs), mkList(reifiedAssocs)) +// } +// +// val AnnotatedType(anns, underlying, selfsym) = tpe +// mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym)) +// } + + // previous solution to reifying tough types involved creating dummy symbols (see ``registerReifiableSymbol'' calls below) + // however such symbols lost all the connections with their origins and became almost useless, except for typechecking + // hence this approach was replaced by less powerful, but more principled one based on ``reifyFreeType'' + // it's possible that later on we will revise and revive ``reifyToughType'', but for now it's disabled under an implementation restriction +// /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */ +// // This is the uncharted territory in the reifier +// private def reifyToughType(tpe: Type): Tree = { +// if (reifyDebug) println("tough type: %s (%s)".format(tpe, tpe.kind)) +// +// def reifyScope(scope: Scope): Tree = { +// scope foreach registerReifiableSymbol +// mirrorCall(nme.newScopeWith, scope.toList map reify: _*) +// } +// +// tpe match { +// case tpe @ RefinedType(parents, decls) => +// registerReifiableSymbol(tpe.typeSymbol) +// mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) +// case tpe @ ExistentialType(tparams, underlying) => +// tparams foreach registerReifiableSymbol +// mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) +// case tpe @ ClassInfoType(parents, decls, clazz) => +// registerReifiableSymbol(clazz) +// mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) +// case tpe @ MethodType(params, restpe) => +// params foreach registerReifiableSymbol +// mirrorFactoryCall(tpe, reify(params), reify(restpe)) +// case tpe @ PolyType(tparams, underlying) => +// tparams foreach registerReifiableSymbol +// mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) +// case _ => +// throw new Error("internal error: %s (%s) is not supported".format(tpe, tpe.kind)) +// } +// } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/Util.scala b/src/compiler/scala/reflect/reify/codegen/Util.scala new file mode 100644 index 0000000000..bb369a1adb --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/Util.scala @@ -0,0 +1,112 @@ +package scala.reflect.reify +package codegen + +trait Util { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + val reifyDebug = settings.Yreifydebug.value + val reifyCopypaste = settings.Yreifycopypaste.value + val reifyTrace = scala.tools.nsc.util.trace when reifyDebug + object reifiedNodePrinters extends { val global: mirror.type = mirror } with tools.nsc.ast.NodePrinters with NodePrinters + val reifiedNodeToString = reifiedNodePrinters.reifiedNodeToString + + def reifyList(xs: List[Any]): Tree = + mkList(xs map reify) + + def reifyProduct(x: Product): Tree = + reifyProduct(x.productPrefix, x.productIterator.toList) + + def reifyProduct(prefix: String, elements: List[Any]): Tree = { + // reflection would be more robust, but, hey, this is a hot path + if (prefix.startsWith("Tuple")) scalaFactoryCall(prefix, (elements map reify).toList: _*) + else mirrorCall(prefix, (elements map reify): _*) + } + + // helper functions + + /** Reify a case object defined in Mirror */ + def reifyMirrorObject(name: String): Tree = + mirrorSelect(name) + + def reifyMirrorObject(x: Product): Tree = + reifyMirrorObject(x.productPrefix) + + def call(fname: String, args: Tree*): Tree = + Apply(termPath(fname), args.toList) + + def mirrorSelect(name: String): Tree = + termPath(nme.MIRROR_PREFIX + name) + + def mirrorCall(name: TermName, args: Tree*): Tree = + call("" + (nme.MIRROR_PREFIX append name), args: _*) + + def mirrorCall(name: String, args: Tree*): Tree = + call(nme.MIRROR_PREFIX + name, args: _*) + + def mirrorFactoryCall(value: Product, args: Tree*): Tree = + mirrorFactoryCall(value.productPrefix, args: _*) + + def mirrorFactoryCall(prefix: String, args: Tree*): Tree = + mirrorCall(prefix, args: _*) + + def scalaFactoryCall(name: String, args: Tree*): Tree = + call("scala." + name + ".apply", args: _*) + + def mkList(args: List[Tree]): Tree = + scalaFactoryCall("collection.immutable.List", args: _*) + + /** + * An (unreified) path that refers to definition with given fully qualified name + * @param mkName Creator for last portion of name (either TermName or TypeName) + */ + 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 */ + def termPath(fullname: String): Tree = path(fullname, newTermName) + + /** An (unreified) path that refers to type definition with given fully qualified name */ + def typePath(fullname: String): Tree = path(fullname, newTypeName) + + def isTough(tpe: Type) = { + def isTough(tpe: Type) = tpe match { + case _: RefinedType => true + case _: ExistentialType => true + case _: ClassInfoType => true + case _: MethodType => true + case _: PolyType => true + case _ => false + } + + tpe != null && (tpe exists isTough) + } + + def isAnnotated(tpe: Type) = { + def isAnnotated(tpe: Type) = tpe match { + case _: AnnotatedType => true + case _ => false + } + + tpe != null && (tpe exists isAnnotated) + } + + def origin(sym: Symbol) = { + var origin = "" + if (sym.owner != NoSymbol) origin += "defined by %s".format(sym.owner.name) + if (sym.pos != NoPosition) origin += " in %s:%s:%s".format(sym.pos.source.file.name, sym.pos.line, sym.pos.column) + if (origin == "") origin = "of unknown origin" + origin + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/package.scala b/src/compiler/scala/reflect/reify/package.scala new file mode 100644 index 0000000000..85cf92fe2f --- /dev/null +++ b/src/compiler/scala/reflect/reify/package.scala @@ -0,0 +1,22 @@ +package scala.reflect + +import scala.tools.nsc.Global + +package object reify { + def mkReifier(global: Global)(typer: global.analyzer.Typer, prefix: global.Tree, reifee: Any, dontSpliceAtTopLevel: Boolean = false, requireConcreteTypeTag: Boolean = false): Reifier { val mirror: global.type } = { + val typer1: typer.type = typer + val prefix1: prefix.type = prefix + val reifee1 = reifee + val dontSpliceAtTopLevel1 = dontSpliceAtTopLevel + val requireConcreteTypeTag1 = requireConcreteTypeTag + + new { + val mirror: global.type = global + val typer = typer1 + val prefix = prefix1 + val reifee = reifee1 + val dontSpliceAtTopLevel = dontSpliceAtTopLevel1 + val requireConcreteTypeTag = requireConcreteTypeTag1 + } with Reifier + } +} diff --git a/src/compiler/scala/reflect/reify/phases/Calculate.scala b/src/compiler/scala/reflect/reify/phases/Calculate.scala new file mode 100644 index 0000000000..e4f3fce407 --- /dev/null +++ b/src/compiler/scala/reflect/reify/phases/Calculate.scala @@ -0,0 +1,59 @@ +package scala.reflect.reify +package phases + +trait Calculate { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + implicit class RichSymbol(sym: Symbol) { + def metalevel: Int = { assert(sym != NoSymbol); localSymbols.getOrElse(sym, 0) } + def isLocalToReifee = (localSymbols contains sym) // [Eugene] how do I account for local skolems? + } + + implicit class RichType(tpe: Type) { + def isLocalToReifee = tpe != null && (tpe exists (tp => (localSymbols contains tp.typeSymbol) || (localSymbols contains tp.termSymbol))) + } + + private var localSymbols = collection.mutable.Map[Symbol, Int]() // set of all symbols that are local to the tree to be reified + private def registerLocalSymbol(sym: Symbol, metalevel: Int): Unit = + if (sym != null && sym != NoSymbol) { + if (localSymbols contains sym) + assert(localSymbols(sym) == metalevel, "metalevel mismatch: expected %s, actual %s".format(localSymbols(sym), metalevel)) + localSymbols(sym) = metalevel + } + + /** + * Merely traverses the reifiee and records local symbols along with their metalevels. + */ + val calculate = new Traverser { + // see the explanation of metalevels in ``Metalevels'' + var currMetalevel = 1 + + override def traverse(tree: Tree): Unit = tree match { + case TreeSplice(_) => + currMetalevel -= 1 + try super.traverse(tree) + finally currMetalevel += 1 + 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)))) + registerLocalSymbol(tree.symbol, currMetalevel) + + 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) + registerLocalSymbol(related, currMetalevel) + } + super.traverse(tree) + case _ => + super.traverse(tree) + } + } +} diff --git a/src/compiler/scala/reflect/reify/phases/Metalevels.scala b/src/compiler/scala/reflect/reify/phases/Metalevels.scala new file mode 100644 index 0000000000..a329a1043d --- /dev/null +++ b/src/compiler/scala/reflect/reify/phases/Metalevels.scala @@ -0,0 +1,148 @@ +package scala.reflect.reify +package phases + +trait Metalevels { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + /** + * Makes sense of cross-stage bindings. + * + * ================ + * + * Analysis of cross-stage bindings becomes convenient if we introduce the notion of metalevels. + * Metalevel of a tree is a number that gets incremented every time you reify something and gets decremented when you splice something. + * Metalevel of a symbol is equal to the metalevel of its definition. + * + * Example 1. Consider the following snippet: + * + * reify { + * val x = 2 // metalevel of symbol x is 1, because it's declared inside reify + * val y = reify{x} // metalevel of symbol y is 1, because it's declared inside reify + * // metalevel of Ident(x) is 2, because it's inside two reifies + * y.eval // metalevel of Ident(y) is 0, because it's inside a designator of a splice + * } + * + * Cross-stage bindings are introduced when symbol.metalevel != curr_metalevel. + * Both bindings introduced in Example 1 are cross-stage. + * + * Depending on what side of the inequality is greater, the following situations might occur: + * + * 1) symbol.metalevel < curr_metalevel. In this case reifier will generate a free variable + * that captures both the name of the symbol (to be compiled successfully) and its value (to be run successfully). + * For example, x in Example 1 will be reified as follows: Ident(newFreeVar("x", IntClass.tpe, x)) + * + * 2) symbol.metalevel > curr_metalevel. This leads to a metalevel breach that violates intuitive perception of splicing. + * As defined in macro spec, splicing takes a tree and inserts it into another tree - as simple as that. + * However, how exactly do we do that in the case of y.eval? In this very scenario we can use dataflow analysis and inline it, + * but what if y were a var, and what if it were calculated randomly at runtime? + * + * This question has a genuinely simple answer. Sure, we cannot resolve such splices statically (i.e. during macro expansion of ``reify''), + * but now we have runtime toolboxes, so noone stops us from picking up that reified tree and evaluating it at runtime + * (in fact, this is something that ``Expr.eval'' and ``Expr.value'' do transparently). + * + * This is akin to early vs late binding dilemma. + * The prior is faster, plus, the latter (implemented with reflection) might not work because of visibility issues or might be not available on all platforms. + * But the latter still has its uses, so I'm allowing metalevel breaches, but introducing the -Xlog-runtime-evals to log them. + * + * ================ + * + * As we can see, the only problem is the fact that lhs'es of eval can be code blocks that can capture variables from the outside. + * Code inside the lhs of an eval is not reified, while the code from the enclosing reify is. + * + * Hence some bindings become cross-stage, which is not bad per se (in fact, some cross-stage bindings have sane semantics, as in the example above). + * However this affects freevars, since they are delicate inter-dimensional beings that refer to both current and next planes of existence. + * When splicing tears the fabric of the reality apart, some freevars have to go single-dimensional to retain their sanity. + * + * Example 2. Consider the following snippet: + * + * reify { + * val x = 2 + * reify{x}.eval + * } + * + * Since the result of the inner reify is wrapped in an eval, it won't be reified + * together with the other parts of the outer reify, but will be inserted into that result verbatim. + * + * The inner reify produces an Expr[Int] that wraps Ident(freeVar("x", IntClass.tpe, x)). + * However the freevar the reification points to will vanish when the compiler processes the outer reify. + * That's why we need to replace that freevar with a regular symbol that will point to reified x. + * + * Example 3. Consider the following fragment: + * + * reify { + * val x = 2 + * val y = reify{x} + * y.eval + * } + * + * In this case the inner reify doesn't appear next to eval, so it will be reified together with x. + * This means that no special processing is needed here. + * + * Example 4. Consider the following fragment: + * + * reify { + * val x = 2 + * { + * val y = 2 + * val z = reify{reify{x + y}} + * z.eval + * }.eval + * } + * + * The reasoning from Example 2 still holds here - we do need to inline the freevar that refers to x. + * However, we must not touch anything inside the eval'd block, because it's not getting reified. + */ + var metalevels = new Transformer { + var insideSplice = false + var freedefsToInline = collection.mutable.Map[String, ValDef]() + + def withinSplice[T](op: => T) = { + val old = insideSplice + insideSplice = true + try op + finally insideSplice = old + } + + // Q: here we deal with all sorts of reified trees. what about ReifiedType(_, _, _, _)? + // A: nothing. reified trees give us problems because they sometimes create dimensional rifts as described above + // to the contrast, reified types (i.e. synthetic typetags materialized by Implicits.scala) always stay on the same metalevel as their enclosing code + override def transform(tree: Tree): Tree = tree match { + case InlineableTreeSplice(splicee, inlinedSymbolTable, _, _, flavor) => + if (reifyDebug) println("entering inlineable splice: " + splicee) + val Block(mrDef :: symbolTable, expr) = splicee + // [Eugene] how to express the fact that a scrutinee is both of some type and matches an extractor? + val freedefsToInline = symbolTable collect { case freedef @ FreeTermDef(_, _, binding, _) if binding.symbol.isLocalToReifee => freedef.asInstanceOf[ValDef] } + freedefsToInline foreach (vdef => this.freedefsToInline(vdef.name) = vdef) + val symbolTable1 = symbolTable diff freedefsToInline + val tree1 = Select(Block(mrDef :: symbolTable1, expr), flavor) + if (reifyDebug) println("trimmed %s inlineable free defs from its symbol table: %s".format(freedefsToInline.length, freedefsToInline map (_.name) mkString(", "))) + withinSplice { super.transform(tree1) } + case TreeSplice(splicee) => + if (reifyDebug) println("entering splice: " + splicee) + val hasBreaches = splicee exists (_.symbol.metalevel > 0) + if (!insideSplice && hasBreaches) { + if (settings.logRuntimeSplices.value) reporter.echo(tree.pos, "this splice cannot be resolved statically") + if (reifyDebug) println("metalevel breach in %s: %s".format(tree, (splicee filter (_.symbol.metalevel > 0) map (_.symbol) distinct) mkString ", ")) + } + withinSplice { super.transform(tree) } + // todo. also inline usages of ``freedefsToInline'' in the symbolTable itself + // e.g. a free$Foo can well use free$x, if Foo is path-dependent w.r.t x + // FreeRef(_, _) check won't work, because metalevels of symbol table and body are different, hence, freerefs in symbol table look different from freerefs in body + // todo. also perform garbage collection on local symbols + // so that local symbols used only in type signatures of free vars get removed + case FreeRef(mr, name) if freedefsToInline contains name => + if (reifyDebug) println("inlineable free ref: %s in %s".format(name, showRaw(tree))) + val freedef @ FreeDef(_, _, binding, _) = freedefsToInline(name) + if (reifyDebug) println("related definition: %s".format(showRaw(freedef))) + val inlined = reify(binding) + if (reifyDebug) println("verdict: inlined as %s".format(showRaw(inlined))) + inlined + case _ => + super.transform(tree) + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala new file mode 100644 index 0000000000..f6d6423605 --- /dev/null +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -0,0 +1,42 @@ +package scala.reflect.reify +package phases + +import scala.runtime.ScalaRunTime.isAnyVal +import scala.runtime.ScalaRunTime.isTuple +import scala.reflect.reify.codegen._ + +trait Reify extends Symbols + with Types + with Names + with Trees + with Positions + with Util { + + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + /** + * Reifies any supported value. + * For internal use only, use ``reified'' instead. + */ + def reify(reifee: Any): Tree = reifee match { + // before adding some case here, in global scope, please, consider + // whether it can be localized like reifyAnnotationInfo or reifyScope + // this will help reification stay as sane as possible + case sym: Symbol => reifySymRef(sym) + case tpe: Type => reifyType(tpe) + case name: Name => reifyName(name) + case tree: Tree => reifyTree(tree) + case pos: Position => reifyPosition(pos) + case mods: mirror.Modifiers => reifyModifiers(mods) + case xs: List[_] => reifyList(xs) + case s: String => Literal(Constant(s)) + case v if isAnyVal(v) => Literal(Constant(v)) + case null => Literal(Constant(null)) + case _ => + throw new Error("reifee %s of type %s is not supported".format(reifee, reifee.getClass)) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala new file mode 100644 index 0000000000..e700604612 --- /dev/null +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -0,0 +1,296 @@ +package scala.reflect.reify +package phases + +import scala.tools.nsc.symtab.Flags._ + +trait Reshape { + self: Reifier => + + import mirror._ + import definitions._ + import treeInfo._ + + /** + * Rolls back certain changes that were introduced during typechecking of the reifee. + * + * These include: + * * Replacing type trees with TypeTree(tpe) + * * Transforming Modifiers.annotations into Symbol.annotations + * * Transforming Annotated annotations into AnnotatedType annotations + * * Transforming Annotated(annot, expr) into Typed(expr, TypeTree(Annotated(annot, _)) + * * Non-idempotencies of the typechecker: https://issues.scala-lang.org/browse/SI-5464 + */ + val reshape = new Transformer { + var currentSymbol: Symbol = NoSymbol + + override def transform(tree: Tree) = { + currentSymbol = tree.symbol + + val preTyper = tree match { + case tree if tree.isErroneous => + tree + case tt @ TypeTree() => + toPreTyperTypeTree(tt) + case toa @ TypedOrAnnotated(_) => + toPreTyperTypedOrAnnotated(toa) + case ta @ TypeApply(hk, ts) => + val discard = ts collect { case tt: TypeTree => tt } exists isDiscarded + 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 + var body1 = trimAccessors(classDef, body) + body1 = trimSyntheticCaseClassMembers(classDef, body1) + 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 + var body1 = trimAccessors(moduledef, body) + body1 = trimSyntheticCaseClassMembers(moduledef, body1) + var impl1 = Template(parents, self, body1).copyAttrs(impl) + ModuleDef(mods, name, impl1).copyAttrs(moduledef) + case template @ Template(parents, self, body) => + val discardedParents = parents collect { case tt: TypeTree => tt } filter isDiscarded + if (reifyDebug && discardedParents.length > 0) println("discarding parents in Template: " + discardedParents.mkString(", ")) + val parents1 = parents diff discardedParents + val body1 = trimSyntheticCaseClassCompanions(body) + Template(parents1, 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 Literal(const @ Constant(tpe: Type)) => + // todo. implement this + ??? + case Literal(const @ Constant(sym: Symbol)) => + // todo. implement this + ??? + case _ => + tree + } + + super.transform(preTyper) + } + + override def transformModifiers(mods: Modifiers) = { + val mods1 = toPreTyperModifiers(mods, currentSymbol) + super.transformModifiers(mods1) + } + + private def toPreTyperModifiers(mods: Modifiers, sym: Symbol) = { + if (!sym.annotations.isEmpty) { + val Modifiers(flags, privateWithin, annotations) = mods + val postTyper = sym.annotations filter (_.original != EmptyTree) + if (reifyDebug && !postTyper.isEmpty) println("reify symbol annotations for: " + sym) + if (reifyDebug && !postTyper.isEmpty) println("originals are: " + sym.annotations) + val preTyper = postTyper map toPreTyperAnnotation + mods.withAnnotations(preTyper) + } else { + mods + } + } + + /** Restore 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. + * Thus we apply a workaround for that in typedAnnotated. I hope this will be the only workaround in this department. + * + * upd. Recently I went ahead and started using original for all TypeTrees, regardless of whether they refer to local symbols or not. + * As a result, ``reifyType'' is never called directly by tree reification (and, wow, it seems to work great!). + * The only usage of ``reifyType'' now is for servicing typetags, however, I have some ideas how to get rid of that as well. + */ + private def isDiscarded(tt: TypeTree) = tt.original == null + private def toPreTyperTypeTree(tt: TypeTree): Tree = { + if (tt.original != null) { + // here we rely on the fact that the originals that reach this point + // have all necessary symbols attached to them (i.e. that they can be recompiled in any lexical context) + // if this assumption fails, please, don't be quick to add postprocessing here (like I did before) + // but rather try to fix this in Typer, so that it produces quality originals (like it's done for typedAnnotated) + if (reifyDebug) println("TypeTree, essential: %s (%s)".format(tt.tpe, tt.tpe.kind)) + if (reifyDebug) println("verdict: rolled back to original %s".format(tt.original)) + transform(tt.original) + } 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("TypeTree, non-essential: %s (%s)".format(tt.tpe, tt.tpe.kind)) + if (reifyDebug) println("verdict: discarded") + TypeTree() + } + } + + private def toPreTyperTypedOrAnnotated(tree: Tree): Tree = tree match { + case ty @ Typed(expr1, tt @ TypeTree()) => + if (reifyDebug) println("reify typed: " + tree) + val annotatedArg = { + def loop(tree: Tree): Tree = tree match { + case annotated1 @ Annotated(ann, annotated2 @ Annotated(_, _)) => loop(annotated2) + case annotated1 @ Annotated(ann, arg) => arg + case _ => EmptyTree + } + + loop(tt.original) + } + if (annotatedArg != EmptyTree) { + if (annotatedArg.isType) { + if (reifyDebug) println("verdict: was an annotated type, reify as usual") + ty + } else { + if (reifyDebug) println("verdict: was an annotated value, equivalent is " + tt.original) + toPreTyperTypedOrAnnotated(tt.original) + } + } else { + if (reifyDebug) println("verdict: wasn't annotated, reify as usual") + ty + } + case at @ Annotated(annot, arg) => + if (reifyDebug) println("reify type annotations for: " + tree) + assert(at.tpe.isInstanceOf[AnnotatedType], "%s (%s)".format(at.tpe, at.tpe.kind)) + val annot1 = toPreTyperAnnotation(at.tpe.asInstanceOf[AnnotatedType].annotations(0)) + if (reifyDebug) println("originals are: " + annot1) + Annotated(annot1, arg).copyAttrs(at) + } + + /** Restore 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 = { + 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)) } + } + + def extractOriginal: PartialFunction[Tree, Tree] = { case Apply(Select(New(tpt), _), _) => tpt } + assert(extractOriginal.isDefinedAt(ann.original), showRaw(ann.original)) + New(TypeTree(ann.atp) setOriginal extractOriginal(ann.original), List(args)) + } + + // [Eugene] is this implemented correctly? + private def trimAccessors(deff: Tree, stats: List[Tree]): List[Tree] = { + val symdefs = stats collect { case vodef: ValOrDefDef => vodef } map (vodeff => vodeff.symbol -> vodeff) toMap + val accessors = collection.mutable.Map[ValDef, List[DefDef]]() + stats collect { case ddef: DefDef => ddef } foreach (defdef => { + val valdef = symdefs get defdef.symbol.accessedOrSelf collect { case vdef: ValDef => vdef } getOrElse null + if (valdef != null) accessors(valdef) = accessors.getOrElse(valdef, Nil) :+ defdef + + def detectBeanAccessors(prefix: String): Unit = { + if (defdef.name.startsWith(prefix)) { + var name = defdef.name.toString.substring(prefix.length) + def uncapitalize(s: String) = if (s.length == 0) "" else { val chars = s.toCharArray; chars(0) = chars(0).toLower; new String(chars) } + def findValDef(name: String) = symdefs.values collect { case vdef: ValDef if nme.dropLocalSuffix(vdef.name).toString == name => vdef } headOption; + val valdef = findValDef(name) orElse findValDef(uncapitalize(name)) orNull; + if (valdef != null) accessors(valdef) = accessors.getOrElse(valdef, Nil) :+ defdef + } + } + detectBeanAccessors("get") + detectBeanAccessors("set") + detectBeanAccessors("is") + }); + + var stats1 = stats flatMap { + case vdef @ ValDef(mods, name, tpt, rhs) => + val mods1 = if (accessors.contains(vdef)) { + val ddef = accessors(vdef)(0) // any accessor will do + val Modifiers(flags, privateWithin, annotations) = mods + var flags1 = flags & ~LOCAL + if (!ddef.symbol.isPrivate) flags1 = flags1 & ~PRIVATE + val privateWithin1 = ddef.mods.privateWithin + val annotations1 = accessors(vdef).foldLeft(annotations)((curr, acc) => curr ++ (acc.symbol.annotations map toPreTyperAnnotation)) + Modifiers(flags1, privateWithin1, annotations1) setPositions mods.positions + } else { + mods + } + val mods2 = toPreTyperModifiers(mods1, vdef.symbol) + val name1 = nme.dropLocalSuffix(name) + val vdef1 = ValDef(mods2, name1, tpt, rhs) + if (reifyDebug) println("resetting visibility of field: %s => %s".format(vdef, vdef1)) + Some(vdef1) // no copyAttrs here, because new ValDef and old symbols are not out of sync + case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + if (accessors.values.exists(_.contains(ddef))) { + if (reifyDebug) println("discarding accessor method: " + ddef) + None + } else { + Some(ddef) + } + case tree => + Some(tree) + } + + stats1 + } + + private def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]): List[Tree] = + stats filterNot (memberDef => memberDef.isDef && { + val isSynthetic = memberDef.symbol.isSynthetic + // 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 + }) + + private def trimSyntheticCaseClassCompanions(stats: List[Tree]): List[Tree] = + stats diff (stats collect { case moddef: ModuleDef => moddef } filter (moddef => { + val isSynthetic = moddef.symbol.isSynthetic + // 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 + if (isSynthetic && isCaseCompanion && reifyDebug) println("discarding synthetic case class companion: " + moddef) + isSynthetic && isCaseCompanion + })) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/ClassLoaders.scala b/src/compiler/scala/reflect/runtime/ClassLoaders.scala new file mode 100644 index 0000000000..b73d57c04d --- /dev/null +++ b/src/compiler/scala/reflect/runtime/ClassLoaders.scala @@ -0,0 +1,25 @@ +package scala.reflect +package runtime + +trait ClassLoaders extends internal.SymbolTable { self: SymbolTable => + + def staticClass(fullname: String) = + definitions.getRequiredClass(fullname) + + def staticModule(fullname: String) = + definitions.getRequiredModule(fullname) + + /** If `owner` is a package class (but not the empty package) and `name` is a term name, make a new package + * <owner>.<name>, otherwise return NoSymbol. + * Exception: If owner is root and a java class with given name exists, create symbol in empty package instead. + */ + override def missingHook(owner: Symbol, name: Name): Symbol = + if (owner.isRoot && isJavaClass(name.toString)) + definitions.EmptyPackageClass.info decl name + else if (name.isTermName && owner.hasPackageFlag && !owner.isEmptyPackageClass) + makeScalaPackage(if (owner.isRoot) name.toString else owner.fullName+"."+name).sourceModule + else { + info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass) + super.missingHook(owner, name) + } +} diff --git a/src/compiler/scala/reflect/runtime/ConversionUtil.scala b/src/compiler/scala/reflect/runtime/ConversionUtil.scala index 8c32026e37..e45fc243c6 100644 --- a/src/compiler/scala/reflect/runtime/ConversionUtil.scala +++ b/src/compiler/scala/reflect/runtime/ConversionUtil.scala @@ -12,6 +12,7 @@ trait ConversionUtil { self: SymbolTable => /** A cache that maintains a bijection between Java reflection type `J` * and Scala reflection type `S`. */ + // todo. should be weak protected class TwoWayCache[J, S] { private val toScalaMap = new HashMap[J, S] diff --git a/src/compiler/scala/reflect/runtime/JavaToScala.scala b/src/compiler/scala/reflect/runtime/JavaToScala.scala index 89fd6bab64..6688d77985 100644 --- a/src/compiler/scala/reflect/runtime/JavaToScala.scala +++ b/src/compiler/scala/reflect/runtime/JavaToScala.scala @@ -34,16 +34,42 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => val global: JavaToScala.this.type = self } - protected def defaultReflectiveClassLoader(): JClassLoader = { - val cl = Thread.currentThread.getContextClassLoader - if (cl == null) getClass.getClassLoader else cl - } + /** Defines the classloader that will be used for all class resolution activities in this mirror. + * Is mutable, since sometimes we need to change it in flight (e.g. to make the default mirror work with REPL). + * + * If you want to have a mirror with non-standard class resolution, override this var + * (or, even simpler, use the `mkMirror` function from `scala.reflect` package) + * + * Be careful, though, since fancy stuff might happen. + * Here's one example: + * + * partest uses a URLClassLoader(urls, null) with custom classpath to run workers (in separate threads) + * however it doesn't set the context classloader for them, so they inherit the system classloader + * http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html + * + * Once upon a time, scala.reflect.mirror was loaded using getClass.getClassLoader, + * which also means that classOf[...] constructs such as: + * + * classOf[scala.reflect.ScalaSignature] + * + * in unpickleClass were also loaded by the URLClassLoader + * + * But mirror's classLoader used Thread.currentThread.getContextClassLoader, + * which introduced a subtle bug that made the following snippet incorrectly: + * + * jclazz.getAnnotation(classOf[scala.reflect.ScalaSignature]) + * + * Indeed, jclazz was loaded by context classloader, which defaulted to system classloader, + * while ScalaSignature class was loaded by getClass.getClassLoader, which was incompatible with system classloader. + * As a result, unpickler couldn't see the signature and that blew up the mirror. + */ + var classLoader: ClassLoader /** Paul: It seems the default class loader does not pick up root classes, whereas the system classloader does. * Can you check with your newly acquired classloader fu whether this implementation makes sense? */ def javaClass(path: String): jClass[_] = - javaClass(path, defaultReflectiveClassLoader()) + javaClass(path, classLoader) def javaClass(path: String, classLoader: JClassLoader): jClass[_] = Class.forName(path, true, classLoader) @@ -75,19 +101,70 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => (if (msg eq null) "reflection error while loading " + clazz.name else "error while loading " + clazz.name) + ", " + msg) } - try { - markAbsent(NoType) - val ssig = jclazz.getAnnotation(classOf[scala.reflect.ScalaSignature]) + // don't use classOf[scala.reflect.ScalaSignature] here, because it will use getClass.getClassLoader, not mirror's classLoader + // don't use asInstanceOf either because of the same reason (lol, I cannot believe I fell for it) + // don't use structural types to simplify reflective invocations because of the same reason + // todo. test for this + def loadAnnotation(name: String): java.lang.annotation.Annotation = { + def inferClasspath(cl: ClassLoader) = cl match { + case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" + case _ => "<unknown>" + } + def show(cl: ClassLoader) = cl match { + case cl if cl != null => + "%s of type %s with classpath %s".format(cl, cl.getClass, inferClasspath(cl)) + case null => + import scala.tools.util.PathResolver.Environment._ + "primordial classloader with boot classpath [%s]".format(javaBootClassPath) + } + + try { + val cls_ann = Class.forName(name, true, classLoader) + val anns = jclazz.getAnnotations + val ann = anns find (_.annotationType == cls_ann) orNull; + if (ann == null && anns.find(_.annotationType.getName == name).isDefined) { + val msg = "Mirror classloader mismatch: %s (loaded by %s)%nis unrelated to the mirror's classloader (%s)" + throw new Error(msg.format(jclazz, show(jclazz.getClassLoader), show(classLoader))) + } + ann + } catch { + case ex: ClassNotFoundException => + val msg = "Dysfunctional mirror classloader, cannot load %s: %s." + throw new Error(msg.format(name, show(classLoader)), ex) + } + } + def loadScalaSignature: Option[String] = { + val ssig = loadAnnotation("scala.reflect.ScalaSignature") if (ssig != null) { - info("unpickling Scala "+clazz + " and " + module+ ", owner = " + clazz.owner) - val bytes = ssig.bytes.getBytes - val len = ByteCodecs.decode(bytes) - unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName) + val bytesMethod = ssig.annotationType.getMethod("bytes") + val result = bytesMethod.invoke(ssig) + Some(result.asInstanceOf[String]) } else { - val slsig = jclazz.getAnnotation(classOf[scala.reflect.ScalaLongSignature]) - if (slsig != null) { + None + } + } + def loadScalaLongSignature: Option[Array[String]] = { + val slsig = loadAnnotation("scala.reflect.ScalaLongSignature") + if (slsig != null) { + val bytesMethod = slsig.annotationType.getMethod("bytes") + val result = bytesMethod.invoke(slsig) + Some(result.asInstanceOf[Array[String]]) + } else { + None + } + } + try { + markAbsent(NoType) + val sigs = (loadScalaSignature, loadScalaLongSignature) + sigs match { + case (Some(ssig), _) => + info("unpickling Scala "+clazz + " and " + module+ ", owner = " + clazz.owner) + val bytes = ssig.getBytes + val len = ByteCodecs.decode(bytes) + unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName) + case (_, Some(slsig)) => info("unpickling Scala "+clazz + " and " + module + " with long Scala signature") - val byteSegments = slsig.bytes map (_.getBytes) + val byteSegments = slsig map (_.getBytes) val lens = byteSegments map ByteCodecs.decode val bytes = Array.ofDim[Byte](lens.sum) var len = 0 @@ -96,10 +173,10 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => len += l } unpickler.unpickle(bytes, 0, clazz, module, jclazz.getName) - } else { // class does not have a Scala signature; it's a Java class + case (None, None) => + // class does not have a Scala signature; it's a Java class info("translating reflection info for Java " + jclazz) //debug initClassModule(clazz, module, new FromJavaClassCompleter(clazz, module, jclazz)) - } } } catch { case ex: MissingRequirementError => @@ -383,52 +460,70 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => */ def classToScala(jclazz: jClass[_]): Symbol = classCache.toScala(jclazz) { val jname = javaTypeName(jclazz) - val owner = sOwner(jclazz) - val simpleName = scalaSimpleName(jclazz) - - val sym = { - def lookup = { - def coreLookup(name: Name): Symbol = { - val sym = owner.info.decl(name) - sym orElse { - if (name.startsWith(nme.NAME_JOIN_STRING)) - coreLookup(name.subName(1, name.length)) - else - NoSymbol + + val sym = + if (jname == fulltpnme.RuntimeNothing) + NothingClass + else if (jname == fulltpnme.RuntimeNull) + NullClass + else + { + val owner = sOwner(jclazz) + val simpleName = scalaSimpleName(jclazz) + + def lookup = { + def coreLookup(name: Name): Symbol = { + val sym = owner.info.decl(name) + sym orElse { + if (name.startsWith(nme.NAME_JOIN_STRING)) + coreLookup(name.subName(1, name.length)) + else + NoSymbol + } + } + + if (nme.isModuleName(simpleName)) { + val moduleName = nme.stripModuleSuffix(simpleName).toTermName + val sym = coreLookup(moduleName) + if (sym == NoSymbol) sym else sym.moduleClass + } else { + coreLookup(simpleName) } } - if (nme.isModuleName(simpleName)) { - val moduleName = nme.stripModuleSuffix(simpleName).toTermName - val sym = coreLookup(moduleName) - if (sym == NoSymbol) sym else sym.moduleClass - } else { - coreLookup(simpleName) + val sym = { + if (jclazz.isMemberClass && !nme.isImplClassName(jname)) { + lookup + } else if (jclazz.isLocalClass || invalidClassName(jname)) { + // local classes and implementation classes not preserved by unpickling - treat as Java + jclassAsScala(jclazz) + } else if (jclazz.isArray) { + ArrayClass + } else javaTypeToValueClass(jclazz) orElse { + // jclazz is top-level - get signature + lookup + // val (clazz, module) = createClassModule( + // sOwner(jclazz), newTypeName(jclazz.getSimpleName), new TopClassCompleter(_, _)) + // classCache enter (jclazz, clazz) + // clazz + } } - } - if (jclazz.isMemberClass && !nme.isImplClassName(jname)) { - lookup - } else if (jclazz.isLocalClass || invalidClassName(jname)) { - // local classes and implementation classes not preserved by unpickling - treat as Java - jclassAsScala(jclazz) - } else if (jclazz.isArray) { - ArrayClass - } else javaTypeToValueClass(jclazz) orElse { - // jclazz is top-level - get signature - lookup - // val (clazz, module) = createClassModule( - // sOwner(jclazz), newTypeName(jclazz.getSimpleName), new TopClassCompleter(_, _)) - // classCache enter (jclazz, clazz) - // clazz - } - } + if (!sym.isType) { + val classloader = jclazz.getClassLoader + println("classloader is: %s of type %s".format(classloader, classloader.getClass)) + def inferClasspath(cl: ClassLoader) = cl match { + case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" + case _ => "<unknown>" + } + println("classpath is: %s".format(inferClasspath(classloader))) + def msgNoSym = "no symbol could be loaded from %s (scala equivalent is %s) by name %s".format(owner, jclazz, simpleName) + def msgIsNotType = "not a type: symbol %s loaded from %s (scala equivalent is %s) by name %s".format(sym, owner, jclazz, simpleName) + assert(false, if (sym == NoSymbol) msgNoSym else msgIsNotType) + } - if (!sym.isType) { - def msgNoSym = "no symbol could be loaded from %s (scala equivalent is %s) by name %s".format(owner, jclazz, simpleName) - def msgIsNotType = "not a type: symbol %s loaded from %s (scala equivalent is %s) by name %s".format(sym, owner, jclazz, simpleName) - assert(false, if (sym == NoSymbol) msgNoSym else msgIsNotType) - } + sym + } sym.asInstanceOf[ClassSymbol] } diff --git a/src/compiler/scala/reflect/runtime/Memoizer.scala b/src/compiler/scala/reflect/runtime/Memoizer.scala deleted file mode 100644 index 4c1b82ae6d..0000000000 --- a/src/compiler/scala/reflect/runtime/Memoizer.scala +++ /dev/null @@ -1,15 +0,0 @@ -package scala.reflect.runtime - -import collection.mutable.ArrayBuffer -import Mirror.Type - -/** Class that can be used for memoizing types in reified trees */ -class Memoizer { - private val mem = new ArrayBuffer[Mirror.Type] - def get(n: Int): Type = mem(n) - def add(n: Int, tpe: Type): Type = { - while (mem.length <= n) mem += null - mem(n) = tpe - tpe - } -} diff --git a/src/compiler/scala/reflect/runtime/Mirror.scala b/src/compiler/scala/reflect/runtime/Mirror.scala index d3e4dd7619..20024ed058 100644 --- a/src/compiler/scala/reflect/runtime/Mirror.scala +++ b/src/compiler/scala/reflect/runtime/Mirror.scala @@ -1,24 +1,24 @@ package scala.reflect package runtime -import internal.{SomePhase, NoPhase, Phase, TreeGen} import java.lang.reflect.Array +import ReflectionUtils._ +import scala.tools.nsc.util.ScalaClassLoader._ /** The mirror for standard runtime reflection from Java. */ -class Mirror extends Universe with RuntimeTypes with TreeBuildUtil with ToolBoxes with api.Mirror { +class Mirror(var classLoader: ClassLoader) extends Universe with api.Mirror { definitions.init() - import definitions._ def symbolForName(name: String): Symbol = { - val clazz = javaClass(name, defaultReflectiveClassLoader()) + val clazz = javaClass(name, classLoader) classToScala(clazz) } def companionInstance(clazz: Symbol): AnyRef = { - val singleton = ReflectionUtils.singletonInstance(clazz.fullName, defaultReflectiveClassLoader()) + val singleton = singletonInstance(classLoader, clazz.fullName) singleton } @@ -46,17 +46,31 @@ class Mirror extends Universe with RuntimeTypes with TreeBuildUtil with ToolBoxe jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) } - override def classToType(jclazz: java.lang.Class[_]): Type = typeToScala(jclazz) - override def classToSymbol(jclazz: java.lang.Class[_]): Symbol = classToScala(jclazz) + private def validateIncomingClassLoader(wannabeCl: ClassLoader) = { + val ourCls = loaderChain(classLoader) + if (wannabeCl != null && !(ourCls contains wannabeCl)) + throw new Error("class doesn't belong to the classloader chain of the mirror") + } + + def classToType(jclazz: java.lang.Class[_]): Type = { + validateIncomingClassLoader(jclazz.getClassLoader) + typeToScala(jclazz) + } + + def classToSymbol(jclazz: java.lang.Class[_]): Symbol = { + validateIncomingClassLoader(jclazz.getClassLoader) + classToScala(jclazz) + } + + def typeToClass(tpe: Type): java.lang.Class[_] = + typeToJavaClass(tpe) - override def typeToClass(tpe: Type): java.lang.Class[_] = typeToJavaClass(tpe) - override def symbolToClass(sym: Symbol): java.lang.Class[_] = classToJava(sym) + def symbolToClass(sym: Symbol): java.lang.Class[_] = + classToJava(sym) override def inReflexiveMirror = true } -object Mirror extends Mirror - /** test code; should go to tests once things settle down a bit * diff --git a/src/compiler/scala/reflect/runtime/RuntimeTypes.scala b/src/compiler/scala/reflect/runtime/RuntimeTypes.scala deleted file mode 100644 index 84d02ab7a0..0000000000 --- a/src/compiler/scala/reflect/runtime/RuntimeTypes.scala +++ /dev/null @@ -1,27 +0,0 @@ -package scala.reflect -package runtime - -import collection.mutable.ListBuffer - -trait RuntimeTypes extends Universe with api.RuntimeTypes { - - /** To lift path dependent types into reflection, we use InstanceRefSymbols. - * Two of these are equal if they point to the same object reference. Todo: remove - */ - case class InstanceRefSymbol(value: AnyRef) extends TermSymbol(NoSymbol, NoPosition, nme.EMPTY) - object InstanceRefSymbol extends InstanceRefSymbolExtractor - - override private[reflect] def namedType(pre: Type, sym: Symbol, args: List[Type]): Type = { - val tparamBuf = new ListBuffer[Symbol] - val args1 = for (arg <- args) yield arg match { - case _: TypeBounds => - val ex = pre.typeSymbol.freshExistential("$ex") setInfo arg - tparamBuf += ex - TypeRef(NoPrefix, ex, List()) - case _ => - arg - } - existentialAbstraction(tparamBuf.toList, typeRef(pre, sym, args1)) - } - -}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/Settings.scala b/src/compiler/scala/reflect/runtime/Settings.scala index 27e90c94bd..bbe4d60e9c 100644 --- a/src/compiler/scala/reflect/runtime/Settings.scala +++ b/src/compiler/scala/reflect/runtime/Settings.scala @@ -34,5 +34,5 @@ class Settings extends internal.settings.MutableSettings { val maxClassfileName = new IntSetting(255) val Xexperimental = new BooleanSetting(false) val deepCloning = new BooleanSetting (false) - val YvirtPatmat = new BooleanSetting(false) + val XoldPatmat = new BooleanSetting(false) } diff --git a/src/compiler/scala/reflect/runtime/Loaders.scala b/src/compiler/scala/reflect/runtime/SymbolLoaders.scala index 4b35a5b37e..7c1cc16152 100644 --- a/src/compiler/scala/reflect/runtime/Loaders.scala +++ b/src/compiler/scala/reflect/runtime/SymbolLoaders.scala @@ -5,7 +5,7 @@ import internal.Flags import java.lang.{Class => jClass, Package => jPackage} import collection.mutable -trait Loaders { self: SymbolTable => +trait SymbolLoaders { self: SymbolTable => /** The lazy type for root. */ @@ -123,6 +123,11 @@ trait Loaders { self: SymbolTable => } } + /** Assert that packages have package scopes */ + override def validateClassInfo(tp: ClassInfoType) { + assert(!tp.typeSymbol.isPackageClass || tp.decls.isInstanceOf[PackageScope]) + } + override def newPackageScope(pkgClass: Symbol) = new PackageScope(pkgClass) override def scopeTransform(owner: Symbol)(op: => Scope): Scope = diff --git a/src/compiler/scala/reflect/runtime/SymbolTable.scala b/src/compiler/scala/reflect/runtime/SymbolTable.scala index 5331f0a53e..64a5894d01 100644 --- a/src/compiler/scala/reflect/runtime/SymbolTable.scala +++ b/src/compiler/scala/reflect/runtime/SymbolTable.scala @@ -3,29 +3,10 @@ package runtime /** * This symbol table trait fills in the definitions so that class information is obtained by refection. - * It can be used either from the reflexive mirror itself (class Universe), or else from + * It can be used either from the reflexive mirror itself (class Mirror), or else from * a runtime compiler that uses reflection to get a class information (class scala.tools.nsc.ReflectGlobal) */ -trait SymbolTable extends internal.SymbolTable with JavaToScala with ScalaToJava with Loaders with SynchronizedOps { - - /** If `owner` is a package class (but not the empty package) and `name` is a term name, make a new package - * <owner>.<name>, otherwise return NoSymbol. - * Exception: If owner is root and a java class with given name exists, create symbol in empty package instead. - */ - override def missingHook(owner: Symbol, name: Name): Symbol = - if (owner.isRoot && isJavaClass(name.toString)) - definitions.EmptyPackageClass.info decl name - else if (name.isTermName && owner.hasPackageFlag && !owner.isEmptyPackageClass) - makeScalaPackage(if (owner.isRoot) name.toString else owner.fullName+"."+name).sourceModule - else { - info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass) - super.missingHook(owner, name) - } - - /** Assert that packages have package scopes */ - override def validateClassInfo(tp: ClassInfoType) { - assert(!tp.typeSymbol.isPackageClass || tp.decls.isInstanceOf[PackageScope]) - } +trait SymbolTable extends internal.SymbolTable with JavaToScala with ScalaToJava with ClassLoaders with SymbolLoaders with SynchronizedOps { def info(msg: => String) = if (settings.verbose.value) println("[reflect-compiler] "+msg) diff --git a/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala b/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala index 5ae4ec15ee..6fc5f7ed8a 100644 --- a/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala @@ -14,8 +14,11 @@ trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => 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 def newFreeTerm(name: TermName, info: Type, value: => Any, origin: String = null, newFlags: Long = 0L): FreeTerm = + new FreeTerm(name, value, origin) with SynchronizedTermSymbol initFlags newFlags setInfo info + + override def newFreeType(name: TypeName, info: Type, value: => Any, origin: String = null, newFlags: Long = 0L): FreeType = + new FreeType(name, value, origin) with SynchronizedTypeSymbol initFlags newFlags setInfo info override protected def makeNoSymbol: NoSymbol = new NoSymbol with SynchronizedSymbol diff --git a/src/compiler/scala/reflect/runtime/ToolBoxes.scala b/src/compiler/scala/reflect/runtime/ToolBoxes.scala index 28f12b378f..6d832a590f 100644 --- a/src/compiler/scala/reflect/runtime/ToolBoxes.scala +++ b/src/compiler/scala/reflect/runtime/ToolBoxes.scala @@ -1,28 +1,30 @@ package scala.reflect package runtime -import scala.tools.nsc -import scala.tools.nsc.reporters.Reporter -import scala.tools.nsc.reporters.StoreReporter -import scala.tools.nsc.reporters.AbstractReporter +import scala.tools.nsc.reporters._ import scala.tools.nsc.ReflectGlobal import scala.tools.nsc.CompilerCommand import scala.tools.nsc.Global import scala.tools.nsc.typechecker.Modes import scala.tools.nsc.io.VirtualDirectory import scala.tools.nsc.interpreter.AbstractFileClassLoader -import reflect.{mirror => rm} import scala.tools.nsc.util.FreshNameCreator import scala.reflect.internal.Flags import scala.tools.nsc.util.{NoSourceFile, NoFile} import java.lang.{Class => jClass} -import scala.tools.nsc.util.trace +import scala.compat.Platform.EOL trait ToolBoxes extends { self: Universe => - class ToolBox(val reporter: Reporter = new StoreReporter, val options: String = "") { + import self.{Reporter => ApiReporter} + import scala.tools.nsc.reporters.{Reporter => NscReporter} - class ToolBoxGlobal(settings0: nsc.Settings, reporter0: nsc.reporters.Reporter) extends ReflectGlobal(settings0, reporter0) { + def mkToolBox(reporter: ApiReporter = mkSilentReporter(), options: String = "") = new ToolBox(reporter, options) + + class ToolBox(val reporter: ApiReporter, val options: String) extends AbsToolBox { + + class ToolBoxGlobal(settings: scala.tools.nsc.Settings, reporter: NscReporter) + extends ReflectGlobal(settings, reporter, ToolBox.this.classLoader) { import definitions._ private val trace = scala.tools.nsc.util.trace when settings.debug.value @@ -36,64 +38,7 @@ trait ToolBoxes extends { self: Universe => newTermName("__wrapper$" + wrapCount) } - private def moduleFileName(className: String) = className + "$" - - private def isFree(t: Tree) = t.isInstanceOf[Ident] && t.symbol.isInstanceOf[FreeVar] - - def typedTopLevelExpr(tree: Tree, pt: Type): Tree = { - // !!! 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, 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, true, true, settings.Yshowsymkinds.value)) - ttree - } - - def defOwner(tree: Tree): Symbol = tree find (_.isDef) map (_.symbol) match { - case Some(sym) if sym != null && sym != NoSymbol => sym.owner - case _ => NoSymbol - } - - def wrapInObject(expr: Tree, fvs: List[Symbol]): ModuleDef = { - val obj = EmptyPackageClass.newModule(nextWrapperModuleName()) - val minfo = ClassInfoType(List(ObjectClass.tpe), newScope, obj.moduleClass) - obj.moduleClass setInfo minfo - 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, AnyClass.tpe) - minfo.decls enter meth - trace("wrapping ")(defOwner(expr) -> meth) - val methdef = DefDef(meth, expr changeOwner (defOwner(expr) -> meth)) - val moduledef = ModuleDef( - obj, - Template( - List(TypeTree(ObjectClass.tpe)), - emptyValDef, - NoMods, - List(), - List(List()), - List(methdef), - NoPosition)) - trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) - val cleanedUp = resetLocalAttrs(moduledef) - trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymkinds.value)) - cleanedUp - } - - def wrapInPackage(clazz: Tree): PackageDef = - PackageDef(Ident(nme.EMPTY_PACKAGE_NAME), List(clazz)) - - def wrapInCompilationUnit(tree: Tree): CompilationUnit = { - val unit = new CompilationUnit(NoSourceFile) - unit.body = tree - unit - } - - def compileExpr(expr: Tree, fvs: List[Symbol]): String = { + def verifyExpr(expr: Tree): Unit = { // 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 @@ -104,44 +49,190 @@ trait ToolBoxes extends { self: Universe => // 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") + if (!typed.isEmpty) throw new ToolBoxError(ToolBox.this, "reflective toolbox has failed: cannot operate on trees that are already typed") + + val freeTypes = this.freeTypes(expr) + if (freeTypes.length > 0) { + var msg = "reflective toolbox has failed:" + EOL + msg += "unresolved free type variables (namely: " + (freeTypes map (ft => "%s %s".format(ft.name, ft.origin)) mkString ", ") + "). " + msg += "have you forgot to use TypeTag annotations for type parameters external to a reifee? " + msg += "if you have troubles tracking free type variables, consider using -Xlog-free-types" + throw new ToolBoxError(ToolBox.this, msg) } + } - val mdef = wrapInObject(expr, fvs) - val pdef = wrapInPackage(mdef) - val unit = wrapInCompilationUnit(pdef) - val run = new Run - run.compileUnits(List(unit), run.namerPhase) - mdef.symbol.fullName + def typeCheckExpr(expr0: Tree, pt: Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { + verifyExpr(expr0) + + // need to wrap the expr, because otherwise you won't be able to typecheck macros against something that contains free vars + // [Eugene] get rid of the copy/paste w.r.t compileExpr + val freeTerms = this.freeTerms(expr0) + val freeTermNames = collection.mutable.Map[Symbol, TermName]() + freeTerms foreach (ft => { + var name = ft.name.toString + val namesakes = freeTerms takeWhile (_ != ft) filter (ft2 => ft != ft2 && ft.name == ft2.name) + if (namesakes.length > 0) name += ("$" + (namesakes.length + 1)) + freeTermNames += (ft -> newTermName(name + nme.MIRROR_FREE_VALUE_SUFFIX)) + }) + var expr = new Transformer { + override def transform(tree: Tree): Tree = + if (tree.hasSymbol && tree.symbol.isFreeTerm) { + tree match { + case Ident(_) => + Ident(freeTermNames(tree.symbol)) + case _ => + throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + } + } else { + super.transform(tree) + } + }.transform(expr0) + val dummies = freeTerms map (freeTerm => ValDef(NoMods, freeTermNames(freeTerm), TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark")))) + expr = Block(dummies, expr) + + // [Eugene] how can we implement that? + // !!! 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. + val ownerClass = EmptyPackageClass.newClassWithInfo(newTypeName("<expression-owner>"), List(ObjectClass.tpe), newScope) + val owner = ownerClass.newLocalDummy(expr.pos) + var currentTyper = typer.atOwner(expr, owner) + val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) + val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) + def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) + + phase = (new Run).typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled + currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions + reporter.reset() + + trace("typing (implicit views = %s, macros = %s): ".format(!withImplicitViewsDisabled, !withMacrosDisabled))(showAttributed(expr, true, true, settings.Yshowsymkinds.value)) + wrapper(currentTyper.silent(_.typed(expr, analyzer.EXPRmode, pt)) match { + case analyzer.SilentResultValue(result) => + trace("success: ")(showAttributed(result, true, true, settings.Yshowsymkinds.value)) + var Block(dummies, unwrapped) = result + var reversedFreeTermNames = freeTermNames map (_.swap) + // todo. also fixup singleton types + unwrapped = new Transformer { + override def transform(tree: Tree): Tree = + tree match { + case Ident(name) if reversedFreeTermNames contains name => + Ident(reversedFreeTermNames(name)) + case _ => + super.transform(tree) + } + }.transform(unwrapped) + new TreeTypeSubstituter(dummies map (_.symbol), dummies map (dummy => SingleType(NoPrefix, reversedFreeTermNames(dummy.symbol.name)))).traverse(unwrapped) + unwrapped + case error @ analyzer.SilentTypeError(_) => + trace("failed: ")(error.err.errMsg) + if (!silent) throw new ToolBoxError(ToolBox.this, "reflective typecheck has failed: %s".format(error.err.errMsg)) + EmptyTree + }) } - private def getMethod(jclazz: jClass[_], name: String) = - jclazz.getDeclaredMethods.find(_.getName == name).get + def compileExpr(expr: Tree): (Object, java.lang.reflect.Method) = { + verifyExpr(expr) + + def wrapExpr(expr0: Tree): Tree = { + def defOwner(tree: Tree): Symbol = tree find (_.isDef) map (_.symbol) match { + case Some(sym) if sym != null && sym != NoSymbol => sym.owner + case _ => NoSymbol + } + + val freeTerms = this.freeTerms(expr0) + val freeTermNames = collection.mutable.Map[Symbol, TermName]() + freeTerms foreach (ft => { + var name = ft.name.toString + val namesakes = freeTerms takeWhile (_ != ft) filter (ft2 => ft != ft2 && ft.name == ft2.name) + if (namesakes.length > 0) name += ("$" + (namesakes.length + 1)) + freeTermNames += (ft -> newTermName(name + nme.MIRROR_FREE_VALUE_SUFFIX)) + }) + val expr = new Transformer { + override def transform(tree: Tree): Tree = + if (tree.hasSymbol && tree.symbol.isFreeTerm) { + tree match { + case Ident(_) => + Apply(Ident(freeTermNames(tree.symbol)), List()) + case _ => + throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + } + } else { + super.transform(tree) + } + }.transform(expr0) + + val obj = EmptyPackageClass.newModule(nextWrapperModuleName()) + val minfo = ClassInfoType(List(ObjectClass.tpe), newScope, obj.moduleClass) + obj.moduleClass setInfo minfo + obj setInfo obj.moduleClass.tpe + val meth = obj.moduleClass.newMethod(newTermName(wrapperMethodName)) + def makeParam(fv: Symbol) = { + // [Eugene] conventional way of doing this? + val underlying = fv.tpe.resultType + val tpe = appliedType(definitions.FunctionClass(0).tpe, List(underlying)) + meth.newValueParameter(freeTermNames(fv)) setInfo tpe + } + meth setInfo MethodType(freeTerms map makeParam, AnyClass.tpe) + minfo.decls enter meth + trace("wrapping ")(defOwner(expr) -> meth) + val methdef = DefDef(meth, expr changeOwner (defOwner(expr) -> meth)) + val moduledef = ModuleDef( + obj, + Template( + List(TypeTree(ObjectClass.tpe)), + emptyValDef, + NoMods, + List(), + List(List()), + List(methdef), + NoPosition)) + trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) + var cleanedUp = resetLocalAttrs(moduledef) + trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymkinds.value)) + cleanedUp + } - def runExpr(expr: Tree): Any = { - val fvs = (expr filter isFree map (_.symbol)).distinct + val mdef = wrapExpr(expr) + val pdef = PackageDef(Ident(nme.EMPTY_PACKAGE_NAME), List(mdef)) + val unit = new CompilationUnit(NoSourceFile) + unit.body = pdef + val run = new Run reporter.reset() - val className = compileExpr(expr, fvs) + run.compileUnits(List(unit), run.namerPhase) if (reporter.hasErrors) { - throw new Error("reflective compilation has failed") + var msg = "reflective compilation has failed: " + EOL + EOL + msg += ToolBox.this.reporter.infos map (_.msg) mkString EOL + throw new ToolBoxError(ToolBox.this, msg) } + val className = mdef.symbol.fullName if (settings.debug.value) println("generated: "+className) + def moduleFileName(className: String) = className + "$" val jclazz = jClass.forName(moduleFileName(className), true, classLoader) val jmeth = jclazz.getDeclaredMethods.find(_.getName == wrapperMethodName).get val jfield = jclazz.getDeclaredFields.find(_.getName == NameTransformer.MODULE_INSTANCE_NAME).get val singleton = jfield.get(null) + (singleton, jmeth) + } + + def runExpr(expr: Tree, freeTypes: Map[TypeName, Type] = Map[TypeName, Type]()): Any = { + val freeTerms = this.freeTerms(expr) // need to calculate them here, because later on they will be erased + val thunks = freeTerms map (fte => () => fte.value) // need to be lazy in order not to distort evaluation order + // @odersky writes: Not sure we will be able to drop this. I forgot the reason why we dereference () functions, // but there must have been one. So I propose to leave old version in comments to be resurrected if the problem resurfaces. -// val result = jmeth.invoke(singleton, fvs map (sym => sym.asInstanceOf[FreeVar].value.asInstanceOf[AnyRef]): _*) + // @Eugene writes: this dates back to the days when one could only reify functions + // hence, blocks were translated into nullary functions, so + // presumably, it was useful to immediately evaluate them to get the result of a block +// val result = jmeth.invoke(singleton, freeTerms map (sym => sym.asInstanceOf[FreeTermVar].value.asInstanceOf[AnyRef]): _*) // if (etpe.typeSymbol != FunctionClass(0)) result // else { // val applyMeth = result.getClass.getMethod("apply") // applyMeth.invoke(result) // } - jmeth.invoke(singleton, fvs map (sym => sym.asInstanceOf[FreeVar].value.asInstanceOf[AnyRef]): _*) + val (singleton, jmeth) = compileExpr(expr) + jmeth.invoke(singleton, thunks map (_.asInstanceOf[AnyRef]): _*) } def showAttributed(tree: Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = { @@ -161,6 +252,7 @@ trait ToolBoxes extends { self: Universe => } } + // todo. is not going to work with quoted arguments with embedded whitespaces lazy val arguments = options.split(" ") lazy val virtualDirectory = @@ -170,49 +262,96 @@ trait ToolBoxes extends { self: Universe => } lazy val compiler: ToolBoxGlobal = { - val errorFn: String => Unit = reporter.error(scala.tools.nsc.util.NoPosition, _) - val command = reporter match { - case reporter: AbstractReporter => new CompilerCommand(arguments.toList, reporter.settings, errorFn) - case _ => new CompilerCommand(arguments.toList, errorFn) + try { + val errorFn: String => Unit = msg => reporter.log(NoPosition, msg, reporter.ERROR) + // [Eugene] settings shouldn't be passed via reporters, this is crazy +// val command = reporter match { +// case reporter: AbstractReporter => new CompilerCommand(arguments.toList, reporter.settings, errorFn) +// case _ => new CompilerCommand(arguments.toList, errorFn) +// } + val command = new CompilerCommand(arguments.toList, errorFn) + command.settings.outputDirs setSingleOutput virtualDirectory + val nscReporter = new ApiToNscReporterProxy(reporter) { val settings = command.settings } + val instance = new ToolBoxGlobal(command.settings, nscReporter) + if (nscReporter.hasErrors) { + var msg = "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL + msg += reporter.infos map (_.msg) mkString EOL + throw new ToolBoxError(this, msg) + } + instance.phase = (new instance.Run).typerPhase // need to manually set a phase, because otherwise TypeHistory will crash + instance + } catch { + case ex: Throwable => + var msg = "reflective compilation has failed: cannot initialize the compiler due to %s".format(ex.toString) + throw new ToolBoxError(this, msg, ex) } - - command.settings.outputDirs setSingleOutput virtualDirectory - 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 { - val from: self.type = self - } + // @Eugene: how do I make this work without casts? + // lazy val importer = compiler.mkImporter(self) + lazy val importer = compiler.mkImporter(self).asInstanceOf[compiler.Importer { val from: self.type }] lazy val exporter = importer.reverse - lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, defaultReflectiveClassLoader) + lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, self.classLoader) - 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 ttree: compiler.Tree = compiler.typedTopLevelExpr(ctree, pt) - val rmttree = exporter.importTree(ttree).asInstanceOf[rm.Tree] + def typeCheck(tree: Tree, expectedType: Type = WildcardType, freeTypes: Map[FreeType, Type] = Map[FreeType, Type](), silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { + if (compiler.settings.verbose.value) println("typing "+tree+", expectedType = "+expectedType+", freeTypes = "+freeTypes) + var ctree: compiler.Tree = importer.importTree(tree) + var cexpectedType: compiler.Type = importer.importType(expectedType) + + if (compiler.settings.verbose.value) println("substituting "+ctree+", expectedType = "+expectedType) + val cfreeTypes: Map[compiler.FreeType, compiler.Type] = freeTypes map { case (k, v) => (importer.importSymbol(k).asInstanceOf[compiler.FreeType], importer.importType(v)) } + ctree = compiler.substituteFreeTypes(ctree, cfreeTypes) + cexpectedType = compiler.substituteFreeTypes(cexpectedType, cfreeTypes) + + if (compiler.settings.verbose.value) println("typing "+ctree+", expectedType = "+expectedType) + val ttree: compiler.Tree = compiler.typeCheckExpr(ctree, cexpectedType, silent = silent, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled) + val rmttree = exporter.importTree(ttree) rmttree } - def typeCheck(tree: rm.Tree): rm.Tree = - typeCheck(tree, WildcardType.asInstanceOf[rm.Type]) + def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false): Tree = + // todo. implement this + ??? + + def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, reportAmbiguous: Boolean = true): Tree = + // todo. implement this + ??? + + def resetAllAttrs[T <: Tree](tree: T): T = { + val ctree: compiler.Tree = importer.importTree(tree) + val ttree: compiler.Tree = compiler.resetAllAttrs(ctree) + val rmttree = exporter.importTree(ttree) + rmttree.asInstanceOf[T] + } + + def resetLocalAttrs[T <: Tree](tree: T): T = { + val ctree: compiler.Tree = importer.importTree(tree) + val ttree: compiler.Tree = compiler.resetLocalAttrs(ctree) + val rmttree = exporter.importTree(ttree) + rmttree.asInstanceOf[T] + } + + def showAttributed(tree: Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = + compiler.showAttributed(importer.importTree(tree), printTypes, printIds, printKinds) + + def runExpr(tree: Tree, freeTypes: Map[FreeType, Type] = Map[FreeType, Type]()): Any = { + if (compiler.settings.verbose.value) println("running "+tree+", freeTypes = "+freeTypes) + var ctree: compiler.Tree = importer.importTree(tree) - 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) + if (compiler.settings.verbose.value) println("substituting "+ctree) + val cfreeTypes: Map[compiler.FreeType, compiler.Type] = freeTypes map { case (k, v) => (importer.importSymbol(k).asInstanceOf[compiler.FreeType], importer.importType(v)) } + ctree = compiler.substituteFreeTypes(ctree, cfreeTypes) - def runExpr(tree: rm.Tree): Any = { - if (compiler.settings.verbose.value) println("running "+tree) - val ctree: compiler.Tree = importer.importTree(tree.asInstanceOf[Tree]) + if (compiler.settings.verbose.value) println("running "+ctree) compiler.runExpr(ctree) } + + class ToolBoxError(val toolBox: ToolBox, val message: String, val cause: Throwable = null) extends Throwable(message, cause) + + object ToolBoxError extends ToolBoxErrorExtractor { + def unapply(error: ToolBoxError): Option[(ToolBox, String)] = Some((error.toolBox, error.message)) + } } } diff --git a/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala b/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala deleted file mode 100644 index 61001a4778..0000000000 --- a/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala +++ /dev/null @@ -1,49 +0,0 @@ -package scala.reflect -package runtime - -trait TreeBuildUtil extends Universe with api.TreeBuildUtil { - /** 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 - */ - def selectType(owner: Symbol, name: String): Symbol = - owner.info.decl(newTypeName(name)) - - /** Selects term symbol with given name and type from the defined members of prefix type - * @pre The prefix type - * @name The name of the selected member - */ - def selectTerm(owner: Symbol, name: String): Symbol = { - val sym = owner.info.decl(newTermName(name)) - if (sym.isOverloaded) sym suchThat (!_.isMethod) - else sym - } - - def selectOverloadedMethod(owner: Symbol, name: String, index: Int): Symbol = - owner.info.decl(newTermName(name)).alternatives(index) - - def selectParam(owner: Symbol, idx: Int): Symbol = { - def selectInList(params: List[Symbol], idx: Int, fallback: Type): Symbol = { - if (params.isEmpty) selectIn(fallback, idx) - else if (idx == 0) params.head - else selectInList(params.tail, idx - 1, fallback) - } - def selectIn(tpe: Type, idx: Int): Symbol = tpe match { - case PolyType(tparams, res) => selectInList(tparams, idx, res) - case MethodType(params, res) => selectInList(params, idx, res) - case _ => NoSymbol - } - selectIn(owner.info, idx) - } - - 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) - -}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/Universe.scala b/src/compiler/scala/reflect/runtime/Universe.scala index dca6d6041b..fd53308d0a 100644 --- a/src/compiler/scala/reflect/runtime/Universe.scala +++ b/src/compiler/scala/reflect/runtime/Universe.scala @@ -8,12 +8,14 @@ import internal.{SomePhase, NoPhase, Phase, TreeGen} * It also provides methods to go from Java members to Scala members, * using the code in JavaConversions. */ -class Universe extends SymbolTable { +abstract class Universe extends SymbolTable with ToolBoxes { type AbstractFileType = AbstractFile def picklerPhase = SomePhase + type TreeGen = internal.TreeGen + val gen = new TreeGen { val global: Universe.this.type = Universe.this } lazy val settings = new Settings @@ -30,24 +32,12 @@ class Universe extends SymbolTable { def newStrictTreeCopier: TreeCopier = new StrictTreeCopier def newLazyTreeCopier: TreeCopier = new LazyTreeCopier - def focusPos(pos: Position) = pos - def isRangePos(pos: Position) = false - def showPos(pos: Position) = "<unknown position>" - - type Position = String // source file? - val NoPosition = "" - definitions.AnyValClass // force it. - type TreeAnnotation = Position - def NoTreeAnnotation: TreeAnnotation = NoPosition - def positionToAnnotation(pos: Position): TreeAnnotation = pos // TODO - def annotationToPosition(annot: TreeAnnotation): Position = annot //TODO - // establish root association to avoid cyclic dependency errors later - classToScala(classOf[java.lang.Object]).initialize + // don't use classOf[...] here, because it gets serviced by getClass.getClassLoader! + classToScala(Class.forName("java.lang.Object", true, classLoader)).initialize // println("initializing definitions") definitions.init() - } diff --git a/src/compiler/scala/reflect/runtime/package.scala b/src/compiler/scala/reflect/runtime/package.scala new file mode 100644 index 0000000000..52ab2c5deb --- /dev/null +++ b/src/compiler/scala/reflect/runtime/package.scala @@ -0,0 +1,5 @@ +package scala.reflect + +package object runtime { + def mkMirror(classLoader: ClassLoader): api.Mirror = new Mirror(classLoader) +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala index c92474b33e..daa08ef8a7 100644 --- a/src/compiler/scala/tools/ant/Scaladoc.scala +++ b/src/compiler/scala/tools/ant/Scaladoc.scala @@ -75,6 +75,11 @@ class Scaladoc extends ScalaMatchingTask { */ object Flag extends PermissibleValue { val values = List("yes", "no", "on", "off") + def getBooleanValue(value: String, flagName: String): Boolean = + if (Flag.isPermissible(value)) + return ("yes".equals(value) || "on".equals(value)) + else + buildError("Unknown " + flagName + " flag '" + value + "'") } /** The directories that contain source files to compile. */ @@ -127,6 +132,25 @@ class Scaladoc extends ScalaMatchingTask { /** Instruct the ant task not to fail in the event of errors */ private var nofail: Boolean = false + /** Instruct the scaladoc tool to document implicit conversions */ + private var docImplicits: Boolean = false + + /** Instruct the scaladoc tool to document all (including impossible) implicit conversions */ + private var docImplicitsShowAll: Boolean = false + + /** Instruct the scaladoc tool to output implicits debugging information */ + private var docImplicitsDebug: Boolean = false + + /** Instruct the scaladoc tool to create diagrams */ + private var docDiagrams: Boolean = false + + /** Instruct the scaladoc tool to output diagram creation debugging information */ + private var docDiagramsDebug: Boolean = false + + /** Instruct the scaladoc tool to use the binary given to create diagrams */ + private var docDiagramsDotPath: Option[String] = None + + /*============================================================================*\ ** Properties setters ** \*============================================================================*/ @@ -361,12 +385,39 @@ class Scaladoc extends ScalaMatchingTask { * * @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 + "'") - } + def setNoFail(input: String) = + nofail = Flag.getBooleanValue(input, "nofail") + + /** Set the `implicits` info attribute. + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setImplicits(input: String) = + docImplicits = Flag.getBooleanValue(input, "implicits") + + /** Set the `implicitsShowAll` info attribute to enable scaladoc to show all implicits, including those impossible to + * convert to from the default scope + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setImplicitsShowAll(input: String) = + docImplicitsShowAll = Flag.getBooleanValue(input, "implicitsShowAll") + + /** Set the `implicitsDebug` info attribute so scaladoc outputs implicit conversion debug information + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setImplicitsDebug(input: String) = + docImplicitsDebug = Flag.getBooleanValue(input, "implicitsDebug") + + /** Set the `diagrams` bit so Scaladoc adds diagrams to the documentation + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setDiagrams(input: String) = + docDiagrams = Flag.getBooleanValue(input, "diagrams") + + /** Set the `diagramsDebug` bit so Scaladoc outputs diagram building debug information + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setDiagramsDebug(input: String) = + docDiagramsDebug = Flag.getBooleanValue(input, "diagramsDebug") + + /** Set the `diagramsDotPath` attribute to the path where graphviz dot can be found (including the binary file name, + * eg: /usr/bin/dot) */ + def setDiagramsDotPath(input: String) = + docDiagramsDotPath = Some(input) /*============================================================================*\ ** Properties getters ** @@ -560,6 +611,13 @@ class Scaladoc extends ScalaMatchingTask { docSettings.deprecation.value = deprecation docSettings.unchecked.value = unchecked + docSettings.docImplicits.value = docImplicits + docSettings.docImplicitsDebug.value = docImplicitsDebug + docSettings.docImplicitsShowAll.value = docImplicitsShowAll + docSettings.docDiagrams.value = docDiagrams + docSettings.docDiagramsDebug.value = docDiagramsDebug + if(!docDiagramsDotPath.isEmpty) docSettings.docDiagramsDotPath.value = docDiagramsDotPath.get + if (!docgenerator.isEmpty) docSettings.docgenerator.value = docgenerator.get if (!docrootcontent.isEmpty) docSettings.docRootContent.value = docrootcontent.get.getAbsolutePath() log("Scaladoc params = '" + addParams + "'", Project.MSG_DEBUG) diff --git a/src/compiler/scala/tools/cmd/FromString.scala b/src/compiler/scala/tools/cmd/FromString.scala index 3792c26c34..9592e7a716 100644 --- a/src/compiler/scala/tools/cmd/FromString.scala +++ b/src/compiler/scala/tools/cmd/FromString.scala @@ -7,14 +7,14 @@ package scala.tools package cmd import nsc.io.{ Path, File, Directory } -import scala.reflect.OptManifest +import scala.reflect.Manifest /** A general mechanism for defining how a command line argument * (always a String) is transformed into an arbitrary type. A few * example instances are in the companion object, but in general * either IntFromString will suffice or you'll want custom transformers. */ -abstract class FromString[+T](implicit m: OptManifest[T]) extends PartialFunction[String, T] { +abstract class FromString[+T](implicit m: Manifest[T]) extends PartialFunction[String, T] { def apply(s: String): T def isDefinedAt(s: String): Boolean = true def zero: T = apply("") diff --git a/src/compiler/scala/tools/cmd/gen/AnyVals.scala b/src/compiler/scala/tools/cmd/gen/AnyVals.scala index 0869350dd3..e8230b8ca4 100644 --- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala +++ b/src/compiler/scala/tools/cmd/gen/AnyVals.scala @@ -14,20 +14,34 @@ trait AnyValReps { sealed abstract class AnyValNum(name: String, repr: Option[String], javaEquiv: String) extends AnyValRep(name,repr,javaEquiv) { case class Op(val op : String, val doc : String) + + private def companionCoercions(tos: String*) = { + tos.toList map (to => + """implicit def %s2%s(x: %s): %s = x.to%s""".format(javaEquiv, to, name, to.capitalize, to.capitalize) + ) + } + def implicitCoercions: List[String] = javaEquiv match { + case "byte" => companionCoercions("short", "int", "long", "float", "double") + case "short" | "char" => companionCoercions("int", "long", "float", "double") + case "int" => companionCoercions("long", "float", "double") + case "long" => companionCoercions("float", "double") + case "float" => companionCoercions("double") + case _ => Nil + } def isCardinal: Boolean = isIntegerType(this) def unaryOps = { val ops = List( Op("+", "/**\n" + - " * @return this value, unmodified\n" + + " * Returns this value, unmodified.\n" + " */"), Op("-", "/**\n" + - " * @return the negation of this value\n" + + " * Returns the negation of this value.\n" + " */")) if(isCardinal) Op("~", "/**\n" + - " * @return the bitwise negation of this value\n" + + " * Returns the bitwise negation of this value.\n" + " * @example {{{\n" + " * ~5 == -6\n" + " * // in binary: ~00000101 ==\n" + @@ -41,7 +55,7 @@ trait AnyValReps { if (isCardinal) List( Op("|", "/**\n" + - " * @return the bitwise OR of this value and x\n" + + " * Returns the bitwise OR of this value and `x`.\n" + " * @example {{{\n" + " * (0xf0 | 0xaa) == 0xfa\n" + " * // in binary: 11110000\n" + @@ -51,7 +65,7 @@ trait AnyValReps { " * }}}\n" + " */"), Op("&", "/**\n" + - " * @return the bitwise AND of this value and x\n" + + " * Returns the bitwise AND of this value and `x`.\n" + " * @example {{{\n" + " * (0xf0 & 0xaa) == 0xa0\n" + " * // in binary: 11110000\n" + @@ -61,7 +75,7 @@ trait AnyValReps { " * }}}\n" + " */"), Op("^", "/**\n" + - " * @return the bitwise XOR of this value and x\n" + + " * Returns the bitwise XOR of this value and `x`.\n" + " * @example {{{\n" + " * (0xf0 ^ 0xaa) == 0x5a\n" + " * // in binary: 11110000\n" + @@ -76,13 +90,13 @@ trait AnyValReps { if (isCardinal) List( Op("<<", "/**\n" + - " * @return this value bit-shifted left by the specified number of bits,\n" + + " * Returns this value bit-shifted left by the specified number of bits,\n" + " * filling in the new right bits with zeroes.\n" + " * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}}\n" + " */"), Op(">>>", "/**\n" + - " * @return this value bit-shifted right by the specified number of bits,\n" + + " * Returns this value bit-shifted right by the specified number of bits,\n" + " * filling the new left bits with zeroes.\n" + " * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}}\n" + " * @example {{{\n" + @@ -93,7 +107,7 @@ trait AnyValReps { " */"), Op(">>", "/**\n" + - " * @return this value bit-shifted left by the specified number of bits,\n" + + " * Returns this value bit-shifted left by the specified number of bits,\n" + " * 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" + @@ -105,19 +119,19 @@ trait AnyValReps { else Nil def comparisonOps = List( - Op("==", "/**\n * @return `true` if this value is equal x, `false` otherwise\n */"), - Op("!=", "/**\n * @return `true` if this value is not equal to x, `false` otherwise\n */"), - Op("<", "/**\n * @return `true` if this value is less than x, `false` otherwise\n */"), - Op("<=", "/**\n * @return `true` if this value is less than or equal to x, `false` otherwise\n */"), - Op(">", "/**\n * @return `true` if this value is greater than x, `false` otherwise\n */"), - Op(">=", "/**\n * @return `true` if this value is greater than or equal to x, `false` otherwise\n */")) + Op("==", "/**\n * Returns `true` if this value is equal to x, `false` otherwise.\n */"), + Op("!=", "/**\n * Returns `true` if this value is not equal to x, `false` otherwise.\n */"), + Op("<", "/**\n * Returns `true` if this value is less than x, `false` otherwise.\n */"), + Op("<=", "/**\n * Returns `true` if this value is less than or equal to x, `false` otherwise.\n */"), + Op(">", "/**\n * Returns `true` if this value is greater than x, `false` otherwise.\n */"), + Op(">=", "/**\n * Returns `true` if this value is greater than or equal to x, `false` otherwise.\n */")) def otherOps = List( - Op("+", "/**\n * @return the sum of this value and x\n */"), - Op("-", "/**\n * @return the difference of this value and x\n */"), - Op("*", "/**\n * @return the product of this value and x\n */"), - Op("/", "/**\n * @return the quotient of this value and x\n */"), - Op("%", "/**\n * @return the remainder of the division of this value by x\n */")) + Op("+", "/**\n * Returns the sum of this value and `x`.\n */"), + Op("-", "/**\n * Returns the difference of this value and `x`.\n */"), + Op("*", "/**\n * Returns the product of this value and `x`.\n */"), + Op("/", "/**\n * Returns the quotient of this value and `x`.\n */"), + Op("%", "/**\n * Returns the remainder of the division of this value by `x`.\n */")) // Given two numeric value types S and T , the operation type of S and T is defined as follows: // If both S and T are subrange types then the operation type of S and T is Int. @@ -160,7 +174,7 @@ trait AnyValReps { } def objectLines = { val comp = if (isCardinal) cardinalCompanion else floatingCompanion - (comp + allCompanions).trim.lines map interpolate toList + ((comp + allCompanions).trim.lines map interpolate).toList ++ implicitCoercions } /** Makes a set of binary operations based on the given set of ops, args, and resultFn. @@ -224,8 +238,8 @@ trait AnyValReps { def classDoc = interpolate(classDocTemplate) def objectDoc = "" def mkImports = "" - def mkClass = assemble("final class", "AnyVal", classLines) + "\n" - def mkObject = assemble("object", "AnyValCompanion", objectLines) + "\n" + def mkClass = assemble("final class", "private", "AnyVal", classLines) + "\n" + def mkObject = assemble("object", "", "AnyValCompanion", objectLines) + "\n" def make() = List[String]( headerTemplate, mkImports, @@ -235,8 +249,8 @@ trait AnyValReps { mkObject ) mkString "" - def assemble(what: String, parent: String, lines: List[String]): String = { - val decl = "%s %s extends %s ".format(what, name, parent) + def assemble(what: String, ctor: String, parent: String, lines: List[String]): String = { + val decl = "%s %s %s extends %s ".format(what, name, ctor, parent) val body = if (lines.isEmpty) "{ }\n\n" else lines map indent mkString ("{\n", "\n", "\n}\n") decl + body diff --git a/src/compiler/scala/tools/nsc/ClassLoaders.scala b/src/compiler/scala/tools/nsc/ClassLoaders.scala new file mode 100644 index 0000000000..4058ee9324 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ClassLoaders.scala @@ -0,0 +1,64 @@ +package scala.tools.nsc + +import util.ScalaClassLoader + +trait ClassLoaders { self: Global => + + def staticClass(fullname: String) = { + if (self.forMSIL) + throw new UnsupportedOperationException("Scala reflection not available on this platform") + + getClass(newTypeName(fullname)) + } + + def staticModule(fullname: String) = { + if (self.forMSIL) + throw new UnsupportedOperationException("Scala reflection not available on this platform") + + getModule(newTermName(fullname)) + } + + private def getClass(fullname: Name): Symbol = { + var result = getModuleOrClass(fullname.toTypeName) + while (result.isAliasType) result = result.info.typeSymbol + result + } + + private def getModule(fullname: Name): Symbol = + getModuleOrClass(fullname.toTermName) + + private def getModuleOrClass(path: Name): Symbol = + getModuleOrClass(path, path.length) + + private def getModuleOrClass(path: Name, len: Int): Symbol = { + val point = path lastPos('.', len - 1) + val owner = + if (point > 0) getModuleOrClass(path.toTermName, point) + else definitions.RootClass + val name = path subName (point + 1, len) + val sym = owner.info member name + val result = if (path.isTermName) sym.suchThat(_ hasFlag symtab.Flags.MODULE) else sym + if (result != NoSymbol) result + else { + if (settings.debug.value) { log(sym.info); log(sym.info.members) }//debug + if (owner.isRoot && isJavaClass(name.toString)) + definitions.EmptyPackageClass.info decl name + else { + def info(msg: => String) = if (settings.verbose.value) println(msg) + info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass) + MissingRequirementError.notFound((if (path.isTermName) "object " else "class ")+path) + } + } + } + + private def isJavaClass(path: String): Boolean = + try { + val classpath = platform.classPath.asURLs + var classLoader = ScalaClassLoader.fromURLs(classpath) + Class.forName(path, true, classLoader) + true + } catch { + case (_: ClassNotFoundException) | (_: NoClassDefFoundError) | (_: IncompatibleClassChangeError) => + false + } +} diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index d6f57801e7..92a0efff1e 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -61,6 +61,9 @@ trait CompilationUnits { self: Global => /** things to check at end of compilation unit */ val toCheck = new ListBuffer[() => Unit] + /** The features that were already checked for this unit */ + var checkedFeatures = Set[Symbol]() + def position(pos: Int) = source.position(pos) /** The position of a targeted type check @@ -85,12 +88,10 @@ trait CompilationUnits { self: Global => reporter.warning(pos, msg) def deprecationWarning(pos: Position, msg: String) = - if (opt.deprecation) warning(pos, msg) - else currentRun.deprecationWarnings ::= ((pos, msg)) + currentRun.deprecationWarnings.warn(pos, msg) def uncheckedWarning(pos: Position, msg: String) = - if (opt.unchecked) warning(pos, msg) - else currentRun.uncheckedWarnings ::= ((pos, msg)) + currentRun.uncheckedWarnings.warn(pos, msg) def incompleteInputError(pos: Position, msg:String) = reporter.incompleteInputError(pos, msg) @@ -104,10 +105,14 @@ trait CompilationUnits { self: Global => override def toString() = source.toString() def clear() { - fresh = null - body = null + fresh = new FreshNameCreator.Default + body = EmptyTree depends.clear() defined.clear() + synthetics.clear() + toCheck.clear() + checkedFeatures = Set() + icode.clear() } } } diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala index a0c39f71fb..a4a8e1fd11 100644 --- a/src/compiler/scala/tools/nsc/CompileSocket.scala +++ b/src/compiler/scala/tools/nsc/CompileSocket.scala @@ -93,7 +93,7 @@ class CompileSocket extends CompileOutputCommon { /** Start a new server. */ private def startNewServer(vmArgs: String) = { - val cmd = serverCommand(vmArgs split " " toSeq) + val cmd = serverCommand((vmArgs split " ").toSeq) info("[Executing command: %s]" format cmd.mkString(" ")) // Hiding inadequate daemonized implementation from public API for now @@ -206,7 +206,7 @@ class CompileSocket extends CompileOutputCommon { Thread sleep 100 ff.length } - if (Iterator continually check take 50 find (_ > 0) isEmpty) { + if ((Iterator continually check take 50 find (_ > 0)).isEmpty) { ff.delete() fatal("Unable to establish connection to server.") } diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 54bc218912..4c8a27083a 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -47,7 +47,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { /** Creates a help message for a subset of options based on cond */ def createUsageMsg(cond: Setting => Boolean): String = { val baseList = (settings.visibleSettings filter cond).toList sortBy (_.name) - val width = baseList map (_.helpSyntax.length) max + val width = (baseList map (_.helpSyntax.length)).max def format(s: String) = ("%-" + width + "s") format s def helpStr(s: Setting) = { val str = format(s.helpSyntax) + " " + s.helpDescription diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala index b9e9a14adf..f91cb854c6 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala @@ -8,7 +8,7 @@ package scala.tools.nsc import scala.tools.util.PathResolver class GenericRunnerSettings(error: String => Unit) extends Settings(error) { - def classpathURLs = new PathResolver(this) asURLs + def classpathURLs = new PathResolver(this).asURLs val howtorun = ChoiceSetting( diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 5e0c24d304..403b5717b0 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -12,7 +12,7 @@ import compat.Platform.currentTime import scala.tools.util.{ Profiling, PathResolver } import scala.collection.{ mutable, immutable } import io.{ SourceReader, AbstractFile, Path } -import reporters.{ Reporter, ConsoleReporter } +import reporters.{ Reporter => NscReporter, ConsoleReporter } import util.{ NoPosition, Exceptional, ClassPath, SourceFile, NoSourceFile, Statistics, StatisticsInfo, BatchSourceFile, ScriptSourceFile, ShowPickled, ScalaClassLoader, returning } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } import settings.{ AestheticSettings } @@ -31,25 +31,27 @@ import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform } import backend.jvm.GenJVM import backend.opt.{ Inliners, InlineExceptionHandlers, ClosureElimination, DeadCodeElimination } import backend.icode.analysis._ - -class Global(var currentSettings: Settings, var reporter: Reporter) extends SymbolTable - with CompilationUnits - with Plugins - with PhaseAssembly - with Trees - with Reifiers - with TreePrinters - with DocComments - with MacroContext - with symtab.Positions { +import language.postfixOps + +class Global(var currentSettings: Settings, var reporter: NscReporter) extends SymbolTable + with ClassLoaders + with ToolBoxes + with CompilationUnits + with Plugins + with PhaseAssembly + with Trees + with FreeVars + with TreePrinters + with DocComments + with Positions { override def settings = currentSettings - + import definitions.{ findNamedMember, findMemberFromRoot } // alternate constructors ------------------------------------------ - def this(reporter: Reporter) = + def this(reporter: NscReporter) = this(new Settings(err => reporter.error(null, err)), reporter) def this(settings: Settings) = @@ -61,7 +63,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 @@ -78,6 +80,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb // sub-components -------------------------------------------------- /** Generate ASTs */ + type TreeGen = scala.tools.nsc.ast.TreeGen + object gen extends { val global: Global.this.type = Global.this } with TreeGen { @@ -127,7 +131,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 ReifyPrinters { + } with NodePrinters { infolevel = InfoLevel.Verbose } @@ -137,7 +141,6 @@ 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------------------------- @@ -215,7 +218,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb 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)) @@ -319,7 +322,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def showNames = List(showClass, showObject).flatten def showPhase = isActive(settings.Yshow) def showSymbols = settings.Yshowsyms.value - def showTrees = settings.Xshowtrees.value + def showTrees = settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value val showClass = optSetting[String](settings.Xshowcls) map (x => splitClassAndPhase(x, false)) val showObject = optSetting[String](settings.Xshowobj) map (x => splitClassAndPhase(x, true)) @@ -344,7 +347,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb override protected val etaExpandKeepsStar = settings.etaExpandKeepsStar.value // Here comes another one... override protected val enableTypeVarExperimentals = ( - settings.Xexperimental.value || settings.YvirtPatmat.value + settings.Xexperimental.value || !settings.XoldPatmat.value ) // True if -Xscript has been set, indicating a script run. @@ -936,6 +939,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb inform("[running phase " + ph.name + " on " + currentRun.size + " compilation units]") } + /** Collects for certain classes of warnings during this run. */ + class ConditionalWarning(what: String, option: Settings#BooleanSetting) { + val warnings = new mutable.ListBuffer[(Position, String)] + def warn(pos: Position, msg: String) = + if (option.value) reporter.warning(pos, msg) + else warnings += ((pos, msg)) + def summarize() = + if (option.isDefault && warnings.nonEmpty) + reporter.warning(NoPosition, "there were %d %s warnings; re-run with %s for details".format(warnings.size, what, option.name)) + } + /** A Run is a single execution of the compiler on a sets of units */ class Run { @@ -947,9 +961,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** The currently compiled unit; set from GlobalPhase */ var currentUnit: CompilationUnit = NoCompilationUnit - /** Counts for certain classes of warnings during this run. */ - var deprecationWarnings: List[(Position, String)] = Nil - var uncheckedWarnings: List[(Position, String)] = Nil + val deprecationWarnings = new ConditionalWarning("deprecation", settings.deprecation) + val uncheckedWarnings = new ConditionalWarning("unchecked", settings.unchecked) + val featureWarnings = new ConditionalWarning("feature", settings.feature) + val allConditionalWarnings = List(deprecationWarnings, uncheckedWarnings, featureWarnings) + + var reportedFeature = Set[Symbol]() /** A flag whether macro expansions failed */ var macroExpansionFailed = false @@ -1108,7 +1125,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb 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. */ @@ -1239,12 +1256,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb } } else { - def warn(count: Int, what: String, option: Settings#BooleanSetting) = ( - if (option.isDefault && count > 0) - warning("there were %d %s warnings; re-run with %s for details".format(count, what, option.name)) - ) - warn(deprecationWarnings.size, "deprecation", settings.deprecation) - warn(uncheckedWarnings.size, "unchecked", settings.unchecked) + allConditionalWarnings foreach (_.summarize) + if (macroExpansionFailed) warning("some macros could not be expanded and code fell back to overridden methods;"+ "\nrecompiling with generated classfiles on the classpath might help.") @@ -1581,7 +1594,7 @@ object Global { * This allows the use of a custom Global subclass with the software which * wraps Globals, such as scalac, fsc, and the repl. */ - def fromSettings(settings: Settings, reporter: Reporter): Global = { + def fromSettings(settings: Settings, reporter: NscReporter): Global = { // !!! The classpath isn't known until the Global is created, which is too // late, so we have to duplicate it here. Classpath is too tightly coupled, // it is a construct external to the compiler and should be treated as such. @@ -1589,7 +1602,7 @@ object Global { val loader = ScalaClassLoader.fromURLs(new PathResolver(settings).result.asURLs, parentLoader) val name = settings.globalClass.value val clazz = Class.forName(name, true, loader) - val cons = clazz.getConstructor(classOf[Settings], classOf[Reporter]) + val cons = clazz.getConstructor(classOf[Settings], classOf[NscReporter]) cons.newInstance(settings, reporter).asInstanceOf[Global] } @@ -1597,7 +1610,7 @@ object Global { /** A global instantiated this way honors -Yglobal-class setting, and * falls back on calling the Global constructor directly. */ - def apply(settings: Settings, reporter: Reporter): Global = { + def apply(settings: Settings, reporter: NscReporter): Global = { val g = ( if (settings.globalClass.isDefault) null else try fromSettings(settings, reporter) catch { case x => diff --git a/src/compiler/scala/tools/nsc/MacroContext.scala b/src/compiler/scala/tools/nsc/MacroContext.scala deleted file mode 100644 index 9ea1f87125..0000000000 --- a/src/compiler/scala/tools/nsc/MacroContext.scala +++ /dev/null @@ -1,10 +0,0 @@ -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/PhaseAssembly.scala b/src/compiler/scala/tools/nsc/PhaseAssembly.scala index a627b982b6..a9e029e485 100644 --- a/src/compiler/scala/tools/nsc/PhaseAssembly.scala +++ b/src/compiler/scala/tools/nsc/PhaseAssembly.scala @@ -8,6 +8,7 @@ package scala.tools.nsc import java.io.{ BufferedWriter, FileWriter } import scala.collection.mutable +import language.postfixOps /** * PhaseAssembly diff --git a/src/compiler/scala/tools/nsc/Phases.scala b/src/compiler/scala/tools/nsc/Phases.scala index d18495458c..1fa576afb6 100644 --- a/src/compiler/scala/tools/nsc/Phases.scala +++ b/src/compiler/scala/tools/nsc/Phases.scala @@ -7,6 +7,7 @@ package scala.tools.nsc import symtab.Flags import util.TableDef +import language.postfixOps object Phases { val MaxPhases = 64 diff --git a/src/compiler/scala/tools/nsc/ReflectGlobal.scala b/src/compiler/scala/tools/nsc/ReflectGlobal.scala index 3132a9987d..68a6a4d336 100644 --- a/src/compiler/scala/tools/nsc/ReflectGlobal.scala +++ b/src/compiler/scala/tools/nsc/ReflectGlobal.scala @@ -5,7 +5,7 @@ import reporters.Reporter /** A version of Global that uses reflection to get class * infos, instead of reading class or source files. */ -class ReflectGlobal(currentSettings: Settings, reporter: Reporter) +class ReflectGlobal(currentSettings: Settings, reporter: Reporter, var classLoader: ClassLoader) extends Global(currentSettings, reporter) with reflect.runtime.SymbolTable { override def transformedType(sym: Symbol) = @@ -13,4 +13,9 @@ class ReflectGlobal(currentSettings: Settings, reporter: Reporter) uncurry.transformInfo(sym, refChecks.transformInfo(sym, sym.info))) + override def staticClass(fullname: String) = + super[SymbolTable].staticClass(fullname) + + override def staticModule(fullname: String) = + super[SymbolTable].staticModule(fullname) } diff --git a/src/compiler/scala/tools/nsc/ReflectMain.scala b/src/compiler/scala/tools/nsc/ReflectMain.scala index 7167f5aa27..f9a18abc25 100644 --- a/src/compiler/scala/tools/nsc/ReflectMain.scala +++ b/src/compiler/scala/tools/nsc/ReflectMain.scala @@ -1,7 +1,16 @@ package scala.tools.nsc +import util.ScalaClassLoader +import tools.util.PathResolver +import util.ClassPath.DefaultJavaContext + object ReflectMain extends Driver { - override def newCompiler(): Global = new ReflectGlobal(settings, reporter) + private def reflectionClassloaderFromSettings(settings: Settings) = { + val classpath = new PathResolver(settings).result + ScalaClassLoader.fromURLs(classpath.asURLs, getClass.getClassLoader) + } + + override def newCompiler(): Global = new ReflectGlobal(settings, reporter, reflectionClassloaderFromSettings(settings)) }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/ToolBoxes.scala b/src/compiler/scala/tools/nsc/ToolBoxes.scala new file mode 100644 index 0000000000..eb298833b8 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ToolBoxes.scala @@ -0,0 +1,85 @@ +package scala.tools.nsc + +import util.ScalaClassLoader + +trait ToolBoxes { self: Global => + + import self.{Reporter => ApiReporter} + + def mkToolBox(reporter: ApiReporter = mkSilentReporter(), options: String = "") = new ToolBox(reporter, options) + + class ToolBox(val reporter: ApiReporter, val options: String) extends AbsToolBox { + def typeCheck(tree0: Tree, pt: Type = WildcardType, freeTypes: Map[FreeType, Type] = Map[FreeType, Type](), silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { + val tree = substituteFreeTypes(tree0, freeTypes) + val currentTyper = typer + val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) + val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) + def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) + wrapper(currentTyper.silent(_.typed(tree, analyzer.EXPRmode, pt)) match { + case analyzer.SilentResultValue(result) => + result + case error @ analyzer.SilentTypeError(_) => + if (!silent) throw new ToolBoxError(this, "reflective typecheck has failed: %s".format(error.err.errMsg)) + EmptyTree + }) + } + + def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false): Tree = + // todo. implement this + ??? + + def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, reportAmbiguous: Boolean = true): Tree = + // todo. implement this + ??? + + def resetAllAttrs[T <: Tree](tree: T): T = + self.resetAllAttrs(tree) + + def resetLocalAttrs[T <: Tree](tree: T): T = + self.resetLocalAttrs(tree) + + def runExpr(tree0: Tree, freeTypes: Map[FreeType, Type] = Map[FreeType, Type]()): Any = { + var tree = substituteFreeTypes(tree0, freeTypes) + // need to reset the tree, otherwise toolbox will refuse to work with it + tree = resetAllAttrs(tree0.duplicate) + val imported = importer.importTree(tree) + val toolBox = libraryClasspathMirror.mkToolBox(reporter.asInstanceOf[libraryClasspathMirror.Reporter], options) + try toolBox.runExpr(imported) + catch { + case ex: toolBox.ToolBoxError => + throw new ToolBoxError(this, ex.message, ex.cause) + } + } + + // [Eugene] how do I make this work without casts? + // private lazy val importer = libraryClasspathMirror.mkImporter(self) + private lazy val importer = libraryClasspathMirror.mkImporter(self).asInstanceOf[libraryClasspathMirror.Importer { val from: self.type }] + + private lazy val libraryClasspathMirror = { + if (self.forMSIL) + throw new UnsupportedOperationException("Scala reflection not available on this platform") + + val libraryClassLoader = { + val classpath = self.classPath.asURLs + var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + + // [Eugene] a heuristic to detect REPL + if (self.settings.exposeEmptyPackage.value) { + import scala.tools.nsc.interpreter._ + val virtualDirectory = self.settings.outputDirs.getSingleOutput.get + loader = new AbstractFileClassLoader(virtualDirectory, loader) {} + } + + loader + } + + new scala.reflect.runtime.Mirror(libraryClassLoader) + } + + class ToolBoxError(val toolBox: ToolBox, val message: String, val cause: Throwable = null) extends Throwable(message, cause) + + object ToolBoxError extends ToolBoxErrorExtractor { + def unapply(error: ToolBoxError): Option[(ToolBox, String)] = Some((error.toolBox, error.message)) + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index 456e7eae9e..8e7eeed3cc 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package ast import symtab._ -import reporters.Reporter +import reporters.{Reporter => NscReporter} import util.{Position, NoPosition} import util.DocStrings._ import scala.reflect.internal.Chars._ @@ -21,7 +21,7 @@ trait DocComments { self: Global => var cookedDocComments = Map[Symbol, String]() - def reporter: Reporter + def reporter: NscReporter /** The raw doc comment map */ val docComments = mutable.HashMap[Symbol, DocComment]() @@ -252,7 +252,7 @@ trait DocComments { self: Global => def replaceInheritdoc(childSection: String, parentSection: => String) = if (childSection.indexOf("@inheritdoc") == -1) childSection - else + else childSection.replaceAllLiterally("@inheritdoc", parentSection) def getParentSection(section: (Int, Int)): String = { @@ -275,9 +275,9 @@ trait DocComments { self: Global => } child.substring(section._1, section._1 + 7) match { - case param@("@param "|"@tparam"|"@throws") => + case param@("@param "|"@tparam"|"@throws") => sectionString(extractSectionParam(child, section), parentNamedParams(param.trim)) - case _ => + case _ => sectionString(extractSectionTag(child, section), parentTagMap) } } @@ -367,7 +367,7 @@ trait DocComments { self: Global => case vname => lookupVariable(vname, site) match { case Some(replacement) => replaceWith(replacement) - case None => reporter.warning(sym.pos, "Variable " + vname + " undefined in comment for " + sym) + case None => reporter.warning(sym.pos, "Variable " + vname + " undefined in comment for " + sym + " in " + site) } } } diff --git a/src/compiler/scala/tools/nsc/ast/FreeVars.scala b/src/compiler/scala/tools/nsc/ast/FreeVars.scala new file mode 100644 index 0000000000..1bf36e8bf2 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/FreeVars.scala @@ -0,0 +1,26 @@ +package scala.tools.nsc +package ast + +trait FreeVars extends reflect.internal.FreeVars { self: Global => + + import self._ + import definitions._ + import treeInfo._ + + def logFreeVars(position: Position, reified: Tree): Unit = { + if (settings.logFreeTerms.value || settings.logFreeTypes.value) { + reified match { + case Reified(_, symbolTable, _) => + // logging free vars only when they are untyped prevents avalanches of duplicate messages + symbolTable foreach { + case FreeTermDef(_, _, binding, origin) if settings.logFreeTerms.value && binding.tpe == null => + reporter.echo(position, "free term: %s %s".format(showRaw(binding), origin)) + case FreeTypeDef(_, _, binding, origin) if settings.logFreeTypes.value && binding.tpe == null => + reporter.echo(position, "free type: %s %s".format(showRaw(binding), origin)) + case _ => + // do nothing + } + } + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index acbdcd501f..108c8fcbfa 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -8,6 +8,7 @@ package ast import compat.Platform.EOL import symtab.Flags._ +import language.postfixOps /** The object `nodePrinter` converts the internal tree * representation to a string. @@ -27,24 +28,24 @@ abstract class NodePrinters { def nodeToString: Tree => String = if (sys.props contains "scala.colors") nodeToColorizedString else nodeToRegularString - + object nodeToRegularString extends DefaultPrintAST with (Tree => String) { def apply(tree: Tree) = stringify(tree) } - + object nodeToColorizedString extends ColorPrintAST with (Tree => String) { def apply(tree: Tree) = stringify(tree) } trait ColorPrintAST extends DefaultPrintAST { import scala.tools.util.color._ - + def keywordColor = Cyan def typeColor = Yellow def termColor = Blue def flagColor = Red def literalColor = Green - + override def showFlags(tree: MemberDef) = super.showFlags(tree) in flagColor.bright @@ -81,7 +82,7 @@ abstract class NodePrinters { if (tpe == null || tpe == NoType) "" else "tree.tpe=" + tpe } - + def showAttributes(tree: Tree): String = { if (infolevel == InfoLevel.Quiet) "" else { @@ -90,7 +91,7 @@ abstract class NodePrinters { } } } - + trait PrintAST { private val buf = new StringBuilder private var level = 0 @@ -101,7 +102,7 @@ abstract class NodePrinters { def showLiteral(lit: Literal): String def showTypeTree(tt: TypeTree): String def showAttributes(tree: Tree): String // symbol and type - + def showRefTreeName(tree: Tree): String = tree match { case SelectFromTypeTree(qual, name) => showRefTreeName(qual) + "#" + showName(name) case Select(qual, name) => showRefTreeName(qual) + "." + showName(name) @@ -122,8 +123,14 @@ abstract class NodePrinters { def stringify(tree: Tree): String = { buf.clear() - level = 0 - traverse(tree) + if (settings.XshowtreesStringified.value) buf.append(tree.toString + EOL) + if (settings.XshowtreesCompact.value) { + // todo. colors for compact representation + buf.append(showRaw(tree)) + } else { + level = 0 + traverse(tree) + } buf.toString } def traverseAny(x: Any) { @@ -134,7 +141,7 @@ abstract class NodePrinters { } } def println(s: String) = printLine(s, "") - + def printLine(value: String, comment: String) { buf append " " * level buf append value @@ -183,7 +190,7 @@ abstract class NodePrinters { traverseList("Nil", "argument")(args) } } - + def printMultiline(tree: Tree)(body: => Unit) { printMultiline(tree.printingPrefix, showAttributes(tree))(body) } @@ -299,7 +306,7 @@ abstract class NodePrinters { } case Template(parents, self, body) => printMultiline(tree) { - val ps0 = parents map { p => + val ps0 = parents map { p => if (p.tpe eq null) p match { case x: RefTree => showRefTree(x) case x => "" + x @@ -339,7 +346,7 @@ abstract class NodePrinters { traverseList("[]", "type parameter")(tparams) traverse(rhs) } - + case PackageDef(pid, stats) => printMultiline("PackageDef", "")(pid :: stats foreach traverse) diff --git a/src/compiler/scala/tools/nsc/ast/Positions.scala b/src/compiler/scala/tools/nsc/ast/Positions.scala new file mode 100644 index 0000000000..83a67cfbe3 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/Positions.scala @@ -0,0 +1,44 @@ +package scala.tools.nsc +package ast + +import scala.tools.nsc.util.{ SourceFile, Position, OffsetPosition, NoPosition } + +trait Positions extends scala.reflect.internal.Positions { + self: Global => + + def rangePos(source: SourceFile, start: Int, point: Int, end: Int) = + new OffsetPosition(source, point) + + def validatePositions(tree: Tree) {} + + // [Eugene] disabling this for now. imo it doesn't justify pollution of the public API + // override def _checkSetAnnotation(tree: Tree, annot: TreeAnnotation): Unit = { + // if (tree.pos != NoPosition && tree.pos != annot.pos) debugwarn("Overwriting annotation "+ tree.annotation +" of tree "+ tree +" with annotation "+ annot) + // // if ((tree.annotation.isInstanceOf[scala.tools.nsc.util.Position] || !annot.isInstanceOf[scala.tools.nsc.util.Position]) && tree.isInstanceOf[Block]) + // // println("Updating block from "+ tree.annotation +" to "+ annot) + // } + + class ValidatingPosAssigner extends PosAssigner { + var pos: Position = _ + override def traverse(t: Tree) { + if (t eq EmptyTree) () + else if (t.pos == NoPosition) super.traverse(t setPos pos) + else if (globalPhase.id <= currentRun.picklerPhase.id) { + // When we prune due to encountering a position, traverse the + // pruned children so we can warn about those lacking positions. + t.children foreach { c => + if ((c eq EmptyTree) || (c eq emptyValDef)) () + else if (c.pos == NoPosition) { + reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase) + inform("parent: " + treeSymStatus(t)) + inform(" child: " + treeSymStatus(c) + "\n") + } + } + } + } + } + + override protected[this] lazy val posAssigner: PosAssigner = + if (settings.Yrangepos.value && settings.debug.value || settings.Yposdebug.value) new ValidatingPosAssigner + else new DefaultPosAssigner +} diff --git a/src/compiler/scala/tools/nsc/ast/Reifiers.scala b/src/compiler/scala/tools/nsc/ast/Reifiers.scala deleted file mode 100644 index 04468a096d..0000000000 --- a/src/compiler/scala/tools/nsc/ast/Reifiers.scala +++ /dev/null @@ -1,761 +0,0 @@ -/* 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)), Literal(Constant(sym.isClass))) - ) - ) - } - - /** - * 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 deleted file mode 100644 index fce59bb099..0000000000 --- a/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala +++ /dev/null @@ -1,75 +0,0 @@ -/* 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/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index 3302c11127..b4beb231ab 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -18,6 +18,7 @@ import scala.concurrent.Lock import scala.text._ import symtab.Flags._ import symtab.SymbolTable +import language.implicitConversions /** * Tree browsers can show the AST in a graphical and interactive diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index 2b75925d9a..283bdecf26 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -9,6 +9,7 @@ package ast import PartialFunction._ import symtab.Flags +import language.implicitConversions /** A DSL for generating scala code. The goal is that the * code generating code should look a lot like the code it diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index ad26ccad5e..d3e64811d3 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -9,6 +9,7 @@ package ast import scala.collection.mutable.ListBuffer import symtab.Flags._ import symtab.SymbolTable +import language.postfixOps /** XXX to resolve: TreeGen only assumes global is a SymbolTable, but * TreeDSL at the moment expects a Global. Can we get by with SymbolTable? @@ -71,14 +72,6 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { Annotated(Ident(nme.synthSwitch), expr) } - // 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 - } - } - def hasSynthCaseSymbol(t: Tree) = (t.symbol ne null) && (t.symbol hasFlag (CASE | SYNTHETIC)) // TODO: would be so much nicer if we would know during match-translation (i.e., type checking) @@ -86,9 +79,11 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { 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 caseVirtualizedMatchOpt(orig: Tree, prologue: List[Tree], cases: List[Tree], matchEndDef: Tree, wrap: Tree => Tree): Tree = unknownTree(orig) - def apply(matchExpr: Tree): Tree = (matchExpr: @unchecked) match { + def genVirtualizedMatch(prologue: List[Tree], cases: List[Tree], matchEndDef: Tree): Tree = Block(prologue ++ cases, matchEndDef) + + def apply(matchExpr: Tree): Tree = matchExpr match { // old-style match or virtpatmat switch case Match(selector, cases) => // println("simple match: "+ (selector, cases) + "for:\n"+ matchExpr ) caseMatch(matchExpr, selector, cases, identity) @@ -99,11 +94,15 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { 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) + case Block(stats, matchEndDef) if opt.virtPatmat && (stats forall hasSynthCaseSymbol) => + // the assumption is once we encounter a case, the remainder of the block will consist of cases + // the prologue may be empty, usually it is the valdef that stores the scrut + val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + caseVirtualizedMatchOpt(matchExpr, prologue, cases, matchEndDef, 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 Block(outerStats, orig@Block(stats, matchEndDef)) if opt.virtPatmat && (stats forall hasSynthCaseSymbol) => + val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + caseVirtualizedMatchOpt(matchExpr, prologue, cases, matchEndDef, m => copyBlock(matchExpr, outerStats, m)) case other => unknownTree(other) } @@ -119,35 +118,6 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { } } - 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 - } - 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( @@ -207,22 +177,6 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { def mkSysErrorCall(message: String): Tree = mkMethodCall(Sys_error, List(Literal(Constant(message)))) - /** A creator for a call to a scala.reflect.Manifest or ClassManifest factory method. - * - * @param full full or partial manifest (target will be Manifest or ClassManifest) - * @param constructor name of the factory method (e.g. "classType") - * @param tparg the type argument - * @param args value arguments - * @return the tree - */ - def mkManifestFactoryCall(full: Boolean, constructor: String, tparg: Type, args: List[Tree]): Tree = - mkMethodCall( - if (full) FullManifestModule else PartialManifestModule, - newTermName(constructor), - List(tparg), - args - ) - /** Make a synchronized block on 'monitor'. */ def mkSynchronized(monitor: Tree, body: Tree): Tree = Apply(Select(monitor, Object_synchronized), List(body)) diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 43c231cf2d..04452c68e5 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -13,6 +13,7 @@ import scala.reflect.internal.Flags.PARAM import scala.reflect.internal.Flags.PARAMACCESSOR import scala.reflect.internal.Flags.PRESUPER import scala.reflect.internal.Flags.TRAIT +import scala.compat.Platform.EOL trait Trees extends reflect.internal.Trees { self: Global => @@ -33,30 +34,6 @@ trait Trees extends reflect.internal.Trees { self: Global => ) } - class ValidatingPosAssigner extends PosAssigner { - var pos: Position = _ - override def traverse(t: Tree) { - if (t eq EmptyTree) () - else if (t.pos == NoPosition) super.traverse(t setPos pos) - else if (globalPhase.id <= currentRun.picklerPhase.id) { - // When we prune due to encountering a position, traverse the - // pruned children so we can warn about those lacking positions. - t.children foreach { c => - if ((c eq EmptyTree) || (c eq emptyValDef)) () - else if (c.pos == NoPosition) { - reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase) - inform("parent: " + treeSymStatus(t)) - inform(" child: " + treeSymStatus(c) + "\n") - } - } - } - } - } - - override protected[this] lazy val posAssigner: PosAssigner = - if (settings.Yrangepos.value && settings.debug.value || settings.Yposdebug.value) new ValidatingPosAssigner - else new DefaultPosAssigner - // --- additional cases -------------------------------------------------------- /** Only used during parsing */ case class Parens(args: List[Tree]) extends Tree @@ -81,18 +58,11 @@ trait Trees extends reflect.internal.Trees { self: Global => case class InjectDerivedValue(arg: Tree) extends SymTree + class PostfixSelect(qual: Tree, name: Name) extends Select(qual, name) + /** emitted by typer, eliminated by refchecks */ case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends TypTree - /** Marks underlying reference to id as boxed. - * @pre: id must refer to a captured variable - * A reference such marked will refer to the boxed entity, no dereferencing - * with `.elem` is done on it. - * This tree node can be emitted by macros such as reify that call markBoxedReference. - * It is eliminated in LambdaLift, where the boxing conversion takes place. - */ - case class ReferenceToBoxed(idt: Ident) extends TermTree - // --- factory methods ---------------------------------------------------------- /** Generates a template with constructor corresponding to @@ -118,7 +88,7 @@ trait Trees extends reflect.internal.Trees { self: Global => // create parameters for <init> as synthetic trees. var vparamss1 = vparamss map (vps => vps.map { vd => - atPos(focusPos(vd.pos)) { + atPos(vd.pos.focus) { ValDef( Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM | BYNAMEPARAM) | PARAM | PARAMACCESSOR) withAnnotations vd.mods.annotations, vd.name, vd.tpt.duplicate, vd.rhs.duplicate) @@ -130,7 +100,7 @@ trait Trees extends reflect.internal.Trees { self: Global => // !!! 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 + tpt = atPos(vdef.pos.focus)(TypeTree() setOriginal tpt setPos tpt.pos.focus), // atPos in case rhs = EmptyTree ) } @@ -198,8 +168,6 @@ trait Trees extends reflect.internal.Trees { self: Global => traverser.traverse(qualifier) case InjectDerivedValue(arg) => traverser.traverse(arg) - case ReferenceToBoxed(idt) => - traverser.traverse(idt) case TypeTreeWithDeferredRefCheck() => // (and rewrap the result? how to update the deferred check? would need to store wrapped tree instead of returning it from check) case _ => super.xtraverse(traverser, tree) @@ -209,7 +177,6 @@ trait Trees extends reflect.internal.Trees { self: Global => def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray def InjectDerivedValue(tree: Tree, arg: Tree): InjectDerivedValue - def ReferenceToBoxed(tree: Tree, idt: Ident): ReferenceToBoxed def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck } @@ -223,8 +190,6 @@ trait Trees extends reflect.internal.Trees { self: Global => new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree) def InjectDerivedValue(tree: Tree, arg: Tree) = new InjectDerivedValue(arg) - def ReferenceToBoxed(tree: Tree, idt: Ident) = - new ReferenceToBoxed(idt).copyAttrs(tree) def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match { case dc@TypeTreeWithDeferredRefCheck() => new TypeTreeWithDeferredRefCheck()(dc.check).copyAttrs(tree) } @@ -246,11 +211,6 @@ trait Trees extends reflect.internal.Trees { self: Global => if (arg0 == arg) => t case _ => this.treeCopy.InjectDerivedValue(tree, arg) } - def ReferenceToBoxed(tree: Tree, idt: Ident) = tree match { - case t @ ReferenceToBoxed(idt0) - if (idt0 == idt) => t - case _ => this.treeCopy.ReferenceToBoxed(tree, idt) - } def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match { case t @ TypeTreeWithDeferredRefCheck() => t case _ => this.treeCopy.TypeTreeWithDeferredRefCheck(tree) @@ -277,9 +237,6 @@ trait Trees extends reflect.internal.Trees { self: Global => case InjectDerivedValue(arg) => transformer.treeCopy.InjectDerivedValue( tree, transformer.transform(arg)) - case ReferenceToBoxed(idt) => - transformer.treeCopy.ReferenceToBoxed( - tree, transformer.transform(idt) match { case idt1: Ident => idt1 }) case TypeTreeWithDeferredRefCheck() => transformer.treeCopy.TypeTreeWithDeferredRefCheck(tree) } @@ -296,8 +253,9 @@ trait Trees extends reflect.internal.Trees { self: Global => // def resetAllAttrs[A<:Tree](x:A): A = { new ResetAttrsTraverser().traverse(x); x } // def resetLocalAttrs[A<:Tree](x:A): A = { new ResetLocalAttrsTraverser().traverse(x); x } - def resetAllAttrs[A<:Tree](x:A): A = new ResetAttrs(false).transform(x) - def resetLocalAttrs[A<:Tree](x:A): A = new ResetAttrs(true).transform(x) + def resetAllAttrs[A <: Tree](x: A, leaveAlone: Tree => Boolean = null): A = new ResetAttrs(false, leaveAlone).transform(x) + def resetLocalAttrs[A <: Tree](x: A, leaveAlone: Tree => Boolean = null): A = new ResetAttrs(true, leaveAlone).transform(x) + def resetLocalAttrsKeepLabels[A<:Tree](x: A, leaveAlone: Tree => Boolean = null): A = new ResetAttrs(true, leaveAlone, true).transform(x) /** A transformer which resets symbol and tpe fields of all nodes in a given tree, * with special treatment of: @@ -308,7 +266,7 @@ 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) { + private class ResetAttrs(localOnly: Boolean, leaveAlone: Tree => Boolean = null, keepLabels: Boolean = false) { val debug = settings.debug.value val trace = scala.tools.nsc.util.trace when debug @@ -328,6 +286,12 @@ trait Trees extends reflect.internal.Trees { self: Global => registerLocal(sym) registerLocal(sym.sourceModule) registerLocal(sym.moduleClass) + registerLocal(sym.companionClass) + registerLocal(sym.companionModule) + sym match { + case sym: TermSymbol => registerLocal(sym.referenced) + case _ => ; + } } } @@ -335,10 +299,8 @@ trait Trees extends reflect.internal.Trees { self: Global => tree match { case _: DefTree | Function(_, _) | Template(_, _, _) => markLocal(tree) - case _ if tree.symbol.isInstanceOf[FreeVar] => - markLocal(tree) case _ => - ; + tree } super.traverse(tree) @@ -346,43 +308,48 @@ trait Trees extends reflect.internal.Trees { self: Global => } class Transformer extends self.Transformer { - override def transform(tree: Tree): Tree = super.transform { - tree match { - case tpt: TypeTree => - if (tpt.original != null) { - transform(tpt.original) - } else { - if (tpt.tpe != null && (tpt.wasEmpty || (tpt.tpe exists (tp => locals contains tp.typeSymbol)))) - tpt.tpe = null - tree + override def transform(tree: Tree): Tree = { + if (leaveAlone != null && leaveAlone(tree)) + tree + else + super.transform { + tree match { + case tpt: TypeTree => + if (tpt.original != null) { + transform(tpt.original) + } else { + if (tpt.tpe != null && (tpt.wasEmpty || (tpt.tpe exists (tp => locals contains tp.typeSymbol)))) + tpt.tpe = null + tree + } + case TypeApply(fn, args) if args map transform exists (_.isEmpty) => + transform(fn) + case This(_) if tree.symbol != null && tree.symbol.isPackageClass => + tree + case EmptyTree => + tree + case _ => + if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)) && !(keepLabels && tree.symbol.isLabel)) + tree.symbol = NoSymbol + tree.tpe = null + tree } - case TypeApply(fn, args) if args map transform exists (_.isEmpty) => - transform(fn) - case This(_) if tree.symbol != null && tree.symbol.isPackageClass => - tree - case EmptyTree => - tree - case _ => - if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol))) - tree.symbol = NoSymbol - tree.tpe = null - tree - } + } } } def transform[T <: Tree](x: T): T = { + if (localOnly) new MarkLocals().traverse(x) - if (debug) { + if (localOnly && debug) { assert(locals.size == orderedLocals.size) - val eoln = System.getProperty("line.separator") - val msg = orderedLocals.toList filter {_ != NoSymbol} map {" " + _} mkString eoln + val msg = orderedLocals.toList filter {_ != NoSymbol} map {" " + _} mkString EOL trace("locals (%d total): %n".format(orderedLocals.size))(msg) } val x1 = new Transformer().transform(x) - assert(x.getClass isInstance x1) + assert(x.getClass isInstance x1, x1.getClass) x1.asInstanceOf[T] } } @@ -397,4 +364,4 @@ trait Trees extends reflect.internal.Trees { self: Global => */ - }
\ No newline at end of file + } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index e7e3eaabf5..eb4deeeee2 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -70,6 +70,9 @@ trait ParsersCommon extends ScannersCommon { @inline final def inBracesOrNil[T](body: => List[T]): List[T] = inBracesOrError(body, Nil) @inline final def inBracesOrUnit[T](body: => Tree): Tree = inBracesOrError(body, Literal(Constant())) + @inline final def dropAnyBraces[T](body: => T): T = + if (in.token == LBRACE) inBraces(body) + else body @inline final def inBrackets[T](body: => T): T = { accept(LBRACKET) @@ -1106,7 +1109,7 @@ self => * }}} * @note The returned tree does not yet have a position */ - def literal(isNegated: Boolean = false): Tree = { + def literal(isNegated: Boolean = false, inPattern: Boolean = false): Tree = { def finish(value: Any): Tree = { val t = Literal(Constant(value)) in.nextToken() @@ -1115,7 +1118,7 @@ self => if (in.token == SYMBOLLIT) Apply(scalaDot(nme.Symbol), List(finish(in.strVal))) else if (in.token == INTERPOLATIONID) - interpolatedString() + interpolatedString(inPattern) else finish(in.token match { case CHARLIT => in.charVal case INTLIT => in.intVal(isNegated).toInt @@ -1141,7 +1144,7 @@ self => } } - private def interpolatedString(): Tree = atPos(in.offset) { + private def interpolatedString(inPattern: Boolean = false): Tree = atPos(in.offset) { val start = in.offset val interpolator = in.name @@ -1151,8 +1154,11 @@ self => while (in.token == STRINGPART) { partsBuf += literal() exprBuf += { - if (in.token == IDENTIFIER) atPos(in.offset)(Ident(ident())) - else expr() + if (inPattern) dropAnyBraces(pattern()) + else { + if (in.token == IDENTIFIER) atPos(in.offset)(Ident(ident())) + else expr() + } } } if (in.token == STRINGLIT) partsBuf += literal() @@ -1456,11 +1462,12 @@ self => return reduceStack(true, base, top, 0, true) top = next } else { + // postfix expression val topinfo = opstack.head opstack = opstack.tail val od = stripParens(reduceStack(true, base, topinfo.operand, 0, true)) return atPos(od.pos.startOrPoint, topinfo.offset) { - Select(od, topinfo.operator.encode) + new PostfixSelect(od, topinfo.operator.encode) } } } @@ -1771,7 +1778,9 @@ self => * }}} */ def pattern2(): Tree = { + val nameOffset = in.offset val p = pattern3() + if (in.token != AT) p else p match { case Ident(nme.WILDCARD) => @@ -1834,7 +1843,7 @@ self => case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => t match { case Ident(nme.MINUS) => - return atPos(start) { literal(isNegated = true) } + return atPos(start) { literal(isNegated = true, inPattern = true) } case _ => } case _ => @@ -1852,7 +1861,7 @@ self => atPos(start, start) { Ident(nme.WILDCARD) } case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | INTERPOLATIONID | SYMBOLLIT | TRUE | FALSE | NULL => - atPos(start) { literal() } + atPos(start) { literal(inPattern = true) } case LPAREN => atPos(start)(makeParens(noSeq.patterns())) case XMLSTART => @@ -2421,10 +2430,10 @@ self => */ /** {{{ - * FunDef ::= FunSig `:' Type `=' Expr - * | FunSig [nl] `{' Block `}' - * | this ParamClause ParamClauses (`=' ConstrExpr | [nl] ConstrBlock) - * | `macro' FunSig [`:' Type] `=' Expr + * FunDef ::= FunSig [`:' Type] `=' [`macro'] Expr + * | FunSig [nl] `{' Block `}' + * | `this' ParamClause ParamClauses + * (`=' ConstrExpr | [nl] ConstrBlock) * FunDcl ::= FunSig [`:' Type] * FunSig ::= id [FunTypeParamClause] ParamClauses * }}} @@ -2444,18 +2453,14 @@ self => } else { val nameOffset = in.offset + val isBackquoted = in.token == BACKQUOTED_IDENT val name = ident() - if (name == nme.macro_ && isIdent && settings.Xmacros.value) - funDefRest(start, in.offset, mods | Flags.MACRO, ident()) - else - funDefRest(start, nameOffset, mods, name) + funDefRest(start, nameOffset, mods, name) } } def funDefRest(start: Int, nameOffset: Int, mods: Modifiers, name: Name): Tree = { val result = atPos(start, if (name.toTermName == nme.ERROR) start else nameOffset) { - val isMacro = mods hasFlag Flags.MACRO - val isTypeMacro = isMacro && name.isTypeName var newmods = mods // contextBoundBuf is for context bounded type parameters of the form // [T : B] or [T : => B]; it contains the equivalent implicit parameter type, @@ -2463,12 +2468,10 @@ self => val contextBoundBuf = new ListBuffer[Tree] val tparams = typeParamClauseOpt(name, contextBoundBuf) val vparamss = paramClauses(name, contextBoundBuf.toList, false) - if (!isMacro) newLineOptWhenFollowedBy(LBRACE) - var restype = if (isTypeMacro) TypeTree() else fromWithinReturnType(typedOpt()) + newLineOptWhenFollowedBy(LBRACE) + var restype = fromWithinReturnType(typedOpt()) val rhs = - if (isMacro) - equalsExpr() - else if (isStatSep || in.token == RBRACE) { + if (isStatSep || in.token == RBRACE) { if (restype.isEmpty) restype = scalaUnitConstr newmods |= Flags.DEFERRED EmptyTree @@ -2476,10 +2479,17 @@ self => restype = scalaUnitConstr blockExpr() } else { - if (name == nme.macro_ && isIdent && in.token != EQUALS) { - warning("this syntactically invalid code resembles a macro definition. have you forgotten to enable -Xmacros?") + if (in.token == EQUALS) { + in.nextTokenAllow(nme.MACROkw) + if (settings.Xmacros.value && in.token == MACRO || // [Martin] Xmacros can be retired now + in.token == IDENTIFIER && in.name == nme.MACROkw) { + in.nextToken() + newmods |= Flags.MACRO + } + } else { + accept(EQUALS) } - equalsExpr() + expr() } DefDef(newmods, name, tparams, vparamss, restype, rhs) } @@ -2529,7 +2539,7 @@ self => /** {{{ * TypeDef ::= type Id [TypeParamClause] `=' Type - * | `macro' FunSig `=' Expr + * | FunSig `=' Expr * TypeDcl ::= type Id [TypeParamClause] TypeBounds * }}} */ @@ -2537,22 +2547,20 @@ self => in.nextToken() newLinesOpt() atPos(start, in.offset) { + val nameOffset = in.offset + val isBackquoted = in.token == BACKQUOTED_IDENT val name = identForType() - if (name == nme.macro_.toTypeName && isIdent && settings.Xmacros.value) { - funDefRest(start, in.offset, mods | Flags.MACRO, identForType()) - } else { - // @M! a type alias as well as an abstract type may declare type parameters - val tparams = typeParamClauseOpt(name, null) - in.token match { - case EQUALS => - in.nextToken() - TypeDef(mods, name, tparams, typ()) - case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE => - TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds()) - case _ => - syntaxErrorOrIncomplete("`=', `>:', or `<:' expected", true) - EmptyTree - } + // @M! a type alias as well as an abstract type may declare type parameters + val tparams = typeParamClauseOpt(name, null) + in.token match { + case EQUALS => + in.nextToken() + TypeDef(mods, name, tparams, typ()) + case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE => + TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds()) + case _ => + syntaxErrorOrIncomplete("`=', `>:', or `<:' expected", true) + EmptyTree } } } @@ -2599,14 +2607,14 @@ self => def classDef(start: Int, mods: Modifiers): ClassDef = { in.nextToken val nameOffset = in.offset + val isBackquoted = in.token == BACKQUOTED_IDENT val name = identForType() - atPos(start, if (name == tpnme.ERROR) start else nameOffset) { savingClassContextBounds { val contextBoundBuf = new ListBuffer[Tree] val tparams = typeParamClauseOpt(name, contextBoundBuf) classContextBounds = contextBoundBuf.toList - val tstart = in.offset :: classContextBounds.map(_.pos.startOrPoint) min; + val tstart = (in.offset :: classContextBounds.map(_.pos.startOrPoint)).min if (!classContextBounds.isEmpty && mods.isTrait) { syntaxError("traits cannot have type parameters with context bounds `: ...' nor view bounds `<% ...'", false) classContextBounds = List() @@ -2640,6 +2648,7 @@ self => def objectDef(start: Int, mods: Modifiers): ModuleDef = { in.nextToken val nameOffset = in.offset + val isBackquoted = in.token == BACKQUOTED_IDENT val name = ident() val tstart = in.offset atPos(start, if (name == nme.ERROR) start else nameOffset) { @@ -2818,6 +2827,7 @@ self => * }}} */ def packaging(start: Int): Tree = { + val nameOffset = in.offset val pkg = pkgQualId() val stats = inBracesOrNil(topStatSeq()) makePackaging(start, pkg, stats) @@ -3020,8 +3030,10 @@ self => ts ++= topStatSeq() } } else { + val nameOffset = in.offset in.flushDoc val pkg = pkgQualId() + if (in.token == EOF) { ts += makePackaging(start, pkg, List()) } else if (isStatSep) { diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 2895d02dfe..87072f3172 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -113,10 +113,18 @@ trait Scanners extends ScannersCommon { } /** Clear buffer and set name and token */ - private def finishNamed() { + private def finishNamed(idtoken: Int = IDENTIFIER) { name = newTermName(cbuf.toString) - token = name2token(name) cbuf.clear() + token = idtoken + if (idtoken == IDENTIFIER) { + val idx = name.start - kwOffset + if (idx >= 0 && idx < kwArray.length) { + token = kwArray(idx) + if (token == IDENTIFIER && allowIdent != name) + deprecationWarning(name+" is now a reserved word; usage as an identifier is deprecated") + } + } } /** Clear buffer and set string */ @@ -190,6 +198,20 @@ trait Scanners extends ScannersCommon { off } + /** Allow an otherwise deprecated ident here */ + private var allowIdent: Name = nme.EMPTY + + /** Get next token, and allow the otherwise deprecated ident `name` */ + def nextTokenAllow(name: Name) = { + val prev = allowIdent + allowIdent = name + try { + nextToken() + } finally { + allowIdent = prev + } + } + /** Produce next token, filling TokenData fields of Scanner. */ def nextToken() { @@ -231,6 +253,12 @@ trait Scanners extends ScannersCommon { lastOffset -= 1 } if (inStringInterpolation) fetchStringPart() else fetchToken() + if(token == ERROR) { + if (inMultiLineInterpolation) + sepRegions = sepRegions.tail.tail + else if (inStringInterpolation) + sepRegions = sepRegions.tail + } } else { this copyFrom next next.token = EMPTY @@ -328,7 +356,7 @@ trait Scanners extends ScannersCommon { putChar(ch) nextChar() getIdentRest() - if (ch == '"' && token == IDENTIFIER && settings.Xexperimental.value) + if (ch == '"' && token == IDENTIFIER) token = INTERPOLATIONID case '<' => // is XMLSTART? val last = if (charOffset >= 2) buf(charOffset - 2) else ' ' @@ -562,9 +590,8 @@ trait Scanners extends ScannersCommon { getLitChars('`') if (ch == '`') { nextChar() - finishNamed() + finishNamed(BACKQUOTED_IDENT) if (name.length == 0) syntaxError("empty quoted identifier") - token = BACKQUOTED_IDENT } else syntaxError("unclosed quoted identifier") } @@ -697,7 +724,7 @@ trait Scanners extends ScannersCommon { do { putChar(ch) nextRawChar() - } while (Character.isUnicodeIdentifierPart(ch)) + } while (ch != SU && Character.isUnicodeIdentifierPart(ch)) next.token = IDENTIFIER next.name = newTermName(cbuf.toString) cbuf.clear() @@ -1124,8 +1151,9 @@ trait Scanners extends ScannersCommon { nme.VIEWBOUNDkw -> VIEWBOUND, nme.SUPERTYPEkw -> SUPERTYPE, nme.HASHkw -> HASH, - nme.ATkw -> AT - ) + nme.ATkw -> AT, + nme.MACROkw -> IDENTIFIER, + nme.THENkw -> IDENTIFIER) private var kwOffset: Int = -1 private val kwArray: Array[Int] = { @@ -1134,14 +1162,7 @@ trait Scanners extends ScannersCommon { arr } - final val token2name = allKeywords map (_.swap) toMap - - /** Convert name to token */ - final def name2token(name: Name) = { - val idx = name.start - kwOffset - if (idx >= 0 && idx < kwArray.length) kwArray(idx) - else IDENTIFIER - } + final val token2name = (allKeywords map (_.swap)).toMap // Token representation ---------------------------------------------------- diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala index 849437e4ff..0f2a3e0395 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala @@ -11,6 +11,7 @@ import xml.{ EntityRef, Text } import xml.XML.{ xmlns } import symtab.Flags.MUTABLE import scala.tools.util.StringOps.splitWhere +import language.implicitConversions /** This class builds instance of `Tree` that represent XML. * diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala index fb4daefd57..a4a062609b 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -110,6 +110,8 @@ object Tokens extends Tokens { final val MATCH = 58 final val FORSOME = 59 final val LAZY = 61 + final val MACRO = 62 // not yet used in 2.10 + final val THEN = 63 // not yet used in 2.10 def isKeyword(code: Int) = code >= IF && code <= LAZY diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index 4f3b0bf951..dcbabd6517 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -11,6 +11,7 @@ import scala.collection.{ mutable, immutable } import mutable.{ ListBuffer, ArrayBuffer } import util.{ Position, NoPosition } import backend.icode.analysis.ProgramPoint +import language.postfixOps trait BasicBlocks { self: ICodes => diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index ec303d76ee..ff58de5f12 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -13,6 +13,7 @@ import scala.collection.mutable.{ ListBuffer, Buffer } import scala.tools.nsc.symtab._ import scala.annotation.switch import PartialFunction._ +import language.postfixOps /** This class ... * diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 36651541b2..71795a02aa 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -44,7 +44,7 @@ trait Members { def blocksList: List[BasicBlock] = blocks.toList def instructions = blocksList flatMap (_.iterator) def blockCount = blocks.size - def instructionCount = blocks map (_.length) sum + def instructionCount = (blocks map (_.length)).sum def touched = _touched def touched_=(b: Boolean): Unit = { diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index 7ad7cadd92..f61f78ebb2 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -44,7 +44,7 @@ trait TypeKinds { self: ICodes => } /** Reverse map for toType */ private lazy val reversePrimitiveMap: Map[TypeKind, Symbol] = - primitiveTypeMap map (_.swap) toMap + (primitiveTypeMap map (_.swap)).toMap /** This class represents a type kind. Type kinds * represent the types that the VM know (or the ICode diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala index f5be82a776..4427da92c8 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala @@ -566,8 +566,8 @@ abstract class CopyPropagation { method.blocks map { b => "\nIN(%s):\t Bindings: %s".format(b.label, in(b).bindings) + "\nIN(%s):\t Stack: %s".format(b.label, in(b).stack) - } mkString - ) + } + ).mkString } /* class CopyAnalysis */ } diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala index 49f5b51d51..5f261ba05e 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala @@ -97,6 +97,6 @@ abstract class Liveness { } } override def toString() = - method.blocks map (b => "\nlive-in(%s)=%s\nlive-out(%s)=%s".format(b, in(b), b, out(b))) mkString + (method.blocks map (b => "\nlive-in(%s)=%s\nlive-out(%s)=%s".format(b, in(b), b, out(b)))).mkString } /* Liveness analysis */ } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index c217869a48..0bc41b51bb 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -13,6 +13,7 @@ import scala.tools.nsc.util.ScalaClassLoader import scala.tools.util.JavapClass import java.util.jar.{ JarEntry, JarOutputStream, Attributes } import Attributes.Name +import language.postfixOps /** For the last mile: turning generated bytecode in memory into * something you can use. Has implementations for writing to class diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index be1e466f4e..f7898f2aa2 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -19,6 +19,7 @@ import JAccessFlags._ import JObjectType.{ JAVA_LANG_STRING, JAVA_LANG_OBJECT } import java.util.jar.{ JarEntry, JarOutputStream } import scala.tools.nsc.io.AbstractFile +import language.postfixOps /** This class ... * @@ -651,7 +652,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with case StringTag => buf put 's'.toByte buf putShort cpool.addUtf8(const.stringValue).toShort - case ClassTag => + case ClazzTag => buf put 'c'.toByte buf putShort cpool.addUtf8(javaType(const.typeValue).getSignature()).toShort case EnumTag => diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala index b74981b999..807a3dd0bb 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala @@ -121,7 +121,7 @@ trait GenJVMUtil { case DoubleTag => jcode emitPUSH const.doubleValue case StringTag => jcode emitPUSH const.stringValue case NullTag => jcode.emitACONST_NULL() - case ClassTag => + case ClazzTag => val kind = toTypeKind(const.typeValue) val toPush = if (kind.isValueType) classLiteral(kind) diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index 2fb615f893..66e7becb12 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -15,6 +15,7 @@ import scala.tools.nsc.symtab._ import ch.epfl.lamp.compiler.msil.{Type => MsilType, _} import ch.epfl.lamp.compiler.msil.emit._ import ch.epfl.lamp.compiler.msil.util.PECustomMod +import language.postfixOps abstract class GenMSIL extends SubComponent { import global._ @@ -365,7 +366,7 @@ abstract class GenMSIL extends SubComponent { arr.foreach(emitConst) } - // TODO: other Tags: NoTag, UnitTag, ClassTag, EnumTag, ArrayTag ??? + // TODO: other Tags: NoTag, UnitTag, ClazzTag, EnumTag, ArrayTag ??? case _ => abort("could not handle attribute argument: " + const) } @@ -388,7 +389,7 @@ abstract class GenMSIL extends SubComponent { case DoubleTag => buf.put(0x0d.toByte) case StringTag => buf.put(0x0e.toByte) - // TODO: other Tags: NoTag, UnitTag, ClassTag, EnumTag ??? + // TODO: other Tags: NoTag, UnitTag, ClazzTag, EnumTag ??? // ArrayTag falls in here case _ => abort("could not handle attribute argument: " + c) @@ -968,7 +969,7 @@ abstract class GenMSIL extends SubComponent { case DoubleTag => mcode.Emit(OpCodes.Ldc_R8, const.doubleValue) case StringTag => mcode.Emit(OpCodes.Ldstr, const.stringValue) case NullTag => mcode.Emit(OpCodes.Ldnull) - case ClassTag => + case ClazzTag => mcode.Emit(OpCodes.Ldtoken, msilType(const.typeValue)) mcode.Emit(OpCodes.Call, TYPE_FROM_HANDLE) case _ => abort("Unknown constant value: " + const) diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 95c371fa8b..d4ee9b6b48 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -82,7 +82,7 @@ abstract class DeadCodeElimination extends SubComponent { mark sweep(m) accessedLocals = accessedLocals.distinct - if (m.locals diff accessedLocals nonEmpty) { + if ((m.locals diff accessedLocals).nonEmpty) { log("Removed dead locals: " + (m.locals diff accessedLocals)) m.locals = accessedLocals.reverse } diff --git a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala index ec137203bf..0d47352215 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala @@ -360,7 +360,7 @@ abstract class InlineExceptionHandlers extends SubComponent { val caughtException = toTypeKind(caughtClass.tpe) // copy the exception handler code once again, dropping the LOAD_EXCEPTION val copy = handler.code.newBlock - copy.emitOnly(handler.iterator drop dropCount toSeq: _*) + copy.emitOnly((handler.iterator drop dropCount).toSeq: _*) // extend the handlers of the handler to the copy for (parentHandler <- handler.method.exh ; if parentHandler covers handler) { diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index dfe9081ee5..cf51a5b926 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -854,7 +854,7 @@ abstract class Inliners extends SubComponent { def lookupIMethod(meth: Symbol, receiver: Symbol): Option[IMethod] = { def tryParent(sym: Symbol) = icodes icode sym flatMap (_ lookupMethod meth) - receiver.info.baseClasses.iterator map tryParent find (_.isDefined) flatten + (receiver.info.baseClasses.iterator map tryParent find (_.isDefined)).flatten } } /* class Inliner */ } /* class Inliners */ diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index f32564f097..76a8b87ba7 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -58,7 +58,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor case Right(sourceCode) => new compiler.Run() compileSources List(new BatchSourceFile("newSource", sourceCode)) } - + if (reporter.hasErrors) return None @@ -80,6 +80,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor val modelFactory = ( new { override val global: compiler.type = compiler } with model.ModelFactory(compiler, settings) + with model.ModelFactoryImplicitSupport with model.comment.CommentFactory with model.TreeFactory { override def templateShouldDocument(sym: compiler.Symbol) = @@ -89,7 +90,8 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor modelFactory.makeModel match { case Some(madeModel) => - println("model contains " + modelFactory.templatesCount + " documentable templates") + if (settings.reportModel) + println("model contains " + modelFactory.templatesCount + " documentable templates") Some(madeModel) case None => println("no documentable class found in compilation units") diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index 45a2ad78b4..17bfb7d21d 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -8,6 +8,7 @@ package doc import java.io.File import java.lang.System +import language.postfixOps /** An extended version of compiler settings, with additional Scaladoc-specific options. * @param error A function that prints a string to the appropriate error stream. */ @@ -87,6 +88,38 @@ class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) { "" ) + val docImplicits = BooleanSetting ( + "-implicits", + "Document members inherited by implicit conversions." + ) + + val docImplicitsDebug = BooleanSetting ( + "-implicits-debug", + "Show debugging information for members inherited by implicit conversions." + ) + + val docImplicitsShowAll = BooleanSetting ( + "-implicits-show-all", + "Show members inherited by implicit conversions that are impossible in the default scope. " + + "(for example conversions that require Numeric[String] to be in scope)" + ) + + val docDiagrams = BooleanSetting ( + "-diagrams", + "Create inheritance diagrams for classes, traits and packages." + ) + + val docDiagramsDebug = BooleanSetting ( + "-diagrams-debug", + "Show debugging information for the diagram creation process." + ) + + val docDiagramsDotPath = PathSetting ( + "-diagrams-dot-path", + "The path to the dot executable used to generate the inheritance diagrams. Ex: /usr/bin/dot", + "dot" // by default, just pick up the system-wide dot + ) + // Somewhere slightly before r18708 scaladoc stopped building unless the // self-type check was suppressed. I hijacked the slotted-for-removal-anyway // suppress-vt-warnings option and renamed it for this purpose. @@ -94,9 +127,102 @@ class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) { // For improved help output. def scaladocSpecific = Set[Settings#Setting]( - docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator + docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator, docRootContent, useStupidTypes, + docDiagrams, docDiagramsDebug, docDiagramsDotPath, + docImplicits, docImplicitsDebug, docImplicitsShowAll ) val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name) override def isScaladoc = true + + // unset by the testsuite, we don't need to count the entities in the model + var reportModel = true + + /** + * This is the hardcoded area of Scaladoc. This is where "undesirable" stuff gets eliminated. I know it's not pretty, + * but ultimately scaladoc has to be useful. :) + */ + object hardcoded { + + /** The common context bounds and some humanly explanations. Feel free to add more explanations + * `<root>.scala.package.Numeric` is the type class + * `tparam` is the name of the type parameter it gets (this only describes type classes with 1 type param) + * the function result should be a humanly-understandable description of the type class + */ + val knownTypeClasses: Map[String, String => String] = Map() + + ("<root>.scala.package.Numeric" -> ((tparam: String) => tparam + " is a numeric class, such as Int, Long, Float or Double")) + + ("<root>.scala.package.Integral" -> ((tparam: String) => tparam + " is an integral numeric class, such as Int or Long")) + + ("<root>.scala.package.Fractional" -> ((tparam: String) => tparam + " is a fractional numeric class, such as Float or Double")) + + ("<root>.scala.reflect.Manifest" -> ((tparam: String) => tparam + " is accompanied by a Manifest, which is a runtime representation of its type that survives erasure")) + + ("<root>.scala.reflect.ClassManifest" -> ((tparam: String) => tparam + " is accompanied by a ClassManifest, which is a runtime representation of its type that survives erasure")) + + ("<root>.scala.reflect.OptManifest" -> ((tparam: String) => tparam + " is accompanied by an OptManifest, which can be either a runtime representation of its type or the NoManifest, which means the runtime type is not available")) + + /** + * Set of classes to exclude from index and diagrams + * TODO: Should be configurable + */ + def isExcluded(qname: String) = { + ( ( qname.startsWith("scala.Tuple") || qname.startsWith("scala.Product") || + qname.startsWith("scala.Function") || qname.startsWith("scala.runtime.AbstractFunction") + ) && !( + qname == "scala.Tuple1" || qname == "scala.Tuple2" || + qname == "scala.Product" || qname == "scala.Product1" || qname == "scala.Product2" || + qname == "scala.Function" || qname == "scala.Function1" || qname == "scala.Function2" || + qname == "scala.runtime.AbstractFunction0" || qname == "scala.runtime.AbstractFunction1" || + qname == "scala.runtime.AbstractFunction2" + ) + ) + } + + /** Common conversion targets that affect any class in Scala */ + val commonConversionTargets = List( + "scala.Predef.any2stringfmt", + "scala.Predef.any2stringadd", + "scala.Predef.any2ArrowAssoc", + "scala.Predef.any2Ensuring") + + /** There's a reason all these are specialized by hand but documenting each of them is beyond the point */ + val arraySkipConversions = List( + "scala.Predef.refArrayOps", + "scala.Predef.intArrayOps", + "scala.Predef.doubleArrayOps", + "scala.Predef.longArrayOps", + "scala.Predef.floatArrayOps", + "scala.Predef.charArrayOps", + "scala.Predef.byteArrayOps", + "scala.Predef.shortArrayOps", + "scala.Predef.booleanArrayOps", + "scala.Predef.unitArrayOps", + "scala.LowPriorityImplicits.wrapRefArray", + "scala.LowPriorityImplicits.wrapIntArray", + "scala.LowPriorityImplicits.wrapDoubleArray", + "scala.LowPriorityImplicits.wrapLongArray", + "scala.LowPriorityImplicits.wrapFloatArray", + "scala.LowPriorityImplicits.wrapCharArray", + "scala.LowPriorityImplicits.wrapByteArray", + "scala.LowPriorityImplicits.wrapShortArray", + "scala.LowPriorityImplicits.wrapBooleanArray", + "scala.LowPriorityImplicits.wrapUnitArray", + "scala.LowPriorityImplicits.genericWrapArray") + + // included as names as here we don't have access to a Global with Definitions :( + def valueClassList = List("unit", "boolean", "byte", "short", "char", "int", "long", "float", "double") + def valueClassFilterPrefixes = List("scala.LowPriorityImplicits", "scala.Predef") + + /** Dirty, dirty, dirty hack: the value params conversions can all kick in -- and they are disambiguated by priority + * but showing priority in scaladoc would make no sense -- so we have to manually remove the conversions that we + * know will never get a chance to kick in. Anyway, DIRTY DIRTY DIRTY! */ + def valueClassFilter(value: String, conversionName: String): Boolean = { + val valueName = value.toLowerCase + val otherValues = valueClassList.filterNot(_ == valueName) + + for (prefix <- valueClassFilterPrefixes) + if (conversionName.startsWith(prefix)) + for (otherValue <- otherValues) + if (conversionName.startsWith(prefix + "." + otherValue)) + return false + + true + } + } } diff --git a/src/compiler/scala/tools/nsc/doc/Uncompilable.scala b/src/compiler/scala/tools/nsc/doc/Uncompilable.scala index 9b29ebd745..8f426a443d 100644 --- a/src/compiler/scala/tools/nsc/doc/Uncompilable.scala +++ b/src/compiler/scala/tools/nsc/doc/Uncompilable.scala @@ -5,6 +5,8 @@ package scala.tools.nsc package doc +import language.implicitConversions +import language.postfixOps /** Some glue between DocParser (which reads source files which can't be compiled) * and the scaladoc model. @@ -14,7 +16,7 @@ trait Uncompilable { val settings: Settings import global.{ reporter, inform, warning, newTypeName, newTermName, Symbol, Name, DocComment, NoSymbol } - import global.definitions.RootClass + import global.definitions.{ RootClass, AnyRefClass } private implicit def translateName(name: Global#Name) = if (name.isTypeName) newTypeName("" + name) else newTermName("" + name) @@ -32,7 +34,7 @@ trait Uncompilable { } def files = settings.uncompilableFiles def symbols = pairs map (_._1) - def templates = symbols filter (x => x.isClass || x.isTrait) toSet + def templates = symbols filter (x => x.isClass || x.isTrait || x == AnyRefClass/* which is now a type alias */) toSet def comments = { if (settings.debug.value || settings.verbose.value) inform("Found %d uncompilable files: %s".format(files.size, files mkString ", ")) diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala index 0116e02e0e..914824d523 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -71,6 +71,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { "signaturebg.gif", "signaturebg2.gif", "typebg.gif", + "conversionbg.gif", "valuemembersbg.gif", "navigation-li-a.png", @@ -80,6 +81,8 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { "selected.png", "selected2-right.png", "selected2.png", + "selected-right-implicits.png", + "selected-implicits.png", "unselected.png" ) diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala index 1544dafc69..e3da8bddea 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala @@ -23,7 +23,7 @@ abstract class HtmlPage extends Page { thisPage => protected def title: String /** The page description */ - protected def description: String = + protected def description: String = // unless overwritten, will display the title in a spaced format, keeping - and . title.replaceAll("[^a-zA-Z0-9\\.\\-]+", " ").replaceAll("\\-+", " - ").replaceAll(" +", " ") @@ -164,15 +164,15 @@ abstract class HtmlPage extends Page { thisPage => } /** Returns the HTML code that represents the template in `tpl` as a hyperlinked name. */ - def templateToHtml(tpl: TemplateEntity) = tpl match { + def templateToHtml(tpl: TemplateEntity, name: String = null) = tpl match { case dTpl: DocTemplateEntity => if (hasPage(dTpl)) { - <a href={ relativeLinkTo(dTpl) } class="extype" name={ dTpl.qualifiedName }>{ dTpl.name }</a> + <a href={ relativeLinkTo(dTpl) } class="extype" name={ dTpl.qualifiedName }>{ if (name eq null) dTpl.name else name }</a> } else { - xml.Text(dTpl.name) + xml.Text(if (name eq null) dTpl.name else name) } case ndTpl: NoDocTemplate => - xml.Text(ndTpl.name) + xml.Text(if (name eq null) ndTpl.name else name) } /** Returns the HTML code that represents the templates in `tpls` as a list of hyperlinked names. */ @@ -192,6 +192,6 @@ abstract class HtmlPage extends Page { thisPage => else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isTrait) "object_to_trait_big.png" else if (ety.isObject) "object_big.png" else if (ety.isPackage) "package_big.png" - else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not + else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not } diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index f059b5c0cb..d3f42ffe6e 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -10,6 +10,7 @@ package page import model._ import scala.xml.{ NodeSeq, Text, UnprefixedAttribute } +import language.postfixOps class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage { @@ -88,21 +89,42 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage <div id="mbrsel"> <div id='textfilter'><span class='pre'/><span class='input'><input type='text' accesskey='/'/></span><span class='post'/></div> - { if (tpl.linearizationTemplates.isEmpty) NodeSeq.Empty else + { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else <div id="order"> <span class="filtertype">Ordering</span> <ol><li class="alpha in"><span>Alphabetic</span></li><li class="inherit out"><span>By inheritance</span></li></ol> </div> } - { if (tpl.linearizationTemplates.isEmpty) NodeSeq.Empty else - <div id="ancestors"> - <span class="filtertype">Inherited</span> - <ol><li class="hideall out"><span>Hide All</span></li> - <li class="showall in"><span>Show all</span></li></ol> - <ol id="linearization">{ - (tpl :: tpl.linearizationTemplates) map { wte => <li class="in" name={ wte.qualifiedName }><span>{ wte.name }</span></li> } - }</ol> - </div> + { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else + { + if (!tpl.linearization.isEmpty) + <div id="ancestors"> + <span class="filtertype">Inherited<br/> + </span> + <ol id="linearization"> + { (tpl :: tpl.linearizationTemplates).map(wte => <li class="in" name={ wte.qualifiedName }><span>{ wte.name }</span></li>) } + </ol> + </div> + else NodeSeq.Empty + } ++ { + if (!tpl.conversions.isEmpty) + <div id="ancestors"> + <span class="filtertype">Implicitly<br/> + </span> + <ol id="implicits"> + { tpl.conversions.map(conv => <li class="in" name={ conv.conversionQualifiedName }><span>{ "by " + conv.conversionShortName }</span></li>) } + </ol> + </div> + else NodeSeq.Empty + } ++ + <div id="ancestors"> + <span class="filtertype"></span> + <ol> + <li class="hideall out"><span>Hide All</span></li> + <li class="showall in"><span>Show all</span></li> + </ol> + <a href="docs.scala-lang.org/overviews/scaladoc/usage.html#members" target="_blank">Learn more about member selection</a> + </div> } { <div id="visbl"> @@ -152,23 +174,25 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage <div id="inheritedMembers"> { + // linearization NodeSeq fromSeq (for ((superTpl, superType) <- (tpl.linearizationTemplates zip tpl.linearizationTypes)) yield <div class="parent" name={ superTpl.qualifiedName }> <h3>Inherited from { - if (tpl.universe.settings.useStupidTypes.value) - superTpl match { - case dtpl: DocTemplateEntity => - val sig = signature(dtpl, false, true) \ "_" - sig - case tpl: TemplateEntity => - tpl.name - } - else - typeToHtml(superType, true) + typeToHtmlWithStupidTypes(tpl, superTpl, superType) }</h3> </div> ) } + { + // implicitly inherited + NodeSeq fromSeq (for (conversion <- (tpl.conversions)) yield + <div class="conversion" name={ conversion.conversionQualifiedName }> + <h3>Inherited by implicit conversion { conversion.conversionShortName } from + { typeToHtml(tpl.resultType, true) } to { typeToHtml(conversion.targetType, true) } + </h3> + </div> + ) + } </div> </div> @@ -219,11 +243,12 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage case d:MemberEntity with Def => defParamsToString(d) case _ => "" } + val memberComment = memberToCommentHtml(mbr, false) <li name={ mbr.definitionName } visbl={ if (mbr.visibility.isProtected) "prt" else "pub" } - data-isabs={ mbr.isAbstract.toString }> + data-isabs={ mbr.isAbstract.toString } fullComment={ if(memberComment.isEmpty) "no" else "yes" }> <a id={ mbr.name +defParamsString +":"+ mbr.resultType.name}/> { signature(mbr, false) } - { memberToCommentHtml(mbr, false) } + { memberComment } </li> } @@ -275,6 +300,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage <p class="comment cmt">{ inlineToHtml(mbr.comment.get.short) }</p> def memberToCommentBodyHtml(mbr: MemberEntity, isSelf: Boolean, isReduced: Boolean = false): NodeSeq = { + val memberComment = if (mbr.comment.isEmpty) NodeSeq.Empty else <div class="comment cmt">{ commentToHtml(mbr.comment) }</div> @@ -326,6 +352,45 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } } + val implicitInformation = mbr.byConversion match { + case Some(conv) => + <dt class="implicit">Implicit information</dt> ++ + { + val targetType = typeToHtml(conv.targetType, true) + val conversionMethod = conv.convertorMethod match { + case Left(member) => Text(member.name) + case Right(name) => Text(name) + } + + // strip off the package object endings, they make things harder to follow + val conversionOwnerQualifiedNane = conv.convertorOwner.qualifiedName.stripSuffix(".package") + val conversionOwner = templateToHtml(conv.convertorOwner, conversionOwnerQualifiedNane) + + val constraintText = conv.constraints match { + case Nil => + NodeSeq.Empty + case List(constraint) => + xml.Text("This conversion will take place only if ") ++ constraintToHtml(constraint) ++ xml.Text(".") + case List(constraint1, constraint2) => + xml.Text("This conversion will take place only if ") ++ constraintToHtml(constraint1) ++ + xml.Text(" and at the same time ") ++ constraintToHtml(constraint2) ++ xml.Text(".") + case constraints => + <br/> ++ "This conversion will take place only if all of the following constraints are met:" ++ <br/> ++ { + var index = 0 + constraints map { constraint => xml.Text({ index += 1; index } + ". ") ++ constraintToHtml(constraint) ++ <br/> } + } + } + + <dd> + This member is added by an implicit conversion from { typeToHtml(mbr.inTemplate.resultType, true) } to + { targetType } performed by method { conversionMethod } in { conversionOwner }. + { constraintText } + </dd> + } + case _ => + NodeSeq.Empty + } + // --- start attributes block vals val attributes: Seq[scala.xml.Node] = { val fvs: List[comment.Paragraph] = visibility(mbr).toList @@ -354,7 +419,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage </div> case _ => NodeSeq.Empty } - } + } val selfType: Seq[scala.xml.Node] = mbr match { case dtpl: DocTemplateEntity if (isSelf && !dtpl.selfType.isEmpty && !isReduced) => @@ -477,7 +542,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } // end attributes block vals --- - val attributesInfo = attributes ++ definitionClasses ++ fullSignature ++ selfType ++ annotations ++ deprecation ++ migration ++ sourceLink ++ mainComment + val attributesInfo = implicitInformation ++ attributes ++ definitionClasses ++ fullSignature ++ selfType ++ annotations ++ deprecation ++ migration ++ sourceLink ++ mainComment val attributesBlock = if (attributesInfo.isEmpty) NodeSeq.Empty @@ -561,12 +626,13 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage </span> <span class="symbol"> { + val nameClass = if (mbr.byConversion.isDefined) "implicit" else "name" val nameHtml = { val value = if (mbr.isConstructor) tpl.name else mbr.name val span = if (mbr.deprecation.isDefined) - <span class={"name deprecated"} title={"Deprecated: "+bodyToStr(mbr.deprecation.get)}>{ value }</span> + <span class={ nameClass + " deprecated"} title={"Deprecated: "+bodyToStr(mbr.deprecation.get)}>{ value }</span> else - <span class={"name"}>{ value }</span> + <span class={ nameClass }>{ value }</span> val encoded = scala.reflect.NameTransformer.encode(value) if (encoded != value) { span % new UnprefixedAttribute("title", @@ -765,4 +831,43 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage case _ => inl.toString } + private def typeToHtmlWithStupidTypes(tpl: TemplateEntity, superTpl: TemplateEntity, superType: TypeEntity): NodeSeq = + if (tpl.universe.settings.useStupidTypes.value) + superTpl match { + case dtpl: DocTemplateEntity => + val sig = signature(dtpl, false, true) \ "_" + sig + case tpl: TemplateEntity => + Text(tpl.name) + } + else + typeToHtml(superType, true) + + private def constraintToHtml(constraint: Constraint): NodeSeq = constraint match { + case ktcc: KnownTypeClassConstraint => + xml.Text(ktcc.typeExplanation(ktcc.typeParamName) + " (" + ktcc.typeParamName + ": ") ++ + templateToHtml(ktcc.typeClassEntity) ++ xml.Text(")") + case tcc: TypeClassConstraint => + xml.Text(tcc.typeParamName + " is ") ++ + <a href="http://stackoverflow.com/questions/2982276/what-is-a-context-bound-in-scala" target="_blank"> + context-bounded</a> ++ xml.Text(" by " + tcc.typeClassEntity.qualifiedName + " (" + tcc.typeParamName + ": ") ++ + templateToHtml(tcc.typeClassEntity) ++ xml.Text(")") + case impl: ImplicitInScopeConstraint => + xml.Text("an implicit value of type ") ++ typeToHtml(impl.implicitType, true) ++ xml.Text(" is in scope") + case eq: EqualTypeParamConstraint => + xml.Text(eq.typeParamName + " is " + eq.rhs.name + " (" + eq.typeParamName + " =:= ") ++ + typeToHtml(eq.rhs, true) ++ xml.Text(")") + case bt: BoundedTypeParamConstraint => + xml.Text(bt.typeParamName + " is a superclass of " + bt.lowerBound.name + " and a subclass of " + + bt.upperBound.name + " (" + bt.typeParamName + " >: ") ++ + typeToHtml(bt.lowerBound, true) ++ xml.Text(" <: ") ++ + typeToHtml(bt.upperBound, true) ++ xml.Text(")") + case lb: LowerBoundedTypeParamConstraint => + xml.Text(lb.typeParamName + " is a superclass of " + lb.lowerBound.name + " (" + lb.typeParamName + " >: ") ++ + typeToHtml(lb.lowerBound, true) ++ xml.Text(")") + case ub: UpperBoundedTypeParamConstraint => + xml.Text(ub.typeParamName + " is a subclass of " + ub.upperBound.name + " (" + ub.typeParamName + " <: ") ++ + typeToHtml(ub.upperBound, true) ++ xml.Text(")") + } + } diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/conversionbg.gif b/src/compiler/scala/tools/nsc/doc/html/resource/lib/conversionbg.gif Binary files differnew file mode 100644 index 0000000000..4be145d0af --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/conversionbg.gif diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-implicits.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-implicits.png Binary files differnew file mode 100644 index 0000000000..bc29efb3e6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-implicits.png diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-right-implicits.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-right-implicits.png Binary files differnew file mode 100644 index 0000000000..8313f4975b --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-right-implicits.png diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css index 6fb83c133e..5a1779bba5 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css @@ -106,7 +106,7 @@ a[href]:hover { font-size: 24pt; text-shadow: black 0px 2px 0px; /* text-shadow: black 0px 0px 0px;*/ -text-decoration: none; +text-decoration: none; } #definition #owner { @@ -162,7 +162,7 @@ text-decoration: none; padding-left: 15px; background: url("arrow-right.png") no-repeat 0 3px transparent; } - + .toggleContainer.open .toggle { background: url("arrow-down.png") no-repeat 0 3px transparent; } @@ -205,6 +205,11 @@ dl.attributes > dt { font-style: italic; } +dl.attributes > dt.implicit { + font-weight: bold; + color: darkgreen; +} + dl.attributes > dd { display: block; padding-left: 10em; @@ -241,6 +246,17 @@ dl.attributes > dd { color: white; } +#inheritedMembers > div.conversion > h3 { + background: #dadada url("conversionbg.gif") repeat-x bottom left; /* gray */ + height: 17px; + font-style: italic; + font-size: 12pt; +} + +#inheritedMembers > div.conversion > h3 * { + color: white; +} + /* Member cells */ div.members > ol { @@ -310,10 +326,21 @@ div.members > ol > li:last-child { font-weight: bold; } -.signature .symbol .params .implicit { +.signature .symbol > .implicit { + display: inline-block; + font-weight: bold; + text-decoration: underline; + color: darkgreen; +} + +.signature .symbol .params > .implicit { font-style: italic; } +.signature .symbol .implicit.deprecated { + text-decoration: line-through; +} + .signature .symbol .name.deprecated { text-decoration: line-through; } @@ -369,15 +396,15 @@ div.members > ol > li:last-child { .cmt {} .cmt p { - margin: 0.7em 0; + margin: 0.7em 0; } .cmt p:first-child { - margin-top: 0; + margin-top: 0; } .cmt p:last-child { - margin-bottom: 0; + margin-bottom: 0; } .cmt h3, @@ -539,7 +566,7 @@ div.fullcommenttop .block { margin-bottom: 5px } -div.fullcomment div.block ol li p, +div.fullcomment div.block ol li p, div.fullcomment div.block ol li { display:inline } @@ -583,10 +610,10 @@ div.fullcomment dl.paramcmts > dd { /* Members filter tool */ #textfilter { - position: relative; - display: block; + position: relative; + display: block; height: 20px; - margin-bottom: 5px; + margin-bottom: 5px; } #textfilter > .pre { @@ -600,7 +627,7 @@ div.fullcomment dl.paramcmts > dd { } #textfilter > .input { - display: block; + display: block; position: absolute; top: 0; right: 20px; @@ -608,10 +635,10 @@ div.fullcomment dl.paramcmts > dd { } #textfilter > .input > input { - height: 20px; - padding: 1px; - font-weight: bold; - color: #000000; + height: 20px; + padding: 1px; + font-weight: bold; + color: #000000; background: #ffffff url("filterboxbarbg.png") repeat-x top left; width: 100%; } @@ -660,6 +687,13 @@ div.fullcomment dl.paramcmts > dd { display: inline-block; } +#mbrsel > div > a { + position:relative; + top: -8px; + font-size: 11px; + text-shadow: #ffffff 0 1px 0; +} + #mbrsel > div > ol#linearization { display: table; margin-left: 70px; @@ -683,9 +717,32 @@ div.fullcomment dl.paramcmts > dd { text-shadow: #ffffff 0 1px 0; } +#mbrsel > div > ol#implicits { + display: table; + margin-left: 70px; +} + +#mbrsel > div > ol#implicits > li.in { + text-decoration: none; + float: left; + padding-right: 10px; + margin-right: 5px; + background: url(selected-right-implicits.png) no-repeat; + background-position: right 0px; +} + +#mbrsel > div > ol#implicits > li.in > span{ + color: #404040; + float: left; + padding: 1px 0 1px 10px; + background: url(selected-implicits.png) no-repeat; + background-position: 0px 0px; + text-shadow: #ffffff 0 1px 0; +} + #mbrsel > div > ol > li { /* padding: 3px 10px;*/ - line-height: 16pt; + line-height: 16pt; display: inline-block; cursor: pointer; } @@ -709,10 +766,10 @@ div.fullcomment dl.paramcmts > dd { } #mbrsel > div > ol > li.out { - text-decoration: none; - float: left; - padding-right: 10px; - margin-right: 5px; + text-decoration: none; + float: left; + padding-right: 10px; + margin-right: 5px; } #mbrsel > div > ol > li.out > span{ @@ -739,10 +796,10 @@ div.fullcomment dl.paramcmts > dd { #mbrsel .showall { color: #4C4C4C; line-height: 16px; - font-weight: bold; + font-weight: bold; } #mbrsel .showall span { color: #4C4C4C; - font-weight: bold; + font-weight: bold; }*/
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js index 3cdd9a7f27..fd5a981cb0 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js @@ -2,21 +2,23 @@ // code by Gilles Dubochet with contributions by Pedro Furlanetto $(document).ready(function(){ - var isHiddenClass; - if (document.title == 'scala.AnyRef') { - isHiddenClass = function (name) { - return name == 'scala.Any'; - }; - } else { - isHiddenClass = function (name) { - return name == 'scala.Any' || name == 'scala.AnyRef'; - }; - } + var isHiddenClass = function (name) { + return name == 'scala.Any' || + name == 'scala.AnyRef' || + name == 'scala.Predef.any2stringfmt' || + name == 'scala.Predef.any2stringadd' || + name == 'scala.Predef.any2ArrowAssoc' || + name == 'scala.Predef.any2Ensuring' + }; + + $("#linearization li:gt(0)").filter(function(){ + return isHiddenClass($(this).attr("name")); + }).removeClass("in").addClass("out"); - $("#linearization li").filter(function(){ + $("#implicits li").filter(function(){ return isHiddenClass($(this).attr("name")); }).removeClass("in").addClass("out"); - + // Pre-filter members filter(); @@ -54,17 +56,38 @@ $(document).ready(function(){ }; filter(); }); - $("#ancestors > ol > li.hideall").click(function() { + + $("#implicits li").click(function(){ + if ($(this).hasClass("in")) { + $(this).removeClass("in"); + $(this).addClass("out"); + } + else if ($(this).hasClass("out")) { + $(this).removeClass("out"); + $(this).addClass("in"); + }; + filter(); + }); + + $("#mbrsel > div[id=ancestors] > ol > li.hideall").click(function() { $("#linearization li.in").removeClass("in").addClass("out"); $("#linearization li:first").removeClass("out").addClass("in"); + $("#implicits li.in").removeClass("in").addClass("out"); filter(); }) - $("#ancestors > ol > li.showall").click(function() { - var filtered = + $("#mbrsel > div[id=ancestors] > ol > li.showall").click(function() { + var filteredLinearization = $("#linearization li.out").filter(function() { return ! isHiddenClass($(this).attr("name")); }); - filtered.removeClass("out").addClass("in"); + filteredLinearization.removeClass("out").addClass("in"); + + var filteredImplicits = + $("#implicits li.out").filter(function() { + return ! isHiddenClass($(this).attr("name")); + }); + filteredImplicits.removeClass("out").addClass("in"); + filter(); }); $("#visbl > ol > li.public").click(function() { @@ -108,8 +131,10 @@ $(document).ready(function(){ }); /* Add toggle arrows */ - var docAllSigs = $("#template li").has(".fullcomment").find(".signature"); - + //var docAllSigs = $("#template li").has(".fullcomment").find(".signature"); + // trying to speed things up a little bit + var docAllSigs = $("#template li[fullComment=yes] .signature"); + function commentToggleFct(signature){ var parent = signature.parent(); var shortComment = $(".shortcomment", parent); @@ -129,7 +154,7 @@ $(document).ready(function(){ docAllSigs.click(function() { commentToggleFct($(this)); }); - + /* Linear super types and known subclasses */ function toggleShowContentFct(outerElement){ var content = $(".hiddenContent", outerElement); @@ -148,20 +173,22 @@ $(document).ready(function(){ $(".toggleContainer").click(function() { toggleShowContentFct($(this)); }); - + // Set parent window title windowTitle(); }); function orderAlpha() { $("#template > div.parent").hide(); - $("#ancestors").show(); + $("#template > div.conversion").hide(); + $("#mbrsel > div[id=ancestors]").show(); filter(); }; function orderInherit() { $("#template > div.parent").show(); - $("#ancestors").hide(); + $("#template > div.conversion").show(); + $("#mbrsel > div[id=ancestors]").hide(); filter(); }; @@ -177,6 +204,9 @@ function initInherit() { $("#inheritedMembers > div.parent").each(function(){ parents[$(this).attr("name")] = $(this); }); + $("#inheritedMembers > div.conversion").each(function(){ + parents[$(this).attr("name")] = $(this); + }); $("#types > ol > li").each(function(){ var mbr = $(this); this.mbrText = mbr.find("> .fullcomment .cmt").text(); @@ -216,6 +246,9 @@ function initInherit() { $("#inheritedMembers > div.parent").each(function() { if ($("> div.members", this).length == 0) { $(this).remove(); }; }); + $("#inheritedMembers > div.conversion").each(function() { + if ($("> div.members", this).length == 0) { $(this).remove(); }; + }); }; function filter(scrollToMember) { @@ -224,13 +257,17 @@ function filter(scrollToMember) { var queryRegExp = new RegExp(query, "i"); var privateMembersHidden = $("#visbl > ol > li.public").hasClass("in"); var orderingAlphabetic = $("#order > ol > li.alpha").hasClass("in"); - var hiddenSuperclassElements = orderingAlphabetic ? $("#linearization > li.out") : $("#linearization > li:gt(0)"); - var hiddenSuperclasses = hiddenSuperclassElements.map(function() { + var hiddenSuperclassElementsLinearization = orderingAlphabetic ? $("#linearization > li.out") : $("#linearization > li:gt(0)"); + var hiddenSuperclassesLinearization = hiddenSuperclassElementsLinearization.map(function() { + return $(this).attr("name"); + }).get(); + var hiddenSuperclassElementsImplicits = orderingAlphabetic ? $("#implicits > li.out") : $("#implicits > li"); + var hiddenSuperclassesImplicits = hiddenSuperclassElementsImplicits.map(function() { return $(this).attr("name"); }).get(); var hideInheritedMembers; - + if(orderingAlphabetic) { $("#inheritedMembers").hide(); hideInheritedMembers = true; @@ -242,9 +279,10 @@ function filter(scrollToMember) { $("#allMembers > .members").each(filterFunc); hideInheritedMembers = false; $("#inheritedMembers > .parent > .members").each(filterFunc); + $("#inheritedMembers > .conversion > .members").each(filterFunc); } - + function filterFunc() { var membersVisible = false; var members = $(this); @@ -262,12 +300,18 @@ function filter(scrollToMember) { ownerIndex = name.lastIndexOf("."); } var owner = name.slice(0, ownerIndex); - for (var i = 0; i < hiddenSuperclasses.length; i++) { - if (hiddenSuperclasses[i] == owner) { + for (var i = 0; i < hiddenSuperclassesLinearization.length; i++) { + if (hiddenSuperclassesLinearization[i] == owner) { mbr.hide(); return; } - } + }; + for (var i = 0; i < hiddenSuperclassesImplicits.length; i++) { + if (hiddenSuperclassesImplicits[i] == owner) { + mbr.hide(); + return; + } + }; } if (query && !(queryRegExp.test(name) || queryRegExp.test(this.mbrText))) { mbr.hide(); @@ -276,7 +320,7 @@ function filter(scrollToMember) { mbr.show(); membersVisible = true; }); - + if (membersVisible) members.show(); else diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala index 6eb14a4907..6488847049 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala @@ -167,6 +167,8 @@ trait MemberEntity extends Entity { /** Whether this member is abstract. */ def isAbstract: Boolean + /** If this member originates from an implicit conversion, we set the implicit information to the correct origin */ + def byConversion: Option[ImplicitConversion] } object MemberEntity { // Oh contravariance, contravariance, wherefore art thou contravariance? @@ -246,6 +248,8 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { * other entity of the pair is the companion. */ def companion: Option[DocTemplateEntity] + /** The implicit conversions this template (class or trait, objects and packages are not affected) */ + def conversions: List[ImplicitConversion] } @@ -413,3 +417,106 @@ trait Annotation extends Entity { def arguments: List[ValueArgument] } + +/** A trait that signals the member results from an implicit conversion */ +trait ImplicitConversion { + + /** The source of the implicit conversion*/ + def source: DocTemplateEntity + + /** The result type after the conversion */ + def targetType: TypeEntity + + /** The entity for the method that performed the conversion, if it's documented (or just its name, otherwise) */ + def convertorMethod: Either[MemberEntity, String] + + /** A short name of the convertion */ + def conversionShortName: String + + /** A qualified name uniquely identifying the convertion (currently: the conversion method's qualified name) */ + def conversionQualifiedName: String + + /** The entity that performed the conversion */ + def convertorOwner: TemplateEntity + + /** The constraints that the transformations puts on the type parameters */ + def constraints: List[Constraint] + + /** The members inherited by this implicit conversion */ + def members: List[MemberEntity] +} + +/** A trait that encapsulates a constraint necessary for implicit conversion */ +trait Constraint { + // /** The implicit conversion during which this constraint appears */ + // def conversion: ImplicitConversion +} + +/** A constraint involving a type parameter which must be in scope */ +trait ImplicitInScopeConstraint extends Constraint { + /** The type of the implicit value required */ + def implicitType: TypeEntity + + /** toString for debugging */ + override def toString = "an implicit _: " + implicitType.name + " must be in scope" +} + +trait TypeClassConstraint extends ImplicitInScopeConstraint with TypeParamConstraint { + /** Type class name */ + def typeClassEntity: TemplateEntity + + /** toString for debugging */ + override def toString = typeParamName + " is a class of type " + typeClassEntity.qualifiedName + " (" + + typeParamName + ": " + typeClassEntity.name + ")" +} + +trait KnownTypeClassConstraint extends TypeClassConstraint { + /** Type explanation, takes the type parameter name and generates the explanation */ + def typeExplanation: (String) => String + + /** toString for debugging */ + override def toString = typeExplanation(typeParamName) + " (" + typeParamName + ": " + typeClassEntity.name + ")" +} + +/** A constraint involving a type parameter */ +trait TypeParamConstraint extends Constraint { + /** The type parameter involved */ + def typeParamName: String +} + +trait EqualTypeParamConstraint extends TypeParamConstraint { + /** The rhs */ + def rhs: TypeEntity + /** toString for debugging */ + override def toString = typeParamName + " is " + rhs.name + " (" + typeParamName + " =:= " + rhs.name + ")" +} + +trait BoundedTypeParamConstraint extends TypeParamConstraint { + /** The lower bound */ + def lowerBound: TypeEntity + + /** The upper bound */ + def upperBound: TypeEntity + + /** toString for debugging */ + override def toString = typeParamName + " is a superclass of " + lowerBound.name + " and a subclass of " + + upperBound.name + " (" + typeParamName + " >: " + lowerBound.name + " <: " + upperBound.name + ")" +} + +trait LowerBoundedTypeParamConstraint extends TypeParamConstraint { + /** The lower bound */ + def lowerBound: TypeEntity + + /** toString for debugging */ + override def toString = typeParamName + " is a superclass of " + lowerBound.name + " (" + typeParamName + " >: " + + lowerBound.name + ")" +} + +trait UpperBoundedTypeParamConstraint extends TypeParamConstraint { + /** The lower bound */ + def upperBound: TypeEntity + + /** toString for debugging */ + override def toString = typeParamName + " is a subclass of " + upperBound.name + " (" + typeParamName + " <: " + + upperBound.name + ")" +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala index ef3c2beffb..6392de22ff 100755 --- a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala @@ -8,6 +8,7 @@ package doc package model import scala.collection._ +import language.reflectiveCalls object IndexModelFactory { @@ -15,7 +16,7 @@ object IndexModelFactory { lazy val firstLetterIndex: Map[Char, SymbolMap] = { - val result = new mutable.HashMap[Char,SymbolMap] { + object result extends mutable.HashMap[Char,SymbolMap] { /* Owner template ordering */ implicit def orderingSet = math.Ordering.String.on { x: MemberEntity => x.name.toLowerCase } diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 670c9bbb3b..124a7509e8 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -17,7 +17,7 @@ import model.{ RootPackage => RootPackageEntity } /** This trait extracts all required information for documentation from compilation units */ class ModelFactory(val global: Global, val settings: doc.Settings) { - thisFactory: ModelFactory with CommentFactory with TreeFactory => + thisFactory: ModelFactory with ModelFactoryImplicitSupport with CommentFactory with TreeFactory => import global._ import definitions.{ ObjectClass, RootPackage, EmptyPackage, NothingClass, AnyClass, AnyValClass, AnyRefClass } @@ -95,7 +95,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isDocTemplate = false } - abstract class MemberImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity { + abstract class MemberImpl(sym: Symbol, implConv: ImplicitConversionImpl = null, inTpl: => DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity { lazy val comment = if (inTpl == null) None else thisFactory.comment(sym, inTpl) override def inTemplate = inTpl @@ -128,7 +128,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { 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")) + /* Resetting the DEFERRED flag is a little trick here for refined types: (example from scala.collections) + * {{{ + * implicit def traversable2ops[T](t: collection.GenTraversableOnce[T]) = new TraversableOps[T] { + * def isParallel = ... + * }}} + * the type the method returns is TraversableOps, which has all-abstract symbols. But in reality, it couldn't have + * any abstract terms, otherwise it would fail compilation. So we reset the DEFERRED flag. */ + if (!sym.isTrait && (sym hasFlag Flags.DEFERRED) && (implConv eq null)) fgs += Paragraph(Text("abstract")) if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final")) fgs.toList } @@ -162,7 +169,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case NullaryMethodType(res) => resultTpe(res) case _ => tpe } - makeTypeInTemplateContext(resultTpe(sym.tpe), inTemplate, sym) + val tpe = if (implConv eq null) sym.tpe else implConv.toType memberInfo sym + makeTypeInTemplateContext(resultTpe(tpe), inTemplate, sym) } def isDef = false def isVal = false @@ -173,15 +181,17 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isAliasType = false def isAbstractType = false def isAbstract = - ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED))) || + // for the explanation of implConv == null see comment on flags + ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED)) && (implConv == null)) || sym.isAbstractClass || sym.isAbstractType) && !sym.isSynthetic def isTemplate = false + def byConversion = if (implConv ne null) Some(implConv) else None } /** The instantiation of `TemplateImpl` triggers the creation of the following entities: * All ancestors of the template and all non-package members. */ - abstract class DocTemplateImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends MemberImpl(sym, inTpl) with TemplateImpl with HigherKindedImpl with DocTemplateEntity { + abstract class DocTemplateImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends MemberImpl(sym, null, inTpl) with TemplateImpl with HigherKindedImpl with DocTemplateEntity { //if (inTpl != null) println("mbr " + sym + " in " + (inTpl.toRoot map (_.sym)).mkString(" > ")) if (settings.verbose.value) inform("Creating doc template for " + sym) @@ -245,16 +255,20 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def subClasses = if (subClassesCache == null) Nil else subClassesCache.toList - protected lazy val memberSyms = + val conversions = if (settings.docImplicits.value) makeImplicitConversions(sym, this) else Nil + + lazy val memberSyms = // Only this class's constructors are part of its members, inherited constructors are not. sym.info.members.filter(s => localShouldDocument(s) && (!s.isConstructor || s.owner == sym) && !isPureBridge(sym) ) - val members = memberSyms flatMap (makeMember(_, this)) - val templates = members collect { case c: DocTemplateEntity => c } - val methods = members collect { case d: Def => d } - val values = members collect { case v: Val => v } - val abstractTypes = members collect { case t: AbstractType => t } - val aliasTypes = members collect { case t: AliasType => t } + val members = (memberSyms.flatMap(makeMember(_, null, this))) ::: + (conversions.flatMap((_.members))) // also take in the members from implicit conversions + + val templates = members collect { case c: DocTemplateEntity => c } + val methods = members collect { case d: Def => d } + val values = members collect { case v: Val => v } + val abstractTypes = members collect { case t: AbstractType => t } + val aliasTypes = members collect { case t: AliasType => t } override def isTemplate = true def isDocTemplate = true def companion = sym.companionSymbol match { @@ -273,18 +287,22 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { abstract class RootPackageImpl(sym: Symbol) extends PackageImpl(sym, null) with RootPackageEntity - abstract class NonTemplateMemberImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends MemberImpl(sym, inTpl) with NonTemplateMemberEntity { + abstract class NonTemplateMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl) extends MemberImpl(sym, implConv, inTpl) with NonTemplateMemberEntity { override def qualifiedName = optimize(inTemplate.qualifiedName + "#" + name) - lazy val definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "#" + name) + lazy val definitionName = + if (implConv == null) optimize(inDefinitionTemplates.head.qualifiedName + "#" + name) + else optimize(implConv.conversionQualifiedName + "#" + name) def isUseCase = sym.isSynthetic def isBridge = sym.isBridge } - abstract class NonTemplateParamMemberImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends NonTemplateMemberImpl(sym, inTpl) { - def valueParams = - sym.paramss map { ps => (ps.zipWithIndex) map { case (p, i) => + abstract class NonTemplateParamMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl) extends NonTemplateMemberImpl(sym, implConv, inTpl) { + def valueParams = { + val info = if (implConv eq null) sym.info else implConv.toType memberInfo sym + info.paramss map { ps => (ps.zipWithIndex) map { case (p, i) => if (p.nameString contains "$") makeValueParam(p, inTpl, optimize("arg" + i)) else makeValueParam(p, inTpl) }} + } } abstract class ParameterImpl(sym: Symbol, inTpl: => TemplateImpl) extends EntityImpl(sym, inTpl) with ParameterEntity { @@ -356,7 +374,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { override def qualifiedName = "_root_" override def inheritedFrom = Nil override def isRootPackage = true - override protected lazy val memberSyms = + override lazy val memberSyms = (bSym.info.members ++ EmptyPackage.info.members) filter { s => s != EmptyPackage && s != RootPackage } @@ -380,11 +398,13 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { else if (bSym.isPackage) makeTemplate(bSym.owner) match { case inPkg: PackageImpl => makePackage(bSym, inPkg) getOrElse (new NoDocTemplateImpl(bSym, inPkg)) + case inNoDocTpl: NoDocTemplateImpl => new NoDocTemplateImpl(bSym, inNoDocTpl) case _ => throw new Error("'" + bSym + "' must be in a package") } else if (templateShouldDocument(bSym)) makeTemplate(bSym.owner) match { case inDTpl: DocTemplateImpl => makeDocTemplate(bSym, inDTpl) + case inNoDocTpl: NoDocTemplateImpl => new NoDocTemplateImpl(bSym, inNoDocTpl) case _ => throw new Error("'" + bSym + "' must be in documentable template") } else @@ -454,18 +474,19 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } /** */ - def makeMember(aSym: Symbol, inTpl: => DocTemplateImpl): List[MemberImpl] = { + // TODO: Should be able to override the type + def makeMember(aSym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl): List[MemberImpl] = { def makeMember0(bSym: Symbol, _useCaseOf: Option[MemberImpl]): Option[MemberImpl] = { if (bSym.isGetter && bSym.isLazy) - Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { override lazy val comment = // The analyser does not duplicate the lazy val's DocDef when it introduces its accessor. thisFactory.comment(bSym.accessed, inTpl) // This hack should be removed after analyser is fixed. override def isLazyVal = true override def useCaseOf = _useCaseOf }) else if (bSym.isGetter && bSym.accessed.isMutable) - Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { override def isVar = true override def useCaseOf = _useCaseOf }) @@ -481,36 +502,36 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } else bSym } - Some(new NonTemplateParamMemberImpl(cSym, inTpl) with HigherKindedImpl with Def { + Some(new NonTemplateParamMemberImpl(cSym, implConv, inTpl) with HigherKindedImpl with Def { override def isDef = true override def useCaseOf = _useCaseOf }) } - else if (bSym.isConstructor) - Some(new NonTemplateParamMemberImpl(bSym, inTpl) with Constructor { + else if (bSym.isConstructor && (implConv == null)) + Some(new NonTemplateParamMemberImpl(bSym, implConv, inTpl) with Constructor { override def isConstructor = true def isPrimary = sym.isPrimaryConstructor override def useCaseOf = _useCaseOf }) else if (bSym.isGetter) // Scala field accessor or Java field - Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { override def isVal = true override def useCaseOf = _useCaseOf }) else if (bSym.isAbstractType) - Some(new NonTemplateMemberImpl(bSym, inTpl) with TypeBoundsImpl with HigherKindedImpl with AbstractType { + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with TypeBoundsImpl with HigherKindedImpl with AbstractType { override def isAbstractType = true override def useCaseOf = _useCaseOf }) - else if (bSym.isAliasType) - Some(new NonTemplateMemberImpl(bSym, inTpl) with HigherKindedImpl with AliasType { + else if (bSym.isAliasType && bSym != AnyRefClass) + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with HigherKindedImpl with AliasType { override def isAliasType = true def alias = makeTypeInTemplateContext(sym.tpe.dealias, inTpl, sym) override def useCaseOf = _useCaseOf }) else if (bSym.isPackage) inTpl match { case inPkg: PackageImpl => makePackage(bSym, inPkg) } - else if ((bSym.isClass || bSym.isModule) && templateShouldDocument(bSym)) + else if ((bSym.isClass || bSym.isModule || bSym == AnyRefClass) && templateShouldDocument(bSym)) Some(makeDocTemplate(bSym, inTpl)) else None @@ -520,16 +541,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { Nil else { val allSyms = useCases(aSym, inTpl.sym) map { case (bSym, bComment, bPos) => - docComments.put(bSym, DocComment(bComment, bPos)) // put the comment in the list, don't parse it yet, closes SI-4898 + docComments.put(bSym, DocComment(bComment, bPos)) // put the comment in the list, don't parse it yet, closes SI-4898 bSym } val member = makeMember0(aSym, None) - if (allSyms.isEmpty) - member.toList - else - // Use cases replace the original definitions - SI-5054 - allSyms flatMap { makeMember0(_, member) } + if (allSyms.isEmpty) + member.toList + else + // Use cases replace the original definitions - SI-5054 + allSyms flatMap { makeMember0(_, member) } } } @@ -639,9 +660,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // nameBuffer append stripPrefixes.foldLeft(pre.prefixString)(_ stripPrefix _) // } val bSym = normalizeTemplate(aSym) - if (bSym.isNonClassType) + if (bSym.isNonClassType) { nameBuffer append bSym.decodedName - else { + } else { val tpl = makeTemplate(bSym) val pos0 = nameBuffer.length refBuffer += pos0 -> (tpl, tpl.name.length) @@ -674,9 +695,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { nameBuffer append '⇒' appendType0(result) /* Polymorphic types */ - case PolyType(tparams, result) => assert(tparams nonEmpty) + case PolyType(tparams, result) => assert(tparams.nonEmpty) // throw new Error("Polymorphic type '" + tpe + "' cannot be printed as a type") - def typeParamsToString(tps: List[Symbol]): String = if(tps isEmpty) "" else + def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else tps.map{tparam => tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams) }.mkString("[", ", ", "]") @@ -692,8 +713,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def templateShouldDocument(aSym: Symbol): Boolean = { - // TODO: document sourceless entities (e.g., Any, etc), based on a new Setting to be added - (aSym.isPackageClass || (aSym.sourceFile != null)) && localShouldDocument(aSym) && + // TODO: document sourceless entities (e.g., Any, etc), based on a new Setting to be added + (aSym.isPackageClass || (aSym.sourceFile != null)) && localShouldDocument(aSym) && ( aSym.owner == NoSymbol || templateShouldDocument(aSym.owner) ) && !isEmptyJavaObject(aSym) } diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala new file mode 100644 index 0000000000..0e44933ac6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -0,0 +1,519 @@ +/* NSC -- new Scala compiler -- Copyright 2007-2012 LAMP/EPFL + * + * This trait finds implicit conversions for a class in the default scope and creates scaladoc entries for each of them. + * + * @author Vlad Ureche + * @author Adriaan Moors + */ + +package scala.tools.nsc +package doc +package model + +import comment._ + +import scala.collection._ +import scala.util.matching.Regex + +import symtab.Flags +import io._ + +import model.{ RootPackage => RootPackageEntity } + +/** + * This trait finds implicit conversions for a class in the default scope and creates scaladoc entries for each of them. + * + * Let's take this as an example: + * {{{ + * object Test { + * class A + * + * class B { + * def foo = 1 + * } + * + * class C extends B { + * def bar = 2 + * class implicit + * } + * + * D def conv(a: A) = new C + * } + * }}} + * + * Overview: + * - scaladoc-ing the above classes, `A` will get two more methods: foo and bar, over its default methods + * - the nested classes (specifically `D` above), abstract types, type aliases and constructor members are not added to + * `A` (see makeMember0 in ModelFactory, last 3 cases) + * - the members added by implicit conversion are always listed under the implicit conversion, not under the class they + * actually come from (`foo` will be listed as coming from the implicit conversion to `C` instead of `B`) - see + * `definitionName` in MemberImpl + * + * Internals: + * TODO: Give an overview here + */ +trait ModelFactoryImplicitSupport { + thisFactory: ModelFactory with CommentFactory with TreeFactory => + + import global._ + import global.analyzer._ + import global.definitions._ + import settings.hardcoded + + // debugging: + val DEBUG: Boolean = settings.docImplicitsDebug.value + val ERROR: Boolean = true // currently we show all errors + @inline final def debug(msg: => String) = if (DEBUG) println(msg) + @inline final def error(msg: => String) = if (ERROR) println(msg) + + /** This is a flag that indicates whether to eliminate implicits that cannot be satisfied within the current scope. + * For example, if an implicit conversion requires that there is a Numeric[T] in scope: + * {{{ + * class A[T] + * class B extends A[Int] + * class C extends A[String] + * implicit def pimpA[T: Numeric](a: A[T]): D + * }}} + * For B, no constraints are generated as Numeric[Int] is already in the default scope. On the other hand, for the + * conversion from C to D, depending on -implicits-show-all, the conversion can: + * - not be generated at all, since there's no Numeric[String] in scope (if ran without -implicits-show-all) + * - generated with a *weird* constraint, Numeric[String] as the user might add it by hand (if flag is enabled) + */ + val implicitsShowAll: Boolean = settings.docImplicitsShowAll.value + class ImplicitNotFound(tpe: Type) extends Exception("No implicit of type " + tpe + " found in scope.") + + /* ============== IMPLEMENTATION PROVIDING ENTITY TYPES ============== */ + + class ImplicitConversionImpl( + val sym: Symbol, + val convSym: Symbol, + val toType: Type, + val constrs: List[Constraint], + inTpl: => DocTemplateImpl) + extends ImplicitConversion { + + def source: DocTemplateEntity = inTpl + + def targetType: TypeEntity = makeType(toType, inTpl) + + def convertorOwner: TemplateEntity = + if (convSym != NoSymbol) + makeTemplate(convSym.owner) + else { + error("Scaladoc implicits: Implicit conversion from " + sym.tpe + " to " + toType + " done by " + convSym + " = NoSymbol!") + makeRootPackage.get // surely the root package was created :) + } + + def convertorMethod: Either[MemberEntity, String] = { + var convertor: MemberEntity = null + + convertorOwner match { + case doc: DocTemplateImpl => + val convertors = members.collect { case m: MemberImpl if m.sym == convSym => m } + if (convertors.length == 1) + convertor = convertors.head + case _ => + } + if (convertor ne null) + Left(convertor) + else + Right(convSym.nameString) + } + + def conversionShortName = convSym.nameString + + def conversionQualifiedName = convertorOwner.qualifiedName + "." + convSym.nameString + + lazy val constraints: List[Constraint] = constrs + + val members: List[MemberEntity] = { + // Obtain the members inherited by the implicit conversion + var memberSyms = toType.members.filter(implicitShouldDocument(_)) + val existingMembers = sym.info.members + + // Debugging part :) + debug(sym.nameString + "\n" + "=" * sym.nameString.length()) + debug(" * conversion " + convSym + " from " + sym.tpe + " to " + toType) + + // Members inherited by implicit conversions cannot override actual members + memberSyms = memberSyms.filterNot((sym1: Symbol) => + existingMembers.exists(sym2 => sym1.name == sym2.name && + !isDistinguishableFrom(toType.memberInfo(sym1), sym.info.memberInfo(sym2)))) + + debug(" -> full type: " + toType) + if (constraints.length != 0) { + debug(" -> constraints: ") + constraints foreach { constr => debug(" - " + constr) } + } + debug(" -> members:") + memberSyms foreach (sym => debug(" - "+ sym.decodedName +" : " + sym.info)) + debug("") + + memberSyms.flatMap((makeMember(_, this, inTpl))) + } + } + + /* ============== MAKER METHODS ============== */ + + /** + * Make the implicit conversion objects + * + * A word about the scope of the implicit conversions: currently we look at a very basic context composed of the + * default Scala imports (Predef._ for example) and the companion object of the current class, if one exists. In the + * future we might want to extend this to more complex scopes. + */ + def makeImplicitConversions(sym: Symbol, inTpl: => DocTemplateImpl): List[ImplicitConversion] = + // Nothing and Null are somewhat special -- they can be transformed by any implicit conversion available in scope. + // But we don't want that, so we'll simply refuse to find implicit conversions on for Nothing and Null + if (!(sym.isClass || sym.isTrait || sym == AnyRefClass) || sym == NothingClass || sym == NullClass) Nil + else { + var context: global.analyzer.Context = global.analyzer.rootContext(NoCompilationUnit) + + val results = global.analyzer.allViewsFrom(sym.tpe, context, sym.typeParams) + var conversions = results.flatMap(result => makeImplicitConversion(sym, result._1, result._2, context, inTpl)) + conversions = conversions.filterNot(_.members.isEmpty) + + // Filter out specialized conversions from array + if (sym == ArrayClass) + conversions = conversions.filterNot((conv: ImplicitConversion) => + hardcoded.arraySkipConversions.contains(conv.conversionQualifiedName)) + + // Filter out non-sensical conversions from value types + if (isScalaValueType(sym.tpe)) + conversions = conversions.filter((ic: ImplicitConversion) => + hardcoded.valueClassFilter(sym.nameString, ic.conversionQualifiedName)) + + // Put the class-specific conversions in front + val (ownConversions, commonConversions) = + conversions.partition(conv => !hardcoded.commonConversionTargets.contains(conv.conversionQualifiedName)) + + ownConversions ::: commonConversions + } + + /** makeImplicitConversion performs the heavier lifting to get the implicit listing: + * - for each possible conversion function (also called view) + * * figures out the final result of the view (to what is our class transformed?) + * * figures out the necessary constraints on the type parameters (such as T <: Int) and the context (such as Numeric[T]) + * * lists all inherited members + * + * What? in details: + * - say we start from a class A[T1, T2, T3, T4] + * - we have an implicit function (view) in scope: + * def pimpA[T3 <: Long, T4](a: A[Int, Foo[Bar[X]], T3, T4])(implicit ev1: Manifest[T4], ev2: Numeric[T4]): PimpedA + * - A is converted to PimpedA ONLY if a couple of constraints are satisfied: + * * T1 must be equal to Int + * * T2 must be equal to Foo[Bar[X]] + * * T3 must be upper bounded by Long + * * there must be evidence of Numeric[T4] and a Mainfest[T4] within scope + * - the final type is PimpedA and A therefore inherits a couple of members from pimpedA + * + * How? + * some notes: + * - Scala's type inference will want to solve all type parameters down to actual types, but we only want constraints + * to maintain generality + * - therefore, allViewsFrom wraps type parameters into "untouchable" type variables that only gather constraints, + * but are never solved down to a type + * - these must be reverted back to the type parameters and the constraints must be extracted and simplified (this is + * done by the uniteConstraints and boundedTParamsConstraints. Be sure to check them out + * - we also need to transform implicit parameters in the view's signature into constraints, such that Numeric[T4] + * appears as a constraint + */ + def makeImplicitConversion(sym: Symbol, result: SearchResult, constrs: List[TypeConstraint], context: Context, inTpl: => DocTemplateImpl): List[ImplicitConversion] = + if (result.tree == EmptyTree) Nil + else { + // `result` will contain the type of the view (= implicit conversion method) + // the search introduces untouchable type variables, but we want to get back to type parameters + val viewFullType = result.tree.tpe + // set the previously implicit parameters to being explicit + + val (viewSimplifiedType, viewImplicitTypes) = removeImplicitParameters(viewFullType) + + // TODO: Isolate this corner case :) - Predef.<%< and put it in the testsuite + if (viewSimplifiedType.params.length != 1) { + // This is known to be caused by the `<%<` object in Predef: + // {{{ + // sealed abstract class <%<[-From, +To] extends (From => To) with Serializable + // object <%< { + // implicit def conformsOrViewsAs[A <% B, B]: A <%< B = new (A <%< B) {def apply(x: A) = x} + // } + // }}} + // so we just won't generate an implicit conversion for implicit methods that only take implicit parameters + return Nil + } + + // type the view application so we get the exact type of the result (not the formal type) + val viewTree = result.tree.setType(viewSimplifiedType) + val appliedTree = new ApplyImplicitView(viewTree, List(Ident("<argument>") setType viewTree.tpe.paramTypes.head)) + val appliedTreeTyped: Tree = { + val newContext = context.makeImplicit(context.ambiguousErrors) + val newTyper = global.analyzer.newTyper(newContext) + newTyper.silent(_.typed(appliedTree, global.analyzer.EXPRmode, WildcardType), false) match { + + case global.analyzer.SilentResultValue(t: Tree) => t + case global.analyzer.SilentTypeError(err) => + global.reporter.warning(sym.pos, err.toString) + return Nil + } + } + + // now we have the final type: + val toType = wildcardToNothing(typeVarToOriginOrWildcard(appliedTreeTyped.tpe.finalResultType)) + + try { + // Transform bound constraints into scaladoc constraints + val implParamConstraints = makeImplicitConstraints(viewImplicitTypes, sym, context, inTpl) + val boundsConstraints = makeBoundedConstraints(sym.typeParams, constrs, inTpl) + // TODO: no substitution constraints appear in the library and compiler scaladoc. Maybe they can be removed? + val substConstraints = makeSubstitutionConstraints(result.subst, inTpl) + val constraints = implParamConstraints ::: boundsConstraints ::: substConstraints + + List(new ImplicitConversionImpl(sym, result.tree.symbol, toType, constraints, inTpl)) + } catch { + case i: ImplicitNotFound => + //println(" Eliminating: " + toType) + Nil + } + } + + def makeImplicitConstraints(types: List[Type], sym: Symbol, context: Context, inTpl: => DocTemplateImpl): List[Constraint] = + types.flatMap((tpe:Type) => { + // TODO: Before creating constraints, map typeVarToOriginOrWildcard on the implicitTypes + val implType = typeVarToOriginOrWildcard(tpe) + val qualifiedName = implType.typeSymbol.ownerChain.reverse.map(_.nameString).mkString(".") + + var available: Option[Boolean] = None + + // see: https://groups.google.com/forum/?hl=en&fromgroups#!topic/scala-internals/gm_fr0RKzC4 + // + // println(implType + " => " + implType.isTrivial) + // var tpes: List[Type] = List(implType) + // while (!tpes.isEmpty) { + // val tpe = tpes.head + // tpes = tpes.tail + // tpe match { + // case TypeRef(pre, sym, args) => + // tpes = pre :: args ::: tpes + // println(tpe + " => " + tpe.isTrivial) + // case _ => + // println(tpe + " (of type" + tpe.getClass + ") => " + tpe.isTrivial) + // } + // } + // println("\n") + + // look for type variables in the type. If there are none, we can decide if the implicit is there or not + if (implType.isTrivial) { + try { + context.flushBuffer() /* any errors here should not prevent future findings */ + // TODO: Not sure this is the right thing to do -- seems similar to what scalac should be doing + val context2 = context.make(context.unit, context.tree, sym.owner, context.scope, context.imports) + val search = inferImplicit(EmptyTree, tpe, false, false, context2, false) + context.flushBuffer() /* any errors here should not prevent future findings */ + + available = Some(search.tree != EmptyTree) + } catch { + case _ => + } + } + + available match { + case Some(true) => + Nil + case Some(false) if (!implicitsShowAll) => + // if -implicits-show-all is not set, we get rid of impossible conversions (such as Numeric[String]) + throw new ImplicitNotFound(implType) + case _ => + val typeParamNames = sym.typeParams.map(_.name) + + // TODO: This is maybe the worst hack I ever did - it's as dirty as hell, but it seems to work, so until I + // learn more about symbols, it'll have to do. + implType match { + case TypeRef(pre, sym, List(TypeRef(NoPrefix, targ, Nil))) if (typeParamNames contains targ.name) => + hardcoded.knownTypeClasses.get(qualifiedName) match { + case Some(explanation) => + List(new KnownTypeClassConstraint { + val typeParamName = targ.nameString + val typeExplanation = explanation + val typeClassEntity = makeTemplate(sym) + val implicitType: TypeEntity = makeType(implType, inTpl) + }) + case None => + List(new TypeClassConstraint { + val typeParamName = targ.nameString + val typeClassEntity = makeTemplate(sym) + val implicitType: TypeEntity = makeType(implType, inTpl) + }) + } + case _ => + List(new ImplicitInScopeConstraint{ + val implicitType: TypeEntity = makeType(implType, inTpl) + }) + } + } + }) + + def makeSubstitutionConstraints(subst: TreeTypeSubstituter, inTpl: => DocTemplateImpl): List[Constraint] = + (subst.from zip subst.to) map { + case (from, to) => + new EqualTypeParamConstraint { + error("Scaladoc implicits: Unexpected type substitution constraint from: " + from + " to: " + to) + val typeParamName = from.toString + val rhs = makeType(to, inTpl) + } + } + + def makeBoundedConstraints(tparams: List[Symbol], constrs: List[TypeConstraint], inTpl: => DocTemplateImpl): List[Constraint] = + (tparams zip constrs) flatMap { + case (tparam, constr) => { + uniteConstraints(constr) match { + case (loBounds, upBounds) => (loBounds filter (_ != NothingClass.tpe), upBounds filter (_ != AnyClass.tpe)) match { + case (Nil, Nil) => + Nil + case (List(lo), List(up)) if (lo == up) => + List(new EqualTypeParamConstraint { + val typeParamName = tparam.nameString + val rhs = makeType(lo, inTpl) + }) + case (List(lo), List(up)) => + List(new BoundedTypeParamConstraint { + val typeParamName = tparam.nameString + val lowerBound = makeType(lo, inTpl) + val upperBound = makeType(up, inTpl) + }) + case (List(lo), Nil) => + List(new LowerBoundedTypeParamConstraint { + val typeParamName = tparam.nameString + val lowerBound = makeType(lo, inTpl) + }) + case (Nil, List(up)) => + List(new UpperBoundedTypeParamConstraint { + val typeParamName = tparam.nameString + val upperBound = makeType(up, inTpl) + }) + case other => + // this is likely an error on the lub/glb side + error("Scaladoc implicits: Error computing lub/glb for: " + (tparam, constr) + ":\n" + other) + Nil + } + } + } + } + + /** + * uniteConstraints takes a TypeConstraint instance and simplifies the constraints inside + * + * Normally TypeConstraint contains multiple lower and upper bounds, and we want to reduce this to a lower and an + * upper bound. Here are a couple of catches we need to be aware of: + * - before finding a view (implicit method in scope that maps class A[T1,T2,.. Tn] to something else) the type + * parameters are transformed into "untouchable" type variables so that type inference does not attempt to + * fully solve them down to a type but rather constrains them on both sides just enough for the view to be + * applicable -- now, we want to transform those type variables back to the original type parameters + * - some of the bounds fail type inference and therefore refer to Nothing => when performing unification (lub, glb) + * they start looking ugly => we (unsoundly) transform Nothing to WildcardType so we fool the unification algorithms + * into thinking there's nothing there + * - we don't want the wildcard types surviving the unification so we replace them back to Nothings + */ + def uniteConstraints(constr: TypeConstraint): (List[Type], List[Type]) = + try { + (List(wildcardToNothing(lub(constr.loBounds map typeVarToOriginOrWildcard))), + List(wildcardToNothing(glb(constr.hiBounds map typeVarToOriginOrWildcard)))) + } catch { + // does this actually ever happen? (probably when type vars occur in the bounds) + case x: Throwable => (constr.loBounds.distinct, constr.hiBounds.distinct) + } + + /** + * Make implicits explicit - Not used curently + */ + object implicitToExplicit extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case MethodType(params, resultType) => + MethodType(params.map(param => if (param.isImplicit) param.cloneSymbol.resetFlag(Flags.IMPLICIT) else param), resultType) + case other => + other + } + } + + /** + * removeImplicitParameters transforms implicit parameters from the view result type into constraints and + * returns the simplified type of the view + * + * for the example view: + * implicit def pimpMyClass[T](a: MyClass[T])(implicit ev: Numeric[T]): PimpedMyClass[T] + * the implicit view result type is: + * (a: MyClass[T])(implicit ev: Numeric[T]): PimpedMyClass[T] + * and the simplified type will be: + * MyClass[T] => PimpedMyClass[T] + */ + def removeImplicitParameters(viewType: Type): (Type, List[Type]) = { + + val params = viewType.paramss.flatten + val (normalParams, implParams) = params.partition(!_.isImplicit) + val simplifiedType = MethodType(normalParams, viewType.finalResultType) + val implicitTypes = implParams.map(_.tpe) + + (simplifiedType, implicitTypes) + } + + /** + * typeVarsToOriginOrWildcard transforms the "untouchable" type variables into either their origins (the original + * type parameters) or into wildcard types if nothing matches + */ + object typeVarToOriginOrWildcard extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case tv: TypeVar => + if (tv.constr.inst.typeSymbol == NothingClass) + WildcardType + else + tv.origin //appliedType(tv.origin.typeConstructor, tv.typeArgs map this) + case other => + if (other.typeSymbol == NothingClass) + WildcardType + else + other + } + } + + /** + * wildcardToNothing transforms wildcard types back to Nothing + */ + object wildcardToNothing extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case WildcardType => + NothingClass.tpe + case other => + other + } + } + + /** implicitShouldDocument decides whether a member inherited by implicit conversion should be documented */ + def implicitShouldDocument(aSym: Symbol): Boolean = { + // We shouldn't document: + // - constructors + // - common methods (in Any, AnyRef, Object) as they are automatically removed + // - private and protected members (not accessible following an implicit conversion) + // - members starting with _ (usually reserved for internal stuff) + localShouldDocument(aSym) && (!aSym.isConstructor) && (aSym.owner != ObjectClass) && + (aSym.owner != AnyClass) && (aSym.owner != AnyRefClass) && + (!aSym.isProtected) && (!aSym.isPrivate) && (!aSym.name.startsWith("_")) && + (aSym.isMethod || aSym.isGetter || aSym.isSetter) && + (aSym.nameString != "getClass") + } + + /* To put it very bluntly: checks if you can call implicitly added method with t1 when t2 is already there in the + * class. We suppose the name of the two members coincides + * + * The trick here is that the resultType does not matter - the condition for removal it that paramss have the same + * structure (A => B => C may not override (A, B) => C) and that all the types involved are + * of the implcit conversion's member are subtypes of the parent members' parameters */ + def isDistinguishableFrom(t1: Type, t2: Type): Boolean = + if (t1.paramss.map(_.length) == t2.paramss.map(_.length)) { + for ((t1p, t2p) <- t1.paramss.flatten zip t2.paramss.flatten) + if (!isSubType(t1 memberInfo t1p, t2 memberInfo t2p)) + return true // if on the corresponding parameter you give a type that is in t1 but not in t2 + // example: + // def foo(a: Either[Int, Double]): Int = 3 + // def foo(b: Left[T1]): Int = 6 + // a.foo(Right(4.5d)) prints out 3 :) + false + } else true // the member structure is different foo(3, 5) vs foo(3)(5) +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala b/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala index 988f2e0ba9..f948d53c8b 100755 --- a/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala @@ -52,7 +52,7 @@ trait TreeFactory { thisTreeFactory: ModelFactory with TreeFactory => if (asym.isSetter) asym = asym.getter(asym.owner) makeTemplate(asym.owner) match { case docTmpl: DocTemplateImpl => - val mbrs: List[MemberImpl] = makeMember(asym,docTmpl) + val mbrs: List[MemberImpl] = makeMember(asym, null, docTmpl) mbrs foreach { mbr => refs += ((start, (mbr,end))) } case _ => } diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index bc5cd4a958..e6bc76f676 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -13,6 +13,7 @@ import scala.collection._ import scala.util.matching.Regex import scala.annotation.switch import util.{NoPosition, Position} +import language.postfixOps /** The comment parser transforms raw comment strings into `Comment` objects. * Call `parse` to run the parser. Note that the parser is stateless and diff --git a/src/compiler/scala/tools/nsc/interactive/BuildManager.scala b/src/compiler/scala/tools/nsc/interactive/BuildManager.scala index 2ef34cdd96..0f89236861 100644 --- a/src/compiler/scala/tools/nsc/interactive/BuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/BuildManager.scala @@ -13,6 +13,7 @@ import util.FakePos import dependencies._ import io.AbstractFile +import language.implicitConversions trait BuildManager { diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 5b298b3761..ec73af4dca 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -19,6 +19,7 @@ import scala.tools.nsc.io.Pickler._ import scala.tools.nsc.typechecker.DivergentImplicit import scala.annotation.tailrec import symtab.Flags.{ACCESSOR, PARAMACCESSOR} +import language.implicitConversions /** The main class of the presentation compiler in an interactive environment such as an IDE */ @@ -530,6 +531,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") unit.defined.clear() unit.synthetics.clear() unit.toCheck.clear() + unit.checkedFeatures = Set() unit.targetPos = NoPosition unit.contexts.clear() unit.problems.clear() @@ -957,7 +959,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") if (ownerTpe.isErroneous) List() else new ImplicitSearch( tree, functionType(List(ownerTpe), AnyClass.tpe), isView = true, - context.makeImplicit(reportAmbiguousErrors = false)).allImplicits + context0 = context.makeImplicit(reportAmbiguousErrors = false)).allImplicits for (view <- applicableViews) { val vtree = viewApply(view) val vpre = stabilizedType(vtree) diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala index 88e3827403..72e5ee42ed 100644 --- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala +++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package interactive import ast.Trees -import symtab.Positions +import ast.Positions import scala.tools.nsc.util.{SourceFile, Position, RangePosition, NoPosition, WorkScheduler} import scala.collection.mutable.ListBuffer diff --git a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala index 34a0a4abb6..e2dcc48709 100644 --- a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala +++ b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala @@ -79,7 +79,7 @@ trait ScratchPadMaker { self: Global => addSandbox(stat) } else { val resName = nextRes() - val dispResName = resName filter ('$' !=) + val dispResName = resName filter ('$' != _) patches += Patch(stat.pos.start, "val " + resName + " = ") addSandbox(stat) toPrint += resultString(nameType(dispResName, stat.tpe), resName) diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala index 40bbd3fa8e..99541ff4b4 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala @@ -81,7 +81,7 @@ private[tests] trait CoreTestDefs else { reporter.println("\naskHyperlinkPos for `" + tree.symbol.name + "` at " + format(pos) + " " + pos.source.file.name) val r = new Response[Position] - // `tree.symbol.sourceFile` was discovered to be null when testing -Yvirtpatmat on the akka presentation test, where a position had shifted to point to `Int` + // `tree.symbol.sourceFile` was discovered to be null when testing using virtpatmat on the akka presentation test, where a position had shifted to point to `Int` // askHyperlinkPos for `Int` at (73,19) pi.scala --> class Int in package scala has null sourceFile! val treePath = if (tree.symbol.sourceFile ne null) tree.symbol.sourceFile.path else null val treeName = if (tree.symbol.sourceFile ne null) tree.symbol.sourceFile.name else null diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index 108d4377a8..bf386d8a12 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -23,6 +23,7 @@ import scala.reflect.NameTransformer._ import util.ScalaClassLoader import ScalaClassLoader._ import scala.tools.util._ +import language.{implicitConversions, existentials} /** The Scala interactive shell. It provides a read-eval-print loop * around the Interpreter class. diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 0c64bb2901..43e01bebfd 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -13,6 +13,7 @@ import scala.sys.BooleanProp import io.VirtualDirectory import scala.tools.nsc.io.AbstractFile import reporters._ +import reporters.{Reporter => NscReporter} import symtab.Flags import scala.reflect.internal.Names import scala.tools.util.PathResolver @@ -23,6 +24,7 @@ import scala.collection.{ mutable, immutable } import scala.util.control.Exception.{ ultimately } import IMain._ import java.util.concurrent.Future +import language.implicitConversions /** directory to save .class files to */ private class ReplVirtualDirectory(out: JPrintWriter) extends VirtualDirectory("(memory)", None) { @@ -202,8 +204,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends }).toList } - implicit def installReplTypeOps(tp: Type): ReplTypeOps = new ReplTypeOps(tp) - class ReplTypeOps(tp: Type) { + implicit class ReplTypeOps(tp: Type) { def orElse(other: => Type): Type = if (tp ne NoType) tp else other def andAlso(fn: Type => Type): Type = if (tp eq NoType) tp else fn(tp) } @@ -274,7 +275,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends protected def createLineManager(classLoader: ClassLoader): Line.Manager = new Line.Manager(classLoader) /** Instantiate a compiler. Overridable. */ - protected def newCompiler(settings: Settings, reporter: Reporter) = { + protected def newCompiler(settings: Settings, reporter: NscReporter) = { settings.outputDirs setSingleOutput virtualDirectory settings.exposeEmptyPackage.value = true @@ -340,7 +341,14 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def getInterpreterClassLoader() = classLoader // Set the current Java "context" class loader to this interpreter's class loader - def setContextClassLoader() = classLoader.setAsContext() + def setContextClassLoader() = { + classLoader.setAsContext() + + // this is risky, but it's our only possibility to make default reflexive mirror to work with REPL + // so far we have only used the default mirror to create a few manifests for the compiler + // so it shouldn't be in conflict with our classloader, especially since it respects its parent + scala.reflect.mirror.classLoader = classLoader + } /** Given a simple repl-defined name, returns the real name of * the class representing it, e.g. for "Bippy" it may return @@ -817,7 +825,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } def lastWarnings: List[(Position, String)] = ( if (lastRun == null) Nil - else removeDupWarnings(lastRun.deprecationWarnings.reverse) ++ lastRun.uncheckedWarnings.reverse + else removeDupWarnings(lastRun.allConditionalWarnings flatMap (_.warnings)) ) private var lastRun: Run = _ private def evalMethod(name: String) = evalClass.getMethods filter (_.getName == name) match { diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala index f9c1907696..a86462ad5f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala @@ -287,7 +287,12 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput } // chasing down results which won't parse + // This used to work fine, now it reports a type error before any + // exception gets to us. See SI-5657. Don't have time to deal with + // it, so disabling everything. def execute(line: String): Option[ExecResult] = { + return None // disabled + val parsed = Parsed(line) def noDotOrSlash = line forall (ch => ch != '.' && ch != '/') diff --git a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala index 9469baa4e2..3520a60ee5 100644 --- a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala +++ b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala @@ -8,6 +8,7 @@ package interpreter import collection.{ mutable, immutable } import mutable.ListBuffer +import language.implicitConversions class ProcessResult(val line: String) { import sys.process._ diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index 68bfeafbc6..40993eb916 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -9,6 +9,7 @@ package interpreter import scala.collection.{ mutable, immutable } import scala.PartialFunction.cond import scala.reflect.internal.Chars +import language.implicitConversions trait MemberHandlers { val intp: IMain @@ -120,7 +121,7 @@ trait MemberHandlers { private def vparamss = member.vparamss 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 definesValue = !isMacro && (vparamss.isEmpty || vparamss.head.isEmpty && vparamss.tail.isEmpty) override def resultExtractionCode(req: Request) = if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else "" } diff --git a/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala b/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala index e92888d89b..e2b1bf34d6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala +++ b/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala @@ -7,6 +7,7 @@ package scala.tools.nsc package interpreter import NamedParam._ +import language.implicitConversions trait NamedParamCreator { protected def freshName: () => String @@ -43,4 +44,4 @@ trait NamedParam { def tpe: String def value: Any override def toString = name + ": " + tpe -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/interpreter/Phased.scala b/src/compiler/scala/tools/nsc/interpreter/Phased.scala index d164d1cbb0..f39c025a86 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Phased.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Phased.scala @@ -7,6 +7,7 @@ package scala.tools.nsc package interpreter import scala.collection.{ mutable, immutable } +import language.implicitConversions /** Mix this into an object and use it as a phasing * swiss army knife. diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index 14876425f4..ecf9dd219b 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package interpreter -import scala.reflect.AnyValManifest import scala.collection.{ mutable, immutable } import scala.util.matching.Regex import scala.tools.nsc.util.{ BatchSourceFile } @@ -14,6 +13,7 @@ import session.{ History } import scala.io.Codec import java.net.{ URL, MalformedURLException } import io.{ Path } +import language.implicitConversions /** Collecting some power mode examples. diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala index 8c589eba60..0c26aa8b28 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala @@ -27,6 +27,12 @@ trait ReplConfig { try Console println msg catch { case x: AssertionError => Console.println("Assertion error printing debugging output: " + x) } + private[nsc] def repldbgex(ex: Throwable): Unit = { + if (isReplDebug) { + echo("Caught/suppressing: " + ex) + ex.printStackTrace + } + } private[nsc] def repldbg(msg: => String) = if (isReplDebug) echo(msg) private[nsc] def repltrace(msg: => String) = if (isReplTrace) echo(msg) private[nsc] def replinfo(msg: => String) = if (isReplInfo) echo(msg) diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplTokens.scala b/src/compiler/scala/tools/nsc/interpreter/ReplTokens.scala index 1c7b256c33..e63fabf151 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplTokens.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplTokens.scala @@ -9,6 +9,7 @@ package interpreter import util.{ BatchSourceFile, Indenter } import scala.tools.nsc.ast.parser.Tokens._ import java.lang.Integer.toOctalString +import language.{implicitConversions, existentials} /** This began as an attempt at a completely minimal * pretty printer for a token stream, but as it turns out diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala index ad6e8dc48d..8b891e0010 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala @@ -6,6 +6,8 @@ package scala.tools.nsc package interpreter +import language.implicitConversions + /** A class which the repl utilizes to expose predefined objects. * The base implementation is empty; the standard repl implementation * is StdReplVals. @@ -58,7 +60,7 @@ object ReplVals { * I have this forwarder which widens the type and then cast the result back * to the dependent type. */ - def manifestToType(m: OptManifest[_]): Global#Type = + def manifestToType(m: Manifest[_]): Global#Type = definitions.manifestToType(m) class AppliedTypeFromManifests(sym: Symbol) { diff --git a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala index 5edc8fd202..59a7b9b5d2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala +++ b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package interpreter class RichClass[T](val clazz: Class[T]) { - def toManifest: Manifest[T] = Manifest.classType(clazz) + def toManifest: Manifest[T] = Manifest[T](ClassManifest[T](clazz).tpe) def toTypeString: String = TypeStrings.fromClazz(clazz) // Sadly isAnonymousClass does not return true for scala anonymous diff --git a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala index 6b56d881fc..872ac00bfd 100644 --- a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala @@ -10,6 +10,7 @@ import java.lang.{ reflect => r } import r.TypeVariable import scala.reflect.NameTransformer import NameTransformer._ +import scala.reflect.{mirror => rm} /** Logic for turning a type into a String. The goal is to be * able to take some arbitrary object 'x' and obtain the most precise @@ -72,8 +73,12 @@ trait TypeStrings { brackets(clazz.getTypeParameters map tvarString: _*) } - private def tparamString[T: Manifest] : String = - brackets(manifest[T].typeArguments map (m => tvarString(List(m.erasure))): _*) + private def tparamString[T: Manifest] : String = { + // [Eugene to Paul] needs review!! + def typeArguments: List[rm.Type] = manifest[T].tpe.typeArguments + def typeVariables: List[java.lang.Class[_]] = typeArguments map (targ => rm.typeToClass(targ)) + brackets(typeArguments map (jc => tvarString(List(jc))): _*) + } /** Going for an overabundance of caution right now. Later these types * can be a lot more precise, but right now the manifests have a habit of diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala index e78e92c8f8..7d2610e3c8 100644 --- a/src/compiler/scala/tools/nsc/interpreter/package.scala +++ b/src/compiler/scala/tools/nsc/interpreter/package.scala @@ -5,6 +5,8 @@ package scala.tools.nsc +import language.implicitConversions + /** The main REPL related classes and values are as follows. * In addition to standard compiler classes Global and Settings, there are: * @@ -33,6 +35,8 @@ package object interpreter extends ReplConfig with ReplStrings { val IR = Results + implicit def postfixOps = language.postfixOps // make all postfix ops in this package compile without warning + private[interpreter] implicit def javaCharSeqCollectionToScala(xs: JCollection[_ <: CharSequence]): List[String] = { import collection.JavaConverters._ xs.asScala.toList map ("" + _) diff --git a/src/compiler/scala/tools/nsc/interpreter/session/package.scala b/src/compiler/scala/tools/nsc/interpreter/session/package.scala index 8fbba2f05e..4e5b08c8cb 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/package.scala +++ b/src/compiler/scala/tools/nsc/interpreter/session/package.scala @@ -5,6 +5,7 @@ package scala.tools.nsc package interpreter +import language.implicitConversions /** Files having to do with the state of a repl session: * lines of text entered, types and terms defined, etc. diff --git a/src/compiler/scala/tools/nsc/io/File.scala b/src/compiler/scala/tools/nsc/io/File.scala index cc512493d9..06cb20e4ac 100644 --- a/src/compiler/scala/tools/nsc/io/File.scala +++ b/src/compiler/scala/tools/nsc/io/File.scala @@ -15,6 +15,7 @@ import java.io.{ BufferedInputStream, BufferedOutputStream, IOException, PrintStream, PrintWriter, Closeable => JCloseable } import java.nio.channels.{ Channel, FileChannel } import scala.io.Codec +import language.{reflectiveCalls, implicitConversions} object File { def pathSeparator = java.io.File.pathSeparator diff --git a/src/compiler/scala/tools/nsc/io/Jar.scala b/src/compiler/scala/tools/nsc/io/Jar.scala index bbed5a9e20..b322df986c 100644 --- a/src/compiler/scala/tools/nsc/io/Jar.scala +++ b/src/compiler/scala/tools/nsc/io/Jar.scala @@ -11,6 +11,7 @@ import java.util.jar._ import collection.JavaConverters._ import Attributes.Name import util.ClassPath +import language.implicitConversions // Attributes.Name instances: // diff --git a/src/compiler/scala/tools/nsc/io/Path.scala b/src/compiler/scala/tools/nsc/io/Path.scala index a1b8e5e4d5..b8cf15bfcf 100644 --- a/src/compiler/scala/tools/nsc/io/Path.scala +++ b/src/compiler/scala/tools/nsc/io/Path.scala @@ -11,6 +11,7 @@ import java.io.{ BufferedInputStream, BufferedOutputStream, RandomAccessFile } import java.net.{ URI, URL } import scala.util.Random.alphanumeric +import language.implicitConversions /** An abstraction for filesystem paths. The differences between * Path, File, and Directory are primarily to communicate intent. diff --git a/src/compiler/scala/tools/nsc/io/Pickler.scala b/src/compiler/scala/tools/nsc/io/Pickler.scala index 80b6e086da..86c7ca7b9a 100644 --- a/src/compiler/scala/tools/nsc/io/Pickler.scala +++ b/src/compiler/scala/tools/nsc/io/Pickler.scala @@ -3,6 +3,7 @@ package scala.tools.nsc.io import annotation.unchecked import Lexer._ import java.io.Writer +import language.implicitConversions /** An abstract class for writing and reading Scala objects to and * from a legible representation. The presesentation follows the following grammar: @@ -168,14 +169,11 @@ object Pickler { 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) { + implicit class TildeDecorator[S](x: S) { /** Infix method that forms a `~`-pair. */ def ~ [T](y: T): S ~ T = new ~ (x, y) } - /** An implicit wrapper that adds `~` as a method to any value. */ - implicit def tildeDecorator[S](x: S): TildeDecorator[S] = new TildeDecorator(x) - /** A converter from binary functions to functions over `~`-pairs */ implicit def fromTilde[T1, T2, R](f: (T1, T2) => R): T1 ~ T2 => R = { case x1 ~ x2 => f(x1, x2) } diff --git a/src/compiler/scala/tools/nsc/io/package.scala b/src/compiler/scala/tools/nsc/io/package.scala index 88679e6dce..52e6de0bed 100644 --- a/src/compiler/scala/tools/nsc/io/package.scala +++ b/src/compiler/scala/tools/nsc/io/package.scala @@ -8,8 +8,11 @@ package scala.tools.nsc import java.util.concurrent.{ Future, Callable } import java.util.{ Timer, TimerTask } import java.util.jar.{ Attributes } +import language.implicitConversions package object io { + implicit def postfixOps = language.postfixOps // make all postfix ops in this package compile without warning + type JManifest = java.util.jar.Manifest type JFile = java.io.File @@ -41,4 +44,4 @@ package object io { alarm.schedule(tt, seconds * 1000) alarm } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 06b06c50a6..ca3f3ff635 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -12,6 +12,7 @@ import scala.tools.nsc.util.OffsetPosition import scala.collection.mutable.ListBuffer import symtab.Flags import JavaTokens._ +import language.implicitConversions trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val global : Global diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index 45b63b0ca0..d47756e757 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -10,6 +10,7 @@ import scala.tools.nsc.util._ import scala.reflect.internal.Chars._ import JavaTokens._ import scala.annotation.switch +import language.implicitConversions // Todo merge these better with Scanners trait JavaScanners extends ast.parser.ScannersCommon { diff --git a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala index 371f4bc4d8..249f754e8f 100644 --- a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala +++ b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala @@ -10,6 +10,7 @@ import transform.ExplicitOuter import ast.{ TreePrinters, Trees } import java.io.{ StringWriter, PrintWriter } import annotation.elidable +import language.postfixOps /** Ancillary bits of ParallelMatching which are better off * out of the way. @@ -29,11 +30,10 @@ trait MatchSupport extends ast.TreeDSL { self: ParallelMatching => object Types { import definitions._ - implicit def enrichType(x: Type): RichType = new RichType(x) val subrangeTypes = Set(ByteClass, ShortClass, CharClass, IntClass) - class RichType(undecodedTpe: Type) { + implicit class RichType(undecodedTpe: Type) { def tpe = decodedEqualsType(undecodedTpe) def isAnyRef = tpe <:< AnyRefClass.tpe diff --git a/src/compiler/scala/tools/nsc/matching/Matrix.scala b/src/compiler/scala/tools/nsc/matching/Matrix.scala index e1ff88557e..b29fac225a 100644 --- a/src/compiler/scala/tools/nsc/matching/Matrix.scala +++ b/src/compiler/scala/tools/nsc/matching/Matrix.scala @@ -9,6 +9,7 @@ package matching import transform.ExplicitOuter import symtab.Flags import scala.collection.mutable +import language.implicitConversions trait Matrix extends MatrixAdditions { self: ExplicitOuter with ParallelMatching => @@ -255,4 +256,4 @@ trait Matrix extends MatrixAdditions { recordSyntheticSym(owner.newVariable(n, pos, flagsLong) setInfo tpe) } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index 43aad9f591..7346d9c59f 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -14,6 +14,7 @@ import transform.ExplicitOuter import symtab.Flags import mutable.ListBuffer import annotation.elidable +import language.postfixOps trait ParallelMatching extends ast.TreeDSL with MatchSupport diff --git a/src/compiler/scala/tools/nsc/matching/PatternBindings.scala b/src/compiler/scala/tools/nsc/matching/PatternBindings.scala index 56297f0195..b2a721c586 100644 --- a/src/compiler/scala/tools/nsc/matching/PatternBindings.scala +++ b/src/compiler/scala/tools/nsc/matching/PatternBindings.scala @@ -8,6 +8,7 @@ package matching import transform.ExplicitOuter import PartialFunction._ +import language.postfixOps trait PatternBindings extends ast.TreeDSL { @@ -133,4 +134,4 @@ trait PatternBindings extends ast.TreeDSL } val NoBinding: Bindings = new Bindings(Nil) -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala index 9254ec8628..48c4a9b5b3 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala @@ -138,7 +138,7 @@ object Plugin { } yield entry)).distinct val loader = loaderFor(alljars) - alljars map (loadFrom(_, loader)) flatten + (alljars map (loadFrom(_, loader))).flatten } /** Instantiate a plugin class, given the class and diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index da913a1601..ba042b7b78 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -70,7 +70,7 @@ trait Plugins { } } - val plugs = pick(roughPluginsList, Set(), phasesSet map (_.phaseName) toSet) + val plugs = pick(roughPluginsList, Set(), (phasesSet map (_.phaseName)).toSet) /** Verify requirements are present. */ for (req <- settings.require.value ; if !(plugs exists (_.name == req))) @@ -112,5 +112,5 @@ trait Plugins { def pluginOptionsHelp: String = (for (plug <- roughPluginsList ; help <- plug.optionsHelp) yield { "\nOptions for plugin '%s':\n%s\n".format(plug.name, help) - }) mkString + }).mkString } diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala index 2ba8c8eb6b..ab8fe23909 100644 --- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala @@ -35,17 +35,22 @@ abstract class AbstractReporter extends Reporter { else _severity if (severity == INFO) { - if (isVerbose || force) + if (isVerbose || force) { + severity.count += 1 display(pos, msg, severity) + } } else { val hidden = testAndLog(pos, severity) if (severity == WARNING && noWarnings) () else { - if (!hidden || isPromptSet) + if (!hidden || isPromptSet) { + severity.count += 1 display(pos, msg, severity) - else if (settings.debug.value) + } else if (settings.debug.value) { + severity.count += 1 display(pos, "[ suppressed ] " + msg, severity) + } if (isPromptSet) displayPrompt diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala index f5335fb0f5..956c43c35a 100644 --- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -75,7 +75,6 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr } def display(pos: Position, msg: String, severity: Severity) { - severity.count += 1 if (severity != ERROR || severity.count <= ERROR_LIMIT) print(pos, msg, severity) } diff --git a/src/compiler/scala/tools/nsc/scratchpad/Executor.scala b/src/compiler/scala/tools/nsc/scratchpad/Executor.scala index ff0f94d897..8a918a829c 100644 --- a/src/compiler/scala/tools/nsc/scratchpad/Executor.scala +++ b/src/compiler/scala/tools/nsc/scratchpad/Executor.scala @@ -28,7 +28,7 @@ object Executor { Console.setOut(newOut) Console.setErr(newOut) try { - singletonInstance(name, classLoader) + singletonInstance(classLoader, name) } catch { case ex: Throwable => unwrapThrowable(ex) match { diff --git a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala index 9cea935a63..c1dad2da82 100644 --- a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala @@ -35,7 +35,7 @@ trait AbsSettings extends scala.reflect.internal.settings.AbsSettings { case s: AbsSettings => this.userSetSettings == s.userSetSettings case _ => false } - override def toString() = "Settings {\n%s}\n" format (userSetSettings map (" " + _ + "\n") mkString) + override def toString() = "Settings {\n%s}\n" format (userSetSettings map (" " + _ + "\n")).mkString def toConciseString = userSetSettings.mkString("(", " ", ")") def checkDependencies = diff --git a/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala b/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala index c010c6a3ea..63775ff1c5 100644 --- a/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala @@ -23,6 +23,7 @@ trait AestheticSettings { def deprecation = settings.deprecation.value def experimental = settings.Xexperimental.value def fatalWarnings = settings.fatalWarnings.value + def feature = settings.feature.value def future = settings.future.value def logClasspath = settings.Ylogcp.value def printStats = settings.Ystatistics.value @@ -30,7 +31,7 @@ trait AestheticSettings { def target = settings.target.value def unchecked = settings.unchecked.value def verbose = settings.verbose.value - def virtPatmat = settings.YvirtPatmat.value + def virtPatmat = !settings.XoldPatmat.value /** Derived values */ def jvm = target startsWith "jvm" diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index e7959f36b2..a52e3b8bbe 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -28,7 +28,7 @@ class MutableSettings(val errorFn: String => Unit) settings } - protected def copyInto(settings: MutableSettings) { + def copyInto(settings: MutableSettings) { allSettings foreach { thisSetting => val otherSetting = settings.allSettings find { _.name == thisSetting.name } otherSetting foreach { otherSetting => @@ -136,7 +136,7 @@ class MutableSettings(val errorFn: String => Unit) val (p, args) = StringOps.splitWhere(s, _ == ':', true) getOrElse (return None) // any non-Nil return value means failure and we return s unmodified - tryToSetIfExists(p, args split "," toList, (s: Setting) => s.tryToSetColon _) + tryToSetIfExists(p, (args split ",").toList, (s: Setting) => s.tryToSetColon _) } // if arg is of form -Xfoo or -Xfoo bar (name = "-Xfoo") diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 14b3bcc8ce..d4c2ffa832 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -11,6 +11,7 @@ package settings import annotation.elidable import scala.tools.util.PathResolver.Defaults import scala.collection.mutable +import language.{implicitConversions, existentials} trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings @@ -62,6 +63,7 @@ trait ScalaSettings extends AbsScalaSettings val classpath = PathSetting ("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp" val d = OutputSetting (outputDirs, ".") val nospecialization = BooleanSetting ("-no-specialization", "Ignore @specialize annotations.") + val language = MultiStringSetting("-language", "feature", "Enable one or more language features.") /** * -X "Advanced" settings @@ -80,6 +82,9 @@ trait ScalaSettings extends AbsScalaSettings val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more detail on why some implicits are not applicable.") val logImplicitConv = BooleanSetting ("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.") val logReflectiveCalls = BooleanSetting("-Xlog-reflective-calls", "Print a message when a reflective method call is generated") + val logRuntimeSplices = BooleanSetting("-Xlog-runtime-splices", "Print a message when Expr.eval or Expr.value cannot be resolved statically.") + val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") + val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) val Xmigration28 = BooleanSetting ("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8.") val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") @@ -103,6 +108,8 @@ trait ScalaSettings extends AbsScalaSettings val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.") val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "") + val XoldPatmat = BooleanSetting ("-Xoldpatmat", "Use the pre-2.10 pattern matcher. Otherwise, the 'virtualizing' pattern matcher is used in 2.10.") + /** Compatibility stubs for options whose value name did * not previously match the option name. */ @@ -116,62 +123,61 @@ trait ScalaSettings extends AbsScalaSettings /** * -Y "Private" settings */ - val overrideObjects = BooleanSetting ("-Yoverride-objects", "Allow member objects to be overridden.") - val overrideVars = BooleanSetting ("-Yoverride-vars", "Allow vars to be overridden.") - val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options.") - val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after") - val check = PhasesSetting ("-Ycheck", "Check the tree at the end of") - val Yshow = PhasesSetting ("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after") - val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination.") - val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees.") - val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL.") - val Xdce = BooleanSetting ("-Ydead-code", "Perform dead code elimination.") - val debug = BooleanSetting ("-Ydebug", "Increase the quantity of debugging output.") - // val doc = BooleanSetting ("-Ydoc", "Generate documentation") - val termConflict = ChoiceSetting ("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", - List("package", "object", "error"), "error") - val inline = BooleanSetting ("-Yinline", "Perform inlining when possible.") - val inlineHandlers= BooleanSetting ("-Yinline-handlers", "Perform exception handler inlining when possible.") - val Xlinearizer = ChoiceSetting ("-Ylinearizer", "which", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo") - val log = PhasesSetting ("-Ylog", "Log operations during") - val Ylogcp = BooleanSetting ("-Ylog-classpath", "Output information about what classpath is being applied.") - val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.") - val noimports = BooleanSetting ("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") - val nopredef = BooleanSetting ("-Yno-predef", "Compile without importing Predef.") - val noAdaptedArgs = BooleanSetting ("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.") - val Yprofile = PhasesSetting ("-Yprofile", "(Requires jvm -agentpath to contain yjgpagent) Profile CPU usage of") - val YprofileMem = BooleanSetting ("-Yprofile-memory", "Profile memory, get heap snapshot after each compiler run (requires yjpagent, see above).") - val YprofileClass = StringSetting ("-Yprofile-class", "class", "Name of profiler class.", "scala.tools.util.YourkitProfiling") - val Yrecursion = IntSetting ("-Yrecursion", "Set recursion depth used when locking symbols.", 0, Some((0, Int.MaxValue)), (_: String) => None) - val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.") - val Xshowtrees = BooleanSetting ("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs.") - val Yshowsyms = BooleanSetting ("-Yshow-syms", "Print the AST symbol hierarchy after each phase.") - val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") - val skip = PhasesSetting ("-Yskip", "Skip") - val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") - val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") - val Ynosqueeze = BooleanSetting ("-Yno-squeeze", "Disable creation of compact code in matching.") - val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (util.Statistics.enabled = _) - val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat - val stopBefore = PhasesSetting ("-Ystop-before", "Stop before") - val refinementMethodDispatch = - ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", - List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") - val globalClass = StringSetting ("-Yglobal-class", "class", "subclass of scala.tools.nsc.Global to use for compiler", "") - val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.") - val YrichExes = BooleanSetting ("-Yrich-exceptions", - "Fancier exceptions. Set source search path with -D" + - sys.SystemProperties.traceSourcePath.key) - val Ybuilderdebug = ChoiceSetting("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none") - val Yreifycopypaste = - BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") - val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") - val Ynotnull = BooleanSetting ("-Ynotnull", "Enable (experimental and incomplete) scala.NotNull.") - val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.") - val etaExpandKeepsStar = BooleanSetting("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.") - val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.") - val YvirtPatmat = BooleanSetting ("-Yvirtpatmat", "Translate pattern matches into flatMap/orElse calls. See scala.MatchingStrategy.") - val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") + val overrideObjects = BooleanSetting ("-Yoverride-objects", "Allow member objects to be overridden.") + val overrideVars = BooleanSetting ("-Yoverride-vars", "Allow vars to be overridden.") + val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options.") + val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after") + val check = PhasesSetting ("-Ycheck", "Check the tree at the end of") + val Yshow = PhasesSetting ("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after") + val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination.") + val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees.") + val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL.") + val Xdce = BooleanSetting ("-Ydead-code", "Perform dead code elimination.") + val debug = BooleanSetting ("-Ydebug", "Increase the quantity of debugging output.") + //val doc = BooleanSetting ("-Ydoc", "Generate documentation") + val termConflict = ChoiceSetting ("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") + val inline = BooleanSetting ("-Yinline", "Perform inlining when possible.") + val inlineHandlers = BooleanSetting ("-Yinline-handlers", "Perform exception handler inlining when possible.") + val Xlinearizer = ChoiceSetting ("-Ylinearizer", "which", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo") + val log = PhasesSetting ("-Ylog", "Log operations during") + val Ylogcp = BooleanSetting ("-Ylog-classpath", "Output information about what classpath is being applied.") + val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.") + val noimports = BooleanSetting ("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") + val nopredef = BooleanSetting ("-Yno-predef", "Compile without importing Predef.") + val noAdaptedArgs = BooleanSetting ("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.") + val Yprofile = PhasesSetting ("-Yprofile", "(Requires jvm -agentpath to contain yjgpagent) Profile CPU usage of") + val YprofileMem = BooleanSetting ("-Yprofile-memory", "Profile memory, get heap snapshot after each compiler run (requires yjpagent, see above).") + val YprofileClass = StringSetting ("-Yprofile-class", "class", "Name of profiler class.", "scala.tools.util.YourkitProfiling") + val Yrecursion = IntSetting ("-Yrecursion", "Set recursion depth used when locking symbols.", 0, Some((0, Int.MaxValue)), (_: String) => None) + val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.") + val Xshowtrees = BooleanSetting ("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs in formatted form.") + val XshowtreesCompact + = BooleanSetting ("-Yshow-trees-compact", "(Requires -Xprint:) Print detailed ASTs in compact form.") + val XshowtreesStringified + = BooleanSetting ("-Yshow-trees-stringified", "(Requires -Xprint:) Print stringifications along with detailed ASTs.") + val Yshowsyms = BooleanSetting ("-Yshow-syms", "Print the AST symbol hierarchy after each phase.") + val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") + val skip = PhasesSetting ("-Yskip", "Skip") + val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") + val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") + val Ynosqueeze = BooleanSetting ("-Yno-squeeze", "Disable creation of compact code in matching.") + val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (util.Statistics.enabled = _) + val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat + val stopBefore = PhasesSetting ("-Ystop-before", "Stop before") + val refinementMethodDispatch + = ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") + val globalClass = StringSetting ("-Yglobal-class", "class", "subclass of scala.tools.nsc.Global to use for compiler", "") + val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.") + val YrichExes = BooleanSetting ("-Yrich-exceptions", "Fancier exceptions. Set source search path with -D" + sys.SystemProperties.traceSourcePath.key) + val Ybuilderdebug = ChoiceSetting ("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none") + val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") + val Ymacrocopypaste = BooleanSetting ("-Ymacro-copypaste", "Dump macro expansions in copypasteable representation.") + val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") + val Ynotnull = BooleanSetting ("-Ynotnull", "Enable (experimental and incomplete) scala.NotNull.") + val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.") + val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.") + val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.") + val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() val YnoProductN = BooleanSetting ("-Yno-productN", "Do not add ProductN to case classes") @@ -180,26 +186,30 @@ trait ScalaSettings extends AbsScalaSettings /** Area-specific debug output. */ - val Ybuildmanagerdebug = BooleanSetting("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.") - val Ycompletion = BooleanSetting("-Ycompletion-debug", "Trace all tab completion activity.") - val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.") - val Yidedebug = BooleanSetting("-Yide-debug", "Generate, validate and output trees using the interactive compiler.") - val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") - val Ymacrodebug = BooleanSetting("-Ymacro-debug", "Trace macro-related activities: generation of synthetics, expansion, exceptions.") - val Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.") - val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.") - val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.") - val Yrepldebug = BooleanSetting("-Yrepl-debug", "Trace all repl activity.") andThen (interpreter.replProps.debug setValue _) - val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.") + val Ybuildmanagerdebug = BooleanSetting("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.") + val Ycompletion = BooleanSetting("-Ycompletion-debug", "Trace all tab completion activity.") + val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.") + val Yidedebug = BooleanSetting("-Yide-debug", "Generate, validate and output trees using the interactive compiler.") + val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") + val Yissuedebug = BooleanSetting("-Yissue-debug", "Print stack traces when a context issues an error.") + val Ymacrodebug = BooleanSetting("-Ymacro-debug", "Trace macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.") + val Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.") + val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.") + val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.") + val Yrepldebug = BooleanSetting("-Yrepl-debug", "Trace all repl activity.") andThen (interpreter.replProps.debug setValue _) + val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.") /** Groups of Settings. */ - val future = BooleanSetting ("-Xfuture", "Turn on future language features.") enabling futureSettings - val optimise = BooleanSetting ("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize" enabling optimiseSettings - val Xexperimental = BooleanSetting ("-Xexperimental", "Enable experimental extensions.") enabling experimentalSettings + val future = BooleanSetting("-Xfuture", "Turn on future language features.") enabling futureSettings + val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize" enabling optimiseSettings + val Xexperimental = BooleanSetting("-Xexperimental", "Enable experimental extensions.") enabling experimentalSettings // Feature extensions - val Xmacros = BooleanSetting ("-Xmacros", "Enable macros.") + val Xmacros = BooleanSetting("-Xmacros", "Enable macros.") // [Martin] Can be retired now. + val XmacroSettings = MultiStringSetting("-Xmacro-settings", "option", "Custom settings for macros.") + val XmacroPrimaryClasspath = PathSetting("-Xmacro-primary-classpath", "Classpath to load macros implementations from, defaults to compilation classpath (aka \"library classpath\".", "") + val XmacroFallbackClasspath = PathSetting("-Xmacro-fallback-classpath", "Classpath to load macros implementations from if they cannot be loaded from library classpath.", "") /** * IDE-specific settings diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index c5b477c7bd..a69b88a5df 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -32,6 +32,7 @@ trait StandardScalaSettings { val deprecation = BooleanSetting ("-deprecation", "Emit warning and location for usages of deprecated APIs.") val encoding = StringSetting ("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding) val explaintypes = BooleanSetting ("-explaintypes", "Explain type errors in more detail.") + val feature = BooleanSetting ("-feature", "Emit warning and location for usages of features that should be imported explicitly.") val g = ChoiceSetting ("-g", "level", "Set level of generated debugging info.", List("none", "source", "line", "vars", "notailcalls"), "vars") val help = BooleanSetting ("-help", "Print a synopsis of standard options") val make = ChoiceSetting ("-make", "policy", "Recompilation detection policy", List("all", "changed", "immediate", "transitive", "transitivenocp"), "all") diff --git a/src/compiler/scala/tools/nsc/symtab/Positions.scala b/src/compiler/scala/tools/nsc/symtab/Positions.scala deleted file mode 100644 index 94b619de90..0000000000 --- a/src/compiler/scala/tools/nsc/symtab/Positions.scala +++ /dev/null @@ -1,30 +0,0 @@ -package scala.tools.nsc -package symtab - -import scala.tools.nsc.util.{ SourceFile, Position, OffsetPosition, NoPosition } - -trait Positions extends scala.reflect.internal.Positions { -self: scala.tools.nsc.symtab.SymbolTable => - - def rangePos(source: SourceFile, start: Int, point: Int, end: Int) = - new OffsetPosition(source, point) - - def validatePositions(tree: Tree) {} - - type Position = scala.tools.nsc.util.Position - val NoPosition = scala.tools.nsc.util.NoPosition - - type TreeAnnotation = scala.tools.nsc.util.TreeAnnotation - def NoTreeAnnotation: TreeAnnotation = NoPosition - def positionToAnnotation(pos: Position): TreeAnnotation = pos - def annotationToPosition(annot: TreeAnnotation): Position = annot.pos - override def _checkSetAnnotation(tree: Tree, annot: TreeAnnotation): Unit = { - if (tree.pos != NoPosition && tree.pos != annot.pos) debugwarn("Overwriting annotation "+ tree.annotation +" of tree "+ tree +" with annotation "+ annot) - // if ((tree.annotation.isInstanceOf[scala.tools.nsc.util.Position] || !annot.isInstanceOf[scala.tools.nsc.util.Position]) && tree.isInstanceOf[Block]) - // println("Updating block from "+ tree.annotation +" to "+ annot) - } - def focusPos(pos: Position): Position = pos.focus - def isRangePos(pos: Position): Boolean = pos.isRange - def showPos(pos: Position): String = pos.show - -} diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala index e62070a239..3cf5cc2f54 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala @@ -7,6 +7,8 @@ package scala.tools.nsc package symtab import scala.collection.{ mutable, immutable } +import language.implicitConversions +import language.postfixOps /** Printing the symbol graph (for those symbols attached to an AST node) * after each phase. diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 758f870d6b..52648380ec 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -149,7 +149,7 @@ abstract class Pickler extends SubComponent { putChildren(sym, children.toList sortBy (_.sealedSortName)) } - for (annot <- sym.annotations filter (ann => ann.isStatic && !ann.isErroneous) reverse) + for (annot <- (sym.annotations filter (ann => ann.isStatic && !ann.isErroneous)).reverse) putAnnotation(sym, annot) } else if (sym != NoSymbol) { @@ -425,7 +425,7 @@ abstract class Pickler extends SubComponent { private def putConstant(c: Constant) { if (putEntry(c)) { if (c.tag == StringTag) putEntry(newTermName(c.stringValue)) - else if (c.tag == ClassTag) putType(c.typeValue) + else if (c.tag == ClazzTag) putType(c.typeValue) else if (c.tag == EnumTag) putSymbol(c.symbolValue) } } @@ -606,7 +606,7 @@ abstract class Pickler extends SubComponent { else if (c.tag == FloatTag) writeLong(floatToIntBits(c.floatValue)) else if (c.tag == DoubleTag) writeLong(doubleToLongBits(c.doubleValue)) else if (c.tag == StringTag) writeRef(newTermName(c.stringValue)) - else if (c.tag == ClassTag) writeRef(c.typeValue) + else if (c.tag == ClazzTag) writeRef(c.typeValue) else if (c.tag == EnumTag) writeRef(c.symbolValue) LITERAL + c.tag // also treats UnitTag, NullTag; no value required case AnnotatedType(annotations, tp, selfsym) => @@ -1059,7 +1059,7 @@ abstract class Pickler extends SubComponent { else if (c.tag == FloatTag) print("Float "+c.floatValue) else if (c.tag == DoubleTag) print("Double "+c.doubleValue) else if (c.tag == StringTag) { print("String "); printRef(newTermName(c.stringValue)) } - else if (c.tag == ClassTag) { print("Class "); printRef(c.typeValue) } + else if (c.tag == ClazzTag) { print("Class "); printRef(c.typeValue) } else if (c.tag == EnumTag) { print("Enum "); printRef(c.symbolValue) } case AnnotatedType(annots, tp, selfsym) => if (settings.selfInAnnots.value) { diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala index 4b847fa94a..028d6f2484 100644 --- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala @@ -12,6 +12,7 @@ import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute, import scala.collection.{ mutable, immutable } import scala.reflect.internal.pickling.UnPickler import ch.epfl.lamp.compiler.msil.Type.TMVarUsage +import language.implicitConversions /** * @author Nikolay Mihaylov diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 97e844f6d8..5a11926048 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -179,7 +179,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => /** If `tp` refers to a non-interface trait, return a * reference to its implementation class. Otherwise return `tp`. */ - def mixinToImplClass(tp: Type): Type = erasure(implSym) { + def mixinToImplClass(tp: Type): Type = AddInterfaces.this.erasure(implSym) { tp match { //@MATN: no normalize needed (comes after erasure) case TypeRef(pre, sym, _) if sym.needsImplClass => typeRef(pre, implClass(sym), Nil) diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index e6f5dc5b5f..fd15d92e37 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -9,6 +9,7 @@ package transform import symtab._ import Flags._ import scala.collection._ +import language.postfixOps abstract class CleanUp extends Transform with ast.TreeDSL { import global._ @@ -542,7 +543,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { if (forMSIL) savingStatics( transformTemplate(tree) ) else transformTemplate(tree) - case Literal(c) if (c.tag == ClassTag) && !forMSIL=> + case Literal(c) if (c.tag == ClazzTag) && !forMSIL=> val tpe = c.typeValue typedWithPos(tree.pos) { if (isPrimitiveValueClass(tpe.typeSymbol)) { diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index ecfc2b6084..5e61359a25 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -210,7 +210,7 @@ abstract class Erasure extends AddInterfaces } else parents ) - ps map boxedSig mkString + (ps map boxedSig).mkString } def boxedSig(tp: Type) = jsig(tp, primitiveOK = false) def boundsSig(bounds: List[Type]) = traceSig("boundsSig", bounds) { @@ -480,11 +480,10 @@ abstract class Erasure extends AddInterfaces // TODO: should we do this for user-defined unapplies as well? // does the first argument list have exactly one argument -- for user-defined unapplies we can't be sure def maybeWrap(bridgingCall: Tree): Tree = { - val canReturnNone = afterErasure( - member.isSynthetic - && (member.name == nme.unapply || member.name == nme.unapplySeq) - && !(member.tpe <:< other.tpe) // no static guarantees (TODO: is the subtype test ever true?) - ) + val canReturnNone = ( // can't statically know which member is going to be selected, so don't let this depend on member.isSynthetic + (member.name == nme.unapply || member.name == nme.unapplySeq) + && !afterErasure((member.tpe <:< other.tpe))) // no static guarantees (TODO: is the subtype test ever true?) + if (canReturnNone) { import CODE._ val typeTest = gen.mkIsInstanceOf(REF(bridge.firstParam), member.tpe.params.head.tpe) @@ -734,7 +733,7 @@ abstract class Erasure extends AddInterfaces /** A replacement for the standard typer's `typed1` method. */ - override protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = { + override def typed1(tree: Tree, mode: Int, pt: Type): Tree = { val tree1 = try { tree match { case InjectDerivedValue(arg) => @@ -1090,7 +1089,7 @@ abstract class Erasure extends AddInterfaces case Match(selector, cases) => Match(Typed(selector, TypeTree(selector.tpe)), cases) - case Literal(ct) if ct.tag == ClassTag + case Literal(ct) if ct.tag == ClazzTag && ct.typeValue.typeSymbol != definitions.UnitClass => val erased = ct.typeValue match { case TypeRef(pre, clazz, args) if clazz.isDerivedValueClass => scalaErasure.eraseNormalClassRef(pre, clazz) diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 16c7c3c3ff..8daad8a2ac 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -357,7 +357,7 @@ abstract class ExplicitOuter extends InfoTransform */ def mixinOuterAccessorDef(mixinClass: Symbol): Tree = { val outerAcc = outerAccessor(mixinClass) overridingSymbol currentClass - def mixinPrefix = currentClass.thisType baseType mixinClass prefix; + def mixinPrefix = (currentClass.thisType baseType mixinClass).prefix assert(outerAcc != NoSymbol, "No outer accessor for inner mixin " + mixinClass + " in " + currentClass) // I added the mixinPrefix.typeArgs.nonEmpty condition to address the // crash in SI-4970. I feel quite sure this can be improved. diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 3515c1d521..007457ef7b 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -50,7 +50,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { val index = alts indexOf imeth assert(index >= 0, alts+" does not contain "+imeth) def altName(index: Int) = newTermName("extension"+index+"$"+imeth.name) - altName(index) #:: ((0 until alts.length).toStream filter (index !=) map altName) + altName(index) #:: ((0 until alts.length).toStream filter (index != _) map altName) case tpe => assert(tpe != NoType, imeth.name+" not found in "+imeth.owner+"'s decls: "+imeth.owner.info.decls) Stream(newTermName("extension$"+imeth.name)) diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index f6dc8fbfb0..6bddfe8d57 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -19,19 +19,6 @@ 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) = - if (sym.isCapturedVariable) { - val symClass = tpe.typeSymbol - def refType(valueRef: Map[Symbol, Symbol], objectRefClass: Symbol) = - if (isPrimitiveValueClass(symClass) && symClass != UnitClass) valueRef(symClass).tpe - else if (erasedTypes) objectRefClass.tpe - else appliedType(objectRefClass, tpe) - if (sym.hasAnnotation(VolatileAttr)) refType(volatileRefClass, VolatileObjectRefClass) - else refType(refClass, ObjectRefClass) - } else tpe - private val lifted = new TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(NoPrefix, sym, Nil) if sym.isClass && !sym.isPackageClass => @@ -46,7 +33,8 @@ abstract class LambdaLift extends InfoTransform { } def transformInfo(sym: Symbol, tp: Type): Type = - boxIfCaptured(sym, lifted(tp), erasedTypes = true) + if (sym.isCapturedVariable) capturedVariableType(sym, tpe = lifted(tp), erasedTypes = true) + else lifted(tp) protected def newTransformer(unit: CompilationUnit): Transformer = new LambdaLifter(unit) @@ -471,6 +459,8 @@ abstract class LambdaLift extends InfoTransform { private def preTransform(tree: Tree) = super.transform(tree) setType lifted(tree.tpe) override def transform(tree: Tree): Tree = tree match { + case Select(ReferenceToBoxed(idt), elem) if elem == nme.elem => + postTransform(preTransform(idt), isBoxedRef = false) case ReferenceToBoxed(idt) => postTransform(preTransform(idt), isBoxedRef = true) case _ => diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 6d4dab57a3..19f32a5411 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -123,7 +123,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ private def rebindSuper(base: Symbol, member: Symbol, mixinClass: Symbol): Symbol = afterPickler { - var bcs = base.info.baseClasses.dropWhile(mixinClass !=).tail + var bcs = base.info.baseClasses.dropWhile(mixinClass != _).tail var sym: Symbol = NoSymbol debuglog("starting rebindsuper " + base + " " + member + ":" + member.tpe + " " + mixinClass + " " + base.info.baseClasses + "/" + bcs) @@ -778,7 +778,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val fields0 = usedBits(cl) if (requiredBitmaps(fields0) < bitmapNum) { - val fields1 = cl.info.decls filter isNonLocalFieldWithBitmap size; + val fields1 = (cl.info.decls filter isNonLocalFieldWithBitmap).size return { if (requiredBitmaps(fields0 + fields1) >= bitmapNum) Some(bitmapFor(cl, offset, valSym, false)) @@ -1085,7 +1085,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // add forwarders assert(sym.alias != NoSymbol, sym) // debuglog("New forwarder: " + sym.defString + " => " + sym.alias.defString) - addDefDef(sym, Apply(staticRef(sym.alias), gen.mkAttributedThis(clazz) :: sym.paramss.head.map(Ident))) + if (!sym.isTermMacro) addDefDef(sym, Apply(staticRef(sym.alias), gen.mkAttributedThis(clazz) :: sym.paramss.head.map(Ident))) } } } diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 12d2513756..ff671088ac 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -8,6 +8,7 @@ package transform import scala.tools.nsc.symtab.Flags import scala.collection.{ mutable, immutable } +import language.postfixOps /** Specialize code on types. * @@ -1327,7 +1328,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } else super.transform(tree) - case TypeApply(Select(qual, name), targs) + case TypeApply(sel @ Select(qual, name), targs) if (!specializedTypeVars(symbol.info).isEmpty && name != nme.CONSTRUCTOR) => debuglog("checking typeapp for rerouting: " + tree + " with sym.tpe: " + symbol.tpe + " tree.tpe: " + tree.tpe) val qual1 = transform(qual) @@ -1341,14 +1342,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val residualTargs = symbol.info.typeParams zip targs collect { case (tvar, targ) if !env.contains(tvar) || !isPrimitiveValueClass(env(tvar).typeSymbol) => targ } + // See SI-5583. Don't know why it happens now if it didn't before. + if (specMember.info.typeParams.isEmpty && residualTargs.nonEmpty) { + log("!!! Type args to be applied, but symbol says no parameters: " + ((specMember.defString, residualTargs))) + localTyper.typed(sel) + } + else { + ifDebug(assert(residualTargs.length == specMember.info.typeParams.length, + "residual: %s, tparams: %s, env: %s".format(residualTargs, specMember.info.typeParams, env)) + ) - ifDebug(assert(residualTargs.length == specMember.info.typeParams.length, - "residual: %s, tparams: %s, env: %s".format(residualTargs, symbol.info.typeParams, env)) - ) - - val tree1 = gen.mkTypeApply(Select(qual1, specMember), residualTargs) - debuglog("rewrote " + tree + " to " + tree1) - localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method + val tree1 = gen.mkTypeApply(Select(qual1, specMember), residualTargs) + debuglog("rewrote " + tree + " to " + tree1) + localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method + } case None => super.transform(tree) } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index f90d3d45fe..57cd51ad35 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -8,6 +8,7 @@ package transform import symtab.Flags._ import scala.collection.{ mutable, immutable } +import language.postfixOps /*<export> */ /** - uncurry all symbol and tree types (@see UnCurryPhase) -- this includes normalizing all proper types. @@ -240,7 +241,6 @@ abstract class UnCurry extends InfoTransform def owner = fun.symbol.owner def targs = fun.tpe.typeArgs def isPartial = fun.tpe.typeSymbol == PartialFunctionClass - assert(!(opt.virtPatmat && isPartial)) // empty-selector matches have already been translated into instantiations of anonymous (partial) functions def parents = if (isFunctionType(fun.tpe)) List(abstractFunctionForFunctionType(fun.tpe), SerializableClass.tpe) @@ -281,7 +281,44 @@ abstract class UnCurry extends InfoTransform val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), List(x)) val body = localTyper.typedPos(fun.pos) { import CODE._ - gen.mkUncheckedMatch(gen.withDefaultCase(substParam(fun.body), scrut => REF(default) APPLY (REF(x)))) + def defaultAction(scrut: Tree) = REF(default) APPLY (REF(x)) + + object withDefaultTransformer extends gen.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(/*gen.mkUnchecked*/(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 + } + override def caseVirtualizedMatchOpt(orig: Tree, prologue: List[Tree], cases: List[Tree], matchEndDef: Tree, wrap: Tree => Tree): Tree = { import CODE._ + val scrutRef = REF(prologue.head.symbol) // scrut valdef is always emitted (except for nested matchers that handle alternatives) + + val casesNewSynthCatchAll = cases.init :+ (deriveLabelDef(cases.last){ + case Apply(matchEnd, List(Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _)))) if exTpt.tpe.typeSymbol eq MatchErrorClass => + assert(matchEnd.symbol == matchEndDef.symbol, "matchEnd discrepancy "+(matchEnd, matchEndDef)) + matchEnd APPLY (defaultAction(scrutRef)) + case x => x + } setSymbol cases.last.symbol setType null) + + val LabelDef(_, List(matchRes), rhs) = matchEndDef + val matchEnd = matchEndDef.symbol + matchRes setType B1.tpe + rhs setType B1.tpe + matchEndDef setType B1.tpe + matchRes.symbol setInfo B1.tpe + matchEnd setInfo MethodType(List(matchRes.symbol), B1.tpe) + cases foreach (c => c.symbol setInfo MethodType(List(), B1.tpe)) + + wrap(Block(prologue ++ casesNewSynthCatchAll, matchEndDef)) + } + } + + withDefaultTransformer(substParam(fun.body)) } body.changeOwner(fun.symbol -> methSym) @@ -293,30 +330,115 @@ abstract class UnCurry extends InfoTransform methDef } - // duplicate before applyOrElseMethodDef is run so we start with the same symbols as applyOrElseMethodDef + // duplicate before applyOrElseMethodDef is run so that it does not mess up our trees and label symbols (we have a fresh set) // otherwise `TreeSymSubstituter(fun.vparams map (_.symbol), params)` won't work as the subst has been run already - val bodyForIDA = fun.body.duplicate + val bodyForIDA = { + val duped = fun.body.duplicate + val oldParams = new mutable.ListBuffer[Symbol]() + val newParams = new mutable.ListBuffer[Symbol]() + + val oldSyms0 = + duped filter { + case l@LabelDef(_, params, _) => + params foreach {p => + val oldSym = p.symbol + p.symbol = oldSym.cloneSymbol + oldParams += oldSym + newParams += p.symbol + } + true + case _ => false + } map (_.symbol) + val oldSyms = oldParams.toList ++ oldSyms0 + val newSyms = newParams.toList ++ (oldSyms0 map (_.cloneSymbol)) + // println("duping "+ oldSyms +" --> "+ (newSyms map (_.ownerChain))) + + val substLabels = new TreeSymSubstituter(oldSyms, newSyms) + + substLabels(duped) + } + def isDefinedAtMethodDef = { val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL) val params = methSym newSyntheticValueParams formals methSym setInfoAndEnter MethodType(params, BooleanClass.tpe) val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params) - def doSubst(x: Tree) = substParam(resetLocalAttrs(x)) // see pos/t1761 for why `resetLocalAttrs` + def doSubst(x: Tree) = substParam(resetLocalAttrsKeepLabels(x)) // see pos/t1761 for why `resetLocalAttrs`, but must keep label symbols around + 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 = { import CODE._ - gen.mkUncheckedMatch( - if (cases exists treeInfo.isDefaultCase) TRUE_typed + val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) + if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) TRUE_typed else doSubst(wrap( - Match(selector, - (cases map (c => deriveCaseDef(c)(x => TRUE_typed))) :+ ( + Match(/*gen.mkUnchecked*/(selector), + (casesNoSynthCatchAll map (c => deriveCaseDef(c)(x => TRUE_typed))) :+ ( DEFAULT ==> FALSE_typed) ))) - ) + } + 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 = _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 + // (the alternative, making `one` CBN, would entail moving away from Option) + Apply(fun.duplicate, List(gen.mkZeroContravariantAfterTyper(a.tpe))) + case _ => + super.transform(tree) + } + } + doSubst(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, prologue: List[Tree], cases: List[Tree], matchEndDef: Tree, wrap: Tree => Tree) = { + val matchEnd = matchEndDef.symbol + val LabelDef(_, List(matchRes), rhs) = matchEndDef + matchRes setType BooleanClass.tpe + rhs setType BooleanClass.tpe + matchEndDef setType BooleanClass.tpe + matchRes.symbol setInfo BooleanClass.tpe + matchEnd setInfo MethodType(List(matchRes.symbol), BooleanClass.tpe) + cases foreach (c => c.symbol setInfo MethodType(List(), BooleanClass.tpe)) + // println("matchEnd: "+ matchEnd) + + // when the type of the selector contains a skolem owned by the applyOrElseMethod, should reskolemize everything, + // for now, just cast the RHS (since we're just trying to keep the typer happy, the cast is meaningless) + // ARGH -- this is why I would prefer the typedMatchAnonFun approach (but alas, CPS blocks that) + val newPrologue = prologue match { + case List(vd@ValDef(mods, name, tpt, rhs)) => List(treeCopy.ValDef(vd, mods, name, tpt, gen.mkAsInstanceOf(rhs, tpt.tpe, true, false))) + case _ => prologue + } + object casesReturnTrue 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, return true instead + case Apply(fun, List(res)) if fun.symbol eq matchEnd => + // println("matchend call "+ fun.symbol) + Apply(fun, List(TRUE_typed)) setType BooleanClass.tpe + case _ => super.transform(tree) + } + } + val newCatchAll = cases.last match { + case LabelDef(n, ps, Apply(matchEnd1, List(Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _))))) if exTpt.tpe.typeSymbol eq MatchErrorClass => + assert(matchEnd1.symbol == matchEnd, "matchEnd discrepancy "+(matchEnd, matchEndDef)) + List(treeCopy.LabelDef(cases.last, n, ps, matchEnd APPLY (FALSE_typed)) setSymbol cases.last.symbol) + case x => Nil + } + val casesWithoutCatchAll = if(newCatchAll.isEmpty) cases else cases.init + doSubst(wrap(Block(newPrologue ++ casesReturnTrue.transformTrees(casesWithoutCatchAll) ++ newCatchAll, matchEndDef))) + + // val duped = idaBlock //.duplicate // TODO: duplication of labeldefs is BROKEN + // duped foreach { + // case l@LabelDef(name, params, rhs) if gen.hasSynthCaseSymbol(l) => println("newInfo"+ l.symbol.info) + // case _ => + // } } } + val body = isDefinedAtTransformer(bodyForIDA) body.changeOwner(fun.symbol -> methSym) @@ -327,11 +449,14 @@ abstract class UnCurry extends InfoTransform if (isPartial) List(applyOrElseMethodDef, isDefinedAtMethodDef) else List(applyMethodDef) - localTyper.typedPos(fun.pos) { + // println("MEMBERS "+ members) + val res = localTyper.typedPos(fun.pos) { Block( List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, fun.pos)), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } + // println("MEMBERS TYPED "+ members) + res } def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = { @@ -358,18 +483,18 @@ abstract class UnCurry extends InfoTransform def sequenceToArray(tree: Tree) = { val toArraySym = tree.tpe member nme.toArray assert(toArraySym != NoSymbol) - def getManifest(tp: Type): Tree = { - val manifestOpt = localTyper.findManifest(tp, false) + def getClassTag(tp: Type): Tree = { + val tag = localTyper.resolveClassTag(tree, tp) // Don't want bottom types getting any further than this (SI-4024) - if (tp.typeSymbol.isBottomClass) getManifest(AnyClass.tpe) - else if (!manifestOpt.tree.isEmpty) manifestOpt.tree - else if (tp.bounds.hi ne tp) getManifest(tp.bounds.hi) - else localTyper.getManifestTree(tree, tp, false) + if (tp.typeSymbol.isBottomClass) getClassTag(AnyClass.tpe) + else if (!tag.isEmpty) tag + else if (tp.bounds.hi ne tp) getClassTag(tp.bounds.hi) + else localTyper.TyperErrorGen.MissingClassTagError(tree, tp) } afterUncurry { localTyper.typedPos(pos) { Apply(gen.mkAttributedSelect(tree, toArraySym), - List(getManifest(tree.tpe.baseType(TraversableClass).typeArgs.head))) + List(getClassTag(tree.tpe.baseType(TraversableClass).typeArgs.head))) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index ff0bdf7580..e9c3bef737 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -277,11 +277,6 @@ trait ContextErrors { setError(tree) } - def MultiDimensionalArrayError(tree: Tree) = { - issueNormalTypeError(tree, "cannot create a generic multi-dimensional array of more than "+ definitions.MaxArrayDims+" dimensions") - setError(tree) - } - //typedSuper def MixinMissingParentClassNameError(tree: Tree, mix: Name, clazz: Symbol) = issueNormalTypeError(tree, mix+" does not name a parent class of "+clazz) @@ -344,6 +339,11 @@ trait ContextErrors { setError(tree) } + def MacroEtaError(tree: Tree) = { + issueNormalTypeError(tree, "macros cannot be eta-expanded") + setError(tree) + } + //typedReturn def ReturnOutsideOfDefError(tree: Tree) = { issueNormalTypeError(tree, "return outside method definition") @@ -453,6 +453,9 @@ trait ContextErrors { // doTypeApply //tryNamesDefaults + def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) = + NormalTypeError(tree, "macros application do not support named and/or default arguments") + def WrongNumberOfArgsError(tree: Tree, fun: Tree) = NormalTypeError(tree, "wrong number of arguments for "+ treeSymTypeMsg(fun)) @@ -480,7 +483,7 @@ trait ContextErrors { val keep = missing take 3 map (_.name) ".\nUnspecified value parameter%s %s".format( if (missing.tail.isEmpty) "" else "s", - if (missing drop 3 nonEmpty) (keep :+ "...").mkString(", ") + if ((missing drop 3).nonEmpty) (keep :+ "...").mkString(", ") else keep.mkString("", ", ", ".") ) } @@ -501,6 +504,9 @@ trait ContextErrors { def ApplyWithoutArgsError(tree: Tree, fun: Tree) = NormalTypeError(tree, fun.tpe+" does not take parameters") + def DynamicVarArgUnsupported(tree: Tree, name: String) = + issueNormalTypeError(tree, name+ " does not support passing a vararg parameter") + //checkClassType def TypeNotAStablePrefixError(tpt: Tree, pre: Type) = { issueNormalTypeError(tpt, "type "+pre+" is not a stable prefix") @@ -581,9 +587,9 @@ trait ContextErrors { def AbstractExistentiallyOverParamerizedTpeError(tree: Tree, tp: Type) = issueNormalTypeError(tree, "can't existentially abstract over parameterized type " + tp) - //manifestTreee - def MissingManifestError(tree: Tree, full: Boolean, tp: Type) = { - issueNormalTypeError(tree, "cannot find "+(if (full) "" else "class ")+"manifest for element type "+tp) + // classTagTree + def MissingClassTagError(tree: Tree, tp: Type) = { + issueNormalTypeError(tree, "cannot find class tag for element type "+tp) setError(tree) } @@ -622,7 +628,6 @@ trait ContextErrors { 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 "" ) ) } @@ -642,7 +647,7 @@ trait ContextErrors { private def applyErrorMsg(tree: Tree, msg: String, argtpes: List[Type], pt: Type) = { def asParams(xs: List[Any]) = xs.mkString("(", ", ", ")") - def resType = if (pt isWildcard) "" else " with expected result type " + pt + def resType = if (pt.isWildcard) "" else " with expected result type " + pt def allTypes = (alternatives(tree) flatMap (_.paramTypes)) ++ argtpes :+ pt def locals = alternatives(tree) flatMap (_.typeParams) @@ -832,7 +837,7 @@ trait ContextErrors { implicit val context0 = context object SymValidateErrors extends Enumeration { - val ImplicitConstr, ImplicitNotTerm, ImplicitTopObject, + val ImplicitConstr, ImplicitNotTermOrClass, ImplicitAtToplevel, OverrideClass, SealedNonClass, AbstractNonClass, OverrideConstr, AbstractOverride, LazyAndEarlyInit, ByNameParameter, AbstractVar = Value @@ -848,6 +853,19 @@ trait ContextErrors { def TypeSigError(tree: Tree, ex: TypeError) = { ex match { + case CyclicReference(_, _) if tree.symbol.isTermMacro => + // say, we have a macro def `foo` and its macro impl `impl` + // if impl: 1) omits return type, 2) has anything implicit in its body, 3) sees foo + // + // then implicit search will trigger an error + // (note that this is not a compilation error, it's an artifact of implicit search algorithm) + // normally, such "errors" are discarded by `isCyclicOrErroneous` in Implicits.scala + // but in our case this won't work, because isCyclicOrErroneous catches CyclicReference exceptions + // while our error will manifest itself as a "recursive method needs a return type" + // + // hence we (together with reportTypeError in TypeDiagnostics) make sure that this CyclicReference + // evades all the handlers on its way and successfully reaches `isCyclicOrErroneous` in Implicits + throw ex case CyclicReference(sym, info: TypeCompleter) => issueNormalTypeError(tree, typer.cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) case _ => @@ -898,10 +916,10 @@ trait ContextErrors { case ImplicitConstr => "`implicit' modifier not allowed for constructors" - case ImplicitNotTerm => - "`implicit' modifier can be used only for values, variables and methods" + case ImplicitNotTermOrClass => + "`implicit' modifier can be used only for values, variables, methods and classes" - case ImplicitTopObject => + case ImplicitAtToplevel => "`implicit' modifier cannot be used for top-level objects" case OverrideClass => diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 9b1f395ad0..e2d4efab83 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -67,6 +67,7 @@ trait Contexts { self: Analyzer => val c = sc.make(unit, tree, sc.owner, sc.scope, sc.imports) if (erasedTypes) c.setThrowErrors() else c.setReportErrors() c.implicitsEnabled = !erasedTypes + c.enrichmentEnabled = c.implicitsEnabled c } @@ -105,7 +106,7 @@ trait Contexts { self: Analyzer => // not inherited to child contexts var depth: Int = 0 var imports: List[ImportInfo] = List() // currently visible imports - var openImplicits: List[(Type,Symbol)] = List() // types for which implicit arguments + var openImplicits: List[(Type,Tree)] = List() // types for which implicit arguments // are currently searched // for a named application block (Tree) the corresponding NamedApplyInfo var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None @@ -119,6 +120,8 @@ trait Contexts { self: Analyzer => var diagnostic: List[String] = Nil // these messages are printed when issuing an error var implicitsEnabled = false + var macrosEnabled = true + var enrichmentEnabled = false // to selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed var checking = false var retyping = false @@ -181,11 +184,49 @@ trait Contexts { self: Analyzer => def logError(err: AbsTypeError) = buffer += err + def withImplicitsEnabled[T](op: => T): T = { + val saved = implicitsEnabled + implicitsEnabled = true + try op + finally implicitsEnabled = saved + } + def withImplicitsDisabled[T](op: => T): T = { val saved = implicitsEnabled implicitsEnabled = false + val savedP = enrichmentEnabled + enrichmentEnabled = false try op - finally implicitsEnabled = saved + finally { + implicitsEnabled = saved + enrichmentEnabled = savedP + } + } + + def withImplicitsDisabledAllowEnrichment[T](op: => T): T = { + val saved = implicitsEnabled + implicitsEnabled = false + val savedP = enrichmentEnabled + enrichmentEnabled = true + try op + finally { + implicitsEnabled = saved + enrichmentEnabled = savedP + } + } + + def withMacrosEnabled[T](op: => T): T = { + val saved = macrosEnabled + macrosEnabled = true + try op + finally macrosEnabled = saved + } + + def withMacrosDisabled[T](op: => T): T = { + val saved = macrosEnabled + macrosEnabled = false + try op + finally macrosEnabled = saved } def make(unit: CompilationUnit, tree: Tree, owner: Symbol, @@ -223,6 +264,8 @@ trait Contexts { self: Analyzer => c.diagnostic = this.diagnostic c.typingIndentLevel = typingIndentLevel c.implicitsEnabled = this.implicitsEnabled + c.macrosEnabled = this.macrosEnabled + c.enrichmentEnabled = this.enrichmentEnabled c.checking = this.checking c.retyping = this.retyping c.openImplicits = this.openImplicits @@ -237,6 +280,7 @@ trait Contexts { self: Analyzer => val c = make(unit, EmptyTree, owner, scope, imports) c.setReportErrors() c.implicitsEnabled = true + c.macrosEnabled = true c } @@ -274,6 +318,7 @@ trait Contexts { self: Analyzer => def makeImplicit(reportAmbiguousErrors: Boolean) = { val c = makeSilent(reportAmbiguousErrors) c.implicitsEnabled = false + c.enrichmentEnabled = false c } @@ -312,6 +357,7 @@ trait Contexts { self: Analyzer => def issue(err: AbsTypeError) { debugwarn("issue error: " + err.errMsg) + if (settings.Yissuedebug.value) (new Exception).printStackTrace() if (reportErrors) unitError(err.errPos, addDiagString(err.errMsg)) else if (bufferErrors) { buffer += err } else throw new TypeError(err.errPos, err.errMsg) @@ -319,6 +365,7 @@ trait Contexts { self: Analyzer => def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { debugwarn("issue ambiguous error: " + err.errMsg) + if (settings.Yissuedebug.value) (new Exception).printStackTrace() if (ambiguousErrors) { if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) unitError(err.errPos, err.errMsg) @@ -328,6 +375,7 @@ trait Contexts { self: Analyzer => def issueAmbiguousError(err: AbsTypeError) { debugwarn("issue ambiguous error: " + err.errMsg) + if (settings.Yissuedebug.value) (new Exception).printStackTrace() if (ambiguousErrors) unitError(err.errPos, addDiagString(err.errMsg)) else if (bufferErrors) { buffer += err } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 75440a1136..4fb9362ccc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -32,7 +32,10 @@ trait Implicits { import global.typer.{ printTyping, deindentTyping, indentTyping, printInference } def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = - inferImplicit(tree, pt, reportAmbiguous, isView, context, true) + inferImplicit(tree, pt, reportAmbiguous, isView, context, true, NoPosition) + + def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult = + inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, NoPosition) /** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch` * for more info how the search is conducted. @@ -48,9 +51,12 @@ trait Implicits { * @param saveAmbiguousDivergent False if any divergent/ambiguous errors should be ignored after * implicits search, * true if they should be reported (used in further typechecking). + * @param pos Position that is should be used for tracing and error reporting + * (useful when we infer synthetic stuff and pass EmptyTree in the `tree` argument) + * If it's set NoPosition, then position-based services will use `tree.pos` * @return A search result */ - def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult = { + def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean, pos: Position): SearchResult = { printInference("[infer %s] %s with pt=%s in %s".format( if (isView) "view" else "implicit", tree, pt, context.owner.enclClass) @@ -71,9 +77,11 @@ trait Implicits { if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty) printTyping("typing implicit: %s %s".format(tree, context.undetparamsString)) val implicitSearchContext = context.makeImplicit(reportAmbiguous) - val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext).bestImplicit - if (saveAmbiguousDivergent && implicitSearchContext.hasErrors) + val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit + if (saveAmbiguousDivergent && implicitSearchContext.hasErrors) { context.updateBuffer(implicitSearchContext.errBuffer.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent)) + debugwarn("update buffer: " + implicitSearchContext.errBuffer) + } printInference("[infer implicit] inferred " + result) context.undetparams = context.undetparams filterNot result.subst.from.contains @@ -86,6 +94,27 @@ trait Implicits { result } + /** Find all views from type `tp` (in which `tpars` are free) + * + * Note that the trees in the search results in the returned list share the same type variables. + * Ignore their constr field! The list of type constraints returned along with each tree specifies the constraints that + * must be met by the corresponding type parameter in `tpars` (for the returned implicit view to be valid). + * + * @arg tp from-type for the implicit conversion + * @arg context search implicits here + * @arg tpars symbols that should be considered free type variables + * (implicit search should not try to solve them, just track their constraints) + */ + def allViewsFrom(tp: Type, context: Context, tpars: List[Symbol]): List[(SearchResult, List[TypeConstraint])] = { + // my untouchable typevars are better than yours (they can't be constrained by them) + val tvars = tpars map (TypeVar untouchable _) + val tpSubsted = tp.subst(tpars, tvars) + + val search = new ImplicitSearch(EmptyTree, functionType(List(tpSubsted), AnyClass.tpe), true, context.makeImplicit(false)) + + search.allImplicitsPoly(tvars) + } + private final val sizeLimit = 50000 private type Infos = List[ImplicitInfo] private type Infoss = List[List[ImplicitInfo]] @@ -100,8 +129,6 @@ trait Implicits { improvesCache.clear() } - private val ManifestSymbols = Set(PartialManifestClass, FullManifestClass, OptManifestClass) - /* Map a polytype to one in which all type parameters and argument-dependent types are replaced by wildcards. * Consider `implicit def b(implicit x: A): x.T = error("")`. We need to approximate DebruijnIndex types * when checking whether `b` is a valid implicit, as we haven't even searched a value for the implicit arg `x`, @@ -139,7 +166,7 @@ trait Implicits { } def isCyclicOrErroneous = - try containsError(tpe) + try sym.hasFlag(LOCKED) || containsError(tpe) catch { case _: CyclicReference => true } var useCountArg: Int = 0 @@ -251,8 +278,11 @@ trait Implicits { * @param pt The original expected type of the implicit. * @param isView We are looking for a view * @param context0 The context used for the implicit search + * @param pos0 Position that is preferable for use in tracing and error reporting + * (useful when we infer synthetic stuff and pass EmptyTree in the `tree` argument) + * If it's set to NoPosition, then position-based services will use `tree.pos` */ - class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context) + class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context, pos0: Position = NoPosition) extends Typer(context0) with ImplicitsContextErrors { printTyping( ptBlock("new ImplicitSearch", @@ -264,6 +294,13 @@ trait Implicits { ) ) // assert(tree.isEmpty || tree.pos.isDefined, tree) + def pos = if (pos0 != NoPosition) pos0 else tree.pos + + def failure(what: Any, reason: String, pos: Position = this.pos): SearchResult = { + if (settings.XlogImplicits.value) + reporter.echo(pos, what+" is not a valid implicit value for "+pt+" because:\n"+reason) + SearchFailure + } import infer._ /** Is implicit info `info1` better than implicit info `info2`? @@ -351,18 +388,18 @@ trait Implicits { * @pre `info.tpe` does not contain an error */ private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean): SearchResult = { - (context.openImplicits find { case (tp, sym) => sym == tree.symbol && dominates(pt, tp)}) match { + (context.openImplicits find { case (tp, tree1) => tree1.symbol == tree.symbol && dominates(pt, tp)}) match { case Some(pending) => - // println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG + //println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG throw DivergentImplicit case None => try { - context.openImplicits = (pt, tree.symbol) :: context.openImplicits + context.openImplicits = (pt, tree) :: context.openImplicits // println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG typedImplicit0(info, ptChecked) } catch { case ex: DivergentImplicit => - // println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG + //println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG if (context.openImplicits.tail.isEmpty) { if (!(pt.isErroneous)) DivergingImplicitExpansionError(tree, pt, info.sym)(context) @@ -494,7 +531,7 @@ trait Implicits { private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean): SearchResult = { incCounter(plausiblyCompatibleImplicits) - printTyping( + printTyping ( ptBlock("typedImplicit0", "info.name" -> info.name, "ptChecked" -> ptChecked, @@ -515,7 +552,7 @@ trait Implicits { private def typedImplicit1(info: ImplicitInfo): SearchResult = { incCounter(matchingImplicits) - val itree = atPos(tree.pos.focus) { + val itree = atPos(pos.focus) { if (info.pre == NoPrefix) Ident(info.name) else Select(gen.mkAttributedQualifier(info.pre), info.name) } @@ -523,11 +560,7 @@ trait Implicits { typeDebug.ptTree(itree), wildPt, info.name, info.tpe) ) - def fail(reason: String): SearchResult = { - if (settings.XlogImplicits.value) - inform(itree+" is not a valid implicit value for "+pt+" because:\n"+reason) - SearchFailure - } + def fail(reason: String): SearchResult = failure(itree, reason) try { val itree1 = if (isView) { @@ -677,7 +710,7 @@ trait Implicits { def comesBefore(sym: Symbol, owner: Symbol) = { val ownerPos = owner.pos.pointOrElse(Int.MaxValue) sym.pos.pointOrElse(0) < ownerPos && ( - if (sym hasAccessorFlag) { + if (sym.hasAccessorFlag) { val symAcc = sym.accessed // #3373 symAcc.pos.pointOrElse(0) < ownerPos && !(owner.ownerChain exists (o => (o eq sym) || (o eq symAcc))) // probably faster to iterate only once, don't feel like duplicating hasTransOwner for this case @@ -707,6 +740,7 @@ trait Implicits { info.isCyclicOrErroneous || isView && isPredefMemberNamed(info.sym, nme.conforms) || isShadowed(info.name) + || (!context.macrosEnabled && info.sym.isTermMacro) ) /** True if a given ImplicitInfo (already known isValid) is eligible. @@ -796,7 +830,7 @@ trait Implicits { /** Returns all eligible ImplicitInfos and their SearchResults in a map. */ - def findAll() = eligible map (info => (info, typedImplicit(info, false))) toMap + def findAll() = (eligible map (info => (info, typedImplicit(info, false)))).toMap /** Returns the SearchResult of the best match. */ @@ -825,7 +859,7 @@ trait Implicits { throw DivergentImplicit if (invalidImplicits.nonEmpty) - setAddendum(tree.pos, () => + setAddendum(pos, () => "\n Note: implicit "+invalidImplicits.head+" is not applicable here"+ " because it comes after the application point and it lacks an explicit result type") } @@ -1085,111 +1119,58 @@ trait Implicits { implicitInfoss1 } - /** Creates a tree that calls the relevant factory method in object - * reflect.Manifest for type 'tp'. An EmptyTree is returned if - * no manifest is found. todo: make this instantiate take type params as well? - */ - private def manifestOfType(tp: Type, full: Boolean): SearchResult = { - - /** Creates a tree that calls the factory method called constructor in object reflect.Manifest */ - def manifestFactoryCall(constructor: String, tparg: Type, args: Tree*): Tree = - if (args contains EmptyTree) EmptyTree - else typedPos(tree.pos.focus) { - val mani = gen.mkManifestFactoryCall(full, constructor, tparg, args.toList) - if (settings.debug.value) println("generated manifest: "+mani) // DEBUG - mani - } + // these should be lazy, otherwise we wouldn't be able to compile scala-library with starr + private val TagSymbols = Set(ClassTagClass, TypeTagClass, ConcreteTypeTagClass) + private val TagMaterializers = Map( + ClassTagClass -> MacroInternal_materializeClassTag, + TypeTagClass -> MacroInternal_materializeTypeTag, + ConcreteTypeTagClass -> MacroInternal_materializeConcreteTypeTag + ) - /** Creates a tree representing one of the singleton manifests.*/ - def findSingletonManifest(name: String) = typedPos(tree.pos.focus) { - Select(gen.mkAttributedRef(FullManifestModule), name) - } + def tagOfType(pre: Type, tp: Type, tagClass: Symbol): SearchResult = { + def success(arg: Tree) = + try { + val tree1 = typed(atPos(pos.focus)(arg)) + def isErroneous = tree exists (_.isErroneous) + if (context.hasErrors) failure(tp, "failed to typecheck the materialized typetag: %n%s".format(context.errBuffer.head.errMsg), context.errBuffer.head.errPos) + else new SearchResult(tree1, EmptyTreeTypeSubstituter) + } catch { + case ex: TypeError => + failure(arg, "failed to typecheck the materialized typetag: %n%s".format(ex.msg), ex.pos) + } - /** Re-wraps a type in a manifest before calling inferImplicit on the result */ - def findManifest(tp: Type, manifestClass: Symbol = if (full) FullManifestClass else PartialManifestClass) = - inferImplicit(tree, appliedType(manifestClass, tp), true, false, context).tree - - def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass) - def mot(tp0: Type, from: List[Symbol], to: List[Type]): SearchResult = { - implicit def wrapResult(tree: Tree): SearchResult = - if (tree == EmptyTree) SearchFailure else new SearchResult(tree, if (from.isEmpty) EmptyTreeTypeSubstituter else new TreeTypeSubstituter(from, to)) - - val tp1 = tp0.normalize - tp1 match { - case ThisType(_) | SingleType(_, _) => - // can't generate a reference to a value that's abstracted over by an existential - if (containsExistential(tp1)) EmptyTree - else manifestFactoryCall("singleType", tp, gen.mkAttributedQualifier(tp1)) - case ConstantType(value) => - manifestOfType(tp1.deconst, full) - case TypeRef(pre, sym, args) => - if (isPrimitiveValueClass(sym) || isPhantomClass(sym)) { - findSingletonManifest(sym.name.toString) - } else if (sym == ObjectClass || sym == AnyRefClass) { - findSingletonManifest("Object") - } else if (sym == RepeatedParamClass || sym == ByNameParamClass) { - EmptyTree - } else if (sym == ArrayClass && args.length == 1) { - manifestFactoryCall("arrayType", args.head, findManifest(args.head)) - } else if (sym.isClass) { - val classarg0 = gen.mkClassOf(tp1) - val classarg = tp match { - case _: ExistentialType => gen.mkCast(classarg0, ClassType(tp)) - case _ => classarg0 - } - val suffix = classarg :: (args map findSubManifest) - manifestFactoryCall( - "classType", tp, - (if ((pre eq NoPrefix) || pre.typeSymbol.isStaticOwner) suffix - else findSubManifest(pre) :: suffix): _*) - } else if (sym.isExistentiallyBound && full) { - manifestFactoryCall("wildcardType", tp, - findManifest(tp.bounds.lo), findManifest(tp.bounds.hi)) - } - // looking for a manifest of a type parameter that hasn't been inferred by now, - // can't do much, but let's not fail - else if (undetParams contains sym) { - // #3859: need to include the mapping from sym -> NothingClass.tpe in the SearchResult - mot(NothingClass.tpe, sym :: from, NothingClass.tpe :: to) - } else { - // a manifest should have been found by normal searchImplicit - EmptyTree - } - case RefinedType(parents, decls) => // !!! not yet: if !full || decls.isEmpty => - // refinement is not generated yet - if (hasLength(parents, 1)) findManifest(parents.head) - else if (full) manifestFactoryCall("intersectionType", tp, parents map findSubManifest: _*) - else mot(erasure.intersectionDominator(parents), from, to) - case ExistentialType(tparams, result) => - mot(tp1.skolemizeExistential, from, to) - case _ => - EmptyTree -/* !!! 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 Reifier() - val rtree = reifier.reifyTopLevel(tp1) - manifestFactoryCall("apply", tp, rtree) -*/ - } + val prefix = (tagClass, pre) match { + // ClassTags only exist for scala.reflect.mirror, so their materializer doesn't care about prefixes + case (ClassTagClass, _) => + gen.mkAttributedRef(Reflect_mirror) setType singleType(Reflect_mirror.owner.thisPrefix, Reflect_mirror) + // [Eugene to Martin] this is the crux of the interaction between implicits and reifiers + // here we need to turn a (supposedly path-dependent) type into a tree that will be used as a prefix + // I'm not sure if I've done this right - please, review + case (_, SingleType(prePre, preSym)) => + gen.mkAttributedRef(prePre, preSym) setType pre + // necessary only to compile typetags used inside the Universe cake + case (_, ThisType(thisSym)) => + gen.mkAttributedThis(thisSym) + case _ => + // if ``pre'' is not a PDT, e.g. if someone wrote + // implicitly[scala.reflect.makro.Context#TypeTag[Int]] + // then we need to fail, because we don't know the prefix to use during type reification + return failure(tp, "tag error: unsupported prefix type %s (%s)".format(pre, pre.kind)) } - mot(tp, Nil, Nil) + // todo. migrate hardcoded materialization in Implicits to corresponding implicit macros + var materializer = atPos(pos.focus)(Apply(TypeApply(Ident(TagMaterializers(tagClass)), List(TypeTree(tp))), List(prefix))) + if (settings.XlogImplicits.value) println("materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) + success(materializer) } - def wrapResult(tree: Tree): SearchResult = - if (tree == EmptyTree) SearchFailure else new SearchResult(tree, EmptyTreeTypeSubstituter) - /** The manifest corresponding to type `pt`, provided `pt` is an instance of Manifest. */ - private def implicitManifestOrOfExpectedType(pt: Type): SearchResult = pt.dealias match { - case TypeRef(_, sym, args) if ManifestSymbols(sym) => - manifestOfType(args.head, sym == FullManifestClass) match { - case SearchFailure if sym == OptManifestClass => wrapResult(gen.mkAttributedRef(NoManifest)) - case result => result - } + private def implicitTagOrOfExpectedType(pt: Type): SearchResult = pt.dealias match { + case TypeRef(pre, sym, args) if TagSymbols(sym) => + tagOfType(pre, args.head, sym) case tp@TypeRef(_, sym, _) if sym.isAbstractType => - implicitManifestOrOfExpectedType(tp.bounds.lo) // #3977: use tp (==pt.dealias), not pt (if pt is a type alias, pt.bounds.lo == pt) + implicitTagOrOfExpectedType(tp.bounds.lo) // #3977: use tp (==pt.dealias), not pt (if pt is a type alias, pt.bounds.lo == pt) case _ => searchImplicit(implicitsOfExpectedType, false) // shouldn't we pass `pt` to `implicitsOfExpectedType`, or is the recursive case @@ -1199,7 +1180,9 @@ trait Implicits { /** The result of the implicit search: * First search implicits visible in current context. * If that fails, search implicits in expected type `pt`. - * If that fails, and `pt` is an instance of Manifest, try to construct a manifest. + * // [Eugene] the following two lines should be deleted after we migrate delegate manifest materialization to implicit macros + * If that fails, and `pt` is an instance of a ClassTag, try to construct a class tag. + * If that fails, and `pt` is an instance of a TypeTag, try to construct a type tag. * If all fails return SearchFailure */ def bestImplicit: SearchResult = { @@ -1219,7 +1202,7 @@ trait Implicits { val failstart = startTimer(oftypeFailNanos) val succstart = startTimer(oftypeSucceedNanos) - result = implicitManifestOrOfExpectedType(pt) + result = implicitTagOrOfExpectedType(pt) if (result == SearchFailure) { context.updateBuffer(previousErrs) @@ -1240,6 +1223,26 @@ trait Implicits { def search(iss: Infoss, isLocal: Boolean) = applicableInfos(iss, isLocal).values (search(context.implicitss, true) ++ search(implicitsOfExpectedType, false)).toList.filter(_.tree ne EmptyTree) } + + // find all implicits for some type that contains type variables + // collect the constraints that result from typing each implicit + def allImplicitsPoly(tvars: List[TypeVar]): List[(SearchResult, List[TypeConstraint])] = { + def resetTVars() = tvars foreach { _.constr = new TypeConstraint } + + def eligibleInfos(iss: Infoss, isLocal: Boolean) = new ImplicitComputation(iss, if (isLocal) util.HashSet[Name](512) else null).eligible + val allEligibleInfos = (eligibleInfos(context.implicitss, true) ++ eligibleInfos(implicitsOfExpectedType, false)).toList + + allEligibleInfos flatMap { ii => + // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) + // thus, start each type var off with a fresh for every typedImplicit + resetTVars() + // any previous errors should not affect us now + context.flushBuffer() + val res = typedImplicit(ii, false) + if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) + else Nil + } + } } object ImplicitNotFoundMsg { @@ -1248,7 +1251,7 @@ trait Implicits { // where `X` refers to a type parameter of `sym` def check(sym: Symbol): Option[String] = sym.getAnnotation(ImplicitNotFoundClass).flatMap(_.stringArg(0) match { - case Some(m) => new Message(sym, m) validate + case Some(m) => new Message(sym, m).validate case None => Some("Missing argument `msg` on implicitNotFound annotation.") }) @@ -1273,7 +1276,7 @@ trait Implicits { val decls = typeParamNames.toSet (refs &~ decls) match { - case s if s isEmpty => None + case s if s.isEmpty => None case unboundNames => val singular = unboundNames.size == 1 Some("The type parameter"+( if(singular) " " else "s " )+ unboundNames.mkString(", ") + diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index ebf8e3fc9a..fb0616c890 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -67,7 +67,7 @@ trait Infer { */ def freshVar(tparam: Symbol): TypeVar = TypeVar(tparam) - private class NoInstance(msg: String) extends Throwable(msg) with ControlThrowable { } + class NoInstance(msg: String) extends Throwable(msg) with ControlThrowable { } private class DeferredNoInstance(getmsg: () => String) extends NoInstance("") { override def getMessage(): String = getmsg() } @@ -83,7 +83,7 @@ trait Infer { def apply(tp: Type): Type = tp match { case WildcardType | BoundedWildcardType(_) | NoType => throw new NoInstance("undetermined type") - case tv @ TypeVar(origin, constr) => + case tv @ TypeVar(origin, constr) if !tv.untouchable => if (constr.inst == NoType) { throw new DeferredNoInstance(() => "no unique instantiation of type variable " + origin + " could be found") @@ -267,6 +267,16 @@ trait Infer { setError(tree) } else { + if (context.owner.isTermMacro && (sym1 hasFlag LOCKED)) { + // we must not let CyclicReference to be thrown from sym1.info + // because that would mark sym1 erroneous, which it is not + // but if it's a true CyclicReference then macro def will report it + // see comments to TypeSigError for an explanation of this special case + // [Eugene] is there a better way? + val dummy = new TypeCompleter { val tree = EmptyTree; override def complete(sym: Symbol) {} } + throw CyclicReference(sym1, dummy) + } + if (sym1.isTerm) sym1.cookJavaRawInfo() // xform java rawtypes into existentials @@ -310,6 +320,8 @@ trait Infer { /** Like weakly compatible but don't apply any implicit conversions yet. * Used when comparing the result type of a method with its prototype. + * [Martin] I think Infer is also created by Erasure, with the default + * implementation of isCoercible */ def isConservativelyCompatible(tp: Type, pt: Type): Boolean = context.withImplicitsDisabled(isWeaklyCompatible(tp, pt)) @@ -426,12 +438,15 @@ trait Infer { tvars map (tvar => WildcardType) } + /** [Martin] Can someone comment this please? I have no idea what it's for + * and the code is not exactly readable. + */ object AdjustedTypeArgs { val Result = collection.mutable.LinkedHashMap type Result = collection.mutable.LinkedHashMap[Symbol, Option[Type]] def unapply(m: Result): Some[(List[Symbol], List[Type])] = Some(toLists( - m collect {case (p, Some(a)) => (p, a)} unzip )) + (m collect {case (p, Some(a)) => (p, a)}).unzip )) object Undets { def unapply(m: Result): Some[(List[Symbol], List[Type], List[Symbol])] = Some(toLists{ @@ -751,7 +766,7 @@ trait Infer { isAsSpecific(res, ftpe2) case mt: MethodType if mt.isImplicit => isAsSpecific(ftpe1.resultType, ftpe2) - case MethodType(params, _) if params nonEmpty => + case MethodType(params, _) if params.nonEmpty => var argtpes = params map (_.tpe) if (isVarArgsList(params) && isVarArgsList(ftpe2.params)) argtpes = argtpes map (argtpe => @@ -761,7 +776,7 @@ trait Infer { isAsSpecific(PolyType(tparams, res), ftpe2) case PolyType(tparams, mt: MethodType) if mt.isImplicit => isAsSpecific(PolyType(tparams, mt.resultType), ftpe2) - case PolyType(_, MethodType(params, _)) if params nonEmpty => + case PolyType(_, MethodType(params, _)) if params.nonEmpty => isApplicable(List(), ftpe2, params map (_.tpe), WildcardType) // case NullaryMethodType(res) => // isAsSpecific(res, ftpe2) @@ -992,6 +1007,7 @@ trait Infer { PolymorphicExpressionInstantiationError(tree, undetparams, pt) } else { new TreeTypeSubstituter(undetparams, targs).traverse(tree) + notifyUndetparamsInferred(undetparams, targs) } } @@ -1028,6 +1044,7 @@ trait Infer { if (checkBounds(fn, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")) { val treeSubst = new TreeTypeSubstituter(okparams, okargs) treeSubst traverseTrees fn :: args + notifyUndetparamsInferred(okparams, okargs) leftUndet match { case Nil => Nil @@ -1116,6 +1133,7 @@ trait Infer { (inferFor(pt) orElse inferForApproxPt) map { targs => new TreeTypeSubstituter(undetparams, targs).traverse(tree) + notifyUndetparamsInferred(undetparams, targs) } 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) @@ -1171,6 +1189,50 @@ trait Infer { } } + /** Does `tp` contain any types that cannot be checked at run-time (i.e., after erasure, will isInstanceOf[erased(tp)] imply conceptualIsInstanceOf[tp]?) + * we should find a way to ask erasure: hey, is `tp` going to make it through you with all of its isInstanceOf resolving powers intact? + * TODO: at the very least, reduce duplication wrt checkCheckable + */ + def containsUnchecked(tp: Type): Boolean = { + def check(tp: Type, bound: List[Symbol]): Boolean = { + def isSurroundingTypeParam(sym: Symbol) = { + val e = context.scope.lookupEntry(sym.name) + ( (e ne null) + && (e.sym == sym ) + && !e.sym.isTypeParameterOrSkolem + && (e.owner == context.scope) + ) + } + def isLocalBinding(sym: Symbol) = ( + sym.isAbstractType && ( + (bound contains sym) + || (sym.name == tpnme.WILDCARD) + || isSurroundingTypeParam(sym) + ) + ) + tp.normalize match { + case SingleType(pre, _) => + check(pre, bound) + case TypeRef(_, ArrayClass, arg :: _) => + check(arg, bound) + case tp @ TypeRef(pre, sym, args) => + ( (sym.isAbstractType && !isLocalBinding(sym)) + || (args exists (x => !isLocalBinding(x.typeSymbol))) + || check(pre, bound) + ) + // case RefinedType(_, decls) if decls.nonEmpty => + // patternWarning(tp, "refinement ") + case RefinedType(parents, _) => + parents exists (p => check(p, bound)) + case ExistentialType(quantified, tp1) => + check(tp1, bound ::: quantified) + case _ => + false + } + } + check(tp, Nil) + } + def checkCheckable(tree: Tree, tp: Type, kind: String) { def patternWarning(tp0: Type, prefix: String) = { context.unit.uncheckedWarning(tree.pos, prefix+tp0+" in type "+kind+tp+" is unchecked since it is eliminated by erasure") @@ -1568,9 +1630,9 @@ trait Infer { else infer } - /** Assign <code>tree</code> the type of unique polymorphic alternative + /** Assign <code>tree</code> the type of all polymorphic alternatives * with <code>nparams</code> as the number of type parameters, if it exists. - * If several or none such polymorphic alternatives exist, error. + * If no such polymorphic alternative exist, error. * * @param tree ... * @param nparams ... diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index e43b1fab0b..b7e0eaef2b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -3,135 +3,696 @@ package typechecker import symtab.Flags._ import scala.tools.nsc.util._ +import scala.tools.nsc.util.ClassPath._ import scala.reflect.ReflectionUtils +import scala.collection.mutable.ListBuffer +import scala.compat.Platform.EOL +import scala.reflect.makro.runtime.{Context => MacroContext} +import scala.reflect.runtime.Mirror +import util.Statistics._ +/** + * Code to deal with macros, namely with: + * * Compilation of macro definitions + * * Expansion of macro applications + * + * Say we have in a class C: + * + * def foo[T](xs: List[T]): T = macro fooBar + * + * Then fooBar needs to point to a static method of the following form: + * + * def fooBar[T: c.TypeTag] + * (c: scala.reflect.makro.Context) + * (xs: c.Expr[List[T]]) + * : c.mirror.Tree = { + * ... + * } + * + * Then, if foo is called in qual.foo[Int](elems), where qual: D, + * the macro application is expanded to a reflective invocation of fooBar with parameters + * + * (simpleMacroContext{ type PrefixType = D; val prefix = qual }) + * (Expr(elems)) + * (TypeTag(Int)) + */ trait Macros { self: Analyzer => import global._ import definitions._ - def macroMeth(mac: Symbol): Symbol = { - var owner = mac.owner - if (!owner.isModuleClass) owner = owner.companionModule.moduleClass - owner.info.decl(nme.macroMethodName(mac.name)) - } + val macroDebug = settings.Ymacrodebug.value + val macroCopypaste = settings.Ymacrocopypaste.value + val macroTrace = scala.tools.nsc.util.trace when macroDebug - def macroArgs(tree: Tree): (List[List[Tree]]) = tree match { - case Apply(fn, args) => - macroArgs(fn) :+ args - case TypeApply(fn, args) => - macroArgs(fn) :+ args - case Select(qual, name) => - List(List(qual)) - case _ => - List(List()) - } + val globalMacroCache = collection.mutable.Map[Any, Any]() + val perRunMacroCache = perRunCaches.newMap[Symbol, collection.mutable.Map[Any, Any]] - /** - * The definition of the method implementing a macro. Example: - * Say we have in a class C + /** A list of compatible macro implementation signatures. * - * def macro foo[T](xs: List[T]): T = expr + * In the example above: + * (c: scala.reflect.makro.Context)(xs: c.Expr[List[T]]): c.Expr[T] * - * Then the following macro method is generated for `foo`: - * - * 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 - * } + * @param macroDef The macro definition symbol + * @param tparams The type parameters of the macro definition + * @param vparamss The value parameters of the macro definition + * @param retTpe The return type of the macro definition + */ + private def macroImplSigs(macroDef: Symbol, tparams: List[TypeDef], vparamss: List[List[ValDef]], retTpe: Type): (List[List[List[Symbol]]], Type) = { + // had to move method's body to an object because of the recursive dependencies between sigma and param + object SigGenerator { + val hasThis = macroDef.owner.isClass + val ownerTpe = macroDef.owner match { + case owner if owner.isModuleClass => new UniqueThisType(macroDef.owner) + case owner if owner.isClass => macroDef.owner.tpe + case _ => NoType + } + val hasTparams = !tparams.isEmpty + + def sigma(tpe: Type): Type = { + class SigmaTypeMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = pre match { + case ThisType(sym) if sym == macroDef.owner => + SingleType(SingleType(SingleType(NoPrefix, paramsCtx(0)), MacroContextPrefix), ExprValue) + case SingleType(NoPrefix, sym) => + vparamss.flatten.find(_.symbol == sym) match { + case Some(macroDefParam) => + SingleType(SingleType(NoPrefix, param(macroDefParam)), ExprValue) + case _ => + pre + } + case _ => + pre + } + val args1 = args map mapOver + TypeRef(pre1, sym, args1) + case _ => + mapOver(tp) + } + } + + new SigmaTypeMap() apply tpe + } + + def makeParam(name: Name, pos: Position, tpe: Type, flags: Long = 0L) = + macroDef.newValueParameter(name, pos, flags) setInfo tpe + val ctxParam = makeParam(nme.macroContext, macroDef.pos, MacroContextClass.tpe, SYNTHETIC) + def implType(isType: Boolean, origTpe: Type): Type = + if (isRepeatedParamType(origTpe)) + appliedType( + RepeatedParamClass.typeConstructor, + List(implType(isType, sigma(origTpe.typeArgs.head)))) + else { + val tsym = getMember(MacroContextClass, if (isType) tpnme.TypeTag else tpnme.Expr) + typeRef(singleType(NoPrefix, ctxParam), tsym, List(sigma(origTpe))) + } + val paramCache = collection.mutable.Map[Symbol, Symbol]() + def param(tree: Tree): Symbol = + paramCache.getOrElseUpdate(tree.symbol, { + // [Eugene] deskolemization became necessary once I implemented inference of macro def return type + // please, verify this solution, but for now I'll leave it here - cargo cult for the win + val sym = tree.symbol.deSkolemize + val sigParam = makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe)) + if (sym.isSynthetic) sigParam.flags |= SYNTHETIC + sigParam + }) + + val paramsCtx = List(ctxParam) + val paramsThis = List(makeParam(nme.macroThis, macroDef.pos, implType(false, ownerTpe), SYNTHETIC)) + val paramsTparams = tparams map param + val paramssParams = vparamss map (_ map param) + + var paramsss = List[List[List[Symbol]]]() + // tparams are no longer part of a signature, they get into macro implementations via context bounds +// if (hasTparams && hasThis) paramsss :+= paramsCtx :: paramsThis :: paramsTparams :: paramssParams +// if (hasTparams) paramsss :+= paramsCtx :: paramsTparams :: paramssParams + // _this params are no longer part of a signature, its gets into macro implementations via Context.prefix +// if (hasThis) paramsss :+= paramsCtx :: paramsThis :: paramssParams + paramsss :+= paramsCtx :: paramssParams + + val tsym = getMember(MacroContextClass, tpnme.Expr) + val implRetTpe = typeRef(singleType(NoPrefix, ctxParam), tsym, List(sigma(retTpe))) + } + + import SigGenerator._ + macroTrace("generating macroImplSigs for: ")(macroDef) + macroTrace("tparams are: ")(tparams) + macroTrace("vparamss are: ")(vparamss) + macroTrace("retTpe is: ")(retTpe) + macroTrace("macroImplSigs are: ")(paramsss, implRetTpe) + } + + private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Option[Symbol]): List[List[Symbol]] = { + if (paramss.length == 0) + return paramss + + val wannabe = if (paramss.head.length == 1) paramss.head.head else NoSymbol + val contextParam = if (wannabe != NoSymbol && wannabe.tpe <:< definitions.MacroContextClass.tpe) wannabe else NoSymbol + + val lastParamList0 = paramss.lastOption getOrElse Nil + val lastParamList = lastParamList0 flatMap (param => param.tpe match { + case TypeRef(SingleType(NoPrefix, contextParam), sym, List(tparam)) => + var wannabe = sym + while (wannabe.isAliasType) wannabe = wannabe.info.typeSymbol + if (wannabe != definitions.TypeTagClass) + List(param) + else + transform(param, tparam.typeSymbol) map (_ :: Nil) getOrElse Nil + case _ => + List(param) + }) + + var result = paramss.dropRight(1) :+ lastParamList + if (lastParamList0.isEmpty ^ lastParamList.isEmpty) { + result = result dropRight 1 + } + + result + } + + /** As specified above, body of a macro definition must reference its implementation. + * This function verifies that the body indeed refers to a method, and that + * the referenced macro implementation is compatible with the given macro definition. * - * If macro has no type arguments, the third parameter list is omitted (it's not empty, but omitted altogether). + * This means that macro implementation (fooBar in example above) must: + * 1) Refer to a statically accessible, non-overloaded method. + * 2) Have the right parameter lists as outlined in the SIP / in the doc comment of this class. * - * To find out the desugared representation of your particular macro, compile it with -Ymacro-debug. + * @return typechecked rhs of the given macro definition */ - 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.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.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)) - if (mdef.tparams.nonEmpty) formals = (mdef.tparams map tparamInMacro) :: formals - - atPos(mdef.pos) { - new DefDef( // can't call DefDef here; need to find out why - mods = mdef.mods &~ MACRO &~ OVERRIDE, - name = nme.macroMethodName(mdef.name), - tparams = List(), - vparamss = globParamSec :: thisParamSec :: formals, - tpt = globTree, - wrapImplicit(mdef.rhs)) + def typedMacroBody(typer: Typer, ddef: DefDef): Tree = { + import typer.context + if (macroDebug) println("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) + + typer.checkFeature(ddef.pos, MacrosFeature) + + // [Eugene to Martin] todo. copy/pasted this from checkFeature, because don't know a better way + // this is necessary to prevent macros from typechecking/expanding when they are not enabled + // `checkFeature` call alone is not enough, because it merely posts validation callback to unit.toCheck + def hasImport = inferImplicit(EmptyTree: Tree, MacrosFeature.tpe, true, false, typer.context) != SearchFailure + val nestedOwners = MacrosFeature.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse + val featureName = (nestedOwners map (_.name + ".")).mkString + MacrosFeature.name + def hasOption = settings.language.value contains featureName + if (!hasImport && !hasOption) { + ddef.symbol setFlag IS_ERROR + return EmptyTree + } + + implicit class AugmentedString(s: String) { + def abbreviateCoreAliases: String = { // hack! + var result = s + result = result.replace("c.mirror.TypeTag", "c.TypeTag") + result = result.replace("c.mirror.Expr", "c.Expr") + result + } + } + + var hasErrors = false + def reportError(pos: Position, msg: String) = { + hasErrors = true + context.error(pos, msg) + } + + val macroDef = ddef.symbol + val defpos = macroDef.pos + val implpos = ddef.rhs.pos + assert(macroDef.isTermMacro, ddef) + + def invalidBodyError() = + reportError(defpos, + "macro body has wrong shape:" + + "\n required: macro <reference to implementation object>.<implementation method name>" + + "\n or : macro <implementation method name>") + def validatePreTyper(rhs: Tree): Unit = rhs match { + // we do allow macro invocations inside macro bodies + // personally I don't mind if pre-typer tree is a macro invocation + // that later resolves to a valid reference to a macro implementation + // however, I don't think that invalidBodyError() should hint at that + // let this be an Easter Egg :) + case Apply(_, _) => ; + case TypeApply(_, _) => ; + case Super(_, _) => ; + case This(_) => ; + case Ident(_) => ; + case Select(_, _) => ; + case _ => invalidBodyError() + } + def validatePostTyper(rhs1: Tree): Unit = { + def loop(tree: Tree): Unit = { + def errorNotStatic() = + reportError(implpos, "macro implementation must be in statically accessible object") + + def ensureRoot(sym: Symbol) = + if (!sym.isModule && !sym.isModuleClass) errorNotStatic() + + def ensureModule(sym: Symbol) = + if (!sym.isModule) errorNotStatic() + + tree match { + case TypeApply(fun, _) => + loop(fun) + case Super(qual, _) => + ensureRoot(macroDef.owner) + loop(qual) + case This(_) => + ensureRoot(tree.symbol) + case Select(qual, name) if name.isTypeName => + loop(qual) + case Select(qual, name) if name.isTermName => + if (tree.symbol != rhs1.symbol) ensureModule(tree.symbol) + loop(qual) + case Ident(name) if name.isTypeName => + ; + case Ident(name) if name.isTermName => + if (tree.symbol != rhs1.symbol) ensureModule(tree.symbol) + case _ => + invalidBodyError() + } + } + + loop(rhs1) + } + + val rhs = ddef.rhs + validatePreTyper(rhs) + if (hasErrors) macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) + + // we use typed1 instead of typed, because otherwise adapt is going to mess us up + // if adapt sees <qualifier>.<method>, it will want to perform eta-expansion and will fail + // unfortunately, this means that we have to manually trigger macro expansion + // because it's adapt which is responsible for automatic expansion during typechecking + def typecheckRhs(rhs: Tree): Tree = { + try { + val prevNumErrors = reporter.ERROR.count // [Eugene] funnily enough, the isErroneous check is not enough + var rhs1 = if (hasErrors) EmptyTree else typer.typed1(rhs, EXPRmode, WildcardType) + def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors + def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous + while (!typecheckedWithErrors && rhsNeedsMacroExpansion) { + rhs1 = macroExpand1(typer, rhs1) match { + case Success(expanded) => + try { + val typechecked = typer.typed1(expanded, EXPRmode, WildcardType) + if (macroDebug) { + println("typechecked1:") + println(typechecked) + println(showRaw(typechecked)) + } + + typechecked + } finally { + openMacros = openMacros.tail + } + case Fallback(fallback) => + typer.typed1(fallback, EXPRmode, WildcardType) + case Other(result) => + result + } + } + rhs1 + } catch { + case ex: TypeError => + typer.reportTypeError(context, rhs.pos, ex) + typer.infer.setError(rhs) + } + } + + val prevNumErrors = reporter.ERROR.count // funnily enough, the isErroneous check is not enough + var rhs1 = typecheckRhs(rhs) + def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors + hasErrors = hasErrors || typecheckedWithErrors + if (typecheckedWithErrors) macroTrace("body of a macro def failed to typecheck: ")(ddef) + + val macroImpl = rhs1.symbol + macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(rhs1), Nil) + if (!hasErrors) { + if (macroImpl == null) { + invalidBodyError() + } else { + if (!macroImpl.isMethod) + invalidBodyError() + if (macroImpl.isOverloaded) + reportError(implpos, "macro implementation cannot be overloaded") + if (!macroImpl.typeParams.isEmpty && (!rhs1.isInstanceOf[TypeApply])) + reportError(implpos, "macro implementation reference needs type arguments") + if (!hasErrors) + validatePostTyper(rhs1) + } + if (hasErrors) + macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) } + + if (!hasErrors) { + def checkCompatibility(reqparamss: List[List[Symbol]], actparamss: List[List[Symbol]], reqres: Type, actres: Type): List[String] = { + var hasErrors = false + var errors = List[String]() + def compatibilityError(msg: String) { + hasErrors = true + errors :+= msg + } + + val flatreqparams = reqparamss.flatten + val flatactparams = actparamss.flatten + val tparams = macroImpl.typeParams + val tvars = tparams map freshVar + def lengthMsg(which: String, extra: Symbol) = + "parameter lists have different length, "+which+" extra parameter "+extra.defString + if (actparamss.length != reqparamss.length) + compatibilityError("number of parameter sections differ") + + if (!hasErrors) { + try { + for ((rparams, aparams) <- reqparamss zip actparamss) { + if (rparams.length < aparams.length) + compatibilityError(lengthMsg("found", aparams(rparams.length))) + if (aparams.length < rparams.length) + compatibilityError(lengthMsg("required", rparams(aparams.length)).abbreviateCoreAliases) + } + // if the implementation signature is already deemed to be incompatible, we bail out + // otherwise, high-order type magic employed below might crash in weird ways + if (!hasErrors) { + for ((rparams, aparams) <- reqparamss zip actparamss) { + for ((rparam, aparam) <- rparams zip aparams) { + def isRepeated(param: Symbol) = param.tpe.typeSymbol == RepeatedParamClass + if (rparam.name != aparam.name && !rparam.isSynthetic) { + val rparam1 = rparam + val aparam1 = aparam + compatibilityError("parameter names differ: "+rparam.name+" != "+aparam.name) + } + if (isRepeated(rparam) && !isRepeated(aparam)) + compatibilityError("types incompatible for parameter "+rparam.name+": corresponding is not a vararg parameter") + if (!isRepeated(rparam) && isRepeated(aparam)) + compatibilityError("types incompatible for parameter "+aparam.name+": corresponding is not a vararg parameter") + if (!hasErrors) { + var atpe = aparam.tpe.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars) + + // strip the { type PrefixType = ... } refinement off the Context or otherwise we get compatibility errors + atpe = atpe match { + case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe + case _ => atpe + } + + val ok = if (macroDebug) withTypesExplained(rparam.tpe <:< atpe) else rparam.tpe <:< atpe + if (!ok) { + compatibilityError("type mismatch for parameter "+rparam.name+": "+rparam.tpe.toString.abbreviateCoreAliases+" does not conform to "+atpe) + } + } + } + } + } + if (!hasErrors) { + val atpe = actres.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars) + val ok = if (macroDebug) withTypesExplained(atpe <:< reqres) else atpe <:< reqres + if (!ok) { + compatibilityError("type mismatch for return type : "+reqres.toString.abbreviateCoreAliases+" does not conform to "+(if (ddef.tpt.tpe != null) atpe.toString else atpe.toString.abbreviateCoreAliases)) + } + } + if (!hasErrors) { + val targs = solvedTypes(tvars, tparams, tparams map varianceInType(actres), false, + lubDepth(flatactparams map (_.tpe)) max lubDepth(flatreqparams map (_.tpe))) + val boundsOk = typer.silent(_.infer.checkBounds(ddef, NoPrefix, NoSymbol, tparams, targs, "")) + boundsOk match { + case SilentResultValue(true) => ; + case SilentResultValue(false) | SilentTypeError(_) => + val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds) + compatibilityError("type arguments " + targs.mkString("[", ",", "]") + + " do not conform to " + tparams.head.owner + "'s type parameter bounds " + + (tparams map (_.defString)).mkString("[", ",", "]")) + } + } + } catch { + case ex: NoInstance => + compatibilityError( + "type parameters "+(tparams map (_.defString) mkString ", ")+" cannot be instantiated\n"+ + ex.getMessage) + } + } + + errors.toList + } + + var actparamss = macroImpl.paramss + actparamss = transformTypeTagEvidenceParams(actparamss, (param, tparam) => None) + + val rettpe = if (ddef.tpt.tpe != null) ddef.tpt.tpe else computeMacroDefTypeFromMacroImpl(ddef, macroDef, macroImpl) + val (reqparamsss0, reqres0) = macroImplSigs(macroDef, ddef.tparams, ddef.vparamss, rettpe) + var reqparamsss = reqparamsss0 + + // prohibit implicit params on macro implementations + // we don't have to do this, but it appears to be more clear than allowing them + val implicitParams = actparamss.flatten filter (_.isImplicit) + if (implicitParams.length > 0) { + reportError(implicitParams.head.pos, "macro implementations cannot have implicit parameters other than TypeTag evidences") + macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) + } + + if (!hasErrors) { + val reqres = reqres0 + val actres = macroImpl.tpe.finalResultType + def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean) = { + var argsPart = (pss map (ps => ps map (_.defString) mkString ("(", ", ", ")"))).mkString + if (abbreviate) argsPart = argsPart.abbreviateCoreAliases + var retPart = restpe.toString + if (abbreviate || ddef.tpt.tpe == null) retPart = retPart.abbreviateCoreAliases + argsPart + ": " + retPart + } + def compatibilityError(addendum: String) = + reportError(implpos, + "macro implementation has wrong shape:"+ + "\n required: "+showMeth(reqparamsss.head, reqres, true) + + (reqparamsss.tail map (paramss => "\n or : "+showMeth(paramss, reqres, true)) mkString "")+ + "\n found : "+showMeth(actparamss, actres, false)+ + "\n"+addendum) + + macroTrace("considering " + reqparamsss.length + " possibilities of compatible macro impl signatures for macro def: ")(ddef.name) + val results = reqparamsss map (checkCompatibility(_, actparamss, reqres, actres)) + if (macroDebug) (reqparamsss zip results) foreach { case (reqparamss, result) => + println("%s %s".format(if (result.isEmpty) "[ OK ]" else "[FAILED]", reqparamss)) + result foreach (errorMsg => println(" " + errorMsg)) + } + + if (results forall (!_.isEmpty)) { + var index = reqparamsss indexWhere (_.length == actparamss.length) + if (index == -1) index = 0 + val mostRelevantMessage = results(index).head + compatibilityError(mostRelevantMessage) + } else { + assert((results filter (_.isEmpty)).length == 1, results) + if (macroDebug) (reqparamsss zip results) filter (_._2.isEmpty) foreach { case (reqparamss, result) => + println("typechecked macro impl as: " + reqparamss) + } + } + } + } + + // if this macro definition is erroneous, then there's no sense in expanding its usages + // in the previous prototype macro implementations were magically generated from macro definitions + // so macro definitions and its usages couldn't be compiled in the same compilation run + // however, now definitions and implementations are decoupled, so it's everything is possible + // hence, we now use IS_ERROR flag to serve as an indicator that given macro definition is broken + if (hasErrors) { + macroDef setFlag IS_ERROR + } + + rhs1 } - 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.Ymacrodebug.value - val sym = namer.enterSyntheticSym(trace("macro def: ")(macroMethDef(ddef))) - trace("added to "+namer.context.owner.enclClass+": ")(sym) + def computeMacroDefTypeFromMacroImpl(macroDdef: DefDef, macroDef: Symbol, macroImpl: Symbol): Type = { + // get return type from method type + def unwrapRet(tpe: Type): Type = { + def loop(tpe: Type) = tpe match { + case NullaryMethodType(ret) => ret + case mtpe @ MethodType(_, ret) => unwrapRet(ret) + case _ => tpe + } + + tpe match { + case PolyType(_, tpe) => loop(tpe) + case _ => loop(tpe) + } } + var metaType = unwrapRet(macroImpl.tpe) + + // downgrade from metalevel-0 to metalevel-1 + def inferRuntimeType(metaType: Type): Type = metaType match { + case TypeRef(pre, sym, args) if sym.name == tpnme.Expr && args.length == 1 => + args.head + case _ => + AnyClass.tpe + } + var runtimeType = inferRuntimeType(metaType) + + // transform type parameters of a macro implementation into type parameters of a macro definition + runtimeType = runtimeType map { + case TypeRef(pre, sym, args) => + // [Eugene] not sure which of these deSkolemizes are necessary + // sym.paramPos is unreliable (see another case below) + val tparams = macroImpl.typeParams map (_.deSkolemize) + val paramPos = tparams indexOf sym.deSkolemize + val sym1 = if (paramPos == -1) sym else { + val ann = macroDef.getAnnotation(MacroImplAnnotation) + ann match { + case Some(ann) => + val TypeApply(_, implRefTargs) = ann.args(0) + val implRefTarg = implRefTargs(paramPos).tpe.typeSymbol + implRefTarg + case None => + sym + } + } + TypeRef(pre, sym1, args) + case tpe => + tpe + } + + // as stated in the spec, before being matched to macroimpl, type and value parameters of macrodef + // undergo a special transformation, sigma, that adapts them to the different metalevel macroimpl lives in + // as a result, we need to reverse this transformation when inferring macrodef ret from macroimpl ret + def unsigma(tpe: Type): Type = { + // unfortunately, we cannot dereference ``paramss'', because we're in the middle of inferring a type for ``macroDef'' +// val defParamss = macroDef.paramss + val defParamss = macroDdef.vparamss map (_ map (_.symbol)) + var implParamss = macroImpl.paramss + implParamss = transformTypeTagEvidenceParams(implParamss, (param, tparam) => None) + + val implCtxParam = if (implParamss.length > 0 && implParamss(0).length > 0) implParamss(0)(0) else null + def implParamToDefParam(implParam: Symbol): Symbol = { + val indices = (((implParamss drop 1).zipWithIndex) map { case (implParams, index) => (index, implParams indexOf implParam) } filter (_._2 != -1)).headOption + val defParam = indices flatMap { + case (plistIndex, pIndex) => + if (defParamss.length <= plistIndex) None + else if (defParamss(plistIndex).length <= pIndex) None + else Some(defParamss(plistIndex)(pIndex)) + } + defParam.orNull + } + + class UnsigmaTypeMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = pre match { + case SingleType(SingleType(SingleType(NoPrefix, param), prefix), value) if param == implCtxParam && prefix == MacroContextPrefix && value == ExprValue => + ThisType(macroDef.owner) + case SingleType(SingleType(NoPrefix, param), value) if implParamToDefParam(param) != null && value == ExprValue => + val macroDefParam = implParamToDefParam(param) + SingleType(NoPrefix, macroDefParam) + case _ => + pre + } + val args1 = args map mapOver + TypeRef(pre1, sym, args1) + case _ => + mapOver(tp) + } + } + + new UnsigmaTypeMap() apply tpe + } + runtimeType = unsigma(runtimeType) + + runtimeType } - 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 - 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) {} + /** Primary mirror that is used to resolve and run macro implementations. + * Loads classes from -Xmacro-primary-classpath, or from -cp if the option is not specified. + */ + private lazy val primaryMirror: Mirror = { + if (global.forMSIL) + throw new UnsupportedOperationException("Scala reflection not available on this platform") + + val libraryClassLoader = { + if (settings.XmacroPrimaryClasspath.value != "") { + if (macroDebug) println("primary macro mirror: initializing from -Xmacro-primary-classpath: %s".format(settings.XmacroPrimaryClasspath.value)) + val classpath = toURLs(settings.XmacroFallbackClasspath.value) + ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + } else { + if (macroDebug) println("primary macro mirror: initializing from -cp: %s".format(global.classPath.asURLs)) + val classpath = global.classPath.asURLs + var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + + // [Eugene] a 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 } + } - loader + new Mirror(libraryClassLoader) { override def toString = "<primary macro mirror>" } + } + + /** Fallback mirror that is used to resolve and run macro implementations. + * Loads classes from -Xmacro-fallback-classpath aka "macro fallback classpath". + */ + private lazy val fallbackMirror: Mirror = { + if (global.forMSIL) + throw new UnsupportedOperationException("Scala reflection not available on this platform") + + val fallbackClassLoader = { + if (macroDebug) println("fallback macro mirror: initializing from -Xmacro-fallback-classpath: %s".format(settings.XmacroFallbackClasspath.value)) + val classpath = toURLs(settings.XmacroFallbackClasspath.value) + ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) } - override def defaultReflectiveClassLoader() = libraryClassLoader + new Mirror(fallbackClassLoader) { override def toString = "<fallback macro mirror>" } } - /** Return optionally address of companion object and implementation method symbol - * of given macro; or None if implementation classfile cannot be loaded or does - * not contain the macro implementation. + /** Produces a function that can be used to invoke macro implementation for a given macro definition: + * 1) Looks up macro implementation symbol in this universe. + * 2) Loads its enclosing class from the primary mirror. + * 3) Loads the companion of that enclosing class from the primary mirror. + * 4) Resolves macro implementation within the loaded companion. + * 5) If 2-4 fails, repeats them for the fallback mirror. + * + * @return Some(runtime) if macro implementation can be loaded successfully from either of the mirrors, + * None otherwise. */ - 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 { - trace("loading implementation class: ")(mmeth.owner.fullName) - trace("classloader is: ")("%s of type %s".format(mirror.libraryClassLoader, mirror.libraryClassLoader.getClass)) + private def macroRuntime(macroDef: Symbol): Option[List[Any] => Any] = { + macroTrace("looking for macro implementation: ")(macroDef) + macroTrace("macroDef is annotated with: ")(macroDef.annotations) + + val ann = macroDef.getAnnotation(MacroImplAnnotation) + if (ann == None) { + macroTrace("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef) + return None + } + + val macroImpl = ann.get.args(0).symbol + if (macroImpl == NoSymbol) { + macroTrace("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef) + return None + } + + if (macroDebug) println("resolved implementation %s at %s".format(macroImpl, macroImpl.pos)) + if (macroImpl.isErroneous) { + macroTrace("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef) + return None + } + + def loadMacroImpl(macroMirror: Mirror): Option[(Object, macroMirror.Symbol)] = { + try { + // this logic relies on the assumptions that were valid for the old macro prototype + // namely that macro implementations can only be defined in top-level classes and modules + // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different + // for example, a macro def could be defined in a trait that is implemented by an object + // there are some more clever cases when seemingly non-static method ends up being statically accessible + // however, the code below doesn't account for these guys, because it'd take a look of time to get it right + // for now I leave it as a todo and move along to more the important stuff + + macroTrace("loading implementation class from %s: ".format(macroMirror))(macroImpl.owner.fullName) + macroTrace("classloader is: ")("%s of type %s".format(macroMirror.classLoader, if (macroMirror.classLoader != null) macroMirror.classLoader.getClass.toString else "primordial classloader")) def inferClasspath(cl: ClassLoader) = cl match { case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" + case null => "[" + scala.tools.util.PathResolver.Environment.javaBootClassPath + "]" case _ => "<unknown>" } - trace("classpath is: ")(inferClasspath(mirror.libraryClassLoader)) + macroTrace("classpath is: ")(inferClasspath(macroMirror.classLoader)) - // @xeno.by: relies on the fact that macros can only be defined in static classes + // [Eugene] relies on the fact that macro implementations can only be defined in static classes + // [Martin to Eugene] There's similar logic buried in Symbol#flatname. Maybe we can refactor? def classfile(sym: Symbol): String = { def recur(sym: Symbol): String = sym match { case sym if sym.owner.isPackageClass => @@ -146,145 +707,541 @@ trait Macros { self: Analyzer => 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) + // [Eugene] this doesn't work for inner classes + // neither does macroImpl.owner.javaClassName, so I had to roll my own implementation + //val receiverName = macroImpl.owner.fullName + val implClassName = classfile(macroImpl.owner) + val implClassSymbol: macroMirror.Symbol = macroMirror.symbolForName(implClassName) - if (debug) { - println("receiverClass is: " + receiverClass.fullNameString) + if (macroDebug) { + println("implClassSymbol is: " + implClassSymbol.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))) + if (implClassSymbol != macroMirror.NoSymbol) { + val implClass = macroMirror.classToJava(implClassSymbol) + val implSource = implClass.getProtectionDomain.getCodeSource + println("implClass is %s from %s".format(implClass, implSource)) + println("implClassLoader is %s with classpath %s".format(implClass.getClassLoader, inferClasspath(implClass.getClassLoader))) + } } - val receiverObj = receiverClass.companionModule - trace("receiverObj is: ")(receiverObj.fullNameString) + val implObjSymbol = implClassSymbol.companionModule + macroTrace("implObjSymbol is: ")(implObjSymbol.fullNameString) - if (receiverObj == mirror.NoSymbol) None + if (implObjSymbol == macroMirror.NoSymbol) None else { - // @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)) - if (debug) { - println("rmeth is: " + rmeth.fullNameString) - println("jrmeth is: " + mirror.methodToJava(rmeth)) + // yet another reflection method that doesn't work for inner classes + //val receiver = macroMirror.companionInstance(receiverClass) + val implObj = try { + val implObjClass = java.lang.Class.forName(implClassName, true, macroMirror.classLoader) + implObjClass getField "MODULE$" get null + } catch { + case ex: NoSuchFieldException => macroTrace("exception when loading implObj: ")(ex); null + case ex: NoClassDefFoundError => macroTrace("exception when loading implObj: ")(ex); null + case ex: ClassNotFoundException => macroTrace("exception when loading implObj: ")(ex); null } - if (rmeth == mirror.NoSymbol) None + if (implObj == null) None else { - Some((receiver, rmeth)) + val implMethSymbol = implObjSymbol.info.member(macroMirror.newTermName(macroImpl.name.toString)) + if (macroDebug) { + println("implMethSymbol is: " + implMethSymbol.fullNameString) + println("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol)) + } + + if (implMethSymbol == macroMirror.NoSymbol) None + else { + if (macroDebug) println("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol)) + Some((implObj, implMethSymbol)) + } } } + } catch { + case ex: ClassNotFoundException => + macroTrace("implementation class failed to load: ")(ex.toString) + None } - } catch { - case ex: ClassNotFoundException => - trace("implementation class failed to load: ")(ex.toString) - None + } + + val primary = loadMacroImpl(primaryMirror) + primary match { + case Some((implObj, implMethSymbol)) => + def runtime(args: List[Any]) = primaryMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any] + Some(runtime) + case None => + if (settings.XmacroFallbackClasspath.value != "") { + if (macroDebug) println("trying to load macro implementation from the fallback mirror: %s".format(settings.XmacroFallbackClasspath.value)) + val fallback = loadMacroImpl(fallbackMirror) + fallback match { + case Some((implObj, implMethSymbol)) => + def runtime(args: List[Any]) = fallbackMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any] + Some(runtime) + case None => + None + } + } else { + None + } } } - /** Return result of macro expansion. - * Or, if that fails, and the macro overrides a method return - * tree that calls this method instead of the macro. + /** Should become private again once we're done with migrating typetag generation from implicits */ + def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext { val mirror: global.type } = + new { + val mirror: global.type = global + val callsiteTyper: mirror.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] + // todo. infer precise typetag for this Expr, namely the PrefixType member of the Context refinement + val prefix = Expr(prefixTree)(TypeTag.Nothing) + val expandee = expandeeTree + } with MacroContext { + override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, openMacros.length - 1 /* exclude myself */) + } + + /** Calculate the arguments to pass to a macro implementation when expanding the provided tree. + * + * This includes inferring the exact type and instance of the macro context to pass, and also + * allowing for missing parameter sections in macro implementation (see ``macroImplParamsss'' for more info). + * + * @return list of runtime objects to pass to the implementation obtained by ``macroRuntime'' */ - 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 + private def macroArgs(typer: Typer, expandee: Tree): Option[List[Any]] = { + var prefixTree: Tree = EmptyTree + var typeArgs = List[Tree]() + val exprArgs = new ListBuffer[List[Expr[_]]] + def collectMacroArgs(tree: Tree): Unit = tree match { + case Apply(fn, args) => + // todo. infer precise typetag for this Expr, namely the declared type of the corresponding macro impl argument + exprArgs.prepend(args map (Expr(_)(TypeTag.Nothing))) + collectMacroArgs(fn) + case TypeApply(fn, args) => + typeArgs = args + collectMacroArgs(fn) + case Select(qual, name) => + prefixTree = qual + case _ => + } + collectMacroArgs(expandee) + val context = macroContext(typer, prefixTree, expandee) + var argss: List[List[Any]] = List(context) :: exprArgs.toList + macroTrace("argss: ")(argss) + + val macroDef = expandee.symbol + val ann = macroDef.getAnnotation(MacroImplAnnotation).getOrElse(throw new Error("assertion failed. %s: %s".format(macroDef, macroDef.annotations))) + val macroImpl = ann.args(0).symbol + var paramss = macroImpl.paramss + val tparams = macroImpl.typeParams + macroTrace("paramss: ")(paramss) + + // we need to take care of all possible combos of nullary/empty-paramlist macro defs vs nullary/empty-arglist invocations + // nullary def + nullary invocation => paramss and argss match, everything is okay + // nullary def + empty-arglist invocation => illegal Scala code, impossible, everything is okay + // empty-paramlist def + nullary invocation => uh-oh, we need to append a List() to argss + // empty-paramlist def + empty-arglist invocation => paramss and argss match, everything is okay + // that's almost it, but we need to account for the fact that paramss might have context bounds that mask the empty last paramlist + val paramss_without_evidences = transformTypeTagEvidenceParams(paramss, (param, tparam) => None) + val isEmptyParamlistDef = paramss_without_evidences.length != 0 && paramss_without_evidences.last.isEmpty + val isEmptyArglistInvocation = argss.length != 0 && argss.last.isEmpty + if (isEmptyParamlistDef && !isEmptyArglistInvocation) { + if (macroDebug) println("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") + argss = argss :+ Nil + } + + // nb! check partial application against paramss without evidences + val numParamLists = paramss_without_evidences.length + val numArgLists = argss.length + if (numParamLists != numArgLists) { + typer.context.error(expandee.pos, "macros cannot be partially applied") + return None + } + + // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences + // consider the following example: + // + // class D[T] { + // class C[U] { + // def foo[V] = macro Impls.foo[T, U, V] + // } + // } + // + // val outer1 = new D[Int] + // val outer2 = new outer1.C[String] + // outer2.foo[Boolean] + // + // then T and U need to be inferred from the lexical scope of the call using ``asSeenFrom'' + // whereas V won't be resolved by asSeenFrom and need to be loaded directly from ``expandee'' which needs to contain a TypeApply node + // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim + paramss = transformTypeTagEvidenceParams(paramss, (param, tparam) => Some(tparam)) + if (paramss.lastOption map (params => !params.isEmpty && params.forall(_.isType)) getOrElse false) argss = argss :+ Nil + val evidences = paramss.last takeWhile (_.isType) map (tparam => { + val TypeApply(_, implRefTargs) = ann.args(0) + var implRefTarg = implRefTargs(tparam.paramPos).tpe.typeSymbol + val tpe = if (implRefTarg.isTypeParameterOrSkolem) { + if (implRefTarg.owner == macroDef) { + // [Eugene] doesn't work when macro def is compiled separately from its usages + // then implRefTarg is not a skolem and isn't equal to any of macroDef.typeParams +// val paramPos = implRefTarg.deSkolemize.paramPos + val paramPos = macroDef.typeParams.indexWhere(_.name == implRefTarg.name) + typeArgs(paramPos).tpe + } else + implRefTarg.tpe.asSeenFrom( + if (prefixTree == EmptyTree) macroDef.owner.tpe else prefixTree.tpe, + macroDef.owner) + } else + implRefTarg.tpe + if (macroDebug) println("resolved tparam %s as %s".format(tparam, tpe)) + tpe + }) map (tpe => { + val ttag = TypeTag(tpe) + if (ttag.isConcrete) ttag.toConcrete else ttag + }) + argss = argss.dropRight(1) :+ (evidences ++ argss.last) + + assert(argss.length == paramss.length, "argss: %s, paramss: %s".format(argss, 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 = rawArgss.flatten + macroTrace("rawArgs: ")(rawArgs) + Some(rawArgs) + } + + /** Keeps track of macros in-flight. + * See more informations in comments to ``openMacros'' in ``scala.reflect.makro.Context''. + */ + var openMacros = List[MacroContext]() + + /** Performs macro expansion: + * 1) Checks whether the expansion needs to be delayed (see ``mustDelayMacroExpansion'') + * 2) Loads macro implementation using ``macroMirror'' + * 3) Synthesizes invocation arguments for the macro implementation + * 4) Checks that the result is a tree bound to this universe + * 5) Typechecks the result against the return type of the macro definition + * + * If -Ymacro-debug is enabled, you will get detailed log of how exactly this function + * performs class loading and method resolution in order to load the macro implementation. + * The log will also include other non-trivial steps of macro expansion. + * + * If -Ymacro-copypaste is enabled along with -Ymacro-debug, you will get macro expansions + * logged in the form that can be copy/pasted verbatim into REPL (useful for debugging!). + * + * @return + * the expansion result if the expansion has been successful, + * the fallback method invocation if the expansion has been unsuccessful, but there is a fallback, + * the expandee unchanged if the expansion has been delayed, + * the expandee fully expanded if the expansion has been delayed before and has been expanded now, + * the expandee with an error marker set if the expansion has been cancelled due malformed arguments or implementation + * the expandee with an error marker set if there has been an error + */ + def macroExpand(typer: Typer, expandee: Tree, pt: Type): Tree = + macroExpand1(typer, expandee) match { + case Success(expanded) => try { - // @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 + var expectedTpe = expandee.tpe + + // [Eugene] weird situation. what's the conventional way to deal with it? + val isNullaryInvocation = expandee match { + case TypeApply(Select(_, _), _) => true + case Select(_, _) => true + case _ => false } - } catch { - case ex => - val realex = ReflectionUtils.unwrapThrowable(ex) - 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 + if (isNullaryInvocation) expectedTpe match { + case MethodType(Nil, restpe) => + macroTrace("nullary invocation of a method with an empty parameter list. unwrapping expectedTpe from " + expectedTpe + " to:")(restpe) + expectedTpe = restpe + case _ => ; + } + + var typechecked = typer.context.withImplicitsEnabled(typer.typed(expanded, EXPRmode, expectedTpe)) + if (macroDebug) { + println("typechecked1:") + println(typechecked) + println(showRaw(typechecked)) + } + + typechecked = typer.context.withImplicitsEnabled(typer.typed(typechecked, EXPRmode, pt)) + if (macroDebug) { + println("typechecked2:") + println(typechecked) + println(showRaw(typechecked)) + } + + typechecked } finally { - nodePrinters.infolevel = savedInfolevel + openMacros = openMacros.tail } - case None => - def notFound() = { - typer.context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name) - None - } - def fallBackToOverridden(tree: Tree): Option[Tree] = { - tree match { - case Select(qual, name) if (macroDef.isMacro) => - macroDef.allOverriddenSymbols match { - case first :: _ => - Some(Select(qual, name) setPos tree.pos setSymbol first) - case _ => - trace("macro is not overridden: ")(tree) - notFound() + case Fallback(fallback) => + typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt)) + case Other(result) => + result + } + + private sealed abstract class MacroExpansionResult extends Product with Serializable + private case class Success(expanded: Tree) extends MacroExpansionResult + private case class Fallback(fallback: Tree) extends MacroExpansionResult + private case class Other(result: Tree) extends MacroExpansionResult + private def Delay(expandee: Tree) = Other(expandee) + private def Skip(expanded: Tree) = Other(expanded) + private def Cancel(expandee: Tree) = Other(expandee) + private def Failure(expandee: Tree) = Other(expandee) + private def fail(typer: Typer, expandee: Tree, msg: String = null) = { + if (macroDebug || macroCopypaste) { + var msg1 = if (msg contains "exception during macro expansion") msg.split(EOL).drop(1).headOption.getOrElse("?") else msg + if (macroDebug) msg1 = msg + println("macro expansion has failed: %s".format(msg1)) + } + val pos = if (expandee.pos != NoPosition) expandee.pos else openMacros.find(c => c.expandee.pos != NoPosition).map(_.expandee.pos).getOrElse(NoPosition) + if (msg != null) typer.context.error(pos, msg) + typer.infer.setError(expandee) + Failure(expandee) + } + + /** Does the same as ``macroExpand'', but without typechecking the expansion + * Meant for internal use within the macro infrastructure, don't use it elsewhere. + */ + private def macroExpand1(typer: Typer, expandee: Tree): MacroExpansionResult = { + // if a macro implementation is incompatible or any of the arguments are erroneous + // there is no sense to expand the macro itself => it will only make matters worse + if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { + val reason = if (expandee.symbol.isErroneous) "incompatible macro implementation" else "erroneous arguments" + macroTrace("cancelled macro expansion because of %s: ".format(reason))(expandee) + return Cancel(typer.infer.setError(expandee)) + } + + if (!isDelayed(expandee)) { + if (macroDebug || macroCopypaste) println("typechecking macro expansion %s at %s".format(expandee, expandee.pos)) + + val undetparams = calculateUndetparams(expandee) + if (undetparams.size != 0) { + macroTrace("macro expansion is delayed: ")(expandee) + delayed += expandee -> (typer.context, undetparams) + Delay(expandee) + } else { + val start = startTimer(macroExpandNanos) + incCounter(macroExpandCount) + try { + val macroDef = expandee.symbol + macroRuntime(macroDef) match { + case Some(runtime) => + val savedInfolevel = nodePrinters.infolevel + try { + // 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 args = macroArgs(typer, expandee) + args match { + case Some(args) => + // adding stuff to openMacros is easy, but removing it is a nightmare + // it needs to be sprinkled over several different code locations + val (context: MacroContext) :: _ = args + openMacros = context :: openMacros + val expanded: MacroExpansionResult = try { + val prevNumErrors = reporter.ERROR.count + val expanded = runtime(args) + val currNumErrors = reporter.ERROR.count + if (currNumErrors != prevNumErrors) { + fail(typer, expandee) // errors have been reported by the macro itself + } else { + expanded match { + case expanded: Expr[_] => + if (macroDebug || macroCopypaste) { + if (macroDebug) println("original:") + println(expanded.tree) + println(showRaw(expanded.tree)) + } + + freeTerms(expanded.tree) foreach (fte => typer.context.error(expandee.pos, + ("macro expansion contains free term variable %s %s. "+ + "have you forgot to use eval when splicing this variable into a reifee? " + + "if you have troubles tracking free term variables, consider using -Xlog-free-terms").format(fte.name, fte.origin))) + freeTypes(expanded.tree) foreach (fty => typer.context.error(expandee.pos, + ("macro expansion contains free type variable %s %s. "+ + "have you forgot to use c.TypeTag annotation for this type parameter? " + + "if you have troubles tracking free type variables, consider using -Xlog-free-types").format(fty.name, fty.origin))) + + val currNumErrors = reporter.ERROR.count + if (currNumErrors != prevNumErrors) { + fail(typer, expandee) + } else { + // inherit the position from the first position-ful expandee in macro callstack + // this is essential for sane error messages + var tree = expanded.tree + var position = openMacros.find(c => c.expandee.pos != NoPosition).map(_.expandee.pos).getOrElse(NoPosition) + tree = atPos(position.focus)(tree) + + // now macro expansion gets typechecked against the macro definition return type + // however, this happens in macroExpand, not here in macroExpand1 + Success(tree) + } + case expanded if expanded.isInstanceOf[Expr[_]] => + val msg = "macro must return a compiler-specific expr; returned value is Expr, but it doesn't belong to this compiler's universe" + fail(typer, expandee, msg) + case expanded => + val msg = "macro must return a compiler-specific expr; returned value is of class: %s".format(expanded.getClass) + fail(typer, expandee, msg) + } + } + } catch { + case ex: Throwable => + openMacros = openMacros.tail + throw ex + } + if (!expanded.isInstanceOf[Success]) openMacros = openMacros.tail + expanded + case None => + fail(typer, expandee) // error has been reported by macroArgs + } + } catch { + case ex => + // [Eugene] any ideas about how to improve this one? + val realex = ReflectionUtils.unwrapThrowable(ex) + realex match { + case realex: reflect.makro.runtime.AbortMacroException => + if (macroDebug || macroCopypaste) println("macro expansion has failed: %s".format(realex.msg)) + fail(typer, expandee) // error has been reported by abort + case _ => + val message = { + try { + // the most reliable way of obtaining currently executing method + // http://stackoverflow.com/questions/442747/getting-the-name-of-the-current-executing-method + val currentMethodName = new Object(){}.getClass().getEnclosingMethod().getName + val relevancyThreshold = realex.getStackTrace().indexWhere(este => este.getMethodName == currentMethodName) + if (relevancyThreshold == -1) None + else { + var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1) + var framesTillReflectiveInvocationOfMacroImpl = relevantElements.reverse.indexWhere(_.isNativeMethod) + 1 + relevantElements = relevantElements dropRight framesTillReflectiveInvocationOfMacroImpl + + realex.setStackTrace(relevantElements) + val message = new java.io.StringWriter() + realex.printStackTrace(new java.io.PrintWriter(message)) + Some(EOL + message) + } + } catch { + // if the magic above goes boom, just fall back to uninformative, but better than nothing, getMessage + case ex: Throwable => + None + } + } getOrElse realex.getMessage + fail(typer, expandee, "exception during macro expansion: " + message) + } + } finally { + nodePrinters.infolevel = savedInfolevel } - case Apply(fn, args) => - fallBackToOverridden(fn) match { - case Some(fn1) => Some(Apply(fn1, args) setPos tree.pos) - case _ => None + case None => + def notFound() = { + typer.context.error(expandee.pos, "macro implementation not found: " + macroDef.name + " " + + "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)\n" + + "if you do need to define macro implementations along with the rest of your program, consider two-phase compilation with -Xmacro-fallback-classpath " + + "in the second phase pointing to the output of the first phase") + None } - case TypeApply(fn, args) => - fallBackToOverridden(fn) match { - case Some(fn1) => Some(TypeApply(fn1, args) setPos tree.pos) - case _ => None + def fallBackToOverridden(tree: Tree): Option[Tree] = { + tree match { + case Select(qual, name) if (macroDef.isTermMacro) => + macroDef.allOverriddenSymbols match { + case first :: _ => + Some(Select(qual, name) setPos tree.pos setSymbol first) + case _ => + macroTrace("macro is not overridden: ")(tree) + notFound() + } + case Apply(fn, args) => + fallBackToOverridden(fn) match { + case Some(fn1) => Some(Apply(fn1, args) setPos tree.pos) + case _ => None + } + case TypeApply(fn, args) => + fallBackToOverridden(fn) match { + case Some(fn1) => Some(TypeApply(fn1, args) setPos tree.pos) + case _ => None + } + case _ => + macroTrace("unexpected tree in fallback: ")(tree) + notFound() + } + } + fallBackToOverridden(expandee) match { + case Some(tree1) => + macroTrace("falling back to ")(tree1) + currentRun.macroExpansionFailed = true + Fallback(tree1) + case None => + fail(typer, expandee) } - case _ => - trace("unexpected tree in fallback: ")(tree) - notFound() } + } finally { + stopTimer(macroExpandNanos, start) } - fallBackToOverridden(tree) match { - case Some(tree1) => - trace("falling back to ")(tree1) - currentRun.macroExpansionFailed = true - Some(tree1) - case None => - None - } + } + } else { + val undetparams = calculateUndetparams(expandee) + if (undetparams.size != 0) + Delay(expandee) + else + Skip(macroExpandAll(typer, expandee)) } } + + /** Without any restrictions on macro expansion, macro applications will expand at will, + * and when type inference is involved, expansions will end up using yet uninferred type params. + * + * For some macros this might be ok (thanks to TreeTypeSubstituter that replaces + * the occurrences of undetparams with their inferred values), but in general case this won't work. + * E.g. for reification simple substitution is not enough - we actually need to re-reify inferred types. + * + * Luckily, there exists a very simple way to fix the problem: delay macro expansion until everything is inferred. + * Here are the exact rules. Macro application gets delayed if any of its subtrees contain: + * 1) type vars (tpe.isInstanceOf[TypeVar]) // [Eugene] this check is disabled right now, because TypeVars seem to be created from undetparams anyways + * 2) undetparams (sym.isTypeParameter && !sym.isSkolem) + */ + var hasPendingMacroExpansions = false + private val delayed = perRunCaches.newWeakMap[Tree, (Context, collection.mutable.Set[Int])] + private def isDelayed(expandee: Tree) = delayed contains expandee + private def calculateUndetparams(expandee: Tree): collection.mutable.Set[Int] = + delayed.get(expandee).map(_._2).getOrElse { + val calculated = collection.mutable.Set[Int]() + expandee foreach (sub => { + def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym.id + if (sub.symbol != null) traverse(sub.symbol) + if (sub.tpe != null) sub.tpe foreach (sub => traverse(sub.typeSymbol)) + }) + calculated + } + private val undetparams = perRunCaches.newSet[Int] + def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = undetparams ++= newUndets map (_.id) + def notifyUndetparamsInferred(undetNoMore: List[Symbol], inferreds: List[Type]): Unit = { + undetparams --= undetNoMore map (_.id) + if (!delayed.isEmpty) + delayed.toList foreach { + case (expandee, (_, undetparams)) if !undetparams.isEmpty => + undetparams --= undetNoMore map (_.id) + if (undetparams.isEmpty) { + hasPendingMacroExpansions = true + macroTrace("macro expansion is pending: ")(expandee) + } + case _ => + // do nothing + } + } + + /** Performs macro expansion on all subtrees of a given tree. + * Innermost macros are expanded first, outermost macros are expanded last. + * See the documentation for ``macroExpand'' for more information. + */ + def macroExpandAll(typer: Typer, expandee: Tree): Tree = + new Transformer { + override def transform(tree: Tree) = super.transform(tree match { + // todo. expansion should work from the inside out + case wannabe if (delayed contains wannabe) && calculateUndetparams(wannabe).isEmpty => + val (context, _) = delayed(wannabe) + delayed -= wannabe + macroExpand(newTyper(context), wannabe, WildcardType) + case _ => + tree + }) + }.transform(expandee) } diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 3d8c2ea564..9e06cbe0d3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -8,6 +8,7 @@ package typechecker import symtab.Flags._ import scala.collection.{ mutable, immutable } import scala.tools.util.StringOps.{ ojoin } +import language.higherKinds /** Logic related to method synthesis which involves cooperation between * Namer and Typer. @@ -28,6 +29,7 @@ trait MethodSynthesis { else DefDef(sym, body) def applyTypeInternal(manifests: List[M[_]]): Type = { + // [Eugene to Paul] needs review!! val symbols = manifests map manifestToSymbol val container :: args = symbols val tparams = container.typeConstructor.typeParams @@ -58,14 +60,32 @@ trait MethodSynthesis { def newMethodType[F](owner: Symbol)(implicit m: Manifest[F]): Type = { val fnSymbol = manifestToSymbol(m) - assert(fnSymbol isSubClass FunctionClass(m.typeArguments.size - 1), (owner, m)) - val symbols = m.typeArguments map (m => manifestToSymbol(m)) - val formals = symbols.init map (_.typeConstructor) + assert(fnSymbol isSubClass FunctionClass(m.tpe.typeArguments.size - 1), (owner, m)) + // [Eugene to Paul] needs review!! + // val symbols = m.typeArguments map (m => manifestToSymbol(m)) + // val formals = symbols.init map (_.typeConstructor) + val formals = manifestToType(m).typeArguments val params = owner newSyntheticValueParams formals - - MethodType(params, symbols.last.typeConstructor) + MethodType(params, formals.last) } - } + + /** The annotations amongst those found on the original symbol which + * should be propagated to this kind of accessor. + */ + def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = { + initial filter { ann => + // There are no meta-annotation arguments attached to `ann` + if (ann.metaAnnotations.isEmpty) { + // A meta-annotation matching `annotKind` exists on `ann`'s definition. + (ann.defaultTargets contains category) || + // `ann`'s definition has no meta-annotations, and `keepClean` is true. + (ann.defaultTargets.isEmpty && keepClean) + } + // There are meta-annotation arguments, and one of them matches `annotKind` + else ann.metaAnnotations exists (_ matches category) + } + } + } import synthesisUtil._ class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { @@ -155,28 +175,23 @@ trait MethodSynthesis { /** There are two key methods in here. * - * 1) enterGetterSetter is called from Namer with a ValDef which - * may need accessors. Some setup is performed. In general this - * creates symbols and enters them into the scope of the owner. + * 1) Enter methods such as enterGetterSetterare called + * from Namer with a tree which may generate further trees such as accessors or + * implicit wrappers. Some setup is performed. In general this creates symbols + * and enters them into the scope of the owner. * - * 2) finishGetterSetter is called from Typer when a Template is typed. + * 2) addDerivedTrees is called from Typer when a Template is typed. * It completes the job, returning a list of trees with their symbols - * set to those created in enterGetterSetter. Those trees then become + * set to those created in the enter methods. Those trees then become * part of the typed template. */ trait MethodSynth { self: Namer => import NamerErrorGen._ - - /** TODO - synthesize method. - */ - def enterImplicitClass(tree: ClassDef) { - /** e.g. - val ClassDef(mods, name, tparams, impl) = tree - val converter = ImplicitClassConverter(tree).createAndEnterSymbol() - ... - */ + + def enterImplicitWrapper(tree: ClassDef) { + ImplicitClassWrapper(tree).createAndEnterSymbol() } def enterGetterSetter(tree: ValDef) { @@ -203,7 +218,8 @@ trait MethodSynthesis { enterBeans(tree) } - def finishGetterSetter(typer: Typer, stat: Tree): List[Tree] = stat match { + + def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) && !vd.symbol.isLazy => // If we don't save the annotations, they seem to wander off. val annotations = stat.symbol.initialize.annotations @@ -211,9 +227,19 @@ trait MethodSynthesis { map (acc => atPos(vd.pos.focus)(acc derive annotations)) filterNot (_ eq EmptyTree) ) + case cd @ ClassDef(mods, _, _, _) if mods.isImplicit => + val annotations = stat.symbol.initialize.annotations + // TODO: need to shuffle annotations between wrapper and class. + val wrapper = ImplicitClassWrapper(cd) + val meth = wrapper.derivedSym + val mdef = context.unit.synthetics(meth) + context.unit.synthetics -= meth + meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, false) + cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, true) + List(cd, mdef) case _ => List(stat) - } + } def standardAccessors(vd: ValDef): List[DerivedFromValDef] = ( if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd)) @@ -234,35 +260,59 @@ trait MethodSynthesis { field ::: standardAccessors(vd) ::: beanAccessors(vd) } + /** This trait assembles what's needed for synthesizing derived methods. + * Important: Typically, instances of this trait are created TWICE for each derived + * symbol; once form Namers in an enter method, and once from Typers in addDerivedTrees. + * So it's important that creating an instance of Derived does not have a side effect, + * or if it has a side effect, control that it is done only once. + */ trait Derived { - /** The tree from which we are deriving a synthetic member. */ + + /** The tree from which we are deriving a synthetic member. Typically, that's + * given as an argument of the instance. */ def tree: Tree + + /** The name of the method */ def name: TermName + + /** The flags that are retained from the original symbol */ + def flagsMask: Long + + /** The flags that the derived symbol has in addition to those retained from + * the original symbol*/ def flagsExtra: Long - - /** The tree, symbol, and type completer for the synthetic member. */ + + /** type completer for the synthetic member. + */ def completer(sym: Symbol): Type + + /** The derived symbol. It is assumed that this symbol already exists and has been + * entered in the parent scope when derivedSym is called */ def derivedSym: Symbol + + /** The definition tree of the derived symbol. */ def derivedTree: Tree } - + trait DerivedFromMemberDef extends Derived { def tree: MemberDef - + def enclClass: Symbol + // Final methods to make the rest easier to reason about. final def mods = tree.mods final def basisSym = tree.symbol - final def enclClass = basisSym.enclClass final def derivedFlags: Long = basisSym.flags & flagsMask | flagsExtra } - + trait DerivedFromClassDef extends DerivedFromMemberDef { def tree: ClassDef + final def enclClass = basisSym.owner.enclClass } trait DerivedFromValDef extends DerivedFromMemberDef { def tree: ValDef + final def enclClass = basisSym.enclClass /** Which meta-annotation is associated with this kind of entity. * Presently one of: field, getter, setter, beanGetter, beanSetter, param. @@ -286,22 +336,6 @@ trait MethodSynthesis { enterInScope(sym) sym setInfo completer(sym) } - /** The annotations amongst those found on the original symbol which - * should be propagated to this kind of accessor. - */ - private def deriveAnnotations(initial: List[AnnotationInfo]): List[AnnotationInfo] = { - initial filter { ann => - // There are no meta-annotation arguments attached to `ann` - if (ann.metaAnnotations.isEmpty) { - // A meta-annotation matching `annotKind` exists on `ann`'s definition. - (ann.defaultTargets contains category) || - // `ann`'s definition has no meta-annotations, and `keepClean` is true. - (ann.defaultTargets.isEmpty && keepClean) - } - // There are meta-annotation arguments, and one of them matches `annotKind` - else ann.metaAnnotations exists (_ matches category) - } - } private def logDerived(result: Tree): Tree = { debuglog("[+derived] " + ojoin(mods.flagString, basisSym.accurateKindString, basisSym.getterName.decode) + " (" + derivedSym + ")\n " + result) @@ -310,7 +344,7 @@ trait MethodSynthesis { } final def derive(initial: List[AnnotationInfo]): Tree = { validate() - derivedSym setAnnotations deriveAnnotations(initial) + derivedSym setAnnotations deriveAnnotations(initial, category, keepClean) logDerived(derivedTree) } } @@ -334,15 +368,21 @@ trait MethodSynthesis { /** A synthetic method which performs the implicit conversion implied by * the declaration of an implicit class. Yet to be written. */ - case class ImplicitClassConverter(tree: ClassDef) extends DerivedFromClassDef { - def completer(sym: Symbol): Type = ??? - def derivedSym: Symbol = ??? - def derivedTree: DefDef = ??? - def flagsExtra: Long = ??? - def flagsMask: Long = ??? - def name: TermName = ??? - } - + case class ImplicitClassWrapper(tree: ClassDef) extends DerivedFromClassDef { + def completer(sym: Symbol): Type = ??? // not needed + def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree) + def derivedSym: Symbol = { + val result = enclClass.info decl name + assert(result != NoSymbol, "not found: "+name+" in "+enclClass+" "+enclClass.info.decls) + result + } + def derivedTree: DefDef = + factoryMeth(mods & flagsMask | flagsExtra, name, tree, symbolic = false) + def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC + def flagsMask: Long = AccessFlags + def name: TermName = tree.name.toTermName + } + case class Getter(tree: ValDef) extends DerivedGetter { def name = tree.name def category = GetterTargetClass @@ -373,7 +413,7 @@ trait MethodSynthesis { case ExistentialType(_, _) => TypeTree() case tp => TypeTree(tp) } - tpt setPos focusPos(derivedSym.pos) + tpt setPos derivedSym.pos.focus // keep type tree of original abstract field if (mods.isDeferred) tpt setOriginal tree.tpt diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 2539091966..ffd00751e0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -99,7 +99,7 @@ trait Namers extends MethodSynthesis { owner.unsafeTypeParams foreach (paramContext.scope enter _) newNamer(paramContext) } - + def enclosingNamerWithScope(scope: Scope) = { var cx = context while (cx != NoContext && cx.scope != scope) cx = cx.outer @@ -624,11 +624,6 @@ trait Namers extends MethodSynthesis { enterCopyMethodOrGetter(tree, tparams) else sym setInfo completerOf(tree, tparams) - - if (mods hasFlag MACRO) { - if (!(sym.owner.isClass && sym.owner.isStatic)) - context.error(tree.pos, "macro definition must appear in globally accessible class") - } } def enterClassDef(tree: ClassDef) { @@ -651,14 +646,6 @@ trait Namers extends MethodSynthesis { val m = ensureCompanionObject(tree) classAndNamerOfModule(m) = (tree, null) } - val hasMacro = impl.body exists { - case DefDef(mods, _, _, _, _, _) => mods hasFlag MACRO - case _ => false - } - if (hasMacro) { - val m = ensureCompanionObject(tree) - classOfModuleClass(m.moduleClass) = new WeakReference(tree) - } val owner = tree.symbol.owner if (owner.isPackageObjectClass) { context.unit.warning(tree.pos, @@ -666,10 +653,12 @@ trait Namers extends MethodSynthesis { "If possible, define " + tree.symbol + " in " + owner.skipPackageObject + " instead." ) } - + // Suggested location only. - if (mods.isImplicit) - enterImplicitClass(tree) + if (mods.isImplicit) { + log("enter implicit wrapper "+tree+", owner = "+owner) + enterImplicitWrapper(tree) + } } // this logic is needed in case typer was interrupted half @@ -684,7 +673,7 @@ trait Namers extends MethodSynthesis { val acc = sym.lazyAccessor if (acc != NoSymbol) enterIfNotThere(acc) } - defaultParametersOfMethod(sym) foreach enterIfNotThere + defaultParametersOfMethod(sym) foreach { symRef => enterIfNotThere(symRef()) } } this.context } @@ -809,7 +798,9 @@ trait Namers extends MethodSynthesis { */ private def assignTypeToTree(tree: ValOrDefDef, defnTyper: Typer, pt: Type): Type = { // compute result type from rhs - val typedBody = defnTyper.computeType(tree.rhs, pt) + val typedBody = + if (tree.symbol.isTermMacro) defnTyper.computeMacroDefType(tree, pt) + else defnTyper.computeType(tree.rhs, pt) val sym = if (owner.isMethod) owner else tree.symbol val typedDefn = widenIfNecessary(sym, typedBody, pt) assignTypeToTree(tree, typedDefn) @@ -871,10 +862,8 @@ trait Namers extends MethodSynthesis { Namers.this.classOfModuleClass get clazz foreach { cdefRef => val cdef = cdefRef() if (cdef.mods.isCase) addApplyUnapply(cdef, templateNamer) - if (settings.Xmacros.value) addMacroMethods(cdef.impl, templateNamer) classOfModuleClass -= clazz } - if (settings.Xmacros.value) addMacroMethods(templ, templateNamer) } // add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because @@ -1029,12 +1018,20 @@ trait Namers extends MethodSynthesis { } addDefaultGetters(meth, vparamss, tparams, overriddenSymbol) + // macro defs need to be typechecked in advance + // because @macroImpl annotation only gets assigned during typechecking + // otherwise we might find ourselves in the situation when we specified -Xmacro-fallback-classpath + // but macros still don't expand + // that might happen because macro def doesn't have its link a macro impl yet + if (ddef.symbol.isTermMacro) { + val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol)) + typer.computeMacroDefType(ddef, pt) + } + thisMethodType({ val rt = ( if (!tpt.isEmpty) { typer.typedType(tpt).tpe - } else if (meth.isMacro) { - assignTypeToTree(ddef, AnyClass.tpe) } else { // replace deSkolemized symbols with skolemized ones // (for resultPt computed by looking at overridden symbol, right?) @@ -1161,7 +1158,7 @@ trait Namers extends MethodSynthesis { // if compiling the same local block several times (which can happen in interactive mode) // we might otherwise not find the default symbol, because the second time it the // method symbol will be re-entered in the scope but the default parameter will not. - defaultParametersOfMethod(meth) += default + defaultParametersOfMethod(meth) += new WeakReference(default) } } else if (baseHasDefault) { // the parameter does not have a default itself, but the @@ -1404,10 +1401,10 @@ trait Namers extends MethodSynthesis { if (sym.isImplicit) { if (sym.isConstructor) fail(ImplicitConstr) - if (!sym.isTerm) - fail(ImplicitNotTerm) + if (!(sym.isTerm || (sym.isClass && !sym.isTrait))) + fail(ImplicitNotTermOrClass) if (sym.owner.isPackageClass) - fail(ImplicitTopObject) + fail(ImplicitAtToplevel) } if (sym.isClass) { if (sym.isAnyOverride && !sym.hasFlag(TRAIT)) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 2573678f8c..be269cf4b2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -8,6 +8,7 @@ package typechecker import symtab.Flags._ import scala.collection.mutable +import scala.ref.WeakReference /** * @author Lukas Rytz @@ -20,7 +21,7 @@ trait NamesDefaults { self: Analyzer => import NamesDefaultsErrorsGen._ val defaultParametersOfMethod = - perRunCaches.newWeakMap[Symbol, Set[Symbol]]() withDefaultValue Set() + perRunCaches.newWeakMap[Symbol, Set[WeakReference[Symbol]]]() withDefaultValue Set() case class NamedApplyInfo( qual: Option[Tree], @@ -47,7 +48,7 @@ trait NamesDefaults { self: Analyzer => /** @param pos maps indices from new to old (!) */ def reorderArgsInv[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = { val argsArray = args.toArray - argsArray.indices map (i => argsArray(pos(i))) toList + (argsArray.indices map (i => argsArray(pos(i)))).toList } /** returns `true` if every element is equal to its index */ diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index aff8368f75..1fd9f6fc13 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -8,6 +8,7 @@ package typechecker import symtab._ import Flags.{MUTABLE, METHOD, LABEL, SYNTHETIC} +import language.postfixOps /** Translate pattern matching into method calls (these methods form a zero-plus monad), similar in spirit to how for-comprehensions are compiled. * @@ -21,7 +22,7 @@ import Flags.{MUTABLE, METHOD, LABEL, SYNTHETIC} * - Array patterns * - implement spec more closely (see TODO's) * - DCE - * - use manifests for type testing + * - use TypeTags for type testing * * (longer-term) TODO: * - user-defined unapplyProd @@ -118,6 +119,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore => import typer.{typed, context, silent, reallyExists} + // import typer.infer.containsUnchecked /** 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. @@ -130,10 +132,10 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => * this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch) */ def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type, scrutType: Type, matchFailGenOverride: Option[Tree => Tree] = None): Tree = { - // we don't transform after typers - // (that would require much more sophistication when generating trees, + // we don't transform after uncurry + // (that would require more sophistication when generating trees, // and the only place that emits Matches after typers is for exception handling anyway) - assert(phase.id <= currentRun.typerPhase.id, phase) + assert(phase.id < currentRun.uncurryPhase.id, phase) val scrutSym = freshSym(scrut.pos, pureType(scrutType)) setFlag SYNTH_CASE // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental @@ -157,12 +159,12 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, pt)(caseDef), EmptySubstitution)) } - for(cases <- emitTypeSwitch(bindersAndCases, pt) toList; + for(cases <- emitTypeSwitch(bindersAndCases, pt).toList; if cases forall treeInfo.isCatchCase; // must check again, since it's not guaranteed -- TODO: can we eliminate this? e.g., a type test could test for a trait or a non-trivial prefix, which are not handled by the back-end cse <- cases) yield fixerUpper(matchOwner, pos)(cse).asInstanceOf[CaseDef] } - val catches = if (swatches nonEmpty) swatches else { + val catches = if (swatches.nonEmpty) swatches else { val scrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, pt)(caseDef), EmptySubstitution))} @@ -819,13 +821,34 @@ class Foo(x: Other) { x._1 } // no error in this order cond } - // TODO: also need to test when erasing pt loses crucial information (and if we can recover it using a manifest) - def needsTypeTest(tp: Type, pt: Type) = !(tp <:< pt) - private def typeTest(binder: Symbol, pt: Type) = maybeWithOuterCheck(binder, pt)(codegen._isInstanceOf(binder, pt)) + // containsUnchecked: also need to test when erasing pt loses crucial information (maybe we can recover it using a TypeTag) + def needsTypeTest(tp: Type, pt: Type): Boolean = !(tp <:< pt) // || containsUnchecked(pt) + // TODO: try to find the TypeTag for the binder's type and the expected type, and if they exists, + // check that the TypeTag of the binder's type conforms to the TypeTag of the expected type + private def typeTest(binderToTest: Symbol, expectedTp: Type, disableOuterCheck: Boolean = false, dynamic: Boolean = false): Tree = { import CODE._ + // def coreTest = + if (disableOuterCheck) codegen._isInstanceOf(binderToTest, expectedTp) else maybeWithOuterCheck(binderToTest, expectedTp)(codegen._isInstanceOf(binderToTest, expectedTp)) + // if (opt.experimental && containsUnchecked(expectedTp)) { + // if (dynamic) { + // val expectedTpTagTree = findManifest(expectedTp, true) + // if (!expectedTpTagTree.isEmpty) + // ((expectedTpTagTree DOT "erasure".toTermName) DOT "isAssignableFrom".toTermName)(REF(binderToTest) DOT nme.getClass_) + // else + // coreTest + // } else { + // val expectedTpTagTree = findManifest(expectedTp, true) + // val binderTpTagTree = findManifest(binderToTest.info, true) + // if(!(expectedTpTagTree.isEmpty || binderTpTagTree.isEmpty)) + // coreTest AND (binderTpTagTree DOT nme.CONFORMS)(expectedTpTagTree) + // else + // coreTest + // } + // } else coreTest + } // need to substitute since binder may be used outside of the next extractor call (say, in the body of the case) case class TypeTestTreeMaker(prevBinder: Symbol, nextBinderTp: Type, pos: Position) extends CondTreeMaker { - val cond = typeTest(prevBinder, nextBinderTp) + val cond = typeTest(prevBinder, nextBinderTp, dynamic = true) val res = codegen._asInstanceOf(prevBinder, nextBinderTp) override def toString = "TT"+(prevBinder, nextBinderTp) } @@ -865,7 +888,7 @@ class Foo(x: Other) { x._1 } // no error in this order // TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null` // don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false") def genEqualsAndInstanceOf(sym: Symbol): Tree - = codegen._equals(REF(sym), patBinder) AND codegen._isInstanceOf(patBinder, pt.widen) + = codegen._equals(REF(sym), patBinder) AND typeTest(patBinder, pt.widen, disableOuterCheck = true) def isRefTp(tp: Type) = tp <:< AnyRefClass.tpe @@ -873,7 +896,7 @@ class Foo(x: Other) { x._1 } // no error in this order def isMatchUnlessNull = isRefTp(pt) && !needsTypeTest(patBinderTp, pt) // TODO: [SPEC] type test for Array - // TODO: use manifests to improve tests (for erased types we can do better when we have a manifest) + // TODO: use TypeTags to improve tests (for erased types we can do better when we have a TypeTag) pt match { case SingleType(_, sym) /*this implies sym.isStable*/ => genEqualsAndInstanceOf(sym) // TODO: [SPEC] the spec requires `eq` instead of `==` here case ThisType(sym) if sym.isModule => genEqualsAndInstanceOf(sym) // must use == to support e.g. List() == Nil @@ -1082,22 +1105,14 @@ class Foo(x: Other) { x._1 } // no error in this order def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya def and(a: Tree, b: Tree): Tree = a AND b + // drop annotations generated by CPS plugin etc, since its annotationchecker rejects T @cps[U] <: Any + // let's assume for now annotations don't affect casts, drop them there, and bring them back using the outer Typed tree + private def mkCast(t: Tree, tp: Type) = Typed(gen.mkAsInstanceOf(t, tp.withoutAnnotations, true, false), TypeTree() setType tp) // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) - def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = /*repackExistential*/(tp) - if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX) - else gen.mkAsInstanceOf(t, tpX, true, false) - } - - def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), /*repackExistential*/(tp), true, false) - // { val tpX = /*repackExistential*/(tp) + def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tp)) t else mkCast(t, tp) + def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else mkCast(REF(b), tp) + def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), tp.withoutAnnotations, true, false) // if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } - // else gen.mkIsInstanceOf(REF(b), tpX, true, false) - // } - - def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = /*repackExistential*/(tp) - if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX) - else gen.mkAsInstanceOf(REF(b), tpX, true, false) - } // duplicated out of frustration with cast generation def mkZero(tp: Type): Tree = { @@ -1635,7 +1650,7 @@ class Foo(x: Other) { x._1 } // no error in this order */ def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { val matchEnd = NoSymbol.newLabel(freshName("matchEnd"), NoPosition) setFlag SYNTH_CASE - val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe + val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations // matchEnd setInfo MethodType(List(matchRes), restpe) def newCaseSym = NoSymbol.newLabel(freshName("case"), NoPosition) setInfo MethodType(Nil, restpe) setFlag SYNTH_CASE @@ -1649,7 +1664,11 @@ class Foo(x: Other) { x._1 } // no error in this order def catchAll = matchFailGen map { matchFailGen => val scrutRef = if(scrutSym ne NoSymbol) REF(scrutSym) else EmptyTree // for alternatives - LabelDef(nextCase, Nil, matchEnd APPLY (_asInstanceOf(matchFailGen(scrutRef), restpe))) // need to jump to matchEnd with result generated by matchFailGen (could be `FALSE` for isDefinedAt) + // must jump to matchEnd, use result generated by matchFailGen (could be `FALSE` for isDefinedAt) + LabelDef(nextCase, Nil, matchEnd APPLY (matchFailGen(scrutRef))) + // don't cast the arg to matchEnd when using PartialFun synth in uncurry, since it won't detect the throw (see gen.withDefaultCase) + // the cast is necessary when using typedMatchAnonFun-style PartialFun synth: + // (_asInstanceOf(matchFailGen(scrutRef), restpe)) } toList // catchAll.isEmpty iff no synthetic default case needed (the (last) user-defined case is a default) // if the last user-defined case is a default, it will never jump to the next case; it will go immediately to matchEnd diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index c4b08f6ea2..21336c2375 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -10,6 +10,7 @@ import symtab.Flags._ import collection.{ mutable, immutable } import transform.InfoTransform import scala.collection.mutable.ListBuffer +import language.postfixOps /** <p> * Post-attribution checking and transformation. @@ -227,6 +228,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R * 1.8.1 M's type is a subtype of O's type, or * 1.8.2 M is of type []S, O is of type ()T and S <: T, or * 1.8.3 M is of type ()S, O is of type []T and S <: T, or + * 1.9. If M is a macro def, O cannot be deferred. + * 1.10. If M is not a macro def, O cannot be a macro def. * 2. Check that only abstract classes have deferred members * 3. Check that concrete classes do not have deferred definitions * that are not implemented in a subclass. @@ -356,7 +359,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R /** Is the intersection between given two lists of overridden symbols empty? */ def intersectionIsEmpty(syms1: List[Symbol], syms2: List[Symbol]) = - !(syms1 exists (syms2 contains)) + !(syms1 exists (syms2 contains _)) if (typesOnly) checkOverrideTypes() else { @@ -416,6 +419,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R } else if (other.isValue && other.isLazy && !other.isSourceMethod && !other.isDeferred && member.isValue && !member.isLazy) { overrideError("must be declared lazy to override a concrete lazy value") + } else if (other.isDeferred && member.isTermMacro) { // (1.9) + overrideError("cannot override an abstract method") + } else if (other.isTermMacro && !member.isTermMacro) { // (1.10) + overrideError("cannot override a macro") } else { checkOverrideTypes() if (settings.warnNullaryOverride.value) { @@ -1669,9 +1676,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R inPattern = false treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) case LabelDef(_, _, _) if gen.hasSynthCaseSymbol(result) => + val old = inPattern inPattern = true val res = deriveLabelDef(result)(transform) - inPattern = false + inPattern = old res case _ => super.transform(result) diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index da87d38ab0..868c236ee9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -50,7 +50,7 @@ trait SyntheticMethods extends ast.TreeDSL { import synthesizer._ if (clazz0 == AnyValClass || isPrimitiveValueClass(clazz0)) return { - if (clazz0.info member nme.getClass_ isDeferred) { + if ((clazz0.info member nme.getClass_).isDeferred) { // XXX dummy implementation for now val getClassMethod = createMethod(nme.getClass_, getClassReturnType(clazz.tpe))(_ => NULL) deriveTemplate(templ)(_ :+ getClassMethod) @@ -303,7 +303,7 @@ trait SyntheticMethods extends ast.TreeDSL { lb += logResult("case accessor new")(newAcc) } - lb ++= templ.body ++= synthesize() toList + (lb ++= templ.body ++= synthesize()).toList } if (phase.id > currentRun.typerPhase.id) templ diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 8895905ca7..4319dd10c7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -14,8 +14,9 @@ import util.returning abstract class TreeCheckers extends Analyzer { import global._ - private val everything = ListBuffer[(Phase, Map[Tree, (Symbol, Type)])]() + private val everything = ListBuffer[(Phase, Map[Tree, (Symbol, Type)])]() private val currentTrees = mutable.Map[Tree, (Symbol, Type)]() + private val tpeOfTree = mutable.HashMap[Tree, Type]() if (settings.debug.value) { sys addShutdownHook { @@ -28,7 +29,7 @@ abstract class TreeCheckers extends Analyzer { } } - private def classstr(x: AnyRef) = x.getClass.getName split """\\.|\\$""" last; + private def classstr(x: AnyRef) = (x.getClass.getName split """\\.|\\$""").last private def typestr(x: Type) = " (tpe = " + x + ")" private def treestr(t: Tree) = t + " [" + classstr(t) + "]" + typestr(t.tpe) private def ownerstr(s: Symbol) = "'" + s + "'" + s.locationString @@ -49,12 +50,13 @@ abstract class TreeCheckers extends Analyzer { object SymbolTracker extends Traverser { type PhaseMap = mutable.HashMap[Symbol, List[Tree]] + val defSyms = mutable.HashMap[Symbol, List[DefTree]]() withDefaultValue Nil + val newSyms = mutable.HashSet[Symbol]() val maps = ListBuffer[(Phase, PhaseMap)]() + val movedMsgs = ListBuffer[String]() + def prev = maps.init.last._2 def latest = maps.last._2 - val defSyms = mutable.HashMap[Symbol, List[DefTree]]() - val newSyms = mutable.HashSet[Symbol]() - val movedMsgs = new ListBuffer[String] def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString) def inPrev(sym: Symbol) = { @@ -119,10 +121,8 @@ abstract class TreeCheckers extends Analyzer { if (sym != null && sym != NoSymbol) { record(sym, tree) tree match { - case x: DefTree => - if (defSyms contains sym) defSyms(sym) = defSyms(sym) :+ x - else defSyms(sym) = List(x) - case _ => () + case x: DefTree => defSyms(sym) :+= x + case _ => () } } @@ -130,8 +130,6 @@ abstract class TreeCheckers extends Analyzer { } } - lazy val tpeOfTree = mutable.HashMap[Tree, Type]() - def posstr(p: Position) = try p.source.path + ":" + p.line catch { case _: UnsupportedOperationException => p.toString } @@ -147,9 +145,7 @@ abstract class TreeCheckers extends Analyzer { if (!cond) errorFn(msg) def checkTrees() { - if (settings.verbose.value) - Console.println("[consistency check at the beginning of phase " + phase + "]") - + informFn("[consistency check at the beginning of phase " + phase + "]") currentRun.units foreach check } @@ -172,7 +168,7 @@ abstract class TreeCheckers extends Analyzer { informProgress("checking "+unit) val context = rootContext(unit) context.checking = true - tpeOfTree.clear + tpeOfTree.clear() SymbolTracker.check(phase, unit) val checker = new TreeChecker(context) runWithUnit(unit) { diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 3233b7b07c..38c2c5f719 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -456,14 +456,20 @@ trait TypeDiagnostics { ex match { case CyclicReference(sym, info: TypeCompleter) => - val pos = info.tree match { - case Import(expr, _) => expr.pos - case _ => ex.pos + if (context0.owner.isTermMacro) { + // see comments to TypeSigError for an explanation of this special case + // [Eugene] is there a better way? + throw ex + } else { + val pos = info.tree match { + case Import(expr, _) => expr.pos + case _ => ex.pos + } + contextError(context0, pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) + + if (sym == ObjectClass) + throw new FatalError("cannot redefine root "+sym) } - contextError(context0, pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) - - if (sym == ObjectClass) - throw new FatalError("cannot redefine root "+sym) case _ => contextError(context0, ex.pos, ex) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f558e0afc7..0541f85f31 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -51,6 +51,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { transformed.clear() } + // [Eugene] shouldn't this be converted to resetAllAttrs? object UnTyper extends Traverser { override def traverse(tree: Tree) = { if (tree != EmptyTree) tree.tpe = null @@ -181,7 +182,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case _ => def wrapImplicit(from: Type): Tree = { val result = inferImplicit(tree, functionType(List(from), to), reportAmbiguous, true, context, saveErrors) - if (result.subst != EmptyTreeTypeSubstituter) result.subst traverse tree + if (result.subst != EmptyTreeTypeSubstituter) { + result.subst traverse tree + notifyUndetparamsInferred(result.subst.from, result.subst.to) + } result.tree } val result = wrapImplicit(from) @@ -508,7 +512,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { res } - /** The typer for a label definition. If this is part of a template we * first have to enter the label definition. */ @@ -671,7 +674,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (tree.tpe.isInstanceOf[MethodType] && pre.isStable && sym.tpe.params.isEmpty && (isStableContext(tree, mode, pt) || sym.isModule)) - tree.setType(MethodType(List(), singleType(pre, sym))) + tree.setType(MethodType(List(), singleType(pre, sym))) // TODO: should this be a NullaryMethodType? else tree } @@ -730,6 +733,39 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } + def checkFeature(pos: Position, featureTrait: Symbol, construct: => String = "") = + if (!isPastTyper) { + val nestedOwners = + featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse + val featureName = (nestedOwners map (_.name + ".")).mkString + featureTrait.name + unit.toCheck += { () => + def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, true, false, context) != SearchFailure + def hasOption = settings.language.value contains featureName + if (!hasImport && !hasOption) { + val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) = + featureTrait getAnnotation LanguageFeatureAnnot + val req = if (required) "needs to" else "should" + var raw = featureDesc + " " + req + " be enabled\n" + + "by making the implicit value language." + featureName + " visible." + if (!(currentRun.reportedFeature contains featureTrait)) + raw += "\nThis can be achieved by adding the import clause 'import language." + featureName + "'\n" + + "or by setting the compiler option -language:" + featureName + ".\n" + + "See the Scala docs for value scala.language." + featureName + " for a discussion\n" + + "why the feature " + req + " be explicitly enabled." + currentRun.reportedFeature += featureTrait + val msg = raw replace ("#", construct) + if (required) unit.error(pos, msg) + else currentRun.featureWarnings.warn(pos, msg) + } + } + } + + def checkExistentialsFeature(pos: Position, tpe: Type, prefix: String) = tpe match { + case extp: ExistentialType if !extp.isRepresentableWithWildcards => + checkFeature(pos, ExistentialsFeature, prefix+" "+tpe) + case _ => + } + /** 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 @@ -769,7 +805,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree = { def adaptToImplicitMethod(mt: MethodType): Tree = { - if (context.undetparams nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0` + if (context.undetparams.nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0` // dropped so that type args of implicit method are inferred even if polymorphic expressions are allowed // needed for implicits in 2.8 collection library -- maybe once #3346 is fixed, we can reinstate the condition? context.undetparams = inferExprInstance(tree, context.extractUndetparams(), pt, @@ -813,7 +849,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Block(_, tree1) => tree1.symbol case _ => tree.symbol } - if (!meth.isConstructor && !meth.isMacro && isFunctionType(pt)) { // (4.2) + if (!meth.isConstructor && !meth.isTermMacro && isFunctionType(pt)) { // (4.2) debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt) checkParamsConvertible(tree, tree.tpe) val tree0 = etaExpand(context.unit, tree) @@ -1008,12 +1044,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else TypeApply(tree, tparams1 map (tparam => TypeTree(tparam.tpeHK) setPos tree.pos.focus)) setPos tree.pos //@M/tcpolyinfer: changed tparam.tpe to tparam.tpeHK context.undetparams ++= tparams1 + notifyUndetparamsAdded(tparams1) adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original) case mt: MethodType if mt.isImplicit && ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1) adaptToImplicitMethod(mt) case mt: MethodType if (((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) && - (context.undetparams.isEmpty || inPolyMode(mode))) => + (context.undetparams.isEmpty || inPolyMode(mode))) && !(tree.symbol != null && tree.symbol.isTermMacro) => instantiateToMethodType(mt) case _ => @@ -1026,13 +1063,10 @@ 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 && !(tree exists (_.isErroneous))) - macroExpand(tree, this) match { - case Some(expanded: Tree) => - typed(expanded, mode, pt) - case None => - setError(tree) // error already reported - } + else if (context.macrosEnabled && // when macros are enabled + inExprModeButNot(mode, FUNmode) && !tree.isDef && // and typechecking application + tree.symbol != null && tree.symbol.isTermMacro) // of a term macro + macroExpand(this, tree, pt) else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) adaptConstrPattern() else if (inAllModes(mode, EXPRmode | FUNmode) && @@ -1169,7 +1203,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { && !qtpe.typeSymbol.isBottomClass && qtpe != WildcardType && !qual.isInstanceOf[ApplyImplicitView] // don't chain views - && context.implicitsEnabled + && (context.implicitsEnabled || context.enrichmentEnabled) // Elaborating `context.implicitsEnabled`: // don't try to adapt a top-level type that's the subject of an implicit search // this happens because, if isView, typedImplicit tries to apply the "current" implicit value to @@ -1653,7 +1687,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val body = if (isPastTyper || reporter.hasErrors) templ.body - else templ.body flatMap rewrappingWrapperTrees(namer.finishGetterSetter(Typer.this, _)) + else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) val body1 = typedStats(body, templ.symbol) @@ -1906,8 +1940,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { meth.owner.isAnonOrRefinementClass)) InvalidConstructorDefError(ddef) typed(ddef.rhs) - } else if (meth.isMacro) { - EmptyTree + } else if (meth.isTermMacro) { + // typechecking macro bodies is sort of unconventional + // that's why we employ our custom typing scheme orchestrated outside of the typer + transformedOr(ddef.rhs, typedMacroBody(this, ddef)) } else { transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe) } @@ -1937,6 +1973,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (meth.isStructuralRefinementMember) checkMethodStructuralCompatible(meth) + if (meth.isImplicit && !meth.isSynthetic) meth.info.paramss match { + case List(param) :: _ if !param.isImplicit => + checkFeature(ddef.pos, ImplicitConversionsFeature, meth.toString) + case _ => + } + treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType } @@ -1968,6 +2010,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case TypeBounds(lo1, hi1) if (!(lo1 <:< hi1)) => LowerBoundError(tdef, lo1, hi1) case _ => () } + + if (tdef.symbol.isDeferred && tdef.symbol.info.isHigherKinded) + checkFeature(tdef.pos, HigherKindsFeature) + treeCopy.TypeDef(tdef, typedMods, tdef.name, tparams1, rhs1) setType NoType } @@ -2111,7 +2157,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { var body1: Tree = typed(cdef.body, pt) val contextWithTypeBounds = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) - if (contextWithTypeBounds.savedTypeBounds nonEmpty) { + if (contextWithTypeBounds.savedTypeBounds.nonEmpty) { body1.tpe = contextWithTypeBounds restoreTypeBounds body1.tpe // insert a cast if something typechecked under the GADT constraints, @@ -2147,13 +2193,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) - def prepareTranslateMatch(selector0: Tree, cases: List[CaseDef], mode: Int, resTp: Type) = { + def typedMatch(selector0: Tree, cases: List[CaseDef], mode: Int, resTp: Type) = { val (selector, doTranslation) = selector0 match { case Annotated(Ident(nme.synthSwitch), selector) => (selector, false) case s => (s, true) } val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - val selectorTp = packCaptured(selector1.tpe.widen) + val selectorTp = packCaptured(selector1.tpe.widen.withoutAnnotations) val casesTyped = typedCases(cases, selectorTp, resTp) val caseTypes = casesTyped map (c => packedType(c, context.owner).deconst) @@ -2164,7 +2210,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (selector1, selectorTp, casesAdapted, ownType, doTranslation) } - def translateMatch(selector1: Tree, selectorTp: Type, casesAdapted: List[CaseDef], ownType: Type, doTranslation: Boolean, matchFailGen: Option[Tree => Tree] = None) = { + def translatedMatch(selector1: Tree, selectorTp: Type, casesAdapted: List[CaseDef], ownType: Type, doTranslation: Boolean, matchFailGen: Option[Tree => Tree] = None) = { def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) case _ => tp @@ -2174,10 +2220,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { Match(selector1, casesAdapted) setType ownType // setType of the Match to avoid recursing endlessly } else { val scrutType = repeatedToSeq(elimAnonymousClass(selectorTp)) - // we've packed the type for each case in prepareTranslateMatch so that if all cases have the same existential case, we get a clean lub + // we've packed the type for each case in typedMatch so that if all cases have the same existential case, we get a clean lub // here, we should open up the existential again // relevant test cases: pos/existentials-harmful.scala, pos/gadt-gilles.scala, pos/t2683.scala, pos/virtpatmat_exist4.scala - MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownType.skolemizeExistential(context.owner, context.tree)), scrutType, matchFailGen) + // TODO: fix skolemizeExistential (it should preserve annotations, right?) + val ownTypeSkolemized = ownType.skolemizeExistential(context.owner, context.tree) withAnnotations ownType.annotations + MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownTypeSkolemized), scrutType, matchFailGen) } } @@ -2202,7 +2250,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { vparams map {p => if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe} } - def mkParams(methodSym: Symbol, formals: List[Type]/* = deriveFormals*/) = { + def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = { selOverride match { case None if targs.isEmpty => MissingParameterTypeAnonMatchError(tree, pt); (Nil, EmptyTree) case None => @@ -2212,7 +2260,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (ps, sel) case Some((vparams, sel)) => val newParamSyms = (vparams, formals).zipped map {(p, tp) => - methodSym.newValueParameter(p.name, focusPos(p.pos), SYNTHETIC) setInfo tp + methodSym.newValueParameter(p.name, p.pos.focus, SYNTHETIC) setInfo tp } (newParamSyms, sel.duplicate) @@ -2228,7 +2276,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // rig the show so we can get started typing the method body -- later we'll correct the infos... anonClass setInfo ClassInfoType(List(ObjectClass.tpe, pt, SerializableClass.tpe), newScope, anonClass) val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL) - val (paramSyms, selector) = mkParams(methodSym, deriveFormals) + val (paramSyms, selector) = mkParams(methodSym) if (selector eq EmptyTree) EmptyTree else { @@ -2237,7 +2285,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) paramSyms foreach (methodBodyTyper.context.scope enter _) - val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, cases, mode, ptRes) + val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, cases, mode, ptRes) val methFormals = paramSyms map (_.tpe) val parents = List(abstractFunctionType(methFormals, resTp), SerializableClass.tpe) @@ -2245,7 +2293,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { anonClass setInfo ClassInfoType(parents, newScope, anonClass) methodSym setInfoAndEnter MethodType(paramSyms, resTp) - DefDef(methodSym, methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation)) + DefDef(methodSym, methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation)) } } @@ -2267,7 +2315,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else { // applyOrElse's default parameter: val B1 = methodSym newTypeParameter(newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp) - val default = methodSym newValueParameter(newTermName("default"), focusPos(tree.pos), SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe) + val default = methodSym newValueParameter(newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe) val paramSyms = List(x, default) methodSym setInfoAndEnter polyType(List(A1, B1), MethodType(paramSyms, B1.tpe)) @@ -2275,7 +2323,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) paramSyms foreach (methodBodyTyper.context.scope enter _) - val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, cases, mode, ptRes) + val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, cases, mode, ptRes) anonClass setInfo ClassInfoType(parents(List(argTp, resTp)), newScope, anonClass) B1 setInfo TypeBounds.lower(resTp) @@ -2284,7 +2332,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // use applyOrElse's first parameter since the scrut's type has been widened def doDefault(scrut_ignored: Tree) = REF(default) APPLY (REF(x)) - val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, B1.tpe, doTranslation, Some(doDefault)) + val body = methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, B1.tpe, doTranslation, Some(doDefault)) DefDef(methodSym, body) } @@ -2292,15 +2340,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def isDefinedAtMethod = { val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos, FINAL) - val (paramSyms, selector) = mkParams(methodSym, deriveFormals) + val (paramSyms, selector) = mkParams(methodSym) if (selector eq EmptyTree) EmptyTree else { val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) paramSyms foreach (methodBodyTyper.context.scope enter _) methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe) - val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, casesTrue, mode, BooleanClass.tpe) - val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, Some(scrutinee => FALSE_typed)) + val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe) + val body = methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, Some(scrutinee => FALSE_typed)) DefDef(methodSym, body) } @@ -2361,29 +2409,21 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - fun.body match { - case Match(sel, cases) if opt.virtPatmat => - // go to outer context -- must discard the context that was created for the Function since we're discarding the function - // thus, its symbol, which serves as the current context.owner, is not the right owner - // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) - newTyper(context.outer).typedMatchAnonFun(fun, cases, mode, pt, Some((fun.vparams, sel))) - case _ => - val vparamSyms = fun.vparams map { vparam => - enterSym(context, vparam) - if (context.retyping) context.scope enter vparam.symbol - vparam.symbol - } - val vparams = fun.vparams mapConserve (typedValDef) - // for (vparam <- vparams) { - // checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () - // } - val formals = vparamSyms map (_.tpe) - val body1 = typed(fun.body, respt) - val restpe = packedType(body1, fun.symbol).deconst.resultType - val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) - // body = checkNoEscaping.locals(context.scope, restpe, body) - treeCopy.Function(fun, vparams, body1).setType(funtpe) + val vparamSyms = fun.vparams map { vparam => + enterSym(context, vparam) + if (context.retyping) context.scope enter vparam.symbol + vparam.symbol } + val vparams = fun.vparams mapConserve (typedValDef) +// for (vparam <- vparams) { +// checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () +// } + val formals = vparamSyms map (_.tpe) + val body1 = typed(fun.body, respt) + val restpe = packedType(body1, fun.symbol).deconst.resultType + val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) +// body = checkNoEscaping.locals(context.scope, restpe, body) + treeCopy.Function(fun, vparams, body1).setType(funtpe) } } @@ -2474,54 +2514,53 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { || (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate) ) - def checkNoDoubleDefsAndAddSynthetics(stats: List[Tree]): List[Tree] = { + def checkNoDoubleDefs(stats: List[Tree]): Unit = { val scope = if (inBlock) context.scope else context.owner.info.decls - var newStats = new ListBuffer[Tree] - var needsCheck = true - var moreToAdd = true - while (moreToAdd) { - val initSize = scope.size - var e = scope.elems - while ((e ne null) && e.owner == scope) { - - // check no double def - if (needsCheck) { - var e1 = scope.lookupNextEntry(e) - while ((e1 ne null) && e1.owner == scope) { - if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) && - (e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe) || e.sym.isMacro && e1.sym.isMacro)) - // default getters are defined twice when multiple overloads have defaults. an - // error for this is issued in RefChecks.checkDefaultsInOverloaded - if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefault && - !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 - } - e1 = scope.lookupNextEntry(e1) + var e = scope.elems + while ((e ne null) && e.owner == scope) { + var e1 = scope.lookupNextEntry(e) + while ((e1 ne null) && e1.owner == scope) { + if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) && + (e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe))) + // default getters are defined twice when multiple overloads have defaults. an + // 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 } - } - - // add synthetics - context.unit.synthetics get e.sym foreach { tree => - newStats += typedStat(tree) // might add even more synthetics to the scope - context.unit.synthetics -= e.sym + e1 = scope.lookupNextEntry(e1) } - e = e.next } - needsCheck = false - // the type completer of a synthetic might add more synthetics. example: if the - // factory method of a case class (i.e. the constructor) has a default. - moreToAdd = initSize != scope.size + } + + def addSynthetics(stats: List[Tree]): List[Tree] = { + val scope = if (inBlock) context.scope else context.owner.info.decls + var newStats = new ListBuffer[Tree] + var moreToAdd = true + while (moreToAdd) { + val initElems = scope.elems + for (sym <- scope) + for (tree <- context.unit.synthetics get sym) { + newStats += typedStat(tree) // might add even more synthetics to the scope + context.unit.synthetics -= sym + } + // the type completer of a synthetic might add more synthetics. example: if the + // factory method of a case class (i.e. the constructor) has a default. + moreToAdd = scope.elems ne initElems } if (newStats.isEmpty) stats else { // put default getters next to the method they belong to, // same for companion objects. fixes #2489 and #4036. + // [Martin] This is pretty ugly. I think we could avoid + // this code by associating defaults and companion objects + // with the original tree instead of the new symbol. def matches(stat: Tree, synt: Tree) = (stat, synt) match { case (DefDef(_, statName, _, _, _, _), DefDef(mods, syntName, _, _, _, _)) => mods.hasDefaultFlag && syntName.toString.startsWith(statName.toString) @@ -2551,7 +2590,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } context.updateBuffer(statsErrors) if (phase.erasedTypes) stats1 - else checkNoDoubleDefsAndAddSynthetics(stats1) + else { + checkNoDoubleDefs(stats1) + addSynthetics(stats1) + } } def typedArg(arg: Tree, mode: Int, newmode: Int, pt: Type): Tree = { @@ -2575,7 +2617,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else if (isByNameParamType(formals.head)) 0 else BYVALmode ) - val tree = typedArg(args.head, mode, typedMode, adapted.head) + var tree = typedArg(args.head, mode, typedMode, adapted.head) + if (hasPendingMacroExpansions) tree = macroExpandAll(this, tree) // formals may be empty, so don't call tail tree :: loop(args.tail, formals drop 1, adapted.tail) } @@ -2737,6 +2780,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def tryNamesDefaults: Tree = { val lencmp = compareLengths(args, formals) + def checkNotMacro() = { + if (fun.symbol != null && fun.symbol.filter(sym => sym != null && sym.isTermMacro && !sym.isErroneous) != NoSymbol) + duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun)) + } + if (mt.isErroneous) duplErrTree else if (inPatternMode(mode)) { // #2064 @@ -2755,8 +2803,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else if (isIdentity(argPos) && !isNamedApplyBlock(fun)) { // if there's no re-ordering, and fun is not transformed, no need to transform // more than an optimization, e.g. important in "synchronized { x = update-x }" + checkNotMacro() doTypedApply(tree, fun, namelessArgs, mode, pt) } else { + checkNotMacro() transformNamedApplication(Typer.this, mode, pt)( treeCopy.Apply(tree, fun, namelessArgs), argPos) } @@ -2764,6 +2814,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // defaults are needed. they are added to the argument list in named style as // calls to the default getters. Example: // foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a)) + checkNotMacro() val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x) if (fun1.isErroneous) duplErrTree else { @@ -2802,7 +2853,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (args exists isNamed) || // uses a named argument isNamedApplyBlock(fun)) { // fun was transformed to a named apply block => // integrate this application into the block - tryNamesDefaults + if (dyna.isApplyDynamicNamed(fun)) dyna.typedNamedApply(tree, fun, args, mode, pt) + else tryNamesDefaults } else { val tparams = context.extractUndetparams() if (tparams.isEmpty) { // all type params are defined @@ -3111,7 +3163,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (hasError) annotationError - else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(ann).setPos(ann.pos) + else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(Apply(typedFun, args)).setPos(ann.pos) } } else if (requireJava) { reportAnnotationError(NestedAnnotationError(ann, annType)) @@ -3151,7 +3203,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()).setOriginal(ann).setPos(t.pos) + AnnotationInfo(annType, args, List()).setOriginal(typedAnn).setPos(t.pos) case Block(stats, expr) => context.warning(t.pos, "Usage of named or default arguments transformed this annotation\n"+ @@ -3423,7 +3475,108 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case ErrorType => setError(treeCopy.TypeApply(tree, fun, args)) case _ => - TypedApplyDoesNotTakeTpeParametersError(tree, fun) + fun match { + // drop the application for an applyDynamic or selectDynamic call since it has been pushed down + case treeInfo.DynamicApplication(_, _) => fun + case _ => TypedApplyDoesNotTakeTpeParametersError(tree, fun) + } + } + + object dyna { + import treeInfo.{isApplyDynamicName, DynamicUpdate, DynamicApplicationNamed} + + def acceptsApplyDynamic(tp: Type) = tp.typeSymbol isNonBottomSubClass DynamicClass + + /** Returns `Some(t)` if `name` can be selected dynamically on `qual`, `None` if not. + * `t` specifies the type to be passed to the applyDynamic/selectDynamic call (unless it is NoType) + * NOTE: currently either returns None or Some(NoType) (scala-virtualized extends this to Some(t) for selections on staged Structs) + */ + def acceptsApplyDynamicWithType(qual: Tree, name: Name): Option[Type] = + // don't selectDynamic selectDynamic, do select dynamic at unknown type, + // in scala-virtualized, we may return a Some(tp) where tp ne NoType + if (!isApplyDynamicName(name) && acceptsApplyDynamic(qual.tpe.widen)) Some(NoType) + else None + + def isDynamicallyUpdatable(tree: Tree) = tree match { + case DynamicUpdate(qual, name) => + // if the qualifier is a Dynamic, that's all we need to know + acceptsApplyDynamic(qual.tpe) + case _ => false + } + + def isApplyDynamicNamed(fun: Tree): Boolean = fun match { + case DynamicApplicationNamed(qual, _) if acceptsApplyDynamic(qual.tpe.widen) => true + case _ => false + // look deeper? + // val methPart = treeInfo.methPart(fun) + // println("methPart of "+ fun +" is "+ methPart) + // if (methPart ne fun) isApplyDynamicNamed(methPart) + // else false + } + + def typedNamedApply(orig: Tree, fun: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + def argToBinding(arg: Tree): Tree = arg match { + case AssignOrNamedArg(Ident(name), rhs) => gen.mkTuple(List(CODE.LIT(name.toString), rhs)) + case _ => gen.mkTuple(List(CODE.LIT(""), arg)) + } + typed(treeCopy.Apply(orig, fun, args map argToBinding), mode, pt) + } + + /** Translate selection that does not typecheck according to the normal rules into a selectDynamic/applyDynamic. + * + * foo.method("blah") ~~> foo.applyDynamic("method")("blah") + * foo.method(x = "blah") ~~> foo.applyDynamicNamed("method")(("x", "blah")) + * foo.varia = 10 ~~> foo.updateDynamic("varia")(10) + * foo.field ~~> foo.selectDynamic("field") + * foo.arr(10) = 13 ~~> foo.selectDynamic("arr").update(10, 13) + * + * what if we want foo.field == foo.selectDynamic("field") == 1, but `foo.field = 10` == `foo.selectDynamic("field").update(10)` == () + * what would the signature for selectDynamic be? (hint: it needs to depend on whether an update call is coming or not) + * + * need to distinguish selectDynamic and applyDynamic somehow: the former must return the selected value, the latter must accept an apply or an update + * - could have only selectDynamic and pass it a boolean whether more is to come, + * so that it can either return the bare value or something that can handle the apply/update + * HOWEVER that makes it hard to return unrelated values for the two cases + * --> selectDynamic's return type is now dependent on the boolean flag whether more is to come + * - simplest solution: have two method calls + * + */ + def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = + acceptsApplyDynamicWithType(qual, name) map { tp => + // tp eq NoType => can call xxxDynamic, but not passing any type args (unless specified explicitly by the user) + // in scala-virtualized, when not NoType, tp is passed as type argument (for selection on a staged Struct) + + // strip off type application -- we're not doing much with outer, so don't bother preserving cxTree's attributes etc + val (outer, explicitTargs) = cxTree match { + case TypeApply(fun, targs) => (fun, targs) + case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs) + case t => (t, Nil) + } + + @inline def hasNamedArg(as: List[Tree]) = as collectFirst {case AssignOrNamedArg(lhs, rhs) =>} nonEmpty + + // note: context.tree includes at most one Apply node + // thus, we can't use it to detect we're going to receive named args in expressions such as: + // qual.sel(a)(a2, arg2 = "a2") + val oper = outer match { + case Apply(`tree`, as) => + val oper = + if (hasNamedArg(as)) nme.applyDynamicNamed + else nme.applyDynamic + // not supported: foo.bar(a1,..., an: _*) + if (treeInfo.isWildcardStarArgList(as)) { + DynamicVarArgUnsupported(tree, oper) + return Some(setError(tree)) + } else oper + case Assign(`tree`, _) => nme.updateDynamic + case _ => nme.selectDynamic + } + + val dynSel = Select(qual, oper) + val tappSel = if (explicitTargs nonEmpty) TypeApply(dynSel, explicitTargs) else dynSel + + atPos(qual.pos)(Apply(tappSel, List(Literal(Constant(name.decode))))) + } } @inline final def deindentTyping() = context.typingIndentLevel -= 2 @@ -3437,7 +3590,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { println(s) } - protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = { + def typed1(tree: Tree, mode: Int, pt: Type): Tree = { def isPatternMode = inPatternMode(mode) //Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")") @@ -3451,10 +3604,26 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def typedAnnotated(ann: Tree, arg1: Tree): Tree = { - def mkTypeTree(tpe: Type) = TypeTree(tpe) setOriginal tree setPos tree.pos.focus /** mode for typing the annotation itself */ val annotMode = mode & ~TYPEmode | EXPRmode + def resultingTypeTree(tpe: Type) = { + // we need symbol-ful originals for reification + // hence we go the extra mile to hand-craft tis guy + val original = + if (arg1.isType) + (tree, arg1) match { + case (Annotated(annot, arg), tt @ TypeTree()) => Annotated(annot, tt.original) + // this clause is needed to correctly compile stuff like "new C @D" or "@(inline @getter)" + case (Annotated(annot, arg), _) => Annotated(annot, arg1) + case _ => throw new Error("unexpected trees in typedAnnotated: tree = %s, arg1 = %s".format(showRaw(tree), showRaw(arg1))) + } + else + tree + original setType ann.tpe + TypeTree(tpe) setOriginal original setPos tree.pos.focus + } + if (arg1.isType) { // make sure the annotation is only typechecked once if (ann.tpe == null) { @@ -3497,11 +3666,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { arg1 // simply drop erroneous annotations else { ann.tpe = atype - mkTypeTree(atype) + resultingTypeTree(atype) } } else { // the annotation was typechecked before - mkTypeTree(ann.tpe) + resultingTypeTree(ann.tpe) } } else { @@ -3510,7 +3679,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ann.tpe = arg1.tpe.withAnnotation(annotInfo) } val atype = ann.tpe - Typed(arg1, mkTypeTree(atype)) setPos tree.pos setType atype + Typed(arg1, resultingTypeTree(atype)) setPos tree.pos setType atype } } @@ -3580,10 +3749,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case _ => } } +// if (varsym.isVariable || +// // setter-rewrite has been done above, so rule out methods here, but, wait a minute, why are we assigning to non-variables after erasure?! +// (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) { if (varsym.isVariable || varsym.isValue && phase.erasedTypes) { val rhs1 = typed(rhs, EXPRmode | BYVALmode, lhs1.tpe) treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitClass.tpe } + else if(dyna.isDynamicallyUpdatable(lhs1)) { + val rhs1 = typed(rhs, EXPRmode | BYVALmode, WildcardType) + typed1(Apply(lhs1, List(rhs1)), mode, pt) + } else fail() } @@ -3598,12 +3774,16 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { lazy val thenTp = packedType(thenp1, context.owner) lazy val elseTp = packedType(elsep1, context.owner) + // println("typedIf: "+(thenp1.tpe, elsep1.tpe, ptOrLub(List(thenp1.tpe, elsep1.tpe)),"\n", thenTp, elseTp, thenTp =:= elseTp)) val (owntype, needAdapt) = // in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway // in the special (though common) case where the types are equal, it pays to pack before comparing // especially virtpatmat needs more aggressive unification of skolemized types // this breaks src/library/scala/collection/immutable/TrieIterator.scala - if (opt.virtPatmat && !isPastTyper && thenTp =:= elseTp) (thenp1.tpe, false) // use unpacked type + if ( opt.virtPatmat && !isPastTyper + && thenp1.tpe.annotations.isEmpty && elsep1.tpe.annotations.isEmpty // annotated types need to be lubbed regardless (at least, continations break if you by pass them like this) + && thenTp =:= elseTp + ) (thenp1.tpe, false) // use unpacked type // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala else ptOrLub(List(thenp1.tpe, elsep1.tpe)) @@ -3615,13 +3795,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def typedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = { - if (opt.virtPatmat && !isPastTyper) { - if (selector ne EmptyTree) { - val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = prepareTranslateMatch(selector, cases, mode, pt) - typed(translateMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt) - } else typedMatchAnonFun(tree, cases, mode, pt) - } else if (selector == EmptyTree) { + // translation only happens when (selector != EmptyTree) && !isPastTyper && opt.virtPatmat + def typedTranslatedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = { + if (selector == EmptyTree) { val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1 val params = for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { @@ -3632,7 +3808,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val selector1 = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) } val body = treeCopy.Match(tree, selector1, cases) typed1(atPos(tree.pos) { Function(params, body) }, mode, pt) - } else { + } else if (!((phase.id < currentRun.uncurryPhase.id) && opt.virtPatmat)) { val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) @@ -3640,6 +3816,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { cases1 = cases1 map (adaptCase(_, mode, owntype)) } treeCopy.Match(tree, selector1, cases1) setType owntype + } else { + val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = typedMatch(selector, cases, mode, pt) + typed(translatedMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt) } } @@ -3676,6 +3855,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (checkStablePrefixClassType(tpt0)) if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { context.undetparams = cloneSymbols(tpt0.symbol.typeParams) + notifyUndetparamsAdded(context.undetparams) TypeTree().setOriginal(tpt0) .setType(appliedType(tpt0.tpe, context.undetparams map (_.tpeHK))) // @PP: tpeHK! #3343, #4018, #4347. } else tpt0 @@ -4030,7 +4210,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } else { member(qual, name) } - if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & EXPRmode) != 0) { + + // symbol not found? --> try to convert implicitly to a type that does have the required member + // added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an xml member to StringContext, which in turn has an unapply[Seq] method) + if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & (EXPRmode | PATTERNmode)) != 0) { val qual1 = if (member(qual, name) != NoSymbol) qual else adaptToMemberWithArgs(tree, qual, name, mode, true, true) @@ -4038,6 +4221,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (qual1 ne qual) return typed(treeCopy.Select(tree, qual1, name), mode, pt) } + if (!reallyExists(sym)) { if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } @@ -4045,16 +4229,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } // try to expand according to Dynamic rules. - - if (settings.Xexperimental.value && (qual.tpe.widen.typeSymbol isNonBottomSubClass DynamicClass)) { - var dynInvoke = Apply(Select(qual, nme.applyDynamic), List(Literal(Constant(name.decode)))) - context.tree match { - case Apply(tree1, args) if tree1 eq tree => - ; - case _ => - dynInvoke = Apply(dynInvoke, List()) - } - return typed1(util.trace("dynatype: ")(dynInvoke), mode, pt) + dyna.mkInvoke(context.tree, tree, qual, name) match { + case Some(invocation) => + return typed1(invocation, mode, pt) + case _ => } if (settings.debug.value) { @@ -4113,7 +4291,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 + case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs.nonEmpty => // TODO: somehow the new qual is not checked in refchecks treeCopy.SelectFromTypeTree( result, (TypeTreeWithDeferredRefCheck(){ () => val tp = qual.tpe; val sym = tp.typeSymbolDirect @@ -4504,7 +4682,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { typedIf(cond, thenp, elsep) case tree @ Match(selector, cases) => - typedMatch(tree, selector, cases) + typedTranslatedMatch(tree, selector, cases) case Return(expr) => typedReturn(expr) @@ -4520,7 +4698,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { catches1 = catches1 map (adaptCase(_, mode, owntype)) } - if(!isPastTyper && opt.virtPatmat) { + if((phase.id < currentRun.uncurryPhase.id) && opt.virtPatmat) { catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos) } @@ -4534,7 +4712,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { typedNew(tpt) case Typed(expr, Function(List(), EmptyTree)) => - typedEta(checkDead(typed1(expr, mode, pt))) + // find out whether the programmer is trying to eta-expand a macro def + // to do that we need to typecheck the tree first (we need a symbol of the eta-expandee) + // that typecheck must not trigger macro expansions, so we explicitly prohibit them + // Q: "but, " - you may ask - ", `typed1` doesn't call adapt, which does macro expansion, so why explicit check?" + // A: solely for robustness reasons. this mechanism might change in the future, which might break unprotected code + val expr1 = context.withMacrosDisabled(typed1(expr, mode, pt)) + expr1 match { + case macroDef if macroDef.symbol != null && macroDef.symbol.isTermMacro && !macroDef.symbol.isErroneous => + MacroEtaError(expr1) + case _ => + typedEta(checkDead(expr1)) + } case Typed(expr0, tpt @ Ident(tpnme.WILDCARD_STAR)) => val expr = typed(expr0, onlyStickyModes(mode), WildcardType) @@ -4608,18 +4797,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { tpt.tpe.typeSymbol == ArrayClass && args.length == 1 && erasure.GenericArray.unapply(tpt.tpe).isDefined) => // !!! todo simplify by using extractor - // convert new Array[T](len) to evidence[ClassManifest[T]].newArray(len) - // convert new Array^N[T](len) for N > 1 to evidence[ClassManifest[T]].newArrayN(len) - val Some((level, manifType)) = erasure.GenericArray.unapply(tpt.tpe) - if (level > MaxArrayDims) - MultiDimensionalArrayError(tree) - else { + // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len) + // convert new Array^N[T](len) for N > 1 to evidence[ClassTag[Array[...Array[T]...]]].newArray(len), where Array HK gets applied (N-1) times + // [Eugene] no more MaxArrayDims. ClassTags are flexible enough to allow creation of arrays of arbitrary dimensionality (w.r.t JVM restrictions) + val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe) + val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.asType, List(tpe))).last val newArrayApp = atPos(tree.pos) { - val manif = getManifestTree(tree, manifType, false) - new ApplyToImplicitArgs(Select(manif, if (level == 1) "newArray" else "newArray"+level), args) + val tag = resolveClassTag(tree, tagType) + if (tag.isEmpty) MissingClassTagError(tree, tagType) + else new ApplyToImplicitArgs(Select(tag, nme.newArray), args) } typed(newArrayApp, mode, pt) - } case tree1 => tree1 } @@ -4666,6 +4854,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else typedSelect(qual1, name) + if (tree.isInstanceOf[PostfixSelect]) + checkFeature(tree.pos, PostfixOpsFeature, name.decode) + if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember) + checkFeature(tree1.pos, ReflectiveCallsFeature, tree1.symbol.toString) + if (qual1.symbol == RootPackage) treeCopy.Ident(tree1, name) else tree1 @@ -4679,7 +4872,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case ReferenceToBoxed(idt @ Ident(_)) => val id1 = typed1(idt, mode, pt) match { case id: Ident => id } - treeCopy.ReferenceToBoxed(tree, id1) setType AnyRefClass.tpe + // [Eugene] am I doing it right? + val erasedTypes = phaseId(currentPeriod) >= currentRun.erasurePhase.id + val tpe = capturedVariableType(idt.symbol, erasedTypes = erasedTypes) + treeCopy.ReferenceToBoxed(tree, id1) setType tpe case Literal(value) => tree setType ( @@ -4708,9 +4904,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe) case etpt @ ExistentialTypeTree(_, _) => - typerWithLocalContext(context.makeNewScope(tree, context.owner)){ + val tree1 = typerWithLocalContext(context.makeNewScope(tree, context.owner)){ _.typedExistentialTypeTree(etpt, mode) } + checkExistentialsFeature(tree1.pos, tree1.tpe, "the existential type") + tree1 case dc@TypeTreeWithDeferredRefCheck() => dc // TODO: should we re-type the wrapped tree? then we need to change TypeTreeWithDeferredRefCheck's representation to include the wrapped tree explicitly (instead of in its closure) case tpt @ TypeTree() => @@ -4760,6 +4958,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ptLine("typing %s: pt = %s".format(ptTree(tree), pt), "undetparams" -> context.undetparams, "implicitsEnabled" -> context.implicitsEnabled, + "enrichmentEnabled" -> context.enrichmentEnabled, + "mode" -> modeString(mode), "silent" -> context.bufferErrors, "context.owner" -> context.owner ) @@ -4863,7 +5063,22 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // We disable implicits because otherwise some constructs will // type check which should not. The pattern matcher does not // perform implicit conversions in an attempt to consummate a match. - context.withImplicitsDisabled(typed(tree, PATTERNmode, pt)) + + // on the one hand, + // "abc" match { case Seq('a', 'b', 'c') => true } + // should be ruled out statically, otherwise this is a runtime + // error both because there is an implicit from String to Seq + // (even though such implicits are not used by the matcher) and + // because the typer is fine with concluding that "abc" might + // be of type "String with Seq[T]" and thus eligible for a call + // to unapplySeq. + + // on the other hand, we want to be able to use implicits to add members retro-actively (e.g., add xml to StringContext) + + // as a compromise, context.enrichmentEnabled tells adaptToMember to go ahead and enrich, + // but arbitrary conversions (in adapt) are disabled + // TODO: can we achieve the pattern matching bit of the string interpolation SIP without this? + context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt)) } /** Types a (fully parameterized) type tree */ @@ -4916,9 +5131,40 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedTypeConstructor(tree: Tree): Tree = typedTypeConstructor(tree, NOmode) def computeType(tree: Tree, pt: Type): Type = { + // macros employ different logic of `computeType` + assert(!context.owner.isTermMacro, context.owner) val tree1 = typed(tree, pt) transformed(tree) = tree1 - packedType(tree1, context.owner) + val tpe = packedType(tree1, context.owner) + checkExistentialsFeature(tree.pos, tpe, "inferred existential type") + tpe + } + + def computeMacroDefType(tree: Tree, pt: Type): Type = { + assert(context.owner.isTermMacro, context.owner) + assert(tree.symbol.isTermMacro, tree.symbol) + assert(tree.isInstanceOf[DefDef], tree.getClass) + val ddef = tree.asInstanceOf[DefDef] + + val tree1 = + if (transformed contains ddef.rhs) { + // macro defs are typechecked in `methodSig` (by calling this method) in order to establish their link to macro implementation asap + // if a macro def doesn't have explicitly specified return type, this method will be called again by `assignTypeToTree` + // here we guard against this case + transformed(ddef.rhs) + } else { + val tree1 = typedMacroBody(this, ddef) + transformed(ddef.rhs) = tree1 + tree1 + } + + val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous)) + if (isMacroBodyOkay) computeMacroDefTypeFromMacroImpl(ddef, tree.symbol, tree1.symbol) else AnyClass.tpe + } + + def transformedOr(tree: Tree, op: => Tree): Tree = transformed.get(tree) match { + case Some(tree1) => transformed -= tree; tree1 + case None => op } def transformedOrTyped(tree: Tree, mode: Int, pt: Type): Tree = transformed.get(tree) match { @@ -4926,21 +5172,36 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case None => typed(tree, mode, pt) } - def findManifest(tp: Type, full: Boolean) = beforeTyper { + // `tree` is only necessary here for its position + // but that's invaluable for error reporting, so I decided to include it into this method's contract + // before passing EmptyTree, please, consider passing something meaningful first + def resolveClassTag(tree: Tree, tp: Type): Tree = beforeTyper { inferImplicit( EmptyTree, - appliedType((if (full) FullManifestClass else PartialManifestClass).typeConstructor, List(tp)), - true, false, context) + appliedType(ClassTagClass.typeConstructor, List(tp)), + /*reportAmbiguous =*/ true, + /*isView =*/ false, + /*context =*/ context, + /*saveAmbiguousDivergent =*/ true, + /*pos =*/ tree.pos + ).tree } - def getManifestTree(tree: Tree, tp: Type, full: Boolean): Tree = { - val manifestOpt = findManifest(tp, full) - if (manifestOpt.tree.isEmpty) { - MissingManifestError(tree, full, tp) - } else { - manifestOpt.tree - } + // `tree` is only necessary here for its position + // but that's invaluable for error reporting, so I decided to include it into this method's contract + // before passing EmptyTree, please, consider passing something meaningful first + def resolveTypeTag(tree: Tree, pre: Type, tp: Type, full: Boolean): Tree = beforeTyper { + inferImplicit( + EmptyTree, + appliedType(singleType(pre, pre member (if (full) ConcreteTypeTagClass else TypeTagClass).name), List(tp)), + /*reportAmbiguous =*/ true, + /*isView =*/ false, + /*context =*/ context, + /*saveAmbiguousDivergent =*/ true, + /*pos =*/ tree.pos + ).tree } + /* def convertToTypeTree(tree: Tree): Tree = tree match { case TypeTree() => tree diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 4f5b6868ae..1ebcea4a07 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -112,8 +112,8 @@ trait Unapplies extends ast.TreeDSL private def toIdent(x: DefTree) = Ident(x.name) setPos x.pos.focus - private def classType(cdef: ClassDef, tparams: List[TypeDef]): Tree = { - val tycon = REF(cdef.symbol) + private def classType(cdef: ClassDef, tparams: List[TypeDef], symbolic: Boolean = true): Tree = { + val tycon = if (symbolic) REF(cdef.symbol) else Ident(cdef.name) if (tparams.isEmpty) tycon else AppliedTypeTree(tycon, tparams map toIdent) } @@ -166,15 +166,20 @@ trait Unapplies extends ast.TreeDSL /** The apply method corresponding to a case class */ - def caseModuleApplyMeth(cdef: ClassDef): DefDef = { + def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef, symbolic: Boolean): DefDef = { val tparams = cdef.tparams map copyUntypedInvariant val cparamss = constrParamss(cdef) + def classtpe = classType(cdef, tparams, symbolic) atPos(cdef.pos.focus)( - DefDef(caseMods, nme.apply, tparams, cparamss, classType(cdef, tparams), - New(classType(cdef, tparams), mmap(cparamss)(gen.paramToArg))) + DefDef(mods, name, tparams, cparamss, classtpe, + New(classtpe, mmap(cparamss)(gen.paramToArg))) ) } + /** The apply method corresponding to a case class + */ + def caseModuleApplyMeth(cdef: ClassDef): DefDef = factoryMeth(caseMods, nme.apply, cdef, symbolic = true) + /** The unapply method corresponding to a case class */ def caseModuleUnapplyMeth(cdef: ClassDef): DefDef = { @@ -195,7 +200,7 @@ trait Unapplies extends ast.TreeDSL def caseClassCopyMeth(cdef: ClassDef): Option[DefDef] = { def isDisallowed(vd: ValDef) = isRepeatedParamType(vd.tpt) || isByNameParamType(vd.tpt) val cparamss = constrParamss(cdef) - val flat = cparamss flatten + val flat = cparamss.flatten if (cdef.symbol.hasAbstractFlag || (flat exists isDisallowed)) None else { diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index ce10ee34a2..5dd9ce0e02 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -27,9 +27,9 @@ object ClassPath { def scalaCompiler = locate[Global] def infoFor[T](value: T) = info(value.getClass) - def info[T](clazz: Class[T]) = new ClassAndJarInfo()(ClassManifest fromClass clazz) + def info[T](clazz: Class[T]) = new ClassAndJarInfo()(ClassManifest[T](clazz)) def info[T: ClassManifest] = new ClassAndJarInfo[T] - def locate[T: ClassManifest] = info[T] rootClasspath + def locate[T: ClassManifest] = info[T].rootClasspath def locateJar[T: ClassManifest] = info[T].rootPossibles find (x => isJarOrZip(x)) map (x => File(x)) def locateDir[T: ClassManifest] = info[T].rootPossibles find (_.isDirectory) map (_.toDirectory) @@ -373,7 +373,7 @@ extends ClassPath[T] { this(entries.toIndexedSeq, context) def name = entries.head.name - def asURLs = entries flatMap (_.asURLs) toList + def asURLs = (entries flatMap (_.asURLs)).toList lazy val sourcepaths: IndexedSeq[AbstractFile] = entries flatMap (_.sourcepaths) override def origin = Some(entries map (x => x.origin getOrElse x.name) mkString ("Merged(", ", ", ")")) diff --git a/src/compiler/scala/tools/nsc/util/CommandLineParser.scala b/src/compiler/scala/tools/nsc/util/CommandLineParser.scala index 00fe49d36a..78bfd5e908 100644 --- a/src/compiler/scala/tools/nsc/util/CommandLineParser.scala +++ b/src/compiler/scala/tools/nsc/util/CommandLineParser.scala @@ -20,11 +20,10 @@ import scala.collection.mutable.ListBuffer */ trait ParserUtil extends Parsers { - class ParserPlus[+T](underlying: Parser[T]) { + protected implicit class ParserPlus[+T](underlying: Parser[T]) { def !~>[U](p: => Parser[U]): Parser[U] = (underlying ~! p) ^^ { case a~b => b } def <~![U](p: => Parser[U]): Parser[T] = (underlying ~! p) ^^ { case a~b => a } } - protected implicit def parser2parserPlus[T](p: Parser[T]): ParserPlus[T] = new ParserPlus(p) } case class CommandLine( diff --git a/src/compiler/scala/tools/nsc/util/Exceptional.scala b/src/compiler/scala/tools/nsc/util/Exceptional.scala index 667b7d15a6..7452aa1b67 100644 --- a/src/compiler/scala/tools/nsc/util/Exceptional.scala +++ b/src/compiler/scala/tools/nsc/util/Exceptional.scala @@ -5,6 +5,7 @@ import java.util.concurrent.ExecutionException import java.lang.reflect.{ InvocationTargetException, UndeclaredThrowableException } import io.{ Sources, Fileish } import scala.tools.util.StringOps._ +import language.implicitConversions /** A simple throwable wrapper so it looks more like a parade of * glittering frame-shaped beauties than the other thing. diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index bc74717366..573f7bc7b2 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -33,62 +33,24 @@ object Position { } } -/** - * A tree does not directly store a Position. It stores a TreeAnnotation, which /typically/ is a Position. - * - * A TreeAnnotion may encompass more than just a Position, though, depending on the exact subclass of TreeAnnotation. - */ -trait TreeAnnotation { - def pos: Position -} +trait Position extends scala.reflect.api.Position with scala.reflect.api.Attachment { + /** Exposes itself as payload of Attachment */ + // necessary for conformance with Attachment + def pos: Position = this + /** A bit weird method that is necessary to safely update positions without destroying custom attachments */ + // necessary for conformance with Attachment + def withPos(pos: scala.reflect.api.Position) = pos -/** The Position class and its subclasses represent positions of ASTs and symbols. - * Except for NoPosition and FakePos, every position refers to a SourceFile - * and to an offset in the sourcefile (its `point`). For batch compilation, - * that's all. For interactive IDE's there are also RangePositions - * and TransparentPositions. A RangePosition indicates a start and an end - * in addition to its point. TransparentPositions are a subclass of RangePositions. - * Range positions that are not transparent are called opaque. - * Trees with RangePositions need to satisfy the following invariants. - * - * INV1: A tree with an offset position never contains a child - * with a range position - * INV2: If the child of a tree with a range position also has a range position, - * then the child's range is contained in the parent's range. - * INV3: Opaque range positions of children of the same node are non-overlapping - * (this means their overlap is at most a single point). - * - * The following tests are useful on positions: - * - * pos.isDefined true if position is not a NoPosition nor a FakePosition - * pos.isRange true if position is a range - * pos.isOpaqueRange true if position is an opaque range - * - * The following accessor methods are provided: - * - * pos.source The source file of the position, which must be defined - * pos.point The offset of the position's point, which must be defined - * pos.start The start of the position, which must be a range - * pos.end The end of the position, which must be a range - * - * There are also convenience methods, such as - * - * pos.startOrPoint - * pos.endOrPoint - * pos.pointOrElse(default) - * - * These are less strict about the kind of position on which they can be applied. - * - * The following conversion methods are often used: - * - * pos.focus converts a range position to an offset position, keeping its point; - * returns all other positions unchanged. - * pos.makeTransparent converts an opaque range position into a transparent one. - * returns all other positions unchanged. - */ -trait Position extends TreeAnnotation { - def pos: Position = this + /** Java file corresponding to the source file of this position. + */ + // necessary for conformance with scala.reflect.api.Position + def fileInfo: java.io.File = source.file.file + + /** Contents of the source file that contains this position. + */ + // necessary for conformance with scala.reflect.api.Position + def fileContent: Array[Char] = source.content /** An optional value containing the source file referred to by this position, or * None if not defined. @@ -134,74 +96,74 @@ trait Position extends TreeAnnotation { def offset: Option[Int] = if (isDefined) Some(point) else None /** The same position with a different start value (if a range) */ - def withStart(off: Int) = this + def withStart(off: Int): Position = this /** The same position with a different end value (if a range) */ - def withEnd(off: Int) = this + def withEnd(off: Int): Position = this /** The same position with a different point value (if a range or offset) */ - def withPoint(off: Int) = this + def withPoint(off: Int): Position = this /** The same position with a different source value, and its values shifted by given offset */ - def withSource(source: SourceFile, shift: Int) = this + def withSource(source: SourceFile, shift: Int): Position = this /** If this is a range, the union with the other range, with the point of this position. * Otherwise, this position */ - def union(pos: Position) = this + def union(pos: scala.reflect.api.Position): Position = this /** If this is a range position, the offset position of its start. * Otherwise the position itself */ - def focusStart = this + def focusStart: Position = this /** If this is a range position, the offset position of its point. * Otherwise the position itself */ - def focus = this + def focus: Position = this /** If this is a range position, the offset position of its end. * Otherwise the position itself */ - def focusEnd = this + def focusEnd: Position = this /** Does this position include the given position `pos`. * This holds if `this` is a range position and its range [start..end] * is the same or covers the range of the given position, which may or may not be a range position. */ - def includes(pos: Position) = false + def includes(pos: scala.reflect.api.Position): Boolean = false /** Does this position properly include the given position `pos` ("properly" meaning their * ranges are not the same)? */ - def properlyIncludes(pos: Position) = + def properlyIncludes(pos: scala.reflect.api.Position): Boolean = includes(pos) && (start < pos.startOrPoint || pos.endOrPoint < end) /** Does this position precede that position? * This holds if both positions are defined and the end point of this position * is not larger than the start point of the given position. */ - def precedes(pos: Position) = + def precedes(pos: scala.reflect.api.Position): Boolean = isDefined && pos.isDefined && endOrPoint <= pos.startOrPoint /** Does this position properly precede the given position `pos` ("properly" meaning their ranges * do not share a common point). */ - def properlyPrecedes(pos: Position) = + def properlyPrecedes(pos: scala.reflect.api.Position): Boolean = isDefined && pos.isDefined && endOrPoint < pos.startOrPoint /** Does this position overlap with that position? * This holds if both positions are ranges and there is an interval of * non-zero length that is shared by both position ranges. */ - def overlaps(pos: Position) = + def overlaps(pos: scala.reflect.api.Position): Boolean = isRange && pos.isRange && ((pos.start < end && start < pos.end) || (start < pos.end && pos.start < end)) /** Does this position cover the same range as that position? * Holds only if both position are ranges */ - def sameRange(pos: Position) = + def sameRange(pos: scala.reflect.api.Position): Boolean = isRange && pos.isRange && start == pos.start && end == pos.end def line: Int = throw new UnsupportedOperationException("Position.line") @@ -219,11 +181,11 @@ trait Position extends TreeAnnotation { * file. If the SourceFile is a normal SourceFile, simply * return this. */ - def inUltimateSource(source : SourceFile) = + def inUltimateSource(source : SourceFile): Position = if (source == null) this else source.positionInUltimateSource(this) - def dbgString = toString - def safeLine = try line catch { case _: UnsupportedOperationException => -1 } + def dbgString: String = toString + def safeLine: Int = try line catch { case _: UnsupportedOperationException => -1 } def show: String = "["+toString+"]" } @@ -254,8 +216,10 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e col + 1 } - override def union(pos: Position) = - if (pos.isRange) pos else this + override def union(pos: scala.reflect.api.Position) = + // [Eugene] how do I get rid of this cast? + // I could introduce a "type PositionType <: scala.reflect.api.Position", but that's also ugly + if (pos.isRange) pos.asInstanceOf[Position] else this override def equals(that : Any) = that match { case that : OffsetPosition => point == that.point && source.file == that.source.file @@ -265,7 +229,7 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e override def toString = { val pointmsg = if (point > source.length) "out-of-bounds-" else "offset=" - "source-%s,line-%s,%s%s".format(source.path, line, pointmsg, point) + "source-%s,line-%s,%s%s".format(source.file.canonicalPath, line, pointmsg, point) } override def show = "["+point+"]" } @@ -289,8 +253,8 @@ extends OffsetPosition(source, point) { } override def focusEnd = new OffsetPosition(source, end) override def makeTransparent = new TransparentPosition(source, start, point, end) - override def includes(pos: Position) = pos.isDefined && start <= pos.startOrPoint && pos.endOrPoint <= end - override def union(pos: Position) = + override def includes(pos: scala.reflect.api.Position) = pos.isDefined && start <= pos.startOrPoint && pos.endOrPoint <= end + override def union(pos: scala.reflect.api.Position): Position = if (pos.isRange) new RangePosition(source, start min pos.start, point, end max pos.end) else this override def toSingleLine: Position = source match { @@ -301,7 +265,7 @@ extends OffsetPosition(source, point) { case _ => this } - override def toString = "RangePosition("+source+", "+start+", "+point+", "+end+")" + override def toString = "RangePosition("+source.file.canonicalPath+", "+start+", "+point+", "+end+")" override def show = "["+start+":"+end+"]" private var focusCache: Position = NoPosition } @@ -311,10 +275,4 @@ class TransparentPosition(source: SourceFile, start: Int, point: Int, end: Int) override def isTransparent = true override def makeTransparent = this override def show = "<"+start+":"+end+">" -} - - - - - - +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala index fda713c5c6..700fe0c1a6 100644 --- a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala +++ b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala @@ -14,6 +14,8 @@ import java.net.URL import scala.reflect.ReflectionUtils.unwrapHandler import ScalaClassLoader._ import scala.util.control.Exception.{ catching } +import language.implicitConversions + // import Exceptional.unwrap trait HasClassPath { diff --git a/src/compiler/scala/tools/nsc/util/Statistics.scala b/src/compiler/scala/tools/nsc/util/Statistics.scala index d1cdd30dd8..61c7695911 100644 --- a/src/compiler/scala/tools/nsc/util/Statistics.scala +++ b/src/compiler/scala/tools/nsc/util/Statistics.scala @@ -57,6 +57,9 @@ class Statistics extends scala.reflect.internal.util.Statistics { val counter2: SubCounter = new SubCounter(subtypeCount) val timer1: Timer = new Timer val timer2: Timer = new Timer + + val macroExpandCount = new Counter + val macroExpandNanos = new Timer } object Statistics extends Statistics @@ -125,34 +128,36 @@ abstract class StatisticsInfo { inform("ms type-flow-analysis: " + analysis.timer.millis) if (phase.name == "typer") { - inform("time spent typechecking : "+showRelTyper(typerNanos)) - inform("time classfilereading : "+showRelTyper(classReadNanos)) - inform("time spent in implicits : "+showRelTyper(implicitNanos)) - inform(" successful in scope : "+showRelTyper(inscopeSucceedNanos)) - inform(" failed in scope : "+showRelTyper(inscopeFailNanos)) - inform(" successful of type : "+showRelTyper(oftypeSucceedNanos)) - inform(" failed of type : "+showRelTyper(oftypeFailNanos)) - inform(" assembling parts : "+showRelTyper(subtypeETNanos)) - inform(" matchesPT : "+showRelTyper(matchesPtNanos)) - inform("implicit cache hits : "+showRelative(implicitCacheHits.value + implicitCacheMisses.value)(implicitCacheHits.value)) - inform("time spent in failed : "+showRelTyper(failedSilentNanos)) - inform(" failed apply : "+showRelTyper(failedApplyNanos)) - inform(" failed op= : "+showRelTyper(failedOpEqNanos)) - inform("time spent ref scanning : "+showRelTyper(isReferencedNanos)) - inform("micros by tree node : "+showCounts(microsByType)) - inform("#visits by tree node : "+showCounts(visitsByType)) + inform("time spent typechecking : " + showRelTyper(typerNanos)) + inform("time classfilereading : " + showRelTyper(classReadNanos)) + inform("time spent in implicits : " + showRelTyper(implicitNanos)) + inform(" successful in scope : " + showRelTyper(inscopeSucceedNanos)) + inform(" failed in scope : " + showRelTyper(inscopeFailNanos)) + inform(" successful of type : " + showRelTyper(oftypeSucceedNanos)) + inform(" failed of type : " + showRelTyper(oftypeFailNanos)) + inform(" assembling parts : " + showRelTyper(subtypeETNanos)) + inform(" matchesPT : " + showRelTyper(matchesPtNanos)) + inform("implicit cache hits : " + showRelative(implicitCacheHits.value + implicitCacheMisses.value)(implicitCacheHits.value)) + inform("time spent in failed : " + showRelTyper(failedSilentNanos)) + inform(" failed apply : " + showRelTyper(failedApplyNanos)) + inform(" failed op= : " + showRelTyper(failedOpEqNanos)) + inform("time spent ref scanning : " + showRelTyper(isReferencedNanos)) + inform("micros by tree node : " + showCounts(microsByType)) + inform("#visits by tree node : " + showCounts(visitsByType)) val average = new ClassCounts for (c <- microsByType.keysIterator) average(c) = microsByType(c)/visitsByType(c) - inform("avg micros by tree node : "+showCounts(average)) - inform("time spent in <:< : "+showRelTyper(subtypeNanos)) - inform("time spent in findmember : "+showRelTyper(findMemberNanos)) - inform("time spent in asSeenFrom : "+showRelTyper(asSeenFromNanos)) - inform("#implicit searches : " + implicitSearchCount) + inform("avg micros by tree node : " + showCounts(average)) + inform("time spent in <:< : " + showRelTyper(subtypeNanos)) + inform("time spent in findmember : " + showRelTyper(findMemberNanos)) + inform("time spent in asSeenFrom : " + showRelTyper(asSeenFromNanos)) + inform("#implicit searches : " + implicitSearchCount) inform("#tried, plausible, matching, typed, found implicits: "+triedImplicits+", "+plausiblyCompatibleImplicits+", "+matchingImplicits+", "+typedImplicits+", "+foundImplicits) - inform("#implicit improves tests : " + improvesCount) - inform("#implicit improves cached: " + improvesCachedCount) - inform("#implicit inscope hits : " + inscopeImplicitHits) - inform("#implicit oftype hits : " + oftypeImplicitHits) + inform("#implicit improves tests : " + improvesCount) + inform("#implicit improves cached : " + improvesCachedCount) + inform("#implicit inscope hits : " + inscopeImplicitHits) + inform("#implicit oftype hits : " + oftypeImplicitHits) + inform("#macro expansions : " + macroExpandCount) + inform("#time spent in macroExpand : " + showRelTyper(macroExpandNanos)) } if (ctr1 != null) inform("#ctr1 : " + ctr1) diff --git a/src/compiler/scala/tools/nsc/util/package.scala b/src/compiler/scala/tools/nsc/util/package.scala index 88e6c51e9f..1336cca3c5 100644 --- a/src/compiler/scala/tools/nsc/util/package.scala +++ b/src/compiler/scala/tools/nsc/util/package.scala @@ -9,6 +9,8 @@ import java.io.{ OutputStream, PrintStream, ByteArrayOutputStream, PrintWriter, package object util { + implicit def postfixOps = language.postfixOps // make all postfix ops in this package compile without warning + // forwarder for old code that builds against 2.9 and 2.10 val Chars = scala.reflect.internal.Chars diff --git a/src/compiler/scala/tools/reflect/package.scala b/src/compiler/scala/tools/reflect/package.scala index 8f5e099fbf..f5c836a4e9 100644 --- a/src/compiler/scala/tools/reflect/package.scala +++ b/src/compiler/scala/tools/reflect/package.scala @@ -27,7 +27,7 @@ package object reflect { } } - def zeroOfClass(clazz: Class[_]) = zeroOf(Manifest.classType(clazz)) + def zeroOfClass(clazz: Class[_]) = zeroOf(Manifest(ClassManifest(clazz).tpe)) def zeroOf[T](implicit m: Manifest[T]): AnyRef = { if (m == manifest[Boolean] || m == manifest[jl.Boolean]) false: jl.Boolean else if (m == manifest[Unit] || m == manifest[jl.Void] || m == manifest[scala.runtime.BoxedUnit]) scala.runtime.BoxedUnit.UNIT |