diff options
Diffstat (limited to 'src/compiler/scala')
297 files changed, 5394 insertions, 17151 deletions
diff --git a/src/compiler/scala/reflect/macros/runtime/Context.scala b/src/compiler/scala/reflect/macros/runtime/Context.scala index 8e8b0fcea1..76c684f6d7 100644 --- a/src/compiler/scala/reflect/macros/runtime/Context.scala +++ b/src/compiler/scala/reflect/macros/runtime/Context.scala @@ -14,6 +14,7 @@ abstract class Context extends scala.reflect.macros.Context with Parsers with Evals with ExprUtils + with Synthetics with Traces { val universe: Global diff --git a/src/compiler/scala/reflect/macros/runtime/Enclosures.scala b/src/compiler/scala/reflect/macros/runtime/Enclosures.scala index be5f2dbe83..8fe0b09700 100644 --- a/src/compiler/scala/reflect/macros/runtime/Enclosures.scala +++ b/src/compiler/scala/reflect/macros/runtime/Enclosures.scala @@ -1,23 +1,35 @@ package scala.reflect.macros package runtime +import scala.reflect.{ClassTag, classTag} + trait Enclosures { self: Context => import universe._ - import mirror._ - private def site = callsiteTyper.context - private def enclTrees = site.enclosingContextChain map (_.tree) - private def enclPoses = enclosingMacros map (_.macroApplication.pos) filterNot (_ eq NoPosition) + type MacroRole = analyzer.MacroRole + def APPLY_ROLE = analyzer.APPLY_ROLE + def macroRole: MacroRole + + private lazy val site = callsiteTyper.context + private lazy val enclTrees = site.enclosingContextChain map (_.tree) + private lazy val enclPoses = enclosingMacros map (_.macroApplication.pos) filterNot (_ eq NoPosition) + + private def lenientEnclosure[T <: Tree : ClassTag]: Tree = enclTrees collectFirst { case x: T => x } getOrElse EmptyTree + private def strictEnclosure[T <: Tree : ClassTag]: T = enclTrees collectFirst { case x: T => x } getOrElse (throw new EnclosureException(classTag[T].runtimeClass, enclTrees)) // vals are eager to simplify debugging // after all we wouldn't save that much time by making them lazy val macroApplication: Tree = expandee - val enclosingClass: Tree = enclTrees collectFirst { case x: ImplDef => x } getOrElse EmptyTree + def enclosingPackage: PackageDef = strictEnclosure[PackageDef] + val enclosingClass: Tree = lenientEnclosure[ImplDef] + def enclosingImpl: ImplDef = strictEnclosure[ImplDef] + def enclosingTemplate: Template = strictEnclosure[Template] val enclosingImplicits: List[(Type, Tree)] = site.openImplicits val enclosingMacros: List[Context] = this :: universe.analyzer.openMacros // include self - val enclosingMethod: Tree = site.enclMethod.tree + val enclosingMethod: Tree = lenientEnclosure[DefDef] + def enclosingDef: DefDef = strictEnclosure[DefDef] val enclosingPosition: Position = if (enclPoses.isEmpty) NoPosition else enclPoses.head.pos val enclosingUnit: CompilationUnit = universe.currentRun.currentUnit val enclosingRun: Run = universe.currentRun diff --git a/src/compiler/scala/reflect/macros/runtime/ExprUtils.scala b/src/compiler/scala/reflect/macros/runtime/ExprUtils.scala index 672699f00e..a719beed97 100644 --- a/src/compiler/scala/reflect/macros/runtime/ExprUtils.scala +++ b/src/compiler/scala/reflect/macros/runtime/ExprUtils.scala @@ -5,7 +5,6 @@ trait ExprUtils { self: Context => import universe._ - import mirror._ def literalNull = Expr[Null](Literal(Constant(null)))(TypeTag.Null) diff --git a/src/compiler/scala/reflect/macros/runtime/Names.scala b/src/compiler/scala/reflect/macros/runtime/Names.scala index ee9f3a56d3..635e8bcd45 100644 --- a/src/compiler/scala/reflect/macros/runtime/Names.scala +++ b/src/compiler/scala/reflect/macros/runtime/Names.scala @@ -7,11 +7,20 @@ trait Names { lazy val freshNameCreator = callsiteTyper.context.unit.fresh def fresh(): String = - freshNameCreator.newName() + freshName() def fresh(name: String): String = - freshNameCreator.newName(name) + freshName(name) def fresh[NameType <: Name](name: NameType): NameType = + freshName[NameType](name) + + def freshName(): String = + freshNameCreator.newName() + + def freshName(name: String): String = + freshNameCreator.newName(name) + + def freshName[NameType <: Name](name: NameType): NameType = name.mapName(freshNameCreator.newName(_)).asInstanceOf[NameType] }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/runtime/Synthetics.scala b/src/compiler/scala/reflect/macros/runtime/Synthetics.scala new file mode 100644 index 0000000000..73f3ab8d20 --- /dev/null +++ b/src/compiler/scala/reflect/macros/runtime/Synthetics.scala @@ -0,0 +1,83 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + */ + +package scala.reflect.macros +package runtime + +import java.util.UUID._ +import scala.reflect.internal.Flags._ +import scala.reflect.internal.util.BatchSourceFile +import scala.reflect.io.VirtualFile + +trait Synthetics { + self: Context => + + import global._ + import mirror.wrapMissing + + // getClassIfDefined and getModuleIfDefined cannot be used here + // because they don't work for stuff declared in the empty package + // (as specified in SLS, code inside non-empty packages cannot see + // declarations from the empty package, so compiler internals + // default to ignoring contents of the empty package) + // to the contrast, staticModule and staticClass are designed + // to be a part of the reflection API and, therefore, they + // correctly resolve all names + private def topLevelSymbol(name: Name): Symbol = wrapMissing { + if (name.isTermName) mirror.staticModule(name.toString) + else mirror.staticClass(name.toString) + } + + def topLevelDef(name: Name): Tree = + enclosingRun.units.toList.map(_.body).flatMap { + // it's okay to check `stat.symbol` here, because currently macros expand strictly after namer + // which means that by the earliest time one can call this method all top-level definitions will have already been entered + case PackageDef(_, stats) => stats filter (stat => stat.symbol != NoSymbol && stat.symbol == topLevelSymbol(name)) + case _ => Nil // should never happen, but better be safe than sorry + }.headOption getOrElse EmptyTree + + def topLevelRef(name: Name): Tree = { + if (topLevelDef(name).nonEmpty) gen.mkUnattributedRef(name) + else EmptyTree + } + + // TODO: provide a way to specify a pretty name for debugging purposes + private def randomFileName() = ( + "macroSynthetic-" + randomUUID().toString.replace("-", "") + ".scala" + ) + + def introduceTopLevel[T: PackageSpec](packagePrototype: T, definition: universe.ImplDef): RefTree = + introduceTopLevel(packagePrototype, List(definition)).head + + def introduceTopLevel[T: PackageSpec](packagePrototype: T, definitions: universe.ImplDef*): List[RefTree] = + introduceTopLevel(packagePrototype, definitions.toList) + + private def introduceTopLevel[T: PackageSpec](packagePrototype: T, definitions: List[universe.ImplDef]): List[RefTree] = { + val code @ PackageDef(pid, _) = implicitly[PackageSpec[T]].mkPackageDef(packagePrototype, definitions) + val syntheticFileName = randomFileName() + // compatibility with SBT + // on the one hand, we need to specify some jfile here, otherwise sbt crashes with an NPE (SI-6870) + // on the other hand, we can't specify the obvious enclosingUnit, because then sbt somehow fails to run tests using type macros + // okay, now let's specify a guaranteedly non-existent file in an existing directory (so that we don't run into permission problems) + val relatedJfile = enclosingUnit.source.file.file + val fakeJfile = if (relatedJfile != null) new java.io.File(relatedJfile.getParent, syntheticFileName) else null + val virtualFile = new VirtualFile(syntheticFileName) { override def file = fakeJfile } + val sourceFile = new BatchSourceFile(virtualFile, code.toString) + val unit = new CompilationUnit(sourceFile) + unit.body = code + universe.currentRun.compileLate(unit) + definitions map (definition => Select(pid, definition.name)) + } + + protected def mkPackageDef(name: String, stats: List[Tree]) = gen.mkPackageDef(name, stats) + + protected def mkPackageDef(name: TermName, stats: List[Tree]) = gen.mkPackageDef(name.toString, stats) + + protected def mkPackageDef(tree: RefTree, stats: List[Tree]) = PackageDef(tree, stats) + + protected def mkPackageDef(sym: Symbol, stats: List[Tree]) = { + assert(sym hasFlag PACKAGE, s"expected a package or package class symbol, found: $sym") + gen.mkPackageDef(sym.fullName.toString, stats) + } +} diff --git a/src/compiler/scala/reflect/macros/runtime/Typers.scala b/src/compiler/scala/reflect/macros/runtime/Typers.scala index f9add91b9a..7e268247dd 100644 --- a/src/compiler/scala/reflect/macros/runtime/Typers.scala +++ b/src/compiler/scala/reflect/macros/runtime/Typers.scala @@ -1,6 +1,8 @@ package scala.reflect.macros package runtime +import scala.reflect.internal.Mode + trait Typers { self: Context => @@ -22,7 +24,7 @@ trait Typers { // 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, universe.analyzer.EXPRmode, pt)) match { + wrapper(callsiteTyper.silent(_.typed(tree, Mode.EXPRmode, pt)) match { case universe.analyzer.SilentResultValue(result) => macroLogVerbose(result) result @@ -62,4 +64,4 @@ trait Typers { def resetAllAttrs(tree: Tree): Tree = universe.resetAllAttrs(tree) def resetLocalAttrs(tree: Tree): Tree = universe.resetLocalAttrs(tree) -}
\ No newline at end of file +} diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 7c66d5b9eb..3a68794c97 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -7,7 +7,6 @@ trait Errors { self: Reifier => import global._ - import definitions._ def defaultErrorPosition = { val stack = currents collect { case t: Tree if t.pos != NoPosition => t.pos } @@ -22,11 +21,6 @@ trait Errors { throw new ReificationException(defaultErrorPosition, msg) } - def CannotReifySymbol(sym: Symbol) = { - val msg = "implementation restriction: cannot reify symbol %s (%s)".format(sym, sym.accurateKindString) - throw new ReificationException(defaultErrorPosition, msg) - } - def CannotReifyWeakType(details: Any) = { val msg = "cannot create a TypeTag" + details + ": use WeakTypeTag instead" throw new ReificationException(defaultErrorPosition, msg) diff --git a/src/compiler/scala/reflect/reify/Phases.scala b/src/compiler/scala/reflect/reify/Phases.scala index 1710cae2a5..d43532090c 100644 --- a/src/compiler/scala/reflect/reify/Phases.scala +++ b/src/compiler/scala/reflect/reify/Phases.scala @@ -10,7 +10,6 @@ trait Phases extends Reshape self: Reifier => import global._ - import definitions._ private var alreadyRun = false @@ -26,7 +25,7 @@ trait Phases extends Reshape if (reifyDebug) println("[reshape phase]") tree = reshape.transform(tree) if (reifyDebug) println("[interlude]") - if (reifyDebug) println("reifee = " + (if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)) + if (reifyDebug) println("reifee = " + (if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)) if (reifyDebug) println("[calculate phase]") calculate.traverse(tree) @@ -41,4 +40,4 @@ trait Phases extends Reshape 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 index 47669f57b0..b3224b1aa6 100644 --- a/src/compiler/scala/reflect/reify/Reifier.scala +++ b/src/compiler/scala/reflect/reify/Reifier.scala @@ -57,7 +57,7 @@ abstract class Reifier extends States val result = reifee match { case tree: Tree => - reifyTrace("reifying = ")(if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) + reifyTrace("reifying = ")(if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) reifyTrace("reifee is located at: ")(tree.pos) reifyTrace("universe = ")(universe) reifyTrace("mirror = ")(mirror) diff --git a/src/compiler/scala/reflect/reify/States.scala b/src/compiler/scala/reflect/reify/States.scala index 58455c9f3c..29bfa19845 100644 --- a/src/compiler/scala/reflect/reify/States.scala +++ b/src/compiler/scala/reflect/reify/States.scala @@ -4,7 +4,6 @@ trait States { self: Reifier => import global._ - import definitions._ /** Encapsulates reifier state * diff --git a/src/compiler/scala/reflect/reify/Taggers.scala b/src/compiler/scala/reflect/reify/Taggers.scala index cbaee41890..9659134e5b 100644 --- a/src/compiler/scala/reflect/reify/Taggers.scala +++ b/src/compiler/scala/reflect/reify/Taggers.scala @@ -8,7 +8,6 @@ abstract class Taggers { import c.universe._ import definitions._ - import treeBuild._ val coreTags = Map( ByteTpe -> nme.Byte, @@ -59,7 +58,7 @@ abstract class Taggers { val result = tpe match { case coreTpe if coreTags contains coreTpe => - val ref = if (tagModule.owner.isPackageClass) Ident(tagModule) else Select(prefix, tagModule.name) + val ref = if (tagModule.isTopLevel) Ident(tagModule) else Select(prefix, tagModule.name) Select(ref, coreTags(coreTpe)) case _ => translatingReificationErrors(materializer) diff --git a/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala b/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala index dec491aabe..5a454e1e07 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala @@ -5,7 +5,6 @@ trait GenAnnotationInfos { self: Reifier => import global._ - import definitions._ // usually annotations are reified as their originals from Modifiers // however, when reifying free and tough types, we're forced to reify annotation infos as is @@ -52,4 +51,4 @@ trait GenAnnotationInfos { val reifiedAssocs = ann.assocs map (assoc => scalaFactoryCall(nme.Tuple2, reify(assoc._1), reifyClassfileAnnotArg(assoc._2))) mirrorFactoryCall(nme.Annotation, reify(ann.atp), mkList(reifiedArgs), mkListMap(reifiedAssocs)) } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/reflect/reify/codegen/GenNames.scala b/src/compiler/scala/reflect/reify/codegen/GenNames.scala index 4abf88f475..4266c6f8d6 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenNames.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenNames.scala @@ -5,10 +5,9 @@ trait GenNames { self: Reifier => import global._ - import definitions._ def reifyName(name: Name) = { - val factory = if (name.isTypeName) nme.nmeNewTypeName else nme.nmeNewTermName + val factory = if (name.isTypeName) nme.TypeName else nme.TermName mirrorCall(factory, Literal(Constant(name.toString))) } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/reflect/reify/codegen/GenPositions.scala b/src/compiler/scala/reflect/reify/codegen/GenPositions.scala index 8c5db04454..1d151c5135 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenPositions.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenPositions.scala @@ -5,7 +5,6 @@ trait GenPositions { self: Reifier => import global._ - import definitions._ // 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 @@ -14,4 +13,4 @@ trait GenPositions { // 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/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 47c966ea24..67bc93d407 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -7,7 +7,6 @@ trait GenSymbols { self: Reifier => import global._ - import definitions._ /** Symbol table of the reifee. * @@ -101,7 +100,7 @@ trait GenSymbols { def reifyFreeTerm(binding: Tree): Tree = reifyIntoSymtab(binding.symbol) { sym => if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")") - val name = newTermName(nme.REIFY_FREE_PREFIX + sym.name + (if (sym.isType) nme.REIFY_FREE_THIS_SUFFIX else "")) + val name = newTermName("" + nme.REIFY_FREE_PREFIX + sym.name + (if (sym.isType) nme.REIFY_FREE_THIS_SUFFIX else "")) // We need to note whether the free value being reified is stable or not to guide subsequent reflective compilation. // Here's why reflection compilation needs our help. // @@ -142,14 +141,14 @@ trait GenSymbols { reifyIntoSymtab(binding.symbol) { sym => if (reifyDebug) println("Free type: %s (%s)".format(sym, sym.accurateKindString)) state.reificationIsConcrete = false - val name = newTermName(nme.REIFY_FREE_PREFIX + sym.name) + val name: TermName = nme.REIFY_FREE_PREFIX append sym.name Reification(name, binding, mirrorBuildCall(nme.newFreeType, reify(sym.name.toString), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) } def reifySymDef(sym: Symbol): Tree = reifyIntoSymtab(sym) { sym => if (reifyDebug) println("Sym def: %s (%s)".format(sym, sym.accurateKindString)) - val name = newTermName(nme.REIFY_SYMDEF_PREFIX + sym.name) + val name: TermName = nme.REIFY_SYMDEF_PREFIX append sym.name def reifiedOwner = if (sym.owner.isLocatable) reify(sym.owner) else reifySymDef(sym.owner) Reification(name, Ident(sym), mirrorBuildCall(nme.newNestedSymbol, reifiedOwner, reify(sym.name), reify(sym.pos), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(sym.isClass))) } @@ -173,7 +172,7 @@ trait GenSymbols { val reification = reificode(sym) import reification.{name, binding} val tree = reification.tree updateAttachment ReifyBindingAttachment(binding) - state.symtab += (sym, name, tree) + state.symtab += (sym, name.toTermName, tree) } fromSymtab } diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 9894e359b4..df2eeaa932 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -45,7 +45,9 @@ trait GenTrees { case global.EmptyTree => reifyMirrorObject(EmptyTree) case global.emptyValDef => - mirrorBuildSelect(nme.emptyValDef) + mirrorSelect(nme.emptyValDef) + case global.pendingSuperCall => + mirrorSelect(nme.pendingSuperCall) case FreeDef(_, _, _, _, _) => reifyNestedFreeDef(tree) case FreeRef(_, _) => @@ -64,7 +66,7 @@ trait GenTrees { // 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 (reifyTreeSymbols && tree.hasSymbol) { + if (reifyTreeSymbols && tree.hasSymbolField) { if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree)) rtree = mirrorBuildCall(nme.setSymbol, rtree, reify(tree.symbol)) } @@ -86,8 +88,8 @@ trait GenTrees { // see ``Metalevels'' for more info about metalevel breaches // and about how we deal with splices that contain them - val isMetalevelBreach = splicee exists (sub => sub.hasSymbol && sub.symbol != NoSymbol && sub.symbol.metalevel > 0) - val isRuntimeEval = splicee exists (sub => sub.hasSymbol && sub.symbol == ExprSplice) + val isMetalevelBreach = splicee exists (sub => sub.hasSymbolField && sub.symbol != NoSymbol && sub.symbol.metalevel > 0) + val isRuntimeEval = splicee exists (sub => sub.hasSymbolField && sub.symbol == ExprSplice) if (isMetalevelBreach || isRuntimeEval) { // we used to convert dynamic splices into runtime evals transparently, but we no longer do that // why? see comments in ``Metalevels'' diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala index bb7e1f9b56..2370f18e3a 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala @@ -73,7 +73,6 @@ trait GenTypes { if (reifyDebug) println("splicing " + tpe) val tagFlavor = if (concrete) tpnme.TypeTag.toString else tpnme.WeakTypeTag.toString - val key = (tagFlavor, tpe.typeSymbol) // 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(universe, tagFlavor, tpe)) diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala index 49877b4286..e0570d61f2 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -5,7 +5,6 @@ trait GenUtils { self: Reifier => import global._ - import definitions._ def reifyList(xs: List[Any]): Tree = mkList(xs map reify) @@ -31,41 +30,32 @@ trait GenUtils { def call(fname: String, args: Tree*): Tree = Apply(termPath(fname), args.toList) - def mirrorSelect(name: String): Tree = - termPath(nme.UNIVERSE_PREFIX + name) + def mirrorSelect(name: String): Tree = termPath(nme.UNIVERSE_PREFIX + name) + def mirrorSelect(name: TermName): Tree = mirrorSelect(name.toString) - def mirrorBuildSelect(name: String): Tree = - termPath(nme.UNIVERSE_BUILD_PREFIX + name) - - def mirrorMirrorSelect(name: String): Tree = - termPath(nme.MIRROR_PREFIX + name) + def mirrorMirrorSelect(name: TermName): Tree = + termPath("" + nme.MIRROR_PREFIX + name) def mirrorCall(name: TermName, args: Tree*): Tree = - call("" + (nme.UNIVERSE_PREFIX append name), args: _*) - - def mirrorCall(name: String, args: Tree*): Tree = - call(nme.UNIVERSE_PREFIX + name, args: _*) + call("" + nme.UNIVERSE_PREFIX + name, args: _*) def mirrorBuildCall(name: TermName, args: Tree*): Tree = - call("" + (nme.UNIVERSE_BUILD_PREFIX append name), args: _*) - - def mirrorBuildCall(name: String, args: Tree*): Tree = - call(nme.UNIVERSE_BUILD_PREFIX + name, args: _*) + call("" + nme.UNIVERSE_BUILD_PREFIX + name, args: _*) def mirrorMirrorCall(name: TermName, args: Tree*): Tree = - call("" + (nme.MIRROR_PREFIX append name), args: _*) - - def mirrorMirrorCall(name: String, args: Tree*): Tree = - call(nme.MIRROR_PREFIX + name, args: _*) + 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 mirrorFactoryCall(prefix: TermName, args: Tree*): Tree = + mirrorCall("" + prefix, args: _*) + + def scalaFactoryCall(name: TermName, args: Tree*): Tree = + call(s"scala.$name.apply", args: _*) def scalaFactoryCall(name: String, args: Tree*): Tree = - call("scala." + name + ".apply", args: _*) + scalaFactoryCall(name: TermName, args: _*) def mkList(args: List[Tree]): Tree = scalaFactoryCall("collection.immutable.List", args: _*) @@ -91,22 +81,6 @@ trait GenUtils { /** 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) - } - object TypedOrAnnotated { def unapply(tree: Tree): Option[Tree] = tree match { case ty @ Typed(_, _) => @@ -118,15 +92,6 @@ trait GenUtils { } } - def isAnnotated(tpe: Type) = { - def isAnnotated(tpe: Type) = tpe match { - case _: AnnotatedType => true - case _ => false - } - - tpe != null && (tpe exists isAnnotated) - } - def isSemiConcreteTypeMember(tpe: Type) = tpe match { case TypeRef(SingleType(_, _), sym, _) if sym.isAbstractType && !sym.isExistential => true case _ => false @@ -145,4 +110,4 @@ trait GenUtils { 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 index 55f8684df2..78f85c2634 100644 --- a/src/compiler/scala/reflect/reify/package.scala +++ b/src/compiler/scala/reflect/reify/package.scala @@ -1,11 +1,10 @@ package scala.reflect -import scala.language.implicitConversions -import scala.reflect.macros.{Context, ReificationException, UnexpectedReificationException} +import scala.reflect.macros.ReificationException import scala.tools.nsc.Global package object reify { - private def mkReifier(global1: Global)(typer: global1.analyzer.Typer, universe: global1.Tree, mirror: global1.Tree, reifee: Any, concrete: Boolean = false): Reifier { val global: global1.type } = { + private def mkReifier(global1: Global)(typer: global1.analyzer.Typer, universe: global1.Tree, mirror: global1.Tree, reifee: Any, concrete: Boolean): Reifier { val global: global1.type } = { val typer1: typer.type = typer val universe1: universe.type = universe val mirror1: mirror.type = mirror @@ -24,7 +23,8 @@ package object reify { private[reify] def mkDefaultMirrorRef(global: Global)(universe: global.Tree, typer0: global.analyzer.Typer): global.Tree = { import global._ - import definitions._ + import definitions.JavaUniverseClass + val enclosingErasure = { val rClassTree = reifyEnclosingRuntimeClass(global)(typer0) // HACK around SI-6259 @@ -56,7 +56,7 @@ package object reify { if (concrete) throw new ReificationException(enclosingMacroPosition, "tpe %s is an unresolved spliceable type".format(tpe)) } - tpe.normalize match { + tpe.dealiasWiden match { case TypeRef(_, ArrayClass, componentTpe :: Nil) => val componentErasure = reifyRuntimeClass(global)(typer0, componentTpe, concrete) gen.mkMethodCall(arrayClassMethod, List(componentErasure)) @@ -71,7 +71,6 @@ package object reify { // a class/object body, this will return an EmptyTree. def reifyEnclosingRuntimeClass(global: Global)(typer0: global.analyzer.Typer): global.Tree = { import global._ - import definitions._ def isThisInScope = typer0.context.enclosingContextChain exists (_.tree.isInstanceOf[ImplDef]) if (isThisInScope) { val enclosingClasses = typer0.context.enclosingContextChain map (_.tree) collect { case classDef: ClassDef => classDef } diff --git a/src/compiler/scala/reflect/reify/phases/Calculate.scala b/src/compiler/scala/reflect/reify/phases/Calculate.scala index 4d1e22abe7..5566fd7a77 100644 --- a/src/compiler/scala/reflect/reify/phases/Calculate.scala +++ b/src/compiler/scala/reflect/reify/phases/Calculate.scala @@ -5,7 +5,6 @@ trait Calculate { self: Reifier => import global._ - import definitions._ implicit class RichCalculateSymbol(sym: Symbol) { def metalevel: Int = { assert(sym != null && sym != NoSymbol); localSymbols.getOrElse(sym, 0) } diff --git a/src/compiler/scala/reflect/reify/phases/Metalevels.scala b/src/compiler/scala/reflect/reify/phases/Metalevels.scala index fbbd12a42f..cccf080dbf 100644 --- a/src/compiler/scala/reflect/reify/phases/Metalevels.scala +++ b/src/compiler/scala/reflect/reify/phases/Metalevels.scala @@ -1,11 +1,12 @@ package scala.reflect.reify package phases +import scala.collection.{ mutable } + trait Metalevels { self: Reifier => import global._ - import definitions._ /** * Makes sense of cross-stage bindings. @@ -102,7 +103,7 @@ trait Metalevels { */ val metalevels = new Transformer { var insideSplice = false - var inlineableBindings = scala.collection.mutable.Map[TermName, Tree]() + val inlineableBindings = mutable.Map[TermName, Tree]() def withinSplice[T](op: => T) = { val old = insideSplice @@ -124,7 +125,7 @@ trait Metalevels { withinSplice { super.transform(TreeSplice(ReifiedTree(universe, mirror, symtab1, rtree, tpe, rtpe, concrete))) } case TreeSplice(splicee) => if (reifyDebug) println("entering splice: " + splicee) - val breaches = splicee filter (sub => sub.hasSymbol && sub.symbol != NoSymbol && sub.symbol.metalevel > 0) + val breaches = splicee filter (sub => sub.hasSymbolField && sub.symbol != NoSymbol && sub.symbol.metalevel > 0) if (!insideSplice && breaches.nonEmpty) { // we used to convert dynamic splices into runtime evals transparently, but we no longer do that // why? see comments above diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index 8e13a45cdb..eda4cba2bf 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -2,7 +2,6 @@ package scala.reflect.reify package phases import scala.runtime.ScalaRunTime.isAnyVal -import scala.runtime.ScalaRunTime.isTuple import scala.reflect.reify.codegen._ trait Reify extends GenSymbols @@ -16,7 +15,6 @@ trait Reify extends GenSymbols self: Reifier => import global._ - import definitions._ private object reifyStack { def currents: List[Any] = state.reifyStack @@ -59,4 +57,4 @@ trait Reify extends GenSymbols 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 index 7406f5d02d..71fe4ddeea 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -48,13 +48,13 @@ trait Reshape { val Template(parents, self, body) = impl var body1 = trimAccessors(classDef, reshapeLazyVals(body)) body1 = trimSyntheticCaseClassMembers(classDef, body1) - var impl1 = Template(parents, self, body1).copyAttrs(impl) + val 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, reshapeLazyVals(body)) body1 = trimSyntheticCaseClassMembers(moduledef, body1) - var impl1 = Template(parents, self, body1).copyAttrs(impl) + val 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 @@ -89,8 +89,8 @@ trait Reshape { } private def undoMacroExpansion(tree: Tree): Tree = - tree.attachments.get[MacroExpansionAttachment] match { - case Some(MacroExpansionAttachment(original)) => + tree.attachments.get[analyzer.MacroExpansionAttachment] match { + case Some(analyzer.MacroExpansionAttachment(original, _)) => original match { // this hack is necessary until I fix implicit macros // so far tag materialization is implemented by sneaky macros hidden in scala-compiler.jar @@ -116,7 +116,6 @@ trait Reshape { 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) @@ -256,10 +255,10 @@ trait Reshape { val DefDef(mods0, name0, _, _, tpt0, rhs0) = ddef val name1 = nme.dropLocalSuffix(name0) val Modifiers(flags0, privateWithin0, annotations0) = mods0 - var flags1 = (flags0 & GetterFlags) & ~(STABLE | ACCESSOR | METHOD) + val flags1 = (flags0 & GetterFlags) & ~(STABLE | ACCESSOR | METHOD) val mods1 = Modifiers(flags1, privateWithin0, annotations0) setPositions mods0.positions val mods2 = toPreTyperModifiers(mods1, ddef.symbol) - ValDef(mods2, name1, tpt0, extractRhs(rhs0)) + ValDef(mods2, name1.toTermName, tpt0, extractRhs(rhs0)) } private def trimAccessors(deff: Tree, stats: List[Tree]): List[Tree] = { @@ -271,7 +270,7 @@ trait Reshape { def detectBeanAccessors(prefix: String): Unit = { if (defdef.name.startsWith(prefix)) { - var name = defdef.name.toString.substring(prefix.length) + val 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 @@ -283,11 +282,11 @@ trait Reshape { detectBeanAccessors("is") }); - var stats1 = stats flatMap { + val stats1 = stats flatMap { case vdef @ ValDef(mods, name, tpt, rhs) if !mods.isLazy => val mods1 = if (accessors.contains(vdef)) { val ddef = accessors(vdef)(0) // any accessor will do - val Modifiers(flags, privateWithin, annotations) = mods + val Modifiers(flags, _, annotations) = mods var flags1 = flags & ~LOCAL if (!ddef.symbol.isPrivate) flags1 = flags1 & ~PRIVATE val privateWithin1 = ddef.mods.privateWithin @@ -298,7 +297,7 @@ trait Reshape { } val mods2 = toPreTyperModifiers(mods1, vdef.symbol) val name1 = nme.dropLocalSuffix(name) - val vdef1 = ValDef(mods2, name1, tpt, rhs) + val vdef1 = ValDef(mods2, name1.toTermName, 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 now out of sync case ddef: DefDef if !ddef.mods.isLazy => @@ -330,7 +329,8 @@ trait Reshape { case Some(ddef) => toPreTyperLazyVal(ddef) case None => - CannotReifyInvalidLazyVal(vdef) + if (reifyDebug) println("couldn't find corresponding lazy val accessor") + vdef } if (reifyDebug) println(s"reconstructed lazy val is $vdef1") vdef1::Nil diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index 134ae13890..d57188bf6e 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -187,7 +187,7 @@ trait Extractors { Literal(Constant(origin: String))))) if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newFreeTerm == nme.newFreeTerm && uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => - Some(uref1, name, reifyBinding(tree), flags, origin) + Some((uref1, name, reifyBinding(tree), flags, origin)) case _ => None } @@ -204,7 +204,7 @@ trait Extractors { Literal(Constant(origin: String))))) if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newFreeType == nme.newFreeType && uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => - Some(uref1, name, reifyBinding(tree), flags, origin) + Some((uref1, name, reifyBinding(tree), flags, origin)) case _ => None } diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index aca18c7df7..0740f8d0b6 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -11,8 +11,6 @@ trait NodePrinters { self: Utils => import global._ - import definitions._ - import Flag._ object reifiedNodeToString extends (Tree => String) { def apply(tree: Tree): String = { @@ -25,8 +23,8 @@ trait NodePrinters { // Rolling a full-fledged, robust TreePrinter would be several times more code. // Also as of late we have tests that ensure that UX won't be broken by random changes to the reifier. val lines = (tree.toString.split(EOL) drop 1 dropRight 1).toList splitAt 2 - var (List(universe, mirror), reification) = lines - reification = (for (line <- reification) yield { + val (List(universe, mirror), reification0) = lines + val reification = (for (line <- reification0) yield { var s = line substring 2 s = s.replace(nme.UNIVERSE_PREFIX.toString, "") s = s.replace(".apply", "") @@ -38,8 +36,8 @@ trait NodePrinters { flagsAreUsed = true show(m.group(1).toLong) }) - s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()") - s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => { + s = s.replace("Modifiers(0L, TypeName(\"\"), List())", "Modifiers()") + s = """Modifiers\((\d+)[lL], TypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => { val buf = new scala.collection.mutable.ListBuffer[String] val annotations = m.group(3) @@ -48,7 +46,7 @@ trait NodePrinters { val privateWithin = "" + m.group(2) if (buf.nonEmpty || privateWithin != "") - buf.append("newTypeName(\"" + privateWithin + "\")") + buf.append("TypeName(\"" + privateWithin + "\")") val bits = m.group(1) if (buf.nonEmpty || bits != "0L") { @@ -77,10 +75,10 @@ trait NodePrinters { printout += universe.trim if (mirrorIsUsed) printout += mirror.replace("Mirror[", "scala.reflect.api.Mirror[").trim val imports = scala.collection.mutable.ListBuffer[String](); - imports += nme.UNIVERSE_SHORT + imports += nme.UNIVERSE_SHORT.toString // if (buildIsUsed) imports += nme.build - if (mirrorIsUsed) imports += nme.MIRROR_SHORT - if (flagsAreUsed) imports += nme.Flag + if (mirrorIsUsed) imports += nme.MIRROR_SHORT.toString + if (flagsAreUsed) imports += nme.Flag.toString printout += s"""import ${imports map (_ + "._") mkString ", "}""" val name = if (isExpr) "tree" else "tpe" diff --git a/src/compiler/scala/reflect/reify/utils/SymbolTables.scala b/src/compiler/scala/reflect/reify/utils/SymbolTables.scala index dbb0836e0a..5f8de9894f 100644 --- a/src/compiler/scala/reflect/reify/utils/SymbolTables.scala +++ b/src/compiler/scala/reflect/reify/utils/SymbolTables.scala @@ -8,8 +8,6 @@ trait SymbolTables { self: Utils => import global._ - import definitions._ - import Flag._ class SymbolTable private[SymbolTable] ( private[SymbolTable] val symtab: immutable.ListMap[Symbol, Tree] = immutable.ListMap[Symbol, Tree](), @@ -17,9 +15,6 @@ trait SymbolTables { private[SymbolTable] val original: Option[List[Tree]] = None) { def syms: List[Symbol] = symtab.keys.toList - def isConcrete: Boolean = symtab.values forall (sym => !FreeTypeDef.unapply(sym).isDefined) - -// def aliases: Map[Symbol, List[TermName]] = aliases.distinct groupBy (_._1) mapValues (_ map (_._2)) def symDef(sym: Symbol): Tree = symtab.getOrElse(sym, EmptyTree) @@ -89,11 +84,6 @@ trait SymbolTables { add(ValDef(NoMods, freshName(name0), TypeTree(), reification) updateAttachment bindingAttachment) } - private def add(sym: Symbol, name: TermName): SymbolTable = { - if (!(syms contains sym)) error("cannot add an alias to a symbol not in the symbol table") - add(sym, name, EmptyTree) - } - private def remove(sym: Symbol): SymbolTable = { val newSymtab = symtab - sym val newAliases = aliases filter (_._1 != sym) @@ -107,7 +97,7 @@ trait SymbolTables { newSymtab = newSymtab map { case ((sym, tree)) => val ValDef(mods, primaryName, tpt, rhs) = tree val tree1 = - if (!(newAliases contains (sym, primaryName))) { + if (!(newAliases contains ((sym, primaryName)))) { val primaryName1 = newAliases.find(_._1 == sym).get._2 ValDef(mods, primaryName1, tpt, rhs).copyAttrs(tree) } else tree @@ -143,7 +133,7 @@ trait SymbolTables { var result = new SymbolTable(original = Some(encoded)) encoded foreach (entry => (entry.attachments.get[ReifyBindingAttachment], entry.attachments.get[ReifyAliasAttachment]) match { case (Some(ReifyBindingAttachment(_)), _) => result += entry - case (_, Some(ReifyAliasAttachment(sym, alias))) => result = new SymbolTable(result.symtab, result.aliases :+ (sym, alias)) + case (_, Some(ReifyAliasAttachment(sym, alias))) => result = new SymbolTable(result.symtab, result.aliases :+ ((sym, alias))) case _ => // do nothing, this is boilerplate that can easily be recreated by subsequent `result.encode` }) result @@ -214,4 +204,4 @@ trait SymbolTables { } } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/ant/Pack200Task.scala b/src/compiler/scala/tools/ant/Pack200Task.scala index 255efe55ec..3180911414 100644 --- a/src/compiler/scala/tools/ant/Pack200Task.scala +++ b/src/compiler/scala/tools/ant/Pack200Task.scala @@ -99,8 +99,8 @@ class Pack200Task extends ScalaMatchingTask { private def getFileList: List[File] = { var files: List[File] = Nil val fs = getImplicitFileSet - var ds = fs.getDirectoryScanner(getProject()) - var dir = fs.getDir(getProject()) + val ds = fs.getDirectoryScanner(getProject()) + val dir = fs.getDir(getProject()) for (filename <- ds.getIncludedFiles() if filename.toLowerCase.endsWith(".jar")) { val file = new File(dir, filename) diff --git a/src/compiler/scala/tools/ant/ScalaTool.scala b/src/compiler/scala/tools/ant/ScalaTool.scala index 57d24f6213..633145a97c 100644 --- a/src/compiler/scala/tools/ant/ScalaTool.scala +++ b/src/compiler/scala/tools/ant/ScalaTool.scala @@ -108,7 +108,7 @@ class ScalaTool extends ScalaMatchingTask { * for general purpose scripts, as this does not assume all elements are * relative to the Ant `basedir`. Additionally, the platform specific * demarcation of any script variables (e.g. `${SCALA_HOME}` or - * `%SCALA_HOME%`) can be specified in a platform independant way (e.g. + * `%SCALA_HOME%`) can be specified in a platform independent way (e.g. * `@SCALA_HOME@`) and automatically translated for you. */ def setClassPath(input: String) { diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index 73d09e82ba..3b8ae202f6 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -19,6 +19,7 @@ import org.apache.tools.ant.util.facade.{FacadeTaskHelper, ImplementationSpecificArgument} import scala.tools.nsc.{Global, Settings, CompilerCommand} +import scala.tools.nsc.interactive.RangePositions import scala.tools.nsc.io.{Path => SPath} import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} @@ -55,8 +56,6 @@ import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} * - `usejavacp`, * - `failonerror`, * - `scalacdebugging`, - * - `assemname`, - * - `assemrefs`. * * It also takes the following parameters as nested elements: * - `src` (for `srcdir`), @@ -99,7 +98,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { /** Defines valid values for the `target` property. */ object Target extends PermissibleValue { - val values = List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil") + val values = List("jvm-1.5", "jvm-1.6", "jvm-1.7") } /** Defines valid values for the `deprecation` and `unchecked` properties. */ @@ -169,11 +168,6 @@ class Scalac extends ScalaMatchingTask with ScalacShared { /** Indicates whether compilation errors will fail the build; defaults to true. */ protected var failonerror: Boolean = true - // Name of the output assembly (only relevant with -target:msil) - protected var assemname: Option[String] = None - // List of assemblies referenced by the program (only relevant with -target:msil) - protected var assemrefs: Option[String] = None - /** Prints out the files being compiled by the scalac ant task * (not only the number of files). */ protected var scalacDebugging: Boolean = false @@ -420,9 +414,6 @@ class Scalac extends ScalaMatchingTask with ScalacShared { * @param input The specified flag */ def setScalacdebugging(input: Boolean) { scalacDebugging = input } - def setAssemname(input: String) { assemname = Some(input) } - def setAssemrefs(input: String) { assemrefs = Some(input) } - /** Sets the `compilerarg` as a nested compilerarg Ant parameter. * @return A compiler argument to be configured. */ def createCompilerArg(): ImplementationSpecificArgument = { @@ -518,7 +509,10 @@ class Scalac extends ScalaMatchingTask with ScalacShared { new Settings(error) protected def newGlobal(settings: Settings, reporter: Reporter) = - new Global(settings, reporter) + if (settings.Yrangepos.value) + new Global(settings, reporter) with RangePositions + else + new Global(settings, reporter) /*============================================================================*\ ** The big execute method ** @@ -612,9 +606,6 @@ class Scalac extends ScalaMatchingTask with ScalacShared { if (!unchecked.isEmpty) settings.unchecked.value = unchecked.get if (!usejavacp.isEmpty) settings.usejavacp.value = usejavacp.get - if (!assemname.isEmpty) settings.assemname.value = assemname.get - if (!assemrefs.isEmpty) settings.assemrefs.value = assemrefs.get - val jvmargs = scalacCompilerArgs.getArgs filter (_ startsWith "-J") if (!jvmargs.isEmpty) settings.jvmargs.value = jvmargs.toList val defines = scalacCompilerArgs.getArgs filter (_ startsWith "-D") diff --git a/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala b/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala index 9cdf484080..d5545fe76a 100644 --- a/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala +++ b/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala @@ -80,7 +80,7 @@ class ScalacFork extends ScalaMatchingTask with ScalacShared with TaskArgs { private def createMapper() = { val mapper = new GlobPatternMapper() - val extension = if (isMSIL) "*.msil" else "*.class" + val extension = "*.class" mapper setTo extension mapper setFrom "*.scala" @@ -104,9 +104,6 @@ class ScalacFork extends ScalaMatchingTask with ScalacShared with TaskArgs { sourcePath foreach (settings.sourcepath = _) settings.extraParams = extraArgsFlat - if (isMSIL) - settings.sourcedir = sourceDir - val mapper = createMapper() val includedFiles: Array[File] = diff --git a/src/compiler/scala/tools/ant/sabbus/Settings.scala b/src/compiler/scala/tools/ant/sabbus/Settings.scala index fde61e9564..d0fefdaa03 100644 --- a/src/compiler/scala/tools/ant/sabbus/Settings.scala +++ b/src/compiler/scala/tools/ant/sabbus/Settings.scala @@ -10,7 +10,7 @@ package scala.tools.ant.sabbus import java.io.File -import org.apache.tools.ant.types.{Path, Reference} +import org.apache.tools.ant.types.Path class Settings { diff --git a/src/compiler/scala/tools/ant/sabbus/TaskArgs.scala b/src/compiler/scala/tools/ant/sabbus/TaskArgs.scala index 6bb1aaa306..b061bcf7fb 100644 --- a/src/compiler/scala/tools/ant/sabbus/TaskArgs.scala +++ b/src/compiler/scala/tools/ant/sabbus/TaskArgs.scala @@ -98,6 +98,4 @@ trait TaskArgs extends CompilationPathProperty { val parts = a.getParts if(parts eq null) Seq[String]() else parts.toSeq } - - def isMSIL = compTarget exists (_ == "msil") } diff --git a/src/compiler/scala/tools/ant/templates/tool-unix.tmpl b/src/compiler/scala/tools/ant/templates/tool-unix.tmpl index f1c6c52785..84ccaba749 100644 --- a/src/compiler/scala/tools/ant/templates/tool-unix.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-unix.tmpl @@ -102,6 +102,9 @@ if [[ -n "$cygwin" ]]; then format=windows fi SCALA_HOME="$(cygpath --$format "$SCALA_HOME")" + if [[ -n "$JAVA_HOME" ]]; then + JAVA_HOME="$(cygpath --$format "$JAVA_HOME")" + fi TOOL_CLASSPATH="$(cygpath --path --$format "$TOOL_CLASSPATH")" elif [[ -n "$mingw" ]]; then SCALA_HOME="$(cmd //c echo "$SCALA_HOME")" diff --git a/src/compiler/scala/tools/cmd/CommandLine.scala b/src/compiler/scala/tools/cmd/CommandLine.scala index 75f96d3c4b..cf0463423c 100644 --- a/src/compiler/scala/tools/cmd/CommandLine.scala +++ b/src/compiler/scala/tools/cmd/CommandLine.scala @@ -19,7 +19,7 @@ class CommandLine(val spec: Reference, val originalArgs: List[String]) extends C def this(spec: Reference, line: String) = this(spec, Parser tokenize line) def this(spec: Reference, args: Array[String]) = this(spec, args.toList) - import spec.{ isAnyOption, isUnaryOption, isBinaryOption, isExpandOption } + import spec.{ isUnaryOption, isBinaryOption, isExpandOption } val Terminator = "--" val ValueForUnaryOption = "true" // so if --opt is given, x(--opt) = true diff --git a/src/compiler/scala/tools/cmd/FromString.scala b/src/compiler/scala/tools/cmd/FromString.scala index cba2e99998..433bbb167e 100644 --- a/src/compiler/scala/tools/cmd/FromString.scala +++ b/src/compiler/scala/tools/cmd/FromString.scala @@ -6,7 +6,7 @@ package scala.tools package cmd -import nsc.io.{ Path, File, Directory } +import scala.tools.nsc.io.{ File, Directory } import scala.reflect.runtime.{universe => ru} import scala.tools.reflect.StdRuntimeTags._ @@ -24,18 +24,11 @@ abstract class FromString[+T](implicit t: ru.TypeTag[T]) extends PartialFunction } object FromString { - // We need these because we clash with the String => Path implicits. - private def toFile(s: String) = new File(new java.io.File(s)) + // We need this because we clash with the String => Path implicits. private def toDir(s: String) = new Directory(new java.io.File(s)) /** Path related stringifiers. */ - val ExistingFile: FromString[File] = new FromString[File]()(tagOfFile) { - override def isDefinedAt(s: String) = toFile(s).isFile - def apply(s: String): File = - if (isDefinedAt(s)) toFile(s) - else cmd.runAndExit(println("'%s' is not an existing file." format s)) - } val ExistingDir: FromString[Directory] = new FromString[Directory]()(tagOfDirectory) { override def isDefinedAt(s: String) = toDir(s).isDirectory def apply(s: String): Directory = diff --git a/src/compiler/scala/tools/cmd/Reference.scala b/src/compiler/scala/tools/cmd/Reference.scala index bcbb454771..ec2a414065 100644 --- a/src/compiler/scala/tools/cmd/Reference.scala +++ b/src/compiler/scala/tools/cmd/Reference.scala @@ -26,7 +26,6 @@ trait Reference extends Spec { def isUnaryOption(s: String) = unary contains toOpt(s) def isBinaryOption(s: String) = binary contains toOpt(s) def isExpandOption(s: String) = expansionMap contains toOpt(s) - def isAnyOption(s: String) = isUnaryOption(s) || isBinaryOption(s) || isExpandOption(s) def expandArg(arg: String) = expansionMap.getOrElse(fromOpt(arg), List(arg)) @@ -46,7 +45,7 @@ object Reference { val MaxLine = 80 class Accumulators() { - private var _help = new ListBuffer[() => String] + private val _help = new ListBuffer[() => String] private var _unary = List[String]() private var _binary = List[String]() private var _expand = Map[String, List[String]]() diff --git a/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala b/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala index 903517c5b4..ee7e605425 100644 --- a/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala +++ b/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala @@ -12,8 +12,6 @@ trait CodegenSpec extends Spec with Meta.StdOpts with Interpolation { def referenceSpec = CodegenSpec def programInfo = Spec.Info("codegen", "", "scala.tools.cmd.gen.Codegen") - import FromString.ExistingDir - help("Usage: codegen [<options>]") // val inDir = "in" / "directory containing templates" --^ ExistingDir diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index 355a1fd262..15d365ab8c 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -6,7 +6,7 @@ package scala.tools.nsc import util.FreshNameCreator -import scala.reflect.internal.util.{ Position, NoPosition, BatchSourceFile, SourceFile, NoSourceFile } +import scala.reflect.internal.util.{ SourceFile, NoSourceFile } import scala.collection.mutable import scala.collection.mutable.{ LinkedHashSet, ListBuffer } @@ -26,7 +26,7 @@ trait CompilationUnits { self: Global => class CompilationUnit(val source: SourceFile) extends CompilationUnitContextApi { self => /** the fresh name creator */ - var fresh: FreshNameCreator = new FreshNameCreator.Default + val fresh: FreshNameCreator = new FreshNameCreator.Default def freshTermName(prefix: String): TermName = newTermName(fresh.newName(prefix)) def freshTypeName(prefix: String): TypeName = newTypeName(fresh.newName(prefix)) @@ -36,24 +36,34 @@ trait CompilationUnits { self: Global => def exists = source != NoSourceFile && source != null -// def parseSettings() = { -// val argsmarker = "SCALAC_ARGS" -// if(comments nonEmpty) { -// val pragmas = comments find (_.text.startsWith("//#")) // only parse first one -// pragmas foreach { p => -// val i = p.text.indexOf(argsmarker) -// if(i > 0) -// } -// } -// } /** Note: depends now contains toplevel classes. * To get their sourcefiles, you need to dereference with .sourcefile */ - val depends = mutable.HashSet[Symbol]() + private[this] val _depends = mutable.HashSet[Symbol]() + // SBT compatibility (SI-6875) + // + // imagine we have a file named A.scala, which defines a trait named Foo and a module named Main + // Main contains a call to a macro, which calls c.introduceTopLevel to define a mock for Foo + // c.introduceTopLevel creates a virtual file Virt35af32.scala, which contains a class named FooMock extending Foo, + // and macro expansion instantiates FooMock. the stage is now set. let's see what happens next. + // + // without this workaround in scalac or without being patched itself, sbt will think that + // * Virt35af32 depends on A (because it extends Foo from A) + // * A depends on Virt35af32 (because it contains a macro expansion referring to FooMock from Virt35af32) + // + // after compiling A.scala, SBT will notice that it has a new source file named Virt35af32. + // it will also think that this file hasn't yet been compiled and since A depends on it + // it will think that A needs to be recompiled. + // + // recompilation will lead to another macro expansion. that another macro expansion might choose to create a fresh mock, + // producing another virtual file, say, Virtee509a, which will again trick SBT into thinking that A needs a recompile, + // which will lead to another macro expansion, which will produce another virtual file and so on + def depends = if (exists && !source.file.isVirtual) _depends else mutable.HashSet[Symbol]() /** so we can relink */ - val defined = mutable.HashSet[Symbol]() + private[this] val _defined = mutable.HashSet[Symbol]() + def defined = if (exists && !source.file.isVirtual) _defined else mutable.HashSet[Symbol]() /** Synthetic definitions generated by namer, eliminated by typer. */ @@ -123,18 +133,5 @@ trait CompilationUnits { self: Global => lazy val isJava = source.file.name.endsWith(".java") override def toString() = source.toString() - - def clear() { - 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/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala index 731f6926f0..c756a1b0d9 100644 --- a/src/compiler/scala/tools/nsc/CompileClient.scala +++ b/src/compiler/scala/tools/nsc/CompileClient.scala @@ -5,7 +5,6 @@ package scala.tools.nsc -import java.io.{ BufferedReader, File, InputStreamReader, PrintWriter } import settings.FscSettings import scala.tools.util.CompileOutputCommon import sys.SystemProperties.preferIPv4Stack diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala index 7a0a072bb8..72e8cc69c7 100644 --- a/src/compiler/scala/tools/nsc/CompileServer.scala +++ b/src/compiler/scala/tools/nsc/CompileServer.scala @@ -5,7 +5,7 @@ package scala.tools.nsc -import java.io.{ BufferedOutputStream, FileOutputStream, PrintStream } +import java.io.PrintStream import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} import scala.reflect.internal.util.FakePos //Position import scala.tools.util.SocketServer @@ -29,8 +29,6 @@ class StandardCompileServer extends SocketServer { var shutdown = false var verbose = false - val versionMsg = "Fast " + Properties.versionMsg - val MaxCharge = 0.8 private val runtime = Runtime.getRuntime() @@ -58,9 +56,6 @@ class StandardCompileServer extends SocketServer { (totalMemory - freeMemory).toDouble / maxMemory.toDouble > MaxCharge } - protected def newOfflineCompilerCommand(arguments: List[String], settings: FscSettings): OfflineCompilerCommand = - new OfflineCompilerCommand(arguments, settings) - /** Problematically, Settings are only considered equal if every setting * is exactly equal. In fsc this immediately breaks down because the randomly * chosen temporary outdirs differ between client and server. Among other @@ -92,7 +87,7 @@ class StandardCompileServer extends SocketServer { val args = input.split("\0", -1).toList val newSettings = new FscSettings(fscError) - val command = newOfflineCompilerCommand(args, newSettings) + val command = new OfflineCompilerCommand(args, newSettings) this.verbose = newSettings.verbose.value info("Settings after normalizing paths: " + newSettings) @@ -177,9 +172,9 @@ object CompileServer extends StandardCompileServer { private def createRedirect(filename: String) = new PrintStream((redirectDir / filename).createFile().bufferedOutput()) - def main(args: Array[String]) = + def main(args: Array[String]) = execute(() => (), args) - + /** * Used for internal testing. The callback is called upon * server start, notifying the caller that the server is @@ -204,7 +199,7 @@ object CompileServer extends StandardCompileServer { compileSocket setPort port startupCallback() run() - + compileSocket deletePort port } } diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala index 4051bda914..b9f62f49b3 100644 --- a/src/compiler/scala/tools/nsc/CompileSocket.scala +++ b/src/compiler/scala/tools/nsc/CompileSocket.scala @@ -5,13 +5,9 @@ package scala.tools.nsc -import java.io.{ IOException, FileNotFoundException, PrintWriter, FileOutputStream } -import java.io.{ BufferedReader, FileReader } -import java.util.regex.Pattern -import java.net._ +import java.io.{ FileNotFoundException, PrintWriter, FileOutputStream } import java.security.SecureRandom import io.{ File, Path, Directory, Socket } -import scala.util.control.Exception.catching import scala.tools.util.CompileOutputCommon import scala.reflect.internal.util.StringOps.splitWhere import scala.sys.process._ diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index e994150f6f..0462e69f74 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -5,7 +5,6 @@ package scala.tools.nsc -import scala.collection.mutable.ListBuffer import io.File /** A class representing command line info for scalac */ @@ -15,9 +14,6 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { type Setting = Settings#Setting - /** file extensions of files that the compiler can process */ - lazy val fileEndings = Properties.fileEndings - private val processArgumentsResult = if (shouldProcessArguments) processArguments else (true, Nil) @@ -41,8 +37,6 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { """.stripMargin.trim + "\n" def shortUsage = "Usage: %s <options> <source files>" format cmdName - def createUsagePreface(shouldExplain: Boolean) = - if (shouldExplain) shortUsage + "\n" + explainAdvanced else "" /** Creates a help message for a subset of options based on cond */ def createUsageMsg(cond: Setting => Boolean): String = { diff --git a/src/compiler/scala/tools/nsc/CompilerRun.scala b/src/compiler/scala/tools/nsc/CompilerRun.scala deleted file mode 100644 index 6746b08155..0000000000 --- a/src/compiler/scala/tools/nsc/CompilerRun.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc - -class CompilerRun { - def firstPhase: Phase = NoPhase - def terminalPhase: Phase = NoPhase - def namerPhase: Phase = NoPhase - def typerPhase: Phase = NoPhase - def refchecksPhase: Phase = NoPhase - def explicitouterPhase: Phase = NoPhase - def erasurePhase: Phase = NoPhase - def flattenPhase: Phase = NoPhase - def mixinPhase: Phase = NoPhase - def icodePhase: Phase = NoPhase - def phaseNamed(name: String): Phase = NoPhase -} - diff --git a/src/compiler/scala/tools/nsc/Driver.scala b/src/compiler/scala/tools/nsc/Driver.scala index 814bd58a66..fc247600f6 100644 --- a/src/compiler/scala/tools/nsc/Driver.scala +++ b/src/compiler/scala/tools/nsc/Driver.scala @@ -1,11 +1,11 @@ package scala.tools.nsc -import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} +import scala.tools.nsc.reporters.ConsoleReporter import Properties.{ versionString, copyrightString, residentPromptString } -import scala.reflect.internal.util.{ BatchSourceFile, FakePos } +import scala.reflect.internal.util.FakePos abstract class Driver { - + val prompt = residentPromptString val versionMsg = "Scala compiler " + @@ -68,4 +68,4 @@ abstract class Driver { sys.exit(if (reporter.hasErrors) 1 else 0) } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala index 9c2db11a56..ad75d02bff 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala @@ -39,7 +39,4 @@ class GenericRunnerSettings(error: String => Unit) extends Settings(error) { val nc = BooleanSetting( "-nc", "do not use the fsc compilation daemon") withAbbreviation "-nocompdaemon" - - @deprecated("Use `nc` instead", "2.9.0") def nocompdaemon = nc - @deprecated("Use `save` instead", "2.9.0") def savecompiled = save } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 116684c161..466f5eb17c 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -8,30 +8,25 @@ package scala.tools.nsc import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundException } import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException } import scala.compat.Platform.currentTime -import scala.tools.util.PathResolver import scala.collection.{ mutable, immutable } import io.{ SourceReader, AbstractFile, Path } import reporters.{ Reporter, ConsoleReporter } -import util.{ Exceptional, ClassPath, MergedClassPath, StatisticsInfo, ScalaClassLoader, returning } -import scala.reflect.internal.util.{ NoPosition, OffsetPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile } +import util.{ ClassPath, MergedClassPath, StatisticsInfo, returning, stackTraceString, stackTraceHeadString } +import scala.reflect.internal.util.{ OffsetPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } -import settings.{ AestheticSettings } import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers } import symtab.classfile.Pickler -import dependencies.DependencyAnalysis import plugins.Plugins import ast._ import ast.parser._ import typechecker._ import transform._ import backend.icode.{ ICodes, GenICode, ICodeCheckers } -import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform } -import backend.jvm.{GenJVM, GenASM} +import backend.{ ScalaPrimitives, Platform, JavaPlatform } +import backend.jvm.GenASM import backend.opt.{ Inliners, InlineExceptionHandlers, ClosureElimination, DeadCodeElimination } import backend.icode.analysis._ import scala.language.postfixOps -import scala.reflect.internal.StdAttachments -import scala.reflect.ClassTag class Global(var currentSettings: Settings, var reporter: Reporter) extends SymbolTable @@ -74,8 +69,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def this(settings: Settings) = this(settings, new ConsoleReporter(settings)) - def mkAttributedQualifier(tpe: Type, termSym: Symbol): Tree = gen.mkAttributedQualifier(tpe, termSym) - def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase // platform specific elements @@ -83,8 +76,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) type ThisPlatform = Platform { val global: Global.this.type } lazy val platform: ThisPlatform = - if (forMSIL) new { val global: Global.this.type = Global.this } with MSILPlatform - else new { val global: Global.this.type = Global.this } with JavaPlatform + new { val global: Global.this.type = Global.this } with JavaPlatform type PlatformClassPath = ClassPath[platform.BinaryRepr] type OptClassPath = Option[PlatformClassPath] @@ -172,7 +164,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (lastPrintedSource == source) println(": tree is unchanged since " + lastPrintedPhase) else { - lastPrintedPhase = phase.prev // since we're running inside "afterPhase" + lastPrintedPhase = phase.prev // since we're running inside "exitingPhase" lastPrintedSource = source println("") println(source) @@ -235,13 +227,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // of assert and require (but for now I've reproduced them here, // because there are a million to fix.) @inline final def assert(assertion: Boolean, message: => Any) { - Predef.assert(assertion, supplementErrorMessage("" + message)) + // calling Predef.assert would send a freshly allocated closure wrapping the one received as argument. + if (!assertion) + throw new java.lang.AssertionError("assertion failed: "+ supplementErrorMessage("" + message)) } @inline final def assert(assertion: Boolean) { assert(assertion, "") } @inline final def require(requirement: Boolean, message: => Any) { - Predef.require(requirement, supplementErrorMessage("" + message)) + // calling Predef.require would send a freshly allocated closure wrapping the one received as argument. + if (!requirement) + throw new IllegalArgumentException("requirement failed: "+ supplementErrorMessage("" + message)) } @inline final def require(requirement: Boolean) { require(requirement, "") @@ -257,27 +253,26 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (settings.debug.value) body } - // Warnings issued only under -Ydebug. For messages which should reach - // developer ears, but are not adequately actionable by users. - @inline final override def debugwarn(msg: => String) { - if (settings.debug.value) - warning(msg) + /** This is for WARNINGS which should reach the ears of scala developers + * whenever they occur, but are not useful for normal users. They should + * be precise, explanatory, and infrequent. Please don't use this as a + * logging mechanism. !!! is prefixed to all messages issued via this route + * to make them visually distinct. + */ + @inline final override def devWarning(msg: => String) { + if (settings.developer.value || settings.debug.value) + warning("!!! " + msg) } private def elapsedMessage(msg: String, start: Long) = msg + " in " + (currentTime - start) + "ms" def informComplete(msg: String): Unit = reporter.withoutTruncating(inform(msg)) - def informProgress(msg: String) = if (opt.verbose) inform("[" + msg + "]") - def inform[T](msg: String, value: T): T = returning(value)(x => inform(msg + x)) + def informProgress(msg: String) = if (settings.verbose.value) inform("[" + msg + "]") def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) def logError(msg: String, t: Throwable): Unit = () - def logAfterEveryPhase[T](msg: String)(op: => T) { - log("Running operation '%s' after every phase.\n".format(msg) + describeAfterEveryPhase(op)) - } - override def shouldLogAtThisPhase = settings.log.isSetByUser && ( (settings.log containsPhase globalPhase) || (settings.log containsPhase phase) ) @@ -301,7 +296,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) private val reader: SourceReader = { val defaultEncoding = Properties.sourceEncoding - val defaultReader = Properties.sourceReader def loadCharset(name: String) = try Some(Charset.forName(name)) @@ -314,7 +308,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) None } - val charset = opt.encoding flatMap loadCharset getOrElse { + val charset = ( if (settings.encoding.isSetByUser) Some(settings.encoding.value) else None ) flatMap loadCharset getOrElse { settings.encoding.value = defaultEncoding // A mandatory charset Charset.forName(defaultEncoding) } @@ -329,62 +323,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } - opt.sourceReader flatMap loadReader getOrElse { + ( if (settings.sourceReader.isSetByUser) Some(settings.sourceReader.value) else None ) flatMap loadReader getOrElse { new SourceReader(charset.newDecoder(), reporter) } } - if (!dependencyAnalysis.off) - dependencyAnalysis.loadDependencyAnalysis() - - if (opt.verbose || opt.logClasspath) { + if (settings.verbose.value || settings.Ylogcp.value) { // Uses the "do not truncate" inform informComplete("[search path for source files: " + classPath.sourcepaths.mkString(",") + "]") informComplete("[search path for class files: " + classPath.asClasspathString + "]") } - object opt extends AestheticSettings { - def settings = Global.this.settings - - // protected implicit lazy val globalPhaseOrdering: Ordering[Phase] = Ordering[Int] on (_.id) - def isActive(ph: Settings#PhasesSetting) = ph containsPhase globalPhase - def wasActive(ph: Settings#PhasesSetting) = ph containsPhase globalPhase.prev - - // Allows for syntax like scalac -Xshow-class Random@erasure,typer - private def splitClassAndPhase(str: String, term: Boolean): Name = { - def mkName(s: String) = if (term) newTermName(s) else newTypeName(s) - (str indexOf '@') match { - case -1 => mkName(str) - case idx => - val phasePart = str drop (idx + 1) - settings.Yshow.tryToSetColon(phasePart split ',' toList) - mkName(str take idx) - } - } - - // behavior - - // debugging - def checkPhase = wasActive(settings.check) - def logPhase = isActive(settings.log) - - // Write *.icode files right after GenICode when -Xprint-icode was given. - def writeICodeAtICode = settings.writeICode.isSetByUser && isActive(settings.writeICode) - - // showing/printing things - def browsePhase = isActive(settings.browse) - def echoFilenames = opt.debug && (opt.verbose || currentRun.size < 5) - def noShow = settings.Yshow.isDefault - def printLate = settings.printLate.value - def printPhase = isActive(settings.Xprint) - def showNames = List(showClass, showObject).flatten - def showPhase = isActive(settings.Yshow) - def showSymbols = settings.Yshowsyms.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)) - } - // The current division between scala.reflect.* and scala.tools.nsc.* is pretty // clunky. It is often difficult to have a setting influence something without having // to create it on that side. For this one my strategy is a constant def at the file @@ -393,11 +342,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // Here comes another one... override protected val enableTypeVarExperimentals = settings.Xexperimental.value - // True if -Xscript has been set, indicating a script run. - def isScriptRun = opt.script.isDefined - def getSourceFile(f: AbstractFile): BatchSourceFile = - if (isScriptRun) ScriptSourceFile(f, reader read f) + if (settings.script.isSetByUser) ScriptSourceFile(f, reader read f) else new BatchSourceFile(f, reader read f) def getSourceFile(name: String): SourceFile = { @@ -452,7 +398,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if ((unit ne null) && unit.exists) lastSeenSourceFile = unit.source - if (opt.echoFilenames) + if (settings.debug.value && (settings.verbose.value || currentRun.size < 5)) inform("[running phase " + name + " on " + unit + "]") val unit0 = currentUnit @@ -471,8 +417,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } /** Switch to turn on detailed type logs */ - var printTypings = settings.Ytyperdebug.value - var printInfers = settings.Yinferdebug.value + val printTypings = settings.Ytyperdebug.value + val printInfers = settings.Yinferdebug.value // phaseName = "parser" object syntaxAnalyzer extends { @@ -628,7 +574,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with Inliners - // phaseName = "inlineExceptionHandlers" + // phaseName = "inlinehandlers" object inlineExceptionHandlers extends { val global: Global.this.type = Global.this val runsAfter = List("inliner") @@ -638,7 +584,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // phaseName = "closelim" object closureElimination extends { val global: Global.this.type = Global.this - val runsAfter = List("inlineExceptionHandlers") + val runsAfter = List("inlinehandlers") val runsRightAfter = None } with ClosureElimination @@ -649,13 +595,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with DeadCodeElimination - // phaseName = "jvm", FJBG-based version - object genJVM extends { - val global: Global.this.type = Global.this - val runsAfter = List("dce") - val runsRightAfter = None - } with GenJVM - // phaseName = "jvm", ASM-based version object genASM extends { val global: Global.this.type = Global.this @@ -663,19 +602,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with GenASM - // This phase is optional: only added if settings.make option is given. - // phaseName = "dependencyAnalysis" - object dependencyAnalysis extends { - val global: Global.this.type = Global.this - val runsAfter = List("jvm") - val runsRightAfter = None - } with DependencyAnalysis - // phaseName = "terminal" object terminal extends { val global: Global.this.type = Global.this val phaseName = "terminal" - val runsAfter = List("jvm", "msil") + val runsAfter = List("jvm") val runsRightAfter = None } with SubComponent { private var cache: Option[GlobalPhase] = None @@ -690,13 +621,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } - // phaseName = "SAMPLE PHASE" - object sampleTransform extends { - val global: Global.this.type = Global.this - val runsAfter = List[String]() - val runsRightAfter = None - } with SampleTransform - /** The checkers are for validating the compiler data structures * at phase boundaries. */ @@ -793,13 +717,41 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** A description of the phases that will run */ def phaseDescriptions: String = { - val width = phaseNames map (_.length) max - val fmt = "%" + width + "s %2s %s\n" + val Limit = 16 // phase names should not be absurdly long + val MaxCol = 80 // because some of us edit on green screens + val maxName = (0 /: phaseNames)(_ max _.length) + val width = maxName min Limit + val maxDesc = MaxCol - (width + 6) // descriptions not novels + val fmt = if (settings.verbose.value) s"%${maxName}s %2s %s%n" + else s"%${width}.${width}s %2s %.${maxDesc}s%n" val line1 = fmt.format("phase name", "id", "description") val line2 = fmt.format("----------", "--", "-----------") + + // built-in string precision merely truncates + import java.util.{ Formattable, FormattableFlags, Formatter } + def fmtable(s: String) = new Formattable { + override def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int) { + val p = elliptically(s, precision) + val w = if (width > 0 && p.length < width) { + import FormattableFlags.LEFT_JUSTIFY + val leftly = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY + val sb = new StringBuilder + def pad() = 1 to width - p.length foreach (_ => sb.append(' ')) + if (!leftly) pad() + sb.append(p) + if (leftly) pad() + sb.toString + } else p + formatter.out.append(w) + } + } + def elliptically(s: String, max: Int) = + if (max < 0 || s.length <= max) s + else if (max < 4) s.take(max) + else s.take(max - 3) + "..." val descs = phaseDescriptors.zipWithIndex map { - case (ph, idx) => fmt.format(ph.phaseName, idx + 1, phasesDescMap(ph)) + case (ph, idx) => fmt.format(fmtable(ph.phaseName), idx + 1, fmtable(phasesDescMap(ph))) } line1 :: line2 :: descs mkString } @@ -829,48 +781,16 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Returns List of (phase, value) pairs, including only those * where the value compares unequal to the previous phase's value. */ - def afterEachPhase[T](op: => T): List[(Phase, T)] = { + def afterEachPhase[T](op: => T): List[(Phase, T)] = { // used in tests phaseDescriptors.map(_.ownPhase).filterNot(_ eq NoPhase).foldLeft(List[(Phase, T)]()) { (res, ph) => - val value = afterPhase(ph)(op) + val value = exitingPhase(ph)(op) if (res.nonEmpty && res.head._2 == value) res else ((ph, value)) :: res } reverse } - /** Returns List of ChangeAfterPhase objects, encapsulating those - * phase transitions where the result of the operation gave a different - * list than it had when run during the previous phase. - */ - def changesAfterEachPhase[T](op: => List[T]): List[ChangeAfterPhase[T]] = { - val ops = ((NoPhase, Nil)) :: afterEachPhase(op) - - ops sliding 2 map { - case (_, before) :: (ph, after) :: Nil => - val lost = before filterNot (after contains _) - val gained = after filterNot (before contains _) - ChangeAfterPhase(ph, lost, gained) - case _ => ??? - } toList - } private def numberedPhase(ph: Phase) = "%2d/%s".format(ph.id, ph.name) - case class ChangeAfterPhase[+T](ph: Phase, lost: List[T], gained: List[T]) { - private def mkStr(what: String, xs: List[_]) = ( - if (xs.isEmpty) "" - else xs.mkString(what + " after " + numberedPhase(ph) + " {\n ", "\n ", "\n}\n") - ) - override def toString = mkStr("Lost", lost) + mkStr("Gained", gained) - } - - def describeAfterEachPhase[T](op: => T): List[String] = - afterEachPhase(op) map { case (ph, t) => "[after %-15s] %s".format(numberedPhase(ph), t) } - - def describeAfterEveryPhase[T](op: => T): String = - describeAfterEachPhase(op) map (" " + _ + "\n") mkString - - def printAfterEachPhase[T](op: => T): Unit = - describeAfterEachPhase(op) foreach (m => println(" " + m)) - // ------------ Invalidations --------------------------------- /** Is given package class a system package class that cannot be invalidated? @@ -1093,40 +1013,37 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def currentUnit: CompilationUnit = if (currentRun eq null) NoCompilationUnit else currentRun.currentUnit def currentSource: SourceFile = if (currentUnit.exists) currentUnit.source else lastSeenSourceFile - // TODO - trim these to the absolute minimum. - @inline final def afterErasure[T](op: => T): T = afterPhase(currentRun.erasurePhase)(op) - @inline final def afterPostErasure[T](op: => T): T = afterPhase(currentRun.posterasurePhase)(op) - @inline final def afterExplicitOuter[T](op: => T): T = afterPhase(currentRun.explicitouterPhase)(op) - @inline final def afterFlatten[T](op: => T): T = afterPhase(currentRun.flattenPhase)(op) - @inline final def afterIcode[T](op: => T): T = afterPhase(currentRun.icodePhase)(op) - @inline final def afterMixin[T](op: => T): T = afterPhase(currentRun.mixinPhase)(op) - @inline final def afterPickler[T](op: => T): T = afterPhase(currentRun.picklerPhase)(op) - @inline final def afterRefchecks[T](op: => T): T = afterPhase(currentRun.refchecksPhase)(op) - @inline final def afterSpecialize[T](op: => T): T = afterPhase(currentRun.specializePhase)(op) - @inline final def afterTyper[T](op: => T): T = afterPhase(currentRun.typerPhase)(op) - @inline final def afterUncurry[T](op: => T): T = afterPhase(currentRun.uncurryPhase)(op) - @inline final def beforeErasure[T](op: => T): T = beforePhase(currentRun.erasurePhase)(op) - @inline final def beforeExplicitOuter[T](op: => T): T = beforePhase(currentRun.explicitouterPhase)(op) - @inline final def beforeFlatten[T](op: => T): T = beforePhase(currentRun.flattenPhase)(op) - @inline final def beforeIcode[T](op: => T): T = beforePhase(currentRun.icodePhase)(op) - @inline final def beforeMixin[T](op: => T): T = beforePhase(currentRun.mixinPhase)(op) - @inline final def beforePickler[T](op: => T): T = beforePhase(currentRun.picklerPhase)(op) - @inline final def beforeRefchecks[T](op: => T): T = beforePhase(currentRun.refchecksPhase)(op) - @inline final def beforeSpecialize[T](op: => T): T = beforePhase(currentRun.specializePhase)(op) - @inline final def beforeTyper[T](op: => T): T = beforePhase(currentRun.typerPhase)(op) - @inline final def beforeUncurry[T](op: => T): T = beforePhase(currentRun.uncurryPhase)(op) - - def explainContext(c: analyzer.Context): String = ( - if (c == null) "" else ( - """| context owners: %s - | - |Enclosing block or template: - |%s""".format( - c.owner.ownerChain.takeWhile(!_.isPackageClass).mkString(" -> "), - nodePrinters.nodeToString(c.enclClassOrMethod.tree) - ) - ) + def isGlobalInitialized = ( + definitions.isDefinitionsInitialized + && rootMirror.isMirrorInitialized ) + override def isPastTyper = ( + (curRun ne null) + && isGlobalInitialized // defense against init order issues + && (globalPhase.id > currentRun.typerPhase.id) + ) + + // TODO - trim these to the absolute minimum. + @inline final def exitingErasure[T](op: => T): T = exitingPhase(currentRun.erasurePhase)(op) + @inline final def exitingPostErasure[T](op: => T): T = exitingPhase(currentRun.posterasurePhase)(op) + @inline final def exitingExplicitOuter[T](op: => T): T = exitingPhase(currentRun.explicitouterPhase)(op) + @inline final def exitingFlatten[T](op: => T): T = exitingPhase(currentRun.flattenPhase)(op) + @inline final def exitingMixin[T](op: => T): T = exitingPhase(currentRun.mixinPhase)(op) + @inline final def exitingPickler[T](op: => T): T = exitingPhase(currentRun.picklerPhase)(op) + @inline final def exitingRefchecks[T](op: => T): T = exitingPhase(currentRun.refchecksPhase)(op) + @inline final def exitingSpecialize[T](op: => T): T = exitingPhase(currentRun.specializePhase)(op) + @inline final def exitingTyper[T](op: => T): T = exitingPhase(currentRun.typerPhase)(op) + @inline final def exitingUncurry[T](op: => T): T = exitingPhase(currentRun.uncurryPhase)(op) + @inline final def enteringErasure[T](op: => T): T = enteringPhase(currentRun.erasurePhase)(op) + @inline final def enteringExplicitOuter[T](op: => T): T = enteringPhase(currentRun.explicitouterPhase)(op) + @inline final def enteringFlatten[T](op: => T): T = enteringPhase(currentRun.flattenPhase)(op) + @inline final def enteringIcode[T](op: => T): T = enteringPhase(currentRun.icodePhase)(op) + @inline final def enteringMixin[T](op: => T): T = enteringPhase(currentRun.mixinPhase)(op) + @inline final def enteringPickler[T](op: => T): T = enteringPhase(currentRun.picklerPhase)(op) + @inline final def enteringRefchecks[T](op: => T): T = enteringPhase(currentRun.refchecksPhase)(op) + @inline final def enteringTyper[T](op: => T): T = enteringPhase(currentRun.typerPhase)(op) + @inline final def enteringUncurry[T](op: => T): T = enteringPhase(currentRun.uncurryPhase)(op) + // Owners up to and including the first package class. private def ownerChainString(sym: Symbol): String = ( if (sym == null) "" @@ -1139,9 +1056,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) pairs.toList collect { case (k, v) if v != null => "%20s: %s".format(k, v) } mkString "\n" ) - def explainTree(t: Tree): String = formatExplain( - ) - /** Don't want to introduce new errors trying to report errors, * so swallow exceptions. */ @@ -1155,7 +1069,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val info1 = formatExplain( "while compiling" -> currentSource.path, - "during phase" -> ( if (globalPhase eq phase) phase else "global=%s, atPhase=%s".format(globalPhase, phase) ), + "during phase" -> ( if (globalPhase eq phase) phase else "global=%s, enteringPhase=%s".format(globalPhase, phase) ), "library version" -> scala.util.Properties.versionString, "compiler version" -> Properties.versionString, "reconstructed args" -> settings.recreateArgs.mkString(" ") @@ -1171,7 +1085,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val info3: List[String] = ( ( List("== Enclosing template or block ==", nodePrinters.nodeToString(enclosing).trim) ) ++ ( if (tpe eq null) Nil else List("== Expanded type of tree ==", typeDeconstruct.show(tpe)) ) - ++ ( if (!opt.debug) Nil else List("== Current unit body ==", nodePrinters.nodeToString(currentUnit.body)) ) + ++ ( if (!settings.debug.value) Nil else List("== Current unit body ==", nodePrinters.nodeToString(currentUnit.body)) ) ++ ( List(errorMessage) ) ) @@ -1187,7 +1101,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def echoPhaseSummary(ph: Phase) = { /** Only output a summary message under debug if we aren't echoing each file. */ - if (opt.debug && !opt.echoFilenames) + if (settings.debug.value && !(settings.verbose.value || currentRun.size < 5)) inform("[running phase " + ph.name + " on " + currentRun.size + " compilation units]") } @@ -1198,12 +1112,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (option.value) reporter.warning(pos, msg) else if (!(warnings contains pos)) warnings += ((pos, msg)) def summarize() = - if (option.isDefault && warnings.nonEmpty) - reporter.warning(NoPosition, "there were %d %s warning(s); re-run with %s for details".format(warnings.size, what, option.name)) + if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings.value)) + warning("there were %d %s warning(s); re-run with %s for details".format(warnings.size, what, option.name)) } def newUnitParser(code: String) = new syntaxAnalyzer.UnitParser(newCompilationUnit(code)) - def newUnitScanner(code: String) = new syntaxAnalyzer.UnitScanner(newCompilationUnit(code)) def newCompilationUnit(code: String) = new CompilationUnit(newSourceFile(code)) def newSourceFile(code: String) = new BatchSourceFile("<console>", code) @@ -1226,9 +1139,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings) val allConditionalWarnings = List(deprecationWarnings0, uncheckedWarnings0, featureWarnings, inlinerWarnings) - // for sbt's benefit - def uncheckedWarnings: List[(Position, String)] = uncheckedWarnings0.warnings.toList - def deprecationWarnings: List[(Position, String)] = deprecationWarnings0.warnings.toList + def uncheckedWarnings: List[(Position, String)] = uncheckedWarnings0.warnings.toList // used in sbt + def deprecationWarnings: List[(Position, String)] = deprecationWarnings0.warnings.toList // used in sbt var reportedFeature = Set[Symbol]() @@ -1238,9 +1150,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Have we already supplemented the error message of a compiler crash? */ private[nsc] final var supplementedError = false - /** To be initialized from firstPhase. */ - private var terminalPhase: Phase = NoPhase - private val unitbuf = new mutable.ListBuffer[CompilationUnit] val compiledFiles = new mutable.HashSet[String] @@ -1401,7 +1310,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val namerPhase = phaseNamed("namer") // val packageobjectsPhase = phaseNamed("packageobjects") val typerPhase = phaseNamed("typer") - val inlineclassesPhase = phaseNamed("inlineclasses") + // val inlineclassesPhase = phaseNamed("inlineclasses") // val superaccessorsPhase = phaseNamed("superaccessors") val picklerPhase = phaseNamed("pickler") val refchecksPhase = phaseNamed("refchecks") @@ -1414,22 +1323,19 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val erasurePhase = phaseNamed("erasure") val posterasurePhase = phaseNamed("posterasure") // val lazyvalsPhase = phaseNamed("lazyvals") - val lambdaliftPhase = phaseNamed("lambdalift") + // val lambdaliftPhase = phaseNamed("lambdalift") // val constructorsPhase = phaseNamed("constructors") val flattenPhase = phaseNamed("flatten") val mixinPhase = phaseNamed("mixin") val cleanupPhase = phaseNamed("cleanup") val icodePhase = phaseNamed("icode") val inlinerPhase = phaseNamed("inliner") - val inlineExceptionHandlersPhase = phaseNamed("inlineExceptionHandlers") + val inlineExceptionHandlersPhase = phaseNamed("inlinehandlers") val closelimPhase = phaseNamed("closelim") val dcePhase = phaseNamed("dce") - val jvmPhase = phaseNamed("jvm") - // val msilPhase = phaseNamed("msil") + // val jvmPhase = phaseNamed("jvm") def runIsAt(ph: Phase) = globalPhase.id == ph.id - def runIsPast(ph: Phase) = globalPhase.id > ph.id - // def runIsAtBytecodeGen = (runIsAt(jvmPhase) || runIsAt(msilPhase)) def runIsAtOptimiz = { runIsAt(inlinerPhase) || // listing phases in full for robustness when -Ystop-after has been given. runIsAt(inlineExceptionHandlersPhase) || @@ -1472,7 +1378,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def compiles(sym: Symbol): Boolean = if (sym == NoSymbol) false else if (symSource.isDefinedAt(sym)) true - else if (!sym.owner.isPackageClass) compiles(sym.enclosingTopLevelClass) + else if (!sym.isTopLevel) compiles(sym.enclosingTopLevelClass) else if (sym.isModuleClass) compiles(sym.sourceModule) else false @@ -1498,8 +1404,24 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } - private def showMembers() = - opt.showNames foreach (x => showDef(x, opt.declsOnly, globalPhase)) + private def showMembers() = { + // Allows for syntax like scalac -Xshow-class Random@erasure,typer + def splitClassAndPhase(str: String, term: Boolean): Name = { + def mkName(s: String) = if (term) newTermName(s) else newTypeName(s) + (str indexOf '@') match { + case -1 => mkName(str) + case idx => + val phasePart = str drop (idx + 1) + settings.Yshow.tryToSetColon(phasePart split ',' toList) + mkName(str take idx) + } + } + if (settings.Xshowcls.isSetByUser) + showDef(splitClassAndPhase(settings.Xshowcls.value, false), false, globalPhase) + + if (settings.Xshowobj.isSetByUser) + showDef(splitClassAndPhase(settings.Xshowobj.value, true), false, globalPhase) + } // Similarly, this will only be created under -Yshow-syms. object trackerFactory extends SymbolTrackers { @@ -1507,7 +1429,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) lazy val trackers = currentRun.units.toList map (x => SymbolTracker(x)) def snapshot() = { inform("\n[[symbol layout at end of " + phase + "]]") - afterPhase(phase) { + exitingPhase(phase) { trackers foreach { t => t.snapshot() inform(t.show("Heading from " + phase.prev.name + " to " + phase.name)) @@ -1517,6 +1439,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } def reportCompileErrors() { + if (!reporter.hasErrors && reporter.hasWarnings && settings.fatalWarnings.value) + globalError("No warnings can be incurred under -Xfatal-warnings.") + if (reporter.hasErrors) { for ((sym, file) <- symSource.iterator) { sym.reset(new loaders.SourcefileLoader(file)) @@ -1535,9 +1460,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } /** Compile list of source files */ - def compileSources(_sources: List[SourceFile]) { - val depSources = dependencyAnalysis calculateFiles _sources.distinct - val sources = coreClassesFirst(depSources) + def compileSources(sources: List[SourceFile]) { // there is a problem already, e.g. a plugin was passed a bad option if (reporter.hasErrors) return @@ -1555,12 +1478,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def compileUnits(units: List[CompilationUnit], fromPhase: Phase) { try compileUnitsInternal(units, fromPhase) catch { case ex: Throwable => - val shown = if (settings.verbose.value) { - val pw = new java.io.PrintWriter(new java.io.StringWriter) - ex.printStackTrace(pw) - pw.toString - } else ex.getClass.getName - // ex.printStackTrace(Console.out) // DEBUG for fsc, note that error stacktraces do not print in fsc + val shown = if (settings.verbose.value) + stackTraceString(ex) + else + stackTraceHeadString(ex) // note that error stacktraces do not print in fsc + globalError(supplementErrorMessage("uncaught exception during compilation: " + shown)) throw ex } @@ -1583,37 +1505,40 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // progress update informTime(globalPhase.description, startTime) - - if (opt.writeICodeAtICode || (opt.printPhase && runIsAtOptimiz)) { + val shouldWriteIcode = ( + (settings.writeICode.isSetByUser && (settings.writeICode containsPhase globalPhase)) + || (!settings.Xprint.doAllPhases && (settings.Xprint containsPhase globalPhase) && runIsAtOptimiz) + ) + if (shouldWriteIcode) { // Write *.icode files when -Xprint-icode or -Xprint:<some-optimiz-phase> was given. writeICode() - } else if (opt.printPhase || opt.printLate && runIsAt(cleanupPhase)) { + } else if ((settings.Xprint containsPhase globalPhase) || settings.printLate.value && runIsAt(cleanupPhase)) { // print trees - if (opt.showTrees) nodePrinters.printAll() + if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) nodePrinters.printAll() else printAllUnits() } // print the symbols presently attached to AST nodes - if (opt.showSymbols) + if (settings.Yshowsyms.value) trackerFactory.snapshot() // print members - if (opt.showPhase) + if (settings.Yshow containsPhase globalPhase) showMembers() // browse trees with swing tree viewer - if (opt.browsePhase) + if (settings.browse containsPhase globalPhase) treeBrowser browse (phase.name, units) // move the pointer globalPhase = globalPhase.next // run tree/icode checkers - if (opt.checkPhase) + if (settings.check containsPhase globalPhase.prev) runCheckers() // output collected statistics - if (opt.printStats) + if (settings.Ystatistics.value) statistics.print(phase) advancePhase @@ -1623,23 +1548,19 @@ class Global(var currentSettings: Settings, var reporter: Reporter) units map (_.body) foreach (traceSymbols recordSymbolsInTree _) // In case no phase was specified for -Xshow-class/object, show it now for sure. - if (opt.noShow) + if (settings.Yshow.isDefault) showMembers() reportCompileErrors() symSource.keys foreach (x => resetPackageClass(x.owner)) informTime("total", startTime) - // record dependency data - if (!dependencyAnalysis.off) - dependencyAnalysis.saveDependencyAnalysis() - // Clear any sets or maps created via perRunCaches. perRunCaches.clearAll() // Reset project if (!stopPhase("namer")) { - atPhase(namerPhase) { + enteringPhase(namerPhase) { resetProjectClasses(RootClass) } } @@ -1655,7 +1576,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def compile(filenames: List[String]) { try { val sources: List[SourceFile] = - if (isScriptRun && filenames.size > 1) returning(Nil)(_ => globalError("can only compile one script at a time")) + if (settings.script.isSetByUser && filenames.size > 1) returning(Nil)(_ => globalError("can only compile one script at a time")) else filenames map getSourceFile compileSources(sources) @@ -1679,7 +1600,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (firstPhase ne null) { // we might get here during initialization, is a source is newer than the binary val maxId = math.max(globalPhase.id, typerPhase.id) firstPhase.iterator takeWhile (_.id < maxId) foreach (ph => - atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit)) + enteringPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit)) refreshProgress } } @@ -1688,56 +1609,16 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * is needed for?) */ private def resetPackageClass(pclazz: Symbol) { - atPhase(firstPhase) { - pclazz.setInfo(atPhase(typerPhase)(pclazz.info)) + enteringPhase(firstPhase) { + pclazz.setInfo(enteringPhase(typerPhase)(pclazz.info)) } if (!pclazz.isRoot) resetPackageClass(pclazz.owner) } - - /** - * Re-orders the source files to - * 1. This Space Intentionally Left Blank - * 2. LowPriorityImplicits / EmbeddedControls (i.e. parents of Predef) - * 3. the rest - * - * 1 is to avoid cyclic reference errors. - * 2 is due to the following. When completing "Predef" (*), typedIdent is called - * for its parents (e.g. "LowPriorityImplicits"). typedIdent checks whether - * the symbol reallyExists, which tests if the type of the symbol after running - * its completer is != NoType. - * If the "namer" phase has not yet run for "LowPriorityImplicits", the symbol - * has a SourcefileLoader as type. Calling "doComplete" on it does nothing at - * all, because the source file is part of the files to be compiled anyway. - * So the "reallyExists" test will return "false". - * Only after the namer, the symbol has a lazy type which actually computes - * the info, and "reallyExists" behaves as expected. - * So we need to make sure that the "namer" phase is run on predef's parents - * before running it on predef. - * - * (*) Predef is completed early when calling "mkAttributedRef" during the - * addition of "import Predef._" to sourcefiles. So this situation can't - * happen for user classes. - * - */ - private def coreClassesFirst(files: List[SourceFile]) = { - val goLast = 4 - def rank(f: SourceFile) = { - if (f.file.container.name != "scala") goLast - else f.file.name match { - case "LowPriorityImplicits.scala" => 2 - case "StandardEmbeddings.scala" => 2 - case "EmbeddedControls.scala" => 2 - case "Predef.scala" => 3 /* Predef.scala before Any.scala, etc. */ - case _ => goLast - } - } - files sortBy rank - } } // class Run def printAllUnits() { print("[[syntax trees at end of %25s]]".format(phase)) - afterPhase(phase)(currentRun.units foreach { unit => + exitingPhase(phase)(currentRun.units foreach { unit => nodePrinters showUnit unit }) } @@ -1746,7 +1627,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) */ def showDef(fullName: Name, declsOnly: Boolean, ph: Phase) = { val boringOwners = Set[Symbol](definitions.AnyClass, definitions.AnyRefClass, definitions.ObjectClass) - def phased[T](body: => T): T = afterPhase(ph)(body) + def phased[T](body: => T): T = exitingPhase(ph)(body) def boringMember(sym: Symbol) = boringOwners(sym.owner) def symString(sym: Symbol) = if (sym.isTerm) sym.defString else sym.toString @@ -1792,7 +1673,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val printer = new icodes.TextPrinter(null, icodes.linearizer) icodes.classes.values.foreach((cls) => { val suffix = if (cls.symbol.hasModuleFlag) "$.icode" else ".icode" - var file = getFile(cls.symbol, suffix) + val file = getFile(cls.symbol, suffix) // if (file.exists()) // file = new File(file.getParentFile(), file.getName() + "1") try { @@ -1802,25 +1683,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter) informProgress("wrote " + file) } catch { case ex: IOException => - if (opt.debug) ex.printStackTrace() + if (settings.debug.value) ex.printStackTrace() globalError("could not write file " + file) } }) } - // In order to not outright break code which overrides onlyPresentation (like sbt 0.7.5.RC0) - // I restored and deprecated it. That would be enough to avoid the compilation - // failure, but the override wouldn't accomplish anything. So now forInteractive - // and forScaladoc default to onlyPresentation, which is the same as defaulting - // to false except in old code. The downside is that this leaves us calling a - // deprecated method: but I see no simple way out, so I leave it for now. - def forJVM = opt.jvm - override def forMSIL = opt.msil - def forInteractive = onlyPresentation - def forScaladoc = onlyPresentation + def forInteractive = false + def forScaladoc = false def createJavadoc = false - - @deprecated("Use forInteractive or forScaladoc, depending on what you're after", "2.9.0") - def onlyPresentation = false } object Global { diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala index 7d112dfb3e..c3c919fae4 100644 --- a/src/compiler/scala/tools/nsc/Main.scala +++ b/src/compiler/scala/tools/nsc/Main.scala @@ -7,15 +7,10 @@ package scala.tools.nsc import java.io.File import File.pathSeparator - -import scala.tools.nsc.interactive.{ RefinedBuildManager, SimpleBuildManager } import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} -import scala.reflect.internal.util.{ BatchSourceFile, FakePos } //{Position} -import Properties.msilLibPath /** The main class for NSC, a compiler for the programming - * language Scala. + * language Scala. */ object Main extends Driver with EvalLoop { @@ -46,28 +41,7 @@ object Main extends Driver with EvalLoop { askShutdown false } - else if (settings.Ybuilderdebug.value != "none") { - def fileSet(files : List[String]) = Set.empty ++ (files map AbstractFile.getFile) - - val buildManager = settings.Ybuilderdebug.value match { - case "simple" => new SimpleBuildManager(settings) - case _ => new RefinedBuildManager(settings) - } - buildManager.addSourceFiles(fileSet(command.files)) - - // enter resident mode - loop { line => - val args = line.split(' ').toList - val command = new CompilerCommand(args.toList, settings) - buildManager.update(fileSet(command.files), Set.empty) - } - false - } - else { - if (settings.target.value == "msil") - msilLibPath foreach (x => settings.assemrefs.value += (pathSeparator + x)) - true - } + else true override def newCompiler(): Global = if (settings.Yrangepos.value) new Global(settings, reporter) with interactive.RangePositions diff --git a/src/compiler/scala/tools/nsc/MainBench.scala b/src/compiler/scala/tools/nsc/MainBench.scala index f18ff19d7d..03190a63f3 100644 --- a/src/compiler/scala/tools/nsc/MainBench.scala +++ b/src/compiler/scala/tools/nsc/MainBench.scala @@ -5,28 +5,20 @@ package scala.tools.nsc -import java.io.File -import File.pathSeparator - -import scala.tools.nsc.interactive.{ RefinedBuildManager, SimpleBuildManager } -import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} -import scala.reflect.internal.util.{ BatchSourceFile, FakePos } //{Position} -import Properties.{ versionString, copyrightString, residentPromptString, msilLibPath } import scala.reflect.internal.util.Statistics /** The main class for NSC, a compiler for the programming * language Scala. */ object MainBench extends Driver with EvalLoop { - + lazy val theCompiler = Global(settings, reporter) - + override def newCompiler() = theCompiler - + val NIter = 50 val NBest = 10 - + override def main(args: Array[String]) = { val times = new Array[Long](NIter) var start = System.nanoTime() diff --git a/src/compiler/scala/tools/nsc/MainGenericRunner.scala b/src/compiler/scala/tools/nsc/MainGenericRunner.scala index e4a20b4a8c..adb03ca374 100644 --- a/src/compiler/scala/tools/nsc/MainGenericRunner.scala +++ b/src/compiler/scala/tools/nsc/MainGenericRunner.scala @@ -5,8 +5,6 @@ package scala.tools.nsc -import java.net.URL -import scala.tools.util.PathResolver import io.{ File } import util.{ ClassPath, ScalaClassLoader } import Properties.{ versionString, copyrightString } diff --git a/src/compiler/scala/tools/nsc/ObjectRunner.scala b/src/compiler/scala/tools/nsc/ObjectRunner.scala index f5123513c4..95264aeda6 100644 --- a/src/compiler/scala/tools/nsc/ObjectRunner.scala +++ b/src/compiler/scala/tools/nsc/ObjectRunner.scala @@ -8,15 +8,9 @@ package scala.tools.nsc import java.net.URL import util.ScalaClassLoader -import java.lang.reflect.InvocationTargetException import util.Exceptional.unwrap trait CommonRunner { - /** Check whether a class with the specified name - * exists on the specified class path. */ - def classExists(urls: List[URL], objectName: String): Boolean = - ScalaClassLoader.classExists(urls, objectName) - /** Run a given object, specified by name, using a * specified classpath and argument list. * diff --git a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala index caf6ad14cf..8c54c4a1b0 100644 --- a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala @@ -26,8 +26,8 @@ class OfflineCompilerCommand(arguments: List[String], settings: FscSettings) ext // instead of whatever it's supposed to be doing. val baseDirectory = { val pwd = System.getenv("PWD") - if (pwd != null && !isWin) Directory(pwd) - else Directory.Current getOrElse Directory("/") + if (pwd == null || isWin) Directory.Current getOrElse Directory("/") + else Directory(pwd) } currentDir.value = baseDirectory.path } diff --git a/src/compiler/scala/tools/nsc/PhaseAssembly.scala b/src/compiler/scala/tools/nsc/PhaseAssembly.scala index cff3590b3f..67dc1e3b66 100644 --- a/src/compiler/scala/tools/nsc/PhaseAssembly.scala +++ b/src/compiler/scala/tools/nsc/PhaseAssembly.scala @@ -55,7 +55,7 @@ trait PhaseAssembly { * node object does not exist, then create it. */ def getNodeByPhase(phs: SubComponent): Node = { - var node: Node = getNodeByPhase(phs.phaseName) + val node: Node = getNodeByPhase(phs.phaseName) node.phaseobj match { case None => node.phaseobj = Some(List[SubComponent](phs)) @@ -75,7 +75,7 @@ trait PhaseAssembly { * list of the nodes */ def softConnectNodes(frm: Node, to: Node) { - var e = new Edge(frm, to, false) + val e = new Edge(frm, to, false) this.edges += e frm.after += e @@ -87,7 +87,7 @@ trait PhaseAssembly { * list of the nodes */ def hardConnectNodes(frm: Node, to: Node) { - var e = new Edge(frm, to, true) + val e = new Edge(frm, to, true) this.edges += e frm.after += e @@ -164,7 +164,7 @@ trait PhaseAssembly { } else { - var promote = hl.to.before.filter(e => (!e.hard)) + val promote = hl.to.before.filter(e => (!e.hard)) hl.to.before.clear sanity foreach (edge => hl.to.before += edge) for (edge <- promote) { @@ -182,7 +182,7 @@ trait PhaseAssembly { /** Remove all nodes in the given graph, that have no phase object * Make sure to clean up all edges when removing the node object - * <code>Inform</code> with warnings, if an external phase has a + * `Inform` with warnings, if an external phase has a * dependency on something that is dropped. */ def removeDanglingNodes() { @@ -245,7 +245,7 @@ trait PhaseAssembly { for (phs <- phsSet) { - var fromnode = graph.getNodeByPhase(phs) + val fromnode = graph.getNodeByPhase(phs) phs.runsRightAfter match { case None => @@ -306,7 +306,7 @@ trait PhaseAssembly { sbuf.append("\"" + node.allPhaseNames + "(" + node.level + ")" + "\" [color=\"#0000ff\"]\n") } sbuf.append("}\n") - var out = new BufferedWriter(new FileWriter(filename)) + val out = new BufferedWriter(new FileWriter(filename)) out.write(sbuf.toString) out.flush() out.close() diff --git a/src/compiler/scala/tools/nsc/Phases.scala b/src/compiler/scala/tools/nsc/Phases.scala index 0901ade2d7..e379afce9b 100644 --- a/src/compiler/scala/tools/nsc/Phases.scala +++ b/src/compiler/scala/tools/nsc/Phases.scala @@ -5,7 +5,6 @@ package scala.tools.nsc -import symtab.Flags import scala.reflect.internal.util.TableDef import scala.language.postfixOps @@ -22,7 +21,6 @@ object Phases { } val values = new Array[Cell](MaxPhases + 1) def results = values filterNot (_ == null) - def apply(ph: Phase): T = values(ph.id).value def update(ph: Phase, value: T): Unit = values(ph.id) = Cell(ph, value) } /** A class for recording the elapsed time of each phase in the @@ -40,7 +38,6 @@ object Phases { >> ("ms" -> (_.value)) >+ " " << ("share" -> (_.value.toDouble * 100 / total formatted "%.2f")) } - def formatted = "" + table() } } diff --git a/src/compiler/scala/tools/nsc/Properties.scala b/src/compiler/scala/tools/nsc/Properties.scala index 55fd196716..feb4ded2f2 100644 --- a/src/compiler/scala/tools/nsc/Properties.scala +++ b/src/compiler/scala/tools/nsc/Properties.scala @@ -16,10 +16,6 @@ object Properties extends scala.util.PropertiesTrait { def residentPromptString = scalaPropOrElse("resident.prompt", "\nnsc> ") def shellPromptString = scalaPropOrElse("shell.prompt", "\nscala> ") - // settings based on system properties - def msilLibPath = propOrNone("msil.libpath") - // derived values def isEmacsShell = propOrEmpty("env.emacs") != "" - def fileEndings = fileEndingString.split("""\|""").toList } diff --git a/src/compiler/scala/tools/nsc/ScalaDoc.scala b/src/compiler/scala/tools/nsc/ScalaDoc.scala index ba434bc797..14b76b53b3 100644 --- a/src/compiler/scala/tools/nsc/ScalaDoc.scala +++ b/src/compiler/scala/tools/nsc/ScalaDoc.scala @@ -10,7 +10,6 @@ import java.io.File.pathSeparator import scala.tools.nsc.doc.DocFactory import scala.tools.nsc.reporters.ConsoleReporter import scala.reflect.internal.util.FakePos -import Properties.msilLibPath /** The main class for scaladoc, a front-end for the Scala compiler * that generates documentation from source files. @@ -42,12 +41,8 @@ class ScalaDoc { reporter.warning(null, "Phases are restricted when using Scaladoc") else if (docSettings.help.value || !hasFiles) reporter.echo(command.usageMsg) - else try { - if (docSettings.target.value == "msil") - msilLibPath foreach (x => docSettings.assemrefs.value += (pathSeparator + x)) - - new DocFactory(reporter, docSettings) document command.files - } + else + try { new DocFactory(reporter, docSettings) document command.files } catch { case ex @ FatalError(msg) => if (docSettings.debug.value) ex.printStackTrace() diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index 107c4b3df3..92b2dc79ed 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -7,7 +7,6 @@ package scala.tools.nsc import io.{ Directory, File, Path } import java.io.IOException -import java.net.URL import scala.tools.nsc.reporters.{Reporter,ConsoleReporter} import util.Exceptional.unwrap @@ -49,25 +48,12 @@ class ScriptRunner extends HasCompileSocket { case x => x } - def isScript(settings: Settings) = settings.script.value != "" - /** Choose a jar filename to hold the compiled version of a script. */ private def jarFileFor(scriptFile: String)= File( if (scriptFile endsWith ".jar") scriptFile else scriptFile.stripSuffix(".scala") + ".jar" ) - /** Read the entire contents of a file as a String. */ - private def contentsOfFile(filename: String) = File(filename).slurp() - - /** Split a fully qualified object name into a - * package and an unqualified object name */ - private def splitObjectName(fullname: String): (Option[String], String) = - (fullname lastIndexOf '.') match { - case -1 => (None, fullname) - case idx => (Some(fullname take idx), fullname drop (idx + 1)) - } - /** Compile a script using the fsc compilation daemon. */ private def compileWithDaemon(settings: GenericRunnerSettings, scriptFileIn: String) = { diff --git a/src/compiler/scala/tools/nsc/SubComponent.scala b/src/compiler/scala/tools/nsc/SubComponent.scala index a0468a22b9..9b8582ae02 100644 --- a/src/compiler/scala/tools/nsc/SubComponent.scala +++ b/src/compiler/scala/tools/nsc/SubComponent.scala @@ -47,8 +47,8 @@ abstract class SubComponent { private var ownPhaseCache: WeakReference[Phase] = new WeakReference(null) private var ownPhaseRunId = global.NoRunId - @inline final def beforeOwnPhase[T](op: => T) = global.beforePhase(ownPhase)(op) - @inline final def afterOwnPhase[T](op: => T) = global.afterPhase(ownPhase)(op) + @inline final def beforeOwnPhase[T](op: => T) = global.enteringPhase(ownPhase)(op) + @inline final def afterOwnPhase[T](op: => T) = global.exitingPhase(ownPhase)(op) /** The phase corresponding to this subcomponent in the current compiler run */ def ownPhase: Phase = { diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index e635c5e87d..7e6a323d3d 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -7,10 +7,7 @@ package scala.tools.nsc package ast import symtab._ -import reporters._ -import scala.reflect.internal.util.{Position, NoPosition} import util.DocStrings._ -import scala.reflect.internal.Chars._ import scala.collection.mutable /* @@ -124,8 +121,6 @@ trait DocComments { self: Global => getDocComment(sym) map getUseCases getOrElse List() } - def useCases(sym: Symbol): List[(Symbol, String, Position)] = useCases(sym, sym.enclClass) - /** Returns the javadoc format of doc comment string `s`, including wiki expansion */ def toJavaDoc(s: String): String = expandWiki(s) @@ -325,7 +320,7 @@ trait DocComments { self: Global => } /** Expand variable occurrences in string `str`, until a fix point is reached or - * a expandLimit is exceeded. + * an expandLimit is exceeded. * * @param str The string to be expanded * @param sym The symbol for which doc comments are generated @@ -465,7 +460,7 @@ trait DocComments { self: Global => //val (classes, pkgs) = site.ownerChain.span(!_.isPackageClass) //val sites = (classes ::: List(pkgs.head, rootMirror.RootClass))) //findIn(sites) - findIn(site.ownerChain ::: List(definitions.EmptyPackage)) + findIn(site.ownerChain ::: List(rootMirror.EmptyPackage)) } def getType(str: String, variable: String): Type = { diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index deea4de707..602366a201 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -168,6 +168,13 @@ abstract class NodePrinters { } } + def typeApplyCommon(tree: Tree, fun: Tree, args: List[Tree]) { + printMultiline(tree) { + traverse(fun) + traverseList("[]", "type argument")(args) + } + } + def treePrefix(tree: Tree) = showPosition(tree) + tree.productPrefix def printMultiline(tree: Tree)(body: => Unit) { printMultiline(treePrefix(tree), showAttributes(tree))(body) @@ -203,9 +210,11 @@ abstract class NodePrinters { showPosition(tree) tree match { - case AppliedTypeTree(tpt, args) => applyCommon(tree, tpt, args) - case ApplyDynamic(fun, args) => applyCommon(tree, fun, args) - case Apply(fun, args) => applyCommon(tree, fun, args) + case ApplyDynamic(fun, args) => applyCommon(tree, fun, args) + case Apply(fun, args) => applyCommon(tree, fun, args) + + case TypeApply(fun, args) => typeApplyCommon(tree, fun, args) + case AppliedTypeTree(tpt, args) => typeApplyCommon(tree, tpt, args) case Throw(Ident(name)) => printSingle(tree, name) @@ -312,11 +321,6 @@ abstract class NodePrinters { } case This(qual) => printSingle(tree, qual) - case TypeApply(fun, args) => - printMultiline(tree) { - traverse(fun) - traverseList("[]", "type argument")(args) - } case tt @ TypeTree() => println(showTypeTree(tt)) diff --git a/src/compiler/scala/tools/nsc/ast/Positions.scala b/src/compiler/scala/tools/nsc/ast/Positions.scala index d8fb632f73..e7bd5da9dd 100644 --- a/src/compiler/scala/tools/nsc/ast/Positions.scala +++ b/src/compiler/scala/tools/nsc/ast/Positions.scala @@ -1,7 +1,7 @@ package scala.tools.nsc package ast -import scala.reflect.internal.util.{ SourceFile, Position, OffsetPosition, NoPosition } +import scala.reflect.internal.util.{ SourceFile, OffsetPosition } trait Positions extends scala.reflect.internal.Positions { self: Global => @@ -20,7 +20,7 @@ trait Positions extends scala.reflect.internal.Positions { // 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)) () + if (!c.canHaveAttrs) () else if (c.pos == NoPosition) { reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase) inform("parent: " + treeSymStatus(t)) diff --git a/src/compiler/scala/tools/nsc/ast/Printers.scala b/src/compiler/scala/tools/nsc/ast/Printers.scala index 83222a24b4..b9f348632a 100644 --- a/src/compiler/scala/tools/nsc/ast/Printers.scala +++ b/src/compiler/scala/tools/nsc/ast/Printers.scala @@ -7,8 +7,6 @@ package scala.tools.nsc package ast import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } -import symtab.Flags._ -import symtab.SymbolTable trait Printers extends scala.reflect.internal.Printers { this: Global => @@ -202,91 +200,12 @@ trait Printers extends scala.reflect.internal.Printers { this: Global => override def printTree(tree: Tree) { print(safe(tree)) } } - class TreeMatchTemplate { - // non-trees defined in Trees - // - // case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) - // case class Modifiers(flags: Long, privateWithin: Name, annotations: List[Tree], positions: Map[Long, Position]) - // - def apply(t: Tree): Unit = t match { - // eliminated by typer - case Annotated(annot, arg) => - case AssignOrNamedArg(lhs, rhs) => - case DocDef(comment, definition) => - case Import(expr, selectors) => - - // eliminated by refchecks - case ModuleDef(mods, name, impl) => - case TypeTreeWithDeferredRefCheck() => - - // eliminated by erasure - case TypeDef(mods, name, tparams, rhs) => - case Typed(expr, tpt) => - - // eliminated by cleanup - case ApplyDynamic(qual, args) => - - // eliminated by explicitouter - case Alternative(trees) => - case Bind(name, body) => - case CaseDef(pat, guard, body) => - case Star(elem) => - case UnApply(fun, args) => - - // eliminated by lambdalift - case Function(vparams, body) => - - // eliminated by uncurry - case AppliedTypeTree(tpt, args) => - case CompoundTypeTree(templ) => - case ExistentialTypeTree(tpt, whereClauses) => - case SelectFromTypeTree(qual, selector) => - case SingletonTypeTree(ref) => - case TypeBoundsTree(lo, hi) => - - // survivors - case Apply(fun, args) => - case ArrayValue(elemtpt, trees) => - case Assign(lhs, rhs) => - case Block(stats, expr) => - case ClassDef(mods, name, tparams, impl) => - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - case EmptyTree => - case Ident(name) => - case If(cond, thenp, elsep) => - case LabelDef(name, params, rhs) => - case Literal(value) => - case Match(selector, cases) => - case New(tpt) => - case PackageDef(pid, stats) => - case Return(expr) => - case Select(qualifier, selector) => - case Super(qual, mix) => - case Template(parents, self, body) => - case This(qual) => - case Throw(expr) => - case Try(block, catches, finalizer) => - case TypeApply(fun, args) => - case TypeTree() => - case ValDef(mods, name, tpt, rhs) => - - // missing from the Trees comment - case Parens(args) => // only used during parsing - case SelectFromArray(qual, name, erasure) => // only used during erasure - } - } - def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) def asCompactDebugString(t: Tree): String = render(t, newCompactTreePrinter, true, true, true) def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) - def newStandardTreePrinter(stream: OutputStream): TreePrinter = newStandardTreePrinter(new PrintWriter(stream)) - def newStandardTreePrinter(): TreePrinter = newStandardTreePrinter(new PrintWriter(ConsoleWriter)) - def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer) - def newCompactTreePrinter(stream: OutputStream): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(stream)) - def newCompactTreePrinter(): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(ConsoleWriter)) override def newTreePrinter(writer: PrintWriter): TreePrinter = if (settings.Ycompacttrees.value) newCompactTreePrinter(writer) diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index 5c954096f4..30a9348fb0 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -16,8 +16,6 @@ import javax.swing.tree._ import scala.concurrent.Lock import scala.text._ -import symtab.Flags._ -import symtab.SymbolTable import scala.language.implicitConversions /** @@ -509,7 +507,7 @@ abstract class TreeBrowsers { /** Return a textual representation of this t's symbol */ def symbolText(t: Tree): String = { val prefix = - if (t.hasSymbol) "[has] " + if (t.hasSymbolField) "[has] " else if (t.isDef) "[defines] " else "" @@ -529,10 +527,9 @@ abstract class TreeBrowsers { * attributes */ def symbolAttributes(t: Tree): String = { val s = t.symbol - var att = "" if ((s ne null) && (s != NoSymbol)) { - var str = flagsToString(s.flags) + var str = s.flagString if (s.isStaticMember) str = str + " isStatic "; (str + " annotations: " + s.annotations.mkString("", " ", "") + (if (s.isTypeSkolem) "\ndeSkolemized annotations: " + s.deSkolemize.annotations.mkString("", " ", "") else "")) diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index 9a5b92e795..1c6bba19b3 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -21,7 +21,6 @@ trait TreeDSL { import global._ import definitions._ - import gen.{ scalaDot } object CODE { // Add a null check to a Tree => Tree function @@ -31,24 +30,17 @@ trait TreeDSL { def returning[T](x: T)(f: T => Unit): T = util.returning(x)(f) object LIT extends (Any => Literal) { + def typed(x: Any) = apply(x) setType ConstantType(Constant(x)) def apply(x: Any) = Literal(Constant(x)) def unapply(x: Any) = condOpt(x) { case Literal(Constant(value)) => value } } - // You might think these could all be vals, but empirically I have found that - // at least in the case of UNIT the compiler breaks if you re-use trees. - // However we need stable identifiers to have attractive pattern matching. - // So it's inconsistent until I devise a better way. - val TRUE = LIT(true) - val FALSE = LIT(false) - val ZERO = LIT(0) - def NULL = LIT(null) - def UNIT = LIT(()) - - // for those preferring boring, predictable lives, without the thrills of tree-sharing - // (but with the perk of typed trees) - def TRUE_typed = LIT(true) setType ConstantType(Constant(true)) - def FALSE_typed = LIT(false) setType ConstantType(Constant(false)) + // Boring, predictable trees. + def TRUE = LIT typed true + def FALSE = LIT typed false + def ZERO = LIT(0) + def NULL = LIT(null) + def UNIT = LIT(()) object WILD { def empty = Ident(nme.WILDCARD) @@ -85,16 +77,12 @@ trait TreeDSL { def ANY_EQ (other: Tree) = OBJ_EQ(other AS ObjectClass.tpe) def ANY_== (other: Tree) = fn(target, Any_==, other) def ANY_!= (other: Tree) = fn(target, Any_!=, other) - def OBJ_== (other: Tree) = fn(target, Object_==, other) def OBJ_!= (other: Tree) = fn(target, Object_!=, other) def OBJ_EQ (other: Tree) = fn(target, Object_eq, other) def OBJ_NE (other: Tree) = fn(target, Object_ne, other) - def INT_| (other: Tree) = fn(target, getMember(IntClass, nme.OR), other) - def INT_& (other: Tree) = fn(target, getMember(IntClass, nme.AND), other) def INT_>= (other: Tree) = fn(target, getMember(IntClass, nme.GE), other) def INT_== (other: Tree) = fn(target, getMember(IntClass, nme.EQ), other) - def INT_!= (other: Tree) = fn(target, getMember(IntClass, nme.NE), other) // generic operations on ByteClass, IntClass, LongClass def GEN_| (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.OR), other) @@ -102,9 +90,6 @@ trait TreeDSL { def GEN_== (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.EQ), other) def GEN_!= (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.NE), other) - def BOOL_&& (other: Tree) = fn(target, Boolean_and, other) - def BOOL_|| (other: Tree) = fn(target, Boolean_or, other) - /** Apply, Select, Match **/ def APPLY(params: Tree*) = Apply(target, params.toList) def APPLY(params: List[Tree]) = Apply(target, params) @@ -114,6 +99,10 @@ trait TreeDSL { def DOT(sym: Symbol) = SelectStart(Select(target, sym)) /** Assignment */ + // !!! This method is responsible for some tree sharing, but a diligent + // reviewer pointed out that we shouldn't blindly duplicate these trees + // as there might be DefTrees nested beneath them. It's not entirely + // clear how to proceed, so for now it retains the non-duplicating behavior. def ===(rhs: Tree) = Assign(target, rhs) /** Methods for sequences **/ @@ -130,8 +119,6 @@ trait TreeDSL { def IS(tpe: Type) = gen.mkIsInstanceOf(target, tpe, true) def IS_OBJ(tpe: Type) = gen.mkIsInstanceOf(target, tpe, false) - // XXX having some difficulty expressing nullSafe in a way that doesn't freak out value types - // def TOSTRING() = nullSafe(fn(_: Tree, nme.toString_), LIT("null"))(target) def TOSTRING() = fn(target, nme.toString_) def GETCLASS() = fn(target, Object_getClass) } @@ -159,7 +146,6 @@ trait TreeDSL { def mkTree(rhs: Tree): ResultTreeType def ===(rhs: Tree): ResultTreeType - private var _mods: Modifiers = null private var _tpt: Tree = null private var _pos: Position = null @@ -167,19 +153,12 @@ trait TreeDSL { _tpt = TypeTree(tp) this } - def withFlags(flags: Long*): this.type = { - if (_mods == null) - _mods = defaultMods - - _mods = flags.foldLeft(_mods)(_ | _) - this - } def withPos(pos: Position): this.type = { _pos = pos this } - final def mods = if (_mods == null) defaultMods else _mods + final def mods = defaultMods final def tpt = if (_tpt == null) defaultTpt else _tpt final def pos = if (_pos == null) defaultPos else _pos } @@ -199,7 +178,7 @@ trait TreeDSL { self: VODDStart => type ResultTreeType = ValDef - def mkTree(rhs: Tree): ValDef = ValDef(mods, name, tpt, rhs) + def mkTree(rhs: Tree): ValDef = ValDef(mods, name.toTermName, tpt, rhs) } trait DefCreator { self: VODDStart => @@ -244,7 +223,6 @@ trait TreeDSL { } class TryStart(body: Tree, catches: List[CaseDef], fin: Tree) { def CATCH(xs: CaseDef*) = new TryStart(body, xs.toList, fin) - def FINALLY(x: Tree) = Try(body, catches, x) def ENDTRY = Try(body, catches, fin) } @@ -252,16 +230,9 @@ trait TreeDSL { def DEFAULT: CaseStart = new CaseStart(WILD.empty, EmptyTree) class SymbolMethods(target: Symbol) { - def BIND(body: Tree) = Bind(target, body) - def IS_NULL() = REF(target) OBJ_EQ NULL - def NOT_NULL() = REF(target) OBJ_NE NULL - - def GET() = fn(REF(target), nme.get) - - // name of nth indexed argument to a method (first parameter list), defaults to 1st - def ARG(idx: Int = 0) = Ident(target.paramss.head(idx)) - def ARGS = target.paramss.head - def ARGNAMES = ARGS map Ident + def IS_NULL() = REF(target) OBJ_EQ NULL + def GET() = fn(REF(target), nme.get) + def ARGS = target.paramss.head } /** Top level accessible. */ @@ -269,32 +240,13 @@ trait TreeDSL { def THROW(sym: Symbol, msg: Tree): Throw = Throw(sym.tpe, msg.TOSTRING()) def NEW(tpt: Tree, args: Tree*): Tree = New(tpt, List(args.toList)) - def NEW(sym: Symbol, args: Tree*): Tree = New(sym.tpe, args: _*) - - def DEF(name: Name, tp: Type): DefTreeStart = DEF(name) withType tp - def DEF(name: Name): DefTreeStart = new DefTreeStart(name) def DEF(sym: Symbol): DefSymStart = new DefSymStart(sym) - - def VAL(name: Name, tp: Type): ValTreeStart = VAL(name) withType tp - def VAL(name: Name): ValTreeStart = new ValTreeStart(name) def VAL(sym: Symbol): ValSymStart = new ValSymStart(sym) - def VAR(name: Name, tp: Type): ValTreeStart = VAL(name, tp) withFlags Flags.MUTABLE - def VAR(name: Name): ValTreeStart = VAL(name) withFlags Flags.MUTABLE - def VAR(sym: Symbol): ValSymStart = VAL(sym) withFlags Flags.MUTABLE - - def LAZYVAL(name: Name, tp: Type): ValTreeStart = VAL(name, tp) withFlags Flags.LAZY - def LAZYVAL(name: Name): ValTreeStart = VAL(name) withFlags Flags.LAZY - def LAZYVAL(sym: Symbol): ValSymStart = VAL(sym) withFlags Flags.LAZY - def AND(guards: Tree*) = if (guards.isEmpty) EmptyTree else guards reduceLeft gen.mkAnd - def OR(guards: Tree*) = - if (guards.isEmpty) EmptyTree - else guards reduceLeft gen.mkOr - def IF(tree: Tree) = new IfStart(tree, EmptyTree) def TRY(tree: Tree) = new TryStart(tree, Nil, EmptyTree) def BLOCK(xs: Tree*) = Block(xs.init.toList, xs.last) @@ -312,11 +264,6 @@ trait TreeDSL { case List(tree) if flattenUnary => tree case _ => Apply(TupleClass(trees.length).companionModule, trees: _*) } - def makeTupleType(trees: List[Tree], flattenUnary: Boolean): Tree = trees match { - case Nil => gen.scalaUnitConstr - case List(tree) if flattenUnary => tree - case _ => AppliedTypeTree(REF(TupleClass(trees.length)), trees) - } /** Implicits - some of these should probably disappear **/ implicit def mkTreeMethods(target: Tree): TreeMethods = new TreeMethods(target) diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 99b82d9746..b9eb511a9a 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -8,7 +8,6 @@ package ast import scala.collection.mutable.ListBuffer import symtab.Flags._ -import symtab.SymbolTable import scala.language.postfixOps /** XXX to resolve: TreeGen only assumes global is a SymbolTable, but @@ -22,7 +21,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { def mkCheckInit(tree: Tree): Tree = { val tpe = - if (tree.tpe != null || !tree.hasSymbol) tree.tpe + if (tree.tpe != null || !tree.hasSymbolField) tree.tpe else tree.symbol.tpe if (!global.phase.erasedTypes && settings.warnSelectNullable.value && @@ -52,120 +51,30 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { } // wrap the given expression in a SoftReference so it can be gc-ed - def mkSoftRef(expr: Tree): Tree = atPos(expr.pos)(New(SoftReferenceClass.tpe, expr)) + def mkSoftRef(expr: Tree): Tree = atPos(expr.pos) { + val constructor = SoftReferenceClass.info.nonPrivateMember(nme.CONSTRUCTOR).suchThat(_.paramss.flatten.size == 1) + NewFromConstructor(constructor, expr) + } // annotate the expression with @unchecked def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) { // This can't be "Annotated(New(UncheckedClass), expr)" because annotations // are very picky about things and it crashes the compiler with "unexpected new". - Annotated(New(scalaDot(UncheckedClass.name), ListOfNil), expr) - } - // if it's a Match, mark the selector unchecked; otherwise nothing. - def mkUncheckedMatch(tree: Tree) = tree match { - case Match(selector, cases) => atPos(tree.pos)(Match(mkUnchecked(selector), cases)) - case _ => tree - } - - def mkSynthSwitchSelector(expr: Tree): Tree = atPos(expr.pos) { - // This can't be "Annotated(New(SwitchClass), expr)" because annotations - // are very picky about things and it crashes the compiler with "unexpected new". - Annotated(Ident(nme.synthSwitch), expr) - } - - // TODO: would be so much nicer if we would know during match-translation (i.e., type checking) - // whether we should emit missingCase-style apply (and isDefinedAt), instead of transforming trees post-factum - class MatchMatcher { - def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = unknownTree(orig) - def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = unknownTree(orig) - def caseVirtualizedMatchOpt(orig: Tree, prologue: List[Tree], cases: List[Tree], matchEndDef: Tree, wrap: Tree => Tree): Tree = unknownTree(orig) - - 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) - // old-style match or virtpatmat switch - case Block((vd: ValDef) :: Nil, orig@Match(selector, cases)) => // println("block match: "+ (selector, cases, vd) + "for:\n"+ matchExpr ) - caseMatch(matchExpr, selector, cases, m => copyBlock(matchExpr, List(vd), m)) - // virtpatmat - case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), List(scrut)), List(matcher)) if opt.virtPatmat => // println("virt match: "+ (tgt, targs, scrut, matcher) + "for:\n"+ matchExpr ) - caseVirtualizedMatch(matchExpr, tgt, targs, scrut, matcher) - // optimized version of virtpatmat - case Block(stats, matchEndDef) if opt.virtPatmat && (stats forall treeInfo.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(stats, matchEndDef)) if opt.virtPatmat && (stats forall treeInfo.hasSynthCaseSymbol) => - val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) - caseVirtualizedMatchOpt(matchExpr, prologue, cases, matchEndDef, m => copyBlock(matchExpr, outerStats, m)) - case other => - unknownTree(other) - } - - def unknownTree(t: Tree): Tree = throw new MatchError(t) - def copyBlock(orig: Tree, stats: List[Tree], expr: Tree): Block = Block(stats, expr) - - def dropSyntheticCatchAll(cases: List[CaseDef]): List[CaseDef] = - if (!opt.virtPatmat) cases - else cases filter { - case CaseDef(pat, EmptyTree, Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _))) if (treeInfo.isWildcardArg(pat) && (exTpt.tpe.typeSymbol eq MatchErrorClass)) => false - case CaseDef(pat, guard, body) => true - } - } - - def mkCached(cvar: Symbol, expr: Tree): Tree = { - val cvarRef = mkUnattributedRef(cvar) - Block( - List( - If(Apply(Select(cvarRef, nme.eq), List(Literal(Constant(null)))), - Assign(cvarRef, expr), - EmptyTree)), - cvarRef - ) + Annotated(New(scalaDot(UncheckedClass.name), Nil), expr) } // Builds a tree of the form "{ lhs = rhs ; lhs }" def mkAssignAndReturn(lhs: Symbol, rhs: Tree): Tree = { - val lhsRef = mkUnattributedRef(lhs) + def lhsRef = if (lhs.owner.isClass) Select(This(lhs.owner), lhs) else Ident(lhs) Block(Assign(lhsRef, rhs) :: Nil, lhsRef) } - def mkModuleVarDef(accessor: Symbol) = { - val inClass = accessor.owner.isClass - val extraFlags = if (inClass) PrivateLocal | SYNTHETIC else 0 - - val mval = ( - accessor.owner.newVariable(nme.moduleVarName(accessor.name), accessor.pos.focus, MODULEVAR | extraFlags) - setInfo accessor.tpe.finalResultType - addAnnotation VolatileAttr - ) - if (inClass) - mval.owner.info.decls enter mval - - ValDef(mval) - } - - // def m: T = { if (m$ eq null) m$ = new m$class(...) m$ } - // where (...) are eventual outer accessors - def mkCachedModuleAccessDef(accessor: Symbol, mvar: Symbol) = - DefDef(accessor, mkCached(mvar, newModule(accessor, mvar.tpe))) - - def mkModuleAccessDef(accessor: Symbol, msym: Symbol) = - DefDef(accessor, Select(This(msym.owner), msym)) - def newModule(accessor: Symbol, tpe: Type) = { val ps = tpe.typeSymbol.primaryConstructor.info.paramTypes if (ps.isEmpty) New(tpe) else New(tpe, This(accessor.owner.enclClass)) } - // def m: T; - def mkModuleAccessDcl(accessor: Symbol) = - DefDef(accessor setFlag lateDEFERRED, EmptyTree) - def mkRuntimeCall(meth: Name, args: List[Tree]): Tree = mkRuntimeCall(meth, Nil, args) @@ -206,7 +115,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { else AppliedTypeTree(Ident(clazz), targs map TypeTree) )) } - def mkSuperSelect = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) + def mkSuperInitCall: Select = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) def wildcardStar(tree: Tree) = atPos(tree.pos) { Typed(tree, Ident(tpnme.WILDCARD_STAR)) } @@ -267,25 +176,6 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { else mkCast(tree, pt) - def mkZeroContravariantAfterTyper(tp: Type): Tree = { - // contravariant -- for replacing an argument in a method call - // must use subtyping, as otherwise we miss types like `Any with Int` - val tree = - if (NullClass.tpe <:< tp) Literal(Constant(null)) - else if (UnitClass.tpe <:< tp) Literal(Constant()) - else if (BooleanClass.tpe <:< tp) Literal(Constant(false)) - else if (FloatClass.tpe <:< tp) Literal(Constant(0.0f)) - else if (DoubleClass.tpe <:< tp) Literal(Constant(0.0d)) - else if (ByteClass.tpe <:< tp) Literal(Constant(0.toByte)) - else if (ShortClass.tpe <:< tp) Literal(Constant(0.toShort)) - else if (IntClass.tpe <:< tp) Literal(Constant(0)) - else if (LongClass.tpe <:< tp) Literal(Constant(0L)) - else if (CharClass.tpe <:< tp) Literal(Constant(0.toChar)) - else mkCast(Literal(Constant(null)), tp) - - tree - } - /** Translate names in Select/Ident nodes to type names. */ def convertToTypeName(tree: Tree): Option[RefTree] = tree match { @@ -307,7 +197,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { */ private def mkPackedValDef(expr: Tree, owner: Symbol, name: Name): (ValDef, () => Ident) = { val packedType = typer.packedType(expr, owner) - val sym = owner.newValue(name, expr.pos.makeTransparent, SYNTHETIC) setInfo packedType + val sym = owner.newValue(name.toTermName, expr.pos.makeTransparent, SYNTHETIC) setInfo packedType (ValDef(sym, expr), () => Ident(sym) setPos sym.pos.focus setType expr.tpe) } diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala index cbbb4c8ba8..f53f99a279 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala @@ -6,10 +6,6 @@ package scala.tools.nsc package ast -import scala.reflect.internal.HasFlags -import scala.reflect.internal.Flags._ -import symtab._ - /** This class ... * * @author Martin Odersky @@ -19,8 +15,6 @@ abstract class TreeInfo extends scala.reflect.internal.TreeInfo { val global: Global import global._ - import definitions.ThrowableClass - /** Is tree legal as a member definition of an interface? */ override def isInterfaceMember(tree: Tree): Boolean = tree match { @@ -42,7 +36,4 @@ abstract class TreeInfo extends scala.reflect.internal.TreeInfo { case ClassDef(_, `name`, _, _) :: Nil => true case _ => super.firstDefinesClassOrObject(trees, name) } - - def isInterface(mods: HasFlags, body: List[Tree]) = - mods.isTrait && (body forall isInterfaceMember) } diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 2ad762fd55..4b5e23e177 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -65,6 +65,13 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => // --- factory methods ---------------------------------------------------------- + /** Factory method for a primary constructor super call `super.<init>(args_1)...(args_n)` + */ + def PrimarySuperCall(argss: List[List[Tree]]): Tree = argss match { + case Nil => Apply(gen.mkSuperInitCall, Nil) + case xs :: rest => rest.foldLeft(Apply(gen.mkSuperInitCall, xs): Tree)(Apply.apply) + } + /** Generates a template with constructor corresponding to * * constrmods (vparams1_) ... (vparams_n) preSuper { presupers } @@ -82,7 +89,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * body * } */ - def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): Template = { + def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): Template = { /* Add constructor to template */ // create parameters for <init> as synthetic trees. @@ -116,10 +123,17 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => // convert (implicit ... ) to ()(implicit ... ) if its the only parameter section if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) vparamss1 = List() :: vparamss1; - val superRef: Tree = atPos(superPos)(gen.mkSuperSelect) - val superCall = (superRef /: argss) (Apply.apply) + val superRef: Tree = atPos(superPos)(gen.mkSuperInitCall) + val superCall = pendingSuperCall // we can't know in advance which of the parents will end up as a superclass + // this requires knowing which of the parents is a type macro and which is not + // and that's something that cannot be found out before typer + // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon) + // this means that we don't know what will be the arguments of the super call + // therefore here we emit a dummy which gets populated when the template is named and typechecked List( - atPos(wrappingPos(superPos, lvdefs ::: argss.flatten)) ( + // TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)` + // is it going to be a problem that we can no longer include the `argss`? + atPos(wrappingPos(superPos, lvdefs)) ( DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) } } @@ -137,11 +151,10 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * @param constrMods the modifiers for the class constructor, i.e. as in `class C private (...)` * @param vparamss the value parameters -- if they have symbols they * should be owned by `sym` - * @param argss the supercall arguments * @param body the template statements without primary constructor * and value parameter fields. */ - def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): ClassDef = { + def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): ClassDef = { // "if they have symbols they should be owned by `sym`" assert( mforall(vparamss)(p => (p.symbol eq NoSymbol) || (p.symbol.owner == sym)), @@ -151,7 +164,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => ClassDef(sym, Template(sym.info.parents map TypeTree, if (sym.thisSym == sym || phase.erasedTypes) emptyValDef else ValDef(sym.thisSym), - constrMods, vparamss, argss, body, superPos)) + constrMods, vparamss, body, superPos)) } // --- subcomponents -------------------------------------------------- @@ -324,27 +337,24 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => else super.transform { tree match { + case tree if !tree.canHaveAttrs => + tree 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)))) { - val dupl = tpt.duplicate - dupl.tpe = null - dupl + tpt.duplicate.clearType() } else 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 _ => val dupl = tree.duplicate - if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)) && !(keepLabels && tree.symbol.isLabel)) + if (tree.hasSymbolField && (!localOnly || (locals contains tree.symbol)) && !(keepLabels && tree.symbol.isLabel)) dupl.symbol = NoSymbol - dupl.tpe = null - dupl + dupl.clearType() } } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala index 553a2088a6..639780149e 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala @@ -10,10 +10,8 @@ import scala.collection.mutable import mutable.{ Buffer, ArrayBuffer, ListBuffer } import scala.util.control.ControlThrowable import scala.tools.nsc.util.CharArrayReader -import scala.reflect.internal.util.SourceFile -import scala.xml.{ Text, TextBuffer } +import scala.xml.TextBuffer import scala.xml.parsing.MarkupParserCommon -import scala.xml.Utility.{ isNameStart, isNameChar, isSpace } import scala.reflect.internal.Chars.{ SU, LF } // XXX/Note: many/most of the functions in here are almost direct cut and pastes @@ -26,12 +24,6 @@ import scala.reflect.internal.Chars.{ SU, LF } // I rewrote most of these, but not as yet the library versions: so if you are // tempted to touch any of these, please be aware of that situation and try not // to let it get any worse. -- paulp - -/** This trait ... - * - * @author Burak Emir - * @version 1.0 - */ trait MarkupParsers { self: Parsers => @@ -51,7 +43,7 @@ trait MarkupParsers { class MarkupParser(parser: SourceFileParser, final val preserveWS: Boolean) extends MarkupParserCommon { - import Tokens.{ EMPTY, LBRACE, RBRACE } + import Tokens.{ LBRACE, RBRACE } type PositionType = Position type InputType = CharArrayReader @@ -89,7 +81,7 @@ trait MarkupParsers { var xEmbeddedBlock = false - private var debugLastStartElement = new mutable.Stack[(Int, String)] + private val debugLastStartElement = new mutable.Stack[(Int, String)] private def debugLastPos = debugLastStartElement.top._1 private def debugLastElem = debugLastStartElement.top._2 @@ -124,7 +116,6 @@ trait MarkupParsers { val start = curOffset val key = xName xEQ - val delim = ch val mid = curOffset val value: Tree = ch match { case '"' | '\'' => @@ -219,9 +210,6 @@ trait MarkupParsers { /** Returns true if it encounters an end tag (without consuming it), * appends trees to ts as side-effect. - * - * @param ts ... - * @return ... */ private def content_LT(ts: ArrayBuffer[Tree]): Boolean = { if (ch == '/') @@ -410,7 +398,7 @@ trait MarkupParsers { * | Name [S] '/' '>' */ def xPattern: Tree = { - var start = curOffset + val start = curOffset val qname = xName debugLastStartElement.push((start, qname)) xSpaceOpt diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 6f79f639b9..61c65c211b 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -9,7 +9,8 @@ package scala.tools.nsc package ast.parser -import scala.collection.mutable.{ListBuffer, StringBuilder} +import scala.collection.{ mutable, immutable } +import mutable.{ ListBuffer, StringBuilder } import scala.reflect.internal.{ ModifierFlags => Flags } import scala.reflect.internal.Chars.{ isScalaLetter } import scala.reflect.internal.util.{ SourceFile, OffsetPosition } @@ -94,7 +95,7 @@ trait ParsersCommon extends ScannersCommon { * <ol> * <li> * Places all pattern variables in Bind nodes. In a pattern, for - * identifiers <code>x</code>:<pre> + * identifiers `x`:<pre> * x => x @ _ * x:T => x @ (_ : T)</pre> * </li> @@ -167,7 +168,6 @@ self => object symbXMLBuilder extends SymbolicXMLBuilder(this, preserveWS = true) { // DEBUG choices val global: self.global.type = self.global - def freshName(prefix: String): Name = SourceFileParser.this.freshName(prefix) } def xmlLiteral : Tree = xmlp.xLiteral @@ -299,11 +299,7 @@ self => inScalaPackage = false currentPackage = "" } - private lazy val primitiveNames: Set[Name] = tpnme.ScalaValueNames.toSet - - private def inScalaRootPackage = inScalaPackage && currentPackage == "scala" - private def isScalaArray(name: Name) = inScalaRootPackage && name == tpnme.Array - private def isPrimitiveType(name: Name) = inScalaRootPackage && primitiveNames(name) + private def inScalaRootPackage = inScalaPackage && currentPackage == "scala" def parseStartRule: () => Tree @@ -380,7 +376,6 @@ self => * } * }}} */ - import definitions._ def emptyPkg = atPos(0, 0, 0) { Ident(nme.EMPTY_PACKAGE_NAME) } def emptyInit = DefDef( @@ -389,7 +384,7 @@ self => Nil, ListOfNil, TypeTree(), - Block(List(Apply(gen.mkSuperSelect, Nil)), Literal(Constant(()))) + Block(List(Apply(gen.mkSuperInitCall, Nil)), Literal(Constant(()))) ) // def main @@ -468,7 +463,7 @@ self => /* ------------- ERROR HANDLING ------------------------------------------- */ - var assumedClosingParens = scala.collection.mutable.Map(RPAREN -> 0, RBRACKET -> 0, RBRACE -> 0) + val assumedClosingParens = mutable.Map(RPAREN -> 0, RBRACKET -> 0, RBRACE -> 0) private var inFunReturnType = false @inline private def fromWithinReturnType[T](body: => T): T = { @@ -645,8 +640,6 @@ self => case _ => false } - def isTypeIntro: Boolean = isTypeIntroToken(in.token) - def isStatSeqEnd = in.token == RBRACE || in.token == EOF def isStatSep(token: Int): Boolean = @@ -708,10 +701,10 @@ self => tree match { case Ident(name) => removeAsPlaceholder(name) - makeParam(name, TypeTree() setPos o2p(tree.pos.endOrPoint)) + makeParam(name.toTermName, TypeTree() setPos o2p(tree.pos.endOrPoint)) case Typed(Ident(name), tpe) if tpe.isType => // get the ident! removeAsPlaceholder(name) - makeParam(name, tpe) + makeParam(name.toTermName, tpe) case _ => syntaxError(tree.pos, "not a legal formal parameter", false) makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.endOrPoint)) @@ -770,10 +763,6 @@ self => } } - def checkSize(kind: String, size: Int, max: Int) { - if (size > max) syntaxError("too many "+kind+", maximum = "+max, false) - } - def checkAssoc(offset: Int, op: Name, leftAssoc: Boolean) = if (treeInfo.isLeftAssoc(op) != leftAssoc) syntaxError( @@ -794,7 +783,7 @@ self => val rPos = top.pos val end = if (rPos.isDefined) rPos.endOrPoint else opPos.endOrPoint top = atPos(start, opinfo.offset, end) { - makeBinop(isExpr, opinfo.operand, opinfo.operator, top, opPos) + makeBinop(isExpr, opinfo.operand, opinfo.operator.toTermName, top, opPos) } } top @@ -923,7 +912,7 @@ self => ) def compoundTypeRest(t: Tree): Tree = { - var ts = new ListBuffer[Tree] += t + val ts = new ListBuffer[Tree] += t while (in.token == WITH) { in.nextToken() ts += annotType() @@ -1135,16 +1124,7 @@ self => }) } - private def stringOp(t: Tree, op: TermName) = { - val str = in.strVal - in.nextToken() - if (str.length == 0) t - else atPos(t.pos.startOrPoint) { - Apply(Select(t, op), List(Literal(Constant(str)))) - } - } - - private def interpolatedString(inPattern: Boolean = false): Tree = atPos(in.offset) { + private def interpolatedString(inPattern: Boolean): Tree = atPos(in.offset) { val start = in.offset val interpolator = in.name @@ -1228,15 +1208,6 @@ self => /* ----------- EXPRESSIONS ------------------------------------------------ */ - /** {{{ - * EqualsExpr ::= `=' Expr - * }}} - */ - def equalsExpr(): Tree = { - accept(EQUALS) - expr() - } - def condExpr(): Tree = { if (in.token == LPAREN) { in.nextToken() @@ -1280,7 +1251,7 @@ self => def expr(): Tree = expr(Local) def expr(location: Int): Tree = { - var savedPlaceholderParams = placeholderParams + val savedPlaceholderParams = placeholderParams placeholderParams = List() var res = expr0(location) if (!placeholderParams.isEmpty && !isWildcard(res)) { @@ -1330,26 +1301,24 @@ self => parseTry case WHILE => def parseWhile = { - val start = in.offset atPos(in.skipToken()) { val lname: Name = freshTermName(nme.WHILE_PREFIX) val cond = condExpr() newLinesOpt() val body = expr() - makeWhile(lname, cond, body) + makeWhile(lname.toTermName, cond, body) } } parseWhile case DO => def parseDo = { - val start = in.offset atPos(in.skipToken()) { val lname: Name = freshTermName(nme.DO_WHILE_PREFIX) val body = expr() if (isStatSep) in.nextToken() accept(WHILE) val cond = condExpr() - makeDoWhile(lname, body, cond) + makeDoWhile(lname.toTermName, body, cond) } } parseDo @@ -1508,7 +1477,7 @@ self => def prefixExpr(): Tree = { if (isUnaryOp) { atPos(in.offset) { - val name = nme.toUnaryName(rawIdent()) + val name = nme.toUnaryName(rawIdent().toTermName) if (name == nme.UNARY_- && isNumericLit) simpleExprRest(atPos(in.offset)(literal(isNegated = true)), canApply = true) else @@ -1546,7 +1515,7 @@ self => val pname = freshName("x$") in.nextToken() val id = atPos(start) (Ident(pname)) - val param = atPos(id.pos.focus){ makeSyntheticParam(pname) } + val param = atPos(id.pos.focus){ makeSyntheticParam(pname.toTermName) } placeholderParams = param :: placeholderParams id case LPAREN => @@ -1559,9 +1528,9 @@ self => val nstart = in.skipToken() val npos = r2p(nstart, nstart, in.lastOffset) val tstart = in.offset - val (parents, argss, self, stats) = template(isTrait = false) + val (parents, self, stats) = template() val cpos = r2p(tstart, tstart, in.lastOffset max tstart) - makeNew(parents, self, stats, argss, npos, cpos) + makeNew(parents, self, stats, npos, cpos) case _ => syntaxErrorOrIncomplete("illegal start of simple expression", true) errorTermTree @@ -1615,14 +1584,9 @@ self => * }}} */ def argumentExprs(): List[Tree] = { - def args(): List[Tree] = commaSeparated { - val maybeNamed = isIdent - expr() match { - case a @ Assign(id, rhs) if maybeNamed => - atPos(a.pos) { AssignOrNamedArg(id, rhs) } - case e => e - } - } + def args(): List[Tree] = commaSeparated( + if (isIdent) treeInfo.assignmentToMaybeNamedArg(expr()) else expr() + ) in.token match { case LBRACE => List(blockExpr()) case LPAREN => inParens(if (in.token == RPAREN) Nil else args()) @@ -1806,7 +1770,6 @@ self => * }}} */ def pattern2(): Tree = { - val nameOffset = in.offset val p = pattern3() if (in.token != AT) p @@ -1919,7 +1882,7 @@ self => val start = in.offset in.token match { case IDENTIFIER | BACKQUOTED_IDENT | THIS => - var t = stableId() + val t = stableId() in.token match { case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => t match { @@ -1981,7 +1944,6 @@ self => /** Default entry points into some pattern contexts. */ def pattern(): Tree = noSeq.pattern() - def patterns(): List[Tree] = noSeq.patterns() def seqPatterns(): List[Tree] = seqOK.patterns() def xmlSeqPatterns(): List[Tree] = xmlSeqOK.patterns() // Called from xml parser def argumentPatterns(): List[Tree] = inParens { @@ -1995,11 +1957,11 @@ self => /** Drop `private` modifier when followed by a qualifier. * Contract `abstract` and `override` to ABSOVERRIDE */ - private def normalize(mods: Modifiers): Modifiers = + private def normalizeModifers(mods: Modifiers): Modifiers = if (mods.isPrivate && mods.hasAccessBoundary) - normalize(mods &~ Flags.PRIVATE) + normalizeModifers(mods &~ Flags.PRIVATE) else if (mods hasAllFlags (Flags.ABSTRACT | Flags.OVERRIDE)) - normalize(mods &~ (Flags.ABSTRACT | Flags.OVERRIDE) | Flags.ABSOVERRIDE) + normalizeModifers(mods &~ (Flags.ABSTRACT | Flags.OVERRIDE) | Flags.ABSOVERRIDE) else mods @@ -2044,7 +2006,7 @@ self => * AccessModifier ::= (private | protected) [AccessQualifier] * }}} */ - def accessModifierOpt(): Modifiers = normalize { + def accessModifierOpt(): Modifiers = normalizeModifers { in.token match { case m @ (PRIVATE | PROTECTED) => in.nextToken() ; accessQualifierOpt(Modifiers(flagTokens(m))) case _ => NoMods @@ -2058,7 +2020,7 @@ self => * | override * }}} */ - def modifiers(): Modifiers = normalize { + def modifiers(): Modifiers = normalizeModifers { def loop(mods: Modifiers): Modifiers = in.token match { case PRIVATE | PROTECTED => loop(accessQualifierOpt(addMod(mods, flagTokens(in.token), tokenRange(in)))) @@ -2103,7 +2065,7 @@ self => def annotationExpr(): Tree = atPos(in.offset) { val t = exprSimpleType() if (in.token == LPAREN) New(t, multipleArgumentExprs()) - else New(t, ListOfNil) + else New(t, Nil) } /* -------- PARAMETERS ------------------------------------------- */ @@ -2169,7 +2131,7 @@ self => expr() } else EmptyTree atPos(start, if (name == nme.ERROR) start else nameOffset) { - ValDef((mods | implicitmod | bynamemod) withAnnotations annots, name, tpt, default) + ValDef((mods | implicitmod | bynamemod) withAnnotations annots, name.toTermName, tpt, default) } } def paramClause(): List[ValDef] = { @@ -2186,8 +2148,8 @@ self => val start = in.offset newLineOptWhenFollowedBy(LPAREN) if (ofCaseClass && in.token != LPAREN) - deprecationWarning(in.lastOffset, "case classes without a parameter list have been deprecated;\n"+ - "use either case objects or case classes with `()' as parameter list.") + syntaxError(in.lastOffset, "case classes without a parameter list are not allowed;\n"+ + "use either case objects or case classes with an explicit `()' as a parameter list.") while (implicitmod == 0 && in.token == LPAREN) { in.nextToken() vds += paramClause() @@ -2626,7 +2588,6 @@ self => in.nextToken() newLinesOpt() atPos(start, in.offset) { - val nameOffset = in.offset val name = identForType() // @M! a type alias as well as an abstract type may declare type parameters val tparams = typeParamClauseOpt(name, null) @@ -2730,7 +2691,7 @@ self => atPos(start, if (name == nme.ERROR) start else nameOffset) { val mods1 = if (in.token == SUBTYPE) mods | Flags.DEFERRED else mods val template = templateOpt(mods1, name, NoMods, Nil, tstart) - ModuleDef(mods1, name, template) + ModuleDef(mods1, name.toTermName, template) } } @@ -2739,20 +2700,17 @@ self => * TraitParents ::= AnnotType {with AnnotType} * }}} */ - def templateParents(isTrait: Boolean): (List[Tree], List[List[Tree]]) = { - val parents = new ListBuffer[Tree] += startAnnotType() - val argss = ( - // TODO: the insertion of ListOfNil here is where "new Foo" becomes - // indistinguishable from "new Foo()". - if (in.token == LPAREN && !isTrait) multipleArgumentExprs() - else ListOfNil - ) - - while (in.token == WITH) { - in.nextToken() - parents += startAnnotType() + def templateParents(): List[Tree] = { + val parents = new ListBuffer[Tree] + def readAppliedParent() = { + val start = in.offset + val parent = startAnnotType() + val argss = if (in.token == LPAREN) multipleArgumentExprs() else Nil + parents += atPos(start)((parent /: argss)(Apply.apply)) } - (parents.toList, argss) + readAppliedParent() + while (in.token == WITH) { in.nextToken(); readAppliedParent() } + parents.toList } /** {{{ @@ -2762,12 +2720,12 @@ self => * EarlyDef ::= Annotations Modifiers PatDef * }}} */ - def template(isTrait: Boolean): (List[Tree], List[List[Tree]], ValDef, List[Tree]) = { + def template(): (List[Tree], ValDef, List[Tree]) = { newLineOptWhenFollowedBy(LBRACE) if (in.token == LBRACE) { // @S: pre template body cannot stub like post body can! val (self, body) = templateBody(isPre = true) - if (in.token == WITH && self.isEmpty) { + if (in.token == WITH && (self eq emptyValDef)) { val earlyDefs: List[Tree] = body flatMap { case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => List(copyValDef(vdef)(mods = mods | Flags.PRESUPER)) @@ -2779,16 +2737,16 @@ self => case _ => List() } in.nextToken() - val (parents, argss) = templateParents(isTrait = isTrait) - val (self1, body1) = templateBodyOpt(traitParentSeen = isTrait) - (parents, argss, self1, earlyDefs ::: body1) + val parents = templateParents() + val (self1, body1) = templateBodyOpt(parenMeansSyntaxError = false) + (parents, self1, earlyDefs ::: body1) } else { - (List(), ListOfNil, self, body) + (List(), self, body) } } else { - val (parents, argss) = templateParents(isTrait = isTrait) - val (self, body) = templateBodyOpt(traitParentSeen = isTrait) - (parents, argss, self, body) + val parents = templateParents() + val (self, body) = templateBodyOpt(parenMeansSyntaxError = false) + (parents, self, body) } } @@ -2802,15 +2760,15 @@ self => * }}} */ def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = { - val (parents0, argss, self, body) = ( + val (parents0, self, body) = ( if (in.token == EXTENDS || in.token == SUBTYPE && mods.isTrait) { in.nextToken() - template(isTrait = mods.isTrait) + template() } else { newLineOptWhenFollowedBy(LBRACE) - val (self, body) = templateBodyOpt(traitParentSeen = false) - (List(), ListOfNil, self, body) + val (self, body) = templateBodyOpt(parenMeansSyntaxError = mods.isTrait || name.isTermName) + (List(), self, body) } ) def anyrefParents() = { @@ -2832,7 +2790,7 @@ self => if (inScalaRootPackage && ScalaValueClassNames.contains(name)) Template(parents0, self, anyvalConstructor :: body) else - Template(anyrefParents, self, constrMods, vparamss, argss, body, o2p(tstart)) + Template(anyrefParents, self, constrMods, vparamss, body, o2p(tstart)) } } @@ -2847,14 +2805,15 @@ self => case (self, Nil) => (self, EmptyTree.asList) case result => result } - def templateBodyOpt(traitParentSeen: Boolean): (ValDef, List[Tree]) = { + def templateBodyOpt(parenMeansSyntaxError: Boolean): (ValDef, List[Tree]) = { newLineOptWhenFollowedBy(LBRACE) if (in.token == LBRACE) { templateBody(isPre = false) } else { - if (in.token == LPAREN) - syntaxError((if (traitParentSeen) "parents of traits" else "traits or objects")+ - " may not have parameters", true) + if (in.token == LPAREN) { + if (parenMeansSyntaxError) syntaxError(s"traits or objects may not have parameters", true) + else abort("unexpected opening parenthesis") + } (emptyValDef, List()) } } @@ -2903,7 +2862,6 @@ self => * }}} */ def packaging(start: Int): Tree = { - val nameOffset = in.offset val pkg = pkgQualId() val stats = inBracesOrNil(topStatSeq()) makePackaging(start, pkg, stats) @@ -3113,7 +3071,6 @@ self => ts ++= topStatSeq() } } else { - val nameOffset = in.offset in.flushDoc val pkg = pkgQualId() diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index c05906c740..1554be6ebb 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -10,7 +10,8 @@ import scala.reflect.internal.util._ import scala.reflect.internal.Chars._ import Tokens._ import scala.annotation.switch -import scala.collection.mutable.{ ListBuffer, ArrayBuffer } +import scala.collection.{ mutable, immutable } +import mutable.{ ListBuffer, ArrayBuffer } import scala.xml.Utility.{ isNameStart } /** See Parsers.scala / ParsersCommon for some explanation of ScannersCommon. @@ -26,7 +27,6 @@ trait ScannersCommon { trait ScannerCommon extends CommonTokenData { // things to fill in, in addition to buf, decodeUni which come from CharArrayReader - def warning(off: Int, msg: String): Unit def error (off: Int, msg: String): Unit def incompleteInputError(off: Int, msg: String): Unit def deprecationWarning(off: Int, msg: String): Unit @@ -50,9 +50,6 @@ trait Scanners extends ScannersCommon { /** Offset into source character array */ type Offset = Int - /** An undefined offset */ - val NoOffset: Offset = -1 - trait TokenData extends CommonTokenData { /** the next token */ @@ -88,8 +85,6 @@ trait Scanners extends ScannersCommon { def isAtEnd = charOffset >= buf.length - def flush = { charOffset = offset; nextChar(); this } - def resume(lastCode: Int) = { token = lastCode if (next.token != EMPTY && !reporter.hasErrors) @@ -98,10 +93,6 @@ trait Scanners extends ScannersCommon { nextToken() } - /** the last error offset - */ - var errOffset: Offset = NoOffset - /** A character buffer for literals */ val cbuf = new StringBuilder @@ -405,7 +396,7 @@ trait Scanners extends ScannersCommon { * there a realistic situation where one would need it? */ if (isDigit(ch)) { - if (opt.future) syntaxError("Non-zero numbers may not have a leading zero.") + if (settings.future.value) syntaxError("Non-zero numbers may not have a leading zero.") else deprecationWarning("Treating numbers with a leading zero as octal is deprecated.") } base = 8 @@ -1003,9 +994,9 @@ trait Scanners extends ScannersCommon { val c = lookahead.getc() /** As of scala 2.11, it isn't a number unless c here is a digit, so - * opt.future excludes the rest of the logic. + * settings.future.value excludes the rest of the logic. */ - if (opt.future && !isDigit(c)) + if (settings.future.value && !isDigit(c)) return setStrVal() val isDefinitelyNumber = (c: @switch) match { @@ -1059,7 +1050,6 @@ trait Scanners extends ScannersCommon { def syntaxError(off: Offset, msg: String) { error(off, msg) token = ERROR - errOffset = off } /** generate an error at the current token offset @@ -1072,7 +1062,6 @@ trait Scanners extends ScannersCommon { def incompleteInputError(msg: String) { incompleteInputError(offset, msg) token = EOF - errOffset = offset } override def toString() = token match { @@ -1237,7 +1226,6 @@ trait Scanners extends ScannersCommon { override val decodeUni: Boolean = !settings.nouescape.value // suppress warnings, throw exception on errors - def warning(off: Offset, msg: String): Unit = () def deprecationWarning(off: Offset, msg: String): Unit = () def error (off: Offset, msg: String): Unit = throw new MalformedInput(off, msg) def incompleteInputError(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg) @@ -1248,7 +1236,6 @@ trait Scanners extends ScannersCommon { class UnitScanner(unit: CompilationUnit, patches: List[BracePatch]) extends SourceFileScanner(unit.source) { def this(unit: CompilationUnit) = this(unit, List()) - override def warning(off: Offset, msg: String) = unit.warning(unit.position(off), msg) override def deprecationWarning(off: Offset, msg: String) = unit.deprecationWarning(unit.position(off), msg) override def error (off: Offset, msg: String) = unit.error(unit.position(off), msg) override def incompleteInputError(off: Offset, msg: String) = unit.incompleteInputError(unit.position(off), msg) @@ -1308,7 +1295,7 @@ trait Scanners extends ScannersCommon { } class ParensAnalyzer(unit: CompilationUnit, patches: List[BracePatch]) extends UnitScanner(unit, patches) { - var balance = scala.collection.mutable.Map(RPAREN -> 0, RBRACKET -> 0, RBRACE -> 0) + val balance = mutable.Map(RPAREN -> 0, RBRACKET -> 0, RBRACE -> 0) init() @@ -1430,18 +1417,6 @@ trait Scanners extends ScannersCommon { else bp :: insertPatch(bps, patch) } - def leftColumn(offset: Int) = - if (offset == -1) -1 else column(lineStart(line(offset))) - - def rightColumn(offset: Int, default: Int) = - if (offset == -1) -1 - else { - val rlin = line(offset) - if (lineStart(rlin) == offset) column(offset) - else if (rlin + 1 < lineStart.length) column(lineStart(rlin + 1)) - else default - } - def insertRBrace(): List[BracePatch] = { def insert(bps: List[BracePair]): List[BracePatch] = bps match { case List() => patches @@ -1483,17 +1458,6 @@ trait Scanners extends ScannersCommon { delete(bracePairs) } - def imbalanceMeasure: Int = { - def measureList(bps: List[BracePair]): Int = - (bps map measure).sum - def measure(bp: BracePair): Int = - (if (bp.lindent != bp.rindent) 1 else 0) + measureList(bp.nested) - measureList(bracePairs) - } - - def improves(patches1: List[BracePatch]): Boolean = - imbalanceMeasure > new ParensAnalyzer(unit, patches1).imbalanceMeasure - // don't emit deprecation warnings about identifiers like `macro` or `then` // when skimming through the source file trying to heal braces override def emitIdentifierDeprecationWarnings = false diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala index e8ef670222..4329ccefc7 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala @@ -11,7 +11,6 @@ import scala.xml.{ EntityRef, Text } import scala.xml.XML.{ xmlns } import symtab.Flags.MUTABLE import scala.reflect.internal.util.StringOps.splitWhere -import scala.language.implicitConversions /** This class builds instance of `Tree` that represent XML. * diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala index 8a9ce8907e..f1bf590ebf 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -23,10 +23,14 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse def apply(unit: global.CompilationUnit) { import global._ informProgress("parsing " + unit) - unit.body = - if (unit.isJava) new JavaUnitParser(unit).parse() - else if (reporter.incompleteHandled) new UnitParser(unit).parse() - else new UnitParser(unit).smartParse() + // if the body is already filled in, do nothing + // otherwise compileLate is going to overwrite bodies of synthetic source files + if (unit.body == EmptyTree) { + unit.body = + if (unit.isJava) new JavaUnitParser(unit).parse() + else if (reporter.incompleteHandled) new UnitParser(unit).parse() + else new UnitParser(unit).smartParse() + } if (settings.Yrangepos.value && !reporter.hasErrors) validatePositions(unit.body) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala index c3fd414426..5a7dc4950d 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -6,15 +6,11 @@ package scala.tools.nsc package ast.parser -import scala.annotation.switch - /** Common code between JavaTokens and Tokens. Not as much (and not as concrete) * as one might like because JavaTokens for no clear reason chose new numbers for * identical token sets. */ abstract class Tokens { - import scala.reflect.internal.Chars._ - /** special tokens */ final val EMPTY = -3 final val UNDEF = -2 @@ -34,14 +30,6 @@ abstract class Tokens { def isIdentifier(code: Int): Boolean def isLiteral(code: Int): Boolean - def isKeyword(code: Int): Boolean - def isSymbol(code: Int): Boolean - - final def isSpace(at: Char) = at == ' ' || at == '\t' - final def isNewLine(at: Char) = at == CR || at == LF || at == FF - final def isBrace(code: Int) = code >= LPAREN && code <= RBRACE - final def isOpenBrace(code: Int) = isBrace(code) && (code % 2 == 0) - final def isCloseBrace(code: Int) = isBrace(code) && (code % 2 == 1) } object Tokens extends Tokens { @@ -52,20 +40,10 @@ object Tokens extends Tokens { def isLiteral(code: Int) = code >= CHARLIT && code <= INTERPOLATIONID - /** identifiers */ final val IDENTIFIER = 10 final val BACKQUOTED_IDENT = 11 - def isIdentifier(code: Int) = - code >= IDENTIFIER && code <= BACKQUOTED_IDENT - - @switch def canBeginExpression(code: Int) = code match { - case IDENTIFIER|BACKQUOTED_IDENT|USCORE => true - case LBRACE|LPAREN|LBRACKET|COMMENT => true - case IF|DO|WHILE|FOR|NEW|TRY|THROW => true - case NULL|THIS|TRUE|FALSE => true - case code => isLiteral(code) - } + def isIdentifier(code: Int) = code >= IDENTIFIER && code <= BACKQUOTED_IDENT // used by ide /** keywords */ final val IF = 20 @@ -113,17 +91,6 @@ object Tokens extends Tokens { 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 - - @switch def isDefinition(code: Int) = code match { - case CLASS|TRAIT|OBJECT => true - case CASECLASS|CASEOBJECT => true - case DEF|VAL|VAR => true - case TYPE => true - case _ => false - } - /** special symbols */ final val COMMA = 70 final val SEMI = 71 @@ -141,9 +108,6 @@ object Tokens extends Tokens { final val AT = 83 final val VIEWBOUND = 84 - def isSymbol(code: Int) = - code >= COMMA && code <= VIEWBOUND - /** parenthesis */ final val LPAREN = 90 final val RPAREN = 91 diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index cd93221c50..add932441d 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -26,15 +26,11 @@ abstract class TreeBuilder { def o2p(offset: Int): Position def r2p(start: Int, point: Int, end: Int): Position - def rootId(name: Name) = gen.rootId(name) def rootScalaDot(name: Name) = gen.rootScalaDot(name) def scalaDot(name: Name) = gen.scalaDot(name) def scalaAnyRefConstr = scalaDot(tpnme.AnyRef) - def scalaAnyValConstr = scalaDot(tpnme.AnyVal) - def scalaAnyConstr = scalaDot(tpnme.Any) def scalaUnitConstr = scalaDot(tpnme.Unit) def productConstr = scalaDot(tpnme.Product) - def productConstrN(n: Int) = scalaDot(newTypeName("Product" + n)) def serializableConstr = scalaDot(tpnme.Serializable) def convertToTypeName(t: Tree) = gen.convertToTypeName(t) @@ -175,15 +171,10 @@ abstract class TreeBuilder { /** Create tree representing (unencoded) binary operation expression or pattern. */ def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { - def mkNamed(args: List[Tree]) = - if (isExpr) args map { - case a @ Assign(id @ Ident(name), rhs) => - atPos(a.pos) { AssignOrNamedArg(id, rhs) } - case e => e - } else args + def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args val arguments = right match { case Parens(args) => mkNamed(args) - case _ => List(right) + case _ => List(right) } if (isExpr) { if (treeInfo.isLeftAssoc(op)) { @@ -191,7 +182,7 @@ abstract class TreeBuilder { } else { val x = freshTermName() Block( - List(ValDef(Modifiers(SYNTHETIC), x, TypeTree(), stripParens(left))), + List(ValDef(Modifiers(SYNTHETIC | ARTIFACT), x, TypeTree(), stripParens(left))), Apply(atPos(opPos union right.pos) { Select(stripParens(right), op.encode) }, List(Ident(x)))) } } else { @@ -205,20 +196,26 @@ abstract class TreeBuilder { */ def makeAnonymousNew(stats: List[Tree]): Tree = { val stats1 = if (stats.isEmpty) List(Literal(Constant(()))) else stats - makeNew(Nil, emptyValDef, stats1, ListOfNil, NoPosition, NoPosition) + makeNew(Nil, emptyValDef, stats1, NoPosition, NoPosition) } /** Create positioned tree representing an object creation <new parents { stats } * @param npos the position of the new * @param cpos the position of the anonymous class starting with parents */ - def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree], argss: List[List[Tree]], + def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree], npos: Position, cpos: Position): Tree = if (parents.isEmpty) - makeNew(List(scalaAnyRefConstr), self, stats, argss, npos, cpos) - else if (parents.tail.isEmpty && stats.isEmpty) - atPos(npos union cpos) { New(parents.head, argss) } - else { + makeNew(List(scalaAnyRefConstr), self, stats, npos, cpos) + else if (parents.tail.isEmpty && stats.isEmpty) { + // `Parsers.template` no longer differentiates tpts and their argss + // e.g. `C()` will be represented as a single tree Apply(Ident(C), Nil) + // instead of parents = Ident(C), argss = Nil as before + // this change works great for things that are actually templates + // but in this degenerate case we need to perform postprocessing + val app = treeInfo.dissectApplied(parents.head) + atPos(npos union cpos) { New(app.callee, app.argss) } + } else { val x = tpnme.ANON_CLASS_NAME atPos(npos union cpos) { Block( @@ -226,12 +223,12 @@ abstract class TreeBuilder { atPos(cpos) { ClassDef( Modifiers(FINAL), x, Nil, - Template(parents, self, NoMods, ListOfNil, argss, stats, cpos.focus)) + Template(parents, self, NoMods, ListOfNil, stats, cpos.focus)) }), atPos(npos) { New( Ident(x) setPos npos.focus, - ListOfNil) + Nil) } ) } @@ -379,13 +376,6 @@ abstract class TreeBuilder { def makeCombination(pos: Position, meth: TermName, qual: Tree, pat: Tree, body: Tree): Tree = Apply(Select(qual, meth) setPos qual.pos, List(makeClosure(pos, pat, body))) setPos pos - /** Optionally, if pattern is a `Bind`, the bound name, otherwise None. - */ - def patternVar(pat: Tree): Option[Name] = pat match { - case Bind(name, _) => Some(name) - case _ => None - } - /** If `pat` is not yet a `Bind` wrap it in one with a fresh name */ def makeBind(pat: Tree): Tree = pat match { @@ -451,18 +441,6 @@ abstract class TreeBuilder { def makeForYield(enums: List[Enumerator], body: Tree): Tree = makeFor(nme.map, nme.flatMap, enums, body) - /** Create tree for a lifted expression XX-LIFTING - */ - def makeLifted(gs: List[ValFrom], body: Tree): Tree = { - def combine(gs: List[ValFrom]): ValFrom = (gs: @unchecked) match { - case g :: Nil => g - case ValFrom(pos1, pat1, rhs1) :: gs2 => - val ValFrom(pos2, pat2, rhs2) = combine(gs2) - ValFrom(pos1, makeTuple(List(pat1, pat2), false), Apply(Select(rhs1, nme.zip), List(rhs2))) - } - makeForYield(List(combine(gs)), body) - } - /** Create tree for a pattern alternative */ def makeAlternative(ts: List[Tree]): Tree = { def alternatives(t: Tree): List[Tree] = t match { @@ -497,7 +475,7 @@ abstract class TreeBuilder { def makeCatchFromExpr(catchExpr: Tree): CaseDef = { val binder = freshTermName("x") val pat = Bind(binder, Typed(Ident(nme.WILDCARD), Ident(tpnme.Throwable))) - val catchDef = ValDef(NoMods, freshTermName("catchExpr"), TypeTree(), catchExpr) + val catchDef = ValDef(Modifiers(ARTIFACT), freshTermName("catchExpr"), TypeTree(), catchExpr) val catchFn = Ident(catchDef.name) val body = atPos(catchExpr.pos.makeTransparent)(Block( List(catchDef), @@ -568,7 +546,7 @@ abstract class TreeBuilder { val tmp = freshTermName() val firstDef = atPos(matchExpr.pos) { - ValDef(Modifiers(PrivateLocal | SYNTHETIC | (mods.flags & LAZY)), + ValDef(Modifiers(PrivateLocal | SYNTHETIC | ARTIFACT | (mods.flags & LAZY)), tmp, TypeTree(), matchExpr) } var cnt = 0 diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index fc5d4372c5..08602f87dc 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -8,7 +8,6 @@ package backend import io.AbstractFile import util.{ClassPath,JavaClassPath,MergedClassPath,DeltaClassPath} -import util.ClassPath.{ JavaContext, DefaultJavaContext } import scala.tools.util.PathResolver trait JavaPlatform extends Platform { @@ -39,18 +38,10 @@ trait JavaPlatform extends Platform { // replaces the tighter abstract definition here. If we had DOT typing rules, the two // types would be conjoined and everything would work out. Yet another reason to push for DOT. - private def depAnalysisPhase = - if (settings.make.isDefault) Nil - else List(dependencyAnalysis) - - private def classEmitPhase = - if (settings.target.value == "jvm-1.5-fjbg") genJVM - else genASM - def platformPhases = List( flatten, // get rid of inner classes - classEmitPhase // generate .class files - ) ++ depAnalysisPhase + genASM // generate .class files + ) lazy val externalEquals = getDecl(BoxesRunTimeClass, nme.equals_) lazy val externalEqualsNumNum = getDecl(BoxesRunTimeClass, nme.equalsNumNum) diff --git a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala b/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala deleted file mode 100644 index 4493685b52..0000000000 --- a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package backend - -import ch.epfl.lamp.compiler.{ msil => msillib } -import util.{ ClassPath, MsilClassPath } -import msil.GenMSIL -import io.{ AbstractFile, MsilFile } - -trait MSILPlatform extends Platform { - import global._ - import definitions.{ ComparatorClass, BoxedNumberClass, getMember } - - type BinaryRepr = MsilFile - - if (settings.verbose.value) - inform("[AssemRefs = " + settings.assemrefs.value + "]") - - // phaseName = "msil" - object genMSIL extends { - val global: MSILPlatform.this.global.type = MSILPlatform.this.global - val runsAfter = List[String]("dce") - val runsRightAfter = None - } with GenMSIL - - lazy val classPath = MsilClassPath.fromSettings(settings) - def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]]) - // See discussion in JavaPlatForm for why we need a cast here. - - /** Update classpath with a substituted subentry */ - def updateClassPath(subst: Map[ClassPath[BinaryRepr], ClassPath[BinaryRepr]]) = - throw new UnsupportedOperationException("classpath invalidations not supported on MSIL") - - def platformPhases = List( - genMSIL // generate .msil files - ) - - lazy val externalEquals = getMember(ComparatorClass.companionModule, nme.equals_) - def isMaybeBoxed(sym: Symbol) = sym isNonBottomSubClass BoxedNumberClass - - def newClassLoader(bin: MsilFile): loaders.SymbolLoader = new loaders.MsilFileLoader(bin) - - /** - * Tells whether a class should be loaded and entered into the package - * scope. On .NET, this method returns `false` for all synthetic classes - * (anonymous classes, implementation classes, module classes), their - * symtab is encoded in the pickle of another class. - */ - def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean = { - if (cls.binary.isDefined) { - val typ = cls.binary.get.msilType - if (typ.IsDefined(loaders.clrTypes.SCALA_SYMTAB_ATTR, false)) { - val attrs = typ.GetCustomAttributes(loaders.clrTypes.SCALA_SYMTAB_ATTR, false) - assert(attrs.length == 1, attrs.length) - val a = attrs(0).asInstanceOf[msillib.Attribute] - // symtab_constr takes a byte array argument (the pickle), i.e. typ has a pickle. - // otherwise, symtab_default_constr was used, which marks typ as scala-synthetic. - a.getConstructor() == loaders.clrTypes.SYMTAB_CONSTR - } else true // always load non-scala types - } else true // always load source - } - - def needCompile(bin: MsilFile, src: AbstractFile) = - false // always use compiled file on .net -} diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala index 8cbb5bc980..f6b0701f86 100644 --- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala +++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package backend -import scala.tools.nsc.backend.icode._ import scala.collection.{ mutable, immutable } /** Scala primitive operations are represented as methods in `Any` and @@ -565,7 +564,7 @@ abstract class ScalaPrimitives { import definitions._ val code = getPrimitive(fun) - def elementType = beforeTyper { + def elementType = enteringTyper { val arrayParent = tpe :: tpe.parents collectFirst { case TypeRef(_, ArrayClass, elem :: Nil) => elem } diff --git a/src/compiler/scala/tools/nsc/backend/WorklistAlgorithm.scala b/src/compiler/scala/tools/nsc/backend/WorklistAlgorithm.scala index 798a80ea37..45ca39fee4 100644 --- a/src/compiler/scala/tools/nsc/backend/WorklistAlgorithm.scala +++ b/src/compiler/scala/tools/nsc/backend/WorklistAlgorithm.scala @@ -6,8 +6,7 @@ package scala.tools.nsc package backend -import scala.tools.nsc.ast._ -import scala.collection.{ mutable, immutable } +import scala.collection.mutable /** * Simple implementation of a worklist algorithm. A processing @@ -32,8 +31,6 @@ trait WorklistAlgorithm { * Run the iterative algorithm until the worklist remains empty. * The initializer is run once before the loop starts and should * initialize the worklist. - * - * @param initWorklist ... */ def run(initWorklist: => Unit) = { initWorklist diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index d50d4cd125..24c18e6530 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -17,7 +17,7 @@ trait BasicBlocks { self: ICodes => import opcodes._ - import global.{ ifDebug, settings, log, nme } + import global.{ settings, log, nme } import nme.isExceptionResultName /** Override Array creation for efficiency (to not go through reflection). */ @@ -122,7 +122,7 @@ trait BasicBlocks { def closed: Boolean = hasFlag(CLOSED) def closed_=(b: Boolean) = if (b) setFlag(CLOSED) else resetFlag(CLOSED) - /** When set, the <code>emit</code> methods will be ignored. */ + /** When set, the `emit` methods will be ignored. */ def ignore: Boolean = hasFlag(IGNORING) def ignore_=(b: Boolean) = if (b) setFlag(IGNORING) else resetFlag(IGNORING) @@ -260,7 +260,7 @@ trait BasicBlocks { } } - /** Replaces <code>oldInstr</code> with <code>is</code>. It does not update + /** Replaces `oldInstr` with `is`. It does not update * the position field in the newly inserted instructions, so it behaves * differently than the one-instruction versions of this function. * @@ -280,17 +280,7 @@ trait BasicBlocks { } } - /** Insert instructions in 'is' immediately after index 'idx'. */ - def insertAfter(idx: Int, is: List[Instruction]) { - assert(closed, "Instructions can be replaced only after the basic block is closed") - - instrs = instrs.patch(idx + 1, is, 0) - code.touched = true - } - /** Removes instructions found at the given positions. - * - * @param positions ... */ def removeInstructionsAt(positions: Int*) { assert(closed, this) @@ -311,8 +301,6 @@ trait BasicBlocks { } /** Replaces all instructions found in the map. - * - * @param map ... */ def subst(map: Map[Instruction, Instruction]): Unit = if (!closed) @@ -344,10 +332,6 @@ trait BasicBlocks { * is closed, which sets the DIRTYSUCCS flag. */ def emit(instr: Instruction, pos: Position) { -/* if (closed) { - print() - Console.println("trying to emit: " + instr) - } */ assert(!closed || ignore, this) if (ignore) { @@ -441,11 +425,6 @@ trait BasicBlocks { ignore = true } - def exitIgnoreMode() { - assert(ignore, "Exit ignore mode when not in ignore mode: " + this) - ignore = false - } - /** Return the last instruction of this basic block. */ def lastInstruction = if (closed) instrs(instrs.length - 1) @@ -502,17 +481,6 @@ trait BasicBlocks { override def hashCode = label * 41 + code.hashCode - // Instead of it, rather use a printer - def print() { print(java.lang.System.out) } - - def print(out: java.io.PrintStream) { - out.println("block #"+label+" :") - foreach(i => out.println(" " + i)) - out.print("Successors: ") - successors.foreach((x: BasicBlock) => out.print(" "+x.label.toString())) - out.println() - } - private def succString = if (successors.isEmpty) "[S: N/A]" else successors.distinct.mkString("[S: ", ", ", "]") private def predString = if (predecessors.isEmpty) "[P: N/A]" else predecessors.distinct.mkString("[P: ", ", ", "]") diff --git a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala index 2cebf7ad99..a872e9cd00 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package backend package icode -import scala.collection.{ mutable, immutable } +import scala.collection.immutable /** * Exception handlers are pieces of code that `handle` exceptions on @@ -27,9 +27,6 @@ trait ExceptionHandlers { private var _startBlock: BasicBlock = _; var finalizer: Finalizer = _; - /** Needed for the MSIL backend. */ - var resultKind: TypeKind = _; - def setStartBlock(b: BasicBlock) = { _startBlock = b; b.exceptionHandlerStart = true @@ -71,10 +68,4 @@ trait ExceptionHandlers { override def toString() = "finalizer_" + label override def dup: Finalizer = new Finalizer(method, label, pos) } - - object NoFinalizer extends Finalizer(null, newTermNameCached("<no finalizer>"), NoPosition) { - override def startBlock: BasicBlock = sys.error("NoFinalizer cannot have a start block."); - override def setStartBlock(b: BasicBlock): Unit = sys.error("NoFinalizer cannot have a start block."); - override def dup = this - } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 44d7a1929b..3363f19025 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -13,10 +13,8 @@ import scala.collection.mutable.{ ListBuffer, Buffer } import scala.tools.nsc.symtab._ import scala.annotation.switch import PartialFunction._ -import scala.language.postfixOps -/** This class ... - * +/** * @author Iulian Dragos * @version 1.0 */ @@ -160,18 +158,13 @@ abstract class GenICode extends SubComponent { * and not produce any value. Use genLoad for expressions which leave * a value on top of the stack. * - * @param tree ... - * @param ctx ... * @return a new context. This is necessary for control flow instructions * which may change the current basic block. */ private def genStat(tree: Tree, ctx: Context): Context = tree match { case Assign(lhs @ Select(_, _), rhs) => val isStatic = lhs.symbol.isStaticMember - var ctx1 = if (isStatic) ctx - else if (forMSIL && msil_IsValuetypeInstField(lhs.symbol)) - msil_genLoadQualifierAddress(lhs, ctx) - else genLoadQualifier(lhs, ctx) + var ctx1 = if (isStatic) ctx else genLoadQualifier(lhs, ctx) ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info)) ctx1.bb.emit(STORE_FIELD(lhs.symbol, isStatic), tree.pos) @@ -264,11 +257,6 @@ abstract class GenICode extends SubComponent { } /** Generate primitive array operations. - * - * @param tree ... - * @param ctx ... - * @param code ... - * @return ... */ private def genArrayOp(tree: Tree, ctx: Context, code: Int, expectedType: TypeKind): (Context, TypeKind) = { import scalaPrimitives._ @@ -310,9 +298,6 @@ abstract class GenICode extends SubComponent { val Apply(fun, args) = tree val monitor = ctx.makeLocal(tree.pos, ObjectClass.tpe, "monitor") var monitorResult: Local = null - - // if the synchronized block returns a result, store it in a local variable. just leaving - // it on the stack is not valid in MSIL (stack is cleaned when leaving try-blocks) val argTpe = args.head.tpe val hasResult = expectedType != UNIT if (hasResult) @@ -432,7 +417,7 @@ abstract class GenICode extends SubComponent { private def genPrimitiveOp(tree: Apply, ctx: Context, expectedType: TypeKind): (Context, TypeKind) = { val sym = tree.symbol - val Apply(fun @ Select(receiver, _), args) = tree + val Apply(fun @ Select(receiver, _), _) = tree val code = scalaPrimitives.getPrimitive(sym, receiver.tpe) if (scalaPrimitives.isArithmeticOp(code)) @@ -471,132 +456,6 @@ abstract class GenICode extends SubComponent { } /** - * forMSIL - */ - private def msil_IsValuetypeInstMethod(msym: Symbol) = ( - loaders.clrTypes.methods get msym exists (mMSIL => - mMSIL.IsInstance && mMSIL.DeclaringType.IsValueType - ) - ) - private def msil_IsValuetypeInstField(fsym: Symbol) = ( - loaders.clrTypes.fields get fsym exists (fMSIL => - !fMSIL.IsStatic && fMSIL.DeclaringType.IsValueType - ) - ) - - /** - * forMSIL: Adds a local var, the emitted code requires one more slot on the stack as on entry - */ - private def msil_genLoadZeroOfNonEnumValuetype(ctx: Context, kind: TypeKind, pos: Position, leaveAddressOnStackInstead: Boolean) { - val REFERENCE(clssym) = kind - assert(loaders.clrTypes.isNonEnumValuetype(clssym), clssym) - val local = ctx.makeLocal(pos, clssym.tpe, "tmp") - ctx.method.addLocal(local) - ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(local), pos) - ctx.bb.emit(CIL_INITOBJ(kind), pos) - val instr = if (leaveAddressOnStackInstead) - CIL_LOAD_LOCAL_ADDRESS(local) - else - LOAD_LOCAL(local) - ctx.bb.emit(instr, pos) - } - - /** - * forMSIL - */ - private def msil_genLoadAddressOf(tree: Tree, ctx: Context, expectedType: TypeKind, butRawValueIsAlsoGoodEnough: Boolean): Context = { - var generatedType = expectedType - var addressTaken = false - debuglog("at line: " + (if (tree.pos.isDefined) tree.pos.line else tree.pos)) - - var resCtx: Context = tree match { - - // emits CIL_LOAD_FIELD_ADDRESS - case Select(qualifier, selector) if (!tree.symbol.isModule) => - addressTaken = true - val sym = tree.symbol - generatedType = toTypeKind(sym.info) - - if (sym.isStaticMember) { - ctx.bb.emit(CIL_LOAD_FIELD_ADDRESS(sym, true), tree.pos) - ctx - } else { - val ctx1 = genLoadQualifier(tree, ctx) - ctx1.bb.emit(CIL_LOAD_FIELD_ADDRESS(sym, false), tree.pos) - ctx1 - } - - // emits CIL_LOAD_LOCAL_ADDRESS - case Ident(name) if (!tree.symbol.isPackage && !tree.symbol.isModule)=> - addressTaken = true - val sym = tree.symbol - try { - val Some(l) = ctx.method.lookupLocal(sym) - ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(l), tree.pos) - generatedType = l.kind // actually, should be "V&" but the callsite is aware of this - } catch { - case ex: MatchError => - abort("symbol " + sym + " does not exist in " + ctx.method) - } - ctx - - // emits CIL_LOAD_ARRAY_ITEM_ADDRESS - case Apply(fun, args) => - if (isPrimitive(fun.symbol)) { - - val sym = tree.symbol - val Apply(fun @ Select(receiver, _), args) = tree - val code = scalaPrimitives.getPrimitive(sym, receiver.tpe) - - if (isArrayOp(code)) { - val arrayObj = receiver - val k = toTypeKind(arrayObj.tpe) - val ARRAY(elementType) = k - if (scalaPrimitives.isArrayGet(code)) { - var ctx1 = genLoad(arrayObj, ctx, k) - // load argument on stack - debugassert(args.length == 1, "Too many arguments for array get operation: " + tree) - ctx1 = genLoad(args.head, ctx1, INT) - generatedType = elementType // actually "managed pointer to element type" but the callsite is aware of this - ctx1.bb.emit(CIL_LOAD_ARRAY_ITEM_ADDRESS(elementType), tree.pos) - addressTaken = true - ctx1 - } else null - } else null - } else null - - case This(qual) => - /* TODO: this case handler is a placeholder for the time when Level 2 support for valuetypes is in place, - in particular when invoking other methods on this where this is a valuetype value (boxed or not). - As receiver, a managed pointer is expected, and a plain ldarg.0 achieves just that. */ - addressTaken = true - genLoad(tree, ctx, expectedType) - - case _ => - null /* A method returning ByRef won't pass peverify, so I guess this case handler is dead code. - Even if it's not, the code below to handler !addressTaken below. */ - } - - if (!addressTaken) { - resCtx = genLoad(tree, ctx, expectedType) - if (!butRawValueIsAlsoGoodEnough) { - // raw value on stack (must be an intermediate result, e.g. returned by method call), take address - addressTaken = true - val boxType = expectedType // toTypeKind(expectedType /* TODO FIXME */) - resCtx.bb.emit(BOX(boxType), tree.pos) - resCtx.bb.emit(CIL_UNBOX(boxType), tree.pos) - } - } - - // emit conversion - if (generatedType != expectedType) - abort("Unexpected tree in msil_genLoadAddressOf: " + tree + " at: " + tree.pos) - - resCtx - } - - - /** * Generate code for trees that produce values on the stack * * @param tree The tree to be translated @@ -819,31 +678,15 @@ abstract class GenICode extends SubComponent { debugassert(ctor.owner == cls, "Symbol " + ctor.owner.fullName + " is different than " + tpt) - val ctx2 = if (forMSIL && loaders.clrTypes.isNonEnumValuetype(cls)) { - /* parameterful constructors are the only possible custom constructors, - a default constructor can't be defined for valuetypes, CLR dixit */ - val isDefaultConstructor = args.isEmpty - if (isDefaultConstructor) { - msil_genLoadZeroOfNonEnumValuetype(ctx, rt, tree.pos, leaveAddressOnStackInstead = false) - ctx - } else { - val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) - ctx1.bb.emit(CIL_NEWOBJ(ctor), tree.pos) - ctx1 - } - } else { - val nw = NEW(rt) - ctx.bb.emit(nw, tree.pos) - ctx.bb.emit(DUP(generatedType)) - val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) - - val init = CALL_METHOD(ctor, Static(true)) - nw.init = init - ctx1.bb.emit(init, tree.pos) - ctx1 - } - ctx2 + val nw = NEW(rt) + ctx.bb.emit(nw, tree.pos) + ctx.bb.emit(DUP(generatedType)) + val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) + val init = CALL_METHOD(ctor, Static(true)) + nw.init = init + ctx1.bb.emit(init, tree.pos) + ctx1 case _ => abort("Cannot instantiate " + tpt + " of kind: " + generatedType) } @@ -859,7 +702,7 @@ abstract class GenICode extends SubComponent { // we store this boxed value to a local, even if not really needed. // boxing optimization might use it, and dead code elimination will // take care of unnecessary stores - var loc1 = ctx.makeLocal(tree.pos, expr.tpe, "boxed") + val loc1 = ctx.makeLocal(tree.pos, expr.tpe, "boxed") ctx1.bb.emit(STORE_LOCAL(loc1)) ctx1.bb.emit(LOAD_LOCAL(loc1)) } @@ -877,12 +720,6 @@ abstract class GenICode extends SubComponent { ctx1.bb.emit(UNBOX(boxType), expr.pos) ctx1 - case Apply(fun @ _, List(expr)) if (forMSIL && loaders.clrTypes.isAddressOf(fun.symbol)) => - debuglog("ADDRESSOF : " + fun.symbol.fullName); - val ctx1 = msil_genLoadAddressOf(expr, ctx, toTypeKind(expr.tpe), butRawValueIsAlsoGoodEnough = false) - generatedType = toTypeKind(fun.symbol.tpe.resultType) - ctx1 - case app @ Apply(fun, args) => def genLoadApply6 = { val sym = fun.symbol @@ -924,19 +761,12 @@ abstract class GenICode extends SubComponent { else Dynamic - var ctx1 = - if (invokeStyle.hasInstance) { - if (forMSIL && !(invokeStyle.isInstanceOf[SuperCall]) && msil_IsValuetypeInstMethod(sym)) - msil_genLoadQualifierAddress(fun, ctx) - else - genLoadQualifier(fun, ctx) - } else ctx - + var ctx1 = if (invokeStyle.hasInstance) genLoadQualifier(fun, ctx) else ctx ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx1) val cm = CALL_METHOD(sym, invokeStyle) /** In a couple cases, squirrel away a little extra information in the - * CALL_METHOD for use by GenJVM. + * CALL_METHOD for use by GenASM. */ fun match { case Select(qual, _) => @@ -967,7 +797,6 @@ abstract class GenICode extends SubComponent { genLoadApply6 case ApplyDynamic(qual, args) => - assert(!forMSIL, tree) // TODO - this is where we'd catch dynamic applies for invokedynamic. sys.error("No invokedynamic support yet.") // val ctx1 = genLoad(qual, ctx, ObjectReference) @@ -1006,13 +835,18 @@ abstract class GenICode extends SubComponent { generatedType = toTypeKind(sym.info) val hostClass = findHostClass(qualifier.tpe, sym) log(s"Host class of $sym with qual $qualifier (${qualifier.tpe}) is $hostClass") + val qualSafeToElide = treeInfo isQualifierSafeToElide qualifier + + def genLoadQualUnlessElidable: Context = + if (qualSafeToElide) ctx else genLoadQualifier(tree, ctx) if (sym.isModule) { - genLoadModule(ctx, tree) + genLoadModule(genLoadQualUnlessElidable, tree) } else if (sym.isStaticMember) { - ctx.bb.emit(LOAD_FIELD(sym, true) setHostClass hostClass, tree.pos) - ctx + val ctx1 = genLoadQualUnlessElidable + ctx1.bb.emit(LOAD_FIELD(sym, true) setHostClass hostClass, tree.pos) + ctx1 } else { val ctx1 = genLoadQualifier(tree, ctx) ctx1.bb.emit(LOAD_FIELD(sym, false) setHostClass hostClass, tree.pos) @@ -1105,7 +939,7 @@ abstract class GenICode extends SubComponent { case Match(selector, cases) => def genLoadMatch = { debuglog("Generating SWITCH statement."); - var ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue) + val ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue) val afterCtx = ctx1.newBlock var caseCtx: Context = null generatedType = toTypeKind(tree.tpe) @@ -1180,8 +1014,10 @@ abstract class GenICode extends SubComponent { case NothingReference => ctx.bb.emit(THROW(ThrowableClass)) ; ctx.bb.enterIgnoreMode case NullReference => ctx.bb.emit(Seq(DROP(from), CONSTANT(Constant(null)))) case ThrowableReference if !(ThrowableClass.tpe <:< to.toType) => ctx.bb.emit(CHECK_CAST(to)) // downcast throwables - case BYTE | SHORT | CHAR | INT if to == LONG => coerce(INT, LONG) // widen subrange types - case _ => () + case _ => + // widen subrange types + if (from.isIntSizedType && to == LONG) + coerce(INT, LONG) } else to match { case UNIT => ctx.bb.emit(DROP(from), pos) // value discarding @@ -1198,15 +1034,6 @@ abstract class GenICode extends SubComponent { abort("Unknown qualifier " + tree) } - /** forMSIL */ - private def msil_genLoadQualifierAddress(tree: Tree, ctx: Context): Context = - tree match { - case Select(qualifier, _) => - msil_genLoadAddressOf(qualifier, ctx, toTypeKind(qualifier.tpe), butRawValueIsAlsoGoodEnough = false) - case _ => - abort("Unknown qualifier " + tree) - } - /** * Generate code that loads args into label parameters. */ @@ -1253,7 +1080,9 @@ abstract class GenICode extends SubComponent { if (!tree.symbol.isPackageClass) tree.symbol else tree.symbol.info.member(nme.PACKAGE) match { case NoSymbol => abort("Cannot use package as value: " + tree) - case s => debugwarn("Bug: found package class where package object expected. Converting.") ; s.moduleClass + case s => + devWarning(s"Found ${tree.symbol} where a package object is required. Converting to ${s.moduleClass}") + s.moduleClass } ) debuglog("LOAD_MODULE from %s: %s".format(tree.shortClass, sym)) @@ -1387,15 +1216,11 @@ abstract class GenICode extends SubComponent { // } /** Generate string concatenation. - * - * @param tree ... - * @param ctx ... - * @return ... */ def genStringConcat(tree: Tree, ctx: Context): Context = { liftStringConcat(tree) match { // Optimization for expressions of the form "" + x. We can avoid the StringBuilder. - case List(Literal(Constant("")), arg) if !forMSIL => + case List(Literal(Constant("")), arg) => debuglog("Rewriting \"\" + x as String.valueOf(x) for: " + arg) val ctx1 = genLoad(arg, ctx, ObjectReference) ctx1.bb.emit(CALL_METHOD(String_valueOf, Static(false)), arg.pos) @@ -1575,7 +1400,7 @@ abstract class GenICode extends SubComponent { */ def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context)(thenCtx: Context, elseCtx: Context): Unit = { def getTempLocal = ctx.method.lookupLocal(nme.EQEQ_LOCAL_VAR) getOrElse { - ctx.makeLocal(l.pos, AnyRefClass.tpe, nme.EQEQ_LOCAL_VAR) + ctx.makeLocal(l.pos, AnyRefClass.tpe, nme.EQEQ_LOCAL_VAR.toString) } /** True if the equality comparison is between values that require the use of the rich equality @@ -1704,8 +1529,6 @@ abstract class GenICode extends SubComponent { * If the block consists of a single unconditional jump, prune * it by replacing the instructions in the predecessor to jump * directly to the JUMP target of the block. - * - * @param method ... */ def prune(method: IMethod) = { var changed = false @@ -1825,9 +1648,7 @@ abstract class GenICode extends SubComponent { t match { case t @ Apply(_, args) if sym.isLabel && !boundLabels(sym) => val newSym = getLabel(sym.pos, sym.name) - val tree = Apply(global.gen.mkAttributedRef(newSym), transformTrees(args)) setPos t.pos - tree.tpe = t.tpe - tree + Apply(global.gen.mkAttributedRef(newSym), transformTrees(args)) setPos t.pos setType t.tpe case t @ LabelDef(name, params, rhs) => val newSym = getLabel(t.pos, name) @@ -1961,18 +1782,7 @@ abstract class GenICode extends SubComponent { this } - def removeFinalizer(f: Tree): this.type = { - assert(cleanups.head contains f, - "Illegal nesting of cleanup operations: " + cleanups + " while exiting finalizer " + f); - cleanups = cleanups.tail - this - } - /** Prepare a new context upon entry into a method. - * - * @param m ... - * @param d ... - * @return ... */ def enterMethod(m: IMethod, d: DefDef): Context = { val ctx1 = new Context(this) setMethod(m) @@ -2011,10 +1821,9 @@ abstract class GenICode extends SubComponent { * 'covered' by this exception handler (in addition to the * previously active handlers). */ - private def newExceptionHandler(cls: Symbol, resultKind: TypeKind, pos: Position): ExceptionHandler = { + private def newExceptionHandler(cls: Symbol, pos: Position): ExceptionHandler = { handlerCount += 1 val exh = new ExceptionHandler(method, newTermNameCached("" + handlerCount), cls, pos) - exh.resultKind = resultKind method.addHandler(exh) handlers = exh :: handlers debuglog("added handler: " + exh); @@ -2044,16 +1853,6 @@ abstract class GenICode extends SubComponent { currentExceptionHandlers = currentExceptionHandlers.tail } - /** Remove the given handler from the list of active exception handlers. */ - def removeActiveHandler(exh: ExceptionHandler): Unit = { - assert(handlerCount > 0 && handlers.head == exh, - "Wrong nesting of exception handlers." + this + " for " + exh) - handlerCount -= 1 - handlers = handlers.tail - debuglog("removed handler: " + exh); - - } - /** Clone the current context */ def dup: Context = new Context(this) @@ -2072,19 +1871,19 @@ abstract class GenICode extends SubComponent { * It returns the resulting context, with the same active handlers as * before the call. Use it like: * - * <code> ctx.Try( ctx => { + * ` ctx.Try( ctx => { * ctx.bb.emit(...) // protected block * }, (ThrowableClass, * ctx => { * ctx.bb.emit(...); // exception handler * }), (AnotherExceptionClass, * ctx => {... - * } ))</code> + * } ))` */ def Try(body: Context => Context, handlers: List[(Symbol, TypeKind, Context => Context)], finalizer: Tree, - tree: Tree) = if (forMSIL) TryMsil(body, handlers, finalizer, tree) else { + tree: Tree) = { val outerCtx = this.dup // context for generating exception handlers, covered by finalizer val finalizerCtx = this.dup // context for generating finalizer handler @@ -2115,8 +1914,8 @@ abstract class GenICode extends SubComponent { } else ctx - val finalizerExh = if (finalizer != EmptyTree) Some({ - val exh = outerCtx.newExceptionHandler(NoSymbol, toTypeKind(finalizer.tpe), finalizer.pos) // finalizer covers exception handlers + if (finalizer != EmptyTree) { + val exh = outerCtx.newExceptionHandler(NoSymbol, finalizer.pos) // finalizer covers exception handlers this.addActiveHandler(exh) // .. and body aswell val ctx = finalizerCtx.enterExceptionHandler(exh) val exception = ctx.makeLocal(finalizer.pos, ThrowableClass.tpe, "exc") @@ -2128,91 +1927,29 @@ abstract class GenICode extends SubComponent { ctx1.bb.enterIgnoreMode; ctx1.bb.close finalizerCtx.endHandler() - exh - }) else None - - val exhs = handlers.map { case (sym, kind, handler) => // def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) = - val exh = this.newExceptionHandler(sym, kind, tree.pos) - var ctx1 = outerCtx.enterExceptionHandler(exh) - ctx1.addFinalizer(finalizer, finalizerCtx) - loadException(ctx1, exh, tree.pos) - ctx1 = handler(ctx1) - // emit finalizer - val ctx2 = emitFinalizer(ctx1) - ctx2.bb.closeWith(JUMP(afterCtx.bb)) - outerCtx.endHandler() - exh - } - val bodyCtx = this.newBlock - if (finalizer != EmptyTree) - bodyCtx.addFinalizer(finalizer, finalizerCtx) - - var finalCtx = body(bodyCtx) - finalCtx = emitFinalizer(finalCtx) - - outerCtx.bb.closeWith(JUMP(bodyCtx.bb)) - - finalCtx.bb.closeWith(JUMP(afterCtx.bb)) - - afterCtx - } - - - /** try-catch-finally blocks are actually simpler to emit in MSIL, because there - * is support for `finally` in bytecode. - * - * A - * try { .. } catch { .. } finally { .. } - * block is de-sugared into - * try { try { ..} catch { .. } } finally { .. } - * - * In ICode `finally` block is represented exactly the same as an exception handler, - * but with `NoSymbol` as the exception class. The covered blocks are all blocks of - * the `try { .. } catch { .. }`. - * - * Also, TryMsil does not enter any Finalizers into the `cleanups`, because the - * CLI takes care of running the finalizer when seeing a `leave` statement inside - * a try / catch. - */ - def TryMsil(body: Context => Context, - handlers: List[(Symbol, TypeKind, (Context => Context))], - finalizer: Tree, - tree: Tree) = { - - val outerCtx = this.dup // context for generating exception handlers, covered by finalizer - val finalizerCtx = this.dup // context for generating finalizer handler - val afterCtx = outerCtx.newBlock - - if (finalizer != EmptyTree) { - // finalizer is covers try and all catch blocks, i.e. - // try { try { .. } catch { ..} } finally { .. } - val exh = outerCtx.newExceptionHandler(NoSymbol, UNIT, tree.pos) - this.addActiveHandler(exh) - val ctx = finalizerCtx.enterExceptionHandler(exh) - loadException(ctx, exh, tree.pos) - val ctx1 = genLoad(finalizer, ctx, UNIT) - // need jump for the ICode to be valid. MSIL backend will emit `Endfinally` instead. - ctx1.bb.closeWith(JUMP(afterCtx.bb)) - finalizerCtx.endHandler() } - for (handler <- handlers) { - val exh = this.newExceptionHandler(handler._1, handler._2, tree.pos) + for ((sym, kind, handler) <- handlers) { + val exh = this.newExceptionHandler(sym, tree.pos) var ctx1 = outerCtx.enterExceptionHandler(exh) + ctx1.addFinalizer(finalizer, finalizerCtx) loadException(ctx1, exh, tree.pos) - ctx1 = handler._3(ctx1) - // msil backend will emit `Leave` to jump out of a handler - ctx1.bb.closeWith(JUMP(afterCtx.bb)) + ctx1 = handler(ctx1) + // emit finalizer + val ctx2 = emitFinalizer(ctx1) + ctx2.bb.closeWith(JUMP(afterCtx.bb)) outerCtx.endHandler() } val bodyCtx = this.newBlock + if (finalizer != EmptyTree) + bodyCtx.addFinalizer(finalizer, finalizerCtx) - val finalCtx = body(bodyCtx) + var finalCtx = body(bodyCtx) + finalCtx = emitFinalizer(finalCtx) outerCtx.bb.closeWith(JUMP(bodyCtx.bb)) - // msil backend will emit `Leave` to jump out of a try-block finalCtx.bb.closeWith(JUMP(afterCtx.bb)) afterCtx @@ -2341,7 +2078,6 @@ abstract class GenICode extends SubComponent { val locals: ListBuffer[Local] = new ListBuffer def add(l: Local) = locals += l - def remove(l: Local) = locals -= l /** Return all locals that are in scope. */ def varsInScope: Buffer[Local] = outer.varsInScope.clone() ++= locals diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala index f05def3123..5d32795e24 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala @@ -9,7 +9,6 @@ package icode import scala.collection.mutable import scala.collection.mutable.ListBuffer -import scala.tools.nsc.symtab._ abstract class ICodeCheckers { val global: Global @@ -49,7 +48,7 @@ abstract class ICodeCheckers { * @author Iulian Dragos * @version 1.0, 06/09/2005 * - * @todo Better checks for <code>MONITOR_ENTER/EXIT</code> + * @todo Better checks for `MONITOR_ENTER/EXIT` * Better checks for local var initializations * * @todo Iulian says: I think there's some outdated logic in the checker. @@ -103,7 +102,6 @@ abstract class ICodeCheckers { private def posStr(p: Position) = if (p.isDefined) p.line.toString else "<??>" - private def indent(s: String, spaces: Int): String = indent(s, " " * spaces) private def indent(s: String, prefix: String): String = { val lines = s split "\\n" lines map (prefix + _) mkString "\n" @@ -170,7 +168,6 @@ abstract class ICodeCheckers { val preds = bl.predecessors def hasNothingType(s: TypeStack) = s.nonEmpty && (s.head == NothingReference) - def hasNullType(s: TypeStack) = s.nonEmpty && (s.head == NullReference) /** XXX workaround #1: one stack empty, the other has BoxedUnit. * One example where this arises is: @@ -296,7 +293,7 @@ abstract class ICodeCheckers { else prefix + " with initial stack " + initial.types.mkString("[", ", ", "]") }) - var stack = new TypeStack(initial) + val stack = new TypeStack(initial) def checkStack(len: Int) { if (stack.length < len) ICodeChecker.this.icodeError("Expected at least " + len + " elements on the stack", stack) @@ -369,11 +366,6 @@ abstract class ICodeCheckers { } } - /** Return true if k1 is a subtype of any of the following types, - * according to the somewhat relaxed subtyping standards in effect here. - */ - def isOneOf(k1: TypeKind, kinds: TypeKind*) = kinds exists (k => isSubtype(k1, k)) - def subtypeTest(k1: TypeKind, k2: TypeKind): Unit = if (isSubtype(k1, k2)) () else typeError(k2, k1) @@ -381,10 +373,9 @@ abstract class ICodeCheckers { for (instr <- b) { this.instruction = instr - def checkLocal(local: Local): Unit = { - method lookupLocal local.sym.name getOrElse { - icodeError(" " + local + " is not defined in method " + method) - } + def checkLocal(local: Local) { + if ((method lookupLocal local.sym.name).isEmpty) + icodeError(s" $local is not defined in method $method") } def checkField(obj: TypeKind, field: Symbol): Unit = obj match { case REFERENCE(sym) => @@ -422,10 +413,7 @@ abstract class ICodeCheckers { } /** Checks that the object passed as receiver has a method - * <code>method</code> and that it is callable from the current method. - * - * @param receiver ... - * @param method ... + * `method` and that it is callable from the current method. */ def checkMethod(receiver: TypeKind, method: Symbol) = receiver match { @@ -476,7 +464,7 @@ abstract class ICodeCheckers { subtypeTest(elem, kind) pushStack(elem) case (a, b) => - icodeError(" expected and INT and a array reference, but " + + icodeError(" expected an INT and an array reference, but " + a + ", " + b + " found"); } @@ -495,7 +483,7 @@ abstract class ICodeCheckers { case LOAD_MODULE(module) => checkBool((module.isModule || module.isModuleClass), - "Expected module: " + module + " flags: " + Flags.flagsToString(module.flags)); + "Expected module: " + module + " flags: " + module.flagString); pushStack(toTypeKind(module.tpe)); case STORE_THIS(kind) => diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index 93201089e4..e2d387c65d 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -8,8 +8,6 @@ package backend package icode import java.io.PrintWriter -import scala.collection.mutable -import scala.tools.nsc.symtab._ import analysis.{ Liveness, ReachingDefinitions } import scala.tools.nsc.symtab.classfile.ICodeReader @@ -30,14 +28,14 @@ abstract class ICodes extends AnyRef with Repository { val global: Global - import global.{ log, definitions, settings, perRunCaches } + import global.{ log, definitions, settings, perRunCaches, devWarning } /** The ICode representation of classes */ val classes = perRunCaches.newMap[global.Symbol, IClass]() /** Debugging flag */ def shouldCheckIcode = settings.check contains global.genicode.phaseName - def checkerDebug(msg: String) = if (shouldCheckIcode && global.opt.debug) println(msg) + def checkerDebug(msg: String) = if (shouldCheckIcode && global.settings.debug.value) println(msg) /** The ICode linearizer. */ val linearizer: Linearizer = settings.Xlinearizer.value match { @@ -84,7 +82,7 @@ abstract class ICodes extends AnyRef // Something is leaving open/empty blocks around (see SI-4840) so // let's not kill the deal unless it's nonempty. if (b.isEmpty) { - log("!!! Found open but empty block while inlining " + m + ": removing from block list.") + devWarning(s"Found open but empty block while inlining $m: removing from block list.") m.code removeBlock b } else dumpMethodAndAbort(m, b) diff --git a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala index a38eab4515..80477f0c6e 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala @@ -8,7 +8,6 @@ package scala.tools.nsc package backend package icode -import scala.tools.nsc.ast._ import scala.collection.{ mutable, immutable } import mutable.ListBuffer @@ -198,142 +197,4 @@ trait Linearizers { def linearize(m: IMethod): List[BasicBlock] = m.blocks def linearizeAt(m: IMethod, start: BasicBlock): List[BasicBlock] = sys.error("not implemented") } - - /** The MSIL linearizer is used only for methods with at least one exception handler. - * It makes sure that all the blocks belonging to a `try`, `catch` or `finally` block - * are emitted in an order that allows the lexical nesting of try-catch-finally, just - * like in the source code. - */ - class MSILLinearizer extends Linearizer { - /** The MSIL linearizer first calls a NormalLInearizer. This is because the ILGenerator checks - * the stack size before emitting instructions. For instance, to emit a `store`, there needs - * to be some value on the stack. This can blow up in situations like this: - * ... - * jump 3 - * 4: store_local 0 - * jump 5 - * 3: load_value - * jump 4 - * 5: ... - * here, 3 must be scheduled first. - * - * The NormalLinearizer also removes dead blocks (blocks without predecessor). This is important - * in the following example: - * try { throw new Exception } - * catch { case e => throw e } - * which adds a dead block containing just a "throw" (which, again, would blow up code generation - * because of the stack size; there's no value on the stack when emitting that `throw`) - */ - val normalLinearizer = new NormalLinearizer() - - def linearize(m: IMethod): List[BasicBlock] = { - - val handlersByCovered = m.exh.groupBy(_.covered) - - // number of basic blocks covered by the entire try-catch expression - def size(covered: scala.collection.immutable.Set[BasicBlock]) = { - val hs = handlersByCovered(covered) - covered.size + (hs :\ 0)((h, s) => h.blocks.length + s) - } - - val tryBlocks = handlersByCovered.keys.toList sortBy size - var result = normalLinearizer.linearize(m) - val frozen = mutable.HashSet[BasicBlock](result.head) - - for (tryBlock <- tryBlocks) { - result = groupBlocks(m, result, handlersByCovered(tryBlock), frozen) - } - result - } - - /** @param handlers a list of handlers covering the same blocks (same try, multiple catches) - * @param frozen blocks can't be moved (fist block of a method, blocks directly following a try-catch) - */ - def groupBlocks(method: IMethod, blocks: List[BasicBlock], handlers: List[ExceptionHandler], frozen: mutable.HashSet[BasicBlock]) = { - assert(blocks.head == method.startBlock, method) - - // blocks before the try, and blocks for the try - val beforeAndTry = new ListBuffer[BasicBlock]() - // blocks for the handlers - val catches = handlers map (_ => new ListBuffer[BasicBlock]()) - // blocks to be put at the end - val after = new ListBuffer[BasicBlock]() - - var beforeTry = true - val head = handlers.head - - for (b <- blocks) { - if (head covers b) { - beforeTry = false - beforeAndTry += b - } else { - val handlerIndex = handlers.indexWhere(_.blocks.contains(b)) - if (handlerIndex >= 0) { - catches(handlerIndex) += b - } else if (beforeTry) { - beforeAndTry += b - } else { - after += b - } - } - } - - // reorder the blocks in "catches" so that the "firstBlock" is actually first - (catches, handlers).zipped foreach { (lb, handler) => - lb -= handler.startBlock - handler.startBlock +=: lb - } - - // The first block emitted after a try-catch must be the one that the try / catch - // blocks jump to (because in msil, these jumps cannot be emitted manually) - var firstAfter: Option[BasicBlock] = None - - // Find the (hopefully) unique successor, look at the try and all catch blocks - var blks = head.covered.toList :: handlers.map(_.blocks) - while (firstAfter.isEmpty && !blks.isEmpty) { - val b = blks.head - blks = blks.tail - - val leaving = leavingBlocks(b) - // no leaving blocks when the try or catch ends with THROW or RET - if (!leaving.isEmpty) { - assert(leaving.size <= 1, leaving) - firstAfter = Some(leaving.head) - } - } - if (firstAfter.isDefined) { - val b = firstAfter.get - if (frozen(b)) { - assert(after contains b, b +", "+ method) - } else { - frozen += b - if (beforeAndTry contains b) { - beforeAndTry -= b - } else { - assert(after contains b, after) - after -= b - } - b +=: after - } - } - - for (lb <- catches) { beforeAndTry ++= lb } - beforeAndTry ++= after - beforeAndTry.toList - } - - /** Returns all direct successors of `blocks` wich are not part - * that list, i.e. successors outside the `blocks` list. - */ - private def leavingBlocks(blocks: List[BasicBlock]) = { - val res = new mutable.HashSet[BasicBlock]() - for (b <- blocks; s <- b.directSuccessors; if (!blocks.contains(s))) - res += s - res - } - - def linearizeAt(m: IMethod, start: BasicBlock): List[BasicBlock] = { - sys.error("not implemented") - } - } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 7ba212f42e..248a505b54 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -7,10 +7,8 @@ package scala.tools.nsc package backend package icode -import java.io.PrintWriter import scala.collection.{ mutable, immutable } import scala.reflect.internal.util.{ SourceFile, NoSourceFile } -import symtab.Flags.{ DEFERRED } trait ReferenceEquality { override def hashCode = System.identityHashCode(this) @@ -48,8 +46,13 @@ trait Members { def touched = _touched def touched_=(b: Boolean): Unit = { - if (b) - blocks foreach (_.touched = true) + @annotation.tailrec def loop(xs: List[BasicBlock]) { + xs match { + case Nil => + case x :: xs => x.touched = true ; loop(xs) + } + } + if (b) loop(blocks.toList) _touched = b } @@ -128,9 +131,7 @@ trait Members { override def toString() = symbol.fullName - def lookupField(s: Symbol) = fields find (_.symbol == s) def lookupMethod(s: Symbol) = methods find (_.symbol == s) - def lookupMethod(s: Name) = methods find (_.symbol.name == s) /* returns this methods static ctor if it has one. */ def lookupStaticCtor: Option[IMethod] = methods find (_.symbol.isStaticConstructor) @@ -161,7 +162,6 @@ trait Members { def linearizedBlocks(lin: Linearizer = self.linearizer): List[BasicBlock] = lin linearize this def foreachBlock[U](f: BasicBlock => U): Unit = blocks foreach f - def foreachInstr[U](f: Instruction => U): Unit = foreachBlock(_.toList foreach f) var native = false @@ -194,7 +194,6 @@ trait Members { } def addLocals(ls: List[Local]) = ls foreach addLocal - def addParams(as: List[Local]) = as foreach addParam def lookupLocal(n: Name): Option[Local] = locals find (_.sym.name == n) def lookupLocal(sym: Symbol): Option[Local] = locals find (_.sym == sym) @@ -209,28 +208,7 @@ trait Members { override def toString() = symbol.fullName - def matchesSignature(other: IMethod) = { - (symbol.name == other.symbol.name) && - (params corresponds other.params)(_.kind == _.kind) && - (returnType == other.returnType) - } - import opcodes._ - def checkLocals(): Unit = { - def localsSet = (code.blocks flatMap { bb => - bb.iterator collect { - case LOAD_LOCAL(l) => l - case STORE_LOCAL(l) => l - } - }).toSet - - if (hasCode) { - log("[checking locals of " + this + "]") - locals filterNot localsSet foreach { l => - log("Local " + l + " is not declared in " + this) - } - } - } /** Merge together blocks that have a single successor which has a * single predecessor. Exception handlers are taken into account (they @@ -296,9 +274,6 @@ trait Members { /** Starting PC for this local's visibility range. */ var start: Int = _ - /** Ending PC for this local's visibility range. */ - var end: Int = _ - /** PC-based ranges for this local variable's visibility */ var ranges: List[(Int, Int)] = Nil diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index 8c9a72638d..137e2b556f 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -3,13 +3,10 @@ * @author Martin Odersky */ - - package scala.tools.nsc package backend package icode -import scala.tools.nsc.ast._ import scala.reflect.internal.util.{Position,NoPosition} /* @@ -67,7 +64,7 @@ import scala.reflect.internal.util.{Position,NoPosition} * in the source files. */ trait Opcodes { self: ICodes => - import global.{Symbol, NoSymbol, Type, Name, Constant}; + import global.{Symbol, NoSymbol, Name, Constant}; // categories of ICode instructions final val localsCat = 1 @@ -111,17 +108,11 @@ trait Opcodes { self: ICodes => // Vlad: I wonder why we keep producedTypes around -- it looks like an useless thing to have def producedTypes: List[TypeKind] = Nil - /** This method returns the difference of size of the stack when the instruction is used */ - def difference = produced-consumed - /** The corresponding position in the source file */ private var _pos: Position = NoPosition def pos: Position = _pos - /** Used by dead code elimination. */ - var useful: Boolean = false - def setPos(p: Position): this.type = { _pos = p this @@ -133,13 +124,6 @@ trait Opcodes { self: ICodes => } object opcodes { - - def mayThrow(i: Instruction): Boolean = i match { - case LOAD_LOCAL(_) | STORE_LOCAL(_) | CONSTANT(_) | THIS(_) | CZJUMP(_, _, _, _) - | DROP(_) | DUP(_) | RETURN(_) | LOAD_EXCEPTION(_) | JUMP(_) | CJUMP(_, _, _, _) => false - case _ => true - } - /** Loads "this" on top of the stack. * Stack: ... * ->: ...:ref @@ -715,8 +699,6 @@ trait Opcodes { self: ICodes => /** Is this a static method call? */ def isStatic: Boolean = false - def isSuper: Boolean = false - /** Is this an instance method call? */ def hasInstance: Boolean = true @@ -750,77 +732,7 @@ trait Opcodes { self: ICodes => * On JVM, translated to `invokespecial`. */ case class SuperCall(mix: Name) extends InvokeStyle { - override def isSuper = true override def toString(): String = { "super(" + mix + ")" } } - - - // CLR backend - - case class CIL_LOAD_LOCAL_ADDRESS(local: Local) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = "CIL_LOAD_LOCAL_ADDRESS "+local //+isArgument?" (argument)":""; - - override def consumed = 0 - override def produced = 1 - - override def producedTypes = msil_mgdptr(local.kind) :: Nil - - override def category = localsCat - } - - case class CIL_LOAD_FIELD_ADDRESS(field: Symbol, isStatic: Boolean) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = - "CIL_LOAD_FIELD_ADDRESS " + (if (isStatic) field.fullName else field.toString) - - override def consumed = if (isStatic) 0 else 1 - override def produced = 1 - - override def consumedTypes = if (isStatic) Nil else REFERENCE(field.owner) :: Nil; - override def producedTypes = msil_mgdptr(REFERENCE(field.owner)) :: Nil; - - override def category = fldsCat - } - - case class CIL_LOAD_ARRAY_ITEM_ADDRESS(kind: TypeKind) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = "CIL_LOAD_ARRAY_ITEM_ADDRESS (" + kind + ")" - - override def consumed = 2 - override def produced = 1 - - override def consumedTypes = ARRAY(kind) :: INT :: Nil - override def producedTypes = msil_mgdptr(kind) :: Nil - - override def category = arraysCat - } - - case class CIL_UNBOX(valueType: TypeKind) extends Instruction { - override def toString(): String = "CIL_UNBOX " + valueType - override def consumed = 1 - override def consumedTypes = ObjectReferenceList // actually consumes a 'boxed valueType' - override def produced = 1 - override def producedTypes = msil_mgdptr(valueType) :: Nil - override def category = objsCat - } - - case class CIL_INITOBJ(valueType: TypeKind) extends Instruction { - override def toString(): String = "CIL_INITOBJ " + valueType - override def consumed = 1 - override def consumedTypes = ObjectReferenceList // actually consumes a managed pointer - override def produced = 0 - override def category = objsCat - } - - case class CIL_NEWOBJ(method: Symbol) extends Instruction { - override def toString(): String = "CIL_NEWOBJ " + hostClass.fullName + method.fullName - var hostClass: Symbol = method.owner; - override def consumed = method.tpe.paramTypes.length - override def consumedTypes = method.tpe.paramTypes map toTypeKind - override def produced = 1 - override def producedTypes = toTypeKind(method.tpe.resultType) :: Nil - override def category = objsCat - } } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala b/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala index c8579041ba..351d99f51a 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala @@ -76,25 +76,12 @@ trait Primitives { self: ICodes => /** Pretty printer for primitives */ class PrimitivePrinter(out: PrintWriter) { - def print(s: String): PrimitivePrinter = { out.print(s) this } def print(o: AnyRef): PrimitivePrinter = print(o.toString()) - - def printPrimitive(prim: Primitive) = prim match { - case Negation(kind) => - print("!") - - case Test(op, kind, zero) => - print(op).print(kind) - - case Comparison(op, kind) => - print(op).print("(").print(kind) - - } } /** This class represents a comparison operation. */ diff --git a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala index 6cac641e3e..61af6e5119 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala @@ -8,13 +8,9 @@ package backend package icode import java.io.PrintWriter -import scala.tools.nsc.symtab.Flags -import scala.reflect.internal.util.Position trait Printers { self: ICodes => import global._ - import global.icodes.opcodes._ - import global.icodes._ class TextPrinter(writer: PrintWriter, lin: Linearizer) { private var margin = 0 diff --git a/src/compiler/scala/tools/nsc/backend/icode/Repository.scala b/src/compiler/scala/tools/nsc/backend/icode/Repository.scala index e73015c4da..e92e61c957 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Repository.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Repository.scala @@ -26,17 +26,6 @@ trait Repository { /** The icode of the given class, if available */ def icode(sym: Symbol): Option[IClass] = (classes get sym) orElse (loaded get sym) - /** The icode of the given class. If not available, it loads - * its bytecode. - */ - def icode(sym: Symbol, force: Boolean): IClass = - icode(sym) getOrElse { - log("loading " + sym) - load(sym) - assert(available(sym)) - loaded(sym) - } - /** Load bytecode for given symbol. */ def load(sym: Symbol): Boolean = { try { diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index 4f8fda8024..a32b00f385 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -55,7 +55,7 @@ trait TypeKinds { self: ICodes => def toType: Type = reversePrimitiveMap get this map (_.tpe) getOrElse { this match { - case REFERENCE(cls) => cls.tpe + case REFERENCE(cls) => cls.tpe_* case ARRAY(elem) => arrayType(elem.toType) case _ => abort("Unknown type kind.") } @@ -66,7 +66,6 @@ trait TypeKinds { self: ICodes => def isValueType = false def isBoxedType = false final def isRefOrArrayType = isReferenceType || isArrayType - final def isRefArrayOrBoxType = isRefOrArrayType || isBoxedType final def isNothingType = this == NothingReference final def isNullType = this == NullReference final def isInterfaceType = this match { @@ -114,8 +113,6 @@ trait TypeKinds { self: ICodes => } } - var lubs0 = 0 - /** * The least upper bound of two typekinds. They have to be either * REFERENCE or ARRAY kinds. @@ -139,8 +136,7 @@ trait TypeKinds { self: ICodes => * Here we make the adjustment by rewinding to a pre-erasure state and * sifting through the parents for a class type. */ - def lub0(tk1: TypeKind, tk2: TypeKind): Type = beforeUncurry { - import definitions._ + def lub0(tk1: TypeKind, tk2: TypeKind): Type = enteringUncurry { val tp = global.lub(List(tk1.toType, tk2.toType)) val (front, rest) = tp.parents span (_.typeSymbol.isTrait) @@ -372,10 +368,10 @@ trait TypeKinds { self: ICodes => /** Return the TypeKind of the given type * - * Call to .normalize fixes #3003 (follow type aliases). Otherwise, + * Call to dealiasWiden fixes #3003 (follow type aliases). Otherwise, * arrayOrClassType below would return ObjectReference. */ - def toTypeKind(t: Type): TypeKind = t.normalize match { + def toTypeKind(t: Type): TypeKind = t.dealiasWiden match { case ThisType(ArrayClass) => ObjectReference case ThisType(sym) => REFERENCE(sym) case SingleType(_, sym) => primitiveOrRefType(sym) @@ -431,11 +427,4 @@ trait TypeKinds { self: ICodes => primitiveTypeMap.getOrElse(sym, newReference(sym)) private def primitiveOrClassType(sym: Symbol, targs: List[Type]) = primitiveTypeMap.getOrElse(sym, arrayOrClassType(sym, targs)) - - def msil_mgdptr(tk: TypeKind): TypeKind = (tk: @unchecked) match { - case REFERENCE(cls) => REFERENCE(loaders.clrTypes.mdgptrcls4clssym(cls)) - // TODO have ready class-symbols for the by-ref versions of built-in valuetypes - case _ => abort("cannot obtain a managed pointer for " + tk) - } - } diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala index 23d3d05c64..57d51dad49 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala @@ -15,15 +15,11 @@ package icode trait TypeStacks { self: ICodes => - import opcodes._ - /* This class simulates the type of the operand * stack of the ICode. */ type Rep = List[TypeKind] - object NoTypeStack extends TypeStack(Nil) { } - class TypeStack(var types: Rep) { if (types.nonEmpty) checkerDebug("Created " + this) @@ -71,14 +67,6 @@ trait TypeStacks { def apply(n: Int): TypeKind = types(n) - /** - * A TypeStack agrees with another one if they have the same - * length and each type kind agrees position-wise. Two - * types agree if one is a subtype of the other. - */ - def agreesWith(other: TypeStack): Boolean = - (types corresponds other.types)((t1, t2) => t1 <:< t2 || t2 <:< t1) - /* This method returns a String representation of the stack */ override def toString() = if (types.isEmpty) "[]" 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 53111d0ade..7f32b2b764 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala @@ -26,12 +26,8 @@ abstract class CopyPropagation { case object This extends Location /** Values that can be on the stack. */ - abstract class Value { - def isRecord = false - } - case class Record(cls: Symbol, bindings: mutable.Map[Symbol, Value]) extends Value { - override def isRecord = true - } + abstract class Value { } + case class Record(cls: Symbol, bindings: mutable.Map[Symbol, Value]) extends Value { } /** The value of some location in memory. */ case class Deref(l: Location) extends Value @@ -91,16 +87,6 @@ abstract class CopyPropagation { loop(l) getOrElse Deref(LocalVar(l)) } - /* Return the binding for the given field of the given record */ - def getBinding(r: Record, f: Symbol): Value = { - assert(r.bindings contains f, "Record " + r + " does not contain a field " + f) - - r.bindings(f) match { - case Deref(LocalVar(l)) => getBinding(l) - case target => target - } - } - /** Return a local which contains the same value as this field, if any. * If the field holds a reference to a local, the returned value is the * binding of that local. @@ -463,14 +449,9 @@ abstract class CopyPropagation { } } - /** Update the state <code>s</code> after the call to <code>method</code>. + /** Update the state `s` after the call to `method`. * The stack elements are dropped and replaced by the result of the call. * If the method is impure, all bindings to record fields are cleared. - * - * @param state ... - * @param method ... - * @param static ... - * @return ... */ final def simulateCall(state: copyLattice.State, method: Symbol, static: Boolean): copyLattice.State = { val out = new copyLattice.State(state.bindings, state.stack); @@ -554,10 +535,7 @@ abstract class CopyPropagation { bindings } - /** Is symbol <code>m</code> a pure method? - * - * @param m ... - * @return ... + /** Is symbol `m` a pure method? */ final def isPureMethod(m: Symbol): Boolean = m.isGetter // abstract getters are still pure, as we 'know' diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala index 04c3eedbad..cc3a7eb876 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala @@ -34,15 +34,6 @@ trait DataFlowAnalysis[L <: SemiLattice] { f } - /** Reinitialize, but keep the old solutions. Should be used when reanalyzing the - * same method, after some code transformation. - */ - def reinit(f: => Unit): Unit = { - iterations = 0 - worklist.clear; visited.clear; - f - } - def run(): Unit /** Implements forward dataflow analysis: the transfer function is @@ -82,10 +73,6 @@ trait DataFlowAnalysis[L <: SemiLattice] { sys.error("Could not find element " + e.getMessage) } - /** ... - * - * @param f ... - */ def backwardAnalysis(f: (P, lattice.Elem) => lattice.Elem): Unit = while (worklist.nonEmpty) { if (stat) iterations += 1 diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala index 2717c432e8..48755d4424 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala @@ -52,7 +52,7 @@ abstract class ReachingDefinitions { // it makes it harder to spot the real problems. val result = (a.stack, b.stack).zipped map (_ ++ _) if (settings.debug.value && (a.stack.length != b.stack.length)) - debugwarn("Mismatched stacks in ReachingDefinitions#lub2: " + a.stack + ", " + b.stack + ", returning " + result) + devWarning(s"Mismatched stacks in ReachingDefinitions#lub2: ${a.stack}, ${b.stack}, returning $result") result } ) @@ -155,7 +155,7 @@ abstract class ReachingDefinitions { import lattice.IState def updateReachingDefinition(b: BasicBlock, idx: Int, rd: ListSet[Definition]): ListSet[Definition] = { val STORE_LOCAL(local) = b(idx) - var tmp = local + val tmp = local (rd filter { case (l, _, _) => l != tmp }) + ((tmp, b, idx)) } @@ -197,7 +197,7 @@ abstract class ReachingDefinitions { def findDefs(bb: BasicBlock, idx: Int, m: Int, depth: Int): List[(BasicBlock, Int)] = if (idx > 0) { assert(bb.closed, bb) - var instrs = bb.getArray + val instrs = bb.getArray var res: List[(BasicBlock, Int)] = Nil var i = idx var n = m diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala index b2ecb431ee..c9d295a350 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala @@ -68,7 +68,6 @@ abstract class TypeFlowAnalysis { * names to types and a type stack. */ object typeFlowLattice extends SemiLattice { - import icodes._ type Elem = IState[VarBinding, icodes.TypeStack] val top = new Elem(new VarBinding, typeStackLattice.top) @@ -136,7 +135,7 @@ abstract class TypeFlowAnalysis { timer.start // icodes.lubs0 = 0 forwardAnalysis(blockTransfer) - val t = timer.stop + timer.stop if (settings.debug.value) { linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(visited.contains(b), @@ -269,36 +268,6 @@ abstract class TypeFlowAnalysis { out } // interpret - - class SimulatedStack { - private var types: List[InferredType] = Nil - private var depth = 0 - - /** Remove and return the topmost element on the stack. If the - * stack is empty, return a reference to a negative index on the - * stack, meaning it refers to elements pushed by a predecessor block. - */ - def pop: InferredType = types match { - case head :: rest => - types = rest - head - case _ => - depth -= 1 - TypeOfStackPos(depth) - } - - def pop2: (InferredType, InferredType) = { - (pop, pop) - } - - def push(t: InferredType) { - depth += 1 - types = types ::: List(t) - } - - def push(k: TypeKind) { push(Const(k)) } - } - abstract class InferredType { /** Return the type kind pointed by this inferred type. */ def getKind(in: lattice.Elem): icodes.TypeKind = this match { @@ -326,7 +295,6 @@ abstract class TypeFlowAnalysis { class TransferFunction(consumed: Int, gens: List[Gen]) extends (lattice.Elem => lattice.Elem) { def apply(in: lattice.Elem): lattice.Elem = { val out = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack)) - val bindings = out.vars val stack = out.stack out.stack.pop(consumed) @@ -389,7 +357,7 @@ abstract class TypeFlowAnalysis { timer.start forwardAnalysis(blockTransfer) - val t = timer.stop + timer.stop /* Now that `forwardAnalysis(blockTransfer)` has finished, all inlining candidates can be found in `remainingCALLs`, whose keys are callsites and whose values are pieces of information about the typestack just before the callsite in question. @@ -546,9 +514,6 @@ abstract class TypeFlowAnalysis { relevantBBs ++= blocks } - /* the argument is also included in the result */ - private def transitivePreds(b: BasicBlock): Set[BasicBlock] = { transitivePreds(List(b)) } - /* those BBs in the argument are also included in the result */ private def transitivePreds(starters: Traversable[BasicBlock]): Set[BasicBlock] = { val result = mutable.Set.empty[BasicBlock] @@ -562,19 +527,6 @@ abstract class TypeFlowAnalysis { result.toSet } - /* those BBs in the argument are also included in the result */ - private def transitiveSuccs(starters: Traversable[BasicBlock]): Set[BasicBlock] = { - val result = mutable.Set.empty[BasicBlock] - var toVisit: List[BasicBlock] = starters.toList.distinct - while(toVisit.nonEmpty) { - val h = toVisit.head - toVisit = toVisit.tail - result += h - for(p <- h.successors; if !result(p) && !toVisit.contains(p)) { toVisit = p :: toVisit } - } - result.toSet - } - /* A basic block B is "on the perimeter" of the current control-flow subgraph if none of its successors belongs to that subgraph. * In that case, for the purposes of inlining, we're interested in the typestack right before the last inline candidate in B, not in those afterwards. * In particular we can do without computing the outflow at B. */ @@ -685,12 +637,6 @@ abstract class TypeFlowAnalysis { if(!worklist.contains(b)) { worklist += b } } - /* this is not a general purpose method to add to the worklist, - * because the assert is expected to hold only when called from MTFAGrowable.reinit() */ - private def enqueue(bs: Traversable[BasicBlock]) { - bs foreach enqueue - } - private def blankOut(blocks: scala.collection.Set[BasicBlock]) { blocks foreach { b => in(b) = typeFlowLattice.bottom @@ -761,10 +707,6 @@ abstract class TypeFlowAnalysis { private var lastStart = 0L - def reset() { - millis = 0L - } - def start() { lastStart = System.currentTimeMillis } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index fb1f45fa40..941ccd9a2d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -9,9 +9,8 @@ package backend.jvm import java.io.{ DataOutputStream, FileOutputStream, OutputStream, File => JFile } import scala.tools.nsc.io._ import scala.tools.nsc.util.ScalaClassLoader -import scala.tools.util.JavapClass -import java.util.jar.{ JarEntry, JarOutputStream, Attributes } -import Attributes.Name +import scala.tools.util.{ Javap, JavapClass } +import java.util.jar.Attributes.Name import scala.language.postfixOps /** For the last mile: turning generated bytecode in memory into @@ -23,7 +22,7 @@ trait BytecodeWriters { import global._ private def outputDirectory(sym: Symbol): AbstractFile = ( - settings.outputDirs.outputDirFor(beforeFlatten(sym.sourceFile)) + settings.outputDirs.outputDirFor(enteringFlatten(sym.sourceFile)) ) private def getFile(base: AbstractFile, /*cls.getName()*/ clsName: String, suffix: String): AbstractFile = { var dir = base @@ -60,27 +59,32 @@ trait BytecodeWriters { override def close() = writer.close() } + /** To be mixed-in with the BytecodeWriter that generates + * the class file to be disassembled. + */ trait JavapBytecodeWriter extends BytecodeWriter { val baseDir = Directory(settings.Ygenjavap.value).createDirectory() - - def emitJavap(bytes: Array[Byte], javapFile: io.File) { - val pw = javapFile.printWriter() - val javap = new JavapClass(ScalaClassLoader.appLoader, pw) { - override def findBytes(path: String): Array[Byte] = bytes - } - - try javap(Seq("-verbose", "dummy")) foreach (_.show()) - finally pw.close() + val cl = ScalaClassLoader.appLoader + + def emitJavap(classFile: AbstractFile, javapFile: File) { + val pw = javapFile.printWriter() + try { + val javap = new JavapClass(cl, pw) { + override def findBytes(path: String): Array[Byte] = classFile.toByteArray + } + javap(Seq("-verbose", "-protected", classFile.name)) foreach (_.show()) + } finally pw.close() } abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { super.writeClass(label, jclassName, jclassBytes, sym) - val bytes = getFile(sym, jclassName, ".class").toByteArray + val classFile = getFile(sym, jclassName, ".class") val segments = jclassName.split("[./]") val javapFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "javap" toFile; - javapFile.parent.createDirectory() - emitJavap(bytes, javapFile) + + if (Javap.isAvailable(cl)) emitJavap(classFile, javapFile) + else warning("No javap on classpath, skipping javap output.") } } @@ -102,7 +106,7 @@ trait BytecodeWriters { super.writeClass(label, jclassName, jclassBytes, sym) val pathName = jclassName - var dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile; + val dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile; dumpFile.parent.createDirectory() val outstream = new DataOutputStream(new FileOutputStream(dumpFile.path)) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 0abbe44b02..45c366cc69 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -6,12 +6,9 @@ package scala.tools.nsc package backend.jvm -import java.nio.ByteBuffer import scala.collection.{ mutable, immutable } import scala.reflect.internal.pickling.{ PickleFormat, PickleBuffer } import scala.tools.nsc.symtab._ -import scala.tools.nsc.io.AbstractFile - import scala.tools.asm import asm.Label @@ -32,6 +29,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { /** Create a new phase */ override def newPhase(p: Phase): Phase = new AsmPhase(p) + /** From the reference documentation of the Android SDK: + * The `Parcelable` interface identifies classes whose instances can be written to and restored from a `Parcel`. + * Classes implementing the `Parcelable` interface must also have a static field called `CREATOR`, + * which is an object implementing the `Parcelable.Creator` interface. + */ + private val androidFieldName = newTermName("CREATOR") + + private lazy val AndroidParcelableInterface = rootMirror.getClassIfDefined("android.os.Parcelable") + private lazy val AndroidCreatorClass = rootMirror.getClassIfDefined("android.os.Parcelable$Creator") + /** JVM code generation phase */ class AsmPhase(prev: Phase) extends ICodePhase(prev) { @@ -39,7 +46,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { override def erasedTypes = true def apply(cls: IClass) = sys.error("no implementation") - val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo") + // Lazy val; can't have eager vals in Phase constructors which may + // cause cycles before Global has finished initialization. + lazy val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo") private def initBytecodeWriter(entryPoints: List[IClass]): BytecodeWriter = { settings.outputDirs.getSingleOutput match { @@ -62,13 +71,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { new DirectToJarfileWriter(f.file) case _ => + import scala.tools.util.Javap if (settings.Ygenjavap.isDefault) { if(settings.Ydumpclasses.isDefault) new ClassBytecodeWriter { } else new ClassBytecodeWriter with DumpBytecodeWriter { } } - else new ClassBytecodeWriter with JavapBytecodeWriter { } + else if (Javap.isAvailable()) new ClassBytecodeWriter with JavapBytecodeWriter { } + else { + warning("No javap on classpath, skipping javap output.") + new ClassBytecodeWriter { } + } // TODO A ScalapBytecodeWriter could take asm.util.Textifier as starting point. // Three areas where javap ouput is less than ideal (e.g. when comparing versions of the same classfile) are: @@ -133,7 +147,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { /* don't javaNameCache.clear() because that causes the following tests to fail: * test/files/run/macro-repl-dontexpand.scala * test/files/jvm/interpreter.scala - * TODO but why? what use could javaNameCache possibly see once GenJVM is over? + * TODO but why? what use could javaNameCache possibly see once GenASM is over? */ /* TODO After emitting all class files (e.g., in a separate compiler phase) ASM can perform bytecode verification: @@ -248,7 +262,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } def isTopLevelModule(sym: Symbol): Boolean = - afterPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } + exitingPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } def isStaticModule(sym: Symbol): Boolean = { sym.isModuleClass && !sym.isImplClass && !sym.isLifted @@ -398,7 +412,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { /** basic functionality for class file building */ abstract class JBuilder(bytecodeWriter: BytecodeWriter) { - val EMPTY_JTYPE_ARRAY = Array.empty[asm.Type] val EMPTY_STRING_ARRAY = Array.empty[String] val mdesc_arglessvoid = "()V" @@ -466,7 +479,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { /** Specialized array conversion to prevent calling * java.lang.reflect.Array.newInstance via TraversableOnce.toArray */ - def mkArray(xs: Traversable[asm.Type]): Array[asm.Type] = { val a = new Array[asm.Type](xs.size); xs.copyToArray(a); a } def mkArray(xs: Traversable[String]): Array[String] = { val a = new Array[String](xs.size); xs.copyToArray(a); a } // ----------------------------------------------------------------------------------------- @@ -516,7 +528,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { * of inner class all until root class. */ def collectInnerClass(s: Symbol): Unit = { - // TODO: some beforeFlatten { ... } which accounts for + // TODO: some enteringFlatten { ... } which accounts for // being nested in parameterized classes (if we're going to selectively flatten.) val x = innerClassSymbolFor(s) if(x ne NoSymbol) { @@ -531,7 +543,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { collectInnerClass(sym) - var hasInternalName = (sym.isClass || (sym.isModule && !sym.isMethod)) + val hasInternalName = (sym.isClass || (sym.isModule && !sym.isMethod)) val cachedJN = javaNameCache.getOrElseUpdate(sym, { if (hasInternalName) { sym.javaBinaryName } else { sym.javaSimpleName } @@ -541,12 +553,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { val internalName = cachedJN.toString() val trackedSym = jsymbol(sym) reverseJavaName.get(internalName) match { - case None => + case Some(oldsym) if oldsym.exists && trackedSym.exists => + assert( + // In contrast, neither NothingClass nor NullClass show up bytecode-level. + (oldsym == trackedSym) || (oldsym == RuntimeNothingClass) || (oldsym == RuntimeNullClass) || (oldsym.isModuleClass && (oldsym.sourceModule == trackedSym.sourceModule)), + s"""|Different class symbols have the same bytecode-level internal name: + | name: $internalName + | oldsym: ${oldsym.fullNameString} + | tracked: ${trackedSym.fullNameString} + """.stripMargin + ) + case _ => reverseJavaName.put(internalName, trackedSym) - case Some(oldsym) => - assert((oldsym == trackedSym) || (oldsym == RuntimeNothingClass) || (oldsym == RuntimeNullClass) || - (oldsym.isModuleClass && (oldsym.sourceModule == trackedSym.sourceModule)), // In contrast, neither NothingClass nor NullClass show up bytecode-level. - "how can getCommonSuperclass() do its job if different class symbols get the same bytecode-level internal name: " + internalName) } } @@ -619,7 +637,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { innerSym.rawname + innerSym.moduleSuffix // add inner classes which might not have been referenced yet - afterErasure { + exitingErasure { for (sym <- List(csym, csym.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass) innerClassBuffer += m } @@ -810,7 +828,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { if (!needsGenericSignature(sym)) { return null } - val memberTpe = beforeErasure(owner.thisType.memberInfo(sym)) + val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) if (jsOpt.isEmpty) { return null } @@ -827,10 +845,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // Run the signature parser to catch bogus signatures. val isValidSignature = wrap { // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) - import scala.tools.asm.util.SignatureChecker - if (sym.isMethod) { SignatureChecker checkMethodSignature sig } // requires asm-util.jar - else if (sym.isTerm) { SignatureChecker checkFieldSignature sig } - else { SignatureChecker checkClassSignature sig } + import scala.tools.asm.util.CheckClassAdapter + if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar + else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } + else { CheckClassAdapter checkClassSignature sig } } if(!isValidSignature) { @@ -844,7 +862,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } if ((settings.check containsName phaseName)) { - val normalizedTpe = beforeErasure(erasure.prepareSigMap(memberTpe)) + val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) val bytecodeTpe = owner.thisType.memberInfo(sym) if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { getCurrentCUnit().warning(sym.pos, @@ -1099,7 +1117,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { debuglog("Dumping mirror class for object: " + moduleClass) val linkedClass = moduleClass.companionClass - val linkedModule = linkedClass.companionSymbol lazy val conflictingNames: Set[Name] = { (linkedClass.info.members collect { case sym if sym.name.isTermName => sym.name }).toSet } @@ -1125,16 +1142,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { trait JAndroidBuilder { self: JPlainBuilder => - /** From the reference documentation of the Android SDK: - * The `Parcelable` interface identifies classes whose instances can be written to and restored from a `Parcel`. - * Classes implementing the `Parcelable` interface must also have a static field called `CREATOR`, - * which is an object implementing the `Parcelable.Creator` interface. - */ - private val androidFieldName = newTermName("CREATOR") - - private lazy val AndroidParcelableInterface = rootMirror.getClassIfDefined("android.os.Parcelable") - private lazy val AndroidCreatorClass = rootMirror.getClassIfDefined("android.os.Parcelable$Creator") - def isAndroidParcelableClass(sym: Symbol) = (AndroidParcelableInterface != NoSymbol) && (sym.parentSymbols contains AndroidParcelableInterface) @@ -1142,7 +1149,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { /* Typestate: should be called before emitting fields (because it adds an IField to the current IClass). */ def addCreatorCode(block: BasicBlock) { val fieldSymbol = ( - clasz.symbol.newValue(newTermName(androidFieldName), NoPosition, Flags.STATIC | Flags.FINAL) + clasz.symbol.newValue(androidFieldName, NoPosition, Flags.STATIC | Flags.FINAL) setInfo AndroidCreatorClass.tpe ) val methodSymbol = definitions.getMember(clasz.symbol.companionModule, androidFieldName) @@ -1157,7 +1164,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { jclass.visitField( PublicStaticFinal, - androidFieldName, + androidFieldName.toString, tdesc_creator, null, // no java-generic-signature null // no initial value @@ -1177,7 +1184,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { clinit.visitMethodInsn( asm.Opcodes.INVOKEVIRTUAL, moduleName, - androidFieldName, + androidFieldName.toString, asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*) ) @@ -1185,7 +1192,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { clinit.visitFieldInsn( asm.Opcodes.PUTSTATIC, thisName, - androidFieldName, + androidFieldName.toString, tdesc_creator ) } @@ -1267,8 +1274,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // Additional interface parents based on annotations and other cues def newParentForAttr(attr: Symbol): Option[Symbol] = attr match { - case SerializableAttr => Some(SerializableClass) - case CloneableAttr => Some(CloneableClass) case RemoteAttr => Some(RemoteInterfaceClass) case _ => None } @@ -1371,7 +1376,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { if (lmoc != NoSymbol) { // it must be a top level class (name contains no $s) val isCandidateForForwarders = { - afterPickler { !(lmoc.name.toString contains '$') && lmoc.hasModuleFlag && !lmoc.isImplClass && !lmoc.isNestedClass } + exitingPickler { !(lmoc.name.toString contains '$') && lmoc.hasModuleFlag && !lmoc.isImplClass && !lmoc.isNestedClass } } if (isCandidateForForwarders) { log("Adding static forwarders from '%s' to implementations in '%s'".format(c.symbol, lmoc)) @@ -1700,11 +1705,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { import asm.Opcodes; - def aconst(cst: AnyRef) { - if (cst == null) { jmethod.visitInsn(Opcodes.ACONST_NULL) } - else { jmethod.visitLdcInsn(cst) } - } - final def boolconst(b: Boolean) { iconst(if(b) 1 else 0) } def iconst(cst: Int) { @@ -2150,7 +2150,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def getMerged(): scala.collection.Map[Local, List[Interval]] = { // TODO should but isn't: unbalanced start(s) of scope(s) - val shouldBeEmpty = pending filter { p => val Pair(k, st) = p; st.nonEmpty }; + val shouldBeEmpty = pending filter { p => val Pair(_, st) = p; st.nonEmpty }; val merged = mutable.Map[Local, List[Interval]]() def addToMerged(lv: Local, start: Label, end: Label) { val intv = Interval(start, end) @@ -2213,7 +2213,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } // quest for deterministic output that Map.toList doesn't provide (so that ant test.stability doesn't complain). val srtd = fltnd.sortBy { kr => - val Triple(name: String, local: Local, intrvl: Interval) = kr + val Triple(name: String, _, intrvl: Interval) = kr Triple(intrvl.start, intrvl.end - intrvl.start, name) // ie sort by (start, length, name) } @@ -2333,6 +2333,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { import asm.Opcodes (instr.category: @scala.annotation.switch) match { + case icodes.localsCat => def genLocalInstr() = (instr: @unchecked) match { case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0) @@ -2371,7 +2372,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { case LOAD_MODULE(module) => // assert(module.isModule, "Expected module: " + module) - debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags)); + debuglog("generating LOAD_MODULE for: " + module + " flags: " + module.flagString); if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) { jmethod.visitVarInsn(Opcodes.ALOAD, 0) } else { @@ -2426,7 +2427,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { case icodes.objsCat => def genObjsInstr() = (instr: @unchecked) match { - case BOX(kind) => val MethodNameAndType(mname, mdesc) = jBoxTo(kind) jcode.invokestatic(BoxesRunTime, mname, mdesc) @@ -2448,8 +2448,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def genFldsInstr() = (instr: @unchecked) match { case lf @ LOAD_FIELD(field, isStatic) => - var owner = javaName(lf.hostClass) - debuglog("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags)) + val owner = javaName(lf.hostClass) + debuglog("LOAD_FIELD with owner: " + owner + " flags: " + field.owner.flagString) val fieldJName = javaName(field) val fieldDescr = descriptor(field) val opc = if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD @@ -2867,15 +2867,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { ////////////////////// local vars /////////////////////// - // def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe)) - def sizeOf(k: TypeKind): Int = if(k.isWideType) 2 else 1 - // def indexOf(m: IMethod, sym: Symbol): Int = { - // val Some(local) = m lookupLocal sym - // indexOf(local) - // } - final def indexOf(local: Local): Int = { assert(local.index >= 0, "Invalid index for: " + local + "{" + local.## + "}: ") local.index @@ -3281,8 +3274,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { var wasReduced = false val entryPoints: List[BasicBlock] = m.startBlock :: (m.exh map (_.startBlock)); - var elided = mutable.Set.empty[BasicBlock] // debug - var newTargets = mutable.Set.empty[BasicBlock] // debug + val elided = mutable.Set.empty[BasicBlock] // debug + val newTargets = mutable.Set.empty[BasicBlock] // debug for (ep <- entryPoints) { var reachable = directSuccStar(ep) // this list may contain blocks belonging to jump-chains that we'll skip over diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenAndroid.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenAndroid.scala deleted file mode 100644 index 72b7e35408..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenAndroid.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Stephane Micheloud - */ - - -package scala.tools.nsc -package backend.jvm - -import ch.epfl.lamp.fjbg._ -import symtab.Flags - -trait GenAndroid { - self: GenJVM => - - import global._ - import icodes._ - import opcodes._ - - /** From the reference documentation of the Android SDK: - * The `Parcelable` interface identifies classes whose instances can be - * written to and restored from a `Parcel`. Classes implementing the - * `Parcelable` interface must also have a static field called `CREATOR`, - * which is an object implementing the `Parcelable.Creator` interface. - */ - private val fieldName = newTermName("CREATOR") - - private lazy val AndroidParcelableInterface = rootMirror.getClassIfDefined("android.os.Parcelable") - private lazy val AndroidCreatorClass = rootMirror.getClassIfDefined("android.os.Parcelable$Creator") - - def isAndroidParcelableClass(sym: Symbol) = - (AndroidParcelableInterface != NoSymbol) && - (sym.parentSymbols contains AndroidParcelableInterface) - - def addCreatorCode(codegen: BytecodeGenerator, block: BasicBlock) { - import codegen._ - val fieldSymbol = ( - clasz.symbol.newValue(newTermName(fieldName), NoPosition, Flags.STATIC | Flags.FINAL) - setInfo AndroidCreatorClass.tpe - ) - val methodSymbol = definitions.getMember(clasz.symbol.companionModule, fieldName) - clasz addField new IField(fieldSymbol) - block emit CALL_METHOD(methodSymbol, Static(false)) - block emit STORE_FIELD(fieldSymbol, true) - } - - def legacyAddCreatorCode(codegen: BytecodeGenerator, clinit: JExtendedCode) { - import codegen._ - val creatorType = javaType(AndroidCreatorClass) - jclass.addNewField(PublicStaticFinal, - fieldName, - creatorType) - val moduleName = javaName(clasz.symbol)+"$" - clinit.emitGETSTATIC(moduleName, - nme.MODULE_INSTANCE_FIELD.toString, - new JObjectType(moduleName)) - clinit.emitINVOKEVIRTUAL(moduleName, fieldName, - new JMethodType(creatorType, Array())) - clinit.emitPUTSTATIC(jclass.getName(), fieldName, creatorType) - } - -} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala deleted file mode 100644 index 598965b982..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ /dev/null @@ -1,1921 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Iulian Dragos - */ - -package scala.tools.nsc -package backend.jvm - -import java.io.{ByteArrayOutputStream, DataOutputStream, OutputStream } -import java.nio.ByteBuffer -import scala.collection.{ mutable, immutable } -import scala.reflect.internal.pickling.{ PickleFormat, PickleBuffer } -import scala.tools.nsc.symtab._ -import scala.reflect.internal.util.{ SourceFile, NoSourceFile } -import scala.reflect.internal.ClassfileConstants._ -import ch.epfl.lamp.fjbg._ -import JAccessFlags._ -import JObjectType.{ JAVA_LANG_STRING, JAVA_LANG_OBJECT } -import java.util.jar.{ JarEntry, JarOutputStream } -import scala.tools.nsc.io.AbstractFile -import scala.language.postfixOps - -/** This class ... - * - * @author Iulian Dragos - * @version 1.0 - * - */ -abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with BytecodeWriters with GenJVMASM { - import global._ - import icodes._ - import icodes.opcodes._ - import definitions._ - - val phaseName = "jvm" - - /** Create a new phase */ - override def newPhase(p: Phase): Phase = new JvmPhase(p) - - /** JVM code generation phase - */ - class JvmPhase(prev: Phase) extends ICodePhase(prev) { - def name = phaseName - override def erasedTypes = true - def apply(cls: IClass) = sys.error("no implementation") - - override def run() { - // we reinstantiate the bytecode generator at each run, to allow the GC - // to collect everything - if (settings.debug.value) - inform("[running phase " + name + " on icode]") - - if (settings.Xdce.value) - for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym)) { - log(s"Optimizer eliminated ${sym.fullNameString}") - icodes.classes -= sym - } - - // For predictably ordered error messages. - val sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName) - val entryPoints = sortedClasses filter isJavaEntryPoint - - val bytecodeWriter = settings.outputDirs.getSingleOutput match { - case Some(f) if f hasExtension "jar" => - // If no main class was specified, see if there's only one - // entry point among the classes going into the jar. - if (settings.mainClass.isDefault) { - entryPoints map (_.symbol fullName '.') match { - case Nil => - log("No Main-Class designated or discovered.") - case name :: Nil => - log("Unique entry point: setting Main-Class to " + name) - settings.mainClass.value = name - case names => - log("No Main-Class due to multiple entry points:\n " + names.mkString("\n ")) - } - } - else log("Main-Class was specified: " + settings.mainClass.value) - - new DirectToJarfileWriter(f.file) - - case _ => - if (settings.Ygenjavap.isDefault) { - if(settings.Ydumpclasses.isDefault) - new ClassBytecodeWriter { } - else - new ClassBytecodeWriter with DumpBytecodeWriter { } - } - else new ClassBytecodeWriter with JavapBytecodeWriter { } - } - - val codeGenerator = new BytecodeGenerator(bytecodeWriter) - debuglog("Created new bytecode generator for " + classes.size + " classes.") - - sortedClasses foreach { c => - try codeGenerator.genClass(c) - catch { - case e: JCode.CodeSizeTooBigException => - log("Skipped class %s because it has methods that are too long.".format(c)) - } - } - - bytecodeWriter.close() - classes.clear() - } - } - - var pickledBytes = 0 // statistics - - /** - * Java bytecode generator. - * - */ - class BytecodeGenerator(bytecodeWriter: BytecodeWriter) extends BytecodeUtil { - def this() = this(new ClassBytecodeWriter { }) - def debugLevel = settings.debuginfo.indexOfChoice - import bytecodeWriter.writeClass - - val MIN_SWITCH_DENSITY = 0.7 - val INNER_CLASSES_FLAGS = - (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_INTERFACE | ACC_ABSTRACT) - - val PublicStatic = ACC_PUBLIC | ACC_STATIC - val PublicStaticFinal = ACC_PUBLIC | ACC_STATIC | ACC_FINAL - - val StringBuilderClassName = javaName(definitions.StringBuilderClass) - val BoxesRunTime = "scala.runtime.BoxesRunTime" - - val StringBuilderType = new JObjectType(StringBuilderClassName) // TODO use ASMType.getObjectType - val toStringType = new JMethodType(JAVA_LANG_STRING, JType.EMPTY_ARRAY) // TODO use ASMType.getMethodType - val arrayCloneType = new JMethodType(JAVA_LANG_OBJECT, JType.EMPTY_ARRAY) - val MethodTypeType = new JObjectType("java.dyn.MethodType") - val JavaLangClassType = new JObjectType("java.lang.Class") - val MethodHandleType = new JObjectType("java.dyn.MethodHandle") - - // Scala attributes - val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo") - val BeanInfoSkipAttr = rootMirror.getRequiredClass("scala.beans.BeanInfoSkip") - val BeanDisplayNameAttr = rootMirror.getRequiredClass("scala.beans.BeanDisplayName") - val BeanDescriptionAttr = rootMirror.getRequiredClass("scala.beans.BeanDescription") - - // Additional interface parents based on annotations and other cues - def newParentForAttr(attr: Symbol): Option[Symbol] = attr match { - case SerializableAttr => Some(SerializableClass) - case CloneableAttr => Some(JavaCloneableClass) - case RemoteAttr => Some(RemoteInterfaceClass) - case _ => None - } - - val versionPickle = { - val vp = new PickleBuffer(new Array[Byte](16), -1, 0) - assert(vp.writeIndex == 0, vp) - vp writeNat PickleFormat.MajorVersion - vp writeNat PickleFormat.MinorVersion - vp writeNat 0 - vp - } - - private def helperBoxTo(kind: ValueTypeKind): Tuple2[String, JMethodType] = { - val boxedType = definitions.boxedClass(kind.toType.typeSymbol) - val mtype = new JMethodType(javaType(boxedType), Array(javaType(kind))) - - Pair("boxTo" + boxedType.decodedName, mtype) - } - - private val jBoxTo: Map[TypeKind, Tuple2[String, JMethodType]] = Map( - BOOL -> helperBoxTo(BOOL) , - BYTE -> helperBoxTo(BYTE) , - CHAR -> helperBoxTo(CHAR) , - SHORT -> helperBoxTo(SHORT) , - INT -> helperBoxTo(INT) , - LONG -> helperBoxTo(LONG) , - FLOAT -> helperBoxTo(FLOAT) , - DOUBLE -> helperBoxTo(DOUBLE) - ) - - private def helperUnboxTo(kind: ValueTypeKind): Tuple2[String, JMethodType] = { - val mtype = new JMethodType(javaType(kind), Array(JAVA_LANG_OBJECT)) - val mname = "unboxTo" + kind.toType.typeSymbol.decodedName - - Pair(mname, mtype) - } - - private val jUnboxTo: Map[TypeKind, Tuple2[String, JMethodType]] = Map( - BOOL -> helperUnboxTo(BOOL) , - BYTE -> helperUnboxTo(BYTE) , - CHAR -> helperUnboxTo(CHAR) , - SHORT -> helperUnboxTo(SHORT) , - INT -> helperUnboxTo(INT) , - LONG -> helperUnboxTo(LONG) , - FLOAT -> helperUnboxTo(FLOAT) , - DOUBLE -> helperUnboxTo(DOUBLE) - ) - - var clasz: IClass = _ - var method: IMethod = _ - var jclass: JClass = _ - var jmethod: JMethod = _ - // var jcode: JExtendedCode = _ - - def isParcelableClass = isAndroidParcelableClass(clasz.symbol) - def isRemoteClass = clasz.symbol hasAnnotation RemoteAttr - def serialVUID = clasz.symbol getAnnotation SerialVersionUIDAttr collect { - case AnnotationInfo(_, Literal(const) :: _, _) => const.longValue - } - - val fjbgContext = new FJBGContext(49, 0) - - val emitSource = debugLevel >= 1 - val emitLines = debugLevel >= 2 - val emitVars = debugLevel >= 3 - - // bug had phase with wrong name; leaving enabled for brief pseudo deprecation - private val checkSignatures = ( - (settings.check containsName phaseName) - || (settings.check.value contains "genjvm") && { - global.warning("This option will be removed: please use -Ycheck:%s, not -Ycheck:genjvm." format phaseName) - true - } - ) - - /** For given symbol return a symbol corresponding to a class that should be declared as inner class. - * - * For example: - * class A { - * class B - * object C - * } - * - * then method will return NoSymbol for A, the same symbol for A.B (corresponding to A$B class) and A$C$ symbol - * for A.C. - */ - private def innerClassSymbolFor(s: Symbol): Symbol = - if (s.isClass) s else if (s.isModule) s.moduleClass else NoSymbol - - override def javaName(sym: Symbol): String = { // TODO Miguel says: check whether a single pass over `icodes.classes` can populate `innerClassBuffer` faster. - /** - * Checks if given symbol corresponds to inner class/object and add it to innerClassBuffer - * - * Note: This method is called recursively thus making sure that we add complete chain - * of inner class all until root class. - */ - def collectInnerClass(s: Symbol): Unit = { - // TODO: some beforeFlatten { ... } which accounts for - // being nested in parameterized classes (if we're going to selectively flatten.) - val x = innerClassSymbolFor(s) - if(x ne NoSymbol) { - assert(x.isClass, "not an inner-class symbol") - val isInner = !x.rawowner.isPackageClass - if (isInner) { - innerClassBuffer += x - collectInnerClass(x.rawowner) - } - } - } - collectInnerClass(sym) - - super.javaName(sym) - } - - /** Write a class to disk, adding the Scala signature (pickled type - * information) and inner classes. - * - * @param jclass The FJBG class, where code was emitted - * @param sym The corresponding symbol, used for looking up pickled information - */ - def emitClass(jclass: JClass, sym: Symbol) { - addInnerClasses(jclass) - writeClass("" + sym.name, jclass.getName(), toByteArray(jclass), sym) - } - - /** Returns the ScalaSignature annotation if it must be added to this class, - * none otherwise; furthermore, it adds to `jclass` the ScalaSig marker - * attribute (marking that a scala signature annotation is present) or the - * Scala marker attribute (marking that the signature for this class is in - * another file). The annotation that is returned by this method must be - * added to the class' annotations list when generating them. - * - * @param jclass The class file that is being readied. - * @param sym The symbol for which the signature has been entered in - * the symData map. This is different than the symbol - * that is being generated in the case of a mirror class. - * @return An option that is: - * - defined and contains an annotation info of the - * ScalaSignature type, instantiated with the pickle - * signature for sym (a ScalaSig marker attribute has - * been written); - * - undefined if the jclass/sym couple must not contain a - * signature (a Scala marker attribute has been written). - */ - def scalaSignatureAddingMarker(jclass: JClass, sym: Symbol): Option[AnnotationInfo] = - currentRun.symData get sym match { - case Some(pickle) if !nme.isModuleName(newTermName(jclass.getName)) => - val scalaAttr = - fjbgContext.JOtherAttribute(jclass, jclass, tpnme.ScalaSignatureATTR.toString, - versionPickle.bytes, versionPickle.writeIndex) - jclass addAttribute scalaAttr - val scalaAnnot = { - val sigBytes = ScalaSigBytes(pickle.bytes.take(pickle.writeIndex)) - AnnotationInfo(sigBytes.sigAnnot, Nil, List((nme.bytes, sigBytes))) - } - pickledBytes += pickle.writeIndex - currentRun.symData -= sym - currentRun.symData -= sym.companionSymbol - Some(scalaAnnot) - case _ => - val markerAttr = - fjbgContext.JOtherAttribute(jclass, jclass, tpnme.ScalaATTR.toString, new Array[Byte](0), 0) - jclass addAttribute markerAttr - None - } - - private var innerClassBuffer = mutable.LinkedHashSet[Symbol]() - - /** Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents. - * This is important on Android because there is otherwise an interface explosion. - */ - private def minimizeInterfaces(interfaces: List[Symbol]): List[Symbol] = { - var rest = interfaces - var leaves = List.empty[Symbol] - while(!rest.isEmpty) { - val candidate = rest.head - val nonLeaf = leaves exists { lsym => lsym isSubClass candidate } - if(!nonLeaf) { - leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym }) - } - rest = rest.tail - } - - leaves - } - - def genClass(c: IClass) { - clasz = c - innerClassBuffer.clear() - - val name = javaName(c.symbol) - - val ps = c.symbol.info.parents - - val superClass: Symbol = if(ps.isEmpty) ObjectClass else ps.head.typeSymbol; - - val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses; - val superInterfaces = superInterfaces0 ++ c.symbol.annotations.flatMap(ann => newParentForAttr(ann.symbol)) distinct - - val ifaces = - if(superInterfaces.isEmpty) JClass.NO_INTERFACES - else mkArray(minimizeInterfaces(superInterfaces) map javaName) - - jclass = fjbgContext.JClass(javaFlags(c.symbol), - name, - javaName(superClass), - ifaces, - c.cunit.source.toString) - - if (isStaticModule(c.symbol) || serialVUID != None || isParcelableClass) { - if (isStaticModule(c.symbol)) - addModuleInstanceField - addStaticInit(jclass, c.lookupStaticCtor) - - if (isTopLevelModule(c.symbol)) { - if (c.symbol.companionClass == NoSymbol) - generateMirrorClass(c.symbol, c.cunit.source) - else - log("No mirror class for module with linked class: " + - c.symbol.fullName) - } - } - else { - c.lookupStaticCtor foreach (constructor => addStaticInit(jclass, Some(constructor))) - - // it must be a top level class (name contains no $s) - def isCandidateForForwarders(sym: Symbol): Boolean = - afterPickler { - !(sym.name.toString contains '$') && sym.hasModuleFlag && !sym.isImplClass && !sym.isNestedClass - } - - // At some point this started throwing lots of exceptions as a compile was finishing. - // error: java.lang.AssertionError: - // assertion failed: List(object package$CompositeThrowable, object package$CompositeThrowable) - // ...is the one I've seen repeatedly. Suppressing. - val lmoc = ( - try c.symbol.companionModule - catch { case x: AssertionError => - Console.println("Suppressing failed assert: " + x) - NoSymbol - } - ) - // add static forwarders if there are no name conflicts; see bugs #363 and #1735 - if (lmoc != NoSymbol && !c.symbol.isInterface) { - if (isCandidateForForwarders(lmoc) && !settings.noForwarders.value) { - log("Adding static forwarders from '%s' to implementations in '%s'".format(c.symbol, lmoc)) - addForwarders(jclass, lmoc.moduleClass) - } - } - } - - clasz.fields foreach genField - clasz.methods foreach genMethod - - val ssa = scalaSignatureAddingMarker(jclass, c.symbol) - addGenericSignature(jclass, c.symbol, c.symbol.owner) - addAnnotations(jclass, c.symbol.annotations ++ ssa) - addEnclosingMethodAttribute(jclass, c.symbol) - emitClass(jclass, c.symbol) - - if (c.symbol hasAnnotation BeanInfoAttr) - genBeanInfoClass(c) - } - - private def addEnclosingMethodAttribute(jclass: JClass, clazz: Symbol) { - val sym = clazz.originalEnclosingMethod - if (sym.isMethod) { - debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass)) - jclass addAttribute fjbgContext.JEnclosingMethodAttribute( - jclass, - javaName(sym.enclClass), - javaName(sym), - javaType(sym) - ) - } else if (clazz.isAnonymousClass) { - val enclClass = clazz.rawowner - assert(enclClass.isClass, enclClass) - val sym = enclClass.primaryConstructor - if (sym == NoSymbol) - log("Ran out of room looking for an enclosing method for %s: no constructor here.".format( - enclClass, clazz) - ) - else { - debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass)) - jclass addAttribute fjbgContext.JEnclosingMethodAttribute( - jclass, - javaName(enclClass), - javaName(sym), - javaType(sym).asInstanceOf[JMethodType] - ) - } - } - } - - private def toByteArray(jc: JClass): Array[Byte] = { - val bos = new java.io.ByteArrayOutputStream() - val dos = new java.io.DataOutputStream(bos) - jc.writeTo(dos) - dos.close() - bos.toByteArray - } - - /** - * Generate a bean info class that describes the given class. - * - * @author Ross Judson (ross.judson@soletta.com) - */ - def genBeanInfoClass(c: IClass) { - val description = c.symbol getAnnotation BeanDescriptionAttr - // informProgress(description.toString) - - val beanInfoClass = fjbgContext.JClass(javaFlags(c.symbol), - javaName(c.symbol) + "BeanInfo", - "scala/beans/ScalaBeanInfo", - JClass.NO_INTERFACES, - c.cunit.source.toString) - - var fieldList = List[String]() - for (f <- clasz.fields if f.symbol.hasGetter; - g = f.symbol.getter(c.symbol); - s = f.symbol.setter(c.symbol); - if g.isPublic && !(f.symbol.name startsWith "$")) // inserting $outer breaks the bean - fieldList = javaName(f.symbol) :: javaName(g) :: (if (s != NoSymbol) javaName(s) else null) :: fieldList - val methodList = - for (m <- clasz.methods - if !m.symbol.isConstructor && - m.symbol.isPublic && - !(m.symbol.name startsWith "$") && - !m.symbol.isGetter && - !m.symbol.isSetter) yield javaName(m.symbol) - - val constructor = beanInfoClass.addNewMethod(ACC_PUBLIC, "<init>", JType.VOID, new Array[JType](0), new Array[String](0)) - val jcode = constructor.getCode().asInstanceOf[JExtendedCode] - val strKind = new JObjectType(javaName(StringClass)) - val stringArrayKind = new JArrayType(strKind) - val conType = new JMethodType(JType.VOID, Array(javaType(ClassClass), stringArrayKind, stringArrayKind)) - - def push(lst:Seq[String]) { - var fi = 0 - for (f <- lst) { - jcode.emitDUP() - jcode emitPUSH fi - if (f != null) - jcode emitPUSH f - else - jcode.emitACONST_NULL() - jcode emitASTORE strKind - fi += 1 - } - } - - jcode.emitALOAD_0() - // push the class - jcode emitPUSH javaType(c.symbol).asInstanceOf[JReferenceType] - - // push the string array of field information - jcode emitPUSH fieldList.length - jcode emitANEWARRAY strKind - push(fieldList) - - // push the string array of method information - jcode emitPUSH methodList.length - jcode emitANEWARRAY strKind - push(methodList) - - // invoke the superclass constructor, which will do the - // necessary java reflection and create Method objects. - jcode.emitINVOKESPECIAL("scala/beans/ScalaBeanInfo", "<init>", conType) - jcode.emitRETURN() - - // write the bean information class file. - writeClass("BeanInfo ", beanInfoClass.getName(), toByteArray(beanInfoClass), c.symbol) - } - - /** Add the given 'throws' attributes to jmethod */ - def addExceptionsAttribute(jmethod: JMethod, excs: List[AnnotationInfo]) { - if (excs.isEmpty) return - - val cpool = jmethod.getConstantPool - val buf: ByteBuffer = ByteBuffer.allocate(512) - var nattr = 0 - - // put some random value; the actual number is determined at the end - buf putShort 0xbaba.toShort - - for (ThrownException(exc) <- excs.distinct) { - buf.putShort( - cpool.addClass( - javaName(exc)).shortValue) - nattr += 1 - } - - assert(nattr > 0, nattr) - buf.putShort(0, nattr.toShort) - addAttribute(jmethod, tpnme.ExceptionsATTR, buf) - } - - /** Whether an annotation should be emitted as a Java annotation - * .initialize: if 'annot' is read from pickle, atp might be un-initialized - */ - private def shouldEmitAnnotation(annot: AnnotationInfo) = - annot.symbol.initialize.isJavaDefined && - annot.matches(ClassfileAnnotationClass) && - annot.args.isEmpty - - private def emitJavaAnnotations(cpool: JConstantPool, buf: ByteBuffer, annotations: List[AnnotationInfo]): Int = { - def emitArgument(arg: ClassfileAnnotArg): Unit = arg match { - case LiteralAnnotArg(const) => - const.tag match { - case BooleanTag => - buf put 'Z'.toByte - buf putShort cpool.addInteger(if(const.booleanValue) 1 else 0).toShort - case ByteTag => - buf put 'B'.toByte - buf putShort cpool.addInteger(const.byteValue).toShort - case ShortTag => - buf put 'S'.toByte - buf putShort cpool.addInteger(const.shortValue).toShort - case CharTag => - buf put 'C'.toByte - buf putShort cpool.addInteger(const.charValue).toShort - case IntTag => - buf put 'I'.toByte - buf putShort cpool.addInteger(const.intValue).toShort - case LongTag => - buf put 'J'.toByte - buf putShort cpool.addLong(const.longValue).toShort - case FloatTag => - buf put 'F'.toByte - buf putShort cpool.addFloat(const.floatValue).toShort - case DoubleTag => - buf put 'D'.toByte - buf putShort cpool.addDouble(const.doubleValue).toShort - case StringTag => - buf put 's'.toByte - buf putShort cpool.addUtf8(const.stringValue).toShort - case ClazzTag => - buf put 'c'.toByte - buf putShort cpool.addUtf8(javaType(const.typeValue).getSignature()).toShort - case EnumTag => - buf put 'e'.toByte - buf putShort cpool.addUtf8(javaType(const.tpe).getSignature()).toShort - buf putShort cpool.addUtf8(const.symbolValue.name.toString).toShort - } - - case sb@ScalaSigBytes(bytes) if !sb.isLong => - buf put 's'.toByte - buf putShort cpool.addUtf8(sb.encodedBytes).toShort - - case sb@ScalaSigBytes(bytes) if sb.isLong => - buf put '['.toByte - val stringCount = (sb.encodedBytes.length / 65534) + 1 - buf putShort stringCount.toShort - for (i <- 0 until stringCount) { - buf put 's'.toByte - val j = i * 65535 - val string = sb.encodedBytes.slice(j, j + 65535) - buf putShort cpool.addUtf8(string).toShort - } - - case ArrayAnnotArg(args) => - buf put '['.toByte - buf putShort args.length.toShort - args foreach emitArgument - - case NestedAnnotArg(annInfo) => - buf put '@'.toByte - emitAnnotation(annInfo) - } - - def emitAnnotation(annotInfo: AnnotationInfo) { - val AnnotationInfo(typ, args, assocs) = annotInfo - val jtype = javaType(typ) - buf putShort cpool.addUtf8(jtype.getSignature()).toShort - assert(args.isEmpty, args) - buf putShort assocs.length.toShort - for ((name, value) <- assocs) { - buf putShort cpool.addUtf8(name.toString).toShort - emitArgument(value) - } - } - - var nannots = 0 - val pos = buf.position() - - // put some random value; the actual number of annotations is determined at the end - buf putShort 0xbaba.toShort - - for (annot <- annotations if shouldEmitAnnotation(annot)) { - nannots += 1 - emitAnnotation(annot) - } - - // save the number of annotations - buf.putShort(pos, nannots.toShort) - nannots - } - - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - private def needsGenericSignature(sym: Symbol) = !( - // PP: This condition used to include sym.hasExpandedName, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - settings.Ynogenericsig.value - || sym.isArtifact - || sym.isLiftedMethod - || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) - ) - def addGenericSignature(jmember: JMember, sym: Symbol, owner: Symbol) { - if (needsGenericSignature(sym)) { - val memberTpe = beforeErasure(owner.thisType.memberInfo(sym)) - - erasure.javaSig(sym, memberTpe) foreach { sig => - // This seems useful enough in the general case. - log(sig) - if (checkSignatures) { - val normalizedTpe = beforeErasure(erasure.prepareSigMap(memberTpe)) - val bytecodeTpe = owner.thisType.memberInfo(sym) - if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - clasz.cunit.warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s - |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) - return - } - } - val index = jmember.getConstantPool.addUtf8(sig).toShort - if (opt.verboseDebug) - beforeErasure(println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index)) - - val buf = ByteBuffer.allocate(2) - buf putShort index - addAttribute(jmember, tpnme.SignatureATTR, buf) - } - } - } - - def addAnnotations(jmember: JMember, annotations: List[AnnotationInfo]) { - if (annotations exists (_ matches definitions.DeprecatedAttr)) { - val attr = jmember.getContext().JOtherAttribute( - jmember.getJClass(), jmember, tpnme.DeprecatedATTR.toString, - new Array[Byte](0), 0) - jmember addAttribute attr - } - - val toEmit = annotations filter shouldEmitAnnotation - if (toEmit.isEmpty) return - - val buf: ByteBuffer = ByteBuffer.allocate(2048) - emitJavaAnnotations(jmember.getConstantPool, buf, toEmit) - addAttribute(jmember, tpnme.RuntimeAnnotationATTR, buf) - } - - def addParamAnnotations(jmethod: JMethod, pannotss: List[List[AnnotationInfo]]) { - val annotations = pannotss map (_ filter shouldEmitAnnotation) - if (annotations forall (_.isEmpty)) return - - val buf: ByteBuffer = ByteBuffer.allocate(2048) - - // number of parameters - buf.put(annotations.length.toByte) - for (annots <- annotations) - emitJavaAnnotations(jmethod.getConstantPool, buf, annots) - - addAttribute(jmethod, tpnme.RuntimeParamAnnotationATTR, buf) - } - - def addAttribute(jmember: JMember, name: Name, buf: ByteBuffer) { - if (buf.position() < 2) - return - - val length = buf.position() - val arr = buf.array().slice(0, length) - - val attr = jmember.getContext().JOtherAttribute(jmember.getJClass(), - jmember, - name.toString, - arr, - length) - jmember addAttribute attr - } - - def addInnerClasses(jclass: JClass) { - /** The outer name for this inner class. Note that it returns null - * when the inner class should not get an index in the constant pool. - * That means non-member classes (anonymous). See Section 4.7.5 in the JVMS. - */ - def outerName(innerSym: Symbol): String = { - if (innerSym.originalEnclosingMethod != NoSymbol) - null - else { - val outerName = javaName(innerSym.rawowner) - if (isTopLevelModule(innerSym.rawowner)) "" + nme.stripModuleSuffix(newTermName(outerName)) - else outerName - } - } - - def innerName(innerSym: Symbol): String = - if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction) - null - else - innerSym.rawname + innerSym.moduleSuffix - - // add inner classes which might not have been referenced yet - afterErasure { - for (sym <- List(clasz.symbol, clasz.symbol.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass) - innerClassBuffer += m - } - - val allInners = innerClassBuffer.toList - if (allInners.nonEmpty) { - debuglog(clasz.symbol.fullName('.') + " contains " + allInners.size + " inner classes.") - val innerClassesAttr = jclass.getInnerClasses() - // sort them so inner classes succeed their enclosing class - // to satisfy the Eclipse Java compiler - for (innerSym <- allInners sortBy (_.name.length)) { - val flags = { - val staticFlag = if (innerSym.rawowner.hasModuleFlag) ACC_STATIC else 0 - (javaFlags(innerSym) | staticFlag) & INNER_CLASSES_FLAGS - } - val jname = javaName(innerSym) - val oname = outerName(innerSym) - val iname = innerName(innerSym) - - // Mimicking javap inner class output - debuglog( - if (oname == null || iname == null) "//class " + jname - else "//%s=class %s of class %s".format(iname, jname, oname) - ) - - innerClassesAttr.addEntry(jname, oname, iname, flags) - } - } - } - - def genField(f: IField) { - debuglog("Adding field: " + f.symbol.fullName) - - val jfield = jclass.addNewField( - javaFieldFlags(f.symbol), - javaName(f.symbol), - javaType(f.symbol.tpe) - ) - - addGenericSignature(jfield, f.symbol, clasz.symbol) - addAnnotations(jfield, f.symbol.annotations) - } - - def genMethod(m: IMethod) { - if (m.symbol.isStaticConstructor || definitions.isGetClass(m.symbol)) return - - debuglog("Generating method " + m.symbol.fullName) - method = m - endPC.clear - computeLocalVarsIndex(m) - - var resTpe = javaType(m.symbol.tpe.resultType) - if (m.symbol.isClassConstructor) - resTpe = JType.VOID - - var flags = javaFlags(m.symbol) - if (jclass.isInterface) - flags |= ACC_ABSTRACT - - if (m.symbol.isStrictFP) - flags |= ACC_STRICT - - // native methods of objects are generated in mirror classes - if (method.native) - flags |= ACC_NATIVE - - jmethod = jclass.addNewMethod(flags, - javaName(m.symbol), - resTpe, - mkArray(m.params map (p => javaType(p.kind))), - mkArray(m.params map (p => javaName(p.sym)))) - - addRemoteException(jmethod, m.symbol) - - if (!jmethod.isAbstract() && !method.native) { - val jcode = jmethod.getCode().asInstanceOf[JExtendedCode] - - // add a fake local for debugging purposes - if (emitVars && isClosureApply(method.symbol)) { - val outerField = clasz.symbol.info.decl(nme.OUTER_LOCAL) - if (outerField != NoSymbol) { - log("Adding fake local to represent outer 'this' for closure " + clasz) - val _this = new Local( - method.symbol.newVariable(nme.FAKE_LOCAL_THIS), toTypeKind(outerField.tpe), false) - m.locals = m.locals ::: List(_this) - computeLocalVarsIndex(m) // since we added a new local, we need to recompute indexes - - jcode.emitALOAD_0() - jcode.emitGETFIELD(javaName(clasz.symbol), - javaName(outerField), - javaType(outerField)) - jcode.emitSTORE(indexOf(_this), javaType(_this.kind)) - } - } - - for (local <- m.locals if ! m.params.contains(local)) { - debuglog("add local var: " + local) - jmethod.addNewLocalVariable(javaType(local.kind), javaName(local.sym)) - } - - genCode(m) - if (emitVars) - genLocalVariableTable(m, jcode) - } - - addGenericSignature(jmethod, m.symbol, clasz.symbol) - val (excs, others) = m.symbol.annotations partition (_.symbol == ThrowsClass) - addExceptionsAttribute(jmethod, excs) - addAnnotations(jmethod, others) - addParamAnnotations(jmethod, m.params.map(_.sym.annotations)) - - // check for code size - try jmethod.freeze() - catch { - case e: JCode.CodeSizeTooBigException => - clasz.cunit.error(m.symbol.pos, "Code size exceeds JVM limits: %d".format(e.codeSize)) - throw e - } - } - - /** Adds a @remote annotation, actual use unknown. - */ - private def addRemoteException(jmethod: JMethod, meth: Symbol) { - val needsAnnotation = ( - (isRemoteClass || (meth hasAnnotation RemoteAttr) && jmethod.isPublic) - && !(meth.throwsAnnotations contains RemoteExceptionClass) - ) - if (needsAnnotation) { - val c = Constant(RemoteExceptionClass.tpe) - val arg = Literal(c) setType c.tpe - meth.addAnnotation(appliedType(ThrowsClass, c.tpe), arg) - } - } - - private def isClosureApply(sym: Symbol): Boolean = { - (sym.name == nme.apply) && - sym.owner.isSynthetic && - sym.owner.tpe.parents.exists { t => - val TypeRef(_, sym, _) = t - FunctionClass contains sym - } - } - - def addModuleInstanceField() { - jclass.addNewField(PublicStaticFinal, - nme.MODULE_INSTANCE_FIELD.toString, - jclass.getType()) - } - - def addStaticInit(cls: JClass, mopt: Option[IMethod]) { - val clinitMethod = cls.addNewMethod(PublicStatic, - "<clinit>", - JType.VOID, - JType.EMPTY_ARRAY, - new Array[String](0)) - val clinit = clinitMethod.getCode().asInstanceOf[JExtendedCode] - - mopt match { - case Some(m) => - val oldLastBlock = m.lastBlock - val lastBlock = m.newBlock() - oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) - - if (isStaticModule(clasz.symbol)) { - // call object's private ctor from static ctor - lastBlock emit NEW(REFERENCE(m.symbol.enclClass)) - lastBlock emit CALL_METHOD(m.symbol.enclClass.primaryConstructor, Static(true)) - } - - // add serialVUID code - serialVUID foreach { value => - import Flags._, definitions._ - val fieldName = "serialVersionUID" - val fieldSymbol = clasz.symbol.newValue(newTermName(fieldName), NoPosition, STATIC | FINAL) setInfo LongClass.tpe - clasz addField new IField(fieldSymbol) - lastBlock emit CONSTANT(Constant(value)) - lastBlock emit STORE_FIELD(fieldSymbol, true) - } - - if (isParcelableClass) - addCreatorCode(BytecodeGenerator.this, lastBlock) - - lastBlock emit RETURN(UNIT) - lastBlock.close - - method = m - jmethod = clinitMethod - genCode(m) - case None => - legacyStaticInitializer(cls, clinit) - } - } - - private def legacyStaticInitializer(cls: JClass, clinit: JExtendedCode) { - if (isStaticModule(clasz.symbol)) { - clinit emitNEW cls.getName() - clinit.emitINVOKESPECIAL(cls.getName(), - JMethod.INSTANCE_CONSTRUCTOR_NAME, - JMethodType.ARGLESS_VOID_FUNCTION) - } - - serialVUID foreach { value => - val fieldName = "serialVersionUID" - jclass.addNewField(PublicStaticFinal, fieldName, JType.LONG) - clinit emitPUSH value - clinit.emitPUSH(value) - clinit.emitPUTSTATIC(jclass.getName(), fieldName, JType.LONG) - } - - if (isParcelableClass) - legacyAddCreatorCode(BytecodeGenerator.this, clinit) - - clinit.emitRETURN() - } - - /** Add a forwarder for method m */ - def addForwarder(jclass: JClass, module: Symbol, m: Symbol) { - val moduleName = javaName(module) - val methodInfo = module.thisType.memberInfo(m) - val paramJavaTypes = methodInfo.paramTypes map javaType - val paramNames = 0 until paramJavaTypes.length map ("x_" + _) - // TODO: evaluate the other flags we might be dropping on the floor here. - val flags = PublicStatic | ( - if (m.isVarargsMethod) ACC_VARARGS else 0 - ) - - /** Forwarders must not be marked final, as the JVM will not allow - * redefinition of a final static method, and we don't know what classes - * might be subclassing the companion class. See SI-4827. - */ - val mirrorMethod = jclass.addNewMethod( - flags, - javaName(m), - javaType(methodInfo.resultType), - mkArray(paramJavaTypes), - mkArray(paramNames)) - val mirrorCode = mirrorMethod.getCode().asInstanceOf[JExtendedCode] - mirrorCode.emitGETSTATIC(moduleName, - nme.MODULE_INSTANCE_FIELD.toString, - new JObjectType(moduleName)) - - var i = 0 - var index = 0 - var argTypes = mirrorMethod.getArgumentTypes() - while (i < argTypes.length) { - mirrorCode.emitLOAD(index, argTypes(i)) - index += argTypes(i).getSize() - i += 1 - } - - mirrorCode.emitINVOKEVIRTUAL(moduleName, mirrorMethod.getName, javaType(m).asInstanceOf[JMethodType]) - mirrorCode emitRETURN mirrorMethod.getReturnType() - - addRemoteException(mirrorMethod, m) - // only add generic signature if the method is concrete; bug #1745 - if (!m.isDeferred) - addGenericSignature(mirrorMethod, m, module) - - val (throws, others) = m.annotations partition (_.symbol == ThrowsClass) - addExceptionsAttribute(mirrorMethod, throws) - addAnnotations(mirrorMethod, others) - addParamAnnotations(mirrorMethod, m.info.params.map(_.annotations)) - } - - /** Add forwarders for all methods defined in `module` that don't conflict - * with methods in the companion class of `module`. A conflict arises when - * a method with the same name is defined both in a class and its companion - * object: method signature is not taken into account. - */ - def addForwarders(jclass: JClass, moduleClass: Symbol) { - assert(moduleClass.isModuleClass, moduleClass) - debuglog("Dumping mirror class for object: " + moduleClass) - - val className = jclass.getName - val linkedClass = moduleClass.companionClass - val linkedModule = linkedClass.companionSymbol - lazy val conflictingNames: Set[Name] = { - linkedClass.info.members collect { case sym if sym.name.isTermName => sym.name } toSet - } - debuglog("Potentially conflicting names for forwarders: " + conflictingNames) - - for (m <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flags.METHOD)) { - if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor) - debuglog("No forwarder for '%s' from %s to '%s'".format(m, className, moduleClass)) - else if (conflictingNames(m.name)) - log("No forwarder for " + m + " due to conflict with " + linkedClass.info.member(m.name)) - else { - log("Adding static forwarder for '%s' from %s to '%s'".format(m, className, moduleClass)) - addForwarder(jclass, moduleClass, m) - } - } - } - - /** Generate a mirror class for a top-level module. A mirror class is a class - * containing only static methods that forward to the corresponding method - * on the MODULE instance of the given Scala object. It will only be - * generated if there is no companion class: if there is, an attempt will - * instead be made to add the forwarder methods to the companion class. - */ - def generateMirrorClass(clasz: Symbol, sourceFile: SourceFile) { - import JAccessFlags._ - /* We need to save inner classes buffer and create a new one to make sure - * that we do confuse inner classes of the class we mirror with inner - * classes of the class we are mirroring. These two sets can be different - * as seen in this case: - * - * class A { - * class B - * def b: B = new B - * } - * object C extends A - * - * Here mirror class of C has a static forwarder for (inherited) method `b` - * therefore it refers to class `B` and needs InnerClasses entry. However, - * the real class for `C` (named `C$`) is empty and does not refer to `B` - * thus does not need InnerClasses entry it. - * - * NOTE: This logic has been refactored in GenASM and everything is - * implemented in a much cleaner way by having two separate buffers. - */ - val savedInnerClasses = innerClassBuffer - innerClassBuffer = mutable.LinkedHashSet[Symbol]() - val moduleName = javaName(clasz) // + "$" - val mirrorName = moduleName.substring(0, moduleName.length() - 1) - val mirrorClass = fjbgContext.JClass(ACC_SUPER | ACC_PUBLIC | ACC_FINAL, - mirrorName, - JAVA_LANG_OBJECT.getName, - JClass.NO_INTERFACES, - "" + sourceFile) - - log("Dumping mirror class for '%s'".format(mirrorClass.getName)) - addForwarders(mirrorClass, clasz) - val ssa = scalaSignatureAddingMarker(mirrorClass, clasz.companionSymbol) - addAnnotations(mirrorClass, clasz.annotations ++ ssa) - emitClass(mirrorClass, clasz) - innerClassBuffer = savedInnerClasses - } - - var linearization: List[BasicBlock] = Nil - var isModuleInitialized = false - - /** - * @param m ... - */ - def genCode(m: IMethod) { - val jcode = jmethod.getCode.asInstanceOf[JExtendedCode] - - def makeLabels(bs: List[BasicBlock]) = { - debuglog("Making labels for: " + method) - - mutable.HashMap(bs map (_ -> jcode.newLabel) : _*) - } - - isModuleInitialized = false - - linearization = linearizer.linearize(m) - val labels = makeLabels(linearization) - - var nextBlock: BasicBlock = linearization.head - - def genBlocks(l: List[BasicBlock]): Unit = l match { - case Nil => () - case x :: Nil => nextBlock = null; genBlock(x) - case x :: y :: ys => nextBlock = y; genBlock(x); genBlocks(y :: ys) - } - - /** Generate exception handlers for the current method. */ - def genExceptionHandlers() { - - /** Return a list of pairs of intervals where the handler is active. - * The intervals in the list have to be inclusive in the beginning and - * exclusive in the end: [start, end). - */ - def ranges(e: ExceptionHandler): List[(Int, Int)] = { - var covered = e.covered - var ranges: List[(Int, Int)] = Nil - var start = -1 - var end = -1 - - linearization foreach { b => - if (! (covered contains b) ) { - if (start >= 0) { // we're inside a handler range - end = labels(b).getAnchor() - ranges ::= ((start, end)) - start = -1 - } - } else { - if (start < 0) // we're not inside a handler range - start = labels(b).getAnchor() - - end = endPC(b) - covered -= b - } - } - - /* Add the last interval. Note that since the intervals are - * open-ended to the right, we have to give a number past the actual - * code! - */ - if (start >= 0) { - ranges ::= ((start, jcode.getPC())) - } - - if (!covered.isEmpty) - debuglog("Some covered blocks were not found in method: " + method + - " covered: " + covered + " not in " + linearization) - ranges - } - - for (e <- this.method.exh ; p <- ranges(e).sortBy(_._1)) { - if (p._1 < p._2) { - debuglog("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method + - " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls); - val cls = if (e.cls == NoSymbol || e.cls == ThrowableClass) null - else javaName(e.cls) - jcode.addExceptionHandler(p._1, p._2, - labels(e.startBlock).getAnchor(), - cls) - } else - log("Empty exception range: " + p) - } - } - - def isAccessibleFrom(target: Symbol, site: Symbol): Boolean = { - target.isPublic || target.isProtected && { - (site.enclClass isSubClass target.enclClass) || - (site.enclosingPackage == target.privateWithin) - } - } - - def genCallMethod(call: CALL_METHOD) { - val CALL_METHOD(method, style) = call - val siteSymbol = clasz.symbol - val hostSymbol = call.hostClass - val methodOwner = method.owner - // info calls so that types are up to date; erasure may add lateINTERFACE to traits - hostSymbol.info ; methodOwner.info - - def isInterfaceCall(sym: Symbol) = ( - sym.isInterface && methodOwner != ObjectClass - || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass) - ) - // whether to reference the type of the receiver or - // the type of the method owner (if not an interface!) - val useMethodOwner = ( - style != Dynamic - || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol) - || hostSymbol.isBottomClass - ) - val receiver = if (useMethodOwner) methodOwner else hostSymbol - val jowner = javaName(receiver) - val jname = javaName(method) - val jtype = javaType(method).asInstanceOf[JMethodType] - - def dbg(invoke: String) { - debuglog("%s %s %s.%s:%s".format(invoke, receiver.accessString, jowner, jname, jtype)) - } - - def initModule() { - // we initialize the MODULE$ field immediately after the super ctor - if (isStaticModule(siteSymbol) && !isModuleInitialized && - jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME && - jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) { - isModuleInitialized = true - jcode.emitALOAD_0() - jcode.emitPUTSTATIC(jclass.getName(), - nme.MODULE_INSTANCE_FIELD.toString, - jclass.getType()) - } - } - - style match { - case Static(true) => dbg("invokespecial"); jcode.emitINVOKESPECIAL(jowner, jname, jtype) - case Static(false) => dbg("invokestatic"); jcode.emitINVOKESTATIC(jowner, jname, jtype) - case Dynamic if isInterfaceCall(receiver) => dbg("invokinterface"); jcode.emitINVOKEINTERFACE(jowner, jname, jtype) - case Dynamic => dbg("invokevirtual"); jcode.emitINVOKEVIRTUAL(jowner, jname, jtype) - case SuperCall(_) => - dbg("invokespecial") - jcode.emitINVOKESPECIAL(jowner, jname, jtype) - initModule() - } - } - - def genBlock(b: BasicBlock) { - labels(b).anchorToNext() - - debuglog("Generating code for block: " + b + " at pc: " + labels(b).getAnchor()) - var lastMappedPC = 0 - var lastLineNr = 0 - var crtPC = 0 - - /** local variables whose scope appears in this block. */ - val varsInBlock: mutable.Set[Local] = new mutable.HashSet - val lastInstr = b.lastInstruction - - for (instr <- b) { - instr match { - case THIS(clasz) => jcode.emitALOAD_0() - - case CONSTANT(const) => genConstant(jcode, const) - - case LOAD_ARRAY_ITEM(kind) => - if(kind.isRefOrArrayType) { jcode.emitAALOAD() } - else { - (kind: @unchecked) match { - case UNIT => throw new IllegalArgumentException("invalid type for aload " + kind) - case BOOL | BYTE => jcode.emitBALOAD() - case SHORT => jcode.emitSALOAD() - case CHAR => jcode.emitCALOAD() - case INT => jcode.emitIALOAD() - case LONG => jcode.emitLALOAD() - case FLOAT => jcode.emitFALOAD() - case DOUBLE => jcode.emitDALOAD() - } - } - - case LOAD_LOCAL(local) => jcode.emitLOAD(indexOf(local), javaType(local.kind)) - - case lf @ LOAD_FIELD(field, isStatic) => - var owner = javaName(lf.hostClass) - debuglog("LOAD_FIELD with owner: " + owner + - " flags: " + Flags.flagsToString(field.owner.flags)) - val fieldJName = javaName(field) - val fieldJType = javaType(field) - if (isStatic) jcode.emitGETSTATIC(owner, fieldJName, fieldJType) - else jcode.emitGETFIELD( owner, fieldJName, fieldJType) - - case LOAD_MODULE(module) => - // assert(module.isModule, "Expected module: " + module) - debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags)); - if (clasz.symbol == module.moduleClass && jmethod.getName() != nme.readResolve.toString) - jcode.emitALOAD_0() - else - jcode.emitGETSTATIC(javaName(module) /* + "$" */ , - nme.MODULE_INSTANCE_FIELD.toString, - javaType(module)) - - case STORE_ARRAY_ITEM(kind) => - if(kind.isRefOrArrayType) { jcode.emitAASTORE() } - else { - (kind: @unchecked) match { - case UNIT => throw new IllegalArgumentException("invalid type for astore " + kind) - case BOOL | BYTE => jcode.emitBASTORE() - case SHORT => jcode.emitSASTORE() - case CHAR => jcode.emitCASTORE() - case INT => jcode.emitIASTORE() - case LONG => jcode.emitLASTORE() - case FLOAT => jcode.emitFASTORE() - case DOUBLE => jcode.emitDASTORE() - } - } - - case STORE_LOCAL(local) => - jcode.emitSTORE(indexOf(local), javaType(local.kind)) - - case STORE_THIS(_) => - // this only works for impl classes because the self parameter comes first - // in the method signature. If that changes, this code has to be revisited. - jcode.emitASTORE_0() - - case STORE_FIELD(field, isStatic) => - val owner = javaName(field.owner) - val fieldJName = javaName(field) - val fieldJType = javaType(field) - if (isStatic) jcode.emitPUTSTATIC(owner, fieldJName, fieldJType) - else jcode.emitPUTFIELD( owner, fieldJName, fieldJType) - - case CALL_PRIMITIVE(primitive) => genPrimitive(primitive, instr.pos) - - /** Special handling to access native Array.clone() */ - case call @ CALL_METHOD(definitions.Array_clone, Dynamic) => - val target: String = javaType(call.targetTypeKind).getSignature() - jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType) - - case call @ CALL_METHOD(method, style) => genCallMethod(call) - - case BOX(kind) => - val Pair(mname, mtype) = jBoxTo(kind) - jcode.emitINVOKESTATIC(BoxesRunTime, mname, mtype) - - case UNBOX(kind) => - val Pair(mname, mtype) = jUnboxTo(kind) - jcode.emitINVOKESTATIC(BoxesRunTime, mname, mtype) - - case NEW(REFERENCE(cls)) => - val className = javaName(cls) - jcode emitNEW className - - case CREATE_ARRAY(elem, 1) => - if(elem.isRefOrArrayType) { jcode emitANEWARRAY javaType(elem).asInstanceOf[JReferenceType] } - else { jcode emitNEWARRAY javaType(elem) } - - case CREATE_ARRAY(elem, dims) => - jcode.emitMULTIANEWARRAY(javaType(ArrayN(elem, dims)).asInstanceOf[JReferenceType], dims) - - case IS_INSTANCE(tpe) => - tpe match { - case REFERENCE(cls) => jcode emitINSTANCEOF new JObjectType(javaName(cls)) - case ARRAY(elem) => jcode emitINSTANCEOF new JArrayType(javaType(elem)) - case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) - } - - case CHECK_CAST(tpe) => - tpe match { - case REFERENCE(cls) => if (cls != ObjectClass) { jcode emitCHECKCAST new JObjectType(javaName(cls)) } // No need to checkcast for Objects - case ARRAY(elem) => jcode emitCHECKCAST new JArrayType(javaType(elem)) - case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) - } - - case SWITCH(tags, branches) => - val tagArray = new Array[Array[Int]](tags.length) - var caze = tags - var i = 0 - - while (i < tagArray.length) { - tagArray(i) = new Array[Int](caze.head.length) - caze.head.copyToArray(tagArray(i), 0) - i += 1 - caze = caze.tail - } - val branchArray = jcode.newLabels(tagArray.length) - i = 0 - while (i < branchArray.length) { - branchArray(i) = labels(branches(i)) - i += 1 - } - debuglog("Emitting SWITCH:\ntags: " + tags + "\nbranches: " + branches) - jcode.emitSWITCH(tagArray, - branchArray, - labels(branches.last), - MIN_SWITCH_DENSITY) - () - - case JUMP(whereto) => - if (nextBlock != whereto) - jcode.emitGOTO_maybe_W(labels(whereto), false) // default to short jumps - - case CJUMP(success, failure, cond, kind) => - if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT - if (nextBlock == success) { - jcode.emitIF_ICMP(conds(cond.negate()), labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF_ICMP(conds(cond), labels(success)) - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) - if (nextBlock == success) { - jcode.emitIF_ACMP(conds(cond.negate()), labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF_ACMP(conds(cond), labels(success)) - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } else { - (kind: @unchecked) match { - case LONG => jcode.emitLCMP() - case FLOAT => - if (cond == LT || cond == LE) jcode.emitFCMPG() - else jcode.emitFCMPL() - case DOUBLE => - if (cond == LT || cond == LE) jcode.emitDCMPG() - else jcode.emitDCMPL() - } - if (nextBlock == success) { - jcode.emitIF(conds(cond.negate()), labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF(conds(cond), labels(success)); - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } - - case CZJUMP(success, failure, cond, kind) => - if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT - if (nextBlock == success) { - jcode.emitIF(conds(cond.negate()), labels(failure)) - } else { - jcode.emitIF(conds(cond), labels(success)) - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) - val Success = success - val Failure = failure - (cond, nextBlock) match { - case (EQ, Success) => jcode emitIFNONNULL labels(failure) - case (NE, Failure) => jcode emitIFNONNULL labels(success) - case (EQ, Failure) => jcode emitIFNULL labels(success) - case (NE, Success) => jcode emitIFNULL labels(failure) - case (EQ, _) => - jcode emitIFNULL labels(success) - jcode.emitGOTO_maybe_W(labels(failure), false) - case (NE, _) => - jcode emitIFNONNULL labels(success) - jcode.emitGOTO_maybe_W(labels(failure), false) - case _ => - } - } else { - (kind: @unchecked) match { - case LONG => - jcode.emitLCONST_0() - jcode.emitLCMP() - case FLOAT => - jcode.emitFCONST_0() - if (cond == LT || cond == LE) jcode.emitFCMPG() - else jcode.emitFCMPL() - case DOUBLE => - jcode.emitDCONST_0() - if (cond == LT || cond == LE) jcode.emitDCMPG() - else jcode.emitDCMPL() - } - if (nextBlock == success) { - jcode.emitIF(conds(cond.negate()), labels(failure)) - } else { - jcode.emitIF(conds(cond), labels(success)) - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } - - case RETURN(kind) => jcode emitRETURN javaType(kind) - - case THROW(_) => jcode.emitATHROW() - - case DROP(kind) => - if(kind.isWideType) jcode.emitPOP2() - else jcode.emitPOP() - - case DUP(kind) => - if(kind.isWideType) jcode.emitDUP2() - else jcode.emitDUP() - - case MONITOR_ENTER() => jcode.emitMONITORENTER() - - case MONITOR_EXIT() => jcode.emitMONITOREXIT() - - case SCOPE_ENTER(lv) => - varsInBlock += lv - lv.start = jcode.getPC() - - case SCOPE_EXIT(lv) => - if (varsInBlock(lv)) { - lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges - varsInBlock -= lv - } - else if (b.varsInScope(lv)) { - lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges - b.varsInScope -= lv - } - else dumpMethodAndAbort(method, "Illegal local var nesting") - - case LOAD_EXCEPTION(_) => - () - } - - crtPC = jcode.getPC() - - // assert(instr.pos.source.isEmpty || instr.pos.source.get == (clasz.cunit.source), "sources don't match") - // val crtLine = instr.pos.line.get(lastLineNr); - - val crtLine = try { - if (instr.pos == NoPosition) lastLineNr else (instr.pos).line // check NoPosition to avoid costly exception - } catch { - case _: UnsupportedOperationException => - log("Warning: wrong position in: " + method) - lastLineNr - } - - if (instr eq lastInstr) { endPC(b) = jcode.getPC() } - - //System.err.println("CRTLINE: " + instr.pos + " " + - // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine); - - if (crtPC > lastMappedPC) { - jcode.completeLineNumber(lastMappedPC, crtPC, crtLine) - lastMappedPC = crtPC - lastLineNr = crtLine - } - } - - // local vars that survived this basic block - for (lv <- varsInBlock) { - lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges - } - for (lv <- b.varsInScope) { - lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges - } - } - - - /** - * @param primitive ... - * @param pos ... - */ - def genPrimitive(primitive: Primitive, pos: Position) { - primitive match { - case Negation(kind) => - if(kind.isIntSizedType) { jcode.emitINEG() } - else { - kind match { - case LONG => jcode.emitLNEG() - case FLOAT => jcode.emitFNEG() - case DOUBLE => jcode.emitDNEG() - case _ => abort("Impossible to negate a " + kind) - } - } - - case Arithmetic(op, kind) => - op match { - case ADD => - if(kind.isIntSizedType) { jcode.emitIADD() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLADD() - case FLOAT => jcode.emitFADD() - case DOUBLE => jcode.emitDADD() - } - } - - case SUB => - if(kind.isIntSizedType) { jcode.emitISUB() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLSUB() - case FLOAT => jcode.emitFSUB() - case DOUBLE => jcode.emitDSUB() - } - } - - case MUL => - if(kind.isIntSizedType) { jcode.emitIMUL() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLMUL() - case FLOAT => jcode.emitFMUL() - case DOUBLE => jcode.emitDMUL() - } - } - - case DIV => - if(kind.isIntSizedType) { jcode.emitIDIV() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLDIV() - case FLOAT => jcode.emitFDIV() - case DOUBLE => jcode.emitDDIV() - } - } - - case REM => - if(kind.isIntSizedType) { jcode.emitIREM() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLREM() - case FLOAT => jcode.emitFREM() - case DOUBLE => jcode.emitDREM() - } - } - - case NOT => - if(kind.isIntSizedType) { - jcode.emitPUSH(-1) - jcode.emitIXOR() - } else if(kind == LONG) { - jcode.emitPUSH(-1l) - jcode.emitLXOR() - } else { - abort("Impossible to negate an " + kind) - } - - case _ => - abort("Unknown arithmetic primitive " + primitive) - } - - case Logical(op, kind) => ((op, kind): @unchecked) match { - case (AND, LONG) => jcode.emitLAND() - case (AND, INT) => jcode.emitIAND() - case (AND, _) => - jcode.emitIAND() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - - case (OR, LONG) => jcode.emitLOR() - case (OR, INT) => jcode.emitIOR() - case (OR, _) => - jcode.emitIOR() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - - case (XOR, LONG) => jcode.emitLXOR() - case (XOR, INT) => jcode.emitIXOR() - case (XOR, _) => - jcode.emitIXOR() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - } - - case Shift(op, kind) => ((op, kind): @unchecked) match { - case (LSL, LONG) => jcode.emitLSHL() - case (LSL, INT) => jcode.emitISHL() - case (LSL, _) => - jcode.emitISHL() - jcode.emitT2T(javaType(INT), javaType(kind)) - - case (ASR, LONG) => jcode.emitLSHR() - case (ASR, INT) => jcode.emitISHR() - case (ASR, _) => - jcode.emitISHR() - jcode.emitT2T(javaType(INT), javaType(kind)) - - case (LSR, LONG) => jcode.emitLUSHR() - case (LSR, INT) => jcode.emitIUSHR() - case (LSR, _) => - jcode.emitIUSHR() - jcode.emitT2T(javaType(INT), javaType(kind)) - } - - case Comparison(op, kind) => ((op, kind): @unchecked) match { - case (CMP, LONG) => jcode.emitLCMP() - case (CMPL, FLOAT) => jcode.emitFCMPL() - case (CMPG, FLOAT) => jcode.emitFCMPG() - case (CMPL, DOUBLE) => jcode.emitDCMPL() - case (CMPG, DOUBLE) => jcode.emitDCMPL() - } - - case Conversion(src, dst) => - debuglog("Converting from: " + src + " to: " + dst) - if (dst == BOOL) { - println("Illegal conversion at: " + clasz + " at: " + pos.source + ":" + pos.line) - } else - jcode.emitT2T(javaType(src), javaType(dst)) - - case ArrayLength(_) => - jcode.emitARRAYLENGTH() - - case StartConcat => - jcode emitNEW StringBuilderClassName - jcode.emitDUP() - jcode.emitINVOKESPECIAL(StringBuilderClassName, - JMethod.INSTANCE_CONSTRUCTOR_NAME, - JMethodType.ARGLESS_VOID_FUNCTION) - - case StringConcat(el) => - val jtype = el match { - case REFERENCE(_) | ARRAY(_) => JAVA_LANG_OBJECT - case _ => javaType(el) - } - jcode.emitINVOKEVIRTUAL(StringBuilderClassName, - "append", - new JMethodType(StringBuilderType, - Array(jtype))) - case EndConcat => - jcode.emitINVOKEVIRTUAL(StringBuilderClassName, - "toString", - toStringType) - - case _ => - abort("Unimplemented primitive " + primitive) - } - } - - // genCode starts here - genBlocks(linearization) - - if (this.method.exh != Nil) - genExceptionHandlers; - } - - - /** Emit a Local variable table for debugging purposes. - * Synthetic locals are skipped. All variables are method-scoped. - */ - private def genLocalVariableTable(m: IMethod, jcode: JCode) { - val vars = m.locals filterNot (_.sym.isSynthetic) - if (vars.isEmpty) return - - val pool = jclass.getConstantPool - val pc = jcode.getPC() - var anonCounter = 0 - var entries = 0 - vars.foreach { lv => - lv.ranges = mergeEntries(lv.ranges.reverse); - entries += lv.ranges.length - } - if (!jmethod.isStatic()) entries += 1 - - val lvTab = ByteBuffer.allocate(2 + 10 * entries) - def emitEntry(name: String, signature: String, idx: Short, start: Short, end: Short) { - lvTab putShort start - lvTab putShort end - lvTab putShort pool.addUtf8(name).toShort - lvTab putShort pool.addUtf8(signature).toShort - lvTab putShort idx - } - - lvTab.putShort(entries.toShort) - - if (!jmethod.isStatic()) { - emitEntry("this", jclass.getType().getSignature(), 0, 0.toShort, pc.toShort) - } - - for (lv <- vars) { - val name = if (javaName(lv.sym) eq null) { - anonCounter += 1 - "<anon" + anonCounter + ">" - } else javaName(lv.sym) - - val index = indexOf(lv).toShort - val tpe = javaType(lv.kind).getSignature() - for ((start, end) <- lv.ranges) { - emitEntry(name, tpe, index, start.toShort, (end - start).toShort) - } - } - val attr = - fjbgContext.JOtherAttribute(jclass, - jcode, - tpnme.LocalVariableTableATTR.toString, - lvTab.array()) - jcode addAttribute attr - } - - - /** For each basic block, the first PC address following it. */ - val endPC = new mutable.HashMap[BasicBlock, Int] - - ////////////////////// local vars /////////////////////// - - def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe)) - - def sizeOf(k: TypeKind): Int = if(k.isWideType) 2 else 1 - - def indexOf(m: IMethod, sym: Symbol): Int = { - val Some(local) = m lookupLocal sym - indexOf(local) - } - - def indexOf(local: Local): Int = { - assert(local.index >= 0, "Invalid index for: " + local + "{" + local.## + "}: ") - local.index - } - - /** - * Compute the indexes of each local variable of the given - * method. *Does not assume the parameters come first!* - */ - def computeLocalVarsIndex(m: IMethod) { - var idx = if (m.symbol.isStaticMember) 0 else 1; - - for (l <- m.params) { - debuglog("Index value for " + l + "{" + l.## + "}: " + idx) - l.index = idx - idx += sizeOf(l.kind) - } - - for (l <- m.locals if !(m.params contains l)) { - debuglog("Index value for " + l + "{" + l.## + "}: " + idx) - l.index = idx - idx += sizeOf(l.kind) - } - } - - ////////////////////// Utilities //////////////////////// - - /** Merge adjacent ranges. */ - private def mergeEntries(ranges: List[(Int, Int)]): List[(Int, Int)] = - (ranges.foldLeft(Nil: List[(Int, Int)]) { (collapsed: List[(Int, Int)], p: (Int, Int)) => (collapsed, p) match { - case (Nil, _) => List(p) - case ((s1, e1) :: rest, (s2, e2)) if (e1 == s2) => (s1, e2) :: rest - case _ => p :: collapsed - }}).reverse - } - - private def mkFlags(args: Int*) = args.foldLeft(0)(_ | _) - - /** - * Return the Java modifiers for the given symbol. - * Java modifiers for classes: - * - public, abstract, final, strictfp (not used) - * for interfaces: - * - the same as for classes, without 'final' - * for fields: - * - public, private (*) - * - static, final - * for methods: - * - the same as for fields, plus: - * - abstract, synchronized (not used), strictfp (not used), native (not used) - * - * (*) protected cannot be used, since inner classes 'see' protected members, - * and they would fail verification after lifted. - */ - def javaFlags(sym: Symbol): Int = { - // constructors of module classes should be private - // PP: why are they only being marked private at this stage and not earlier? - val privateFlag = - sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) - - // Final: the only fields which can receive ACC_FINAL are eager vals. - // Neither vars nor lazy vals can, because: - // - // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3 - // "Another problem is that the specification allows aggressive - // optimization of final fields. Within a thread, it is permissible to - // reorder reads of a final field with those modifications of a final - // field that do not take place in the constructor." - // - // A var or lazy val which is marked final still has meaning to the - // scala compiler. The word final is heavily overloaded unfortunately; - // for us it means "not overridable". At present you can't override - // vars regardless; this may change. - // - // The logic does not check .isFinal (which checks flags for the FINAL flag, - // and includes symbols marked lateFINAL) instead inspecting rawflags so - // we can exclude lateFINAL. Such symbols are eligible for inlining, but to - // avoid breaking proxy software which depends on subclassing, we do not - // emit ACC_FINAL. - // Nested objects won't receive ACC_FINAL in order to allow for their overriding. - - val finalFlag = ( - (((sym.rawflags & Flags.FINAL) != 0) || isTopLevelModule(sym)) - && !sym.enclClass.isInterface - && !sym.isClassConstructor - && !sym.isMutable // lazy vals and vars both - ) - - // Primitives are "abstract final" to prohibit instantiation - // without having to provide any implementations, but that is an - // illegal combination of modifiers at the bytecode level so - // suppress final if abstract if present. - mkFlags( - if (privateFlag) ACC_PRIVATE else ACC_PUBLIC, - if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0, - if (sym.isInterface) ACC_INTERFACE else 0, - if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0, - if (sym.isStaticMember) ACC_STATIC else 0, - if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0, - if (sym.isArtifact) ACC_SYNTHETIC else 0, - if (sym.isClass && !sym.isInterface) ACC_SUPER else 0, - if (sym.isVarargsMethod) ACC_VARARGS else 0, - if (sym.hasFlag(Flags.SYNCHRONIZED)) JAVA_ACC_SYNCHRONIZED else 0 - ) - } - def javaFieldFlags(sym: Symbol) = ( - javaFlags(sym) | mkFlags( - if (sym hasAnnotation TransientAttr) ACC_TRANSIENT else 0, - if (sym hasAnnotation VolatileAttr) ACC_VOLATILE else 0, - if (sym.isMutable) 0 else ACC_FINAL - ) - ) - - def isTopLevelModule(sym: Symbol): Boolean = - afterPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } - - def isStaticModule(sym: Symbol): Boolean = { - sym.isModuleClass && !sym.isImplClass && !sym.isLifted - } - -} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala index 540935fd57..50fd59b23f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala @@ -20,7 +20,7 @@ trait GenJVMASM { import definitions._ protected def outputDirectory(sym: Symbol): AbstractFile = - settings.outputDirs outputDirFor beforeFlatten(sym.sourceFile) + settings.outputDirs outputDirFor enteringFlatten(sym.sourceFile) protected def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { var dir = base @@ -65,7 +65,7 @@ trait GenJVMASM { // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. hasApproximate && { // Before erasure so we can identify generic mains. - beforeErasure { + enteringErasure { val companion = sym.linkedClassOfClass val companionMain = companion.tpe.member(nme.main) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala deleted file mode 100644 index e002a614bd..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala +++ /dev/null @@ -1,142 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Iulian Dragos - */ - -package scala.tools.nsc -package backend.jvm - -import scala.collection.{ mutable, immutable } -import ch.epfl.lamp.fjbg._ - -trait GenJVMUtil { - self: GenJVM => - - import global._ - import icodes._ - import icodes.opcodes._ - import definitions._ - - /** Map from type kinds to the Java reference types. It is used for - * loading class constants. @see Predef.classOf. - */ - val classLiteral = immutable.Map[TypeKind, JObjectType]( - UNIT -> new JObjectType("java.lang.Void"), - BOOL -> new JObjectType("java.lang.Boolean"), - BYTE -> new JObjectType("java.lang.Byte"), - SHORT -> new JObjectType("java.lang.Short"), - CHAR -> new JObjectType("java.lang.Character"), - INT -> new JObjectType("java.lang.Integer"), - LONG -> new JObjectType("java.lang.Long"), - FLOAT -> new JObjectType("java.lang.Float"), - DOUBLE -> new JObjectType("java.lang.Double") - ) - - // Don't put this in per run caches. - private val javaNameCache = new mutable.WeakHashMap[Symbol, Name]() ++= List( - NothingClass -> binarynme.RuntimeNothing, - RuntimeNothingClass -> binarynme.RuntimeNothing, - NullClass -> binarynme.RuntimeNull, - RuntimeNullClass -> binarynme.RuntimeNull - ) - - /** This trait may be used by tools who need access to - * utility methods like javaName and javaType. (for instance, - * the Eclipse plugin uses it). - */ - trait BytecodeUtil { - - val conds = immutable.Map[TestOp, Int]( - EQ -> JExtendedCode.COND_EQ, - NE -> JExtendedCode.COND_NE, - LT -> JExtendedCode.COND_LT, - GT -> JExtendedCode.COND_GT, - LE -> JExtendedCode.COND_LE, - GE -> JExtendedCode.COND_GE - ) - - /** Specialized array conversion to prevent calling - * java.lang.reflect.Array.newInstance via TraversableOnce.toArray - */ - - def mkArray(xs: Traversable[JType]): Array[JType] = { val a = new Array[JType](xs.size); xs.copyToArray(a); a } - def mkArray(xs: Traversable[String]): Array[String] = { val a = new Array[String](xs.size); xs.copyToArray(a); a } - - /** Return the a name of this symbol that can be used on the Java - * platform. It removes spaces from names. - * - * Special handling: - * scala.Nothing erases to scala.runtime.Nothing$ - * scala.Null erases to scala.runtime.Null$ - * - * This is needed because they are not real classes, and they mean - * 'abrupt termination upon evaluation of that expression' or null respectively. - * This handling is done already in GenICode, but here we need to remove - * references from method signatures to these types, because such classes can - * not exist in the classpath: the type checker will be very confused. - */ - def javaName(sym: Symbol): String = - javaNameCache.getOrElseUpdate(sym, { - if (sym.isClass || (sym.isModule && !sym.isMethod)) - sym.javaBinaryName - else - sym.javaSimpleName - }).toString - - def javaType(t: TypeKind): JType = (t: @unchecked) match { - case UNIT => JType.VOID - case BOOL => JType.BOOLEAN - case BYTE => JType.BYTE - case SHORT => JType.SHORT - case CHAR => JType.CHAR - case INT => JType.INT - case LONG => JType.LONG - case FLOAT => JType.FLOAT - case DOUBLE => JType.DOUBLE - case REFERENCE(cls) => new JObjectType(javaName(cls)) - case ARRAY(elem) => new JArrayType(javaType(elem)) - } - - def javaType(t: Type): JType = javaType(toTypeKind(t)) - - def javaType(s: Symbol): JType = - if (s.isMethod) - new JMethodType( - if (s.isClassConstructor) JType.VOID else javaType(s.tpe.resultType), - mkArray(s.tpe.paramTypes map javaType) - ) - else - javaType(s.tpe) - - protected def genConstant(jcode: JExtendedCode, const: Constant) { - const.tag match { - case UnitTag => () - case BooleanTag => jcode emitPUSH const.booleanValue - case ByteTag => jcode emitPUSH const.byteValue - case ShortTag => jcode emitPUSH const.shortValue - case CharTag => jcode emitPUSH const.charValue - case IntTag => jcode emitPUSH const.intValue - case LongTag => jcode emitPUSH const.longValue - case FloatTag => jcode emitPUSH const.floatValue - case DoubleTag => jcode emitPUSH const.doubleValue - case StringTag => jcode emitPUSH const.stringValue - case NullTag => jcode.emitACONST_NULL() - case ClazzTag => - val kind = toTypeKind(const.typeValue) - val toPush = - if (kind.isValueType) classLiteral(kind) - else javaType(kind).asInstanceOf[JReferenceType] - - jcode emitPUSH toPush - - case EnumTag => - val sym = const.symbolValue - jcode.emitGETSTATIC(javaName(sym.owner), - javaName(sym), - javaType(sym.tpe.underlying)) - case _ => - abort("Unknown constant value: " + const) - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala deleted file mode 100644 index aaffaa84d8..0000000000 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ /dev/null @@ -1,2358 +0,0 @@ -/* NSC -- new scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Nikolay Mihaylov - */ - - -package scala.tools.nsc -package backend.msil - -import java.io.{File, IOException} -import java.nio.{ByteBuffer, ByteOrder} -import scala.collection.{ mutable, immutable } -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 scala.language.postfixOps - -abstract class GenMSIL extends SubComponent { - import global._ - import loaders.clrTypes - import clrTypes.{types, constructors, methods, fields} - import icodes._ - import icodes.opcodes._ - - val x = loaders - - /** Create a new phase */ - override def newPhase(p: Phase) = new MsilPhase(p) - - val phaseName = "msil" - /** MSIL code generation phase - */ - class MsilPhase(prev: Phase) extends GlobalPhase(prev) { - def name = phaseName - override def newFlags = phaseNewFlags - - override def erasedTypes = true - - override def run() { - if (settings.debug.value) inform("[running phase " + name + " on icode]") - - val codeGenerator = new BytecodeGenerator - - //classes is ICodes.classes, a HashMap[Symbol, IClass] - classes.values foreach codeGenerator.findEntryPoint - if( opt.showClass.isDefined && (codeGenerator.entryPoint == null) ) { // TODO introduce dedicated setting instead - val entryclass = opt.showClass.get.toString - warning("Couldn't find entry class " + entryclass) - } - - codeGenerator.initAssembly - - val classesSorted = classes.values.toList.sortBy(c => c.symbol.id) // simplifies comparing cross-compiler vs. .exe output - classesSorted foreach codeGenerator.createTypeBuilder - classesSorted foreach codeGenerator.createClassMembers - - try { - classesSorted foreach codeGenerator.genClass - } finally { - codeGenerator.writeAssembly - } - } - - override def apply(unit: CompilationUnit) { - abort("MSIL works on icode classes, not on compilation units!") - } - } - - /** - * MSIL bytecode generator. - * - */ - class BytecodeGenerator { - - val MODULE_INSTANCE_NAME = "MODULE$" - - import clrTypes.{VOID => MVOID, BOOLEAN => MBOOL, BYTE => MBYTE, SHORT => MSHORT, - CHAR => MCHAR, INT => MINT, LONG => MLONG, FLOAT => MFLOAT, - DOUBLE => MDOUBLE, OBJECT => MOBJECT, STRING => MSTRING, - STRING_ARRAY => MSTRING_ARRAY, - SYMTAB_CONSTR => SYMTAB_ATTRIBUTE_CONSTRUCTOR, - SYMTAB_DEFAULT_CONSTR => SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR} - - val EXCEPTION = clrTypes.getType("System.Exception") - val MBYTE_ARRAY = clrTypes.mkArrayType(MBYTE) - - val ICLONEABLE = clrTypes.getType("System.ICloneable") - val MEMBERWISE_CLONE = MOBJECT.GetMethod("MemberwiseClone", MsilType.EmptyTypes) - - val MMONITOR = clrTypes.getType("System.Threading.Monitor") - val MMONITOR_ENTER = MMONITOR.GetMethod("Enter", Array(MOBJECT)) - val MMONITOR_EXIT = MMONITOR.GetMethod("Exit", Array(MOBJECT)) - - val MSTRING_BUILDER = clrTypes.getType("System.Text.StringBuilder") - val MSTRING_BUILDER_CONSTR = MSTRING_BUILDER.GetConstructor(MsilType.EmptyTypes) - val MSTRING_BUILDER_TOSTRING = MSTRING_BUILDER.GetMethod("ToString", - MsilType.EmptyTypes) - - val TYPE_FROM_HANDLE = - clrTypes.getType("System.Type").GetMethod("GetTypeFromHandle", Array(clrTypes.getType("System.RuntimeTypeHandle"))) - - val INT_PTR = clrTypes.getType("System.IntPtr") - - val JOBJECT = definitions.ObjectClass - val JSTRING = definitions.StringClass - - val SystemConvert = clrTypes.getType("System.Convert") - - val objParam = Array(MOBJECT) - - val toBool: MethodInfo = SystemConvert.GetMethod("ToBoolean", objParam) // see comment in emitUnbox - val toSByte: MethodInfo = SystemConvert.GetMethod("ToSByte", objParam) - val toShort: MethodInfo = SystemConvert.GetMethod("ToInt16", objParam) - val toChar: MethodInfo = SystemConvert.GetMethod("ToChar", objParam) - val toInt: MethodInfo = SystemConvert.GetMethod("ToInt32", objParam) - val toLong: MethodInfo = SystemConvert.GetMethod("ToInt64", objParam) - val toFloat: MethodInfo = SystemConvert.GetMethod("ToSingle", objParam) - val toDouble: MethodInfo = SystemConvert.GetMethod("ToDouble", objParam) - - //val boxedUnit: FieldInfo = msilType(definitions.BoxedUnitModule.info).GetField("UNIT") - val boxedUnit: FieldInfo = fields(definitions.BoxedUnit_UNIT) - - // Scala attributes - // symtab.Definitions -> object (singleton..) - val SerializableAttr = definitions.SerializableAttr.tpe - val CloneableAttr = definitions.CloneableAttr.tpe - val TransientAtt = definitions.TransientAttr.tpe - // remoting: the architectures are too different, no mapping (no portable code - // possible) - - // java instance methods that are mapped to static methods in .net - // these will need to be called with OpCodes.Call (not Callvirt) - val dynToStatMapped = mutable.HashSet[Symbol]() - - initMappings() - - /** Create the mappings between java and .net classes and methods */ - private def initMappings() { - mapType(definitions.AnyClass, MOBJECT) - mapType(definitions.AnyRefClass, MOBJECT) - //mapType(definitions.NullClass, clrTypes.getType("scala.AllRef$")) - //mapType(definitions.NothingClass, clrTypes.getType("scala.All$")) - // FIXME: for some reason the upper two lines map to null - mapType(definitions.NullClass, EXCEPTION) - mapType(definitions.NothingClass, EXCEPTION) - - mapType(definitions.BooleanClass, MBOOL) - mapType(definitions.ByteClass, MBYTE) - mapType(definitions.ShortClass, MSHORT) - mapType(definitions.CharClass, MCHAR) - mapType(definitions.IntClass, MINT) - mapType(definitions.LongClass, MLONG) - mapType(definitions.FloatClass, MFLOAT) - mapType(definitions.DoubleClass, MDOUBLE) - } - - var clasz: IClass = _ - var method: IMethod = _ - - var massembly: AssemblyBuilder = _ - var mmodule: ModuleBuilder = _ - var mcode: ILGenerator = _ - - var assemName: String = _ - var firstSourceName = "" - var outDir: File = _ - var srcPath: File = _ - var moduleName: String = _ - - def initAssembly() { - - assemName = settings.assemname.value - - if (assemName == "") { - if (entryPoint != null) { - assemName = msilName(entryPoint.enclClass) - // remove the $ at the end (from module-name) - assemName = assemName.substring(0, assemName.length() - 1) - } else { - // assuming filename of first source file - assert(firstSourceName.endsWith(".scala"), firstSourceName) - assemName = firstSourceName.substring(0, firstSourceName.length() - 6) - } - } else { - if (assemName.endsWith(".msil")) - assemName = assemName.substring(0, assemName.length()-5) - if (assemName.endsWith(".il")) - assemName = assemName.substring(0, assemName.length()-3) - val f: File = new File(assemName) - assemName = f.getName() - } - - outDir = new File(settings.outdir.value) - - srcPath = new File(settings.sourcedir.value) - - val assemblyName = new AssemblyName() - assemblyName.Name = assemName - massembly = AssemblyBuilderFactory.DefineDynamicAssembly(assemblyName) - - moduleName = assemName // + (if (entryPoint == null) ".dll" else ".exe") - // filename here: .dll or .exe (in both parameters), second: give absolute-path - mmodule = massembly.DefineDynamicModule(moduleName, - new File(outDir, moduleName).getAbsolutePath()) - assert (mmodule != null) - } - - - /** - * Form of the custom Attribute parameter (Ecma-335.pdf) - * - p. 163 for CustomAttrib Form, - * - p. 164 for FixedArg Form (Array and Element) (if array or not is known!) - * !! least significant byte first if values longer than one byte !! - * - * 1: Prolog (unsigned int16, value 0x0001) -> symtab[0] = 0x01, symtab[1] = 0x00 - * 2: FixedArgs (directly the data, get number and types from related constructor) - * 2.1: length of the array (unsigned int32, 4 bytes, least significant first) - * 2.2: the byte array data - * 3: NumNamed (unsigned int16, number of named fields and properties, 0x0000) - */ - def addSymtabAttribute(sym: Symbol, tBuilder: TypeBuilder) { - def addMarker() { - val markerSymtab = new Array[Byte](4) - markerSymtab(0) = 1.toByte - tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR, markerSymtab) - } - - // both conditions are needed (why exactly..?) - if (tBuilder.Name.endsWith("$") || sym.isModuleClass) { - addMarker() - } else { - currentRun.symData.get(sym) match { - case Some(pickle) => - var size = pickle.writeIndex - val symtab = new Array[Byte](size + 8) - symtab(0) = 1.toByte - for (i <- 2 until 6) { - symtab(i) = (size & 0xff).toByte - size = size >> 8 - } - java.lang.System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex) - - tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab) - - currentRun.symData -= sym - currentRun.symData -= sym.companionSymbol - - case _ => - addMarker() - } - } - } - - /** - * Mutates `member` adding CLR attributes (if any) based on sym.annotations. - * Please notice that CLR custom modifiers are a different beast (see customModifiers below) - * and thus shouldn't be added by this method. - */ - def addAttributes(member: ICustomAttributeSetter, annotations: List[AnnotationInfo]) { - val attributes = annotations.map(_.atp.typeSymbol).collect { - case definitions.TransientAttr => null // TODO this is just an example - } - return // TODO: implement at some point - } - - /** - * What's a CLR custom modifier? Intro available as source comments in compiler.msil.CustomModifier. - * It's basically a marker associated with a location (think of FieldInfo, ParameterInfo, and PropertyInfo) - * and thus that marker (be it optional or required) becomes part of the signature of that location. - * Some annotations will become CLR attributes (see addAttributes above), others custom modifiers (this method). - */ - def customModifiers(annotations: List[AnnotationInfo]): Array[CustomModifier] = { - annotations.map(_.atp.typeSymbol).collect { - case definitions.VolatileAttr => new CustomModifier(true, CustomModifier.VolatileMarker) - } toArray - } - - - - /* - debuglog("creating annotations: " + annotations + " for member : " + member) - for (annot@ AnnotationInfo(typ, annArgs, nvPairs) <- annotations ; - if annot.isConstant) - //!typ.typeSymbol.isJavaDefined - { -// assert(consts.length <= 1, -// "too many constant arguments for annotations; "+consts.toString()) - - // Problem / TODO having the symbol of the annotations type would be nicer - // (i hope that type.typeSymbol is the same as the one in types2create) - // AND: this will crash if the annotations Type is already compiled (-> not a typeBuilder) - // when this is solved, types2create will be the same as icodes.classes, thus superfluous - val annType: TypeBuilder = getType(typ.typeSymbol).asInstanceOf[TypeBuilder] -// val annType: MsilType = getType(typ.typeSymbol) - - // Problem / TODO: i have no idea which constructor is used. This - // information should be available in AnnotationInfo. - annType.CreateType() // else, GetConstructors can't be used - val constr: ConstructorInfo = annType.GetConstructors()(0) - // prevent a second call of CreateType, only needed because there's no - // other way than GetConstructors()(0) to get the constructor, if there's - // no constructor symbol available. - - val args: Array[Byte] = - getAttributeArgs( - annArgs map (_.constant.get), - (for((n,v) <- nvPairs) yield (n, v.constant.get))) - member.SetCustomAttribute(constr, args) - } - } */ - -/* def getAttributeArgs(consts: List[Constant], nvPairs: List[(Name, Constant)]): Array[Byte] = { - val buf = ByteBuffer.allocate(2048) // FIXME: this may be not enough! - buf.order(ByteOrder.LITTLE_ENDIAN) - buf.putShort(1.toShort) // signature - - def emitSerString(str: String) = { - // this is wrong, it has to be the length of the UTF-8 byte array, which - // may be longer (see clr-book on page 302) -// val length: Int = str.length - val strBytes: Array[Byte] = try { - str.getBytes("UTF-8") - } catch { - case _: Error => abort("could not get byte-array for string: " + str) - } - val length: Int = strBytes.length //this length is stored big-endian - if (length < 128) - buf.put(length.toByte) - else if (length < (1<<14)) { - buf.put(((length >> 8) | 0x80).toByte) // the bits 14 and 15 of length are '0' - buf.put((length | 0xff).toByte) - } else if (length < (1 << 29)) { - buf.put(((length >> 24) | 0xc0).toByte) - buf.put(((length >> 16) & 0xff).toByte) - buf.put(((length >> 8) & 0xff).toByte) - buf.put(((length ) & 0xff).toByte) - } else - abort("string too long for attribute parameter: " + length) - buf.put(strBytes) - } - - def emitConst(const: Constant): Unit = const.tag match { - case BooleanTag => buf.put((if (const.booleanValue) 1 else 0).toByte) - case ByteTag => buf.put(const.byteValue) - case ShortTag => buf.putShort(const.shortValue) - case CharTag => buf.putChar(const.charValue) - case IntTag => buf.putInt(const.intValue) - case LongTag => buf.putLong(const.longValue) - case FloatTag => buf.putFloat(const.floatValue) - case DoubleTag => buf.putDouble(const.doubleValue) - case StringTag => - val str: String = const.stringValue - if (str == null) { - buf.put(0xff.toByte) - } else { - emitSerString(str) - } - case ArrayTag => - val arr: Array[Constant] = const.arrayValue - if (arr == null) { - buf.putInt(0xffffffff) - } else { - buf.putInt(arr.length) - arr.foreach(emitConst) - } - - // TODO: other Tags: NoTag, UnitTag, ClazzTag, EnumTag, ArrayTag ??? - - case _ => abort("could not handle attribute argument: " + const) - } - - consts foreach emitConst - buf.putShort(nvPairs.length.toShort) - def emitNamedArg(nvPair: (Name, Constant)) { - // the named argument is a property of the attribute (it can't be a field, since - // all fields in scala are private) - buf.put(0x54.toByte) - - def emitType(c: Constant) = c.tag match { // type of the constant, Ecma-335.pdf, page 151 - case BooleanTag => buf.put(0x02.toByte) - case ByteTag => buf.put(0x05.toByte) - case ShortTag => buf.put(0x06.toByte) - case CharTag => buf.put(0x07.toByte) - case IntTag => buf.put(0x08.toByte) - case LongTag => buf.put(0x0a.toByte) - case FloatTag => buf.put(0x0c.toByte) - case DoubleTag => buf.put(0x0d.toByte) - case StringTag => buf.put(0x0e.toByte) - - // TODO: other Tags: NoTag, UnitTag, ClazzTag, EnumTag ??? - - // ArrayTag falls in here - case _ => abort("could not handle attribute argument: " + c) - } - - val cnst: Constant = nvPair._2 - if (cnst.tag == ArrayTag) { - buf.put(0x1d.toByte) - emitType(cnst.arrayValue(0)) // FIXME: will crash if array length = 0 - } else if (cnst.tag == EnumTag) { - buf.put(0x55.toByte) - // TODO: put a SerString (don't know what exactly, names of the enums somehow..) - } else { - buf.put(0x51.toByte) - emitType(cnst) - } - - emitSerString(nvPair._1.toString) - emitConst(nvPair._2) - } - - val length = buf.position() - buf.array().slice(0, length) - } */ - - def writeAssembly() { - if (entryPoint != null) { - assert(entryPoint.enclClass.isModuleClass, entryPoint.enclClass) - val mainMethod = methods(entryPoint) - val stringArrayTypes: Array[MsilType] = Array(MSTRING_ARRAY) - val globalMain = mmodule.DefineGlobalMethod( - "Main", MethodAttributes.Public | MethodAttributes.Static, - MVOID, stringArrayTypes) - globalMain.DefineParameter(0, ParameterAttributes.None, "args") - massembly.SetEntryPoint(globalMain) - val code = globalMain.GetILGenerator() - val moduleField = getModuleInstanceField(entryPoint.enclClass) - code.Emit(OpCodes.Ldsfld, moduleField) - code.Emit(OpCodes.Ldarg_0) - code.Emit(OpCodes.Callvirt, mainMethod) - code.Emit(OpCodes.Ret) - } - createTypes() - var outDirName: String = null - try { - if (settings.Ygenjavap.isDefault) { // we reuse the JVM-sounding setting because it's conceptually similar - outDirName = outDir.getPath() - massembly.Save(outDirName + "\\" + assemName + ".msil") /* use SingleFileILPrinterVisitor */ - } else { - outDirName = srcPath.getPath() - massembly.Save(settings.Ygenjavap.value, outDirName) /* use MultipleFilesILPrinterVisitor */ - } - } catch { - case e:IOException => abort("Could not write to " + outDirName + ": " + e.getMessage()) - } - } - - private def createTypes() { - for (sym <- classes.keys) { - val iclass = classes(sym) - val tBuilder = types(sym).asInstanceOf[TypeBuilder] - - debuglog("Calling CreatType for " + sym + ", " + tBuilder.toString) - - tBuilder.CreateType() - tBuilder.setSourceFilepath(iclass.cunit.source.file.path) - } - } - - private[GenMSIL] def ilasmFileName(iclass: IClass) : String = { - // method.sourceFile contains just the filename - iclass.cunit.source.file.toString.replace("\\", "\\\\") - } - - private[GenMSIL] def genClass(iclass: IClass) { - val sym = iclass.symbol - debuglog("Generating class " + sym + " flags: " + Flags.flagsToString(sym.flags)) - clasz = iclass - - val tBuilder = getType(sym).asInstanceOf[TypeBuilder] - if (isCloneable(sym)) { - // FIXME: why there's no nme.clone_ ? - // "Clone": if the code is non-portable, "Clone" is defined, not "clone" - // TODO: improve condition (should override AnyRef.clone) - if (iclass.methods.forall(m => { - !((m.symbol.name.toString != "clone" || m.symbol.name.toString != "Clone") && - m.symbol.tpe.paramTypes.length != 0) - })) { - debuglog("auto-generating cloneable method for " + sym) - val attrs: Short = (MethodAttributes.Public | MethodAttributes.Virtual | - MethodAttributes.HideBySig).toShort - val cloneMethod = tBuilder.DefineMethod("Clone", attrs, MOBJECT, - MsilType.EmptyTypes) - val clCode = cloneMethod.GetILGenerator() - clCode.Emit(OpCodes.Ldarg_0) - clCode.Emit(OpCodes.Call, MEMBERWISE_CLONE) - clCode.Emit(OpCodes.Ret) - } - } - - val line = sym.pos.line - tBuilder.setPosition(line, ilasmFileName(iclass)) - - if (isTopLevelModule(sym)) { - if (sym.companionClass == NoSymbol) - generateMirrorClass(sym) - else - log("No mirror class for module with linked class: " + - sym.fullName) - } - - addSymtabAttribute(sym, tBuilder) - addAttributes(tBuilder, sym.annotations) - - if (iclass.symbol != definitions.ArrayClass) - iclass.methods foreach genMethod - - } //genClass - - - private def genMethod(m: IMethod) { - debuglog("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) + - " owner: " + m.symbol.owner) - method = m - localBuilders.clear - computeLocalVarsIndex(m) - - if (m.symbol.isClassConstructor) { - mcode = constructors(m.symbol).asInstanceOf[ConstructorBuilder].GetILGenerator() - } else { - val mBuilder = methods(m.symbol).asInstanceOf[MethodBuilder] - if (!mBuilder.IsAbstract()) - try { - mcode = mBuilder.GetILGenerator() - } catch { - case e: Exception => - java.lang.System.out.println("m.symbol = " + Flags.flagsToString(m.symbol.flags) + " " + m.symbol) - java.lang.System.out.println("m.symbol.owner = " + Flags.flagsToString(m.symbol.owner.flags) + " " + m.symbol.owner) - java.lang.System.out.println("mBuilder = " + mBuilder) - java.lang.System.out.println("mBuilder.DeclaringType = " + - TypeAttributes.toString(mBuilder.DeclaringType.Attributes) + - "::" + mBuilder.DeclaringType) - throw e - } - else - mcode = null - } - - if (mcode != null) { - for (local <- m.locals ; if !(m.params contains local)) { - debuglog("add local var: " + local + ", of kind " + local.kind) - val t: MsilType = msilType(local.kind) - val localBuilder = mcode.DeclareLocal(t) - localBuilder.SetLocalSymInfo(msilName(local.sym)) - localBuilders(local) = localBuilder - } - genCode(m) - } - - } - - /** Special linearizer for methods with at least one exception handler. This - * linearizer brings all basic blocks in the right order so that nested - * try-catch and try-finally blocks can be emitted. - */ - val msilLinearizer = new MSILLinearizer() - - val labels = mutable.HashMap[BasicBlock, Label]() - - /* when emitting .line, it's enough to include the full filename just once per method, thus reducing filesize. - * this scheme relies on the fact that the entry block is emitted first. */ - var dbFilenameSeen = false - - def genCode(m: IMethod) { - - def makeLabels(blocks: List[BasicBlock]) = { - debuglog("Making labels for: " + method) - for (bb <- blocks) labels(bb) = mcode.DefineLabel() - } - - labels.clear - - var linearization = if(!m.exh.isEmpty) msilLinearizer.linearize(m) - else linearizer.linearize(m) - - if (!m.exh.isEmpty) - linearization = computeExceptionMaps(linearization, m) - - makeLabels(linearization) - - // debug val blocksInM = m.code.blocks.toList.sortBy(bb => bb.label) - // debug val blocksInL = linearization.sortBy(bb => bb.label) - // debug val MButNotL = (blocksInM.toSet) diff (blocksInL.toSet) // if non-empty, a jump to B fails to find a label for B (case CJUMP, case CZJUMP) - // debug if(!MButNotL.isEmpty) { } - - dbFilenameSeen = false - genBlocks(linearization) - - // RETURN inside exception blocks are replaced by Leave. The target of the - // leave is a `Ret` outside any exception block (generated here). - if (handlerReturnMethod == m) { - mcode.MarkLabel(handlerReturnLabel) - if (handlerReturnKind != UNIT) - mcode.Emit(OpCodes.Ldloc, handlerReturnLocal) - mcode.Emit(OpCodes.Ret) - } - - beginExBlock.clear() - beginCatchBlock.clear() - endExBlock.clear() - endFinallyLabels.clear() - } - - def genBlocks(blocks: List[BasicBlock], previous: BasicBlock = null) { - blocks match { - case Nil => () - case x :: Nil => genBlock(x, prev = previous, next = null) - case x :: y :: ys => genBlock(x, prev = previous, next = y); genBlocks(y :: ys, previous = x) - } - } - - // the try blocks starting at a certain BasicBlock - val beginExBlock = mutable.HashMap[BasicBlock, List[ExceptionHandler]]() - - // the catch blocks starting / endling at a certain BasicBlock - val beginCatchBlock = mutable.HashMap[BasicBlock, ExceptionHandler]() - val endExBlock = mutable.HashMap[BasicBlock, List[ExceptionHandler]]() - - /** When emitting the code (genBlock), the number of currently active try / catch - * blocks. When seeing a `RETURN` inside a try / catch, we need to - * - store the result in a local (if it's not UNIT) - * - emit `Leave handlerReturnLabel` instead of the Return - * - emit code at the end: load the local and return its value - */ - var currentHandlers = new mutable.Stack[ExceptionHandler] - // The IMethod the Local/Label/Kind below belong to - var handlerReturnMethod: IMethod = _ - // Stores the result when returning inside an exception block - var handlerReturnLocal: LocalBuilder = _ - // Label for a return instruction outside any exception block - var handlerReturnLabel: Label = _ - // The result kind. - var handlerReturnKind: TypeKind = _ - def returnFromHandler(kind: TypeKind): (LocalBuilder, Label) = { - if (handlerReturnMethod != method) { - handlerReturnMethod = method - if (kind != UNIT) { - handlerReturnLocal = mcode.DeclareLocal(msilType(kind)) - handlerReturnLocal.SetLocalSymInfo("$handlerReturn") - } - handlerReturnLabel = mcode.DefineLabel() - handlerReturnKind = kind - } - (handlerReturnLocal, handlerReturnLabel) - } - - /** For try/catch nested inside a finally, we can't use `Leave OutsideFinally`, the - * Leave target has to be inside the finally (and it has to be the `endfinally` instruction). - * So for every finalizer, we have a label which marks the place of the `endfinally`, - * nested try/catch blocks will leave there. - */ - val endFinallyLabels = mutable.HashMap[ExceptionHandler, Label]() - - /** Computes which blocks are the beginning / end of a try or catch block */ - private def computeExceptionMaps(blocks: List[BasicBlock], m: IMethod): List[BasicBlock] = { - val visitedBlocks = new mutable.HashSet[BasicBlock]() - - // handlers which have not been introduced so far - var openHandlers = m.exh - - - /** Example - * try { - * try { - * // *1* - * } catch { - * case h1 => - * } - * } catch { - * case h2 => - * case h3 => - * try { - * - * } catch { - * case h4 => // *2* - * case h5 => - * } - * } - */ - - // Stack of nested try blocks. Each bloc has a List of ExceptionHandler (multiple - // catch statements). Example *1*: Stack(List(h2, h3), List(h1)) - val currentTryHandlers = new mutable.Stack[List[ExceptionHandler]]() - - // Stack of nested catch blocks. The head of the list is the current catch block. The - // tail is all following catch blocks. Example *2*: Stack(List(h3), List(h4, h5)) - val currentCatchHandlers = new mutable.Stack[List[ExceptionHandler]]() - - for (b <- blocks) { - - // are we past the current catch blocks? - def endHandlers(): List[ExceptionHandler] = { - var res: List[ExceptionHandler] = Nil - if (!currentCatchHandlers.isEmpty) { - val handler = currentCatchHandlers.top.head - if (!handler.blocks.contains(b)) { - // all blocks of the handler are either visited, or not part of the linearization (i.e. dead) - assert(handler.blocks.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)), - "Bad linearization of basic blocks inside catch. Found block not part of the handler\n"+ - b.fullString +"\nwhile in catch-part of\n"+ handler) - - val rest = currentCatchHandlers.pop.tail - if (rest.isEmpty) { - // all catch blocks of that exception handler are covered - res = handler :: endHandlers() - } else { - // there are more catch blocks for that try (handlers covering the same) - currentCatchHandlers.push(rest) - beginCatchBlock(b) = rest.head - } - } - } - res - } - val end = endHandlers() - if (!end.isEmpty) endExBlock(b) = end - - // are we past the current try block? - if (!currentTryHandlers.isEmpty) { - val handler = currentTryHandlers.top.head - if (!handler.covers(b)) { - // all of the covered blocks are visited, or not part of the linearization - assert(handler.covered.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)), - "Bad linearization of basic blocks inside try. Found non-covered block\n"+ - b.fullString +"\nwhile in try-part of\n"+ handler) - - assert(handler.startBlock == b, - "Bad linearization of basic blocks. The entry block of a catch does not directly follow the try\n"+ - b.fullString +"\n"+ handler) - - val handlers = currentTryHandlers.pop - currentCatchHandlers.push(handlers) - beginCatchBlock(b) = handler - } - } - - // are there try blocks starting at b? - val (newHandlers, stillOpen) = openHandlers.partition(_.covers(b)) - openHandlers = stillOpen - - val newHandlersBySize = newHandlers.groupBy(_.covered.size) - // big handlers first, smaller ones are nested inside the try of the big one - // (checked by the assertions below) - val sizes = newHandlersBySize.keys.toList.sortWith(_ > _) - - val beginHandlers = new mutable.ListBuffer[ExceptionHandler] - for (s <- sizes) { - val sHandlers = newHandlersBySize(s) - for (h <- sHandlers) { - assert(h.covered == sHandlers.head.covered, - "bad nesting of exception handlers. same size, but not covering same blocks\n"+ - h +"\n"+ sHandlers.head) - assert(h.resultKind == sHandlers.head.resultKind, - "bad nesting of exception handlers. same size, but the same resultKind\n"+ - h +"\n"+ sHandlers.head) - } - for (bigger <- beginHandlers; h <- sHandlers) { - assert(h.covered.subsetOf(bigger.covered), - "bad nesting of exception handlers. try blocks of smaller handler are not nested in bigger one.\n"+ - h +"\n"+ bigger) - assert(h.blocks.toSet.subsetOf(bigger.covered), - "bad nesting of exception handlers. catch blocks of smaller handler are not nested in bigger one.\n"+ - h +"\n"+ bigger) - } - beginHandlers += sHandlers.head - currentTryHandlers.push(sHandlers) - } - beginExBlock(b) = beginHandlers.toList - visitedBlocks += b - } - - // if there handlers left (i.e. handlers covering nothing, or a - // non-existent (dead) block), remove their catch-blocks. - val liveBlocks = if (openHandlers.isEmpty) blocks else { - blocks.filter(b => openHandlers.forall(h => !h.blocks.contains(b))) - } - - /** There might be open handlers, but no more blocks. happens when try/catch end - * with `throw` or `return` - * def foo() { try { .. throw } catch { _ => .. throw } } - * - * In this case we need some code after the catch block for the auto-generated - * `leave` instruction. So we're adding a (dead) `throw new Exception`. - */ - val rest = currentCatchHandlers.map(handlers => { - assert(handlers.length == 1, handlers) - handlers.head - }).toList - - if (rest.isEmpty) { - liveBlocks - } else { - val b = m.code.newBlock - b.emit(Seq( - NEW(REFERENCE(definitions.ThrowableClass)), - DUP(REFERENCE(definitions.ObjectClass)), - CALL_METHOD(definitions.ThrowableClass.primaryConstructor, Static(true)), - THROW(definitions.ThrowableClass) - )) - b.close - endExBlock(b) = rest - liveBlocks ::: List(b) - } - } - - /** - * @param block the BasicBlock to emit code for - * @param next the following BasicBlock, `null` if `block` is the last one - */ - def genBlock(block: BasicBlock, prev: BasicBlock, next: BasicBlock) { - - def loadLocalOrAddress(local: Local, msg : String , loadAddr : Boolean) { - debuglog(msg + " for " + local) - val isArg = local.arg - val i = local.index - if (isArg) - loadArg(mcode, loadAddr)(i) - else - loadLocal(i, local, mcode, loadAddr) - } - - def loadFieldOrAddress(field: Symbol, isStatic: Boolean, msg: String, loadAddr : Boolean) { - debuglog(msg + " with owner: " + field.owner + - " flags: " + Flags.flagsToString(field.owner.flags)) - var fieldInfo = fields.get(field) match { - case Some(fInfo) => fInfo - case None => - val fInfo = getType(field.owner).GetField(msilName(field)) - fields(field) = fInfo - fInfo - } - if (fieldInfo.IsVolatile) { - mcode.Emit(OpCodes.Volatile) - } - if (!fieldInfo.IsLiteral) { - if (loadAddr) { - mcode.Emit(if (isStatic) OpCodes.Ldsflda else OpCodes.Ldflda, fieldInfo) - } else { - mcode.Emit(if (isStatic) OpCodes.Ldsfld else OpCodes.Ldfld, fieldInfo) - } - } else { - assert(!loadAddr, "can't take AddressOf a literal field (not even with readonly. prefix) because no memory was allocated to such field ...") - // TODO the above can be overcome by loading the value, boxing, and finally unboxing. An address to a copy of the raw value will be on the stack. - /* We perform `field inlining' as required by CLR. - * Emit as for a CONSTANT ICode stmt, with the twist that the constant value is available - * as a java.lang.Object and its .NET type allows constant initialization in CLR, i.e. that type - * is one of I1, I2, I4, I8, R4, R8, CHAR, BOOLEAN, STRING, or CLASS (in this last case, - * only accepting nullref as value). See Table 9-1 in Lidin's book on ILAsm. */ - val value = fieldInfo.getValue() - if (value == null) { - mcode.Emit(OpCodes.Ldnull) - } else { - val typ = if (fieldInfo.FieldType.IsEnum) fieldInfo.FieldType.getUnderlyingType - else fieldInfo.FieldType - if (typ == clrTypes.STRING) { - mcode.Emit(OpCodes.Ldstr, value.asInstanceOf[String]) - } else if (typ == clrTypes.BOOLEAN) { - mcode.Emit(if (value.asInstanceOf[Boolean]) OpCodes.Ldc_I4_1 - else OpCodes.Ldc_I4_0) - } else if (typ == clrTypes.BYTE || typ == clrTypes.UBYTE) { - loadI4(value.asInstanceOf[Byte], mcode) - } else if (typ == clrTypes.SHORT || typ == clrTypes.USHORT) { - loadI4(value.asInstanceOf[Int], mcode) - } else if (typ == clrTypes.CHAR) { - loadI4(value.asInstanceOf[Char], mcode) - } else if (typ == clrTypes.INT || typ == clrTypes.UINT) { - loadI4(value.asInstanceOf[Int], mcode) - } else if (typ == clrTypes.LONG || typ == clrTypes.ULONG) { - mcode.Emit(OpCodes.Ldc_I8, value.asInstanceOf[Long]) - } else if (typ == clrTypes.FLOAT) { - mcode.Emit(OpCodes.Ldc_R4, value.asInstanceOf[Float]) - } else if (typ == clrTypes.DOUBLE) { - mcode.Emit(OpCodes.Ldc_R8, value.asInstanceOf[Double]) - } else { - /* TODO one more case is described in Partition II, 16.2: bytearray(...) */ - abort("Unknown type for static literal field: " + fieldInfo) - } - } - } - } - - /** Creating objects works differently on .NET. On the JVM - * - NEW(type) => reference on Stack - * - DUP, load arguments, CALL_METHOD(constructor) - * - * On .NET, the NEW and DUP are ignored, but we emit a special method call - * - load arguments - * - NewObj(constructor) => reference on stack - * - * This variable tells whether the previous instruction was a NEW, - * we expect a DUP which is not emitted. */ - var previousWasNEW = false - - var lastLineNr: Int = 0 - var lastPos: Position = NoPosition - - - // EndExceptionBlock must happen before MarkLabel because it adds the - // Leave instruction. Otherwise, labels(block) points to the Leave - // (inside the catch) instead of the instruction afterwards. - for (handlers <- endExBlock.get(block); exh <- handlers) { - currentHandlers.pop() - for (l <- endFinallyLabels.get(exh)) - mcode.MarkLabel(l) - mcode.EndExceptionBlock() - } - - mcode.MarkLabel(labels(block)) - debuglog("Generating code for block: " + block) - - for (handler <- beginCatchBlock.get(block)) { - if (!currentHandlers.isEmpty && currentHandlers.top.covered == handler.covered) { - currentHandlers.pop() - currentHandlers.push(handler) - } - if (handler.cls == NoSymbol) { - // `finally` blocks are represented the same as `catch`, but with no catch-type - mcode.BeginFinallyBlock() - } else { - val t = getType(handler.cls) - mcode.BeginCatchBlock(t) - } - } - for (handlers <- beginExBlock.get(block); exh <- handlers) { - currentHandlers.push(exh) - mcode.BeginExceptionBlock() - } - - for (instr <- block) { - try { - val currentLineNr = instr.pos.line - val skip = if(instr.pos.isRange) instr.pos.sameRange(lastPos) else (currentLineNr == lastLineNr); - if(!skip || !dbFilenameSeen) { - val fileName = if(dbFilenameSeen) "" else {dbFilenameSeen = true; ilasmFileName(clasz)}; - if(instr.pos.isRange) { - val startLine = instr.pos.focusStart.line - val endLine = instr.pos.focusEnd.line - val startCol = instr.pos.focusStart.column - val endCol = instr.pos.focusEnd.column - mcode.setPosition(startLine, endLine, startCol, endCol, fileName) - } else { - mcode.setPosition(instr.pos.line, fileName) - } - lastLineNr = currentLineNr - lastPos = instr.pos - } - } catch { case _: UnsupportedOperationException => () } - - if (previousWasNEW) - assert(instr.isInstanceOf[DUP], block) - - instr match { - case THIS(clasz) => - mcode.Emit(OpCodes.Ldarg_0) - - case CONSTANT(const) => - const.tag match { - case UnitTag => () - case BooleanTag => mcode.Emit(if (const.booleanValue) OpCodes.Ldc_I4_1 - else OpCodes.Ldc_I4_0) - case ByteTag => loadI4(const.byteValue, mcode) - case ShortTag => loadI4(const.shortValue, mcode) - case CharTag => loadI4(const.charValue, mcode) - case IntTag => loadI4(const.intValue, mcode) - case LongTag => mcode.Emit(OpCodes.Ldc_I8, const.longValue) - case FloatTag => mcode.Emit(OpCodes.Ldc_R4, const.floatValue) - case DoubleTag => mcode.Emit(OpCodes.Ldc_R8, const.doubleValue) - case StringTag => mcode.Emit(OpCodes.Ldstr, const.stringValue) - case NullTag => mcode.Emit(OpCodes.Ldnull) - case ClazzTag => - mcode.Emit(OpCodes.Ldtoken, msilType(const.typeValue)) - mcode.Emit(OpCodes.Call, TYPE_FROM_HANDLE) - case _ => abort("Unknown constant value: " + const) - } - - case LOAD_ARRAY_ITEM(kind) => - (kind: @unchecked) match { - case BOOL => mcode.Emit(OpCodes.Ldelem_I1) - case BYTE => mcode.Emit(OpCodes.Ldelem_I1) // I1 for System.SByte, i.e. a scala.Byte - case SHORT => mcode.Emit(OpCodes.Ldelem_I2) - case CHAR => mcode.Emit(OpCodes.Ldelem_U2) - case INT => mcode.Emit(OpCodes.Ldelem_I4) - case LONG => mcode.Emit(OpCodes.Ldelem_I8) - case FLOAT => mcode.Emit(OpCodes.Ldelem_R4) - case DOUBLE => mcode.Emit(OpCodes.Ldelem_R8) - case REFERENCE(cls) => mcode.Emit(OpCodes.Ldelem_Ref) - case ARRAY(elem) => mcode.Emit(OpCodes.Ldelem_Ref) - - // case UNIT is not possible: an Array[Unit] will be an - // Array[scala.runtime.BoxedUnit] (-> case REFERENCE) - } - - case LOAD_LOCAL(local) => loadLocalOrAddress(local, "load_local", false) - - case CIL_LOAD_LOCAL_ADDRESS(local) => loadLocalOrAddress(local, "cil_load_local_address", true) - - case LOAD_FIELD(field, isStatic) => loadFieldOrAddress(field, isStatic, "load_field", false) - - case CIL_LOAD_FIELD_ADDRESS(field, isStatic) => loadFieldOrAddress(field, isStatic, "cil_load_field_address", true) - - case CIL_LOAD_ARRAY_ITEM_ADDRESS(kind) => mcode.Emit(OpCodes.Ldelema, msilType(kind)) - - case CIL_NEWOBJ(msym) => - assert(msym.isClassConstructor) - val constructorInfo: ConstructorInfo = getConstructor(msym) - mcode.Emit(OpCodes.Newobj, constructorInfo) - - case LOAD_MODULE(module) => - debuglog("Generating LOAD_MODULE for: " + showsym(module)) - mcode.Emit(OpCodes.Ldsfld, getModuleInstanceField(module)) - - case STORE_ARRAY_ITEM(kind) => - (kind: @unchecked) match { - case BOOL => mcode.Emit(OpCodes.Stelem_I1) - case BYTE => mcode.Emit(OpCodes.Stelem_I1) - case SHORT => mcode.Emit(OpCodes.Stelem_I2) - case CHAR => mcode.Emit(OpCodes.Stelem_I2) - case INT => mcode.Emit(OpCodes.Stelem_I4) - case LONG => mcode.Emit(OpCodes.Stelem_I8) - case FLOAT => mcode.Emit(OpCodes.Stelem_R4) - case DOUBLE => mcode.Emit(OpCodes.Stelem_R8) - case REFERENCE(cls) => mcode.Emit(OpCodes.Stelem_Ref) - case ARRAY(elem) => mcode.Emit(OpCodes.Stelem_Ref) // @TODO: test this! (occurs when calling a Array[Object]* vararg param method) - - // case UNIT not possible (see comment at LOAD_ARRAY_ITEM) - } - - case STORE_LOCAL(local) => - val isArg = local.arg - val i = local.index - debuglog("store_local for " + local + ", index " + i) - - // there are some locals defined by the compiler that - // are isArg and are need to be stored. - if (isArg) { - if (i >= -128 && i <= 127) - mcode.Emit(OpCodes.Starg_S, i) - else - mcode.Emit(OpCodes.Starg, i) - } else { - i match { - case 0 => mcode.Emit(OpCodes.Stloc_0) - case 1 => mcode.Emit(OpCodes.Stloc_1) - case 2 => mcode.Emit(OpCodes.Stloc_2) - case 3 => mcode.Emit(OpCodes.Stloc_3) - case _ => - if (i >= -128 && i <= 127) - mcode.Emit(OpCodes.Stloc_S, localBuilders(local)) - else - mcode.Emit(OpCodes.Stloc, localBuilders(local)) - } - } - - case STORE_THIS(_) => - // this only works for impl classes because the self parameter comes first - // in the method signature. If that changes, this code has to be revisited. - mcode.Emit(OpCodes.Starg_S, 0) - - case STORE_FIELD(field, isStatic) => - val fieldInfo = fields.get(field) match { - case Some(fInfo) => fInfo - case None => - val fInfo = getType(field.owner).GetField(msilName(field)) - fields(field) = fInfo - fInfo - } - mcode.Emit(if (isStatic) OpCodes.Stsfld else OpCodes.Stfld, fieldInfo) - - case CALL_PRIMITIVE(primitive) => - genPrimitive(primitive, instr.pos) - - case CALL_METHOD(msym, style) => - if (msym.isClassConstructor) { - val constructorInfo: ConstructorInfo = getConstructor(msym) - (style: @unchecked) match { - // normal constructor calls are Static.. - case Static(_) => - if (method.symbol.isClassConstructor && method.symbol.owner == msym.owner) - // we're generating a constructor (method: IMethod is a constructor), and we're - // calling another constructor of the same class. - - // @LUC TODO: this can probably break, namely when having: class A { def this() { new A() } } - // instead, we should instruct the CALL_METHOD with additional information, know whether it's - // an instance creation constructor call or not. - mcode.Emit(OpCodes.Call, constructorInfo) - else - mcode.Emit(OpCodes.Newobj, constructorInfo) - case SuperCall(_) => - mcode.Emit(OpCodes.Call, constructorInfo) - if (isStaticModule(clasz.symbol) && - notInitializedModules.contains(clasz.symbol) && - method.symbol.isClassConstructor) - { - notInitializedModules -= clasz.symbol - mcode.Emit(OpCodes.Ldarg_0) - mcode.Emit(OpCodes.Stsfld, getModuleInstanceField(clasz.symbol)) - } - } - - } else { - - var doEmit = true - getTypeOpt(msym.owner) match { - case Some(typ) if (typ.IsEnum) => { - def negBool() = { - mcode.Emit(OpCodes.Ldc_I4_0) - mcode.Emit(OpCodes.Ceq) - } - doEmit = false - val name = msym.name - if (name eq nme.EQ) { mcode.Emit(OpCodes.Ceq) } - else if (name eq nme.NE) { mcode.Emit(OpCodes.Ceq); negBool } - else if (name eq nme.LT) { mcode.Emit(OpCodes.Clt) } - else if (name eq nme.LE) { mcode.Emit(OpCodes.Cgt); negBool } - else if (name eq nme.GT) { mcode.Emit(OpCodes.Cgt) } - else if (name eq nme.GE) { mcode.Emit(OpCodes.Clt); negBool } - else if (name eq nme.OR) { mcode.Emit(OpCodes.Or) } - else if (name eq nme.AND) { mcode.Emit(OpCodes.And) } - else if (name eq nme.XOR) { mcode.Emit(OpCodes.Xor) } - else - doEmit = true - } - case _ => () - } - - // method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType - val (isDelegateView, paramType, resType) = beforeTyper { - msym.tpe match { - case MethodType(params, resultType) - if (params.length == 1 && msym.name == nme.view_) => - val paramType = params(0).tpe - val isDel = definitions.isCorrespondingDelegate(resultType, paramType) - (isDel, paramType, resultType) - case _ => (false, null, null) - } - } - if (doEmit && isDelegateView) { - doEmit = false - createDelegateCaller(paramType, resType) - } - - if (doEmit && - (msym.name == nme.PLUS || msym.name == nme.MINUS) - && clrTypes.isDelegateType(msilType(msym.owner.tpe))) - { - doEmit = false - val methodInfo: MethodInfo = getMethod(msym) - // call it as a static method, even if the compiler (symbol) thinks it's virtual - mcode.Emit(OpCodes.Call, methodInfo) - mcode.Emit(OpCodes.Castclass, msilType(msym.owner.tpe)) - } - - if (doEmit && definitions.Delegate_scalaCallers.contains(msym)) { - doEmit = false - val methodSym: Symbol = definitions.Delegate_scalaCallerTargets(msym) - val delegateType: Type = msym.tpe match { - case MethodType(_, retType) => retType - case _ => abort("not a method type: " + msym.tpe) - } - val methodInfo: MethodInfo = getMethod(methodSym) - val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR)) - if (methodSym.isStatic) { - mcode.Emit(OpCodes.Ldftn, methodInfo) - } else { - mcode.Emit(OpCodes.Dup) - mcode.Emit(OpCodes.Ldvirtftn, methodInfo) - } - mcode.Emit(OpCodes.Newobj, delegCtor) - } - - if (doEmit) { - val methodInfo: MethodInfo = getMethod(msym) - (style: @unchecked) match { - case SuperCall(_) => - mcode.Emit(OpCodes.Call, methodInfo) - case Dynamic => - // methodInfo.DeclaringType is null for global methods - val isValuetypeMethod = (methodInfo.DeclaringType ne null) && (methodInfo.DeclaringType.IsValueType) - val isValuetypeVirtualMethod = isValuetypeMethod && (methodInfo.IsVirtual) - if (dynToStatMapped(msym)) { - mcode.Emit(OpCodes.Call, methodInfo) - } else if (isValuetypeVirtualMethod) { - mcode.Emit(OpCodes.Constrained, methodInfo.DeclaringType) - mcode.Emit(OpCodes.Callvirt, methodInfo) - } else if (isValuetypeMethod) { - // otherwise error "Callvirt on a value type method" ensues - mcode.Emit(OpCodes.Call, methodInfo) - } else { - mcode.Emit(OpCodes.Callvirt, methodInfo) - } - case Static(_) => - if(methodInfo.IsVirtual && !mcode.Ldarg0WasJustEmitted) { - mcode.Emit(OpCodes.Callvirt, methodInfo) - } else mcode.Emit(OpCodes.Call, methodInfo) - } - } - } - - case BOX(boxType) => - emitBox(mcode, boxType) - - case UNBOX(boxType) => - emitUnbox(mcode, boxType) - - case CIL_UNBOX(boxType) => - mcode.Emit(OpCodes.Unbox, msilType(boxType)) - - case CIL_INITOBJ(valueType) => - mcode.Emit(OpCodes.Initobj, msilType(valueType)) - - case NEW(REFERENCE(cls)) => - // the next instruction must be a DUP, see comment on `var previousWasNEW` - previousWasNEW = true - - // works also for arrays and reference-types - case CREATE_ARRAY(elem, dims) => - // TODO: handle multi dimensional arrays - assert(dims == 1, "Can't handle multi dimensional arrays") - mcode.Emit(OpCodes.Newarr, msilType(elem)) - - // works for arrays and reference-types - case IS_INSTANCE(tpe) => - mcode.Emit(OpCodes.Isinst, msilType(tpe)) - mcode.Emit(OpCodes.Ldnull) - mcode.Emit(OpCodes.Ceq) - mcode.Emit(OpCodes.Ldc_I4_0) - mcode.Emit(OpCodes.Ceq) - - // works for arrays and reference-types - // part from the scala reference: "S <: T does not imply - // Array[S] <: Array[T] in Scala. However, it is possible - // to cast an array of S to an array of T if such a cast - // is permitted in the host environment." - case CHECK_CAST(tpknd) => - val tMSIL = msilType(tpknd) - mcode.Emit(OpCodes.Castclass, tMSIL) - - // no SWITCH is generated when there's - // - a default case ("case _ => ...") in the matching expr - // - OR is used ("case 1 | 2 => ...") - case SWITCH(tags, branches) => - // tags is List[List[Int]]; a list of integers for every label. - // if the int on stack is 4, and 4 is in the second list => jump - // to second label - // branches is List[BasicBlock] - // the labels to jump to (the last one is the default one) - - val switchLocal = mcode.DeclareLocal(MINT) - // several switch variables will appear with the same name in the - // assembly code, but this makes no truble - switchLocal.SetLocalSymInfo("$switch_var") - - mcode.Emit(OpCodes.Stloc, switchLocal) - var i = 0 - for (l <- tags) { - var targetLabel = labels(branches(i)) - for (i <- l) { - mcode.Emit(OpCodes.Ldloc, switchLocal) - loadI4(i, mcode) - mcode.Emit(OpCodes.Beq, targetLabel) - } - i += 1 - } - val defaultTarget = labels(branches(i)) - if (next != branches(i)) - mcode.Emit(OpCodes.Br, defaultTarget) - - case JUMP(whereto) => - val (leaveHandler, leaveFinally, lfTarget) = leavesHandler(block, whereto) - if (leaveHandler) { - if (leaveFinally) { - if (lfTarget.isDefined) mcode.Emit(OpCodes.Leave, lfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(whereto)) - } else if (next != whereto) - mcode.Emit(OpCodes.Br, labels(whereto)) - - case CJUMP(success, failure, cond, kind) => - // cond is TestOp (see Primitives.scala), and can take - // values EQ, NE, LT, GE LE, GT - // kind is TypeKind - val isFloat = kind == FLOAT || kind == DOUBLE - val emit = (c: TestOp, l: Label) => emitBr(c, l, isFloat) - emitCondBr(block, cond, success, failure, next, emit) - - case CZJUMP(success, failure, cond, kind) => - emitCondBr(block, cond, success, failure, next, emitBrBool(_, _)) - - case RETURN(kind) => - if (currentHandlers.isEmpty) - mcode.Emit(OpCodes.Ret) - else { - val (local, label) = returnFromHandler(kind) - if (kind != UNIT) - mcode.Emit(OpCodes.Stloc, local) - mcode.Emit(OpCodes.Leave, label) - } - - case THROW(_) => - mcode.Emit(OpCodes.Throw) - - case DROP(kind) => - mcode.Emit(OpCodes.Pop) - - case DUP(kind) => - // see comment on `var previousWasNEW` - if (!previousWasNEW) - mcode.Emit(OpCodes.Dup) - else - previousWasNEW = false - - case MONITOR_ENTER() => - mcode.Emit(OpCodes.Call, MMONITOR_ENTER) - - case MONITOR_EXIT() => - mcode.Emit(OpCodes.Call, MMONITOR_EXIT) - - case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION(_) => - () - } - - } // end for (instr <- b) { .. } - } // end genBlock - - def genPrimitive(primitive: Primitive, pos: Position) { - primitive match { - case Negation(kind) => - kind match { - // CHECK: is ist possible to get this for BOOL? in this case, verify. - case BOOL | BYTE | CHAR | SHORT | INT | LONG | FLOAT | DOUBLE => - mcode.Emit(OpCodes.Neg) - - case _ => abort("Impossible to negate a " + kind) - } - - case Arithmetic(op, kind) => - op match { - case ADD => mcode.Emit(OpCodes.Add) - case SUB => mcode.Emit(OpCodes.Sub) - case MUL => mcode.Emit(OpCodes.Mul) - case DIV => mcode.Emit(OpCodes.Div) - case REM => mcode.Emit(OpCodes.Rem) - case NOT => mcode.Emit(OpCodes.Not) //bitwise complement (one's complement) - case _ => abort("Unknown arithmetic primitive " + primitive ) - } - - case Logical(op, kind) => op match { - case AND => mcode.Emit(OpCodes.And) - case OR => mcode.Emit(OpCodes.Or) - case XOR => mcode.Emit(OpCodes.Xor) - } - - case Shift(op, kind) => op match { - case LSL => mcode.Emit(OpCodes.Shl) - case ASR => mcode.Emit(OpCodes.Shr) - case LSR => mcode.Emit(OpCodes.Shr_Un) - } - - case Conversion(src, dst) => - debuglog("Converting from: " + src + " to: " + dst) - - dst match { - case BYTE => mcode.Emit(OpCodes.Conv_I1) // I1 for System.SByte, i.e. a scala.Byte - case SHORT => mcode.Emit(OpCodes.Conv_I2) - case CHAR => mcode.Emit(OpCodes.Conv_U2) - case INT => mcode.Emit(OpCodes.Conv_I4) - case LONG => mcode.Emit(OpCodes.Conv_I8) - case FLOAT => mcode.Emit(OpCodes.Conv_R4) - case DOUBLE => mcode.Emit(OpCodes.Conv_R8) - case _ => - Console.println("Illegal conversion at: " + clasz + - " at: " + pos.source + ":" + pos.line) - } - - case ArrayLength(_) => - mcode.Emit(OpCodes.Ldlen) - - case StartConcat => - mcode.Emit(OpCodes.Newobj, MSTRING_BUILDER_CONSTR) - - - case StringConcat(el) => - val elemType : MsilType = el match { - case REFERENCE(_) | ARRAY(_) => MOBJECT - case _ => msilType(el) - } - - val argTypes:Array[MsilType] = Array(elemType) - val stringBuilderAppend = MSTRING_BUILDER.GetMethod("Append", argTypes ) - mcode.Emit(OpCodes.Callvirt, stringBuilderAppend) - - case EndConcat => - mcode.Emit(OpCodes.Callvirt, MSTRING_BUILDER_TOSTRING) - - case _ => - abort("Unimplemented primitive " + primitive) - } - } // end genPrimitive - - - ////////////////////// loading /////////////////////// - - def loadI4(value: Int, code: ILGenerator): Unit = value match { - case -1 => code.Emit(OpCodes.Ldc_I4_M1) - case 0 => code.Emit(OpCodes.Ldc_I4_0) - case 1 => code.Emit(OpCodes.Ldc_I4_1) - case 2 => code.Emit(OpCodes.Ldc_I4_2) - case 3 => code.Emit(OpCodes.Ldc_I4_3) - case 4 => code.Emit(OpCodes.Ldc_I4_4) - case 5 => code.Emit(OpCodes.Ldc_I4_5) - case 6 => code.Emit(OpCodes.Ldc_I4_6) - case 7 => code.Emit(OpCodes.Ldc_I4_7) - case 8 => code.Emit(OpCodes.Ldc_I4_8) - case _ => - if (value >= -128 && value <= 127) - code.Emit(OpCodes.Ldc_I4_S, value) - else - code.Emit(OpCodes.Ldc_I4, value) - } - - def loadArg(code: ILGenerator, loadAddr: Boolean)(i: Int) = - if (loadAddr) { - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldarga_S, i) - else - code.Emit(OpCodes.Ldarga, i) - } else { - i match { - case 0 => code.Emit(OpCodes.Ldarg_0) - case 1 => code.Emit(OpCodes.Ldarg_1) - case 2 => code.Emit(OpCodes.Ldarg_2) - case 3 => code.Emit(OpCodes.Ldarg_3) - case _ => - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldarg_S, i) - else - code.Emit(OpCodes.Ldarg, i) - } - } - - def loadLocal(i: Int, local: Local, code: ILGenerator, loadAddr: Boolean) = - if (loadAddr) { - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldloca_S, localBuilders(local)) - else - code.Emit(OpCodes.Ldloca, localBuilders(local)) - } else { - i match { - case 0 => code.Emit(OpCodes.Ldloc_0) - case 1 => code.Emit(OpCodes.Ldloc_1) - case 2 => code.Emit(OpCodes.Ldloc_2) - case 3 => code.Emit(OpCodes.Ldloc_3) - case _ => - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldloc_S, localBuilders(local)) - else - code.Emit(OpCodes.Ldloc, localBuilders(local)) - } - } - - ////////////////////// branches /////////////////////// - - /** Returns a Triple (Boolean, Boolean, Option[Label]) - * - whether the jump leaves some exception block (try / catch / finally) - * - whether it leaves a finally handler (finally block, but not it's try / catch) - * - a label where to jump for leaving the finally handler - * . None to leave directly using `endfinally` - * . Some(label) to emit `leave label` (for try / catch inside a finally handler) - */ - def leavesHandler(from: BasicBlock, to: BasicBlock): (Boolean, Boolean, Option[Label]) = - if (currentHandlers.isEmpty) (false, false, None) - else { - val h = currentHandlers.head - val leaveHead = { h.covers(from) != h.covers(to) || - h.blocks.contains(from) != h.blocks.contains(to) } - if (leaveHead) { - // we leave the innermost exception block. - // find out if we also leave som e `finally` handler - currentHandlers.find(e => { - e.cls == NoSymbol && e.blocks.contains(from) != e.blocks.contains(to) - }) match { - case Some(finallyHandler) => - if (h == finallyHandler) { - // the finally handler is the innermost, so we can emit `endfinally` directly - (true, true, None) - } else { - // we need to `Leave` to the `endfinally` of the next outer finally handler - val l = endFinallyLabels.getOrElseUpdate(finallyHandler, mcode.DefineLabel()) - (true, true, Some(l)) - } - case None => - (true, false, None) - } - } else (false, false, None) - } - - def emitCondBr(block: BasicBlock, cond: TestOp, success: BasicBlock, failure: BasicBlock, - next: BasicBlock, emitBrFun: (TestOp, Label) => Unit) { - val (sLeaveHandler, sLeaveFinally, slfTarget) = leavesHandler(block, success) - val (fLeaveHandler, fLeaveFinally, flfTarget) = leavesHandler(block, failure) - - if (sLeaveHandler || fLeaveHandler) { - val sLabelOpt = if (sLeaveHandler) { - val leaveSLabel = mcode.DefineLabel() - emitBrFun(cond, leaveSLabel) - Some(leaveSLabel) - } else { - emitBrFun(cond, labels(success)) - None - } - - if (fLeaveHandler) { - if (fLeaveFinally) { - if (flfTarget.isDefined) mcode.Emit(OpCodes.Leave, flfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(failure)) - } else - mcode.Emit(OpCodes.Br, labels(failure)) - - sLabelOpt.map(l => { - mcode.MarkLabel(l) - if (sLeaveFinally) { - if (slfTarget.isDefined) mcode.Emit(OpCodes.Leave, slfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(success)) - }) - } else { - if (next == success) { - emitBrFun(cond.negate, labels(failure)) - } else { - emitBrFun(cond, labels(success)) - if (next != failure) { - mcode.Emit(OpCodes.Br, labels(failure)) - } - } - } - } - - def emitBr(condition: TestOp, dest: Label, isFloat: Boolean) { - condition match { - case EQ => mcode.Emit(OpCodes.Beq, dest) - case NE => mcode.Emit(OpCodes.Bne_Un, dest) - case LT => mcode.Emit(if (isFloat) OpCodes.Blt_Un else OpCodes.Blt, dest) - case GE => mcode.Emit(if (isFloat) OpCodes.Bge_Un else OpCodes.Bge, dest) - case LE => mcode.Emit(if (isFloat) OpCodes.Ble_Un else OpCodes.Ble, dest) - case GT => mcode.Emit(if (isFloat) OpCodes.Bgt_Un else OpCodes.Bgt, dest) - } - } - - def emitBrBool(cond: TestOp, dest: Label) { - (cond: @unchecked) match { - // EQ -> Brfalse, NE -> Brtrue; this is because we come from - // a CZJUMP. If the value on the stack is 0 (e.g. a boolean - // method returned false), and we are in the case EQ, then - // we need to emit Brfalse (EQ Zero means false). vice versa - case EQ => mcode.Emit(OpCodes.Brfalse, dest) - case NE => mcode.Emit(OpCodes.Brtrue, dest) - } - } - - ////////////////////// local vars /////////////////////// - - /** - * Compute the indexes of each local variable of the given - * method. - */ - def computeLocalVarsIndex(m: IMethod) { - var idx = if (m.symbol.isStaticMember) 0 else 1 - - val params = m.params - for (l <- params) { - debuglog("Index value for parameter " + l + ": " + idx) - l.index = idx - idx += 1 // sizeOf(l.kind) - } - - val locvars = m.locals filterNot (params contains) - idx = 0 - - for (l <- locvars) { - debuglog("Index value for local variable " + l + ": " + idx) - l.index = idx - idx += 1 // sizeOf(l.kind) - } - - } - - ////////////////////// Utilities //////////////////////// - - /** Return the a name of this symbol that can be used on the .NET - * platform. It removes spaces from names. - * - * Special handling: scala.All and scala.AllRef are 'erased' to - * scala.All$ and scala.AllRef$. This is needed because they are - * not real classes, and they mean 'abrupt termination upon evaluation - * of that expression' or 'null' respectively. This handling is - * done already in GenICode, but here we need to remove references - * from method signatures to these types, because such classes can - * not exist in the classpath: the type checker will be very confused. - */ - def msilName(sym: Symbol): String = { - val suffix = sym.moduleSuffix - // Flags.JAVA: "symbol was not defined by a scala-class" (java, or .net-class) - - if (sym == definitions.NothingClass) - return "scala.runtime.Nothing$" - else if (sym == definitions.NullClass) - return "scala.runtime.Null$" - - (if (sym.isClass || (sym.isModule && !sym.isMethod)) { - if (sym.isNestedClass) sym.simpleName - else sym.fullName - } else - sym.simpleName.toString.trim()) + suffix - } - - - ////////////////////// flags /////////////////////// - - def msilTypeFlags(sym: Symbol): Int = { - var mf: Int = TypeAttributes.AutoLayout | TypeAttributes.AnsiClass - - if(sym.isNestedClass) { - mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NestedPrivate else TypeAttributes.NestedPublic) - } else { - mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NotPublic else TypeAttributes.Public) - } - mf = mf | (if (sym hasFlag Flags.ABSTRACT) TypeAttributes.Abstract else 0) - mf = mf | (if (sym.isTrait && !sym.isImplClass) TypeAttributes.Interface else TypeAttributes.Class) - mf = mf | (if (sym isFinal) TypeAttributes.Sealed else 0) - - sym.annotations foreach { a => a match { - case AnnotationInfo(SerializableAttr, _, _) => - // TODO: add the Serializable TypeAttribute also if the annotation - // System.SerializableAttribute is present (.net annotation, not scala) - // Best way to do it: compare with - // definitions.getClass("System.SerializableAttribute").tpe - // when frontend available - mf = mf | TypeAttributes.Serializable - case _ => () - }} - - mf - // static: not possible (or?) - } - - def msilMethodFlags(sym: Symbol): Short = { - var mf: Int = MethodAttributes.HideBySig | - (if (sym hasFlag Flags.PRIVATE) MethodAttributes.Private - else MethodAttributes.Public) - - if (!sym.isClassConstructor) { - if (sym.isStaticMember) - mf = mf | FieldAttributes.Static // coincidentally, same value as for MethodAttributes.Static ... - else { - mf = mf | MethodAttributes.Virtual - if (sym.isFinal && !getType(sym.owner).IsInterface) - mf = mf | MethodAttributes.Final - if (sym.isDeferred || getType(sym.owner).IsInterface) - mf = mf | MethodAttributes.Abstract - } - } - - if (sym.isStaticMember) { - mf = mf | MethodAttributes.Static - } - - // constructors of module classes should be private - if (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) { - mf |= MethodAttributes.Private - mf &= ~(MethodAttributes.Public) - } - - mf.toShort - } - - def msilFieldFlags(sym: Symbol): Short = { - var mf: Int = - if (sym hasFlag Flags.PRIVATE) FieldAttributes.Private - else if (sym hasFlag Flags.PROTECTED) FieldAttributes.FamORAssem - else FieldAttributes.Public - - if (sym hasFlag Flags.FINAL) - mf = mf | FieldAttributes.InitOnly - - if (sym.isStaticMember) - mf = mf | FieldAttributes.Static - - // TRANSIENT: "not serialized", VOLATILE: doesn't exist on .net - // TODO: add this annotation also if the class has the custom attribute - // System.NotSerializedAttribute - sym.annotations.foreach( a => a match { - case AnnotationInfo(TransientAtt, _, _) => - mf = mf | FieldAttributes.NotSerialized - case _ => () - }) - - mf.toShort - } - - ////////////////////// builders, types /////////////////////// - - var entryPoint: Symbol = _ - - val notInitializedModules = mutable.HashSet[Symbol]() - - // TODO: create fields also in def createType, and not in genClass, - // add a getField method (it only works as it is because fields never - // accessed from outside a class) - - val localBuilders = mutable.HashMap[Local, LocalBuilder]() - - private[GenMSIL] def findEntryPoint(cls: IClass) { - - def isEntryPoint(sym: Symbol):Boolean = { - if (isStaticModule(sym.owner) && msilName(sym) == "main") - if (sym.tpe.paramTypes.length == 1) { - toTypeKind(sym.tpe.paramTypes(0)) match { - case ARRAY(elem) => - if (elem.toType.typeSymbol == definitions.StringClass) { - return true - } - case _ => () - } - } - false - } - - if((entryPoint == null) && opt.showClass.isDefined) { // TODO introduce dedicated setting instead - val entryclass = opt.showClass.get.toString - val cfn = cls.symbol.fullName - if(cfn == entryclass) { - for (m <- cls.methods; if isEntryPoint(m.symbol)) { entryPoint = m.symbol } - if(entryPoint == null) { warning("Couldn't find main method in class " + cfn) } - } - } - - if (firstSourceName == "") - if (cls.symbol.sourceFile != null) // is null for nested classes - firstSourceName = cls.symbol.sourceFile.name - } - - // ##################################################################### - // get and create types - - private def msilType(t: TypeKind): MsilType = (t: @unchecked) match { - case UNIT => MVOID - case BOOL => MBOOL - case BYTE => MBYTE - case SHORT => MSHORT - case CHAR => MCHAR - case INT => MINT - case LONG => MLONG - case FLOAT => MFLOAT - case DOUBLE => MDOUBLE - case REFERENCE(cls) => getType(cls) - case ARRAY(elem) => - msilType(elem) match { - // For type builders, cannot call "clrTypes.mkArrayType" because this looks up - // the type "tp" in the assembly (not in the HashMap "types" of the backend). - // This can fail for nested types because the builders are not complete yet. - case tb: TypeBuilder => tb.MakeArrayType() - case tp: MsilType => clrTypes.mkArrayType(tp) - } - } - - private def msilType(tpe: Type): MsilType = msilType(toTypeKind(tpe)) - - private def msilParamTypes(sym: Symbol): Array[MsilType] = { - sym.tpe.paramTypes.map(msilType).toArray - } - - def getType(sym: Symbol) = getTypeOpt(sym).getOrElse(abort(showsym(sym))) - - /** - * Get an MSIL type from a symbol. First look in the clrTypes.types map, then - * lookup the name using clrTypes.getType - */ - def getTypeOpt(sym: Symbol): Option[MsilType] = { - val tmp = types.get(sym) - tmp match { - case typ @ Some(_) => typ - case None => - def typeString(sym: Symbol): String = { - val s = if (sym.isNestedClass) typeString(sym.owner) +"+"+ sym.simpleName - else sym.fullName - if (sym.isModuleClass && !sym.isTrait) s + "$" else s - } - val name = typeString(sym) - val typ = clrTypes.getType(name) - if (typ == null) - None - else { - types(sym) = typ - Some(typ) - } - } - } - - def mapType(sym: Symbol, mType: MsilType) { - assert(mType != null, showsym(sym)) - types(sym) = mType - } - - def createTypeBuilder(iclass: IClass) { - /** - * First look in the clrTypes.types map, if that fails check if it's a class being compiled, otherwise - * lookup by name (clrTypes.getType calls the static method msil.Type.GetType(fullname)). - */ - def msilTypeFromSym(sym: Symbol): MsilType = { - types.get(sym).getOrElse { - classes.get(sym) match { - case Some(iclass) => - msilTypeBuilderFromSym(sym) - case None => - getType(sym) - } - } - } - - def msilTypeBuilderFromSym(sym: Symbol): TypeBuilder = { - if(!(types.contains(sym) && types(sym).isInstanceOf[TypeBuilder])){ - val iclass = classes(sym) - assert(iclass != null) - createTypeBuilder(iclass) - } - types(sym).asInstanceOf[TypeBuilder] - } - - val sym = iclass.symbol - if (types.contains(sym) && types(sym).isInstanceOf[TypeBuilder]) - return - - def isInterface(s: Symbol) = s.isTrait && !s.isImplClass - val parents: List[Type] = - if (sym.info.parents.isEmpty) List(definitions.ObjectClass.tpe) - else sym.info.parents.distinct - - val superType : MsilType = if (isInterface(sym)) null else msilTypeFromSym(parents.head.typeSymbol) - debuglog("super type: " + parents(0).typeSymbol + ", msil type: " + superType) - - val interfaces: Array[MsilType] = - parents.tail.map(p => msilTypeFromSym(p.typeSymbol)).toArray - if (parents.length > 1) { - if (settings.debug.value) { - log("interfaces:") - for (i <- 0.until(interfaces.length)) { - log(" type: " + parents(i + 1).typeSymbol + ", msil type: " + interfaces(i)) - } - } - } - - val tBuilder = if (sym.isNestedClass) { - val ownerT = msilTypeBuilderFromSym(sym.owner).asInstanceOf[TypeBuilder] - ownerT.DefineNestedType(msilName(sym), msilTypeFlags(sym), superType, interfaces) - } else { - mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces) - } - mapType(sym, tBuilder) - } // createTypeBuilder - - def createClassMembers(iclass: IClass) { - try { - createClassMembers0(iclass) - } - catch { - case e: Throwable => - java.lang.System.err.println(showsym(iclass.symbol)) - java.lang.System.err.println("with methods = " + iclass.methods) - throw e - } - } - - def createClassMembers0(iclass: IClass) { - - val mtype = getType(iclass.symbol).asInstanceOf[TypeBuilder] - - for (ifield <- iclass.fields) { - val sym = ifield.symbol - debuglog("Adding field: " + sym.fullName) - - var attributes = msilFieldFlags(sym) - val fieldTypeWithCustomMods = - new PECustomMod(msilType(sym.tpe), - customModifiers(sym.annotations)) - val fBuilder = mtype.DefineField(msilName(sym), - fieldTypeWithCustomMods, - attributes) - fields(sym) = fBuilder - addAttributes(fBuilder, sym.annotations) - } // all iclass.fields iterated over - - if (isStaticModule(iclass.symbol)) { - val sc = iclass.lookupStaticCtor - if (sc.isDefined) { - val m = sc.get - val oldLastBlock = m.lastBlock - val lastBlock = m.newBlock() - oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) - // call object's private ctor from static ctor - lastBlock.emit(CIL_NEWOBJ(iclass.symbol.primaryConstructor)) - lastBlock.emit(DROP(toTypeKind(iclass.symbol.tpe))) - lastBlock emit RETURN(UNIT) - lastBlock.close - } - } - - if (iclass.symbol != definitions.ArrayClass) { - for (m: IMethod <- iclass.methods) { - val sym = m.symbol - debuglog("Creating MethodBuilder for " + Flags.flagsToString(sym.flags) + " " + - sym.owner.fullName + "::" + sym.name) - - val ownerType = getType(sym.enclClass).asInstanceOf[TypeBuilder] - assert(mtype == ownerType, "mtype = " + mtype + "; ownerType = " + ownerType) - var paramTypes = msilParamTypes(sym) - val attr = msilMethodFlags(sym) - - if (m.symbol.isClassConstructor) { - val constr = - ownerType.DefineConstructor(attr, CallingConventions.Standard, paramTypes) - for (i <- 0.until(paramTypes.length)) { - constr.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) - } - mapConstructor(sym, constr) - addAttributes(constr, sym.annotations) - } else { - var resType = msilType(m.returnType) - val method = - ownerType.DefineMethod(msilName(sym), attr, resType, paramTypes) - for (i <- 0.until(paramTypes.length)) { - method.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) - } - if (!methods.contains(sym)) - mapMethod(sym, method) - addAttributes(method, sym.annotations) - debuglog("\t created MethodBuilder " + method) - } - } - } // method builders created for non-array iclass - - if (isStaticModule(iclass.symbol)) { - addModuleInstanceField(iclass.symbol) - notInitializedModules += iclass.symbol - if (iclass.lookupStaticCtor.isEmpty) { - addStaticInit(iclass.symbol) - } - } - - } // createClassMembers0 - - private def isTopLevelModule(sym: Symbol): Boolean = - beforeRefchecks { - sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass - } - - // if the module is lifted it does not need to be initialized in - // its static constructor, and the MODULE$ field is not required. - // the outer class will care about it. - private def isStaticModule(sym: Symbol): Boolean = { - // .net inner classes: removed '!sym.hasFlag(Flags.LIFTED)', added - // 'sym.isStatic'. -> no longer compatible without skipping flatten! - sym.isModuleClass && sym.isStatic && !sym.isImplClass - } - - private def isCloneable(sym: Symbol): Boolean = { - !sym.annotations.forall( a => a match { - case AnnotationInfo(CloneableAttr, _, _) => false - case _ => true - }) - } - - private def addModuleInstanceField(sym: Symbol) { - debuglog("Adding Module-Instance Field for " + showsym(sym)) - val tBuilder = getType(sym).asInstanceOf[TypeBuilder] - val fb = tBuilder.DefineField(MODULE_INSTANCE_NAME, - tBuilder, - (FieldAttributes.Public | - //FieldAttributes.InitOnly | - FieldAttributes.Static).toShort) - fields(sym) = fb - } - - - // the symbol may be a object-symbol (module-symbol), or a module-class-symbol - private def getModuleInstanceField(sym: Symbol): FieldInfo = { - assert(sym.isModule || sym.isModuleClass, "Expected module: " + showsym(sym)) - - // when called by LOAD_MODULE, the corresponding type maybe doesn't - // exist yet -> make a getType - val moduleClassSym = if (sym.isModule) sym.moduleClass else sym - - // TODO: get module field for modules not defined in the - // source currently compiling (e.g. Console) - - fields get moduleClassSym match { - case Some(sym) => sym - case None => - //val mclass = types(moduleClassSym) - val nameInMetadata = nestingAwareFullClassname(moduleClassSym) - val mClass = clrTypes.getType(nameInMetadata) - val mfield = mClass.GetField("MODULE$") - assert(mfield ne null, "module not found " + showsym(moduleClassSym)) - fields(moduleClassSym) = mfield - mfield - } - - //fields(moduleClassSym) - } - - def nestingAwareFullClassname(csym: Symbol) : String = { - val suffix = csym.moduleSuffix - val res = if (csym.isNestedClass) - nestingAwareFullClassname(csym.owner) + "+" + csym.encodedName - else - csym.fullName - res + suffix - } - - /** Adds a static initializer which creates an instance of the module - * class (calls the primary constructor). A special primary constructor - * will be generated (notInitializedModules) which stores the new instance - * in the MODULE$ field right after the super call. - */ - private def addStaticInit(sym: Symbol) { - val tBuilder = getType(sym).asInstanceOf[TypeBuilder] - - val staticInit = tBuilder.DefineConstructor( - (MethodAttributes.Static | MethodAttributes.Public).toShort, - CallingConventions.Standard, - MsilType.EmptyTypes) - - val sicode = staticInit.GetILGenerator() - - val instanceConstructor = constructors(sym.primaryConstructor) - - // there are no constructor parameters. assuming the constructor takes no parameter - // is fine: we call (in the static constructor) the constructor of the module class, - // which takes no arguments - an object definition cannot take constructor arguments. - sicode.Emit(OpCodes.Newobj, instanceConstructor) - // the stsfld is done in the instance constructor, just after the super call. - sicode.Emit(OpCodes.Pop) - - sicode.Emit(OpCodes.Ret) - } - - private def generateMirrorClass(sym: Symbol) { - val tBuilder = getType(sym) - assert(sym.isModuleClass, "Can't generate Mirror-Class for the Non-Module class " + sym) - debuglog("Dumping mirror class for object: " + sym) - val moduleName = msilName(sym) - val mirrorName = moduleName.substring(0, moduleName.length() - 1) - val mirrorTypeBuilder = mmodule.DefineType(mirrorName, - TypeAttributes.Class | - TypeAttributes.Public | - TypeAttributes.Sealed, - MOBJECT, - MsilType.EmptyTypes) - - val iclass = classes(sym) - - for (m <- sym.tpe.nonPrivateMembers - if m.owner != definitions.ObjectClass && !m.isProtected && - m.isMethod && !m.isClassConstructor && !m.isStaticMember && !m.isCase && - !m.isDeferred) - { - debuglog(" Mirroring method: " + m) - val paramTypes = msilParamTypes(m) - val paramNames: Array[String] = new Array[String](paramTypes.length) - for (i <- 0 until paramTypes.length) - paramNames(i) = "x_" + i - - // CHECK: verify if getMethodName is better than msilName - val mirrorMethod = mirrorTypeBuilder.DefineMethod(msilName(m), - (MethodAttributes.Public | - MethodAttributes.Static).toShort, - msilType(m.tpe.resultType), - paramTypes) - - var i = 0 - while (i < paramTypes.length) { - mirrorMethod.DefineParameter(i, ParameterAttributes.None, paramNames(i)) - i += 1 - } - - val mirrorCode = mirrorMethod.GetILGenerator() - mirrorCode.Emit(OpCodes.Ldsfld, getModuleInstanceField(sym)) - val mInfo = getMethod(m) - for (paramidx <- 0.until(paramTypes.length)) { - val mInfoParams = mInfo.GetParameters - val loadAddr = mInfoParams(paramidx).ParameterType.IsByRef - loadArg(mirrorCode, loadAddr)(paramidx) - } - - mirrorCode.Emit(OpCodes.Callvirt, getMethod(m)) - mirrorCode.Emit(OpCodes.Ret) - } - - addSymtabAttribute(sym.sourceModule, mirrorTypeBuilder) - - mirrorTypeBuilder.CreateType() - mirrorTypeBuilder.setSourceFilepath(iclass.cunit.source.file.path) - } - - - // ##################################################################### - // delegate callers - - var delegateCallers: TypeBuilder = _ - var nbDelegateCallers: Int = 0 - - private def initDelegateCallers() = { - delegateCallers = mmodule.DefineType("$DelegateCallers", TypeAttributes.Public | - TypeAttributes.Sealed) - } - - private def createDelegateCaller(functionType: Type, delegateType: Type) = { - if (delegateCallers == null) - initDelegateCallers() - // create a field an store the function-object - val mFunctionType: MsilType = msilType(functionType) - val anonfunField: FieldBuilder = delegateCallers.DefineField( - "$anonfunField$$" + nbDelegateCallers, mFunctionType, - (FieldAttributes.InitOnly | FieldAttributes.Public | FieldAttributes.Static).toShort) - mcode.Emit(OpCodes.Stsfld, anonfunField) - - - // create the static caller method and the delegate object - val (params, returnType) = delegateType.member(nme.apply).tpe match { - case MethodType(delParams, delReturn) => (delParams, delReturn) - case _ => abort("not a delegate type: " + delegateType) - } - val caller: MethodBuilder = delegateCallers.DefineMethod( - "$delegateCaller$$" + nbDelegateCallers, - (MethodAttributes.Final | MethodAttributes.Public | MethodAttributes.Static).toShort, - msilType(returnType), (params map (_.tpe)).map(msilType).toArray) - for (i <- 0 until params.length) - caller.DefineParameter(i, ParameterAttributes.None, "arg" + i) // FIXME: use name of parameter symbol - val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR)) - mcode.Emit(OpCodes.Ldnull) - mcode.Emit(OpCodes.Ldftn, caller) - mcode.Emit(OpCodes.Newobj, delegCtor) - - - // create the static caller method body - val functionApply: MethodInfo = getMethod(functionType.member(nme.apply)) - val dcode: ILGenerator = caller.GetILGenerator() - dcode.Emit(OpCodes.Ldsfld, anonfunField) - for (i <- 0 until params.length) { - loadArg(dcode, false /* TODO confirm whether passing actual as-is to formal is correct wrt the ByRef attribute of the param */)(i) - emitBox(dcode, toTypeKind(params(i).tpe)) - } - dcode.Emit(OpCodes.Callvirt, functionApply) - emitUnbox(dcode, toTypeKind(returnType)) - dcode.Emit(OpCodes.Ret) - - nbDelegateCallers = nbDelegateCallers + 1 - - } //def createDelegateCaller - - def emitBox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match { - // doesn't make sense, unit as parameter.. - case UNIT => code.Emit(OpCodes.Ldsfld, boxedUnit) - case BOOL | BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE => - code.Emit(OpCodes.Box, msilType(boxType)) - case REFERENCE(cls) if clrTypes.isValueType(cls) => - code.Emit(OpCodes.Box, (msilType(boxType))) - case REFERENCE(_) | ARRAY(_) => - warning("Tried to BOX a non-valuetype.") - () - } - - def emitUnbox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match { - case UNIT => code.Emit(OpCodes.Pop) - /* (1) it's essential to keep the code emitted here (as of now plain calls to System.Convert.ToBlaBla methods) - behaviorally.equiv.wrt. BoxesRunTime.unboxToBlaBla methods - (case null: that's easy, case boxed: track changes to unboxBlaBla) - (2) See also: asInstanceOf to cast from Any to number, - tracked in http://lampsvn.epfl.ch/trac/scala/ticket/4437 */ - case BOOL => code.Emit(OpCodes.Call, toBool) - case BYTE => code.Emit(OpCodes.Call, toSByte) - case SHORT => code.Emit(OpCodes.Call, toShort) - case CHAR => code.Emit(OpCodes.Call, toChar) - case INT => code.Emit(OpCodes.Call, toInt) - case LONG => code.Emit(OpCodes.Call, toLong) - case FLOAT => code.Emit(OpCodes.Call, toFloat) - case DOUBLE => code.Emit(OpCodes.Call, toDouble) - case REFERENCE(cls) if clrTypes.isValueType(cls) => - code.Emit(OpCodes.Unbox, msilType(boxType)) - code.Emit(OpCodes.Ldobj, msilType(boxType)) - case REFERENCE(_) | ARRAY(_) => - warning("Tried to UNBOX a non-valuetype.") - () - } - - // ##################################################################### - // get and create methods / constructors - - def getConstructor(sym: Symbol): ConstructorInfo = constructors.get(sym) match { - case Some(constr) => constr - case None => - val mClass = getType(sym.owner) - val constr = mClass.GetConstructor(msilParamTypes(sym)) - if (constr eq null) { - java.lang.System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name) - java.lang.System.out.println("scope = " + sym.owner.tpe.decls) - abort(sym.fullName) - } - else { - mapConstructor(sym, constr) - constr - } - } - - def mapConstructor(sym: Symbol, cInfo: ConstructorInfo) = { - constructors(sym) = cInfo - } - - private def getMethod(sym: Symbol): MethodInfo = { - - methods.get(sym) match { - case Some(method) => method - case None => - val mClass = getType(sym.owner) - try { - val method = mClass.GetMethod(msilName(sym), msilParamTypes(sym), - msilType(sym.tpe.resultType)) - if (method eq null) { - java.lang.System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym)) - java.lang.System.out.println("scope = " + sym.owner.tpe.decls) - abort(sym.fullName) - } - else { - mapMethod(sym, method) - method - } - } - catch { - case e: Exception => - Console.println("While looking up " + mClass + "::" + sym.nameString) - Console.println("\t" + showsym(sym)) - throw e - } - } - } - - /* - * add a mapping between sym and mInfo - */ - private def mapMethod(sym: Symbol, mInfo: MethodInfo) { - assert (mInfo != null, mInfo) - methods(sym) = mInfo - } - - /* - * add mapping between sym and method with newName, paramTypes of newClass - */ - private def mapMethod(sym: Symbol, newClass: MsilType, newName: String, paramTypes: Array[MsilType]) { - val methodInfo = newClass.GetMethod(newName, paramTypes) - assert(methodInfo != null, "Can't find mapping for " + sym + " -> " + - newName + "(" + paramTypes + ")") - mapMethod(sym, methodInfo) - if (methodInfo.IsStatic) - dynToStatMapped += sym - } - - /* - * add mapping between method with name and paramTypes of clazz to - * method with newName and newParamTypes of newClass (used for instance - * for "wait") - */ - private def mapMethod( - clazz: Symbol, name: Name, paramTypes: Array[Type], - newClass: MsilType, newName: String, newParamTypes: Array[MsilType]) { - val methodSym = lookupMethod(clazz, name, paramTypes) - assert(methodSym != null, "cannot find method " + name + "(" + - paramTypes + ")" + " in class " + clazz) - mapMethod(methodSym, newClass, newName, newParamTypes) - } - - /* - * add mapping for member with name and paramTypes to member - * newName of newClass (same parameters) - */ - private def mapMethod( - clazz: Symbol, name: Name, paramTypes: Array[Type], - newClass: MsilType, newName: String) { - mapMethod(clazz, name, paramTypes, newClass, newName, paramTypes map msilType) - } - - /* - * add mapping for all methods with name of clazz to the corresponding - * method (same parameters) with newName of newClass - */ - private def mapMethod( - clazz: Symbol, name: Name, - newClass: MsilType, newName: String) { - val memberSym: Symbol = clazz.tpe.member(name) - memberSym.tpe match { - // alternatives: List[Symbol] - case OverloadedType(_, alternatives) => - alternatives.foreach(s => mapMethod(s, newClass, newName, msilParamTypes(s))) - - // paramTypes: List[Type], resType: Type - case MethodType(params, resType) => - mapMethod(memberSym, newClass, newName, msilParamTypes(memberSym)) - - case _ => - abort("member not found: " + clazz + ", " + name) - } - } - - - /* - * find the method in clazz with name and paramTypes - */ - private def lookupMethod(clazz: Symbol, name: Name, paramTypes: Array[Type]): Symbol = { - val memberSym = clazz.tpe.member(name) - memberSym.tpe match { - case OverloadedType(_, alternatives) => - alternatives.find(s => { - var i: Int = 0 - var typesOK: Boolean = true - if (paramTypes.length == s.tpe.paramTypes.length) { - while(i < paramTypes.length) { - if (paramTypes(i) != s.tpe.paramTypes(i)) - typesOK = false - i += 1 - } - } else { - typesOK = false - } - typesOK - }) match { - case Some(sym) => sym - case None => abort("member of " + clazz + ", " + name + "(" + - paramTypes + ") not found") - } - - case MethodType(_, _) => memberSym - - case _ => abort("member not found: " + name + " of " + clazz) - } - } - - private def showsym(sym: Symbol): String = (sym.toString + - "\n symbol = " + Flags.flagsToString(sym.flags) + " " + sym + - "\n owner = " + Flags.flagsToString(sym.owner.flags) + " " + sym.owner - ) - - } // class BytecodeGenerator - -} // class GenMSIL diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index 23f932b5b4..5dd20a6919 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package backend.opt import scala.tools.nsc.backend.icode.analysis.LubException -import scala.tools.nsc.symtab._ /** * @author Iulian Dragos @@ -120,7 +119,7 @@ abstract class ClosureElimination extends SubComponent { case LOAD_FIELD(f, false) /* if accessible(f, m.symbol) */ => def replaceFieldAccess(r: Record) { - val Record(cls, bindings) = r + val Record(cls, _) = r info.getFieldNonRecordValue(r, f) foreach { v => bb.replaceInstruction(i, DROP(REFERENCE(cls)) :: valueToInstruction(v) :: Nil) debuglog(s"replaced $i with $v") @@ -188,25 +187,17 @@ abstract class ClosureElimination extends SubComponent { case Boxed(LocalVar(v)) => LOAD_LOCAL(v) } - - /** is field 'f' accessible from method 'm'? */ - def accessible(f: Symbol, m: Symbol): Boolean = - f.isPublic || (f.isProtected && (f.enclosingPackageClass == m.enclosingPackageClass)) } /* class ClosureElim */ /** Peephole optimization. */ abstract class PeepholeOpt { - - private var method: IMethod = NoIMethod - /** Concrete implementations will perform their optimizations here */ def peep(bb: BasicBlock, i1: Instruction, i2: Instruction): Option[List[Instruction]] var liveness: global.icodes.liveness.LivenessAnalysis = null def apply(m: IMethod): Unit = if (m.hasCode) { - method = m liveness = new global.icodes.liveness.LivenessAnalysis liveness.init(m) liveness.run diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 1beed3f420..20e1cd2188 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -8,7 +8,6 @@ package scala.tools.nsc package backend.opt import scala.collection.{ mutable, immutable } -import symtab._ /** */ @@ -182,6 +181,8 @@ abstract class DeadCodeElimination extends SubComponent { } } moveToWorkListIf(necessary) + case LOAD_MODULE(sym) if isLoadNeeded(sym) => + moveToWorkList() // SI-4859 Module initialization might side-effect. case _ => () moveToWorkListIf(false) } @@ -412,13 +413,6 @@ abstract class DeadCodeElimination extends SubComponent { compensations } - private def withClosed[a](bb: BasicBlock)(f: => a): a = { - if (bb.nonEmpty) bb.close - val res = f - if (bb.nonEmpty) bb.open - res - } - private def findInstruction(bb: BasicBlock, i: Instruction): InstrLoc = { for (b <- linearizer.linearizeAt(method, bb)) { val idx = b.toList indexWhere (_ eq i) diff --git a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala index ab238af239..4e65c72b0b 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala @@ -4,7 +4,6 @@ package scala.tools.nsc package backend.opt -import scala.util.control.Breaks._ /** * This optimization phase inlines the exception handlers so that further phases can optimize the code better @@ -53,7 +52,7 @@ abstract class InlineExceptionHandlers extends SubComponent { import icodes._ import icodes.opcodes._ - val phaseName = "inlineExceptionHandlers" + val phaseName = "inlinehandlers" /** Create a new phase */ override def newPhase(p: Phase) = new InlineExceptionHandlersPhase(p) diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 521b6cc132..ca1cfc8929 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -50,6 +50,7 @@ abstract class Inliners extends SubComponent { val phaseName = "inliner" /** Debug - for timing the inliner. */ + /**** private def timed[T](s: String, body: => T): T = { val t1 = System.currentTimeMillis() val res = body @@ -60,6 +61,7 @@ abstract class Inliners extends SubComponent { res } + ****/ /** Look up implementation of method 'sym in 'clazz'. */ @@ -193,7 +195,7 @@ abstract class Inliners extends SubComponent { private var currentIClazz: IClass = _ private def warn(pos: Position, msg: String) = currentIClazz.cunit.inlinerWarning(pos, msg) - private def ownedName(sym: Symbol): String = afterUncurry { + private def ownedName(sym: Symbol): String = exitingUncurry { val count = ( if (!sym.isMethod) 1 else if (sym.owner.isAnonymousFunction) 3 @@ -279,7 +281,7 @@ abstract class Inliners extends SubComponent { } val tfa = new analysis.MTFAGrowable() - tfa.stat = global.opt.printStats + tfa.stat = global.settings.Ystatistics.value val staleOut = new mutable.ListBuffer[BasicBlock] val splicedBlocks = mutable.Set.empty[BasicBlock] val staleIn = mutable.Set.empty[BasicBlock] @@ -320,8 +322,8 @@ abstract class Inliners extends SubComponent { if (settings.debug.value) inlineLog("caller", ownedName(m.symbol), "in " + m.symbol.owner.fullName) - var sizeBeforeInlining = m.code.blockCount - var instrBeforeInlining = m.code.instructionCount + val sizeBeforeInlining = m.code.blockCount + val instrBeforeInlining = m.code.instructionCount var retry = false var count = 0 @@ -477,7 +479,7 @@ abstract class Inliners extends SubComponent { * As a whole, both `preInline()` invocations amount to priming the inlining process, * so that the first TFA that is run afterwards is able to gain more information as compared to a cold-start. */ - val totalPreInlines = { + /*val totalPreInlines = */ { // Val name commented out to emphasize it is never used val firstRound = preInline(true) if(firstRound == 0) 0 else (firstRound + preInline(false)) } @@ -569,7 +571,6 @@ abstract class Inliners extends SubComponent { m.normalize if (sizeBeforeInlining > 0) { val instrAfterInlining = m.code.instructionCount - val prefix = if ((instrAfterInlining > 2 * instrBeforeInlining) && (instrAfterInlining > 200)) "!!" else "" val inlinings = caller.inlinedCalls if (inlinings > 0) { val s1 = s"instructions $instrBeforeInlining -> $instrAfterInlining" @@ -584,7 +585,7 @@ abstract class Inliners extends SubComponent { private def isHigherOrderMethod(sym: Symbol) = ( sym.isMethod - && beforeExplicitOuter(sym.info.paramTypes exists isFunctionType) // was "at erasurePhase.prev" + && enteringExplicitOuter(sym.info.paramTypes exists isFunctionType) // was "at erasurePhase.prev" ) /** Should method 'sym' being called in 'receiver' be loaded from disk? */ @@ -601,7 +602,6 @@ abstract class Inliners extends SubComponent { override def toString = m.toString val sym = m.symbol - val name = sym.name def owner = sym.owner def paramTypes = sym.info.paramTypes def minimumStack = paramTypes.length + 1 @@ -617,13 +617,11 @@ abstract class Inliners extends SubComponent { def length = blocks.length def openBlocks = blocks filterNot (_.closed) def instructions = m.code.instructions - // def linearized = linearizer linearize m def isSmall = (length <= SMALL_METHOD_SIZE) && blocks(0).length < 10 def isLarge = length > MAX_INLINE_SIZE def isRecursive = m.recursive def hasHandlers = handlers.nonEmpty || m.bytecodeHasEHs - def hasClosureParam = paramTypes exists (tp => isByNameParamType(tp) || isFunctionType(tp)) def isSynchronized = sym.hasFlag(Flags.SYNCHRONIZED) def hasNonFinalizerHandler = handlers exists { @@ -731,7 +729,6 @@ abstract class Inliners extends SubComponent { */ sealed abstract class InlineSafetyInfo { def isSafe = false - def isUnsafe = !isSafe } case object NeverSafeToInline extends InlineSafetyInfo case object InlineableAtThisCaller extends InlineSafetyInfo { override def isSafe = true } @@ -1031,7 +1028,6 @@ abstract class Inliners extends SubComponent { case Public => true } private def sameSymbols = caller.sym == inc.sym - private def sameOwner = caller.owner == inc.owner /** Gives green light for inlining (which may still be vetoed later). Heuristics: * - it's bad to make the caller larger (> SMALL_METHOD_SIZE) if it was small @@ -1058,8 +1054,8 @@ abstract class Inliners extends SubComponent { if (inc.isMonadic) score += 3 else if (inc.isHigherOrder) score += 1 - if (inc.isInClosure) score += 2 - if (inlinedMethodCount(inc.sym) > 2) score -= 2 + if (inc.isInClosure) score += 2; + if (inlinedMethodCount(inc.sym) > 2) score -= 2; score } } diff --git a/src/compiler/scala/tools/nsc/dependencies/Changes.scala b/src/compiler/scala/tools/nsc/dependencies/Changes.scala index 7f5f412a20..7807f0ba03 100644 --- a/src/compiler/scala/tools/nsc/dependencies/Changes.scala +++ b/src/compiler/scala/tools/nsc/dependencies/Changes.scala @@ -61,12 +61,7 @@ abstract class Changes { annotationsChecked.forall(a => (sym1.hasAnnotation(a) == sym2.hasAnnotation(a))) - private def sameType(tp1: Type, tp2: Type)(implicit strict: Boolean) = { - def typeOf(tp: Type): String = tp.toString + "[" + tp.getClass + "]" - val res = sameType0(tp1, tp2) - //if (!res) println("\t different types: " + typeOf(tp1) + " : " + typeOf(tp2)) - res - } + private def sameType(tp1: Type, tp2: Type)(implicit strict: Boolean) = sameType0(tp1, tp2) private def sameType0(tp1: Type, tp2: Type)(implicit strict: Boolean): Boolean = ((tp1, tp2) match { /*case (ErrorType, _) => false @@ -95,11 +90,11 @@ abstract class Changes { } else !sym1.isTypeParameter || !changedTypeParams.contains(sym1.fullName) + // @M! normalize reduces higher-kinded case to PolyType's testSymbols && sameType(pre1, pre2) && (sym1.variance == sym2.variance) && ((tp1.isHigherKinded && tp2.isHigherKinded && tp1.normalize =:= tp2.normalize) || sameTypes(args1, args2)) - // @M! normalize reduces higher-kinded case to PolyType's case (RefinedType(parents1, ref1), RefinedType(parents2, ref2)) => def isSubScope(s1: Scope, s2: Scope): Boolean = s2.toList.forall { @@ -170,7 +165,6 @@ abstract class Changes { /** Return the list of changes between 'from' and 'toSym.info'. */ def changeSet(from: Type, toSym: Symbol): List[Change] = { - implicit val defaultReason = "types" implicit val defaultStrictTypeRefTest = true val to = toSym.info diff --git a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala deleted file mode 100644 index cdde768274..0000000000 --- a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala +++ /dev/null @@ -1,254 +0,0 @@ -package scala.tools.nsc -package dependencies - -import io.Path -import scala.collection._ -import symtab.Flags -import scala.tools.nsc.io.AbstractFile -import scala.reflect.internal.util.SourceFile - -trait DependencyAnalysis extends SubComponent with Files { - import global._ - - val phaseName = "dependencyAnalysis" - - def off = settings.make.isDefault || settings.make.value == "all" - def shouldCheckClasspath = settings.make.value != "transitivenocp" - - def newPhase(prev: Phase) = new AnalysisPhase(prev) - - private def depPath = Path(settings.dependenciesFile.value) - def loadDependencyAnalysis(): Boolean = ( - depPath.path != "none" && depPath.isFile && loadFrom( - AbstractFile.getFile(depPath), - path => AbstractFile.getFile(depPath.parent resolve Path(path)) - ) - ) - def saveDependencyAnalysis(): Unit = { - if (!depPath.exists) - dependenciesFile = AbstractFile.getFile(depPath.createFile()) - - /** The directory where file lookup should start */ - val rootPath = depPath.parent.normalize - saveDependencies( - file => rootPath.relativize(Path(file.file).normalize).path - ) - } - - lazy val maxDepth = settings.make.value match { - case "changed" => 0 - case "immediate" => 1 - case _ => Int.MaxValue - } - - // todo: order insensible checking and, also checking timestamp? - def validateClasspath(cp1: String, cp2: String): Boolean = cp1 == cp2 - - def nameToFile(src: AbstractFile, name: String) = - settings.outputDirs.outputDirFor(src) - .lookupPathUnchecked(name.toString.replace(".", java.io.File.separator) + ".class", false) - - private var depFile: Option[AbstractFile] = None - - def dependenciesFile_=(file: AbstractFile) { - assert(file ne null) - depFile = Some(file) - } - - def dependenciesFile: Option[AbstractFile] = depFile - - def classpath = settings.classpath.value - def newDeps = new FileDependencies(classpath) - - var dependencies = newDeps - - def managedFiles = dependencies.dependencies.keySet - - /** Top level definitions per source file. */ - val definitions: mutable.Map[AbstractFile, List[Symbol]] = - new mutable.HashMap[AbstractFile, List[Symbol]] { - override def default(f: AbstractFile) = Nil - } - - /** External references used by source file. */ - val references: mutable.Map[AbstractFile, immutable.Set[String]] = - new mutable.HashMap[AbstractFile, immutable.Set[String]] { - override def default(f: AbstractFile) = immutable.Set() - } - - /** External references for inherited members used in the source file */ - val inherited: mutable.Map[AbstractFile, immutable.Set[Inherited]] = - new mutable.HashMap[AbstractFile, immutable.Set[Inherited]] { - override def default(f: AbstractFile) = immutable.Set() - } - - /** Write dependencies to the current file. */ - def saveDependencies(fromFile: AbstractFile => String) = - if(dependenciesFile.isDefined) - dependencies.writeTo(dependenciesFile.get, fromFile) - - /** Load dependencies from the given file and save the file reference for - * future saves. - */ - def loadFrom(f: AbstractFile, toFile: String => AbstractFile): Boolean = { - dependenciesFile = f - FileDependencies.readFrom(f, toFile) match { - case Some(fd) => - val success = if (shouldCheckClasspath) validateClasspath(fd.classpath, classpath) else true - dependencies = if (success) fd else { - if (settings.debug.value) - println("Classpath has changed. Nuking dependencies") - newDeps - } - - success - case None => false - } - } - - def calculateFiles(files: List[SourceFile]): List[SourceFile] = - if (off) files - else if (dependencies.isEmpty) { - println("No known dependencies. Compiling " + - (if (settings.debug.value) files.mkString(", ") else "everything")) - files - } else { - val (direct, indirect) = dependencies.invalidatedFiles(maxDepth); - val filtered = files.filter(x => { - val f = x.file.absolute - direct(f) || indirect(f) || !dependencies.containsFile(f); - }) - filtered match { - case Nil => println("No changes to recompile"); - case x => println("Recompiling " + ( - if(settings.debug.value) x.mkString(", ") else x.length + " files") - ) - } - filtered - } - - case class Inherited(qualifier: String, member: Name) - - class AnalysisPhase(prev: Phase) extends StdPhase(prev) { - - override def cancelled(unit: CompilationUnit) = - super.cancelled(unit) && !unit.isJava - - def apply(unit : global.CompilationUnit) { - val f = unit.source.file.file - // When we're passed strings by the interpreter - // they have no source file. We simply ignore this case - // as irrelevant to dependency analysis. - if (f != null){ - val source: AbstractFile = unit.source.file; - for (d <- unit.icode){ - val name = d.toString - d.symbol match { - case s : ModuleClassSymbol => - val isTopLevelModule = afterPickler { !s.isImplClass && !s.isNestedClass } - - if (isTopLevelModule && (s.companionModule != NoSymbol)) { - dependencies.emits(source, nameToFile(unit.source.file, name)) - } - dependencies.emits(source, nameToFile(unit.source.file, name + "$")) - case _ => - dependencies.emits(source, nameToFile(unit.source.file, name)) - } - } - - dependencies.reset(source) - for (d <- unit.depends; if (d.sourceFile != null)){ - dependencies.depends(source, d.sourceFile) - } - } - - // find all external references in this compilation unit - val file = unit.source.file - references += file -> immutable.Set.empty[String] - inherited += file -> immutable.Set.empty[Inherited] - - val buf = new mutable.ListBuffer[Symbol] - - (new Traverser { - override def traverse(tree: Tree) { - if ((tree.symbol ne null) - && (tree.symbol != NoSymbol) - && (!tree.symbol.isPackage) - && (!tree.symbol.isJavaDefined) - && (!tree.symbol.tpe.isError) - && ((tree.symbol.sourceFile eq null) - || (tree.symbol.sourceFile.path != file.path)) - && (!tree.symbol.isClassConstructor)) { - updateReferences(tree.symbol.fullName) - // was "at uncurryPhase.prev", which is actually non-deterministic - // because the continuations plugin may or may not supply uncurry's - // immediately preceding phase. - beforeRefchecks(checkType(tree.symbol.tpe)) - } - - tree match { - case cdef: ClassDef if !cdef.symbol.hasPackageFlag && - !cdef.symbol.isAnonymousFunction => - if (cdef.symbol != NoSymbol) buf += cdef.symbol - // was "at erasurePhase.prev" - beforeExplicitOuter { - for (s <- cdef.symbol.info.decls) - s match { - case ts: TypeSymbol if !ts.isClass => - checkType(s.tpe) - case _ => - } - } - super.traverse(tree) - - case ddef: DefDef => - // was "at typer.prev" - beforeTyper { checkType(ddef.symbol.tpe) } - super.traverse(tree) - case a @ Select(q, n) if ((a.symbol != NoSymbol) && (q.symbol != null)) => // #2556 - if (!a.symbol.isConstructor && - !a.symbol.owner.isPackageClass && - !isSameType(q.tpe, a.symbol.owner.tpe)) - inherited += file -> - (inherited(file) + Inherited(q.symbol.tpe.resultType.safeToString, n)) - super.traverse(tree) - case _ => - super.traverse(tree) - } - } - - def checkType(tpe: Type): Unit = - tpe match { - case t: MethodType => - checkType(t.resultType) - for (s <- t.params) checkType(s.tpe) - - case t: TypeRef => - if (t.sym.isAliasType) { - updateReferences(t.typeSymbolDirect.fullName) - checkType(t.typeSymbolDirect.info) - } - updateReferences(t.typeSymbol.fullName) - for (tp <- t.args) checkType(tp) - - case t: PolyType => - checkType(t.resultType) - updateReferences(t.typeSymbol.fullName) - - case t: NullaryMethodType => - checkType(t.resultType) - updateReferences(t.typeSymbol.fullName) - - case t => - updateReferences(t.typeSymbol.fullName) - } - - def updateReferences(s: String): Unit = - references += file -> (references(file) + s) - - }).apply(unit.body) - - definitions(unit.source.file) = buf.toList - } - } -} diff --git a/src/compiler/scala/tools/nsc/dependencies/Files.scala b/src/compiler/scala/tools/nsc/dependencies/Files.scala deleted file mode 100644 index 194351a13f..0000000000 --- a/src/compiler/scala/tools/nsc/dependencies/Files.scala +++ /dev/null @@ -1,177 +0,0 @@ -package scala.tools.nsc -package dependencies - -import java.io.{InputStream, OutputStream, PrintStream, InputStreamReader, BufferedReader} -import io.{AbstractFile, PlainFile, VirtualFile} - -import scala.collection._ - - -trait Files { self : SubComponent => - - class FileDependencies(val classpath: String) { - import FileDependencies._ - - class Tracker extends mutable.OpenHashMap[AbstractFile, mutable.Set[AbstractFile]] { - override def default(key: AbstractFile) = { - this(key) = new mutable.HashSet[AbstractFile] - this(key) - } - } - - val dependencies = new Tracker - val targets = new Tracker - - def isEmpty = dependencies.isEmpty && targets.isEmpty - - def emits(source: AbstractFile, result: AbstractFile) = - targets(source) += result - def depends(from: AbstractFile, on: AbstractFile) = - dependencies(from) += on - - def reset(file: AbstractFile) = dependencies -= file - - def cleanEmpty = { - dependencies foreach {case (_, value) => - value retain (x => x.exists && (x ne removedFile))} - dependencies retain ((key, value) => key.exists && !value.isEmpty) - targets foreach {case (_, value) => value retain (_.exists)} - targets retain ((key, value) => key.exists && !value.isEmpty) - } - - def containsFile(f: AbstractFile) = targets.contains(f.absolute) - - def invalidatedFiles(maxDepth: Int) = { - val direct = new mutable.HashSet[AbstractFile] - - for ((file, products) <- targets) { - // This looks a bit odd. It may seem like one should invalidate a file - // if *any* of its dependencies are older than it. The forall is there - // to deal with the fact that a) Some results might have been orphaned - // and b) Some files might not need changing. - direct(file) ||= products.forall(d => d.lastModified < file.lastModified) - } - - val indirect = dependentFiles(maxDepth, direct) - - for ((source, targets) <- targets - if direct(source) || indirect(source) || (source eq removedFile)) { - targets foreach (_.delete) - targets -= source - } - - (direct, indirect) - } - - /** Return the set of files that depend on the given changed files. - * It computes the transitive closure up to the given depth. - */ - def dependentFiles(depth: Int, changed: Set[AbstractFile]): Set[AbstractFile] = { - val indirect = new mutable.HashSet[AbstractFile] - val newInvalidations = new mutable.HashSet[AbstractFile] - - def invalid(file: AbstractFile) = - indirect(file) || changed(file) || (file eq removedFile) - - def go(i: Int) : Unit = if(i > 0) { - newInvalidations.clear - for((target, depends) <- dependencies if !invalid(target); - d <- depends) - newInvalidations(target) ||= invalid(d) - - indirect ++= newInvalidations - if (!newInvalidations.isEmpty) go(i - 1) - } - - go(depth) - - indirect --= changed - } - - def writeTo(file: AbstractFile, fromFile: AbstractFile => String): Unit = - writeToFile(file)(out => writeTo(new PrintStream(out), fromFile)) - - def writeTo(print: PrintStream, fromFile: AbstractFile => String): Unit = { - def emit(tracker: Tracker) = - for ((f, ds) <- tracker; d <- ds) print.println(fromFile(f) + arrow + fromFile(d)) - - cleanEmpty - print.println(classpath) - print.println(separator) - emit(dependencies) - print.println(separator) - emit(targets) - } - } - - object FileDependencies { - private val separator:String = "-------" - private val arrow = " -> " - private val removedFile = new VirtualFile("removed") - - private def validLine(l: String) = (l != null) && (l != separator) - - def readFrom(file: AbstractFile, toFile: String => AbstractFile): Option[FileDependencies] = - readFromFile(file) { in => - val reader = new BufferedReader(new InputStreamReader(in)) - val it = new FileDependencies(reader.readLine) - - def readLines(valid: Boolean)(f: (AbstractFile, AbstractFile) => Unit): Boolean = { - var continue = valid - var line: String = null - while (continue && {line = reader.readLine; validLine(line)}) { - line.split(arrow) match { - case Array(from, on) => f(toFile(from), toFile(on)) - case _ => - global.inform("Parse error: Unrecognised string " + line) - continue = false - } - } - continue - } - - reader.readLine - - val dResult = readLines(true)( - (_, _) match { - case (null, _) => // fromFile is removed, it's ok - case (fromFile, null) => - // onFile is removed, should recompile fromFile - it.depends(fromFile, removedFile) - case (fromFile, onFile) => it.depends(fromFile, onFile) - }) - - readLines(dResult)( - (_, _) match { - case (null, null) => - // source and target are all removed, it's ok - case (null, targetFile) => - // source is removed, should remove relative target later - it.emits(removedFile, targetFile) - case (_, null) => - // it may has been cleaned outside, or removed during last phase - case (sourceFile, targetFile) => it.emits(sourceFile, targetFile) - }) - - Some(it) - } - } - - def writeToFile[T](file: AbstractFile)(f: OutputStream => T) : T = { - val out = file.bufferedOutput - try { - f(out) - } finally { - out.close - } - } - - def readFromFile[T](file: AbstractFile)(f: InputStream => T) : T = { - val in = file.input - try{ - f(in) - } finally { - in.close - } - } -} diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index a091b04993..a99b17dce4 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -8,9 +8,7 @@ package doc import scala.util.control.ControlThrowable import reporters.Reporter -import scala.reflect.internal.util.{ NoPosition, BatchSourceFile} -import io.{ File, Directory } -import DocParser.Parsed +import scala.reflect.internal.util.BatchSourceFile /** A documentation processor controls the process of generating Scala * documentation, which is as follows. @@ -33,15 +31,7 @@ import DocParser.Parsed * @author Gilles Dubochet */ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor => /** The unique compiler instance used by this processor and constructed from its `settings`. */ - object compiler extends Global(settings, reporter) with interactive.RangePositions { - override protected def computeInternalPhases() { - phasesSet += syntaxAnalyzer - phasesSet += analyzer.namerFactory - phasesSet += analyzer.packageObjects - phasesSet += analyzer.typerFactory - } - override def forScaladoc = true - } + object compiler extends ScaladocGlobal(settings, reporter) /** Creates a scaladoc site for all symbols defined in this call's `source`, * as well as those defined in `sources` of previous calls to the same processor. diff --git a/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala b/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala new file mode 100644 index 0000000000..d4777d7800 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala @@ -0,0 +1,105 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package doc + +import scala.util.control.ControlThrowable +import reporters.Reporter +import typechecker.Analyzer +import scala.reflect.internal.util.BatchSourceFile + +trait ScaladocAnalyzer extends Analyzer { + val global : Global // generally, a ScaladocGlobal + import global._ + + override def newTyper(context: Context): ScaladocTyper = new ScaladocTyper(context) + + class ScaladocTyper(context0: Context) extends Typer(context0) { + private def unit = context.unit + + override def typedDocDef(docDef: DocDef, mode: Mode, pt: Type): Tree = { + val sym = docDef.symbol + + if ((sym ne null) && (sym ne NoSymbol)) { + val comment = docDef.comment + docComments(sym) = comment + comment.defineVariables(sym) + val typer1 = newTyper(context.makeNewScope(docDef, context.owner)) + for (useCase <- comment.useCases) { + typer1.silent(_ => typer1 defineUseCases useCase) match { + case SilentTypeError(err) => + unit.warning(useCase.pos, err.errMsg) + case _ => + } + for (useCaseSym <- useCase.defined) { + if (sym.name != useCaseSym.name) + unit.warning(useCase.pos, "@usecase " + useCaseSym.name.decode + " does not match commented symbol: " + sym.name.decode) + } + } + } + + super.typedDocDef(docDef, mode, pt) + } + + def defineUseCases(useCase: UseCase): List[Symbol] = { + def stringParser(str: String): syntaxAnalyzer.Parser = { + val file = new BatchSourceFile(context.unit.source.file, str) { + override def positionInUltimateSource(pos: Position) = { + pos.withSource(context.unit.source, useCase.pos.start) + } + } + val unit = new CompilationUnit(file) + new syntaxAnalyzer.UnitParser(unit) + } + + val trees = stringParser(useCase.body+";").nonLocalDefOrDcl + val enclClass = context.enclClass.owner + + def defineAlias(name: Name) = ( + if (context.scope.lookup(name) == NoSymbol) { + lookupVariable(name.toString.substring(1), enclClass) foreach { repl => + silent(_.typedTypeConstructor(stringParser(repl).typ())) map { tpt => + val alias = enclClass.newAliasType(name.toTypeName, useCase.pos) + val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias) + val newInfo = genPolyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) + alias setInfo newInfo + context.scope.enter(alias) + } + } + } + ) + + for (tree <- trees; t <- tree) + t match { + case Ident(name) if name startsWith '$' => defineAlias(name) + case _ => + } + + useCase.aliases = context.scope.toList + namer.enterSyms(trees) + typedStats(trees, NoSymbol) + useCase.defined = context.scope.toList filterNot (useCase.aliases contains _) + + if (settings.debug.value) + useCase.defined foreach (sym => println("defined use cases: %s:%s".format(sym, sym.tpe))) + + useCase.defined + } + } +} + +class ScaladocGlobal(settings: doc.Settings, reporter: Reporter) extends Global(settings, reporter) with interactive.RangePositions { + override protected def computeInternalPhases() { + phasesSet += syntaxAnalyzer + phasesSet += analyzer.namerFactory + phasesSet += analyzer.packageObjects + phasesSet += analyzer.typerFactory + } + override def forScaladoc = true + override lazy val analyzer = new { + val global: ScaladocGlobal.this.type = ScaladocGlobal.this + } with ScaladocAnalyzer +} diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index 02630a99b2..75312e2279 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -315,10 +315,10 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) /** Common conversion targets that affect any class in Scala */ val commonConversionTargets = Set( - "scala.Predef.any2stringfmt", - "scala.Predef.any2stringadd", - "scala.Predef.any2ArrowAssoc", - "scala.Predef.any2Ensuring", + "scala.Predef.StringFormat", + "scala.Predef.StringAdd", + "scala.Predef.ArrowAssoc", + "scala.Predef.Ensuring", "scala.collection.TraversableOnce.alternateImplicit") /** There's a reason all these are specialized by hand but documenting each of them is beyond the point */ diff --git a/src/compiler/scala/tools/nsc/doc/Uncompilable.scala b/src/compiler/scala/tools/nsc/doc/Uncompilable.scala index d3e5c869e0..9447e36610 100644 --- a/src/compiler/scala/tools/nsc/doc/Uncompilable.scala +++ b/src/compiler/scala/tools/nsc/doc/Uncompilable.scala @@ -15,7 +15,7 @@ trait Uncompilable { val global: Global val settings: Settings - import global.{ reporter, inform, warning, newTypeName, newTermName, Symbol, Name, DocComment, NoSymbol } + import global.{ reporter, inform, warning, newTypeName, newTermName, Symbol, DocComment, NoSymbol } import global.definitions.AnyRefClass import global.rootMirror.RootClass diff --git a/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala index f509c63ba0..c2b3c410fe 100755 --- a/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala +++ b/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2007-2012 LAMP/EPFL + * Copyright 2007-2013 LAMP/EPFL * @author Manohar Jonnalagedda */ @@ -8,11 +8,9 @@ package doc package base import base.comment._ -import reporters.Reporter import scala.collection._ import scala.util.matching.Regex -import scala.annotation.switch -import scala.reflect.internal.util.{NoPosition, Position} +import scala.reflect.internal.util.Position import scala.language.postfixOps /** The comment parser transforms raw comment strings into `Comment` objects. @@ -26,7 +24,7 @@ import scala.language.postfixOps trait CommentFactoryBase { this: MemberLookupBase => val global: Global - import global.{ reporter, definitions, Symbol } + import global.{ reporter, Symbol } /* Creates comments with necessary arguments */ def createComment ( @@ -66,7 +64,6 @@ trait CommentFactoryBase { this: MemberLookupBase => val note = note0 val example = example0 val constructor = constructor0 - val source = source0 val inheritDiagram = inheritDiagram0 val contentDiagram = contentDiagram0 val groupDesc = groupDesc0 @@ -683,7 +680,7 @@ trait CommentFactoryBase { this: MemberLookupBase => def link(): Inline = { val SchemeUri = """([a-z]+:.*)""".r jump("[[") - var parens = 2 + repeatJump('[') + val parens = 2 + repeatJump('[') val start = "[" * parens val stop = "]" * parens //println("link with " + parens + " matching parens") @@ -729,7 +726,7 @@ trait CommentFactoryBase { this: MemberLookupBase => */ def normalizeIndentation(_code: String): String = { - var code = _code.trim + val code = _code.trim var maxSkip = Integer.MAX_VALUE var crtSkip = 0 var wsArea = true @@ -884,20 +881,6 @@ trait CommentFactoryBase { this: MemberLookupBase => count } - final def jumpUntil(chars: String): Int = { - assert(chars.length > 0) - var count = 0 - val c = chars.charAt(0) - while (!check(chars) && char != endOfText) { - nextChar() - while (char != c && char != endOfText) { - nextChar() - count += 1 - } - } - count - } - final def jumpUntil(pred: => Boolean): Int = { var count = 0 while (!pred && char != endOfText) { diff --git a/src/compiler/scala/tools/nsc/doc/base/MemberLookupBase.scala b/src/compiler/scala/tools/nsc/doc/base/MemberLookupBase.scala index cdcfeaae81..8d80333195 100755 --- a/src/compiler/scala/tools/nsc/doc/base/MemberLookupBase.scala +++ b/src/compiler/scala/tools/nsc/doc/base/MemberLookupBase.scala @@ -88,7 +88,7 @@ trait MemberLookupBase { // (4) if we still haven't found anything, create a tooltip Tooltip(query) case List(l) => l - case links => + case links => val chosen = chooseLink(links) def linkToString(link: LinkTo) = { val chosenInfo = diff --git a/src/compiler/scala/tools/nsc/doc/base/comment/Body.scala b/src/compiler/scala/tools/nsc/doc/base/comment/Body.scala index 02e662da85..2a07547de2 100755 --- a/src/compiler/scala/tools/nsc/doc/base/comment/Body.scala +++ b/src/compiler/scala/tools/nsc/doc/base/comment/Body.scala @@ -10,8 +10,6 @@ package comment import scala.collection._ -import java.net.URL - /** A body of text. A comment has a single body, which is composed of * at least one block. Inside every body is exactly one summary (see * [[scala.tools.nsc.doc.model.comment.Summary]]). */ diff --git a/src/compiler/scala/tools/nsc/doc/base/comment/Comment.scala b/src/compiler/scala/tools/nsc/doc/base/comment/Comment.scala index 2b28164ca4..a3d05ae50b 100644 --- a/src/compiler/scala/tools/nsc/doc/base/comment/Comment.scala +++ b/src/compiler/scala/tools/nsc/doc/base/comment/Comment.scala @@ -102,9 +102,6 @@ abstract class Comment { /** A usage example related to the entity. */ def example: List[Body] - /** The comment as it appears in the source text. */ - def source: Option[String] - /** A description for the primary constructor */ def constructor: Option[Body] diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala index 69da322418..829df97fc2 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala @@ -11,7 +11,7 @@ import base._ import base.comment._ import model._ -import scala.xml.{XML, NodeSeq} +import scala.xml.NodeSeq import scala.xml.dtd.{DocType, PublicID} import scala.collection._ import java.io.Writer diff --git a/src/compiler/scala/tools/nsc/doc/html/Page.scala b/src/compiler/scala/tools/nsc/doc/html/Page.scala index 62166f7def..ef9beb1dce 100644 --- a/src/compiler/scala/tools/nsc/doc/html/Page.scala +++ b/src/compiler/scala/tools/nsc/doc/html/Page.scala @@ -88,12 +88,6 @@ abstract class Page { def relativeLinkTo(destClass: TemplateEntity): String = relativeLinkTo(templateToPath(destClass)) - /** A relative link from this page to some destination page in the Scaladoc site. - * @param destPage The page that the link will point to. */ - def relativeLinkTo(destPage: HtmlPage): String = { - relativeLinkTo(destPage.path) - } - /** A relative link from this page to some destination path. * @param destPath The path that the link will point to. */ def relativeLinkTo(destPath: List[String]): String = { diff --git a/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala b/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala index ee78f4ea7a..db9edd165d 100644 --- a/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala +++ b/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala @@ -29,8 +29,8 @@ private[html] object SyntaxHigh { /** Annotations, sorted alphabetically */ val annotations = Array( "BeanProperty", "SerialVersionUID", - "beanGetter", "beanSetter", "bridge", "cloneable", - "deprecated", "deprecatedName", + "beanGetter", "beanSetter", "bridge", + "deprecated", "deprecatedName", "deprecatedOverriding", "deprecatedInheritance", "elidable", "field", "getter", "inline", "migration", "native", "noinline", "param", "remote", "setter", "specialized", "strictfp", "switch", @@ -40,7 +40,7 @@ private[html] object SyntaxHigh { /** Standard library classes/objects, sorted alphabetically */ val standards = Array ( - "WeakTypeTag", "Any", "AnyRef", "AnyVal", "App", "Application", "Array", + "WeakTypeTag", "Any", "AnyRef", "AnyVal", "App", "Array", "Boolean", "Byte", "Char", "Class", "ClassTag", "ClassManifest", "Console", "Double", "Enumeration", "Float", "Function", "Int", "List", "Long", "Manifest", "Map", diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala index c76bdc58d9..c034647320 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala @@ -9,10 +9,8 @@ package html package page import model._ - import scala.collection._ import scala.xml._ -import scala.util.parsing.json.{JSONObject, JSONArray} class Index(universe: doc.Universe, val index: doc.Index) extends HtmlPage { diff --git a/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala b/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala index a205e02533..e3c94505ab 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala @@ -8,7 +8,6 @@ package scala.tools.nsc.doc.html.page import scala.tools.nsc.doc import scala.tools.nsc.doc.model.{Package, DocTemplateEntity} import scala.tools.nsc.doc.html.{Page, HtmlFactory} -import java.nio.channels.Channels import scala.util.parsing.json.{JSONObject, JSONArray} class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Source.scala b/src/compiler/scala/tools/nsc/doc/html/page/Source.scala index 68289b7474..37145756d9 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Source.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Source.scala @@ -8,8 +8,7 @@ package doc package html package page -import model._ -import scala.xml.{NodeSeq, Unparsed} +import scala.xml.NodeSeq import java.io.File class Source(sourceFile: File) extends HtmlPage { 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 ea07ff29c4..0685f9ad70 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -13,8 +13,6 @@ import base.comment._ import model._ import model.diagram._ -import diagram._ - import scala.xml.{ NodeSeq, Text, UnprefixedAttribute } import scala.language.postfixOps @@ -525,7 +523,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp val sourceLink: NodeSeq = mbr match { case dtpl: DocTemplateEntity if (isSelf && dtpl.sourceUrl.isDefined && dtpl.inSource.isDefined && !isReduced) => - val (absFile, line) = dtpl.inSource.get + val (absFile, _) = dtpl.inSource.get <dt>Source</dt> <dd>{ <a href={ dtpl.sourceUrl.get.toString } target="_blank">{ Text(absFile.file.getName) }</a> }</dd> case _ => NodeSeq.Empty @@ -653,7 +651,6 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp case dtpl: DocTemplateEntity if isSelf && !isReduced => val diagram = f(dtpl) if (diagram.isDefined) { - val s = universe.settings val diagramSvg = generator.generate(diagram.get, tpl, this) if (diagramSvg != NodeSeq.Empty) { <div class="toggleContainer block diagram-container" id={ id + "-container"}> @@ -763,7 +760,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp if (isReduced) NodeSeq.Empty else { def paramsToHtml(vlsss: List[List[ValueParam]]): NodeSeq = { def param0(vl: ValueParam): NodeSeq = - // notice the }{ in the next lines, they are necessary to avoid a undesired withspace in output + // notice the }{ in the next lines, they are necessary to avoid an undesired withspace in output <span name={ vl.name }>{ Text(vl.name) }{ Text(": ") ++ typeToHtml(vl.resultType, hasLinks) }{ diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala index 847367838c..512becd04d 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala @@ -10,7 +10,6 @@ package diagram import scala.xml.{NodeSeq, XML, PrefixedAttribute, Elem, MetaData, Null, UnprefixedAttribute} import scala.collection.immutable._ -import javax.xml.parsers.SAXParser import model._ import model.diagram._ @@ -22,8 +21,6 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { private var pathToLib: String = null // maps nodes to unique indices private var node2Index: Map[Node, Int] = null - // maps an index to its corresponding node - private var index2Node: Map[Int, Node] = null // true if the current diagram is a class diagram private var isInheritanceDiagram = false // incoming implicit nodes (needed for determining the CSS class of a node) @@ -42,7 +39,6 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { // clean things up a bit, so we don't leave garbage on the heap this.page = null node2Index = null - index2Node = null incomingImplicitNodes = List() result } @@ -116,7 +112,6 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { node2Index = d.nodes.zipWithIndex.toMap incomingImplicitNodes = List() } - index2Node = node2Index map {_.swap} val implicitsDot = { if (!isInheritanceDiagram) "" @@ -215,7 +210,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { def escape(name: String) = name.replace("&", "&").replace("<", "<").replace(">", ">"); // assemble node attribues in a map - var attr = scala.collection.mutable.Map[String, String]() + val attr = scala.collection.mutable.Map[String, String]() // link node.doctpl match { diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala index 5cdd5c74a4..2fa1bf62f3 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala @@ -10,12 +10,10 @@ import java.io.InputStreamReader import java.io.OutputStreamWriter import java.io.BufferedWriter import java.io.BufferedReader -import java.io.IOException import scala.sys.process._ import scala.concurrent.SyncVar import model._ -import model.diagram._ /** This class takes care of running the graphviz dot utility */ class DotRunner(settings: doc.Settings) { @@ -183,7 +181,7 @@ class DotProcess(settings: doc.Settings) { private[this] def outputFn(stdOut: InputStream): Unit = { val reader = new BufferedReader(new InputStreamReader(stdOut)) - var buffer: StringBuilder = new StringBuilder() + val buffer: StringBuilder = new StringBuilder() try { var line = reader.readLine while (!error && line != null) { @@ -209,7 +207,6 @@ class DotProcess(settings: doc.Settings) { private[this] def errorFn(stdErr: InputStream): Unit = { val reader = new BufferedReader(new InputStreamReader(stdErr)) - var buffer: StringBuilder = new StringBuilder() try { var line = reader.readLine while (line != null) { @@ -225,4 +222,4 @@ class DotProcess(settings: doc.Settings) { errorBuffer.append(" Error thread in " + templateName + ": Exception: " + exc + "\n") } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala index cbc1a23d44..924f203a59 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala @@ -23,10 +23,6 @@ import diagram._ * - type and value parameters; * - annotations. */ trait Entity { - - /** Similar to symbols, so we can track entities */ - def id: Int - /** The name of the entity. Note that the name does not qualify this entity uniquely; use its `qualifiedName` * instead. */ def name : String @@ -59,9 +55,6 @@ trait Entity { /** Indicates whether this entity lives in the types namespace (classes, traits, abstract/alias types) */ def isType: Boolean - - /** Indicates whether this entity lives in the terms namespace (objects, packages, methods, values) */ - def isTerm: Boolean } object Entity { @@ -97,9 +90,6 @@ trait TemplateEntity extends Entity { /** Whether documentation is available for this template. */ def isDocTemplate: Boolean - /** Whether documentation is available for this template. */ - def isNoDocMemberTemplate: Boolean - /** Whether this template is a case class. */ def isCaseClass: Boolean @@ -149,9 +139,6 @@ trait MemberEntity extends Entity { /** Some migration warning if this member has a migration annotation, or none otherwise. */ def migration: Option[Body] - @deprecated("Use `inDefinitionTemplates` instead", "2.9.0") - def inheritedFrom: List[TemplateEntity] - /** For members representing values: the type of the value returned by this member; for members * representing types: the type itself. */ def resultType: TypeEntity @@ -177,12 +164,6 @@ trait MemberEntity extends Entity { /** Whether this member is an abstract type. */ def isAbstractType: Boolean - /** Whether this member is a template. */ - def isTemplate: Boolean - - /** Whether this member is implicit. */ - def isImplicit: Boolean - /** Whether this member is abstract. */ def isAbstract: Boolean @@ -384,14 +365,9 @@ trait RootPackage extends Package /** A non-template member (method, value, lazy value, variable, constructor, alias type, and abstract type). */ trait NonTemplateMemberEntity extends MemberEntity { - /** Whether this member is a use case. A use case is a member which does not exist in the documented code. * It corresponds to a real member, and provides a simplified, yet compatible signature for that member. */ def isUseCase: Boolean - - /** Whether this member is a bridge member. A bridge member does only exist for binary compatibility reasons - * and should not appear in ScalaDoc. */ - def isBridge: Boolean } @@ -506,12 +482,6 @@ trait ImplicitConversion { /** The result type after the conversion */ def targetType: TypeEntity - /** The result type after the conversion - * Note: not all targetTypes have a corresponding template. Examples include conversions resulting in refinement - * types. Need to check it's not option! - */ - def targetTemplate: Option[TemplateEntity] - /** The components of the implicit conversion type parents */ def targetTypeComponents: List[(TemplateEntity, TypeEntity)] diff --git a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala index 4ee6daf73e..1272906df5 100755 --- a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala @@ -17,8 +17,6 @@ object IndexModelFactory { object result extends mutable.HashMap[Char,SymbolMap] { - /* Owner template ordering */ - implicit def orderingSet = math.Ordering.String.on { x: MemberEntity => x.name.toLowerCase } /* symbol name ordering */ implicit def orderingMap = math.Ordering.String diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 0a469c9227..303fe9f184 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -43,20 +43,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def modelFinished: Boolean = _modelFinished private var universe: Universe = null - private def dbg(msg: String) = if (sys.props contains "scala.scaladoc.debug") println(msg) - protected def closestPackage(sym: Symbol) = { - if (sym.isPackage || sym.isPackageClass) sym - else sym.enclosingPackage - } - - private def printWithoutPrefix(memberSym: Symbol, templateSym: Symbol) = { - dbg( - "memberSym " + memberSym + " templateSym " + templateSym + " encls = " + - closestPackage(memberSym) + ", " + closestPackage(templateSym) - ) - memberSym.isOmittablePrefix || (closestPackage(memberSym) == closestPackage(templateSym)) - } - def makeModel: Option[Universe] = { val universe = new Universe { thisUniverse => thisFactory.universe = thisUniverse @@ -86,7 +72,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { /* ============== IMPLEMENTATION PROVIDING ENTITY TYPES ============== */ abstract class EntityImpl(val sym: Symbol, val inTpl: TemplateImpl) extends Entity { - val id = { ids += 1; ids } val name = optimize(sym.nameString) val universe = thisFactory.universe @@ -100,7 +85,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def annotations = sym.annotations.map(makeAnnotation) def inPackageObject: Boolean = sym.owner.isModuleClass && sym.owner.sourceModule.isPackageObject def isType = sym.name.isTypeName - def isTerm = sym.name.isTermName } trait TemplateImpl extends EntityImpl with TemplateEntity { @@ -112,7 +96,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isObject = sym.isModule && !sym.isPackage def isCaseClass = sym.isCaseClass def isRootPackage = false - def isNoDocMemberTemplate = false def selfType = if (sym.thisSym eq sym) None else Some(makeType(sym.thisSym.typeOfThis, this)) } @@ -127,18 +110,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } if (inTpl != null) thisFactory.comment(sym, thisTpl, inTpl) else None } - def group = if (comment.isDefined) comment.get.group.getOrElse(defaultGroup) else defaultGroup + def group = comment flatMap (_.group) getOrElse defaultGroup override def inTemplate = inTpl override def toRoot: List[MemberImpl] = this :: inTpl.toRoot - def inDefinitionTemplates = this match { - case mb: NonTemplateMemberEntity if (mb.useCaseOf.isDefined) => - mb.useCaseOf.get.inDefinitionTemplates - case _ => - if (inTpl == null) - List(makeRootPackage) - else - makeTemplate(sym.owner)::(sym.allOverriddenSymbols map { inhSym => makeTemplate(inhSym.owner) }) - } + def inDefinitionTemplates = + if (inTpl == null) + List(makeRootPackage) + else + makeTemplate(sym.owner)::(sym.allOverriddenSymbols map { inhSym => makeTemplate(inhSym.owner) }) def visibility = { if (sym.isPrivateLocal) PrivateInInstance() else if (sym.isProtectedLocal) ProtectedInInstance() @@ -149,8 +128,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { else None if (sym.isPrivate) PrivateInTemplate(inTpl) else if (sym.isProtected) ProtectedInTemplate(qual getOrElse inTpl) - else if (qual.isDefined) PrivateInTemplate(qual.get) - else Public() + else qual match { + case Some(q) => PrivateInTemplate(q) + case None => Public() + } } } def flags = { @@ -189,9 +170,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { }) else None - def inheritedFrom = - if (inTemplate.sym == this.sym.owner || inTemplate.sym.isPackage) Nil else - makeTemplate(this.sym.owner) :: (sym.allOverriddenSymbols map { os => makeTemplate(os.owner) }) + def resultType = { def resultTpe(tpe: Type): Type = tpe match { // similar to finalResultType, except that it leaves singleton types alone case PolyType(_, res) => resultTpe(res) @@ -199,14 +178,13 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case NullaryMethodType(res) => resultTpe(res) case _ => tpe } - val tpe = if (!isImplicitlyInherited) sym.tpe else byConversion.get.toType memberInfo sym + val tpe = byConversion.fold(sym.tpe) (_.toType memberInfo sym) makeTypeInTemplateContext(resultTpe(tpe), inTemplate, sym) } def isDef = false def isVal = false def isLazyVal = false def isVar = false - def isImplicit = sym.isImplicit def isConstructor = false def isAliasType = false def isAbstractType = false @@ -214,7 +192,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // for the explanation of conversion == null see comment on flags ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED)) && (!isImplicitlyInherited)) || sym.isAbstractClass || sym.isAbstractType) && !sym.isSynthetic - def isTemplate = false + def signature = externalSignature(sym) lazy val signatureCompat = { @@ -255,8 +233,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { * exists, but should not be documented (either it's not included in the source or it's not visible) */ class NoDocTemplateImpl(sym: Symbol, inTpl: TemplateImpl) extends EntityImpl(sym, inTpl) with TemplateImpl with HigherKindedImpl with NoDocTemplate { - assert(modelFinished) - assert(!(noDocTemplatesCache isDefinedAt sym)) + assert(modelFinished, this) + assert(!(noDocTemplatesCache isDefinedAt sym), (sym, noDocTemplatesCache(sym))) noDocTemplatesCache += (sym -> this) def isDocTemplate = false } @@ -268,25 +246,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { */ abstract class MemberTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberImpl(sym, inTpl) with TemplateImpl with HigherKindedImpl with MemberTemplateEntity { // no templates cache for this class, each owner gets its own instance - override def isTemplate = true def isDocTemplate = false - override def isNoDocMemberTemplate = true lazy val definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "." + name) def valueParams: List[List[ValueParam]] = Nil /** TODO, these are now only computed for DocTemplates */ - // Seems unused - // def parentTemplates = - // if (sym.isPackage || sym == AnyClass) - // List() - // else - // sym.tpe.parents.flatMap { tpe: Type => - // val tSym = tpe.typeSymbol - // if (tSym != NoSymbol) - // List(makeTemplate(tSym)) - // else - // List() - // } filter (_.isInstanceOf[DocTemplateEntity]) - def parentTypes = if (sym.isPackage || sym == AnyClass) List() else { val tps = (this match { @@ -306,7 +269,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { * All ancestors of the template and all non-package members. */ abstract class DocTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberTemplateImpl(sym, inTpl) with DocTemplateEntity { - assert(!modelFinished) + assert(!modelFinished, (sym, inTpl)) assert(!(docTemplatesCache isDefinedAt sym), sym) docTemplatesCache += (sym -> this) @@ -394,9 +357,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { lazy val memberSyms = sym.info.members.filter(s => membersShouldDocument(s, this)).toList // the inherited templates (classes, traits or objects) - var memberSymsLazy = memberSyms.filter(t => templateShouldDocument(t, this) && !inOriginalOwner(t, this)) + val memberSymsLazy = memberSyms.filter(t => templateShouldDocument(t, this) && !inOriginalOwner(t, this)) // the direct members (methods, values, vars, types and directly contained templates) - var memberSymsEager = memberSyms.filter(!memberSymsLazy.contains(_)) + val memberSymsEager = memberSyms.filter(!memberSymsLazy.contains(_)) // the members generated by the symbols in memberSymsEager val ownMembers = (memberSymsEager.flatMap(makeMember(_, None, this))) @@ -442,17 +405,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { conversions flatMap (conv => if (!implicitExcluded(conv.conversionQualifiedName)) conv.targetTypeComponents map { - case pair@(template, tpe) => + case (template, tpe) => template match { case d: DocTemplateImpl if (d != this) => d.registerImplicitlyConvertibleClass(this, conv) case _ => // nothing } - (pair._1, pair._2, conv) + (template, tpe, conv) } else List() ) - override def isTemplate = true override def isDocTemplate = true private[this] lazy val companionSymbol = if (sym.isAliasType || sym.isAbstractType) { @@ -527,31 +489,26 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { extends MemberImpl(sym, inTpl) with NonTemplateMemberEntity { override lazy val comment = { val inRealTpl = - /* Variable precendence order for implicitly added members: Take the variable defifinitions from ... - * 1. the target of the implicit conversion - * 2. the definition template (owner) - * 3. the current template - */ - if (conversion.isDefined) findTemplateMaybe(conversion.get.toType.typeSymbol) match { - case Some(d) if d != makeRootPackage => d //in case of NoSymbol, it will give us the root package - case _ => findTemplateMaybe(sym.owner) match { - case Some(d) if d != makeRootPackage => d //in case of NoSymbol, it will give us the root package - case _ => inTpl - } - } else inTpl - if (inRealTpl != null) thisFactory.comment(sym, None, inRealTpl) else None + conversion.fold(Option(inTpl)) { conv => + /* Variable precendence order for implicitly added members: Take the variable defifinitions from ... + * 1. the target of the implicit conversion + * 2. the definition template (owner) + * 3. the current template + */ + findTemplateMaybe(conv.toType.typeSymbol) filterNot (_ == makeRootPackage) orElse ( + findTemplateMaybe(sym.owner) filterNot (_ == makeRootPackage) orElse Option(inTpl) + ) + } + inRealTpl flatMap (thisFactory.comment(sym, None, _)) } + override def inDefinitionTemplates = useCaseOf.fold(super.inDefinitionTemplates)(_.inDefinitionTemplates) + override def qualifiedName = optimize(inTemplate.qualifiedName + "#" + name) lazy val definitionName = { - // this contrived name is here just to satisfy some older tests -- if you decide to remove it, be my guest, and - // also remove property("package object") from test/scaladoc/scalacheck/HtmlFactoryTest.scala so you don't break - // the test suite... - val packageObject = if (inPackageObject) ".package" else "" - if (!conversion.isDefined) optimize(inDefinitionTemplates.head.qualifiedName + packageObject + "#" + name) - else optimize(conversion.get.conversionQualifiedName + packageObject + "#" + name) + val qualifiedName = conversion.fold(inDefinitionTemplates.head.qualifiedName)(_.conversionQualifiedName) + optimize(qualifiedName + "#" + name) } - def isBridge = sym.isBridge def isUseCase = useCaseOf.isDefined override def byConversion: Option[ImplicitConversionImpl] = conversion override def isImplicitlyInherited = { assert(modelFinished); conversion.isDefined } @@ -564,7 +521,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { useCaseOf: Option[MemberEntity], inTpl: DocTemplateImpl) extends NonTemplateMemberImpl(sym, conversion, useCaseOf, inTpl) { def valueParams = { - val info = if (!isImplicitlyInherited) sym.info else conversion.get.toType memberInfo sym + val info = conversion.fold(sym.info)(_.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) }} @@ -666,7 +623,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { */ def createTemplate(aSym: Symbol, inTpl: DocTemplateImpl): Option[MemberImpl] = { // don't call this after the model finished! - assert(!modelFinished) + assert(!modelFinished, (aSym, inTpl)) def createRootPackageComment: Option[Comment] = if(settings.docRootContent.isDefault) None @@ -682,7 +639,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def createDocTemplate(bSym: Symbol, inTpl: DocTemplateImpl): DocTemplateImpl = { - assert(!modelFinished) // only created BEFORE the model is finished + assert(!modelFinished, (bSym, inTpl)) // only created BEFORE the model is finished if (bSym.isAliasType && bSym != AnyRefClass) new DocTemplateImpl(bSym, inTpl) with AliasImpl with AliasType { override def isAliasType = true } else if (bSym.isAbstractType) @@ -713,7 +670,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { override def inTemplate = this override def toRoot = this :: Nil override def qualifiedName = "_root_" - override def inheritedFrom = Nil override def isRootPackage = true override lazy val memberSyms = (bSym.info.members ++ EmptyPackage.info.members).toList filter { s => @@ -783,7 +739,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } } - /** Get the root package */ def makeRootPackage: PackageImpl = docTemplatesCache(RootPackage).asInstanceOf[PackageImpl] // TODO: Should be able to override the type @@ -860,16 +815,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def findMember(aSym: Symbol, inTpl: DocTemplateImpl): Option[MemberImpl] = { - val tplSym = normalizeTemplate(aSym.owner) + normalizeTemplate(aSym.owner) inTpl.members.find(_.sym == aSym) } - @deprecated("Use `findLinkTarget` instead.", "2.10.0") - def findTemplate(query: String): Option[DocTemplateImpl] = { - assert(modelFinished) - docTemplatesCache.values find { (tpl: DocTemplateImpl) => tpl.qualifiedName == query && !packageDropped(tpl) && !tpl.isObject } - } - def findTemplateMaybe(aSym: Symbol): Option[DocTemplateImpl] = { assert(modelFinished) docTemplatesCache.get(normalizeTemplate(aSym)).filterNot(packageDropped(_)) @@ -880,20 +829,12 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def makeTemplate(aSym: Symbol, inTpl: Option[TemplateImpl]): TemplateImpl = { assert(modelFinished) - def makeNoDocTemplate(aSym: Symbol, inTpl: TemplateImpl): NoDocTemplateImpl = { - val bSym = normalizeTemplate(aSym) - noDocTemplatesCache.get(bSym) match { - case Some(noDocTpl) => noDocTpl - case None => new NoDocTemplateImpl(bSym, inTpl) - } - } + def makeNoDocTemplate(aSym: Symbol, inTpl: TemplateImpl): NoDocTemplateImpl = + noDocTemplatesCache getOrElse (aSym, new NoDocTemplateImpl(aSym, inTpl)) - findTemplateMaybe(aSym) match { - case Some(dtpl) => - dtpl - case None => - val bSym = normalizeTemplate(aSym) - makeNoDocTemplate(bSym, if (inTpl.isDefined) inTpl.get else makeTemplate(bSym.owner)) + findTemplateMaybe(aSym) getOrElse { + val bSym = normalizeTemplate(aSym) + makeNoDocTemplate(bSym, inTpl getOrElse makeTemplate(bSym.owner)) } } @@ -904,24 +845,28 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { lazy val annotationClass = makeTemplate(annot.symbol) val arguments = { // lazy - def noParams = annot.args map { _ => None } + def annotArgs = annot.args match { + case Nil => annot.assocs collect { case (_, LiteralAnnotArg(const)) => Literal(const) } + case xs => xs + } + def noParams = annotArgs map (_ => None) + val params: List[Option[ValueParam]] = annotationClass match { case aClass: DocTemplateEntity with Class => (aClass.primaryConstructor map { _.valueParams.head }) match { case Some(vps) => vps map { Some(_) } - case None => noParams + case _ => noParams } case _ => noParams } - assert(params.length == annot.args.length) - (params zip annot.args) flatMap { case (param, arg) => - makeTree(arg) match { - case Some(tree) => - Some(new ValueArgument { - def parameter = param - def value = tree - }) - case None => None + assert(params.length == annotArgs.length, (params, annotArgs)) + + params zip annotArgs flatMap { case (param, arg) => + makeTree(arg) map { tree => + new ValueArgument { + def parameter = param + def value = tree + } } } } @@ -995,10 +940,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { val ignoreParents = Set[Symbol](AnyClass, AnyRefClass, ObjectClass) val filtParents = // we don't want to expose too many links to AnyRef, that will just be redundant information - if (tpl.isDefined && { val sym = tpl.get.sym; (!sym.isModule && parents.length < 2) || (sym == AnyValClass) || (sym == AnyRefClass) || (sym == AnyClass) }) - parents - else - parents.filterNot((p: Type) => ignoreParents(p.typeSymbol)) + tpl match { + case Some(tpl) if (!tpl.sym.isModule && parents.length < 2) || (tpl.sym == AnyValClass) || (tpl.sym == AnyRefClass) || (tpl.sym == AnyClass) => parents + case _ => parents.filterNot((p: Type) => ignoreParents(p.typeSymbol)) + } /** Returns: * - a DocTemplate if the type's symbol is documented @@ -1029,9 +974,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def makeQualifiedName(sym: Symbol, relativeTo: Option[Symbol] = None): String = { - val stop = if (relativeTo.isDefined) relativeTo.get.ownerChain.toSet else Set[Symbol]() + val stop = relativeTo map (_.ownerChain.toSet) getOrElse Set[Symbol]() var sym1 = sym - var path = new StringBuilder() + val path = new StringBuilder() // var path = List[Symbol]() while ((sym1 != NoSymbol) && (path.isEmpty || !stop(sym1))) { diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index f88251b22e..c00afee064 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -11,12 +11,7 @@ package doc package model 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. @@ -56,7 +51,6 @@ trait ModelFactoryImplicitSupport { import global._ import global.analyzer._ import global.definitions._ - import rootMirror.{RootPackage, RootClass, EmptyPackage, EmptyPackageClass} import settings.hardcoded // debugging: @@ -71,7 +65,7 @@ trait ModelFactoryImplicitSupport { * class A[T] * class B extends A[Int] * class C extends A[String] - * implicit def pimpA[T: Numeric](a: A[T]): D + * implicit def enrichA[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: @@ -94,9 +88,9 @@ trait ModelFactoryImplicitSupport { // 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 context: global.analyzer.Context = global.analyzer.rootContext(NoCompilationUnit) - val results = global.analyzer.allViewsFrom(sym.tpe, context, sym.typeParams) + val results = global.analyzer.allViewsFrom(sym.tpe_*, context, sym.typeParams) var conversions = results.flatMap(result => makeImplicitConversion(sym, result._1, result._2, context, inTpl)) // also keep empty conversions, so they appear in diagrams // conversions = conversions.filter(!_.members.isEmpty) @@ -107,7 +101,7 @@ trait ModelFactoryImplicitSupport { hardcoded.arraySkipConversions.contains(conv.conversionQualifiedName)) // Filter out non-sensical conversions from value types - if (isPrimitiveValueType(sym.tpe)) + if (isPrimitiveValueType(sym.tpe_*)) conversions = conversions.filter((ic: ImplicitConversionImpl) => hardcoded.valueClassFilter(sym.nameString, ic.conversionQualifiedName)) @@ -127,13 +121,13 @@ trait ModelFactoryImplicitSupport { * 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: TypeTag[T4], ev2: Numeric[T4]): PimpedA - * - A is converted to PimpedA ONLY if a couple of constraints are satisfied: + * def enrichA[T3 <: Long, T4](a: A[Int, Foo[Bar[X]], T3, T4])(implicit ev1: TypeTag[T4], ev2: Numeric[T4]): EnrichedA + * - A is converted to EnrichedA 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 TypeTag[T4] within scope - * - the final type is PimpedA and A therefore inherits a couple of members from pimpedA + * - the final type is EnrichedA and A therefore inherits a couple of members from enrichA * * How? * some notes: @@ -176,7 +170,7 @@ trait ModelFactoryImplicitSupport { val newContext = context.makeImplicit(context.ambiguousErrors) newContext.macrosEnabled = false val newTyper = global.analyzer.newTyper(newContext) - newTyper.silent(_.typed(appliedTree, global.analyzer.EXPRmode, WildcardType), false) match { + newTyper.silent(_.typed(appliedTree, EXPRmode, WildcardType), false) match { case global.analyzer.SilentResultValue(t: Tree) => t case global.analyzer.SilentTypeError(err) => @@ -349,15 +343,6 @@ trait ModelFactoryImplicitSupport { makeRootPackage } - def targetTemplate: Option[TemplateEntity] = toType match { - // @Vlad: I'm being extra conservative in template creation -- I don't want to create templates for complex types - // such as refinement types because the template can't represent the type corectly (a template corresponds to a - // package, class, trait or object) - case t: TypeRef => Some(makeTemplate(t.sym)) - case RefinedType(parents, decls) => None - case _ => error("Scaladoc implicits: Could not create template for: " + toType + " of type " + toType.getClass); None - } - def targetTypeComponents: List[(TemplateEntity, TypeEntity)] = makeParentTypes(toType, None, inTpl) def convertorMethod: Either[MemberEntity, String] = { @@ -385,7 +370,6 @@ trait ModelFactoryImplicitSupport { lazy val memberImpls: List[MemberImpl] = { // Obtain the members inherited by the implicit conversion val memberSyms = toType.members.filter(implicitShouldDocument(_)).toList - val existingSyms = sym.info.members // Debugging part :) debug(sym.nameString + "\n" + "=" * sym.nameString.length()) @@ -422,66 +406,52 @@ trait ModelFactoryImplicitSupport { /* ========================= HELPER METHODS ========================== */ /** * Computes the shadowing table for all the members in the implicit conversions - * @param mbrs All template's members, including usecases and full signature members + * @param members All template's members, including usecases and full signature members * @param convs All the conversions the template takes part in - * @param inTpl the ususal :) + * @param inTpl the usual :) */ - def makeShadowingTable(mbrs: List[MemberImpl], + def makeShadowingTable(members: List[MemberImpl], convs: List[ImplicitConversionImpl], inTpl: DocTemplateImpl): Map[MemberEntity, ImplicitMemberShadowing] = { assert(modelFinished) - var shadowingTable = Map[MemberEntity, ImplicitMemberShadowing]() + val shadowingTable = mutable.Map[MemberEntity, ImplicitMemberShadowing]() + val membersByName: Map[Name, List[MemberImpl]] = members.groupBy(_.sym.name) + val convsByMember = (Map.empty[MemberImpl, ImplicitConversionImpl] /: convs) { + case (map, conv) => map ++ conv.memberImpls.map (_ -> conv) + } for (conv <- convs) { - val otherConvs = convs.filterNot(_ == conv) + val otherConvMembers: Map[Name, List[MemberImpl]] = convs filterNot (_ == conv) flatMap (_.memberImpls) groupBy (_.sym.name) for (member <- conv.memberImpls) { - // for each member in our list val sym1 = member.sym val tpe1 = conv.toType.memberInfo(sym1) - // check if it's shadowed by a member in the original class - var shadowedBySyms: List[Symbol] = List() - for (mbr <- mbrs) { - val sym2 = mbr.sym - if (sym1.name == sym2.name) { - val shadowed = !settings.docImplicitsSoundShadowing.value || { - val tpe2 = inTpl.sym.info.memberInfo(sym2) - !isDistinguishableFrom(tpe1, tpe2) - } - if (shadowed) - shadowedBySyms ::= sym2 - } + // check if it's shadowed by a member in the original class. + val shadowed = membersByName.get(sym1.name).toList.flatten filter { other => + !settings.docImplicitsSoundShadowing.value || !isDistinguishableFrom(tpe1, inTpl.sym.info.memberInfo(other.sym)) } - val shadowedByMembers = mbrs.filter((mb: MemberImpl) => shadowedBySyms.contains(mb.sym)) - - // check if it's shadowed by another member - var ambiguousByMembers: List[MemberEntity] = List() - for (conv <- otherConvs) - for (member2 <- conv.memberImpls) { - val sym2 = member2.sym - if (sym1.name == sym2.name) { - val tpe2 = conv.toType.memberInfo(sym2) - // Ambiguity should be an equivalence relation - val ambiguated = !isDistinguishableFrom(tpe1, tpe2) || !isDistinguishableFrom(tpe2, tpe1) - if (ambiguated) - ambiguousByMembers ::= member2 - } - } + // check if it's shadowed by another conversion. + val ambiguous = otherConvMembers.get(sym1.name).toList.flatten filter { other => + val tpe2 = convsByMember(other).toType.memberInfo(other.sym) + !isDistinguishableFrom(tpe1, tpe2) || !isDistinguishableFrom(tpe2, tpe1) + } // we finally have the shadowing info - val shadowing = new ImplicitMemberShadowing { - def shadowingMembers: List[MemberEntity] = shadowedByMembers - def ambiguatingMembers: List[MemberEntity] = ambiguousByMembers - } + if (!shadowed.isEmpty || !ambiguous.isEmpty) { + val shadowing = new ImplicitMemberShadowing { + def shadowingMembers: List[MemberEntity] = shadowed + def ambiguatingMembers: List[MemberEntity] = ambiguous + } - shadowingTable += (member -> shadowing) + shadowingTable += (member -> shadowing) + } } } - shadowingTable + shadowingTable.toMap } @@ -511,25 +481,25 @@ trait ModelFactoryImplicitSupport { /** * 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 - } - } + // 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] + * implicit def enrichMyClass[T](a: MyClass[T])(implicit ev: Numeric[T]): EnrichedMyClass[T] * the implicit view result type is: - * (a: MyClass[T])(implicit ev: Numeric[T]): PimpedMyClass[T] + * (a: MyClass[T])(implicit ev: Numeric[T]): EnrichedMyClass[T] * and the simplified type will be: - * MyClass[T] => PimpedMyClass[T] + * MyClass[T] => EnrichedMyClass[T] */ def removeImplicitParameters(viewType: Type): (Type, List[Type]) = { diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala index 844a509b7e..99e9059d79 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala @@ -8,13 +8,6 @@ import base._ import diagram._ import scala.collection._ -import scala.util.matching.Regex - -import symtab.Flags - -import io._ - -import model.{ RootPackage => RootPackageEntity } /** This trait extracts all required information for documentation from compilation units */ trait ModelFactoryTypeSupport { @@ -28,14 +21,11 @@ trait ModelFactoryTypeSupport { import global._ import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass } - import rootMirror.{ RootPackage, RootClass, EmptyPackage } protected val typeCache = new mutable.LinkedHashMap[Type, TypeEntity] /** */ def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = { - def templatePackage = closestPackage(inTpl.sym) - def createTypeEntity = new TypeEntity { private var nameBuffer = new StringBuilder private var refBuffer = new immutable.TreeMap[Int, (LinkTo, Int)] @@ -234,7 +224,6 @@ trait ModelFactoryTypeSupport { def appendClauses = { nameBuffer append " forSome {" var first = true - val qset = quantified.toSet for (sym <- quantified) { if (!first) { nameBuffer append ", " } else first = false if (sym.isSingletonExistential) { diff --git a/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala b/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala index bd7534ded4..b972649194 100755 --- a/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala @@ -21,7 +21,7 @@ trait TreeFactory { thisTreeFactory: ModelFactory with TreeFactory => def makeTree(rhs: Tree): Option[TreeEntity] = { - var expr = new StringBuilder + val expr = new StringBuilder var refs = new immutable.TreeMap[Int, (Entity, Int)] // start, (Entity to be linked to , end) rhs.pos match { @@ -39,7 +39,7 @@ trait TreeFactory { thisTreeFactory: ModelFactory with TreeFactory => * stores it in tree.refs with its position */ def makeLink(rhs: Tree){ - var start = pos.startOrPoint - firstIndex + val start = pos.startOrPoint - firstIndex val end = pos.endOrPoint - firstIndex if(start != end) { var asym = rhs.symbol diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala index c2aa1f17f3..150b293b81 100644 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala +++ b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala @@ -36,20 +36,12 @@ case class InheritanceDiagram(thisNode: ThisNode, override def isInheritanceDiagram = true lazy val depthInfo = new DepthInfo { def maxDepth = 3 - def nodeDepth(node: Node) = - if (node == thisNode) 1 - else if (superClasses.contains(node)) 0 - else if (subClasses.contains(node)) 2 - else if (incomingImplicits.contains(node) || outgoingImplicits.contains(node)) 1 - else -1 } } trait DepthInfo { /** Gives the maximum depth */ def maxDepth: Int - /** Gives the depth of any node in the diagram or -1 if the node is not in the diagram */ - def nodeDepth(node: Node): Int } abstract class Node { @@ -142,5 +134,4 @@ class ContentDiagramDepth(pack: ContentDiagram) extends DepthInfo { } val maxDepth = _maxDepth - def nodeDepth(node: Node) = _nodeDepth.getOrElse(node, -1) -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala index cd60865ce7..96bba0498c 100644 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala +++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala @@ -6,9 +6,6 @@ import model._ import java.util.regex.{Pattern, Matcher} import scala.util.matching.Regex -// statistics -import html.page.diagram.DiagramStats - /** * This trait takes care of parsing @{inheritance, content}Diagram annotations * @@ -153,7 +150,6 @@ trait DiagramDirectiveParser { private val NodeSpecRegex = "\\\"[A-Za-z\\*][A-Za-z\\.\\*]*\\\"" private val NodeSpecPattern = Pattern.compile(NodeSpecRegex) private val EdgeSpecRegex = "\\(" + NodeSpecRegex + "\\s*\\->\\s*" + NodeSpecRegex + "\\)" - private val EdgeSpecPattern = Pattern.compile(NodeSpecRegex) // And the composed regexes: private val HideNodesRegex = new Regex("^hideNodes(\\s*" + NodeSpecRegex + ")+$") private val HideEdgesRegex = new Regex("^hideEdges(\\s*" + EdgeSpecRegex + ")+$") @@ -182,7 +178,7 @@ trait DiagramDirectiveParser { def warning(message: String) = { // we need the position from the package object (well, ideally its comment, but yeah ...) val sym = if (template.sym.isPackage) template.sym.info.member(global.nme.PACKAGE) else template.sym - assert((sym != global.NoSymbol) || (sym == global.definitions.RootPackage)) + assert((sym != global.NoSymbol) || (sym == global.rootMirror.RootPackage)) global.reporter.warning(sym.pos, message) } diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala index 175b4a6472..ebac25bbe4 100644 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala @@ -3,7 +3,6 @@ package model package diagram import model._ -import scala.collection.mutable // statistics import html.page.diagram.DiagramStats @@ -47,7 +46,7 @@ trait DiagramFactory extends DiagramDirectiveParser { val thisNode = ThisNode(tpl.resultType, Some(tpl))(Some(tpl.qualifiedName + " (this " + tpl.kind + ")")) // superclasses - var superclasses: List[Node] = + val superclasses: List[Node] = tpl.parentTypes.collect { case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => NormalNode(p._2, Some(p._1))() }.reverse diff --git a/src/compiler/scala/tools/nsc/interactive/BuildManager.scala b/src/compiler/scala/tools/nsc/interactive/BuildManager.scala deleted file mode 100644 index 3e7ac573e9..0000000000 --- a/src/compiler/scala/tools/nsc/interactive/BuildManager.scala +++ /dev/null @@ -1,93 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2009-2013 Typesafe/Scala Solutions and LAMP/EPFL - * @author Iulian Dragos - * @author Hubert Plocinicak - */ -package scala.tools.nsc -package interactive - -import scala.collection._ - -import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} -import scala.reflect.internal.util.FakePos - -import dependencies._ -import io.AbstractFile -import scala.language.implicitConversions - -trait BuildManager { - - /** Add the given source files to the managed build process. */ - def addSourceFiles(files: Set[AbstractFile]) - - /** Remove the given files from the managed build process. */ - def removeFiles(files: Set[AbstractFile]) - - /** The given files have been modified by the user. Recompile - * them and their dependent files. - */ - def update(added: Set[AbstractFile], removed: Set[AbstractFile]) - - /** Notification that the supplied set of files is being built */ - def buildingFiles(included: Set[AbstractFile]) {} - - /** Load saved dependency information. */ - def loadFrom(file: AbstractFile, toFile: String => AbstractFile) : Boolean - - /** Save dependency information to `file`. */ - def saveTo(file: AbstractFile, fromFile: AbstractFile => String) - - def compiler: scala.tools.nsc.Global - - /** Delete classfiles derived from the supplied set of sources */ - def deleteClassfiles(sources : Set[AbstractFile]) { - val targets = compiler.dependencyAnalysis.dependencies.targets - for(source <- sources; cf <- targets(source)) - cf.delete - } -} - - -/** Simple driver for testing the build manager. It presents - * the user to a 'resident compiler' prompt. Each line is - * interpreted as a set of files that have changed. The builder - * then derives the dependent files and recompiles them. - */ -object BuildManagerTest extends EvalLoop { - - def prompt = "builder > " - - private def buildError(msg: String) { - println(msg + "\n scalac -help gives more information") - } - - def main(args: Array[String]) { - implicit def filesToSet(fs: List[String]): Set[AbstractFile] = { - def partition(s: String, r: Tuple2[List[AbstractFile], List[String]])= { - val v = AbstractFile.getFile(s) - if (v == null) (r._1, s::r._2) else (v::r._1, r._2) - } - val result = fs.foldRight((List[AbstractFile](), List[String]()))(partition) - if (!result._2.isEmpty) - Console.err.println("No such file(s): " + result._2.mkString(",")) - Set.empty ++ result._1 - } - - val settings = new Settings(buildError) - settings.Ybuildmanagerdebug.value = true - val command = new CompilerCommand(args.toList, settings) -// settings.make.value = "off" -// val buildManager: BuildManager = new SimpleBuildManager(settings) - val buildManager: BuildManager = new RefinedBuildManager(settings) - - buildManager.addSourceFiles(command.files) - - // enter resident mode - loop { line => - val args = line.split(' ').toList - val command = new CompilerCommand(args, settings) - buildManager.update(command.files, Set.empty) - } - - } -} diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index 73738ebd21..f10c00b8e0 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -7,8 +7,6 @@ package interactive import scala.util.control.ControlThrowable import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.symtab._ -import scala.tools.nsc.ast._ import scala.tools.nsc.util.FailedInterrupt import scala.tools.nsc.util.EmptyAction import scala.tools.nsc.util.WorkScheduler @@ -139,7 +137,6 @@ trait CompilerControl { self: Global => /** Sets sync var `response` to the fully attributed & typechecked tree contained in `source`. * @pre `source` needs to be loaded. - * * @note Deprecated because of race conditions in the typechecker when the background compiler * is interrupted while typing the same `source`. * @see SI-6578 diff --git a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala index b2568e34bd..93ef4c4d6c 100644 --- a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala +++ b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package interactive import scala.collection.mutable.ArrayBuffer -import scala.reflect.internal.util.Position trait ContextTrees { self: Global => diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 4c2c3e35f8..b0318f40c4 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -8,17 +8,13 @@ package interactive import java.io.{ PrintWriter, StringWriter, FileReader, FileWriter } import scala.collection.mutable import mutable.{LinkedHashMap, SynchronizedMap, HashSet, SynchronizedSet} -import scala.concurrent.SyncVar import scala.util.control.ControlThrowable import scala.tools.nsc.io.{ AbstractFile, LogReplay, Logger, NullLogger, Replayer } -import scala.tools.nsc.util.{ WorkScheduler, MultiHashMap } +import scala.tools.nsc.util.MultiHashMap import scala.reflect.internal.util.{ SourceFile, BatchSourceFile, Position, RangePosition, NoPosition } import scala.tools.nsc.reporters._ import scala.tools.nsc.symtab._ -import scala.tools.nsc.ast._ -import scala.tools.nsc.io.Pickler._ import scala.tools.nsc.typechecker.DivergentImplicit -import scala.annotation.tailrec import symtab.Flags.{ACCESSOR, PARAMACCESSOR} import scala.annotation.elidable import scala.language.implicitConversions @@ -363,7 +359,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } // don't forget to service interrupt requests - val iqs = scheduler.dequeueAllInterrupts(_.execute()) + scheduler.dequeueAllInterrupts(_.execute()) debugLog("ShutdownReq: cleaning work queue (%d items)".format(units.size)) debugLog("Cleanup up responses (%d loadedType pending, %d parsedEntered pending)" @@ -401,41 +397,6 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") if (typerRun != currentTyperRun) demandNewCompilerRun() } - def debugInfo(source : SourceFile, start : Int, length : Int): String = { - println("DEBUG INFO "+source+"/"+start+"/"+length) - val end = start+length - val pos = rangePos(source, start, start, end) - - val tree = locateTree(pos) - val sw = new StringWriter - val pw = new PrintWriter(sw) - newTreePrinter(pw).print(tree) - pw.flush - - val typed = new Response[Tree] - askTypeAt(pos, typed) - val typ = typed.get.left.toOption match { - case Some(tree) => - val sw = new StringWriter - val pw = new PrintWriter(sw) - newTreePrinter(pw).print(tree) - pw.flush - sw.toString - case None => "<None>" - } - - val completionResponse = new Response[List[Member]] - askTypeCompletion(pos, completionResponse) - val completion = completionResponse.get.left.toOption match { - case Some(members) => - members mkString "\n" - case None => "<None>" - } - - source.content.view.drop(start).take(length).mkString+" : "+source.path+" ("+start+", "+end+ - ")\n\nlocateTree:\n"+sw.toString+"\n\naskTypeAt:\n"+typ+"\n\ncompletion:\n"+completion - } - // ----------------- The Background Runner Thread ----------------------- private var threadId = 0 @@ -903,8 +864,6 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") respond(response) { scopeMembers(pos) } } - private val Dollar = newTermName("$") - private class Members[M <: Member] extends LinkedHashMap[Name, Set[M]] { override def default(key: Name) = Set() @@ -920,7 +879,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") def add(sym: Symbol, pre: Type, implicitlyAdded: Boolean)(toMember: (Symbol, Type) => M) { if ((sym.isGetter || sym.isSetter) && sym.accessed != NoSymbol) { add(sym.accessed, pre, implicitlyAdded)(toMember) - } else if (!sym.name.decodedName.containsName(Dollar) && !sym.isSynthetic && sym.hasRawInfo) { + } else if (!sym.name.decodedName.containsName("$") && !sym.isSynthetic && sym.hasRawInfo) { val symtpe = pre.memberType(sym) onTypeError ErrorType matching(sym, symtpe, this(sym.name)) match { case Some(m) => @@ -1043,7 +1002,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") for (sym <- ownerTpe.members) addTypeMember(sym, pre, sym.owner != ownerTpe.typeSymbol, NoSymbol) members.allMembers #:: { - //print("\nadd pimped") + //print("\nadd enrichment") val applicableViews: List[SearchResult] = if (ownerTpe.isErroneous) List() else new ImplicitSearch( @@ -1113,7 +1072,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } @deprecated("SI-6458: Instrumentation logic will be moved out of the compiler.","2.10.0") - def getInstrumented(source: SourceFile, line: Int, response: Response[(String, Array[Char])]) = + def getInstrumented(source: SourceFile, line: Int, response: Response[(String, Array[Char])]) { try { interruptsEnabled = false respond(response) { @@ -1122,18 +1081,10 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } finally { interruptsEnabled = true } + } // ---------------- Helper classes --------------------------- - /** A transformer that replaces tree `from` with tree `to` in a given tree */ - class TreeReplacer(from: Tree, to: Tree) extends Transformer { - override def transform(t: Tree): Tree = { - if (t == from) to - else if ((t.pos includes from.pos) || t.pos.isTransparent) super.transform(t) - else t - } - } - /** The typer run */ class TyperRun extends Run { // units is always empty @@ -1153,7 +1104,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") * @return true iff typechecked correctly */ private def applyPhase(phase: Phase, unit: CompilationUnit) { - atPhase(phase) { phase.asInstanceOf[GlobalPhase] applyPhase unit } + enteringPhase(phase) { phase.asInstanceOf[GlobalPhase] applyPhase unit } } } diff --git a/src/compiler/scala/tools/nsc/interactive/Picklers.scala b/src/compiler/scala/tools/nsc/interactive/Picklers.scala index 84cb03c140..09533fdeb5 100644 --- a/src/compiler/scala/tools/nsc/interactive/Picklers.scala +++ b/src/compiler/scala/tools/nsc/interactive/Picklers.scala @@ -6,12 +6,10 @@ package scala.tools.nsc package interactive import util.InterruptReq -import scala.reflect.internal.util.{SourceFile, BatchSourceFile} -import io.{AbstractFile, PlainFile} - +import scala.reflect.internal.util.{ SourceFile, BatchSourceFile } +import io.{ AbstractFile, PlainFile, Pickler, CondPickler } import util.EmptyAction -import scala.reflect.internal.util.{Position, RangePosition, NoPosition, OffsetPosition, TransparentPosition} -import io.{Pickler, CondPickler} +import scala.reflect.internal.util.{ RangePosition, OffsetPosition, TransparentPosition } import io.Pickler._ import scala.collection.mutable import mutable.ListBuffer diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala index 7b89d5b0aa..d545a5738c 100644 --- a/src/compiler/scala/tools/nsc/interactive/REPL.scala +++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala @@ -5,15 +5,11 @@ package scala.tools.nsc package interactive -import scala.concurrent.SyncVar import scala.reflect.internal.util._ -import scala.tools.nsc.symtab._ -import scala.tools.nsc.ast._ import scala.tools.nsc.reporters._ import scala.tools.nsc.io._ import scala.tools.nsc.scratchpad.SourceInserter -import scala.tools.nsc.interpreter.AbstractFileClassLoader -import java.io.{File, FileWriter} +import java.io.FileWriter /** Interface of interactive compiler to a client such as an IDE */ @@ -63,7 +59,7 @@ object REPL { def main(args: Array[String]) { process(args) - /*sys.*/exit(if (reporter.hasErrors) 1 else 0)// Don't use sys yet as this has to run on 2.8.2 also. + sys.exit(if (reporter.hasErrors) 1 else 0) } def loop(action: (String) => Unit) { @@ -186,7 +182,7 @@ object REPL { println(instrument(arguments, line.toInt)) case List("quit") => comp.askShutdown() - exit(1) // Don't use sys yet as this has to run on 2.8.2 also. + sys.exit(1) case List("structure", file) => doStructure(file) case _ => diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala index b95f1fa7ca..5a1a4cbdeb 100644 --- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala +++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala @@ -8,7 +8,6 @@ package interactive import ast.Trees import ast.Positions import scala.reflect.internal.util.{SourceFile, Position, RangePosition, NoPosition} -import scala.tools.nsc.util.WorkScheduler import scala.collection.mutable.ListBuffer /** Handling range positions @@ -60,7 +59,7 @@ self: scala.tools.nsc.Global => } // -------------- ensuring no overlaps ------------------------------- - + /** Ensure that given tree has no positions that overlap with * any of the positions of `others`. This is done by * shortening the range, assigning TransparentPositions @@ -144,7 +143,7 @@ self: scala.tools.nsc.Global => */ private def setChildrenPos(pos: Position, trees: List[Tree]): Unit = try { for (tree <- trees) { - if (!tree.isEmpty && tree.pos == NoPosition) { + if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) { val children = tree.children if (children.isEmpty) { tree setPos pos.focus @@ -165,7 +164,7 @@ self: scala.tools.nsc.Global => */ override def atPos[T <: Tree](pos: Position)(tree: T): T = { if (pos.isOpaqueRange) { - if (!tree.isEmpty && tree.pos == NoPosition) { + if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) { tree.setPos(pos) val children = tree.children if (children.nonEmpty) { @@ -203,7 +202,7 @@ self: scala.tools.nsc.Global => def validate(tree: Tree, encltree: Tree): Unit = { - if (!tree.isEmpty) { + if (!tree.isEmpty && tree.canHaveAttrs) { if (settings.Yposdebug.value && (settings.verbose.value || settings.Yrangepos.value)) println("[%10s] %s".format("validate", treeStatus(tree, encltree))) diff --git a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala deleted file mode 100644 index b2ef45a7d8..0000000000 --- a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala +++ /dev/null @@ -1,355 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2009-2013 Typesafe/Scala Solutions and LAMP/EPFL - * @author Iulian Dragos - * @author Hubert Plocinicak - */ -package scala.tools.nsc -package interactive - -import scala.collection._ -import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} -import scala.util.control.Breaks._ -import scala.tools.nsc.symtab.Flags - -import dependencies._ -import scala.reflect.internal.util.FakePos -import util.ClassPath -import io.AbstractFile -import scala.tools.util.PathResolver - -/** A more defined build manager, based on change sets. For each - * updated source file, it computes the set of changes to its - * definitions, then checks all dependent units to see if the - * changes require a compilation. It repeats this process until - * a fixpoint is reached. - */ -@deprecated("Use sbt incremental compilation mechanism", "2.10.0") -class RefinedBuildManager(val settings: Settings) extends Changes with BuildManager { - - class BuilderGlobal(settings: Settings, reporter : Reporter) extends scala.tools.nsc.Global(settings, reporter) { - - def this(settings: Settings) = - this(settings, new ConsoleReporter(settings)) - - override def computeInternalPhases() { - super.computeInternalPhases - phasesSet += dependencyAnalysis - } - lazy val _classpath = new NoSourcePathPathResolver(settings).result - override def classPath = _classpath.asInstanceOf[ClassPath[platform.BinaryRepr]] - // See discussion in JavaPlatForm for why we need a cast here. - - def newRun() = new Run() - } - - class NoSourcePathPathResolver(settings: Settings) extends PathResolver(settings) { - override def containers = Calculated.basis.dropRight(1).flatten.distinct - } - - protected def newCompiler(settings: Settings) = new BuilderGlobal(settings) - - val compiler = newCompiler(settings) - import compiler.{ Symbol, Type, beforeErasure } - import compiler.dependencyAnalysis.Inherited - - private case class SymWithHistory(sym: Symbol, befErasure: Type) - - /** Managed source files. */ - private val sources: mutable.Set[AbstractFile] = new mutable.HashSet[AbstractFile] - - private val definitions: mutable.Map[AbstractFile, List[SymWithHistory]] = - new mutable.HashMap[AbstractFile, List[SymWithHistory]] { - override def default(key: AbstractFile) = Nil - } - - /** External references used by source file. */ - private var references: mutable.Map[AbstractFile, immutable.Set[String]] = _ - - /** External references for inherited members */ - private var inherited: mutable.Map[AbstractFile, immutable.Set[Inherited]] = _ - - /** Reverse of definitions, used for caching */ - private var classes: mutable.Map[String, AbstractFile] = - new mutable.HashMap[String, AbstractFile] { - override def default(key: String) = null - } - - /** Add the given source files to the managed build process. */ - def addSourceFiles(files: Set[AbstractFile]) { - sources ++= files - update(files) - } - - /** Remove the given files from the managed build process. */ - def removeFiles(files: Set[AbstractFile]) { - sources --= files - deleteClassfiles(files) - update(invalidatedByRemove(files)) - } - - /** Return the set of invalidated files caused by removing the given files. - */ - private def invalidatedByRemove(files: Set[AbstractFile]): Set[AbstractFile] = { - val changes = new mutable.HashMap[Symbol, List[Change]] - for (f <- files; SymWithHistory(sym, _) <- definitions(f)) - changes += sym -> List(Removed(Class(sym.fullName))) - invalidated(files, changes) - } - - def update(added: Set[AbstractFile], removed: Set[AbstractFile]) { - sources --= removed - deleteClassfiles(removed) - update(added ++ invalidatedByRemove(removed)) - } - - /** The given files have been modified by the user. Recompile - * them and all files that depend on them. Only files that - * have been previously added as source files are recompiled. - * Files that were already compiled are taken out from the result - * of the dependency analysis. - */ - private def update(files: Set[AbstractFile]) = { - val coll: mutable.Map[AbstractFile, immutable.Set[AbstractFile]] = - mutable.HashMap[AbstractFile, immutable.Set[AbstractFile]]() - compiler.reporter.reset() - - // See if we really have corresponding symbols, not just those - // which share the name - def isCorrespondingSym(from: Symbol, to: Symbol): Boolean = - (from.hasFlag(Flags.TRAIT) == to.hasFlag(Flags.TRAIT)) && // has to run in 2.8, so no hasTraitFlag - (from.hasFlag(Flags.MODULE) == to.hasFlag(Flags.MODULE)) - - // For testing purposes only, order irrelevant for compilation - def toStringSet(set: Set[AbstractFile]): String = - set.toList sortBy (_.name) mkString("Set(", ", ", ")") - - def update0(files: Set[AbstractFile]): Unit = if (!files.isEmpty) { - deleteClassfiles(files) - val run = compiler.newRun() - if (settings.Ybuildmanagerdebug.value) - compiler.inform("compiling " + toStringSet(files)) - buildingFiles(files) - - run.compileFiles(files.toList) - if (compiler.reporter.hasErrors) { - return - } - - // Deterministic behaviour required by partest - val changesOf = new mutable.HashMap[Symbol, List[Change]] { - override def toString: String = { - val changesOrdered = - toList.map(e => { - e._1.toString + " -> " + - e._2.sortBy(_.toString).mkString("List(", ", ", ")") - }) - changesOrdered.sorted.mkString("Map(", ", ", ")") - } - } - val additionalDefs: mutable.HashSet[AbstractFile] = mutable.HashSet.empty - - val defs = compiler.dependencyAnalysis.definitions - for (src <- files) { - if (definitions(src).isEmpty) - additionalDefs ++= compiler.dependencyAnalysis. - dependencies.dependentFiles(1, mutable.Set(src)) - else { - val syms = defs(src) - for (sym <- syms) { - definitions(src).find( - s => (s.sym.fullName == sym.fullName) && - isCorrespondingSym(s.sym, sym)) match { - case Some(SymWithHistory(oldSym, info)) => - val changes = changeSet(oldSym.info, sym) - val changesErasure = beforeErasure(changeSet(info, sym)) - - changesOf(oldSym) = (changes ++ changesErasure).distinct - case _ => - // a new top level definition - changesOf(sym) = sym.parentSymbols filter (_.isSealed) map (p => - changeChangeSet(p, sym+" extends a sealed "+p)) - } - } - // Create a change for the top level classes that were removed - val removed = definitions(src) filterNot ((s:SymWithHistory) => - syms.find(_.fullName == (s.sym.fullName)) != None) - for (s <- removed) { - changesOf(s.sym) = List(removeChangeSet(s.sym)) - } - } - } - if (settings.Ybuildmanagerdebug.value) - compiler.inform("Changes: " + changesOf) - updateDefinitions(files) - val invalid = invalidated(files, changesOf, additionalDefs) - update0(checkCycles(invalid, files, coll)) - } - - update0(files) - // remove the current run in order to save some memory - compiler.dropRun() - } - - // Attempt to break the cycling reference deps as soon as possible and reduce - // the number of compilations to minimum without having too coarse grained rules - private def checkCycles(files: Set[AbstractFile], initial: Set[AbstractFile], - collect: mutable.Map[AbstractFile, immutable.Set[AbstractFile]]): - Set[AbstractFile] = { - def followChain(set: Set[AbstractFile], rest: immutable.Set[AbstractFile]): - immutable.Set[AbstractFile] = { - val deps:Set[AbstractFile] = set.flatMap( - s => collect.get(s) match { - case Some(x) => x - case _ => Set[AbstractFile]() - }) - val newDeps = deps -- rest - if (newDeps.isEmpty) rest else followChain(newDeps, rest ++ newDeps) - } - var res:Set[AbstractFile] = mutable.Set() - files.foreach( f => - if (collect contains f) { - val chain = followChain(Set(f), immutable.Set()) ++ files - chain.foreach((fc: AbstractFile) => collect += fc -> chain) - res ++= chain - } else - res += f - ) - - initial.foreach((f: AbstractFile) => collect += (f -> (collect.getOrElse(f, immutable.Set()) ++ res))) - if (res.subsetOf(initial)) Set() else res - } - - /** Return the set of source files that are invalidated by the given changes. */ - def invalidated(files: Set[AbstractFile], changesOf: scala.collection.Map[Symbol, List[Change]], - processed: Set[AbstractFile] = Set.empty): - Set[AbstractFile] = { - val buf = new mutable.HashSet[AbstractFile] - val newChangesOf = new mutable.HashMap[Symbol, List[Change]] - var directDeps = - compiler.dependencyAnalysis.dependencies.dependentFiles(1, files) - - def invalidate(file: AbstractFile, reason: String, change: Change) = { - if (settings.Ybuildmanagerdebug.value) - compiler.inform("invalidate " + file + " because " + reason + " [" + change + "]") - buf += file - directDeps -= file - for (syms <- definitions(file)) // fixes #2557 - newChangesOf(syms.sym) = List(change, parentChangeSet(syms.sym)) - break - } - - for ((oldSym, changes) <- changesOf; change <- changes) { - def checkParents(cls: Symbol, file: AbstractFile) { - val parentChange = cls.parentSymbols exists (_.fullName == oldSym.fullName) - // if (settings.buildmanagerdebug.value) - // compiler.inform("checkParents " + cls + " oldSym: " + oldSym + " parentChange: " + parentChange + " " + cls.info.parents) - change match { - case Changed(Class(_)) if parentChange => - invalidate(file, "parents have changed", change) - - case Changed(Definition(_)) if parentChange => - invalidate(file, "inherited method changed", change) - - case Added(Definition(_)) if parentChange => - invalidate(file, "inherited new method", change) - - case Removed(Definition(_)) if parentChange => - invalidate(file, "inherited method removed", change) - - case _ => () - } - } - - def checkInterface(cls: Symbol, file: AbstractFile) { - change match { - case Added(Definition(name)) => - if (cls.info.decls.iterator.exists(_.fullName == name)) - invalidate(file, "of new method with existing name", change) - case Changed(Class(name)) => - if (cls.info.typeSymbol.fullName == name) - invalidate(file, "self type changed", change) - case _ => - () - } - } - - def checkReferences(file: AbstractFile) { - //if (settings.buildmanagerdebug.value) - // compiler.inform(file + ":" + references(file)) - val refs = references(file) - if (refs.isEmpty) - invalidate(file, "it is a direct dependency and we don't yet have finer-grained dependency information", change) - else { - change match { - case Removed(Definition(name)) if refs(name) => - invalidate(file, "it references deleted definition", change) - case Removed(Class(name)) if (refs(name)) => - invalidate(file, "it references deleted class", change) - case Changed(Class(name)) if (refs(name)) => - invalidate(file, "it references changed class", change) - case Changed(Definition(name)) if (refs(name)) => - invalidate(file, "it references changed definition", change) - case Added(Definition(name)) if (refs(name)) => - invalidate(file, "it references added definition", change) - case _ => () - } - } - } - - def checkInheritedReferences(file: AbstractFile) { - val refs = inherited(file) - if (!refs.isEmpty) - change match { - case ParentChanged(Class(name)) => - for (Inherited(q, member) <- refs.find(p => (p != null && p.qualifier == name)); - classFile <- classes.get(q); - defs <- definitions.get(classFile); - s <- defs.find(p => p.sym.fullName == q) - if ((s.sym).tpe.nonPrivateMember(member) == compiler.NoSymbol)) - invalidate(file, "it references invalid (no longer inherited) definition", change) - () - case _ => () - } - } - - for (file <- directDeps) { - breakable { - for (cls <- definitions(file)) checkParents(cls.sym, file) - for (cls <- definitions(file)) checkInterface(cls.sym, file) - checkReferences(file) - checkInheritedReferences(file) - } - } - } - if (buf.isEmpty) - processed - else - invalidated(buf.clone() --= processed, newChangesOf, processed ++ buf) - } - - /** Update the map of definitions per source file */ - private def updateDefinitions(files: Set[AbstractFile]) { - for (src <- files; localDefs = compiler.dependencyAnalysis.definitions(src)) { - definitions(src) = (localDefs map (s => { - this.classes += s.fullName -> src - SymWithHistory(s.cloneSymbol, beforeErasure(s.info.cloneInfo(s))) - })) - } - this.references = compiler.dependencyAnalysis.references - this.inherited = compiler.dependencyAnalysis.inherited - } - - /** Load saved dependency information. */ - def loadFrom(file: AbstractFile, toFile: String => AbstractFile) : Boolean = { - val success = compiler.dependencyAnalysis.loadFrom(file, toFile) - if (success) - sources ++= compiler.dependencyAnalysis.managedFiles - success - } - - /** Save dependency information to `file`. */ - def saveTo(file: AbstractFile, fromFile: AbstractFile => String) { - compiler.dependencyAnalysis.dependenciesFile = file - compiler.dependencyAnalysis.saveDependencies(fromFile) - } -} diff --git a/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala deleted file mode 100644 index 465dcaaf1c..0000000000 --- a/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala +++ /dev/null @@ -1,103 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2009-2013 Typesafe/Scala Solutions and LAMP/EPFL - * @author Martin Odersky - */ -package scala.tools.nsc -package interactive - -import scala.collection._ - -import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} -import dependencies._ - -import scala.reflect.internal.util.FakePos -import io.AbstractFile - -/** A simple build manager, using the default scalac dependency tracker. - * The transitive closure of all dependent files on a modified file - * is recompiled at once. - * - * It is equivalent to using a resident compiler mode with the - * '-make:transitive' option. - */ -class SimpleBuildManager(val settings: Settings) extends BuildManager { - - class BuilderGlobal(settings: Settings, reporter : Reporter) extends scala.tools.nsc.Global(settings, reporter) { - - def this(settings: Settings) = - this(settings, new ConsoleReporter(settings)) - - def newRun() = new Run() - } - - protected def newCompiler(settings: Settings) = new BuilderGlobal(settings) - - val compiler = newCompiler(settings) - - /** Managed source files. */ - private val sources: mutable.Set[AbstractFile] = new mutable.HashSet[AbstractFile] - - /** Add the given source files to the managed build process. */ - def addSourceFiles(files: Set[AbstractFile]) { - sources ++= files - update(files) - } - - /** Remove the given files from the managed build process. */ - def removeFiles(files: Set[AbstractFile]) { - sources --= files - deleteClassfiles(files) - update(invalidatedByRemove(files)) - } - - - /** Return the set of invalidated files caused by removing the given files. */ - private def invalidatedByRemove(files: Set[AbstractFile]): Set[AbstractFile] = { - val deps = compiler.dependencyAnalysis.dependencies - deps.dependentFiles(Int.MaxValue, files) - } - - def update(added: Set[AbstractFile], removed: Set[AbstractFile]) { - sources --= removed - deleteClassfiles(removed) - update(added ++ invalidatedByRemove(removed)) - } - - /** The given files have been modified by the user. Recompile - * them and all files that depend on them. Only files that - * have been previously added as source files are recompiled. - */ - def update(files: Set[AbstractFile]) { - deleteClassfiles(files) - - val deps = compiler.dependencyAnalysis.dependencies - val run = compiler.newRun() - compiler.inform("compiling " + files) - - val toCompile = - (files ++ deps.dependentFiles(Int.MaxValue, files)) intersect sources - - - compiler.inform("Recompiling " + - (if(settings.debug.value) toCompile.mkString(", ") - else toCompile.size + " files")) - - buildingFiles(toCompile) - - run.compileFiles(files.toList) - } - - /** Load saved dependency information. */ - def loadFrom(file: AbstractFile, toFile: String => AbstractFile) : Boolean = { - val success = compiler.dependencyAnalysis.loadFrom(file, toFile) - if (success) - sources ++= compiler.dependencyAnalysis.managedFiles - success - } - - /** Save dependency information to `file`. */ - def saveTo(file: AbstractFile, fromFile: AbstractFile => String) { - compiler.dependencyAnalysis.dependenciesFile = file - compiler.dependencyAnalysis.saveDependencies(fromFile) - } -} diff --git a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala index 597b9012ce..a4a2de9b51 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala @@ -7,14 +7,6 @@ package interactive package tests import core._ - -import java.io.File.pathSeparatorChar -import java.io.File.separatorChar - -import scala.annotation.migration -import scala.reflect.internal.util.Position -import scala.reflect.internal.util.SourceFile - import scala.collection.mutable.ListBuffer /** A base class for writing interactive compiler tests. @@ -109,6 +101,7 @@ abstract class InteractiveTest } /** Perform n random tests with random changes. */ + /**** private def randomTests(n: Int, files: Array[SourceFile]) { val tester = new Tester(n, files, settings) { override val compiler = self.compiler @@ -116,6 +109,7 @@ abstract class InteractiveTest } tester.run() } + ****/ /** shutdown the presentation compiler. */ protected def shutdown() { diff --git a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTestSettings.scala b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTestSettings.scala index 4d85ab9d88..ad5c61b2b0 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTestSettings.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTestSettings.scala @@ -25,7 +25,6 @@ trait InteractiveTestSettings extends TestSettings with PresentationCompilerInst * test. */ override protected def prepareSettings(settings: Settings) { - import java.io.File._ def adjustPaths(paths: settings.PathSetting*) { for (p <- paths if argsString.contains(p.name)) p.value = p.value.map { case '/' => separatorChar @@ -45,10 +44,10 @@ trait InteractiveTestSettings extends TestSettings with PresentationCompilerInst case _ => () } - // Make the --sourcepath path provided in the .flags file (if any) relative to the test's base directory + // Make the --sourcepath path provided in the .flags file (if any) relative to the test's base directory if(settings.sourcepath.isSetByUser) settings.sourcepath.value = (baseDir / Path(settings.sourcepath.value)).path - + adjustPaths(settings.bootclasspath, settings.classpath, settings.javabootclasspath, settings.sourcepath) } @@ -67,4 +66,4 @@ trait InteractiveTestSettings extends TestSettings with PresentationCompilerInst reporter.println("\targsString: %s".format(argsString)) super.printClassPath(reporter) } -}
\ No newline at end of file +} 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 c8e6b6ccce..9085eb56e6 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala @@ -3,7 +3,6 @@ package interactive package tests.core import scala.reflect.internal.util.Position -import scala.tools.nsc.interactive.tests.core._ /** Set of core test definitions that are executed for each test run. */ private[tests] trait CoreTestDefs @@ -77,7 +76,8 @@ private[tests] trait CoreTestDefs // 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 - val sourceFile = sourceFiles.find(_.path == treePath) match { + + sourceFiles.find(_.path == treePath) match { case Some(source) => compiler.askLinkPos(tree.symbol, source, r) r.get match { @@ -97,4 +97,4 @@ private[tests] trait CoreTestDefs } } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala index f304eda753..5cda0e53fb 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala @@ -3,7 +3,6 @@ package interactive package tests.core import reporters.{Reporter => CompilerReporter} -import scala.reflect.internal.util.Position /** Trait encapsulating the creation of a presentation compiler's instance.*/ private[tests] trait PresentationCompilerInstance extends TestSettings { diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerTestDef.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerTestDef.scala index 9cf2aa4fe4..4d5b4e1129 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerTestDef.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerTestDef.scala @@ -1,6 +1,5 @@ package scala.tools.nsc.interactive.tests.core -import scala.tools.nsc.interactive.Global import scala.reflect.internal.util.Position trait PresentationCompilerTestDef { @@ -16,4 +15,4 @@ trait PresentationCompilerTestDef { protected def format(pos: Position): String = (if(pos.isDefined) "(%d,%d)".format(pos.line, pos.column) else "<no position>") -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala index e80b741a8d..676feeba8a 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala @@ -4,7 +4,6 @@ import scala.reflect.internal.util.{SourceFile,BatchSourceFile} import scala.tools.nsc.io.{AbstractFile,Path} private[tests] object SourcesCollector { - import Path._ type SourceFilter = Path => Boolean /** @@ -17,6 +16,5 @@ private[tests] object SourcesCollector { } private def source(file: Path): SourceFile = source(AbstractFile.getFile(file.toFile)) - private def source(filename: String): SourceFile = source(AbstractFile.getFile(filename)) private def source(file: AbstractFile): SourceFile = new BatchSourceFile(file) -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala index 638bca8a72..9fbd337b9d 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala @@ -5,9 +5,9 @@ package scala.tools.nsc package interpreter -import scala.tools.nsc.io.{ File, AbstractFile } +import scala.tools.nsc.io.AbstractFile import util.ScalaClassLoader -import java.net.URL +import java.net.{ URL, URLConnection, URLStreamHandler } import scala.collection.{ mutable, immutable } /** @@ -25,7 +25,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) protected def findAbstractFile(name: String): AbstractFile = { var file: AbstractFile = root - val pathParts = classNameToPath(name) split '/' + val pathParts = name split '/' for (dirPart <- pathParts.init) { file = file.lookupName(dirPart, true) @@ -55,11 +55,26 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) return file } + // parent delegation in JCL uses getResource; so either add parent.getResAsStream + // or implement findResource, which we do here as a study in scarlet (my complexion + // after looking at CLs and URLs) + override def findResource(name: String): URL = findAbstractFile(name) match { + case null => null + case file => new URL(null, "repldir:" + file.path, new URLStreamHandler { + override def openConnection(url: URL): URLConnection = new URLConnection(url) { + override def connect() { } + override def getInputStream = file.input + } + }) + } + + // this inverts delegation order: super.getResAsStr calls parent.getRes if we fail override def getResourceAsStream(name: String) = findAbstractFile(name) match { case null => super.getResourceAsStream(name) case file => file.input } - override def classBytes(name: String): Array[Byte] = findAbstractFile(name) match { + // ScalaClassLoader.classBytes uses getResAsStream, so we'll try again before delegating + override def classBytes(name: String): Array[Byte] = findAbstractFile(classNameToPath(name)) match { case null => super.classBytes(name) case file => file.toByteArray } diff --git a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala index 40e9d3d600..48890a21c6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package interpreter import java.lang.reflect -import java.util.concurrent.ConcurrentHashMap import util.ScalaClassLoader import ScalaClassLoader.appLoader import scala.reflect.NameTransformer._ @@ -39,25 +38,5 @@ object ByteCode { } yield names - /** Attempts to retrieve case parameter names for given class name. - */ - def caseParamNamesForPath(path: String) = - for { - module <- DECODER - method <- decoderMethod("caseParamNames", classOf[String]) - names <- method.invoke(module, path).asInstanceOf[Option[List[String]]] - } - yield names - def aliasesForPackage(pkg: String) = aliasMap flatMap (_(pkg)) - - /** Attempts to find type aliases in package objects. - */ - def aliasForType(path: String): Option[String] = { - val (pkg, name) = (path lastIndexOf '.') match { - case -1 => return None - case idx => (path take idx, path drop (idx + 1)) - } - aliasesForPackage(pkg) flatMap (_ get name) - } } diff --git a/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala deleted file mode 100644 index 1741a82775..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import CodeHandlers.NoSuccess -import scala.util.control.ControlThrowable - -/** - * The start of a simpler interface for utilizing the compiler with piecemeal - * code strings. The "T" here could potentially be a Tree, a Type, a Symbol, - * a Boolean, or something even more exotic. - */ -trait CodeHandlers[T] { - self => - - // Expressions are composed of operators and operands. - def expr(code: String): T - - // Statements occur as parts of blocks and templates. - // A statement can be an import, a definition or an expression, or it can be empty. - // Statements used in the template of a class definition can also be declarations. - def stmt(code: String): T - def stmts(code: String): Seq[T] - - object opt extends CodeHandlers[Option[T]] { - val handler: PartialFunction[Throwable, Option[T]] = { - case _: NoSuccess => None - } - val handlerSeq: PartialFunction[Throwable, Seq[Option[T]]] = { - case _: NoSuccess => Nil - } - - def expr(code: String) = try Some(self.expr(code)) catch handler - def stmt(code: String) = try Some(self.stmt(code)) catch handler - def stmts(code: String) = try (self.stmts(code) map (x => Some(x))) catch handlerSeq - } -} - -object CodeHandlers { - def incomplete() = throw CodeIncomplete - def fail(msg: String) = throw new CodeException(msg) - - trait NoSuccess extends ControlThrowable - class CodeException(msg: String) extends RuntimeException(msg) with NoSuccess { } - object CodeIncomplete extends CodeException("CodeIncomplete") -} diff --git a/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala b/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala index 8042f0aee2..0ab92ab769 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala +++ b/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala @@ -10,5 +10,4 @@ package interpreter */ class CommandLine(arguments: List[String], error: String => Unit) extends CompilerCommand(arguments, error) { override def cmdName = "scala" - override lazy val fileEndings = List(".scalaint") } diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala index 1dfccbfbf7..84a5cb49ae 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala @@ -23,8 +23,6 @@ object NoCompletion extends Completion { } object Completion { - def empty: Completion = NoCompletion - case class Candidates(cursor: Int, candidates: List[String]) { } val NoCandidates = Candidates(-1, Nil) diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala index ab96f415db..3dd5d93390 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala +++ b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala @@ -6,8 +6,6 @@ package scala.tools.nsc package interpreter -import scala.reflect.NameTransformer - /** An interface for objects which are aware of tab completion and * will supply their own candidates and resolve their own paths. */ @@ -53,31 +51,3 @@ trait CompletionAware { results.sorted } } - -object CompletionAware { - val Empty = new CompletionAware { def completions(verbosity: Int) = Nil } - - def unapply(that: Any): Option[CompletionAware] = that match { - case x: CompletionAware => Some((x)) - case _ => None - } - - /** Create a CompletionAware object from the given functions. - * The first should generate the list of completions whenever queried, - * and the second should return Some(CompletionAware) object if - * subcompletions are possible. - */ - def apply(terms: () => List[String], followFunction: String => Option[CompletionAware]): CompletionAware = - new CompletionAware { - def completions = terms() - def completions(verbosity: Int) = completions - override def follow(id: String) = followFunction(id) - } - - /** Convenience factories. - */ - def apply(terms: () => List[String]): CompletionAware = apply(terms, _ => None) - def apply(map: scala.collection.Map[String, CompletionAware]): CompletionAware = - apply(() => map.keys.toList, map.get _) -} - diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala index d14b5c79e0..d24ad60974 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala +++ b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala @@ -38,7 +38,6 @@ trait CompletionOutput { def relativize(str: String): String = quietString(str stripPrefix (pkg + ".")) def relativize(tp: Type): String = relativize(tp.dealiasWiden.toString) - def relativize(sym: Symbol): String = relativize(sym.info) def braceList(tparams: List[String]) = if (tparams.isEmpty) "" else (tparams map relativize).mkString("[", ", ", "]") def parenList(params: List[Any]) = params.mkString("(", ", ", ")") @@ -76,7 +75,7 @@ trait CompletionOutput { } def methodString() = - method.keyString + " " + method.nameString + (method.info.normalize match { + method.keyString + " " + method.nameString + (method.info.dealiasWiden match { case NullaryMethodType(resType) => ": " + typeToString(resType) case PolyType(tparams, resType) => tparamsString(tparams) + typeToString(resType) case mt @ MethodType(_, _) => methodTypeToString(mt) diff --git a/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala b/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala index 07e36f4f27..48af261937 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala @@ -7,19 +7,12 @@ package scala.tools.nsc package interpreter import scala.tools.jline.console.{ ConsoleReader, CursorBuffer } -import scala.tools.jline.console.completer.CompletionHandler -import Completion._ trait ConsoleReaderHelper extends ConsoleReader { - def currentLine = "" + getCursorBuffer.buffer - def currentPos = getCursorBuffer.cursor def terminal = getTerminal() def width = terminal.getWidth() def height = terminal.getHeight() - def paginate = isPaginationEnabled() - def paginate_=(value: Boolean) = setPaginationEnabled(value) - def goBack(num: Int): Unit def readOneKey(prompt: String): Int def eraseLine(): Unit diff --git a/src/compiler/scala/tools/nsc/interpreter/Delimited.scala b/src/compiler/scala/tools/nsc/interpreter/Delimited.scala index 80debfacb9..e88a044931 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Delimited.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Delimited.scala @@ -26,7 +26,6 @@ trait Delimited { def delimited: Char => Boolean def escapeChars: List[Char] = List('\\') - def quoteChars: List[(Char, Char)] = List(('\'', '\''), ('"', '"')) /** Break String into args based on delimiting function. */ @@ -39,6 +38,4 @@ trait Delimited { def isDelimiterChar(ch: Char) = delimited(ch) def isEscapeChar(ch: Char): Boolean = escapeChars contains ch - def isQuoteStart(ch: Char): Boolean = quoteChars map (_._1) contains ch - def isQuoteEnd(ch: Char): Boolean = quoteChars map (_._2) contains ch } diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala index c4a672ac37..b087547cf8 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package interpreter -import scala.reflect.internal.util.BatchSourceFile import scala.tools.nsc.ast.parser.Tokens.EOF trait ExprTyper { @@ -15,10 +14,11 @@ trait ExprTyper { import repl._ import global.{ reporter => _, Import => _, _ } import definitions._ - import syntaxAnalyzer.{ UnitParser, UnitScanner, token2name } + import syntaxAnalyzer.UnitParser import naming.freshInternalVarName - object codeParser extends { val global: repl.global.type = repl.global } with CodeHandlers[Tree] { + object codeParser { + val global: repl.global.type = repl.global def applyRule[T](code: String, rule: UnitParser => T): T = { reporter.reset() val scanner = newUnitParser(code) @@ -29,11 +29,7 @@ trait ExprTyper { result } - - def defns(code: String) = stmts(code) collect { case x: DefTree => x } - def expr(code: String) = applyRule(code, _.expr()) def stmts(code: String) = applyRule(code, _.templateStats()) - def stmt(code: String) = stmts(code).last // guaranteed nonempty } /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */ @@ -46,10 +42,6 @@ trait ExprTyper { else Some(trees) } } - // def parsesAsExpr(line: String) = { - // import codeParser._ - // (opt expr line).isDefined - // } def symbolOfLine(code: String): Symbol = { def asExpr(): Symbol = { @@ -63,7 +55,7 @@ trait ExprTyper { case IR.Success => val sym0 = symbolOfTerm(name) // drop NullaryMethodType - val sym = sym0.cloneSymbol setInfo afterTyper(sym0.info.finalResultType) + val sym = sym0.cloneSymbol setInfo exitingTyper(sym0.info.finalResultType) if (sym.info.typeSymbol eq UnitClass) NoSymbol else sym case _ => NoSymbol diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index b7e07ecdd6..b2af53574f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -8,24 +8,20 @@ package interpreter import Predef.{ println => _, _ } import java.io.{ BufferedReader, FileReader } -import java.util.concurrent.locks.ReentrantLock -import scala.sys.process.Process import session._ -import scala.util.Properties.{ jdkHome, javaVersion } -import scala.tools.util.{ Javap } import scala.annotation.tailrec -import scala.collection.mutable.ListBuffer -import scala.concurrent.ops +import scala.util.Properties.{ jdkHome, javaVersion, versionString, javaVmName } +import scala.tools.util.{ Javap } import util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream } -import interpreter._ import io.{ File, Directory } -import scala.reflect.NameTransformer._ import util.ScalaClassLoader import ScalaClassLoader._ import scala.tools.util._ import scala.language.{implicitConversions, existentials} -import scala.reflect.{ClassTag, classTag} +import scala.reflect.classTag import scala.tools.reflect.StdRuntimeTags._ +import scala.concurrent.{ ExecutionContext, Await, Future, future } +import ExecutionContext.Implicits._ /** The Scala interactive shell. It provides a read-eval-print loop * around the Interpreter class. @@ -42,77 +38,41 @@ import scala.tools.reflect.StdRuntimeTags._ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) extends AnyRef with LoopCommands - with ILoopInit { def this(in0: BufferedReader, out: JPrintWriter) = this(Some(in0), out) def this() = this(None, new JPrintWriter(Console.out, true)) - var in: InteractiveReader = _ // the input stream from which commands come - var settings: Settings = _ - var intp: IMain = _ - @deprecated("Use `intp` instead.", "2.9.0") def interpreter = intp @deprecated("Use `intp` instead.", "2.9.0") def interpreter_= (i: Interpreter): Unit = intp = i - /** Having inherited the difficult "var-ness" of the repl instance, - * I'm trying to work around it by moving operations into a class from - * which it will appear a stable prefix. - */ - private def onIntp[T](f: IMain => T): T = f(intp) - - class IMainOps[T <: IMain](val intp: T) { - import intp._ - import global._ - - def printAfterTyper(msg: => String) = - intp.reporter printUntruncatedMessage afterTyper(msg) - - /** Strip NullaryMethodType artifacts. */ - private def replInfo(sym: Symbol) = { - sym.info match { - case NullaryMethodType(restpe) if sym.isAccessor => restpe - case info => info - } - } - def echoTypeStructure(sym: Symbol) = - printAfterTyper("" + deconstruct.show(replInfo(sym))) + var in: InteractiveReader = _ // the input stream from which commands come + var settings: Settings = _ + var intp: IMain = _ - def echoTypeSignature(sym: Symbol, verbose: Boolean) = { - if (verbose) ILoop.this.echo("// Type signature") - printAfterTyper("" + replInfo(sym)) + private var globalFuture: Future[Boolean] = _ - if (verbose) { - ILoop.this.echo("\n// Internal Type structure") - echoTypeStructure(sym) - } - } + /** Print a welcome message */ + def printWelcome() { + echo(s""" + |Welcome to Scala $versionString ($javaVmName, Java $javaVersion). + |Type in expressions to have them evaluated. + |Type :help for more information.""".trim.stripMargin + ) + replinfo("[info] started at " + new java.util.Date) } - implicit def stabilizeIMain(intp: IMain) = new IMainOps[intp.type](intp) - /** TODO - - * -n normalize - * -l label with case class parameter names - * -c complete - leave nothing out - */ - private def typeCommandInternal(expr: String, verbose: Boolean): Result = { - onIntp { intp => - val sym = intp.symbolOfLine(expr) - if (sym.exists) intp.echoTypeSignature(sym, verbose) - else "" - } + protected def asyncMessage(msg: String) { + if (isReplInfo || isReplPower) + echoAndRefresh(msg) } override def echoCommandMessage(msg: String) { intp.reporter printUntruncatedMessage msg } - def isAsync = !settings.Yreplsync.value lazy val power = new Power(intp, new StdReplVals(this))(tagOfStdReplVals, classTag[StdReplVals]) def history = in.history - /** The context class loader at the time this object was created */ - protected val originalClassLoader = Thread.currentThread.getContextClassLoader - // classpath entries added via :cp var addedClasspath: String = "" @@ -166,20 +126,18 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) def helpCommand(line: String): Result = { if (line == "") helpSummary() else uniqueCommand(line) match { - case Some(lc) => echo("\n" + lc.longHelp) + case Some(lc) => echo("\n" + lc.help) case _ => ambiguousError(line) } } private def helpSummary() = { val usageWidth = commands map (_.usageMsg.length) max - val formatStr = "%-" + usageWidth + "s %s %s" + val formatStr = "%-" + usageWidth + "s %s" echo("All commands can be abbreviated, e.g. :he instead of :help.") - echo("Those marked with a * have more detailed help, e.g. :help imports.\n") commands foreach { cmd => - val star = if (cmd.hasLongHelp) "*" else " " - echo(formatStr.format(cmd.usageMsg, star, cmd.help)) + echo(formatStr.format(cmd.usageMsg, cmd.help)) } } private def ambiguousError(cmd: String): Result = { @@ -229,10 +187,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) out println msg out.flush() } - protected def echoNoNL(msg: String) = { - out print msg - out.flush() - } /** Search the history */ def searchHistory(_cmdline: String) { @@ -243,8 +197,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) echo("%d %s".format(index + offset, line)) } - private var currentPrompt = Properties.shellPromptString - def setPrompt(prompt: String) = currentPrompt = prompt + private val currentPrompt = Properties.shellPromptString + /** Prompt to print when awaiting input */ def prompt = currentPrompt @@ -257,7 +211,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) historyCommand, cmd("h?", "<string>", "search the history", searchHistory), cmd("imports", "[name name ...]", "show import history, identifying sources of names", importsCommand), - cmd("implicits", "[-v]", "show the implicits in scope", implicitsCommand), + cmd("implicits", "[-v]", "show the implicits in scope", intp.implicitsCommand), cmd("javap", "<path|class>", "disassemble a file or class name", javapCommand), cmd("load", "<path>", "load and interpret a Scala file", loadCommand), nullary("paste", "enter paste mode: all input up to ctrl-D compiled together", pasteCommand), @@ -276,25 +230,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) cmd("phase", "<phase>", "set the implicit phase for power commands", phaseCommand) ) - private def dumpCommand(): Result = { - echo("" + power) - history.asStrings takeRight 30 foreach echo - in.redrawLine() - } - private def valsCommand(): Result = power.valsDescription - - private val typeTransforms = List( - "scala.collection.immutable." -> "immutable.", - "scala.collection.mutable." -> "mutable.", - "scala.collection.generic." -> "generic.", - "java.lang." -> "jl.", - "scala.runtime." -> "runtime." - ) - private def importsCommand(line: String): Result = { val tokens = words(line) val handlers = intp.languageWildcardHandlers ++ intp.importHandlers - val isVerbose = tokens contains "-v" handlers.filterNot(_.importedSymbols.isEmpty).zipWithIndex foreach { case (handler, idx) => @@ -316,63 +254,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } - private def implicitsCommand(line: String): Result = onIntp { intp => - import intp._ - import global._ - - def p(x: Any) = intp.reporter.printMessage("" + x) - - // If an argument is given, only show a source with that - // in its name somewhere. - val args = line split "\\s+" - val filtered = intp.implicitSymbolsBySource filter { - case (source, syms) => - (args contains "-v") || { - if (line == "") (source.fullName.toString != "scala.Predef") - else (args exists (source.name.toString contains _)) - } - } - - if (filtered.isEmpty) - return "No implicits have been imported other than those in Predef." - - filtered foreach { - case (source, syms) => - p("/* " + syms.size + " implicit members imported from " + source.fullName + " */") - - // This groups the members by where the symbol is defined - val byOwner = syms groupBy (_.owner) - val sortedOwners = byOwner.toList sortBy { case (owner, _) => afterTyper(source.info.baseClasses indexOf owner) } - - sortedOwners foreach { - case (owner, members) => - // Within each owner, we cluster results based on the final result type - // if there are more than a couple, and sort each cluster based on name. - // This is really just trying to make the 100 or so implicits imported - // by default into something readable. - val memberGroups: List[List[Symbol]] = { - val groups = members groupBy (_.tpe.finalResultType) toList - val (big, small) = groups partition (_._2.size > 3) - val xss = ( - (big sortBy (_._1.toString) map (_._2)) :+ - (small flatMap (_._2)) - ) - - xss map (xs => xs sortBy (_.name.toString)) - } - - val ownerMessage = if (owner == source) " defined in " else " inherited from " - p(" /* " + members.size + ownerMessage + owner.fullName + " */") - - memberGroups foreach { group => - group foreach (s => p(" " + intp.symbolDefString(s))) - p("") - } - } - p("") - } - } - private def findToolsJar() = { val jdkPath = Directory(jdkHome) val jar = jdkPath / "lib" / "tools.jar" toFile; @@ -398,42 +279,17 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } - protected def newJavap() = new JavapClass(addToolsJarToLoader(), new IMain.ReplStrippingWriter(intp)) { - override def tryClass(path: String): Array[Byte] = { - val hd :: rest = path split '.' toList; - // If there are dots in the name, the first segment is the - // key to finding it. - if (rest.nonEmpty) { - intp optFlatName hd match { - case Some(flat) => - val clazz = flat :: rest mkString NAME_JOIN_STRING - val bytes = super.tryClass(clazz) - if (bytes.nonEmpty) bytes - else super.tryClass(clazz + MODULE_SUFFIX_STRING) - case _ => super.tryClass(path) - } - } - else { - // Look for Foo first, then Foo$, but if Foo$ is given explicitly, - // we have to drop the $ to find object Foo, then tack it back onto - // the end of the flattened name. - def className = intp flatName path - def moduleName = (intp flatName path.stripSuffix(MODULE_SUFFIX_STRING)) + MODULE_SUFFIX_STRING - - val bytes = super.tryClass(className) - if (bytes.nonEmpty) bytes - else super.tryClass(moduleName) - } - } - } + protected def newJavap() = + JavapClass(addToolsJarToLoader(), new IMain.ReplStrippingWriter(intp), Some(intp)) + private lazy val javap = substituteAndLog[Javap]("javap", NoJavap)(newJavap()) // Still todo: modules. private def typeCommand(line0: String): Result = { line0.trim match { case "" => ":type [-v] <expression>" - case s if s startsWith "-v " => typeCommandInternal(s stripPrefix "-v " trim, true) - case s => typeCommandInternal(s, false) + case s if s startsWith "-v " => intp.typeCommandInternal(s stripPrefix "-v " trim, true) + case s => intp.typeCommandInternal(s, false) } } @@ -447,8 +303,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) private def javapCommand(line: String): Result = { if (javap == null) ":javap unavailable, no tools.jar at %s. Set JDK_HOME.".format(jdkHome) - else if (javaVersion startsWith "1.7") - ":javap not yet working with java 1.7" else if (line == "") ":javap [-lcsvp] [path1 path2 ...]" else @@ -458,37 +312,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } - private def wrapCommand(line: String): Result = { - def failMsg = "Argument to :wrap must be the name of a method with signature [T](=> T): T" - onIntp { intp => - import intp._ - import global._ + private def pathToPhaseWrapper = intp.originalPath("$r") + ".phased.atCurrent" - words(line) match { - case Nil => - intp.executionWrapper match { - case "" => "No execution wrapper is set." - case s => "Current execution wrapper: " + s - } - case "clear" :: Nil => - intp.executionWrapper match { - case "" => "No execution wrapper is set." - case s => intp.clearExecutionWrapper() ; "Cleared execution wrapper." - } - case wrapper :: Nil => - intp.typeOfExpression(wrapper) match { - case PolyType(List(targ), MethodType(List(arg), restpe)) => - intp setExecutionWrapper intp.pathToTerm(wrapper) - "Set wrapper to '" + wrapper + "'" - case tp => - failMsg + "\nFound: <unknown>" - } - case _ => failMsg - } - } - } - - private def pathToPhaseWrapper = intp.pathToTerm("$r") + ".phased.atCurrent" private def phaseCommand(name: String): Result = { val phased: Phased = power.phased import phased.NoPhaseName @@ -547,33 +372,30 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) true } + // return false if repl should exit + def processLine(line: String): Boolean = { + import scala.concurrent.duration._ + Await.ready(globalFuture, 60.seconds) + + (line ne null) && (command(line) match { + case Result(false, _) => false + case Result(_, Some(line)) => addReplay(line) ; true + case _ => true + }) + } + + private def readOneLine() = { + out.flush() + in readLine prompt + } + /** The main read-eval-print loop for the repl. It calls * command() for each line of input, and stops when * command() returns false. */ - def loop() { - def readOneLine() = { - out.flush() - in readLine prompt - } - // return false if repl should exit - def processLine(line: String): Boolean = { - if (isAsync) { - if (!awaitInitialized()) return false - runThunks() - } - if (line eq null) false // assume null means EOF - else command(line) match { - case Result(false, _) => false - case Result(_, Some(finalLine)) => addReplay(finalLine) ; true - case _ => true - } - } - def innerLoop() { - if ( try processLine(readOneLine()) catch crashRecovery ) - innerLoop() - } - innerLoop() + @tailrec final def loop() { + if ( try processLine(readOneLine()) catch crashRecovery ) + loop() } /** interpret all lines from a specified file */ @@ -819,48 +641,39 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) SimpleReader() } } - def process(settings: Settings): Boolean = savingContextLoader { - this.settings = settings - createInterpreter() - // sets in to some kind of reader depending on environmental cues - in = in0 match { - case Some(reader) => SimpleReader(reader, out, true) - case None => - // some post-initialization - chooseReader(settings) match { - case x: JLineReader => addThunk(x.consoleReader.postInit) ; x - case x => x - } + private def loopPostInit() { + in match { + case x: JLineReader => x.consoleReader.postInit + case _ => } // Bind intp somewhere out of the regular namespace where // we can get at it in generated code. - addThunk(intp.quietBind(NamedParam[IMain]("$intp", intp)(tagOfIMain, classTag[IMain]))) - addThunk({ - import scala.tools.nsc.io._ - import Properties.userHome - import scala.compat.Platform.EOL - val autorun = replProps.replAutorunCode.option flatMap (f => io.File(f).safeSlurp()) - if (autorun.isDefined) intp.quietRun(autorun.get) - }) - - loadFiles(settings) - // it is broken on startup; go ahead and exit - if (intp.reporter.hasErrors) - return false - - // This is about the illusion of snappiness. We call initialize() - // which spins off a separate thread, then print the prompt and try - // our best to look ready. The interlocking lazy vals tend to - // inter-deadlock, so we break the cycle with a single asynchronous - // message to an actor. - if (isAsync) { - intp initialize initializedCallback() - createAsyncListener() // listens for signal to run postInitialization + intp.quietBind(NamedParam[IMain]("$intp", intp)(tagOfIMain, classTag[IMain])) + // Auto-run code via some setting. + ( replProps.replAutorunCode.option + flatMap (f => io.File(f).safeSlurp()) + foreach (intp quietRun _) + ) + // classloader and power mode setup + intp.setContextClassLoader + if (isReplPower) { + replProps.power setValue true + unleashAndSetPhase() + asyncMessage(power.banner) } - else { + } + def process(settings: Settings): Boolean = savingContextLoader { + this.settings = settings + createInterpreter() + + // sets in to some kind of reader depending on environmental cues + in = in0.fold(chooseReader(settings))(r => SimpleReader(r, out, true)) + globalFuture = future { intp.initializeSynchronous() - postInitialization() + loopPostInit() + loadFiles(settings) + !intp.reporter.hasErrors } printWelcome() @@ -871,27 +684,12 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) true } - /** process command-line arguments and do as they request */ - def process(args: Array[String]): Boolean = { - val command = new CommandLine(args.toList, echo) - def neededHelp(): String = - (if (command.settings.help.value) command.usageMsg + "\n" else "") + - (if (command.settings.Xhelp.value) command.xusageMsg + "\n" else "") - - // if they asked for no help and command is valid, we call the real main - neededHelp() match { - case "" => command.ok && process(command.settings) - case help => echoNoNL(help) ; true - } - } - @deprecated("Use `process` instead", "2.9.0") - def main(settings: Settings): Unit = process(settings) + def main(settings: Settings): Unit = process(settings) //used by sbt } object ILoop { implicit def loopToInterpreter(repl: ILoop): IMain = repl.intp - private def echo(msg: String) = Console println msg // Designed primarily for use by test code: take a String with a // bunch of code, and prints out a transcript of what it would look diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala b/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala deleted file mode 100644 index e3c0494fa3..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala +++ /dev/null @@ -1,125 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.reflect.internal.util.Position -import scala.util.control.Exception.ignoring -import scala.tools.nsc.util.stackTraceString - -/** - * Machinery for the asynchronous initialization of the repl. - */ -trait ILoopInit { - self: ILoop => - - /** Print a welcome message */ - def printWelcome() { - import Properties._ - val welcomeMsg = - """|Welcome to Scala %s (%s, Java %s). - |Type in expressions to have them evaluated. - |Type :help for more information.""" . - stripMargin.format(versionString, javaVmName, javaVersion) - echo(welcomeMsg) - replinfo("[info] started at " + new java.util.Date) - } - - protected def asyncMessage(msg: String) { - if (isReplInfo || isReplPower) - echoAndRefresh(msg) - } - - private val initLock = new java.util.concurrent.locks.ReentrantLock() - private val initCompilerCondition = initLock.newCondition() // signal the compiler is initialized - private val initLoopCondition = initLock.newCondition() // signal the whole repl is initialized - private val initStart = System.nanoTime - - private def withLock[T](body: => T): T = { - initLock.lock() - try body - finally initLock.unlock() - } - // a condition used to ensure serial access to the compiler. - @volatile private var initIsComplete = false - @volatile private var initError: String = null - private def elapsed() = "%.3f".format((System.nanoTime - initStart).toDouble / 1000000000L) - - // the method to be called when the interpreter is initialized. - // Very important this method does nothing synchronous (i.e. do - // not try to use the interpreter) because until it returns, the - // repl's lazy val `global` is still locked. - protected def initializedCallback() = withLock(initCompilerCondition.signal()) - - // Spins off a thread which awaits a single message once the interpreter - // has been initialized. - protected def createAsyncListener() = { - io.spawn { - withLock(initCompilerCondition.await()) - asyncMessage("[info] compiler init time: " + elapsed() + " s.") - postInitialization() - } - } - - // called from main repl loop - protected def awaitInitialized(): Boolean = { - if (!initIsComplete) - withLock { while (!initIsComplete) initLoopCondition.await() } - if (initError != null) { - println(""" - |Failed to initialize the REPL due to an unexpected error. - |This is a bug, please, report it along with the error diagnostics printed below. - |%s.""".stripMargin.format(initError) - ) - false - } else true - } - // private def warningsThunks = List( - // () => intp.bind("lastWarnings", "" + typeTag[List[(Position, String)]], intp.lastWarnings _), - // ) - - protected def postInitThunks = List[Option[() => Unit]]( - Some(intp.setContextClassLoader _), - if (isReplPower) Some(() => enablePowerMode(true)) else None - ).flatten - // ++ ( - // warningsThunks - // ) - // called once after init condition is signalled - protected def postInitialization() { - try { - postInitThunks foreach (f => addThunk(f())) - runThunks() - } catch { - case ex: Throwable => - initError = stackTraceString(ex) - throw ex - } finally { - initIsComplete = true - - if (isAsync) { - asyncMessage("[info] total init time: " + elapsed() + " s.") - withLock(initLoopCondition.signal()) - } - } - } - // code to be executed only after the interpreter is initialized - // and the lazy val `global` can be accessed without risk of deadlock. - private var pendingThunks: List[() => Unit] = Nil - protected def addThunk(body: => Unit) = synchronized { - pendingThunks :+= (() => body) - } - protected def runThunks(): Unit = synchronized { - if (pendingThunks.nonEmpty) - repldbg("Clearing " + pendingThunks.size + " thunks.") - - while (pendingThunks.nonEmpty) { - val thunk = pendingThunks.head - pendingThunks = pendingThunks.tail - thunk() - } - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index bed8570bd0..db54b5a2b1 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -11,37 +11,19 @@ import util.stringFromWriter import scala.reflect.internal.util._ import java.net.URL import scala.sys.BooleanProp -import io.VirtualDirectory import scala.tools.nsc.io.AbstractFile import reporters._ -import symtab.Flags -import scala.reflect.internal.Names import scala.tools.util.PathResolver import scala.tools.nsc.util.ScalaClassLoader import ScalaClassLoader.URLClassLoader import scala.tools.nsc.util.Exceptional.unwrap import scala.collection.{ mutable, immutable } -import scala.util.control.Exception.{ ultimately } import IMain._ import java.util.concurrent.Future -import typechecker.Analyzer -import scala.language.implicitConversions import scala.reflect.runtime.{ universe => ru } import scala.reflect.{ ClassTag, classTag } import scala.tools.reflect.StdRuntimeTags._ -/** directory to save .class files to */ -private class ReplVirtualDirectory(out: JPrintWriter) extends VirtualDirectory("(memory)", None) { - private def pp(root: AbstractFile, indentLevel: Int) { - val spaces = " " * indentLevel - out.println(spaces + root.name) - if (root.isDirectory) - root.toList sortBy (_.name) foreach (x => pp(x, indentLevel + 1)) - } - // print the contents hierarchically - def show() = pp(this, 0) -} - /** An interpreter for Scala code. * * The main public entry points are compile(), interpret(), and bind(). @@ -77,16 +59,19 @@ private class ReplVirtualDirectory(out: JPrintWriter) extends VirtualDirectory(" class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends Imports { imain => - /** Leading with the eagerly evaluated. - */ - val virtualDirectory: VirtualDirectory = new ReplVirtualDirectory(out) // "directory" for classfiles - private var currentSettings: Settings = initialSettings - private[nsc] var printResults = true // whether to print result lines - private[nsc] var totalSilence = false // whether to print anything - private var _initializeComplete = false // compiler is initialized - private var _isInitialized: Future[Boolean] = null // set up initialization future - private var bindExceptions = true // whether to bind the lastException variable - private var _executionWrapper = "" // code to be wrapped around all lines + object replOutput extends ReplOutput(settings.Yreploutdir) { } + + @deprecated("Use replOutput.dir instead", "2.11.0") + def virtualDirectory = replOutput.dir + // Used in a test case. + def showDirectory() = replOutput.show(out) + + private[nsc] var printResults = true // whether to print result lines + private[nsc] var totalSilence = false // whether to print anything + private var _initializeComplete = false // compiler is initialized + private var _isInitialized: Future[Boolean] = null // set up initialization future + private var bindExceptions = true // whether to bind the lastException variable + private var _executionWrapper = "" // code to be wrapped around all lines /** We're going to go to some trouble to initialize the compiler asynchronously. * It's critical that nothing call into it until it's been initialized or we will @@ -98,20 +83,11 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends private var _classLoader: AbstractFileClassLoader = null // active classloader private val _compiler: Global = newCompiler(settings, reporter) // our private compiler - private val nextReqId = { - var counter = 0 - () => { counter += 1 ; counter } - } - def compilerClasspath: Seq[URL] = ( if (isInitializeComplete) global.classPath.asURLs else new PathResolver(settings).result.asURLs // the compiler's classpath ) - def settings = currentSettings - def mostRecentLine = prevRequestList match { - case Nil => "" - case req :: _ => req.originalLine - } + def settings = initialSettings // Run the code body with the given boolean settings flipped to true. def withoutWarnings[T](body: => T): T = beQuietDuring { val saved = settings.nowarn.value @@ -126,12 +102,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true)) def this() = this(new Settings()) - lazy val repllog: Logger = new Logger { - val out: JPrintWriter = imain.out - val isInfo: Boolean = BooleanProp keyExists "scala.repl.info" - val isDebug: Boolean = BooleanProp keyExists "scala.repl.debug" - val isTrace: Boolean = BooleanProp keyExists "scala.repl.trace" - } lazy val formatting: Formatting = new Formatting { val prompt = Properties.shellPromptString } @@ -153,6 +123,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends catch AbstractOrMissingHandler() } private def tquoted(s: String) = "\"\"\"" + s + "\"\"\"" + private val logScope = scala.sys.props contains "scala.repl.scope" + private def scopelog(msg: String) = if (logScope) Console.err.println(msg) // argument is a thunk to execute after init is done def initialize(postInitSignal: => Unit) { @@ -187,15 +159,24 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends else null } } - @deprecated("Use `global` for access to the compiler instance.", "2.9.0") - lazy val compiler: global.type = global import global._ - import definitions.{ScalaPackage, JavaLangPackage, termMember, typeMember} - import rootMirror.{RootClass, getClassIfDefined, getModuleIfDefined, getRequiredModule, getRequiredClass} + import definitions.{ ObjectClass, termMember, dropNullaryMethod} + + lazy val runtimeMirror = ru.runtimeMirror(classLoader) + + private def noFatal(body: => Symbol): Symbol = try body catch { case _: FatalError => NoSymbol } + + def getClassIfDefined(path: String) = ( + noFatal(runtimeMirror staticClass path) + orElse noFatal(rootMirror staticClass path) + ) + def getModuleIfDefined(path: String) = ( + noFatal(runtimeMirror staticModule path) + orElse noFatal(rootMirror staticModule path) + ) 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) } @@ -208,10 +189,9 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends // make sure we don't overwrite their unwisely named res3 etc. def freshUserTermName(): TermName = { val name = newTermName(freshUserVarName()) - if (definedNameMap contains name) freshUserTermName() + if (replScope containsName name) freshUserTermName() else name } - def isUserTermName(name: Name) = isUserVarName("" + name) def isInternalTermName(name: Name) = isInternalVarName("" + name) } import naming._ @@ -260,11 +240,12 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends /** Instantiate a compiler. Overridable. */ protected def newCompiler(settings: Settings, reporter: Reporter): ReplGlobal = { - settings.outputDirs setSingleOutput virtualDirectory + settings.outputDirs setSingleOutput replOutput.dir settings.exposeEmptyPackage.value = true - new Global(settings, reporter) with ReplGlobal { - override def toString: String = "<global>" - } + if (settings.Yrangepos.value) + new Global(settings, reporter) with ReplGlobal with interactive.RangePositions { override def toString: String = "<global>" } + else + new Global(settings, reporter) with ReplGlobal { override def toString: String = "<global>" } } /** Parent classloader. Overridable. */ @@ -297,20 +278,59 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends ensureClassLoader() _classLoader } - private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(virtualDirectory, parent) { + + def backticked(s: String): String = ( + (s split '.').toList map { + case "_" => "_" + case s if nme.keywords(newTermName(s)) => s"`$s`" + case s => s + } mkString "." + ) + + abstract class PhaseDependentOps { + def shift[T](op: => T): T + + def path(name: => Name): String = shift(path(symbolOfName(name))) + def path(sym: Symbol): String = backticked(shift(sym.fullName)) + def sig(sym: Symbol): String = shift(sym.defString) + } + object typerOp extends PhaseDependentOps { + def shift[T](op: => T): T = exitingTyper(op) + } + object flatOp extends PhaseDependentOps { + def shift[T](op: => T): T = exitingFlatten(op) + } + + def originalPath(name: String): String = originalPath(name: TermName) + def originalPath(name: Name): String = typerOp path name + def originalPath(sym: Symbol): String = typerOp path sym + def flatPath(sym: Symbol): String = flatOp shift sym.javaClassName + def translatePath(path: String) = { + val sym = if (path endsWith "$") symbolOfTerm(path.init) else symbolOfIdent(path) + sym match { + case NoSymbol => None + case _ => Some(flatPath(sym)) + } + } + def translateEnclosingClass(n: String) = { + def enclosingClass(s: Symbol): Symbol = + if (s == NoSymbol || s.isClass) s else enclosingClass(s.owner) + enclosingClass(symbolOfTerm(n)) match { + case NoSymbol => None + case c => Some(flatPath(c)) + } + } + + private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(replOutput.dir, parent) { /** Overridden here to try translating a simple name to the generated * class name if the original attempt fails. This method is used by * getResourceAsStream as well as findClass. */ - override protected def findAbstractFile(name: String): AbstractFile = { + override protected def findAbstractFile(name: String): AbstractFile = super.findAbstractFile(name) match { - // deadlocks on startup if we try to translate names too early - case null if isInitializeComplete => - generatedName(name) map (x => super.findAbstractFile(x)) orNull - case file => - file + case null => translatePath(name) map (super.findAbstractFile(_)) orNull + case file => file } - } } private def makeClassLoader(): AbstractFileClassLoader = new TranslatingClassLoader(parentClassLoader match { @@ -318,32 +338,11 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends case p => new URLClassLoader(compilerClasspath, p) }) - def getInterpreterClassLoader() = classLoader - // Set the current Java "context" class loader to this interpreter's class loader def setContextClassLoader() = classLoader.setAsContext() - /** Given a simple repl-defined name, returns the real name of - * the class representing it, e.g. for "Bippy" it may return - * {{{ - * $line19.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$Bippy - * }}} - */ - def generatedName(simpleName: String): Option[String] = { - if (simpleName endsWith nme.MODULE_SUFFIX_STRING) optFlatName(simpleName.init) map (_ + nme.MODULE_SUFFIX_STRING) - else optFlatName(simpleName) - } - def flatName(id: String) = optFlatName(id) getOrElse id - def optFlatName(id: String) = requestForIdent(id) map (_ fullFlatName id) - - def allDefinedNames = definedNameMap.keys.toList.sorted - def pathToType(id: String): String = pathToName(newTypeName(id)) - def pathToTerm(id: String): String = pathToName(newTermName(id)) - def pathToName(name: Name): String = { - if (definedNameMap contains name) - definedNameMap(name) fullPath name - else name.toString - } + def allDefinedNames: List[Name] = exitingTyper(replScope.toList.map(_.name).sorted) + def unqualifiedIds: List[String] = allDefinedNames map (_.decode) sorted /** Most recent tree handled which wasn't wholly synthetic. */ private def mostRecentlyHandledTree: Option[Tree] = { @@ -356,51 +355,47 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends None } - /** Stubs for work in progress. */ - def handleTypeRedefinition(name: TypeName, old: Request, req: Request) = { - for (t1 <- old.simpleNameOfType(name) ; t2 <- req.simpleNameOfType(name)) { - repldbg("Redefining type '%s'\n %s -> %s".format(name, t1, t2)) - } - } + private def updateReplScope(sym: Symbol, isDefined: Boolean) { + def log(what: String) { + val mark = if (sym.isType) "t " else "v " + val name = exitingTyper(sym.nameString) + val info = cleanTypeAfterTyper(sym) + val defn = sym defStringSeenAs info - def handleTermRedefinition(name: TermName, old: Request, req: Request) = { - for (t1 <- old.compilerTypeOf get name ; t2 <- req.compilerTypeOf get name) { - // Printing the types here has a tendency to cause assertion errors, like - // assertion failed: fatal: <refinement> has owner value x, but a class owner is required - // so DBG is by-name now to keep it in the family. (It also traps the assertion error, - // but we don't want to unnecessarily risk hosing the compiler's internal state.) - repldbg("Redefining term '%s'\n %s -> %s".format(name, t1, t2)) + scopelog(f"[$mark$what%6s] $name%-25s $defn%s") + } + if (ObjectClass isSubClass sym.owner) return + // unlink previous + replScope lookupAll sym.name foreach { sym => + log("unlink") + replScope unlink sym } + val what = if (isDefined) "define" else "import" + log(what) + replScope enter sym } def recordRequest(req: Request) { - if (req == null || referencedNameMap == null) + if (req == null) return prevRequests += req - req.referencedNames foreach (x => referencedNameMap(x) = req) // warning about serially defining companions. It'd be easy // enough to just redefine them together but that may not always // be what people want so I'm waiting until I can do it better. - for { - name <- req.definedNames filterNot (x => req.definedNames contains x.companionName) - oldReq <- definedNameMap get name.companionName - newSym <- req.definedSymbols get name - oldSym <- oldReq.definedSymbols get name.companionName - if Seq(oldSym, newSym).permutations exists { case Seq(s1, s2) => s1.isClass && s2.isModule } - } { - afterTyper(replwarn(s"warning: previously defined $oldSym is not a companion to $newSym.")) - replwarn("Companions must be defined together; you may wish to use :paste mode for this.") - } - - // Updating the defined name map - req.definedNames foreach { name => - if (definedNameMap contains name) { - if (name.isTypeName) handleTypeRedefinition(name.toTypeName, definedNameMap(name), req) - else handleTermRedefinition(name.toTermName, definedNameMap(name), req) + exitingTyper { + req.defines filterNot (s => req.defines contains s.companionSymbol) foreach { newSym => + val oldSym = replScope lookup newSym.name.companionName + if (Seq(oldSym, newSym).permutations exists { case Seq(s1, s2) => s1.isClass && s2.isModule }) { + replwarn(s"warning: previously defined $oldSym is not a companion to $newSym.") + replwarn("Companions must be defined together; you may wish to use :paste mode for this.") + } } - definedNameMap(name) = req + } + exitingTyper { + req.imports foreach (sym => updateReplScope(sym, isDefined = false)) + req.defines foreach (sym => updateReplScope(sym, isDefined = true)) } } @@ -409,19 +404,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends printMessage(msg) } - def isParseable(line: String): Boolean = { - beSilentDuring { - try parse(line) match { - case Some(xs) => xs.nonEmpty // parses as-is - case None => true // incomplete - } - catch { case x: Exception => // crashed the compiler - replwarn("Exception in isParseable(\"" + line + "\"): " + x) - false - } - } - } - def compileSourcesKeepingRun(sources: SourceFile*) = { val run = new Run() reporter.reset() @@ -448,18 +430,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends executingRequest } - // rewriting "5 // foo" to "val x = { 5 // foo }" creates broken code because - // the close brace is commented out. Strip single-line comments. - // ... but for error message output reasons this is not used, and rather than - // enclosing in braces it is constructed like "val x =\n5 // foo". - private def removeComments(line: String): String = { - showCodeIfDebugging(line) // as we're about to lose our // show - line.lines map (s => s indexOf "//" match { - case -1 => s - case idx => s take idx - }) mkString "\n" - } - private def safePos(t: Tree, alt: Int): Int = try t.pos.startOrPoint catch { case _: UnsupportedOperationException => alt } @@ -551,8 +521,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends Right(buildRequest(line, trees)) } - // normalize non-public types so we don't see protected aliases like Self - def normalizeNonPublic(tp: Type) = tp match { + // dealias non-public types so we don't see protected aliases like Self + def dealiasNonPublic(tp: Type) = tp match { case TypeRef(_, sym, _) if sym.isAliasType && !sym.isPublic => tp.dealias case _ => tp } @@ -615,7 +585,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends */ def bind(name: String, boundType: String, value: Any, modifiers: List[String] = Nil): IR.Result = { val bindRep = new ReadEvalPrint() - val run = bindRep.compile(""" + bindRep.compile(""" |object %s { | var value: %s = _ | def set(x: Any) = value = x.asInstanceOf[%s] @@ -645,24 +615,15 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def rebind(p: NamedParam): IR.Result = { val name = p.name - val oldType = typeOfTerm(name) orElse { return IR.Error } val newType = p.tpe val tempName = freshInternalVarName() quietRun("val %s = %s".format(tempName, name)) quietRun("val %s = %s.asInstanceOf[%s]".format(name, tempName, newType)) } - def quietImport(ids: String*): IR.Result = beQuietDuring(addImports(ids: _*)) - def addImports(ids: String*): IR.Result = - if (ids.isEmpty) IR.Success - else interpret("import " + ids.mkString(", ")) - def quietBind(p: NamedParam): IR.Result = beQuietDuring(bind(p)) def bind(p: NamedParam): IR.Result = bind(p.name, p.tpe, p.value) def bind[T: ru.TypeTag : ClassTag](name: String, value: T): IR.Result = bind((name, value)) - def bindSyntheticValue(x: Any): IR.Result = bindValue(freshInternalVarName(), x) - def bindValue(x: Any): IR.Result = bindValue(freshUserVarName(), x) - def bindValue(name: String, x: Any): IR.Result = bind(name, TypeStrings.fromValue(x), x) /** Reset this interpreter, forgetting all user-specified requests. */ def reset() { @@ -670,9 +631,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends resetClassLoader() resetAllCreators() prevRequests.clear() - referencedNameMap.clear() - definedNameMap.clear() - virtualDirectory.clear() + resetReplScope() + replOutput.dir.clear() } /** This instance is no longer needed, so release any resources @@ -693,10 +653,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends class ReadEvalPrint(lineId: Int) { def this() = this(freshLineId()) - private var lastRun: Run = _ - private var evalCaught: Option[Throwable] = None - private var conditionalWarnings: List[ConditionalWarning] = Nil - val packageName = sessionNames.line + lineId val readName = sessionNames.read val evalName = sessionNames.eval @@ -723,7 +679,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def readPath = pathTo(readName) def evalPath = pathTo(evalName) - def printPath = pathTo(printName) def call(name: String, args: Any*): AnyRef = { val m = evalMethod(name) @@ -738,10 +693,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends try Right(call(name, args: _*)) catch { case ex: Throwable => Left(ex) } - def callOpt(name: String, args: Any*): Option[AnyRef] = - try Some(call(name, args: _*)) - catch { case ex: Throwable => bindError(ex) ; None } - class EvalException(msg: String, cause: Throwable) extends RuntimeException(msg, cause) { } private def evalError(path: String, ex: Throwable) = @@ -753,10 +704,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } lazy val evalClass = load(evalPath) - lazy val evalValue = callEither(resultName) match { - case Left(ex) => evalCaught = Some(ex) ; None - case Right(result) => Some(result) - } def compile(source: String): Boolean = compileAndSaveRun("<console>", source) @@ -764,10 +711,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends * following accessPath into the outer one. */ def resolvePathToSymbol(accessPath: String): Symbol = { - val readRoot = getRequiredModule(readPath) // the outermost wrapper + val readRoot = getModuleIfDefined(readPath) // the outermost wrapper (accessPath split '.').foldLeft(readRoot: Symbol) { case (sym, "") => sym - case (sym, name) => afterTyper(termMember(sym, name)) + case (sym, name) => exitingTyper(termMember(sym, name)) } } /** We get a bunch of repeated warnings for reasons I haven't @@ -800,15 +747,16 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends showCodeIfDebugging(code) val (success, run) = compileSourcesKeepingRun(new BatchSourceFile(label, packaged(code))) updateRecentWarnings(run) - lastRun = run success } } /** One line of code submitted by the user for interpretation */ - // private class Request(val line: String, val trees: List[Tree]) { - val reqId = nextReqId() + def defines = defHandlers flatMap (_.definedSymbols) + def imports = importedSymbols + def value = Some(handlers.last) filter (h => h.definesValue) map (h => definedSymbols(h.definesTerm.get)) getOrElse NoSymbol + val lineRep = new ReadEvalPrint() private var _originalLine: String = null @@ -819,50 +767,31 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val handlers: List[MemberHandler] = trees map (memberHandlers chooseHandler _) def defHandlers = handlers collect { case x: MemberDefHandler => x } - /** all (public) names defined by these statements */ - val definedNames = handlers flatMap (_.definedNames) - /** list of names used by this expression */ val referencedNames: List[Name] = handlers flatMap (_.referencedNames) /** def and val names */ def termNames = handlers flatMap (_.definesTerm) def typeNames = handlers flatMap (_.definesType) - def definedOrImported = handlers flatMap (_.definedOrImported) - def definedSymbolList = defHandlers flatMap (_.definedSymbols) - - def definedTypeSymbol(name: String) = definedSymbols(newTypeName(name)) - def definedTermSymbol(name: String) = definedSymbols(newTermName(name)) + def importedSymbols = handlers flatMap { + case x: ImportHandler => x.importedSymbols + case _ => Nil + } /** Code to import bound names from previous lines - accessPath is code to * append to objectName to access anything bound by request. */ val ComputedImports(importsPreamble, importsTrailer, accessPath) = - importsCode(referencedNames.toSet) - - /** Code to access a variable with the specified name */ - def fullPath(vname: String) = ( - lineRep.readPath + accessPath + ".`%s`".format(vname) - ) - /** Same as fullpath, but after it has been flattened, so: - * $line5.$iw.$iw.$iw.Bippy // fullPath - * $line5.$iw$$iw$$iw$Bippy // fullFlatName - */ - def fullFlatName(name: String) = - lineRep.readPath + accessPath.replace('.', '$') + nme.NAME_JOIN_STRING + name - - /** The unmangled symbol name, but supplemented with line info. */ - def disambiguated(name: Name): String = name + " (in " + lineRep + ")" - - /** Code to access a variable with the specified name */ - def fullPath(vname: Name): String = fullPath(vname.toString) + exitingTyper(importsCode(referencedNames.toSet)) /** the line of code to compute */ def toCompute = line + def fullPath(vname: String) = s"${lineRep.readPath}$accessPath.`$vname`" + /** generate the source code for the object that computes this request */ private object ObjectSourceCode extends CodeAssembler[MemberHandler] { - def path = pathToTerm("$intp") + def path = originalPath("$intp") def envLines = { if (!isReplPower) Nil // power mode only for now // $intp is not bound; punt, but include the line. @@ -872,8 +801,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends ) else List( "def $line = " + tquoted(originalLine), - "def $req = %s.requestForReqId(%s).orNull".format(path, reqId), - "def $trees = if ($req eq null) Nil else $req.trees".format(lineRep.readName, path, reqId) + "def $trees = Nil" ) } @@ -889,13 +817,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends /** We only want to generate this code when the result * is a value which can be referred to as-is. */ - val evalResult = - if (!handlers.last.definesValue) "" - else handlers.last.definesTerm match { - case Some(vname) if typeOf contains vname => - "lazy val %s = %s".format(lineRep.resultName, fullPath(vname)) - case _ => "" - } + val evalResult = Request.this.value match { + case NoSymbol => "" + case sym => "lazy val %s = %s".format(lineRep.resultName, originalPath(sym)) + } // first line evaluates object to make sure constructor is run // initial "" so later code can uniformly be: + etc val preamble = """ @@ -917,15 +842,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val generate = (m: MemberHandler) => m resultExtractionCode Request.this } - // get it - def getEvalTyped[T] : Option[T] = getEval map (_.asInstanceOf[T]) - def getEval: Option[AnyRef] = { - // ensure it has been compiled - compile - // try to load it and call the value method - lineRep.evalValue filterNot (_ == null) - } - /** Compile the object file. Returns whether the compilation succeeded. * If all goes well, the "types" map is computed. */ lazy val compile: Boolean = { @@ -944,7 +860,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val name = dh.member.name definedSymbols get name foreach { sym => dh.member setSymbol sym - repldbg("Set symbol of " + name + " to " + sym.defString) + repldbg("Set symbol of " + name + " to " + symbolDefString(sym)) } } @@ -954,11 +870,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } lazy val resultSymbol = lineRep.resolvePathToSymbol(accessPath) - def applyToResultMember[T](name: Name, f: Symbol => T) = afterTyper(f(resultSymbol.info.nonPrivateDecl(name))) + def applyToResultMember[T](name: Name, f: Symbol => T) = exitingTyper(f(resultSymbol.info.nonPrivateDecl(name))) /* typeOf lookup with encoding */ def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(global.encode(name.toString))) - def simpleNameOfType(name: TypeName) = (compilerTypeOf get name) map (_.typeSymbol.simpleName) private def typeMap[T](f: Type => T) = mapFrom[Name, Name, T](termNames ++ typeNames)(x => f(cleanMemberDecl(resultSymbol, x))) @@ -966,11 +881,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends /** Types of variables defined by this request. */ lazy val compilerTypeOf = typeMap[Type](x => x) withDefaultValue NoType /** String representations of same. */ - lazy val typeOf = typeMap[String](tp => afterTyper(tp.toString)) + lazy val typeOf = typeMap[String](tp => exitingTyper(tp.toString)) - // lazy val definedTypes: Map[Name, Type] = { - // typeNames map (x => x -> afterTyper(resultSymbol.info.nonPrivateDecl(x).tpe)) toMap - // } lazy val definedSymbols = ( termNames.map(x => x -> applyToResultMember(x, x => x)) ++ typeNames.map(x => x -> compilerTypeOf(x).typeSymbolDirect) @@ -1003,45 +915,48 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends private var mostRecentWarnings: List[(global.Position, String)] = Nil def lastWarnings = mostRecentWarnings - def treesForRequestId(id: Int): List[Tree] = - requestForReqId(id).toList flatMap (_.trees) - - def requestForReqId(id: Int): Option[Request] = - if (executingRequest != null && executingRequest.reqId == id) Some(executingRequest) - else prevRequests find (_.reqId == id) + private lazy val importToGlobal = global mkImporter ru + private lazy val importToRuntime = ru mkImporter global + private lazy val javaMirror = ru.rootMirror match { + case x: ru.JavaMirror => x + case _ => null + } + private implicit def importFromRu(sym: ru.Symbol): Symbol = importToGlobal importSymbol sym + private implicit def importToRu(sym: Symbol): ru.Symbol = importToRuntime importSymbol sym - def requestForName(name: Name): Option[Request] = { - assert(definedNameMap != null, "definedNameMap is null") - definedNameMap get name + def classOfTerm(id: String): Option[JClass] = symbolOfTerm(id) match { + case NoSymbol => None + case sym => Some(javaMirror runtimeClass importToRu(sym).asClass) } - def requestForIdent(line: String): Option[Request] = - requestForName(newTermName(line)) orElse requestForName(newTypeName(line)) + def typeOfTerm(id: String): Type = symbolOfTerm(id).tpe - def requestHistoryForName(name: Name): List[Request] = - prevRequests.toList.reverse filter (_.definedNames contains name) + def valueOfTerm(id: String): Option[Any] = exitingTyper { + def value() = { + val sym0 = symbolOfTerm(id) + val sym = (importToRuntime importSymbol sym0).asTerm + val module = runtimeMirror.reflectModule(sym.owner.companionSymbol.asModule).instance + val module1 = runtimeMirror.reflect(module) + val invoker = module1.reflectField(sym) - def definitionForName(name: Name): Option[MemberHandler] = - requestForName(name) flatMap { req => - req.handlers find (_.definedNames contains name) + invoker.get } - def valueOfTerm(id: String): Option[AnyRef] = - requestForName(newTermName(id)) flatMap (_.getEval) - - def classOfTerm(id: String): Option[JClass] = - valueOfTerm(id) map (_.getClass) - - def typeOfTerm(id: String): Type = newTermName(id) match { - case nme.ROOTPKG => RootClass.tpe - case name => requestForName(name).fold(NoType: Type)(_ compilerTypeOf name) + try Some(value()) catch { case _: Exception => None } } - def symbolOfType(id: String): Symbol = - requestForName(newTypeName(id)).fold(NoSymbol: Symbol)(_ definedTypeSymbol id) + /** It's a bit of a shotgun approach, but for now we will gain in + * robustness. Try a symbol-producing operation at phase typer, and + * if that is NoSymbol, try again at phase flatten. I'll be able to + * lose this and run only from exitingTyper as soon as I figure out + * exactly where a flat name is sneaking in when calculating imports. + */ + def tryTwice(op: => Symbol): Symbol = exitingTyper(op) orElse exitingFlatten(op) - def symbolOfTerm(id: String): Symbol = - requestForIdent(newTermName(id)).fold(NoSymbol: Symbol)(_ definedTermSymbol id) + def symbolOfIdent(id: String): Symbol = symbolOfType(id) orElse symbolOfTerm(id) + def symbolOfType(id: String): Symbol = tryTwice(replScope lookup (id: TypeName)) + def symbolOfTerm(id: String): Symbol = tryTwice(replScope lookup (id: TermName)) + def symbolOfName(id: Name): Symbol = replScope lookup id def runtimeClassAndTypeOfTerm(id: String): Option[(JClass, Type)] = { classOfTerm(id) flatMap { clazz => @@ -1062,14 +977,18 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends else NoType } } - def cleanMemberDecl(owner: Symbol, member: Name): Type = afterTyper { - normalizeNonPublic { - owner.info.nonPrivateDecl(member).tpe match { - case NullaryMethodType(tp) => tp - case tp => tp - } - } + + def cleanTypeAfterTyper(sym: => Symbol): Type = { + exitingTyper( + dealiasNonPublic( + dropNullaryMethod( + sym.tpe_* + ) + ) + ) } + def cleanMemberDecl(owner: Symbol, member: Name): Type = + cleanTypeAfterTyper(owner.info nonPrivateDecl member) object exprTyper extends { val repl: IMain.this.type = imain @@ -1083,64 +1002,35 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def typeOfExpression(expr: String, silent: Boolean = true): Type = exprTyper.typeOfExpression(expr, silent) - protected def onlyTerms(xs: List[Name]) = xs collect { case x: TermName => x } - protected def onlyTypes(xs: List[Name]) = xs collect { case x: TypeName => x } + protected def onlyTerms(xs: List[Name]): List[TermName] = xs collect { case x: TermName => x } + protected def onlyTypes(xs: List[Name]): List[TypeName] = xs collect { case x: TypeName => x } def definedTerms = onlyTerms(allDefinedNames) filterNot isInternalTermName def definedTypes = onlyTypes(allDefinedNames) - def definedSymbols = prevRequestList.flatMap(_.definedSymbols.values).toSet[Symbol] - def definedSymbolList = prevRequestList flatMap (_.definedSymbolList) filterNot (s => isInternalTermName(s.name)) + def definedSymbolList = prevRequestList flatMap (_.defines) filterNot (s => isInternalTermName(s.name)) // Terms with user-given names (i.e. not res0 and not synthetic) def namedDefinedTerms = definedTerms filterNot (x => isUserVarName("" + x) || directlyBoundNames(x)) - private def findName(name: Name) = definedSymbols find (_.name == name) getOrElse NoSymbol - - /** Translate a repl-defined identifier into a Symbol. - */ - def apply(name: String): Symbol = - types(name) orElse terms(name) - - def types(name: String): Symbol = { - val tpname = newTypeName(name) - findName(tpname) orElse getClassIfDefined(tpname) + private var _replScope: Scope = _ + private def resetReplScope() { + _replScope = newScope } - def terms(name: String): Symbol = { - val termname = newTypeName(name) - findName(termname) orElse getModuleIfDefined(termname) - } - // [Eugene to Paul] possibly you could make use of TypeTags here - def types[T: ClassTag] : Symbol = types(classTag[T].runtimeClass.getName) - def terms[T: ClassTag] : Symbol = terms(classTag[T].runtimeClass.getName) - def apply[T: ClassTag] : Symbol = apply(classTag[T].runtimeClass.getName) + def replScope = { + if (_replScope eq null) + _replScope = newScope - def classSymbols = allDefSymbols collect { case x: ClassSymbol => x } - def methodSymbols = allDefSymbols collect { case x: MethodSymbol => x } + _replScope + } - /** the previous requests this interpreter has processed */ private var executingRequest: Request = _ private val prevRequests = mutable.ListBuffer[Request]() - private val referencedNameMap = mutable.Map[Name, Request]() - private val definedNameMap = mutable.Map[Name, Request]() private val directlyBoundNames = mutable.Set[Name]() - def allHandlers = prevRequestList flatMap (_.handlers) - def allDefHandlers = allHandlers collect { case x: MemberDefHandler => x } - def allDefSymbols = allDefHandlers map (_.symbol) filter (_ ne NoSymbol) - - def lastRequest = if (prevRequests.isEmpty) null else prevRequests.last - def prevRequestList = prevRequests.toList - def allSeenTypes = prevRequestList flatMap (_.typeOf.values.toList) distinct - def allImplicits = allHandlers filter (_.definesImplicit) flatMap (_.definedNames) - def importHandlers = allHandlers collect { case x: ImportHandler => x } - - def visibleTermNames: List[Name] = definedTerms ++ importedTerms distinct - - /** Another entry point for tab-completion, ids in scope */ - def unqualifiedIds = visibleTermNames map (_.toString) filterNot (_ contains "$") sorted - - /** Parse the ScalaSig to find type aliases */ - def aliasForType(path: String) = ByteCode.aliasForType(path) + def allHandlers = prevRequestList flatMap (_.handlers) + def lastRequest = if (prevRequests.isEmpty) null else prevRequests.last + def prevRequestList = prevRequests.toList + def importHandlers = allHandlers collect { case x: ImportHandler => x } def withoutUnwrapping(op: => Unit): Unit = { val saved = isettings.unwrapStrings @@ -1151,7 +1041,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def symbolDefString(sym: Symbol) = { TypeStrings.quieter( - afterTyper(sym.defString), + exitingTyper(sym.defString), sym.owner.name + ".this.", sym.owner.fullName + "." ) @@ -1161,13 +1051,12 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends /** Secret bookcase entrance for repl debuggers: end the line * with "// show" and see what's going on. */ - def isShow = code.lines exists (_.trim endsWith "// show") - def isShowRaw = code.lines exists (_.trim endsWith "// raw") - - // old style - beSilentDuring(parse(code)) foreach { ts => - ts foreach { t => - withoutUnwrapping(repldbg(asCompactString(t))) + def isShow = code.lines exists (_.trim endsWith "// show") + if (isReplDebug || isShow) { + beSilentDuring(parse(code)) foreach { ts => + ts foreach { t => + withoutUnwrapping(echo(asCompactString(t))) + } } } } diff --git a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala b/src/compiler/scala/tools/nsc/interpreter/ISettings.scala index a8f77afcdf..9541d08db1 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ISettings.scala @@ -12,13 +12,6 @@ package interpreter * @author Lex Spoon, 2007/3/24 **/ class ISettings(intp: IMain) { - /** A list of paths where :load should look */ - var loadPath = List(".") - - /** Set this to true to see repl machinery under -Yrich-exceptions. - */ - var showInternalStackTraces = false - /** The maximum length of toString to use when printing the result * of an evaluation. 0 means no maximum. If a printout requires * more than this number of characters, then the printout is @@ -32,7 +25,7 @@ class ISettings(intp: IMain) { var maxAutoprintCompletion = 250 /** String unwrapping can be disabled if it is causing issues. - * Settings this to false means you will see Strings like "$iw.$iw.". + * Setting this to false means you will see Strings like "$iw.$iw.". */ var unwrapStrings = true @@ -44,7 +37,7 @@ class ISettings(intp: IMain) { } def deprecation: Boolean = intp.settings.deprecation.value - def allSettings = Map( + def allSettings = Map[String, Any]( "maxPrintString" -> maxPrintString, "maxAutoprintCompletion" -> maxAutoprintCompletion, "unwrapStrings" -> unwrapStrings, diff --git a/src/compiler/scala/tools/nsc/interpreter/Imports.scala b/src/compiler/scala/tools/nsc/interpreter/Imports.scala index 73d962b5b0..ff7bfd432c 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Imports.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Imports.scala @@ -12,12 +12,9 @@ trait Imports { self: IMain => import global._ - import definitions.{ ScalaPackage, JavaLangPackage, PredefModule } + import definitions.{ ObjectClass, ScalaPackage, JavaLangPackage, PredefModule } import memberHandlers._ - def isNoImports = settings.noimports.value - def isNoPredef = settings.nopredef.value - /** Synthetic import handlers for the language defined imports. */ private def makeWildcardImportHandler(sym: Symbol): ImportHandler = { val hd :: tl = sym.fullName.split('.').toList map newTermName @@ -31,12 +28,9 @@ trait Imports { /** Symbols whose contents are language-defined to be imported. */ def languageWildcardSyms: List[Symbol] = List(JavaLangPackage, ScalaPackage, PredefModule) - def languageWildcards: List[Type] = languageWildcardSyms map (_.tpe) def languageWildcardHandlers = languageWildcardSyms map makeWildcardImportHandler def allImportedNames = importHandlers flatMap (_.importedNames) - def importedTerms = onlyTerms(allImportedNames) - def importedTypes = onlyTypes(allImportedNames) /** Types which have been wildcard imported, such as: * val x = "abc" ; import x._ // type java.lang.String @@ -52,17 +46,11 @@ trait Imports { def sessionWildcards: List[Type] = { importHandlers filter (_.importsWildcard) map (_.targetType) distinct } - def wildcardTypes = languageWildcards ++ sessionWildcards def languageSymbols = languageWildcardSyms flatMap membersAtPickler def sessionImportedSymbols = importHandlers flatMap (_.importedSymbols) def importedSymbols = languageSymbols ++ sessionImportedSymbols def importedTermSymbols = importedSymbols collect { case x: TermSymbol => x } - def importedTypeSymbols = importedSymbols collect { case x: TypeSymbol => x } - def implicitSymbols = importedSymbols filter (_.isImplicit) - - def importedTermNamed(name: String): Symbol = - importedTermSymbols find (_.name.toString == name) getOrElse NoSymbol /** Tuples of (source, imported symbols) in the order they were imported. */ @@ -146,44 +134,42 @@ trait Imports { code append "object %s {\n".format(impname) trailingBraces append "}\n" accessPath append ("." + impname) - - currentImps.clear + currentImps.clear() + } + def maybeWrap(names: Name*) = if (names exists currentImps) addWrapper() + def wrapBeforeAndAfter[T](op: => T): T = { + addWrapper() + try op finally addWrapper() } - - addWrapper() // loop through previous requests, adding imports for each one - for (ReqAndHandler(req, handler) <- reqsToUse) { - handler match { - // If the user entered an import, then just use it; add an import wrapping - // level if the import might conflict with some other import - case x: ImportHandler => - if (x.importsWildcard || currentImps.exists(x.importedNames contains _)) - addWrapper() - - code append (x.member + "\n") - - // give wildcard imports a import wrapper all to their own - if (x.importsWildcard) addWrapper() - else currentImps ++= x.importedNames - - // For other requests, import each defined name. - // import them explicitly instead of with _, so that - // ambiguity errors will not be generated. Also, quote - // the name of the variable, so that we don't need to - // handle quoting keywords separately. - case x => - for (imv <- x.definedNames) { - if (currentImps contains imv) addWrapper() - - code append ("import " + (req fullPath imv) + "\n") - currentImps += imv - } + wrapBeforeAndAfter { + for (ReqAndHandler(req, handler) <- reqsToUse) { + handler match { + // If the user entered an import, then just use it; add an import wrapping + // level if the import might conflict with some other import + case x: ImportHandler if x.importsWildcard => + wrapBeforeAndAfter(code append (x.member + "\n")) + case x: ImportHandler => + maybeWrap(x.importedNames: _*) + code append (x.member + "\n") + currentImps ++= x.importedNames + + // For other requests, import each defined name. + // import them explicitly instead of with _, so that + // ambiguity errors will not be generated. Also, quote + // the name of the variable, so that we don't need to + // handle quoting keywords separately. + case x => + for (sym <- x.definedSymbols) { + maybeWrap(sym.name) + code append s"import ${x.path}\n" + currentImps += sym.name + } + } } } - // add one extra wrapper, to prevent warnings in the common case of - // redefining the value bound in the last interpreter request. - addWrapper() + ComputedImports(code.toString, trailingBraces.toString, accessPath.toString) } @@ -191,5 +177,5 @@ trait Imports { prevRequestList flatMap (req => req.handlers map (req -> _)) private def membersAtPickler(sym: Symbol): List[Symbol] = - beforePickler(sym.info.nonPrivateMembers.toList) + enteringPickler(sym.info.nonPrivateMembers.toList) } diff --git a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala index 8331fddca6..28ddf2939c 100644 --- a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala @@ -7,8 +7,6 @@ package scala.tools.nsc package interpreter import java.io.IOException -import java.nio.channels.ClosedByInterruptException -import scala.util.control.Exception._ import session.History import InteractiveReader._ import Properties.isMac @@ -17,22 +15,16 @@ import Properties.isMac trait InteractiveReader { val interactive: Boolean - def init(): Unit def reset(): Unit - def history: History def completion: Completion - def eraseLine(): Unit def redrawLine(): Unit - def currentLine: String def readYesOrNo(prompt: String, alt: => Boolean): Boolean = readOneKey(prompt) match { case 'y' => true case 'n' => false case _ => alt } - def readAssumingNo(prompt: String) = readYesOrNo(prompt, false) - def readAssumingYes(prompt: String) = readYesOrNo(prompt, true) protected def readOneLine(prompt: String): String protected def readOneKey(prompt: String): Int @@ -52,6 +44,6 @@ object InteractiveReader { def apply(): InteractiveReader = SimpleReader() @deprecated("Use `apply` instead.", "2.9.0") - def createDefault(): InteractiveReader = apply() + def createDefault(): InteractiveReader = apply() // used by sbt } diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala index 219cb35242..19fa562234 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala @@ -6,10 +6,9 @@ package scala.tools.nsc package interpreter -import scala.tools.jline._ -import scala.tools.jline.console.completer._ import Completion._ import scala.collection.mutable.ListBuffer +import scala.reflect.internal.util.StringOps.longestCommonPrefix // REPL completor - queries supplied interpreter for valid // completions based on current contents of buffer. @@ -29,9 +28,6 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput if (isModule) getModuleIfDefined(name) else getModuleIfDefined(name) ) - def getType(name: String, isModule: Boolean) = getSymbol(name, isModule).tpe - def typeOf(name: String) = getType(name, false) - def moduleOf(name: String) = getType(name, true) trait CompilerCompletion { def tp: Type @@ -47,12 +43,11 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput def anyRefMethodsToShow = Set("isInstanceOf", "asInstanceOf", "toString") def tos(sym: Symbol): String = sym.decodedName - def memberNamed(s: String) = afterTyper(effectiveTp member newTermName(s)) - def hasMethod(s: String) = memberNamed(s).isMethod + def memberNamed(s: String) = exitingTyper(effectiveTp member newTermName(s)) // XXX we'd like to say "filterNot (_.isDeprecated)" but this causes the // compiler to crash for reasons not yet known. - def members = afterTyper((effectiveTp.nonPrivateMembers.toList ++ anyMembers) filter (_.isPublic)) + def members = exitingTyper((effectiveTp.nonPrivateMembers.toList ++ anyMembers) filter (_.isPublic)) def methods = members.toList filter (_.isMethod) def packages = members.toList filter (_.isPackage) def aliases = members.toList filter (_.isAliasType) @@ -111,7 +106,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput def excludeNames: List[String] = (anyref.methodNames filterNot anyRefMethodsToShow) :+ "_root_" def methodSignatureString(sym: Symbol) = { - IMain stripString afterTyper(new MethodSymbolOutput(sym).methodString()) + IMain stripString exitingTyper(new MethodSymbolOutput(sym).methodString()) } def exclude(name: String): Boolean = ( @@ -280,10 +275,6 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput if (parsed.isEmpty) xs map ("." + _) else xs } - // generic interface for querying (e.g. interpreter loop, testing) - def completions(buf: String): List[String] = - topLevelFor(Parsed.dotted(buf + ".", buf.length + 1)) - def completer(): ScalaCompleter = new JLineTabCompletion /** This gets a little bit hairy. It's no small feat delegating everything @@ -301,16 +292,6 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput def isConsecutiveTabs(buf: String, cursor: Int) = cursor == lastCursor && buf == lastBuf - // Longest common prefix - def commonPrefix(xs: List[String]): String = { - if (xs.isEmpty || xs.contains("")) "" - else xs.head.head match { - case ch => - if (xs.tail forall (_.head == ch)) "" + ch + commonPrefix(xs map (_.tail)) - else "" - } - } - // This is jline's entry point for completion. override def complete(buf: String, cursor: Int): Candidates = { verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0 @@ -324,7 +305,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput val newCursor = if (winners contains "") p.cursor else { - val advance = commonPrefix(winners) + val advance = longestCommonPrefix(winners) lastCursor = p.position + advance.length lastBuf = (buf take p.position) + advance repldbg("tryCompletion(%s, _) lastBuf = %s, lastCursor = %s, p.position = %s".format( @@ -335,8 +316,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput Some(Candidates(newCursor, winners)) } - def mkDotted = Parsed.dotted(buf, cursor) withVerbosity verbosity - def mkUndelimited = Parsed.undelimited(buf, cursor) withVerbosity verbosity + def mkDotted = Parsed.dotted(buf, cursor) withVerbosity verbosity // a single dot is special cased to completion on the previous result def lastResultCompletion = diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala index 5fd5b41625..5d41f1bbb4 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala @@ -9,9 +9,7 @@ package interpreter import scala.tools.jline.console.ConsoleReader import scala.tools.jline.console.completer._ import session._ -import scala.collection.JavaConverters._ import Completion._ -import io.Streamable.slurp /** * Reads from the console using JLine. @@ -25,7 +23,6 @@ class JLineReader(_completion: => Completion) extends InteractiveReader { private def term = consoleReader.getTerminal() def reset() = term.reset() - def init() = term.init() def scalaToJline(tc: ScalaCompleter): Completer = new Completer { def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = { @@ -37,11 +34,11 @@ class JLineReader(_completion: => Completion) extends InteractiveReader { } class JLineConsoleReader extends ConsoleReader with ConsoleReaderHelper { + // working around protected/trait/java insufficiencies. + def goBack(num: Int): Unit = back(num) if ((history: History) ne NoHistory) this setHistory history - // working around protected/trait/java insufficiencies. - def goBack(num: Int): Unit = back(num) def readOneKey(prompt: String) = { this.print(prompt) this.flush() @@ -49,7 +46,6 @@ class JLineReader(_completion: => Completion) extends InteractiveReader { } def eraseLine() = consoleReader.resetPromptLine("", "", 0) def redrawLineAndFlush(): Unit = { flush() ; drawLine() ; flush() } - // override def readLine(prompt: String): String // A hook for running code after the repl is done initializing. lazy val postInit: Unit = { @@ -66,11 +62,7 @@ class JLineReader(_completion: => Completion) extends InteractiveReader { } } - def currentLine = consoleReader.getCursorBuffer.buffer.toString def redrawLine() = consoleReader.redrawLineAndFlush() - def eraseLine() = consoleReader.eraseLine() - // Alternate implementation, not sure if/when I need this. - // def eraseLine() = while (consoleReader.delete()) { } def readOneLine(prompt: String) = consoleReader readLine prompt def readOneKey(prompt: String) = consoleReader readOneKey prompt } diff --git a/src/compiler/scala/tools/nsc/interpreter/Logger.scala b/src/compiler/scala/tools/nsc/interpreter/Logger.scala index aeb25fc688..7407daf8d0 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Logger.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Logger.scala @@ -11,8 +11,4 @@ trait Logger { def isDebug: Boolean def isTrace: Boolean def out: JPrintWriter - - def info(msg: => Any): Unit = if (isInfo) out println msg - def debug(msg: => Any): Unit = if (isDebug) out println msg - def trace(msg: => Any): Unit = if (isTrace) out println msg } diff --git a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala index 60325ece30..39979c8fbe 100644 --- a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala +++ b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala @@ -19,13 +19,8 @@ class ProcessResult(val line: String) { val exitCode = builder ! logger def lines = buffer.toList - def show() = lines foreach println override def toString = "`%s` (%d lines, exit %d)".format(line, buffer.size, exitCode) } -object ProcessResult { - implicit def processResultToOutputLines(pr: ProcessResult): List[String] = pr.lines - def apply(line: String): ProcessResult = new ProcessResult(line) -} trait LoopCommands { protected def out: JPrintWriter @@ -35,14 +30,6 @@ trait LoopCommands { // a single interpreter command abstract class LoopCommand(val name: String, val help: String) extends (String => Result) { - private var _longHelp: String = null - final def defaultHelp = usageMsg + " (no extended help available.)" - def hasLongHelp = _longHelp != null || longHelp != defaultHelp - def withLongHelp(text: String): this.type = { _longHelp = text ; this } - def longHelp = _longHelp match { - case null => defaultHelp - case text => text - } def usage: String = "" def usageMsg: String = ":" + name + ( if (usage == "") "" else " " + usage @@ -54,11 +41,6 @@ trait LoopCommands { "usage is " + usageMsg Result(true, None) } - - def onError(msg: String) = { - out.println("error: " + msg) - showUsage() - } } object LoopCommand { def nullary(name: String, help: String, f: () => Result): LoopCommand = @@ -67,9 +49,6 @@ trait LoopCommands { def cmd(name: String, usage: String, help: String, f: String => Result): LoopCommand = if (usage == "") new NullaryCmd(name, help, f) else new LineCmd(name, usage, help, f) - - def varargs(name: String, usage: String, help: String, f: List[String] => Result): LoopCommand = - new VarArgsCmd(name, usage, help, f) } class NullaryCmd(name: String, help: String, f: String => Result) extends LoopCommand(name, help) { diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index 67519cf90c..84a47311e2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -7,8 +7,6 @@ package scala.tools.nsc package interpreter import scala.collection.{ mutable, immutable } -import scala.PartialFunction.cond -import scala.reflect.internal.Chars import scala.reflect.internal.Flags._ import scala.language.implicitConversions @@ -21,8 +19,6 @@ trait MemberHandlers { private def codegenln(leadingPlus: Boolean, xs: String*): String = codegen(leadingPlus, (xs ++ Array("\n")): _*) private def codegenln(xs: String*): String = codegenln(true, xs: _*) - - private def codegen(xs: String*): String = codegen(true, xs: _*) private def codegen(leadingPlus: Boolean, xs: String*): String = { val front = if (leadingPlus) "+ " else "" front + (xs map string2codeQuoted mkString " + ") @@ -52,24 +48,26 @@ trait MemberHandlers { } } + private def isTermMacro(ddef: DefDef): Boolean = ddef.mods.isMacro + def chooseHandler(member: Tree): MemberHandler = member match { - case member: DefDef => new DefHandler(member) - case member: ValDef => new ValHandler(member) - case member: Assign => new AssignHandler(member) - case member: ModuleDef => new ModuleHandler(member) - case member: ClassDef => new ClassHandler(member) - case member: TypeDef => new TypeAliasHandler(member) - case member: Import => new ImportHandler(member) - case DocDef(_, documented) => chooseHandler(documented) - case member => new GenericHandler(member) + case member: DefDef if isTermMacro(member) => new TermMacroHandler(member) + case member: DefDef => new DefHandler(member) + case member: ValDef => new ValHandler(member) + case member: ModuleDef => new ModuleHandler(member) + case member: ClassDef => new ClassHandler(member) + case member: TypeDef => new TypeAliasHandler(member) + case member: Assign => new AssignHandler(member) + case member: Import => new ImportHandler(member) + case DocDef(_, documented) => chooseHandler(documented) + case member => new GenericHandler(member) } sealed abstract class MemberDefHandler(override val member: MemberDef) extends MemberHandler(member) { - def symbol = if (member.symbol eq null) NoSymbol else member.symbol - def name: Name = member.name - def mods: Modifiers = member.mods - def keyword = member.keyword - def prettyName = name.decode + override def name: Name = member.name + def mods: Modifiers = member.mods + def keyword = member.keyword + def prettyName = name.decode override def definesImplicit = member.mods.isImplicit override def definesTerm: Option[TermName] = Some(name.toTermName) filter (_ => name.isTermName) @@ -81,9 +79,11 @@ trait MemberHandlers { * in a single interpreter request. */ sealed abstract class MemberHandler(val member: Tree) { + def name: Name = nme.NO_NAME + def path = intp.originalPath(symbol) + def symbol = if (member.symbol eq null) NoSymbol else member.symbol def definesImplicit = false def definesValue = false - def isLegalTopLevel = false def definesTerm = Option.empty[TermName] def definesType = Option.empty[TypeName] @@ -91,7 +91,6 @@ trait MemberHandlers { lazy val referencedNames = ImportVarsTraverser(member) def importedNames = List[Name]() def definedNames = definesTerm.toList ++ definesType.toList - def definedOrImported = definedNames ++ importedNames def definedSymbols = List[Symbol]() def extraCodeToEvaluate(req: Request): String = "" @@ -114,10 +113,10 @@ trait MemberHandlers { // if this is a lazy val we avoid evaluating it here val resultString = if (mods.isLazy) codegenln(false, "<lazy>") - else any2stringOf(req fullPath name, maxStringElements) + else any2stringOf(path, maxStringElements) val vidString = - if (replProps.vids) """" + " @ " + "%%8x".format(System.identityHashCode(%s)) + " """.trim.format(req fullPath name) + if (replProps.vids) s"""" + " @ " + "%%8x".format(System.identityHashCode($path)) + " """.trim else "" """ + "%s%s: %s = " + %s""".format(string2code(prettyName), vidString, string2code(req typeOf name), resultString) @@ -126,17 +125,26 @@ trait MemberHandlers { } class DefHandler(member: DefDef) extends MemberDefHandler(member) { - private def vparamss = member.vparamss - private def isMacro = member.symbol hasFlag MACRO - // true if not a macro and 0-arity - override def definesValue = !isMacro && flattensToEmpty(vparamss) + override def definesValue = flattensToEmpty(member.vparamss) // true if 0-arity override def resultExtractionCode(req: Request) = if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else "" } + abstract class MacroHandler(member: DefDef) extends MemberDefHandler(member) { + override def definesValue = false + override def definesTerm: Option[TermName] = Some(name.toTermName) + override def definesType: Option[TypeName] = None + override def resultExtractionCode(req: Request) = if (mods.isPublic) codegenln(notification(req)) else "" + def notification(req: Request): String + } + + class TermMacroHandler(member: DefDef) extends MacroHandler(member) { + def notification(req: Request) = s"defined term macro $name: ${req.typeOf(name)}" + } + class AssignHandler(member: Assign) extends MemberHandler(member) { val Assign(lhs, rhs) = member - val name = newTermName(freshInternalVarName()) + override lazy val name = newTermName(freshInternalVarName()) override def definesTerm = Some(name) override def definesValue = true @@ -152,17 +160,16 @@ trait MemberHandlers { } class ModuleHandler(module: ModuleDef) extends MemberDefHandler(module) { - override def definesTerm = Some(name) + override def definesTerm = Some(name.toTermName) override def definesValue = true - override def isLegalTopLevel = true - override def resultExtractionCode(req: Request) = codegenln("defined module ", name) + override def resultExtractionCode(req: Request) = codegenln("defined object ", name) } class ClassHandler(member: ClassDef) extends MemberDefHandler(member) { + override def definedSymbols = List(symbol, symbol.companionSymbol) filterNot (_ == NoSymbol) override def definesType = Some(name.toTypeName) override def definesTerm = Some(name.toTermName) filter (_ => mods.isCase) - override def isLegalTopLevel = true override def resultExtractionCode(req: Request) = codegenln("defined %s %s".format(keyword, name)) @@ -178,21 +185,11 @@ trait MemberHandlers { class ImportHandler(imp: Import) extends MemberHandler(imp) { val Import(expr, selectors) = imp - def targetType: Type = intp.typeOfExpression("" + expr) - override def isLegalTopLevel = true - - def createImportForName(name: Name): String = { - selectors foreach { - case sel @ ImportSelector(old, _, `name`, _) => return "import %s.{ %s }".format(expr, sel) - case _ => () - } - "import %s.%s".format(expr, name) + def targetType = intp.global.rootMirror.getModuleIfDefined("" + expr) match { + case NoSymbol => intp.typeOfExpression("" + expr) + case sym => sym.thisType } - // TODO: Need to track these specially to honor Predef masking attempts, - // because they must be the leading imports in the code generated for each - // line. We can use the same machinery as Contexts now, anyway. - def isPredefImport = isReferenceToPredef(expr) - + private def importableTargetMembers = importableMembers(targetType).toList // wildcard imports, e.g. import foo._ private def selectorWild = selectors filter (_.name == nme.USCOREkw) // renamed imports, e.g. import foo.{ bar => baz } @@ -201,22 +198,16 @@ trait MemberHandlers { /** Whether this import includes a wildcard import */ val importsWildcard = selectorWild.nonEmpty - /** Whether anything imported is implicit .*/ - def importsImplicit = implicitSymbols.nonEmpty - def implicitSymbols = importedSymbols filter (_.isImplicit) def importedSymbols = individualSymbols ++ wildcardSymbols - lazy val individualSymbols: List[Symbol] = - beforePickler(individualNames map (targetType nonPrivateMember _)) - - lazy val wildcardSymbols: List[Symbol] = - if (importsWildcard) beforePickler(targetType.nonPrivateMembers.toList) - else Nil + private val selectorNames = selectorRenames filterNot (_ == nme.USCOREkw) flatMap (_.bothNames) toSet + lazy val individualSymbols: List[Symbol] = exitingTyper(importableTargetMembers filter (m => selectorNames(m.name))) + lazy val wildcardSymbols: List[Symbol] = exitingTyper(if (importsWildcard) importableTargetMembers else Nil) /** Complete list of names imported by a wildcard */ lazy val wildcardNames: List[Name] = wildcardSymbols map (_.name) - lazy val individualNames: List[Name] = selectorRenames filterNot (_ == nme.USCOREkw) flatMap (_.bothNames) + lazy val individualNames: List[Name] = individualSymbols map (_.name) /** The names imported by this statement */ override lazy val importedNames: List[Name] = wildcardNames ++ individualNames diff --git a/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala b/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala index eff0ef59c5..627a881cae 100644 --- a/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala +++ b/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala @@ -14,14 +14,10 @@ import scala.reflect.{ClassTag, classTag} trait NamedParamCreator { protected def freshName: () => String - def apply(name: String, tpe: String, value: Any): NamedParam = NamedParamClass(name, tpe, value) def apply[T: ru.TypeTag : ClassTag](name: String, x: T): NamedParam = new Typed[T](name, x) def apply[T: ru.TypeTag : ClassTag](x: T): NamedParam = apply(freshName(), x) - def clazz(name: String, x: Any): NamedParam = new Untyped(name, x) - def clazz(x: Any): NamedParam = clazz(freshName(), x) - implicit def namedValue[T: ru.TypeTag : ClassTag](name: String, x: T): NamedParam = apply(name, x) implicit def tuple[T: ru.TypeTag : ClassTag](pair: (String, T)): NamedParam = apply(pair._1, pair._2) } diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/compiler/scala/tools/nsc/interpreter/Naming.scala index 0d03a8669a..57f3675ada 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Naming.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Naming.scala @@ -6,6 +6,8 @@ package scala.tools.nsc package interpreter +import scala.util.Properties.lineSeparator + /** This is for name logic which is independent of the compiler (notice there's no Global.) * That includes at least generating, metaquoting, mangling, and unmangling. */ @@ -18,8 +20,14 @@ trait Naming { // <ESC> for ansi codes. val binaryChars = cleaned count (ch => ch < 32 && !ch.isWhitespace && ch != ESC) // Lots of binary chars - translate all supposed whitespace into spaces - if (binaryChars > 5) - cleaned map (ch => if (ch.isWhitespace) ' ' else if (ch < 32) '?' else ch) + // except supposed line endings, otherwise scrubbed lines run together + if (binaryChars > 5) // more than one can count while holding a hamburger + cleaned map { + case c if lineSeparator contains c => c + case c if c.isWhitespace => ' ' + case c if c < 32 => '?' + case c => c + } // Not lots - preserve whitespace and ESC else cleaned map (ch => if (ch.isWhitespace || ch == ESC) ch else if (ch < 32) '?' else ch) @@ -78,7 +86,6 @@ trait Naming { private lazy val userVar = new NameCreator(sessionNames.res) // var name, like res0 private lazy val internalVar = new NameCreator(sessionNames.ires) // internal var name, like $ires0 - def isLineName(name: String) = (name startsWith sessionNames.line) && (name stripPrefix sessionNames.line forall (_.isDigit)) def isUserVarName(name: String) = userVar didGenerate name def isInternalVarName(name: String) = internalVar didGenerate name diff --git a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala index b0be956df8..672a6fd28f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package interpreter -import scala.tools.jline.console.completer.ArgumentCompleter.{ ArgumentDelimiter, ArgumentList } import util.returning /** One instance of a command buffer. @@ -18,7 +17,6 @@ class Parsed private ( ) extends Delimited { def isEmpty = args.isEmpty def isUnqualified = args.size == 1 - def isQualified = args.size > 1 def isAtStart = cursor <= 0 private var _verbosity = 0 @@ -32,7 +30,6 @@ class Parsed private ( def bufferTail = new Parsed(buffer drop headLength, cursor - headLength, delimited) withVerbosity verbosity def prev = new Parsed(buffer, cursor - 1, delimited) withVerbosity verbosity - def next = new Parsed(buffer, cursor + 1, delimited) withVerbosity verbosity def currentChar = buffer(cursor) def currentArg = args.last def position = @@ -42,8 +39,6 @@ class Parsed private ( def isFirstDelimiter = !isEmpty && isDelimiterChar(buffer.head) def isLastDelimiter = !isEmpty && isDelimiterChar(buffer.last) - def firstIfDelimiter = if (isFirstDelimiter) buffer.head.toString else "" - def lastIfDelimiter = if (isLastDelimiter) buffer.last.toString else "" def isQuoted = false // TODO def isEscaped = !isAtStart && isEscapeChar(currentChar) && !isEscapeChar(prev.currentChar) @@ -57,13 +52,9 @@ object Parsed { private def onull(s: String) = if (s == null) "" else s - def apply(s: String): Parsed = apply(onull(s), onull(s).length) def apply(s: String, cursor: Int): Parsed = apply(onull(s), cursor, DefaultDelimiters) def apply(s: String, cursor: Int, delimited: Char => Boolean): Parsed = new Parsed(onull(s), cursor, delimited) - def dotted(s: String): Parsed = dotted(onull(s), onull(s).length) def dotted(s: String, cursor: Int): Parsed = new Parsed(onull(s), cursor, _ == '.') - - def undelimited(s: String, cursor: Int): Parsed = new Parsed(onull(s), cursor, _ => false) } diff --git a/src/compiler/scala/tools/nsc/interpreter/Phased.scala b/src/compiler/scala/tools/nsc/interpreter/Phased.scala index 638944713a..f625124e70 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Phased.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Phased.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package interpreter -import scala.collection.{ mutable, immutable } +import scala.collection.immutable import scala.language.implicitConversions /** Mix this into an object and use it as a phasing @@ -24,7 +24,6 @@ trait Phased { case NoPhaseName => false case name => active = name ; true } - def getMulti = multi def setMulti(phases: Seq[PhaseName]): Boolean = { if (phases contains NoPhaseName) false else { @@ -66,16 +65,8 @@ trait Phased { try parseInternal(str) catch { case _: Exception => NoPhaseName } - def apply[T](body: => T) = immutable.SortedMap[PhaseName, T](atMap(PhaseName.all)(body): _*) - - def atCurrent[T](body: => T): T = atPhase(get)(body) + def atCurrent[T](body: => T): T = enteringPhase(get)(body) def multi[T](body: => T): Seq[T] = multi map (ph => at(ph)(body)) - def all[T](body: => T): Seq[T] = atMulti(PhaseName.all)(body) - def show[T](body: => T): Seq[T] = { - val pairs = atMap(PhaseName.all)(body) - pairs foreach { case (ph, op) => Console.println("%15s -> %s".format(ph, op.toString take 240)) } - pairs map (_._2) - } def at[T](ph: PhaseName)(body: => T): T = { val saved = get @@ -90,11 +81,6 @@ trait Phased { finally setMulti(saved) } - def showAt[T](phs: Seq[PhaseName])(body: => T): Unit = - atMap[T](phs)(body) foreach { - case (ph, op) => Console.println("%15s -> %s".format(ph, op.toString take 240)) - } - def atMap[T](phs: Seq[PhaseName])(body: => T): Seq[(PhaseName, T)] = phs zip atMulti(phs)(body) @@ -112,16 +98,12 @@ trait Phased { def apply(id: Int): PhaseName = all find (_.id == id) getOrElse NoPhaseName implicit def apply(s: String): PhaseName = nameMap(s) - implicit def defaultPhaseName: PhaseName = active } sealed abstract class PhaseName { lazy val id = phase.id lazy val name = toString.toLowerCase def phase = currentRun.phaseNamed(name) def isEmpty = this eq NoPhaseName - - // Execute some code during this phase. - def apply[T](body: => T): T = atPhase(phase)(body) } case object Parser extends PhaseName @@ -158,5 +140,4 @@ trait Phased { } implicit def phaseEnumToPhase(name: PhaseName): Phase = name.phase - implicit def phaseNameToPhase(name: String): Phase = currentRun.phaseNamed(name) } diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index 5e6bf8824d..e517a16b32 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -8,8 +8,6 @@ package interpreter import scala.collection.{ mutable, immutable } import scala.util.matching.Regex -import scala.reflect.internal.util.{ BatchSourceFile } -import session.{ History } import scala.io.Codec import java.net.{ URL, MalformedURLException } import io.{ Path } @@ -48,7 +46,6 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re import intp.{ beQuietDuring, typeOfExpression, interpret, parse } import intp.global._ import definitions.{ compilerTypeFromTag, compilerSymbolFromTag} - import rootMirror.{ getClassIfDefined, getModuleIfDefined } abstract class SymSlurper { def isKeep(sym: Symbol): Boolean @@ -73,7 +70,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re pass += 1 val (repeats, unseen) = todo partition seen unseenHistory += unseen.size - if (opt.verbose) { + if (settings.verbose.value) { println("%3d %s accumulated, %s discarded. This pass: %s unseen, %s repeats".format( pass, keep.size, discarded, unseen.size, repeats.size)) } @@ -148,21 +145,10 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re // First we create the ReplVals instance and bind it to $r intp.bind("$r", replVals) // Then we import everything from $r. - intp interpret ("import " + intp.pathToTerm("$r") + "._") + intp interpret ("import " + intp.originalPath("$r") + "._") // And whatever else there is to do. init.lines foreach (intp interpret _) } - def valsDescription: String = { - def to_str(m: Symbol) = "%12s %s".format( - m.decodedName, "" + elimRefinement(m.accessedOrSelf.tpe) stripPrefix "scala.tools.nsc.") - - ( rutil.info[ReplValsImpl].membersDeclared - filter (m => m.isPublic && !m.hasModuleFlag && !m.isConstructor) - sortBy (_.decodedName) - map to_str - mkString ("Name and type of values imported into the repl in power mode.\n\n", "\n", "") - ) - } trait LowPriorityInternalInfo { implicit def apply[T: ru.TypeTag : ClassTag] : InternalInfo[T] = new InternalInfo[T](None) @@ -175,12 +161,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re * symbol, by only implicitly installing one method, "?", and the rest * of the conveniences exist on that wrapper. */ - trait LowPriorityInternalInfoWrapper { - implicit def apply[T: ru.TypeTag : ClassTag] : InternalInfoWrapper[T] = new InternalInfoWrapper[T](None) - } - object InternalInfoWrapper extends LowPriorityInternalInfoWrapper { - - } + trait LowPriorityInternalInfoWrapper { } class InternalInfoWrapper[T: ru.TypeTag : ClassTag](value: Option[T] = None) { def ? : InternalInfo[T] = new InternalInfo[T](value) } @@ -190,7 +171,6 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re * customizable symbol filter (had to hardcode no-spec to reduce noise) */ class InternalInfo[T](value: Option[T] = None)(implicit typeEvidence: ru.TypeTag[T], runtimeClassEvidence: ClassTag[T]) { - private def newInfo[U: ru.TypeTag : ClassTag](value: U): InternalInfo[U] = new InternalInfo[U](Some(value)) private def isSpecialized(s: Symbol) = s.name.toString contains "$mc" private def isImplClass(s: Symbol) = s.name.toString endsWith "$class" @@ -201,47 +181,15 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re || s.isAnonOrRefinementClass || s.isAnonymousFunction ) - def symbol = compilerSymbolFromTag(tag) - def tpe = compilerTypeFromTag(tag) - def name = symbol.name - def companion = symbol.companionSymbol - def info = symbol.info - def moduleClass = symbol.moduleClass - def owner = symbol.owner - def owners = symbol.ownerChain drop 1 - def signature = symbol.defString - - def decls = info.decls - def declsOverride = membersDeclared filter (_.isOverride) - def declsOriginal = membersDeclared filterNot (_.isOverride) - + def symbol = compilerSymbolFromTag(tag) + def tpe = compilerTypeFromTag(tag) def members = membersUnabridged filterNot excludeMember def membersUnabridged = tpe.members.toList - def membersDeclared = members filterNot excludeMember - def membersInherited = members filterNot (membersDeclared contains _) - def memberTypes = members filter (_.name.isTypeName) - def memberMethods = members filter (_.isMethod) - - def pkg = symbol.enclosingPackage - def pkgName = pkg.fullName - def pkgClass = symbol.enclosingPackageClass - def pkgMembers = pkg.info.members filterNot excludeMember - def pkgClasses = pkgMembers filter (s => s.isClass && s.isDefinedInPackage) - def pkgSymbols = new PackageSlurper(pkgClass).slurp() filterNot excludeMember - - def tag = typeEvidence - def runtimeClass = runtimeClassEvidence.runtimeClass - def shortClass = runtimeClass.getName split "[$.]" last - - def baseClasses = tpe.baseClasses - def baseClassDecls = mapFrom(baseClasses)(_.info.decls.toList.sortBy(_.name)) - def ancestors = baseClasses drop 1 - def ancestorDeclares(name: String) = ancestors filter (_.info member newTermName(name) ne NoSymbol) - def baseTypes = tpe.baseTypeSeq.toList - - def <:<[U: ru.TypeTag : ClassTag](other: U) = tpe <:< newInfo(other).tpe - def lub[U: ru.TypeTag : ClassTag](other: U) = intp.global.lub(List(tpe, newInfo(other).tpe)) - def glb[U: ru.TypeTag : ClassTag](other: U) = intp.global.glb(List(tpe, newInfo(other).tpe)) + def pkg = symbol.enclosingPackage + def tag = typeEvidence + def runtimeClass = runtimeClassEvidence.runtimeClass + def shortClass = runtimeClass.getName split "[$.]" last + def baseClasses = tpe.baseClasses override def toString = value match { case Some(x) => "%s (%s)".format(x, shortClass) @@ -267,7 +215,6 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re } object Prettifier extends LowPriorityPrettifier { def stringOf(x: Any): String = scala.runtime.ScalaRunTime.stringOf(x) - def prettify[T](value: T): TraversableOnce[String] = default[T] prettify value def default[T] = new Prettifier[T] { def prettify(x: T): TraversableOnce[String] = AnyPrettifier prettify x def show(x: T): Unit = AnyPrettifier show x @@ -277,45 +224,21 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re def show(x: T): Unit def prettify(x: T): TraversableOnce[String] - def show(xs: TraversableOnce[T]): Unit = prettify(xs) foreach println def prettify(xs: TraversableOnce[T]): TraversableOnce[String] = xs flatMap (x => prettify(x)) } abstract class PrettifierClass[T: Prettifier]() { val pretty = implicitly[Prettifier[T]] - import pretty._ - def value: Seq[T] def pp(f: Seq[T] => Seq[T]): Unit = pretty prettify f(value) foreach (StringPrettifier show _) def freq[U](p: T => U) = (value.toSeq groupBy p mapValues (_.size)).toList sortBy (-_._2) map (_.swap) - def ppfreq[U](p: T => U): Unit = freq(p) foreach { case (count, key) => println("%5d %s".format(count, key)) } - - def |[U](f: Seq[T] => Seq[U]): Seq[U] = f(value) - def ^^[U](f: T => U): Seq[U] = value map f - def ^?[U](pf: PartialFunction[T, U]): Seq[U] = value collect pf - def >>!(implicit ord: Ordering[T]): Unit = pp(_.sorted.distinct) def >>(implicit ord: Ordering[T]): Unit = pp(_.sorted) def >!(): Unit = pp(_.distinct) def >(): Unit = pp(identity) - - def >#(): Unit = this ># (identity[T] _) - def >#[U](p: T => U): Unit = this ppfreq p - - def >?(p: T => Boolean): Unit = pp(_ filter p) - def >?(s: String): Unit = pp(_ filter (_.toString contains s)) - def >?(r: Regex): Unit = pp(_ filter (_.toString matches fixRegex(r))) - - private def fixRegex(r: scala.util.matching.Regex): String = { - val s = r.pattern.toString - val prefix = if (s startsWith "^") "" else """^.*?""" - val suffix = if (s endsWith "$") "" else """.*$""" - - prefix + s + suffix - } } class MultiPrettifierClass[T: Prettifier](val value: Seq[T]) extends PrettifierClass[T]() { } @@ -339,17 +262,11 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re class RichReplURL(url: URL)(implicit codec: Codec) { def slurp(): String = io.Streamable.slurp(url) } - class RichSymbolList(syms: List[Symbol]) { - def sigs = syms map (_.defString) - def infos = syms map (_.info) - } trait Implicits1 { // fallback implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = new SinglePrettifierClass[T](x) - - implicit def liftToTypeName(s: String): TypeName = newTypeName(s) } trait Implicits2 extends Implicits1 { class RichSymbol(sym: Symbol) { @@ -374,26 +291,13 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in) implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec) - - implicit def liftToTermName(s: String): TermName = newTermName(s) - implicit def replListOfSymbols(xs: List[Symbol]) = new RichSymbolList(xs) } trait ReplUtilities { - // [Eugene to Paul] needs review! - // def module[T: Manifest] = getModuleIfDefined(manifest[T].erasure.getName stripSuffix nme.MODULE_SUFFIX_STRING) - // def clazz[T: Manifest] = getClassIfDefined(manifest[T].erasure.getName) def module[T: ru.TypeTag] = ru.typeOf[T].typeSymbol.suchThat(_.isPackage) def clazz[T: ru.TypeTag] = ru.typeOf[T].typeSymbol.suchThat(_.isClass) def info[T: ru.TypeTag : ClassTag] = InternalInfo[T] def ?[T: ru.TypeTag : ClassTag] = InternalInfo[T] - def url(s: String) = { - try new URL(s) - catch { case _: MalformedURLException => - if (Path(s).exists) Path(s).toURL - else new URL("http://" + s) - } - } def sanitize(s: String): String = sanitize(s.getBytes()) def sanitize(s: Array[Byte]): String = (s map { case x if x.toChar.isControl => '?' @@ -411,20 +315,12 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re lazy val rutil: ReplUtilities = new ReplUtilities { } lazy val phased: Phased = new { val global: intp.global.type = intp.global } with Phased { } - def context(code: String) = analyzer.rootContext(unit(code)) - def source(code: String) = newSourceFile(code) - def unit(code: String) = newCompilationUnit(code) - def trees(code: String) = parse(code) getOrElse Nil - def typeOf(id: String) = intp.typeOfExpression(id) + def unit(code: String) = newCompilationUnit(code) + def trees(code: String) = parse(code) getOrElse Nil - override def toString = """ + override def toString = s""" |** Power mode status ** - |Default phase: %s - |Names: %s - |Identifiers: %s - """.stripMargin.format( - phased.get, - intp.allDefinedNames mkString " ", - intp.unqualifiedIds mkString " " - ) + |Default phase: ${phased.get} + |Names: ${intp.unqualifiedIds mkString " "} + """.stripMargin } diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala index 7cd0f436c4..3392ea0b5e 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala @@ -14,9 +14,7 @@ trait ReplConfig { lazy val replProps = new ReplProps class TapMaker[T](x: T) { - def tapInfo(msg: => String): T = tap(x => replinfo(parens(x))) def tapDebug(msg: => String): T = tap(x => repldbg(parens(x))) - def tapTrace(msg: => String): T = tap(x => repltrace(parens(x))) def tap[U](f: T => U): T = { f(x) x @@ -28,12 +26,6 @@ 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) @@ -45,14 +37,10 @@ trait ReplConfig { repltrace(stackTraceString(unwrap(t))) alt } - private[nsc] def substituteAndLog[T](alt: => T)(body: => T): T = - substituteAndLog("" + alt, alt)(body) private[nsc] def substituteAndLog[T](label: String, alt: => T)(body: => T): T = { try body catch logAndDiscard(label, alt) } - private[nsc] def squashAndLog(label: String)(body: => Unit): Unit = - substituteAndLog(label, ())(body) def isReplTrace: Boolean = replProps.trace def isReplDebug: Boolean = replProps.debug || isReplTrace diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplDir.scala b/src/compiler/scala/tools/nsc/interpreter/ReplDir.scala new file mode 100644 index 0000000000..5d386b47b7 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interpreter/ReplDir.scala @@ -0,0 +1,48 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package interpreter + +import io.VirtualDirectory +import settings.MutableSettings +import scala.reflect.io.{ AbstractFile, PlainDirectory, Directory } +import scala.collection.generic.Clearable + +/** Directory to save .class files to. */ +trait ReplDir extends AbstractFile with Clearable { } + +private class ReplVirtualDir() extends VirtualDirectory("(memory)", None) with ReplDir { } +private class ReplRealDir(dir: Directory) extends PlainDirectory(dir) with ReplDir { + def clear() = { + dir.deleteRecursively() + dir.createDirectory() + } +} + +class ReplOutput(val dirSetting: MutableSettings#StringSetting) { + // outdir for generated classfiles - may be in-memory (the default), + // a generated temporary directory, or a specified outdir. + val dir: ReplDir = ( + if (dirSetting.isDefault) + new ReplVirtualDir() + else if (dirSetting.value == "") + new ReplRealDir(Directory.makeTemp("repl")) + else + new ReplRealDir(Directory(dirSetting.value)) + ) + + // print the contents hierarchically + def show(out: JPrintWriter) = { + def pp(root: AbstractFile, indentLevel: Int) { + val label = root.name + val spaces = " " * indentLevel + out.println(spaces + label) + if (root.isDirectory) + root.toList sortBy (_.name) foreach (x => pp(x, indentLevel + 1)) + } + pp(dir, 0) + } +} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala index 7c698a2f3e..0eabd84234 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package interpreter -import reporters._ import typechecker.Analyzer /** A layer on top of Global so I can guarantee some extra @@ -25,7 +24,7 @@ trait ReplGlobal extends Global { val global: ReplGlobal.this.type = ReplGlobal.this } with Analyzer { override def newTyper(context: Context): Typer = new Typer(context) { - override def typed(tree: Tree, mode: Int, pt: Type): Tree = { + override def typed(tree: Tree, mode: Mode, pt: Type): Tree = { val res = super.typed(tree, mode, pt) tree match { case Ident(name) if !tree.symbol.hasPackageFlag && !name.toString.startsWith("$") => diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala b/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala index bc3e7a10d7..2364918494 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala @@ -13,15 +13,11 @@ class ReplProps { private def bool(name: String) = BooleanProp.keyExists(name) private def int(name: String) = IntProp(name) - val jlineDebug = bool("scala.tools.jline.internal.Log.debug") - val jlineTrace = bool("scala.tools.jline.internal.Log.trace") - val info = bool("scala.repl.info") val debug = bool("scala.repl.debug") val trace = bool("scala.repl.trace") val power = bool("scala.repl.power") - val replInitCode = Prop[JFile]("scala.repl.initcode") val replAutorunCode = Prop[JFile]("scala.repl.autoruncode") val powerInitCode = Prop[JFile]("scala.repl.power.initcode") val powerBanner = Prop[JFile]("scala.repl.power.banner") diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala b/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala index f8ecc6c6fe..08472bbc64 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala @@ -6,8 +6,6 @@ package scala.tools.nsc package interpreter -import scala.collection.{ mutable, immutable } -import scala.PartialFunction.cond import scala.reflect.internal.Chars trait ReplStrings { @@ -31,5 +29,4 @@ trait ReplStrings { "scala.runtime.ScalaRunTime.replStringOf(%s, %s)".format(x, maxlen) def words(s: String) = s.trim split "\\s+" filterNot (_ == "") toList - def isQuoted(s: String) = (s.length >= 2) && (s.head == s.last) && ("\"'" contains s.head) } diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala index 53478bdc5d..ea100b25f2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala @@ -57,7 +57,6 @@ object ReplVals { */ def mkCompilerTypeFromTag[T <: Global](global: T) = { import global._ - import definitions._ /** We can't use definitions.compilerTypeFromTag directly because we're passing * it to map and the compiler refuses to perform eta expansion on a method diff --git a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala index 4371f7fe05..36cdf65510 100644 --- a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala +++ b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala @@ -10,7 +10,6 @@ import scala.reflect.{ ClassTag, classTag } class RichClass[T](val clazz: Class[T]) { def toTag: ClassTag[T] = ClassTag[T](clazz) - def toTypeString: String = TypeStrings.fromClazz(clazz) // Sadly isAnonymousClass does not return true for scala anonymous // classes because our naming scheme is not doing well against the @@ -20,14 +19,12 @@ class RichClass[T](val clazz: Class[T]) { catch { case _: java.lang.InternalError => false } // good ol' "Malformed class name" ) - /** It's not easy... to be... me... */ - def supermans: List[ClassTag[_]] = supers map (_.toTag) + def supertags: List[ClassTag[_]] = supers map (_.toTag) def superNames: List[String] = supers map (_.getName) def interfaces: List[JClass] = supers filter (_.isInterface) def hasAncestorName(f: String => Boolean) = superNames exists f def hasAncestor(f: JClass => Boolean) = supers exists f - def hasAncestorInPackage(pkg: String) = hasAncestorName(_ startsWith (pkg + ".")) def supers: List[JClass] = { def loop(x: JClass): List[JClass] = x.getSuperclass match { diff --git a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala index bccd8158ec..2d0917d91f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala @@ -19,11 +19,8 @@ extends InteractiveReader val history = NoHistory val completion = NoCompletion - def init() = () def reset() = () - def eraseLine() = () def redrawLine() = () - def currentLine = "" def readOneLine(prompt: String): String = { if (interactive) { out.print(prompt) @@ -40,4 +37,4 @@ object SimpleReader { def apply(in: BufferedReader = defaultIn, out: JPrintWriter = defaultOut, interactive: Boolean = true): SimpleReader = new SimpleReader(in, out, interactive) -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala index 60399f53bf..33311f5bb3 100644 --- a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala @@ -13,15 +13,12 @@ import NameTransformer._ import scala.reflect.runtime.{universe => ru} import scala.reflect.{ClassTag, classTag} import typechecker.DestructureTypes -import scala.reflect.internal.util.StringOps.ojoin -import scala.language.implicitConversions /** A more principled system for turning types into strings. */ trait StructuredTypeStrings extends DestructureTypes { val global: Global import global._ - import definitions._ case class LabelAndType(label: String, typeName: String) { } object LabelAndType { @@ -36,10 +33,8 @@ trait StructuredTypeStrings extends DestructureTypes { val NoGrouping = Grouping("", "", "", false) val ListGrouping = Grouping("(", ", ", ")", false) val ProductGrouping = Grouping("(", ", ", ")", true) - val ParamGrouping = Grouping("(", ", ", ")", true) val BlockGrouping = Grouping(" { ", "; ", "}", false) - private implicit def lowerName(n: Name): String = "" + n private def str(level: Int)(body: => String): String = " " * level + body private def block(level: Int, grouping: Grouping)(name: String, nodes: List[TypeNode]): String = { val l1 = str(level)(name + grouping.ldelim) @@ -49,7 +44,6 @@ trait StructuredTypeStrings extends DestructureTypes { l1 +: l2 :+ l3 mkString "\n" } private def maybeBlock(level: Int, grouping: Grouping)(name: String, nodes: List[TypeNode]): String = { - import grouping._ val threshold = 70 val try1 = str(level)(name + grouping.join(nodes map (_.show(0, grouping.labels)): _*)) @@ -57,7 +51,7 @@ trait StructuredTypeStrings extends DestructureTypes { else block(level, grouping)(name, nodes) } private def shortClass(x: Any) = { - if (opt.debug) { + if (settings.debug.value) { val name = (x.getClass.getName split '.').last val isAnon = name.reverse takeWhile (_ != '$') forall (_.isDigit) val str = if (isAnon) name else (name split '$').last @@ -194,7 +188,6 @@ trait TypeStrings { else enclClass.getName + "." + (name stripPrefix enclPre) ) } - def scalaName(ct: ClassTag[_]): String = scalaName(ct.runtimeClass) def anyClass(x: Any): JClass = if (x == null) null else x.getClass private def brackets(tps: String*): String = @@ -212,14 +205,8 @@ trait TypeStrings { } private def tparamString[T: ru.TypeTag] : String = { - def typeArguments: List[ru.Type] = { - import ru.TypeRefTag // otherwise the pattern match will be unchecked - // because TypeRef is an abstract type - ru.typeOf[T] match { case ru.TypeRef(_, _, args) => args; case _ => Nil } - } - // [Eugene to Paul] need to use not the `rootMirror`, but a mirror with the REPL's classloader - // how do I get to it? acquiring context classloader seems unreliable because of multithreading - def typeVariables: List[java.lang.Class[_]] = typeArguments map (targ => ru.rootMirror.runtimeClass(targ)) + import ru._ // get TypeRefTag in scope so that pattern match works (TypeRef is an abstract type) + def typeArguments: List[ru.Type] = ru.typeOf[T] match { case ru.TypeRef(_, _, args) => args; case _ => Nil } brackets(typeArguments map (jc => tvarString(List(jc))): _*) } @@ -231,7 +218,6 @@ trait TypeStrings { * practice to rely on toString for correctness) generated the VALID string * representation of the type. */ - def fromTypedValue[T: ru.TypeTag : ClassTag](x: T): String = fromTag[T] def fromValue(value: Any): String = if (value == null) "Null" else fromClazz(anyClass(value)) def fromClazz(clazz: JClass): String = scalaName(clazz) + tparamString(clazz) def fromTag[T: ru.TypeTag : ClassTag] : String = scalaName(classTag[T].runtimeClass) + tparamString[T] @@ -251,13 +237,6 @@ trait TypeStrings { case (res, (k, v)) => res.replaceAll(k, v) } } - - val typeTransforms = List( - "java.lang." -> "", - "scala.collection.immutable." -> "immutable.", - "scala.collection.mutable." -> "mutable.", - "scala.collection.generic." -> "generic." - ) } object TypeStrings extends TypeStrings { } diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala index e3440c9f8b..52a085080b 100644 --- a/src/compiler/scala/tools/nsc/interpreter/package.scala +++ b/src/compiler/scala/tools/nsc/interpreter/package.scala @@ -6,6 +6,10 @@ package scala.tools.nsc import scala.language.implicitConversions +import scala.reflect.{ classTag, ClassTag } +import scala.reflect.runtime.{ universe => ru } +import scala.reflect.{ClassTag, classTag} +import scala.reflect.api.{Mirror, TypeCreator, Universe => ApiUniverse} /** The main REPL related classes and values are as follows. * In addition to standard compiler classes Global and Settings, there are: @@ -44,6 +48,110 @@ package object interpreter extends ReplConfig with ReplStrings { private[nsc] implicit def enrichClass[T](clazz: Class[T]) = new RichClass[T](clazz) private[nsc] implicit def enrichAnyRefWithTap[T](x: T) = new TapMaker(x) - private[nsc] def tracing[T](msg: String)(x: T): T = x.tapTrace(msg) private[nsc] def debugging[T](msg: String)(x: T) = x.tapDebug(msg) + + private val ourClassloader = getClass.getClassLoader + + def staticTypeTag[T: ClassTag]: ru.TypeTag[T] = ru.TypeTag[T]( + ru.runtimeMirror(ourClassloader), + new TypeCreator { + def apply[U <: ApiUniverse with Singleton](m: Mirror[U]): U # Type = + m.staticClass(classTag[T].runtimeClass.getName).toTypeConstructor.asInstanceOf[U # Type] + }) + + /** This class serves to trick the compiler into treating a var + * (intp, in ILoop) as a stable identifier. + */ + implicit class IMainOps(val intp: IMain) { + import intp._ + import global.{ reporter => _, _ } + import definitions._ + + protected def echo(msg: String) = { + Console.out println msg + Console.out.flush() + } + + def implicitsCommand(line: String): String = { + def p(x: Any) = intp.reporter.printMessage("" + x) + + // If an argument is given, only show a source with that + // in its name somewhere. + val args = line split "\\s+" + val filtered = intp.implicitSymbolsBySource filter { + case (source, syms) => + (args contains "-v") || { + if (line == "") (source.fullName.toString != "scala.Predef") + else (args exists (source.name.toString contains _)) + } + } + + if (filtered.isEmpty) + return "No implicits have been imported other than those in Predef." + + filtered foreach { + case (source, syms) => + p("/* " + syms.size + " implicit members imported from " + source.fullName + " */") + + // This groups the members by where the symbol is defined + val byOwner = syms groupBy (_.owner) + val sortedOwners = byOwner.toList sortBy { case (owner, _) => exitingTyper(source.info.baseClasses indexOf owner) } + + sortedOwners foreach { + case (owner, members) => + // Within each owner, we cluster results based on the final result type + // if there are more than a couple, and sort each cluster based on name. + // This is really just trying to make the 100 or so implicits imported + // by default into something readable. + val memberGroups: List[List[Symbol]] = { + val groups = members groupBy (_.tpe.finalResultType) toList + val (big, small) = groups partition (_._2.size > 3) + val xss = ( + (big sortBy (_._1.toString) map (_._2)) :+ + (small flatMap (_._2)) + ) + + xss map (xs => xs sortBy (_.name.toString)) + } + + val ownerMessage = if (owner == source) " defined in " else " inherited from " + p(" /* " + members.size + ownerMessage + owner.fullName + " */") + + memberGroups foreach { group => + group foreach (s => p(" " + intp.symbolDefString(s))) + p("") + } + } + p("") + } + "" + } + + /** TODO - + * -n normalize + * -l label with case class parameter names + * -c complete - leave nothing out + */ + def typeCommandInternal(expr: String, verbose: Boolean): Unit = + symbolOfLine(expr) andAlso (echoTypeSignature(_, verbose)) + + def printAfterTyper(msg: => String) = + reporter printUntruncatedMessage exitingTyper(msg) + + private def replInfo(sym: Symbol) = + if (sym.isAccessor) dropNullaryMethod(sym.info) else sym.info + + def echoTypeStructure(sym: Symbol) = + printAfterTyper("" + deconstruct.show(replInfo(sym))) + + def echoTypeSignature(sym: Symbol, verbose: Boolean) = { + if (verbose) echo("// Type signature") + printAfterTyper("" + replInfo(sym)) + + if (verbose) { + echo("\n// Internal Type structure") + echoTypeStructure(sym) + } + } + } } diff --git a/src/compiler/scala/tools/nsc/interpreter/session/History.scala b/src/compiler/scala/tools/nsc/interpreter/session/History.scala index daa05b86db..794d41adc7 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/History.scala +++ b/src/compiler/scala/tools/nsc/interpreter/session/History.scala @@ -14,15 +14,9 @@ trait History { def asStrings: List[String] def index: Int def size: Int - def grep(s: String): List[String] } object NoHistory extends History { def asStrings = Nil - def grep(s: String) = Nil def index = 0 def size = 0 } - -object History { - def empty: History = NoHistory -} diff --git a/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala b/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala index 9f4e2b9df3..89998e438a 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala +++ b/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala @@ -54,9 +54,5 @@ class SimpleHistory extends JLineHistory { def moveTo(idx: Int) = (idx > 0) && (idx <= lastIndex) && setTo(idx) def moveToEnd(): Unit = setTo(size) - // scala legacy interface - def asList: List[JEntry] = toEntries().toList - def asJavaList = entries() - def asStrings = buf.toList - def grep(s: String) = buf.toList filter (_ contains s) + def asStrings = buf.toList } diff --git a/src/compiler/scala/tools/nsc/io/Fileish.scala b/src/compiler/scala/tools/nsc/io/Fileish.scala deleted file mode 100644 index 7b4e385dd8..0000000000 --- a/src/compiler/scala/tools/nsc/io/Fileish.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package io - -import java.io.{ InputStream } -import java.util.jar.JarEntry - -/** A common interface for File-based things and Stream-based things. - * (In particular, io.File and JarEntry.) - */ -class Fileish(val path: Path, val input: () => InputStream) extends Streamable.Chars { - def inputStream() = input() - - def parent = path.parent - def name = path.name - def isSourceFile = path.hasExtension("java", "scala") - - private lazy val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } - lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") - lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "." - - override def toString = path.path -} - -object Fileish { - def apply(f: File): Fileish = new Fileish(f, () => f.inputStream()) - def apply(f: JarEntry, in: () => InputStream): Fileish = new Fileish(Path(f.getName), in) - def apply(path: String, in: () => InputStream): Fileish = new Fileish(Path(path), in) -} diff --git a/src/compiler/scala/tools/nsc/io/Jar.scala b/src/compiler/scala/tools/nsc/io/Jar.scala index e919621338..0dca75dab9 100644 --- a/src/compiler/scala/tools/nsc/io/Jar.scala +++ b/src/compiler/scala/tools/nsc/io/Jar.scala @@ -10,7 +10,6 @@ import java.io.{ InputStream, OutputStream, IOException, FileNotFoundException, import java.util.jar._ import scala.collection.JavaConverters._ import Attributes.Name -import util.ClassPath import scala.language.implicitConversions // Attributes.Name instances: @@ -37,9 +36,6 @@ class Jar(file: File) extends Iterable[JarEntry] { def this(jfile: JFile) = this(File(jfile)) def this(path: String) = this(File(path)) - protected def errorFn(msg: String): Unit = Console println msg - - lazy val jarFile = new JarFile(file.jfile) lazy val manifest = withJarInput(s => Option(s.getManifest)) def mainClass = manifest map (f => f(Name.MAIN_CLASS)) @@ -51,6 +47,20 @@ class Jar(file: File) extends Iterable[JarEntry] { case _ => Nil } + /** Invoke f with input for named jar entry (or None). */ + def withEntryStream[A](name: String)(f: Option[InputStream] => A) = { + val jarFile = new JarFile(file.jfile) + def apply() = + jarFile getEntry name match { + case null => f(None) + case entry => + val in = Some(jarFile getInputStream entry) + try f(in) + finally in map (_.close()) + } + try apply() finally jarFile.close() + } + def withJarInput[T](f: JarInputStream => T): T = { val in = new JarInputStream(file.inputStream()) try f(in) @@ -64,12 +74,6 @@ class Jar(file: File) extends Iterable[JarEntry] { Iterator continually in.getNextJarEntry() takeWhile (_ != null) foreach f } override def iterator: Iterator[JarEntry] = this.toList.iterator - def fileishIterator: Iterator[Fileish] = jarFile.entries.asScala map (x => Fileish(x, () => getEntryStream(x))) - - private def getEntryStream(entry: JarEntry) = jarFile getInputStream entry match { - case null => errorFn("No such entry: " + entry) ; null - case x => x - } override def toString = "" + file } @@ -131,7 +135,6 @@ object Jar { m } def apply(manifest: JManifest): WManifest = new WManifest(manifest) - implicit def unenrichManifest(x: WManifest): JManifest = x.underlying } class WManifest(manifest: JManifest) { for ((k, v) <- initialMainAttrs) @@ -148,12 +151,7 @@ object Jar { } def apply(name: Attributes.Name): String = attrs(name) - def apply(name: String): String = apply(new Attributes.Name(name)) def update(key: Attributes.Name, value: String) = attrs.put(key, value) - def update(key: String, value: String) = attrs.put(new Attributes.Name(key), value) - - def mainClass: String = apply(Name.MAIN_CLASS) - def mainClass_=(value: String) = update(Name.MAIN_CLASS, value) } // See http://download.java.net/jdk7/docs/api/java/nio/file/Path.html diff --git a/src/compiler/scala/tools/nsc/io/Lexer.scala b/src/compiler/scala/tools/nsc/io/Lexer.scala index 5ffb5b4d4f..aed6e882e6 100644 --- a/src/compiler/scala/tools/nsc/io/Lexer.scala +++ b/src/compiler/scala/tools/nsc/io/Lexer.scala @@ -1,16 +1,14 @@ package scala.tools.nsc.io -import java.io.{Reader, Writer, StringReader, StringWriter} -import scala.collection.mutable.{Buffer, ArrayBuffer} -import scala.math.BigInt +import java.io.Reader /** Companion object of class `Lexer` which defines tokens and some utility concepts * used for tokens and lexers */ object Lexer { - /** An exception raised if a if input does not correspond to what's expected - * @param rdr the lexer form which the bad input is read + /** An exception raised if an input does not correspond to what's expected + * @param rdr the lexer from which the bad input is read * @param msg the error message */ class MalformedInput(val rdr: Lexer, val msg: String) extends Exception("Malformed JSON input at "+rdr.tokenPos+": "+msg) diff --git a/src/compiler/scala/tools/nsc/io/MsilFile.scala b/src/compiler/scala/tools/nsc/io/MsilFile.scala deleted file mode 100644 index 2f0a71fc60..0000000000 --- a/src/compiler/scala/tools/nsc/io/MsilFile.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package io - -import ch.epfl.lamp.compiler.msil.{ Type => MsilType, _ } - -/** This class wraps an MsilType. It exists only so - * ClassPath can treat all of JVM/MSIL/bin/src files - * uniformly, as AbstractFiles. - */ -class MsilFile(val msilType: MsilType) extends VirtualFile(msilType.FullName, msilType.Namespace) { -} - -object NoMsilFile extends MsilFile(null) { } diff --git a/src/compiler/scala/tools/nsc/io/Pickler.scala b/src/compiler/scala/tools/nsc/io/Pickler.scala index b03a921e87..5d32c10143 100644 --- a/src/compiler/scala/tools/nsc/io/Pickler.scala +++ b/src/compiler/scala/tools/nsc/io/Pickler.scala @@ -1,6 +1,5 @@ package scala.tools.nsc.io -import scala.annotation.unchecked import Lexer._ import java.io.Writer import scala.language.implicitConversions @@ -71,14 +70,6 @@ abstract class Pickler[T] { */ def wrapped [U] (in: T => U)(out: U => T): Pickler[U] = wrappedPickler(this)(in)(out) - /** A pickler obtained from the current pickler by also admitting `null` as - * a handled value, represented as the token `null`. - * - * @param fromNull an implicit evidence parameter ensuring that the type of values - * handled by this pickler contains `null`. - */ - def orNull(implicit fromNull: Null <:< T): Pickler[T] = nullablePickler(this) - /** A conditional pickler obtained from the current pickler. * @param cond the condition to test to find out whether pickler can handle * some Scala value. @@ -93,9 +84,6 @@ abstract class Pickler[T] { } object Pickler { - - var picklerDebugMode = false - /** A base class representing unpickler result. It has two subclasses: * `UnpickleSucess` for successful unpicklings and `UnpickleFailure` for failures, * where a value of the given type `T` could not be unpickled from input. @@ -175,17 +163,6 @@ object Pickler { def ~ [T](y: T): S ~ T = new ~ (x, y) } - /** 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) } - - /** An converter from unctions returning Options over pair to functions returning `~`-pairs - * The converted function will raise a `MatchError` where the original function returned - * a `None`. This converter is useful for turning `unapply` methods of case classes - * into wrapper methods that can be passed as second argument to `wrap`. - */ - implicit def toTilde[T1, T2, S](f: S => Option[(T1, T2)]): S => T1 ~ T2 = { x => (f(x): @unchecked) match { case Some((x1, x2)) => x1 ~ x2 } } - /** Same as `p.labelled(label)`. */ def labelledPickler[T](label: String, p: Pickler[T]): Pickler[T] = new Pickler[T] { @@ -249,16 +226,6 @@ object Pickler { def unpickle(rd: Lexer) = p.unpickle(rd) orElse qq.unpickle(rd) } - /** Same as `p.orNull` - */ - def nullablePickler[T](p: Pickler[T])(implicit fromNull: Null <:< T): Pickler[T] = new Pickler[T] { - def pickle(wr: Writer, x: T) = - if (x == null) wr.write("null") else p.pickle(wr, x) - def unpickle(rd: Lexer): Unpickled[T] = - if (rd.token == NullLit) nextSuccess(rd, fromNull(null)) - else p.unpickle(rd) - } - /** A conditional pickler for singleton objects. It represents these * with the object's underlying class as a label. * Example: Object scala.None would be represented as `scala.None$()`. @@ -330,22 +297,9 @@ object Pickler { implicit val longPickler: Pickler[Long] = tokenPickler("integer literal") { case IntLit(s) => s.toLong } - /** A pickler for values of type `Double`, represented as floating point literals */ - implicit val doublePickler: Pickler[Double] = - tokenPickler("floating point literal") { case FloatLit(s) => s.toDouble } - - /** A pickler for values of type `Byte`, represented as integer literals */ - implicit val bytePickler: Pickler[Byte] = longPickler.wrapped { _.toByte } { _.toLong } - - /** A pickler for values of type `Short`, represented as integer literals */ - implicit val shortPickler: Pickler[Short] = longPickler.wrapped { _.toShort } { _.toLong } - /** A pickler for values of type `Int`, represented as integer literals */ implicit val intPickler: Pickler[Int] = longPickler.wrapped { _.toInt } { _.toLong } - /** A pickler for values of type `Float`, represented as floating point literals */ - implicit val floatPickler: Pickler[Float] = doublePickler.wrapped { _.toFloat } { _.toLong } - /** A conditional pickler for the boolean value `true` */ private val truePickler = tokenPickler("boolean literal") { case TrueLit => true } cond { _ == true } @@ -373,52 +327,15 @@ object Pickler { } } - /** A pickler for values of type `Char`, represented as string literals of length 1 */ - implicit val charPickler: Pickler[Char] = - stringPickler - .wrapped { s => require(s.length == 1, "single character string literal expected, but "+quoted(s)+" found"); s(0) } { _.toString } - - /** A pickler for pairs, represented as `~`-pairs */ - implicit def tuple2Pickler[T1: Pickler, T2: Pickler]: Pickler[(T1, T2)] = - (pkl[T1] ~ pkl[T2]) - .wrapped { case x1 ~ x2 => (x1, x2) } { case (x1, x2) => x1 ~ x2 } - .labelled ("tuple2") - /** A pickler for 3-tuples, represented as `~`-tuples */ implicit def tuple3Pickler[T1, T2, T3](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3]): Pickler[(T1, T2, T3)] = (p1 ~ p2 ~ p3) .wrapped { case x1 ~ x2 ~ x3 => (x1, x2, x3) } { case (x1, x2, x3) => x1 ~ x2 ~ x3 } .labelled ("tuple3") - /** A pickler for 4-tuples, represented as `~`-tuples */ - implicit def tuple4Pickler[T1, T2, T3, T4](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3], p4: Pickler[T4]): Pickler[(T1, T2, T3, T4)] = - (p1 ~ p2 ~ p3 ~ p4) - .wrapped { case x1 ~ x2 ~ x3 ~ x4 => (x1, x2, x3, x4) } { case (x1, x2, x3, x4) => x1 ~ x2 ~ x3 ~ x4 } - .labelled ("tuple4") - - /** A conditional pickler for the `scala.None` object */ - implicit val nonePickler = singletonPickler(None) - - /** A conditional pickler for instances of class `scala.Some` */ - implicit def somePickler[T: Pickler]: CondPickler[Some[T]] = - pkl[T] - .wrapped { Some(_) } { _.get } - .asClass (classOf[Some[T]]) - - /** A pickler for optional values */ - implicit def optionPickler[T: Pickler]: Pickler[Option[T]] = nonePickler | somePickler[T] - /** A pickler for list values */ implicit def listPickler[T: Pickler]: Pickler[List[T]] = iterPickler[T] .wrapped { _.toList } { _.iterator } .labelled ("scala.List") - - /** A pickler for vector values */ - implicit def vectorPickler[T: Pickler]: Pickler[Vector[T]] = - iterPickler[T] .wrapped { Vector() ++ _ } { _.iterator } .labelled ("scala.Vector") - - /** A pickler for array values */ - implicit def array[T : ClassTag : Pickler]: Pickler[Array[T]] = - iterPickler[T] .wrapped { _.toArray} { _.iterator } .labelled ("scala.Array") } /** A subclass of Pickler can indicate whether a particular value can be pickled by instances diff --git a/src/compiler/scala/tools/nsc/io/Replayer.scala b/src/compiler/scala/tools/nsc/io/Replayer.scala index 5cb61b6cb1..e3dc8939a3 100644 --- a/src/compiler/scala/tools/nsc/io/Replayer.scala +++ b/src/compiler/scala/tools/nsc/io/Replayer.scala @@ -3,7 +3,7 @@ package scala.tools.nsc.io import java.io.{Reader, Writer} import Pickler._ -import Lexer.{Token, EOF} +import Lexer.EOF abstract class LogReplay { def logreplay(event: String, x: => Boolean): Boolean diff --git a/src/compiler/scala/tools/nsc/io/Socket.scala b/src/compiler/scala/tools/nsc/io/Socket.scala index e766c1b2fd..4925c50d85 100644 --- a/src/compiler/scala/tools/nsc/io/Socket.scala +++ b/src/compiler/scala/tools/nsc/io/Socket.scala @@ -28,13 +28,10 @@ object Socket { private val optHandler = handlerFn[Option[T]](_ => None) private val eitherHandler = handlerFn[Either[Throwable, T]](x => Left(x)) - def getOrElse[T1 >: T](alt: T1): T1 = opt getOrElse alt def either: Either[Throwable, T] = try Right(f()) catch eitherHandler def opt: Option[T] = try Some(f()) catch optHandler } - def newIPv4Server(port: Int = 0) = new Box(() => preferringIPv4(new ServerSocket(0))) - def newServer(port: Int = 0) = new Box(() => new ServerSocket(0)) def localhost(port: Int) = apply(InetAddress.getLocalHost(), port) def apply(host: InetAddress, port: Int) = new Box(() => new Socket(new JSocket(host, port))) def apply(host: String, port: Int) = new Box(() => new Socket(new JSocket(host, port))) @@ -62,4 +59,4 @@ class Socket(jsocket: JSocket) extends Streamable.Bytes with Closeable { out.close() } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/io/SourceReader.scala b/src/compiler/scala/tools/nsc/io/SourceReader.scala index 569270f530..ece78db2cf 100644 --- a/src/compiler/scala/tools/nsc/io/SourceReader.scala +++ b/src/compiler/scala/tools/nsc/io/SourceReader.scala @@ -9,7 +9,7 @@ package io import java.io.{ FileInputStream, InputStream, IOException } import java.nio.{ByteBuffer, CharBuffer} -import java.nio.channels.{FileChannel, ReadableByteChannel, Channels} +import java.nio.channels.{ ReadableByteChannel, Channels } import java.nio.charset.{CharsetDecoder, CoderResult} import scala.tools.nsc.reporters._ @@ -33,9 +33,6 @@ class SourceReader(decoder: CharsetDecoder, reporter: Reporter) { "Please try specifying another one using the -encoding option") } - /** Reads the file with the specified name. */ - def read(filename: String): Array[Char]= read(new JFile(filename)) - /** Reads the specified file. */ def read(file: JFile): Array[Char] = { val c = new FileInputStream(file).getChannel diff --git a/src/compiler/scala/tools/nsc/io/package.scala b/src/compiler/scala/tools/nsc/io/package.scala index 711696bb6e..0b2db115fb 100644 --- a/src/compiler/scala/tools/nsc/io/package.scala +++ b/src/compiler/scala/tools/nsc/io/package.scala @@ -7,7 +7,6 @@ package scala.tools.nsc import java.util.concurrent.{ Future, Callable } import java.util.{ Timer, TimerTask } -import java.util.jar.{ Attributes } import scala.language.implicitConversions package object io { @@ -21,14 +20,10 @@ package object io { type Path = scala.reflect.io.Path val Path = scala.reflect.io.Path type PlainFile = scala.reflect.io.PlainFile - val PlainFile = scala.reflect.io.PlainFile val Streamable = scala.reflect.io.Streamable type VirtualDirectory = scala.reflect.io.VirtualDirectory type VirtualFile = scala.reflect.io.VirtualFile - val ZipArchive = scala.reflect.io.ZipArchive type ZipArchive = scala.reflect.io.ZipArchive - - implicit def postfixOps = scala.language.postfixOps // make all postfix ops in this package compile without warning type JManifest = java.util.jar.Manifest type JFile = java.io.File @@ -39,23 +34,11 @@ package object io { def runnable(body: => Unit): Runnable = new Runnable { override def run() = body } def callable[T](body: => T): Callable[T] = new Callable[T] { override def call() = body } def spawn[T](body: => T): Future[T] = daemonThreadPool submit callable(body) - def submit(runnable: Runnable) = daemonThreadPool submit runnable - // Create, start, and return a daemon thread - def daemonize(body: => Unit): Thread = newThread(_ setDaemon true)(body) def newThread(f: Thread => Unit)(body: => Unit): Thread = { val thread = new Thread(runnable(body)) f(thread) thread.start thread } - - // Set a timer to execute the given code. - def timer(seconds: Int)(body: => Unit): Timer = { - val alarm = new Timer(true) // daemon - val tt = new TimerTask { def run() = body } - - alarm.schedule(tt, seconds * 1000) - alarm - } } diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 43a8402fc7..bb82cfb827 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -35,7 +35,6 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { abstract class JavaParser extends ParserCommon { val in: JavaScanner - protected def posToReport: Int = in.currentPos def freshName(prefix : String): Name protected implicit def i2p(offset : Int) : Position private implicit def p2i(pos : Position): Int = if (pos.isDefined) pos.point else -1 @@ -94,11 +93,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { if (skipIt) skip() } - def warning(msg: String) : Unit = warning(in.currentPos, msg) - def errorTypeTree = TypeTree().setType(ErrorType) setPos in.currentPos - def errorTermTree = Literal(Constant(null)) setPos in.currentPos - def errorPatternTree = blankExpr setPos in.currentPos // --------- tree building ----------------------------- @@ -130,7 +125,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def makeSyntheticParam(count: Int, tpt: Tree): ValDef = makeParam(nme.syntheticParamName(count), tpt) def makeParam(name: String, tpt: Tree): ValDef = - makeParam(newTypeName(name), tpt) + makeParam(name: TermName, tpt) def makeParam(name: TermName, tpt: Tree): ValDef = ValDef(Modifiers(Flags.JAVA | Flags.PARAM), name, tpt, EmptyTree) @@ -178,11 +173,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def accept(token: Int): Int = { val pos = in.currentPos if (in.token != token) { - val posToReport = - //if (in.currentPos.line(unit.source).get(0) > in.lastPos.line(unit.source).get(0)) - // in.lastPos - //else - in.currentPos + val posToReport = in.currentPos val msg = JavaScannerConfiguration.token2string(token) + " expected but " + JavaScannerConfiguration.token2string(in.token) + " found." @@ -348,46 +339,10 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { /** Annotation ::= TypeName [`(` AnnotationArgument {`,` AnnotationArgument} `)`] */ def annotation() { - val pos = in.currentPos - var t = qualId() + qualId() if (in.token == LPAREN) { skipAhead(); accept(RPAREN) } else if (in.token == LBRACE) { skipAhead(); accept(RBRACE) } } -/* - def annotationArg() = { - val pos = in.token - if (in.token == IDENTIFIER && in.lookaheadToken == ASSIGN) { - val name = ident() - accept(ASSIGN) - atPos(pos) { - ValDef(Modifiers(Flags.JAVA), name, TypeTree(), elementValue()) - } - } else { - elementValue() - } - } - - def elementValue(): Tree = - if (in.token == AT) annotation() - else if (in.token == LBRACE) elementValueArrayInitializer() - else expression1() - - def elementValueArrayInitializer() = { - accept(LBRACE) - val buf = new ListBuffer[Tree] - def loop() = - if (in.token != RBRACE) { - buf += elementValue() - if (in.token == COMMA) { - in.nextToken - loop() - } - } - loop() - accept(RBRACE) - buf.toList - } - */ def modifiers(inInterface: Boolean): Modifiers = { var flags: Long = Flags.JAVA @@ -493,7 +448,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { AppliedTypeTree(scalaDot(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME), List(t)) } } - varDecl(in.currentPos, Modifiers(Flags.JAVA | Flags.PARAM), t, ident()) + varDecl(in.currentPos, Modifiers(Flags.JAVA | Flags.PARAM), t, ident().toTermName) } def optThrows() { @@ -551,7 +506,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { if (parentToken == AT && in.token == DEFAULT) { val annot = atPos(pos) { - New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), ListOfNil) + New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil) } mods1 = mods1 withAnnotations List(annot) skipTo(SEMI) @@ -587,7 +542,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { * these potential definitions are real or not. */ def fieldDecls(pos: Position, mods: Modifiers, tpt: Tree, name: Name): List[Tree] = { - val buf = ListBuffer[Tree](varDecl(pos, mods, tpt, name)) + val buf = ListBuffer[Tree](varDecl(pos, mods, tpt, name.toTermName)) val maybe = new ListBuffer[Tree] // potential variable definitions. while (in.token == COMMA) { in.nextToken @@ -595,10 +550,10 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val name = ident() if (in.token == ASSIGN || in.token == SEMI) { // ... followed by a `=` or `;`, we know it's a real variable definition buf ++= maybe - buf += varDecl(in.currentPos, mods, tpt.duplicate, name) + buf += varDecl(in.currentPos, mods, tpt.duplicate, name.toTermName) maybe.clear() } else if (in.token == COMMA) { // ... if there's a comma after the ident, it could be a real vardef or not. - maybe += varDecl(in.currentPos, mods, tpt.duplicate, name) + maybe += varDecl(in.currentPos, mods, tpt.duplicate, name.toTermName) } else { // ... if there's something else we were still in the initializer of the // previous var def; skip to next comma or semicolon. skipTo(COMMA, SEMI) @@ -875,7 +830,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { // The STABLE flag is to signal to namer that this was read from a // java enum, and so should be given a Constant type (thereby making // it usable in annotations.) - ValDef(Modifiers(Flags.STABLE | Flags.JAVA | Flags.STATIC), name, enumType, blankExpr) + ValDef(Modifiers(Flags.STABLE | Flags.JAVA | Flags.STATIC), name.toTermName, enumType, blankExpr) } } diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index e230585a8b..84eee36f18 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -57,23 +57,14 @@ trait JavaScanners extends ast.parser.ScannersCommon { /** ... */ abstract class AbstractJavaScanner extends AbstractJavaTokenData { - implicit def p2g(pos: Position): ScanPosition implicit def g2p(pos: ScanPosition): Position - /** the last error position - */ - var errpos: ScanPosition - var lastPos: ScanPosition - def skipToken: ScanPosition def nextToken(): Unit def next: AbstractJavaTokenData def intVal(negated: Boolean): Long def floatVal(negated: Boolean): Double def intVal: Long = intVal(false) def floatVal: Double = floatVal(false) - //def token2string(token : Int) : String = configuration.token2string(token) - /** return recent scala doc, if any */ - def flushDoc: DocComment def currentPos: Position } @@ -227,17 +218,9 @@ trait JavaScanners extends ast.parser.ScannersCommon { abstract class JavaScanner extends AbstractJavaScanner with JavaTokenData with Cloneable with ScannerCommon { override def intVal = super.intVal// todo: needed? override def floatVal = super.floatVal - override var errpos: Int = NoPos def currentPos: Position = g2p(pos - 1) - var in: JavaCharArrayReader = _ - def dup: JavaScanner = { - val dup = clone().asInstanceOf[JavaScanner] - dup.in = in.dup - dup - } - /** character buffer for literals */ val cbuf = new StringBuilder() @@ -256,12 +239,6 @@ trait JavaScanners extends ast.parser.ScannersCommon { */ var docBuffer: StringBuilder = null - def flushDoc: DocComment = { - val ret = if (docBuffer != null) DocComment(docBuffer.toString, NoPosition) else null - docBuffer = null - ret - } - /** add the given character to the documentation buffer */ protected def putDocChar(c: Char) { @@ -277,13 +254,6 @@ trait JavaScanners extends ast.parser.ScannersCommon { // Get next token ------------------------------------------------------------ - /** read next token and return last position - */ - def skipToken: Int = { - val p = pos; nextToken - p - 1 - } - def nextToken() { if (next.token == EMPTY) { fetchToken() @@ -308,7 +278,6 @@ trait JavaScanners extends ast.parser.ScannersCommon { private def fetchToken() { if (token == EOF) return lastPos = in.cpos - 1 - //var index = bp while (true) { in.ch match { case ' ' | '\t' | CR | LF | FF => @@ -868,7 +837,6 @@ trait JavaScanners extends ast.parser.ScannersCommon { def syntaxError(pos: Int, msg: String) { error(pos, msg) token = ERROR - errpos = pos } /** generate an error at the current token position @@ -879,7 +847,6 @@ trait JavaScanners extends ast.parser.ScannersCommon { def incompleteInputError(msg: String) { incompleteInputError(pos, msg) token = EOF - errpos = pos } override def toString() = token match { @@ -913,16 +880,12 @@ trait JavaScanners extends ast.parser.ScannersCommon { } } - /** ... - */ class JavaUnitScanner(unit: CompilationUnit) extends JavaScanner { in = new JavaCharArrayReader(unit.source.content, !settings.nouescape.value, syntaxError) init - def warning(pos: Int, msg: String) = unit.warning(pos, msg) def error (pos: Int, msg: String) = unit. error(pos, msg) def incompleteInputError(pos: Int, msg: String) = unit.incompleteInputError(pos, msg) def deprecationWarning(pos: Int, msg: String) = unit.deprecationWarning(pos, msg) - implicit def p2g(pos: Position): Int = if (pos.isDefined) pos.point else -1 implicit def g2p(pos: Int): Position = new OffsetPosition(unit.source, pos) } } diff --git a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala index a562de291d..953a3c6d82 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala @@ -68,9 +68,6 @@ object JavaTokens extends ast.parser.Tokens { final val VOLATILE = 68 final val WHILE = 69 - def isKeyword(code : Int) = - code >= ABSTRACT && code <= WHILE - /** special symbols */ final val COMMA = 70 final val SEMI = 71 @@ -115,9 +112,6 @@ object JavaTokens extends ast.parser.Tokens { final val GTGTEQ = 113 final val GTGTGTEQ = 114 - def isSymbol(code : Int) = - code >= COMMA && code <= GTGTGTEQ - /** parenthesis */ final val LPAREN = 115 final val RPAREN = 116 diff --git a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala deleted file mode 100644 index 5ca9fd5062..0000000000 --- a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala +++ /dev/null @@ -1,138 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * Author: Paul Phillips - */ - -package scala.tools.nsc -package matching - -import transform.ExplicitOuter -import ast.{ Printers, Trees } -import java.io.{ StringWriter, PrintWriter } -import scala.annotation.elidable -import scala.language.postfixOps - -/** Ancillary bits of ParallelMatching which are better off - * out of the way. - */ -trait MatchSupport extends ast.TreeDSL { self: ParallelMatching => - - import global.{ typer => _, _ } - import CODE._ - - /** Debugging support: enable with -Ypmat-debug **/ - private final def trace = settings.Ypmatdebug.value - - def impossible: Nothing = abort("this never happens") - - def treeCollect[T](tree: Tree, pf: PartialFunction[Tree, T]): List[T] = - tree filter (pf isDefinedAt _) map (x => pf(x)) - - object Types { - import definitions._ - - val subrangeTypes = Set[Symbol](ByteClass, ShortClass, CharClass, IntClass) - - implicit class RichType(undecodedTpe: Type) { - def tpe = decodedEqualsType(undecodedTpe) - def isAnyRef = tpe <:< AnyRefClass.tpe - - // These tests for final classes can inspect the typeSymbol - private def is(s: Symbol) = tpe.typeSymbol eq s - def isByte = is(ByteClass) - def isShort = is(ShortClass) - def isInt = is(IntClass) - def isChar = is(CharClass) - def isBoolean = is(BooleanClass) - def isNothing = is(NothingClass) - def isArray = is(ArrayClass) - } - } - - object Debug { - def typeToString(t: Type): String = t match { - case NoType => "x" - case x => x.toString - } - def symbolToString(s: Symbol): String = s match { - case x => x.toString - } - def treeToString(t: Tree): String = treeInfo.unbind(t) match { - case EmptyTree => "?" - case WILD() => "_" - case Literal(Constant(x)) => "LIT(%s)".format(x) - case Apply(fn, args) => "%s(%s)".format(treeToString(fn), args map treeToString mkString ",") - case Typed(expr, tpt) => "%s: %s".format(treeToString(expr), treeToString(tpt)) - case x => x.toString + " (" + x.getClass + ")" - } - - // Formatting for some error messages - private val NPAD = 15 - def pad(s: String): String = "%%%ds" format (NPAD-1) format s - def pad(s: Any): String = pad(s match { - case x: Tree => treeToString(x) - case x => x.toString - }) - - // pretty print for debugging - def pp(x: Any): String = pp(x, false) - def pp(x: Any, newlines: Boolean): String = { - val stripStrings = List("""java\.lang\.""", """\$iw\.""") - - def clean(s: String): String = - stripStrings.foldLeft(s)((s, x) => s.replaceAll(x, "")) - - def pplist(xs: List[Any]): String = - if (newlines) (xs map (" " + _ + "\n")).mkString("\n", "", "") - else xs.mkString("(", ", ", ")") - - pp(x match { - case s: String => return clean(s) - case x: Tree => asCompactString(x) - case xs: List[_] => pplist(xs map pp) - case x: Tuple2[_,_] => "%s -> %s".format(pp(x._1), pp(x._2)) - case x => x.toString - }) - } - - @elidable(elidable.FINE) def TRACE(f: String, xs: Any*): Unit = { - if (trace) { - val msg = if (xs.isEmpty) f else f.format(xs map pp: _*) - println(msg) - } - } - @elidable(elidable.FINE) def traceCategory(cat: String, f: String, xs: Any*) = { - if (trace) - TRACE("[" + """%10s""".format(cat) + "] " + f, xs: _*) - } - def tracing[T](s: String)(x: T): T = { - if (trace) - println(("[" + """%10s""".format(s) + "] %s") format pp(x)) - - x - } - private[nsc] def printing[T](fmt: String, xs: Any*)(x: T): T = { - println(fmt.format(xs: _*) + " == " + x) - x - } - private[nsc] def debugging[T](fmt: String, xs: Any*)(x: T): T = { - if (settings.debug.value) printing(fmt, xs: _*)(x) - else x - } - - def indent(s: Any) = s.toString() split "\n" map (" " + _) mkString "\n" - def indentAll(s: Seq[Any]) = s map (" " + _.toString() + "\n") mkString - } - - /** Drops the 'i'th element of a list. - */ - def dropIndex[T](xs: List[T], n: Int) = { - val (l1, l2) = xs splitAt n - l1 ::: (l2 drop 1) - } - - /** Extract the nth element of a list and return it and the remainder. - */ - def extractIndex[T](xs: List[T], n: Int): (T, List[T]) = - (xs(n), dropIndex(xs, n)) -} diff --git a/src/compiler/scala/tools/nsc/matching/Matrix.scala b/src/compiler/scala/tools/nsc/matching/Matrix.scala deleted file mode 100644 index daefe4c545..0000000000 --- a/src/compiler/scala/tools/nsc/matching/Matrix.scala +++ /dev/null @@ -1,259 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * Author: Paul Phillips - */ - -package scala.tools.nsc -package matching - -import transform.ExplicitOuter -import symtab.Flags -import scala.collection.mutable -import scala.language.implicitConversions - -trait Matrix extends MatrixAdditions { - self: ExplicitOuter with ParallelMatching => - - import global.{ typer => _, _ } - import analyzer.Typer - import CODE._ - import Debug._ - import Flags.{ SYNTHETIC, MUTABLE } - - private[matching] val NO_EXHAUSTIVE = Flags.TRANS_FLAG - - /** Translation of match expressions. - * - * `p`: pattern - * `g`: guard - * `bx`: body index - * - * internal representation is (tvars:List[Symbol], rows:List[Row]) - * - * tmp1 tmp_n - * Row( p_11 ... p_1n g_1 b_1 ) + subst - * - * Row( p_m1 ... p_mn g_m b_m ) + subst - * - * Implementation based on the algorithm described in - * - * "A Term Pattern-Match Compiler Inspired by Finite Automata Theory" - * Mikael Pettersson - * ftp://ftp.ida.liu.se/pub/labs/pelab/papers/cc92pmc.ps.gz - * - * @author Burak Emir - */ - - /** "The Mixture Rule" - - {v=pat1, pats1 .. } {q1} - match {.. } {..} - {v=patn, patsn .. } {qn} - - The is the real work-horse of the algorithm. There is some column whose top-most pattern is a - constructor. (Forsimplicity, itisdepicted above asthe left-most column, but anycolumn will do.) - The goal is to build a test state with the variablevand some outgoing arcs (one for each construc- - tor and possibly a default arc). Foreach constructor in the selected column, its arc is defined as - follows: - - Let {i1,...,ij} be the rows-indices of the patterns in the column that match c. Since the pat- - terns are viewed as regular expressions, this will be the indices of the patterns that either - have the same constructor c, or are wildcards. - - Let {pat1,...,patj} be the patterns in the column corresponding to the indices computed - above, and let nbe the arity of the constructor c, i.e. the number of sub-patterns it has. For - eachpati, its n sub-patterns are extracted; if pat i is a wildcard, nwildcards are produced - instead, each tagged with the right path variable. This results in a pattern matrix with n - columns and j rows. This matrix is then appended to the result of selecting, from each col- - umn in the rest of the original matrix, those rows whose indices are in {i1,...,ij}. Finally - the indices are used to select the corresponding final states that go with these rows. Note - that the order of the indices is significant; selected rows do not change their relative orders. - The arc for the constructor c is now defined as (c’,state), where c’ is cwith any - immediate sub-patterns replaced by their path variables (thus c’ is a simple pattern), and - state is the result of recursively applying match to the new matrix and the new sequence - of final states. - - Finally, the possibility for matching failure is considered. If the set of constructors is exhaustive, - then no more arcs are computed. Otherwise, a default arc(_,state)is the last arc. If there are - any wildcard patterns in the selected column, then their rows are selected from the rest of the - matrix and the final states, and the state is the result of applying match to the new matrix and - states. Otherwise,the error state is used after its reference count has been incremented. - **/ - - /** Handles all translation of pattern matching. - */ - def handlePattern( - selector: Tree, // tree being matched upon (called scrutinee after this) - cases: List[CaseDef], // list of cases in the match - isChecked: Boolean, // whether exhaustiveness checking is enabled (disabled with @unchecked) - context: MatrixContext): Tree = - { - import context._ - TRACE("handlePattern", "(%s: %s) match { %s cases }", selector, selector.tpe, cases.size) - - val matrixInit: MatrixInit = { - val v = copyVar(selector, isChecked, selector.tpe, "temp") - MatrixInit(List(v), cases, atPos(selector.pos)(MATCHERROR(v.ident))) - } - val matrix = new MatchMatrix(context) { lazy val data = matrixInit } - val mch = typer typed matrix.expansion.toTree - val dfatree = typer typed Block(matrix.data.valDefs, mch) - - // redundancy check - matrix.targets filter (_.unreached) foreach (cs => cunit.error(cs.body.pos, "unreachable code")) - // optimize performs squeezing and resets any remaining NO_EXHAUSTIVE - tracing("handlePattern")(matrix optimize dfatree) - } - - case class MatrixContext( - cunit: CompilationUnit, // current unit - handleOuter: Tree => Tree, // for outer pointer - typer: Typer, // a local typer - owner: Symbol, // the current owner - matchResultType: Type) // the expected result type of the whole match - extends Squeezer - { - private def ifNull[T](x: T, alt: T) = if (x == null) alt else x - - // NO_EXHAUSTIVE communicates there should be no exhaustiveness checking - private def flags(checked: Boolean) = if (checked) Nil else List(NO_EXHAUSTIVE) - - // Recording the symbols of the synthetics we create so we don't go clearing - // anyone else's mutable flags. - private val _syntheticSyms = mutable.HashSet[Symbol]() - def clearSyntheticSyms() = { - _syntheticSyms foreach (_ resetFlag (NO_EXHAUSTIVE|MUTABLE)) - debuglog("Cleared NO_EXHAUSTIVE/MUTABLE on " + _syntheticSyms.size + " synthetic symbols.") - _syntheticSyms.clear() - } - def recordSyntheticSym(sym: Symbol): Symbol = { - _syntheticSyms += sym - if (_syntheticSyms.size > 25000) { - cunit.error(owner.pos, "Sanity check failed: over 25000 symbols created for pattern match.") - abort("This is a bug in the pattern matcher.") - } - sym - } - - case class MatrixInit( - roots: List[PatternVar], - cases: List[CaseDef], - default: Tree - ) { - def tvars = roots map (_.lhs) - def valDefs = roots map (_.valDef) - override def toString() = "MatrixInit(roots = %s, %d cases)".format(pp(roots), cases.size) - } - - implicit def pvlist2pvgroup(xs: List[PatternVar]): PatternVarGroup = - PatternVarGroup(xs) - - object PatternVarGroup { - def apply(xs: PatternVar*) = new PatternVarGroup(xs.toList) - def apply(xs: List[PatternVar]) = new PatternVarGroup(xs) - - // XXX - transitional - def fromBindings(vlist: List[Binding], freeVars: List[Symbol] = Nil) = { - def vmap(v: Symbol): Option[Binding] = vlist find (_.pvar eq v) - val info = - if (freeVars.isEmpty) vlist - else (freeVars map vmap).flatten - - val xs = - for (Binding(lhs, rhs) <- info) yield - new PatternVar(lhs, Ident(rhs) setType lhs.tpe, !(rhs hasFlag NO_EXHAUSTIVE)) - - new PatternVarGroup(xs) - } - } - - val emptyPatternVarGroup = PatternVarGroup() - class PatternVarGroup(val pvs: List[PatternVar]) { - def syms = pvs map (_.sym) - def valDefs = pvs map (_.valDef) - def idents = pvs map (_.ident) - - def extractIndex(index: Int): (PatternVar, PatternVarGroup) = { - val (t, ts) = self.extractIndex(pvs, index) - (t, PatternVarGroup(ts)) - } - - def isEmpty = pvs.isEmpty - def size = pvs.size - def head = pvs.head - def ::(t: PatternVar) = PatternVarGroup(t :: pvs) - def :::(ts: List[PatternVar]) = PatternVarGroup(ts ::: pvs) - def ++(other: PatternVarGroup) = PatternVarGroup(pvs ::: other.pvs) - - def apply(i: Int) = pvs(i) - def zipWithIndex = pvs.zipWithIndex - def indices = pvs.indices - def map[T](f: PatternVar => T) = pvs map f - def filter(p: PatternVar => Boolean) = PatternVarGroup(pvs filter p) - - override def toString() = pp(pvs) - } - - /** Every temporary variable allocated is put in a PatternVar. - */ - class PatternVar(val lhs: Symbol, val rhs: Tree, val checked: Boolean) { - def sym = lhs - def tpe = lhs.tpe - if (checked) - lhs resetFlag NO_EXHAUSTIVE - else - lhs setFlag NO_EXHAUSTIVE - - // See #1427 for an example of a crash which occurs unless we retype: - // in that instance there is an existential in the pattern. - lazy val ident = typer typed Ident(lhs) - lazy val valDef = typer typedValDef ValDef(lhs, rhs) - - override def toString() = "%s: %s = %s".format(lhs, tpe, rhs) - } - - /** Given a tree, creates a new synthetic variable of the same type - * and assigns the tree to it. - */ - def copyVar( - root: Tree, - checked: Boolean, - _tpe: Type = null, - label: String = "temp"): PatternVar = - { - val tpe = ifNull(_tpe, root.tpe) - val name = cunit.freshTermName(label) - val sym = newVar(root.pos, tpe, flags(checked), name) - - tracing("copy")(new PatternVar(sym, root, checked)) - } - - /** Creates a new synthetic variable of the specified type and - * assigns the result of f(symbol) to it. - */ - def createVar(tpe: Type, f: Symbol => Tree, checked: Boolean) = { - val lhs = newVar(owner.pos, tpe, flags(checked)) - val rhs = f(lhs) - - tracing("create")(new PatternVar(lhs, rhs, checked)) - } - def createLazy(tpe: Type, f: Symbol => Tree, checked: Boolean) = { - val lhs = newVar(owner.pos, tpe, Flags.LAZY :: flags(checked)) - val rhs = f(lhs) - - tracing("createLazy")(new PatternVar(lhs, rhs, checked)) - } - - private def newVar( - pos: Position, - tpe: Type, - flags: List[Long] = Nil, - name: TermName = null): Symbol = - { - val n = if (name == null) cunit.freshTermName("temp") else name - // careful: pos has special meaning - val flagsLong = (SYNTHETIC.toLong /: flags)(_|_) - recordSyntheticSym(owner.newVariable(n, pos, flagsLong) setInfo tpe) - } - } -} diff --git a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala deleted file mode 100644 index 7220253003..0000000000 --- a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala +++ /dev/null @@ -1,193 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * Author: Paul Phillips - */ - -package scala.tools.nsc -package matching - -import transform.ExplicitOuter -import PartialFunction._ - -/** Traits which are mixed into MatchMatrix, but separated out as - * (somewhat) independent components to keep them on the sidelines. - */ -trait MatrixAdditions extends ast.TreeDSL { - self: ExplicitOuter with ParallelMatching => - - import global.{ typer => _, _ } - import symtab.Flags - import CODE._ - import Debug._ - import treeInfo._ - import definitions.{ isPrimitiveValueClass } - - /** The Squeezer, responsible for all the squeezing. - */ - private[matching] trait Squeezer { - self: MatrixContext => - - private val settings_squeeze = !settings.Ynosqueeze.value - - class RefTraverser(vd: ValDef) extends Traverser { - private val targetSymbol = vd.symbol - private var safeRefs = 0 - private var isSafe = true - - def canDrop = isSafe && safeRefs == 0 - def canInline = isSafe && safeRefs == 1 - - override def traverse(tree: Tree): Unit = tree match { - case t: Ident if t.symbol eq targetSymbol => - // target symbol's owner should match currentOwner - if (targetSymbol.owner == currentOwner) safeRefs += 1 - else isSafe = false - - case LabelDef(_, params, rhs) => - if (params exists (_.symbol eq targetSymbol)) // cannot substitute this one - isSafe = false - - traverse(rhs) - case _ if safeRefs > 1 => () - case _ => - super.traverse(tree) - } - } - - /** Compresses multiple Blocks. */ - private def combineBlocks(stats: List[Tree], expr: Tree): Tree = expr match { - case Block(stats1, expr1) if stats.isEmpty => combineBlocks(stats1, expr1) - case _ => Block(stats, expr) - } - def squeezedBlock(vds: List[Tree], exp: Tree): Tree = - if (settings_squeeze) combineBlocks(Nil, squeezedBlock1(vds, exp)) - else combineBlocks(vds, exp) - - private def squeezedBlock1(vds: List[Tree], exp: Tree): Tree = { - lazy val squeezedTail = squeezedBlock(vds.tail, exp) - def default = squeezedTail match { - case Block(vds2, exp2) => Block(vds.head :: vds2, exp2) - case exp2 => Block(vds.head :: Nil, exp2) - } - - if (vds.isEmpty) exp - else vds.head match { - case vd: ValDef => - val rt = new RefTraverser(vd) - rt.atOwner(owner)(rt traverse squeezedTail) - - if (rt.canDrop) - squeezedTail - else if (isConstantType(vd.symbol.tpe) || rt.canInline) - new TreeSubstituter(List(vd.symbol), List(vd.rhs)) transform squeezedTail - else - default - case _ => default - } - } - } - - /** The Optimizer, responsible for some of the optimizing. - */ - private[matching] trait MatchMatrixOptimizer { - self: MatchMatrix => - - import self.context._ - - final def optimize(tree: Tree): Tree = { - // Uses treeInfo extractors rather than looking at trees directly - // because the many Blocks obscure our vision. - object lxtt extends Transformer { - override def transform(tree: Tree): Tree = tree match { - case Block(stats, ld @ LabelDef(_, _, body)) if targets exists (_ shouldInline ld.symbol) => - squeezedBlock(transformStats(stats, currentOwner), body) - case IsIf(cond, IsTrue(), IsFalse()) => - transform(cond) - case IsIf(cond1, IsIf(cond2, thenp, elsep1), elsep2) if elsep1 equalsStructure elsep2 => - transform(typer typed If(gen.mkAnd(cond1, cond2), thenp, elsep2)) - case If(cond1, IsIf(cond2, thenp, Apply(jmp, Nil)), ld: LabelDef) if jmp.symbol eq ld.symbol => - transform(typer typed If(gen.mkAnd(cond1, cond2), thenp, ld)) - case _ => - super.transform(tree) - } - } - try lxtt transform tree - finally clearSyntheticSyms() - } - } - - /** The Exhauster. - */ - private[matching] trait MatrixExhaustiveness { - self: MatchMatrix => - - import self.context._ - - /** Exhaustiveness checking requires looking for sealed classes - * and if found, making sure all children are covered by a pattern. - */ - class ExhaustivenessChecker(rep: Rep, matchPos: Position) { - val Rep(tvars, rows) = rep - - import Flags.{ MUTABLE, ABSTRACT, SEALED } - - private case class Combo(index: Int, sym: Symbol) { } - - /* True if the patterns in 'row' cover the given type symbol combination, and has no guard. */ - private def rowCoversCombo(row: Row, combos: List[Combo]) = - row.guard.isEmpty && combos.forall(c => row.pats(c.index) covers c.sym) - - private def requiresExhaustive(sym: Symbol) = { - (sym.isMutable) && // indicates that have not yet checked exhaustivity - !(sym hasFlag NO_EXHAUSTIVE) && // indicates @unchecked - (sym.tpe.typeSymbol.isSealed) && - !isPrimitiveValueClass(sym.tpe.typeSymbol) // make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte - } - - private lazy val inexhaustives: List[List[Combo]] = { - // let's please not get too clever side-effecting the mutable flag. - val toCollect = tvars.zipWithIndex filter { case (pv, i) => requiresExhaustive(pv.sym) } - val collected = toCollect map { case (pv, i) => - // okay, now reset the flag - pv.sym resetFlag MUTABLE - - i -> ( - pv.tpe.typeSymbol.sealedDescendants.toList sortBy (_.sealedSortName) - // symbols which are both sealed and abstract need not be covered themselves, because - // all of their children must be and they cannot otherwise be created. - filterNot (x => x.isSealed && x.isAbstractClass && !isPrimitiveValueClass(x)) - // have to filter out children which cannot match: see ticket #3683 for an example - filter (_.tpe matchesPattern pv.tpe) - ) - } - - val folded = - collected.foldRight(List[List[Combo]]())((c, xs) => { - val (i, syms) = c match { case (i, set) => (i, set.toList) } - xs match { - case Nil => syms map (s => List(Combo(i, s))) - case _ => for (s <- syms ; rest <- xs) yield Combo(i, s) :: rest - } - }) - - folded filterNot (combo => rows exists (r => rowCoversCombo(r, combo))) - } - - private def mkPad(xs: List[Combo], i: Int): String = xs match { - case Nil => pad("*") - case Combo(j, sym) :: rest => if (j == i) pad(sym.name.toString) else mkPad(rest, i) - } - private def mkMissingStr(open: List[Combo]) = - "missing combination %s\n" format tvars.indices.map(mkPad(open, _)).mkString - - /** The only public method. */ - def check = { - def errMsg = (inexhaustives map mkMissingStr).mkString - if (inexhaustives.nonEmpty) - cunit.warning(matchPos, "match is not exhaustive!\n" + errMsg) - - rep - } - } - } -}
\ 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 deleted file mode 100644 index dbb9b7a003..0000000000 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ /dev/null @@ -1,870 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * Copyright 2007 Google Inc. All Rights Reserved. - * Author: bqe@google.com (Burak Emir) - */ - -package scala.tools.nsc -package matching - -import PartialFunction._ -import scala.collection.{ mutable } -import scala.reflect.internal.util.Position -import transform.ExplicitOuter -import symtab.Flags -import mutable.ListBuffer -import scala.annotation.elidable -import scala.language.postfixOps -import scala.tools.nsc.settings.ScalaVersion - -trait ParallelMatching extends ast.TreeDSL - with MatchSupport - with Matrix - with Patterns - with PatternBindings -{ - self: ExplicitOuter => - - import global.{ typer => _, _ } - import definitions.{ - AnyRefClass, IntClass, BooleanClass, SomeClass, OptionClass, - getProductArgs, productProj, Object_eq, Any_asInstanceOf - } - import CODE._ - import Types._ - import Debug._ - - /** Transition **/ - def toPats(xs: List[Tree]): List[Pattern] = xs map Pattern.apply - - /** The umbrella matrix class. **/ - abstract class MatchMatrix(val context: MatrixContext) extends MatchMatrixOptimizer with MatrixExhaustiveness { - import context._ - - def data: MatrixContext#MatrixInit - - lazy val MatrixInit(roots, cases, failTree) = data - lazy val (rows, targets) = expand(roots, cases).unzip - lazy val expansion: Rep = make(roots, rows) - - private val shortCuts = perRunCaches.newMap[Int, Symbol]() - - final def createShortCut(theLabel: Symbol): Int = { - val key = shortCuts.size + 1 - shortCuts(key) = theLabel - -key - } - def createLabelDef(namePrefix: String, body: Tree, params: List[Symbol] = Nil, restpe: Type = matchResultType) = { - val labelName = cunit.freshTermName(namePrefix) - val labelSym = owner.newLabel(labelName, owner.pos) - val labelInfo = MethodType(params, restpe) - - LabelDef(labelSym setInfo labelInfo, params, body setType restpe) - } - - /** This is the recursively focal point for translating the current - * list of pattern variables and a list of pattern match rows into - * a tree suitable for entering erasure. - * - * The first time it is called, the variables are (copies of) the - * original pattern matcher roots, and the rows correspond to the - * original casedefs. - */ - final def make(roots1: PatternVarGroup, rows1: List[Row]): Rep = { - traceCategory("New Match", "%sx%s (%s)", roots1.size, rows1.size, roots1.syms.mkString(", ")) - def classifyPat(opat: Pattern, j: Int): Pattern = opat simplify roots1(j) - - val newRows = rows1 flatMap (_ expandAlternatives classifyPat) - if (rows1.length != newRows.length) make(roots1, newRows) // recursive call if any change - else { - val rep = Rep(roots1, newRows) - new ExhaustivenessChecker(rep, roots.head.sym.pos).check - rep - } - } - - override def toString() = "MatchMatrix(%s) { %s }".format(matchResultType, indentAll(targets)) - - /** - * Encapsulates a symbol being matched on. It is created from a - * PatternVar, which encapsulates the symbol's creation and assignment. - * - * We never match on trees directly - a temporary variable is created - * (in a PatternVar) for any expression being matched on. - */ - class Scrutinee(val pv: PatternVar) { - import definitions._ - - // presenting a face of our symbol - def sym = pv.sym - def tpe = sym.tpe - def pos = sym.pos - def id = ID(sym) setPos pos // attributed ident - - def accessors = if (isCaseClass) sym.caseFieldAccessors else Nil - def accessorTypes = accessors map (x => (tpe memberType x).resultType) - - lazy val accessorPatternVars = PatternVarGroup( - for ((accessor, tpe) <- accessors zip accessorTypes) yield - createVar(tpe, _ => fn(id, accessor)) - ) - - private def extraValDefs = if (pv.rhs.isEmpty) Nil else List(pv.valDef) - def allValDefs = extraValDefs ::: accessorPatternVars.valDefs - - // tests - def isDefined = sym ne NoSymbol - def isSubrangeType = subrangeTypes(tpe.typeSymbol) - def isCaseClass = tpe.typeSymbol.isCase - - // sequences - def seqType = tpe.widen baseType SeqClass - def elemType = tpe typeArgs 0 - - private def elemAt(i: Int) = (id DOT (tpe member nme.apply))(LIT(i)) - private def createElemVar(i: Int) = createVar(elemType, _ => elemAt(i)) - private def createSeqVar(drop: Int) = createVar(seqType, _ => id DROP drop) - - def createSequenceVars(count: Int): List[PatternVar] = - (0 to count).toList map (i => if (i < count) createElemVar(i) else createSeqVar(i)) - - // for propagating "unchecked" to synthetic vars - def isChecked = !(sym hasFlag NO_EXHAUSTIVE) - def flags: List[Long] = List(NO_EXHAUSTIVE) filter (sym hasFlag _) - - // this is probably where this actually belongs - def createVar(tpe: Type, f: Symbol => Tree) = context.createVar(tpe, f, isChecked) - - def castedTo(headType: Type) = - if (tpe =:= headType) this - else new Scrutinee(createVar(headType, lhs => gen.mkAsInstanceOf(id, lhs.tpe))) - - override def toString() = "(%s: %s)".format(id, tpe) - } - - def isPatternSwitch(scrut: Scrutinee, ps: List[Pattern]): Option[PatternSwitch] = { - def isSwitchableConst(x: Pattern) = cond(x) { case x: LiteralPattern if x.isSwitchable => true } - def isSwitchableDefault(x: Pattern) = isSwitchableConst(x) || x.isDefault - - // TODO - scala> (5: Any) match { case 5 => 5 ; case 6 => 7 } - // ... should compile to a switch. It doesn't because the scrut isn't Int/Char, but - // that could be handle in an if/else since every pattern requires an Int. - // More immediately, Byte and Short scruts should also work. - if (!scrut.isSubrangeType) None - else { - val (_lits, others) = ps span isSwitchableConst - val lits = _lits collect { case x: LiteralPattern => x } - - condOpt(others) { - case Nil => new PatternSwitch(scrut, lits, None) - // TODO: This needs to also allow the case that the last is a compatible type pattern. - case List(x) if isSwitchableDefault(x) => new PatternSwitch(scrut, lits, Some(x)) - } - } - } - - class PatternSwitch( - scrut: Scrutinee, - override val ps: List[LiteralPattern], - val defaultPattern: Option[Pattern] - ) extends PatternMatch(scrut, ps) { - require(scrut.isSubrangeType && (ps forall (_.isSwitchable))) - } - - case class PatternMatch(scrut: Scrutinee, ps: List[Pattern]) { - def head = ps.head - def tail = ps.tail - def size = ps.length - - def headType = head.necessaryType - private val dummyCount = if (head.isCaseClass) headType.typeSymbol.caseFieldAccessors.length else 0 - def dummies = emptyPatterns(dummyCount) - - def apply(i: Int): Pattern = ps(i) - def pzip() = ps.zipWithIndex - def pzip[T](others: List[T]) = { - assert(ps.size == others.size, "Internal error: ps = %s, others = %s".format(ps, others)) - ps zip others - } - - // Any unapply - returns Some(true) if a type test is needed before the unapply can - // be called (e.g. def unapply(x: Foo) = { ... } but our scrutinee is type Any.) - object AnyUnapply { - def unapply(x: Pattern): Option[Boolean] = condOpt(x.tree) { - case UnapplyParamType(tpe) => !(scrut.tpe <:< tpe) - } - } - - def mkRule(rest: Rep): RuleApplication = { - tracing("Rule")(head match { - case x if isEquals(x.tree.tpe) => new MixEquals(this, rest) - case x: SequencePattern => new MixSequence(this, rest, x) - case AnyUnapply(false) => new MixUnapply(this, rest) - case _ => - isPatternSwitch(scrut, ps) match { - case Some(x) => new MixLiteralInts(x, rest) - case _ => new MixTypes(this, rest) - } - }) - } - override def toString() = "%s match {%s}".format(scrut, indentAll(ps)) - } // PatternMatch - - /***** Rule Applications *****/ - - sealed abstract class RuleApplication { - def pmatch: PatternMatch - def rest: Rep - def cond: Tree - def success: Tree - def failure: Tree - - lazy val PatternMatch(scrut, patterns) = pmatch - lazy val head = pmatch.head - lazy val codegen: Tree = IF (cond) THEN (success) ELSE (failure) - - def mkFail(xs: List[Row]): Tree = - if (xs.isEmpty) failTree - else remake(xs).toTree - - def remake( - rows: List[Row], - pvgroup: PatternVarGroup = emptyPatternVarGroup, - includeScrut: Boolean = true): Rep = - { - val scrutpvs = if (includeScrut) List(scrut.pv) else Nil - make(pvgroup.pvs ::: scrutpvs ::: rest.tvars, rows) - } - - /** translate outcome of the rule application into code (possible involving recursive application of rewriting) */ - def tree(): Tree - - override def toString = - "Rule/%s (%s =^= %s)".format(getClass.getSimpleName, scrut, head) - } - - /** {case ... if guard => bx} else {guardedRest} */ - /** VariableRule: The top-most rows has only variable (non-constructor) patterns. */ - case class VariableRule(subst: Bindings, guard: Tree, guardedRest: Rep, bx: Int) extends RuleApplication { - def pmatch: PatternMatch = impossible - def rest: Rep = guardedRest - - private lazy val (valDefs, successTree) = targets(bx) applyBindings subst.toMap - lazy val cond = guard - lazy val success = successTree - lazy val failure = guardedRest.toTree - - final def tree(): Tree = - if (bx < 0) REF(shortCuts(-bx)) - else squeezedBlock( - valDefs, - if (cond.isEmpty) success else codegen - ) - - override def toString = "(case %d) {\n Bindings: %s\n\n if (%s) { %s }\n else { %s }\n}".format( - bx, subst, guard, success, guardedRest - ) - } - - class MixLiteralInts(val pmatch: PatternSwitch, val rest: Rep) extends RuleApplication { - val literals = pmatch.ps - val defaultPattern = pmatch.defaultPattern - - private lazy val casted: Tree = - if (!scrut.tpe.isInt) scrut.id DOT nme.toInt else scrut.id - - // creates a row transformer for injecting the default case bindings at a given index - private def addDefaultVars(index: Int): Row => Row = - if (defaultVars.isEmpty) identity - else rebindAll(_, pmatch(index).boundVariables, scrut.sym) - - // add bindings for all the given vs to the given tvar - private def rebindAll(r: Row, vs: Iterable[Symbol], tvar: Symbol) = - r rebind r.subst.add(vs, tvar) - - private def bindVars(Tag: Int, orig: Bindings): Bindings = { - def myBindVars(rest: List[(Int, List[Symbol])], bnd: Bindings): Bindings = rest match { - case Nil => bnd - case (Tag,vs)::xs => myBindVars(xs, bnd.add(vs, scrut.sym)) - case (_, vs)::xs => myBindVars(xs, bnd) - } - myBindVars(varMap, orig) - } - - // bound vars and rows for default pattern (only one row, but a list is easier to use later) - lazy val (defaultVars, defaultRows) = defaultPattern match { - case None => (Nil, Nil) - case Some(p) => (p.boundVariables, List(rebindAll(rest rows literals.size, p.boundVariables, scrut.sym))) - } - - // literalMap is a map from each literal to a list of row indices. - // varMap is a list from each literal to a list of the defined vars. - lazy val (litPairs, varMap) = ( - literals.zipWithIndex map { - case (lit, index) => - val tag = lit.intValue - (tag -> index, tag -> lit.boundVariables) - } unzip - ) - def literalMap = litPairs groupBy (_._1) map { - case (k, vs) => (k, vs map (_._2)) - } - - lazy val cases = - for ((tag, indices) <- literalMap.toList.sortBy(_._1)) yield { - val newRows = indices map (i => addDefaultVars(i)(rest rows i)) - val r = remake(newRows ++ defaultRows, includeScrut = false) - val r2 = make(r.tvars, r.rows map (x => x rebind bindVars(tag, x.subst))) - - CASE(Literal(Constant(tag))) ==> r2.toTree - } - - lazy val defaultTree = remake(defaultRows, includeScrut = false).toTree - def defaultCase = CASE(WILD(IntClass.tpe)) ==> defaultTree - - // cond/success/failure only used if there is exactly one case. - lazy val cond = scrut.id MEMBER_== cases.head.pat - lazy val success = cases.head.body - lazy val failure = defaultTree - - // only one case becomes if/else, otherwise match - def tree() = - if (cases.size == 1) codegen - else casted MATCH (cases :+ defaultCase: _*) - } - - /** mixture rule for unapply pattern - */ - class MixUnapply(val pmatch: PatternMatch, val rest: Rep) extends RuleApplication { - val Pattern(UnApply(unMethod, unArgs)) = head - val Apply(unTarget, _ :: trailing) = unMethod - - object SameUnapplyCall { - def isSame(t: Tree) = isEquivalentTree(unTarget, t) - def unapply(x: Pattern) = /*tracing("SameUnapplyCall (%s vs. %s)".format(unTarget, x))*/(x match { - case Pattern(UnApply(Apply(fn, _), args)) if isSame(fn) => Some(args) - case _ => None - }) - } - object SameUnapplyPattern { - def isSame(t: Tree) = isEquivalentTree(unMethod, t) - def apply(x: Pattern) = unapply(x).isDefined - def unapply(x: Pattern) = /*tracing("SameUnapplyPattern (%s vs. %s)".format(unMethod, x))*/(x match { - case Pattern(UnApply(t, _)) if isSame(t) => Some(unArgs) - case _ => None - }) - } - - private lazy val zipped = pmatch pzip rest.rows - - lazy val unapplyResult: PatternVar = - scrut.createVar(unMethod.tpe, Apply(unTarget, scrut.id :: trailing) setType _.tpe) - - lazy val cond: Tree = unapplyResult.tpe.normalize match { - case TypeRef(_, BooleanClass, _) => unapplyResult.ident - case TypeRef(_, SomeClass, _) => TRUE - case _ => NOT(unapplyResult.ident DOT nme.isEmpty) - } - - lazy val failure = - mkFail(zipped.tail filterNot (x => SameUnapplyPattern(x._1)) map { case (pat, r) => r insert pat }) - - private def doSuccess: (List[PatternVar], List[PatternVar], List[Row]) = { - // pattern variable for the unapply result of Some(x).get - def unMethodTypeArg = unMethod.tpe.baseType(OptionClass).typeArgs match { - case Nil => log("No type argument for unapply result! " + unMethod.tpe) ; NoType - case arg :: _ => arg - } - lazy val pv = scrut.createVar(unMethodTypeArg, _ => fn(ID(unapplyResult.lhs), nme.get)) - def tuple = pv.lhs - - // at this point it's Some[T1,T2...] - lazy val tpes = getProductArgs(tuple.tpe) - - // one pattern variable per tuple element - lazy val tuplePVs = - for ((tpe, i) <- tpes.zipWithIndex) yield - scrut.createVar(tpe, _ => fn(ID(tuple), productProj(tuple, i + 1))) - - // the filter prevents infinite unapply recursion - def mkNewRows(sameFilter: (List[Tree]) => List[Tree]) = { - val dum = if (unArgs.length <= 1) unArgs.length else tpes.size - for ((pat, r) <- zipped) yield pat match { - case SameUnapplyCall(xs) => r.insert2(toPats(sameFilter(xs)) :+ NoPattern, pat.boundVariables, scrut.sym) - case _ => r insert (emptyPatterns(dum) :+ pat) - } - } - - // 0 is Boolean, 1 is Option[T], 2+ is Option[(T1,T2,...)] - unArgs.length match { - case 0 => (Nil, Nil, mkNewRows((xs) => Nil)) - case 1 => (List(pv), List(pv), mkNewRows(xs => List(xs.head))) - case _ => (pv :: tuplePVs, tuplePVs, mkNewRows(identity)) - } - } - - lazy val success = { - val (squeezePVs, pvs, rows) = doSuccess - val srep = remake(rows, pvs).toTree - - squeezedBlock(squeezePVs map (_.valDef), srep) - } - - final def tree() = - squeezedBlock(List(handleOuter(unapplyResult.valDef)), codegen) - } - - /** Handle Sequence patterns (including Star patterns.) - * Note: pivot == head, just better typed. - */ - sealed class MixSequence(val pmatch: PatternMatch, val rest: Rep, pivot: SequencePattern) extends RuleApplication { - require(scrut.tpe <:< head.tpe) - - def hasStar = pivot.hasStar - private def pivotLen = pivot.nonStarLength - private def seqDummies = emptyPatterns(pivot.elems.length + 1) - - // Should the given pattern join the expanded pivot in the success matrix? If so, - // this partial function will be defined for the pattern, and the result of the apply - // is the expanded sequence of new patterns. - lazy val successMatrixFn = new PartialFunction[Pattern, List[Pattern]] { - private def seqIsDefinedAt(x: SequenceLikePattern) = (hasStar, x.hasStar) match { - case (true, true) => true - case (true, false) => pivotLen <= x.nonStarLength - case (false, true) => pivotLen >= x.nonStarLength - case (false, false) => pivotLen == x.nonStarLength - } - - def isDefinedAt(pat: Pattern) = pat match { - case x: SequenceLikePattern => seqIsDefinedAt(x) - case WildcardPattern() => true - case _ => false - } - - def apply(pat: Pattern): List[Pattern] = pat match { - case x: SequenceLikePattern => - def isSameLength = pivotLen == x.nonStarLength - def rebound = x.nonStarPatterns :+ (x.elemPatterns.last rebindTo WILD(scrut.seqType)) - - (pivot.hasStar, x.hasStar, isSameLength) match { - case (true, true, true) => rebound :+ NoPattern - case (true, true, false) => (seqDummies drop 1) :+ x - case (true, false, true) => x.elemPatterns ++ List(NilPattern, NoPattern) - case (false, true, true) => rebound - case (false, false, true) => x.elemPatterns :+ NoPattern - case _ => seqDummies - } - - case _ => seqDummies - } - } - - // Should the given pattern be in the fail matrix? This is true of any sequences - // as long as the result of the length test on the pivot doesn't make it impossible: - // for instance if neither sequence is right ignoring and they are of different - // lengths, the later one cannot match since its length must be wrong. - def failureMatrixFn(c: Pattern) = (pivot ne c) && (c match { - case x: SequenceLikePattern => - (hasStar, x.hasStar) match { - case (_, true) => true - case (true, false) => pivotLen > x.nonStarLength - case (false, false) => pivotLen != x.nonStarLength - } - case WildcardPattern() => true - case _ => false - }) - - // divide the remaining rows into success/failure branches, expanding subsequences of patterns - val successRows = pmatch pzip rest.rows collect { - case (c, row) if successMatrixFn isDefinedAt c => row insert successMatrixFn(c) - } - val failRows = pmatch pzip rest.rows collect { - case (c, row) if failureMatrixFn(c) => row insert c - } - - // the discrimination test for sequences is a call to lengthCompare. Note that - // this logic must be fully consistent wiith successMatrixFn and failureMatrixFn above: - // any inconsistency will (and frequently has) manifested as pattern matcher crashes. - lazy val cond = { - // the method call symbol - val methodOp: Symbol = head.tpe member nme.lengthCompare - - // the comparison to perform. If the pivot is right ignoring, then a scrutinee sequence - // of >= pivot length could match it; otherwise it must be exactly equal. - val compareOp: (Tree, Tree) => Tree = if (hasStar) _ INT_>= _ else _ INT_== _ - - // scrutinee.lengthCompare(pivotLength) [== | >=] 0 - val compareFn: Tree => Tree = (t: Tree) => compareOp((t DOT methodOp)(LIT(pivotLen)), ZERO) - - // wrapping in a null check on the scrutinee - // XXX this needs to use the logic in "def condition" - nullSafe(compareFn, FALSE)(scrut.id) - // condition(head.tpe, scrut.id, head.boundVariables.nonEmpty) - } - lazy val success = { - // one pattern var per sequence element up to elemCount, and one more for the rest of the sequence - lazy val pvs = scrut createSequenceVars pivotLen - - squeezedBlock(pvs map (_.valDef), remake(successRows, pvs, hasStar).toTree) - } - lazy val failure = remake(failRows).toTree - - final def tree(): Tree = codegen - } - - class MixEquals(val pmatch: PatternMatch, val rest: Rep) extends RuleApplication { - private lazy val rhs = - decodedEqualsType(head.tpe) match { - case SingleType(pre, sym) => REF(pre, sym) - case PseudoType(o) => o - } - private lazy val labelDef = - createLabelDef("fail%", remake((rest.rows.tail, pmatch.tail).zipped map (_ insert _)).toTree) - - lazy val cond = handleOuter(rhs MEMBER_== scrut.id) - lazy val successOne = rest.rows.head.insert2(List(NoPattern), head.boundVariables, scrut.sym) - lazy val successTwo = Row(emptyPatterns(1 + rest.tvars.size), NoBinding, EmptyTree, createShortCut(labelDef.symbol)) - lazy val success = remake(List(successOne, successTwo)).toTree - lazy val failure = labelDef - - final def tree() = codegen - override def toString() = "MixEquals(%s == %s)".format(scrut, head) - } - - /** Mixture rule for type tests. - * moreSpecific: more specific patterns - * subsumed: more general patterns (subsuming current), rows index and subpatterns - * remaining: remaining, rows index and pattern - */ - class MixTypes(val pmatch: PatternMatch, val rest: Rep) extends RuleApplication { - case class Yes(bx: Int, moreSpecific: Pattern, subsumed: List[Pattern]) - case class No(bx: Int, remaining: Pattern) - - val (yeses, noes) = { - val _ys = new ListBuffer[Yes] - val _ns = new ListBuffer[No] - - for ((pattern, j) <- pmatch.pzip()) { - // scrutinee, head of pattern group - val (s, p) = (pattern.tpe, head.necessaryType) - - def isEquivalent = head.necessaryType =:= pattern.tpe - def isObjectTest = pattern.isObject && (p =:= pattern.necessaryType) - - def sMatchesP = matches(s, p) - def pMatchesS = matches(p, s) - - def ifEquiv(yes: Pattern): Pattern = if (isEquivalent) yes else pattern - - def passl(p: Pattern = NoPattern, ps: List[Pattern] = pmatch.dummies) = Some(Yes(j, p, ps)) - def passr() = Some( No(j, pattern)) - - def typed(pp: Tree) = passl(ifEquiv(Pattern(pp))) - def subs() = passl(ifEquiv(NoPattern), pattern subpatterns pmatch) - - val (oneY, oneN) = pattern match { - case Pattern(LIT(null)) if !(p =:= s) => (None, passr) // (1) - case x if isObjectTest => (passl(), None) // (2) - case Pattern(Typed(pp, _)) if sMatchesP => (typed(pp), None) // (4) - // The next line used to be this which "fixed" 1697 but introduced - // numerous regressions including #3136. - // case Pattern(_: UnApply, _) => (passl(), passr) - case Pattern(_: UnApply) => (None, passr) - case x if !x.isDefault && sMatchesP => (subs(), None) - case x if x.isDefault || pMatchesS => (passl(), passr) - case _ => (None, passr) - } - oneY map (_ys +=) - oneN map (_ns +=) - } - (_ys.toList, _ns.toList) - } - - val moreSpecific = yeses map (_.moreSpecific) - val subsumed = yeses map (x => (x.bx, x.subsumed)) - val remaining = noes map (x => (x.bx, x.remaining)) - - private def mkZipped = - for (Yes(j, moreSpecific, subsumed) <- yeses) yield - j -> (moreSpecific :: subsumed) - - lazy val casted = scrut castedTo pmatch.headType - lazy val cond = condition(casted.tpe, scrut, head.boundVariables.nonEmpty) - - private def isAnyMoreSpecific = yeses exists (x => !x.moreSpecific.isEmpty) - lazy val (subtests, subtestVars) = - if (isAnyMoreSpecific) (mkZipped, List(casted.pv)) - else (subsumed, Nil) - - lazy val newRows = - for ((j, ps) <- subtests) yield - (rest rows j).insert2(ps, pmatch(j).boundVariables, casted.sym) - - lazy val success = { - val srep = remake(newRows, subtestVars ::: casted.accessorPatternVars, includeScrut = false) - squeezedBlock(casted.allValDefs, srep.toTree) - } - - lazy val failure = - mkFail(remaining map { case (p1, p2) => rest rows p1 insert p2 }) - - final def tree(): Tree = codegen - } - - /*** States, Rows, Etc. ***/ - - case class Row(pats: List[Pattern], subst: Bindings, guard: Tree, bx: Int) { - private def nobindings = subst.get().isEmpty - private def bindstr = if (nobindings) "" else pp(subst) - - /** Extracts the 'i'th pattern. */ - def extractColumn(i: Int) = { - val (x, xs) = extractIndex(pats, i) - (x, copy(pats = xs)) - } - - /** Replaces the 'i'th pattern with the argument. */ - def replaceAt(i: Int, p: Pattern) = { - val newps = (pats take i) ::: p :: (pats drop (i + 1)) - copy(pats = newps) - } - - def insert(h: Pattern) = copy(pats = h :: pats) - def insert(hs: List[Pattern]) = copy(pats = hs ::: pats) // prepends supplied pattern - def rebind(b: Bindings) = copy(subst = b) // substitutes for bindings - - def insert2(hs: List[Pattern], vs: Iterable[Symbol], tvar: Symbol) = - tracing("insert2")(copy(pats = hs ::: pats, subst = subst.add(vs, tvar))) - - // returns this rows with alternatives expanded - def expandAlternatives(classifyPat: (Pattern, Int) => Pattern): List[Row] = { - def isNotAlternative(p: Pattern) = !cond(p.tree) { case _: Alternative => true } - - // classify all the top level patterns - alternatives come back unaltered - val newPats: List[Pattern] = pats.zipWithIndex map classifyPat.tupled - // see if any alternatives were in there - val (ps, others) = newPats span isNotAlternative - // make a new row for each alternative, with it spliced into the original position - if (others.isEmpty) List(copy(pats = ps)) - else extractBindings(others.head) map (x => replaceAt(ps.size, x)) - } - override def toString() = { - val bs = if (nobindings) "" else "\n" + bindstr - "Row(%d)(%s%s)".format(bx, pp(pats), bs) - } - } - abstract class State { - def bx: Int // index into the list of rows - def params: List[Symbol] // bound names to be supplied as arguments to labeldef - def body: Tree // body to execute upon match - def label: Option[LabelDef] // label definition for this state - - // Called with a bindings map when a match is achieved. - // Returns a list of variable declarations based on the labeldef parameters - // and the given substitution, and the body to execute. - protected def applyBindingsImpl(subst: Map[Symbol, Symbol]): (List[ValDef], Tree) - - final def applyBindings(subst: Map[Symbol, Symbol]): (List[ValDef], Tree) = { - _referenceCount += 1 - applyBindingsImpl(subst) - } - - private var _referenceCount = 0 - def referenceCount = _referenceCount - def unreached = referenceCount == 0 - def shouldInline(sym: Symbol) = referenceCount == 1 && label.exists(_.symbol == sym) - - // Creates a simple Ident if the symbol's type conforms to - // the val definition's type, or a casted Ident if not. - private def newValIdent(lhs: Symbol, rhs: Symbol) = - if (rhs.tpe <:< lhs.tpe) Ident(rhs) - else gen.mkTypeApply(Ident(rhs), Any_asInstanceOf, List(lhs.tpe)) - - protected def newValDefinition(lhs: Symbol, rhs: Symbol) = - typer typedValDef ValDef(lhs, newValIdent(lhs, rhs)) - - protected def newValReference(lhs: Symbol, rhs: Symbol) = - typer typed newValIdent(lhs, rhs) - - protected def valDefsFor(subst: Map[Symbol, Symbol]) = mapSubst(subst)(newValDefinition) - protected def identsFor(subst: Map[Symbol, Symbol]) = mapSubst(subst)(newValReference) - - protected def mapSubst[T](subst: Map[Symbol, Symbol])(f: (Symbol, Symbol) => T): List[T] = - params flatMap { lhs => - subst get lhs map (rhs => f(lhs, rhs)) orElse { - // This should not happen; the code should be structured so it is - // impossible, but that still lies ahead. - cunit.warning(lhs.pos, "No binding") - None - } - } - - // typer is not able to digest a body of type Nothing being assigned result type Unit - protected def caseResultType = - if (body.tpe.isNothing) body.tpe else matchResultType - } - - case class LiteralState(bx: Int, params: List[Symbol], body: Tree) extends State { - def label = None - - protected def applyBindingsImpl(subst: Map[Symbol, Symbol]) = - (valDefsFor(subst), body.duplicate setType caseResultType) - } - - case class FinalState(bx: Int, params: List[Symbol], body: Tree) extends State { - traceCategory("Final State", "(%s) => %s", paramsString, body) - def label = Some(labelDef) - - private lazy val labelDef = createLabelDef("body%" + bx, body, params, caseResultType) - - protected def applyBindingsImpl(subst: Map[Symbol, Symbol]) = { - val tree = - if (referenceCount > 1) ID(labelDef.symbol) APPLY identsFor(subst) - else labelDef - - (valDefsFor(subst), tree) - } - - private def paramsString = params map (s => s.name + ": " + s.tpe) mkString ", " - override def toString() = pp("(%s) => %s".format(pp(params), body)) - } - - case class Rep(val tvars: PatternVarGroup, val rows: List[Row]) { - lazy val Row(pats, subst, guard, index) = rows.head - lazy val guardedRest = if (guard.isEmpty) Rep(Nil, Nil) else make(tvars, rows.tail) - lazy val (defaults, others) = pats span (_.isDefault) - - /** Cut out the column containing the non-default pattern. */ - class Cut(index: Int) { - /** The first two separate out the 'i'th pattern in each row from the remainder. */ - private val (_column, _rows) = rows map (_ extractColumn index) unzip - - /** Now the 'i'th tvar is separated out and used as a new Scrutinee. */ - private val (_pv, _tvars) = tvars extractIndex index - - /** The non-default pattern (others.head) replaces the column head. */ - private val (_ncol, _nrep) = - (others.head :: _column.tail, make(_tvars, _rows)) - - def mix() = { - val newScrut = new Scrutinee(new PatternVar(_pv.sym, EmptyTree, _pv.checked)) - PatternMatch(newScrut, _ncol) mkRule _nrep - } - } - - /** Converts this to a tree - recursively acquires subreps. */ - final def toTree(): Tree = tracing("toTree")(typer typed applyRule()) - - /** The VariableRule. */ - private def variable() = { - val binding = (defaults map (_.boundVariables) zip tvars.pvs) . - foldLeft(subst)((b, pair) => b.add(pair._1, pair._2.lhs)) - - VariableRule(binding, guard, guardedRest, index) - } - /** The MixtureRule: picks a rewrite rule to apply. */ - private def mixture() = new Cut(defaults.size) mix() - - /** Applying the rule will result in one of: - * - * VariableRule - if all patterns are default patterns - * MixtureRule - if one or more patterns are not default patterns - * Error - no rows remaining - */ - final def applyRule(): Tree = - if (rows.isEmpty) failTree - else if (others.isEmpty) variable.tree() - else mixture.tree() - - def ppn(x: Any) = pp(x, newlines = true) - override def toString() = - if (tvars.isEmpty) "Rep(%d) = %s".format(rows.size, ppn(rows)) - else "Rep(%dx%d)%s%s".format(tvars.size, rows.size, ppn(tvars), ppn(rows)) - } - - /** Expands the patterns recursively. */ - final def expand(roots: List[PatternVar], cases: List[CaseDef]) = tracing("expand") { - for ((CaseDef(pat, guard, body), bx) <- cases.zipWithIndex) yield { - val subtrees = pat match { - case x if roots.length <= 1 => List(x) - case Apply(_, args) => args - case WILD() => emptyTrees(roots.length) - } - val params = pat filter (_.isInstanceOf[Bind]) map (_.symbol) distinct - val row = Row(toPats(subtrees), NoBinding, guard, bx) - val state = body match { - case x: Literal => LiteralState(bx, params, body) - case _ => FinalState(bx, params, body) - } - - row -> state - } - } - - /** returns the condition in "if (cond) k1 else k2" - */ - final def condition(tpe: Type, scrut: Scrutinee, isBound: Boolean): Tree = { - assert(scrut.isDefined) - val cond = handleOuter(condition(tpe, scrut.id, isBound)) - - if (!needsOuterTest(tpe, scrut.tpe, owner)) cond - else addOuterCondition(cond, tpe, scrut.id) - } - - final def condition(tpe: Type, scrutTree: Tree, isBound: Boolean): Tree = { - assert((tpe ne NoType) && (scrutTree.tpe ne NoType)) - def isMatchUnlessNull = scrutTree.tpe <:< tpe && tpe.isAnyRef - def isRef = scrutTree.tpe.isAnyRef - - // See ticket #1503 for the motivation behind checking for a binding. - // The upshot is that it is unsound to assume equality means the right - // type, but if the value doesn't appear on the right hand side of the - // match that's unimportant; so we add an instance check only if there - // is a binding. - def bindingWarning() = { - if (isBound && settings.Xmigration.value < ScalaVersion.twoDotEight) { - cunit.warning(scrutTree.pos, - "A bound pattern such as 'x @ Pattern' now matches fewer cases than the same pattern with no binding.") - } - } - - def genEquals(sym: Symbol): Tree = { - val t1: Tree = REF(sym) MEMBER_== scrutTree - - if (isBound) { - bindingWarning() - t1 AND (scrutTree IS tpe.widen) - } - else t1 - } - - typer typed { - tpe match { - case ConstantType(Constant(null)) if isRef => scrutTree OBJ_EQ NULL - case ConstantType(const) => scrutTree MEMBER_== Literal(const) - case SingleType(NoPrefix, sym) => genEquals(sym) - case SingleType(pre, sym) if sym.isStable => genEquals(sym) - case ThisType(sym) if sym.isModule => genEquals(sym) - case _ if isMatchUnlessNull => scrutTree OBJ_NE NULL - case _ => scrutTree IS tpe - } - } - } - - /** adds a test comparing the dynamic outer to the static outer */ - final def addOuterCondition(cond: Tree, tpe2test: Type, scrut: Tree) = { - val TypeRef(prefix, _, _) = tpe2test - val theRef = handleOuter(prefix match { - case NoPrefix => abort("assertion failed: NoPrefix") - case ThisType(clazz) => THIS(clazz) - case pre => REF(pre.prefix, pre.termSymbol) - }) - outerAccessor(tpe2test.typeSymbol) match { - case NoSymbol => ifDebug(cunit.warning(scrut.pos, "no outer acc for " + tpe2test.typeSymbol)) ; cond - case outerAcc => - val casted = gen.mkAsInstanceOf(scrut, tpe2test, any = true, wrapInApply = true) - cond AND ((casted DOT outerAcc)() OBJ_EQ theRef) - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/matching/PatternBindings.scala b/src/compiler/scala/tools/nsc/matching/PatternBindings.scala deleted file mode 100644 index 7b2fcf0e9b..0000000000 --- a/src/compiler/scala/tools/nsc/matching/PatternBindings.scala +++ /dev/null @@ -1,137 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * Author: Paul Phillips - */ - -package scala.tools.nsc -package matching - -import transform.ExplicitOuter -import PartialFunction._ -import scala.language.postfixOps - -trait PatternBindings extends ast.TreeDSL -{ - self: ExplicitOuter with ParallelMatching => - - import global.{ typer => _, _ } - import definitions.{ EqualsPatternClass } - import CODE._ - import Debug._ - - /** EqualsPattern **/ - def isEquals(tpe: Type) = tpe.typeSymbol == EqualsPatternClass - def mkEqualsRef(tpe: Type) = typeRef(NoPrefix, EqualsPatternClass, List(tpe)) - def decodedEqualsType(tpe: Type) = - if (tpe.typeSymbol == EqualsPatternClass) tpe.typeArgs.head else tpe - - // A subtype test which creates fresh existentials for type - // parameters on the right hand side. - def matches(arg1: Type, arg2: Type) = decodedEqualsType(arg1) matchesPattern decodedEqualsType(arg2) - - // For spotting duplicate unapplies - def isEquivalentTree(t1: Tree, t2: Tree) = (t1.symbol == t2.symbol) && (t1 equalsStructure t2) - - // Reproduce the Bind trees wrapping oldTree around newTree - def moveBindings(oldTree: Tree, newTree: Tree): Tree = oldTree match { - case b @ Bind(x, body) => Bind(b.symbol, moveBindings(body, newTree)) - case _ => newTree - } - - // used as argument to `EqualsPatternClass` - case class PseudoType(o: Tree) extends SimpleTypeProxy { - override def underlying: Type = o.tpe - override def safeToString: String = "PseudoType("+o+")" - } - - // If the given pattern contains alternatives, return it as a list of patterns. - // Makes typed copies of any bindings found so all alternatives point to final state. - def extractBindings(p: Pattern): List[Pattern] = - toPats(_extractBindings(p.boundTree, identity)) - - private def _extractBindings(p: Tree, prevBindings: Tree => Tree): List[Tree] = { - def newPrev(b: Bind) = (x: Tree) => treeCopy.Bind(b, b.name, x) setType x.tpe - - p match { - case b @ Bind(_, body) => _extractBindings(body, newPrev(b)) - case Alternative(ps) => ps map prevBindings - } - } - - trait PatternBindingLogic { - self: Pattern => - - // This is for traversing the pattern tree - pattern types which might have - // bound variables beneath them return a list of said patterns for flatMapping. - def subpatternsForVars: List[Pattern] = Nil - - // The outermost Bind(x1, Bind(x2, ...)) surrounding the tree. - private var _boundTree: Tree = tree - def boundTree = _boundTree - def setBound(x: Bind): Pattern = { - _boundTree = x - this - } - def boundVariables = strip(boundTree) - - // If a tree has bindings, boundTree looks something like - // Bind(v3, Bind(v2, Bind(v1, tree))) - // This takes the given tree and creates a new pattern - // using the same bindings. - def rebindTo(t: Tree): Pattern = Pattern(moveBindings(boundTree, t)) - - // Wrap this pattern's bindings around (_: Type) - def rebindToType(tpe: Type, ascription: Type = null): Pattern = { - val aType = if (ascription == null) tpe else ascription - rebindTo(Typed(WILD(tpe), TypeTree(aType)) setType tpe) - } - - // Wrap them around _ - def rebindToEmpty(tpe: Type): Pattern = - rebindTo(Typed(EmptyTree, TypeTree(tpe)) setType tpe) - - // Wrap them around a singleton type for an EqualsPattern check. - def rebindToEqualsCheck(): Pattern = - rebindToType(equalsCheck) - - // Like rebindToEqualsCheck, but subtly different. Not trying to be - // mysterious -- I haven't sorted it all out yet. - def rebindToObjectCheck(): Pattern = - rebindToType(mkEqualsRef(sufficientType), sufficientType) - - /** Helpers **/ - private def wrapBindings(vs: List[Symbol], pat: Tree): Tree = vs match { - case Nil => pat - case x :: xs => Bind(x, wrapBindings(xs, pat)) setType pat.tpe - } - private def strip(t: Tree): List[Symbol] = t match { - case b @ Bind(_, pat) => b.symbol :: strip(pat) - case _ => Nil - } - private def deepstrip(t: Tree): List[Symbol] = - treeCollect(t, { case x: Bind => x.symbol }) - } - - case class Binding(pvar: Symbol, tvar: Symbol) { - override def toString() = pvar.name + " -> " + tvar.name - } - - class Bindings(private val vlist: List[Binding]) { - // if (!vlist.isEmpty) - // traceCategory("Bindings", this.toString) - - def get() = vlist - def toMap = vlist map (x => (x.pvar, x.tvar)) toMap - - def add(vs: Iterable[Symbol], tvar: Symbol): Bindings = { - val newBindings = vs.toList map (v => Binding(v, tvar)) - new Bindings(newBindings ++ vlist) - } - - override def toString() = - if (vlist.isEmpty) "<none>" - else vlist.mkString(", ") - } - - val NoBinding: Bindings = new Bindings(Nil) -} diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala deleted file mode 100644 index f116a7c4c7..0000000000 --- a/src/compiler/scala/tools/nsc/matching/Patterns.scala +++ /dev/null @@ -1,499 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * Author: Paul Phillips - */ - -package scala.tools.nsc -package matching - -import symtab.Flags -import PartialFunction._ - -/** Patterns are wrappers for Trees with enhanced semantics. - * - * @author Paul Phillips - */ - -trait Patterns extends ast.TreeDSL { - self: transform.ExplicitOuter => - - import global.{ typer => _, _ } - import definitions._ - import CODE._ - import Debug._ - import treeInfo.{ unbind, isStar, isVarPattern } - - type PatternMatch = MatchMatrix#PatternMatch - private type PatternVar = MatrixContext#PatternVar - - // Fresh patterns - def emptyPatterns(i: Int): List[Pattern] = List.fill(i)(NoPattern) - def emptyTrees(i: Int): List[Tree] = List.fill(i)(EmptyTree) - - // An empty pattern - def NoPattern = WildcardPattern() - - // The constant null pattern - def NullPattern = LiteralPattern(NULL) - - // The Nil pattern - def NilPattern = Pattern(gen.mkNil) - - // 8.1.1 - case class VariablePattern(tree: Ident) extends NamePattern { - lazy val Ident(name) = tree - require(isVarPattern(tree) && name != nme.WILDCARD) - override def covers(sym: Symbol) = true - override def description = "%s".format(name) - } - - // 8.1.1 (b) - case class WildcardPattern() extends Pattern { - def tree = EmptyTree - override def covers(sym: Symbol) = true - override def isDefault = true - override def description = "_" - } - - // 8.1.2 - case class TypedPattern(tree: Typed) extends Pattern { - lazy val Typed(expr, tpt) = tree - - override def covers(sym: Symbol) = newMatchesPattern(sym, tpt.tpe) - override def sufficientType = tpt.tpe - override def subpatternsForVars: List[Pattern] = List(Pattern(expr)) - override def simplify(pv: PatternVar) = Pattern(expr) match { - case ExtractorPattern(ua) if pv.sym.tpe <:< tpt.tpe => this rebindTo expr - case _ => this - } - override def description = "%s: %s".format(Pattern(expr), tpt) - } - - // 8.1.3 - case class LiteralPattern(tree: Literal) extends Pattern { - lazy val Literal(const @ Constant(value)) = tree - - def isSwitchable = cond(const.tag) { case ByteTag | ShortTag | IntTag | CharTag => true } - def intValue = const.intValue - override def description = { - val s = if (value == null) "null" else value.toString - "Lit(%s)".format(s) - } - } - - // 8.1.4 (a) - case class ApplyIdentPattern(tree: Apply) extends ApplyPattern with NamePattern { - // XXX - see bug 3411 for code which violates this assumption - // require (!isVarPattern(fn) && args.isEmpty) - lazy val ident @ Ident(name) = fn - - override def sufficientType = Pattern(ident).equalsCheck - override def simplify(pv: PatternVar) = this.rebindToObjectCheck() - override def description = "Id(%s)".format(name) - } - // 8.1.4 (b) - case class ApplySelectPattern(tree: Apply) extends ApplyPattern with SelectPattern { - require (args.isEmpty) - lazy val Apply(select: Select, _) = tree - - override lazy val sufficientType = qualifier.tpe match { - case t: ThisType => singleType(t, sym) // this.X - case _ => - qualifier match { - case _: Apply => PseudoType(tree) - case _ => singleType(Pattern(qualifier).necessaryType, sym) - } - } - - override def covers(sym: Symbol) = newMatchesPattern(sym, sufficientType) - override def simplify(pv: PatternVar) = this.rebindToObjectCheck() - override def description = backticked match { - case Some(s) => "this." + s - case _ => "Sel(%s.%s)".format(Pattern(qualifier), name) - } - - } - // 8.1.4 (c) - case class StableIdPattern(tree: Select) extends SelectPattern { - def select = tree - override def description = "St(%s)".format(printableSegments.mkString(" . ")) - private def printableSegments = - pathSegments filter (x => !x.isEmpty && (x.toString != "$iw")) - } - // 8.1.4 (d) - case class ObjectPattern(tree: Apply) extends ApplyPattern { // NamePattern? - require(!fn.isType && isModule) - - override def covers(sym: Symbol) = newMatchesPattern(sym, sufficientType) - override def sufficientType = tpe.narrow - override def simplify(pv: PatternVar) = this.rebindToObjectCheck() - override def description = "Obj(%s)".format(fn) - } - // 8.1.4 (e) - case class SimpleIdPattern(tree: Ident) extends NamePattern { - val Ident(name) = tree - override def covers(sym: Symbol) = newMatchesPattern(sym, tpe.narrow) - override def description = "Id(%s)".format(name) - } - - // 8.1.5 - case class ConstructorPattern(tree: Apply) extends ApplyPattern with NamePattern { - require(fn.isType && this.isCaseClass, "tree: " + tree + " fn: " + fn) - def name = tpe.typeSymbol.name - def cleanName = tpe.typeSymbol.decodedName - def hasPrefix = tpe.prefix.prefixString != "" - def prefixedName = - if (hasPrefix) "%s.%s".format(tpe.prefix.prefixString, cleanName) - else cleanName - - private def isColonColon = cleanName == "::" - - override def subpatterns(pm: MatchMatrix#PatternMatch) = - if (pm.head.isCaseClass) toPats(args) - else super.subpatterns(pm) - - override def simplify(pv: PatternVar) = - if (args.isEmpty) this rebindToEmpty tree.tpe - else this - - override def covers(sym: Symbol) = { - debugging("[constructor] Does " + this + " cover " + sym + " ? ") { - sym.tpe.typeSymbol == this.tpe.typeSymbol - } - } - override def description = { - if (isColonColon) "%s :: %s".format(Pattern(args(0)), Pattern(args(1))) - else "%s(%s)".format(name, toPats(args).mkString(", ")) - } - } - // 8.1.6 - case class TuplePattern(tree: Apply) extends ApplyPattern { - override def description = "((%s))".format(args.size, toPats(args).mkString(", ")) - } - - // 8.1.7 / 8.1.8 (unapply and unapplySeq calls) - case class ExtractorPattern(tree: UnApply) extends UnapplyPattern { - private def uaTyped = Typed(tree, TypeTree(arg.tpe)) setType arg.tpe - - override def simplify(pv: PatternVar) = { - if (pv.tpe <:< arg.tpe) this - else this rebindTo uaTyped - } - override def description = "Unapply(%s => %s)".format(necessaryType, resTypesString) - } - - // Special List handling. It was like that when I got here. - case class ListExtractorPattern(tree: UnApply, tpt: Tree, elems: List[Tree]) extends UnapplyPattern with SequenceLikePattern { - // As yet I can't testify this is doing any good relative to using - // tpt.tpe, but it doesn't seem to hurt either. - private lazy val packedType = global.typer.computeType(tpt, tpt.tpe) - private lazy val consRef = appliedType(ConsClass, packedType) - private lazy val listRef = appliedType(ListClass, packedType) - private lazy val seqRef = appliedType(SeqClass, packedType) - - private def thisSeqRef = { - val tc = (tree.tpe baseType SeqClass).typeConstructor - if (tc.typeParams.size == 1) appliedType(tc, List(packedType)) - else seqRef - } - - // Fold a list into a well-typed x :: y :: etc :: tree. - private def listFolder(hd: Tree, tl: Tree): Tree = unbind(hd) match { - case t @ Star(_) => moveBindings(hd, WILD(t.tpe)) - case _ => - val dummyMethod = NoSymbol.newTermSymbol(newTermName("matching$dummy")) - val consType = MethodType(dummyMethod newSyntheticValueParams List(packedType, listRef), consRef) - - Apply(TypeTree(consType), List(hd, tl)) setType consRef - } - private def foldedPatterns = elems.foldRight(gen.mkNil)((x, y) => listFolder(x, y)) - override def necessaryType = if (nonStarPatterns.nonEmpty) consRef else listRef - - override def simplify(pv: PatternVar) = { - if (pv.tpe <:< necessaryType) - Pattern(foldedPatterns) - else - this rebindTo (Typed(tree, TypeTree(necessaryType)) setType necessaryType) - } - override def description = "List(%s => %s)".format(packedType, resTypesString) - } - - trait SequenceLikePattern extends Pattern { - def elems: List[Tree] - override def hasStar = elems.nonEmpty && isStar(elems.last) - - def elemPatterns = toPats(elems) - def nonStarElems = if (hasStar) elems.init else elems - def nonStarPatterns = toPats(nonStarElems) - def nonStarLength = nonStarElems.length - } - - // 8.1.8 (b) (literal ArrayValues) - case class SequencePattern(tree: ArrayValue) extends Pattern with SequenceLikePattern { - lazy val ArrayValue(elemtpt, elems) = tree - - override def subpatternsForVars: List[Pattern] = elemPatterns - override def description = "Seq(%s)".format(elemPatterns mkString ", ") - } - - // 8.1.8 (c) - case class StarPattern(tree: Star) extends Pattern { - lazy val Star(elem) = tree - override def description = "_*" - } - // XXX temporary? - case class ThisPattern(tree: This) extends NamePattern { - lazy val This(name) = tree - override def description = "this" - } - - // 8.1.9 - // InfixPattern ... subsumed by Constructor/Extractor Patterns - - // 8.1.10 - case class AlternativePattern(tree: Alternative) extends Pattern { - private lazy val Alternative(subtrees) = tree - private def alts = toPats(subtrees) - override def description = "Alt(%s)".format(alts mkString " | ") - } - - // 8.1.11 - // XMLPattern ... for now, subsumed by SequencePattern, but if we want - // to make it work right, it probably needs special handling. - - private def abortUnknownTree(tree: Tree) = - abort("Unknown Tree reached pattern matcher: %s/%s".format(tree, tree.getClass)) - - object Pattern { - // a small tree -> pattern cache - private val cache = perRunCaches.newMap[Tree, Pattern]() - - def apply(tree: Tree): Pattern = { - if (cache contains tree) - return cache(tree) - - val p = tree match { - case x: Bind => apply(unbind(tree)) setBound x - case EmptyTree => WildcardPattern() - case Ident(nme.WILDCARD) => WildcardPattern() - case x @ Alternative(ps) => AlternativePattern(x) - case x: Apply => ApplyPattern(x) - case x: Typed => TypedPattern(x) - case x: Literal => LiteralPattern(x) - case x: UnApply => UnapplyPattern(x) - case x: Ident => if (isVarPattern(x)) VariablePattern(x) else SimpleIdPattern(x) - case x: ArrayValue => SequencePattern(x) - case x: Select => StableIdPattern(x) - case x: Star => StarPattern(x) - case x: This => ThisPattern(x) // XXX ? - case _ => abortUnknownTree(tree) - } - cache(tree) = p - - // limiting the trace output - p match { - case WildcardPattern() => p - case _: LiteralPattern => p - case _ => tracing("Pattern")(p) - } - } - // matching on Pattern(...) always skips the bindings. - def unapply(other: Any): Option[Tree] = other match { - case x: Tree => unapply(Pattern(x)) - case x: Pattern => Some(x.tree) - case _ => None - } - } - - object UnapplyPattern { - private object UnapplySeq { - def unapply(x: UnApply) = x match { - case UnApply( - Apply(TypeApply(Select(qual, nme.unapplySeq), List(tpt)), _), - List(ArrayValue(_, elems))) => - Some((qual.symbol, tpt, elems)) - case _ => - None - } - } - - def apply(x: UnApply): Pattern = x match { - case UnapplySeq(ListModule, tpt, elems) => - ListExtractorPattern(x, tpt, elems) - case _ => - ExtractorPattern(x) - } - } - - // right now a tree like x @ Apply(fn, Nil) where !fn.isType - // is handled by creating a singleton type: - // - // val stype = Types.singleType(x.tpe.prefix, x.symbol) - // - // and then passing that as a type argument to EqualsPatternClass: - // - // val tpe = typeRef(NoPrefix, EqualsPatternClass, List(stype)) - // - // then creating a Typed pattern and rebinding. - // - // val newpat = Typed(EmptyTree, TypeTree(tpe)) setType tpe) - // - // This is also how Select(qual, name) is handled. - object ApplyPattern { - def apply(x: Apply): Pattern = { - val Apply(fn, args) = x - def isModule = x.symbol.isModule || x.tpe.termSymbol.isModule - - if (fn.isType) { - if (isTupleType(fn.tpe)) TuplePattern(x) - else ConstructorPattern(x) - } - else if (args.isEmpty) { - if (isModule) ObjectPattern(x) - else fn match { - case _: Ident => ApplyIdentPattern(x) - case _: Select => ApplySelectPattern(x) - } - } - else abortUnknownTree(x) - } - } - - /** Some intermediate pattern classes with shared structure **/ - - sealed trait SelectPattern extends NamePattern { - def select: Select - lazy val Select(qualifier, name) = select - def pathSegments = getPathSegments(tree) - def backticked: Option[String] = qualifier match { - case _: This if nme.isVariableName(name) => Some("`%s`".format(name)) - case _ => None - } - override def covers(sym: Symbol) = newMatchesPattern(sym, tree.tpe) - protected def getPathSegments(t: Tree): List[Name] = t match { - case Select(q, name) => name :: getPathSegments(q) - case Apply(f, Nil) => getPathSegments(f) - case _ => Nil - } - } - - sealed trait NamePattern extends Pattern { - def name: Name - override def sufficientType = tpe.narrow - override def simplify(pv: PatternVar) = this.rebindToEqualsCheck() - override def description = name.toString - } - - sealed trait UnapplyPattern extends Pattern { - lazy val UnApply(unfn, args) = tree - lazy val Apply(fn, _) = unfn - lazy val MethodType(List(arg, _*), _) = fn.tpe - - // Covers if the symbol matches the unapply method's argument type, - // and the return type of the unapply is Some. - override def covers(sym: Symbol) = newMatchesPattern(sym, arg.tpe) - - // TODO: for alwaysCovers: - // fn.tpe.finalResultType.typeSymbol == SomeClass - - override def necessaryType = arg.tpe - override def subpatternsForVars = args match { - case List(ArrayValue(elemtpe, elems)) => toPats(elems) - case _ => toPats(args) - } - - def resTypes = analyzer.unapplyTypeList(unfn.pos, unfn.symbol, unfn.tpe, args.length) - def resTypesString = resTypes match { - case Nil => "Boolean" - case xs => xs.mkString(", ") - } - } - - sealed trait ApplyPattern extends Pattern { - lazy val Apply(fn, args) = tree - override def subpatternsForVars: List[Pattern] = toPats(args) - - override def dummies = - if (!this.isCaseClass) Nil - else emptyPatterns(sufficientType.typeSymbol.caseFieldAccessors.size) - - def isConstructorPattern = fn.isType - override def covers(sym: Symbol) = newMatchesPattern(sym, fn.tpe) - } - - sealed abstract class Pattern extends PatternBindingLogic { - def tree: Tree - - // returns either a simplification of this pattern or identity. - def simplify(pv: PatternVar): Pattern = this - - // the right number of dummies for this pattern - def dummies: List[Pattern] = Nil - - // Is this a default pattern (untyped "_" or an EmptyTree inserted by the matcher) - def isDefault = false - - // what type must a scrutinee have to have any chance of matching this pattern? - def necessaryType = tpe - - // what type could a scrutinee have which would automatically indicate a match? - // (nullness and guards will still be checked.) - def sufficientType = tpe - - // the subpatterns for this pattern (at the moment, that means constructor arguments) - def subpatterns(pm: MatchMatrix#PatternMatch): List[Pattern] = pm.dummies - - // if this pattern should be considered to cover the given symbol - def covers(sym: Symbol): Boolean = newMatchesPattern(sym, sufficientType) - def newMatchesPattern(sym: Symbol, pattp: Type) = { - debugging("[" + kindString + "] Does " + pattp + " cover " + sym + " ? ") { - (sym.isModuleClass && (sym.tpe.typeSymbol eq pattp.typeSymbol)) || - (sym.tpe.baseTypeSeq exists (_ matchesPattern pattp)) - } - } - - def sym = tree.symbol - def tpe = tree.tpe - def isEmpty = tree.isEmpty - - def isModule = sym.isModule || tpe.termSymbol.isModule - def isCaseClass = tpe.typeSymbol.isCase - def isObject = (sym != null) && (sym != NoSymbol) && tpe.prefix.isStable // XXX not entire logic - - def hasStar = false - - def setType(tpe: Type): this.type = { - tree setType tpe - this - } - - def equalsCheck = - tracing("equalsCheck")( - if (sym.isValue) singleType(NoPrefix, sym) - else tpe.narrow - ) - - /** Standard methods **/ - override def equals(other: Any) = other match { - case x: Pattern => this.boundTree == x.boundTree - case _ => super.equals(other) - } - override def hashCode() = boundTree.hashCode() - def description = super.toString - - final override def toString = description - - def toTypeString() = "%s <: x <: %s".format(necessaryType, sufficientType) - def kindString = "" - } - - /*** Extractors ***/ - - object UnapplyParamType { - def unapply(x: Tree): Option[Type] = condOpt(unbind(x)) { - case UnApply(Apply(fn, _), _) => fn.tpe match { - case m: MethodType => m.paramTypes.head - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/package.scala b/src/compiler/scala/tools/nsc/package.scala index 00a9f3b39c..ee1668a38a 100644 --- a/src/compiler/scala/tools/nsc/package.scala +++ b/src/compiler/scala/tools/nsc/package.scala @@ -6,9 +6,21 @@ package scala.tools package object nsc { + type Mode = scala.reflect.internal.Mode + val Mode = scala.reflect.internal.Mode + + def EXPRmode = Mode.EXPRmode + def BYVALmode = Mode.BYVALmode + def POLYmode = Mode.POLYmode + def TAPPmode = Mode.TAPPmode + def FUNmode = Mode.FUNmode + type Phase = scala.reflect.internal.Phase val NoPhase = scala.reflect.internal.NoPhase + type Variance = scala.reflect.internal.Variance + val Variance = scala.reflect.internal.Variance + type FatalError = scala.reflect.internal.FatalError val FatalError = scala.reflect.internal.FatalError diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala index 2050ce7ffd..b0113f7696 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala @@ -6,13 +6,14 @@ package scala.tools.nsc package plugins -import io.{ File, Path, Jar } -import java.net.URLClassLoader -import java.util.jar.JarFile +import scala.tools.nsc.io.{ Jar } +import scala.tools.nsc.util.ScalaClassLoader +import scala.reflect.io.{ Directory, File, Path } +import java.io.InputStream import java.util.zip.ZipException -import scala.collection.mutable -import mutable.ListBuffer +import scala.collection.mutable.ListBuffer +import scala.util.{ Try, Success, Failure } import scala.xml.XML /** Information about a plugin loaded from a jar file. @@ -37,11 +38,13 @@ abstract class Plugin { val description: String /** The compiler that this plugin uses. This is normally equated - * to a constructor parameter in the concrete subclass. */ + * to a constructor parameter in the concrete subclass. + */ val global: Global /** Handle any plugin-specific options. The `-P:plugname:` part - * will not be present. */ + * will not be present. + */ def processOptions(options: List[String], error: String => Unit) { if (!options.isEmpty) error("Error: " + name + " has no options") @@ -63,90 +66,86 @@ object Plugin { private val PluginXML = "scalac-plugin.xml" - /** Create a class loader with the specified file plus + /** Create a class loader with the specified locations plus * the loader that loaded the Scala compiler. */ - private def loaderFor(jarfiles: Seq[Path]): ClassLoader = { + private def loaderFor(locations: Seq[Path]): ScalaClassLoader = { val compilerLoader = classOf[Plugin].getClassLoader - val jarurls = jarfiles map (_.toURL) + val urls = locations map (_.toURL) - new URLClassLoader(jarurls.toArray, compilerLoader) + ScalaClassLoader fromURLs (urls, compilerLoader) } - /** Try to load a plugin description from the specified - * file, returning <code>None</code> if it does not work. + /** Try to load a plugin description from the specified location. */ - private def loadDescription(jarfile: Path): Option[PluginDescription] = - // XXX Return to this once we have some ARM support - if (!jarfile.exists) None - else try { - val jar = new JarFile(jarfile.jfile) - - try { - jar getEntry PluginXML match { - case null => None - case entry => - val in = jar getInputStream entry - val packXML = XML load in - in.close() - - PluginDescription fromXML packXML - } - } - finally jar.close() - } - catch { - case _: ZipException => None + private def loadDescriptionFromJar(jarp: Path): Try[PluginDescription] = { + // XXX Return to this once we have more ARM support + def read(is: Option[InputStream]) = is match { + case None => throw new RuntimeException(s"Missing $PluginXML in $jarp") + case _ => PluginDescription fromXML (XML load is.get) } + Try(new Jar(jarp.jfile).withEntryStream(PluginXML)(read)) + } + + private def loadDescriptionFromFile(f: Path): Try[PluginDescription] = + Try(XML loadFile f.jfile) map (PluginDescription fromXML _) type AnyClass = Class[_] - /** Loads a plugin class from the named jar file. + /** Use a class loader to load the plugin class. * - * @return `None` if the jar file has no plugin in it or - * if the plugin is badly formed. + * @return `None` on failure */ - def loadFrom(jarfile: Path, loader: ClassLoader): Option[AnyClass] = - loadDescription(jarfile) match { - case None => - println("Warning: could not load descriptor for plugin %s".format(jarfile)) - None - case Some(pdesc) => - try Some(loader loadClass pdesc.classname) catch { - case _: Exception => - println("Warning: class not found for plugin in %s (%s)".format(jarfile, pdesc.classname)) - None - } + def load(pd: PluginDescription, loader: ClassLoader): Try[AnyClass] = { + Try[AnyClass] { + loader loadClass pd.classname + } recoverWith { + case _: Exception => + Failure(new RuntimeException(s"Warning: class not found: ${pd.classname}")) } + } - /** Load all plugins found in the argument list, both in the - * jar files explicitly listed, and in the jar files in the - * directories specified. Skips all plugins in `ignoring`. + /** Load all plugins specified by the arguments. + * Each of `jars` must be a valid plugin archive or exploded archive. + * Each of `dirs` may be a directory containing arbitrary plugin archives. + * Skips all plugins named in `ignoring`. * A single classloader is created and used to load all of them. */ def loadAllFrom( jars: List[Path], dirs: List[Path], - ignoring: List[String]): List[AnyClass] = + ignoring: List[String]): List[Try[AnyClass]] = { - val alljars = (jars ::: (for { - dir <- dirs if dir.isDirectory - entry <- dir.toDirectory.files.toList sortBy (_.name) -// was: if Path.isJarOrZip(entry) - if Jar.isJarOrZip(entry) - pdesc <- loadDescription(entry) - if !(ignoring contains pdesc.name) - } yield entry)).distinct - - val loader = loaderFor(alljars) - (alljars map (loadFrom(_, loader))).flatten + // List[(jar, Success(descriptor))] in dir + def scan(d: Directory) = for { + f <- d.files.toList sortBy (_.name) + if Jar isJarOrZip f + pd = loadDescriptionFromJar(f) + if pd.isSuccess + } yield (f, pd) + // (dir, Try(descriptor)) + def explode(d: Directory) = d -> loadDescriptionFromFile(d / PluginXML) + // (j, Try(descriptor)) + def required(j: Path) = j -> loadDescriptionFromJar(j) + + type Paired = Pair[Path, Try[PluginDescription]] + val included: List[Paired] = (dirs flatMap (_ ifDirectory scan)).flatten + val exploded: List[Paired] = jars flatMap (_ ifDirectory explode) + val explicit: List[Paired] = jars flatMap (_ ifFile required) + def ignored(p: Paired) = p match { + case (path, Success(pd)) => ignoring contains pd.name + case _ => false + } + val (locs, pds) = ((explicit ::: exploded ::: included) filterNot ignored).unzip + + val loader = loaderFor(locs.distinct) + pds filter (_.isSuccess) map (_.get) map (Plugin load (_, loader)) } /** Instantiate a plugin class, given the class and * the compiler it is to be used in. */ def instantiate(clazz: AnyClass, global: Global): Plugin = { - val constructor = clazz getConstructor classOf[Global] - (constructor newInstance global).asInstanceOf[Plugin] + (clazz getConstructor classOf[Global] newInstance global).asInstanceOf[Plugin] } } diff --git a/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala b/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala index 4d98b2563c..c6e1af7ea4 100644 --- a/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala +++ b/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala @@ -18,8 +18,12 @@ abstract class PluginComponent extends SubComponent { /** Internal flag to tell external from internal phases */ final override val internal = false - /** Phases supplied by plugins should not have give the runsRightAfter constraint, - * but can override it */ + /** Phases supplied by plugins should not have to supply the + * runsRightAfter constraint, but can override it. + */ val runsRightAfter: Option[String] = None + /** Useful for -Xshow-phases. */ + def description: String = "" + } diff --git a/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala b/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala index bd567400fb..27693d1a45 100644 --- a/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala +++ b/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala @@ -6,27 +6,22 @@ package scala.tools.nsc package plugins -import scala.xml.{Node,NodeSeq} +import scala.xml.Node /** A description of a compiler plugin, suitable for serialization * to XML for inclusion in the plugin's .jar file. * * @author Lex Spoon * @version 1.0, 2007-5-21 + * @param name A short name of the plugin, used to identify it in + * various contexts. The phase defined by the plugin + * should have the same name. + * @param classname The name of the main Plugin class. */ -abstract class PluginDescription { - - /** A short name of the compiler, used to identify it in - * various contexts. The phase defined by the plugin - * should have the same name. - */ - val name: String - - /** The name of the main class for the plugin */ - val classname: String +case class PluginDescription(name: String, classname: String) { /** An XML representation of this description. It can be - * read back using <code>PluginDescription.fromXML</code>. + * read back using `PluginDescription.fromXML`. * It should be stored inside the jar archive file. */ def toXML: Node = { @@ -44,32 +39,24 @@ abstract class PluginDescription { */ object PluginDescription { - def fromXML(xml: Node): Option[PluginDescription] = { - // check the top-level tag - xml match { - case <plugin>{_*}</plugin> => () - case _ => return None - } + def fromXML(xml: Node): PluginDescription = { // extract one field def getField(field: String): Option[String] = { val text = (xml \\ field).text.trim if (text == "") None else Some(text) } - - // extract the required fields - val name1 = getField("name") match { - case None => return None - case Some(str) => str + def extracted = { + val name = "name" + val claas = "classname" + val vs = Map(name -> getField(name), claas -> getField(claas)) + if (vs.values exists (_.isEmpty)) fail() + else PluginDescription(name = vs(name).get, classname = vs(claas).get) } - val classname1 = getField("classname") match { - case None => return None - case Some(str) => str + def fail() = throw new RuntimeException("Bad plugin descriptor.") + // check the top-level tag + xml match { + case <plugin>{_*}</plugin> => extracted + case _ => fail() } - - Some(new PluginDescription { - val name = name1 - val classname = classname1 - }) } - } diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 736bd826e4..bb7d54d8f6 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -7,7 +7,8 @@ package scala.tools.nsc package plugins -import io.{ File, Path } +import scala.reflect.io.{ File, Path } +import scala.tools.util.PathResolver.Defaults /** Support for run-time loading of compiler plugins. * @@ -25,8 +26,14 @@ trait Plugins { */ protected def loadRoughPluginsList(): List[Plugin] = { val jars = settings.plugin.value map Path.apply - val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map Path.apply - val classes = Plugin.loadAllFrom(jars, dirs, settings.disable.value) + def injectDefault(s: String) = if (s.isEmpty) Defaults.scalaPluginPath else s + val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map injectDefault map Path.apply + val maybes = Plugin.loadAllFrom(jars, dirs, settings.disable.value) + val (goods, errors) = maybes partition (_.isSuccess) + errors foreach (_ recover { + case e: Exception => inform(e.getMessage) + }) + val classes = goods map (_.get) // flatten // Each plugin must only be instantiated once. A common pattern // is to register annotation checkers during object construction, so @@ -106,7 +113,7 @@ trait Plugins { * @see phasesSet */ protected def computePluginPhases(): Unit = - phasesSet ++= (plugins flatMap (_.components)) + for (p <- plugins; c <- p.components) addToPhasesSet(c, c.description) /** Summary of the options for all loaded plugins */ def pluginOptionsHelp: String = diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala index c7ee11dec0..025fc8e068 100644 --- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala @@ -29,11 +29,7 @@ abstract class AbstractReporter extends Reporter { private def noWarnings = settings.nowarnings.value private def isPromptSet = settings.prompt.value - protected def info0(pos: Position, msg: String, _severity: Severity, force: Boolean) { - val severity = - if (settings.fatalWarnings.value && _severity == WARNING) ERROR - else _severity - + protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean) { if (severity == INFO) { if (isVerbose || force) { severity.count += 1 diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala index e847fb5b86..bda195f9d3 100644 --- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -34,9 +34,6 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr } /** Returns the number of errors issued totally as a string. - * - * @param severity ... - * @return ... */ private def getCountString(severity: Severity): String = StringOps.countElementsAsString((severity).count, label(severity)) @@ -52,17 +49,12 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr printMessage(pos, clabel(severity) + msg) } - /** - * @param pos ... - */ def printSourceLine(pos: Position) { printMessage(pos.lineContent.stripLineEnd) printColumnMarker(pos) } /** Prints the column marker of the given position. - * - * @param pos ... */ def printColumnMarker(pos: Position) = if (pos.isDefined) { printMessage(" " * (pos.column - 1) + "^") } @@ -94,6 +86,5 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr } } - private def abort(msg: String) = throw new Error(msg) override def flush() { writer.flush() } } diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala index 8871ae6555..817ec47ab3 100644 --- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package reporters import scala.reflect.internal.util._ -import scala.reflect.internal.util.StringOps._ /** * This interface provides methods to issue information, warning and diff --git a/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala b/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala index 10e9982594..3aecc06b1e 100644 --- a/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala +++ b/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala @@ -2,9 +2,6 @@ package scala.tools.nsc.scratchpad import java.io.{FileInputStream, InputStreamReader, IOException} -import scala.runtime.ScalaRunTime.stringOf -import java.lang.reflect.InvocationTargetException -import scala.reflect.runtime.ReflectionUtils._ import scala.collection.mutable.ArrayBuffer @deprecated("SI-6458: Instrumentation logic will be moved out of the compiler.","2.10.0") diff --git a/src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala b/src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala index 01dccd7521..61c1717fea 100644 --- a/src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala +++ b/src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala @@ -1,8 +1,6 @@ package scala.tools.nsc package scratchpad -import java.io.Writer -import scala.reflect.internal.util.SourceFile import scala.reflect.internal.Chars._ @deprecated("SI-6458: Instrumentation logic will be moved out of the compiler.","2.10.0") diff --git a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala index adabeb02a3..4727e6d867 100644 --- a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala @@ -47,8 +47,6 @@ trait AbsSettings extends scala.reflect.internal.settings.AbsSettings { } }) - implicit lazy val SettingOrdering: Ordering[Setting] = Ordering.ordered - trait AbsSetting extends Ordered[Setting] with AbsSettingValue { def name: String def helpDescription: String @@ -83,14 +81,6 @@ trait AbsSettings extends scala.reflect.internal.settings.AbsSettings { this } - /** If the appearance of the setting should halt argument processing. */ - private var isTerminatorSetting = false - def shouldStopProcessing = isTerminatorSetting - def stopProcessing(): this.type = { - isTerminatorSetting = true - this - } - /** Issue error and return */ def errorAndValue[T](msg: String, x: T): T = { errorFn(msg) ; x } @@ -110,6 +100,7 @@ trait AbsSettings extends scala.reflect.internal.settings.AbsSettings { /** Attempt to set from a properties file style property value. * Currently used by Eclipse SDT only. + * !!! Needs test. */ def tryToSetFromPropertyValue(s: String): Unit = tryToSet(s :: Nil) @@ -133,7 +124,7 @@ trait AbsSettings extends scala.reflect.internal.settings.AbsSettings { case _ => false } override def hashCode() = name.hashCode + value.hashCode - override def toString() = name + " = " + value + override def toString() = name + " = " + (if (value == "") "\"\"" else value) } trait InternalSetting extends AbsSetting { diff --git a/src/compiler/scala/tools/nsc/settings/AdvancedScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/AdvancedScalaSettings.scala deleted file mode 100644 index 0bec113743..0000000000 --- a/src/compiler/scala/tools/nsc/settings/AdvancedScalaSettings.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package settings - -trait AdvancedScalaSettings { - self: AbsScalaSettings => - - abstract class X extends SettingGroup("-X") { - val assemextdirs: StringSetting - val assemname: StringSetting - val assempath: StringSetting - val checkinit: BooleanSetting - val disableassertions: BooleanSetting - val elidebelow: IntSetting - val experimental: BooleanSetting - val future: BooleanSetting - val generatephasegraph: StringSetting - val logimplicits: BooleanSetting - val mainClass: StringSetting - val migration: BooleanSetting - val noforwarders: BooleanSetting - val nojline: BooleanSetting - val nouescape: BooleanSetting - val plugin: MultiStringSetting - val plugindisable: MultiStringSetting - val pluginlist: BooleanSetting - val pluginrequire: MultiStringSetting - val pluginsdir: StringSetting - val print: PhasesSetting - val printicode: BooleanSetting - val printpos: BooleanSetting - val printtypes: BooleanSetting - val prompt: BooleanSetting - val resident: BooleanSetting - val script: StringSetting - val showclass: StringSetting - val showobject: StringSetting - val showphases: BooleanSetting - val sourcedir: StringSetting - val sourcereader: StringSetting - } - // def Xexperimental = X.experimental - // def Xmigration28 = X.migration - // def Xnojline = X.nojline - // def Xprint = X.print - // def Xprintpos = X.printpos - // def Xshowcls = X.showclass - // def Xshowobj = X.showobject - // def assemextdirs = X.assemextdirs - // def assemname = X.assemname - // def assemrefs = X.assempath - // def checkInit = X.checkinit - // def disable = X.plugindisable - // def elideLevel = X.elidelevel - // def future = X.future - // def genPhaseGraph = X.generatephasegraph - // def logimplicits = X.logimplicits - // def noForwarders = X.noforwarders - // def noassertions = X.disableassertions - // def nouescape = X.nouescape - // def plugin = X.plugin - // def pluginsDir = X.pluginsdir - // def printtypes = X.printtypes - // def prompt = X.prompt - // def require = X.require - // def resident = X.resident - // def script = X.script - // def showPhases = X.showphases - // def showPlugins = X.pluginlist - // def sourceReader = X.sourcereader - // def sourcedir = X.sourcedir - // def writeICode = X.printicode -}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala b/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala deleted file mode 100644 index da2c89d707..0000000000 --- a/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package settings - -/** Taking flag checking to a somewhat higher level. */ -trait AestheticSettings { - def settings: Settings - - // Some(value) if setting has been set by user, None otherwise. - def optSetting[T](s: Settings#Setting): Option[T] = - if (s.isDefault) None else Some(s.value.asInstanceOf[T]) - - def script = optSetting[String](settings.script) - def encoding = optSetting[String](settings.encoding) - def sourceReader = optSetting[String](settings.sourceReader) - - def debug = settings.debug.value - def declsOnly = false - 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 - def target = settings.target.value - def unchecked = settings.unchecked.value - def verbose = settings.verbose.value - def virtPatmat = !settings.XoldPatmat.value - - /** Derived values */ - def jvm = target startsWith "jvm" - def msil = target == "msil" - def verboseDebug = debug && verbose -} diff --git a/src/compiler/scala/tools/nsc/settings/FscSettings.scala b/src/compiler/scala/tools/nsc/settings/FscSettings.scala index 5c852ae07c..34c8e8df9a 100644 --- a/src/compiler/scala/tools/nsc/settings/FscSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/FscSettings.scala @@ -8,7 +8,7 @@ package nsc package settings import util.ClassPath -import io.{ Directory, Path, AbstractFile } +import io.{ Path, AbstractFile } class FscSettings(error: String => Unit) extends Settings(error) { outer => @@ -38,7 +38,7 @@ class FscSettings(error: String => Unit) extends Settings(error) { private def holdsPath = Set[Settings#Setting]( d, dependencyfile, pluginsDir, Ygenjavap ) - + override def processArguments(arguments: List[String], processAll: Boolean): (Boolean, List[String]) = { val (r, args) = super.processArguments(arguments, processAll) // we need to ensure the files specified with relative locations are absolutized based on the currentDir diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index e4f99474e1..0e44ef63a1 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -10,7 +10,6 @@ package settings import io.{ AbstractFile, Jar, Path, PlainFile, VirtualDirectory } import scala.reflect.internal.util.StringOps -import scala.collection.mutable.ListBuffer import scala.io.Source import scala.reflect.{ ClassTag, classTag } @@ -63,30 +62,23 @@ class MutableSettings(val errorFn: String => Unit) (checkDependencies, residualArgs) case "--" :: xs => (checkDependencies, xs) + // discard empties, sometimes they appear because of ant or etc. + // but discard carefully, because an empty string is valid as an argument + // to an option, e.g. -cp "" . So we discard them only when they appear + // where an option should be, not where an argument to an option should be. + case "" :: xs => + loop(xs, residualArgs) case x :: xs => - val isOpt = x startsWith "-" - if (isOpt) { - val newArgs = parseParams(args) - if (args eq newArgs) { - errorFn(s"bad option: '$x'") - (false, args) + if (x startsWith "-") { + parseParams(args) match { + case newArgs if newArgs eq args => errorFn(s"bad option: '$x'") ; (false, args) + case newArgs => loop(newArgs, residualArgs) } - // discard empties, sometimes they appear because of ant or etc. - // but discard carefully, because an empty string is valid as an argument - // to an option, e.g. -cp "" . So we discard them only when they appear - // in option position. - else if (x == "") { - loop(xs, residualArgs) - } - else lookupSetting(x) match { - case Some(s) if s.shouldStopProcessing => (checkDependencies, newArgs) - case _ => loop(newArgs, residualArgs) - } - } - else { - if (processAll) loop(xs, residualArgs :+ x) - else (checkDependencies, args) } + else if (processAll) + loop(xs, residualArgs :+ x) + else + (checkDependencies, args) } loop(arguments, Nil) } @@ -184,7 +176,7 @@ class MutableSettings(val errorFn: String => Unit) * The class loader defining `T` should provide resources `app.class.path` * and `boot.class.path`. These resources should contain the application * and boot classpaths in the same form as would be passed on the command line.*/ - def embeddedDefaults[T: ClassTag]: Unit = + def embeddedDefaults[T: ClassTag]: Unit = // called from sbt and repl embeddedDefaults(classTag[T].runtimeClass.getClassLoader) /** Initializes these settings for embedded use by a class from the given class loader. @@ -248,7 +240,7 @@ class MutableSettings(val errorFn: String => Unit) /** Add a destination directory for sources found under srcdir. * Both directories should exits. */ - def add(srcDir: String, outDir: String): Unit = + def add(srcDir: String, outDir: String): Unit = // used in ide? add(checkDir(AbstractFile.getDirectory(srcDir), srcDir), checkDir(AbstractFile.getDirectory(outDir), outDir)) @@ -443,7 +435,7 @@ class MutableSettings(val errorFn: String => Unit) def tryToSet(args: List[String]) = { value = true ; Some(args) } def unparse: List[String] = if (value) List(name) else Nil - override def tryToSetFromPropertyValue(s : String) { + override def tryToSetFromPropertyValue(s : String) { // used from ide value = s.equalsIgnoreCase("true") } } @@ -565,7 +557,7 @@ class MutableSettings(val errorFn: String => Unit) Some(rest) } override def tryToSetColon(args: List[String]) = tryToSet(args) - override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) + override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) // used from ide def unparse: List[String] = value map (name + ":" + _) withHelpSyntax(name + ":<" + arg + ">") @@ -599,7 +591,7 @@ class MutableSettings(val errorFn: String => Unit) } def unparse: List[String] = if (value == default) Nil else List(name + ":" + value) - override def tryToSetFromPropertyValue(s: String) = tryToSetColon(s::Nil) + override def tryToSetFromPropertyValue(s: String) = tryToSetColon(s::Nil) // used from ide withHelpSyntax(name + ":<" + helpArg + ">") } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 3df6334ec1..a5496f829d 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -52,14 +52,14 @@ trait ScalaSettings extends AbsScalaSettings val jvmargs = PrefixSetting("-J<flag>", "-J", "Pass <flag> directly to the runtime system.") val defines = PrefixSetting("-Dproperty=value", "-D", "Pass -Dproperty=value directly to the runtime system.") - val toolcp = PathSetting("-toolcp", "Add to the runner classpath.", "") + /*val toolcp =*/ PathSetting("-toolcp", "Add to the runner classpath.", "") val nobootcp = BooleanSetting("-nobootcp", "Do not use the boot classpath for the scala jars.") /** * Standard settings */ // argfiles is only for the help message - val argfiles = BooleanSetting ("@<file>", "A text file containing compiler arguments (options and source files)") + /*val argfiles = */ BooleanSetting ("@<file>", "A text file containing compiler arguments (options and source files)") 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.") @@ -69,11 +69,8 @@ trait ScalaSettings extends AbsScalaSettings * -X "Advanced" settings */ val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options.") - val assemname = StringSetting ("-Xassem-name", "file", "(Requires -target:msil) Name of the output assembly.", "").dependsOn(target, "msil") - val assemrefs = StringSetting ("-Xassem-path", "path", "(Requires -target:msil) List of assemblies referenced by the program.", ".").dependsOn(target, "msil") - val assemextdirs = StringSetting ("-Xassem-extdirs", "dirs", "(Requires -target:msil) List of directories containing assemblies. default:lib", Defaults.scalaLibDir.path).dependsOn(target, "msil") - val sourcedir = StringSetting ("-Xsourcedir", "directory", "(Requires -target:msil) Mirror source folder structure in output directory.", ".").dependsOn(target, "msil") val checkInit = BooleanSetting ("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") + val developer = BooleanSetting ("-Xdev", "Indicates user is a developer - issue warnings about anything which seems amiss") val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions or assumptions.") val elidebelow = IntSetting ("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument", elidable.MINIMUM, None, elidable.byName get _) @@ -107,14 +104,12 @@ 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.") val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") /** Compatibility stubs for options whose value name did * not previously match the option name. */ - def XO = optimise def debuginfo = g def dependenciesFile = dependencyfile def nowarnings = nowarn @@ -127,6 +122,7 @@ trait ScalaSettings extends AbsScalaSettings 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 breakCycles = BooleanSetting ("-Ybreak-cycles", "Attempt to break cycles encountered during typing") 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") @@ -166,10 +162,9 @@ trait ScalaSettings extends AbsScalaSettings val refinementMethodDispatch = ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.") - 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 Ymacronoexpand = BooleanSetting ("-Ymacro-no-expand", "Don't expand macros. Might be useful for scaladoc and presentation compiler, but will crash anything which uses macros and gets past typer.") val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") + val Yreploutdir = StringSetting ("-Yrepl-outdir", "path", "Write repl-generated classfiles to given output directory (use \"\" to generate a temporary dir)" , "") val 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.") @@ -179,12 +174,8 @@ trait ScalaSettings extends AbsScalaSettings val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() - def stop = stopAfter - /** 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.") diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index e866ad6ae0..9338d9e5b5 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -41,16 +41,10 @@ trait StandardScalaSettings { val optimise: BooleanSetting // depends on post hook which mutates other settings val print = BooleanSetting ("-print", "Print program with Scala-specific features removed.") val target = ChoiceSetting ("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", - List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil"), - "jvm-1.6") + List("jvm-1.5", "jvm-1.6", "jvm-1.7"), "jvm-1.6") val unchecked = BooleanSetting ("-unchecked", "Enable additional warnings where generated code depends on assumptions.") val uniqid = BooleanSetting ("-uniqid", "Uniquely tag all identifiers in debugging output.") val usejavacp = BooleanSetting ("-usejavacp", "Utilize the java.class.path in classpath resolution.") val verbose = BooleanSetting ("-verbose", "Output messages about what the compiler is doing.") val version = BooleanSetting ("-version", "Print product version and exit.") - - /** These are @<file> and -Dkey=val style settings, which don't - * nicely map to identifiers. - */ - val argfiles: BooleanSetting // exists only to echo help message, should be done differently } diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 9f9879210c..2649a150ad 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -26,11 +26,11 @@ trait Warnings { // These warnings should be pretty quiet unless you're doing // something inadvisable. protected def lintWarnings = List( - // warnDeadCode, warnInaccessible, warnNullaryOverride, warnNullaryUnit, - warnAdaptedArgs + warnAdaptedArgs, + warnInferAny ) // Warning groups. @@ -38,9 +38,13 @@ trait Warnings { BooleanSetting("-Xlint", "Enable recommended additional warnings.") withPostSetHook (_ => lintWarnings foreach (_.value = true)) ) - val warnEverything = ( + + /*val warnEverything = */ ( BooleanSetting("-Ywarn-all", "Enable all -Y warnings.") - withPostSetHook (_ => lintWarnings foreach (_.value = true)) + withPostSetHook { _ => + lint.value = true + allWarnings foreach (_.value = true) + } ) // Individual warnings. @@ -53,9 +57,10 @@ trait Warnings { val warnInaccessible = BooleanSetting ("-Ywarn-inaccessible", "Warn about inaccessible types in method signatures.") val warnNullaryOverride = BooleanSetting ("-Ywarn-nullary-override", "Warn when non-nullary overrides nullary, e.g. `def foo()` over `def foo`.") + val warnInferAny = BooleanSetting ("-Ywarn-infer-any", "Warn when a type argument is inferred to be `Any`.") // Backward compatibility. - def Xwarnfatal = fatalWarnings - def Xchecknull = warnSelectNullable - def Ywarndeadcode = warnDeadCode + @deprecated("Use fatalWarnings", "2.11.0") def Xwarnfatal = fatalWarnings // used by sbt + @deprecated("Use warnSelectNullable", "2.11.0") def Xchecknull = warnSelectNullable // used by ide + @deprecated("Use warnDeadCode", "2.11.0") def Ywarndeadcode = warnDeadCode // used by ide } diff --git a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala index f2aab36b51..4e4efef607 100644 --- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package symtab -import scala.reflect.internal.util.BatchSourceFile import scala.tools.nsc.io.AbstractFile /** A subclass of SymbolLoaders that implements browsing behavior. @@ -28,7 +27,7 @@ abstract class BrowsingLoaders extends SymbolLoaders { override protected def enterIfNew(owner: Symbol, member: Symbol, completer: SymbolLoader): Symbol = { completer.sourcefile match { case Some(src) => - (if (member.isModule) member.moduleClass else member).sourceFile = src + (if (member.isModule) member.moduleClass else member).associatedFile = src case _ => } val decls = owner.info.decls diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 348c7f688f..129331f435 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -10,10 +10,9 @@ import java.io.IOException import scala.compat.Platform.currentTime import scala.tools.nsc.util.{ ClassPath } import classfile.ClassfileParser -import scala.reflect.internal.Flags._ import scala.reflect.internal.MissingRequirementError import scala.reflect.internal.util.Statistics -import scala.tools.nsc.io.{ AbstractFile, MsilFile } +import scala.reflect.io.{ AbstractFile, NoAbstractFile } /** This class ... * @@ -153,7 +152,7 @@ abstract class SymbolLoaders { def sourcefile: Option[AbstractFile] = None /** - * Description of the resource (ClassPath, AbstractFile, MsilFile) + * Description of the resource (ClassPath, AbstractFile) * being processed by this loader */ protected def description: String @@ -162,8 +161,8 @@ abstract class SymbolLoaders { private def setSource(sym: Symbol) { sourcefile foreach (sf => sym match { - case cls: ClassSymbol => cls.sourceFile = sf - case mod: ModuleSymbol => mod.moduleClass.sourceFile = sf + case cls: ClassSymbol => cls.associatedFile = sf + case mod: ModuleSymbol => mod.moduleClass.associatedFile = sf case _ => () }) } @@ -226,7 +225,6 @@ abstract class SymbolLoaders { assert(root.isPackageClass, root) root.setInfo(new PackageClassInfoType(newScope, root)) - val sourcepaths = classpath.sourcepaths if (!root.isRoot) { for (classRep <- classpath.classes if platform.doLoad(classRep)) { initializeFromClassPath(root, classRep) @@ -252,7 +250,7 @@ abstract class SymbolLoaders { protected def doComplete(root: Symbol) { val start = if (Statistics.canEnable) Statistics.startTimer(classReadNanos) else null classfileParser.parse(classfile, root) - if (root.associatedFile eq null) { + if (root.associatedFile eq NoAbstractFile) { root match { // In fact, the ModuleSymbol forwards its setter to the module class case _: ClassSymbol | _: ModuleSymbol => @@ -267,16 +265,6 @@ abstract class SymbolLoaders { override def sourcefile: Option[AbstractFile] = classfileParser.srcfile } - class MsilFileLoader(msilFile: MsilFile) extends SymbolLoader with FlagAssigningCompleter { - private def typ = msilFile.msilType - private object typeParser extends clr.TypeParser { - val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global - } - - protected def description = "MsilFile "+ typ.FullName + ", assembly "+ typ.Assembly.FullName - protected def doComplete(root: Symbol) { typeParser.parse(typ, root) } - } - class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter { protected def description = "source file "+ srcfile.toString override def fromSource = true @@ -289,11 +277,6 @@ abstract class SymbolLoaders { protected def doComplete(root: Symbol) { root.sourceModule.initialize } } - object clrTypes extends clr.CLRTypes { - val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global - if (global.forMSIL) init() - } - /** used from classfile parser to avoid cyclies */ var parentsLevel = 0 var pendingLoadActions: List[() => Unit] = Nil diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala index 7a84441e09..035244e421 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package symtab -import scala.collection.{ mutable, immutable } import scala.language.implicitConversions import scala.language.postfixOps @@ -17,9 +16,6 @@ trait SymbolTrackers { val global: Global import global._ - private implicit lazy val TreeOrdering: Ordering[Tree] = - Ordering by (x => (x.shortClass, x.symbol)) - private implicit lazy val SymbolOrdering: Ordering[Symbol] = Ordering by (x => (x.kindString, x.name.toString)) @@ -76,7 +72,6 @@ trait SymbolTrackers { private def isFlagsChange(sym: Symbol) = changed.flags contains sym private implicit def NodeOrdering: Ordering[Node] = Ordering by (_.root) - private def ownersString(sym: Symbol, num: Int) = sym.ownerChain drop 1 take num mkString " -> " object Node { def nodes(syms: Set[Symbol]): List[Node] = { @@ -114,7 +109,6 @@ trait SymbolTrackers { case Some(oldFlags) => val added = masked & ~oldFlags val removed = oldFlags & ~masked - val steady = masked & ~(added | removed) val all = masked | oldFlags val strs = 0 to 63 map { bit => val flag = 1L << bit @@ -181,7 +175,7 @@ trait SymbolTrackers { } def show(label: String): String = { val hierarchy = Node(current) - val Change(added, removed, symMap, owners, flags) = history.head + val Change(_, removed, symMap, _, _) = history.head def detailString(sym: Symbol) = { val ownerString = sym.ownerChain splitAt 3 match { case (front, back) => diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/AbstractFileReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/AbstractFileReader.scala index 427b5bf887..17e3b08ec2 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/AbstractFileReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/AbstractFileReader.scala @@ -29,11 +29,6 @@ class AbstractFileReader(val file: AbstractFile) { */ var bp: Int = 0 - /** return byte at offset 'pos' - */ - @throws(classOf[IndexOutOfBoundsException]) - def byteAt(pos: Int): Byte = buf(pos) - /** read a byte */ @throws(classOf[IndexOutOfBoundsException]) @@ -45,7 +40,7 @@ class AbstractFileReader(val file: AbstractFile) { /** read some bytes */ - def nextBytes(len: Int): Array[Byte] = { + def nextBytes(len: Int): Array[Byte] = { // used in ide bp += len buf.slice(bp - len, bp) } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index a517a33279..a5f41dc82b 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -23,15 +23,14 @@ import scala.tools.nsc.io.AbstractFile abstract class ClassfileParser { val global: Global import global._ - import definitions.{ AnnotationClass, ClassfileAnnotationClass } import scala.reflect.internal.ClassfileConstants._ import Flags._ protected var in: AbstractFileReader = _ // the class file reader protected var clazz: Symbol = _ // the class symbol containing dynamic members protected var staticModule: Symbol = _ // the module symbol containing static members - protected var instanceScope: Scope = _ // the scope of all instance definitions - protected var staticScope: Scope = _ // the scope of all static definitions + protected var instanceScope: Scope = _ // the scope of all instance definitions + protected var staticScope: Scope = _ // the scope of all static definitions protected var pool: ConstantPool = _ // the classfile's constant pool protected var isScala: Boolean = _ // does class file describe a scala class? protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation? @@ -186,7 +185,7 @@ abstract class ClassfileParser { if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) val name = getExternalName(in.getChar(start + 1)) if (nme.isModuleName(name)) - c = rootMirror.getModule(nme.stripModuleSuffix(name)) + c = rootMirror.getModuleByName(nme.stripModuleSuffix(name)) else c = classNameToSymbol(name) @@ -237,7 +236,7 @@ abstract class ClassfileParser { //assert(name.endsWith("$"), "Not a module class: " + name) f = forceMangledName(name dropRight 1, true) if (f == NoSymbol) - f = rootMirror.getModule(name dropRight 1) + f = rootMirror.getModuleByName(name dropRight 1) } else { val origName = nme.originalName(name) val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol @@ -253,8 +252,8 @@ abstract class ClassfileParser { } else { log("Couldn't find " + name + ": " + tpe + " inside: \n" + ownerTpe) f = tpe match { - case MethodType(_, _) => owner.newMethod(name, owner.pos) - case _ => owner.newVariable(name, owner.pos) + case MethodType(_, _) => owner.newMethod(name.toTermName, owner.pos) + case _ => owner.newVariable(name.toTermName, owner.pos) } f setInfo tpe log("created fake member " + f.fullName) @@ -283,7 +282,7 @@ abstract class ClassfileParser { if (in.buf(start).toInt != CONSTANT_NAMEANDTYPE) errorBadTag(start) val name = getName(in.getChar(start + 1).toInt) // create a dummy symbol for method types - val dummySym = ownerTpe.typeSymbol.newMethod(name, ownerTpe.typeSymbol.pos) + val dummySym = ownerTpe.typeSymbol.newMethod(name.toTermName, ownerTpe.typeSymbol.pos) var tpe = getType(dummySym, in.getChar(start + 3).toInt) // fix the return type, which is blindly set to the class currently parsed @@ -361,7 +360,7 @@ abstract class ClassfileParser { } value match { case ct: Constant => ct - case cls: Symbol => Constant(cls.tpe) + case cls: Symbol => Constant(cls.tpe_*) case arr: Type => Constant(arr) } } @@ -423,9 +422,9 @@ abstract class ClassfileParser { var sym: Symbol = rootMirror.RootClass // was "at flatten.prev" - beforeFlatten { + enteringFlatten { for (part0 <- parts; if !(part0 == ""); part = newTermName(part0)) { - val sym1 = beforeIcode { + val sym1 = enteringIcode { sym.linkedClassOfClass.info sym.info.decl(part.encode) }//.suchThat(module == _.isModule) @@ -458,7 +457,7 @@ abstract class ClassfileParser { ss = name.subName(start, end) sym = owner.info.decls lookup ss if (sym == NoSymbol) { - sym = owner.newPackage(ss) setInfo completer + sym = owner.newPackage(ss.toTermName) setInfo completer sym.moduleClass setInfo completer owner.info.decls enter sym } @@ -478,7 +477,7 @@ abstract class ClassfileParser { if (name.pos('.') == name.length) definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName) else - rootMirror.getClass(name) // see tickets #2464, #3756 + rootMirror.getClassByName(name) // see tickets #2464, #3756 } catch { case _: FatalError => loadClassSymbol(name) } @@ -500,8 +499,8 @@ abstract class ClassfileParser { def parseClass() { val jflags = in.nextChar val isAnnotation = hasAnnotation(jflags) - var sflags = toScalaClassFlags(jflags) - var nameIdx = in.nextChar + val sflags = toScalaClassFlags(jflags) + val nameIdx = in.nextChar currentClass = pool.getClassName(nameIdx) /** Parse parents for Java classes. For Scala, return AnyRef, since the real type will be unpickled. @@ -515,9 +514,9 @@ abstract class ClassfileParser { } else raiseLoaderLevel { val superType = if (isAnnotation) { in.nextChar; definitions.AnnotationClass.tpe } - else pool.getSuperClass(in.nextChar).tpe + else pool.getSuperClass(in.nextChar).tpe_* val ifaceCount = in.nextChar - var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(in.nextChar).tpe + var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(in.nextChar).tpe_* if (isAnnotation) ifaces = definitions.ClassfileAnnotationClass.tpe :: ifaces superType :: ifaces } @@ -565,7 +564,7 @@ abstract class ClassfileParser { 0 until in.nextChar foreach (_ => parseMethod()) val needsConstructor = ( !sawPrivateConstructor - && instanceScope.lookup(nme.CONSTRUCTOR) == NoSymbol + && !(instanceScope containsName nme.CONSTRUCTOR) && (sflags & INTERFACE) == 0 ) if (needsConstructor) @@ -599,13 +598,13 @@ abstract class ClassfileParser { def parseField() { val jflags = in.nextChar - var sflags = toScalaFieldFlags(jflags) + val sflags = toScalaFieldFlags(jflags) if ((sflags & PRIVATE) != 0L && !global.settings.optimise.value) { in.skip(4); skipAttributes() } else { val name = pool.getName(in.nextChar) val info = pool.getType(in.nextChar) - val sym = getOwner(jflags).newValue(name, NoPosition, sflags) + val sym = getOwner(jflags).newValue(name.toTermName, NoPosition, sflags) val isEnum = (jflags & JAVA_ACC_ENUM) != 0 sym setInfo { @@ -629,7 +628,7 @@ abstract class ClassfileParser { def parseMethod() { val jflags = in.nextChar.toInt - var sflags = toScalaMethodFlags(jflags) + val sflags = toScalaMethodFlags(jflags) if (isPrivate(jflags) && !global.settings.optimise.value) { val name = pool.getName(in.nextChar) if (name == nme.CONSTRUCTOR) @@ -640,7 +639,7 @@ abstract class ClassfileParser { in.skip(4); skipAttributes() } else { val name = pool.getName(in.nextChar) - val sym = getOwner(jflags).newMethod(name, NoPosition, sflags) + val sym = getOwner(jflags).newMethod(name.toTermName, NoPosition, sflags) var info = pool.getType(sym, (in.nextChar)) if (name == nme.CONSTRUCTOR) info match { @@ -735,18 +734,14 @@ abstract class ClassfileParser { } accept('>') assert(xs.length > 0, tp) - newExistentialType(existentials.toList, typeRef(pre, classSym, xs.toList)) - } else if (classSym.isMonomorphicType) { - tp - } else { - // raw type - existentially quantify all type parameters - val eparams = typeParamsToExistentials(classSym, classSym.unsafeTypeParams) - val t = typeRef(pre, classSym, eparams.map(_.tpeHK)) - val res = newExistentialType(eparams, t) - if (settings.debug.value && settings.verbose.value) - println("raw type " + classSym + " -> " + res) - res + logResult("new existential")(newExistentialType(existentials.toList, typeRef(pre, classSym, xs.toList))) } + // isMonomorphicType is false if the info is incomplete, as it usually is here + // so have to check unsafeTypeParams.isEmpty before worrying about raw type case below, + // or we'll create a boatload of needless existentials. + else if (classSym.isMonomorphicType || classSym.unsafeTypeParams.isEmpty) tp + // raw type - existentially quantify all type parameters + else logResult(s"raw type from $classSym")(definitions.unsafeClassExistentialType(classSym)) case tp => assert(sig.charAt(index) != '<', tp) tp @@ -754,7 +749,7 @@ abstract class ClassfileParser { val classSym = classNameToSymbol(subName(c => c == ';' || c == '<')) assert(!classSym.isOverloaded, classSym.alternatives) - var tpe = processClassType(processInner(classSym.tpe)) + var tpe = processClassType(processInner(classSym.tpe_*)) while (sig.charAt(index) == '.') { accept('.') val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName @@ -787,7 +782,7 @@ abstract class ClassfileParser { index += 1 val restype = if (sym != null && sym.isClassConstructor) { accept('V') - clazz.tpe + clazz.tpe_* } else sig2type(tparams, skiptvs) JavaMethodType(sym.newSyntheticValueParams(paramtypes.toList), restype) @@ -875,7 +870,7 @@ abstract class ClassfileParser { sym.setFlag(SYNTHETIC | ARTIFACT) in.skip(attrLen) case tpnme.BridgeATTR => - sym.setFlag(BRIDGE) + sym.setFlag(BRIDGE | ARTIFACT) in.skip(attrLen) case tpnme.DeprecatedATTR => val arg = Literal(Constant("see corresponding Javadoc for more information.")) @@ -1080,7 +1075,7 @@ abstract class ClassfileParser { def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) { val completer = new global.loaders.ClassfileLoader(file) val name = entry.originalName - var sflags = toScalaClassFlags(jflags) + val sflags = toScalaClassFlags(jflags) val owner = getOwner(jflags) val scope = getScope(jflags) val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer @@ -1168,21 +1163,7 @@ abstract class ClassfileParser { originalName + " in " + outerName + "(" + externalName +")" } - object innerClasses extends scala.collection.mutable.HashMap[Name, InnerClassEntry] { - /** Return the Symbol of the top level class enclosing `name`, - * or 'name's symbol if no entry found for `name`. - */ - def topLevelClass(name: Name): Symbol = { - val tlName = if (isDefinedAt(name)) { - var entry = this(name) - while (isDefinedAt(entry.outerName)) - entry = this(entry.outerName) - entry.outerName - } else - name - classNameToSymbol(tlName) - } - + object innerClasses extends mutable.HashMap[Name, InnerClassEntry] { /** Return the class symbol for `externalName`. It looks it up in its outer class. * Forces all outer class symbols to be completed. * @@ -1207,7 +1188,7 @@ abstract class ClassfileParser { // if loading during initialization of `definitions` typerPhase is not yet set. // in that case we simply load the member at the current phase if (currentRun.typerPhase != null) - beforeTyper(getMember(sym, innerName.toTypeName)) + enteringTyper(getMember(sym, innerName.toTypeName)) else getMember(sym, innerName.toTypeName) @@ -1239,16 +1220,20 @@ abstract class ClassfileParser { } def skipAttributes() { - val attrCount = in.nextChar - for (i <- 0 until attrCount) { - in.skip(2); in.skip(in.nextInt) + var attrCount: Int = in.nextChar + while (attrCount > 0) { + in skip 2 + in skip in.nextInt + attrCount -= 1 } } def skipMembers() { - val memberCount = in.nextChar - for (i <- 0 until memberCount) { - in.skip(6); skipAttributes() + var memberCount: Int = in.nextChar + while (memberCount > 0) { + in skip 6 + skipAttributes() + memberCount -= 1 } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 13c0d8993a..79b08bcabf 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -9,9 +9,7 @@ package classfile import scala.collection.{ mutable, immutable } import mutable.ListBuffer -import backend.icode._ import ClassfileConstants._ -import scala.reflect.internal.Flags._ /** ICode reader from Java bytecode. * @@ -33,7 +31,6 @@ abstract class ICodeReader extends ClassfileParser { * for non-static members. */ def readClass(cls: Symbol): (IClass, IClass) = { - var classFile: io.AbstractFile = null; cls.info // ensure accurate type information isScalaModule = cls.isModule && !cls.isJavaDefined @@ -58,11 +55,9 @@ abstract class ICodeReader extends ClassfileParser { override def parseClass() { this.instanceCode = new IClass(clazz) this.staticCode = new IClass(staticModule) - val jflags = in.nextChar - val isAttribute = (jflags & JAVA_ACC_ANNOTATION) != 0 - val sflags = toScalaClassFlags(jflags) // what, this is never used?? - val c = pool getClassSymbol in.nextChar + in.nextChar + pool getClassSymbol in.nextChar parseInnerClasses() in.skip(2) // super class @@ -85,7 +80,7 @@ abstract class ICodeReader extends ClassfileParser { val jflags = in.nextChar val name = pool getName in.nextChar val owner = getOwner(jflags) - val dummySym = owner.newMethod(name, owner.pos, toScalaMethodFlags(jflags)) + val dummySym = owner.newMethod(name.toTermName, owner.pos, toScalaMethodFlags(jflags)) try { val ch = in.nextChar @@ -99,7 +94,7 @@ abstract class ICodeReader extends ClassfileParser { if (sym == NoSymbol) sym = owner.info.findMember(newTermName(name + nme.LOCAL_SUFFIX_STRING), 0, 0, false).suchThat(_.tpe =:= tpe) if (sym == NoSymbol) { - sym = if (field) owner.newValue(name, owner.pos, toScalaFieldFlags(jflags)) else dummySym + sym = if (field) owner.newValue(name.toTermName, owner.pos, toScalaFieldFlags(jflags)) else dummySym sym setInfoAndEnter tpe log(s"ICodeReader could not locate ${name.decode} in $owner. Created ${sym.defString}.") } @@ -125,7 +120,7 @@ abstract class ICodeReader extends ClassfileParser { override def parseMethod() { val (jflags, sym) = parseMember(false) - var beginning = in.bp + val beginning = in.bp try { if (sym != NoSymbol) { this.method = new IMethod(sym) @@ -170,11 +165,11 @@ abstract class ICodeReader extends ClassfileParser { } else if (nme.isModuleName(name)) { val strippedName = nme.stripModuleSuffix(name) - forceMangledName(newTermName(strippedName.decode), true) orElse rootMirror.getModule(strippedName) + forceMangledName(newTermName(strippedName.decode), true) orElse rootMirror.getModuleByName(strippedName) } else { forceMangledName(name, false) - afterFlatten(rootMirror.getClassByName(name.toTypeName)) + exitingFlatten(rootMirror.getClassByName(name.toTypeName)) } if (sym.isModule) sym.moduleClass @@ -637,9 +632,9 @@ abstract class ICodeReader extends ClassfileParser { else instanceCode class LinearCode { - var instrs: ListBuffer[(Int, Instruction)] = new ListBuffer - var jmpTargets: mutable.Set[Int] = perRunCaches.newSet[Int]() - var locals: mutable.Map[Int, List[(Local, TypeKind)]] = perRunCaches.newMap() + val instrs: ListBuffer[(Int, Instruction)] = new ListBuffer + val jmpTargets: mutable.Set[Int] = perRunCaches.newSet[Int]() + val locals: mutable.Map[Int, List[(Local, TypeKind)]] = perRunCaches.newMap() var containsDUPX = false var containsNEW = false @@ -669,7 +664,6 @@ abstract class ICodeReader extends ClassfileParser { val blocks = makeBasicBlocks var otherBlock: BasicBlock = NoBasicBlock - var disableJmpTarget = false for ((pc, instr) <- instrs.iterator) { // Console.println("> " + pc + ": " + instr); @@ -720,11 +714,9 @@ abstract class ICodeReader extends ClassfileParser { val tfa = new analysis.MethodTFA() { import analysis._ - import analysis.typeFlowLattice.IState /** Abstract interpretation for one instruction. */ override def mutatingInterpret(out: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = { - val bindings = out.vars val stack = out.stack import stack.push i match { @@ -901,7 +893,7 @@ abstract class ICodeReader extends ClassfileParser { for (bb <- method.code.blocks ; (i, idx) <- bb.toList.zipWithIndex) i match { case cm @ CALL_METHOD(m, Static(true)) if m.isClassConstructor => - def loop(bb0: BasicBlock, idx0: Int, depth: Int = 0): Unit = { + def loop(bb0: BasicBlock, idx0: Int, depth: Int): Unit = { rdef.findDefs(bb0, idx0, 1, depth) match { case ((bb1, idx1)) :: _ => bb1(idx1) match { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index e8b0cd2696..c8b7fcee8f 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -26,15 +26,23 @@ import Flags._ abstract class Pickler extends SubComponent { import global._ - private final val showSig = false - val phaseName = "pickler" - currentRun - def newPhase(prev: Phase): StdPhase = new PicklePhase(prev) class PicklePhase(prev: Phase) extends StdPhase(prev) { + override def run() { + super.run() + // This is run here rather than after typer because I found + // some symbols - usually annotations, possibly others - had not + // yet performed the necessary symbol lookup, leading to + // spurious claims of unusedness. + if (settings.lint.value) { + log("Clearing recorded import selectors.") + analyzer.clearUnusedImports() + } + } + def apply(unit: CompilationUnit) { def pickle(tree: Tree) { def add(sym: Symbol, pickle: Pickle) = { @@ -68,17 +76,15 @@ abstract class Pickler extends SubComponent { return } - if (!t.isDef && t.hasSymbol && t.symbol.isTermMacro) { - unit.error(t.pos, t.symbol.typeParams.length match { - case 0 => "macro has not been expanded" - case 1 => "this type parameter must be specified" - case _ => "these type parameters must be specified" - }) + if (!t.isDef && t.hasSymbolField && t.symbol.isTermMacro) { + unit.error(t.pos, "macro has not been expanded") return } } pickle(unit.body) + if (settings.lint.value) + analyzer.warnUnusedImports(unit) } } @@ -138,11 +144,34 @@ abstract class Pickler extends SubComponent { true } + /** If the symbol is a type skolem, deskolemize and log it. + * If we fail to deskolemize, in a method like + * trait Trait[+A] { def f[CC[X]] : CC[A] } + * the applied type CC[A] will hold a different CC symbol + * than the type-constructor type-parameter CC. + */ + private def deskolemize(sym: Symbol) = { + if (sym.isTypeSkolem) { + val sym1 = sym.deSkolemize + log({ + val what0 = sym.defString + val what = sym1.defString match { + case `what0` => what0 + case other => what0 + "->" + other + } + val where = sym.enclMethod.fullLocationString + s"deskolemizing $what in $where" + }) + sym1 + } + else sym + } + /** Store symbol in index. If symbol is local, also store everything it references. - * - * @param sym ... */ - def putSymbol(sym: Symbol) { + def putSymbol(sym0: Symbol) { + val sym = deskolemize(sym0) + if (putEntry(sym)) { if (isLocal(sym)) { putEntry(sym.name) @@ -177,7 +206,7 @@ abstract class Pickler extends SubComponent { */ private def putType(tp: Type): Unit = if (putEntry(tp)) { tp match { - case NoType | NoPrefix /*| DeBruijnIndex(_, _) */ => + case NoType | NoPrefix => ; case ThisType(sym) => putSymbol(sym) @@ -235,7 +264,7 @@ abstract class Pickler extends SubComponent { private def putTree(tree: Tree): Unit = if (putEntry(tree)) { if (tree != EmptyTree) putType(tree.tpe) - if (tree.hasSymbol) + if (tree.hasSymbolField) putSymbol(tree.symbol) tree match { @@ -427,7 +456,7 @@ abstract class Pickler extends SubComponent { * argument of some Annotation */ private def putMods(mods: Modifiers) = if (putEntry(mods)) { // annotations in Modifiers are removed by the typechecker - val Modifiers(flags, privateWithin, Nil) = mods + val Modifiers(_, privateWithin, Nil) = mods putEntry(privateWithin) } @@ -495,7 +524,13 @@ abstract class Pickler extends SubComponent { /** Write a reference to object, i.e., the object's number in the map index. */ - private def writeRef(ref: AnyRef) { writeNat(index(ref)) } + private def writeRef(ref0: AnyRef) { + val ref = ref0 match { + case sym: Symbol => deskolemize(sym) + case _ => ref0 + } + writeNat(index(ref)) + } private def writeRefs(refs: List[AnyRef]) { refs foreach writeRef } private def writeRefsWithLength(refs: List[AnyRef]) { writeNat(refs.length) @@ -568,7 +603,7 @@ abstract class Pickler extends SubComponent { tag case sym: ClassSymbol => writeSymInfo(sym) - if (sym.thisSym.tpe != sym.tpe) writeRef(sym.typeOfThis) + if (sym.thisSym.tpe_* != sym.tpe_*) writeRef(sym.typeOfThis) CLASSsym case sym: TypeSymbol => writeSymInfo(sym) @@ -609,8 +644,6 @@ abstract class Pickler extends SubComponent { writeRef(restpe); writeRefs(tparams); POLYtpe case ExistentialType(tparams, restpe) => writeRef(restpe); writeRefs(tparams); EXISTENTIALtpe - // case DeBruijnIndex(l, i) => - // writeNat(l); writeNat(i); DEBRUIJNINDEXtpe case c @ Constant(_) => if (c.tag == BooleanTag) writeLong(if (c.booleanValue) 1 else 0) else if (ByteTag <= c.tag && c.tag <= LongTag) writeLong(c.longValue) @@ -993,115 +1026,6 @@ abstract class Pickler extends SubComponent { patchNat(startpos + 1, writeIndex - (startpos + 2)) } - /** Print entry for diagnostics */ - def printEntryAtIndex(idx: Int) = printEntry(entries(idx)) - def printEntry(entry: AnyRef) { - def printRef(ref: AnyRef) { - print(index(ref)+ - (if (ref.isInstanceOf[Name]) "("+ref+") " else " ")) - } - def printRefs(refs: List[AnyRef]) { refs foreach printRef } - def printSymInfo(sym: Symbol) { - var posOffset = 0 - printRef(sym.name) - printRef(localizedOwner(sym)) - print(flagsToString(sym.flags & PickledFlags)+" ") - if (sym.hasAccessBoundary) printRef(sym.privateWithin) - printRef(sym.info) - } - def printBody(entry: AnyRef) = entry match { - case name: Name => - print((if (name.isTermName) "TERMname " else "TYPEname ")+name) - case NoSymbol => - print("NONEsym") - case sym: Symbol if !isLocal(sym) => - if (sym.isModuleClass) { - print("EXTMODCLASSref "); printRef(sym.name.toTermName) - } else { - print("EXTref "); printRef(sym.name) - } - if (!sym.owner.isRoot) printRef(sym.owner) - case sym: ClassSymbol => - print("CLASSsym ") - printSymInfo(sym) - if (sym.thisSym.tpe != sym.tpe) printRef(sym.typeOfThis) - case sym: TypeSymbol => - print(if (sym.isAbstractType) "TYPEsym " else "ALIASsym ") - printSymInfo(sym) - case sym: TermSymbol => - print(if (sym.isModule) "MODULEsym " else "VALsym ") - printSymInfo(sym) - if (sym.alias != NoSymbol) printRef(sym.alias) - case NoType => - print("NOtpe") - case NoPrefix => - print("NOPREFIXtpe") - case ThisType(sym) => - print("THIStpe "); printRef(sym) - case SingleType(pre, sym) => - print("SINGLEtpe "); printRef(pre); printRef(sym); - case ConstantType(value) => - print("CONSTANTtpe "); printRef(value); - case TypeRef(pre, sym, args) => - print("TYPEREFtpe "); printRef(pre); printRef(sym); printRefs(args); - case TypeBounds(lo, hi) => - print("TYPEBOUNDStpe "); printRef(lo); printRef(hi); - case tp @ RefinedType(parents, decls) => - print("REFINEDtpe "); printRef(tp.typeSymbol); printRefs(parents); - case ClassInfoType(parents, decls, clazz) => - print("CLASSINFOtpe "); printRef(clazz); printRefs(parents); - case mt @ MethodType(formals, restpe) => - print("METHODtpe"); printRef(restpe); printRefs(formals) - case PolyType(tparams, restpe) => - print("POLYtpe "); printRef(restpe); printRefs(tparams); - case ExistentialType(tparams, restpe) => - print("EXISTENTIALtpe "); printRef(restpe); printRefs(tparams); - print("||| "+entry) - // case DeBruijnIndex(l, i) => - // print("DEBRUIJNINDEXtpe "); print(l+" "+i) - case c @ Constant(_) => - print("LITERAL ") - if (c.tag == BooleanTag) print("Boolean "+(if (c.booleanValue) 1 else 0)) - else if (c.tag == ByteTag) print("Byte "+c.longValue) - else if (c.tag == ShortTag) print("Short "+c.longValue) - else if (c.tag == CharTag) print("Char "+c.longValue) - else if (c.tag == IntTag) print("Int "+c.longValue) - else if (c.tag == LongTag) print("Long "+c.longValue) - 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 == 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) { - print("ANNOTATEDWSELFtpe ") - printRef(tp) - printRef(selfsym) - printRefs(annots) - } else { - print("ANNOTATEDtpe ") - printRef(tp) - printRefs(annots) - } - case (target: Symbol, AnnotationInfo(atp, args, Nil)) => - print("SYMANNOT ") - printRef(target) - printRef(atp) - for (c <- args) printRef(c) - case (target: Symbol, children: List[_]) => - print("CHILDREN ") - printRef(target) - for (c <- children) printRef(c.asInstanceOf[Symbol]) - case AnnotationInfo(atp, args, Nil) => - print("ANNOTINFO") - printRef(atp) - for (c <- args) printRef(c) - case _ => - throw new FatalError("bad entry: " + entry + " " + entry.getClass) - } - printBody(entry); println() - } - /** Write byte array */ def writeArray() { assert(writeIndex == 0) diff --git a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala b/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala deleted file mode 100644 index 40189b9444..0000000000 --- a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala +++ /dev/null @@ -1,137 +0,0 @@ -/* NSC -- new scala compiler - * Copyright 2004-2013 LAMP/EPFL - */ - - -package scala.tools.nsc -package symtab -package clr - -import java.io.File -import java.util.{Comparator, StringTokenizer} -import scala.util.Sorting -import ch.epfl.lamp.compiler.msil._ -import scala.collection.{ mutable, immutable } -import scala.reflect.internal.util.{Position, NoPosition} - -/** - * Collects all types from all reference assemblies. - */ -abstract class CLRTypes { - - val global: Global - import global.Symbol - import global.definitions - - //########################################################################## - - var BYTE: Type = _ - var UBYTE: Type = _ - var SHORT: Type = _ - var USHORT: Type = _ - var CHAR: Type = _ - var INT: Type = _ - var UINT: Type = _ - var LONG: Type = _ - var ULONG: Type = _ - var FLOAT: Type = _ - var DOUBLE: Type = _ - var BOOLEAN: Type = _ - var VOID: Type = _ - var ENUM: Type = _ - var DELEGATE: Type = _ - - var OBJECT: Type = _ - var STRING: Type = _ - var STRING_ARRAY: Type = _ - - var VALUE_TYPE: Type = _ - - var SCALA_SYMTAB_ATTR: Type = _ - var SYMTAB_CONSTR: ConstructorInfo = _ - var SYMTAB_DEFAULT_CONSTR: ConstructorInfo = _ - - var DELEGATE_COMBINE: MethodInfo = _ - var DELEGATE_REMOVE: MethodInfo = _ - - val types: mutable.Map[Symbol,Type] = new mutable.HashMap - val constructors: mutable.Map[Symbol,ConstructorInfo] = new mutable.HashMap - val methods: mutable.Map[Symbol,MethodInfo] = new mutable.HashMap - val fields: mutable.Map[Symbol, FieldInfo] = new mutable.HashMap - val sym2type: mutable.Map[Type,Symbol] = new mutable.HashMap - val addressOfViews = new mutable.HashSet[Symbol] - val mdgptrcls4clssym: mutable.Map[ /*cls*/ Symbol, /*cls*/ Symbol] = new mutable.HashMap - - def isAddressOf(msym : Symbol) = addressOfViews.contains(msym) - - def isNonEnumValuetype(cls: Symbol) = { - val msilTOpt = types.get(cls) - val res = msilTOpt.isDefined && { - val msilT = msilTOpt.get - msilT.IsValueType && !msilT.IsEnum - } - res - } - - def isValueType(cls: Symbol): Boolean = { - val opt = types.get(cls) - opt.isDefined && opt.get.IsValueType - } - - def init() = try { // initialize - // the MsilClasspath (nsc/util/Classpath.scala) initializes the msil-library by calling - // Assembly.LoadFrom("mscorlib.dll"), so this type should be found - Type.initMSCORLIB(getTypeSafe("System.String").Assembly) - - BYTE = getTypeSafe("System.SByte") - UBYTE = getTypeSafe("System.Byte") - CHAR = getTypeSafe("System.Char") - SHORT = getTypeSafe("System.Int16") - USHORT = getTypeSafe("System.UInt16") - INT = getTypeSafe("System.Int32") - UINT = getTypeSafe("System.UInt32") - LONG = getTypeSafe("System.Int64") - ULONG = getTypeSafe("System.UInt64") - FLOAT = getTypeSafe("System.Single") - DOUBLE = getTypeSafe("System.Double") - BOOLEAN = getTypeSafe("System.Boolean") - VOID = getTypeSafe("System.Void") - ENUM = getTypeSafe("System.Enum") - DELEGATE = getTypeSafe("System.MulticastDelegate") - - OBJECT = getTypeSafe("System.Object") - STRING = getTypeSafe("System.String") - STRING_ARRAY = getTypeSafe("System.String[]") - VALUE_TYPE = getTypeSafe("System.ValueType") - - SCALA_SYMTAB_ATTR = getTypeSafe("scala.runtime.SymtabAttribute") - val bytearray: Array[Type] = Array(Type.GetType("System.Byte[]")) - SYMTAB_CONSTR = SCALA_SYMTAB_ATTR.GetConstructor(bytearray) - SYMTAB_DEFAULT_CONSTR = SCALA_SYMTAB_ATTR.GetConstructor(Type.EmptyTypes) - - val delegate: Type = getTypeSafe("System.Delegate") - val dargs: Array[Type] = Array(delegate, delegate) - DELEGATE_COMBINE = delegate.GetMethod("Combine", dargs) - DELEGATE_REMOVE = delegate.GetMethod("Remove", dargs) - } - catch { - case e: RuntimeException => - Console.println(e.getMessage) - throw e - } - - //########################################################################## - // type mapping and lookup - - def getType(name: String): Type = Type.GetType(name) - - def getTypeSafe(name: String): Type = { - val t = Type.GetType(name) - assert(t != null, name) - t - } - - def mkArrayType(elemType: Type): Type = getType(elemType.FullName + "[]") - - def isDelegateType(t: Type): Boolean = { t.BaseType() == DELEGATE } -} // CLRTypes diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala deleted file mode 100644 index 5a0253c18b..0000000000 --- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala +++ /dev/null @@ -1,850 +0,0 @@ -/* NSC -- new scala compiler - * Copyright 2004-2013 LAMP/EPFL - */ - -package scala.tools.nsc -package symtab -package clr - -import java.io.IOException -import io.MsilFile -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 scala.language.implicitConversions - -/** - * @author Nikolay Mihaylov - */ -abstract class TypeParser { - - val global: Global - - import global._ - import loaders.clrTypes - - //########################################################################## - - private var clazz: Symbol = _ - private var instanceDefs: Scope = _ // was members - private var staticModule: Symbol = _ // was staticsClass - private var staticDefs: Scope = _ // was statics - - protected def statics: Symbol = staticModule.moduleClass - - protected var busy: Boolean = false // lock to detect recursive reads - - private object unpickler extends UnPickler { - val global: TypeParser.this.global.type = TypeParser.this.global - } - - def parse(typ: MSILType, root: Symbol) { - - def handleError(e: Throwable) = { - if (settings.debug.value) e.printStackTrace() //debug - throw new IOException("type '" + typ.FullName + "' is broken\n(" + e.getMessage() + ")") - } - assert(!busy) - busy = true - - if (root.isModule) { - this.clazz = root.companionClass - this.staticModule = root - } else { - this.clazz = root - this.staticModule = root.companionModule - } - try { - parseClass(typ) - } catch { - case e: FatalError => handleError(e) - case e: RuntimeException => handleError(e) - } - busy = false - } - - class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter { - override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") } - } - - /* the names `classTParams` and `newTParams` stem from the forJVM version (ClassfileParser.sigToType()) - * but there are differences that should be kept in mind. - * forMSIL, a nested class knows nothing about any type-params in the nesting class, - * therefore newTParams is redundant (other than for recording lexical order), - * it always contains the same elements as classTParams.value */ - val classTParams = scala.collection.mutable.Map[Int,Symbol]() // TODO should this be a stack? (i.e., is it possible for >1 invocation to getCLRType on the same TypeParser instance be active ) - val newTParams = new scala.collection.mutable.ListBuffer[Symbol]() - val methodTParams = scala.collection.mutable.Map[Int,Symbol]() - - private def sig2typeBounds(tvarCILDef: GenericParamAndConstraints): Type = { - val ts = new scala.collection.mutable.ListBuffer[Type] - for (cnstrnt <- tvarCILDef.Constraints) { - ts += getCLRType(cnstrnt) // TODO we're definitely not at or after erasure, no need to call objToAny, right? - } - TypeBounds.upper(intersectionType(ts.toList, clazz)) - // TODO variance??? - } - - private def createViewFromTo(viewSuffix : String, fromTpe : Type, toTpe : Type, - addToboxMethodMap : Boolean, isAddressOf : Boolean) : Symbol = { - val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead? - val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(List(fromTpe)), toTpe) - val vmsym = createMethod(nme.view_ + viewSuffix, flags, viewMethodType, null, true); - // !!! this used to mutate a mutable map in definitions, but that map became - // immutable and this kept "working" with a no-op. So now it's commented out - // since I retired the deprecated code which allowed for that bug. - // - // if (addToboxMethodMap) definitions.boxMethod(clazz) = vmsym - - if (isAddressOf) clrTypes.addressOfViews += vmsym - vmsym - } - - private def createDefaultConstructor(typ: MSILType) { - val attrs = MethodAttributes.Public | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName // TODO instance - val declType= typ - val method = new ConstructorInfo(declType, attrs, Array[MSILType]()) - val flags = Flags.JAVA - val owner = clazz - val methodSym = owner.newMethod(nme.CONSTRUCTOR, NoPosition, flags) - val rettype = clazz.tpe - val mtype = methodType(Array[MSILType](), rettype); - val mInfo = mtype(methodSym) - methodSym.setInfo(mInfo) - instanceDefs.enter(methodSym); - clrTypes.constructors(methodSym) = method - } - - private def parseClass(typ: MSILType) { - - { - val t4c = clrTypes.types.get(clazz) - assert(t4c == None || t4c == Some(typ)) - } - clrTypes.types(clazz) = typ - - { - val c4t = clrTypes.sym2type.get(typ) - assert(c4t == None || c4t == Some(clazz)) - } - clrTypes.sym2type(typ) = clazz - - if (typ.IsDefined(clrTypes.SCALA_SYMTAB_ATTR, false)) { - val attrs = typ.GetCustomAttributes(clrTypes.SCALA_SYMTAB_ATTR, false); - assert (attrs.length == 1, attrs.length); - val a = attrs(0).asInstanceOf[MSILAttribute]; - assert (a.getConstructor() == clrTypes.SYMTAB_CONSTR); - val symtab = a.getConstructorArguments()(0).asInstanceOf[Array[Byte]] - unpickler.unpickle(symtab, 0, clazz, staticModule, typ.FullName); - val mClass = clrTypes.getType(typ.FullName + "$"); - if (mClass != null) { - clrTypes.types(statics) = mClass; - val moduleInstance = mClass.GetField("MODULE$"); - assert (moduleInstance != null, mClass); - clrTypes.fields(statics) = moduleInstance; - } - return - } - val flags = translateAttributes(typ) - - var clazzBoxed : Symbol = NoSymbol - var clazzMgdPtr : Symbol = NoSymbol - - val canBeTakenAddressOf = (typ.IsValueType || typ.IsEnum) && (typ.FullName != "System.Enum") - - if(canBeTakenAddressOf) { - clazzBoxed = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("Boxed")) - clazzMgdPtr = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("MgdPtr")) - clrTypes.mdgptrcls4clssym(clazz) = clazzMgdPtr - /* adding typMgdPtr to clrTypes.sym2type should happen early (before metadata for supertypes is parsed, - before metadata for members are parsed) so that clazzMgdPtr can be found by getClRType. */ - val typMgdPtr = MSILType.mkByRef(typ) - clrTypes.types(clazzMgdPtr) = typMgdPtr - clrTypes.sym2type(typMgdPtr) = clazzMgdPtr - /* clazzMgdPtr but not clazzBoxed is mapped by clrTypes.types into an msil.Type instance, - because there's no metadata-level representation for a "boxed valuetype" */ - val instanceDefsMgdPtr = newScope - val classInfoMgdPtr = ClassInfoType(definitions.anyvalparam, instanceDefsMgdPtr, clazzMgdPtr) - clazzMgdPtr.setFlag(flags) - clazzMgdPtr.setInfo(classInfoMgdPtr) - } - -/* START CLR generics (snippet 1) */ - // first pass - for (tvarCILDef <- typ.getSortedTVars() ) { - val tpname = newTypeName(tvarCILDef.Name.replaceAll("!", "")) // TODO are really all type-params named in all assemblies out there? (NO) - val tpsym = clazz.newTypeParameter(tpname) - classTParams.put(tvarCILDef.Number, tpsym) - newTParams += tpsym - // TODO wouldn't the following also be needed later, i.e. during getCLRType - tpsym.setInfo(definitions.AnyClass.tpe) - } - // second pass - for (tvarCILDef <- typ.getSortedTVars() ) { - val tpsym = classTParams(tvarCILDef.Number) - tpsym.setInfo(sig2typeBounds(tvarCILDef)) // we never skip bounds unlike in forJVM - } -/* END CLR generics (snippet 1) */ - val ownTypeParams = newTParams.toList -/* START CLR generics (snippet 2) */ - if (!ownTypeParams.isEmpty) { - clazz.setInfo(new TypeParamsType(ownTypeParams)) - if(typ.IsValueType && !typ.IsEnum) { - clazzBoxed.setInfo(new TypeParamsType(ownTypeParams)) - } - } -/* END CLR generics (snippet 2) */ - instanceDefs = newScope - staticDefs = newScope - - val classInfoAsInMetadata = { - val ifaces: Array[MSILType] = typ.getInterfaces() - val superType = if (typ.BaseType() != null) getCLRType(typ.BaseType()) - else if (typ.IsInterface()) definitions.ObjectClass.tpe - else definitions.AnyClass.tpe; // this branch activates for System.Object only. - // parents (i.e., base type and interfaces) - val parents = new scala.collection.mutable.ListBuffer[Type]() - parents += superType - for (iface <- ifaces) { - parents += getCLRType(iface) // here the variance doesn't matter - } - // methods, properties, events, fields are entered in a moment - if (canBeTakenAddressOf) { - val instanceDefsBoxed = newScope - ClassInfoType(parents.toList, instanceDefsBoxed, clazzBoxed) - } else - ClassInfoType(parents.toList, instanceDefs, clazz) - } - - val staticInfo = ClassInfoType(List(), staticDefs, statics) - - clazz.setFlag(flags) - - if (canBeTakenAddressOf) { - clazzBoxed.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata - else genPolyType(ownTypeParams, classInfoAsInMetadata) ) - clazzBoxed.setFlag(flags) - val rawValueInfoType = ClassInfoType(definitions.anyvalparam, instanceDefs, clazz) - clazz.setInfo( if (ownTypeParams.isEmpty) rawValueInfoType - else genPolyType(ownTypeParams, rawValueInfoType) ) - } else { - clazz.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata - else genPolyType(ownTypeParams, classInfoAsInMetadata) ) - } - - // TODO I don't remember if statics.setInfo and staticModule.setInfo should also know about type params - statics.setFlag(Flags.JAVA) - statics.setInfo(staticInfo) - staticModule.setFlag(Flags.JAVA) - staticModule.setInfo(statics.tpe) - - - if (canBeTakenAddressOf) { - // implicit conversions are owned by staticModule.moduleClass - createViewFromTo("2Boxed", clazz.tpe, clazzBoxed.tpe, addToboxMethodMap = true, isAddressOf = false) - // createViewFromTo("2Object", clazz.tpe, definitions.ObjectClass.tpe, addToboxMethodMap = true, isAddressOf = false) - createViewFromTo("2MgdPtr", clazz.tpe, clazzMgdPtr.tpe, addToboxMethodMap = false, isAddressOf = true) - // a return can't have type managed-pointer, thus a dereference-conversion is not needed - // similarly, a method can't declare as return type "boxed valuetype" - if (!typ.IsEnum) { - // a synthetic default constructor for raw-type allows `new X' syntax - createDefaultConstructor(typ) - } - } - - // import nested types - for (ntype <- typ.getNestedTypes() if !(ntype.IsNestedPrivate || ntype.IsNestedAssembly || ntype.IsNestedFamANDAssem) - || ntype.IsInterface /* TODO why shouldn't nested ifaces be type-parsed too? */ ) - { - val loader = new loaders.MsilFileLoader(new MsilFile(ntype)) - val nclazz = statics.newClass(ntype.Name) - val nmodule = statics.newModule(ntype.Name) - nclazz.setInfo(loader) - nmodule.setInfo(loader) - staticDefs.enter(nclazz) - staticDefs.enter(nmodule) - - assert(nclazz.companionModule == nmodule, nmodule) - assert(nmodule.companionClass == nclazz, nclazz) - } - - val fields = typ.getFields() - for (field <- fields - if !(field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly) - if (getCLRType(field.FieldType) != null) - ) { - assert (!field.FieldType.IsPointer && !field.FieldType.IsByRef, "CLR requirement") - val flags = translateAttributes(field); - val name = newTermName(field.Name); - val fieldType = - if (field.IsLiteral && !field.FieldType.IsEnum && isDefinedAtgetConstant(getCLRType(field.FieldType))) - ConstantType(getConstant(getCLRType(field.FieldType), field.getValue)) - else - getCLRType(field.FieldType) - val owner = if (field.IsStatic()) statics else clazz; - val sym = owner.newValue(name, NoPosition, flags).setInfo(fieldType); - // TODO: set private within!!! -> look at typechecker/Namers.scala - (if (field.IsStatic()) staticDefs else instanceDefs).enter(sym); - clrTypes.fields(sym) = field; - } - - for (constr <- typ.getConstructors() if !constr.IsStatic() && !constr.IsPrivate() && - !constr.IsAssembly() && !constr.IsFamilyAndAssembly() && !constr.HasPtrParamOrRetType()) - createMethod(constr); - - // initially also contains getters and setters of properties. - val methodsSet = new mutable.HashSet[MethodInfo](); - methodsSet ++= typ.getMethods(); - - for (prop <- typ.getProperties) { - val propType: Type = getCLSType(prop.PropertyType); - if (propType != null) { - val getter: MethodInfo = prop.GetGetMethod(true); - val setter: MethodInfo = prop.GetSetMethod(true); - var gparamsLength: Int = -1; - if (!(getter == null || getter.IsPrivate || getter.IsAssembly - || getter.IsFamilyAndAssembly || getter.HasPtrParamOrRetType)) - { - assert(prop.PropertyType == getter.ReturnType); - val gparams: Array[ParameterInfo] = getter.GetParameters(); - gparamsLength = gparams.length; - val name: TermName = if (gparamsLength == 0) prop.Name else nme.apply; - val flags = translateAttributes(getter); - val owner: Symbol = if (getter.IsStatic) statics else clazz; - val methodSym = owner.newMethod(name, NoPosition, flags) - val mtype: Type = if (gparamsLength == 0) NullaryMethodType(propType) // .NET properties can't be polymorphic - else methodType(getter, getter.ReturnType)(methodSym) - methodSym.setInfo(mtype); - methodSym.setFlag(Flags.ACCESSOR); - (if (getter.IsStatic) staticDefs else instanceDefs).enter(methodSym) - clrTypes.methods(methodSym) = getter; - methodsSet -= getter; - } - if (!(setter == null || setter.IsPrivate || setter.IsAssembly - || setter.IsFamilyAndAssembly || setter.HasPtrParamOrRetType)) - { - val sparams: Array[ParameterInfo] = setter.GetParameters() - if(getter != null) - assert(getter.IsStatic == setter.IsStatic); - assert(setter.ReturnType == clrTypes.VOID); - if(getter != null) - assert(sparams.length == gparamsLength + 1, "" + getter + "; " + setter); - - val name: TermName = if (gparamsLength == 0) nme.getterToSetter(prop.Name) - else nme.update; - val flags = translateAttributes(setter); - val mtype = methodType(setter, definitions.UnitClass.tpe); - val owner: Symbol = if (setter.IsStatic) statics else clazz; - val methodSym = owner.newMethod(name, NoPosition, flags) - methodSym.setInfo(mtype(methodSym)) - methodSym.setFlag(Flags.ACCESSOR); - (if (setter.IsStatic) staticDefs else instanceDefs).enter(methodSym); - clrTypes.methods(methodSym) = setter; - methodsSet -= setter; - } - } - } - -/* for (event <- typ.GetEvents) { - // adding += and -= methods to add delegates to an event. - // raising the event ist not possible from outside the class (this is so - // generally in .net world) - val adder: MethodInfo = event.GetAddMethod(); - val remover: MethodInfo = event.GetRemoveMethod(); - if (!(adder == null || adder.IsPrivate || adder.IsAssembly - || adder.IsFamilyAndAssembly)) - { - assert(adder.ReturnType == clrTypes.VOID); - assert(adder.GetParameters().map(_.ParameterType).toList == List(event.EventHandlerType)); - val name = encode("+="); - val flags = translateAttributes(adder); - val mtype: Type = methodType(adder, adder.ReturnType); - createMethod(name, flags, mtype, adder, adder.IsStatic) - methodsSet -= adder; - } - if (!(remover == null || remover.IsPrivate || remover.IsAssembly - || remover.IsFamilyAndAssembly)) - { - assert(remover.ReturnType == clrTypes.VOID); - assert(remover.GetParameters().map(_.ParameterType).toList == List(event.EventHandlerType)); - val name = encode("-="); - val flags = translateAttributes(remover); - val mtype: Type = methodType(remover, remover.ReturnType); - createMethod(name, flags, mtype, remover, remover.IsStatic) - methodsSet -= remover; - } - } */ - -/* Adds view amounting to syntax sugar for a CLR implicit overload. - The long-form syntax can also be supported if "methodsSet -= method" (last statement) is removed. - - /* remember, there's typ.getMethods and type.GetMethods */ - for (method <- typ.getMethods) - if(!method.HasPtrParamOrRetType && - method.IsPublic && method.IsStatic && method.IsSpecialName && - method.Name == "op_Implicit") { - // create a view: typ => method's return type - val viewRetType: Type = getCLRType(method.ReturnType) - val viewParamTypes: List[Type] = method.GetParameters().map(_.ParameterType).map(getCLSType).toList; - /* The spec says "The operator method shall be defined as a static method on either the operand or return type." - * We don't consider the declaring type for the purposes of definitions.functionType, - * instead we regard op_Implicit's argument type and return type as defining the view's signature. - */ - if (viewRetType != null && !viewParamTypes.contains(null)) { - /* The check above applies e.g. to System.Decimal that has a conversion from UInt16, a non-CLS type, whose CLS-mapping returns null */ - val funType: Type = definitions.functionType(viewParamTypes, viewRetType); - val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead? - val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(viewParamTypes), funType) - val vmsym = createMethod(nme.view_, flags, viewMethodType, method, true); - methodsSet -= method; - } - } -*/ - - for (method <- methodsSet.iterator) - if (!method.IsPrivate() && !method.IsAssembly() && !method.IsFamilyAndAssembly() - && !method.HasPtrParamOrRetType) - createMethod(method); - - // Create methods and views for delegate support - if (clrTypes.isDelegateType(typ)) { - createDelegateView(typ) - createDelegateChainers(typ) - } - - // for enumerations introduce comparison and bitwise logical operations; - // the backend will recognize them and replace them with comparison or - // bitwise logical operations on the primitive underlying type - - if (typ.IsEnum) { - val ENUM_CMP_NAMES = List(nme.EQ, nme.NE, nme.LT, nme.LE, nme.GT, nme.GE); - val ENUM_BIT_LOG_NAMES = List(nme.OR, nme.AND, nme.XOR); - - val flags = Flags.JAVA | Flags.FINAL - for (cmpName <- ENUM_CMP_NAMES) { - val enumCmp = clazz.newMethod(cmpName) - val enumCmpType = JavaMethodType(enumCmp.newSyntheticValueParams(List(clazz.tpe)), definitions.BooleanClass.tpe) - enumCmp.setFlag(flags).setInfo(enumCmpType) - instanceDefs.enter(enumCmp) - } - - for (bitLogName <- ENUM_BIT_LOG_NAMES) { - val enumBitLog = clazz.newMethod(bitLogName) - val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), clazz.tpe /* was classInfo, infinite typer */) - enumBitLog.setFlag(flags).setInfo(enumBitLogType) - instanceDefs.enter(enumBitLog) - } - } - - } // parseClass - - private def populateMethodTParams(method: MethodBase, methodSym: MethodSymbol) : List[Symbol] = { - if(!method.IsGeneric) Nil - else { - methodTParams.clear - val newMethodTParams = new scala.collection.mutable.ListBuffer[Symbol]() - - // first pass - for (mvarCILDef <- method.getSortedMVars() ) { - val mtpname = newTypeName(mvarCILDef.Name.replaceAll("!", "")) // TODO are really all method-level-type-params named in all assemblies out there? (NO) - val mtpsym = methodSym.newTypeParameter(mtpname) - methodTParams.put(mvarCILDef.Number, mtpsym) - newMethodTParams += mtpsym - // TODO wouldn't the following also be needed later, i.e. during getCLRType - mtpsym.setInfo(definitions.AnyClass.tpe) - } - // second pass - for (mvarCILDef <- method.getSortedMVars() ) { - val mtpsym = methodTParams(mvarCILDef.Number) - mtpsym.setInfo(sig2typeBounds(mvarCILDef)) // we never skip bounds unlike in forJVM - } - - newMethodTParams.toList - } - } - - private def createMethod(method: MethodBase) { - - val flags = translateAttributes(method); - val owner = if (method.IsStatic()) statics else clazz; - val methodSym = owner.newMethod(getName(method), NoPosition, flags) - /* START CLR generics (snippet 3) */ - val newMethodTParams = populateMethodTParams(method, methodSym) - /* END CLR generics (snippet 3) */ - - val rettype = if (method.IsConstructor()) clazz.tpe - else getCLSType(method.asInstanceOf[MethodInfo].ReturnType); - if (rettype == null) return; - val mtype = methodType(method, rettype); - if (mtype == null) return; -/* START CLR generics (snippet 4) */ - val mInfo = if (method.IsGeneric) genPolyType(newMethodTParams, mtype(methodSym)) - else mtype(methodSym) -/* END CLR generics (snippet 4) */ -/* START CLR non-generics (snippet 4) - val mInfo = mtype(methodSym) - END CLR non-generics (snippet 4) */ - methodSym.setInfo(mInfo) - (if (method.IsStatic()) staticDefs else instanceDefs).enter(methodSym); - if (method.IsConstructor()) - clrTypes.constructors(methodSym) = method.asInstanceOf[ConstructorInfo] - else clrTypes.methods(methodSym) = method.asInstanceOf[MethodInfo]; - } - - private def createMethod(name: TermName, flags: Long, args: Array[MSILType], retType: MSILType, method: MethodInfo, statik: Boolean): Symbol = { - val mtype = methodType(args, getCLSType(retType)) - assert(mtype != null) - createMethod(name, flags, mtype, method, statik) - } - - private def createMethod(name: TermName, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = { - val methodSym: Symbol = (if (statik) statics else clazz).newMethod(name) - methodSym.setFlag(flags).setInfo(mtype(methodSym)) - (if (statik) staticDefs else instanceDefs).enter(methodSym) - if (method != null) - clrTypes.methods(methodSym) = method - methodSym - } - - private def createDelegateView(typ: MSILType) = { - val invoke: MethodInfo = typ.GetMember("Invoke")(0).asInstanceOf[MethodInfo]; - val invokeRetType: Type = getCLRType(invoke.ReturnType); - val invokeParamTypes: List[Type] =invoke.GetParameters().map(_.ParameterType).map(getCLSType).toList; - val funType: Type = definitions.functionType(invokeParamTypes, invokeRetType); - - val typClrType: Type = getCLRType(typ); - val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? think not needed - - // create the forward view: delegate => function - val delegateParamTypes: List[Type] = List(typClrType); - // not ImplicitMethodType, this is for methods with implicit parameters (not implicit methods) - val forwardViewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(delegateParamTypes), funType) - val fmsym = createMethod(nme.view_, flags, forwardViewMethodType, null, true); - - // create the backward view: function => delegate - val functionParamTypes: List[Type] = List(funType); - val backwardViewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(functionParamTypes), typClrType) - val bmsym = createMethod(nme.view_, flags, backwardViewMethodType, null, true); - } - - private def createDelegateChainers(typ: MSILType) = { - val flags: Long = Flags.JAVA | Flags.FINAL - val args: Array[MSILType] = Array(typ) - - var s = createMethod(encode("+="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_COMBINE, false); - s = createMethod(encode("-="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_REMOVE, false); - - s = createMethod(nme.PLUS, flags, args, typ, clrTypes.DELEGATE_COMBINE, false); - s = createMethod(nme.MINUS, flags, args, typ, clrTypes.DELEGATE_REMOVE, false); - } - - private def getName(method: MethodBase): TermName = { - - def operatorOverload(name : String, paramsArity : Int) : Option[Name] = paramsArity match { - case 1 => name match { - // PartitionI.10.3.1 - case "op_Decrement" => Some(encode("--")) - case "op_Increment" => Some(encode("++")) - case "op_UnaryNegation" => Some(nme.UNARY_-) - case "op_UnaryPlus" => Some(nme.UNARY_+) - case "op_LogicalNot" => Some(nme.UNARY_!) - case "op_OnesComplement" => Some(nme.UNARY_~) - /* op_True and op_False have no operator symbol assigned, - Other methods that will have to be written in full are: - op_AddressOf & (unary) - op_PointerDereference * (unary) */ - case _ => None - } - case 2 => name match { - // PartitionI.10.3.2 - case "op_Addition" => Some(nme.ADD) - case "op_Subtraction" => Some(nme.SUB) - case "op_Multiply" => Some(nme.MUL) - case "op_Division" => Some(nme.DIV) - case "op_Modulus" => Some(nme.MOD) - case "op_ExclusiveOr" => Some(nme.XOR) - case "op_BitwiseAnd" => Some(nme.AND) - case "op_BitwiseOr" => Some(nme.OR) - case "op_LogicalAnd" => Some(nme.ZAND) - case "op_LogicalOr" => Some(nme.ZOR) - case "op_LeftShift" => Some(nme.LSL) - case "op_RightShift" => Some(nme.ASR) - case "op_Equality" => Some(nme.EQ) - case "op_GreaterThan" => Some(nme.GT) - case "op_LessThan" => Some(nme.LT) - case "op_Inequality" => Some(nme.NE) - case "op_GreaterThanOrEqual" => Some(nme.GE) - case "op_LessThanOrEqual" => Some(nme.LE) - - /* op_MemberSelection is reserved in Scala */ - - /* The standard does not assign operator symbols to op_Assign , op_SignedRightShift , op_UnsignedRightShift , - * and op_UnsignedRightShiftAssignment so those names will be used instead to invoke those methods. */ - - /* - The remaining binary operators are not overloaded in C# and are therefore not in widespread use. They have to be written in full. - - op_RightShiftAssignment >>= - op_MultiplicationAssignment *= - op_PointerToMemberSelection ->* - op_SubtractionAssignment -= - op_ExclusiveOrAssignment ^= - op_LeftShiftAssignment <<= - op_ModulusAssignment %= - op_AdditionAssignment += - op_BitwiseAndAssignment &= - op_BitwiseOrAssignment |= - op_Comma , - op_DivisionAssignment /= - */ - case _ => None - } - case _ => None - } - - if (method.IsConstructor()) return nme.CONSTRUCTOR; - val name = method.Name; - if (method.IsStatic()) { - if(method.IsSpecialName) { - val paramsArity = method.GetParameters().size - // handle operator overload, otherwise handle as any static method - val operName = operatorOverload(name, paramsArity) - if (operName.isDefined) { return operName.get; } - } - return newTermName(name); - } - val params = method.GetParameters(); - name match { - case "GetHashCode" if (params.length == 0) => nme.hashCode_; - case "ToString" if (params.length == 0) => nme.toString_; - case "Finalize" if (params.length == 0) => nme.finalize_; - case "Equals" if (params.length == 1 && params(0).ParameterType == clrTypes.OBJECT) => - nme.equals_; - case "Invoke" if (clrTypes.isDelegateType(method.DeclaringType)) => nme.apply; - case _ => newTermName(name); - } - } - - //########################################################################## - - private def methodType(method: MethodBase, rettype: MSILType): Symbol => Type = { - val rtype = getCLSType(rettype); - if (rtype == null) null else methodType(method, rtype); - } - - /** Return a method type for the given method. */ - private def methodType(method: MethodBase, rettype: Type): Symbol => Type = - methodType(method.GetParameters().map(_.ParameterType), rettype); - - /** Return a method type for the provided argument types and return type. */ - private def methodType(argtypes: Array[MSILType], rettype: Type): Symbol => Type = { - def paramType(typ: MSILType): Type = - if (typ eq clrTypes.OBJECT) definitions.AnyClass.tpe // TODO a hack to compile scalalib, should be definitions.AnyRefClass.tpe - else getCLSType(typ); - val ptypes = argtypes.map(paramType).toList; - if (ptypes.contains(null)) null - else method => JavaMethodType(method.newSyntheticValueParams(ptypes), rettype); - } - - //########################################################################## - - private def getClassType(typ: MSILType): Type = { - assert(typ != null); - val res = rootMirror.getClassByName(typ.FullName.replace('+', '.') : TypeName).tpe; - //if (res.isError()) - // global.reporter.error("unknown class reference " + type.FullName); - res - } - - private def getCLSType(typ: MSILType): Type = { // getCLS returns non-null for types GenMSIL can handle, be they CLS-compliant or not - if (typ.IsTMVarUsage()) - /* START CLR generics (snippet 5) */ - getCLRType(typ) - /* END CLR generics (snippet 5) */ - /* START CLR non-generics (snippet 5) - null - END CLR non-generics (snippet 5) */ - else if ( /* TODO hack if UBYE, uncommented, "ambiguous reference to overloaded definition" ensues, for example for System.Math.Max(x, y) */ - typ == clrTypes.USHORT || typ == clrTypes.UINT || typ == clrTypes.ULONG - /* || typ == clrTypes.UBYTE */ - || typ.IsNotPublic() || typ.IsNestedPrivate() - || typ.IsNestedAssembly() || typ.IsNestedFamANDAssem() - || typ.IsPointer() - || (typ.IsArray() && getCLRType(typ.GetElementType()) == null) /* TODO hack: getCLR instead of getCLS */ - || (typ.IsByRef() && !typ.GetElementType().CanBeTakenAddressOf())) - null - else - getCLRType(typ) - } - - private def getCLRTypeIfPrimitiveNullOtherwise(typ: MSILType): Type = - if (typ == clrTypes.OBJECT) - definitions.ObjectClass.tpe; - else if (typ == clrTypes.VALUE_TYPE) - definitions.AnyValClass.tpe - else if (typ == clrTypes.STRING) - definitions.StringClass.tpe; - else if (typ == clrTypes.VOID) - definitions.UnitClass.tpe - else if (typ == clrTypes.BOOLEAN) - definitions.BooleanClass.tpe - else if (typ == clrTypes.CHAR) - definitions.CharClass.tpe - else if ((typ == clrTypes.BYTE) || (typ == clrTypes.UBYTE)) // TODO U... is a hack to compile scalalib - definitions.ByteClass.tpe - else if ((typ == clrTypes.SHORT) || (typ == clrTypes.SHORT)) // TODO U... is a hack to compile scalalib - definitions.ShortClass.tpe - else if ((typ == clrTypes.INT) || (typ == clrTypes.UINT)) // TODO U... is a hack to compile scalalib - definitions.IntClass.tpe - else if ((typ == clrTypes.LONG) || (typ == clrTypes.LONG)) // TODO U... is a hack to compile scalalib - definitions.LongClass.tpe - else if (typ == clrTypes.FLOAT) - definitions.FloatClass.tpe - else if (typ == clrTypes.DOUBLE) - definitions.DoubleClass.tpe - else null - - - private def getCLRType(tMSIL: MSILType): Type = { - var res = getCLRTypeIfPrimitiveNullOtherwise(tMSIL) - if (res != null) res - else if (tMSIL.isInstanceOf[ConstructedType]) { - val ct = tMSIL.asInstanceOf[ConstructedType] - /* START CLR generics (snippet 6) */ - val cttpArgs = ct.typeArgs.map(tmsil => getCLRType(tmsil)).toList - appliedType(getCLRType(ct.instantiatedType), cttpArgs) - /* END CLR generics (snippet 6) */ - /* START CLR non-generics (snippet 6) - getCLRType(ct.instantiatedType) - END CLR non-generics (snippet 6) */ - } else if (tMSIL.isInstanceOf[TMVarUsage]) { - /* START CLR generics (snippet 7) */ - val tVarUsage = tMSIL.asInstanceOf[TMVarUsage] - val tVarNumber = tVarUsage.Number - if (tVarUsage.isTVar) classTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst - else methodTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst - /* END CLR generics (snippet 7) */ - /* START CLR non-generics (snippet 7) - null // definitions.ObjectClass.tpe - END CLR non-generics (snippet 7) */ - } else if (tMSIL.IsArray()) { - var elemtp = getCLRType(tMSIL.GetElementType()) - // cut&pasted from ClassfileParser - // make unbounded Array[T] where T is a type variable into Array[T with Object] - // (this is necessary because such arrays have a representation which is incompatible - // with arrays of primitive types). - // TODO does that incompatibility also apply to .NET? - if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< definitions.ObjectClass.tpe)) - elemtp = intersectionType(List(elemtp, definitions.ObjectClass.tpe)) - appliedType(definitions.ArrayClass.tpe, List(elemtp)) - } else { - res = clrTypes.sym2type.get(tMSIL) match { - case Some(sym) => sym.tpe - case None => if (tMSIL.IsByRef && tMSIL.GetElementType.IsValueType) { - val addressed = getCLRType(tMSIL.GetElementType) - val clasym = addressed.typeSymbolDirect // TODO should be .typeSymbol? - clasym.info.load(clasym) - val secondAttempt = clrTypes.sym2type.get(tMSIL) - secondAttempt match { case Some(sym) => sym.tpe - case None => null - } - } else getClassType(tMSIL) - } - if (res == null) - null // TODO new RuntimeException() - else res - } - } - - // the values are Java-Box-Classes (e.g. Integer, Boolean, Character) - // java.lang.Number to get the value (if a number, not for boolean, character) - // see ch.epfl.lamp.compiler.msil.util.PEStream.java - def getConstant(constType: Type, value: Object): Constant = { - val typeClass = constType.typeSymbol - if (typeClass == definitions.BooleanClass) - Constant(value.asInstanceOf[java.lang.Boolean].booleanValue) - else if (typeClass == definitions.ByteClass) - Constant(value.asInstanceOf[java.lang.Number].byteValue) - else if (typeClass == definitions.ShortClass) - Constant(value.asInstanceOf[java.lang.Number].shortValue) - else if (typeClass == definitions.CharClass) - Constant(value.asInstanceOf[java.lang.Character].charValue) - else if (typeClass == definitions.IntClass) - Constant(value.asInstanceOf[java.lang.Number].intValue) - else if (typeClass == definitions.LongClass) - Constant(value.asInstanceOf[java.lang.Number].longValue) - else if (typeClass == definitions.FloatClass) - Constant(value.asInstanceOf[java.lang.Number].floatValue) - else if (typeClass == definitions.DoubleClass) - Constant(value.asInstanceOf[java.lang.Number].doubleValue) - else if (typeClass == definitions.StringClass) - Constant(value.asInstanceOf[java.lang.String]) - else - abort("illegal value: " + value + ", class-symbol: " + typeClass) - } - - def isDefinedAtgetConstant(constType: Type): Boolean = { - val typeClass = constType.typeSymbol - if ( (typeClass == definitions.BooleanClass) - || (typeClass == definitions.ByteClass) - || (typeClass == definitions.ShortClass) - || (typeClass == definitions.CharClass) - || (typeClass == definitions.IntClass) - || (typeClass == definitions.LongClass) - || (typeClass == definitions.FloatClass) - || (typeClass == definitions.DoubleClass) - || (typeClass == definitions.StringClass) - ) - true - else - false - } - - private def translateAttributes(typ: MSILType): Long = { - var flags: Long = Flags.JAVA; - if (typ.IsNotPublic() || typ.IsNestedPrivate() - || typ.IsNestedAssembly() || typ.IsNestedFamANDAssem()) - flags = flags | Flags.PRIVATE; - else if (typ.IsNestedFamily() || typ.IsNestedFamORAssem()) - flags = flags | Flags.PROTECTED; - if (typ.IsAbstract()) - flags = flags | Flags.ABSTRACT; - if (typ.IsSealed()) - flags = flags | Flags.FINAL; - if (typ.IsInterface()) - flags = flags | Flags.INTERFACE | Flags.TRAIT | Flags.ABSTRACT; - - flags - } - - private def translateAttributes(field: FieldInfo): Long = { - var flags: Long = Flags.JAVA; - if (field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly()) - flags = flags | Flags.PRIVATE; - else if (field.IsFamily() || field.IsFamilyOrAssembly()) - flags = flags | Flags.PROTECTED; - if (field.IsInitOnly() || field.IsLiteral()) - flags = flags | Flags.FINAL; - else - flags = flags | Flags.MUTABLE; - if (field.IsStatic) - flags = flags | Flags.STATIC - - flags - } - - private def translateAttributes(method: MethodBase): Long = { - var flags: Long = Flags.JAVA; - if (method.IsPrivate() || method.IsAssembly() || method.IsFamilyAndAssembly()) - flags = flags | Flags.PRIVATE; - else if (method.IsFamily() || method.IsFamilyOrAssembly()) - flags = flags | Flags.PROTECTED; - if (method.IsAbstract()) - flags = flags | Flags.DEFERRED; - if (method.IsStatic) - flags = flags | Flags.STATIC - - flags - } -} diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index bacd8c39e1..5fbc15f858 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -8,8 +8,6 @@ package transform import symtab._ import Flags._ -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.ListBuffer abstract class AddInterfaces extends InfoTransform { self: Erasure => import global._ // the global environment @@ -94,7 +92,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => impl.typeOfThis = iface.typeOfThis impl.thisSym setName iface.thisSym.name } - impl.sourceFile = iface.sourceFile + impl.associatedFile = iface.sourceFile if (inClass) iface.owner.info.decls enter impl @@ -111,7 +109,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => def implClass(iface: Symbol): Symbol = { iface.info - implClassMap.getOrElse(iface, atPhase(implClassPhase) { + implClassMap.getOrElse(iface, enteringPhase(implClassPhase) { if (iface.implClass eq NoSymbol) debuglog(s"${iface.fullLocationString} has no implClass yet, creating it now.") else @@ -196,7 +194,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => case PolyType(_, restpe) => implType(restpe) } - implSym setInfo implType(beforeErasure(iface.info)) + implSym setInfo implType(enteringErasure(iface.info)) } override def load(clazz: Symbol) { complete(clazz) } @@ -317,10 +315,10 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => // body until now, because the typer knows that Any has no // constructor and won't accept a call to super.init. assert((clazz isSubClass AnyValClass) || clazz.info.parents.isEmpty, clazz) - Block(List(Apply(gen.mkSuperSelect, Nil)), expr) + Block(List(Apply(gen.mkSuperInitCall, Nil)), expr) case Block(stats, expr) => - // needs `hasSymbol` check because `supercall` could be a block (named / default args) + // needs `hasSymbolField` check because `supercall` could be a block (named / default args) val (presuper, supercall :: rest) = stats span (t => t.hasSymbolWhich(_ hasFlag PRESUPER)) treeCopy.Block(tree, presuper ::: (supercall :: mixinConstructorCalls ::: rest), expr) } @@ -352,7 +350,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => val mix1 = mix if (mix == tpnme.EMPTY) mix else { - val ps = beforeErasure { + val ps = enteringErasure { sym.info.parents dropWhile (p => p.symbol.name != mix) } assert(!ps.isEmpty, tree); diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 7a0b034fd0..f5c8907991 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -46,7 +46,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { result } private def transformTemplate(tree: Tree) = { - val Template(parents, self, body) = tree + val Template(_, _, body) = tree clearStatics() val newBody = transformTrees(body) val templ = deriveTemplate(tree)(_ => transformTrees(newStaticMembers.toList) ::: newBody) @@ -69,12 +69,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL { case "mono-cache" => MONO_CACHE case "poly-cache" => POLY_CACHE } - - def shouldRewriteTry(tree: Try) = { - val sym = tree.tpe.typeSymbol - forMSIL && (sym != UnitClass) && (sym != NothingClass) - } - private def typedWithPos(pos: Position)(tree: Tree) = localTyper.typedPos(pos)(tree) @@ -120,7 +114,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { } def addStaticMethodToClass(forBody: (Symbol, Symbol) => Tree): Symbol = { - val methSym = currentClass.newMethod(mkTerm(nme.reflMethodName), ad.pos, STATIC | SYNTHETIC) + val methSym = currentClass.newMethod(mkTerm(nme.reflMethodName.toString), ad.pos, STATIC | SYNTHETIC) val params = methSym.newSyntheticValueParams(List(ClassClass.tpe)) methSym setInfoAndEnter MethodType(params, MethodClass.tpe) @@ -238,13 +232,13 @@ abstract class CleanUp extends Transform with ast.TreeDSL { val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe BLOCK( - VAR(methodCache) === getPolyCache, + VAL(methodCache) === getPolyCache, IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK( REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)), REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache)) ) ENDIF, - VAR(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)), + VAL(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)), IF (REF(methodSym) OBJ_NE NULL) . THEN (Return(REF(methodSym))) ELSE { @@ -555,10 +549,9 @@ abstract class CleanUp extends Transform with ast.TreeDSL { * constructor. */ case Template(parents, self, body) => localTyper = typer.atOwner(tree, currentClass) - if (forMSIL) savingStatics( transformTemplate(tree) ) - else transformTemplate(tree) + transformTemplate(tree) - case Literal(c) if (c.tag == ClazzTag) && !forMSIL=> + case Literal(c) if c.tag == ClazzTag => val tpe = c.typeValue typedWithPos(tree.pos) { if (isPrimitiveValueClass(tpe.typeSymbol)) { @@ -571,24 +564,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL { else tree } - /* MSIL requires that the stack is empty at the end of a try-block. - * Hence, we here rewrite all try blocks with a result != {Unit, All} such that they - * store their result in a local variable. The catch blocks are adjusted as well. - * The try tree is subsituted by a block whose result expression is read of that variable. */ - case theTry @ Try(block, catches, finalizer) if shouldRewriteTry(theTry) => - def transformTry = { - val tpe = theTry.tpe.widen - val tempVar = currentOwner.newVariable(mkTerm(nme.EXCEPTION_RESULT_PREFIX), theTry.pos).setInfo(tpe) - def assignBlock(rhs: Tree) = super.transform(BLOCK(Ident(tempVar) === transform(rhs))) - - val newBlock = assignBlock(block) - val newCatches = for (CaseDef(pattern, guard, body) <- catches) yield - (CASE(super.transform(pattern)) IF (super.transform(guard))) ==> assignBlock(body) - val newTry = Try(newBlock, newCatches, super.transform(finalizer)) - - typedWithPos(theTry.pos)(BLOCK(VAL(tempVar) === EmptyTree, newTry, Ident(tempVar))) - } - transformTry /* * This transformation should identify Scala symbol invocations in the tree and replace them * with references to a static member. Also, whenever a class has at least a single symbol invocation @@ -657,9 +632,8 @@ abstract class CleanUp extends Transform with ast.TreeDSL { // create a symbol for the static field val stfieldSym = ( currentClass.newVariable(mkTerm("symbol$"), pos, PRIVATE | STATIC | SYNTHETIC | FINAL) - setInfo SymbolClass.tpe + setInfoAndEnter SymbolClass.tpe ) - currentClass.info.decls enter stfieldSym // create field definition and initialization val stfieldDef = theTyper.typedPos(pos)(VAL(stfieldSym) === rhs) diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 4891ef2fd1..e99b42a402 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -60,7 +60,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { // The constructor parameter corresponding to an accessor def parameter(acc: Symbol): Symbol = - parameterNamed(nme.getterName(acc.originalName)) + parameterNamed(nme.getterName(acc.originalName.toTermName)) // The constructor parameter with given name. This means the parameter // has given name, or starts with given name, and continues with a `$` afterwards. @@ -130,7 +130,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { if (from.name != nme.OUTER || from.tpe.typeSymbol.isPrimitiveValueClass) result else localTyper.typedPos(to.pos) { - IF (from OBJ_EQ NULL) THEN Throw(NullPointerExceptionClass.tpe) ELSE result + IF (from OBJ_EQ NULL) THEN Throw(NewFromConstructor(NPEConstructor)) ELSE result } } @@ -281,7 +281,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { specializedStats find { case Assign(sel @ Select(This(_), _), rhs) => ( (sel.symbol hasFlag SPECIALIZED) - && (nme.unspecializedName(nme.localToGetter(sel.symbol.name)) == nme.localToGetter(sym.name)) + && (nme.unspecializedName(nme.localToGetter(sel.symbol.name.toTermName)) == nme.localToGetter(sym.name.toTermName)) ) case _ => false } @@ -399,7 +399,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def addGetter(sym: Symbol): Symbol = { val getr = addAccessor( - sym, nme.getterName(sym.name), getterFlags(sym.flags)) + sym, nme.getterName(sym.name.toTermName), getterFlags(sym.flags)) getr setInfo MethodType(List(), sym.tpe) defBuf += localTyper.typedPos(sym.pos)(DefDef(getr, Select(This(clazz), sym))) getr @@ -408,7 +408,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def addSetter(sym: Symbol): Symbol = { sym setFlag MUTABLE val setr = addAccessor( - sym, nme.getterToSetter(nme.getterName(sym.name)), setterFlags(sym.flags)) + sym, nme.getterToSetter(nme.getterName(sym.name.toTermName)), setterFlags(sym.flags)) setr setInfo MethodType(setr.newSyntheticValueParams(List(sym.tpe)), UnitClass.tpe) defBuf += localTyper.typed { //util.trace("adding setter def for "+setr) { @@ -422,7 +422,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def ensureAccessor(sym: Symbol)(acc: => Symbol) = if (sym.owner == clazz && !sym.isMethod && sym.isPrivate) { // there's an access to a naked field of the enclosing class - var getr = acc + val getr = acc getr makeNotPrivate clazz getr } else { @@ -511,7 +511,6 @@ abstract class Constructors extends Transform with ast.TreeDSL { sym = closureClass, constrMods = Modifiers(0), vparamss = List(List(outerFieldDef)), - argss = ListOfNil, body = List(applyMethodDef), superPos = impl.pos) } @@ -529,7 +528,8 @@ abstract class Constructors extends Transform with ast.TreeDSL { (pre ::: supercalls, rest) } - var (uptoSuperStats, remainingConstrStats) = splitAtSuper(constrStatBuf.toList) + val (uptoSuperStats, remainingConstrStats0) = splitAtSuper(constrStatBuf.toList) + var remainingConstrStats = remainingConstrStats0 /** XXX This is not corect: remainingConstrStats.nonEmpty excludes too much, * but excluding it includes too much. The constructor sequence being mimicked diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 889d309ba9..f380b9d04f 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -10,6 +10,7 @@ import scala.reflect.internal.ClassfileConstants._ import scala.collection.{ mutable, immutable } import symtab._ import Flags._ +import scala.reflect.internal.Mode._ abstract class Erasure extends AddInterfaces with scala.reflect.internal.transform.Erasure @@ -49,7 +50,7 @@ abstract class Erasure extends AddInterfaces if (sym == ArrayClass) args foreach traverse else if (sym.isTypeParameterOrSkolem || sym.isExistentiallyBound || !args.isEmpty) result = true else if (sym.isClass) traverse(rebindInnerClass(pre, sym)) // #2585 - else if (!sym.owner.isPackageClass) traverse(pre) + else if (!sym.isTopLevel) traverse(pre) case PolyType(_, _) | ExistentialType(_, _) => result = true case RefinedType(parents, _) => @@ -100,7 +101,7 @@ abstract class Erasure extends AddInterfaces * unboxing some primitive types and further simplifications as they are done in jsig. */ val prepareSigMap = new TypeMap { - def squashBoxed(tp: Type): Type = tp.normalize match { + def squashBoxed(tp: Type): Type = tp.dealiasWiden match { case t @ RefinedType(parents, decls) => val parents1 = parents mapConserve squashBoxed if (parents1 eq parents) tp @@ -113,7 +114,7 @@ abstract class Erasure extends AddInterfaces if (boxedClass contains t.typeSymbol) ObjectClass.tpe else tp } - def apply(tp: Type): Type = tp.normalize match { + def apply(tp: Type): Type = tp.dealiasWiden match { case tp1 @ TypeBounds(lo, hi) => val lo1 = squashBoxed(apply(lo)) val hi1 = squashBoxed(apply(hi)) @@ -144,7 +145,7 @@ abstract class Erasure extends AddInterfaces } case tp1 @ MethodType(params, restpe) => val params1 = mapOver(params) - val restpe1 = if (restpe.normalize.typeSymbol == UnitClass) UnitClass.tpe else apply(restpe) + val restpe1 = if (restpe.typeSymbol == UnitClass) UnitClass.tpe else apply(restpe) if ((params1 eq params) && (restpe1 eq restpe)) tp1 else MethodType(params1, restpe1) case tp1 @ RefinedType(parents, decls) => @@ -162,8 +163,8 @@ abstract class Erasure extends AddInterfaces } } - private def hiBounds(bounds: TypeBounds): List[Type] = bounds.hi.normalize match { - case RefinedType(parents, _) => parents map (_.normalize) + private def hiBounds(bounds: TypeBounds): List[Type] = bounds.hi.dealiasWiden match { + case RefinedType(parents, _) => parents map (_.dealiasWiden) case tp => tp :: Nil } @@ -172,7 +173,7 @@ abstract class Erasure extends AddInterfaces /** The Java signature of type 'info', for symbol sym. The symbol is used to give the right return * type for constructors. */ - def javaSig(sym0: Symbol, info: Type): Option[String] = beforeErasure { + def javaSig(sym0: Symbol, info: Type): Option[String] = enteringErasure { val isTraitSignature = sym0.enclClass.isTrait def superSig(parents: List[Type]) = { @@ -206,7 +207,7 @@ abstract class Erasure extends AddInterfaces // Anything which could conceivably be a module (i.e. isn't known to be // a type parameter or similar) must go through here or the signature is // likely to end up with Foo<T>.Empty where it needs Foo<T>.Empty$. - def fullNameInSig(sym: Symbol) = "L" + beforeIcode(sym.javaBinaryName) + def fullNameInSig(sym: Symbol) = "L" + enteringIcode(sym.javaBinaryName) def jsig(tp0: Type, existentiallyBound: List[Symbol] = Nil, toplevel: Boolean = false, primitiveOK: Boolean = true): String = { val tp = tp0.dealias @@ -340,15 +341,14 @@ abstract class Erasure extends AddInterfaces case _ => tp.deconst } } - + // Each primitive value class has its own getClass for ultra-precise class object typing. private lazy val primitiveGetClassMethods = Set[Symbol](Any_getClass, AnyVal_getClass) ++ ( ScalaValueClasses map (_.tpe member nme.getClass_) ) - + // ## requires a little translation private lazy val poundPoundMethods = Set[Symbol](Any_##, Object_##) - // Methods on Any/Object which we rewrite here while we still know what // is a primitive and what arrived boxed. private lazy val interceptedMethods = poundPoundMethods ++ primitiveGetClassMethods @@ -404,7 +404,7 @@ abstract class Erasure extends AddInterfaces val bridgeTarget = mutable.HashMap[Symbol, Symbol]() var bridges = List[Tree]() - val opc = beforeExplicitOuter { + val opc = enteringExplicitOuter { new overridingPairs.Cursor(root) { override def parents = List(root.info.firstParent) override def exclude(sym: Symbol) = !sym.isMethod || sym.isPrivate || super.exclude(sym) @@ -416,7 +416,7 @@ abstract class Erasure extends AddInterfaces val member = opc.overriding val other = opc.overridden //println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString)//DEBUG - if (beforeExplicitOuter(!member.isDeferred)) + if (enteringExplicitOuter(!member.isDeferred)) checkPair(member, other) opc.next @@ -446,11 +446,11 @@ abstract class Erasure extends AddInterfaces sm"""bridge generated for member ${fulldef(member)} |which overrides ${fulldef(other)} |clashes with definition of $what; - |both have erased type ${afterPostErasure(bridge.tpe)}""") + |both have erased type ${exitingPostErasure(bridge.tpe)}""") } for (bc <- root.baseClasses) { if (settings.debug.value) - afterPostErasure(println( + exitingPostErasure(println( sm"""check bridge overrides in $bc |${bc.info.nonPrivateDecl(bridge.name)} |${site.memberType(bridge)} @@ -459,13 +459,13 @@ abstract class Erasure extends AddInterfaces def overriddenBy(sym: Symbol) = sym.matchingSymbol(bc, site).alternatives filter (sym => !sym.isBridge) - for (overBridge <- afterPostErasure(overriddenBy(bridge))) { + for (overBridge <- exitingPostErasure(overriddenBy(bridge))) { if (overBridge == member) { clashError("the member itself") } else { val overMembers = overriddenBy(member) if (!overMembers.exists(overMember => - afterPostErasure(overMember.tpe =:= overBridge.tpe))) { + exitingPostErasure(overMember.tpe =:= overBridge.tpe))) { clashError(fulldef(overBridge)) } } @@ -476,7 +476,7 @@ abstract class Erasure extends AddInterfaces def checkPair(member: Symbol, other: Symbol) { val otpe = specialErasure(root)(other.tpe) - val bridgeNeeded = afterErasure ( + val bridgeNeeded = exitingErasure ( !(other.tpe =:= member.tpe) && !(deconstMap(other.tpe) =:= deconstMap(member.tpe)) && { var e = bridgesScope.lookupEntry(member.name) @@ -488,7 +488,7 @@ abstract class Erasure extends AddInterfaces if (!bridgeNeeded) return - val newFlags = (member.flags | BRIDGE) & ~(ACCESSOR | DEFERRED | LAZY | lateDEFERRED) + val newFlags = (member.flags | BRIDGE | ARTIFACT) & ~(ACCESSOR | DEFERRED | LAZY | lateDEFERRED) val bridge = other.cloneSymbolImpl(root, newFlags) setPos root.pos debuglog("generating bridge from %s (%s): %s to %s: %s".format( @@ -503,9 +503,9 @@ abstract class Erasure extends AddInterfaces if (!(member.tpe exists (_.typeSymbol.isDerivedValueClass)) || checkBridgeOverrides(member, other, bridge)) { - afterErasure(root.info.decls enter bridge) + exitingErasure(root.info.decls enter bridge) if (other.owner == root) { - afterErasure(root.info.decls.unlink(other)) + exitingErasure(root.info.decls.unlink(other)) toBeRemoved += other } @@ -514,7 +514,7 @@ abstract class Erasure extends AddInterfaces } } - def makeBridgeDefDef(bridge: Symbol, member: Symbol, other: Symbol) = afterErasure { + def makeBridgeDefDef(bridge: Symbol, member: Symbol, other: Symbol) = exitingErasure { // type checking ensures we can safely call `other`, but unless `member.tpe <:< other.tpe`, // calling `member` is not guaranteed to succeed in general, there's // nothing we can do about this, except for an unapply: when this subtype test fails, @@ -525,10 +525,10 @@ abstract class Erasure extends AddInterfaces def maybeWrap(bridgingCall: Tree): Tree = { val guardExtractor = ( // 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?) + && !exitingErasure((member.tpe <:< other.tpe))) // no static guarantees (TODO: is the subtype test ever true?) import CODE._ - val _false = FALSE_typed + val _false = FALSE val pt = member.tpe.resultType lazy val zero = if (_false.tpe <:< pt) _false @@ -730,19 +730,11 @@ abstract class Erasure extends AddInterfaces case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) if tree.symbol == Any_asInstanceOf => val qual1 = typedQualifier(qual, NOmode, ObjectClass.tpe) // need to have an expected type, see #3037 - val qualClass = qual1.tpe.typeSymbol -/* - val targClass = targ.tpe.typeSymbol - if (isNumericValueClass(qualClass) && isNumericValueClass(targClass)) - // convert numeric type casts - atPos(tree.pos)(Apply(Select(qual1, "to" + targClass.name), List())) - else -*/ if (isPrimitiveValueType(targ.tpe) || isErasedValueType(targ.tpe)) { val noNullCheckNeeded = targ.tpe match { case ErasedValueType(tref) => - atPhase(currentRun.erasurePhase) { + enteringPhase(currentRun.erasurePhase) { isPrimitiveValueClass(erasedValueClassArg(tref).typeSymbol) } case _ => @@ -750,7 +742,6 @@ abstract class Erasure extends AddInterfaces } if (noNullCheckNeeded) unbox(qual1, targ.tpe) else { - def nullConst = Literal(Constant(null)) setType NullClass.tpe val untyped = // util.trace("new asinstanceof test") { gen.evalOnce(qual1, context.owner, context.unit) { qual => @@ -816,19 +807,19 @@ abstract class Erasure extends AddInterfaces /** A replacement for the standard typer's adapt method. */ - override protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree = + override protected def adapt(tree: Tree, mode: Mode, pt: Type, original: Tree = EmptyTree): Tree = adaptToType(tree, pt) /** A replacement for the standard typer's `typed1` method. */ - override def typed1(tree: Tree, mode: Int, pt: Type): Tree = { + override def typed1(tree: Tree, mode: Mode, pt: Type): Tree = { val tree1 = try { tree match { case InjectDerivedValue(arg) => (tree.attachments.get[TypeRefAttachment]: @unchecked) match { case Some(itype) => val tref = itype.tpe - val argPt = atPhase(currentRun.erasurePhase)(erasedValueClassArg(tref)) + val argPt = enteringPhase(currentRun.erasurePhase)(erasedValueClassArg(tref)) log(s"transforming inject $arg -> $tref/$argPt") val result = typed(arg, mode, argPt) log(s"transformed inject $arg -> $tref/$argPt = $result:${result.tpe}") @@ -872,8 +863,7 @@ abstract class Erasure extends AddInterfaces alt => alt == first || !(first.tpe looselyMatches alt.tpe) } if (tree.symbol ne sym1) { - tree1.symbol = sym1 - tree1.tpe = sym1.tpe + tree1 setSymbol sym1 setType sym1.tpe } } tree1 @@ -901,20 +891,20 @@ abstract class Erasure extends AddInterfaces private def checkNoDoubleDefs(root: Symbol) { def doubleDefError(sym1: Symbol, sym2: Symbol) { // the .toString must also be computed at the earlier phase - val tpe1 = afterRefchecks(root.thisType.memberType(sym1)) - val tpe2 = afterRefchecks(root.thisType.memberType(sym2)) + val tpe1 = exitingRefchecks(root.thisType.memberType(sym1)) + val tpe2 = exitingRefchecks(root.thisType.memberType(sym2)) if (!tpe1.isErroneous && !tpe2.isErroneous) unit.error( if (sym1.owner == root) sym1.pos else root.pos, (if (sym1.owner == sym2.owner) "double definition:\n" else if (sym1.owner == root) "name clash between defined and inherited member:\n" else "name clash between inherited members:\n") + - sym1 + ":" + afterRefchecks(tpe1.toString) + + sym1 + ":" + exitingRefchecks(tpe1.toString) + (if (sym1.owner == root) "" else sym1.locationString) + " and\n" + - sym2 + ":" + afterRefchecks(tpe2.toString) + + sym2 + ":" + exitingRefchecks(tpe2.toString) + (if (sym2.owner == root) " at line " + (sym2.pos).line else sym2.locationString) + "\nhave same type" + - (if (afterRefchecks(tpe1 =:= tpe2)) "" else " after erasure: " + afterPostErasure(sym1.tpe))) + (if (exitingRefchecks(tpe1 =:= tpe2)) "" else " after erasure: " + exitingPostErasure(sym1.tpe))) sym1.setInfo(ErrorType) } @@ -924,7 +914,7 @@ abstract class Erasure extends AddInterfaces if (e.sym.isTerm) { var e1 = decls.lookupNextEntry(e) while (e1 ne null) { - if (afterPostErasure(e1.sym.info =:= e.sym.info)) doubleDefError(e.sym, e1.sym) + if (exitingPostErasure(e1.sym.info =:= e.sym.info)) doubleDefError(e.sym, e1.sym) e1 = decls.lookupNextEntry(e1) } } @@ -932,16 +922,17 @@ abstract class Erasure extends AddInterfaces } val opc = new overridingPairs.Cursor(root) { - override def exclude(sym: Symbol): Boolean = - (!sym.isTerm || sym.isPrivate || super.exclude(sym) - // specialized members have no type history before 'specialize', causing double def errors for curried defs - || !sym.hasTypeAt(currentRun.refchecksPhase.id)) + override def exclude(sym: Symbol): Boolean = ( + !sym.isTerm || sym.isPrivate || super.exclude(sym) + // specialized members have no type history before 'specialize', causing double def errors for curried defs + || !sym.hasTypeAt(currentRun.refchecksPhase.id) + ) override def matches(sym1: Symbol, sym2: Symbol): Boolean = - afterPostErasure(sym1.tpe =:= sym2.tpe) + exitingPostErasure(sym1.tpe =:= sym2.tpe) } while (opc.hasNext) { - if (!afterRefchecks( + if (!exitingRefchecks( root.thisType.memberType(opc.overriding) matches root.thisType.memberType(opc.overridden))) { debuglog("" + opc.overriding.locationString + " " + @@ -960,8 +951,8 @@ abstract class Erasure extends AddInterfaces for (member <- root.info.nonPrivateMember(other.name).alternatives) { if (member != other && !(member hasFlag BRIDGE) && - afterErasure(member.tpe =:= other.tpe) && - !afterRefchecks( + exitingErasure(member.tpe =:= other.tpe) && + !exitingRefchecks( root.thisType.memberType(member) matches root.thisType.memberType(other))) { debuglog("" + member.locationString + " " + member.infosString + other.locationString + " " + other.infosString); doubleDefError(member, other) @@ -1083,6 +1074,7 @@ abstract class Erasure extends AddInterfaces } else if (fn.symbol == Any_isInstanceOf) { preEraseIsInstanceOf } else if (fn.symbol.owner.isRefinementClass && !fn.symbol.isOverridingSymbol) { + // !!! Another spot where we produce overloaded types (see test run/t6301) ApplyDynamic(qualifier, args) setSymbol fn.symbol setPos tree.pos } else if (fn.symbol.isMethodWithExtension && !fn.symbol.tpe.isErroneous) { Apply(gen.mkAttributedRef(extensionMethods.extensionMethod(fn.symbol)), qualifier :: args) @@ -1141,7 +1133,8 @@ abstract class Erasure extends AddInterfaces SelectFromArray(qual, name, erasure(tree.symbol)(qual.tpe)).copyAttrs(fn), args) } - } else if (args.isEmpty && interceptedMethods(fn.symbol)) { + } + else if (args.isEmpty && interceptedMethods(fn.symbol)) { if (poundPoundMethods.contains(fn.symbol)) { // This is unattractive, but without it we crash here on ().## because after // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int. @@ -1153,13 +1146,24 @@ abstract class Erasure extends AddInterfaces case s @ (ShortClass | ByteClass | CharClass) => numericConversion(qual, s) case BooleanClass => If(qual, LIT(true.##), LIT(false.##)) case _ => - global.typer.typed(gen.mkRuntimeCall(nme.hash_, List(qual))) + // Since we are past typer, we need to avoid creating trees carrying + // overloaded types. This logic is custom (and technically incomplete, + // although serviceable) for def hash. What is really needed is for + // the overloading logic presently hidden away in a few different + // places to be properly exposed so we can just call "resolveOverload" + // after typer. Until then: + val alts = ScalaRunTimeModule.info.member(nme.hash_).alternatives + def alt1 = alts find (_.info.paramTypes.head =:= qual.tpe) + def alt2 = ScalaRunTimeModule.info.member(nme.hash_) suchThat (_.info.paramTypes.head.typeSymbol == AnyClass) + val newTree = gen.mkRuntimeCall(nme.hash_, qual :: Nil) setSymbol (alt1 getOrElse alt2) + + global.typer.typed(newTree) } } else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen)))) } else if (primitiveGetClassMethods.contains(fn.symbol)) { - // if we got here then we're trying to send a primitive getClass method to either + // if we got here then we're trying to send a primitive getClass method to either // a) an Any, in which cage Object_getClass works because Any erases to object. Or // // b) a non-primitive, e.g. because the qualifier's type is a refinement type where one parent @@ -1270,13 +1274,12 @@ abstract class Erasure extends AddInterfaces tree1 setType specialScalaErasure(tree1.tpe) case ArrayValue(elemtpt, trees) => treeCopy.ArrayValue( - tree1, elemtpt setType specialScalaErasure.applyInArray(elemtpt.tpe), trees map transform) setType null + tree1, elemtpt setType specialScalaErasure.applyInArray(elemtpt.tpe), trees map transform).clearType() case DefDef(_, _, _, _, tpt, _) => - val result = super.transform(tree1) setType null - tpt.tpe = specialErasure(tree1.symbol)(tree1.symbol.tpe).resultType - result + try super.transform(tree1).clearType() + finally tpt setType specialErasure(tree1.symbol)(tree1.symbol.tpe).resultType case _ => - super.transform(tree1) setType null + super.transform(tree1).clearType() } } } @@ -1288,7 +1291,7 @@ abstract class Erasure extends AddInterfaces override def transform(tree: Tree): Tree = { val tree1 = preTransformer.transform(tree) // log("tree after pretransform: "+tree1) - afterErasure { + exitingErasure { val tree2 = mixinTransformer.transform(tree1) // debuglog("tree after addinterfaces: \n" + tree2) diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 2f28a16416..965612f926 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -10,7 +10,6 @@ import symtab._ import Flags.{ CASE => _, _ } import scala.collection.mutable import scala.collection.mutable.ListBuffer -import matching.{ Patterns, ParallelMatching } import scala.tools.nsc.settings.ScalaVersion /** This class ... @@ -19,15 +18,12 @@ import scala.tools.nsc.settings.ScalaVersion * @version 1.0 */ abstract class ExplicitOuter extends InfoTransform - with Patterns - with ParallelMatching with TypingTransformers with ast.TreeDSL { import global._ import definitions._ import CODE._ - import Debug.TRACE /** The following flags may be set by this phase: */ override def phaseNewFlags: Long = notPROTECTED @@ -78,19 +74,11 @@ abstract class ExplicitOuter extends InfoTransform class RemoveBindingsTransformer(toRemove: Set[Symbol]) extends Transformer { override def transform(tree: Tree) = tree match { - case Bind(_, body) if toRemove(tree.symbol) => - TRACE("Dropping unused binding: " + tree.symbol) - super.transform(body) + case Bind(_, body) if toRemove(tree.symbol) => super.transform(body) case _ => super.transform(tree) } } - /** Issue a migration warning for instance checks which might be on an Array and - * for which the type parameter conforms to Seq, because these answers changed in 2.8. - */ - def isArraySeqTest(lhs: Type, rhs: Type) = - (ArrayClass.tpe <:< lhs.widen) && (rhs.widen matchesPattern SeqClass.tpe) - def outerAccessor(clazz: Symbol): Symbol = { val firstTry = clazz.info.decl(nme.expandedName(nme.OUTER, clazz)) if (firstTry != NoSymbol && firstTry.outerSource == clazz) firstTry @@ -99,7 +87,7 @@ abstract class ExplicitOuter extends InfoTransform def newOuterAccessor(clazz: Symbol) = { val accFlags = SYNTHETIC | ARTIFACT | METHOD | STABLE | ( if (clazz.isTrait) DEFERRED else 0 ) val sym = clazz.newMethod(nme.OUTER, clazz.pos, accFlags) - val restpe = if (clazz.isTrait) clazz.outerClass.tpe else clazz.outerClass.thisType + val restpe = if (clazz.isTrait) clazz.outerClass.tpe_* else clazz.outerClass.thisType sym expandName clazz sym.referenced = clazz @@ -118,7 +106,7 @@ abstract class ExplicitOuter extends InfoTransform * <ol> * <li> * Add an outer parameter to the formal parameters of a constructor - * in a inner non-trait class; + * in an inner non-trait class; * </li> * <li> * Add a protected $outer field to an inner class which is @@ -166,16 +154,13 @@ abstract class ExplicitOuter extends InfoTransform var decls1 = decls if (isInner(clazz) && !clazz.isInterface) { decls1 = decls.cloneScope - val outerAcc = clazz.newMethod(nme.OUTER, clazz.pos) // 3 - outerAcc expandName clazz - - decls1 enter newOuterAccessor(clazz) + decls1 enter newOuterAccessor(clazz) // 3 if (hasOuterField(clazz)) //2 decls1 enter newOuterField(clazz) } if (!clazz.isTrait && !parents.isEmpty) { for (mc <- clazz.mixinClasses) { - val mixinOuterAcc: Symbol = afterExplicitOuter(outerAccessor(mc)) + val mixinOuterAcc: Symbol = exitingExplicitOuter(outerAccessor(mc)) if (mixinOuterAcc != NoSymbol) { if (decls1 eq decls) decls1 = decls.cloneScope val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED) @@ -256,11 +241,6 @@ abstract class ExplicitOuter extends InfoTransform * <blockquote><pre>`base'.$outer$$C1 ... .$outer$$Cn</pre></blockquote> * which refers to the outer instance of class to of * value base. The result is typed but not positioned. - * - * @param base ... - * @param from ... - * @param to ... - * @return ... */ protected def outerPath(base: Tree, from: Symbol, to: Symbol): Tree = { //Console.println("outerPath from "+from+" to "+to+" at "+base+":"+base.tpe) @@ -369,7 +349,7 @@ abstract class ExplicitOuter extends InfoTransform */ def outerAccessorDef: Tree = { val outerAcc = outerAccessor(currentClass) - var rhs: Tree = + val rhs: Tree = if (outerAcc.isDeferred) EmptyTree else This(currentClass) DOT outerField(currentClass) @@ -405,74 +385,6 @@ abstract class ExplicitOuter extends InfoTransform } } - // requires settings.XoldPatmat.value - def matchTranslation(tree: Match) = { - val Match(selector, cases) = tree - var nselector = transform(selector) - - def makeGuardDef(vs: List[Symbol], guard: Tree) = { - val gdname = unit.freshTermName("gd") - val method = currentOwner.newMethod(gdname, tree.pos, SYNTHETIC) - val params = method newSyntheticValueParams vs.map(_.tpe) - method setInfo new MethodType(params, BooleanClass.tpe) - - localTyper typed { - DEF(method) === guard.changeOwner(currentOwner -> method).substituteSymbols(vs, params) - } - } - - val nguard = new ListBuffer[Tree] - val ncases = - for (CaseDef(pat, guard, body) <- cases) yield { - // Strip out any unused pattern bindings up front - val patternIdents = for (b @ Bind(_, _) <- pat) yield b.symbol - val references: Set[Symbol] = Set(guard, body) flatMap { t => for (id @ Ident(name) <- t) yield id.symbol } - val (used, unused) = patternIdents partition references - val strippedPat = if (unused.isEmpty) pat else new RemoveBindingsTransformer(unused.toSet) transform pat - - val gdcall = - if (guard == EmptyTree) EmptyTree - else { - val guardDef = makeGuardDef(used, guard) - nguard += transform(guardDef) // building up list of guards - - localTyper typed (Ident(guardDef.symbol) APPLY (used map Ident)) - } - - (CASE(transform(strippedPat)) IF gdcall) ==> transform(body) - } - - val (checkExhaustive, requireSwitch) = nselector match { - case Typed(nselector1, tpt) => - val unchecked = tpt.tpe hasAnnotation UncheckedClass - if (unchecked) - nselector = nselector1 - - // Don't require a tableswitch if there are 1-2 casedefs - // since the matcher intentionally emits an if-then-else. - (!unchecked, treeInfo.isSwitchAnnotation(tpt.tpe) && ncases.size > 2) - case _ => - (true, false) - } - - val t = atPos(tree.pos) { - val context = MatrixContext(currentUnit, transform, localTyper, currentOwner, tree.tpe) - val t_untyped = handlePattern(nselector, ncases, checkExhaustive, context) - - /* if @switch annotation is present, verify the resulting tree is a Match */ - if (requireSwitch) t_untyped match { - case Block(_, Match(_, _)) => // ok - case _ => - unit.error(tree.pos, "could not emit switch for @switch annotated match") - } - - localTyper.typed(t_untyped, context.matchResultType) - } - - if (nguard.isEmpty) t - else Block(nguard.toList, t) setType t.tpe - } - /** The main transformation method */ override def transform(tree: Tree): Tree = { val sym = tree.symbol @@ -557,14 +469,10 @@ abstract class ExplicitOuter extends InfoTransform }) super.transform(treeCopy.Apply(tree, sel, outerVal :: args)) - // entry point for pattern matcher translation - case m: Match if settings.XoldPatmat.value => // the new pattern matcher runs in its own phase right after typer - matchTranslation(m) - // for the new pattern matcher // base.<outer>.eq(o) --> base.$outer().eq(o) if there's an accessor, else the whole tree becomes TRUE // TODO remove the synthetic `<outer>` method from outerFor?? - case Apply(eqsel@Select(eqapp@Apply(sel@Select(base, nme.OUTER_SYNTH), Nil), eq), args) if !settings.XoldPatmat.value => + case Apply(eqsel@Select(eqapp@Apply(sel@Select(base, nme.OUTER_SYNTH), Nil), eq), args) => val outerFor = sel.symbol.owner.toInterface // TODO: toInterface necessary? val acc = outerAccessor(outerFor) @@ -584,13 +492,6 @@ abstract class ExplicitOuter extends InfoTransform } case _ => - if (settings.Xmigration.value < ScalaVersion.twoDotEight) tree match { - case TypeApply(fn @ Select(qual, _), args) if fn.symbol == Object_isInstanceOf || fn.symbol == Any_isInstanceOf => - if (isArraySeqTest(qual.tpe, args.head.tpe)) - unit.warning(tree.pos, "An Array will no longer match as Seq[_].") - case _ => () - } - val x = super.transform(tree) if (x.tpe eq null) x else x setType transformInfo(currentOwner, x.tpe) @@ -599,7 +500,7 @@ abstract class ExplicitOuter extends InfoTransform /** The transformation method for whole compilation units */ override def transformUnit(unit: CompilationUnit) { - afterExplicitOuter(super.transformUnit(unit)) + exitingExplicitOuter(super.transformUnit(unit)) } } diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index bc54054028..672d9d232a 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -8,9 +8,6 @@ package transform import symtab._ import Flags._ import scala.collection.{ mutable, immutable } -import scala.collection.mutable -import scala.tools.nsc.util.FreshNameCreator -import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple } /** * Perform Step 1 in the inline classes SIP: Creates extension methods for all @@ -23,7 +20,6 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { import global._ // the global environment import definitions._ // standard classes and methods - import typer.{ typed, atOwner } // methods to type trees /** the following two members override abstract members in Transform */ val phaseName: String = "extmethods" @@ -70,7 +66,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { } /** Return the extension method that corresponds to given instance method `meth`. */ - def extensionMethod(imeth: Symbol): Symbol = atPhase(currentRun.refchecksPhase) { + def extensionMethod(imeth: Symbol): Symbol = enteringPhase(currentRun.refchecksPhase) { val companionInfo = companionModuleForce(imeth.owner).info val candidates = extensionNames(imeth) map (companionInfo.decl(_)) filter (_.exists) val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe) @@ -87,7 +83,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { | | ${candidates.map(c => c.name+":"+normalize(c.tpe, imeth.owner)).mkString("\n")} | - | Eligible Names: ${extensionNames(imeth).mkString(",")}"""") + | Eligible Names: ${extensionNames(imeth).mkString(",")}" """) matching.head } @@ -185,6 +181,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { // bad: [B#16154 >: A#16149, A#16155 <: AnyRef#2189]($this#16156: Foo#6965[A#16155])(x#16157: B#16154)List#2457[B#16154] // good: [B#16151 >: A#16149, A#16149 <: AnyRef#2189]($this#16150: Foo#6965[A#16149])(x#16153: B#16151)List#2457[B#16151] } + override def transform(tree: Tree): Tree = { tree match { case Template(_, _, _) => @@ -206,7 +203,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { val companion = origThis.companionModule def makeExtensionMethodSymbol = { - val extensionName = extensionNames(origMeth).head + val extensionName = extensionNames(origMeth).head.toTermName val extensionMeth = ( companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) setAnnotations origMeth.annotations @@ -256,12 +253,13 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = super.transformStats(stats, exprOwner) map { - case md @ ModuleDef(_, _, _) if extensionDefs contains md.symbol => - val defns = extensionDefs(md.symbol).toList map (member => - atOwner(md.symbol)(localTyper.typedPos(md.pos.focus)(member)) - ) - extensionDefs -= md.symbol - deriveModuleDef(md)(tmpl => deriveTemplate(tmpl)(_ ++ defns)) + case md @ ModuleDef(_, _, _) => + val extraStats = extensionDefs remove md.symbol match { + case Some(defns) => defns.toList map (defn => atOwner(md.symbol)(localTyper.typedPos(md.pos.focus)(defn.duplicate))) + case _ => Nil + } + if (extraStats.isEmpty) md + else deriveModuleDef(md)(tmpl => deriveTemplate(tmpl)(_ ++ extraStats)) case stat => stat } diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index cd26f95958..a370b45be0 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -8,28 +8,23 @@ package transform import symtab._ import Flags._ -import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer abstract class Flatten extends InfoTransform { import global._ - import definitions._ + import treeInfo.isQualifierSafeToElide /** the following two members override abstract members in Transform */ val phaseName: String = "flatten" - /** Updates the owning scope with the given symbol; returns the old symbol. + /** Updates the owning scope with the given symbol, unlinking any others. */ - private def replaceSymbolInCurrentScope(sym: Symbol): Symbol = afterFlatten { + private def replaceSymbolInCurrentScope(sym: Symbol): Unit = exitingFlatten { val scope = sym.owner.info.decls - val old = scope lookup sym.name andAlso scope.unlink + val old = (scope lookupUnshadowedEntries sym.name).toList + old foreach (scope unlink _) scope enter sym - - if (old eq NoSymbol) - log(s"lifted ${sym.fullLocationString}") - else - log(s"lifted ${sym.fullLocationString} after unlinking existing $old from scope.") - + log(s"lifted ${sym.fullLocationString}" + ( if (old.isEmpty) "" else s" after unlinking $old from scope." )) old } @@ -53,7 +48,7 @@ abstract class Flatten extends InfoTransform { clazz.isClass && !clazz.isPackageClass && { // Cannot flatten here: class A[T] { object B } // was "at erasurePhase.prev" - beforeErasure(clazz.typeParams.isEmpty) + enteringErasure(clazz.typeParams.isEmpty) } } @@ -67,11 +62,11 @@ abstract class Flatten extends InfoTransform { val decls1 = scopeTransform(clazz) { val decls1 = newScope if (clazz.isPackageClass) { - afterFlatten { decls foreach (decls1 enter _) } + exitingFlatten { decls foreach (decls1 enter _) } } else { val oldowner = clazz.owner - afterFlatten { oldowner.info } + exitingFlatten { oldowner.info } parents1 = parents mapConserve (this) for (sym <- decls) { @@ -122,8 +117,14 @@ abstract class Flatten extends InfoTransform { case ClassDef(_, _, _, _) if sym.isNestedClass => liftedDefs(sym.enclosingTopLevelClass.owner) += tree EmptyTree - case Select(qual, name) if (sym.isStaticModule && !sym.owner.isPackageClass) => - afterFlatten(atPos(tree.pos)(gen.mkAttributedRef(sym))) + case Select(qual, name) if sym.isStaticModule && !sym.isTopLevel => + exitingFlatten { + atPos(tree.pos) { + val ref = gen.mkAttributedRef(sym) + if (isQualifierSafeToElide(qual)) ref + else Block(List(qual), ref).setType(tree.tpe) // need to execute the qualifier but refer directly to the lifted module. + } + } case _ => tree } diff --git a/src/compiler/scala/tools/nsc/transform/InfoTransform.scala b/src/compiler/scala/tools/nsc/transform/InfoTransform.scala index b6dbacaa29..dc321e26ca 100644 --- a/src/compiler/scala/tools/nsc/transform/InfoTransform.scala +++ b/src/compiler/scala/tools/nsc/transform/InfoTransform.scala @@ -10,11 +10,11 @@ package transform * An InfoTransform contains a compiler phase that transforms trees and symbol infos -- making sure they stay consistent. * The symbol info is transformed assuming it is consistent right before this phase. * The info transformation is triggered by Symbol::rawInfo, which caches the results in the symbol's type history. - * This way sym.info (during an atPhase(p)) can look up what the symbol's info should look like at the beginning of phase p. + * This way sym.info (during an enteringPhase(p)) can look up what the symbol's info should look like at the beginning of phase p. * (If the transformed info had not been stored yet, rawInfo will compute the info by composing the info-transformers * of the most recent phase before p, up to the transformer of the phase right before p.) * - * Concretely, atPhase(p) { sym.info } yields the info *before* phase p has transformed it. Imagine you're a phase and it all makes sense. + * Concretely, enteringPhase(p) { sym.info } yields the info *before* phase p has transformed it. Imagine you're a phase and it all makes sense. */ trait InfoTransform extends Transform { import global.{Symbol, Type, InfoTransformer, infoTransformers} diff --git a/src/compiler/scala/tools/nsc/transform/InlineErasure.scala b/src/compiler/scala/tools/nsc/transform/InlineErasure.scala index 0af3cf732f..83dbc23014 100644 --- a/src/compiler/scala/tools/nsc/transform/InlineErasure.scala +++ b/src/compiler/scala/tools/nsc/transform/InlineErasure.scala @@ -1,9 +1,11 @@ package scala.tools.nsc package transform -trait InlineErasure { self: Erasure => - +trait InlineErasure { + self: Erasure => + +/** import global._ import definitions._ - -}
\ No newline at end of file + **/ +} diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 631468dd0c..a4b725d313 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -245,10 +245,10 @@ abstract class LambdaLift extends InfoTransform { freshen(sym.name + nme.NAME_JOIN_STRING + sym.owner.name + nme.NAME_JOIN_STRING) } else { // SI-5652 If the lifted symbol is accessed from an inner class, it will be made public. (where?) - // Generating a a unique name, mangled with the enclosing class name, avoids a VerifyError + // Generating a unique name, mangled with the enclosing class name, avoids a VerifyError // in the case that a sub-class happens to lifts out a method with the *same* name. - val name = freshen(sym.name + nme.NAME_JOIN_STRING) - if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym)) nme.expandedName(name, sym.enclClass) + val name = freshen("" + sym.name + nme.NAME_JOIN_STRING) + if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym)) nme.expandedName(name.toTermName, sym.enclClass) else name } } @@ -290,7 +290,7 @@ abstract class LambdaLift extends InfoTransform { proxies(owner) = for (fv <- freeValues.toList) yield { val proxyName = proxyNames.getOrElse(fv, fv.name) - val proxy = owner.newValue(proxyName, owner.pos, newFlags) setInfo fv.info + val proxy = owner.newValue(proxyName.toTermName, owner.pos, newFlags) setInfo fv.info if (owner.isClass) owner.info.decls enter proxy proxy } @@ -456,7 +456,7 @@ abstract class LambdaLift extends InfoTransform { } case arg => arg } - + /** Wrap expr argument in new *Ref(..) constructor. But try/catch * is a problem because a throw will clear the stack and post catch * we would expect the partially-constructed object to be on the stack @@ -464,11 +464,11 @@ abstract class LambdaLift extends InfoTransform { * search for "leaf" result expressions where we know its safe * to put the new *Ref(..) constructor or, if all else fails, transform * an expr to { val temp=expr; new *Ref(temp) }. - * The reason we narrowly look for try/catch in captured var definitions + * The reason we narrowly look for try/catch in captured var definitions * is because other try/catch expression have already been lifted * see SI-6863 */ - def refConstr(expr: Tree): Tree = typer.typedPos(expr.pos) {expr match { + def refConstr(expr: Tree): Tree = typer.typedPos(expr.pos)(expr match { // very simple expressions can be wrapped in a new *Ref(expr) because they can't have // a try/catch in final expression position. case Ident(_) | Apply(_, _) | Literal(_) | New(_) | Select(_, _) | Throw(_) | Assign(_, _) | ValDef(_, _, _, _) | Return(_) | EmptyTree => @@ -488,9 +488,9 @@ abstract class LambdaLift extends InfoTransform { debuglog("assigning expr to temp: " + (expr.pos)) val tempSym = currentOwner.newValue(unit.freshTermName("temp"), expr.pos) setInfo expr.tpe val tempDef = ValDef(tempSym, expr) setPos expr.pos - val tempRef = Ident(tempSym) setPos expr.pos + val tempRef = Ident(tempSym) setPos expr.pos Block(tempDef, New(sym.tpe, tempRef)) - }} + }) def refConstrCase(cdef: CaseDef): CaseDef = CaseDef(cdef.pat, cdef.guard, refConstr(cdef.body)) diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index 21213cf9d9..e6c9afb042 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -68,7 +68,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD curTree = tree tree match { - + case Block(_, _) => val block1 = super.transform(tree) val Block(stats, expr) = block1 @@ -79,7 +79,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD List(stat) }) treeCopy.Block(block1, stats1, expr) - + case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) { val (res, slowPathDef) = if (!sym.owner.isClass && sym.isLazy) { val enclosingClassOrDummyOrMethod = { @@ -100,9 +100,9 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym) sym.resetFlag((if (lazyUnit(sym)) 0 else LAZY) | ACCESSOR) (rhs1, sDef) - } else + } else (transform(rhs), EmptyTree) - + val ddef1 = deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res) if (slowPathDef != EmptyTree) Block(slowPathDef, ddef1) else ddef1 } @@ -189,10 +189,10 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD case _ => prependStats(bmps, rhs) } } - + def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): Tree = { - val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), lzyVal.pos, STABLE | PRIVATE) + val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE) defSym setInfo MethodType(List(), lzyVal.tpe.resultType) defSym.owner = lzyVal.owner debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal") @@ -201,8 +201,8 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) DEF(defSym).mkTree(addBitmapDefs(lzyVal, BLOCK(rhs, retVal))) setSymbol defSym } - - + + def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): (Tree, Tree) = { val slowPathDef: Tree = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal) @@ -221,7 +221,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD * Similarly as for normal lazy val members (see Mixin), the result will be a tree of the form * { if ((bitmap&n & MASK) == 0) this.l$compute() * else l$ - * + * * def l$compute() = { synchronized(enclosing_class_or_dummy) { * if ((bitmap$n & MASK) == 0) { * l$ = <rhs> @@ -278,7 +278,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD bmps(n) else { val sym = meth.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), meth.pos).setInfo(ByteClass.tpe) - beforeTyper { + enteringTyper { sym addAnnotation VolatileAttr } diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index c9c68d080d..3e5ac6922e 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -68,7 +68,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * maps all other types to themselves. */ private def toInterface(tp: Type): Type = - beforeMixin(tp.typeSymbol.toInterface).tpe + enteringMixin(tp.typeSymbol.toInterface).tpe private def isFieldWithBitmap(field: Symbol) = { field.info // ensure that nested objects are transformed @@ -102,7 +102,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { private val toInterfaceMap = new TypeMap { def apply(tp: Type): Type = mapOver( tp match { case TypeRef(pre, sym, args) if sym.isImplClass => - typeRef(pre, beforeMixin(sym.toInterface), args) + typeRef(pre, enteringMixin(sym.toInterface), args) case _ => tp }) } @@ -119,7 +119,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * @param mixinClass The mixin class that produced the superaccessor */ private def rebindSuper(base: Symbol, member: Symbol, mixinClass: Symbol): Symbol = - afterPickler { + exitingPickler { var bcs = base.info.baseClasses.dropWhile(mixinClass != _).tail var sym: Symbol = NoSymbol debuglog("starting rebindsuper " + base + " " + member + ":" + member.tpe + @@ -165,7 +165,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { addMember(clazz, cloneBeforeErasure(mixinClass, mixinMember, clazz)) def cloneBeforeErasure(mixinClass: Symbol, mixinMember: Symbol, clazz: Symbol): Symbol = { - val newSym = beforeErasure { + val newSym = enteringErasure { // since we used `mixinMember` from the interface that represents the trait that's // being mixed in, have to instantiate the interface type params (that may occur in mixinMember's // info) as they are seen from the class. We can't use the member that we get from the @@ -197,9 +197,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * - lazy fields don't get a setter. */ def addLateInterfaceMembers(clazz: Symbol) { - def makeConcrete(member: Symbol) = - member setPos clazz.pos resetFlag (DEFERRED | lateDEFERRED) - if (treatedClassInfos(clazz) != clazz.info) { treatedClassInfos(clazz) = clazz.info assert(phase == currentRun.mixinPhase, phase) @@ -210,14 +207,14 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // println("creating new getter for "+ field +" : "+ field.info +" at "+ field.locationString+(field hasFlag MUTABLE)) val newFlags = field.flags & ~PrivateLocal | ACCESSOR | lateDEFERRED | ( if (field.isMutable) 0 else STABLE ) // TODO preserve pre-erasure info? - clazz.newMethod(nme.getterName(field.name), field.pos, newFlags) setInfo MethodType(Nil, field.info) + clazz.newMethod(nme.getterName(field.name.toTermName), field.pos, newFlags) setInfo MethodType(Nil, field.info) } /** Create a new setter. Setters are never private or local. They are * always accessors and deferred. */ def newSetter(field: Symbol): Symbol = { //println("creating new setter for "+field+field.locationString+(field hasFlag MUTABLE)) - val setterName = nme.getterToSetter(nme.getterName(field.name)) + val setterName = nme.getterToSetter(nme.getterName(field.name.toTermName)) val newFlags = field.flags & ~PrivateLocal | ACCESSOR | lateDEFERRED val setter = clazz.newMethod(setterName, field.pos, newFlags) // TODO preserve pre-erasure info? @@ -292,7 +289,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { for (mixinMember <- mixinClass.info.decls) { if (isConcreteAccessor(mixinMember)) { if (isOverriddenAccessor(mixinMember, clazz.info.baseClasses)) - debugwarn("!!! is overridden val: "+mixinMember.fullLocationString) + devWarning(s"Overridden concrete accessor: ${mixinMember.fullLocationString}") else { // mixin field accessors val mixedInAccessor = cloneAndAddMixinMember(mixinClass, mixinMember) @@ -311,14 +308,14 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // mixinMember is a value of type unit. No field needed ; case _ => // otherwise mixin a field as well - // atPhase: the private field is moved to the implementation class by erasure, + // enteringPhase: the private field is moved to the implementation class by erasure, // so it can no longer be found in the mixinMember's owner (the trait) - val accessed = beforePickler(mixinMember.accessed) + val accessed = enteringPickler(mixinMember.accessed) // #3857, need to retain info before erasure when cloning (since cloning only // carries over the current entry in the type history) - val sym = beforeErasure { + val sym = enteringErasure { // so we have a type history entry before erasure - clazz.newValue(nme.getterToLocal(mixinMember.name), mixinMember.pos).setInfo(mixinMember.tpe.resultType) + clazz.newValue(nme.getterToLocal(mixinMember.name.toTermName), mixinMember.pos).setInfo(mixinMember.tpe.resultType) } sym updateInfo mixinMember.tpe.resultType // info at current phase @@ -379,35 +376,34 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { var parents1 = parents var decls1 = decls if (!clazz.isPackageClass) { - afterMixin(clazz.owner.info) + exitingMixin(clazz.owner.info) if (clazz.isImplClass) { clazz setFlag lateMODULE var sourceModule = clazz.owner.info.decls.lookup(sym.name.toTermName) - if (sourceModule != NoSymbol) { - sourceModule setPos sym.pos - if (sourceModule.flags != MODULE) { - log("!!! Directly setting sourceModule flags from %s to MODULE".format(flagsToString(sourceModule.flags))) - sourceModule.flags = MODULE - } - } - else { + if (sourceModule == NoSymbol) { sourceModule = ( clazz.owner.newModuleSymbol(sym.name.toTermName, sym.pos, MODULE) setModuleClass sym.asInstanceOf[ClassSymbol] ) clazz.owner.info.decls enter sourceModule } + else { + sourceModule setPos sym.pos + if (sourceModule.flags != MODULE) { + log("!!! Directly setting sourceModule flags from %s to MODULE".format(sourceModule.flagString)) + sourceModule.flags = MODULE + } + } sourceModule setInfo sym.tpe // Companion module isn't visible for anonymous class at this point anyway - assert(clazz.sourceModule != NoSymbol || clazz.isAnonymousClass, - clazz + " has no sourceModule: sym = " + sym + " sym.tpe = " + sym.tpe) + assert(clazz.sourceModule != NoSymbol || clazz.isAnonymousClass, s"$clazz has no sourceModule: $sym ${sym.tpe}") parents1 = List() decls1 = newScopeWith(decls.toList filter isImplementedStatically: _*) } else if (!parents.isEmpty) { parents1 = parents.head :: (parents.tail map toInterface) } } - //decls1 = atPhase(phase.next)(newScopeWith(decls1.toList: _*))//debug + //decls1 = enteringPhase(phase.next)(newScopeWith(decls1.toList: _*))//debug if ((parents1 eq parents) && (decls1 eq decls)) tp else ClassInfoType(parents1, decls1, clazz) @@ -437,7 +433,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { tree match { case Assign(lhs, rhs) => traverse(rhs) // assignments don't count case _ => - if (tree.hasSymbol && tree.symbol != NoSymbol) { + if (tree.hasSymbolField && tree.symbol != NoSymbol) { val sym = tree.symbol if ((sym.hasAccessorFlag || (sym.isTerm && !sym.isMethod)) && sym.isPrivate @@ -515,7 +511,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * - create a new method definition that also has a `self` parameter * (which comes first) Iuli: this position is assumed by tail call elimination * on a different receiver. Storing a new 'this' assumes it is located at - * index 0 in the local variable table. See 'STORE_THIS' and GenJVM/GenMSIL. + * index 0 in the local variable table. See 'STORE_THIS' and GenASM. * - Map implementation class types in type-apply's to their interfaces * - Remove all fields in implementation classes */ @@ -524,7 +520,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { tree match { case Template(parents, self, body) => localTyper = erasure.newTyper(rootContext.make(tree, currentOwner)) - afterMixin(currentOwner.owner.info)//todo: needed? + exitingMixin(currentOwner.owner.info)//todo: needed? if (!currentOwner.isTrait && !isPrimitiveValueClass(currentOwner)) addMixedinMembers(currentOwner, unit) @@ -543,17 +539,23 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { else EmptyTree } else { - if (currentOwner.isTrait && sym.isSetter && !beforePickler(sym.isDeferred)) { + if (currentOwner.isTrait && sym.isSetter && !enteringPickler(sym.isDeferred)) { sym.addAnnotation(TraitSetterAnnotationClass) } tree } + // !!! What is this doing, and why is it only looking for exactly + // one type parameter? It would seem to be + // "Map implementation class types in type-apply's to their interfaces" + // from the comment on preTransform, but is there some way we should know + // that impl class types in type applies can only appear in single + // type parameter type constructors? case Apply(tapp @ TypeApply(fn, List(arg)), List()) => if (arg.tpe.typeSymbol.isImplClass) { val ifacetpe = toInterface(arg.tpe) - arg.tpe = ifacetpe - tapp.tpe = MethodType(List(), ifacetpe) - tree.tpe = ifacetpe + arg setType ifacetpe + tapp setType MethodType(Nil, ifacetpe) + tree setType ifacetpe } tree case ValDef(_, _, _, _) if currentOwner.isImplClass => @@ -590,8 +592,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { tree } - /** Create a static reference to given symbol <code>sym</code> of the - * form <code>M.sym</code> where M is the symbol's implementation module. + /** Create a static reference to given symbol `sym` of the + * form `M.sym` where M is the symbol's implementation module. */ private def staticRef(sym: Symbol): Tree = { sym.owner.info //todo: needed? @@ -673,8 +675,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def addValDef(sym: Symbol, rhs: Tree = EmptyTree) = addDef(position(sym), ValDef(sym, rhs)) /** Add `newdefs` to `stats`, removing any abstract method definitions - * in <code>stats</code> that are matched by some symbol defined in - * <code>newDefs</code>. + * in `stats` that are matched by some symbol defined in + * `newDefs`. */ def add(stats: List[Tree], newDefs: List[Tree]) = { val newSyms = newDefs map (_.symbol) @@ -702,7 +704,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val rhs0 = (Super(clazz, tpnme.EMPTY) DOT stat.symbol.alias)(vparams map (v => Ident(v.symbol)): _*) val rhs1 = localTyped(stat.pos, rhs0, stat.symbol.tpe.resultType) - deriveDefDef(stat)(_ => beforeMixin(transform(rhs1))) + deriveDefDef(stat)(_ => enteringMixin(transform(rhs1))) case _ => stat } @@ -713,7 +715,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ def bitmapFor(clazz0: Symbol, offset: Int, field: Symbol): Symbol = { val category = bitmapCategory(field) - val bitmapName = nme.newBitmapName(category, offset / flagsPerBitmap(field)) + val bitmapName = nme.newBitmapName(category, offset / flagsPerBitmap(field)).toTermName val sym = clazz0.info.decl(bitmapName) assert(!sym.isOverloaded, sym) @@ -721,7 +723,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def createBitmap: Symbol = { val bitmapKind = bitmapKindForCategory(category) val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo bitmapKind.tpe - beforeTyper(sym addAnnotation VolatileAttr) + enteringTyper(sym addAnnotation VolatileAttr) category match { case nme.BITMAP_TRANSIENT | nme.BITMAP_CHECKINIT_TRANSIENT => sym addAnnotation TransientAttr @@ -777,7 +779,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree, attrThis: Tree, args: List[Tree]): Symbol = { - val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), lzyVal.pos, PRIVATE) + val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, PRIVATE) val params = defSym newSyntheticValueParams args.map(_.symbol.tpe) defSym setInfoAndEnter MethodType(params, lzyVal.tpe.resultType) val rhs: Tree = (gen.mkSynchronizedCheck(attrThis, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) @@ -803,7 +805,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ class TreeSymSubstituterWithCopying(from: List[Symbol], to: List[Symbol]) extends TreeSymSubstituter(from, to) { override def transform(tree: Tree): Tree = - if (tree.hasSymbol && from.contains(tree.symbol)) + if (tree.hasSymbolField && from.contains(tree.symbol)) super.transform(tree.duplicate) else super.transform(tree.duplicate) @@ -870,7 +872,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val cond = Apply(Select(moduleVarRef, Object_eq), List(NULL)) mkFastPathBody(clazz, moduleSym, cond, List(assign), List(NULL), returnTree, attrThis, args) case _ => - abort("Invalid getter " + rhs + " for module in class " + clazz) + abort(s"Invalid getter $rhs for module in $clazz") } def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = { @@ -878,11 +880,11 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val bitmapSym = bitmapFor(clazz, offset, sym) val kind = bitmapKind(sym) val mask = maskForOffset(offset, sym, kind) - val msg = "Uninitialized field: " + unit.source + ": " + pos.line + val msg = s"Uninitialized field: ${unit.source}: ${pos.line}" val result = IF (mkTest(clazz, mask, bitmapSym, false, kind)) . THEN (retVal) . - ELSE (THROW(UninitializedErrorClass, LIT(msg))) + ELSE (Throw(NewFromConstructor(UninitializedFieldConstructor, LIT(msg)))) typedPos(pos)(BLOCK(result, retVal)) } @@ -978,12 +980,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def addInitBits(clazz: Symbol, rhs: Tree): Tree = new AddInitBitsTransformer(clazz) transform rhs - def isCheckInitField(field: Symbol) = - needsInitFlag(field) && !field.isDeferred - - def superClassesToCheck(clazz: Symbol) = - clazz.ancestors filterNot (_ hasFlag TRAIT | JAVA) - // begin addNewDefs /** Fill the map from fields to offset numbers. @@ -1044,16 +1040,17 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } // if class is not a trait add accessor definitions else if (!clazz.isTrait) { + // This needs to be a def to avoid sharing trees + def accessedRef = accessedReference(sym) if (sym.hasAccessorFlag && (!sym.isDeferred || sym.hasFlag(lateDEFERRED))) { // add accessor definitions addDefDef(sym, { - val accessedRef = accessedReference(sym) if (sym.isSetter) { if (isOverriddenSetter(sym)) UNIT else accessedRef match { - case Literal(_) => accessedRef - case _ => - val init = Assign(accessedRef, Ident(sym.firstParam)) + case ref @ Literal(_) => ref + case ref => + val init = Assign(ref, Ident(sym.firstParam)) val getter = sym.getter(clazz) if (!needsInitFlag(getter)) init @@ -1068,11 +1065,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } else if (sym.isModule && !(sym hasFlag LIFTED | BRIDGE)) { // add modules - val vdef = gen.mkModuleVarDef(sym) - addDef(position(sym), vdef) + val vsym = sym.owner.newModuleVarSymbol(sym) + addDef(position(sym), ValDef(vsym)) - val rhs = gen.newModule(sym, vdef.symbol.tpe) - val assignAndRet = gen.mkAssignAndReturn(vdef.symbol, rhs) + // !!! TODO - unravel the enormous duplication between this code and + // eliminateModuleDefs in RefChecks. + val rhs = gen.newModule(sym, vsym.tpe) + val assignAndRet = gen.mkAssignAndReturn(vsym, rhs) val attrThis = gen.mkAttributedThis(clazz) val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet, sym, List()) @@ -1090,7 +1089,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // add forwarders assert(sym.alias != NoSymbol, sym) // debuglog("New forwarder: " + sym.defString + " => " + sym.alias.defString) - if (!sym.isTermMacro) addDefDef(sym, Apply(staticRef(sym.alias), gen.mkAttributedThis(clazz) :: sym.paramss.head.map(Ident))) + if (!sym.isMacro) addDefDef(sym, Apply(staticRef(sym.alias), gen.mkAttributedThis(clazz) :: sym.paramss.head.map(Ident))) } } } @@ -1135,7 +1134,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // change every node type that refers to an implementation class to its // corresponding interface, unless the node's symbol is an implementation class. if (tree.tpe.typeSymbol.isImplClass && ((sym eq null) || !sym.isImplClass)) - tree.tpe = toInterface(tree.tpe) + tree modifyType toInterface tree match { case templ @ Template(parents, self, body) => @@ -1151,9 +1150,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { qual case Apply(Select(qual, _), args) => - /** Changes <code>qual.m(args)</code> where m refers to an implementation + /** Changes `qual.m(args)` where m refers to an implementation * class method to Q.m(S, args) where Q is the implementation module of - * <code>m</code> and S is the self parameter for the call, which + * `m` and S is the self parameter for the call, which * is determined as follows: * - if qual != super, qual itself * - if qual == super, and we are in an implementation class, @@ -1164,7 +1163,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def implSym = implClass(sym.owner).info.member(sym.name) assert(target ne NoSymbol, List(sym + ":", sym.tpe, sym.owner, implClass(sym.owner), implSym, - beforePrevPhase(implSym.tpe), phase) mkString " " + enteringPrevPhase(implSym.tpe), phase) mkString " " ) typedPos(tree.pos)(Apply(staticRef(target), transformSuper(qual) :: args)) } @@ -1193,7 +1192,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { typedPos(tree.pos)((transformSuper(qual) DOT sym1)()) } else { - staticCall(beforePrevPhase(sym.overridingSymbol(implClass(sym.owner)))) + staticCall(enteringPrevPhase(sym.overridingSymbol(implClass(sym.owner)))) } } else { @@ -1211,7 +1210,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { tree case Select(qual, name) if sym.owner.isImplClass && !isStaticOnly(sym) => - assert(!sym.isMethod, "no method allowed here: %s%s %s".format(sym, sym.isImplOnly, flagsToString(sym.flags))) + assert(!sym.isMethod, "no method allowed here: %s%s %s".format(sym, sym.isImplOnly, sym.flagString)) // refer to fields in some implementation class via an abstract // getter in the interface. val iface = toInterface(sym.owner.tpe).typeSymbol @@ -1258,7 +1257,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val tree1 = super.transform(preTransform(tree)) // localTyper needed when not flattening inner classes. parts after an // inner class will otherwise be typechecked with a wrong scope - try afterMixin(postTransform(tree1)) + try exitingMixin(postTransform(tree1)) finally localTyper = saved } } diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index 67be81bd3c..28e6e3be26 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -31,11 +31,11 @@ abstract class OverridingPairs { private val self = base.thisType /** Symbols to exclude: Here these are constructors, private locals, - * and bridges. But it may be refined in subclasses. + * and hidden symbols, including bridges. But it may be refined in subclasses. * */ protected def exclude(sym: Symbol): Boolean = - sym.isConstructor || sym.isPrivateLocal || sym.hasFlag(BRIDGE) + sym.isConstructor || sym.isPrivateLocal || sym.isArtifact /** The parents of base (may also be refined). */ diff --git a/src/compiler/scala/tools/nsc/transform/PostErasure.scala b/src/compiler/scala/tools/nsc/transform/PostErasure.scala index 3ef32caa2c..a8dc47046b 100644 --- a/src/compiler/scala/tools/nsc/transform/PostErasure.scala +++ b/src/compiler/scala/tools/nsc/transform/PostErasure.scala @@ -24,7 +24,7 @@ trait PostErasure extends InfoTransform with TypingTransformers { case ConstantType(Constant(tp: Type)) => ConstantType(Constant(apply(tp))) case ErasedValueType(tref) => - atPhase(currentRun.erasurePhase)(erasure.erasedValueClassArg(tref)) + enteringPhase(currentRun.erasurePhase)(erasure.erasedValueClassArg(tref)) case _ => mapOver(tp) } } @@ -39,7 +39,7 @@ trait PostErasure extends InfoTransform with TypingTransformers { Apply(sel @ Select( Apply(Select(New(tpt), nme.CONSTRUCTOR), List(arg)), acc), List()) - if atPhase(currentRun.erasurePhase) { + if enteringPhase(currentRun.erasurePhase) { tpt.tpe.typeSymbol.isDerivedValueClass && sel.symbol == tpt.tpe.typeSymbol.derivedValueClassUnbox } => @@ -50,7 +50,7 @@ trait PostErasure extends InfoTransform with TypingTransformers { Apply(Select(New(tpt1), nme.CONSTRUCTOR), List(arg1)), cmp), List(Apply(Select(New(tpt2), nme.CONSTRUCTOR), List(arg2)))) - if atPhase(currentRun.erasurePhase) { + if enteringPhase(currentRun.erasurePhase) { tpt1.tpe.typeSymbol.isDerivedValueClass && (sel.symbol == Object_== || sel.symbol == Object_!=) && tpt2.tpe.typeSymbol == tpt1.tpe.typeSymbol diff --git a/src/compiler/scala/tools/nsc/transform/SampleTransform.scala b/src/compiler/scala/tools/nsc/transform/SampleTransform.scala index 44d8860916..cffb483072 100644 --- a/src/compiler/scala/tools/nsc/transform/SampleTransform.scala +++ b/src/compiler/scala/tools/nsc/transform/SampleTransform.scala @@ -11,9 +11,8 @@ package transform abstract class SampleTransform extends Transform { // inherits abstract value `global` and class `Phase` from Transform - import global._ // the global environment - import definitions._ // standard classes and methods - import typer.{typed, atOwner} // methods to type trees + import global._ // the global environment + import typer.typed // method to type trees /** the following two members override abstract members in Transform */ val phaseName: String = "sample-phase" diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 232148676c..0cd7f516ef 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -10,6 +10,7 @@ import scala.tools.nsc.symtab.Flags import scala.collection.{ mutable, immutable } import scala.language.postfixOps import scala.language.existentials +import scala.annotation.tailrec /** Specialize code on types. * @@ -101,7 +102,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Concrete methods that use a specialized type, or override such methods. */ private val concreteSpecMethods = perRunCaches.newWeakSet[Symbol]() - private def specializedTypes(tps: List[Symbol]) = tps filter (_.isSpecialized) private def specializedOn(sym: Symbol): List[Symbol] = { sym getAnnotation SpecializedClass match { case Some(AnnotationInfo(_, Nil, _)) => specializableTypes.map(_.typeSymbol) @@ -119,6 +119,22 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } + @annotation.tailrec private def findSymbol[T](candidates: List[T], f: T => Symbol): Symbol = { + if (candidates.isEmpty) NoSymbol + else f(candidates.head) match { + case NoSymbol => findSymbol(candidates.tail, f) + case sym => sym + } + } + private def hasNewParents(tree: Tree) = { + val parents = tree.symbol.info.parents + val prev = enteringPrevPhase(tree.symbol.info.parents) + (parents != prev) && { + debuglog(s"$tree parents changed from: $prev to: $parents") + true + } + } + // If we replace `isBoundedGeneric` with (tp <:< AnyRefClass.tpe), // then pos/spec-List.scala fails - why? Does this kind of check fail // for similar reasons? Does `sym.isAbstractType` make a difference? @@ -169,16 +185,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } - /** Returns the generic class that was specialized to 'sClass', or - * 'sClass' itself if sClass is not a specialized subclass. - */ - def genericClass(sClass: Symbol): Symbol = - if (sClass.isSpecialized) sClass.superClass - else sClass - case class Overload(sym: Symbol, env: TypeEnv) { override def toString = "specialized overload " + sym + " in " + env - def matchesSym(other: Symbol) = sym.tpe =:= other.tpe + def matchesSym(sym1: Symbol) = sym.info =:= sym1.info def matchesEnv(env1: TypeEnv) = TypeEnv.includes(env, env1) } private def newOverload(method: Symbol, specializedMethod: Symbol, env: TypeEnv) = { @@ -207,8 +216,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * type bounds of other @specialized type parameters (and not in its result type). */ def degenerate = false - - def isAccessor = false } /** Symbol is a special overloaded method of 'original', in the environment env. */ @@ -226,11 +233,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def target = t } - /** Symbol is a specialized accessor for the `target` field. */ - case class SpecializedAccessor(target: Symbol) extends SpecializedInfo { - override def isAccessor = true + /** Symbol is a special overload of the super accessor. */ + case class SpecialSuperAccessor(t: Symbol) extends SpecializedInfo { + def target = t } + /** Symbol is a specialized accessor for the `target` field. */ + case class SpecializedAccessor(target: Symbol) extends SpecializedInfo { } + /** Symbol is a specialized method whose body should be the target's method body. */ case class Implementation(target: Symbol) extends SpecializedInfo @@ -268,9 +278,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def specializedParams(sym: Symbol): List[Symbol] = sym.info.typeParams filter (_.isSpecialized) - def splitParams(tps: List[Symbol]) = - tps partition (_.isSpecialized) - /** Given an original class symbol and a list of types its type parameters are instantiated at * returns a list of type parameters that should remain in the TypeRef when instantiating a * specialized type. @@ -317,11 +324,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ private def specializedName(name: Name, types1: List[Type], types2: List[Type]): TermName = { if (nme.INITIALIZER == name || (types1.isEmpty && types2.isEmpty)) - name + name.toTermName else if (nme.isSetterName(name)) - nme.getterToSetter(specializedName(nme.setterToGetter(name), types1, types2)) + nme.getterToSetter(specializedName(nme.setterToGetter(name.toTermName), types1, types2)) else if (nme.isLocalName(name)) - nme.getterToLocal(specializedName(nme.localToGetter(name), types1, types2)) + nme.getterToLocal(specializedName(nme.localToGetter(name.toTermName), types1, types2)) else { val (base, cs, ms) = nme.splitSpecializedName(name) newTermName(base.toString + "$" @@ -397,11 +404,16 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case _ => false }) def specializedTypeVars(tpes: List[Type]): immutable.Set[Symbol] = { - val buf = Set.newBuilder[Symbol] - tpes foreach (tp => buf ++= specializedTypeVars(tp)) - buf.result + @tailrec def loop(result: immutable.Set[Symbol], xs: List[Type]): immutable.Set[Symbol] = { + if (xs.isEmpty) result + else loop(result ++ specializedTypeVars(xs.head), xs.tail) + } + loop(immutable.Set.empty, tpes) } - def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = beforeTyper(specializedTypeVars(sym.info)) + def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = ( + if (definitions.neverHasTypeParameters(sym)) immutable.Set.empty + else enteringTyper(specializedTypeVars(sym.info)) + ) /** Return the set of @specialized type variables mentioned by the given type. * It only counts type variables that appear: @@ -412,7 +424,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def specializedTypeVars(tpe: Type): immutable.Set[Symbol] = tpe match { case TypeRef(pre, sym, args) => if (sym.isAliasType) - specializedTypeVars(tpe.normalize) + specializedTypeVars(tpe.dealiasWiden) else if (sym.isTypeParameter && sym.isSpecialized || (sym.isTypeSkolem && sym.deSkolemize.isSpecialized)) Set(sym) else if (sym == ArrayClass) @@ -430,7 +442,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case AnnotatedType(_, tp, _) => specializedTypeVars(tp) case TypeBounds(lo, hi) => specializedTypeVars(lo :: hi :: Nil) case RefinedType(parents, _) => parents flatMap specializedTypeVars toSet - case _ => Set() + case _ => immutable.Set.empty } /** Returns the type parameter in the specialized class `sClass` that corresponds to type parameter @@ -525,7 +537,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long, newName: Name = null) = member.cloneSymbol(sClass, flagFn(member.flags | SPECIALIZED), newName) - sClass.sourceFile = clazz.sourceFile + sClass.associatedFile = clazz.sourceFile currentRun.symSource(sClass) = clazz.sourceFile // needed later on by mixin val env = mapAnyRefsInSpecSym(env0, clazz, sClass) @@ -537,7 +549,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { var newClassTParams: List[Symbol] = Nil // unspecialized type parameters of 'specializedClass' (cloned) // has to be a val in order to be computed early. It is later called - // within 'atPhase(next)', which would lead to an infinite cycle otherwise + // within 'enteringPhase(next)', which would lead to an infinite cycle otherwise val specializedInfoType: Type = { oldClassTParams = survivingParams(clazz.info.typeParams, env) newClassTParams = produceTypeParameters(oldClassTParams, sClass, env) map subst(env) @@ -557,7 +569,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { var res: List[Type] = Nil // log(specializedClass + ": seeking specialized parents of class with parents: " + parents.map(_.typeSymbol)) for (p <- parents) { - val stp = afterSpecialize(specializedType(p)) + val stp = exitingSpecialize(specializedType(p)) if (stp != p) if (p.typeSymbol.isTrait) res ::= stp else if (currentRun.compiles(clazz)) @@ -567,7 +579,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { res } - var parents = List(applyContext(beforeTyper(clazz.tpe))) + var parents = List(applyContext(enteringTyper(clazz.tpe_*))) // log("!!! Parents: " + parents + ", sym: " + parents.map(_.typeSymbol)) if (parents.head.typeSymbol.isTrait) parents = parents.head.parents.head :: parents @@ -589,7 +601,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { GenPolyType(newClassTParams, ClassInfoType(parents ::: extraSpecializedMixins, decls1, sClass)) } - afterSpecialize(sClass setInfo specializedInfoType) + exitingSpecialize(sClass setInfo specializedInfoType) val fullEnv = outerEnv ++ env /** Enter 'sym' in the scope of the current specialized class. It's type is @@ -683,7 +695,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def mkAccessor(field: Symbol, name: Name) = { val newFlags = (SPECIALIZED | m.getter(clazz).flags) & ~(LOCAL | CASEACCESSOR | PARAMACCESSOR) // we rely on the super class to initialize param accessors - val sym = sClass.newMethod(name, field.pos, newFlags) + val sym = sClass.newMethod(name.toTermName, field.pos, newFlags) info(sym) = SpecializedAccessor(field) sym } @@ -702,7 +714,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // debuglog("m: " + m + " isLocal: " + nme.isLocalName(m.name) + " specVal: " + specVal.name + " isLocal: " + nme.isLocalName(specVal.name)) if (nme.isLocalName(m.name)) { - val specGetter = mkAccessor(specVal, nme.localToGetter(specVal.name)) setInfo MethodType(Nil, specVal.info) + val specGetter = mkAccessor(specVal, nme.localToGetter(specVal.name.toTermName)) setInfo MethodType(Nil, specVal.info) val origGetter = overrideIn(sClass, m.getter(clazz)) info(origGetter) = Forward(specGetter) enterMember(specGetter) @@ -777,7 +789,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (existing != NoSymbol) clazz.owner.info.decls.unlink(existing) - afterSpecialize(clazz.owner.info.decls enter spc) //!!! assumes fully specialized classes + exitingSpecialize(clazz.owner.info.decls enter spc) //!!! assumes fully specialized classes } if (subclasses.nonEmpty) clazz.resetFlag(FINAL) cleanAnyRefSpecCache(clazz, decls1) @@ -795,7 +807,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ private def normalizeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv): List[Symbol] = { sym :: ( - if (!sym.isMethod || beforeTyper(sym.typeParams.isEmpty)) Nil + if (!sym.isMethod || enteringTyper(sym.typeParams.isEmpty)) Nil else { // debuglog("normalizeMember: " + sym.fullNameAsName('.').decode) var specializingOn = specializedParams(sym) @@ -874,6 +886,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } val specMember = subst(outerEnv)(specializedOverload(owner, sym, spec)) + owner.info.decls.enter(specMember) typeEnv(specMember) = typeEnv(sym) ++ outerEnv ++ spec wasSpecializedForTypeVars(specMember) ++= spec collect { case (s, tp) if s.tpe == tp => s } @@ -902,10 +915,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } /** Return the specialized overload of `m`, in the given environment. */ - private def specializedOverload(owner: Symbol, sym: Symbol, env: TypeEnv): Symbol = { + private def specializedOverload(owner: Symbol, sym: Symbol, env: TypeEnv, nameSymbol: Symbol = NoSymbol): Symbol = { val newFlags = (sym.flags | SPECIALIZED) & ~(DEFERRED | CASEACCESSOR) // this method properly duplicates the symbol's info - ( sym.cloneSymbol(owner, newFlags, newName = specializedName(sym, env)) + val specname = specializedName(nameSymbol orElse sym, env) + ( sym.cloneSymbol(owner, newFlags, newName = specname) modifyInfo (info => subst(env, info.asSeenFrom(owner.thisType, sym.owner))) ) } @@ -952,7 +966,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { checkOverriddenTParams(overridden) val env = unify(overridden.info, overriding.info, emptyEnv, false, true) - def atNext = afterSpecialize(overridden.owner.info.decl(specializedName(overridden, env))) + def atNext = exitingSpecialize(overridden.owner.info.decl(specializedName(overridden, env))) if (TypeEnv.restrict(env, stvars).nonEmpty && TypeEnv.isValid(env, overridden) && atNext != NoSymbol) { debuglog(" " + pp(env) + " found " + atNext) @@ -965,19 +979,37 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } (clazz.info.decls flatMap { overriding => needsSpecialOverride(overriding) match { - case (NoSymbol, _) => None + case (NoSymbol, _) => + if (overriding.isSuperAccessor) { + val alias = overriding.alias + debuglog("checking special overload for super accessor: %s, alias for %s".format(overriding.fullName, alias.fullName)) + needsSpecialOverride(alias) match { + case nope @ (NoSymbol, _) => None + case (overridden, env) => + val om = specializedOverload(clazz, overriding, env, overridden) + om.setName(nme.superName(om.name)) + om.asInstanceOf[TermSymbol].setAlias(info(alias).target) + om.owner.info.decls.enter(om) + info(om) = SpecialSuperAccessor(om) + om.makeNotPrivate(om.owner) + newOverload(overriding, om, env) + Some(om) + } + } else None case (overridden, env) => val om = specializedOverload(clazz, overridden, env) + clazz.info.decls.enter(om) foreachWithIndex(om.paramss) { (params, i) => foreachWithIndex(params) { (param, j) => param.name = overriding.paramss(i)(j).name // SI-6555 Retain the parameter names from the subclass. } } debuglog("specialized overload %s for %s in %s: %s".format(om, overriding.name.decode, pp(env), om.info)) + if (overriding.isAbstractOverride) om.setFlag(ABSOVERRIDE) typeEnv(om) = env addConcreteSpecMethod(overriding) info(om) = ( - if (overriding.isDeferred) { // abstract override + if (overriding.isDeferred) { // abstract override debuglog("abstract override " + overriding.fullName + " with specialized " + om.fullName) Forward(overriding) } @@ -998,7 +1030,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } ) newOverload(overriding, om, env) - ifDebug(afterSpecialize(assert( + ifDebug(exitingSpecialize(assert( overridden.owner.info.decl(om.name) != NoSymbol, "Could not find " + om.name + " in " + overridden.owner.info.decls)) ) @@ -1027,7 +1059,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (isPrimitiveValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1)) env + ((sym1, tp2)) else if (isSpecializedAnyRefSubtype(tp2, sym1)) - env + ((sym1, tp2)) // env + ((sym1, AnyRefClass.tpe)) + env + ((sym1, tp2)) else if (strict) unifyError(tp1, tp2) else @@ -1084,10 +1116,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } - /** Apply type bindings in the given environment `env` to all declarations. */ - private def subst(env: TypeEnv, decls: List[Symbol]): List[Symbol] = - decls map subst(env) - /** Apply the type environment 'env' to the given type. All type * bindings are supposed to be to primitive types. A type variable * that is annotated with 'uncheckedVariance' is mapped to the corresponding @@ -1114,33 +1142,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { private def subst(env: TypeEnv)(decl: Symbol): Symbol = decl modifyInfo (info => - if (decl.isConstructor) MethodType(subst(env, info).params, decl.owner.tpe) + if (decl.isConstructor) MethodType(subst(env, info).params, decl.owner.tpe_*) else subst(env, info) ) - /** Checks if the type parameter symbol is not specialized - * and is used as type parameters when extending a class with a specialized - * type parameter. - * At some point we may remove this restriction. - * - * Example: - * - * class Base[@specialized T] - * class Derived[T] extends Base[T] // a non-specialized T is - * // used as a type param for Base - * // -> returning true - */ - private def notSpecializedIn(tsym: Symbol, supertpe: Type) = supertpe match { - case TypeRef(_, supersym, supertargs) => - val tspec = specializedOn(tsym).toSet - for (supt <- supersym.typeParams) { - val supspec = specializedOn(supt).toSet - if (tspec != supspec && tspec.subsetOf(supspec)) - reporter.error(tsym.pos, "Type parameter has to be specialized at least for the same types as in the superclass. Missing types: " + (supspec.diff(tspec)).mkString(", ")) - } - case _ => //log("nope") - } - private def unspecializableClass(tp: Type) = ( definitions.isRepeatedParamType(tp) // ??? || tp.typeSymbol.isJavaDefined @@ -1156,7 +1161,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case cinfo @ ClassInfoType(parents, decls, clazz) if !unspecializableClass(cinfo) => val tparams = tpe.typeParams if (tparams.isEmpty) - afterSpecialize(parents map (_.typeSymbol.info)) + exitingSpecialize(parents map (_.typeSymbol.info)) val parents1 = parents mapConserve specializedType if (parents ne parents1) { @@ -1177,7 +1182,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * * A conflicting type environment could still be satisfiable. */ - def conflicting(env: TypeEnv) = !nonConflicting(env) def nonConflicting(env: TypeEnv) = env forall { case (tvar, tpe) => (subst(env, tvar.info.bounds.lo) <:< tpe) && (tpe <:< subst(env, tvar.info.bounds.hi)) } @@ -1247,9 +1251,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { class BodyDuplicator(_context: Context) extends super.BodyDuplicator(_context) { override def castType(tree: Tree, pt: Type): Tree = { - // log(" expected type: " + pt) - // log(" tree type: " + tree.tpe) - tree.tpe = if (tree.tpe != null) fixType(tree.tpe) else null + tree modifyType fixType // log(" tree type: " + tree.tpe) val ntree = if (tree.tpe != null && !(tree.tpe <:< pt)) { val casttpe = CastMap(tree.tpe) @@ -1257,8 +1259,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { else if (casttpe <:< CastMap(pt)) gen.mkCast(tree, pt) else tree } else tree - ntree.tpe = null - ntree + + ntree.clearType() } } @@ -1300,7 +1302,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (sym.isPrivate) debuglog( "seeing private member %s, currentClass: %s, owner: %s, isAccessible: %b, isLocalName: %b".format( sym, currentClass, sym.owner.enclClass, isAccessible(sym), nme.isLocalName(sym.name)) - ) + ) if (shouldMakePublic(sym) && !isAccessible(sym)) { debuglog("changing private flag of " + sym) sym.makeNotPrivate(sym.owner) @@ -1385,59 +1387,72 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def transform1(tree: Tree) = { val symbol = tree.symbol - /** The specialized symbol of 'tree.symbol' for tree.tpe, if there is one */ - def specSym(qual: Tree): Option[Symbol] = { + def specSym(qual: Tree): Symbol = { val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) - debuglog("[specSym] checking for rerouting: %s with \n\tsym.tpe: %s, \n\ttree.tpe: %s \n\tenv: %s \n\tname: %s" - .format(tree, symbol.tpe, tree.tpe, env, specializedName(symbol, env))) - if (!env.isEmpty) { // a method? - val specCandidates = qual.tpe.member(specializedName(symbol, env)) - val specMember = specCandidates suchThat { s => - doesConform(symbol, tree.tpe, qual.tpe.memberType(s), env) - } + def isMatch(member: Symbol) = ( + doesConform(symbol, tree.tpe, qual.tpe memberType member, env) + && TypeEnv.includes(typeEnv(member), env) + ) + if (env.isEmpty) NoSymbol + else qual.tpe member specializedName(symbol, env) suchThat isMatch + } - debuglog("[specSym] found: " + specCandidates.tpe + ", instantiated as: " + tree.tpe) - debuglog("[specSym] found specMember: " + specMember) - if (specMember ne NoSymbol) - if (TypeEnv.includes(typeEnv(specMember), env)) Some(specMember) - else { - debuglog("wrong environments for specialized member: \n\ttypeEnv(%s) = %s\n\tenv = %s".format(specMember, typeEnv(specMember), env)) - None - } - else None - } else None + def matchingSymbolInPrefix(pre: Type, member: Symbol, env: TypeEnv): Symbol = { + pre member specializedName(member, env) suchThat (_.tpe matches subst(env, member.tpe)) + } + + def transformSelect(sel: Select) = { + val Select(qual, name) = sel + debuglog(s"specializing Select(sym=${symbol.defString}, tree.tpe=${tree.tpe})") + + val qual1 = transform(qual) + def copySelect = treeCopy.Select(tree, qual1, name) + def newSelect(member: Symbol) = atPos(tree.pos)(Select(qual1, member)) + def typedOp(member: Symbol) = localTyper typedOperator newSelect(member) + def typedTree(member: Symbol) = localTyper typed newSelect(member) + + val ignoreEnv = specializedTypeVars(symbol.info).isEmpty || name == nme.CONSTRUCTOR + if (ignoreEnv) overloads(symbol) find (_ matchesSym symbol) match { + case Some(Overload(member, _)) => typedOp(member) + case _ => copySelect + } + else { + val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) + overloads(symbol) find (_ matchesEnv env) match { + case Some(Overload(member, _)) => typedOp(member) + case _ => + matchingSymbolInPrefix(qual1.tpe, symbol, env) match { + case NoSymbol => copySelect + case member if member.isMethod => typedOp(member) + case member => typedTree(member) + } + } + } } curTree = tree tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => def transformNew = { - debuglog("Attempting to specialize new %s(%s)".format(tpt, args.mkString(", "))) - val found = findSpec(tpt.tpe) - if (found.typeSymbol ne tpt.tpe.typeSymbol) { - // the ctor can be specialized - debuglog("** instantiated specialized type: " + found) - reportError { - localTyper.typedPos(tree.pos)(New(found, transformTrees(args): _*)) - } { - _ => super.transform(tree) + debuglog("Attempting to specialize new %s(%s)".format(tpt, args.mkString(", "))) + val found = specializedType(tpt.tpe) + if (found.typeSymbol ne tpt.tpe.typeSymbol) { // the ctor can be specialized + val inst = New(found, transformTrees(args): _*) + reportError(localTyper.typedPos(tree.pos)(inst))(_ => super.transform(tree)) } - } else super.transform(tree) + else + super.transform(tree) } transformNew - case Apply(sel @ Select(sup @ Super(qual, name), name1), args) - if (sup.symbol.info.parents != beforePrevPhase(sup.symbol.info.parents)) => + case Apply(sel @ Select(sup @ Super(qual, name), name1), args) if hasNewParents(sup) => def transformSuperApply = { - - def parents = sup.symbol.info.parents - debuglog(tree + " parents changed from: " + beforePrevPhase(parents) + " to: " + parents) - - val res = localTyper.typed( - Apply(Select(Super(qual, name) setPos sup.pos, name1) setPos sel.pos, transformTrees(args)) setPos tree.pos) - debuglog("retyping call to super, from: " + symbol + " to " + res.symbol) - res + val sup1 = Super(qual, name) setPos sup.pos + val tree1 = Apply(Select(sup1, name1) setPos sel.pos, transformTrees(args)) + val res = localTyper.typedPos(tree.pos)(tree1) + debuglog(s"retyping call to super, from: $symbol to ${res.symbol}") + res } transformSuperApply @@ -1448,7 +1463,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val qual1 = transform(qual) // log(">>> TypeApply: " + tree + ", qual1: " + qual1) specSym(qual1) match { - case Some(specMember) => + case NoSymbol => + // See pos/exponential-spec.scala - can't call transform on the whole tree again. + treeCopy.TypeApply(tree, treeCopy.Select(sel, qual1, name), transformTrees(targs)) + case specMember => debuglog("found " + specMember.fullName) ifDebug(assert(symbol.info.typeParams.length == targs.length, symbol.info.typeParams + " / " + targs)) @@ -1458,7 +1476,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } // 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))) + devWarning("Type args to be applied, but symbol says no parameters: " + ((specMember.defString, residualTargs))) localTyper.typed(sel) } else { @@ -1470,11 +1488,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { debuglog("rewrote " + tree + " to " + tree1) localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method } - - case None => - treeCopy.TypeApply(tree, treeCopy.Select(sel, qual1, name), super.transformTrees(targs)) - // See pos/exponential-spec.scala - can't call transform on the whole tree again. - // super.transform(tree) } } transformTypeApply @@ -1484,36 +1497,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.\n" + pos.lineContent) tree - case Select(qual, name) if name != nme.CONSTRUCTOR && specializedTypeVars(symbol.info).nonEmpty => - debuglog("specializing Select %s [tree.tpe: %s]".format(symbol.defString, tree.tpe)) - val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) - if (env.isEmpty) super.transform(tree) - else { - val qual1 = transform(qual) - def reselect(member: Symbol) = { - val newSelect = atPos(tree.pos)(Select(qual1, member)) - if (member.isMethod) localTyper typedOperator newSelect - else localTyper typed newSelect - } - overloads(symbol) find (_ matchesEnv env) match { - case Some(Overload(member, _)) => reselect(member) - case _ => - val specMember = qual1.tpe.member(specializedName(symbol, env)).suchThat(_.tpe matches subst(env, symbol.tpe)) - if (specMember ne NoSymbol) - reselect(specMember) - else - treeCopy.Select(tree, qual1, name) - } - } - case Select(qual, _) => - overloads(symbol) find (_ matchesSym symbol) match { - case Some(Overload(member, _)) => - val newTree = Select(transform(qual), member) - debuglog(s"** routing $tree to ${member.fullName} tree: $newTree") - localTyper.typedOperator(atPos(tree.pos)(newTree)) - case None => - super.transform(tree) - } + case sel @ Select(_, _) => + transformSelect(sel) case PackageDef(pid, stats) => tree.symbol.info // make sure specializations have been performed @@ -1538,47 +1523,37 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { transformTemplate case ddef @ DefDef(_, _, _, vparamss, _, _) if info.isDefinedAt(symbol) => - def transformDefDef = { - // log("--> method: " + ddef + " in " + ddef.symbol.owner + ", " + info(symbol)) - def reportTypeError(body: =>Tree) = reportError(body)(_ => ddef) - + def transformDefDef = { if (symbol.isConstructor) { - - val t = atOwner(symbol)(forwardCtorCall(tree.pos, gen.mkSuperSelect, vparamss, symbol.owner)) - + val t = atOwner(symbol)(forwardCtorCall(tree.pos, gen.mkSuperInitCall, vparamss, symbol.owner)) if (symbol.isPrimaryConstructor) localTyper.typedPos(symbol.pos)(deriveDefDef(tree)(_ => Block(List(t), Literal(Constant())))) else // duplicate the original constructor - reportTypeError(duplicateBody(ddef, info(symbol).target)) + reportError(duplicateBody(ddef, info(symbol).target))(_ => ddef) } else info(symbol) match { case Implementation(target) => assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName) // we have an rhs, specialize it - val tree1 = reportTypeError { - duplicateBody(ddef, target) - } + val tree1 = reportError(duplicateBody(ddef, target))(_ => ddef) debuglog("implementation: " + tree1) deriveDefDef(tree1)(transform) case NormalizedMember(target) => - val constraints = satisfiabilityConstraints(typeEnv(symbol)) - log("constraints: " + constraints) - if (target.isDeferred || constraints == None) { - deriveDefDef(tree)(_ => localTyper typed gen.mkSysErrorCall("Fatal error in code generation: this should never be called.")) - } else { - // we have an rhs, specialize it - val tree1 = reportTypeError { - duplicateBody(ddef, target, constraints.get) - } - debuglog("implementation: " + tree1) - deriveDefDef(tree1)(transform) + logResult("constraints")(satisfiabilityConstraints(typeEnv(symbol))) match { + case Some(constraint) if !target.isDeferred => + // we have an rhs, specialize it + val tree1 = reportError(duplicateBody(ddef, target, constraint))(_ => ddef) + debuglog("implementation: " + tree1) + deriveDefDef(tree1)(transform) + case _ => + deriveDefDef(tree)(_ => localTyper typed gen.mkSysErrorCall("Fatal error in code generation: this should never be called.")) } case SpecialOverride(target) => assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName) //debuglog("moving implementation, body of target " + target + ": " + body(target)) - debuglog("%s is param accessor? %b".format(ddef.symbol, ddef.symbol.isParamAccessor)) + log("%s is param accessor? %b".format(ddef.symbol, ddef.symbol.isParamAccessor)) // we have an rhs, specialize it val tree1 = addBody(ddef, target) (new ChangeOwnerTraverser(target, tree1.symbol))(tree1.rhs) @@ -1626,6 +1601,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case Abstract(targ) => debuglog("abstract: " + targ) localTyper.typed(deriveDefDef(tree)(rhs => rhs)) + + case SpecialSuperAccessor(targ) => + debuglog("special super accessor: " + targ + " for " + tree) + localTyper.typed(deriveDefDef(tree)(rhs => rhs)) } } transformDefDef @@ -1647,7 +1626,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { deriveValDef(newValDef)(transform) } transformValDef - case _ => super.transform(tree) } @@ -1684,7 +1662,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val symbol = tree.symbol debuglog("specializing body of" + symbol.defString) val DefDef(_, _, tparams, vparams :: Nil, tpt, _) = tree -// val (_, origtparams) = splitParams(source.typeParams) val env = typeEnv(symbol) val boundTvars = env.keySet val origtparams = source.typeParams.filter(tparam => !boundTvars(tparam) || !isPrimitiveValueType(env(tparam))) @@ -1711,8 +1688,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { false) // don't make private fields public val newBody = symSubstituter(body(source).duplicate) - tpt.tpe = tpt.tpe.substSym(oldtparams, newtparams) - + tpt modifyType (_.substSym(oldtparams, newtparams)) copyDefDef(tree)(vparamss = List(newSyms map ValDef), rhs = newBody) } @@ -1819,6 +1795,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * }} */ private def forwardCtorCall(pos: scala.reflect.internal.util.Position, receiver: Tree, paramss: List[List[ValDef]], clazz: Symbol): Tree = { + log(s"forwardCtorCall($pos, $receiver, $paramss, $clazz)") /** A constructor parameter `f` initializes a specialized field * iff: @@ -1855,16 +1832,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { //! TODO: make sure the param types are seen from the right prefix map2(fun.info.paramTypes, vparams)((tp, arg) => gen.maybeMkAsInstanceOf(Ident(arg), tp, arg.tpe)) ) - private def findSpec(tp: Type): Type = tp match { - case TypeRef(pre, sym, _ :: _) => specializedType(tp) - case _ => tp - } class SpecializationTransformer(unit: CompilationUnit) extends Transformer { informProgress("specializing " + unit) override def transform(tree: Tree) = { val resultTree = if (settings.nospecialization.value) tree - else afterSpecialize(specializeCalls(unit).transform(tree)) + else exitingSpecialize(specializeCalls(unit).transform(tree)) // Remove the final modifier and @inline annotation from anything in the // original class (since it's being overridden in at least onesubclass). @@ -1884,11 +1857,4 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { resultTree } } - - def printSpecStats() { - println(" concreteSpecMembers: %7d".format(concreteSpecMethods.size)) - println(" overloads: %7d".format(overloads.size)) - println(" typeEnv: %7d".format(typeEnv.size)) - println(" info: %7d".format(info.size)) - } } diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index a767850cba..c375bc4362 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -17,7 +17,7 @@ import Flags.SYNTHETIC abstract class TailCalls extends Transform { import global._ // the global environment import definitions._ // standard classes and methods - import typer.{ typed, typedPos } // methods to type trees + import typer.typedPos // methods to type trees val phaseName: String = "tailcalls" @@ -82,7 +82,7 @@ abstract class TailCalls extends Transform { * that label. * </p> * <p> - * Assumes: <code>Uncurry</code> has been run already, and no multiple + * Assumes: `Uncurry` has been run already, and no multiple * parameter lists exit. * </p> */ @@ -147,10 +147,9 @@ abstract class TailCalls extends Transform { } def enclosingType = method.enclClass.typeOfThis - def methodTypeParams = method.tpe.typeParams def isEligible = method.isEffectivelyFinal // @tailrec annotation indicates mandatory transformation - def isMandatory = method.hasAnnotation(TailrecClass) && !forMSIL + def isMandatory = method.hasAnnotation(TailrecClass) def isTransformed = isEligible && accessed(label) def tailrecFailure() = unit.error(failPos, "could not optimize @tailrec annotated " + method + ": " + failReason) @@ -230,7 +229,6 @@ abstract class TailCalls extends Transform { } else if (!matchesTypeArgs) failHere("it is called recursively with different type arguments") else if (receiver == EmptyTree) rewriteTailCall(This(currentClass)) - else if (forMSIL) fail("it cannot be optimized on MSIL") else if (!receiverIsSame) failHere("it changes type of 'this' on a polymorphic recursive call") else rewriteTailCall(receiver) } diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala index c7bc16f249..73f39225bd 100644 --- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala +++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala @@ -6,8 +6,6 @@ package scala.tools.nsc package transform -import scala.collection.{ mutable, immutable } - /** A base class for transforms. * A transform contains a compiler phase which applies a tree transformer. */ @@ -23,13 +21,11 @@ trait TypingTransformers { else analyzer.newTyper(analyzer.rootContext(unit, EmptyTree, true)) protected var curTree: Tree = _ - protected def typedPos(pos: Position)(tree: Tree) = localTyper typed { atPos(pos)(tree) } override final def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans) def atOwner[A](tree: Tree, owner: Symbol)(trans: => A): A = { val savedLocalTyper = localTyper -// println("transformer atOwner: " + owner + " isPackage? " + owner.isPackage) localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner) val result = super.atOwner(owner)(trans) localTyper = savedLocalTyper diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index e9f403aea0..413ef473c3 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -61,24 +61,6 @@ abstract class UnCurry extends InfoTransform // uncurry and uncurryType expand type aliases - /** Traverse tree omitting local method definitions. - * If a `return` is encountered, set `returnFound` to true. - * Used for MSIL only. - */ - private object lookForReturns extends Traverser { - var returnFound = false - override def traverse(tree: Tree): Unit = tree match { - case Return(_) => returnFound = true - case DefDef(_, _, _, _, _, _) => ; - case _ => super.traverse(tree) - } - def found(tree: Tree) = { - returnFound = false - traverse(tree) - returnFound - } - } - class UnCurryTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { private var needTryLift = false private var inPattern = false @@ -112,8 +94,6 @@ abstract class UnCurry extends InfoTransform private lazy val serialVersionUIDAnnotation = AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List()) - private var nprinted = 0 - // I don't have a clue why I'm catching TypeErrors here, but it's better // than spewing stack traces at end users for internal errors. Examples // which hit at this point should not be hard to come by, but the immediate @@ -134,7 +114,8 @@ abstract class UnCurry extends InfoTransform def isByNameRef(tree: Tree) = ( tree.isTerm && !byNameArgs(tree) - && tree.hasSymbolWhich(s => isByNameParamType(s.tpe)) + && (tree.symbol ne null) + && (isByName(tree.symbol)) ) /** Uncurry a type of a tree node. @@ -205,6 +186,9 @@ abstract class UnCurry extends InfoTransform val keyDef = ValDef(key, New(ObjectClass.tpe)) val tryCatch = Try(body, pat -> rhs) + for (Try(t, catches, _) <- body ; cdef <- catches ; if treeInfo catchesThrowable cdef) + unit.warning(body.pos, "catch block may intercept non-local return from " + meth) + Block(List(keyDef), tryCatch) } } @@ -228,8 +212,6 @@ abstract class UnCurry extends InfoTransform * } * new $anon() * - * If `settings.XoldPatmat.value`, also synthesized AbstractPartialFunction subclasses (see synthPartialFunction). - * */ def transformFunction(fun: Function): Tree = { fun.tpe match { @@ -245,9 +227,6 @@ abstract class UnCurry extends InfoTransform deEta(fun) match { // nullary or parameterless case fun1 if fun1 ne fun => fun1 - case _ if fun.tpe.typeSymbol == PartialFunctionClass => - // only get here when running under -Xoldpatmat - synthPartialFunction(fun) case _ => val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe)) val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation @@ -277,137 +256,13 @@ abstract class UnCurry extends InfoTransform localTyper.typedPos(fun.pos) { Block( - List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, List(applyMethodDef), fun.pos)), + List(ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos)), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } } } - /** Transform a function node (x => body) of type PartialFunction[T, R] where - * body = expr match { case P_i if G_i => E_i }_i=1..n - * to (assuming none of the cases is a default case): - * - * class $anon() extends AbstractPartialFunction[T, R] with Serializable { - * def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = (expr: @unchecked) match { - * case P_1 if G_1 => E_1 - * ... - * case P_n if G_n => E_n - * case _ => default(expr) - * } - * def isDefinedAt(x: T): boolean = (x: @unchecked) match { - * case P_1 if G_1 => true - * ... - * case P_n if G_n => true - * case _ => false - * } - * } - * new $anon() - * - * If there's a default case, the original match is used for applyOrElse, and isDefinedAt returns `true` - */ - def synthPartialFunction(fun: Function) = { - if (!settings.XoldPatmat.value) debugwarn("Under the new pattern matching scheme, PartialFunction should have been synthesized during typers.") - - val targs = fun.tpe.typeArgs - val (formals, restpe) = (targs.init, targs.last) - - val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation - val parents = addSerializable(appliedType(AbstractPartialFunctionClass, targs: _*)) - anonClass setInfo ClassInfoType(parents, newScope, anonClass) - - // 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 = { - 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 applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = - val applyOrElseMethodDef = { - val methSym = anonClass.newMethod(fun.pos, nme.applyOrElse) setFlag (FINAL | OVERRIDE) - - val List(argtpe) = formals - val A1 = methSym newTypeParameter(newTypeName("A1")) setInfo TypeBounds.upper(argtpe) - val B1 = methSym newTypeParameter(newTypeName("B1")) setInfo TypeBounds.lower(restpe) - val methFormals = List(A1.tpe, functionType(List(A1.tpe), B1.tpe)) - val params@List(x, default) = methSym newSyntheticValueParams methFormals - methSym setInfoAndEnter polyType(List(A1, B1), MethodType(params, B1.tpe)) - - val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), List(x)) - val body = localTyper.typedPos(fun.pos) { import CODE._ - def defaultAction(scrut: Tree) = REF(default) APPLY (REF(x)) - - substParam(fun.body) match { - case orig@Match(selector, cases) => - if (cases exists treeInfo.isDefaultCase) orig - else { - val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(selector.duplicate)) - Match(/*gen.mkUnchecked*/(selector), cases :+ defaultCase) - } - - } - } - body.changeOwner(fun.symbol -> methSym) - - val methDef = DefDef(methSym, body) - - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - methDef.tpt setType localTyper.packedType(body, methSym) - methDef - } - - val isDefinedAtMethodDef = { - val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL | SYNTHETIC) - 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(resetLocalAttrsKeepLabels(x)) // see pos/t1761 for why `resetLocalAttrs`, but must keep label symbols around - - val body = bodyForIDA match { - case Match(selector, cases) => - if (cases exists treeInfo.isDefaultCase) TRUE_typed - else - doSubst(Match(/*gen.mkUnchecked*/(selector), - (cases map (c => deriveCaseDef(c)(x => TRUE_typed))) :+ ( - DEFAULT ==> FALSE_typed))) - - } - body.changeOwner(fun.symbol -> methSym) - - DefDef(methSym, body) - } - - localTyper.typedPos(fun.pos) { - Block( - List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, List(applyOrElseMethodDef, isDefinedAtMethodDef), fun.pos)), - Typed(New(anonClass.tpe), TypeTree(fun.tpe))) - } - } - def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = { val isJava = fun.isJavaDefined def transformVarargs(varargsElemType: Type) = { @@ -416,7 +271,7 @@ abstract class UnCurry extends InfoTransform // when calling into scala varargs, make sure it's a sequence. def arrayToSequence(tree: Tree, elemtp: Type) = { - afterUncurry { + exitingUncurry { localTyper.typedPos(pos) { val pt = arrayType(elemtp) val adaptedTree = // might need to cast to Array[elemtp], as arrays are not covariant @@ -446,7 +301,7 @@ abstract class UnCurry extends InfoTransform case _ => EmptyTree } } - afterUncurry { + exitingUncurry { localTyper.typedPos(pos) { gen.mkMethodCall(tree, toArraySym, Nil, List(traversableClassTag(tree.tpe))) } @@ -470,7 +325,7 @@ abstract class UnCurry extends InfoTransform else arrayToSequence(mkArray, varargsElemType) } - afterUncurry { + exitingUncurry { if (isJava && !isReferenceArray(suffix.tpe) && isArrayOfSymbol(fun.tpe.params.last.tpe, ObjectClass)) { // The array isn't statically known to be a reference array, so call ScalaRuntime.toObjectArray. suffix = localTyper.typedPos(pos) { @@ -546,13 +401,6 @@ abstract class UnCurry extends InfoTransform finally needTryLift = saved } - /** A try or synchronized needs to be lifted anyway for MSIL if it contains - * return statements. These are disallowed in the CLR. By lifting - * such returns will be converted to throws. - */ - def shouldBeLiftedAnyway(tree: Tree) = false && // buggy, see #1981 - forMSIL && lookForReturns.found(tree) - /** Transform tree `t` to { def f = t; f } where `f` is a fresh name */ def liftTree(tree: Tree) = { @@ -625,14 +473,10 @@ abstract class UnCurry extends InfoTransform treeCopy.UnApply(tree, fn1, args1) case Apply(fn, args) => - if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head)) - transform(treeCopy.Apply(tree, fn, List(liftTree(args.head)))) - else { - val needLift = needTryLift || !fn.symbol.isLabel // SI-6749, no need to lift in args to label jumps. - withNeedLift(needLift) { - val formals = fn.tpe.paramTypes - treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) - } + val needLift = needTryLift || !fn.symbol.isLabel // SI-6749, no need to lift in args to label jumps. + withNeedLift(needLift) { + val formals = fn.tpe.paramTypes + treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) } case Assign(_: RefTree, _) => @@ -652,7 +496,7 @@ abstract class UnCurry extends InfoTransform super.transform(tree) case Try(block, catches, finalizer) => - if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) + if (needTryLift) transform(liftTree(tree)) else super.transform(tree) case CaseDef(pat, guard, body) => @@ -677,11 +521,11 @@ abstract class UnCurry extends InfoTransform tree1 } ) - assert(result.tpe != null, result + " tpe is null") + assert(result.tpe != null, result.shortClass + " tpe is null:\n" + result) result setType uncurryTreeType(result.tpe) } - def postTransform(tree: Tree): Tree = afterUncurry { + def postTransform(tree: Tree): Tree = exitingUncurry { def applyUnary(): Tree = { // TODO_NMT: verify that the inner tree of a type-apply also gets parens if the // whole tree is a polymorphic nullary method application @@ -708,35 +552,6 @@ abstract class UnCurry extends InfoTransform def isDefaultCatch(cdef: CaseDef) = isThrowable(cdef.pat) && cdef.guard.isEmpty - def postTransformTry(tree: Try) = { - val body = tree.block - val catches = tree.catches - val finalizer = tree.finalizer - if (opt.virtPatmat) { - if (catches exists (cd => !treeInfo.isCatchCase(cd))) - debugwarn("VPM BUG! illegal try/catch " + catches) - tree - } else if (catches forall treeInfo.isCatchCase) { - tree - } else { - val exname = unit.freshTermName("ex$") - val cases = - if ((catches exists treeInfo.isDefaultCase) || isDefaultCatch(catches.last)) catches - else catches :+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Throw(Ident(exname))) - val catchall = - atPos(tree.pos) { - CaseDef( - Bind(exname, Ident(nme.WILDCARD)), - EmptyTree, - Match(Ident(exname), cases)) - } - debuglog("rewrote try: " + catches + " ==> " + catchall); - val catches1 = localTyper.typedCases( - List(catchall), ThrowableClass.tpe, WildcardType) - treeCopy.Try(tree, body, catches1, finalizer) - } - } - tree match { /* Some uncurry post transformations add members to templates. * @@ -775,7 +590,9 @@ abstract class UnCurry extends InfoTransform addJavaVarargsForwarders(dd, flatdd) case tree: Try => - postTransformTry(tree) + if (tree.catches exists (cd => !treeInfo.isCatchCase(cd))) + devWarning("VPM BUG - illegal try/catch " + tree.catches) + tree case Apply(Apply(fn, args), args1) => treeCopy.Apply(tree, fn, args ::: args1) @@ -832,7 +649,7 @@ abstract class UnCurry extends InfoTransform final case class Packed(param: ValDef, tempVal: ValDef) extends ParamTransform def isDependent(dd: DefDef): Boolean = - beforeUncurry { + enteringUncurry { val methType = dd.symbol.info methType.isDependentMethodType && mexists(methType.paramss)(_.info exists (_.isImmediatelyDependent)) } @@ -915,10 +732,6 @@ abstract class UnCurry extends InfoTransform if (!dd.symbol.hasAnnotation(VarargsClass) || !repeatedParams.contains(dd.symbol)) return flatdd - def toSeqType(tp: Type): Type = { - val arg = elementType(ArrayClass, tp) - seqType(arg) - } def toArrayType(tp: Type): Type = { val arg = elementType(SeqClass, tp) // to prevent generation of an `Object` parameter from `Array[T]` parameter later @@ -953,7 +766,7 @@ abstract class UnCurry extends InfoTransform } // create the symbol - val forwsym = currentClass.newMethod(dd.name, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags) setInfo forwtype + val forwsym = currentClass.newMethod(dd.name.toTermName, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags) setInfo forwtype // create the tree val forwtree = theTyper.typedPos(dd.pos) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index b50486306d..36121f2653 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -16,7 +16,6 @@ trait Analyzer extends AnyRef with Typers with Infer with Implicits - with Variances with EtaExpansion with SyntheticMethods with Unapplies @@ -88,22 +87,25 @@ trait Analyzer extends AnyRef override def run() { val start = if (Statistics.canEnable) Statistics.startTimer(typerNanos) else null global.echoPhaseSummary(this) - currentRun.units foreach applyPhase - undoLog.clear() - // need to clear it after as well or 10K+ accumulated entries are - // uncollectable the rest of the way. + for (unit <- currentRun.units) { + applyPhase(unit) + undoLog.clear() + } if (Statistics.canEnable) Statistics.stopTimer(typerNanos, start) } def apply(unit: CompilationUnit) { try { - unit.body = newTyper(rootContext(unit)).typed(unit.body) + val typer = newTyper(rootContext(unit)) + unit.body = typer.typed(unit.body) if (global.settings.Yrangepos.value && !global.reporter.hasErrors) global.validatePositions(unit.body) for (workItem <- unit.toCheck) workItem() - } finally { + if (settings.lint.value) + typer checkUnused unit + } + finally { unit.toCheck.clear() } } } } } - diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala index 28f620dbb5..4210d0b9fb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -33,7 +33,7 @@ trait AnalyzerPlugins { self: Analyzer => /** * Let analyzer plugins change the expected type before type checking a tree. */ - def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = pt + def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Mode): Type = pt /** * Let analyzer plugins modify the type that has been computed for a tree. @@ -44,7 +44,7 @@ trait AnalyzerPlugins { self: Analyzer => * @param mode Mode that was used for typing `tree` * @param pt Expected type that was used for typing `tree` */ - def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = tpe + def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = tpe /** * Let analyzer plugins change the types assigned to definitions. For definitions that have @@ -133,7 +133,7 @@ trait AnalyzerPlugins { self: Analyzer => * Decide whether this analyzer plugin can adapt a tree that has an annotated type to the * given type tp, taking into account the given mode (see method adapt in trait Typers). */ - def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = false + def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = false /** * Adapt a tree that has an annotated type to the given type tp, taking into account the given @@ -142,7 +142,7 @@ trait AnalyzerPlugins { self: Analyzer => * An implementation cannot rely on canAdaptAnnotations being called before. If the implementing * class cannot do the adapting, it should return the tree unchanged. */ - def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = tree + def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = tree /** * Modify the type of a return expression. By default, return expressions have type @@ -169,13 +169,13 @@ trait AnalyzerPlugins { self: Analyzer => /** @see AnalyzerPlugin.pluginsPt */ - def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = + def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Mode): Type = if (analyzerPlugins.isEmpty) pt else analyzerPlugins.foldLeft(pt)((pt, plugin) => if (!plugin.isActive()) pt else plugin.pluginsPt(pt, typer, tree, mode)) /** @see AnalyzerPlugin.pluginsTyped */ - def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = { + def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = { // support deprecated methods in annotation checkers val annotCheckersTpe = addAnnotations(tree, tpe) if (analyzerPlugins.isEmpty) annotCheckersTpe @@ -196,7 +196,7 @@ trait AnalyzerPlugins { self: Analyzer => if (!plugin.isActive()) tpe else plugin.pluginsTypeSigAccessor(tpe, typer, tree, sym)) /** @see AnalyzerPlugin.canAdaptAnnotations */ - def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = { + def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = { // support deprecated methods in annotation checkers val annotCheckersExists = global.canAdaptAnnotations(tree, mode, pt) annotCheckersExists || { @@ -207,7 +207,7 @@ trait AnalyzerPlugins { self: Analyzer => } /** @see AnalyzerPlugin.adaptAnnotations */ - def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = { + def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = { // support deprecated methods in annotation checkers val annotCheckersTree = global.adaptAnnotations(tree, mode, pt) if (analyzerPlugins.isEmpty) annotCheckersTree diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index d30b5c2601..88bfa6099d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -6,12 +6,8 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.ListBuffer -import scala.util.control.ControlThrowable -import symtab.Flags._ -import scala.annotation.tailrec import Checkability._ +import scala.language.postfixOps /** On pattern matcher checkability: * @@ -66,6 +62,9 @@ trait Checkable { bases foreach { bc => val tps1 = (from baseType bc).typeArgs val tps2 = (tvarType baseType bc).typeArgs + if (tps1.size != tps2.size) + devWarning(s"Unequally sized type arg lists in propagateKnownTypes($from, $to): ($tps1, $tps2)") + (tps1, tps2).zipped foreach (_ =:= _) // Alternate, variance respecting formulation causes // neg/unchecked3.scala to fail (abstract types). TODO - @@ -82,7 +81,7 @@ trait Checkable { val resArgs = tparams zip tvars map { case (_, tvar) if tvar.instValid => tvar.constr.inst - case (tparam, _) => tparam.tpe + case (tparam, _) => tparam.tpeHK } appliedType(to, resArgs: _*) } @@ -112,7 +111,7 @@ trait Checkable { private class CheckabilityChecker(val X: Type, val P: Type) { def Xsym = X.typeSymbol def Psym = P.typeSymbol - def XR = propagateKnownTypes(X, Psym) + def XR = if (Xsym == AnyClass) classExistentialType(Psym) else propagateKnownTypes(X, Psym) // sadly the spec says (new java.lang.Boolean(true)).isInstanceOf[scala.Boolean] def P1 = X matchesPattern P def P2 = !Psym.isPrimitiveValueClass && isNeverSubType(X, P) @@ -134,7 +133,7 @@ trait Checkable { else if (P3) RuntimeCheckable else if (uncheckableType == NoType) { // Avoid warning (except ourselves) if we can't pinpoint the uncheckable type - debugwarn("Checkability checker says 'Uncheckable', but uncheckable type cannot be found:\n" + summaryString) + debuglog("Checkability checker says 'Uncheckable', but uncheckable type cannot be found:\n" + summaryString) CheckabilityError } else Uncheckable @@ -154,6 +153,7 @@ trait Checkable { def neverSubClass = isNeverSubClass(Xsym, Psym) def neverMatches = result == StaticallyFalse def isUncheckable = result == Uncheckable + def isCheckable = !isUncheckable def uncheckableMessage = uncheckableType match { case NoType => "something" case tp @ RefinedType(_, _) => "refinement " + tp @@ -203,11 +203,12 @@ trait Checkable { def isNeverSubClass(sym1: Symbol, sym2: Symbol) = areIrreconcilableAsParents(sym1, sym2) private def isNeverSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = /*logResult(s"isNeverSubArgs($tps1, $tps2, $tparams)")*/ { - def isNeverSubArg(t1: Type, t2: Type, variance: Int) = { - if (variance > 0) isNeverSubType(t2, t1) - else if (variance < 0) isNeverSubType(t1, t2) - else isNeverSameType(t1, t2) - } + def isNeverSubArg(t1: Type, t2: Type, variance: Variance) = ( + if (variance.isInvariant) isNeverSameType(t1, t2) + else if (variance.isCovariant) isNeverSubType(t2, t1) + else if (variance.isContravariant) isNeverSubType(t1, t2) + else false + ) exists3(tps1, tps2, tparams map (_.variance))(isNeverSubArg) } private def isNeverSameType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { @@ -232,6 +233,17 @@ trait Checkable { trait InferCheckable { self: Inferencer => + def isUncheckable(P0: Type) = !isCheckable(P0) + + def isCheckable(P0: Type): Boolean = ( + uncheckedOk(P0) || (P0.widen match { + case TypeRef(_, NothingClass | NullClass | AnyValClass, _) => false + case RefinedType(_, decls) if !decls.isEmpty => false + case p => + new CheckabilityChecker(AnyClass.tpe, p) isCheckable + }) + ) + /** TODO: much better error positions. * Kind of stuck right now because they just pass us the one tree. * TODO: Eliminate inPattern, canRemedy, which have no place here. diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala index 89e2ee44be..65bfd8e34e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package typechecker - import java.lang.ArithmeticException /** This class ... @@ -18,7 +17,6 @@ abstract class ConstantFolder { val global: Global import global._ - import definitions._ /** If tree is a constant operation, replace with result. */ def apply(tree: Tree): Tree = fold(tree, tree match { @@ -29,9 +27,6 @@ abstract class ConstantFolder { /** If tree is a constant value that can be converted to type `pt`, perform * the conversion. - * - * @param tree ... - * @param pt ... */ def apply(tree: Tree, pt: Type): Tree = fold(apply(tree), tree.tpe match { case ConstantType(x) => x convertTo pt diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 4bf7f78167..580f024b40 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -6,14 +6,14 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } import scala.reflect.internal.util.StringOps.{ countElementsAsString, countAsString } -import symtab.Flags.{ PRIVATE, PROTECTED, IS_ERROR } +import symtab.Flags.IS_ERROR import scala.compat.Platform.EOL import scala.reflect.runtime.ReflectionUtils import scala.reflect.macros.runtime.AbortMacroException import scala.util.control.NonFatal import scala.tools.nsc.util.stackTraceString +import scala.reflect.io.NoAbstractFile trait ContextErrors { self: Analyzer => @@ -153,11 +153,10 @@ trait ContextErrors { // members present, then display along with the expected members. This is done here because // this is the last point where we still have access to the original tree, rather than just // the found/req types. - val foundType: Type = req.normalize match { + val foundType: Type = req.dealiasWiden match { case RefinedType(parents, decls) if !decls.isEmpty && found.typeSymbol.isAnonOrRefinementClass => - val retyped = typed (tree.duplicate setType null) + val retyped = typed (tree.duplicate.clearType()) val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) - if (foundDecls.isEmpty || (found.typeSymbol eq NoSymbol)) found else { // The members arrive marked private, presumably because there was no @@ -171,11 +170,11 @@ trait ContextErrors { case _ => found } - assert(!found.isErroneous && !req.isErroneous, (found, req)) + assert(!foundType.isErroneous && !req.isErroneous, (foundType, req)) - issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req))) ) + issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(foundType, req, infer.isPossiblyMissingArgs(foundType, req))) ) if (settings.explaintypes.value) - explainTypes(found, req) + explainTypes(foundType, req) } def WithFilterError(tree: Tree, ex: AbsTypeError) = { @@ -184,14 +183,18 @@ trait ContextErrors { } def ParentTypesError(templ: Template, ex: TypeError) = { - templ.tpe = null - issueNormalTypeError(templ, ex.getMessage()) + templ.clearType() + issueNormalTypeError(templ, ex.getMessage()) + setError(templ) } // additional parentTypes errors - def ConstrArgsInTraitParentTpeError(arg: Tree, parent: Symbol) = + def ConstrArgsInParentWhichIsTraitError(arg: Tree, parent: Symbol) = issueNormalTypeError(arg, parent + " is a trait; does not take constructor arguments") + def ConstrArgsInParentOfTraitError(arg: Tree, parent: Symbol) = + issueNormalTypeError(arg, "parents of traits may not have parameters") + def MissingTypeArgumentsParentTpeError(supertpt: Tree) = issueNormalTypeError(supertpt, "missing type arguments") @@ -513,7 +516,7 @@ trait ContextErrors { NormalTypeError(tree, fun.tpe+" does not take parameters") // Dynamic - def DynamicVarArgUnsupported(tree: Tree, name: String) = + def DynamicVarArgUnsupported(tree: Tree, name: Name) = issueNormalTypeError(tree, name+ " does not support passing a vararg parameter") def DynamicRewriteError(tree: Tree, err: AbsTypeError) = { @@ -559,11 +562,13 @@ trait ContextErrors { //adapt def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = { - issueNormalTypeError(tree, - "missing arguments for " + meth.fullLocationString + ( + val message = + if (meth.isMacro) MacroPartialApplicationErrorMessage + else "missing arguments for " + meth.fullLocationString + ( if (meth.isConstructor) "" else ";\nfollow this method with `_' if you want to treat it as a partially applied function" - )) + ) + issueNormalTypeError(tree, message) setError(tree) } @@ -640,7 +645,7 @@ trait ContextErrors { val addendums = List( if (sym0.associatedFile eq sym1.associatedFile) Some("conflicting symbols both originated in file '%s'".format(sym0.associatedFile.canonicalPath)) - else if ((sym0.associatedFile ne null) && (sym1.associatedFile ne null)) + else if ((sym0.associatedFile ne NoAbstractFile) && (sym1.associatedFile ne NoAbstractFile)) Some("conflicting symbols originated in files '%s' and '%s'".format(sym0.associatedFile.canonicalPath, sym1.associatedFile.canonicalPath)) else None , if (isBug) Some("Note: this may be due to a bug in the compiler involving wildcards in package objects") else None @@ -657,8 +662,8 @@ trait ContextErrors { def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) = issueTypeError(PosAndMsgTypeError(errPos, "cyclic aliasing or subtyping involving "+sym0)) - def CyclicReferenceError(errPos: Position, lockedSym: Symbol) = - issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym)) + def CyclicReferenceError(errPos: Position, tp: Type, lockedSym: Symbol) = + issueTypeError(PosAndMsgTypeError(errPos, s"illegal cyclic reference involving $tp and $lockedSym")) // macro-related errors (also see MacroErrors below) @@ -667,22 +672,30 @@ trait ContextErrors { setError(tree) } + def MacroTooManyArgumentListsError(expandee: Tree, fun: Symbol) = { + NormalTypeError(expandee, "too many argument lists for " + fun) + } + + def MacroInvalidExpansionError(expandee: Tree, role: String, allowedExpansions: String) = { + issueNormalTypeError(expandee, s"macro in $role role can only expand into $allowedExpansions") + } + // same reason as for MacroBodyTypecheckException case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable - private def macroExpansionError(expandee: Tree, msg: String = null, pos: Position = NoPosition) = { + private def macroExpansionError(expandee: Tree, msg: String, pos: Position = NoPosition) = { def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg macroLogLite("macro expansion has failed: %s".format(msgForLog)) - val errorPos = if (pos != NoPosition) pos else (if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition) if (msg != null) context.error(pos, msg) // issueTypeError(PosAndMsgTypeError(..)) won't work => swallows positions setError(expandee) throw MacroExpansionException } + def MacroPartialApplicationErrorMessage = "macros cannot be partially applied" def MacroPartialApplicationError(expandee: Tree) = { // macroExpansionError won't work => swallows positions, hence needed to do issueTypeError // kinda contradictory to the comment in `macroExpansionError`, but this is how it works - issueNormalTypeError(expandee, "macros cannot be partially applied") + issueNormalTypeError(expandee, MacroPartialApplicationErrorMessage) setError(expandee) throw MacroExpansionException } @@ -748,13 +761,16 @@ trait ContextErrors { macroExpansionError(expandee, template(sym.name.nameKind).format(sym.name + " " + sym.origin, forgotten)) } - def MacroExpansionIsNotExprError(expandee: Tree, expanded: Any) = + def MacroExpansionHasInvalidTypeError(expandee: Tree, expanded: Any) = { + val expected = "expr" + val isPathMismatch = expanded != null && expanded.isInstanceOf[scala.reflect.api.Exprs#Expr[_]] macroExpansionError(expandee, - "macro must return a compiler-specific expr; returned value is " + ( + s"macro must return a compiler-specific $expected; returned value is " + ( if (expanded == null) "null" - else if (expanded.isInstanceOf[Expr[_]]) " Expr, but it doesn't belong to this compiler's universe" + else if (isPathMismatch) s" $expected, but it doesn't belong to this compiler" else " of " + expanded.getClass )) + } def MacroImplementationNotFoundError(expandee: Tree) = { val message = @@ -806,7 +822,10 @@ trait ContextErrors { ) } - def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String) = { + def AccessError(tree: Tree, sym: Symbol, ctx: Context, explanation: String): AbsTypeError = + AccessError(tree, sym, ctx.enclClass.owner.thisType, ctx.enclClass.owner, explanation) + + def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String): AbsTypeError = { def errMsg = { val location = if (sym.isClassConstructor) owner0 else pre.widen.directObjectString @@ -1033,8 +1052,8 @@ trait ContextErrors { val s1 = if (prevSym.isModule) "case class companion " else "" val s2 = if (prevSym.isSynthetic) "(compiler-generated) " + s1 else "" val s3 = if (prevSym.isCase) "case class " + prevSym.name else "" + prevSym - val where = if (currentSym.owner.isPackageClass != prevSym.owner.isPackageClass) { - val inOrOut = if (prevSym.owner.isPackageClass) "outside of" else "in" + val where = if (currentSym.isTopLevel != prevSym.isTopLevel) { + val inOrOut = if (prevSym.isTopLevel) "outside of" else "in" " %s package object %s".format(inOrOut, ""+prevSym.effectiveOwner.name) } else "" @@ -1044,9 +1063,6 @@ trait ContextErrors { def MaxParametersCaseClassError(tree: Tree) = issueNormalTypeError(tree, "Implementation restriction: case classes cannot have more than " + definitions.MaxFunctionArity + " parameters.") - def InheritsItselfError(tree: Tree) = - issueNormalTypeError(tree, tree.tpe.typeSymbol+" inherits itself") - def MissingParameterOrValTypeError(vparam: Tree) = issueNormalTypeError(vparam, "missing parameter type") diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index f2a2ef4d61..711085e6c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -6,8 +6,7 @@ package scala.tools.nsc package typechecker -import symtab.Flags._ -import scala.collection.mutable.{LinkedHashSet, Set} +import scala.collection.mutable import scala.annotation.tailrec /** @@ -16,6 +15,7 @@ import scala.annotation.tailrec */ trait Contexts { self: Analyzer => import global._ + import definitions.{ JavaLangPackage, ScalaPackage, PredefModule } object NoContext extends Context { outer = this @@ -28,13 +28,17 @@ trait Contexts { self: Analyzer => override def toString = "NoContext" } private object RootImports { - import definitions._ // Possible lists of root imports val javaList = JavaLangPackage :: Nil val javaAndScalaList = JavaLangPackage :: ScalaPackage :: Nil val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil } + def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) = + LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") + def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = + LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") + private lazy val startContext = { NoContext.make( Template(List(), emptyValDef, List()) setSymbol global.NoSymbol setType global.NoType, @@ -42,6 +46,28 @@ trait Contexts { self: Analyzer => rootMirror.RootClass.info.decls) } + private lazy val allUsedSelectors = + mutable.Map[ImportInfo, Set[ImportSelector]]() withDefaultValue Set() + private lazy val allImportInfos = + mutable.Map[CompilationUnit, List[ImportInfo]]() withDefaultValue Nil + + def clearUnusedImports() { + allUsedSelectors.clear() + allImportInfos.clear() + } + def warnUnusedImports(unit: CompilationUnit) = { + val imps = allImportInfos(unit).reverse.distinct + + for (imp <- imps) { + val used = allUsedSelectors(imp) + def isMask(s: ImportSelector) = s.name != nme.WILDCARD && s.rename == nme.WILDCARD + + imp.tree.selectors filterNot (s => isMask(s) || used(s)) foreach { sel => + unit.warning(imp posOf sel, "Unused import") + } + } + } + var lastAccessCheckDetails: String = "" /** List of symbols to import from in a root context. Typically that @@ -64,7 +90,6 @@ trait Contexts { self: Analyzer => def rootContext(unit: CompilationUnit): Context = rootContext(unit, EmptyTree, false) def rootContext(unit: CompilationUnit, tree: Tree): Context = rootContext(unit, tree, false) def rootContext(unit: CompilationUnit, tree: Tree, erasedTypes: Boolean): Context = { - import definitions._ var sc = startContext for (sym <- rootImports(unit)) { sc = sc.makeNewImport(sym) @@ -81,8 +106,8 @@ trait Contexts { self: Analyzer => var sc = startContext while (sc != NoContext) { sc.tree match { - case Import(qual, _) => qual.tpe = singleType(qual.symbol.owner.thisType, qual.symbol) - case _ => + case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol) + case _ => } sc = sc.outer } @@ -113,7 +138,7 @@ trait Contexts { self: Analyzer => } var enclMethod: Context = _ // The next outer context whose tree is a method - var variance: Int = _ // Variance relative to enclosing class + var variance: Variance = Variance.Invariant // Variance relative to enclosing class private var _undetparams: List[Symbol] = List() // Undetermined type parameters, // not inherited to child contexts var depth: Int = 0 @@ -143,8 +168,8 @@ trait Contexts { self: Analyzer => var typingIndentLevel: Int = 0 def typingIndent = " " * typingIndentLevel - var buffer: Set[AbsTypeError] = _ - var warningsBuffer: Set[(Position, String)] = _ + var buffer: mutable.Set[AbsTypeError] = _ + var warningsBuffer: mutable.Set[(Position, String)] = _ def enclClassOrMethod: Context = if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this @@ -184,25 +209,23 @@ trait Contexts { self: Analyzer => def setThrowErrors() = mode &= (~AllMask) def setAmbiguousErrors(report: Boolean) = if (report) mode |= AmbiguousErrors else mode &= notThrowMask - def updateBuffer(errors: Set[AbsTypeError]) = buffer ++= errors + def updateBuffer(errors: mutable.Set[AbsTypeError]) = buffer ++= errors def condBufferFlush(removeP: AbsTypeError => Boolean) { val elems = buffer.filter(removeP) buffer --= elems } def flushBuffer() { buffer.clear() } - def flushAndReturnBuffer(): Set[AbsTypeError] = { + def flushAndReturnBuffer(): mutable.Set[AbsTypeError] = { val current = buffer.clone() buffer.clear() current } - def flushAndReturnWarningsBuffer(): Set[(Position, String)] = { + def flushAndReturnWarningsBuffer(): mutable.Set[(Position, String)] = { val current = warningsBuffer.clone() warningsBuffer.clear() current } - def logError(err: AbsTypeError) = buffer += err - def withImplicitsEnabled[T](op: => T): T = { val saved = implicitsEnabled implicitsEnabled = true @@ -288,27 +311,23 @@ trait Contexts { self: Analyzer => c.checking = this.checking c.retyping = this.retyping c.openImplicits = this.openImplicits - c.buffer = if (this.buffer == null) LinkedHashSet[AbsTypeError]() else this.buffer // need to initialize - c.warningsBuffer = if (this.warningsBuffer == null) LinkedHashSet[(Position, String)]() else this.warningsBuffer + c.buffer = if (this.buffer == null) mutable.LinkedHashSet[AbsTypeError]() else this.buffer // need to initialize + c.warningsBuffer = if (this.warningsBuffer == null) mutable.LinkedHashSet[(Position, String)]() else this.warningsBuffer registerContext(c.asInstanceOf[analyzer.Context]) debuglog("[context] ++ " + c.unit + " / " + tree.summaryString) c } - // TODO: remove? Doesn't seem to be used - def make(unit: CompilationUnit): Context = { - val c = make(unit, EmptyTree, owner, scope, imports) - c.setReportErrors() - c.implicitsEnabled = true - c.macrosEnabled = true - c - } - def makeNewImport(sym: Symbol): Context = makeNewImport(gen.mkWildcardImport(sym)) - def makeNewImport(imp: Import): Context = - make(unit, imp, owner, scope, new ImportInfo(imp, depth) :: imports) + def makeNewImport(imp: Import): Context = { + val impInfo = new ImportInfo(imp, depth) + if (settings.lint.value && imp.pos.isDefined) // pos.isDefined excludes java.lang/scala/Predef imports + allImportInfos(unit) ::= impInfo + + make(unit, imp, owner, scope, impInfo :: imports) + } def make(tree: Tree, owner: Symbol, scope: Scope): Context = if (tree == this.tree && owner == this.owner && scope == this.scope) this @@ -331,7 +350,7 @@ trait Contexts { self: Analyzer => val c = make(newtree) c.setBufferErrors() c.setAmbiguousErrors(reportAmbiguousErrors) - c.buffer = new LinkedHashSet[AbsTypeError]() + c.buffer = mutable.LinkedHashSet[AbsTypeError]() c } @@ -388,8 +407,10 @@ trait Contexts { self: Analyzer => unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg) @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) { - debugwarn("issue error: " + err.errMsg) - if (settings.Yissuedebug.value) (new Exception).printStackTrace() + if (settings.Yissuedebug.value) { + log("issue error: " + err.errMsg) + (new Exception).printStackTrace() + } if (pf isDefinedAt err) pf(err) else if (bufferErrors) { buffer += err } else throw new TypeError(err.errPos, err.errMsg) @@ -436,16 +457,7 @@ trait Contexts { self: Analyzer => case _ => outer.isLocal() } - /** Fast path for some slow checks (ambiguous assignment in Refchecks, and - * existence of __match for MatchTranslation in virtpatmat.) This logic probably - * needs improvement. - */ - def isNameInScope(name: Name) = ( - enclosingContextChain exists (ctx => - (ctx.scope.lookupEntry(name) != null) - || (ctx.owner.rawInfo.member(name) != NoSymbol) - ) - ) + def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess // nextOuter determines which context is searched next for implicits // (after `this`, which contributes `newImplicits` below.) In @@ -480,17 +492,6 @@ trait Contexts { self: Analyzer => sub.isNonBottomSubClass(base) || sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base) - /** Return closest enclosing context that defines a superclass of `clazz`, or a - * companion module of a superclass of `clazz`, or NoContext if none exists */ - def enclosingSuperClassContext(clazz: Symbol): Context = { - var c = this.enclClass - while (c != NoContext && - !clazz.isNonBottomSubClass(c.owner) && - !(c.owner.isModuleClass && clazz.isNonBottomSubClass(c.owner.companionClass))) - c = c.outer.enclClass - c - } - /** Return the closest enclosing context that defines a subclass of `clazz` * or a companion object thereof, or `NoContext` if no such context exists. */ @@ -501,8 +502,7 @@ trait Contexts { self: Analyzer => c } - /** Is `sym` accessible as a member of tree `site` with type - * `pre` in current context? + /** Is `sym` accessible as a member of `pre` in current context? */ def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean = false): Boolean = { lastAccessCheckDetails = "" @@ -528,20 +528,6 @@ trait Contexts { self: Analyzer => } else (owner hasTransOwner ab) } -/* - var c = this - while (c != NoContext && c.owner != owner) { - if (c.outer eq null) abort("accessWithin(" + owner + ") " + c);//debug - if (c.outer.enclClass eq null) abort("accessWithin(" + owner + ") " + c);//debug - c = c.outer.enclClass - } - c != NoContext - } -*/ - /** Is `clazz` a subclass of an enclosing class? */ - def isSubClassOfEnclosing(clazz: Symbol): Boolean = - enclosingSuperClassContext(clazz) != NoContext - def isSubThisType(pre: Type, clazz: Symbol): Boolean = pre match { case ThisType(pclazz) => pclazz isNonBottomSubClass clazz case _ => false @@ -660,7 +646,7 @@ trait Contexts { self: Analyzer => case ImportSelector(from, _, to, _) :: sels1 => var impls = collect(sels1) filter (info => info.name != from) if (to != nme.WILDCARD) { - for (sym <- imp.importedSymbol(to).alternatives) + for (sym <- importedAccessibleSymbol(imp, to).alternatives) if (isQualifyingImplicit(to, sym, pre, imported = true)) impls = new ImplicitInfo(to, pre, sym) :: impls } @@ -706,6 +692,280 @@ trait Contexts { self: Analyzer => implicitsCache } + /** It's possible that seemingly conflicting identifiers are + * identifiably the same after type normalization. In such cases, + * allow compilation to proceed. A typical example is: + * package object foo { type InputStream = java.io.InputStream } + * import foo._, java.io._ + */ + private def resolveAmbiguousImport(name: Name, imp1: ImportInfo, imp2: ImportInfo): Option[ImportInfo] = { + val imp1Explicit = imp1 isExplicitImport name + val imp2Explicit = imp2 isExplicitImport name + val ambiguous = if (imp1.depth == imp2.depth) imp1Explicit == imp2Explicit else !imp1Explicit && imp2Explicit + val imp1Symbol = (imp1 importedSymbol name).initialize filter (s => isAccessible(s, imp1.qual.tpe, superAccess = false)) + val imp2Symbol = (imp2 importedSymbol name).initialize filter (s => isAccessible(s, imp2.qual.tpe, superAccess = false)) + + // The types of the qualifiers from which the ambiguous imports come. + // If the ambiguous name is a value, these must be the same. + def t1 = imp1.qual.tpe + def t2 = imp2.qual.tpe + // The types of the ambiguous symbols, seen as members of their qualifiers. + // If the ambiguous name is a monomorphic type, we can relax this far. + def mt1 = t1 memberType imp1Symbol + def mt2 = t2 memberType imp2Symbol + + def characterize = List( + s"types: $t1 =:= $t2 ${t1 =:= t2} members: ${mt1 =:= mt2}", + s"member type 1: $mt1", + s"member type 2: $mt2" + ).mkString("\n ") + + if (!ambiguous || !imp2Symbol.exists) Some(imp1) + else if (!imp1Symbol.exists) Some(imp2) + else ( + // The symbol names are checked rather than the symbols themselves because + // each time an overloaded member is looked up it receives a new symbol. + // So foo.member("x") != foo.member("x") if x is overloaded. This seems + // likely to be the cause of other bugs too... + if (t1 =:= t2 && imp1Symbol.name == imp2Symbol.name) { + log(s"Suppressing ambiguous import: $t1 =:= $t2 && $imp1Symbol == $imp2Symbol") + Some(imp1) + } + // Monomorphism restriction on types is in part because type aliases could have the + // same target type but attach different variance to the parameters. Maybe it can be + // relaxed, but doesn't seem worth it at present. + else if (mt1 =:= mt2 && name.isTypeName && imp1Symbol.isMonomorphicType && imp2Symbol.isMonomorphicType) { + log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $imp1Symbol and $imp2Symbol are equivalent") + Some(imp1) + } + else { + log(s"Import is genuinely ambiguous:\n " + characterize) + None + } + ) + } + + /** The symbol with name `name` imported via the import in `imp`, + * if any such symbol is accessible from this context. + */ + def importedAccessibleSymbol(imp: ImportInfo, name: Name): Symbol = + importedAccessibleSymbol(imp, name, requireExplicit = false) + + private def importedAccessibleSymbol(imp: ImportInfo, name: Name, requireExplicit: Boolean): Symbol = + imp.importedSymbol(name, requireExplicit) filter (s => isAccessible(s, imp.qual.tpe, superAccess = false)) + + /** Is `sym` defined in package object of package `pkg`? + * Since sym may be defined in some parent of the package object, + * we cannot inspect its owner only; we have to go through the + * info of the package object. However to avoid cycles we'll check + * what other ways we can before pushing that way. + */ + def isInPackageObject(sym: Symbol, pkg: Symbol): Boolean = { + def uninitialized(what: String) = { + log(s"Cannot look for $sym in package object of $pkg; $what is not initialized.") + false + } + def pkgClass = if (pkg.isTerm) pkg.moduleClass else pkg + def matchesInfo = ( + // need to be careful here to not get a cyclic reference during bootstrap + if (pkg.isInitialized) { + val module = pkg.info member nme.PACKAGEkw + if (module.isInitialized) + module.info.member(sym.name).alternatives contains sym + else + uninitialized("" + module) + } + else uninitialized("" + pkg) + ) + def inPackageObject(sym: Symbol) = ( + // To be in the package object, one of these must be true: + // 1) sym.owner is a package object class, and sym.owner.owner is the package class for `pkg` + // 2) sym.owner is inherited by the correct package object class + // We try to establish 1) by inspecting the owners directly, and then we try + // to rule out 2), and only if both those fail do we resort to looking in the info. + !sym.isPackage && (sym.owner ne NoSymbol) && ( + if (sym.owner.isPackageObjectClass) + sym.owner.owner == pkgClass + else + !sym.owner.isPackageClass && matchesInfo + ) + ) + + // An overloaded symbol might not have the expected owner! + // The alternatives must be inspected directly. + pkgClass.isPackageClass && ( + if (sym.isOverloaded) + sym.alternatives forall (isInPackageObject(_, pkg)) + else + inPackageObject(sym) + ) + } + + /** Find the symbol of a simple name starting from this context. + * All names are filtered through the "qualifies" predicate, + * the search continuing as long as no qualifying name is found. + */ + def lookupSymbol(name: Name, qualifies: Symbol => Boolean): NameLookup = { + var lookupError: NameLookup = null // set to non-null if a definite error is encountered + var inaccessible: NameLookup = null // records inaccessible symbol for error reporting in case none is found + var defSym: Symbol = NoSymbol // the directly found symbol + var pre: Type = NoPrefix // the prefix type of defSym, if a class member + var cx: Context = this // the context under consideration + var symbolDepth: Int = -1 // the depth of the directly found symbol + + def finish(qual: Tree, sym: Symbol): NameLookup = ( + if (lookupError ne null) lookupError + else sym match { + case NoSymbol if inaccessible ne null => inaccessible + case NoSymbol => LookupNotFound + case _ => LookupSucceeded(qual, sym) + } + ) + def finishDefSym(sym: Symbol, pre0: Type): NameLookup = + if (requiresQualifier(sym)) + finish(gen.mkAttributedQualifier(pre0), sym) + else + finish(EmptyTree, sym) + + def isPackageOwnedInDifferentUnit(s: Symbol) = ( + s.isDefinedInPackage && ( + !currentRun.compiles(s) + || unit.exists && s.sourceFile != unit.source.file + ) + ) + def requiresQualifier(s: Symbol) = ( + s.owner.isClass + && !s.owner.isPackageClass + && !s.isTypeParameterOrSkolem + ) + def lookupInPrefix(name: Name) = pre member name filter qualifies + def accessibleInPrefix(s: Symbol) = isAccessible(s, pre, superAccess = false) + + def searchPrefix = { + cx = cx.enclClass + val found0 = lookupInPrefix(name) + val found1 = found0 filter accessibleInPrefix + if (found0.exists && !found1.exists && inaccessible == null) + inaccessible = LookupInaccessible(found0, analyzer.lastAccessCheckDetails) + + found1 + } + + def lookupInScope(scope: Scope) = + (scope lookupUnshadowedEntries name filter (e => qualifies(e.sym))).toList + + def newOverloaded(owner: Symbol, pre: Type, entries: List[ScopeEntry]) = + logResult(s"!!! lookup overloaded")(owner.newOverloaded(pre, entries map (_.sym))) + + // Constructor lookup should only look in the decls of the enclosing class + // not in the self-type, nor in the enclosing context, nor in imports (SI-4460, SI-6745) + if (name == nme.CONSTRUCTOR) return { + val enclClassSym = cx.enclClass.owner + val scope = cx.enclClass.prefix.baseType(enclClassSym).decls + val constructorSym = lookupInScope(scope) match { + case Nil => NoSymbol + case hd :: Nil => hd.sym + case entries => newOverloaded(enclClassSym, cx.enclClass.prefix, entries) + } + finishDefSym(constructorSym, cx.enclClass.prefix) + } + + // cx.scope eq null arises during FixInvalidSyms in Duplicators + while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) { + pre = cx.enclClass.prefix + defSym = lookupInScope(cx.scope) match { + case Nil => searchPrefix + case entries @ (hd :: tl) => + // we have a winner: record the symbol depth + symbolDepth = (cx.depth - cx.scope.nestingLevel) + hd.depth + if (tl.isEmpty) hd.sym + else newOverloaded(cx.owner, pre, entries) + } + if (!defSym.exists) + cx = cx.outer // push further outward + } + if (symbolDepth < 0) + symbolDepth = cx.depth + + var impSym: Symbol = NoSymbol + var imports = Context.this.imports + def imp1 = imports.head + def imp2 = imports.tail.head + def sameDepth = imp1.depth == imp2.depth + def imp1Explicit = imp1 isExplicitImport name + def imp2Explicit = imp2 isExplicitImport name + + def lookupImport(imp: ImportInfo, requireExplicit: Boolean) = + importedAccessibleSymbol(imp, name, requireExplicit) filter qualifies + + while (!impSym.exists && imports.nonEmpty && imp1.depth > symbolDepth) { + impSym = lookupImport(imp1, requireExplicit = false) + if (!impSym.exists) + imports = imports.tail + } + + if (defSym.exists && impSym.exists) { + // imported symbols take precedence over package-owned symbols in different compilation units. + if (isPackageOwnedInDifferentUnit(defSym)) + defSym = NoSymbol + // Defined symbols take precedence over erroneous imports. + else if (impSym.isError || impSym.name == nme.CONSTRUCTOR) + impSym = NoSymbol + // Otherwise they are irreconcilably ambiguous + else + return ambiguousDefnAndImport(defSym.owner, imp1) + } + + // At this point only one or the other of defSym and impSym might be set. + if (defSym.exists) + finishDefSym(defSym, pre) + else if (impSym.exists) { + // We continue walking down the imports as long as the tail is non-empty, which gives us: + // imports == imp1 :: imp2 :: _ + // And at least one of the following is true: + // - imp1 and imp2 are at the same depth + // - imp1 is a wildcard import, so all explicit imports from outer scopes must be checked + def keepLooking = ( + lookupError == null + && imports.tail.nonEmpty + && (sameDepth || !imp1Explicit) + ) + // If we find a competitor imp2 which imports the same name, possible outcomes are: + // + // - same depth, imp1 wild, imp2 explicit: imp2 wins, drop imp1 + // - same depth, imp1 wild, imp2 wild: ambiguity check + // - same depth, imp1 explicit, imp2 explicit: ambiguity check + // - differing depth, imp1 wild, imp2 explicit: ambiguity check + // - all others: imp1 wins, drop imp2 + // + // The ambiguity check is: if we can verify that both imports refer to the same + // symbol (e.g. import foo.X followed by import foo._) then we discard imp2 + // and proceed. If we cannot, issue an ambiguity error. + while (keepLooking) { + // If not at the same depth, limit the lookup to explicit imports. + // This is desirable from a performance standpoint (compare to + // filtering after the fact) but also necessary to keep the unused + // import check from being misled by symbol lookups which are not + // actually used. + val other = lookupImport(imp2, requireExplicit = !sameDepth) + def imp1wins = { imports = imp1 :: imports.tail.tail } + def imp2wins = { impSym = other ; imports = imports.tail } + + if (!other.exists) // imp1 wins; drop imp2 and continue. + imp1wins + else if (sameDepth && !imp1Explicit && imp2Explicit) // imp2 wins; drop imp1 and continue. + imp2wins + else resolveAmbiguousImport(name, imp1, imp2) match { + case Some(imp) => if (imp eq imp1) imp1wins else imp2wins + case _ => lookupError = ambiguousImports(imp1, imp2) + } + } + // optimization: don't write out package prefixes + finish(resetPos(imp1.qual.duplicate), impSym) + } + else finish(EmptyTree, NoSymbol) + } + /** * Find a symbol in this context or one of its outers. * @@ -730,11 +990,14 @@ trait Contexts { self: Analyzer => } //class Context class ImportInfo(val tree: Import, val depth: Int) { + def pos = tree.pos + def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos + /** The prefix expression */ def qual: Tree = tree.symbol.info match { case ImportType(expr) => expr - case ErrorType => tree setType NoType // fix for #2870 - case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info) //debug + case ErrorType => tree setType NoType // fix for #2870 + case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info) //debug } /** Is name imported explicitly, not via wildcard? */ @@ -743,25 +1006,53 @@ trait Contexts { self: Analyzer => /** The symbol with name `name` imported from import clause `tree`. */ - def importedSymbol(name: Name): Symbol = { + def importedSymbol(name: Name): Symbol = importedSymbol(name, requireExplicit = false) + + private def recordUsage(sel: ImportSelector, result: Symbol) { + def posstr = pos.source.file.name + ":" + posOf(sel).safeLine + def resstr = if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)" else s"(expr=${tree.expr}, ${result.fullLocationString})" + debuglog(s"In $this at $posstr, selector '${selectorString(sel)}' resolved to $resstr") + allUsedSelectors(this) += sel + } + + /** If requireExplicit is true, wildcard imports are not considered. */ + def importedSymbol(name: Name, requireExplicit: Boolean): Symbol = { var result: Symbol = NoSymbol var renamed = false var selectors = tree.selectors - while (selectors != Nil && result == NoSymbol) { - if (selectors.head.rename == name.toTermName) + def current = selectors.head + while (selectors.nonEmpty && result == NoSymbol) { + if (current.rename == name.toTermName) result = qual.tpe.nonLocalMember( // new to address #2733: consider only non-local members for imports - if (name.isTypeName) selectors.head.name.toTypeName else selectors.head.name) - else if (selectors.head.name == name.toTermName) + if (name.isTypeName) current.name.toTypeName else current.name) + else if (current.name == name.toTermName) renamed = true - else if (selectors.head.name == nme.WILDCARD && !renamed) + else if (current.name == nme.WILDCARD && !renamed && !requireExplicit) result = qual.tpe.nonLocalMember(name) - selectors = selectors.tail + + if (result == NoSymbol) + selectors = selectors.tail } - result + if (settings.lint.value && selectors.nonEmpty && result != NoSymbol && pos != NoPosition) + recordUsage(current, result) + + // Harden against the fallout from bugs like SI-6745 + // + // [JZ] I considered issuing a devWarning and moving the + // check inside the above loop, as I believe that + // this always represents a mistake on the part of + // the caller. + if (definitions isImportable result) result + else NoSymbol + } + private def selectorString(s: ImportSelector): String = { + if (s.name == nme.WILDCARD && s.rename == null) "_" + else if (s.name == s.rename) "" + s.name + else s.name + " => " + s.rename } def allImportedSymbols: Iterable[Symbol] = - qual.tpe.members flatMap (transformImport(tree.selectors, _)) + importableMembers(qual.tpe) flatMap (transformImport(tree.selectors, _)) private def transformImport(selectors: List[ImportSelector], sym: Symbol): List[Symbol] = selectors match { case List() => List() @@ -772,7 +1063,12 @@ trait Contexts { self: Analyzer => case _ :: rest => transformImport(rest, sym) } - override def toString() = tree.toString() + override def hashCode = tree.## + override def equals(other: Any) = other match { + case that: ImportInfo => (tree == that.tree) + case _ => false + } + override def toString = tree.toString } case class ImportType(expr: Tree) extends Type { diff --git a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala index 3e249e57bb..73572bcae9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala +++ b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala @@ -6,8 +6,6 @@ package scala.tools.nsc package typechecker -import scala.language.implicitConversions - /** A generic means of breaking down types into their subcomponents. * Types are decomposed top down, and recognizable substructure is * dispatched via self-apparently named methods. Those methods can @@ -37,8 +35,6 @@ trait DestructureTypes { def wrapSequence(nodes: List[Node]): Node def wrapAtom[U](value: U): Node - private implicit def liftToTerm(name: String): TermName = newTermName(name) - private val openSymbols = scala.collection.mutable.Set[Symbol]() private def nodeList[T](elems: List[T], mkNode: T => Node): Node = @@ -68,15 +64,6 @@ trait DestructureTypes { }, tree.productPrefix ) - def wrapSymbol(label: String, sym: Symbol): Node = { - if (sym eq NoSymbol) wrapEmpty - else atom(label, sym) - } - def wrapInfo(sym: Symbol) = sym.info match { - case TypeBounds(lo, hi) => typeBounds(lo, hi) - case PolyType(tparams, restpe) => polyFunction(tparams, restpe) - case _ => wrapEmpty - } def wrapSymbolInfo(sym: Symbol): Node = { if ((sym eq NoSymbol) || openSymbols(sym)) wrapEmpty else { @@ -99,7 +86,6 @@ trait DestructureTypes { def constant(label: String, const: Constant): Node = atom(label, const) def scope(decls: Scope): Node = node("decls", scopeMemberList(decls.toList)) - def const[T](named: (String, T)): Node = constant(named._1, Constant(named._2)) def resultType(restpe: Type): Node = this("resultType", restpe) def typeParams(tps: List[Symbol]): Node = node("typeParams", symbolList(tps)) @@ -188,7 +174,6 @@ trait DestructureTypes { case AntiPolyType(pre, targs) => product(tp, prefix(pre), typeArgs(targs)) case ClassInfoType(parents, decls, clazz) => product(tp, parentList(parents), scope(decls), wrapAtom(clazz)) case ConstantType(const) => product(tp, constant("value", const)) - case DeBruijnIndex(level, index, args) => product(tp, const("level" -> level), const("index" -> index), typeArgs(args)) case OverloadedType(pre, alts) => product(tp, prefix(pre), node("alts", typeList(alts map pre.memberType))) case RefinedType(parents, decls) => product(tp, parentList(parents), scope(decls)) case SingleType(pre, sym) => product(tp, prefix(pre), wrapAtom(sym)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index f6142a81be..face149b9f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -19,11 +19,6 @@ abstract class Duplicators extends Analyzer { import global._ import definitions.{ AnyRefClass, AnyValClass } - def retyped(context: Context, tree: Tree): Tree = { - resetClassOwners - (newBodyDuplicator(context)).typed(tree) - } - /** Retype the given tree in the given context. Use this method when retyping * a method in a different class. The typer will replace references to the this of * the old class with the new class, and map symbols through the given 'env'. The @@ -42,9 +37,6 @@ abstract class Duplicators extends Analyzer { protected def newBodyDuplicator(context: Context) = new BodyDuplicator(context) - def retypedMethod(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol): Tree = - (newBodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis) - /** Return the special typer for duplicate method bodies. */ override def newTyper(context: Context): Typer = newBodyDuplicator(context) @@ -186,31 +178,6 @@ abstract class Duplicators extends Analyzer { stats.foreach(invalidate(_, owner)) } - def retypedMethod(ddef: DefDef, oldThis: Symbol, newThis: Symbol): Tree = { - oldClassOwner = oldThis - newClassOwner = newThis - invalidateAll(ddef.tparams) - mforeach(ddef.vparamss) { vdef => - invalidate(vdef) - vdef.tpe = null - } - ddef.symbol = NoSymbol - enterSym(context, ddef) - debuglog("remapping this of " + oldClassOwner + " to " + newClassOwner) - typed(ddef) - } - - private def inspectTpe(tpe: Type) = { - tpe match { - case MethodType(_, res) => - res + ", " + res.bounds.hi + ", " + (res.bounds.hi match { - case TypeRef(_, _, args) if (args.length > 0) => args(0) + ", " + args(0).bounds.hi - case _ => "non-tref: " + res.bounds.hi.getClass - }) - case _ => - } - } - /** Optionally cast this tree into some other type, if required. * Unless overridden, just returns the tree. */ @@ -230,10 +197,10 @@ abstract class Duplicators extends Analyzer { * their symbols are recreated ad-hoc and their types are fixed inline, instead of letting the * namer/typer handle them, or Idents that refer to them. */ - override def typed(tree: Tree, mode: Int, pt: Type): Tree = { + override def typed(tree: Tree, mode: Mode, pt: Type): Tree = { debuglog("typing " + tree + ": " + tree.tpe + ", " + tree.getClass) val origtreesym = tree.symbol - if (tree.hasSymbol && tree.symbol != NoSymbol + if (tree.hasSymbolField && tree.symbol != NoSymbol && !tree.symbol.isLabel // labels cannot be retyped by the type checker as LabelDef has no ValDef/return type trees && invalidSyms.isDefinedAt(tree.symbol)) { debuglog("removed symbol " + tree.symbol) @@ -243,40 +210,35 @@ abstract class Duplicators extends Analyzer { tree match { case ttree @ TypeTree() => // log("fixing tpe: " + tree.tpe + " with sym: " + tree.tpe.typeSymbol) - ttree.tpe = fixType(ttree.tpe) - ttree + ttree modifyType fixType case Block(stats, res) => debuglog("invalidating block") invalidateAll(stats) invalidate(res) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case ClassDef(_, _, _, tmpl @ Template(parents, _, stats)) => // log("invalidating classdef " + tree) tmpl.symbol = tree.symbol.newLocalDummy(tree.pos) invalidateAll(stats, tree.symbol) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case ddef @ DefDef(_, _, _, _, tpt, rhs) => - ddef.tpt.tpe = fixType(ddef.tpt.tpe) - ddef.tpe = null - super.typed(ddef, mode, pt) + ddef.tpt modifyType fixType + super.typed(ddef.clearType(), mode, pt) case vdef @ ValDef(mods, name, tpt, rhs) => // log("vdef fixing tpe: " + tree.tpe + " with sym: " + tree.tpe.typeSymbol + " and " + invalidSyms) //if (mods.hasFlag(Flags.LAZY)) vdef.symbol.resetFlag(Flags.MUTABLE) // Martin to Iulian: lazy vars can now appear because they are no longer boxed; Please check that deleting this statement is OK. - vdef.tpt.tpe = fixType(vdef.tpt.tpe) - vdef.tpe = null - super.typed(vdef, mode, pt) + vdef.tpt modifyType fixType + super.typed(vdef.clearType(), mode, pt) case ldef @ LabelDef(name, params, rhs) => // log("label def: " + ldef) // in case the rhs contains any definitions -- TODO: is this necessary? invalidate(rhs) - ldef.tpe = null + ldef.clearType() // is this LabelDef generated by tailcalls? val isTailLabel = (ldef.params.length >= 1) && (ldef.params.head.name == nme.THIS) @@ -294,27 +256,23 @@ abstract class Duplicators extends Analyzer { val params1 = params map newParam val rhs1 = (new TreeSubstituter(params map (_.symbol), params1) transform rhs) // TODO: duplicate? - rhs1.tpe = null - super.typed(treeCopy.LabelDef(tree, name, params1, rhs1), mode, pt) + super.typed(treeCopy.LabelDef(tree, name, params1, rhs1.clearType()), mode, pt) case Bind(name, _) => // log("bind: " + tree) invalidate(tree) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case Ident(_) if tree.symbol.isLabel => debuglog("Ident to labeldef " + tree + " switched to ") tree.symbol = updateSym(tree.symbol) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case Ident(_) if (origtreesym ne null) && origtreesym.isLazy => debuglog("Ident to a lazy val " + tree + ", " + tree.symbol + " updated to " + origtreesym) tree.symbol = updateSym(origtreesym) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case Select(th @ This(_), sel) if (oldClassOwner ne null) && (th.symbol == oldClassOwner) => // We use the symbol name instead of the tree name because the symbol @@ -336,9 +294,15 @@ abstract class Duplicators extends Analyzer { case ((alt, tpe)) :: Nil => log(s"Arrested overloaded type in Duplicators, narrowing to ${alt.defStringSeenAs(tpe)}\n Overload was: $memberString") Select(This(newClassOwner), alt) - case _ => - log(s"Could not disambiguate $memberString in Duplicators. Attempting name-based selection, but this may not end well...") - nameSelection + case xs => + alts filter (alt => (alt.paramss corresponds tree.symbol.paramss)(_.size == _.size)) match { + case alt :: Nil => + log(s"Resorted to parameter list arity to disambiguate to $alt\n Overload was: $memberString") + Select(This(newClassOwner), alt) + case _ => + log(s"Could not disambiguate $memberTypes. Attempting name-based selection, but we may crash later.") + nameSelection + } } } else nameSelection @@ -397,7 +361,7 @@ abstract class Duplicators extends Analyzer { case _ => debuglog("Duplicators default case: " + tree.summaryString) debuglog(" ---> " + tree) - if (tree.hasSymbol && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { + if (tree.hasSymbolField && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==) } val ntree = castType(tree, pt) diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index bbba7e0435..4fbb788c7b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -33,7 +33,7 @@ trait EtaExpansion { self: Analyzer => } /** <p> - * Expand partial function applications of type <code>type</code>. + * Expand partial function applications of type `type`. * </p><pre> * p.f(es_1)...(es_n) * ==> { @@ -56,11 +56,8 @@ trait EtaExpansion { self: Analyzer => } val defs = new ListBuffer[Tree] - /** Append to <code>defs</code> value definitions for all non-stable - * subexpressions of the function application <code>tree</code>. - * - * @param tree ... - * @return ... + /** Append to `defs` value definitions for all non-stable + * subexpressions of the function application `tree`. */ def liftoutPrefix(tree: Tree): Tree = { def liftout(tree: Tree, byName: Boolean): Tree = @@ -97,11 +94,11 @@ trait EtaExpansion { self: Analyzer => // with repeated params, there might be more or fewer args than params liftout(arg, byName(i).getOrElse(false)) } - treeCopy.Apply(tree, liftoutPrefix(fn), newArgs) setType null + treeCopy.Apply(tree, liftoutPrefix(fn), newArgs).clearType() case TypeApply(fn, args) => - treeCopy.TypeApply(tree, liftoutPrefix(fn), args) setType null + treeCopy.TypeApply(tree, liftoutPrefix(fn), args).clearType() case Select(qual, name) => - treeCopy.Select(tree, liftout(qual, false), name) setSymbol NoSymbol setType null + treeCopy.Select(tree, liftout(qual, false), name).clearType() setSymbol NoSymbol case Ident(name) => tree } @@ -118,7 +115,7 @@ trait EtaExpansion { self: Analyzer => val origTpe = sym.tpe val isRepeated = definitions.isRepeatedParamType(origTpe) // SI-4176 Don't leak A* in eta-expanded function types. See t4176b.scala - val droppedStarTpe = if (settings.etaExpandKeepsStar.value) origTpe else dropRepeatedParamType(origTpe) + val droppedStarTpe = if (settings.etaExpandKeepsStar.value) origTpe else dropIllegalStarTypes(origTpe) val valDef = ValDef(Modifiers(SYNTHETIC | PARAM), sym.name.toTermName, TypeTree(droppedStarTpe), EmptyTree) (valDef, isRepeated) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index c4ae18ba48..788825a6b6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -30,7 +30,7 @@ trait Implicits { import global._ import definitions._ import ImplicitsStats._ - import typeDebug.{ ptTree, ptBlock, ptLine } + import typeDebug.{ ptBlock, ptLine } import global.typer.{ printTyping, deindentTyping, indentTyping, printInference } def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = @@ -82,7 +82,7 @@ trait Implicits { 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) + debuglog("update buffer: " + implicitSearchContext.errBuffer) } printInference("[infer implicit] inferred " + result) context.undetparams = context.undetparams filterNot result.subst.from.contains @@ -132,7 +132,7 @@ trait Implicits { } /* 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 + * Consider `implicit def b(implicit x: A): x.T = error("")`. We need to approximate debruijn index types * when checking whether `b` is a valid implicit, as we haven't even searched a value for the implicit arg `x`, * so we have to approximate (otherwise it is excluded a priori). */ @@ -244,11 +244,7 @@ trait Implicits { object HasMember { private val hasMemberCache = perRunCaches.newMap[Name, Type]() def apply(name: Name): Type = hasMemberCache.getOrElseUpdate(name, memberWildcardType(name, WildcardType)) - def unapply(pt: Type): Option[Name] = pt match { - case RefinedType(List(WildcardType), Scope(sym)) if sym.tpe == WildcardType => Some(sym.name) - case _ => None } - } /** An extractor for types of the form ? { name: (? >: argtpe <: Any*)restp } */ @@ -351,7 +347,7 @@ trait Implicits { * if one or both are intersection types with a pair of overlapping parent types. */ private def dominates(dtor: Type, dted: Type): Boolean = { - def core(tp: Type): Type = tp.normalize match { + def core(tp: Type): Type = tp.dealiasWiden match { case RefinedType(parents, defs) => intersectionType(parents map core, tp.typeSymbol.owner) case AnnotatedType(annots, tp, selfsym) => core(tp) case ExistentialType(tparams, result) => core(result).subst(tparams, tparams map (t => core(t.info.bounds.hi))) @@ -366,11 +362,11 @@ trait Implicits { deriveTypeWithWildcards(syms.distinct)(tp) } def sum(xs: List[Int]) = (0 /: xs)(_ + _) - def complexity(tp: Type): Int = tp.normalize match { + def complexity(tp: Type): Int = tp.dealiasWiden match { case NoPrefix => 0 case SingleType(pre, sym) => - if (sym.isPackage) 0 else complexity(tp.normalize.widen) + if (sym.isPackage) 0 else complexity(tp.dealiasWiden) case TypeRef(pre, sym, args) => complexity(pre) + sum(args map complexity) + 1 case RefinedType(parents, _) => @@ -672,11 +668,11 @@ trait Implicits { // duplicating the code here, but this is probably a // hotspot (and you can't just call typed, need to force // re-typecheck) - // TODO: the return tree is ignored. This seems to make - // no difference, but it's bad practice regardless. - - - val checked = itree2 match { + // + // This is just called for the side effect of error detection, + // see SI-6966 to see what goes wrong if we use the result of this + // as the SearchResult. + itree2 match { case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args) case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c case t => t @@ -1005,7 +1001,7 @@ trait Implicits { case Some(imap) => imap case None => val result = new InfoMap - getClassParts(sym.tpe)(result, new mutable.HashSet(), pending + sym) + getClassParts(sym.tpeHK)(result, new mutable.HashSet(), pending + sym) infoMapCache(sym) = result result } @@ -1131,7 +1127,7 @@ trait Implicits { } ) // todo. migrate hardcoded materialization in Implicits to corresponding implicit macros - var materializer = atPos(pos.focus)(gen.mkMethodCall(TagMaterializers(tagClass), List(tp), if (prefix != EmptyTree) List(prefix) else List())) + val materializer = atPos(pos.focus)(gen.mkMethodCall(TagMaterializers(tagClass), List(tp), if (prefix != EmptyTree) List(prefix) else List())) if (settings.XlogImplicits.value) reporter.echo(pos, "materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) if (context.macrosEnabled) success(materializer) // don't call `failure` here. if macros are disabled, we just fail silently @@ -1319,17 +1315,12 @@ trait Implicits { // `materializeImplicit` does some preprocessing for `pt` // is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`? - if (result.isFailure) result = searchImplicit(implicitsOfExpectedType, false) + if (result.isFailure && !wasAmbigious) result = searchImplicit(implicitsOfExpectedType, false) if (result.isFailure) { context.updateBuffer(previousErrs) if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) } else { - if (wasAmbigious && settings.lint.value) - reporter.warning(tree.pos, - "Search of in-scope implicits was ambiguous, and the implicit scope was searched. In Scala 2.11.0, this code will not compile. See SI-6667. \n" + - previousErrs.map(_.errMsg).mkString("\n")) - if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits) } @@ -1404,7 +1395,6 @@ trait Implicits { interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc? def validate: Option[String] = { - import scala.util.matching.Regex; import scala.collection.breakOut // is there a shorter way to avoid the intermediate toList? val refs = """\$\{([^}]+)\}""".r.findAllIn(msg).matchData.map(_ group 1).toSet val decls = typeParamNames.toSet @@ -1430,9 +1420,7 @@ object ImplicitsStats { val subtypeImpl = Statistics.newSubCounter(" of which in implicit", subtypeCount) val findMemberImpl = Statistics.newSubCounter(" of which in implicit", findMemberCount) val subtypeAppInfos = Statistics.newSubCounter(" of which in app impl", subtypeCount) - val subtypeImprovCount = Statistics.newSubCounter(" of which in improves", subtypeCount) val implicitSearchCount = Statistics.newCounter ("#implicit searches", "typer") - val triedImplicits = Statistics.newSubCounter(" #tried", implicitSearchCount) val plausiblyCompatibleImplicits = Statistics.newSubCounter(" #plausibly compatible", implicitSearchCount) val matchingImplicits = Statistics.newSubCounter(" #matching", implicitSearchCount) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 7161043dcf..0207c841d2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -6,11 +6,10 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } +import scala.collection.immutable import scala.collection.mutable.ListBuffer import scala.util.control.ControlThrowable import symtab.Flags._ -import scala.annotation.tailrec /** This trait ... * @@ -30,8 +29,8 @@ trait Infer extends Checkable { private def assertNonCyclic(tvar: TypeVar) = assert(tvar.constr.inst != tvar, tvar.origin) - /** The formal parameter types corresponding to <code>formals</code>. - * If <code>formals</code> has a repeated last parameter, a list of + /** The formal parameter types corresponding to `formals`. + * If `formals` has a repeated last parameter, a list of * (nargs - params.length + 1) copies of its type is returned. * By-name types are replaced with their underlying type. * @@ -49,6 +48,24 @@ trait Infer extends Checkable { } else formals1 } + /** Sorts the alternatives according to the given comparison function. + * Returns a list containing the best alternative as well as any which + * the best fails to improve upon. + */ + private def bestAlternatives(alternatives: List[Symbol])(isBetter: (Symbol, Symbol) => Boolean): List[Symbol] = { + def improves(sym1: Symbol, sym2: Symbol) = ( + sym2 == NoSymbol + || sym2.isError + || sym2.hasAnnotation(BridgeClass) + || isBetter(sym1, sym2) + ) + + alternatives sortWith improves match { + case best :: rest if rest.nonEmpty => best :: rest.filterNot(alt => improves(best, alt)) + case bests => bests + } + } + /** Returns `(formals, formalsExpanded)` where `formalsExpanded` are the expected types * for the `nbSubPats` sub-patterns of an extractor pattern, of which the corresponding * unapply[Seq] call is assumed to have result type `resTp`. @@ -133,21 +150,7 @@ trait Infer extends Checkable { else (formals, formalsExpanded) } - def actualTypes(actuals: List[Type], nformals: Int): List[Type] = - if (nformals == 1 && !hasLength(actuals, 1)) - List(if (actuals.isEmpty) UnitClass.tpe else tupleType(actuals)) - else actuals - - def actualArgs(pos: Position, actuals: List[Tree], nformals: Int): List[Tree] = { - val inRange = nformals == 1 && !hasLength(actuals, 1) && actuals.lengthCompare(MaxTupleArity) <= 0 - if (inRange && !phase.erasedTypes) List(atPos(pos)(gen.mkTuple(actuals))) - else actuals - } - /** A fresh type variable with given type parameter as origin. - * - * @param tparam ... - * @return ... */ def freshVar(tparam: Symbol): TypeVar = TypeVar(tparam) @@ -170,14 +173,13 @@ trait Infer extends Checkable { 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") + s"no unique instantiation of type variable $origin could be found") } else if (excludedVars(tv)) { throw new NoInstance("cyclic instantiation") } else { excludedVars += tv - val res = apply(constr.inst) - excludedVars -= tv - res + try apply(constr.inst) + finally excludedVars -= tv } case _ => mapOver(tp) @@ -185,9 +187,6 @@ trait Infer extends Checkable { } /** Is type fully defined, i.e. no embedded anytypes or wildcards in it? - * - * @param tp ... - * @return ... */ private[typechecker] def isFullyDefined(tp: Type): Boolean = tp match { case WildcardType | BoundedWildcardType(_) | NoType => @@ -220,7 +219,7 @@ trait Infer extends Checkable { * @throws NoInstance */ def solvedTypes(tvars: List[TypeVar], tparams: List[Symbol], - variances: List[Int], upper: Boolean, depth: Int): List[Type] = { + variances: List[Variance], upper: Boolean, depth: Int): List[Type] = { if (tvars.nonEmpty) printInference("[solve types] solving for " + tparams.map(_.name).mkString(", ") + " in " + tvars.mkString(", ")) @@ -259,6 +258,8 @@ trait Infer extends Checkable { * This method seems to be performance critical. */ def normalize(tp: Type): Type = tp match { + case pt @ PolyType(tparams, restpe) => + logResult(s"Normalizing $tp in infer")(normalize(restpe)) case mt @ MethodType(params, restpe) if mt.isImplicit => normalize(restpe) case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => @@ -291,7 +292,7 @@ trait Infer extends Checkable { def errorValue = if (context.reportErrors) context.owner.newErrorValue(name) else stdErrorValue def errorSym = if (tree.isType) errorClass else errorValue - if (tree.hasSymbol) + if (tree.hasSymbolField) tree setSymbol errorSym tree setType ErrorType @@ -317,8 +318,8 @@ trait Infer extends Checkable { /* -- Tests & Checks---------------------------------------------------- */ - /** Check that <code>sym</code> is defined and accessible as a member of - * tree <code>site</code> with type <code>pre</code> in current context. + /** Check that `sym` is defined and accessible as a member of + * tree `site` with type `pre` in current context. * * Note: pre is not refchecked -- moreover, refchecking the resulting tree may not refcheck pre, * since pre may not occur in its type (callers should wrap the result in a TypeTreeWithDeferredRefCheck) @@ -327,7 +328,6 @@ trait Infer extends Checkable { if (sym.isError) { tree setSymbol sym setType ErrorType } else { - val topClass = context.owner.enclosingTopLevelClass if (context.unit.exists) context.unit.depends += sym.enclosingTopLevelClass @@ -471,14 +471,9 @@ trait Infer extends Checkable { } /** Return inferred type arguments of polymorphic expression, given - * its type parameters and result type and a prototype <code>pt</code>. + * its type parameters and result type and a prototype `pt`. * If no minimal type variables exist that make the - * instantiated type a subtype of <code>pt</code>, return null. - * - * @param tparams ... - * @param restpe ... - * @param pt ... - * @return ... + * instantiated type a subtype of `pt`, return null. */ private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type, useWeaklyCompatible: Boolean = false): (List[Type], List[TypeVar]) = { val tvars = tparams map freshVar @@ -505,24 +500,18 @@ trait Infer extends Checkable { /** Return inferred proto-type arguments of function, given * its type and value parameters and result type, and a - * prototype <code>pt</code> for the function result. + * prototype `pt` for the function result. * Type arguments need to be either determined precisely by * the prototype, or they are maximized, if they occur only covariantly * in the value parameter list. * If instantiation of a type parameter fails, * take WildcardType for the proto-type argument. - * - * @param tparams ... - * @param formals ... - * @param restype ... - * @param pt ... - * @return ... */ def protoTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type, pt: Type): List[Type] = { /** Map type variable to its instance, or, if `variance` is covariant/contravariant, * to its upper/lower bound */ - def instantiateToBound(tvar: TypeVar, variance: Int): Type = try { + def instantiateToBound(tvar: TypeVar, variance: Variance): Type = { lazy val hiBounds = tvar.constr.hiBounds lazy val loBounds = tvar.constr.loBounds lazy val upper = glb(hiBounds) @@ -535,23 +524,21 @@ trait Infer extends Checkable { //Console.println("instantiate "+tvar+tvar.constr+" variance = "+variance);//DEBUG if (tvar.constr.inst != NoType) instantiate(tvar.constr.inst) - else if ((variance & COVARIANT) != 0 && hiBounds.nonEmpty) - setInst(upper) - else if ((variance & CONTRAVARIANT) != 0 && loBounds.nonEmpty) + else if (loBounds.nonEmpty && variance.isContravariant) setInst(lower) - else if (hiBounds.nonEmpty && loBounds.nonEmpty && upper <:< lower) + else if (hiBounds.nonEmpty && (variance.isPositive || loBounds.nonEmpty && upper <:< lower)) setInst(upper) else WildcardType - } catch { - case ex: NoInstance => WildcardType } val tvars = tparams map freshVar if (isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) map2(tparams, tvars)((tparam, tvar) => - instantiateToBound(tvar, varianceInTypes(formals)(tparam))) + try instantiateToBound(tvar, varianceInTypes(formals)(tparam)) + catch { case ex: NoInstance => WildcardType } + ) else - tvars map (tvar => WildcardType) + tvars map (_ => WildcardType) } /** [Martin] Can someone comment this please? I have no idea what it's for @@ -598,7 +585,7 @@ trait Infer extends Checkable { * * Rewrite for repeated param types: Map T* entries to Seq[T]. * @return map from tparams to inferred arg, if inference was successful, tparams that map to None are considered left undetermined - * type parameters that are inferred as `scala.Nothing` and that are not covariant in <code>restpe</code> are taken to be undetermined + * type parameters that are inferred as `scala.Nothing` and that are not covariant in `restpe` are taken to be undetermined */ def adjustTypeArgs(tparams: List[Symbol], tvars: List[TypeVar], targs: List[Type], restpe: Type = WildcardType): AdjustedTypeArgs.Result = { val buf = AdjustedTypeArgs.Result.newBuilder[Symbol, Option[Type]] @@ -606,17 +593,16 @@ trait Infer extends Checkable { foreach3(tparams, tvars, targs) { (tparam, tvar, targ) => val retract = ( targ.typeSymbol == NothingClass // only retract Nothings - && (restpe.isWildcard || (varianceInType(restpe)(tparam) & COVARIANT) == 0) // don't retract covariant occurrences + && (restpe.isWildcard || !varianceInType(restpe)(tparam).isPositive) // don't retract covariant occurrences ) - // checks opt.virtPatmat directly so one need not run under -Xexperimental to use virtpatmat buf += ((tparam, if (retract) None else Some( if (targ.typeSymbol == RepeatedParamClass) targ.baseType(SeqClass) else if (targ.typeSymbol == JavaRepeatedParamClass) targ.baseType(ArrayClass) // this infers Foo.type instead of "object Foo" (see also widenIfNecessary) - else if (targ.typeSymbol.isModuleClass || ((opt.experimental || opt.virtPatmat) && tvar.constr.avoidWiden)) targ + else if (targ.typeSymbol.isModuleClass || tvar.constr.avoidWiden) targ else targ.widen ) )) @@ -626,7 +612,7 @@ trait Infer extends Checkable { /** Return inferred type arguments, given type parameters, formal parameters, * argument types, result type and expected result type. - * If this is not possible, throw a <code>NoInstance</code> exception. + * If this is not possible, throw a `NoInstance` exception. * Undetermined type arguments are represented by `definitions.NothingClass.tpe`. * No check that inferred parameters conform to their bounds is made here. * @@ -683,9 +669,57 @@ trait Infer extends Checkable { tvars, tparams, tparams map varianceInTypes(formals), false, lubDepth(formals) max lubDepth(argtpes) ) + // Can warn about inferring Any/AnyVal as long as they don't appear + // explicitly anywhere amongst the formal, argument, result, or expected type. + def canWarnAboutAny = !(pt :: restpe :: formals ::: argtpes exists (t => (t contains AnyClass) || (t contains AnyValClass))) + def argumentPosition(idx: Int): Position = context.tree match { + case x: ValOrDefDef => x.rhs match { + case Apply(fn, args) if idx < args.size => args(idx).pos + case _ => context.tree.pos + } + case _ => context.tree.pos + } + if (settings.warnInferAny.value && context.reportErrors && canWarnAboutAny) { + foreachWithIndex(targs) ((targ, idx) => + targ.typeSymbol match { + case sym @ (AnyClass | AnyValClass) => + context.unit.warning(argumentPosition(idx), s"a type was inferred to be `${sym.name}`; this may indicate a programming error.") + case _ => + } + ) + } adjustTypeArgs(tparams, tvars, targs, restpe) } + /** One must step carefully when assessing applicability due to + * complications from varargs, tuple-conversion, named arguments. + * This method is used to filter out inapplicable methods, + * its behavior slightly configurable based on what stage of + * overloading resolution we're at. + * + * This method has boolean parameters, which is usually suboptimal + * but I didn't work out a better way. They don't have defaults, + * and the method's scope is limited. + */ + private[typechecker] def isApplicableBasedOnArity(tpe: Type, argsCount: Int, varargsStar: Boolean, tuplingAllowed: Boolean): Boolean = followApply(tpe) match { + case OverloadedType(pre, alts) => + alts exists (alt => isApplicableBasedOnArity(pre memberType alt, argsCount, varargsStar, tuplingAllowed)) + case _ => + val paramsCount = tpe.params.length + val simpleMatch = paramsCount == argsCount + val varargsTarget = isVarArgsList(tpe.params) + def varargsMatch = varargsTarget && (paramsCount - 1) <= argsCount + def tuplingMatch = tuplingAllowed && eligibleForTupleConversion(paramsCount, argsCount, varargsTarget) + + // A varargs star call, e.g. (x, y:_*) can only match a varargs method + // with the same number of parameters. See SI-5859 for an example of what + // would fail were this not enforced before we arrived at isApplicable. + if (varargsStar) + varargsTarget && simpleMatch + else + simpleMatch || varargsMatch || tuplingMatch + } + private[typechecker] def followApply(tp: Type): Type = tp match { case NullaryMethodType(restp) => val restp1 = followApply(restp) @@ -702,14 +736,6 @@ trait Infer extends Checkable { else OverloadedType(tp, appmeth.alternatives) } - def hasExactlyNumParams(tp: Type, n: Int): Boolean = tp match { - case OverloadedType(pre, alts) => - alts exists (alt => hasExactlyNumParams(pre.memberType(alt), n)) - case _ => - val len = tp.params.length - len == n || isVarArgsList(tp.params) && len <= n + 1 - } - /** * Verifies whether the named application is valid. The logic is very * similar to the one in NamesDefaults.removeNames. @@ -755,17 +781,54 @@ trait Infer extends Checkable { (argtpes1, argPos, namesOK) } - /** don't do a () to (()) conversion for methods whose second parameter - * is a varargs. This is a fairly kludgey way to address #3224. - * We'll probably find a better way to do this by identifying - * tupled and n-ary methods, but thiws is something for a future major revision. + /** True if the given parameter list can accept a tupled argument list, + * and the argument list can be tupled (based on its length.) */ - def isUnitForVarArgs(args: List[AnyRef], params: List[Symbol]): Boolean = - args.isEmpty && hasLength(params, 2) && isVarArgsList(params) + def eligibleForTupleConversion(paramsCount: Int, argsCount: Int, varargsTarget: Boolean): Boolean = { + def canSendTuple = argsCount match { + case 0 => !varargsTarget // avoid () to (()) conversion - SI-3224 + case 1 => false // can't tuple a single argument + case n => n <= MaxTupleArity // <= 22 arguments + } + def canReceiveTuple = paramsCount match { + case 1 => true + case 2 => varargsTarget + case _ => false + } + canSendTuple && canReceiveTuple + } + def eligibleForTupleConversion(formals: List[Type], argsCount: Int): Boolean = formals match { + case p :: Nil => eligibleForTupleConversion(1, argsCount, varargsTarget = isScalaRepeatedParamType(p)) + case _ :: p :: Nil if isScalaRepeatedParamType(p) => eligibleForTupleConversion(2, argsCount, varargsTarget = true) + case _ => false + } - /** Is there an instantiation of free type variables <code>undetparams</code> - * such that function type <code>ftpe</code> is applicable to - * <code>argtpes</code> and its result conform to <code>pt</code>? + /** The type of an argument list after being coerced to a tuple. + * @pre: the argument list is eligible for tuple conversion. + */ + private def typeAfterTupleConversion(argtpes: List[Type]): Type = ( + if (argtpes.isEmpty) UnitClass.tpe // aka "Tuple0" + else tupleType(argtpes map { + case NamedType(name, tp) => UnitClass.tpe // not a named arg - only assignments here + case RepeatedType(tp) => tp // but probably shouldn't be tupling a call containing :_* + case tp => tp + }) + ) + + /** If the argument list needs to be tupled for the parameter list, + * a list containing the type of the tuple. Otherwise, the original + * argument list. + */ + def tupleIfNecessary(formals: List[Type], argtpes: List[Type]): List[Type] = { + if (eligibleForTupleConversion(formals, argtpes.size)) + typeAfterTupleConversion(argtpes) :: Nil + else + argtpes + } + + /** Is there an instantiation of free type variables `undetparams` + * such that function type `ftpe` is applicable to + * `argtpes` and its result conform to `pt`? * * @param undetparams ... * @param ftpe the type of the function (often a MethodType) @@ -780,23 +843,16 @@ trait Infer extends Checkable { argtpes0: List[Type], pt: Type): Boolean = ftpe match { case OverloadedType(pre, alts) => - alts exists (alt => isApplicable(undetparams, pre.memberType(alt), argtpes0, pt)) + alts exists (alt => isApplicable(undetparams, pre memberType alt, argtpes0, pt)) case ExistentialType(tparams, qtpe) => isApplicable(undetparams, qtpe, argtpes0, pt) case mt @ MethodType(params, _) => - val formals = formalTypes(mt.paramTypes, argtpes0.length, removeByName = false) - - def tryTupleApply: Boolean = { - // if 1 formal, 1 argtpe (a tuple), otherwise unmodified argtpes0 - val tupleArgTpes = actualTypes(argtpes0 map { - // no assignment is treated as named argument here - case NamedType(name, tp) => UnitClass.tpe - case tp => tp - }, formals.length) - - !sameLength(argtpes0, tupleArgTpes) && - !isUnitForVarArgs(argtpes0, params) && - isApplicable(undetparams, ftpe, tupleArgTpes, pt) + val argslen = argtpes0.length + val formals = formalTypes(mt.paramTypes, argslen, removeByName = false) + + def tryTupleApply = { + val tupled = tupleIfNecessary(mt.paramTypes, argtpes0) + (tupled ne argtpes0) && isApplicable(undetparams, ftpe, tupled, pt) } def typesCompatible(argtpes: List[Type]) = { val restpe = ftpe.resultType(argtpes) @@ -818,17 +874,16 @@ trait Infer extends Checkable { val lencmp = compareLengths(argtpes0, formals) if (lencmp > 0) tryTupleApply else if (lencmp == 0) { - if (!argtpes0.exists(_.isInstanceOf[NamedType])) { // fast track if no named arguments are used + if (!containsNamedType(argtpes0)) typesCompatible(argtpes0) - } else { // named arguments are used val (argtpes1, argPos, namesOK) = checkNames(argtpes0, params) // when using named application, the vararg param has to be specified exactly once - ( namesOK && (isIdentity(argPos) || sameLength(formals, params)) && - // nb. arguments and names are OK, check if types are compatible - typesCompatible(reorderArgs(argtpes1, argPos)) + ( namesOK + && (allArgsArePositional(argPos) || sameLength(formals, params)) + && typesCompatible(reorderArgs(argtpes1, argPos)) // nb. arguments and names are OK, check if types are compatible ) } } @@ -872,17 +927,13 @@ trait Infer extends Checkable { } else res1 } - /** Is type <code>ftpe1</code> strictly more specific than type <code>ftpe2</code> + /** Is type `ftpe1` strictly more specific than type `ftpe2` * when both are alternatives in an overloaded function? * @see SLS (sec:overloading-resolution) - * - * @param ftpe1 ... - * @param ftpe2 ... - * @return ... */ def isAsSpecific(ftpe1: Type, ftpe2: Type): Boolean = ftpe1 match { case OverloadedType(pre, alts) => - alts exists (alt => isAsSpecific(pre.memberType(alt), ftpe2)) + alts exists (alt => isAsSpecific(pre memberType alt, ftpe2)) case et: ExistentialType => isAsSpecific(ftpe1.skolemizeExistential, ftpe2) //et.withTypeVars(isAsSpecific(_, ftpe2)) @@ -909,7 +960,7 @@ trait Infer extends Checkable { case _ => ftpe2 match { case OverloadedType(pre, alts) => - alts forall (alt => isAsSpecific(ftpe1, pre.memberType(alt))) + alts forall (alt => isAsSpecific(ftpe1, pre memberType alt)) case et: ExistentialType => et.withTypeVars(isAsSpecific(ftpe1, _)) case mt: MethodType => @@ -1123,25 +1174,20 @@ trait Infer extends Checkable { /** Substitute free type variables `undetparams` of polymorphic argument * expression `tree` to `targs`, Error if `targs` is null. - * - * @param tree ... - * @param undetparams ... - * @param targs ... - * @param pt ... */ - private def substExpr(tree: Tree, undetparams: List[Symbol], - targs: List[Type], pt: Type) { + private def substExpr(tree: Tree, undetparams: List[Symbol], targs: List[Type], pt: Type) { if (targs eq null) { if (!tree.tpe.isErroneous && !pt.isErroneous) PolymorphicExpressionInstantiationError(tree, undetparams, pt) - } else { + } + else { new TreeTypeSubstituter(undetparams, targs).traverse(tree) notifyUndetparamsInferred(undetparams, targs) } } - /** Substitute free type variables <code>undetparams</code> of application - * <code>fn(args)</code>, given prototype <code>pt</code>. + /** Substitute free type variables `undetparams` of application + * `fn(args)`, given prototype `pt`. * * @param fn fn: the function that needs to be instantiated. * @param undetparams the parameters that need to be determined @@ -1156,7 +1202,7 @@ trait Infer extends Checkable { try { val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 val formals = formalTypes(mt.paramTypes, args.length) - val argtpes = actualTypes(args map (x => elimAnonymousClass(x.tpe.deconst)), formals.length) + val argtpes = tupleIfNecessary(formals, args map (x => elimAnonymousClass(x.tpe.deconst))) val restpe = fn.tpe.resultType(argtpes) val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) = @@ -1192,17 +1238,15 @@ trait Infer extends Checkable { } } - def widen(tp: Type): Type = abstractTypesToBounds(tp) - - /** Substitute free type variables <code>undetparams</code> of type constructor - * <code>tree</code> in pattern, given prototype <code>pt</code>. + /** Substitute free type variables `undetparams` of type constructor + * `tree` in pattern, given prototype `pt`. * * @param tree the constuctor that needs to be instantiated * @param undetparams the undetermined type parameters * @param pt the expected result type of the instance */ def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type) { - val pt = widen(pt0) + val pt = abstractTypesToBounds(pt0) val ptparams = freeTypeParamsOfTerms(pt) val ctorTp = tree.tpe val resTp = ctorTp.finalResultType @@ -1262,17 +1306,20 @@ trait Infer extends Checkable { } } else None - (inferFor(pt) orElse inferForApproxPt) map { targs => + val inferred = inferFor(pt) orElse inferForApproxPt + + inferred match { + case Some(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)")) + case _ => + def full = if (isFullyDefined(pt)) "(fully defined)" else "(not fully defined)" + devWarning(s"failed inferConstructorInstance for $tree: ${tree.tpe} undet=$undetparams, pt=$pt $full") // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) ConstrInstantiationError(tree, resTp, pt) } } - def instBounds(tvar: TypeVar): (Type, Type) = { val tparam = tvar.origin.typeSymbol val instType = toOrigin(tvar.constr.inst) @@ -1288,7 +1335,7 @@ trait Infer extends Checkable { val tvars1 = tvars map (_.cloneInternal) // Note: right now it's not clear that solving is complete, or how it can be made complete! // So we should come back to this and investigate. - solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (x => COVARIANT), false) + solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (_ => Variance.Covariant), false) } // this is quite nasty: it destructively changes the info of the syms of e.g., method type params (see #3692, where the type param T's bounds were set to >: T <: T, so that parts looped) @@ -1320,51 +1367,6 @@ trait Infer extends Checkable { } } - /** 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) - } - - /** Type intersection of simple type tp1 with general type tp2. * The result eliminates some redundancies. */ @@ -1383,7 +1385,7 @@ trait Infer extends Checkable { } def inferTypedPattern(tree0: Tree, pattp: Type, pt0: Type, canRemedy: Boolean): Type = { - val pt = widen(pt0) + val pt = abstractTypesToBounds(pt0) val ptparams = freeTypeParamsOfTerms(pt) val tpparams = freeTypeParamsOfTerms(pattp) @@ -1503,62 +1505,39 @@ trait Infer extends Checkable { } */ - /** Assign <code>tree</code> the symbol and type of the alternative which - * matches prototype <code>pt</code>, if it exists. + /** Assign `tree` the symbol and type of the alternative which + * matches prototype `pt`, if it exists. * If several alternatives match `pt`, take parameterless one. * If no alternative matches `pt`, take the parameterless one anyway. */ def inferExprAlternative(tree: Tree, pt: Type) = tree.tpe match { case OverloadedType(pre, alts) => tryTwice { isSecondTry => val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) - val noAlternatives = alts0.isEmpty - val alts1 = if (noAlternatives) alts else alts0 + val alts1 = if (alts0.isEmpty) alts else alts0 - //println("trying "+alts1+(alts1 map (_.tpe))+(alts1 map (_.locationString))+" for "+pt) - def improves(sym1: Symbol, sym2: Symbol): Boolean = - sym2 == NoSymbol || sym2.hasAnnotation(BridgeClass) || - { val tp1 = pre.memberType(sym1) + val bests = bestAlternatives(alts1) { (sym1, sym2) => + val tp1 = pre.memberType(sym1) val tp2 = pre.memberType(sym2) - (tp2 == ErrorType || - !global.typer.infer.isWeaklyCompatible(tp2, pt) && global.typer.infer.isWeaklyCompatible(tp1, pt) || - isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)) } - val best = ((NoSymbol: Symbol) /: alts1) ((best, alt) => - if (improves(alt, best)) alt else best) - - val competing = alts1 dropWhile (alt => best == alt || improves(best, alt)) - - if (best == NoSymbol) { - if (settings.debug.value) { - tree match { - case Select(qual, _) => - Console.println("qual: " + qual + ":" + qual.tpe + - " with decls " + qual.tpe.decls + - " with members " + qual.tpe.members + - " with members " + qual.tpe.member(newTermName("$minus"))) - case _ => + ( tp2 == ErrorType + || (!isWeaklyCompatible(tp2, pt) && isWeaklyCompatible(tp1, pt)) + || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) + ) } - } - // todo: missing test case - NoBestExprAlternativeError(tree, pt, isSecondTry) - } else if (!competing.isEmpty) { - if (noAlternatives) NoBestExprAlternativeError(tree, pt, isSecondTry) - else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt, isSecondTry) - else { + // todo: missing test case for bests.isEmpty + bests match { + case best :: Nil => tree setSymbol best setType (pre memberType best) + case best :: competing :: _ if alts0.nonEmpty => // SI-6912 Don't give up and leave an OverloadedType on the tree. // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try // unless an error is issued. We're not issuing an error, in the assumption that it would be // spurious in light of the erroneous expected type - setError(tree) + if (pt.isErroneous) setError(tree) + else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) } - } else { -// val applicable = alts1 filter (alt => -// global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt)) -// checkNotShadowed(tree.pos, pre, best, applicable) - tree.setSymbol(best).setType(pre.memberType(best)) } } - } @inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = { val oldState = context.state @@ -1574,48 +1553,47 @@ trait Infer extends Checkable { private def paramMatchesName(param: Symbol, name: Name) = param.name == name || param.deprecatedParamName.exists(_ == name) - // Check the first parameter list the same way. - private def methodMatchesName(method: Symbol, name: Name) = method.paramss match { - case ps :: _ => ps exists (p => paramMatchesName(p, name)) - case _ => false + private def containsNamedType(argtpes: List[Type]): Boolean = argtpes match { + case Nil => false + case NamedType(_, _) :: _ => true + case _ :: rest => containsNamedType(rest) } - - private def resolveOverloadedMethod(argtpes: List[Type], eligible: List[Symbol]) = { + private def namesOfNamedArguments(argtpes: List[Type]) = + argtpes collect { case NamedType(name, _) => name } + + /** Given a list of argument types and eligible method overloads, whittle the + * list down to the methods which should be considered for specificity + * testing, taking into account here: + * - named arguments at the call site (keep only methods with name-matching parameters) + * - if multiple methods are eligible, drop any methods which take default arguments + * - drop any where arity cannot match under any conditions (allowing for + * overloaded applies, varargs, and tupling conversions) + * This method is conservative; it can tolerate some varieties of false positive, + * but no false negatives. + * + * @param eligible the overloaded method symbols + * @param argtpes the argument types at the call site + * @param varargsStar true if the call site has a `: _*` attached to the last argument + */ + private def overloadsToConsiderBySpecificity(eligible: List[Symbol], argtpes: List[Type], varargsStar: Boolean): List[Symbol] = { // If there are any foo=bar style arguments, and any of the overloaded // methods has a parameter named `foo`, then only those methods are considered. - val namesOfArgs = argtpes collect { case NamedType(name, _) => name } - val namesMatch = ( - if (namesOfArgs.isEmpty) Nil - else eligible filter { m => - namesOfArgs forall { name => - methodMatchesName(m, name) + val namesMatch = namesOfNamedArguments(argtpes) match { + case Nil => Nil + case names => eligible filter (m => names forall (name => m.info.params exists (p => paramMatchesName(p, name)))) } - } + if (namesMatch.nonEmpty) + namesMatch + else if (eligible.isEmpty || eligible.tail.isEmpty) + eligible + else + eligible filter (alt => + !alt.hasDefault && isApplicableBasedOnArity(alt.tpe, argtpes.length, varargsStar, tuplingAllowed = true) ) - - if (namesMatch.nonEmpty) namesMatch - else if (eligible.isEmpty || eligible.tail.isEmpty) eligible - else eligible filter { alt => - // for functional values, the `apply` method might be overloaded - val mtypes = followApply(alt.tpe) match { - case OverloadedType(_, alts) => alts map (_.tpe) - case t => t :: Nil } - // Drop those that use a default; keep those that use vararg/tupling conversion. - mtypes exists (t => - !t.typeSymbol.hasDefaultFlag && ( - compareLengths(t.params, argtpes) < 0 // tupling (*) - || hasExactlyNumParams(t, argtpes.length) // same nb or vararg - ) - ) - // (*) more arguments than parameters, but still applicable: tupling conversion works. - // todo: should not return "false" when paramTypes = (Unit) no argument is given - // (tupling would work) - } - } - /** Assign <code>tree</code> the type of an alternative which is applicable - * to <code>argtpes</code>, and whose result type is compatible with `pt`. + /** Assign `tree` the type of an alternative which is applicable + * to `argtpes`, and whose result type is compatible with `pt`. * If several applicable alternatives exist, drop the alternatives which use * default arguments, then select the most specialized one. * If no applicable alternative exists, and pt != WildcardType, try again @@ -1627,52 +1605,42 @@ trait Infer extends Checkable { * of some NamedType does not exist in an alternative's parameter names, * the type is replaces by `Unit`, i.e. the argument is treated as an * assignment expression. + * + * @pre tree.tpe is an OverloadedType. */ - def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], - argtpes: List[Type], pt0: Type, varArgsOnly: Boolean = false, lastInferAttempt: Boolean = true): Unit = tree.tpe match { - case OverloadedType(pre, alts) => - val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 - tryTwice { isSecondTry => - debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") - - def varargsApplicableCheck(alt: Symbol) = !varArgsOnly || ( - isVarArgsList(alt.tpe.params) - && (argtpes.size >= alt.tpe.params.size) // must be checked now due to SI-5859 - ) - val applicable = resolveOverloadedMethod(argtpes, - alts filter (alt => - varargsApplicableCheck(alt) - && inSilentMode(context)(isApplicable(undetparams, followApply(pre memberType alt), argtpes, pt)) - ) - ) - - def improves(sym1: Symbol, sym2: Symbol) = { - // util.trace("improve "+sym1+sym1.locationString+" on "+sym2+sym2.locationString) - sym2 == NoSymbol || sym2.isError || sym2.hasAnnotation(BridgeClass) || - isStrictlyMoreSpecific(followApply(pre.memberType(sym1)), - followApply(pre.memberType(sym2)), sym1, sym2) + def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], argtpes0: List[Type], pt0: Type): Unit = { + val OverloadedType(pre, alts) = tree.tpe + var varargsStar = false + val argtpes = argtpes0 mapConserve { + case RepeatedType(tp) => varargsStar = true ; tp + case tp => tp + } + def followType(sym: Symbol) = followApply(pre memberType sym) + def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = { + val applicable0 = alts filter (alt => inSilentMode(context)(isApplicable(undetparams, followType(alt), argtpes, pt))) + val applicable = overloadsToConsiderBySpecificity(applicable0, argtpes, varargsStar) + val ranked = bestAlternatives(applicable)((sym1, sym2) => + isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2) + ) + ranked match { + case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous + case best :: Nil => tree setSymbol best setType (pre memberType best) // success + case Nil if pt eq WildcardType => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed + case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType } - - val best = ((NoSymbol: Symbol) /: applicable) ((best, alt) => - if (improves(alt, best)) alt else best) - val competing = applicable.dropWhile(alt => best == alt || improves(best, alt)) - if (best == NoSymbol) { - if (pt == WildcardType) NoBestMethodAlternativeError(tree, argtpes, pt, isSecondTry && lastInferAttempt) - else inferMethodAlternative(tree, undetparams, argtpes, WildcardType, lastInferAttempt = isSecondTry) - } else if (!competing.isEmpty) { - AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt, isSecondTry && lastInferAttempt) - } else { -// checkNotShadowed(tree.pos, pre, best, applicable) - tree.setSymbol(best).setType(pre.memberType(best)) } + // This potentially makes up to four attempts: tryTwice may execute + // with and without views enabled, and bestForExpectedType will try again + // with pt = WildcardType if it fails with pt != WildcardType. + tryTwice { isLastTry => + val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 + debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") + bestForExpectedType(pt, isLastTry) } - case _ => } /** Try inference twice, once without views and once with views, * unless views are already disabled. - * - * @param infer ... */ def tryTwice(infer: Boolean => Unit): Unit = { if (context.implicitsEnabled) { @@ -1707,12 +1675,9 @@ trait Infer extends Checkable { else infer(true) } - /** Assign <code>tree</code> the type of all polymorphic alternatives - * with <code>nparams</code> as the number of type parameters, if it exists. + /** Assign `tree` the type of all polymorphic alternatives + * with `nparams` as the number of type parameters, if it exists. * If no such polymorphic alternative exist, error. - * - * @param tree ... - * @param nparams ... */ def inferPolyAlternatives(tree: Tree, argtypes: List[Type]): Unit = { val OverloadedType(pre, alts) = tree.tpe diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 0ba30ffa73..8829a9a92e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -3,15 +3,11 @@ package typechecker import symtab.Flags._ import scala.tools.nsc.util._ -import scala.tools.nsc.util.ClassPath._ import scala.reflect.runtime.ReflectionUtils import scala.collection.mutable.ListBuffer -import scala.compat.Platform.EOL +import scala.reflect.ClassTag import scala.reflect.internal.util.Statistics import scala.reflect.macros.util._ -import java.lang.{Class => jClass} -import java.lang.reflect.{Array => jArray, Method => jMethod} -import scala.reflect.internal.util.Collections._ import scala.util.control.ControlThrowable import scala.reflect.macros.runtime.AbortMacroException @@ -128,7 +124,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // todo. refactor when fixing SI-5498 def className: String = { def loop(sym: Symbol): String = sym match { - case sym if sym.owner.isPackageClass => + case sym if sym.isTopLevel => val suffix = if (sym.isModuleClass) "$" else "" sym.fullName + suffix case sym => @@ -293,51 +289,51 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { private def macroImplSig(macroDef: Symbol, tparams: List[TypeDef], vparamss: List[List[ValDef]], retTpe: Type): (List[List[Symbol]], Type) = { // had to move method's body to an object because of the recursive dependencies between sigma and param object SigGenerator { - 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, ctxParam), MacroContextPrefix), ExprValue) - case SingleType(NoPrefix, sym) => - mfind(vparamss)(_.symbol == sym) match { - case Some(macroDefParam) => SingleType(SingleType(NoPrefix, param(macroDefParam)), ExprValue) - case _ => pre - } - case _ => - pre - } - TypeRef(pre1, sym, args map mapOver) - case _ => - mapOver(tp) - } + def WeakTagClass = getMember(MacroContextClass, tpnme.WeakTypeTag) + def ExprClass = getMember(MacroContextClass, tpnme.Expr) + val cache = scala.collection.mutable.Map[Symbol, Symbol]() + val ctxParam = makeParam(nme.macroContext, macroDef.pos, MacroContextClass.tpe, SYNTHETIC) + val paramss = List(ctxParam) :: mmap(vparamss)(param) + val implReturnType = typeRef(singleType(NoPrefix, ctxParam), ExprClass, List(sigma(retTpe))) + + object SigmaTypeMap extends TypeMap { + def mapPrefix(pre: Type) = pre match { + case ThisType(sym) if sym == macroDef.owner => + singleType(singleType(singleType(NoPrefix, ctxParam), MacroContextPrefix), ExprValue) + case SingleType(NoPrefix, sym) => + mfind(vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue)) + case _ => + mapOver(pre) + } + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = mapPrefix(pre) + val args1 = mapOverArgs(args, sym.typeParams) + if ((pre eq pre1) && (args eq args1)) tp + else typeRef(pre1, sym, args1) + case _ => + mapOver(tp) } - - new SigmaTypeMap() apply tpe } + def sigma(tpe: Type): Type = SigmaTypeMap(tpe) + + def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) = + macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe + def implType(isType: Boolean, origTpe: Type): Type = { + def tsym = if (isType) WeakTagClass else ExprClass + def targ = origTpe.typeArgs.headOption getOrElse NoType - 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.WeakTypeTag else tpnme.Expr) + scalaRepeatedType(implType(isType, sigma(targ))) + else typeRef(singleType(NoPrefix, ctxParam), tsym, List(sigma(origTpe))) - } - val paramCache = scala.collection.mutable.Map[Symbol, Symbol]() - def param(tree: Tree): Symbol = - paramCache.getOrElseUpdate(tree.symbol, { + } + def param(tree: Tree): Symbol = ( + cache.getOrElseUpdate(tree.symbol, { val sym = tree.symbol makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe), sym.flags) }) - - val paramss = List(ctxParam) :: mmap(vparamss)(param) - val implRetTpe = typeRef(singleType(NoPrefix, ctxParam), getMember(MacroContextClass, tpnme.Expr), List(sigma(retTpe))) + ) } import SigGenerator._ @@ -345,7 +341,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { macroTraceVerbose("tparams are: ")(tparams) macroTraceVerbose("vparamss are: ")(vparamss) macroTraceVerbose("retTpe is: ")(retTpe) - macroTraceVerbose("macroImplSig is: ")((paramss, implRetTpe)) + macroTraceVerbose("macroImplSig is: ")((paramss, implReturnType)) } /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method, @@ -362,7 +358,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // Phase I: sanity checks val macroDef = macroDdef.symbol macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos)) - assert(macroDef.isTermMacro, macroDdef) + assert(macroDef.isMacro, macroDdef) if (fastTrack contains macroDef) MacroDefIsFastTrack() if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) MacroFeatureNotEnabled() @@ -377,7 +373,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // doesn't manifest itself as an error in the resulting tree val prevNumErrors = reporter.ERROR.count var rhs1 = typer.typed1(rhs, EXPRmode, WildcardType) - def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous + def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isMacro && !rhs1.symbol.isErroneous while (rhsNeedsMacroExpansion) { rhs1 = macroExpand1(typer, rhs1) match { case Success(expanded) => @@ -390,8 +386,12 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } case Fallback(fallback) => typer.typed1(fallback, EXPRmode, WildcardType) - case Other(result) => - result + case Delayed(delayed) => + delayed + case Skipped(skipped) => + skipped + case Failure(failure) => + failure } } val typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors @@ -475,9 +475,6 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * Is also capable of detecting REPL and reusing its classloader. */ lazy val macroClassloader: ClassLoader = { - if (global.forMSIL) - throw new UnsupportedOperationException("Scala reflection not available on this platform") - val classpath = global.classPath.asURLs macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath)) val loader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) @@ -541,19 +538,22 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } } - private def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext = + private def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext = { new { val universe: self.global.type = self.global val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] - val expandee = expandeeTree + val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).original orElse expandeeTree + val macroRole = universe.analyzer.macroExpanderAttachment(expandeeTree).role } with UnaffiliatedMacroContext { val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing) override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */) } + } /** Calculate the arguments to pass to a macro implementation when expanding the provided tree. */ case class MacroArgs(c: MacroContext, others: List[Any]) + private def macroArgs(typer: Typer, expandee: Tree): MacroArgs = { val macroDef = expandee.symbol val prefixTree = expandee.collect{ case Select(qual, name) => qual }.headOption.getOrElse(EmptyTree) @@ -582,9 +582,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val preparedArgss: List[List[Any]] = if (fastTrack contains macroDef) { - if (fastTrack(macroDef) validate context) argss + // Take a dry run of the fast track implementation + if (fastTrack(macroDef) validate expandee) argss else typer.TyperErrorGen.MacroPartialApplicationError(expandee) - } else { + } + else { // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences // consider the following example: // @@ -650,21 +652,37 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { private def popMacroContext() = _openMacros = _openMacros.tail def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition - private sealed abstract class MacroExpansionResult - private case class Success(expanded: Tree) extends MacroExpansionResult - private case class Fallback(fallback: Tree) extends MacroExpansionResult { currentRun.seenMacroExpansionsFallingBack = true } - private case class Other(result: Tree) extends MacroExpansionResult - private def Delay(expanded: Tree) = Other(expanded) - private def Skip(expanded: Tree) = Other(expanded) - private def Cancel(expandee: Tree) = Other(expandee) - private def Failure(expandee: Tree) = Other(expandee) + /** Describes the role that the macro expandee is performing. + */ + type MacroRole = String + final def APPLY_ROLE: MacroRole = "APPLY_ROLE" + private val roleNames = Map(APPLY_ROLE -> "apply") /** 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 + * + * ========= Expandable trees ========= + * + * A term of one of the following shapes: + * + * Ident(<term macro>) + * Select(<any qualifier>, <term macro>) + * TypeApply(<any of the above>, <targs>) + * Apply(...Apply(<any of the above>, <args1>)...<argsN>) + * + * ========= Macro expansion ========= + * + * First of all `macroExpandXXX`: + * 1) If necessary desugars the `expandee` to fit into `macroExpand1` + * + * Then `macroExpand1`: + * 2) Checks whether the expansion needs to be delayed (see `mustDelayMacroExpansion`) + * 3) Loads macro implementation using `macroMirror` + * 4) Synthesizes invocation arguments for the macro implementation + * 5) Checks that the result is a tree or an expr bound to this universe + * + * Finally `macroExpandXXX`: + * 6) Validates the expansion against the white list of supported tree shapes + * 7) Typechecks the result as required by the circumstances of the macro application * * If -Ymacro-debug-lite is enabled, you will get basic notifications about macro expansion * along with macro expansions logged in the form that can be copy/pasted verbatim into REPL. @@ -675,60 +693,126 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * * @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 fallback tree 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, mode: Int = EXPRmode, pt: Type = WildcardType): Tree = { - if (settings.Ymacronoexpand.value) return expandee // SI-6812 - val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null - if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) - try { - macroExpand1(typer, expandee) match { - case Success(expanded) => - try { - def typecheck(phase: String, tree: Tree, pt: Type): Tree = { - if (tree.isErroneous) return tree - macroLogVerbose(s"typechecking against $phase $pt: $expanded") - val numErrors = reporter.ERROR.count - def hasNewErrors = reporter.ERROR.count > numErrors - val result = typer.context.withImplicitsEnabled(typer.typed(tree, EXPRmode, pt)) - macroTraceVerbose(s"""${if (hasNewErrors) "failed to typecheck" else "successfully typechecked"} against $phase $pt:\n$result\n""")(result) + private abstract class MacroExpander[Result: ClassTag](val role: MacroRole, val typer: Typer, val expandee: Tree) { + def allowExpandee(expandee: Tree): Boolean = true + def allowExpanded(expanded: Tree): Boolean = true + def allowedExpansions: String = "anything" + def allowResult(result: Result): Boolean = true + + def onSuccess(expanded: Tree): Result + def onFallback(expanded: Tree): Result + def onSuppressed(expandee: Tree): Result = expandee match { case expandee: Result => expandee } + def onDelayed(expanded: Tree): Result = expanded match { case expanded: Result => expanded } + def onSkipped(expanded: Tree): Result = expanded match { case expanded: Result => expanded } + def onFailure(expanded: Tree): Result = { typer.infer.setError(expandee); expandee match { case expandee: Result => expandee } } + + def apply(desugared: Tree): Result = { + if (isMacroExpansionSuppressed(desugared)) onSuppressed(expandee) + else expand(desugared) + } + + protected def expand(desugared: Tree): Result = { + def showDetailed(tree: Tree) = showRaw(tree, printIds = true, printTypes = true) + def summary() = s"expander = $this, expandee = ${showDetailed(expandee)}, desugared = ${if (expandee == desugared) () else showDetailed(desugared)}" + if (macroDebugVerbose) println(s"macroExpand: ${summary()}") + assert(allowExpandee(expandee), summary()) + + val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null + if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) + try { + linkExpandeeAndDesugared(expandee, desugared, role) + macroExpand1(typer, desugared) match { + case Success(expanded) => + if (allowExpanded(expanded)) { + // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc + val expanded1 = try onSuccess(duplicateAndKeepPositions(expanded)) finally popMacroContext() + if (!hasMacroExpansionAttachment(expanded1)) linkExpandeeAndExpanded(expandee, expanded1) + if (allowResult(expanded1)) expanded1 else onFailure(expanded) + } else { + typer.TyperErrorGen.MacroInvalidExpansionError(expandee, roleNames(role), allowedExpansions) + onFailure(expanded) } + case Fallback(fallback) => onFallback(fallback) + case Delayed(delayed) => onDelayed(delayed) + case Skipped(skipped) => onSkipped(skipped) + case Failure(failure) => onFailure(failure) + } + } finally { + if (Statistics.canEnable) Statistics.stopTimer(macroExpandNanos, start) + } + } + } - var expectedTpe = expandee.tpe - if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType - var typechecked = typecheck("macro def return type", expanded, expectedTpe) - typechecked = typecheck("expected type", typechecked, pt) - typechecked - } finally { - popMacroContext() - } - case Fallback(fallback) => - typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt)) - case Other(result) => - result + /** Expands a tree that carries a term, which happens to be a term macro. + * @see MacroExpander + */ + private abstract class TermMacroExpander(role: MacroRole, typer: Typer, expandee: Tree, mode: Mode, pt: Type) + extends MacroExpander[Tree](role, typer, expandee) { + override def allowedExpansions: String = "term trees" + override def allowExpandee(expandee: Tree) = expandee.isTerm + override def onSuccess(expanded: Tree) = typer.typed(expanded, mode, pt) + override def onFallback(fallback: Tree) = typer.typed(fallback, mode, pt) + } + + /** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`. + * @see MacroExpander + */ + def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, pt: Type) = { + object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, pt) { + override def onSuccess(expanded: Tree) = { + // prematurely annotate the tree with a macro expansion attachment + // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup + linkExpandeeAndExpanded(expandee, expanded) + var expectedTpe = expandee.tpe + if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType + // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled + // therefore we need to re-enable the conversions back temporarily + if (macroDebugVerbose) println(s"typecheck #1 (against expectedTpe = $expectedTpe): $expanded") + val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, expectedTpe)) + if (expanded1.isErrorTyped) { + if (macroDebugVerbose) println(s"typecheck #1 has failed: ${typer.context.errBuffer}") + expanded1 + } else { + if (macroDebugVerbose) println(s"typecheck #2 (against pt = $pt): $expanded1") + val expanded2 = typer.context.withImplicitsEnabled(super.onSuccess(expanded1)) + if (macroDebugVerbose && expanded2.isErrorTyped) println(s"typecheck #2 has failed: ${typer.context.errBuffer}") + expanded2 + } } - } finally { - if (Statistics.canEnable) Statistics.stopTimer(macroExpandNanos, start) } + expander(expandee) } + /** Captures statuses of macro expansions performed by `macroExpand1'. + */ + private sealed abstract class MacroStatus(val result: Tree) + private case class Success(expanded: Tree) extends MacroStatus(expanded) + private case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.seenMacroExpansionsFallingBack = true } + private case class Delayed(delayed: Tree) extends MacroStatus(delayed) + private case class Skipped(skipped: Tree) extends MacroStatus(skipped) + private case class Failure(failure: Tree) extends MacroStatus(failure) + private def Delay(expanded: Tree) = Delayed(expanded) + private def Skip(expanded: Tree) = Skipped(expanded) + private def Cancel(expandee: Tree) = 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 = + private def macroExpand1(typer: Typer, expandee: Tree): MacroStatus = { // verbose printing might cause recursive macro expansions, so I'm shutting it down here withInfoLevel(nodePrinters.InfoLevel.Quiet) { if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" macroTraceVerbose("cancelled macro expansion because of %s: ".format(reason))(expandee) - return Cancel(typer.infer.setError(expandee)) + Cancel(typer.infer.setError(expandee)) } - - try { + else try { val runtime = macroRuntime(expandee.symbol) if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) else macroExpandWithoutRuntime(typer, expandee) @@ -736,18 +820,23 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { case typer.TyperErrorGen.MacroExpansionException => Failure(expandee) } } + } /** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded * Meant for internal use within the macro infrastructure, don't use it elsewhere. */ - private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroExpansionResult = { + private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroStatus = { val wasDelayed = isDelayed(expandee) val undetparams = calculateUndetparams(expandee) val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty (wasDelayed, nowDelayed) match { - case (true, true) => Delay(expandee) - case (true, false) => Skip(macroExpandAll(typer, expandee)) + case (true, true) => + Delay(expandee) + case (true, false) => + val expanded = macroExpandAll(typer, expandee) + if (expanded exists (_.isErroneous)) Failure(expandee) + else Skip(expanded) case (false, true) => macroLogLite("macro expansion is delayed: %s".format(expandee)) delayed += expandee -> undetparams @@ -762,15 +851,16 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { def hasNewErrors = reporter.ERROR.count > numErrors val expanded = { pushMacroContext(args.c); runtime(args) } if (hasNewErrors) MacroGeneratedTypeError(expandee) + def validateResultingTree(expanded: Tree) = { + macroLogVerbose("original:") + macroLogLite("" + expanded + "\n" + showRaw(expanded)) + val freeSyms = expanded.freeTerms ++ expanded.freeTypes + freeSyms foreach (sym => MacroFreeSymbolError(expandee, sym)) + Success(atPos(enclosingMacroPosition.focus)(expanded)) + } expanded match { - case expanded: Expr[_] => - macroLogVerbose("original:") - macroLogLite("" + expanded.tree + "\n" + showRaw(expanded.tree)) - val freeSyms = expanded.tree.freeTerms ++ expanded.tree.freeTypes - freeSyms foreach (sym => MacroFreeSymbolError(expandee, sym)) - Success(atPos(enclosingMacroPosition.focus)(expanded.tree updateAttachment MacroExpansionAttachment(expandee))) - case _ => - MacroExpansionIsNotExprError(expandee, expanded) + case expanded: Expr[_] if expandee.symbol.isTermMacro => validateResultingTree(expanded.tree) + case _ => MacroExpansionHasInvalidTypeError(expandee, expanded) } } catch { case ex: Throwable => @@ -791,7 +881,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { /** Expands a macro when a runtime (i.e. the macro implementation) cannot be loaded * Meant for internal use within the macro infrastructure, don't use it elsewhere. */ - private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroExpansionResult = { + private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroStatus = { import typer.TyperErrorGen._ val fallbackSym = expandee.symbol.nextOverriddenSymbol orElse MacroImplementationNotFoundError(expandee) macroTraceLite("falling back to: ")(fallbackSym) @@ -861,13 +951,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { new Transformer { override def transform(tree: Tree) = super.transform(tree match { // todo. expansion should work from the inside out - case tree if (delayed contains tree) && calculateUndetparams(tree).isEmpty => + case tree if (delayed contains tree) && calculateUndetparams(tree).isEmpty && !tree.isErroneous => val context = tree.attachments.get[MacroRuntimeAttachment].get.typerContext delayed -= tree context.implicitsEnabled = typer.context.implicitsEnabled context.enrichmentEnabled = typer.context.enrichmentEnabled context.macrosEnabled = typer.context.macrosEnabled - macroExpand(newTyper(context), tree, EXPRmode, WildcardType) + macroExpandApply(newTyper(context), tree, EXPRmode, WildcardType) case _ => tree }) diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 99557d1527..8c686107b4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package typechecker import symtab.Flags._ -import scala.collection.{ mutable, immutable } import scala.reflect.internal.util.StringOps.{ ojoin } import scala.reflect.ClassTag import scala.reflect.runtime.{ universe => ru } @@ -30,66 +29,30 @@ trait MethodSynthesis { if (sym.isLazy) ValDef(sym, body) else DefDef(sym, body) - def applyTypeInternal(tags: List[TT[_]]): Type = { - val symbols = tags map compilerSymbolFromTag - val container :: args = symbols - val tparams = container.typeConstructor.typeParams - - // Conservative at present - if manifests were more usable this could do a lot more. - // [Eugene to Paul] all right, they are now. what do you have in mind? - require(symbols forall (_ ne NoSymbol), "Must find all tags: " + symbols) - require(container.owner.isPackageClass, "Container must be a top-level class in a package: " + container) - require(tparams.size == args.size, "Arguments must match type constructor arity: " + tparams + ", " + args) - - appliedType(container, args map (_.tpe): _*) - } - - def companionType[T](implicit ct: CT[T]) = - rootMirror.getRequiredModule(ct.runtimeClass.getName).tpe - - // Use these like `applyType[List, Int]` or `applyType[Map, Int, String]` - def applyType[CC](implicit t1: TT[CC]): Type = - applyTypeInternal(List(t1)) - - def applyType[CC[X1], X1](implicit t1: TT[CC[_]], t2: TT[X1]): Type = - applyTypeInternal(List(t1, t2)) - - def applyType[CC[X1, X2], X1, X2](implicit t1: TT[CC[_,_]], t2: TT[X1], t3: TT[X2]): Type = - applyTypeInternal(List(t1, t2, t3)) - - def applyType[CC[X1, X2, X3], X1, X2, X3](implicit t1: TT[CC[_,_,_]], t2: TT[X1], t3: TT[X2], t4: TT[X3]): Type = - applyTypeInternal(List(t1, t2, t3, t4)) - - def newMethodType[F](owner: Symbol)(implicit t: TT[F]): Type = { - val fnSymbol = compilerSymbolFromTag(t) - val formals = compilerTypeFromTag(t).typeArguments - assert(fnSymbol isSubClass FunctionClass(formals.size - 1), (owner, t)) - val params = owner newSyntheticValueParams formals - 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) + /** 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) { def mkThis = This(clazz) setPos clazz.pos.focus - def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)(Select(mkThis, sym)) + def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)( + if (clazz.isClass) Select(This(clazz), sym) else Ident(sym) + ) private def isOverride(name: TermName) = clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz)) @@ -99,7 +62,7 @@ trait MethodSynthesis { overrideFlag | SYNTHETIC } def newMethodFlags(method: Symbol) = { - val overrideFlag = if (isOverride(method.name)) OVERRIDE else 0L + val overrideFlag = if (isOverride(method.name.toTermName)) OVERRIDE else 0L (method.flags | overrideFlag | SYNTHETIC) & ~DEFERRED } @@ -107,11 +70,13 @@ trait MethodSynthesis { localTyper typed ValOrDefDef(method, f(method)) private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = { - val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) + val name1 = name.toTermName + val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1)) finishMethod(m setInfoAndEnter info, f) } private def createInternal(name: Name, f: Symbol => Tree, infoFn: Symbol => Type): Tree = { - val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) + val name1 = name.toTermName + val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1)) finishMethod(m setInfoAndEnter infoFn(m), f) } private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = { @@ -119,22 +84,9 @@ trait MethodSynthesis { finishMethod(clazz.info.decls enter m, f) } - private def cloneInternal(original: Symbol, f: Symbol => Tree): Tree = - cloneInternal(original, f, original.name) - def clazzMember(name: Name) = clazz.info nonPrivateMember name def typeInClazz(sym: Symbol) = clazz.thisType memberType sym - /** Function argument takes the newly created method symbol of - * the same type as `name` in clazz, and returns the tree to be - * added to the template. - */ - def overrideMethod(name: Name)(f: Symbol => Tree): Tree = - overrideMethod(clazzMember(name))(f) - - def overrideMethod(original: Symbol)(f: Symbol => Tree): Tree = - cloneInternal(original, sym => f(sym setFlag OVERRIDE)) - def deriveMethod(original: Symbol, nameFn: Name => Name)(f: Symbol => Tree): Tree = cloneInternal(original, f, nameFn(original.name)) @@ -174,7 +126,7 @@ trait MethodSynthesis { /** There are two key methods in here. * - * 1) Enter methods such as enterGetterSetterare called + * 1) Enter methods such as enterGetterSetter are 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. @@ -219,14 +171,46 @@ trait MethodSynthesis { enterBeans(tree) } + /** This is called for those ValDefs which addDerivedTrees ignores, but + * which might have a warnable annotation situation. + */ + private def warnForDroppedAnnotations(tree: Tree) { + val annotations = tree.symbol.initialize.annotations + val targetClass = defaultAnnotationTarget(tree) + val retained = deriveAnnotations(annotations, targetClass, keepClean = true) + + annotations filterNot (retained contains _) foreach (ann => issueAnnotationWarning(tree, ann, targetClass)) + } + private def issueAnnotationWarning(tree: Tree, ann: AnnotationInfo, defaultTarget: Symbol) { + global.reporter.warning(ann.pos, + s"no valid targets for annotation on ${tree.symbol} - it is discarded unused. " + + s"You may specify targets with meta-annotations, e.g. @($ann @${defaultTarget.name})") + } + def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) => // If we don't save the annotations, they seem to wander off. val annotations = stat.symbol.initialize.annotations - ( allValDefDerived(vd) + val trees = ( + allValDefDerived(vd) map (acc => atPos(vd.pos.focus)(acc derive annotations)) filterNot (_ eq EmptyTree) ) + // Verify each annotation landed safely somewhere, else warn. + // Filtering when isParamAccessor is a necessary simplification + // because there's a bunch of unwritten annotation code involving + // the propagation of annotations - constructor parameter annotations + // may need to make their way to parameters of the constructor as + // well as fields of the class, etc. + if (!mods.isParamAccessor) annotations foreach (ann => + if (!trees.exists(_.symbol hasAnnotation ann.symbol)) + issueAnnotationWarning(vd, ann, GetterTargetClass) + ) + + trees + case vd: ValDef => + warnForDroppedAnnotations(vd) + vd :: Nil case cd @ ClassDef(mods, _, _, _) if mods.isImplicit => val annotations = stat.symbol.initialize.annotations // TODO: need to shuffle annotations between wrapper and class. @@ -253,8 +237,7 @@ trait MethodSynthesis { ) def beanAccessors(vd: ValDef): List[DerivedFromValDef] = { val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil - if (forMSIL) Nil - else if (vd.symbol hasAnnotation BeanPropertyAttr) + if (vd.symbol hasAnnotation BeanPropertyAttr) BeanGetter(vd) :: setter else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr) BooleanBeanGetter(vd) :: setter @@ -312,7 +295,6 @@ trait MethodSynthesis { // Final methods to make the rest easier to reason about. final def mods = tree.mods final def basisSym = tree.symbol - final def derivedFlags: Long = basisSym.flags & flagsMask | flagsExtra } trait DerivedFromClassDef extends DerivedFromMemberDef { @@ -458,7 +440,7 @@ trait MethodSynthesis { case class LazyValGetter(tree: ValDef) extends BaseGetter(tree) { class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol) extends ChangeOwnerTraverser(oldowner, newowner) { - + override def traverse(tree: Tree) { tree match { case _: DefTree => change(tree.symbol.moduleClass) @@ -558,7 +540,7 @@ trait MethodSynthesis { // No Symbols available. private def beanAccessorsFromNames(tree: ValDef) = { - val ValDef(mods, name, tpt, _) = tree + val ValDef(mods, _, _, _) = tree val hasBP = mods hasAnnotationNamed tpnme.BeanPropertyAnnot val hasBoolBP = mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot @@ -575,9 +557,6 @@ trait MethodSynthesis { } protected def enterBeans(tree: ValDef) { - if (forMSIL) - return - val ValDef(mods, name, _, _) = tree val beans = beanAccessorsFromNames(tree) if (beans.nonEmpty) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Modes.scala b/src/compiler/scala/tools/nsc/typechecker/Modes.scala deleted file mode 100644 index d650762ac1..0000000000 --- a/src/compiler/scala/tools/nsc/typechecker/Modes.scala +++ /dev/null @@ -1,140 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package typechecker - -/** Mode constants. - */ -trait Modes { - /** NOmode, EXPRmode and PATTERNmode are mutually exclusive. - */ - final val NOmode = 0x000 - final val EXPRmode = 0x001 - final val PATTERNmode = 0x002 - - /** TYPEmode needs a comment. <-- XXX. - */ - final val TYPEmode = 0x004 - - /** SCCmode is orthogonal to above. When set we are - * in the this or super constructor call of a constructor. - */ - final val SCCmode = 0x008 - - /** FUNmode is orthogonal to above. - * When set we are looking for a method or constructor. - */ - final val FUNmode = 0x010 - - /** POLYmode is orthogonal to above. - * When set expression types can be polymorphic. - */ - final val POLYmode = 0x020 - - /** QUALmode is orthogonal to above. When set - * expressions may be packages and Java statics modules. - */ - final val QUALmode = 0x040 - - /** TAPPmode is set for the function/type constructor - * part of a type application. When set we do not decompose PolyTypes. - */ - final val TAPPmode = 0x080 - - /** SUPERCONSTRmode is set for the super - * in a superclass constructor call super.<init>. - */ - final val SUPERCONSTRmode = 0x100 - - /** SNDTRYmode indicates that an application is typed for the 2nd time. - * In that case functions may no longer be coerced with implicit views. - */ - final val SNDTRYmode = 0x200 - - /** LHSmode is set for the left-hand side of an assignment. - */ - final val LHSmode = 0x400 - - /** STARmode is set when star patterns are allowed. - * (This was formerly called REGPATmode.) - */ - final val STARmode = 0x1000 - - /** ALTmode is set when we are under a pattern alternative. - */ - final val ALTmode = 0x2000 - - /** HKmode is set when we are typing a higher-kinded type. - * adapt should then check kind-arity based on the prototypical type's - * kind arity. Type arguments should not be inferred. - */ - final val HKmode = 0x4000 // @M: could also use POLYmode | TAPPmode - - /** BYVALmode is set when we are typing an expression - * that occurs in a by-value position. An expression e1 is in by-value - * position within expression e2 iff it will be reduced to a value at that - * position during the evaluation of e2. Examples are by-value function - * arguments or the conditional of an if-then-else clause. - * This mode has been added to support continuations. - */ - final val BYVALmode = 0x8000 - - /** TYPEPATmode is set when we are typing a type in a pattern. - */ - final val TYPEPATmode = 0x10000 - - /** RETmode is set when we are typing a return expression. - */ - final val RETmode = 0x20000 - - final private val StickyModes = EXPRmode | PATTERNmode | TYPEmode | ALTmode - - final def onlyStickyModes(mode: Int) = - mode & StickyModes - - final def forFunMode(mode: Int) = - mode & (StickyModes | SCCmode) | FUNmode | POLYmode | BYVALmode - - final def forTypeMode(mode: Int) = - if (inAnyMode(mode, PATTERNmode | TYPEPATmode)) TYPEmode | TYPEPATmode - else TYPEmode - - final def inAllModes(mode: Int, required: Int) = (mode & required) == required - final def inAnyMode(mode: Int, required: Int) = (mode & required) != 0 - final def inNoModes(mode: Int, prohibited: Int) = (mode & prohibited) == 0 - final def inHKMode(mode: Int) = (mode & HKmode) != 0 - final def inFunMode(mode: Int) = (mode & FUNmode) != 0 - final def inPolyMode(mode: Int) = (mode & POLYmode) != 0 - final def inPatternMode(mode: Int) = (mode & PATTERNmode) != 0 - final def inExprModeOr(mode: Int, others: Int) = (mode & (EXPRmode | others)) != 0 - final def inExprModeButNot(mode: Int, prohibited: Int) = - (mode & (EXPRmode | prohibited)) == EXPRmode - - /** Translates a mask of mode flags into something readable. - */ - private val modeNameMap = Map[Int, String]( - (1 << 0) -> "EXPRmode", - (1 << 1) -> "PATTERNmode", - (1 << 2) -> "TYPEmode", - (1 << 3) -> "SCCmode", - (1 << 4) -> "FUNmode", - (1 << 5) -> "POLYmode", - (1 << 6) -> "QUALmode", - (1 << 7) -> "TAPPmode", - (1 << 8) -> "SUPERCONSTRmode", - (1 << 9) -> "SNDTRYmode", - (1 << 10) -> "LHSmode", - (1 << 11) -> "<DOES NOT EXIST mode>", - (1 << 12) -> "STARmode", - (1 << 13) -> "ALTmode", - (1 << 14) -> "HKmode", - (1 << 15) -> "BYVALmode", - (1 << 16) -> "TYPEPATmode" - ) - def modeString(mode: Int): String = - if (mode == 0) "NOmode" - else (modeNameMap filterKeys (bit => inAllModes(mode, bit))).values mkString " " -} diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 341dbfbe1f..77b0749c20 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -8,9 +8,7 @@ package typechecker import scala.collection.mutable import scala.annotation.tailrec -import scala.ref.WeakReference import symtab.Flags._ -import scala.tools.nsc.io.AbstractFile /** This trait declares methods to create symbols and to enter them into scopes. * @@ -150,7 +148,7 @@ trait Namers extends MethodSynthesis { sym reset NoType setFlag newFlags setPos pos sym.moduleClass andAlso (updatePosFlags(_, pos, moduleClassFlags(flags))) - if (sym.owner.isPackageClass) { + if (sym.isTopLevel) { companionSymbolOf(sym, context) andAlso { companion => val assignNoType = companion.rawInfo match { case _: SymLoader => true @@ -173,10 +171,13 @@ trait Namers extends MethodSynthesis { else innerNamer } + // FIXME - this logic needs to be thoroughly explained + // and justified. I know it's wrong with repect to package + // objects, but I think it's also wrong in other ways. protected def conflict(newS: Symbol, oldS: Symbol) = ( ( !oldS.isSourceMethod || nme.isSetterName(newS.name) - || newS.owner.isPackageClass + || newS.isTopLevel ) && !( // @M: allow repeated use of `_` for higher-order type params (newS.owner.isTypeParameter || newS.owner.isAbstractType) @@ -187,7 +188,7 @@ trait Namers extends MethodSynthesis { ) private def allowsOverload(sym: Symbol) = ( - sym.isSourceMethod && sym.owner.isClass && !sym.owner.isPackageClass + sym.isSourceMethod && sym.owner.isClass && !sym.isTopLevel ) private def inCurrentScope(m: Symbol): Boolean = { @@ -200,6 +201,19 @@ trait Namers extends MethodSynthesis { /** Enter symbol into given scope and return symbol itself */ def enterInScope(sym: Symbol, scope: Scope): Symbol = { + // FIXME - this is broken in a number of ways. + // + // 1) If "sym" allows overloading, that is not itself sufficient to skip + // the check, because "prev.sym" also must allow overloading. + // + // 2) There is nothing which reconciles a package's scope with + // the package object's scope. This is the source of many bugs + // with e.g. defining a case class in a package object. When + // compiling against classes, the class symbol is created in the + // package and in the package object, and the conflict is undetected. + // There is also a non-deterministic outcome for situations like + // an object with the same name as a method in the package object. + // allow for overloaded methods if (!allowsOverload(sym)) { val prev = scope.lookupEntry(sym.name) @@ -300,11 +314,11 @@ trait Namers extends MethodSynthesis { case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => owner.newConstructor(pos, flags) case DefDef(_, _, _, _, _, _) => owner.newMethod(name.toTermName, pos, flags) case ClassDef(_, _, _, _) => owner.newClassSymbol(name.toTypeName, pos, flags) - case ModuleDef(_, _, _) => owner.newModule(name, pos, flags) + case ModuleDef(_, _, _) => owner.newModule(name.toTermName, pos, flags) case PackageDef(pid, _) => createPackageSymbol(pos, pid) case ValDef(_, _, _, _) => - if (isParameter) owner.newValueParameter(name, pos, flags) - else owner.newValue(name, pos, flags) + if (isParameter) owner.newValueParameter(name.toTermName, pos, flags) + else owner.newValue(name.toTermName, pos, flags) } } private def createFieldSymbol(tree: ValDef): TermSymbol = @@ -335,11 +349,10 @@ trait Namers extends MethodSynthesis { } private def enterClassSymbol(tree: ClassDef, clazz: ClassSymbol): Symbol = { - val file = contextFile if (clazz.sourceFile != null && clazz.sourceFile != contextFile) - debugwarn("!!! Source mismatch in " + clazz + ": " + clazz.sourceFile + " vs. " + contextFile) + devWarning(s"Source file mismatch in $clazz: ${clazz.sourceFile} vs. $contextFile") - clazz.sourceFile = contextFile + clazz.associatedFile = contextFile if (clazz.sourceFile != null) { assert(currentRun.canRedefine(clazz) || clazz.sourceFile == currentRun.symSource(clazz), clazz.sourceFile) currentRun.symSource(clazz) = clazz.sourceFile @@ -353,7 +366,7 @@ trait Namers extends MethodSynthesis { val existing = context.scope.lookup(tree.name) val isRedefinition = ( existing.isType - && existing.owner.isPackageClass + && existing.isTopLevel && context.scope == existing.owner.info.decls && currentRun.canRedefine(existing) ) @@ -366,8 +379,8 @@ trait Namers extends MethodSynthesis { else assignAndEnterSymbol(tree) setFlag inConstructorFlag } clazz match { - case csym: ClassSymbol if csym.owner.isPackageClass => enterClassSymbol(tree, csym) - case _ => clazz + case csym: ClassSymbol if csym.isTopLevel => enterClassSymbol(tree, csym) + case _ => clazz } } @@ -379,8 +392,8 @@ trait Namers extends MethodSynthesis { if (sym eq NoSymbol) return val ctx = if (context.owner.isPackageObjectClass) context.outer else context - val module = if (sym.isModule) sym else ctx.scope lookup tree.name.toTermName - val clazz = if (sym.isClass) sym else ctx.scope lookup tree.name.toTypeName + val module = if (sym.isModule) sym else ctx.scope lookupModule tree.name + val clazz = if (sym.isClass) sym else ctx.scope lookupClass tree.name val fails = ( module.isModule && clazz.isClass @@ -426,8 +439,8 @@ trait Namers extends MethodSynthesis { m.moduleClass setFlag moduleClassFlags(moduleFlags) setPrivateWithin(tree, m.moduleClass) } - if (m.owner.isPackageClass && !m.isPackage) { - m.moduleClass.sourceFile = contextFile + if (m.isTopLevel && !m.isPackage) { + m.moduleClass.associatedFile = contextFile currentRun.symSource(m) = m.moduleClass.sourceFile registerTopLevelSym(m) } @@ -614,7 +627,7 @@ trait Namers extends MethodSynthesis { // via "x$lzy" as can be seen in test #3927. val sym = ( if (owner.isClass) createFieldSymbol(tree) - else owner.newValue(tree.name append nme.LAZY_LOCAL, tree.pos, tree.mods.flags & ~IMPLICIT) + else owner.newValue(tree.name append nme.LAZY_LOCAL, tree.pos, (tree.mods.flags | ARTIFACT) & ~IMPLICIT) ) enterValSymbol(tree, sym setFlag MUTABLE setLazyAccessor lazyAccessor) } @@ -635,7 +648,7 @@ trait Namers extends MethodSynthesis { case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => assignAndEnterFinishedSymbol(tree) case DefDef(mods, name, tparams, _, _, _) => - val bridgeFlag = if (mods hasAnnotationNamed tpnme.bridgeAnnot) BRIDGE else 0 + val bridgeFlag = if (mods hasAnnotationNamed tpnme.bridgeAnnot) BRIDGE | ARTIFACT else 0 val sym = assignAndEnterSymbol(tree) setFlag bridgeFlag if (name == nme.copy && sym.isSynthetic) @@ -645,7 +658,7 @@ trait Namers extends MethodSynthesis { } def enterClassDef(tree: ClassDef) { - val ClassDef(mods, name, tparams, impl) = tree + val ClassDef(mods, _, _, impl) = tree val primaryConstructorArity = treeInfo.firstConstructorArgs(impl.body).size tree.symbol = enterClassSymbol(tree) tree.symbol setInfo completerOf(tree) @@ -709,41 +722,55 @@ trait Namers extends MethodSynthesis { // --- Lazy Type Assignment -------------------------------------------------- - def initializeLowerBounds(tp: Type): Type = { + def findCyclicalLowerBound(tp: Type): Symbol = { tp match { case TypeBounds(lo, _) => // check that lower bound is not an F-bound - for (TypeRef(_, sym, _) <- lo) - sym.initialize + // but carefully: class Foo[T <: Bar[_ >: T]] should be allowed + for (tp1 @ TypeRef(_, sym, _) <- lo) { + if (settings.breakCycles.value) { + if (!sym.maybeInitialize) { + log(s"Cycle inspecting $lo for possible f-bounds: ${sym.fullLocationString}") + return sym + } + } + else sym.initialize + } case _ => } - tp + NoSymbol } def monoTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => + // this early test is there to avoid infinite baseTypes when + // adding setters and getters --> bug798 + // It is a def in an attempt to provide some insulation against + // uninitialized symbols misleading us. It is not a certainty + // this accomplishes anything, but performance is a non-consideration + // on these flag checks so it can't hurt. + def needsCycleCheck = sym.isNonClassType && !sym.isParameter && !sym.isExistential logAndValidate(sym) { - val tp = initializeLowerBounds(typeSig(tree)) + val tp = typeSig(tree) + + findCyclicalLowerBound(tp) andAlso { sym => + if (needsCycleCheck) { + // neg/t1224: trait C[T] ; trait A { type T >: C[T] <: C[C[T]] } + // To avoid an infinite loop on the above, we cannot break all cycles + log(s"Reinitializing info of $sym to catch any genuine cycles") + sym reset sym.info + sym.initialize + } + } sym setInfo { if (sym.isJavaDefined) RestrictJavaArraysMap(tp) else tp } - // this early test is there to avoid infinite baseTypes when - // adding setters and getters --> bug798 - val needsCycleCheck = (sym.isAliasType || sym.isAbstractType) && !sym.isParameter - if (needsCycleCheck && !typer.checkNonCyclic(tree.pos, tp)) - sym setInfo ErrorType + if (needsCycleCheck) { + log(s"Needs cycle check: ${sym.debugLocationString}") + if (!typer.checkNonCyclic(tree.pos, tp)) + sym setInfo ErrorType + } } - // tree match { - // case ClassDef(_, _, _, impl) => - // val parentsOK = ( - // treeInfo.isInterface(sym, impl.body) - // || (sym eq ArrayClass) - // || (sym isSubClass AnyValClass) - // ) - // if (!parentsOK) - // ensureParent(sym, AnyRefClass) - // case _ => () - // } } def moduleClassTypeCompleter(tree: ModuleDef) = { @@ -802,7 +829,7 @@ trait Namers extends MethodSynthesis { false } - val tpe1 = dropRepeatedParamType(tpe.deconst) + val tpe1 = dropIllegalStarTypes(tpe.deconst) val tpe2 = tpe1.widen // This infers Foo.type instead of "object Foo" @@ -845,7 +872,7 @@ trait Namers extends MethodSynthesis { val sym = ( if (hasType || hasName) { - owner.typeOfThis = if (hasType) selfTypeCompleter(tpt) else owner.tpe + owner.typeOfThis = if (hasType) selfTypeCompleter(tpt) else owner.tpe_* val selfSym = owner.thisSym setPos self.pos if (hasName) selfSym setName name else selfSym } @@ -860,16 +887,11 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner def checkParent(tpt: Tree): Type = { - val tp = tpt.tpe - val inheritsSelf = tp.typeSymbol == owner - if (inheritsSelf) - InheritsItselfError(tpt) - - if (inheritsSelf || tp.isError) AnyRefClass.tpe - else tp + if (tpt.tpe.isError) AnyRefClass.tpe + else tpt.tpe } - val parents = typer.parentTypes(templ) map checkParent + val parents = typer.typedParentTypes(templ) map checkParent enterSelf(templ.self) @@ -895,11 +917,10 @@ trait Namers extends MethodSynthesis { val modClass = companionSymbolOf(clazz, context).moduleClass modClass.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => val cdef = cma.caseClass - def hasCopy(decls: Scope) = (decls lookup nme.copy) != NoSymbol + def hasCopy = (decls containsName nme.copy) || parents.exists(_ member nme.copy exists) + // SI-5956 needs (cdef.symbol == clazz): there can be multiple class symbols with the same name - if (cdef.symbol == clazz && !hasCopy(decls) && - !parents.exists(p => hasCopy(p.typeSymbol.info.decls)) && - !parents.flatMap(_.baseClasses).distinct.exists(bc => hasCopy(bc.info.decls))) + if (cdef.symbol == clazz && !hasCopy) addCopyMethod(cdef, templateNamer) } } @@ -944,9 +965,9 @@ trait Namers extends MethodSynthesis { // Assign the moduleClass info (templateSig returns a ClassInfoType) val clazz = moduleSym.moduleClass clazz setInfo pluginsTp - // clazz.tpe returns a `ModuleTypeRef(clazz)`, a typeRef that links to the module class `clazz` + // clazz.tpe_* returns a `ModuleTypeRef(clazz)`, a typeRef that links to the module class `clazz` // (clazz.info would the ClassInfoType, which is not what should be assigned to the module symbol) - clazz.tpe + clazz.tpe_* } /** @@ -1104,7 +1125,7 @@ trait Namers extends MethodSynthesis { } if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { - tpt defineType context.enclClass.owner.tpe + tpt defineType context.enclClass.owner.tpe_* tpt setPos meth.pos.focus } @@ -1140,7 +1161,7 @@ trait Namers extends MethodSynthesis { // because @macroImpl annotation only gets assigned during typechecking // otherwise macro defs wouldn't be able to robustly coexist with their clients // because a client could be typechecked before a macro def that it uses - if (meth.isTermMacro) { + if (meth.isMacro) { typer.computeMacroDefType(ddef, resTpFromOverride) } @@ -1277,9 +1298,9 @@ trait Namers extends MethodSynthesis { // 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. - val att = meth.attachments.get[DefaultsOfLocalMethodAttachment] match { + meth.attachments.get[DefaultsOfLocalMethodAttachment] match { case Some(att) => att.defaultGetters += default - case None => meth.updateAttachment(new DefaultsOfLocalMethodAttachment(default)) + case None => meth.updateAttachment(new DefaultsOfLocalMethodAttachment(default)) } } } else if (baseHasDefault) { @@ -1360,8 +1381,7 @@ trait Namers extends MethodSynthesis { transformed(imp) = newImport // copy symbol and type attributes back into old expression // so that the structure builder will find it. - expr.symbol = expr1.symbol - expr.tpe = expr1.tpe + expr setSymbol expr1.symbol setType expr1.tpe ImportType(expr1) } } @@ -1414,7 +1434,7 @@ trait Namers extends MethodSynthesis { AnnotationInfo lazily { val context1 = typer.context.make(ann) context1.setReportErrors() - beforeTyper(newTyper(context1) typedAnnotation ann) + enteringTyper(newTyper(context1) typedAnnotation ann) } } if (ainfos.nonEmpty) { @@ -1466,12 +1486,6 @@ trait Namers extends MethodSynthesis { tpe } - def ensureParent(clazz: Symbol, parent: Symbol) = { - val info0 = clazz.info - val info1 = includeParent(info0, parent) - if (info0 ne info1) clazz setInfo info1 - } - class LogTransitions[S](onEnter: S => String, onExit: S => String) { val enabled = settings.debug.value @inline final def apply[T](entity: S)(body: => T): T = { @@ -1532,7 +1546,7 @@ trait Namers extends MethodSynthesis { fail(ImplicitConstr) if (!(sym.isTerm || (sym.isClass && !sym.isTrait))) fail(ImplicitNotTermOrClass) - if (sym.owner.isPackageClass) + if (sym.isTopLevel) fail(ImplicitAtToplevel) } if (sym.isClass) { diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 2340c78f8c..fa141e95e1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -8,7 +8,6 @@ package typechecker import symtab.Flags._ import scala.collection.mutable -import scala.ref.WeakReference import scala.reflect.ClassTag /** @@ -42,13 +41,11 @@ trait NamesDefaults { self: Analyzer => blockTyper: Typer ) { } - val noApplyInfo = NamedApplyInfo(None, Nil, Nil, null) - - def nameOf(arg: Tree) = arg match { - case AssignOrNamedArg(Ident(name), rhs) => Some(name) - case _ => None + private def nameOfNamedArg(arg: Tree) = Some(arg) collect { case AssignOrNamedArg(Ident(name), _) => name } + def isNamedArg(arg: Tree) = arg match { + case AssignOrNamedArg(Ident(_), _) => true + case _ => false } - def isNamed(arg: Tree) = nameOf(arg).isDefined /** @param pos maps indices from old to new */ def reorderArgs[T: ClassTag](args: List[T], pos: Int => Int): List[T] = { @@ -58,13 +55,13 @@ trait NamesDefaults { self: Analyzer => } /** @param pos maps indices from new to old (!) */ - def reorderArgsInv[T: ClassTag](args: List[T], pos: Int => Int): List[T] = { + private def reorderArgsInv[T: ClassTag](args: List[T], pos: Int => Int): List[T] = { val argsArray = args.toArray (argsArray.indices map (i => argsArray(pos(i)))).toList } /** returns `true` if every element is equal to its index */ - def isIdentity(a: Array[Int]) = (0 until a.length).forall(i => a(i) == i) + def allArgsArePositional(a: Array[Int]) = (0 until a.length).forall(i => a(i) == i) /** * Transform a function application into a Block, and assigns typer.context @@ -107,7 +104,7 @@ trait NamesDefaults { self: Analyzer => * @return the transformed application (a Block) together with the NamedApplyInfo. * if isNamedApplyBlock(tree), returns the existing context.namedApplyBlockInfo */ - def transformNamedApplication(typer: Typer, mode: Int, pt: Type) + def transformNamedApplication(typer: Typer, mode: Mode, pt: Type) (tree: Tree, argPos: Int => Int): Tree = { import typer._ import typer.infer._ @@ -164,14 +161,14 @@ trait NamesDefaults { self: Analyzer => // never used for constructor calls, they always have a stable qualifier def blockWithQualifier(qual: Tree, selected: Name) = { - val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos) setInfo qual.tpe + val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos, newFlags = ARTIFACT) setInfo qual.tpe blockTyper.context.scope enter sym val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType) // it stays in Vegas: SI-5720, SI-5727 qual changeOwner (blockTyper.context.owner -> sym) val newQual = atPos(qual.pos.focus)(blockTyper.typedQualifier(Ident(sym.name))) - var baseFunTransformed = atPos(baseFun.pos.makeTransparent) { + val baseFunTransformed = atPos(baseFun.pos.makeTransparent) { // setSymbol below is important because the 'selected' function might be overloaded. by // assigning the correct method symbol, typedSelect will just assign the type. the reason // to still call 'typed' is to correctly infer singleton types, SI-5259. @@ -286,7 +283,7 @@ trait NamesDefaults { self: Analyzer => } else arg.tpe ).widen // have to widen or types inferred from literal defaults will be singletons - val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo ( + val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos, newFlags = ARTIFACT) setInfo ( if (byName) functionType(Nil, argTpe) else argTpe ) Some((context.scope.enter(s), byName, repeated)) @@ -325,7 +322,7 @@ trait NamesDefaults { self: Analyzer => assert(isNamedApplyBlock(transformedFun), transformedFun) val NamedApplyInfo(qual, targs, vargss, blockTyper) = context.namedApplyBlockInfo.get._2 - val existingBlock @ Block(stats, funOnly) = transformedFun + val Block(stats, funOnly) = transformedFun // type the application without names; put the arguments in definition-site order val typedApp = doTypedApply(tree, funOnly, reorderArgs(namelessArgs, argPos), mode, pt) @@ -370,7 +367,7 @@ trait NamesDefaults { self: Analyzer => } } - def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOf _): (List[Symbol], Boolean) = { + def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOfNamedArg _): (List[Symbol], Boolean) = { val namedArgs = args.dropWhile(arg => { val n = argName(arg) n.isEmpty || params.forall(p => p.name != n.get) @@ -405,7 +402,7 @@ trait NamesDefaults { self: Analyzer => // TODO #3649 can create spurious errors when companion object is gone (because it becomes unlinked from scope) if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649 else { - var default1 = qual match { + var default1: Tree = qual match { case Some(q) => gen.mkAttributedSelect(q.duplicate, defGetter) case None => gen.mkAttributedRef(defGetter) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index f7579ad249..bbbc5cbb18 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -37,13 +37,11 @@ import scala.reflect.internal.Types * - recover exhaustivity/unreachability of user-defined extractors by partitioning the types they match on using an HList or similar type-level structure */ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL { // self: Analyzer => - import Statistics._ import PatternMatchingStats._ val global: Global // need to repeat here because otherwise last mixin defines global as // SymbolTable. If we had DOT this would not be an issue import global._ // the global environment - import definitions._ // standard classes and methods val phaseName: String = "patmat" @@ -69,9 +67,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - def newTransformer(unit: CompilationUnit): Transformer = - if (opt.virtPatmat) new MatchTransformer(unit) - else noopTransformer + def newTransformer(unit: CompilationUnit): Transformer = new MatchTransformer(unit) // duplicated from CPSUtils (avoid dependency from compiler -> cps plugin...) private lazy val MarkerCPSAdaptPlus = rootMirror.getClassIfDefined("scala.util.continuations.cpsPlus") @@ -189,7 +185,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore => import typer.{typed, context, silent, reallyExists} - // import typer.infer.containsUnchecked // Why is it so difficult to say "here's a name and a context, give me any // matching symbol in scope" ? I am sure this code is wrong, but attempts to @@ -279,7 +274,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // 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) - if(phase.id >= currentRun.uncurryPhase.id) debugwarn("running translateMatch at "+ phase +" on "+ selector +" match "+ cases) + if (phase.id >= currentRun.uncurryPhase.id) + devWarning(s"running translateMatch past uncurry (at $phase) on $selector match $cases") + patmatDebug("translating "+ cases.mkString("{", "\n", "}")) val start = if (Statistics.canEnable) Statistics.startTimer(patmatNanos) else null @@ -300,7 +297,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val pt = repeatedToSeq(ptUnCPS) // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner)) - val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental @@ -488,7 +484,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL **/ // must treat Typed and Bind together -- we need to know the patBinder of the Bind pattern to get at the actual type case MaybeBoundTyped(subPatBinder, pt) => - val next = glb(List(patBinder.info.widen, pt)).normalize + val next = glb(List(patBinder.info.dealiasWiden, pt)).normalize // a typed pattern never has any subtrees noFurtherSubPats(TypeTestTreeMaker(subPatBinder, patBinder, pt, next)(pos)) @@ -566,55 +562,56 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def fromCaseClass(fun: Tree, args: List[Tree]): Option[ExtractorCall] = Some(new ExtractorCallProd(fun, args)) // THE PRINCIPLED SLOW PATH -- NOT USED + // !!! Use it, test it, or delete it, else it is unlikely to be an asset. // generate a call to the (synthetically generated) extractor of a case class // NOTE: it's an apply, not a select, since in general an extractor call may have multiple argument lists (including an implicit one) // that we need to preserve, so we supply the scrutinee as Ident(nme.SELECTOR_DUMMY), // and replace that dummy by a reference to the actual binder in translateExtractorPattern - def fromCaseClassUnapply(fun: Tree, args: List[Tree]): Option[ExtractorCall] = { - // TODO: can we rework the typer so we don't have to do all this twice? - // undo rewrite performed in (5) of adapt - val orig = fun match {case tpt: TypeTree => tpt.original case _ => fun} - val origSym = orig.symbol - val extractor = unapplyMember(origSym.filter(sym => reallyExists(unapplyMember(sym.tpe))).tpe) - - if((fun.tpe eq null) || fun.tpe.isError || (extractor eq NoSymbol)) { - None - } else { - // this is a tricky balance: pos/t602.scala, pos/sudoku.scala, run/virtpatmat_alts.scala must all be happy - // bypass typing at own risk: val extractorCall = Select(orig, extractor) setType caseClassApplyToUnapplyTp(fun.tpe) - // can't always infer type arguments (pos/t602): - /* case class Span[K <: Ordered[K]](low: Option[K]) { - override def equals(x: Any): Boolean = x match { - case Span((low0 @ _)) if low0 equals low => true - } - }*/ - // so... leave undetermined type params floating around if we have to - // (if we don't infer types, uninstantiated type params show up later: pos/sudoku.scala) - // (see also run/virtpatmat_alts.scala) - val savedUndets = context.undetparams - val extractorCall = try { - context.undetparams = Nil - silent(_.typed(Apply(Select(orig, extractor), List(Ident(nme.SELECTOR_DUMMY) setType fun.tpe.finalResultType)), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { - case SilentResultValue(extractorCall) => extractorCall // if !extractorCall.containsError() - case _ => - // this fails to resolve overloading properly... - // Apply(typedOperator(Select(orig, extractor)), List(Ident(nme.SELECTOR_DUMMY))) // no need to set the type of the dummy arg, it will be replaced anyway - - // patmatDebug("funtpe after = "+ fun.tpe.finalResultType) - // patmatDebug("orig: "+(orig, orig.tpe)) - val tgt = typed(orig, EXPRmode | QUALmode | POLYmode, HasMember(extractor.name)) // can't specify fun.tpe.finalResultType as the type for the extractor's arg, - // as it may have been inferred incorrectly (see t602, where it's com.mosol.sl.Span[Any], instead of com.mosol.sl.Span[?K]) - // patmatDebug("tgt = "+ (tgt, tgt.tpe)) - val oper = typed(Select(tgt, extractor.name), EXPRmode | FUNmode | POLYmode | TAPPmode, WildcardType) - // patmatDebug("oper: "+ (oper, oper.tpe)) - Apply(oper, List(Ident(nme.SELECTOR_DUMMY))) // no need to set the type of the dummy arg, it will be replaced anyway - } - } finally context.undetparams = savedUndets - - Some(this(extractorCall, args)) // TODO: simplify spliceApply? + // def fromCaseClassUnapply(fun: Tree, args: List[Tree]): Option[ExtractorCall] = { + // // TODO: can we rework the typer so we don't have to do all this twice? + // // undo rewrite performed in (5) of adapt + // val orig = fun match {case tpt: TypeTree => tpt.original case _ => fun} + // val origSym = orig.symbol + // val extractor = unapplyMember(origSym.filter(sym => reallyExists(unapplyMember(sym.tpe))).tpe) + + // if((fun.tpe eq null) || fun.tpe.isError || (extractor eq NoSymbol)) { + // None + // } else { + // // this is a tricky balance: pos/t602.scala, pos/sudoku.scala, run/virtpatmat_alts.scala must all be happy + // // bypass typing at own risk: val extractorCall = Select(orig, extractor) setType caseClassApplyToUnapplyTp(fun.tpe) + // // can't always infer type arguments (pos/t602): + // /* case class Span[K <: Ordered[K]](low: Option[K]) { + // override def equals(x: Any): Boolean = x match { + // case Span((low0 @ _)) if low0 equals low => true + // } + // }*/ + // // so... leave undetermined type params floating around if we have to + // // (if we don't infer types, uninstantiated type params show up later: pos/sudoku.scala) + // // (see also run/virtpatmat_alts.scala) + // val savedUndets = context.undetparams + // val extractorCall = try { + // context.undetparams = Nil + // silent(_.typed(Apply(Select(orig, extractor), List(Ident(nme.SELECTOR_DUMMY) setType fun.tpe.finalResultType)), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { + // case SilentResultValue(extractorCall) => extractorCall // if !extractorCall.containsError() + // case _ => + // // this fails to resolve overloading properly... + // // Apply(typedOperator(Select(orig, extractor)), List(Ident(nme.SELECTOR_DUMMY))) // no need to set the type of the dummy arg, it will be replaced anyway + + // // patmatDebug("funtpe after = "+ fun.tpe.finalResultType) + // // patmatDebug("orig: "+(orig, orig.tpe)) + // val tgt = typed(orig, EXPRmode | QUALmode | POLYmode, HasMember(extractor.name)) // can't specify fun.tpe.finalResultType as the type for the extractor's arg, + // // as it may have been inferred incorrectly (see t602, where it's com.mosol.sl.Span[Any], instead of com.mosol.sl.Span[?K]) + // // patmatDebug("tgt = "+ (tgt, tgt.tpe)) + // val oper = typed(Select(tgt, extractor.name), EXPRmode | FUNmode | POLYmode | TAPPmode, WildcardType) + // // patmatDebug("oper: "+ (oper, oper.tpe)) + // Apply(oper, List(Ident(nme.SELECTOR_DUMMY))) // no need to set the type of the dummy arg, it will be replaced anyway + // } + // } finally context.undetparams = savedUndets + + // Some(this(extractorCall, args)) // TODO: simplify spliceApply? + // } + // } } - } - } abstract class ExtractorCall(val args: List[Tree]) { val nbSubPats = args.length @@ -1197,7 +1194,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL type Result = Tree def and(a: Result, b: Result): Result = a AND b - def tru = TRUE_typed + def tru = TRUE def typeTest(testedBinder: Symbol, expectedTp: Type) = codegen._isInstanceOf(testedBinder, expectedTp) def nonNullTest(testedBinder: Symbol) = REF(testedBinder) OBJ_NE NULL def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) @@ -1207,7 +1204,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val expectedOuter = expectedTp.prefix match { case ThisType(clazz) => THIS(clazz) case pre if pre != NoType => REF(pre.prefix, pre.termSymbol) - case _ => TRUE_typed // fallback for SI-6183 + case _ => TRUE // fallback for SI-6183 } // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` @@ -1360,10 +1357,10 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // one alternative may still generate multiple trees (e.g., an extractor call + equality test) // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers val combinedAlts = altss map (altTreeMakers => - ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE_typed)))(casegen)) + ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE)))(casegen)) ) - val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE_typed)) + val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE)) codegenAlt.ifThenElseZero(findAltMatcher, substitution(next)) } } @@ -1506,10 +1503,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // local / context-free def _asInstanceOf(b: Symbol, tp: Type): Tree - def _asInstanceOf(t: Tree, tp: Type): Tree def _equals(checker: Tree, binder: Symbol): Tree def _isInstanceOf(b: Symbol, tp: Type): Tree - def and(a: Tree, b: Tree): Tree def drop(tgt: Tree)(n: Int): Tree def index(tgt: Tree)(i: Int): Tree def mkZero(tp: Type): Tree @@ -1523,7 +1518,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def flatMap(prev: Tree, b: Symbol, next: Tree): Tree def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree def flatMapGuard(cond: Tree, next: Tree): Tree - def ifThenElseZero(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero + def ifThenElseZero(c: Tree, thenp: Tree): Tree = IF (c) THEN thenp ELSE zero protected def zero: Tree } @@ -1551,18 +1546,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL abstract class CommonCodegen extends AbsCodegen { import CODE._ def fun(arg: Symbol, body: Tree): Tree = Function(List(ValDef(arg)), body) - def genTypeApply(tfun: Tree, args: Type*): Tree = if(args contains NoType) tfun else TypeApply(tfun, args.toList map TypeTree) def tupleSel(binder: Symbol)(i: Int): Tree = (REF(binder) DOT nme.productAccessorName(i)) // make tree that accesses the i'th component of the tuple referenced by binder def index(tgt: Tree)(i: Int): Tree = tgt APPLY (LIT(i)) def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n)) 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 // 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): Tree = if (t.tpe != NoType && t.isTyped && typesConform(t.tpe, tp)) t else gen.mkCastPreservingAnnotations(t, tp) def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else gen.mkCastPreservingAnnotations(REF(b), tp) def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), tp.withoutAnnotations, true, false) - // if (typesConform(b.info, tpX)) { patmatDebug("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } // duplicated out of frustration with cast generation def mkZero(tp: Type): Tree = { @@ -1611,7 +1602,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // __match.zero protected def zero: Tree = _match(vpmName.zero) // __match.guard(`c`, `then`) - def guard(c: Tree, then: Tree): Tree = _match(vpmName.guard) APPLY (c, then) + def guard(c: Tree, thenp: Tree): Tree = _match(vpmName.guard) APPLY (c, thenp) //// methods in the monad instance -- used directly in translation // `prev`.flatMap(`b` => `next`) @@ -1905,7 +1896,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def toString(x: AnyRef) = if (x eq null) "" else x.toString if (cols.isEmpty || cols.tails.isEmpty) cols map toString else { - val (colStrs, colLens) = cols map {c => val s = toString(c); (s, s.length)} unzip + val colLens = cols map (c => toString(c).length) val maxLen = max(colLens) val avgLen = colLens.sum/colLens.length val goalLen = maxLen min avgLen*2 @@ -2174,7 +2165,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // throws an AnalysisBudget.Exception when the prop results in a CNF that's too big // TODO: be smarter/more efficient about this (http://lara.epfl.ch/w/sav09:tseitin_s_encoding) def eqFreePropToSolvable(p: Prop): Formula = { - def negationNormalFormNot(p: Prop, budget: Int = AnalysisBudget.max): Prop = + def negationNormalFormNot(p: Prop, budget: Int): Prop = if (budget <= 0) throw AnalysisBudget.exceeded else p match { case And(a, b) => Or(negationNormalFormNot(a, budget - 1), negationNormalFormNot(b, budget - 1)) @@ -2388,9 +2379,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL private[this] val id: Int = Var.nextId // private[this] var canModify: Option[Array[StackTraceElement]] = None - private[this] def ensureCanModify = {} //if (canModify.nonEmpty) patmatDebug("BUG!"+ this +" modified after having been observed: "+ canModify.get.mkString("\n")) + private[this] def ensureCanModify() = {} //if (canModify.nonEmpty) patmatDebug("BUG!"+ this +" modified after having been observed: "+ canModify.get.mkString("\n")) - private[this] def observed = {} //canModify = Some(Thread.currentThread.getStackTrace) + private[this] def observed() = {} //canModify = Some(Thread.currentThread.getStackTrace) // don't access until all potential equalities have been registered using registerEquality private[this] val symForEqualsTo = new scala.collection.mutable.HashMap[Const, Sym] @@ -2543,7 +2534,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL private lazy val equalitySyms = {observed; symForEqualsTo.values.toList} // don't call until all equalities have been registered and registerNull has been called (if needed) - def describe = toString + ": " + staticTp + domain.map(_.mkString(" ::= ", " | ", "// "+ symForEqualsTo.keys)).getOrElse(symForEqualsTo.keys.mkString(" ::= ", " | ", " | ...")) + " // = " + path + def describe = { + def domain_s = domain match { + case Some(d) => d mkString (" ::= ", " | ", "// "+ symForEqualsTo.keys) + case _ => symForEqualsTo.keys mkString (" ::= ", " | ", " | ...") + } + s"$this: ${staticTp}${domain_s} // = $path" + } override def toString = "V"+ id } @@ -2629,7 +2626,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // corresponds to a type test that does not imply any value-equality (well, except for outer checks, which we don't model yet) sealed class TypeConst(val tp: Type) extends Const { assert(!(tp =:= NullTp)) - private[this] val id: Int = Const.nextTypeId + /*private[this] val id: Int = */ Const.nextTypeId val wideTp = widenToClass(tp) def isValue = false @@ -2667,7 +2664,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } val toString = - if (p.hasSymbol && p.symbol.isStable) p.symbol.name.toString // tp.toString + if (p.hasSymbolField && p.symbol.isStable) p.symbol.name.toString // tp.toString else p.toString //+"#"+ id Const.unique(narrowTp, new ValueConst(narrowTp, checkableType(wideTp), toString)) // must make wide type checkable so that it is comparable to types from TypeConst @@ -2677,7 +2674,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL sealed class ValueConst(val tp: Type, val wideTp: Type, override val toString: String) extends Const { // patmatDebug("VC"+(tp, wideTp, toString)) assert(!(tp =:= NullTp)) // TODO: assert(!tp.isStable) - private[this] val id: Int = Const.nextValueId + /*private[this] val id: Int = */Const.nextValueId def isValue = true } @@ -2904,7 +2901,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // when does the match fail? val matchFails = Not(\/(symbolicCases)) - val vars = gatherVariables(matchFails) // debug output: patmatDebug("analysing:") @@ -3002,8 +2998,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL v +"(="+ v.path +": "+ v.staticTpCheckable +") "+ assignment }.mkString("\n") - def modelString(model: Model) = varAssignmentString(modelToVarAssignment(model)) - // return constructor call when the model is a true counter example // (the variables don't take into account type information derived from other variables, // so, naively, you might try to construct a counter example like _ :: Nil(_ :: _, _ :: _), @@ -3643,7 +3637,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree], unchecked: Boolean): Option[Tree] = { import CODE._ val regularSwitchMaker = new RegularSwitchMaker(scrutSym, matchFailGenOverride, unchecked) // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result - if (regularSwitchMaker.switchableTpe(scrutSym.tpe.dealias)) { // TODO: switch to dealiasWiden in 2.11 + if (regularSwitchMaker.switchableTpe(scrutSym.tpe.dealiasWiden)) { val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) if (caseDefsWithDefault isEmpty) None // not worth emitting a switch. else { @@ -3662,7 +3656,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // for the catch-cases in a try/catch private object typeSwitchMaker extends SwitchMaker { val unchecked = false - def switchableTpe(tp: Type) = true val alternativesSupported = false // TODO: needs either back-end support of flattening of alternatives during typers val canJump = false @@ -3708,11 +3701,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL trait OptimizedCodegen extends CodegenCore with TypedSubstitution with OptimizedMatchMonadInterface { override def codegen: AbsCodegen = optimizedCodegen - // trait AbsOptimizedCodegen extends AbsCodegen { - // def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree - // } - // def optimizedCodegen: AbsOptimizedCodegen - // when we know we're targetting Option, do some inlining the optimizer won't do // for example, `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard // this is a special instance of the advanced inlining optimization that takes a method call on @@ -3815,7 +3803,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = ifThenElseZero(cond, BLOCK( - condSym === TRUE_typed, + condSym === TRUE, nextBinder === res, next )) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index b9fdd7280e..60a73036f8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -71,7 +71,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if (sym.hasAccessBoundary) "" + sym.privateWithin.name else "" ) - def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.normalize, tp2.normalize) match { + def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match { case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => rtp1 <:< rtp2 case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => @@ -133,7 +133,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } } - if (settings.lint.value) { + + // Check for doomed attempt to overload applyDynamic + if (clazz isSubClass DynamicClass) { + for ((_, m1 :: m2 :: _) <- (clazz.info member nme.applyDynamic).alternatives groupBy (_.typeParams.length)) { + unit.error(m1.pos, "implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)") + } + } + + // This has become noisy with implicit classes. + if (settings.lint.value && settings.developer.value) { clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym => val alts = clazz.info.decl(sym.name).alternatives if (alts.size > 1) @@ -240,7 +249,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case class MixinOverrideError(member: Symbol, msg: String) - var mixinOverrideErrors = new ListBuffer[MixinOverrideError]() + val mixinOverrideErrors = new ListBuffer[MixinOverrideError]() def printMixinOverrideErrors() { mixinOverrideErrors.toList match { @@ -417,9 +426,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans 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") + overrideError("cannot be used here - term macros cannot override abstract methods") } else if (other.isTermMacro && !member.isTermMacro) { // (1.10) - overrideError("cannot override a macro") + overrideError("cannot be used here - only term macros can override term macros") } else { checkOverrideTypes() checkOverrideDeprecated() @@ -465,12 +474,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // check a type alias's RHS corresponds to its declaration // this overlaps somewhat with validateVariance if(member.isAliasType) { - // println("checkKindBounds" + ((List(member), List(memberTp.normalize), self, member.owner))) - val kindErrors = typer.infer.checkKindBounds(List(member), List(memberTp.normalize), self, member.owner) + // println("checkKindBounds" + ((List(member), List(memberTp.dealiasWiden), self, member.owner))) + val kindErrors = typer.infer.checkKindBounds(List(member), List(memberTp.dealiasWiden), self, member.owner) if(!kindErrors.isEmpty) unit.error(member.pos, - "The kind of the right-hand side "+memberTp.normalize+" of "+member.keyString+" "+ + "The kind of the right-hand side "+memberTp.dealiasWiden+" of "+member.keyString+" "+ member.varianceString + member.nameString+ " does not conform to its expected kind."+ kindErrors.toList.mkString("\n", ", ", "")) } else if (member.isAbstractType) { @@ -489,7 +498,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if (member.isStable && !otherTp.isVolatile) { if (memberTp.isVolatile) overrideError("has a volatile type; cannot override a member with non-volatile type") - else memberTp.normalize.resultType match { + else memberTp.dealiasWiden.resultType match { case rt: RefinedType if !(rt =:= otherTp) && !(checkedCombinations contains rt.parents) => // might mask some inconsistencies -- check overrides checkedCombinations += rt.parents @@ -548,13 +557,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def uncurryAndErase(tp: Type) = erasure.erasure(sym)(uncurry.transformInfo(sym, tp)) val tp1 = uncurryAndErase(clazz.thisType.memberType(sym)) val tp2 = uncurryAndErase(clazz.thisType.memberType(other)) - afterErasure(tp1 matches tp2) + exitingErasure(tp1 matches tp2) }) def ignoreDeferred(member: Symbol) = ( (member.isAbstractType && !member.isFBounded) || ( member.isJavaDefined && - // the test requires afterErasure so shouldn't be + // the test requires exitingErasure so shouldn't be // done if the compiler has no erasure phase available (currentRun.erasurePhase == NoPhase || javaErasedOverridingSym(member) != NoSymbol) ) @@ -837,161 +846,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // Variance Checking -------------------------------------------------------- - private val ContraVariance = -1 - private val NoVariance = 0 - private val CoVariance = 1 - private val AnyVariance = 2 - - private val escapedPrivateLocals = new mutable.HashSet[Symbol] - - val varianceValidator = new Traverser { - - /** Validate variance of info of symbol `base` */ - private def validateVariance(base: Symbol) { - // A flag for when we're in a refinement, meaning method parameter types - // need to be checked. - var inRefinement = false - - def varianceString(variance: Int): String = - if (variance == 1) "covariant" - else if (variance == -1) "contravariant" - else "invariant"; - - /** The variance of a symbol occurrence of `tvar` - * seen at the level of the definition of `base`. - * The search proceeds from `base` to the owner of `tvar`. - * Initially the state is covariant, but it might change along the search. - */ - def relativeVariance(tvar: Symbol): Int = { - val clazz = tvar.owner - var sym = base - var state = CoVariance - while (sym != clazz && state != AnyVariance) { - //Console.println("flip: " + sym + " " + sym.isParameter());//DEBUG - // Flip occurrences of type parameters and parameters, unless - // - it's a constructor, or case class factory or extractor - // - it's a type parameter of tvar's owner. - if (sym.isParameter && !sym.owner.isConstructor && !sym.owner.isCaseApplyOrUnapply && - !(tvar.isTypeParameterOrSkolem && sym.isTypeParameterOrSkolem && - tvar.owner == sym.owner)) state = -state; - else if (!sym.owner.isClass || - sym.isTerm && ((sym.isPrivateLocal || sym.isProtectedLocal || sym.isSuperAccessor /* super accessors are implicitly local #4345*/) && !(escapedPrivateLocals contains sym))) { - // return AnyVariance if `sym` is local to a term - // or is private[this] or protected[this] - state = AnyVariance - } else if (sym.isAliasType) { - // return AnyVariance if `sym` is an alias type - // that does not override anything. This is OK, because we always - // expand aliases for variance checking. - // However, if `sym` does override a type in a base class - // we have to assume NoVariance, as there might then be - // references to the type parameter that are not variance checked. - state = if (sym.isOverridingSymbol) NoVariance else AnyVariance - } - sym = sym.owner - } - state - } - - /** Validate that the type `tp` is variance-correct, assuming - * the type occurs itself at variance position given by `variance` - */ - def validateVariance(tp: Type, variance: Int): Unit = tp match { - case ErrorType => - case WildcardType => - case BoundedWildcardType(bounds) => - validateVariance(bounds, variance) - case NoType => - case NoPrefix => - case ThisType(_) => - case ConstantType(_) => - // case DeBruijnIndex(_, _) => - case SingleType(pre, sym) => - validateVariance(pre, variance) - case TypeRef(pre, sym, args) => -// println("validate "+sym+" at "+relativeVariance(sym)) - if (sym.isAliasType/* && relativeVariance(sym) == AnyVariance*/) - validateVariance(tp.normalize, variance) - else if (sym.variance != NoVariance) { - val v = relativeVariance(sym) - if (v != AnyVariance && sym.variance != v * variance) { - //Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG - def tpString(tp: Type) = tp match { - case ClassInfoType(parents, _, clazz) => "supertype "+intersectionType(parents, clazz.owner) - case _ => "type "+tp - } - unit.error(base.pos, - varianceString(sym.variance) + " " + sym + - " occurs in " + varianceString(v * variance) + - " position in " + tpString(base.info) + " of " + base); - } - } - validateVariance(pre, variance) - // @M for higher-kinded typeref, args.isEmpty - // However, these args respect variances by construction anyway - // -- the interesting case is in type application, see checkKindBounds in Infer - if (args.nonEmpty) - validateVarianceArgs(args, variance, sym.typeParams) - case ClassInfoType(parents, decls, symbol) => - validateVariances(parents, variance) - case RefinedType(parents, decls) => - validateVariances(parents, variance) - val saved = inRefinement - inRefinement = true - for (sym <- decls) - validateVariance(sym.info, if (sym.isAliasType) NoVariance else variance) - inRefinement = saved - case TypeBounds(lo, hi) => - validateVariance(lo, -variance) - validateVariance(hi, variance) - case mt @ MethodType(formals, result) => - if (inRefinement) - validateVariances(mt.paramTypes, -variance) - validateVariance(result, variance) - case NullaryMethodType(result) => - validateVariance(result, variance) - case PolyType(tparams, result) => - // type parameters will be validated separately, because they are defined explicitly. - validateVariance(result, variance) - case ExistentialType(tparams, result) => - validateVariances(tparams map (_.info), variance) - validateVariance(result, variance) - case AnnotatedType(annots, tp, selfsym) => - if (!annots.exists(_ matches uncheckedVarianceClass)) - validateVariance(tp, variance) - } - - def validateVariances(tps: List[Type], variance: Int) { - tps foreach (tp => validateVariance(tp, variance)) - } - - def validateVarianceArgs(tps: List[Type], variance: Int, tparams: List[Symbol]) { - foreach2(tps, tparams)((tp, tparam) => validateVariance(tp, variance * tparam.variance)) - } - - validateVariance(base.info, CoVariance) + object varianceValidator extends VarianceValidator { + private def tpString(tp: Type) = tp match { + case ClassInfoType(parents, _, clazz) => "supertype "+intersectionType(parents, clazz.owner) + case _ => "type "+tp } - - override def traverse(tree: Tree) { - tree match { - case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => - validateVariance(tree.symbol) - super.traverse(tree) - // ModuleDefs need not be considered because they have been eliminated already - case ValDef(_, _, _, _) => - if (!tree.symbol.hasLocalFlag) - validateVariance(tree.symbol) - case DefDef(_, _, tparams, vparamss, _, _) => - // No variance check for object-private/protected methods/values. - if (!tree.symbol.hasLocalFlag) { - validateVariance(tree.symbol) - traverseTrees(tparams) - traverseTreess(vparamss) - } - case Template(_, _, _) => - super.traverse(tree) - case _ => - } + override def issueVarianceError(base: Symbol, sym: Symbol, required: Variance) { + currentRun.currentUnit.error(base.pos, + s"${sym.variance} $sym occurs in $required position in ${tpString(base.info)} of $base") } } @@ -1111,15 +973,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass - // test is behind a platform guard - def isJavaNumber(s: Symbol) = !forMSIL && (s isSubClass JavaNumberClass) + def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass // includes java.lang.Number if appropriate [SI-5779] def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s) def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s) // used to short-circuit unrelatedTypes check if both sides are special def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) - // unused - def possibleNumericCount = onSyms(_ filter (x => isNumeric(x) || isMaybeValue(x)) size) val nullCount = onSyms(_ filter (_ == NullClass) size) def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { @@ -1157,7 +1016,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans nonSensiblyNeq() } else if (isNumeric(receiver)) { - if (!isNumeric(actual) && !forMSIL) + if (!isNumeric(actual)) if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc" nonSensiblyNeq() } @@ -1230,7 +1089,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans /* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */ def toConstructor(pos: Position, tpe: Type): Tree = { - var rtpe = tpe.finalResultType + val rtpe = tpe.finalResultType assert(rtpe.typeSymbol hasFlag CASE, tpe); localTyper.typedOperator { atPos(pos) { @@ -1249,57 +1108,61 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans finally popLevel() } - /** Eliminate ModuleDefs. - * - A top level object is replaced with their module class. - * - An inner object is transformed into a module var, created on first access. + /** Eliminate ModuleDefs. In all cases the ModuleDef (carrying a module symbol) is + * replaced with a ClassDef (carrying the corresponding module class symbol) with additional + * trees created as follows: * - * In both cases, this transformation returns the list of replacement trees: - * - Top level: the module class accessor definition - * - Inner: a class definition, declaration of module var, and module var accessor + * 1) A statically reachable object (either top-level or nested only in objects) receives + * no additional trees. + * 2) An inner object which matches an existing member (e.g. implements an interface) + * receives an accessor DefDef to implement the interface. + * 3) An inner object otherwise receives a private ValDef which declares a module var + * (the field which holds the module class - it has a name like Foo$module) and an + * accessor for that field. The instance is created lazily, on first access. */ - private def eliminateModuleDefs(tree: Tree): List[Tree] = { - val ModuleDef(mods, name, impl) = tree - val sym = tree.symbol - val classSym = sym.moduleClass - val cdef = ClassDef(mods | MODULE, name.toTypeName, Nil, impl) setSymbol classSym setType NoType - - def findOrCreateModuleVar() = localTyper.typedPos(tree.pos) { - // See SI-5012, SI-6712. + private def eliminateModuleDefs(moduleDef: Tree): List[Tree] = exitingRefchecks { + val ModuleDef(mods, name, impl) = moduleDef + val module = moduleDef.symbol + val site = module.owner + val moduleName = module.name.toTermName + // The typer doesn't take kindly to seeing this ClassDef; we have to + // set NoType so it will be ignored. + val cdef = ClassDef(module.moduleClass, impl) setType NoType + + // Create the module var unless the immediate owner is a class and + // the module var already exists there. See SI-5012, SI-6712. + def findOrCreateModuleVar() = { val vsym = ( - if (sym.owner.isTerm) NoSymbol - else sym.enclClass.info.decl(nme.moduleVarName(sym.name.toTermName)) + if (site.isTerm) NoSymbol + else site.info decl nme.moduleVarName(moduleName) ) - // In case we are dealing with local symbol then we already have - // to correct error with forward reference - if (vsym == NoSymbol) gen.mkModuleVarDef(sym) - else ValDef(vsym) + vsym orElse (site newModuleVarSymbol module) } - def createStaticModuleAccessor() = afterRefchecks { - val method = ( - sym.owner.newMethod(sym.name.toTermName, sym.pos, (sym.flags | STABLE) & ~MODULE) - setInfoAndEnter NullaryMethodType(sym.moduleClass.tpe) - ) - localTyper.typedPos(tree.pos)(gen.mkModuleAccessDef(method, sym)) + def newInnerObject() = { + // Create the module var unless it is already in the module owner's scope. + // The lookup is on module.enclClass and not module.owner lest there be a + // nullary method between us and the class; see SI-5012. + val moduleVar = findOrCreateModuleVar() + val rhs = gen.newModule(module, moduleVar.tpe) + val body = if (site.isTrait) rhs else gen.mkAssignAndReturn(moduleVar, rhs) + val accessor = DefDef(module, body.changeOwner(moduleVar -> module)) + + ValDef(moduleVar) :: accessor :: Nil } - def createInnerModuleAccessor(vdef: Tree) = List( - vdef, - localTyper.typedPos(tree.pos) { - val vsym = vdef.symbol - afterRefchecks { - val rhs = gen.newModule(sym, vsym.tpe) - val body = if (sym.owner.isTrait) rhs else gen.mkAssignAndReturn(vsym, rhs) - DefDef(sym, body.changeOwner(vsym -> sym)) - } - } - ) - transformTrees(cdef :: { - if (!sym.isStatic) - createInnerModuleAccessor(findOrCreateModuleVar) - else if (sym.isOverridingSymbol) - List(createStaticModuleAccessor()) + def matchingInnerObject() = { + val newFlags = (module.flags | STABLE) & ~MODULE + val newInfo = NullaryMethodType(module.moduleClass.tpe) + val accessor = site.newMethod(moduleName, module.pos, newFlags) setInfoAndEnter newInfo + + DefDef(accessor, Select(This(site), module)) :: Nil + } + val newTrees = cdef :: ( + if (module.isStatic) + if (module.isOverridingSymbol) matchingInnerObject() else Nil else - Nil - }) + newInnerObject() + ) + transformTrees(newTrees map localTyper.typedPos(moduleDef.pos)) } def transformStat(tree: Tree, index: Int): List[Tree] = tree match { @@ -1313,7 +1176,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } case ModuleDef(_, _, _) => eliminateModuleDefs(tree) case ValDef(_, _, _, _) => - val tree1 @ ValDef(_, _, _, rhs) = transform(tree) // important to do before forward reference check + val tree1 = transform(tree) // important to do before forward reference check if (tree1.symbol.isLazy) tree1 :: Nil else { val lazySym = tree.symbol.lazyAccessorOrSelf @@ -1373,12 +1236,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans */ private def checkMigration(sym: Symbol, pos: Position) = { if (sym.hasMigrationAnnotation) { - val changed = try + val changed = try settings.Xmigration.value < ScalaVersion(sym.migrationVersion.get) catch { - case e : NumberFormatException => + case e : NumberFormatException => unit.warning(pos, s"${sym.fullLocationString} has an unparsable version number: ${e.getMessage()}") - // if we can't parse the format on the migration annotation just conservatively assume it changed + // if we can't parse the format on the migration annotation just conservatively assume it changed true } if (changed) @@ -1445,7 +1308,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // if the unnormalized type is accessible, that's good enough if (inaccessible.isEmpty) () // or if the normalized type is, that's good too - else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.normalize, member).isEmpty) () + else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) () // otherwise warn about the inaccessible syms in the unnormalized type else inaccessible foreach (sym => warnLessAccessible(sym, member)) } @@ -1536,9 +1399,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case TypeApply(fun, targs) => isClassTypeAccessible(fun) case Select(module, apply) => - // Fixes SI-5626. Classes in refinement types cannot be constructed with `new`. In this case, - // the companion class is actually not a ClassSymbol, but a reference to an abstract type. - module.symbol.companionClass.isClass + ( // SI-4859 `CaseClass1().InnerCaseClass2()` must not be rewritten to `new InnerCaseClass2()`; + // {expr; Outer}.Inner() must not be rewritten to `new Outer.Inner()`. + treeInfo.isQualifierSafeToElide(module) && + // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case, + // the companion class is actually not a ClassSymbol, but a reference to an abstract type. + module.symbol.companionClass.isClass + ) } val doTransform = @@ -1582,7 +1449,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans tree } private def transformSelect(tree: Select): Tree = { - val Select(qual, name) = tree + val Select(qual, _) = tree val sym = tree.symbol /** Note: if a symbol has both @deprecated and @migration annotations and both @@ -1596,18 +1463,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkMigration(sym, tree.pos) checkCompileTimeOnly(sym, tree.pos) - if (sym eq NoSymbol) { - unit.warning(tree.pos, "Select node has NoSymbol! " + tree + " / " + tree.tpe) - } - else if (currentClass != sym.owner && sym.hasLocalFlag) { - var o = currentClass - var hidden = false - while (!hidden && o != sym.owner && o != sym.owner.moduleClass && !o.isPackage) { - hidden = o.isTerm || o.isPrivateLocal - o = o.owner - } - if (!hidden) escapedPrivateLocals += sym - } + if (sym eq NoSymbol) + devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe) + else if (sym.hasLocalFlag) + varianceValidator.checkForEscape(sym, currentClass) def checkSuper(mix: Name) = // term should have been eliminated by super accessors @@ -1772,7 +1631,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans result match { case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => - if (result.symbol.isLocal || result.symbol.owner.isPackageClass) + if (result.symbol.isLocal || result.symbol.isTopLevel) varianceValidator.traverse(result) case _ => } diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 64c5b41638..64fcda3b80 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -4,7 +4,127 @@ package typechecker trait StdAttachments { self: Analyzer => + import global._ + + /** Carries information necessary to expand the host tree. + * At times we need to store this info, because macro expansion can be delayed until its targs are inferred. + * After a macro application has been successfully expanded, this attachment is destroyed. + */ type UnaffiliatedMacroContext = scala.reflect.macros.runtime.Context type MacroContext = UnaffiliatedMacroContext { val universe: self.global.type } case class MacroRuntimeAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) + + /** Scratchpad for the macro expander, which is used to store all intermediate data except the details about the runtime. + */ + case class MacroExpanderAttachment(original: Tree, desugared: Tree, role: MacroRole) + + /** Loads underlying MacroExpanderAttachment from a macro expandee or returns a default value for that attachment. + */ + def macroExpanderAttachment(tree: Tree): MacroExpanderAttachment = + tree.attachments.get[MacroExpanderAttachment] getOrElse { + tree match { + case Apply(fn, _) if tree.isInstanceOf[ApplyToImplicitArgs] => macroExpanderAttachment(fn) + case _ => MacroExpanderAttachment(tree, EmptyTree, APPLY_ROLE) + } + } + + /** After macro expansion is completed, links the expandee and the expansion result + * by annotating them both with a `MacroExpansionAttachment`. + */ + def linkExpandeeAndDesugared(expandee: Tree, desugared: Tree, role: MacroRole): Unit = { + val metadata = MacroExpanderAttachment(expandee, desugared, role) + expandee updateAttachment metadata + desugared updateAttachment metadata + } + + /** Is added by the macro engine to originals and results of macro expansions. + * Stores the original expandee as it entered the `macroExpand` function. + */ + case class MacroExpansionAttachment(expandee: Tree, expanded: Any) + + /** Determines whether the target is either an original or a result of a macro expansion. + * The parameter is of type `Any`, because macros can expand both into trees and into annotations. + */ + def hasMacroExpansionAttachment(any: Any): Boolean = any match { + case tree: Tree => tree.attachments.get[MacroExpansionAttachment].isDefined + case _ => false + } + + /** After macro expansion is completed, links the expandee and the expansion result by annotating them both with a `MacroExpansionAttachment`. + * The `expanded` parameter is of type `Any`, because macros can expand both into trees and into annotations. + */ + def linkExpandeeAndExpanded(expandee: Tree, expanded: Any): Unit = { + val metadata = MacroExpansionAttachment(expandee, expanded) + expandee updateAttachment metadata + expanded match { + case expanded: Tree => expanded updateAttachment metadata + case _ => // do nothing + } + } + + /** Checks whether there is any tree resulting from a macro expansion and associated with the current tree. + */ + object ExpandedIntoTree { + def unapply(tree: Tree): Option[Tree] = tree.attachments.get[MacroExpansionAttachment] match { + case Some(MacroExpansionAttachment(_, tree: Tree)) => Some(tree) + case _ => None + } + } + + /** When present, suppresses macro expansion for the host. + * This is occasionally necessary, e.g. to prohibit eta-expansion of macros. + * + * Does not affect expandability of child nodes, there's context.withMacrosDisabled for that + * (but think thrice before using that API - see the discussion at https://github.com/scala/scala/pull/1639). + */ + case object SuppressMacroExpansionAttachment + + /** Suppresses macro expansion of the tree by putting SuppressMacroExpansionAttachment on it. + */ + def suppressMacroExpansion(tree: Tree) = tree.updateAttachment(SuppressMacroExpansionAttachment) + + /** Unsuppresses macro expansion of the tree by removing SuppressMacroExpansionAttachment from it and its children. + */ + def unsuppressMacroExpansion(tree: Tree): Tree = { + tree.removeAttachment[SuppressMacroExpansionAttachment.type] + tree match { + // see the comment to `isMacroExpansionSuppressed` to learn why we need + // a special traversal strategy here + case Apply(fn, _) => unsuppressMacroExpansion(fn) + case TypeApply(fn, _) => unsuppressMacroExpansion(fn) + case _ => // do nothing + } + tree + } + + /** Determines whether a tree should not be expanded, because someone has put SuppressMacroExpansionAttachment on it or one of its children. + */ + def isMacroExpansionSuppressed(tree: Tree): Boolean = + if (tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined) true + else tree match { + // we have to account for the fact that during typechecking an expandee might become wrapped, + // i.e. surrounded by an inferred implicit argument application or by an inferred type argument application. + // in that case the expandee itself will no longer be suppressed and we need to look at the core + case Apply(fn, _) => isMacroExpansionSuppressed(fn) + case TypeApply(fn, _) => isMacroExpansionSuppressed(fn) + case _ => false + } + + /** After being synthesized by the parser, primary constructors aren't fully baked yet. + * A call to super in such constructors is just a fill-me-in-later dummy resolved later + * by `parentTypes`. This attachment coordinates `parentTypes` and `typedTemplate` and + * allows them to complete the synthesis. + */ + case class SuperArgsAttachment(argss: List[List[Tree]]) + + /** Convenience method for `SuperArgsAttachment`. + * Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern, + * so it really benefits from a dedicated extractor. + */ + def superArgs(tree: Tree): Option[List[List[Tree]]] = + tree.attachments.get[SuperArgsAttachment] collect { case SuperArgsAttachment(argss) => argss } + + /** Determines whether the given tree has an associated SuperArgsAttachment. + */ + def hasSuperArgs(tree: Tree): Boolean = superArgs(tree).nonEmpty }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 67639eb530..f2129992e5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -60,8 +60,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val clazz = qual.symbol val supername = nme.superName(name) val superAcc = clazz.info.decl(supername).suchThat(_.alias == sym) orElse { - debuglog("add super acc " + sym + sym.locationString + " to `" + clazz);//debug - val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE) setAlias sym + debuglog(s"add super acc ${sym.fullLocationString} to $clazz") + val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE | ARTIFACT) setAlias sym val tpe = clazz.thisType memberType sym match { case t if sym.isModule && !sym.isMethod => NullaryMethodType(t) case t => t @@ -295,7 +295,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT && !sym.owner.isTrait && (sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass) && (qual.symbol.info.member(sym.name) ne NoSymbol) - && !needsProtectedAccessor(sym, tree.pos)) + && !needsProtectedAccessor(sym, tree.pos) + ) if (shouldEnsureAccessor) { log("Ensuring accessor for call to protected " + sym.fullLocationString + " from " + currentClass) ensureAccessor(sel) @@ -391,7 +392,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT * typed. */ private def makeAccessor(tree: Select, targs: List[Tree]): Tree = { - val Select(qual, name) = tree + val Select(qual, _) = tree val sym = tree.symbol val clazz = hostForAccessorOf(sym, currentClass) @@ -416,7 +417,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } val protAcc = clazz.info.decl(accName).suchThat(s => s == NoSymbol || s.tpe =:= accType(s)) orElse { - val newAcc = clazz.newMethod(nme.protName(sym.originalName), tree.pos) + val newAcc = clazz.newMethod(nme.protName(sym.originalName), tree.pos, newFlags = ARTIFACT) newAcc setInfoAndEnter accType(newAcc) val code = DefDef(newAcc, { @@ -427,7 +428,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT args.foldLeft(base)(Apply(_, _)) }) - debuglog("" + code) + debuglog("created protected accessor: " + code) storeAccessorDefinition(clazz, code) newAcc } @@ -439,7 +440,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case _ => mkApply(TypeApply(selection, targs)) } } - debuglog("Replaced " + tree + " with " + res) + debuglog(s"Replaced $tree with $res") if (hasArgs) localTyper.typedOperator(res) else localTyper.typed(res) } @@ -478,7 +479,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val accName = nme.protSetterName(field.originalName) val protectedAccessor = clazz.info decl accName orElse { - val protAcc = clazz.newMethod(accName, field.pos) + val protAcc = clazz.newMethod(accName, field.pos, newFlags = ARTIFACT) val paramTypes = List(clazz.typeOfThis, field.tpe) val params = protAcc newSyntheticValueParams paramTypes val accessorType = MethodType(params, UnitClass.tpe) @@ -510,9 +511,6 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT def accessibleThroughSubclassing = validCurrentOwner && clazz.thisSym.isSubClass(sym.owner) && !clazz.isTrait - def packageAccessBoundry(sym: Symbol) = - sym.accessBoundary(sym.enclosingPackageClass) - val isCandidate = ( sym.isProtected && sym.isJavaDefined diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 242eb9c9fe..a2b0530c26 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -6,9 +6,8 @@ package scala.tools.nsc package typechecker -import symtab.Flags +import scala.collection.{ mutable, immutable } import symtab.Flags._ -import scala.collection.mutable import scala.collection.mutable.ListBuffer /** Synthetic method implementations for case classes and case objects. @@ -94,7 +93,7 @@ trait SyntheticMethods extends ast.TreeDSL { // like Tags and Arrays which are not robust and infer things // which they shouldn't. val accessorLub = ( - if (opt.experimental) { + if (settings.Xexperimental.value) { global.weakLub(accessors map (_.tpe.finalResultType))._1 match { case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents) case tp => tp @@ -121,20 +120,11 @@ trait SyntheticMethods extends ast.TreeDSL { (m0 ne meth) && !m0.isDeferred && !m0.isSynthetic && (m0.owner != AnyValClass) && (typeInClazz(m0) matches typeInClazz(meth)) } } - def readConstantValue[T](name: String, default: T = null.asInstanceOf[T]): T = { - clazzMember(newTermName(name)).info match { - case NullaryMethodType(ConstantType(Constant(value))) => value.asInstanceOf[T] - case _ => default - } - } def productIteratorMethod = { createMethod(nme.productIterator, iteratorOfType(accessorLub))(_ => gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(accessorLub), List(mkThis)) ) } - def projectionMethod(accessor: Symbol, num: Int) = { - createMethod(nme.productAccessorName(num), accessor.tpe.resultType)(_ => REF(accessor)) - } /** Common code for productElement and (currently disabled) productElementName */ @@ -238,10 +228,15 @@ trait SyntheticMethods extends ast.TreeDSL { /** The _1, _2, etc. methods to implement ProductN, disabled * until we figure out how to introduce ProductN without cycles. */ - def productNMethods = { + /**** + def productNMethods = { val accs = accessors.toIndexedSeq 1 to arity map (num => productProj(arity, num) -> (() => projectionMethod(accs(num - 1), num))) } + def projectionMethod(accessor: Symbol, num: Int) = { + createMethod(nme.productAccessorName(num), accessor.tpe.resultType)(_ => REF(accessor)) + } + ****/ // methods for both classes and objects def productMethods = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Tags.scala b/src/compiler/scala/tools/nsc/typechecker/Tags.scala index d82fbd7c77..45aa1bcbdb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Tags.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Tags.scala @@ -10,7 +10,7 @@ trait Tags { trait Tag { self: Typer => - private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = beforeTyper { + private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = enteringTyper { def wrapper (tree: => Tree): Tree = if (allowMaterialization) (context.withMacrosEnabled[Tree](tree)) else (context.withMacrosDisabled[Tree](tree)) wrapper(inferImplicit( EmptyTree, diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 88d10f1d72..52497411d1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package typechecker -import scala.tools.nsc.symtab.Flags._ import scala.collection.mutable import mutable.ListBuffer import util.returning @@ -144,13 +143,6 @@ abstract class TreeCheckers extends Analyzer { currentRun.units foreach (x => wrap(x)(check(x))) } - def printingTypings[T](body: => T): T = { - val saved = global.printTypings - global.printTypings = true - val result = body - global.printTypings = saved - result - } def runWithUnit[T](unit: CompilationUnit)(body: => Unit): Unit = { hasError = false val unit0 = currentUnit @@ -189,10 +181,6 @@ abstract class TreeCheckers extends Analyzer { errorFn(t1.pos, "trees differ\n old: " + treestr(t1) + "\n new: " + treestr(t2)) private def typesDiffer(tree: Tree, tp1: Type, tp2: Type) = errorFn(tree.pos, "types differ\n old: " + tp1 + "\n new: " + tp2 + "\n tree: " + tree) - private def ownersDiffer(tree: Tree, shouldBe: Symbol) = { - val sym = tree.symbol - errorFn(tree.pos, sym + " has wrong owner: " + ownerstr(sym.owner) + ", should be: " + ownerstr(shouldBe)) - } /** XXX Disabled reporting of position errors until there is less noise. */ private def noPos(t: Tree) = @@ -204,14 +192,11 @@ abstract class TreeCheckers extends Analyzer { if (t.symbol == NoSymbol) errorFn(t.pos, "no symbol: " + treestr(t)) - override def typed(tree: Tree, mode: Int, pt: Type): Tree = returning(tree) { + override def typed(tree: Tree, mode: Mode, pt: Type): Tree = returning(tree) { case EmptyTree | TypeTree() => () case _ if tree.tpe != null => - tpeOfTree.getOrElseUpdate(tree, { - val saved = tree.tpe - tree.tpe = null - saved - }) + tpeOfTree.getOrElseUpdate(tree, try tree.tpe finally tree.clearType()) + wrap(tree)(super.typed(tree, mode, pt) match { case _: Literal => () case x if x ne tree => treesDiffer(tree, x) @@ -284,7 +269,7 @@ abstract class TreeCheckers extends Analyzer { def cond(s: Symbol) = !s.isTerm || s.isMethod || s == sym.owner if (sym.owner != currentOwner) { - val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse fail("DefTree can't find owner: ") + val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse { fail("DefTree can't find owner: ") ; NoSymbol } if (sym.owner != expected) fail(sm"""| | currentOwner chain: ${currentOwner.ownerChain take 3 mkString " -> "} @@ -344,7 +329,7 @@ abstract class TreeCheckers extends Analyzer { if (oldtpe =:= tree.tpe) () else typesDiffer(tree, oldtpe, tree.tpe) - tree.tpe = oldtpe + tree setType oldtpe super.traverse(tree) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 9376cb5237..81ea5630d0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -8,7 +8,6 @@ package typechecker import scala.collection.mutable import scala.collection.mutable.ListBuffer -import scala.util.control.ControlThrowable import scala.util.control.Exception.ultimately import symtab.Flags._ import PartialFunction._ @@ -37,15 +36,6 @@ trait TypeDiagnostics { import global._ import definitions._ - import global.typer.{ infer, context } - - /** The common situation of making sure nothing is erroneous could be - * nicer if Symbols, Types, and Trees all implemented some common interface - * in which isErroneous and similar would be placed. - */ - def noErroneousTypes(tps: Type*) = tps forall (x => !x.isErroneous) - def noErroneousSyms(syms: Symbol*) = syms forall (x => !x.isErroneous) - def noErroneousTrees(trees: Tree*) = trees forall (x => !x.isErroneous) /** For errors which are artifacts of the implementation: such messages * indicate that the restriction may be lifted in the future. @@ -58,7 +48,7 @@ trait TypeDiagnostics { /** A map of Positions to addendums - if an error involves a position in * the map, the addendum should also be printed. */ - private var addendums = perRunCaches.newMap[Position, () => String]() + private val addendums = perRunCaches.newMap[Position, () => String]() private var isTyperInPattern = false /** Devising new ways of communicating error info out of @@ -174,11 +164,6 @@ trait TypeDiagnostics { case xs => " where " + (disambiguate(xs map (_.existentialToString)) mkString ", ") } - def varianceWord(sym: Symbol): String = - if (sym.variance == 1) "covariant" - else if (sym.variance == -1) "contravariant" - else "invariant" - def explainAlias(tp: Type) = { // Don't automatically normalize standard aliases; they still will be // expanded if necessary to disambiguate simple identifiers. @@ -223,12 +208,12 @@ trait TypeDiagnostics { // force measures than comparing normalized Strings were producing error messages // like "and java.util.ArrayList[String] <: java.util.ArrayList[String]" but there // should be a cleaner way to do this. - if (found.normalize.toString == tp.normalize.toString) "" + if (found.dealiasWiden.toString == tp.dealiasWiden.toString) "" else " (and %s <: %s)".format(found, tp) ) val explainDef = { val prepend = if (isJava) "Java-defined " else "" - "%s%s is %s in %s.".format(prepend, reqsym, varianceWord(param), param) + "%s%s is %s in %s.".format(prepend, reqsym, param.variance, param) } // Don't suggest they change the class declaration if it's somewhere // under scala.* or defined in a java class, because attempting either @@ -248,7 +233,7 @@ trait TypeDiagnostics { || ((arg <:< reqArg) && param.isCovariant) || ((reqArg <:< arg) && param.isContravariant) ) - val invariant = param.variance == 0 + val invariant = param.variance.isInvariant if (conforms) Some("") else if ((arg <:< reqArg) && invariant) mkMsg(true) // covariant relationship @@ -291,7 +276,6 @@ trait TypeDiagnostics { // distinguished from the other types in the same error message private val savedName = sym.name def restoreName() = sym.name = savedName - def isAltered = sym.name != savedName def modifyName(f: String => String) = sym setName newTypeName(f(sym.name.toString)) /** Prepend java.lang, scala., or Predef. if this type originated @@ -421,6 +405,120 @@ trait TypeDiagnostics { def permanentlyHiddenWarning(pos: Position, hidden: Name, defn: Symbol) = contextWarning(pos, "imported `%s' is permanently hidden by definition of %s".format(hidden, defn.fullLocationString)) + object checkUnused { + val ignoreNames = Set[TermName]("readResolve", "readObject", "writeObject", "writeReplace") + + class UnusedPrivates extends Traverser { + val defnTrees = ListBuffer[MemberDef]() + val targets = mutable.Set[Symbol]() + val setVars = mutable.Set[Symbol]() + val treeTypes = mutable.Set[Type]() + + def defnSymbols = defnTrees.toList map (_.symbol) + def localVars = defnSymbols filter (t => t.isLocal && t.isVar) + + def qualifiesTerm(sym: Symbol) = ( + (sym.isModule || sym.isMethod || sym.isPrivateLocal || sym.isLocal) + && !nme.isLocalName(sym.name) + && !sym.isParameter + && !sym.isParamAccessor // could improve this, but it's a pain + && !sym.isEarlyInitialized // lots of false positives in the way these are encoded + && !(sym.isGetter && sym.accessed.isEarlyInitialized) + ) + def qualifiesType(sym: Symbol) = !sym.isDefinedInPackage + def qualifies(sym: Symbol) = ( + (sym ne null) + && (sym.isTerm && qualifiesTerm(sym) || sym.isType && qualifiesType(sym)) + ) + + override def traverse(t: Tree): Unit = { + t match { + case t: MemberDef if qualifies(t.symbol) => defnTrees += t + case t: RefTree if t.symbol ne null => targets += t.symbol + case Assign(lhs, _) if lhs.symbol != null => setVars += lhs.symbol + case _ => + } + // Only record type references which don't originate within the + // definition of the class being referenced. + if (t.tpe ne null) { + for (tp <- t.tpe ; if !treeTypes(tp) && !currentOwner.ownerChain.contains(tp.typeSymbol)) { + tp match { + case NoType | NoPrefix => + case NullaryMethodType(_) => + case MethodType(_, _) => + case _ => + log(s"$tp referenced from $currentOwner") + treeTypes += tp + } + } + // e.g. val a = new Foo ; new a.Bar ; don't let a be reported as unused. + t.tpe.prefix foreach { + case SingleType(_, sym) => targets += sym + case _ => + } + } + super.traverse(t) + } + def isUnusedType(m: Symbol): Boolean = ( + m.isType + && !m.isTypeParameterOrSkolem // would be nice to improve this + && (m.isPrivate || m.isLocal) + && !(treeTypes.exists(tp => tp exists (t => t.typeSymbolDirect == m))) + ) + def isUnusedTerm(m: Symbol): Boolean = ( + (m.isTerm) + && (m.isPrivate || m.isLocal) + && !targets(m) + && !(m.name == nme.WILDCARD) // e.g. val _ = foo + && !ignoreNames(m.name.toTermName) // serialization methods + && !isConstantType(m.info.resultType) // subject to constant inlining + && !treeTypes.exists(_ contains m) // e.g. val a = new Foo ; new a.Bar + ) + def unusedTypes = defnTrees.toList filter (t => isUnusedType(t.symbol)) + def unusedTerms = defnTrees.toList filter (v => isUnusedTerm(v.symbol)) + // local vars which are never set, except those already returned in unused + def unsetVars = localVars filter (v => !setVars(v) && !isUnusedTerm(v)) + } + + def apply(unit: CompilationUnit) = { + val p = new UnusedPrivates + p traverse unit.body + val unused = p.unusedTerms + unused foreach { defn: DefTree => + val sym = defn.symbol + val isDefaultGetter = sym.name containsName nme.DEFAULT_GETTER_STRING + val pos = ( + if (defn.pos.isDefined) defn.pos + else if (sym.pos.isDefined) sym.pos + else sym match { + case sym: TermSymbol => sym.referenced.pos + case _ => NoPosition + } + ) + val why = if (sym.isPrivate) "private" else "local" + val what = ( + if (isDefaultGetter) "default argument" + else if (sym.isConstructor) "constructor" + else if (sym.isVar || sym.isGetter && sym.accessed.isVar) "var" + else if (sym.isVal || sym.isGetter && sym.accessed.isVal) "val" + else if (sym.isSetter) "setter" + else if (sym.isMethod) "method" + else if (sym.isModule) "object" + else "term" + ) + unit.warning(pos, s"$why $what in ${sym.owner} is never used") + } + p.unsetVars foreach { v => + unit.warning(v.pos, s"local var ${v.name} in ${v.owner} is never set - it could be a val") + } + p.unusedTypes foreach { t => + val sym = t.symbol + val why = if (sym.isPrivate) "private" else "local" + unit.warning(t.pos, s"$why ${sym.fullLocationString} is never used") + } + } + } + object checkDead { private val exprStack: mutable.Stack[Symbol] = mutable.Stack(NoSymbol) // The method being applied to `tree` when `apply` is called. @@ -451,7 +549,7 @@ trait TypeDiagnostics { } // The checkDead call from typedArg is more selective. - def inMode(mode: Int, tree: Tree): Tree = { + def inMode(mode: Mode, tree: Tree): Tree = { val modeOK = (mode & (EXPRmode | BYVALmode | POLYmode)) == (EXPRmode | BYVALmode) if (modeOK) apply(tree) else tree diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9dde952d25..561ca7f382 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -13,9 +13,10 @@ package scala.tools.nsc package typechecker import scala.collection.mutable -import scala.reflect.internal.util.{ BatchSourceFile, Statistics } +import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance } import mutable.ListBuffer import symtab.Flags._ +import Mode._ // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -24,14 +25,14 @@ import symtab.Flags._ * @author Martin Odersky * @version 1.0 */ -trait Typers extends Modes with Adaptations with Tags { +trait Typers extends Adaptations with Tags { self: Analyzer => import global._ import definitions._ import TypersStats._ - final def forArgMode(fun: Tree, mode: Int) = + final def forArgMode(fun: Tree, mode: Mode) = if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode else mode @@ -52,27 +53,33 @@ trait Typers extends Modes with Adaptations with Tags { object UnTyper extends Traverser { override def traverse(tree: Tree) = { - if (tree != EmptyTree) tree.tpe = null - if (tree.hasSymbol) tree.symbol = NoSymbol + if (tree.canHaveAttrs) { + tree.clearType() + if (tree.hasSymbolField) tree.symbol = NoSymbol + } super.traverse(tree) } } -/* needed for experimental version where early types can be type arguments - class EarlyMap(clazz: Symbol) extends TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(NoPrefix, sym, List()) if (sym hasFlag PRESUPER) => - TypeRef(ThisType(clazz), sym, List()) - case _ => - mapOver(tp) + + sealed abstract class SilentResult[+T] { + @inline final def map[U](f: T => U): SilentResult[U] = this match { + case SilentResultValue(value) => SilentResultValue(f(value)) + case x: SilentTypeError => x + } + @inline final def filter(p: T => Boolean): SilentResult[T] = this match { + case SilentResultValue(value) if !p(value) => SilentTypeError(TypeErrorWrapper(new TypeError(NoPosition, "!p"))) + case _ => this + } + @inline final def orElse[T1 >: T](f: AbsTypeError => T1): T1 = this match { + case SilentResultValue(value) => value + case SilentTypeError(err) => f(err) } } -*/ - - sealed abstract class SilentResult[+T] case class SilentTypeError(err: AbsTypeError) extends SilentResult[Nothing] { } case class SilentResultValue[+T](value: T) extends SilentResult[T] { } def newTyper(context: Context): Typer = new NormalTyper(context) + private class NormalTyper(context : Context) extends Typer(context) // A transient flag to mark members of anonymous classes @@ -90,14 +97,17 @@ trait Typers extends Modes with Adaptations with Tags { // when true: // - we may virtualize matches (if -Xexperimental and there's a suitable __match in scope) // - we synthesize PartialFunction implementations for `x => x match {...}` and `match {...}` when the expected type is PartialFunction - // this is disabled by: -Xoldpatmat or interactive compilation (we run it for scaladoc due to SI-5933) - private def newPatternMatching = opt.virtPatmat && !forInteractive //&& !forScaladoc && (phase.id < currentRun.uncurryPhase.id) + // this is disabled by: interactive compilation (we run it for scaladoc due to SI-5933) + private def newPatternMatching = !forInteractive //&& !forScaladoc && (phase.id < currentRun.uncurryPhase.id) abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors { import context0.unit import typeDebug.{ ptTree, ptBlock, ptLine } import TyperErrorGen._ + def typedDocDef(docDef: DocDef, mode: Mode, pt: Type): Tree = + typed(docDef.definition, mode, pt) + val infer = new Inferencer(context0) { override def isCoercible(tp: Type, pt: Type): Boolean = undoLog undo { // #3281 tp.isError || pt.isError || @@ -115,10 +125,7 @@ trait Typers extends Modes with Adaptations with Tags { // paramFailed cannot be initialized with params.exists(_.tpe.isError) because that would // hide some valid errors for params preceding the erroneous one. var paramFailed = false - - def mkPositionalArg(argTree: Tree, paramName: Name) = argTree - def mkNamedArg(argTree: Tree, paramName: Name) = atPos(argTree.pos)(new AssignOrNamedArg(Ident(paramName), (argTree))) - var mkArg: (Tree, Name) => Tree = mkPositionalArg + var mkArg: (Name, Tree) => Tree = (_, tree) => tree // DEPMETTODO: instantiate type vars that depend on earlier implicit args (see adapt (4.1)) // @@ -133,9 +140,9 @@ trait Typers extends Modes with Adaptations with Tags { argResultsBuff += res if (res.isSuccess) { - argBuff += mkArg(res.tree, param.name) + argBuff += mkArg(param.name, res.tree) } else { - mkArg = mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args + mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args if (!param.hasDefault && !paramFailed) { context.errBuffer.find(_.kind == ErrorKinds.Divergent) match { case Some(divergentImplicit) => @@ -194,7 +201,7 @@ trait Typers extends Modes with Adaptations with Tags { case PolyType(_, _) => EmptyTree case _ => def wrapImplicit(from: Type): Tree = { - val result = inferImplicit(tree, functionType(from :: Nil, to), reportAmbiguous, true, context, saveErrors) + val result = inferImplicit(tree, functionType(from.withoutAnnotations :: Nil, to), reportAmbiguous, true, context, saveErrors) if (result.subst != EmptyTreeTypeSubstituter) { result.subst traverse tree notifyUndetparamsInferred(result.subst.from, result.subst.to) @@ -227,10 +234,7 @@ trait Typers extends Modes with Adaptations with Tags { case _ => tp } - /** Check that <code>tree</code> is a stable expression. - * - * @param tree ... - * @return ... + /** Check that `tree` is a stable expression. */ def checkStable(tree: Tree): Tree = ( if (treeInfo.isExprSafeToInline(tree)) tree @@ -242,7 +246,7 @@ trait Typers extends Modes with Adaptations with Tags { * of its symbol was not volatile? */ protected def isStableExceptVolatile(tree: Tree) = { - tree.hasSymbol && tree.symbol != NoSymbol && tree.tpe.isVolatile && + tree.hasSymbolField && tree.symbol != NoSymbol && tree.tpe.isVolatile && { val savedTpe = tree.symbol.info val savedSTABLE = tree.symbol getFlag STABLE tree.symbol setInfo AnyRefClass.tpe @@ -284,16 +288,11 @@ trait Typers extends Modes with Adaptations with Tags { ) } - /** Check that type <code>tp</code> is not a subtype of itself. - * - * @param pos ... - * @param tp ... - * @return <code>true</code> if <code>tp</code> is not a subtype of itself. + /** Check that type `tp` is not a subtype of itself. */ def checkNonCyclic(pos: Position, tp: Type): Boolean = { def checkNotLocked(sym: Symbol) = { - sym.initialize - sym.lockOK || { CyclicAliasingOrSubtypingError(pos, sym); false } + sym.initialize.lockOK || { CyclicAliasingOrSubtypingError(pos, sym); false } } tp match { case TypeRef(pre, sym, args) => @@ -304,12 +303,6 @@ trait Typers extends Modes with Adaptations with Tags { case SingleType(pre, sym) => checkNotLocked(sym) -/* - case TypeBounds(lo, hi) => - var ok = true - for (t <- lo) ok = ok & checkNonCyclic(pos, t) - ok -*/ case st: SubType => checkNonCyclic(pos, st.supertype) case ct: CompoundType => @@ -320,19 +313,19 @@ trait Typers extends Modes with Adaptations with Tags { } def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Boolean = try { - if (!lockedSym.lock(CyclicReferenceError(pos, lockedSym))) false + if (!lockedSym.lock(CyclicReferenceError(pos, tp, lockedSym))) false else checkNonCyclic(pos, tp) } finally { lockedSym.unlock() } def checkNonCyclic(sym: Symbol) { - if (!checkNonCyclic(sym.pos, sym.tpe)) sym.setInfo(ErrorType) + if (!checkNonCyclic(sym.pos, sym.tpe_*)) sym.setInfo(ErrorType) } def checkNonCyclic(defn: Tree, tpt: Tree) { if (!checkNonCyclic(defn.pos, tpt.tpe, defn.symbol)) { - tpt.tpe = ErrorType + tpt setType ErrorType defn.symbol.setInfo(ErrorType) } } @@ -363,28 +356,13 @@ trait Typers extends Modes with Adaptations with Tags { private var scope: Scope = _ private var hiddenSymbols: List[Symbol] = _ - /** Check that type <code>tree</code> does not refer to private + /** Check that type `tree` does not refer to private * components unless itself is wrapped in something private - * (<code>owner</code> tells where the type occurs). - * - * @param owner ... - * @param tree ... - * @return ... + * (`owner` tells where the type occurs). */ def privates[T <: Tree](owner: Symbol, tree: T): T = check(owner, EmptyScope, WildcardType, tree) - /** Check that type <code>tree</code> does not refer to entities - * defined in scope <code>scope</code>. - * - * @param scope ... - * @param pt ... - * @param tree ... - * @return ... - */ - def locals[T <: Tree](scope: Scope, pt: Type, tree: T): T = - check(NoSymbol, scope, pt, tree) - private def check[T <: Tree](owner: Symbol, scope: Scope, pt: Type, tree: T): T = { this.owner = owner this.scope = scope @@ -460,7 +438,7 @@ trait Typers extends Modes with Adaptations with Tags { } /** The qualifying class - * of a this or super with prefix <code>qual</code>. + * of a this or super with prefix `qual`. * packageOk is equal false when qualifying class symbol */ def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean) = @@ -546,13 +524,13 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** Does the context of tree <code>tree</code> require a stable type? + /** Does the context of tree `tree` require a stable type? */ - private def isStableContext(tree: Tree, mode: Int, pt: Type) = - isNarrowable(tree.tpe) && ((mode & (EXPRmode | LHSmode)) == EXPRmode) && + private def isStableContext(tree: Tree, mode: Mode, pt: Type) = + isNarrowable(tree.tpe) && mode.inExprMode && mode.inNone(LHSmode) && (xtypes || (pt.isStable || - (mode & QUALmode) != 0 && !tree.symbol.isConstant || + mode.inAll(QUALmode) && !tree.symbol.isConstant || pt.typeSymbol.isAbstractType && pt.bounds.lo.isStable && !(tree.tpe <:< pt)) || pt.typeSymbol.isRefinementClass && !(tree.tpe <:< pt)) @@ -565,11 +543,13 @@ trait Typers extends Modes with Adaptations with Tags { * @return modified tree and new prefix type */ private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): (Tree, Type) = - if (isInPackageObject(sym, pre.typeSymbol)) { + if (context.isInPackageObject(sym, pre.typeSymbol)) { if (pre.typeSymbol == ScalaPackageClass && sym.isTerm) { // short cut some aliases. It seems pattern matching needs this // to notice exhaustiveness and to generate good code when // List extractors are mixed with :: patterns. See Test5 in lists.scala. + // + // TODO SI-6609 Eliminate this special case once the old pattern matcher is removed. def dealias(sym: Symbol) = (atPos(tree.pos.makeTransparent) {gen.mkAttributedRef(sym)} setPos tree.pos, sym.owner.thisType) sym.name match { @@ -598,41 +578,21 @@ trait Typers extends Modes with Adaptations with Tags { (checkAccessible(tree, sym, pre, site), pre) } - /** Is `sym` defined in package object of package `pkg`? - */ - private def isInPackageObject(sym: Symbol, pkg: Symbol) = { - def isInPkgObj(sym: Symbol) = - !sym.owner.isPackage && { - sym.owner.isPackageObjectClass && - sym.owner.owner == pkg || - pkg.isInitialized && { - // need to be careful here to not get a cyclic reference during bootstrap - val pkgobj = pkg.info.member(nme.PACKAGEkw) - pkgobj.isInitialized && - (pkgobj.info.member(sym.name).alternatives contains sym) - } - } - pkg.isPackageClass && { - if (sym.isOverloaded) sym.alternatives forall isInPkgObj - else isInPkgObj(sym) - } - } - /** Post-process an identifier or selection node, performing the following: * 1. Check that non-function pattern expressions are stable * 2. Check that packages and static modules are not used as values * 3. Turn tree type into stable type if possible and required by context. * 4. Give getClass calls a more precise type based on the type of the target of the call. */ - private def stabilize(tree: Tree, pre: Type, mode: Int, pt: Type): Tree = { - if (tree.symbol.isOverloaded && !inFunMode(mode)) + private def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = { + if (tree.symbol.isOverloaded && !mode.inFunMode) inferExprAlternative(tree, pt) val sym = tree.symbol def fail() = NotAValueError(tree, sym) if (tree.isErrorTyped) tree - else if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1) + else if (mode.inPatternNotFunMode && tree.isTerm) { // (1) if (sym.isValue) { val tree1 = checkStable(tree) // A module reference in a pattern has type Foo.type, not "object Foo" @@ -666,13 +626,7 @@ trait Typers extends Modes with Adaptations with Tags { case _ => !phase.erasedTypes } - /** - * @param tree ... - * @param mode ... - * @param pt ... - * @return ... - */ - def stabilizeFun(tree: Tree, mode: Int, pt: Type): Tree = { + def stabilizeFun(tree: Tree, mode: Mode, pt: Type): Tree = { val sym = tree.symbol val pre = tree match { case Select(qual, _) => qual.tpe @@ -840,7 +794,7 @@ trait Typers extends Modes with Adaptations with Tags { * (14) When in mode EXPRmode, apply a view * If all this fails, error */ - protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree = { + protected def adapt(tree: Tree, mode: Mode, pt: Type, original: Tree = EmptyTree): Tree = { def adaptToImplicitMethod(mt: MethodType): Tree = { if (context.undetparams.nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0` @@ -855,29 +809,28 @@ trait Typers extends Modes with Adaptations with Tags { // avoid throwing spurious DivergentImplicit errors if (context.hasErrors) - return setError(tree) - - withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree)){ typer1 => - if (original != EmptyTree && pt != WildcardType) - typer1.silent(tpr => { + setError(tree) + else + withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree))(typer1 => + if (original != EmptyTree && pt != WildcardType) ( + typer1 silent { tpr => val withImplicitArgs = tpr.applyImplicitArgs(tree) if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway else tpr.typed(withImplicitArgs, mode, pt) - }) match { - case SilentResultValue(result) => - result - case _ => + } + orElse { _ => debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) val tree1 = typed(resetAllAttrs(original), mode, WildcardType) // Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin. - tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, pt) + tree1 setType pluginsTyped(tree1.tpe, this, tree1, mode, pt) if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) } + ) else typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) + ) } - } def instantiateToMethodType(mt: MethodType): Tree = { val meth = tree match { @@ -885,11 +838,10 @@ trait Typers extends Modes with Adaptations with Tags { case Block(_, tree1) => tree1.symbol case _ => tree.symbol } - if (!meth.isConstructor && !meth.isTermMacro && isFunctionType(pt)) { // (4.2) + if (!meth.isConstructor && isFunctionType(pt)) { // (4.2) debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt) checkParamsConvertible(tree, tree.tpe) val tree0 = etaExpand(context.unit, tree, this) - // println("eta "+tree+" ---> "+tree0+":"+tree0.tpe+" undet: "+context.undetparams+ " mode: "+Integer.toHexString(mode)) if (context.undetparams.nonEmpty) { // #2624: need to infer type arguments for eta expansion of a polymorphic method @@ -911,13 +863,13 @@ trait Typers extends Modes with Adaptations with Tags { } def adaptType(): Tree = { - if (inFunMode(mode)) { + if (mode.inFunMode) { // todo. the commented line below makes sense for typechecking, say, TypeApply(Ident(`some abstract type symbol`), List(...)) // because otherwise Ident will have its tpe set to a TypeRef, not to a PolyType, and `typedTypeApply` will fail // but this needs additional investigation, because it crashes t5228, gadts1 and maybe something else // tree setType tree.tpe.normalize tree - } else if (tree.hasSymbol && !tree.symbol.typeParams.isEmpty && !inHKMode(mode) && + } else if (tree.hasSymbolField && !tree.symbol.typeParams.isEmpty && !mode.inHKMode && !(tree.symbol.isJavaDefined && context.unit.isJava)) { // (7) // @M When not typing a higher-kinded type ((mode & HKmode) == 0) // or raw type (tree.symbol.isJavaDefined && context.unit.isJava), types must be of kind *, @@ -925,8 +877,8 @@ trait Typers extends Modes with Adaptations with Tags { // @M TODO: why do kind-* tree's have symbols, while higher-kinded ones don't? MissingTypeParametersError(tree) } else if ( // (7.1) @M: check kind-arity - // @M: removed check for tree.hasSymbol and replace tree.symbol by tree.tpe.symbol (TypeTree's must also be checked here, and they don't directly have a symbol) - (inHKMode(mode)) && + // @M: removed check for tree.hasSymbolField and replace tree.symbol by tree.tpe.symbol (TypeTree's must also be checked here, and they don't directly have a symbol) + mode.inHKMode && // @M: don't check tree.tpe.symbol.typeParams. check tree.tpe.typeParams!!! // (e.g., m[Int] --> tree.tpe.symbol.typeParams.length == 1, tree.tpe.typeParams.length == 0!) !sameLength(tree.tpe.typeParams, pt.typeParams) && @@ -975,9 +927,11 @@ trait Typers extends Modes with Adaptations with Tags { def adaptConstrPattern(): Tree = { // (5) def hasUnapplyMember(tp: Type) = reallyExists(unapplyMember(tp)) val overloadedExtractorOfObject = tree.symbol filter (sym => hasUnapplyMember(sym.tpe)) - // if the tree's symbol's type does not define an extractor, maybe the tree's type does - // this is the case when we encounter an arbitrary tree as the target of an unapply call (rather than something that looks like a constructor call) - // (for now, this only happens due to wrapClassTagUnapply, but when we support parameterized extractors, it will become more common place) + // if the tree's symbol's type does not define an extractor, maybe the tree's type does. + // this is the case when we encounter an arbitrary tree as the target of an unapply call + // (rather than something that looks like a constructor call.) (for now, this only happens + // due to wrapClassTagUnapply, but when we support parameterized extractors, it will become + // more common place) val extractor = overloadedExtractorOfObject orElse unapplyMember(tree.tpe) if (extractor != NoSymbol) { // if we did some ad-hoc overloading resolution, update the tree's symbol @@ -987,27 +941,28 @@ trait Typers extends Modes with Adaptations with Tags { tree setSymbol overloadedExtractorOfObject tree.tpe match { - case OverloadedType(pre, alts) => tree.tpe = overloadedType(pre, alts filter (alt => hasUnapplyMember(alt.tpe))) + case OverloadedType(pre, alts) => tree setType overloadedType(pre, alts filter (alt => hasUnapplyMember(alt.tpe))) case _ => } val unapply = unapplyMember(extractor.tpe) val clazz = unapplyParameterType(unapply) - if (unapply.isCase && clazz.isCase && !(clazz.ancestors exists (_.isCase))) { + if (unapply.isCase && clazz.isCase) { // convert synthetic unapply of case class to case class constructor val prefix = tree.tpe.prefix val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) .setOriginal(tree) val skolems = new mutable.ListBuffer[TypeSymbol] - object variantToSkolem extends VariantTypeMap { + object variantToSkolem extends TypeMap(trackVariance = true) { def apply(tp: Type) = mapOver(tp) match { - case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => + // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 + case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => // must initialize or tpSym.tpe might see random type params!! // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala // TODO: why is that?? tpSym.initialize - val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) + val bounds = if (variance.isPositive) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) // origin must be the type param so we can deskolemize val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) @@ -1050,7 +1005,7 @@ trait Typers extends Modes with Adaptations with Tags { } def insertApply(): Tree = { - assert(!inHKMode(mode), modeString(mode)) //@M + assert(!mode.inHKMode, mode) //@M val adapted = adaptToName(tree, nme.apply) def stabilize0(pre: Type): Tree = stabilize(adapted, pre, EXPRmode | QUALmode, WildcardType) // TODO reconcile the overlap between Typers#stablize and TreeGen.stabilize @@ -1062,7 +1017,7 @@ trait Typers extends Modes with Adaptations with Tags { val pre = if (owner.isPackageClass) owner.thisType else if (owner.isClass) context.enclosingSubClassContext(owner).prefix - else NoPrefix + else NoPrefix stabilize0(pre) case Select(qualqual, _) => stabilize0(qualqual.tpe) @@ -1078,26 +1033,26 @@ trait Typers extends Modes with Adaptations with Tags { tree.tpe match { case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1) adaptAnnotations(tree, this, mode, pt) - case ct @ ConstantType(value) if inNoModes(mode, TYPEmode | FUNmode) && (ct <:< pt) && !forScaladoc && !forInteractive => // (0) + case ct @ ConstantType(value) if mode.inNone(TYPEmode | FUNmode) && (ct <:< pt) && !forScaladoc && !forInteractive => // (0) val sym = tree.symbol if (sym != null && sym.isDeprecated) { val msg = sym.toString + sym.locationString + " is deprecated: " + sym.deprecationMessage.getOrElse("") unit.deprecationWarning(tree.pos, msg) } treeCopy.Literal(tree, value) - case OverloadedType(pre, alts) if !inFunMode(mode) => // (1) + case OverloadedType(pre, alts) if !mode.inFunMode => // (1) inferExprAlternative(tree, pt) adapt(tree, mode, pt, original) case NullaryMethodType(restpe) => // (2) adapt(tree setType restpe, mode, pt, original) - case TypeRef(_, ByNameParamClass, List(arg)) if ((mode & EXPRmode) != 0) => // (2) + case TypeRef(_, ByNameParamClass, List(arg)) if mode.inExprMode => // (2) adapt(tree setType arg, mode, pt, original) case tr @ TypeRef(_, sym, _) if sym.isAliasType && tr.dealias.isInstanceOf[ExistentialType] && ((mode & (EXPRmode | LHSmode)) == EXPRmode) => adapt(tree setType tr.dealias.skolemizeExistential(context.owner, tree), mode, pt, original) case et @ ExistentialType(_, _) if ((mode & (EXPRmode | LHSmode)) == EXPRmode) => adapt(tree setType et.skolemizeExistential(context.owner, tree), mode, pt, original) - case PolyType(tparams, restpe) if inNoModes(mode, TAPPmode | PATTERNmode | HKmode) => // (3) + case PolyType(tparams, restpe) if mode.inNone(TAPPmode | PATTERNmode | HKmode) => // (3) // assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function, // we're in HKmode since a higher-kinded type is expected --> hence, don't implicitly apply it to type params! // ticket #2197 triggered turning the assert into a guard @@ -1116,18 +1071,19 @@ trait Typers extends Modes with Adaptations with Tags { adaptToImplicitMethod(mt) case mt: MethodType if (((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) && - (context.undetparams.isEmpty || inPolyMode(mode))) && !(tree.symbol != null && tree.symbol.isTermMacro) => + (context.undetparams.isEmpty || mode.inPolyMode)) && !treeInfo.isMacroApplicationOrBlock(tree) => instantiateToMethodType(mt) case _ => - def shouldInsertApply(tree: Tree) = inAllModes(mode, EXPRmode | FUNmode) && (tree.tpe match { + def vanillaAdapt(tree: Tree) = { + def shouldInsertApply(tree: Tree) = mode.inAll(EXPRmode | FUNmode) && (tree.tpe match { case _: MethodType | _: OverloadedType | _: PolyType => false case _ => applyPossible }) def applyPossible = { def applyMeth = member(adaptToName(tree, nme.apply), nme.apply) dyna.acceptsApplyDynamic(tree.tpe) || ( - if ((mode & TAPPmode) != 0) + if (mode.inAll(TAPPmode)) tree.tpe.typeParams.isEmpty && applyMeth.filter(!_.tpe.typeParams.isEmpty) != NoSymbol else applyMeth.filter(_.tpe.paramSectionCount > 0) != NoSymbol @@ -1135,18 +1091,13 @@ trait Typers extends Modes with Adaptations with Tags { } if (tree.isType) adaptType() - else if ( - inExprModeButNot(mode, FUNmode) && !tree.isDef && // typechecking application - tree.symbol != null && tree.symbol.isTermMacro && // of a macro - !tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined) - macroExpand(this, tree, mode, pt) - else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) + else if (mode.inAll(PATTERNmode | FUNmode)) adaptConstrPattern() else if (shouldInsertApply(tree)) insertApply() - else if (!context.undetparams.isEmpty && !inPolyMode(mode)) { // (9) - assert(!inHKMode(mode), modeString(mode)) //@M - if (inExprModeButNot(mode, FUNmode) && pt.typeSymbol == UnitClass) + else if (!context.undetparams.isEmpty && !mode.inPolyMode) { // (9) + assert(!mode.inHKMode, mode) //@M + if (mode.inExprModeButNot(FUNmode) && pt.typeSymbol == UnitClass) instantiateExpectingUnit(tree, mode) else instantiate(tree, mode, pt) @@ -1154,7 +1105,7 @@ trait Typers extends Modes with Adaptations with Tags { tree } else { def fallBack: Tree = { - if (inPatternMode(mode)) { + if (mode.inPatternMode) { if ((tree.symbol ne null) && tree.symbol.isModule) inferModulePattern(tree, pt) if (isPopulated(tree.tpe, approximateAbstracts(pt))) @@ -1163,7 +1114,7 @@ trait Typers extends Modes with Adaptations with Tags { val tree1 = constfold(tree, pt) // (10) (11) if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original) else { - if (inExprModeButNot(mode, FUNmode)) { + if (mode.inExprModeButNot(FUNmode)) { pt.dealias match { case TypeRef(_, sym, _) => // note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially @@ -1192,13 +1143,6 @@ trait Typers extends Modes with Adaptations with Tags { // (14); the condition prevents chains of views debuglog("inferring view from " + tree.tpe + " to " + pt) val coercion = inferView(tree, tree.tpe, pt, true) - // convert forward views of delegate types into closures wrapped around - // the delegate's apply method (the "Invoke" method, which was translated into apply) - if (forMSIL && coercion != null && isCorrespondingDelegate(tree.tpe, pt)) { - val meth: Symbol = tree.tpe.member(nme.apply) - debuglog("replacing forward delegate view with: " + meth + ":" + meth.tpe) - return typed(Select(tree, meth), mode, pt) - } if (coercion != EmptyTree) { def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe if (settings.logImplicitConv.value) @@ -1220,9 +1164,9 @@ trait Typers extends Modes with Adaptations with Tags { val found = tree.tpe if (!found.isErroneous && !pt.isErroneous) { if ((!context.reportErrors && isPastTyper) || tree.attachments.get[MacroExpansionAttachment].isDefined) { - val (bound, req) = pt match { - case ExistentialType(qs, tpe) => (qs, tpe) - case _ => (Nil, pt) + val bound = pt match { + case ExistentialType(qs, _) => qs + case _ => Nil } val boundOrSkolems = bound ++ pt.skolemsExceptMethodTypeParams if (boundOrSkolems.nonEmpty) { @@ -1276,9 +1220,12 @@ trait Typers extends Modes with Adaptations with Tags { fallBack } } + val tree1 = if (mode.inExprModeButNot(FUNmode) && treeInfo.isMacroApplication(tree)) macroExpandApply(this, tree, mode, pt) else tree + if (tree == tree1) vanillaAdapt(tree1) else tree1 + } } - def instantiate(tree: Tree, mode: Int, pt: Type): Tree = { + def instantiate(tree: Tree, mode: Mode, pt: Type): Tree = { inferExprInstance(tree, context.extractUndetparams(), pt) adapt(tree, mode, pt) } @@ -1286,11 +1233,9 @@ trait Typers extends Modes with Adaptations with Tags { * with expected type Unit, but if that fails, try again with pt = WildcardType * and discard the expression. */ - def instantiateExpectingUnit(tree: Tree, mode: Int): Tree = { + def instantiateExpectingUnit(tree: Tree, mode: Mode): Tree = { val savedUndetparams = context.undetparams - silent(_.instantiate(tree, mode, UnitClass.tpe)) match { - case SilentResultValue(t) => t - case _ => + silent(_.instantiate(tree, mode, UnitClass.tpe)) orElse { _ => context.undetparams = savedUndetparams val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant()))) typed(valueDiscard, mode, UnitClass.tpe) @@ -1350,45 +1295,38 @@ trait Typers extends Modes with Adaptations with Tags { def doAdapt(restpe: Type) = //util.trace("adaptToArgs "+qual+", name = "+name+", argtpes = "+(args map (_.tpe))+", pt = "+pt+" = ") adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe), reportAmbiguous, saveErrors) - if (pt != WildcardType) { - silent(_ => doAdapt(pt)) match { - case SilentResultValue(result) if result != qual => - result - case _ => - debuglog("fallback on implicits in adaptToArguments: "+qual+" . "+name) - doAdapt(WildcardType) - } - } else + + if (pt == WildcardType) doAdapt(pt) + else silent(_ => doAdapt(pt)) filter (_ != qual) orElse (_ => + logResult(s"fallback on implicits in adaptToArguments: $qual.$name")(doAdapt(WildcardType)) + ) } /** Try to apply an implicit conversion to `qual` so that it contains * a method `name`. If that's ambiguous try taking arguments into * account using `adaptToArguments`. */ - def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Int, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { - def onError(reportError: => Tree): Tree = { - context.tree match { + def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def onError(reportError: => Tree): Tree = context.tree match { case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => - silent(_.typedArgs(args, mode)) match { - case SilentResultValue(xs) => - val args = xs.asInstanceOf[List[Tree]] - if (args exists (_.isErrorTyped)) - reportError - else - adaptToArguments(qual, name, args, WildcardType, reportAmbiguous, saveErrors) + ( silent (_.typedArgs(args, mode)) + map (_.asInstanceOf[List[Tree]]) + filter (xs => !(xs exists (_.isErrorTyped))) + map (xs => adaptToArguments(qual, name, xs, WildcardType, reportAmbiguous, saveErrors)) + orElse ( _ => reportError) + ) case _ => reportError } - case _ => - reportError + + silent(_.adaptToMember(qual, HasMember(name), false)) orElse (err => + onError { + if (reportAmbiguous) context issue err + setError(tree) } + ) } - silent(_.adaptToMember(qual, HasMember(name), false)) match { - case SilentResultValue(res) => res - case SilentTypeError(err) => onError({if (reportAmbiguous) { context.issue(err) }; setError(tree)}) - } - } /** Try to apply an implicit conversion to `qual` to that it contains a * member `name` of arbitrary type. @@ -1398,13 +1336,6 @@ trait Typers extends Modes with Adaptations with Tags { if (member(qual, name) != NoSymbol) qual else adaptToMember(qual, HasMember(name)) - private def typePrimaryConstrBody(clazz : Symbol, cbody: Tree, tparams: List[Symbol], enclTparams: List[Symbol], vparamss: List[List[ValDef]]): Tree = { - // XXX: see about using the class's symbol.... - enclTparams foreach (sym => context.scope.enter(sym)) - namer.enterValueParams(vparamss) - typed(cbody) - } - private def validateNoCaseAncestor(clazz: Symbol) = { if (!phase.erasedTypes) { for (ancestor <- clazz.ancestors find (_.isCase)) { @@ -1506,126 +1437,246 @@ trait Typers extends Modes with Adaptations with Tags { unit.error(tparam.pos, "type parameter of value class may not be specialized") } - def parentTypes(templ: Template): List[Tree] = - if (templ.parents.isEmpty) List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) - else try { - val clazz = context.owner - // Normalize supertype and mixins so that supertype is always a class, not a trait. - var supertpt = typedTypeConstructor(templ.parents.head) - val firstParent = supertpt.tpe.typeSymbol - var mixins = templ.parents.tail map typedType - // If first parent is a trait, make it first mixin and add its superclass as first parent - while ((supertpt.tpe.typeSymbol ne null) && supertpt.tpe.typeSymbol.initialize.isTrait) { - val supertpt1 = typedType(supertpt) - if (!supertpt1.isErrorTyped) { - mixins = supertpt1 :: mixins - supertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + /** Typechecks a parent type reference. + * + * This typecheck is harder than it might look, because it should honor early + * definitions and also perform type argument inference with the help of super call + * arguments provided in `encodedtpt`. + * + * The method is called in batches (batch = 1 time per each parent type referenced), + * two batches per definition: once from namer, when entering a ClassDef or a ModuleDef + * and once from typer, when typechecking the definition. + * + * ***Arguments*** + * + * `encodedtpt` represents the parent type reference wrapped in an `Apply` node + * which indicates value arguments (i.e. type macro arguments or super constructor call arguments) + * If no value arguments are provided by the user, the `Apply` node is still + * there, but its `args` will be set to `Nil`. + * This argument is synthesized by `tools.nsc.ast.Parsers.templateParents`. + * + * `templ` is an enclosing template, which contains a primary constructor synthesized by the parser. + * Such a constructor is a DefDef which contains early initializers and maybe a super constructor call + * (I wrote "maybe" because trait constructors don't call super constructors). + * This argument is synthesized by `tools.nsc.ast.Trees.Template`. + * + * `inMixinPosition` indicates whether the reference is not the first in the + * list of parents (and therefore cannot be a class) or the opposite. + * + * ***Return value and side effects*** + * + * Returns a `TypeTree` representing a resolved parent type. + * If the typechecked parent reference implies non-nullary and non-empty argument list, + * this argument list is attached to the returned value in SuperArgsAttachment. + * The attachment is necessary for the subsequent typecheck to fixup a super constructor call + * in the body of the primary constructor (see `typedTemplate` for details). + * + * This method might invoke `typedPrimaryConstrBody`, hence it might cause the side effects + * described in the docs of that method. It might also attribute the Super(_, _) reference + * (if present) inside the primary constructor of `templ`. + * + * ***Example*** + * + * For the following definition: + * + * class D extends { + * val x = 2 + * val y = 4 + * } with B(x)(3) with C(y) with T + * + * this method will be called six times: + * + * (3 times from the namer) + * typedParentType(Apply(Apply(Ident(B), List(Ident(x))), List(3)), templ, inMixinPosition = false) + * typedParentType(Apply(Ident(C), List(Ident(y))), templ, inMixinPosition = true) + * typedParentType(Apply(Ident(T), List()), templ, inMixinPosition = true) + * + * (3 times from the typer) + * <the same three calls> + */ + private def typedParentType(encodedtpt: Tree, templ: Template, inMixinPosition: Boolean): Tree = { + val app = treeInfo.dissectApplied(encodedtpt) + val (treeInfo.Applied(core, targs, argss), decodedtpt) = (app, app.callee) + val argssAreTrivial = argss == Nil || argss == ListOfNil + + // we cannot avoid cyclic references with `initialize` here, because when type macros arrive, + // we'll have to check the probe for isTypeMacro anyways. + // therefore I think it's reasonable to trade a more specific "inherits itself" error + // for a generic, yet understandable "cyclic reference" error + var probe = typedTypeConstructor(core.duplicate).tpe.typeSymbol + if (probe == null) probe = NoSymbol + probe.initialize + + if (probe.isTrait || inMixinPosition) { + if (!argssAreTrivial) { + if (probe.isTrait) ConstrArgsInParentWhichIsTraitError(encodedtpt, probe) + else () // a class in a mixin position - this warrants an error in `validateParentClasses` + // therefore here we do nothing, e.g. don't check that the # of ctor arguments + // matches the # of ctor parameters or stuff like that + } + typedType(decodedtpt) + } else { + var supertpt = typedTypeConstructor(decodedtpt) + val supertparams = if (supertpt.hasSymbolField) supertpt.symbol.typeParams else Nil + if (supertparams.nonEmpty) { + typedPrimaryConstrBody(templ) { + val supertpe = PolyType(supertparams, appliedType(supertpt.tpe, supertparams map (_.tpeHK))) + val supercall = New(supertpe, mmap(argss)(_.duplicate)) + val treeInfo.Applied(Select(ctor, nme.CONSTRUCTOR), _, _) = supercall + ctor setType supertpe // this is an essential hack, otherwise it will occasionally fail to typecheck + atPos(supertpt.pos.focus)(supercall) + } match { + case EmptyTree => MissingTypeArgumentsParentTpeError(supertpt) + case tpt => supertpt = TypeTree(tpt.tpe) setPos supertpt.pos.focus } } - if (supertpt.tpe.typeSymbol == AnyClass && firstParent.isTrait) - supertpt.tpe = AnyRefClass.tpe - - // Determine - // - supertparams: Missing type parameters from supertype - // - supertpe: Given supertype, polymorphic in supertparams - val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else List() - var supertpe = supertpt.tpe - if (!supertparams.isEmpty) - supertpe = PolyType(supertparams, appliedType(supertpe, supertparams map (_.tpeHK))) - - // A method to replace a super reference by a New in a supercall - def transformSuperCall(scall: Tree): Tree = (scall: @unchecked) match { - case Apply(fn, args) => - treeCopy.Apply(scall, transformSuperCall(fn), args map (_.duplicate)) - case Select(Super(_, _), nme.CONSTRUCTOR) => - treeCopy.Select( - scall, - atPos(supertpt.pos.focus)(New(TypeTree(supertpe)) setType supertpe), - nme.CONSTRUCTOR) - } + // this is the place where we tell the typer what argss should be used for the super call + // if argss are nullary or empty, then (see the docs for `typedPrimaryConstrBody`) + // the super call dummy is already good enough, so we don't need to do anything + if (argssAreTrivial) supertpt else supertpt updateAttachment SuperArgsAttachment(argss) + } + } - treeInfo.firstConstructor(templ.body) match { - case constr @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => - // Convert constructor body to block in environment and typecheck it - val (preSuperStats, superCall) = { - val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) - (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) - } - val cstats1 = if (superCall == EmptyTree) preSuperStats else preSuperStats :+ superCall - val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall match { - case Apply(_, _) if supertparams.nonEmpty => transformSuperCall(superCall) - case _ => cunit.duplicate - }) - val outercontext = context.outer - - assert(clazz != NoSymbol, templ) - val cscope = outercontext.makeNewScope(constr, outercontext.owner) - val cbody2 = newTyper(cscope) // called both during completion AND typing. - .typePrimaryConstrBody(clazz, - cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate))) - - superCall match { - case Apply(_, _) => - val treeInfo.Applied(_, _, argss) = superCall - val sarg = argss.flatten.headOption.getOrElse(EmptyTree) - if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) - ConstrArgsInTraitParentTpeError(sarg, firstParent) - if (!supertparams.isEmpty) - supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus - case _ => - if (!supertparams.isEmpty) - MissingTypeArgumentsParentTpeError(supertpt) - } + /** Typechecks the mishmash of trees that happen to be stuffed into the primary constructor of a given template. + * Before commencing the typecheck, replaces the `pendingSuperCall` dummy with the result of `actualSuperCall`. + * `actualSuperCall` can return `EmptyTree`, in which case the dummy is replaced with a literal unit. + * + * ***Return value and side effects*** + * + * If a super call is present in the primary constructor and is not erased by the transform, returns it typechecked. + * Otherwise (e.g. if the primary constructor is missing or the super call isn't there) returns `EmptyTree`. + * + * As a side effect, this method attributes the underlying fields of early vals. + * Early vals aren't typechecked anywhere else, so it's essential to call `typedPrimaryConstrBody` + * at least once per definition. It'd be great to disentangle this logic at some point. + * + * ***Example*** + * + * For the following definition: + * + * class D extends { + * val x = 2 + * val y = 4 + * } with B(x)(3) with C(y) with T + * + * the primary constructor of `templ` will be: + * + * Block(List( + * ValDef(NoMods, x, TypeTree(), 2) + * ValDef(NoMods, y, TypeTree(), 4) + * global.pendingSuperCall, + * Literal(Constant(()))) + * + * Note the `pendingSuperCall` part. This is the representation of a fill-me-in-later supercall dummy, + * which encodes the fact that supercall argss are unknown during parsing and need to be transplanted + * from one of the parent types. Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`. + */ + private def typedPrimaryConstrBody(templ: Template)(actualSuperCall: => Tree): Tree = + treeInfo.firstConstructor(templ.body) match { + case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => + val (preSuperStats, superCall) = { + val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) + (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) + } + val superCall1 = (superCall match { + case global.pendingSuperCall => actualSuperCall + case EmptyTree => EmptyTree + }) orElse cunit + val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1) + val clazz = context.owner + assert(clazz != NoSymbol, templ) + val cscope = context.outer.makeNewScope(ctor, context.outer.owner) + val cbody2 = { // called both during completion AND typing. + val typer1 = newTyper(cscope) + // XXX: see about using the class's symbol.... + clazz.unsafeTypeParams foreach (sym => typer1.context.scope.enter(sym)) + typer1.namer.enterValueParams(vparamss map (_.map(_.duplicate))) + typer1.typed(cbody1) + } - val preSuperVals = treeInfo.preSuperFields(templ.body) - if (preSuperVals.isEmpty && preSuperStats.nonEmpty) - debugwarn("Wanted to zip empty presuper val list with " + preSuperStats) - else - map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) + val preSuperVals = treeInfo.preSuperFields(templ.body) + if (preSuperVals.isEmpty && preSuperStats.nonEmpty) + devWarning("Wanted to zip empty presuper val list with " + preSuperStats) + else + map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt setType ldef.symbol.tpe) - case _ => - if (!supertparams.isEmpty) - MissingTypeArgumentsParentTpeError(supertpt) - } -/* experimental: early types as type arguments - val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef) - val earlyMap = new EarlyMap(clazz) - List.mapConserve(supertpt :: mixins){ tpt => - val tpt1 = checkNoEscaping.privates(clazz, tpt) - if (hasEarlyTypes) tpt1 else tpt1 setType earlyMap(tpt1.tpe) + if (superCall1 == cunit) EmptyTree + else cbody2 match { + case Block(_, expr) => expr + case tree => tree + } + case _ => + EmptyTree + } + + /** Makes sure that the first type tree in the list of parent types is always a class. + * If the first parent is a trait, prepend its supertype to the list until it's a class. + */ + private def normalizeFirstParent(parents: List[Tree]): List[Tree] = parents match { + case first :: rest if treeInfo.isTraitRef(first) => + def explode(supertpt: Tree, acc: List[Tree]): List[Tree] = { + if (treeInfo.isTraitRef(supertpt)) { + val supertpt1 = typedType(supertpt) + if (!supertpt1.isErrorTyped) { + val supersupertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + return explode(supersupertpt, supertpt1 :: acc) + } + } + if (supertpt.tpe.typeSymbol == AnyClass) supertpt setType AnyRefClass.tpe + supertpt :: acc } -*/ + explode(first, Nil) ++ rest + case _ => parents + } - //Console.println("parents("+clazz") = "+supertpt :: mixins);//DEBUG + /** Certain parents are added in the parser before it is known whether + * that class also declared them as parents. For instance, this is an + * error unless we take corrective action here: + * + * case class Foo() extends Serializable + * + * So we strip the duplicates before typer. + */ + private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match { + case Nil => Nil + case x :: xs => + val sym = x.symbol + x :: fixDuplicateSyntheticParents( + if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) + else xs + ) + } - // Certain parents are added in the parser before it is known whether - // that class also declared them as parents. For instance, this is an - // error unless we take corrective action here: - // - // case class Foo() extends Serializable - // - // So we strip the duplicates before typer. - def fixDuplicates(remaining: List[Tree]): List[Tree] = remaining match { - case Nil => Nil - case x :: xs => - val sym = x.symbol - x :: fixDuplicates( - if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) - else xs - ) + def typedParentTypes(templ: Template): List[Tree] = templ.parents match { + case Nil => List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) + case first :: rest => + try { + val supertpts = fixDuplicateSyntheticParents(normalizeFirstParent( + typedParentType(first, templ, inMixinPosition = false) +: + (rest map (typedParentType(_, templ, inMixinPosition = true))))) + + // if that is required to infer the targs of a super call + // typedParentType calls typedPrimaryConstrBody to do the inferring typecheck + // as a side effect, that typecheck also assigns types to the fields underlying early vals + // however if inference is not required, the typecheck doesn't happen + // and therefore early fields have their type trees not assigned + // here we detect this situation and take preventive measures + if (treeInfo.hasUntypedPreSuperFields(templ.body)) + typedPrimaryConstrBody(templ)(EmptyTree) + + supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) + } catch { + case ex: TypeError => + // fallback in case of cyclic errors + // @H none of the tests enter here but I couldn't rule it out + // upd. @E when a definition inherits itself, we end up here + // because `typedParentType` triggers `initialize` for parent types symbols + log("Type error calculating parents in template " + templ) + log("Error: " + ex) + ParentTypesError(templ, ex) + List(TypeTree(AnyRefClass.tpe)) } - - fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, tpt)) - } - catch { - case ex: TypeError => - // fallback in case of cyclic errors - // @H none of the tests enter here but I couldn't rule it out - log("Type error calculating parents in template " + templ) - log("Error: " + ex) - ParentTypesError(templ, ex) - List(TypeTree(AnyRefClass.tpe)) - } + } /** <p>Check that</p> * <ul> @@ -1684,9 +1735,6 @@ trait Typers extends Modes with Adaptations with Tags { !selfType.isErroneous && !parent.tpe.isErroneous) { - //Console.println(context.owner);//DEBUG - //Console.println(context.owner.unsafeTypeParams);//DEBUG - //Console.println(List.fromArray(context.owner.info.closure));//DEBUG pending += ParentSelfTypeConformanceError(parent, selfType) if (settings.explaintypes.value) explainTypes(selfType, parent.tpe.typeOfThis) } @@ -1702,13 +1750,6 @@ trait Typers extends Modes with Adaptations with Tags { for (p <- parents) validateParentClass(p, superclazz) } -/* - if (settings.Xshowcls.value != "" && - settings.Xshowcls.value == context.owner.fullName) - println("INFO "+context.owner+ - ", baseclasses = "+(context.owner.info.baseClasses map (_.fullName))+ - ", lin = "+(context.owner.info.baseClasses map (context.owner.thisType.baseType))) -*/ pending.foreach(ErrorUtils.issueTypeError) } @@ -1732,29 +1773,28 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** - * @param cdef ... - * @return ... - */ def typedClassDef(cdef: ClassDef): Tree = { -// attributes(cdef) val clazz = cdef.symbol val typedMods = typedModifiers(cdef.mods) assert(clazz != NoSymbol, cdef) reenterTypeParams(cdef.tparams) val tparams1 = cdef.tparams mapConserve (typedTypeDef) val impl1 = typerReportAnyContextErrors(context.make(cdef.impl, clazz, newScope)) { - _.typedTemplate(cdef.impl, parentTypes(cdef.impl)) + _.typedTemplate(cdef.impl, typedParentTypes(cdef.impl)) } val impl2 = finishMethodSynthesis(impl1, clazz, context) if (clazz.isTrait && clazz.info.parents.nonEmpty && clazz.info.firstParent.typeSymbol == AnyClass) checkEphemeral(clazz, impl2.body) - if ((clazz != ClassfileAnnotationClass) && - (clazz isNonBottomSubClass ClassfileAnnotationClass)) - restrictionWarning(cdef.pos, unit, - "subclassing Classfile does not\n"+ - "make your annotation visible at runtime. If that is what\n"+ - "you want, you must write the annotation class in Java.") + + if ((clazz isNonBottomSubClass ClassfileAnnotationClass) && (clazz != ClassfileAnnotationClass)) { + if (!clazz.owner.isPackageClass) + unit.error(clazz.pos, "inner classes cannot be classfile annotations") + else restrictionWarning(cdef.pos, unit, + """|subclassing Classfile does not + |make your annotation visible at runtime. If that is what + |you want, you must write the annotation class in Java.""".stripMargin) + } + if (!isPastTyper) { for (ann <- clazz.getAnnotation(DeprecatedAttr)) { val m = companionSymbolOf(clazz, context) @@ -1766,10 +1806,6 @@ trait Typers extends Modes with Adaptations with Tags { .setType(NoType) } - /** - * @param mdef ... - * @return ... - */ def typedModuleDef(mdef: ModuleDef): Tree = { // initialize all constructors of the linked class: the type completer (Namer.methodSig) // might add default getters to this object. example: "object T; class T(x: Int = 1)" @@ -1788,7 +1824,7 @@ trait Typers extends Modes with Adaptations with Tags { ) val impl1 = typerReportAnyContextErrors(context.make(mdef.impl, clazz, newScope)) { _.typedTemplate(mdef.impl, { - parentTypes(mdef.impl) ++ ( + typedParentTypes(mdef.impl) ++ ( if (noSerializable) Nil else { clazz.makeSerializable() @@ -1853,13 +1889,7 @@ trait Typers extends Modes with Adaptations with Tags { if (txt eq context) namer.enterSym(tree) else newNamer(txt).enterSym(tree) - /** - * @param templ ... - * @param parents1 ... - * <li> <!-- 2 --> - * Check that inner classes do not inherit from Annotation - * </li> - * @return ... + /** <!-- 2 --> Check that inner classes do not inherit from Annotation */ def typedTemplate(templ: Template, parents1: List[Tree]): Template = { val clazz = context.owner @@ -1890,19 +1920,34 @@ trait Typers extends Modes with Adaptations with Tags { // the following is necessary for templates generated later assert(clazz.info.decls != EmptyScope, clazz) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body) - validateParentClasses(parents1, selfType) + if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore + validateParentClasses(parents1, selfType) if (clazz.isCase) validateNoCaseAncestor(clazz) + if (clazz.isTrait && hasSuperArgs(parents1.head)) + ConstrArgsInParentOfTraitError(parents1.head, clazz) - if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass) + if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.isTopLevel) unit.error(clazz.pos, "inner classes cannot be classfile annotations") if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) - val body = - if (isPastTyper || reporter.hasErrors) templ.body - else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) + val body = { + val body = + if (isPastTyper || reporter.hasErrors) templ.body + else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) + val primaryCtor = treeInfo.firstConstructor(body) + val primaryCtor1 = primaryCtor match { + case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) => + val argss = superArgs(parents1.head) getOrElse Nil + val pos = wrappingPos(parents1.head.pos, argss.flatten) + val superCall = atPos(pos)(PrimarySuperCall(argss)) + deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos + case _ => primaryCtor + } + body mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat } + } val body1 = typedStats(body, templ.symbol) @@ -1915,7 +1960,7 @@ trait Typers extends Modes with Adaptations with Tags { } } - treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe + treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe_* } /** Remove definition annotations from modifiers (they have been saved @@ -1933,10 +1978,6 @@ trait Typers extends Modes with Adaptations with Tags { def typedModifiers(mods: Modifiers): Modifiers = mods.copy(annotations = Nil) setPositions mods.positions - /** - * @param vdef ... - * @return ... - */ def typedValDef(vdef: ValDef): ValDef = { val sym = vdef.symbol val valDefTyper = { @@ -1958,7 +1999,7 @@ trait Typers extends Modes with Adaptations with Tags { checkNonCyclic(vdef, tpt1) if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable) - VolatileValueError(vdef) + VolatileValueError(vdef) val rhs1 = if (vdef.rhs.isEmpty) { @@ -1988,10 +2029,6 @@ trait Typers extends Modes with Adaptations with Tags { } /** Enter all aliases of local parameter accessors. - * - * @param clazz ... - * @param vparamss ... - * @param rhs ... */ def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) { debuglog(s"computing param aliases for $clazz:${clazz.primaryConstructor.tpe}:$rhs") @@ -2124,7 +2161,7 @@ trait Typers extends Modes with Adaptations with Tags { foreachWithIndex(paramssTypes(meth.tpe)) { (paramList, listIdx) => foreachWithIndex(paramList) { (paramType, paramIdx) => - val sym = paramType.typeSymbol + val sym = paramType.typeSymbol def paramPos = nthParamPos(listIdx, paramIdx) /** Not enough to look for abstract types; have to recursively check the bounds @@ -2139,66 +2176,21 @@ trait Typers extends Modes with Adaptations with Tags { || (!sym.hasTransOwner(meth) && failStruct(paramPos, "a type member of that refinement", what)) || checkAbstract(sym.info.bounds.hi, "Type bound") ) - } + } tp0.dealiasWidenChain forall (t => check(t.typeSymbol)) } checkAbstract(paramType, "Parameter type") - if (sym.isDerivedValueClass) - failStruct(paramPos, "a user-defined value class") - if (paramType.isInstanceOf[ThisType] && sym == meth.owner) - failStruct(paramPos, "the type of that refinement (self type)") - } + if (sym.isDerivedValueClass) + failStruct(paramPos, "a user-defined value class") + if (paramType.isInstanceOf[ThisType] && sym == meth.owner) + failStruct(paramPos, "the type of that refinement (self type)") } + } if (resultType.typeSymbol.isDerivedValueClass) failStruct(ddef.tpt.pos, "a user-defined value class", where = "Result type") } - def typedUseCase(useCase: UseCase) { - def stringParser(str: String): syntaxAnalyzer.Parser = { - val file = new BatchSourceFile(context.unit.source.file, str) { - override def positionInUltimateSource(pos: Position) = { - pos.withSource(context.unit.source, useCase.pos.start) - } - } - val unit = new CompilationUnit(file) - new syntaxAnalyzer.UnitParser(unit) - } - val trees = stringParser(useCase.body+";").nonLocalDefOrDcl - val enclClass = context.enclClass.owner - def defineAlias(name: Name) = - if (context.scope.lookup(name) == NoSymbol) { - lookupVariable(name.toString.substring(1), enclClass) match { - case Some(repl) => - silent(_.typedTypeConstructor(stringParser(repl).typ())) match { - case SilentResultValue(tpt) => - val alias = enclClass.newAliasType(name.toTypeName, useCase.pos) - val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias) - val newInfo = genPolyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) - alias setInfo newInfo - context.scope.enter(alias) - case _ => - } - case _ => - } - } - for (tree <- trees; t <- tree) - t match { - case Ident(name) if name startsWith '$' => defineAlias(name) - case _ => - } - useCase.aliases = context.scope.toList - namer.enterSyms(trees) - typedStats(trees, NoSymbol) - useCase.defined = context.scope.toList filterNot (useCase.aliases contains _) - if (settings.debug.value) - useCase.defined foreach (sym => println("defined use cases: %s:%s".format(sym, sym.tpe))) - } - - /** - * @param ddef ... - * @return ... - */ def typedDefDef(ddef: DefDef): DefDef = { val meth = ddef.symbol.initialize @@ -2223,7 +2215,7 @@ trait Typers extends Modes with Adaptations with Tags { if (isRepeatedParamType(vparam1.symbol.tpe)) StarParamNotLastError(vparam1) - var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt)) + val tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt)) checkNonCyclic(ddef, tpt1) ddef.tpt.setType(tpt1.tpe) val typedMods = typedModifiers(ddef.mods) @@ -2235,7 +2227,7 @@ trait Typers extends Modes with Adaptations with Tags { meth.owner.isAnonOrRefinementClass)) InvalidConstructorDefError(ddef) typed(ddef.rhs) - } else if (meth.isTermMacro) { + } else if (meth.isMacro) { // 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)) @@ -2327,7 +2319,7 @@ trait Typers extends Modes with Adaptations with Tags { if (!nme.isLoopHeaderLabel(ldef.symbol.name) || isPastTyper) { val restpe = ldef.symbol.tpe.resultType val rhs1 = typed(ldef.rhs, restpe) - ldef.params foreach (param => param.tpe = param.symbol.tpe) + ldef.params foreach (param => param setType param.symbol.tpe) deriveLabelDef(ldef)(_ => rhs1) setType restpe } else { @@ -2335,26 +2327,20 @@ trait Typers extends Modes with Adaptations with Tags { val rhs1 = typed(ldef.rhs) val restpe = rhs1.tpe if (restpe == initpe) { // stable result, no need to check again - ldef.params foreach (param => param.tpe = param.symbol.tpe) + ldef.params foreach (param => param setType param.symbol.tpe) treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs1) setType restpe } else { context.scope.unlink(ldef.symbol) val sym2 = namer.enterInScope( context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(List(), restpe)) val rhs2 = typed(resetAllAttrs(ldef.rhs), restpe) - ldef.params foreach (param => param.tpe = param.symbol.tpe) + ldef.params foreach (param => param setType param.symbol.tpe) deriveLabelDef(ldef)(_ => rhs2) setSymbol sym2 setType restpe } } } - /** - * @param block ... - * @param mode ... - * @param pt ... - * @return ... - */ - def typedBlock(block: Block, mode: Int, pt: Type): Block = { + def typedBlock(block: Block, mode: Mode, pt: Type): Block = { val syntheticPrivates = new ListBuffer[Symbol] try { namer.enterSyms(block.stats) @@ -2416,7 +2402,7 @@ trait Typers extends Modes with Adaptations with Tags { case _ => stat::Nil }) val stats2 = typedStats(stats1, context.owner) - val expr1 = typed(block.expr, mode & ~(FUNmode | QUALmode), pt) + val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode), pt) treeCopy.Block(block, stats2, expr1) .setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst) } finally { @@ -2426,12 +2412,6 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** - * @param cdef ... - * @param pattpe ... - * @param pt ... - * @return ... - */ def typedCase(cdef: CaseDef, pattpe: Type, pt: Type): CaseDef = { // verify no _* except in last position for (Apply(_, xs) <- cdef.pat ; x <- xs dropRight 1 ; if treeInfo isStar x) @@ -2460,13 +2440,13 @@ trait Typers extends Modes with Adaptations with Tags { val contextWithTypeBounds = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) if (contextWithTypeBounds.savedTypeBounds.nonEmpty) { - body1.tpe = contextWithTypeBounds restoreTypeBounds body1.tpe + body1 modifyType (contextWithTypeBounds restoreTypeBounds _) // insert a cast if something typechecked under the GADT constraints, // but not in real life (i.e., now that's we've reset the method's type skolems' // infos back to their pre-GADT-constraint state) if (isFullyDefined(pt) && !(body1.tpe <:< pt)) - body1 = typedPos(body1.pos)(gen.mkCast(body1, pt.normalize)) + body1 = typedPos(body1.pos)(gen.mkCast(body1, pt.dealiasWiden)) } @@ -2474,57 +2454,37 @@ trait Typers extends Modes with Adaptations with Tags { treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe } - // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher - // the flags are used to avoid accidentally deskolemizing unrelated skolems of skolems - object deskolemizeGADTSkolems extends TypeMap { - def apply(tp: Type): Type = mapOver(tp) match { - case TypeRef(pre, sym, args) if sym.isGADTSkolem => - typeRef(NoPrefix, sym.deSkolemize, args) - case tp1 => tp1 - } - } - def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = cases mapConserve { cdef => newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) } - def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) + def adaptCase(cdef: CaseDef, mode: Mode, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) def ptOrLub(tps: List[Type], pt: Type ) = if (isFullyDefined(pt)) (pt, false) else weakLub(tps map (_.deconst)) def ptOrLubPacked(trees: List[Tree], pt: Type) = if (isFullyDefined(pt)) (pt, false) else weakLub(trees map (c => packedType(c, context.owner).deconst)) // takes untyped sub-trees of a match and type checks them - def typedMatch(selector: Tree, cases: List[CaseDef], mode: Int, pt: Type, tree: Tree = EmptyTree): Match = { + def typedMatch(selector: Tree, cases: List[CaseDef], mode: Mode, pt: Type, tree: Tree = EmptyTree): Match = { val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) val selectorTp = packCaptured(selector1.tpe.widen).skolemizeExistential(context.owner, selector) val casesTyped = typedCases(cases, selectorTp, pt) - val (resTp, needAdapt) = - if (opt.virtPatmat) ptOrLubPacked(casesTyped, pt) - else ptOrLub(casesTyped map (_.tpe), pt) + val (resTp, needAdapt) = ptOrLubPacked(casesTyped, pt) val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, resTp)) - val matchTyped = treeCopy.Match(tree, selector1, casesAdapted) setType resTp - if (!newPatternMatching) // TODO: remove this in 2.11 -- only needed for old pattern matcher - new TypeMapTreeSubstituter(deskolemizeGADTSkolems).traverse(matchTyped) - matchTyped + treeCopy.Match(tree, selector1, casesAdapted) setType resTp } - // match has been typed -- virtualize it if we're feeling experimental - // (virtualized matches are expanded during type checking so they have the full context available) - // otherwise, do nothing: matches are translated during phase `patmat` (unless -Xoldpatmat) - def virtualizedMatch(match_ : Match, mode: Int, pt: Type) = { - import patmat.{vpmName, PureMatchTranslator, OptimizingMatchTranslator} + // match has been typed -- virtualize it during type checking so the full context is available + def virtualizedMatch(match_ : Match, mode: Mode, pt: Type) = { + import patmat.{ vpmName, PureMatchTranslator } // TODO: add fallback __match sentinel to predef val matchStrategy: Tree = - if (!(newPatternMatching && opt.experimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen - else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { - case SilentResultValue(ms) => ms - case _ => null - } + if (!(newPatternMatching && settings.Xexperimental.value && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen + else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) orElse (_ => null) if (matchStrategy ne null) // virtualize typed((new PureMatchTranslator(this.asInstanceOf[patmat.global.analyzer.Typer] /*TODO*/, matchStrategy)).translateMatch(match_), mode, pt) @@ -2554,11 +2514,9 @@ trait Typers extends Modes with Adaptations with Tags { * an alternative TODO: add partial function AST node or equivalent and get rid of this synthesis --> do everything in uncurry (or later) * however, note that pattern matching codegen is designed to run *before* uncurry */ - def synthesizePartialFunction(paramName: TermName, paramPos: Position, tree: Tree, mode: Int, pt0: Type): Tree = { - assert(pt0.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt0.") - - val pt = deskolemizeGADTSkolems(pt0) - val targs = pt.normalize.typeArgs + def synthesizePartialFunction(paramName: TermName, paramPos: Position, tree: Tree, mode: Mode, pt: Type): Tree = { + assert(pt.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt.") + val targs = pt.dealiasWiden.typeArgs // if targs.head isn't fully defined, we can translate --> error targs match { @@ -2583,7 +2541,7 @@ trait Typers extends Modes with Adaptations with Tags { val Match(sel, cases) = tree // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up - val casesTrue = cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE_typed)).duplicate.asInstanceOf[CaseDef]) + val casesTrue = cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE)).duplicate.asInstanceOf[CaseDef]) // must generate a new tree every time def selector: Tree = gen.mkUnchecked( @@ -2702,7 +2660,7 @@ trait Typers extends Modes with Adaptations with Tags { methodBodyTyper.context.scope enter paramSym methodSym setInfo MethodType(List(paramSym), BooleanClass.tpe) - val defaultCase = mkDefaultCase(FALSE_typed) + val defaultCase = mkDefaultCase(FALSE) val match_ = methodBodyTyper.typedMatch(selector, casesTrue :+ defaultCase, mode, BooleanClass.tpe) DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, BooleanClass.tpe)) @@ -2750,7 +2708,7 @@ trait Typers extends Modes with Adaptations with Tags { members foreach (m => anonClass.info.decls enter m.symbol) val typedBlock = typedPos(tree.pos, mode, pt) { - Block(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)( + Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)( Apply(Select(New(Ident(anonClass.name).setSymbol(anonClass)), nme.CONSTRUCTOR), List()) )) } @@ -2769,17 +2727,17 @@ trait Typers extends Modes with Adaptations with Tags { * @param pt ... * @return ... */ - private def typedFunction(fun: Function, mode: Int, pt: Type): Tree = { + private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = { val numVparams = fun.vparams.length if (numVparams > definitions.MaxFunctionArity) return MaxFunctionArityError(fun) def decompose(pt: Type): (Symbol, List[Type], Type) = if ((isFunctionType(pt) || (pt.typeSymbol == PartialFunctionClass && numVparams == 1 && fun.body.isInstanceOf[Match])) && // see bug901 for a reason why next conditions are needed - ( pt.normalize.typeArgs.length - 1 == numVparams + ( pt.dealiasWiden.typeArgs.length - 1 == numVparams || fun.vparams.exists(_.tpt.isEmpty) )) - (pt.typeSymbol, pt.normalize.typeArgs.init, pt.normalize.typeArgs.last) + (pt.typeSymbol, pt.dealiasWiden.typeArgs.init, pt.dealiasWiden.typeArgs.last) else (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) @@ -2794,15 +2752,13 @@ trait Typers extends Modes with Adaptations with Tags { else { fun match { case etaExpansion(vparams, fn, args) => - silent(_.typed(fn, forFunMode(mode), pt)) match { - case SilentResultValue(fn1) if context.undetparams.isEmpty => + silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 => // if context,undetparams is not empty, the function was polymorphic, // so we need the missing arguments to infer its type. See #871 //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams) if (isFunctionType(ftpe) && isFullyDefined(ftpe)) return typedFunction(fun, mode, ftpe) - case _ => } case _ => } @@ -2831,16 +2787,13 @@ trait Typers extends Modes with Adaptations with Tags { 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 vparams = fun.vparams mapConserve typedValDef 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 funtpe = appliedType(clazz, formals :+ restpe: _*) + + treeCopy.Function(fun, vparams, body1) setType funtpe } } } @@ -2858,31 +2811,15 @@ trait Typers extends Modes with Adaptations with Tags { val att = templ.attachments.get[CompoundTypeTreeOriginalAttachment].getOrElse(CompoundTypeTreeOriginalAttachment(Nil, Nil)) templ.removeAttachment[CompoundTypeTreeOriginalAttachment] templ updateAttachment att.copy(stats = stats1) - for (stat <- stats1 if stat.isDef) { - val member = stat.symbol - if (!(context.owner.ancestors forall - (bc => member.matchingSymbol(bc, context.owner.thisType) == NoSymbol))) { - member setFlag OVERRIDE + for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol) + stat.symbol setFlag OVERRIDE } } - } - } def typedImport(imp : Import) : Import = (transformed remove imp) match { case Some(imp1: Import) => imp1 case _ => log("unhandled import: "+imp+" in "+unit); imp } - private def isWarnablePureExpression(tree: Tree) = tree match { - case EmptyTree | Literal(Constant(())) => false - case _ => - !tree.isErrorTyped && (treeInfo isExprSafeToInline tree) && { - val sym = tree.symbol - (sym == null) || !(sym.isModule || sym.isLazy) || { - debuglog("'Pure' but side-effecting expression in statement position: " + tree) - false - } - } - } def typedStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { val inBlock = exprOwner == context.owner @@ -2919,7 +2856,7 @@ trait Typers extends Modes with Adaptations with Tags { ConstructorsOrderError(stat) } - if (isWarnablePureExpression(result)) context.warning(stat.pos, + if (treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, "a pure expression does nothing in statement position; " + "you may be omitting necessary parentheses" ) @@ -2970,7 +2907,7 @@ trait Typers extends Modes with Adaptations with Tags { // SI-5877 The decls of a package include decls of the package object. But we don't want to add // the corresponding synthetics to the package class, only to the package object class. def shouldAdd(sym: Symbol) = - inBlock || !isInPackageObject(sym, context.owner) + inBlock || !context.isInPackageObject(sym, context.owner) for (sym <- scope if shouldAdd(sym)) for (tree <- context.unit.synthetics get sym) { newStats += typedStat(tree) // might add even more synthetics to the scope @@ -3023,14 +2960,14 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedArg(arg: Tree, mode: Int, newmode: Int, pt: Type): Tree = { - val typedMode = onlyStickyModes(mode) | newmode - val t = withCondConstrTyper((mode & SCCmode) != 0)(_.typed(arg, typedMode, pt)) + def typedArg(arg: Tree, mode: Mode, newmode: Mode, pt: Type): Tree = { + val typedMode = mode.onlySticky | newmode + val t = withCondConstrTyper((mode & SCCmode) != NOmode)(_.typed(arg, typedMode, pt)) checkDead.inMode(typedMode, t) } - def typedArgs(args: List[Tree], mode: Int) = - args mapConserve (arg => typedArg(arg, mode, 0, WildcardType)) + def typedArgs(args: List[Tree], mode: Mode) = + args mapConserve (arg => typedArg(arg, mode, NOmode, WildcardType)) /** Type trees in `args0` against corresponding expected type in `adapted0`. * @@ -3040,8 +2977,8 @@ trait Typers extends Modes with Adaptations with Tags { * * (docs reverse-engineered -- AM) */ - def typedArgs(args0: List[Tree], mode: Int, formals0: List[Type], adapted0: List[Type]): List[Tree] = { - val sticky = onlyStickyModes(mode) + def typedArgs(args0: List[Tree], mode: Mode, formals0: List[Type], adapted0: List[Type]): List[Tree] = { + val sticky = mode.onlySticky def loop(args: List[Tree], formals: List[Type], adapted: List[Type]): List[Tree] = { if (args.isEmpty || adapted.isEmpty) Nil else { @@ -3049,11 +2986,10 @@ trait Typers extends Modes with Adaptations with Tags { val isVarArgs = formals.isEmpty || formals.tail.isEmpty && isRepeatedParamType(formals.head) val typedMode = sticky | ( if (isVarArgs) STARmode | BYVALmode - else if (isByNameParamType(formals.head)) 0 + else if (isByNameParamType(formals.head)) NOmode else BYVALmode ) 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) } @@ -3101,18 +3037,18 @@ trait Typers extends Modes with Adaptations with Tags { } } - def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { // TODO_NMT: check the assumption that args nonEmpty def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } def preSelectOverloaded(fun: Tree): Tree = { - if (fun.hasSymbol && fun.symbol.isOverloaded) { + if (fun.hasSymbolField && fun.symbol.isOverloaded) { // remove alternatives with wrong number of parameters without looking at types. - // less expensive than including them in inferMethodAlternatvie (see below). + // less expensive than including them in inferMethodAlternative (see below). def shapeType(arg: Tree): Type = arg match { case Function(vparams, body) => - functionType(vparams map (vparam => AnyClass.tpe), shapeType(body)) + functionType(vparams map (_ => AnyClass.tpe), shapeType(body)) case AssignOrNamedArg(Ident(name), rhs) => NamedType(name, shapeType(rhs)) case _ => @@ -3120,7 +3056,6 @@ trait Typers extends Modes with Adaptations with Tags { } val argtypes = args map shapeType val pre = fun.symbol.tpe.prefix - var sym = fun.symbol filter { alt => // must use pt as expected type, not WildcardType (a tempting quick fix to #2665) // now fixed by using isWeaklyCompatible in exprTypeArgs @@ -3132,20 +3067,19 @@ trait Typers extends Modes with Adaptations with Tags { // Types: "refs = Array(Map(), Map())". I determined that inference fails if there are at // least two invariant type parameters. See the test case I checked in to help backstop: // pos/isApplicableSafe.scala. - isApplicableSafe(context.undetparams, followApply(pre.memberType(alt)), argtypes, pt) + isApplicableSafe(context.undetparams, followApply(pre memberType alt), argtypes, pt) } if (sym.isOverloaded) { - val sym1 = sym filter (alt => { // eliminate functions that would result from tupling transforms // keeps alternatives with repeated params - hasExactlyNumParams(followApply(alt.tpe), argtypes.length) || - // also keep alts which define at least one default - alt.tpe.paramss.exists(_.exists(_.hasDefault)) - }) + val sym1 = sym filter (alt => + isApplicableBasedOnArity(pre memberType alt, argtypes.length, varargsStar = false, tuplingAllowed = false) + || alt.tpe.params.exists(_.hasDefault) + ) if (sym1 != NoSymbol) sym = sym1 } if (sym == NoSymbol) fun - else adapt(fun setSymbol sym setType pre.memberType(sym), forFunMode(mode), WildcardType) + else adapt(fun setSymbol sym setType pre.memberType(sym), mode.forFunMode, WildcardType) } else fun } @@ -3155,7 +3089,6 @@ trait Typers extends Modes with Adaptations with Tags { case OverloadedType(pre, alts) => def handleOverloaded = { val undetparams = context.extractUndetparams() - val argtpes = new ListBuffer[Type] val amode = forArgMode(fun, mode) val args1 = args map { @@ -3164,7 +3097,11 @@ trait Typers extends Modes with Adaptations with Tags { val rhs1 = typedArg(rhs, amode, BYVALmode, WildcardType) argtpes += NamedType(name, rhs1.tpe.deconst) // the assign is untyped; that's ok because we call doTypedApply - atPos(arg.pos) { new AssignOrNamedArg(arg.lhs, rhs1) } + treeCopy.AssignOrNamedArg(arg, arg.lhs, rhs1) + case arg @ Typed(repeated, Ident(tpnme.WILDCARD_STAR)) => + val arg1 = typedArg(arg, amode, BYVALmode, WildcardType) + argtpes += RepeatedType(arg1.tpe.deconst) + arg1 case arg => val arg1 = typedArg(arg, amode, BYVALmode, WildcardType) argtpes += arg1.tpe.deconst @@ -3174,8 +3111,8 @@ trait Typers extends Modes with Adaptations with Tags { if (context.hasErrors) setError(tree) else { - inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) - doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + inferMethodAlternative(fun, undetparams, argtpes.toList, pt) + doTypedApply(tree, adapt(fun, mode.forFunMode, WildcardType), args1, mode, pt) } } handleOverloaded @@ -3183,35 +3120,31 @@ trait Typers extends Modes with Adaptations with Tags { case mt @ MethodType(params, _) => val paramTypes = mt.paramTypes // repeat vararg as often as needed, remove by-name - val formals = formalTypes(paramTypes, args.length) + val argslen = args.length + val formals = formalTypes(paramTypes, argslen) /** Try packing all arguments into a Tuple and apply `fun` * to that. This is the last thing which is tried (after * default arguments) */ - def tryTupleApply: Option[Tree] = { - // if 1 formal, 1 arg (a tuple), otherwise unmodified args - val tupleArgs = actualArgs(tree.pos.makeTransparent, args, formals.length) - - if (!sameLength(tupleArgs, args) && !isUnitForVarArgs(args, params)) { + def tryTupleApply: Option[Tree] = ( + if (eligibleForTupleConversion(paramTypes, argslen) && !phase.erasedTypes) { + val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args))) // expected one argument, but got 0 or >1 ==> try applying to tuple // the inner "doTypedApply" does "extractUndetparams" => restore when it fails val savedUndetparams = context.undetparams - silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) match { - case SilentResultValue(t) => + silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) map { t => // Depending on user options, may warn or error here if // a Unit or tuple was inserted. Some(t) filter (tupledTree => - !inExprModeButNot(mode, FUNmode) + !mode.inExprModeButNot(FUNmode) || tupledTree.symbol == null || checkValidAdaptation(tupledTree, args) ) - case _ => - context.undetparams = savedUndetparams - None + } orElse { _ => context.undetparams = savedUndetparams ; None } } - } else None - } + else None + ) /** Treats an application which uses named or default arguments. * Also works if names + a vararg used: when names are used, the vararg @@ -3222,12 +3155,12 @@ trait Typers extends Modes with Adaptations with Tags { val lencmp = compareLengths(args, formals) def checkNotMacro() = { - if (fun.symbol != null && fun.symbol.filter(sym => sym != null && sym.isTermMacro && !sym.isErroneous) != NoSymbol) + if (treeInfo.isMacroApplication(fun)) tryTupleApply getOrElse duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun)) } if (mt.isErroneous) duplErrTree - else if (inPatternMode(mode)) { + else if (mode.inPatternMode) { // #2064 duplErrorTree(WrongNumberOfArgsError(tree, fun)) } else if (lencmp > 0) { @@ -3238,10 +3171,10 @@ trait Typers extends Modes with Adaptations with Tags { val (namelessArgs, argPos) = removeNames(Typer.this)(args, params) if (namelessArgs exists (_.isErroneous)) { duplErrTree - } else if (!isIdentity(argPos) && !sameLength(formals, params)) - // !isIdentity indicates that named arguments are used to re-order arguments + } else if (!allArgsArePositional(argPos) && !sameLength(formals, params)) + // !allArgsArePositional indicates that named arguments are used to re-order arguments duplErrorTree(MultipleVarargError(tree)) - else if (isIdentity(argPos) && !isNamedApplyBlock(fun)) { + else if (allArgsArePositional(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() @@ -3291,7 +3224,7 @@ trait Typers extends Modes with Adaptations with Tags { } if (!sameLength(formals, args) || // wrong nb of arguments - (args exists isNamed) || // uses a named argument + (args exists isNamedArg) || // uses a named argument isNamedApplyBlock(fun)) { // fun was transformed to a named apply block => // integrate this application into the block if (dyna.isApplyDynamicNamed(fun)) dyna.typedNamedApply(tree, fun, args, mode, pt) @@ -3322,30 +3255,10 @@ trait Typers extends Modes with Adaptations with Tags { // precise(foo) : foo.type => foo.type val restpe = mt.resultType(args1 map (arg => gen.stableTypeFor(arg) getOrElse arg.tpe)) def ifPatternSkipFormals(tp: Type) = tp match { - case MethodType(_, rtp) if (inPatternMode(mode)) => rtp + case MethodType(_, rtp) if (mode.inPatternMode) => rtp case _ => tp } - // Replace the Delegate-Chainer methods += and -= with corresponding - // + and - calls, which are translated in the code generator into - // Combine and Remove - if (forMSIL) { - fun match { - case Select(qual, name) => - if (isSubType(qual.tpe, DelegateClass.tpe) - && (name == encode("+=") || name == encode("-="))) { - val n = if (name == encode("+=")) nme.PLUS else nme.MINUS - val f = Select(qual, n) - // the compiler thinks, the PLUS method takes only one argument, - // but he thinks it's an instance method -> still two ref's on the stack - // -> translated by backend - val rhs = treeCopy.Apply(tree, f, args) - return typed(Assign(qual, rhs)) - } - case _ => () - } - } - /** * This is translating uses of List() into Nil. This is less * than ideal from a consistency standpoint, but it shouldn't be @@ -3368,7 +3281,7 @@ trait Typers extends Modes with Adaptations with Tags { doTypedApply(tree, fun, args, mode, pt) } else { def handlePolymorphicCall = { - assert(!inPatternMode(mode), modeString(mode)) // this case cannot arise for patterns + assert(!mode.inPatternMode, mode) // this case cannot arise for patterns val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt) val strictTargs = map2(lenientTargs, tparams)((targ, tparam) => if (targ == WildcardType) tparam.tpeHK else targ) @@ -3394,9 +3307,8 @@ trait Typers extends Modes with Adaptations with Tags { // define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun" // returns those undetparams which have not been instantiated. val undetparams = inferMethodInstance(fun, tparams, args1, pt) - val result = doTypedApply(tree, fun, args1, mode, pt) - context.undetparams = undetparams - result + try doTypedApply(tree, fun, args1, mode, pt) + finally context.undetparams = undetparams } } handlePolymorphicCall @@ -3410,15 +3322,16 @@ trait Typers extends Modes with Adaptations with Tags { if (!tree.isErrorTyped) setError(tree) else tree // @H change to setError(treeCopy.Apply(tree, fun, args)) - case otpe if inPatternMode(mode) && unapplyMember(otpe).exists => + case otpe if mode.inPatternMode && unapplyMember(otpe).exists => doTypedUnapply(tree, fun0, fun, args, mode, pt) case _ => - duplErrorTree(ApplyWithoutArgsError(tree, fun)) + if (treeInfo.isMacroApplication(tree)) duplErrorTree(MacroTooManyArgumentListsError(tree, fun.symbol)) + else duplErrorTree(ApplyWithoutArgsError(tree, fun)) } } - def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } @@ -3453,7 +3366,7 @@ trait Typers extends Modes with Adaptations with Tags { else None if (!isApplicableSafe(Nil, unappType, List(pt), WildcardType)) { - //Console.println("UNAPP: need to typetest, arg.tpe = "+arg.tpe+", unappType = "+unappType) + //Console.println(s"UNAPP: need to typetest, arg: ${arg.tpe} unappType: $unappType") val (freeVars, unappFormal) = freshArgType(unappType.skolemizeExistential(context.owner, tree)) val unapplyContext = context.makeNewScope(context.tree, context.owner) freeVars foreach unapplyContext.scope.enter @@ -3463,29 +3376,26 @@ trait Typers extends Modes with Adaptations with Tags { // turn any unresolved type variables in freevars into existential skolems val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) - arg.tpe = pattp.substSym(freeVars, skolems) + arg setType pattp.substSym(freeVars, skolems) argDummy setInfo arg.tpe } - // setType null is necessary so that ref will be stabilized; see bug 881 - val fun1 = typedPos(fun.pos)(Apply(Select(fun setType null, unapp), List(arg))) + // clearing the type is necessary so that ref will be stabilized; see bug 881 + val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapp), List(arg))) if (fun1.tpe.isErroneous) duplErrTree else { - val resTp = fun1.tpe.finalResultType.normalize + val resTp = fun1.tpe.finalResultType.dealiasWiden val nbSubPats = args.length val (formals, formalsExpanded) = extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol) if (formals == null) duplErrorTree(WrongNumberOfArgsError(tree, fun)) else { val args1 = typedArgs(args, mode, formals, formalsExpanded) - // This used to be the following (failing) assert: - // assert(isFullyDefined(pt), tree+" ==> "+UnApply(fun1, args1)+", pt = "+pt) - // I modified as follows. See SI-1048. - val pt1 = if (isFullyDefined(pt)) pt else makeFullyDefined(pt) + val pt1 = if (isFullyDefined(pt)) pt else makeFullyDefined(pt) // SI-1048 val itype = glb(List(pt1, arg.tpe)) - arg.tpe = pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) + arg setType pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) val unapply = UnApply(fun1, args1) setPos tree.pos setType itype // if the type that the unapply method expects for its argument is uncheckable, wrap in classtag extractor @@ -3520,16 +3430,21 @@ trait Typers extends Modes with Adaptations with Tags { // if there's a ClassTag that allows us to turn the unchecked type test for `pt` into a checked type test // return the corresponding extractor (an instance of ClassTag[`pt`]) - def extractorForUncheckedType(pos: Position, pt: Type): Option[Tree] = if (!opt.virtPatmat || isPastTyper) None else { + def extractorForUncheckedType(pos: Position, pt: Type): Option[Tree] = if (isPastTyper) None else { // only look at top-level type, can't (reliably) do anything about unchecked type args (in general) - pt.normalize.typeConstructor match { + // but at least make a proper type before passing it elsewhere + val pt1 = pt.dealiasWiden match { + case tr @ TypeRef(pre, sym, args) if args.nonEmpty => copyTypeRef(tr, pre, sym, sym.typeParams map (_.tpeHK)) // replace actual type args with dummies + case pt1 => pt1 + } + pt1 match { // if at least one of the types in an intersection is checkable, use the checkable ones // this avoids problems as in run/matchonseq.scala, where the expected type is `Coll with scala.collection.SeqLike` // Coll is an abstract type, but SeqLike of course is not - case RefinedType(parents, _) if (parents.length >= 2) && (parents.exists(tp => !infer.containsUnchecked(tp))) => + case RefinedType(ps, _) if ps.length > 1 && (ps exists infer.isCheckable) => None - case ptCheckable if infer.containsUnchecked(ptCheckable) => + case ptCheckable if infer isUncheckable ptCheckable => val classTagExtractor = resolveClassTag(pos, ptCheckable) if (classTagExtractor != EmptyTree && unapplyMember(classTagExtractor.tpe) != NoSymbol) @@ -3537,30 +3452,38 @@ trait Typers extends Modes with Adaptations with Tags { else None case _ => None - } + } } /** * Convert an annotation constructor call into an AnnotationInfo. - * - * @param annClass the expected annotation class */ - def typedAnnotation(ann: Tree, mode: Int = EXPRmode, selfsym: Symbol = NoSymbol, annClass: Symbol = AnnotationClass, requireJava: Boolean = false): AnnotationInfo = { - lazy val annotationError = AnnotationInfo(ErrorType, Nil, Nil) + def typedAnnotation(ann: Tree, mode: Mode = EXPRmode, selfsym: Symbol = NoSymbol): AnnotationInfo = { var hasError: Boolean = false val pending = ListBuffer[AbsTypeError]() + def finish(res: AnnotationInfo): AnnotationInfo = { + if (hasError) { + pending.foreach(ErrorUtils.issueTypeError) + ErroneousAnnotation + } + else res + } + def reportAnnotationError(err: AbsTypeError) = { pending += err hasError = true - annotationError + ErroneousAnnotation } /** Calling constfold right here is necessary because some trees (negated * floats and literals in particular) are not yet folded. */ def tryConst(tr: Tree, pt: Type): Option[LiteralAnnotArg] = { - val const: Constant = typed(constfold(tr), EXPRmode, pt) match { + // The typed tree may be relevantly different than the tree `tr`, + // e.g. it may have encountered an implicit conversion. + val ttree = typed(constfold(tr), EXPRmode, pt) + val const: Constant = ttree match { case l @ Literal(c) if !l.isErroneous => c case tree => tree.tpe match { case ConstantType(c) => c @@ -3569,7 +3492,7 @@ trait Typers extends Modes with Adaptations with Tags { } if (const == null) { - reportAnnotationError(AnnotationNotAConstantError(tr)); None + reportAnnotationError(AnnotationNotAConstantError(ttree)); None } else if (const.value == null) { reportAnnotationError(AnnotationArgNullError(tr)); None } else @@ -3584,14 +3507,21 @@ trait Typers extends Modes with Adaptations with Tags { reportAnnotationError(ArrayConstantsError(tree)); None case ann @ Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => - val annInfo = typedAnnotation(ann, mode, NoSymbol, pt.typeSymbol, true) + val annInfo = typedAnnotation(ann, mode, NoSymbol) + val annType = annInfo.tpe + + if (!annType.typeSymbol.isSubClass(pt.typeSymbol)) + reportAnnotationError(AnnotationTypeMismatchError(tpt, annType, annType)) + else if (!annType.typeSymbol.isSubClass(ClassfileAnnotationClass)) + reportAnnotationError(NestedAnnotationError(ann, annType)) + if (annInfo.atp.isErroneous) { hasError = true; None } else Some(NestedAnnotArg(annInfo)) // use of Array.apply[T: ClassTag](xs: T*): Array[T] // and Array.apply(x: Int, xs: Int*): Array[Int] (and similar) case Apply(fun, args) => - val typedFun = typed(fun, forFunMode(mode), WildcardType) + val typedFun = typed(fun, mode.forFunMode, WildcardType) if (typedFun.symbol.owner == ArrayModule.moduleClass && typedFun.symbol.name == nme.apply) pt match { case TypeRef(_, ArrayClass, targ :: _) => @@ -3619,44 +3549,42 @@ trait Typers extends Modes with Adaptations with Tags { } // begin typedAnnotation - val (fun, argss) = { - def extract(fun: Tree, outerArgss: List[List[Tree]]): - (Tree, List[List[Tree]]) = fun match { - case Apply(f, args) => - extract(f, args :: outerArgss) - case Select(New(tpt), nme.CONSTRUCTOR) => - (fun, outerArgss) - case _ => - reportAnnotationError(UnexpectedTreeAnnotation(fun)) - (setError(fun), outerArgss) - } - extract(ann, List()) - } - - val res = if (fun.isErroneous) annotationError - else { - val typedFun @ Select(New(tpt), _) = typed(fun, forFunMode(mode), WildcardType) - val annType = tpt.tpe + val treeInfo.Applied(fun0, targs, argss) = ann + if (fun0.isErroneous) + return finish(ErroneousAnnotation) + val typedFun0 = typed(fun0, mode.forFunMode, WildcardType) + val typedFunPart = ( + // If there are dummy type arguments in typeFun part, it suggests we + // must type the actual constructor call, not only the select. The value + // arguments are how the type arguments will be inferred. + if (targs.isEmpty && typedFun0.exists(t => isDummyAppliedType(t.tpe))) + logResult(s"Retyped $typedFun0 to find type args")(typed(argss.foldLeft(fun0)(Apply(_, _)))) + else + typedFun0 + ) + val treeInfo.Applied(typedFun @ Select(New(annTpt), _), _, _) = typedFunPart + val annType = annTpt.tpe - if (typedFun.isErroneous) annotationError + finish( + if (typedFun.isErroneous) + ErroneousAnnotation else if (annType.typeSymbol isNonBottomSubClass ClassfileAnnotationClass) { // annotation to be saved as java classfile annotation val isJava = typedFun.symbol.owner.isJavaDefined - if (!annType.typeSymbol.isNonBottomSubClass(annClass)) { - reportAnnotationError(AnnotationTypeMismatchError(tpt, annClass.tpe, annType)) - } else if (argss.length > 1) { + if (argss.length > 1) { reportAnnotationError(MultipleArgumentListForAnnotationError(ann)) - } else { + } + else { val annScope = annType.decls .filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined) val names = new scala.collection.mutable.HashSet[Symbol] - def hasValue = names exists (_.name == nme.value) names ++= (if (isJava) annScope.iterator else typedFun.tpe.params.iterator) + + def hasValue = names exists (_.name == nme.value) val args = argss match { - case List(List(arg)) if !isNamed(arg) && hasValue => - List(new AssignOrNamedArg(Ident(nme.value), arg)) - case as :: _ => as + case (arg :: Nil) :: Nil if !isNamedArg(arg) && hasValue => gen.mkNamedArg(nme.value, arg) :: Nil + case args :: Nil => args } val nvPairs = args map { @@ -3686,45 +3614,32 @@ trait Typers extends Modes with Adaptations with Tags { reportAnnotationError(AnnotationMissingArgError(ann, annType, sym)) } - if (hasError) annotationError + if (hasError) ErroneousAnnotation 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)) - } else { + } + else { val typedAnn = if (selfsym == NoSymbol) { // local dummy fixes SI-5544 val localTyper = newTyper(context.make(ann, context.owner.newLocalDummy(ann.pos))) - localTyper.typed(ann, mode, annClass.tpe) - } else { - // Since a selfsym is supplied, the annotation should have - // an extra "self" identifier in scope for type checking. - // This is implemented by wrapping the rhs - // in a function like "self => rhs" during type checking, - // and then stripping the "self =>" and substituting - // in the supplied selfsym. + localTyper.typed(ann, mode, annType) + } + else { + // Since a selfsym is supplied, the annotation should have an extra + // "self" identifier in scope for type checking. This is implemented + // by wrapping the rhs in a function like "self => rhs" during type + // checking, and then stripping the "self =>" and substituting in + // the supplied selfsym. val funcparm = ValDef(NoMods, nme.self, TypeTree(selfsym.info), EmptyTree) - val func = Function(List(funcparm), ann.duplicate) - // The .duplicate of annot.constr - // deals with problems that - // accur if this annotation is - // later typed again, which - // the compiler sometimes does. - // The problem is that "self" - // ident's within annot.constr - // will retain the old symbol - // from the previous typing. - val fun1clazz = FunctionClass(1) - val funcType = typeRef(fun1clazz.tpe.prefix, - fun1clazz, - List(selfsym.info, annClass.tpe)) - - (typed(func, mode, funcType): @unchecked) match { - case t @ Function(List(arg), rhs) => - val subs = - new TreeSymSubstituter(List(arg.symbol),List(selfsym)) - subs(rhs) - } + // The .duplicate of annot.constr deals with problems that accur + // if this annotation is later typed again, which the compiler + // sometimes does. The problem is that "self" ident's within + // annot.constr will retain the old symbol from the previous typing. + val func = Function(funcparm :: Nil, ann.duplicate) + val funcType = appliedType(FunctionClass(1), selfsym.info, annType) + val Function(arg :: Nil, rhs) = typed(func, mode, funcType) + + rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil) } def annInfo(t: Tree): AnnotationInfo = t match { @@ -3750,15 +3665,9 @@ trait Typers extends Modes with Adaptations with Tags { if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2) unit.deprecationWarning(ann.pos, "@deprecated now takes two arguments; see the scaladoc.") - if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) annotationError + if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) ErroneousAnnotation else annInfo(typedAnn) - } - } - - if (hasError) { - pending.foreach(ErrorUtils.issueTypeError) - annotationError - } else res + }) } def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type? @@ -3878,7 +3787,8 @@ trait Typers extends Modes with Adaptations with Tags { else containsDef(owner, sym) || isRawParameter(sym) || isCapturedExistential(sym) def containsLocal(tp: Type): Boolean = tp exists (t => isLocal(t.typeSymbol) || isLocal(t.termSymbol)) - val normalizeLocals = new TypeMap { + + val dealiasLocals = new TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, args) => if (sym.isAliasType && containsLocal(tp)) apply(tp.dealias) @@ -3931,31 +3841,31 @@ trait Typers extends Modes with Adaptations with Tags { for (sym <- remainingSyms) addLocals(sym.existentialBound) } - val normalizedTpe = normalizeLocals(tree.tpe) - addLocals(normalizedTpe) - packSymbols(localSyms.toList, normalizedTpe) + val dealiasedType = dealiasLocals(tree.tpe) + addLocals(dealiasedType) + packSymbols(localSyms.toList, dealiasedType) } def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) = if (!checkClassType(tpt) && noGen) tpt else atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) - protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Int): Tree = { + protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Mode): Tree = { for (wc <- tree.whereClauses) if (wc.symbol == NoSymbol) { namer.enterSym(wc); wc.symbol setFlag EXISTENTIAL } else context.scope enter wc.symbol val whereClauses1 = typedStats(tree.whereClauses, context.owner) - for (vd @ ValDef(_, _, _, _) <- tree.whereClauses) + for (vd @ ValDef(_, _, _, _) <- whereClauses1) if (vd.symbol.tpe.isVolatile) AbstractionFromVolatileTypeError(vd) val tpt1 = typedType(tree.tpt, mode) - existentialTransform(tree.whereClauses map (_.symbol), tpt1.tpe)((tparams, tp) => + existentialTransform(whereClauses1 map (_.symbol), tpt1.tpe)((tparams, tp) => TypeTree(newExistentialType(tparams, tp)) setOriginal tree ) } // lifted out of typed1 because it's needed in typedImplicit0 - protected def typedTypeApply(tree: Tree, mode: Int, fun: Tree, args: List[Tree]): Tree = fun.tpe match { + protected def typedTypeApply(tree: Tree, mode: Mode, fun: Tree, args: List[Tree]): Tree = fun.tpe match { case OverloadedType(pre, alts) => inferPolyAlternatives(fun, args map (_.tpe)) val tparams = fun.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree) @@ -4044,7 +3954,7 @@ trait Typers extends Modes with Adaptations with Tags { // else false } - def typedNamedApply(orig: Tree, fun: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + def typedNamedApply(orig: Tree, fun: Tree, args: List[Tree], mode: Mode, 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)) @@ -4114,13 +4024,9 @@ trait Typers extends Modes with Adaptations with Tags { } } - def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = { - silent(typeTree) match { - case SilentResultValue(r) => r - case SilentTypeError(err) => DynamicRewriteError(tree, err) + def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = + silent(typeTree) orElse (err => DynamicRewriteError(tree, err)) } - } - } final def deindentTyping() = context.typingIndentLevel -= 2 final def indentTyping() = context.typingIndentLevel += 2 @@ -4133,22 +4039,33 @@ trait Typers extends Modes with Adaptations with Tags { println(s) } - def typed1(tree: Tree, mode: Int, pt: Type): Tree = { - def isPatternMode = inPatternMode(mode) + def typed1(tree: Tree, mode: Mode, pt: Type): Tree = { + def isPatternMode = mode.inPatternMode + def inPatternConstructor = mode.inAll(PATTERNmode | FUNmode) + def isQualifierMode = mode.inAll(QUALmode) - //Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")") - //@M! get the type of the qualifier in a Select tree, otherwise: NoType - def prefixType(fun: Tree): Type = fun match { - case Select(qualifier, _) => qualifier.tpe -// case Ident(name) => ?? - case _ => NoType + // Lookup in the given class using the root mirror. + def lookupInOwner(owner: Symbol, name: Name): Symbol = + if (isQualifierMode) rootMirror.missingHook(owner, name) else NoSymbol + + // Lookup in the given qualifier. Used in last-ditch efforts by typedIdent and typedSelect. + def lookupInRoot(name: Name): Symbol = lookupInOwner(rootMirror.RootClass, name) + def lookupInEmpty(name: Name): Symbol = rootMirror.EmptyPackageClass.info member name + + def lookupInQualifier(qual: Tree, name: Name): Symbol = ( + if (name == nme.ERROR || qual.tpe.widen.isErroneous) + NoSymbol + else lookupInOwner(qual.tpe.typeSymbol, name) orElse { + NotAMemberError(tree, qual, name) + NoSymbol } + ) def typedAnnotated(atd: Annotated): Tree = { val ann = atd.annot val arg1 = typed(atd.arg, mode, pt) /** mode for typing the annotation itself */ - val annotMode = mode & ~TYPEmode | EXPRmode + val annotMode = (mode &~ TYPEmode) | EXPRmode def resultingTypeTree(tpe: Type) = { // we need symbol-ful originals for reification @@ -4203,7 +4120,7 @@ trait Typers extends Modes with Adaptations with Tags { // Erroneous annotations were already reported in typedAnnotation arg1 // simply drop erroneous annotations else { - ann.tpe = atype + ann setType atype resultingTypeTree(atype) } } else { @@ -4214,7 +4131,7 @@ trait Typers extends Modes with Adaptations with Tags { else { if (ann.tpe == null) { val annotInfo = typedAnnotation(ann, annotMode) - ann.tpe = arg1.tpe.withAnnotation(annotInfo) + ann setType arg1.tpe.withAnnotation(annotInfo) } val atype = ann.tpe Typed(arg1, resultingTypeTree(atype)) setPos tree.pos setType atype @@ -4238,7 +4155,7 @@ trait Typers extends Modes with Adaptations with Tags { if (name != tpnme.WILDCARD) namer.enterInScope(sym) else context.scope.enter(sym) - tree setSymbol sym setType sym.tpe + tree setSymbol sym setType sym.tpeHK case name: TermName => val sym = @@ -4246,7 +4163,7 @@ trait Typers extends Modes with Adaptations with Tags { else context.owner.newValue(name, tree.pos) if (name != nme.WILDCARD) { - if ((mode & ALTmode) != 0) VariableInPatternAlternativeError(tree) + if (mode.inAll(ALTmode)) VariableInPatternAlternativeError(tree) namer.enterInScope(sym) } @@ -4334,8 +4251,7 @@ trait Typers extends Modes with Adaptations with Tags { // 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 - && 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) + if (!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.deconst, false) // use unpacked type. Important to deconst, as is done in ptOrLub, otherwise `if (???) 0 else 0` evaluates to 0 (SI-6331) // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala @@ -4349,7 +4265,7 @@ trait Typers extends Modes with Adaptations with Tags { } } - // under -Xexperimental (and not -Xoldpatmat), and when there's a suitable __match in scope, virtualize the pattern match + // When there's a suitable __match in scope, virtualize the pattern match // otherwise, type the Match and leave it until phase `patmat` (immediately after typer) // empty-selector matches are transformed into synthetic PartialFunction implementations when the expected type demands it def typedVirtualizedMatch(tree: Match): Tree = { @@ -4359,7 +4275,7 @@ trait Typers extends Modes with Adaptations with Tags { if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass)) synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, tree, mode, pt) else { - val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1 + val arity = if (isFunctionType(pt)) pt.dealiasWiden.typeArgs.length - 1 else 1 val params = for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { ValDef(Modifiers(PARAM | SYNTHETIC), @@ -4415,7 +4331,7 @@ trait Typers extends Modes with Adaptations with Tags { // given a dealiased type. val tpt0 = typedTypeConstructor(tpt) modifyType (_.dealias) if (checkStablePrefixClassType(tpt0)) - if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { + if (tpt0.hasSymbolField && !tpt0.symbol.typeParams.isEmpty) { context.undetparams = cloneSymbols(tpt0.symbol.typeParams) notifyUndetparamsAdded(context.undetparams) TypeTree().setOriginal(tpt0) @@ -4445,7 +4361,7 @@ trait Typers extends Modes with Adaptations with Tags { NotAMemberError(tpt, TypeTree(tp), nme.CONSTRUCTOR) setError(tpt) } - else if (!( tp == sym.thisSym.tpe // when there's no explicit self type -- with (#3612) or without self variable + else if (!( tp == sym.thisSym.tpe_* // when there's no explicit self type -- with (#3612) or without self variable // sym.thisSym.tpe == tp.typeOfThis (except for objects) || narrowRhs(tp) <:< tp.typeOfThis || phase.erasedTypes @@ -4469,35 +4385,14 @@ trait Typers extends Modes with Adaptations with Tags { else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) case MethodType(formals, _) => if (isFunctionType(pt)) expr1 - else expr1 match { - case Select(qual, name) if (forMSIL && - pt != WildcardType && - pt != ErrorType && - isSubType(pt, DelegateClass.tpe)) => - val scalaCaller = newScalaCaller(pt) - addScalaCallerInfo(scalaCaller, expr1.symbol) - val n: Name = scalaCaller.name - val del = Ident(DelegateClass) setType DelegateClass.tpe - val f = Select(del, n) - //val f1 = TypeApply(f, List(Ident(pt.symbol) setType pt)) - val args: List[Tree] = if(expr1.symbol.isStatic) List(Literal(Constant(null))) - else List(qual) // where the scala-method is located - val rhs = Apply(f, args) - typed(rhs) - case _ => - adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) - } + else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) case ErrorType => expr1 case _ => UnderscoreEtaError(expr1) } - /** - * @param args ... - * @return ... - */ - def tryTypedArgs(args: List[Tree], mode: Int): Option[List[Tree]] = { + def tryTypedArgs(args: List[Tree], mode: Mode): Option[List[Tree]] = { val c = context.makeSilent(false) c.retyping = true try { @@ -4570,19 +4465,14 @@ trait Typers extends Modes with Adaptations with Tags { setError(treeCopy.Apply(tree, fun, args)) } - silent(_.doTypedApply(tree, fun, args, mode, pt)) match { - case SilentResultValue(t) => - t - case SilentTypeError(err) => - onError(err) + silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError } - } def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = { val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable if (stableApplication && isPatternMode) { // treat stable function applications f() as expressions. - typed1(tree, mode & ~PATTERNmode | EXPRmode, pt) + typed1(tree, (mode &~ PATTERNmode) | EXPRmode, pt) } else { val funpt = if (isPatternMode) pt else WildcardType val appStart = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null @@ -4605,9 +4495,9 @@ trait Typers extends Modes with Adaptations with Tags { reportError } } - silent(_.typed(fun, forFunMode(mode), funpt), - if ((mode & EXPRmode) != 0) false else context.ambiguousErrors, - if ((mode & EXPRmode) != 0) tree else context.tree) match { + silent(_.typed(fun, mode.forFunMode, funpt), + if (mode.inExprMode) false else context.ambiguousErrors, + if (mode.inExprMode) tree else context.tree) match { case SilentResultValue(fun1) => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 if (Statistics.canEnable) Statistics.incCounter(typedApplyCount) @@ -4626,15 +4516,6 @@ trait Typers extends Modes with Adaptations with Tags { if (useTry) tryTypedApply(fun2, args) else doTypedApply(tree, fun2, args, mode, pt) - /* - if (fun2.hasSymbol && fun2.symbol.isConstructor && (mode & EXPRmode) != 0) { - res.tpe = res.tpe.notNull - } - */ - // TODO: In theory we should be able to call: - //if (fun2.hasSymbol && fun2.symbol.name == nme.apply && fun2.symbol.owner == ArrayClass) { - // But this causes cyclic reference for Array class in Cleanup. It is easy to overcome this - // by calling ArrayClass.info here (or some other place before specialize). if (fun2.symbol == Array_apply && !res.isErrorTyped) { val checked = gen.mkCheckInit(res) // this check is needed to avoid infinite recursion in Duplicators @@ -4649,37 +4530,36 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedApply(tree: Apply) = { - val fun = tree.fun - val args = tree.args - fun match { - case Block(stats, expr) => - typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) - case _ => - normalTypedApply(tree, fun, args) match { - case Apply(Select(New(tpt), name), args) - if (tpt.tpe != null && - 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[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.toTypeConstructor, List(tpe))).last - atPos(tree.pos) { - val tag = resolveClassTag(tree.pos, tagType) - if (tag.isEmpty) MissingClassTagError(tree, tagType) - else typed(new ApplyToImplicitArgs(Select(tag, nme.newArray), args)) + // 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 + object ArrayInstantiation { + def unapply(tree: Apply) = tree match { + case Apply(Select(New(tpt), name), arg :: Nil) if tpt.tpe != null && tpt.tpe.typeSymbol == ArrayClass => + Some(tpt.tpe) collect { + case erasure.GenericArray(level, componentType) => + val tagType = (1 until level).foldLeft(componentType)((res, _) => arrayType(res)) + + resolveClassTag(tree.pos, tagType) match { + case EmptyTree => MissingClassTagError(tree, tagType) + case tag => atPos(tree.pos)(new ApplyToImplicitArgs(Select(tag, nme.newArray), arg :: Nil)) } - case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => //SI-5696 - TooManyArgumentListsForConstructor(tree) - case tree1 => - tree1 } + case _ => None } } + def typedApply(tree: Apply) = tree match { + case Apply(Block(stats, expr), args) => + typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) + case Apply(fun, args) => + normalTypedApply(tree, fun, args) match { + case ArrayInstantiation(tree1) => typed(tree1, mode, pt) + case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => TooManyArgumentListsForConstructor(tree) //SI-5696 + case tree1 => tree1 + } + } + def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree]): Tree = { val prefix = name.toTermName stripSuffix nme.EQL def mkAssign(vble: Tree): Tree = @@ -4733,8 +4613,6 @@ trait Typers extends Modes with Adaptations with Tags { case This(_) => qual1.symbol case _ => qual1.tpe.typeSymbol } - //println(clazz+"/"+qual1.tpe.typeSymbol+"/"+qual1) - def findMixinSuper(site: Type): Type = { var ps = site.parents filter (_.typeSymbol.name == mix) if (ps.isEmpty) @@ -4742,11 +4620,6 @@ trait Typers extends Modes with Adaptations with Tags { if (ps.isEmpty) { debuglog("Fatal: couldn't find site " + site + " in " + site.parents.map(_.typeSymbol.name)) if (phase.erasedTypes && context.enclClass.owner.isImplClass) { - // println(qual1) - // println(clazz) - // println(site) - // println(site.parents) - // println(mix) // the reference to super class got lost during erasure restrictionError(tree.pos, unit, "traits may not select fields or methods from super[C] where C is a class") ErrorType @@ -4764,7 +4637,7 @@ trait Typers extends Modes with Adaptations with Tags { val owntype = ( if (!mix.isEmpty) findMixinSuper(clazz.tpe) - else if ((mode & SUPERCONSTRmode) != 0) clazz.info.firstParent + else if (mode.inAll(SUPERCONSTRmode)) clazz.info.firstParent else intersectionType(clazz.info.parents) ) treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) @@ -4778,14 +4651,28 @@ trait Typers extends Modes with Adaptations with Tags { if (isStableContext(tree, mode, pt)) tree setType clazz.thisType else tree } - /** Attribute a selection where <code>tree</code> is <code>qual.name</code>. - * <code>qual</code> is already attributed. - * - * @param qual ... - * @param name ... - * @return ... + /** Attribute a selection where `tree` is `qual.name`. + * `qual` is already attributed. */ def typedSelect(tree: Tree, qual: Tree, name: Name): Tree = { + val t = typedSelectInternal(tree, qual, name) + // Checking for OverloadedTypes being handed out after overloading + // resolution has already happened. + if (isPastTyper) t.tpe match { + case OverloadedType(pre, alts) => + if (alts forall (s => (s.owner == ObjectClass) || (s.owner == AnyClass) || isPrimitiveValueClass(s.owner))) () + else if (settings.debug.value) printCaller( + s"""|Select received overloaded type during $phase, but typer is over. + |If this type reaches the backend, we are likely doomed to crash. + |$t has these overloads: + |${alts map (s => " " + s.defStringSeenAs(pre memberType s)) mkString "\n"} + |""".stripMargin + )("") + case _ => + } + t + } + def typedSelectInternal(tree: Tree, qual: Tree, name: Name): Tree = { def asDynamicCall = dyna.mkInvoke(context.tree, tree, qual, name) map { t => dyna.wrapErrors(t, (_.typed1(t, mode, pt))) } @@ -4794,7 +4681,7 @@ trait Typers extends Modes with Adaptations with Tags { // 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 (name != nme.CONSTRUCTOR && inExprModeOr(mode, PATTERNmode)) { + if (name != nme.CONSTRUCTOR && mode.inExprModeOr(PATTERNmode)) { val qual1 = adaptToMemberWithArgs(tree, qual, name, mode, true, true) if ((qual1 ne qual) && !qual1.isErrorTyped) return typed(treeCopy.Select(tree, qual1, name), mode, pt) @@ -4802,47 +4689,45 @@ trait Typers extends Modes with Adaptations with Tags { NoSymbol } if (phase.erasedTypes && qual.isInstanceOf[Super] && tree.symbol != NoSymbol) - qual.tpe = tree.symbol.owner.tpe + qual setType tree.symbol.owner.tpe if (!reallyExists(sym)) { def handleMissing: Tree = { - if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { - val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } - if (tree1 != EmptyTree) return typed1(tree1, mode, pt) - } - - // try to expand according to Dynamic rules. - asDynamicCall foreach (x => return x) - - debuglog( - "qual = " + qual + ":" + qual.tpe + - "\nSymbol=" + qual.tpe.termSymbol + "\nsymbol-info = " + qual.tpe.termSymbol.info + - "\nscope-id = " + qual.tpe.termSymbol.info.decls.hashCode() + "\nmembers = " + qual.tpe.members + - "\nname = " + name + "\nfound = " + sym + "\nowner = " + context.enclClass.owner) - - def makeInteractiveErrorTree = { - val tree1 = tree match { + def errorTree = tree match { + case _ if !forInteractive => tree case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) } - setError(tree1) + def asTypeSelection = ( + if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { + atPos(tree.pos)(gen.convertToSelectFromType(qual, name)) match { + case EmptyTree => None + case tree1 => Some(typed1(tree1, mode, pt)) } - - if (name == nme.ERROR && forInteractive) - return makeInteractiveErrorTree - - if (!qual.tpe.widen.isErroneous) { - if ((mode & QUALmode) != 0) { - val lastTry = rootMirror.missingHook(qual.tpe.typeSymbol, name) - if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) } - NotAMemberError(tree, qual, name) - } - - if (forInteractive) makeInteractiveErrorTree else setError(tree) + else None + ) + debuglog(s""" + |qual=$qual:${qual.tpe} + |symbol=${qual.tpe.termSymbol.defString} + |scope-id=${qual.tpe.termSymbol.info.decls.hashCode} + |members=${qual.tpe.members mkString ", "} + |name=$name + |found=$sym + |owner=${context.enclClass.owner} + """.stripMargin) + + // 1) Try converting a term selection on a java class into a type selection. + // 2) Try expanding according to Dynamic rules. + // 3) Try looking up the name in the qualifier. + asTypeSelection orElse asDynamicCall getOrElse (lookupInQualifier(qual, name) match { + case NoSymbol => setError(errorTree) + case found => typed1(tree setSymbol found, mode, pt) + }) } handleMissing - } else { + } + else { val tree1 = tree match { case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) @@ -4913,10 +4798,7 @@ trait Typers extends Modes with Adaptations with Tags { val tree1 = // temporarily use `filter` and an alternative for `withFilter` if (name == nme.withFilter) - silent(_ => typedSelect(tree, qual1, name)) match { - case SilentResultValue(result) => - result - case _ => + silent(_ => typedSelect(tree, qual1, name)) orElse { _ => silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { case SilentResultValue(result2) => unit.deprecationWarning( @@ -4940,6 +4822,18 @@ trait Typers extends Modes with Adaptations with Tags { } } + /** A symbol qualifies if: + * - it exists + * - it is not stale (stale symbols are made to disappear here) + * - if we are in a pattern constructor, method definitions do not qualify + * unless they are stable. Otherwise, 'case x :: xs' would find the :: method. + */ + def qualifies(sym: Symbol) = ( + sym.hasRawInfo + && reallyExists(sym) + && !(inPatternConstructor && sym.isMethod && !sym.isStable) + ) + /** Attribute an identifier consisting of a simple name or an outer reference. * * @param tree The tree representing the identifier. @@ -4948,251 +4842,56 @@ trait Typers extends Modes with Adaptations with Tags { * (2) Change imported symbols to selections */ def typedIdent(tree: Tree, name: Name): Tree = { - var errorContainer: AbsTypeError = null - def ambiguousError(msg: String) = { - assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") - errorContainer = AmbiguousIdentError(tree, name, msg) - } - def identError(tree: AbsTypeError) = { - assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") - errorContainer = tree - } + // setting to enable unqualified idents in empty package (used by the repl) + def inEmptyPackage = if (settings.exposeEmptyPackage.value) lookupInEmpty(name) else NoSymbol + + def issue(err: AbsTypeError) = { + // Avoiding some spurious error messages: see SI-2388. + val suppress = reporter.hasErrors && (name startsWith tpnme.ANON_CLASS_NAME) + if (!suppress) + ErrorUtils.issueTypeError(err) - var defSym: Symbol = tree.symbol // the directly found symbol - var pre: Type = NoPrefix // the prefix type of defSym, if a class member - var qual: Tree = EmptyTree // the qualifier tree if transformed tree is a select - var inaccessibleSym: Symbol = NoSymbol // the first symbol that was found but that was discarded - // for being inaccessible; used for error reporting - var inaccessibleExplanation: String = "" - - // If a special setting is given, the empty package will be checked as a - // last ditch effort before failing. This method sets defSym and returns - // true if a member of the given name exists. - def checkEmptyPackage(): Boolean = { - defSym = rootMirror.EmptyPackageClass.tpe.nonPrivateMember(name) - defSym != NoSymbol + setError(tree) } - def startingIdentContext = ( // ignore current variable scope in patterns to enforce linearity - if ((mode & (PATTERNmode | TYPEPATmode)) == 0) context - else context.outer - ) - // A symbol qualifies if it exists and is not stale. Stale symbols - // are made to disappear here. In addition, - // if we are in a constructor of a pattern, we ignore all definitions - // which are methods (note: if we don't do that - // case x :: xs in class List would return the :: method) - // unless they are stable or are accessors (the latter exception is for better error messages). - def qualifies(sym: Symbol): Boolean = { - sym.hasRawInfo && // this condition avoids crashing on self-referential pattern variables - reallyExists(sym) && - ((mode & PATTERNmode | FUNmode) != (PATTERNmode | FUNmode) || !sym.isSourceMethod || sym.hasFlag(ACCESSOR)) - } - - if (defSym == NoSymbol) { - var defEntry: ScopeEntry = null // the scope entry of defSym, if defined in a local scope - - var cx = startingIdentContext - while (defSym == NoSymbol && cx != NoContext && (cx.scope ne null)) { // cx.scope eq null arises during FixInvalidSyms in Duplicators - pre = cx.enclClass.prefix - defEntry = cx.scope.lookupEntry(name) - if ((defEntry ne null) && qualifies(defEntry.sym)) { - // Right here is where SI-1987, overloading in package objects, can be - // seen to go wrong. There is an overloaded symbol, but when referring - // to the unqualified identifier from elsewhere in the package, only - // the last definition is visible. So overloading mis-resolves and is - // definition-order dependent, bad things. See run/t1987.scala. - // - // I assume the actual problem involves how/where these symbols are entered - // into the scope. But since I didn't figure out how to fix it that way, I - // catch it here by looking up package-object-defined symbols in the prefix. - if (isInPackageObject(defEntry.sym, pre.typeSymbol)) { - defSym = pre.member(defEntry.sym.name) - if (defSym ne defEntry.sym) { - qual = gen.mkAttributedQualifier(pre) - log(sm""" - | !!! Overloaded package object member resolved incorrectly. - | prefix: $pre - | Discarded: ${defEntry.sym.defString} - | Using: ${defSym.defString} - """) - } - } - else - defSym = defEntry.sym - } - else { - cx = cx.enclClass - val foundSym = pre.member(name) filter qualifies - defSym = foundSym filter (context.isAccessible(_, pre, false)) - if (defSym == NoSymbol) { - if ((foundSym ne NoSymbol) && (inaccessibleSym eq NoSymbol)) { - inaccessibleSym = foundSym - inaccessibleExplanation = analyzer.lastAccessCheckDetails - } - cx = cx.outer - } - } - } - - val symDepth = if (defEntry eq null) cx.depth - else cx.depth - (cx.scope.nestingLevel - defEntry.owner.nestingLevel) - var impSym: Symbol = NoSymbol // the imported symbol - var imports = context.imports // impSym != NoSymbol => it is imported from imports.head - while (!reallyExists(impSym) && !imports.isEmpty && imports.head.depth > symDepth) { - impSym = imports.head.importedSymbol(name) - if (!impSym.exists) imports = imports.tail - } - - // detect ambiguous definition/import, - // update `defSym` to be the final resolved symbol, - // update `pre` to be `sym`s prefix type in case it is an imported member, - // and compute value of: - - if (defSym.exists && impSym.exists) { - // imported symbols take precedence over package-owned symbols in different - // compilation units. Defined symbols take precedence over erroneous imports. - if (defSym.isDefinedInPackage && - (!currentRun.compiles(defSym) || - context.unit.exists && defSym.sourceFile != context.unit.source.file)) - defSym = NoSymbol - else if (impSym.isError || impSym.name == nme.CONSTRUCTOR) - impSym = NoSymbol - } - if (defSym.exists) { - if (impSym.exists) - ambiguousError( - "it is both defined in "+defSym.owner + - " and imported subsequently by \n"+imports.head) - else if (!defSym.owner.isClass || defSym.owner.isPackageClass || defSym.isTypeParameterOrSkolem) - pre = NoPrefix - else - qual = atPos(tree.pos.focusStart)(gen.mkAttributedQualifier(pre)) - } else { - if (impSym.exists) { - var impSym1: Symbol = NoSymbol - var imports1 = imports.tail - - /** It's possible that seemingly conflicting identifiers are - * identifiably the same after type normalization. In such cases, - * allow compilation to proceed. A typical example is: - * package object foo { type InputStream = java.io.InputStream } - * import foo._, java.io._ - */ - def ambiguousImport() = { - // The types of the qualifiers from which the ambiguous imports come. - // If the ambiguous name is a value, these must be the same. - def t1 = imports.head.qual.tpe - def t2 = imports1.head.qual.tpe - // The types of the ambiguous symbols, seen as members of their qualifiers. - // If the ambiguous name is a monomorphic type, we can relax this far. - def mt1 = t1 memberType impSym - def mt2 = t2 memberType impSym1 - def characterize = List( - s"types: $t1 =:= $t2 ${t1 =:= t2} members: ${mt1 =:= mt2}", - s"member type 1: $mt1", - s"member type 2: $mt2", - s"$impSym == $impSym1 ${impSym == impSym1}", - s"${impSym.debugLocationString} ${impSym.getClass}", - s"${impSym1.debugLocationString} ${impSym1.getClass}" - ).mkString("\n ") - - // The symbol names are checked rather than the symbols themselves because - // each time an overloaded member is looked up it receives a new symbol. - // So foo.member("x") != foo.member("x") if x is overloaded. This seems - // likely to be the cause of other bugs too... - if (t1 =:= t2 && impSym.name == impSym1.name) - log(s"Suppressing ambiguous import: $t1 =:= $t2 && $impSym == $impSym1") - // Monomorphism restriction on types is in part because type aliases could have the - // same target type but attach different variance to the parameters. Maybe it can be - // relaxed, but doesn't seem worth it at present. - else if (mt1 =:= mt2 && name.isTypeName && impSym.isMonomorphicType && impSym1.isMonomorphicType) - log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $impSym and $impSym1 are equivalent") - else { - log(s"Import is genuinely ambiguous:\n " + characterize) - ambiguousError(s"it is imported twice in the same scope by\n${imports.head}\nand ${imports1.head}") + val startContext = if (mode.inNone(PATTERNmode | TYPEPATmode)) context else context.outer + val nameLookup = tree.symbol match { + case NoSymbol => startContext.lookupSymbol(name, qualifies) + case sym => LookupSucceeded(EmptyTree, sym) + } + import InferErrorGen._ + nameLookup match { + case LookupAmbiguous(msg) => issue(AmbiguousIdentError(tree, name, msg)) + case LookupInaccessible(sym, msg) => issue(AccessError(tree, sym, context, msg)) + case LookupNotFound => + inEmptyPackage orElse lookupInRoot(name) match { + case NoSymbol => issue(SymbolNotFoundError(tree, name, context.owner, startContext)) + case sym => typed1(tree setSymbol sym, mode, pt) } - } - while (errorContainer == null && !imports1.isEmpty && - (!imports.head.isExplicitImport(name) || - imports1.head.depth == imports.head.depth)) { - impSym1 = imports1.head.importedSymbol(name) - if (reallyExists(impSym1)) { - if (imports1.head.isExplicitImport(name)) { - if (imports.head.isExplicitImport(name) || - imports1.head.depth != imports.head.depth) ambiguousImport() - impSym = impSym1 - imports = imports1 - } else if (!imports.head.isExplicitImport(name) && - imports1.head.depth == imports.head.depth) ambiguousImport() - } - imports1 = imports1.tail - } - defSym = impSym - val qual0 = imports.head.qual - if (!(shortenImports && qual0.symbol.isPackage)) // optimization: don't write out package prefixes - qual = atPos(tree.pos.focusStart)(resetPos(qual0.duplicate)) - pre = qual.tpe - } - else if (settings.exposeEmptyPackage.value && checkEmptyPackage()) - log("Allowing empty package member " + name + " due to settings.") - else { - if ((mode & QUALmode) != 0) { - val lastTry = rootMirror.missingHook(rootMirror.RootClass, name) - if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) - } - if (settings.debug.value) { - log(context.imports)//debug - } - if (inaccessibleSym eq NoSymbol) { - // Avoiding some spurious error messages: see SI-2388. - if (reporter.hasErrors && (name startsWith tpnme.ANON_CLASS_NAME)) () - else identError(SymbolNotFoundError(tree, name, context.owner, startingIdentContext)) - } else - identError(InferErrorGen.AccessError( - tree, inaccessibleSym, context.enclClass.owner.thisType, context.enclClass.owner, - inaccessibleExplanation - )) - defSym = context.owner.newErrorSymbol(name) - } - } - } - if (errorContainer != null) { - ErrorUtils.issueTypeError(errorContainer) - setError(tree) - } else { - if (defSym.owner.isPackageClass) - pre = defSym.owner.thisType - - // Inferring classOf type parameter from expected type. - if (defSym.isThisSym) { - typed1(This(defSym.owner) setPos tree.pos, mode, pt) - } + case LookupSucceeded(qual, sym) => + (// this -> Foo.this + if (sym.isThisSym) + typed1(This(sym.owner) setPos tree.pos, mode, pt) // Inferring classOf type parameter from expected type. Otherwise an // actual call to the stubbed classOf method is generated, returning null. - else if (isPredefMemberNamed(defSym, nme.classOf) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) + else if (isPredefMemberNamed(sym, nme.classOf) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) typedClassOf(tree, TypeTree(pt.typeArgs.head)) else { - val tree1 = ( - if (qual == EmptyTree) tree - // atPos necessary because qualifier might come from startContext - else atPos(tree.pos)(Select(qual, name) setAttachments tree.attachments) - ) - val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) - // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right? - val tree3 = stabilize(tree2, pre2, mode, pt) + val pre1 = if (sym.isTopLevel) sym.owner.thisType else if (qual == EmptyTree) NoPrefix else qual.tpe + val tree1 = if (qual == EmptyTree) tree else atPos(tree.pos)(Select(atPos(tree.pos.focusStart)(qual), name)) + val (tree2, pre2) = makeAccessible(tree1, sym, pre1, qual) // SI-5967 Important to replace param type A* with Seq[A] when seen from from a reference, to avoid // inference errors in pattern matching. - tree3 setType dropRepeatedParamType(tree3.tpe) + stabilize(tree2, pre2, mode, pt) modifyType dropIllegalStarTypes + }) setAttachments tree.attachments } } - } def typedIdentOrWildcard(tree: Ident) = { val name = tree.name if (Statistics.canEnable) Statistics.incCounter(typedIdentCount) - if ((name == nme.WILDCARD && (mode & (PATTERNmode | FUNmode)) == PATTERNmode) || - (name == tpnme.WILDCARD && (mode & TYPEmode) != 0)) + if ((name == nme.WILDCARD && mode.inPatternNotFunMode) || + (name == tpnme.WILDCARD && mode.inAll(TYPEmode))) tree setType makeFullyDefined(pt) else typedIdent(tree, name) @@ -5224,7 +4923,7 @@ trait Typers extends Modes with Adaptations with Tags { val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) if (tpt1.isErrorTyped) { tpt1 - } else if (!tpt1.hasSymbol) { + } else if (!tpt1.hasSymbolField) { AppliedTypeNoParametersError(tree, tpt1.tpe) } else { val tparams = tpt1.symbol.typeParams @@ -5282,27 +4981,6 @@ trait Typers extends Modes with Adaptations with Tags { treeCopy.PackageDef(tree, pid1, stats1) setType NoType } - def typedDocDef(docdef: DocDef) = { - if (forScaladoc && (sym ne null) && (sym ne NoSymbol)) { - val comment = docdef.comment - docComments(sym) = comment - comment.defineVariables(sym) - val typer1 = newTyper(context.makeNewScope(tree, context.owner)) - for (useCase <- comment.useCases) { - typer1.silent(_.typedUseCase(useCase)) match { - case SilentTypeError(err) => - unit.warning(useCase.pos, err.errMsg) - case _ => - } - for (useCaseSym <- useCase.defined) { - if (sym.name != useCaseSym.name) - unit.warning(useCase.pos, "@usecase " + useCaseSym.name.decode + " does not match commented symbol: " + sym.name.decode) - } - } - } - typed(docdef.definition, mode, pt) - } - /** * The typer with the correct context for a method definition. If the method is a default getter for * a constructor default, the resulting typer has a constructor context (fixes SI-5543). @@ -5319,7 +4997,7 @@ trait Typers extends Modes with Adaptations with Tags { } def typedStar(tree: Star) = { - if ((mode & STARmode) == 0 && !isPastTyper) + if (mode.inNone(STARmode) && !isPastTyper) StarPatternWithVarargParametersError(tree) treeCopy.Star(tree, typed(tree.elem, mode, pt)) setType makeFullyDefined(pt) } @@ -5340,8 +5018,8 @@ trait Typers extends Modes with Adaptations with Tags { def unbound(t: Tree) = t.symbol == null || t.symbol == NoSymbol cdef.pat match { case Bind(name, i @ Ident(_)) if unbound(i) => warn(name) - case i @ Ident(name) if unbound(i) => warn(name) - case _ => + case i @ Ident(name) if unbound(i) => warn(name) + case _ => } } @@ -5372,16 +5050,16 @@ trait Typers extends Modes with Adaptations with Tags { // that typecheck must not trigger macro expansions, so we explicitly prohibit them // however we cannot do `context.withMacrosDisabled` // because `expr` might contain nested macro calls (see SI-6673) - val exprTyped = typed1(expr updateAttachment SuppressMacroExpansionAttachment, mode, pt) + val exprTyped = typed1(suppressMacroExpansion(expr), mode, pt) exprTyped match { - case macroDef if macroDef.symbol != null && macroDef.symbol.isTermMacro && !macroDef.symbol.isErroneous => + case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(exprTyped) case _ => typedEta(checkDead(exprTyped)) } case Ident(tpnme.WILDCARD_STAR) => - val exprTyped = typed(expr, onlyStickyModes(mode), WildcardType) + val exprTyped = typed(expr, mode.onlySticky, WildcardType) def subArrayType(pt: Type) = if (isPrimitiveValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt) else { @@ -5390,8 +5068,8 @@ trait Typers extends Modes with Adaptations with Tags { } val (exprAdapted, baseClass) = exprTyped.tpe.typeSymbol match { - case ArrayClass => (adapt(exprTyped, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) - case _ => (adapt(exprTyped, onlyStickyModes(mode), seqType(pt)), SeqClass) + case ArrayClass => (adapt(exprTyped, mode.onlySticky, subArrayType(pt)), ArrayClass) + case _ => (adapt(exprTyped, mode.onlySticky, seqType(pt)), SeqClass) } exprAdapted.tpe.baseType(baseClass) match { case TypeRef(_, _, List(elemtp)) => @@ -5402,7 +5080,7 @@ trait Typers extends Modes with Adaptations with Tags { case _ => val tptTyped = typedType(tpt, mode) - val exprTyped = typed(expr, onlyStickyModes(mode), tptTyped.tpe.deconst) + val exprTyped = typed(expr, mode.onlySticky, tptTyped.tpe.deconst) val treeTyped = treeCopy.Typed(tree, exprTyped, tptTyped) if (isPatternMode) { @@ -5434,7 +5112,7 @@ trait Typers extends Modes with Adaptations with Tags { //val undets = context.undetparams // @M: fun is typed in TAPPmode because it is being applied to its actual type parameters - val fun1 = typed(fun, forFunMode(mode) | TAPPmode, WildcardType) + val fun1 = typed(fun, mode.forFunMode | TAPPmode, WildcardType) val tparams = fun1.symbol.typeParams //@M TODO: val undets_fun = context.undetparams ? @@ -5566,7 +5244,7 @@ trait Typers extends Modes with Adaptations with Tags { case tree: TypeDef => typedTypeDef(tree) case tree: LabelDef => labelTyper(tree).typedLabelDef(tree) case tree: PackageDef => typedPackageDef(tree) - case tree: DocDef => typedDocDef(tree) + case tree: DocDef => typedDocDef(tree, mode, pt) case tree: Annotated => typedAnnotated(tree) case tree: SingletonTypeTree => typedSingletonTypeTree(tree) case tree: SelectFromTypeTree => typedSelectFromTypeTree(tree) @@ -5587,13 +5265,7 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** - * @param tree ... - * @param mode ... - * @param pt ... - * @return ... - */ - def typed(tree: Tree, mode: Int, pt: Type): Tree = { + def typed(tree: Tree, mode: Mode, pt: Type): Tree = { lastTreeToTyper = tree indentTyping() @@ -5604,18 +5276,18 @@ trait Typers extends Modes with Adaptations with Tags { try { if (context.retyping && (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))) { - tree.tpe = null - if (tree.hasSymbol) tree.symbol = NoSymbol + tree.clearType() + if (tree.hasSymbolField) tree.symbol = NoSymbol } val alreadyTyped = tree.tpe ne null - var tree1: Tree = if (alreadyTyped) tree else { + val tree1: Tree = if (alreadyTyped) tree else { printTyping( ptLine("typing %s: pt = %s".format(ptTree(tree), ptPlugins), "undetparams" -> context.undetparams, "implicitsEnabled" -> context.implicitsEnabled, "enrichmentEnabled" -> context.enrichmentEnabled, - "mode" -> modeString(mode), + "mode" -> mode, "silent" -> context.bufferErrors, "context.owner" -> context.owner ) @@ -5634,8 +5306,13 @@ trait Typers extends Modes with Adaptations with Tags { ) } - tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, ptPlugins) - val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, ptPlugins, tree) + tree1 modifyType (pluginsTyped(_, this, tree1, mode, ptPlugins)) + val result = + if (tree1.isEmpty) tree1 + else { + val result = adapt(tree1, mode, ptPlugins, tree) + if (hasPendingMacroExpansions) macroExpandAll(this, result) else result + } if (!alreadyTyped) { printTyping("adapted %s: %s to %s, %s".format( @@ -5646,7 +5323,7 @@ trait Typers extends Modes with Adaptations with Tags { result } catch { case ex: TypeError => - tree.tpe = null + tree.clearType() // The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere. printTyping("caught %s: while typing %s".format(ex, tree)) //DEBUG @@ -5671,41 +5348,34 @@ trait Typers extends Modes with Adaptations with Tags { def atOwner(tree: Tree, owner: Symbol): Typer = newTyper(context.make(tree, owner)) - /** Types expression or definition <code>tree</code>. - * - * @param tree ... - * @return ... + /** Types expression or definition `tree`. */ def typed(tree: Tree): Tree = { val ret = typed(tree, EXPRmode, WildcardType) ret } - def typedPos(pos: Position, mode: Int, pt: Type)(tree: Tree) = typed(atPos(pos)(tree), mode, pt) + def typedPos(pos: Position, mode: Mode, pt: Type)(tree: Tree) = typed(atPos(pos)(tree), mode, pt) def typedPos(pos: Position)(tree: Tree) = typed(atPos(pos)(tree)) // TODO: see if this formulation would impose any penalty, since // it makes for a lot less casting. // def typedPos[T <: Tree](pos: Position)(tree: T): T = typed(atPos(pos)(tree)).asInstanceOf[T] - /** Types expression <code>tree</code> with given prototype <code>pt</code>. - * - * @param tree ... - * @param pt ... - * @return ... + /** Types expression `tree` with given prototype `pt`. */ def typed(tree: Tree, pt: Type): Tree = typed(tree, EXPRmode, pt) - /** Types qualifier <code>tree</code> of a select node. - * E.g. is tree occurs in a context like <code>tree.m</code>. + /** Types qualifier `tree` of a select node. + * E.g. is tree occurs in a context like `tree.m`. */ - def typedQualifier(tree: Tree, mode: Int, pt: Type): Tree = + def typedQualifier(tree: Tree, mode: Mode, pt: Type): Tree = typed(tree, EXPRmode | QUALmode | POLYmode | mode & TYPEPATmode, pt) // TR: don't set BYVALmode, since qualifier might end up as by-name param to an implicit - /** Types qualifier <code>tree</code> of a select node. - * E.g. is tree occurs in a context like <code>tree.m</code>. + /** Types qualifier `tree` of a select node. + * E.g. is tree occurs in a context like `tree.m`. */ - def typedQualifier(tree: Tree, mode: Int): Tree = + def typedQualifier(tree: Tree, mode: Mode): Tree = typedQualifier(tree, mode, WildcardType) def typedQualifier(tree: Tree): Tree = typedQualifier(tree, NOmode, WildcardType) @@ -5714,7 +5384,7 @@ trait Typers extends Modes with Adaptations with Tags { def typedOperator(tree: Tree): Tree = typed(tree, EXPRmode | FUNmode | POLYmode | TAPPmode, WildcardType) - /** Types a pattern with prototype <code>pt</code> */ + /** Types a pattern with prototype `pt` */ def typedPattern(tree: Tree, pt: Type): Tree = { // We disable implicits because otherwise some constructs will // type check which should not. The pattern matcher does not @@ -5738,25 +5408,23 @@ trait Typers extends Modes with Adaptations with Tags { } /** Types a (fully parameterized) type tree */ - def typedType(tree: Tree, mode: Int): Tree = - typed(tree, forTypeMode(mode), WildcardType) + def typedType(tree: Tree, mode: Mode): Tree = + typed(tree, mode.forTypeMode, WildcardType) /** Types a (fully parameterized) type tree */ def typedType(tree: Tree): Tree = typedType(tree, NOmode) /** Types a higher-kinded type tree -- pt denotes the expected kind*/ - def typedHigherKindedType(tree: Tree, mode: Int, pt: Type): Tree = + def typedHigherKindedType(tree: Tree, mode: Mode, pt: Type): Tree = if (pt.typeParams.isEmpty) typedType(tree, mode) // kind is known and it's * else typed(tree, HKmode, pt) - def typedHigherKindedType(tree: Tree, mode: Int): Tree = + def typedHigherKindedType(tree: Tree, mode: Mode): Tree = typed(tree, HKmode, WildcardType) - def typedHigherKindedType(tree: Tree): Tree = typedHigherKindedType(tree, NOmode) - /** Types a type constructor tree used in a new or supertype */ - def typedTypeConstructor(tree: Tree, mode: Int): Tree = { - val result = typed(tree, forTypeMode(mode) | FUNmode, WildcardType) + def typedTypeConstructor(tree: Tree, mode: Mode): Tree = { + val result = typed(tree, mode.forTypeMode | FUNmode, WildcardType) // get rid of type aliases for the following check (#1241) result.tpe.dealias match { @@ -5777,7 +5445,7 @@ trait Typers extends Modes with Adaptations with Tags { def computeType(tree: Tree, pt: Type): Type = { // macros employ different logic of `computeType` - assert(!context.owner.isTermMacro, context.owner) + assert(!context.owner.isMacro, context.owner) val tree1 = typed(tree, pt) transformed(tree) = tree1 val tpe = packedType(tree1, context.owner) @@ -5786,8 +5454,8 @@ trait Typers extends Modes with Adaptations with Tags { } def computeMacroDefType(tree: Tree, pt: Type): Type = { - assert(context.owner.isTermMacro, context.owner) - assert(tree.symbol.isTermMacro, tree.symbol) + assert(context.owner.isMacro, context.owner) + assert(tree.symbol.isMacro, tree.symbol) assert(tree.isInstanceOf[DefDef], tree.getClass) val ddef = tree.asInstanceOf[DefDef] @@ -5813,32 +5481,21 @@ trait Typers extends Modes with Adaptations with Tags { case None => op } - def transformedOrTyped(tree: Tree, mode: Int, pt: Type): Tree = transformed.get(tree) match { + def transformedOrTyped(tree: Tree, mode: Mode, pt: Type): Tree = transformed.get(tree) match { case Some(tree1) => transformed -= tree; tree1 case None => typed(tree, mode, pt) } - -/* - def convertToTypeTree(tree: Tree): Tree = tree match { - case TypeTree() => tree - case _ => TypeTree(tree.tpe) } -*/ } -} object TypersStats { import scala.reflect.internal.TypesStats._ - import scala.reflect.internal.BaseTypeSeqsStats._ val typedIdentCount = Statistics.newCounter("#typechecked identifiers") val typedSelectCount = Statistics.newCounter("#typechecked selections") val typedApplyCount = Statistics.newCounter("#typechecked applications") val rawTypeFailed = Statistics.newSubCounter (" of which in failed", rawTypeCount) val subtypeFailed = Statistics.newSubCounter(" of which in failed", subtypeCount) val findMemberFailed = Statistics.newSubCounter(" of which in failed", findMemberCount) - val compoundBaseTypeSeqCount = Statistics.newSubCounter(" of which for compound types", baseTypeSeqCount) - val typerefBaseTypeSeqCount = Statistics.newSubCounter(" of which for typerefs", baseTypeSeqCount) - val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) val failedSilentNanos = Statistics.newSubTimer("time spent in failed", typerNanos) val failedApplyNanos = Statistics.newSubTimer(" failed apply", typerNanos) val failedOpEqNanos = Statistics.newSubTimer(" failed op=", typerNanos) diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index b51dc0ccd5..589e5ce6fd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -23,7 +23,6 @@ trait Unapplies extends ast.TreeDSL private val unapplyParamName = nme.x_0 - // In the typeCompleter (templateSig) of a case class (resp it's module), // synthetic `copy` (reps `apply`, `unapply`) methods are added. To compute // their signatures, the corresponding ClassDef is needed. During naming (in @@ -46,17 +45,6 @@ trait Unapplies extends ast.TreeDSL } } - /** returns type of the unapply method returning T_0...T_n - * for n == 0, boolean - * for n == 1, Some[T0] - * else Some[Product[Ti]] - */ - def unapplyReturnTypeExpected(argsLength: Int) = argsLength match { - case 0 => BooleanClass.tpe - case 1 => optionType(WildcardType) - case n => optionType(productType((List fill n)(WildcardType))) - } - /** returns unapply or unapplySeq if available */ def unapplyMember(tp: Type): Symbol = (tp member nme.unapply) match { case NoSymbol => tp member nme.unapplySeq @@ -148,7 +136,7 @@ trait Unapplies extends ast.TreeDSL ModuleDef( Modifiers(cdef.mods.flags & AccessFlags | SYNTHETIC, cdef.mods.privateWithin), cdef.name.toTermName, - Template(parents, emptyValDef, NoMods, Nil, ListOfNil, body, cdef.impl.pos.focus)) + Template(parents, emptyValDef, NoMods, Nil, body, cdef.impl.pos.focus)) } private val caseMods = Modifiers(SYNTHETIC | CASE) diff --git a/src/compiler/scala/tools/nsc/typechecker/Variances.scala b/src/compiler/scala/tools/nsc/typechecker/Variances.scala deleted file mode 100644 index ea436a71fb..0000000000 --- a/src/compiler/scala/tools/nsc/typechecker/Variances.scala +++ /dev/null @@ -1,94 +0,0 @@ -/* NSC -- new scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package typechecker - -import symtab.Flags.{ VarianceFlags => VARIANCES, _ } - -/** Variances form a lattice, 0 <= COVARIANT <= Variances, 0 <= CONTRAVARIANT <= VARIANCES - */ -trait Variances { - - val global: Global - import global._ - - /** Flip between covariant and contravariant */ - private def flip(v: Int): Int = { - if (v == COVARIANT) CONTRAVARIANT - else if (v == CONTRAVARIANT) COVARIANT - else v - } - - /** Map everything below VARIANCES to 0 */ - private def cut(v: Int): Int = - if (v == VARIANCES) v else 0 - - /** Compute variance of type parameter `tparam` in types of all symbols `sym`. */ - def varianceInSyms(syms: List[Symbol])(tparam: Symbol): Int = - (VARIANCES /: syms) ((v, sym) => v & varianceInSym(sym)(tparam)) - - /** Compute variance of type parameter `tparam` in type of symbol `sym`. */ - def varianceInSym(sym: Symbol)(tparam: Symbol): Int = - if (sym.isAliasType) cut(varianceInType(sym.info)(tparam)) - else varianceInType(sym.info)(tparam) - - /** Compute variance of type parameter `tparam` in all types `tps`. */ - def varianceInTypes(tps: List[Type])(tparam: Symbol): Int = - (VARIANCES /: tps) ((v, tp) => v & varianceInType(tp)(tparam)) - - /** Compute variance of type parameter `tparam` in all type arguments - * <code>tps</code> which correspond to formal type parameters `tparams1`. - */ - def varianceInArgs(tps: List[Type], tparams1: List[Symbol])(tparam: Symbol): Int = { - var v: Int = VARIANCES; - for ((tp, tparam1) <- tps zip tparams1) { - val v1 = varianceInType(tp)(tparam) - v = v & (if (tparam1.isCovariant) v1 - else if (tparam1.isContravariant) flip(v1) - else cut(v1)) - } - v - } - - /** Compute variance of type parameter `tparam` in all type annotations `annots`. */ - def varianceInAttribs(annots: List[AnnotationInfo])(tparam: Symbol): Int = { - (VARIANCES /: annots) ((v, annot) => v & varianceInAttrib(annot)(tparam)) - } - - /** Compute variance of type parameter `tparam` in type annotation `annot`. */ - def varianceInAttrib(annot: AnnotationInfo)(tparam: Symbol): Int = { - varianceInType(annot.atp)(tparam) - } - - /** Compute variance of type parameter <code>tparam</code> in type <code>tp</code>. */ - def varianceInType(tp: Type)(tparam: Symbol): Int = tp match { - case ErrorType | WildcardType | NoType | NoPrefix | ThisType(_) | ConstantType(_) => - VARIANCES - case BoundedWildcardType(bounds) => - varianceInType(bounds)(tparam) - case SingleType(pre, sym) => - varianceInType(pre)(tparam) - case TypeRef(pre, sym, args) => - if (sym == tparam) COVARIANT - // tparam cannot occur in tp's args if tp is a type constructor (those don't have args) - else if (tp.isHigherKinded) varianceInType(pre)(tparam) - else varianceInType(pre)(tparam) & varianceInArgs(args, sym.typeParams)(tparam) - case TypeBounds(lo, hi) => - flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam) - case RefinedType(parents, defs) => - varianceInTypes(parents)(tparam) & varianceInSyms(defs.toList)(tparam) - case MethodType(params, restpe) => - flip(varianceInSyms(params)(tparam)) & varianceInType(restpe)(tparam) - case NullaryMethodType(restpe) => - varianceInType(restpe)(tparam) - case PolyType(tparams, restpe) => - flip(varianceInSyms(tparams)(tparam)) & varianceInType(restpe)(tparam) - case ExistentialType(tparams, restpe) => - varianceInSyms(tparams)(tparam) & varianceInType(restpe)(tparam) - case AnnotatedType(annots, tp, _) => - varianceInAttribs(annots)(tparam) & varianceInType(tp)(tparam) - } -} diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 471e2653cf..c51fc442dc 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -11,7 +11,6 @@ import java.net.URL import scala.collection.{ mutable, immutable } import io.{ File, Directory, Path, Jar, AbstractFile } import scala.reflect.internal.util.StringOps.splitWhere -import scala.reflect.ClassTag import Jar.isJarOrZip import File.pathSeparator import java.net.MalformedURLException @@ -33,10 +32,6 @@ object ClassPath { def lsDir(dir: Directory, filt: String => Boolean = _ => true) = dir.list filter (x => filt(x.name) && (x.isDirectory || isJarOrZip(x))) map (_.path) toList - def basedir(s: String) = - if (s contains File.separator) s.substring(0, s.lastIndexOf(File.separator)) - else "." - if (pattern == "*") lsDir(Directory(".")) else if (pattern endsWith wildSuffix) lsDir(Directory(pattern dropRight 2)) else if (pattern contains '*') { @@ -58,22 +53,6 @@ object ClassPath { /** Split the classpath, apply a transformation function, and reassemble it. */ def map(cp: String, f: String => String): String = join(split(cp) map f: _*) - /** Split the classpath, filter according to predicate, and reassemble. */ - def filter(cp: String, p: String => Boolean): String = join(split(cp) filter p: _*) - - /** Split the classpath and map them into Paths */ - def toPaths(cp: String): List[Path] = split(cp) map (x => Path(x).toAbsolute) - - /** Make all classpath components absolute. */ - def makeAbsolute(cp: String): String = fromPaths(toPaths(cp): _*) - - /** Join the paths as a classpath */ - def fromPaths(paths: Path*): String = join(paths map (_.path): _*) - def fromURLs(urls: URL*): String = fromPaths(urls map (x => Path(x.getPath)) : _*) - - /** Split the classpath and map them into URLs */ - def toURLs(cp: String): List[URL] = toPaths(cp) map (_.toURL) - /** Expand path and possibly expanding stars */ def expandPath(path: String, expandStar: Boolean = true): List[String] = if (expandStar) split(path) flatMap expandS @@ -133,13 +112,6 @@ object ClassPath { for (dir <- expandPath(path, false) ; name <- expandDir(dir) ; entry <- Option(AbstractFile getDirectory name)) yield newClassPath(entry) - def classesAtAllURLS(path: String): List[ClassPath[T]] = - (path split " ").toList flatMap classesAtURL - - def classesAtURL(spec: String) = - for (url <- specToURL(spec).toList ; location <- Option(AbstractFile getURL url)) yield - newClassPath(location) - def classesInExpandedPath(path: String): IndexedSeq[ClassPath[T]] = classesInPathImpl(path, true).toIndexedSeq @@ -216,8 +188,7 @@ abstract class ClassPath[T] { def sourcepaths: IndexedSeq[AbstractFile] /** - * Represents classes which can be loaded with a ClassfileLoader/MsilFileLoader - * and / or a SourcefileLoader. + * Represents classes which can be loaded with a ClassfileLoader and/or SourcefileLoader. */ case class ClassRep(binary: Option[T], source: Option[AbstractFile]) { def name: String = binary match { @@ -408,15 +379,3 @@ class JavaClassPath( containers: IndexedSeq[ClassPath[AbstractFile]], context: JavaContext) extends MergedClassPath[AbstractFile](containers, context) { } - -object JavaClassPath { - def fromURLs(urls: Seq[URL], context: JavaContext): JavaClassPath = { - val containers = { - for (url <- urls ; f = AbstractFile getURL url ; if f != null) yield - new DirectoryClassPath(f, context) - } - new JavaClassPath(containers.toIndexedSeq, context) - } - def fromURLs(urls: Seq[URL]): JavaClassPath = - fromURLs(urls, ClassPath.DefaultJavaContext) -} diff --git a/src/compiler/scala/tools/nsc/util/CommandLineParser.scala b/src/compiler/scala/tools/nsc/util/CommandLineParser.scala index 9cf2c535df..e8f962a9e2 100644 --- a/src/compiler/scala/tools/nsc/util/CommandLineParser.scala +++ b/src/compiler/scala/tools/nsc/util/CommandLineParser.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package util import scala.util.parsing.combinator._ -import scala.util.parsing.input.{ Reader } import scala.util.parsing.input.CharArrayReader.EofCh import scala.collection.mutable.ListBuffer @@ -22,7 +21,6 @@ import scala.collection.mutable.ListBuffer trait ParserUtil extends Parsers { 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 } } } @@ -38,7 +36,6 @@ case class CommandLine( def withUnaryArgs(xs: List[String]) = copy(unaryArguments = xs) def withBinaryArgs(xs: List[String]) = copy(binaryArguments = xs) - def originalArgs = args def assumeBinary = true def enforceArity = true def onlyKnownOptions = false @@ -106,7 +103,6 @@ case class CommandLine( def isSet(arg: String) = args contains arg def get(arg: String) = argMap get arg - def getOrElse(arg: String, orElse: => String) = if (isSet(arg)) apply(arg) else orElse def apply(arg: String) = argMap(arg) override def toString() = "CommandLine(\n%s)\n" format (args map (" " + _ + "\n") mkString) @@ -116,7 +112,6 @@ object CommandLineParser extends RegexParsers with ParserUtil { override def skipWhitespace = false def elemExcept(xs: Elem*): Parser[Elem] = elem("elemExcept", x => x != EofCh && !(xs contains x)) - def elemOf(xs: Elem*): Parser[Elem] = elem("elemOf", xs contains _) def escaped(ch: Char): Parser[String] = "\\" + ch def mkQuoted(ch: Char): Parser[String] = ( elem(ch) !~> rep(escaped(ch) | elemExcept(ch)) <~ ch ^^ (_.mkString) diff --git a/src/compiler/scala/tools/nsc/util/Exceptional.scala b/src/compiler/scala/tools/nsc/util/Exceptional.scala index 34344263e8..1608ffa425 100644 --- a/src/compiler/scala/tools/nsc/util/Exceptional.scala +++ b/src/compiler/scala/tools/nsc/util/Exceptional.scala @@ -3,8 +3,6 @@ package util import java.util.concurrent.ExecutionException import java.lang.reflect.{ InvocationTargetException, UndeclaredThrowableException } -import scala.reflect.internal.util.StringOps._ -import scala.language.implicitConversions object Exceptional { def unwrap(x: Throwable): Throwable = x match { diff --git a/src/compiler/scala/tools/nsc/util/FreshNameCreator.scala b/src/compiler/scala/tools/nsc/util/FreshNameCreator.scala index 5421843438..e877c990f0 100644 --- a/src/compiler/scala/tools/nsc/util/FreshNameCreator.scala +++ b/src/compiler/scala/tools/nsc/util/FreshNameCreator.scala @@ -14,11 +14,6 @@ trait FreshNameCreator { */ def newName(): String def newName(prefix: String): String - - @deprecated("use newName(prefix)", "2.9.0") - def newName(pos: scala.reflect.internal.util.Position, prefix: String): String = newName(prefix) - @deprecated("use newName()", "2.9.0") - def newName(pos: scala.reflect.internal.util.Position): String = newName() } object FreshNameCreator { diff --git a/src/compiler/scala/tools/nsc/util/JavaCharArrayReader.scala b/src/compiler/scala/tools/nsc/util/JavaCharArrayReader.scala index b7ed7903bc..fc3dd2bac2 100644 --- a/src/compiler/scala/tools/nsc/util/JavaCharArrayReader.scala +++ b/src/compiler/scala/tools/nsc/util/JavaCharArrayReader.scala @@ -14,74 +14,32 @@ class JavaCharArrayReader(buf: IndexedSeq[Char], start: Int, /* startline: int, def this(buf: IndexedSeq[Char], decodeUni: Boolean, error: String => Unit) = this(buf, 0, /* 1, 1, */ decodeUni, error) - /** produce a duplicate of this char array reader which starts reading - * at current position, independent of what happens to original reader - */ - def dup: JavaCharArrayReader = clone().asInstanceOf[JavaCharArrayReader] - - /** layout constant - */ - val tabinc = 8 - /** the line and column position of the current character */ var ch: Char = _ var bp = start - var oldBp = -1 - var oldCh: Char = _ - - //private var cline: Int = _ - //private var ccol: Int = _ def cpos = bp var isUnicode: Boolean = _ - var lastLineStartPos: Int = 0 - var lineStartPos: Int = 0 - var lastBlankLinePos: Int = 0 - - private var onlyBlankChars = false - //private var nextline = startline - //private var nextcol = startcol - - private def markNewLine() { - lastLineStartPos = lineStartPos - if (onlyBlankChars) lastBlankLinePos = lineStartPos - lineStartPos = bp - onlyBlankChars = true - //nextline += 1 - //nextcol = 1 - } - - def hasNext: Boolean = if (bp < buf.length) true - else { - false - } - def last: Char = if (bp > start + 2) buf(bp - 2) else ' ' // XML literals + def hasNext = bp < buf.length def next(): Char = { - //cline = nextline - //ccol = nextcol val buf = this.buf.asInstanceOf[collection.mutable.WrappedArray[Char]].array if(!hasNext) { ch = SU return SU // there is an endless stream of SU's at the end } - oldBp = bp - oldCh = ch ch = buf(bp) isUnicode = false bp = bp + 1 ch match { case '\t' => - // nextcol = ((nextcol - 1) / tabinc * tabinc) + tabinc + 1; case CR => if (bp < buf.size && buf(bp) == LF) { ch = LF bp += 1 } - markNewLine() case LF | FF => - markNewLine() case '\\' => def evenSlashPrefix: Boolean = { var p = bp - 2 @@ -90,11 +48,10 @@ class JavaCharArrayReader(buf: IndexedSeq[Char], start: Int, /* startline: int, } def udigit: Int = { val d = digit2int(buf(bp), 16) - if (d >= 0) { bp += 1; /* nextcol = nextcol + 1 */ } + if (d >= 0) bp += 1 else error("error in unicode escape"); d } - // nextcol += 1 if (buf(bp) == 'u' && decodeUni && evenSlashPrefix) { do { bp += 1 //; nextcol += 1 @@ -104,20 +61,10 @@ class JavaCharArrayReader(buf: IndexedSeq[Char], start: Int, /* startline: int, isUnicode = true } case _ => - if (ch > ' ') onlyBlankChars = false - // nextcol += 1 } ch } - def rewind() { - if (oldBp == -1) throw new IllegalArgumentException - bp = oldBp - ch = oldCh - oldBp = -1 - oldCh = 'x' - } - def copy: JavaCharArrayReader = new JavaCharArrayReader(buf, bp, /* nextcol, nextline, */ decodeUni, error) } diff --git a/src/compiler/scala/tools/nsc/util/MsilClassPath.scala b/src/compiler/scala/tools/nsc/util/MsilClassPath.scala deleted file mode 100644 index aa3b7c286d..0000000000 --- a/src/compiler/scala/tools/nsc/util/MsilClassPath.scala +++ /dev/null @@ -1,169 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2006-2013 LAMP/EPFL - * @author Martin Odersky - */ - -// $Id$ - -package scala.tools.nsc -package util - -import java.io.File -import java.net.URL -import java.util.StringTokenizer -import scala.util.Sorting -import scala.collection.mutable -import scala.tools.nsc.io.{ AbstractFile, MsilFile } -import ch.epfl.lamp.compiler.msil.{ Type => MSILType, Assembly } -import ClassPath.{ ClassPathContext, isTraitImplementation } - -/** Keeping the MSIL classpath code in its own file is important to make sure - * we don't accidentally introduce a dependency on msil.jar in the jvm. - */ - -object MsilClassPath { - def collectTypes(assemFile: AbstractFile) = { - var res: Array[MSILType] = MSILType.EmptyTypes - val assem = Assembly.LoadFrom(assemFile.path) - if (assem != null) { - // DeclaringType == null: true for non-inner classes - res = assem.GetTypes() filter (_.DeclaringType == null) - Sorting.stableSort(res, (t1: MSILType, t2: MSILType) => (t1.FullName compareTo t2.FullName) < 0) - } - res - } - - /** On the java side this logic is in PathResolver, but as I'm not really - * up to folding MSIL into that, I am encapsulating it here. - */ - def fromSettings(settings: Settings): MsilClassPath = { - val context = - if (settings.inline.value) new MsilContext - else new MsilContext { override def isValidName(name: String) = !isTraitImplementation(name) } - - import settings._ - new MsilClassPath(assemextdirs.value, assemrefs.value, sourcepath.value, context) - } - - class MsilContext extends ClassPathContext[MsilFile] { - def toBinaryName(rep: MsilFile) = rep.msilType.Name - def newClassPath(assemFile: AbstractFile) = new AssemblyClassPath(MsilClassPath collectTypes assemFile, "", this) - } - - private def assembleEntries(ext: String, user: String, source: String, context: MsilContext): List[ClassPath[MsilFile]] = { - import ClassPath._ - val etr = new mutable.ListBuffer[ClassPath[MsilFile]] - val names = new mutable.HashSet[String] - - // 1. Assemblies from -Xassem-extdirs - for (dirName <- expandPath(ext, expandStar = false)) { - val dir = AbstractFile.getDirectory(dirName) - if (dir ne null) { - for (file <- dir) { - val name = file.name.toLowerCase - if (name.endsWith(".dll") || name.endsWith(".exe")) { - names += name - etr += context.newClassPath(file) - } - } - } - } - - // 2. Assemblies from -Xassem-path - for (fileName <- expandPath(user, expandStar = false)) { - val file = AbstractFile.getFile(fileName) - if (file ne null) { - val name = file.name.toLowerCase - if (name.endsWith(".dll") || name.endsWith(".exe")) { - names += name - etr += context.newClassPath(file) - } - } - } - - def check(n: String) { - if (!names.contains(n)) - throw new AssertionError("Cannot find assembly "+ n + - ". Use -Xassem-extdirs or -Xassem-path to specify its location") - } - check("mscorlib.dll") - check("scalaruntime.dll") - - // 3. Source path - for (dirName <- expandPath(source, expandStar = false)) { - val file = AbstractFile.getDirectory(dirName) - if (file ne null) etr += new SourcePath[MsilFile](file, context) - } - - etr.toList - } -} -import MsilClassPath._ - -/** - * A assembly file (dll / exe) containing classes and namespaces - */ -class AssemblyClassPath(types: Array[MSILType], namespace: String, val context: MsilContext) extends ClassPath[MsilFile] { - def name = { - val i = namespace.lastIndexOf('.') - if (i < 0) namespace - else namespace drop (i + 1) - } - def asURLs = List(new java.net.URL(name)) - def asClasspathString = sys.error("Unknown") // I don't know what if anything makes sense here? - - private lazy val first: Int = { - var m = 0 - var n = types.length - 1 - while (m < n) { - val l = (m + n) / 2 - val res = types(l).FullName.compareTo(namespace) - if (res < 0) m = l + 1 - else n = l - } - if (types(m).FullName.startsWith(namespace)) m else types.length - } - - lazy val classes = { - val cls = new mutable.ListBuffer[ClassRep] - var i = first - while (i < types.length && types(i).Namespace.startsWith(namespace)) { - // CLRTypes used to exclude java.lang.Object and java.lang.String (no idea why..) - if (types(i).Namespace == namespace) - cls += ClassRep(Some(new MsilFile(types(i))), None) - i += 1 - } - cls.toIndexedSeq - } - - lazy val packages = { - val nsSet = new mutable.HashSet[String] - var i = first - while (i < types.length && types(i).Namespace.startsWith(namespace)) { - val subns = types(i).Namespace - if (subns.length > namespace.length) { - // example: namespace = "System", subns = "System.Reflection.Emit" - // => find second "." and "System.Reflection" to nsSet. - val end = subns.indexOf('.', namespace.length + 1) - nsSet += (if (end < 0) subns - else subns.substring(0, end)) - } - i += 1 - } - val xs = for (ns <- nsSet.toList) - yield new AssemblyClassPath(types, ns, context) - - xs.toIndexedSeq - } - - val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq() - - override def toString() = "assembly classpath "+ namespace -} - -/** - * The classpath when compiling with target:msil. Binary files are represented as - * MSILType values. - */ -class MsilClassPath(ext: String, user: String, source: String, context: MsilContext) -extends MergedClassPath[MsilFile](MsilClassPath.assembleEntries(ext, user, source, context), context) { }
\ 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 1f6fa68f57..1d2cc73c6b 100644 --- a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala +++ b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala @@ -46,9 +46,6 @@ trait ScalaClassLoader extends JClassLoader { def create(path: String): AnyRef = tryToInitializeClass[AnyRef](path) map (_.newInstance()) orNull - def constructorsOf[T <: AnyRef : ClassTag]: List[Constructor[T]] = - classTag[T].runtimeClass.getConstructors.toList map (_.asInstanceOf[Constructor[T]]) - /** The actual bytes for a class file, or an empty array if it can't be found. */ def classBytes(className: String): Array[Byte] = classAsStream(className) match { case null => Array() @@ -71,14 +68,6 @@ trait ScalaClassLoader extends JClassLoader { try asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*)) // !!! : AnyRef shouldn't be necessary catch unwrapHandler({ case ex => throw ex }) } - - /** A list comprised of this classloader followed by all its - * (non-null) parent classloaders, if any. - */ - def loaderChain: List[ScalaClassLoader] = this :: (getParent match { - case null => Nil - case p => p.loaderChain - }) } /** Methods for obtaining various classloaders. @@ -99,35 +88,6 @@ object ScalaClassLoader { } def contextLoader = apply(Thread.currentThread.getContextClassLoader) def appLoader = apply(JClassLoader.getSystemClassLoader) - def extLoader = apply(appLoader.getParent) - def bootLoader = apply(null) - def contextChain = loaderChain(contextLoader) - - def pathToErasure[T: ClassTag] = pathToClass(classTag[T].runtimeClass) - def pathToClass(clazz: Class[_]) = clazz.getName.replace('.', JFile.separatorChar) + ".class" - def locate[T: ClassTag] = contextLoader getResource pathToErasure[T] - - /** Tries to guess the classpath by type matching the context classloader - * and its parents, looking for any classloaders which will reveal their - * classpath elements as urls. It it can't find any, creates a classpath - * from the supplied string. - */ - def guessClassPathString(default: String = ""): String = { - val classpathURLs = contextChain flatMap { - case x: HasClassPath => x.classPathURLs - case x: JURLClassLoader => x.getURLs.toSeq - case _ => Nil - } - if (classpathURLs.isEmpty) default - else JavaClassPath.fromURLs(classpathURLs).asClasspathString - } - - def loaderChain(head: JClassLoader) = { - def loop(cl: JClassLoader): List[JClassLoader] = - if (cl == null) Nil else cl :: loop(cl.getParent) - - loop(head) - } def setContext(cl: JClassLoader) = Thread.currentThread.setContextClassLoader(cl) def savingContextLoader[T](body: => T): T = { @@ -142,16 +102,13 @@ object ScalaClassLoader { with HasClassPath { private var classloaderURLs: Seq[URL] = urls - private def classpathString = ClassPath.fromURLs(urls: _*) def classPathURLs: Seq[URL] = classloaderURLs - def classPath: ClassPath[_] = JavaClassPath fromURLs classPathURLs /** Override to widen to public */ override def addURL(url: URL) = { classloaderURLs :+= url super.addURL(url) } - def toLongString = urls.mkString("URLClassLoader(\n ", "\n ", "\n)\n") } def fromURLs(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader = @@ -162,7 +119,6 @@ object ScalaClassLoader { fromURLs(urls) tryToLoadClass name isDefined /** Finding what jar a clazz or instance came from */ - def origin(x: Any): Option[URL] = originOfClass(x.getClass) def originOfClass(x: Class[_]): Option[URL] = Option(x.getProtectionDomain.getCodeSource) flatMap (x => Option(x.getLocation)) } diff --git a/src/compiler/scala/tools/nsc/util/ShowPickled.scala b/src/compiler/scala/tools/nsc/util/ShowPickled.scala index 2b87280c24..759c06dc0f 100644 --- a/src/compiler/scala/tools/nsc/util/ShowPickled.scala +++ b/src/compiler/scala/tools/nsc/util/ShowPickled.scala @@ -7,7 +7,7 @@ package scala.tools package nsc package util -import java.io.{File, FileInputStream, PrintStream} +import java.io.PrintStream import java.lang.Long.toHexString import java.lang.Float.intBitsToFloat import java.lang.Double.longBitsToDouble @@ -94,7 +94,6 @@ object ShowPickled extends Names { case ANNOTATEDtpe => "ANNOTATEDtpe" case ANNOTINFO => "ANNOTINFO" case ANNOTARGARRAY => "ANNOTARGARRAY" - // case DEBRUIJNINDEXtpe => "DEBRUIJNINDEXtpe" case EXISTENTIALtpe => "EXISTENTIALtpe" case TREE => "TREE" case MODIFIERS => "MODIFIERS" diff --git a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala index b103ae9cb0..6997dbd402 100644 --- a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala +++ b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala @@ -6,7 +6,7 @@ package util import java.io.PrintStream /** A simple tracer - * @param out: The print stream where trace info shoul be sent + * @param out: The print stream where trace info should be sent * @param enabled: A condition that must be true for trace info to be produced. */ class SimpleTracer(out: PrintStream, enabled: Boolean = true) { @@ -14,6 +14,5 @@ class SimpleTracer(out: PrintStream, enabled: Boolean = true) { if (enabled) out.println(msg+value) value } - def withOutput(out: PrintStream) = new SimpleTracer(out, enabled) def when(enabled: Boolean): SimpleTracer = new SimpleTracer(out, enabled) } diff --git a/src/compiler/scala/tools/nsc/util/WorkScheduler.scala b/src/compiler/scala/tools/nsc/util/WorkScheduler.scala index b1f4696d3e..4f7a9ff878 100644 --- a/src/compiler/scala/tools/nsc/util/WorkScheduler.scala +++ b/src/compiler/scala/tools/nsc/util/WorkScheduler.scala @@ -7,9 +7,9 @@ class WorkScheduler { type Action = () => Unit - private var todo = new mutable.Queue[Action] - private var throwables = new mutable.Queue[Throwable] - private var interruptReqs = new mutable.Queue[InterruptReq] + private val todo = new mutable.Queue[Action] + private val throwables = new mutable.Queue[Throwable] + private val interruptReqs = new mutable.Queue[InterruptReq] /** Called from server: block until one of todo list, throwables or interruptReqs is nonempty */ def waitForMoreWork() = synchronized { diff --git a/src/compiler/scala/tools/nsc/util/package.scala b/src/compiler/scala/tools/nsc/util/package.scala index d34d4ee092..039fec8605 100644 --- a/src/compiler/scala/tools/nsc/util/package.scala +++ b/src/compiler/scala/tools/nsc/util/package.scala @@ -18,16 +18,9 @@ package object util { type HashSet[T >: Null <: AnyRef] = scala.reflect.internal.util.HashSet[T] val HashSet = scala.reflect.internal.util.HashSet - def onull[T](value: T, orElse: => T): T = if (value == null) orElse else value - /** Apply a function and return the passed value */ def returning[T](x: T)(f: T => Unit): T = { f(x) ; x } - /** Frequency counter */ - def freq[T](xs: Traversable[T]): Map[T, Int] = xs groupBy identity mapValues (_.size) - - def freqrank[T](xs: Traversable[(T, Int)]): List[(Int, T)] = xs.toList map (_.swap) sortBy (-_._1) - /** Execute code and then wait for all non-daemon Threads * created and begun during its execution to complete. */ @@ -54,18 +47,6 @@ package object util { (result, ts2 filterNot (ts1 contains _)) } - /** Given a function and a block of code, evaluates code block, - * calls function with milliseconds elapsed, and returns block result. - */ - def millisElapsedTo[T](f: Long => Unit)(body: => T): T = { - val start = System.currentTimeMillis - val result = body - val end = System.currentTimeMillis - - f(end - start) - result - } - /** Generate a string using a routine that wants to write on a stream. */ def stringFromWriter(writer: PrintWriter => Unit): String = { val stringWriter = new StringWriter() @@ -83,8 +64,19 @@ package object util { } def stackTraceString(ex: Throwable): String = stringFromWriter(ex printStackTrace _) + /** A one line string which contains the class of the exception, the + * message if any, and the first non-Predef location in the stack trace + * (to exclude assert, require, etc.) + */ + def stackTraceHeadString(ex: Throwable): String = { + val frame = ex.getStackTrace.dropWhile(_.getClassName contains "Predef") take 1 mkString "" + val msg = ex.getMessage match { case null | "" => "" ; case s => s"""("$s")""" } + val clazz = ex.getClass.getName.split('.').last + + s"$clazz$msg @ $frame" + } + lazy val trace = new SimpleTracer(System.out) - lazy val errtrace = new SimpleTracer(System.err) @deprecated("Moved to scala.reflect.internal.util.StringOps", "2.10.0") val StringOps = scala.reflect.internal.util.StringOps diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala index d35ac43424..ac50324fa9 100644 --- a/src/compiler/scala/tools/reflect/FastTrack.scala +++ b/src/compiler/scala/tools/reflect/FastTrack.scala @@ -2,7 +2,9 @@ package scala.tools package reflect import scala.reflect.reify.Taggers -import scala.tools.nsc.typechecker.{Analyzer, Macros} +import scala.tools.nsc.typechecker.{ Analyzer, Macros } +import scala.reflect.runtime.Macros.currentMirror +import scala.reflect.api.Universe /** Optimizes system macro expansions by hardwiring them directly to their implementations * bypassing standard reflective load and invoke to avoid the overhead of Java/Scala reflection. @@ -12,30 +14,32 @@ trait FastTrack { import global._ import definitions._ - import scala.language.implicitConversions - private implicit def context2taggers(c0: MacroContext): Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers - private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } = new { val c: c0.type = c0 } with MacroImplementations + import treeInfo.Applied + + private implicit def context2taggers(c0: MacroContext): Taggers { val c: c0.type } = + new { val c: c0.type = c0 } with Taggers + private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } = + new { val c: c0.type = c0 } with MacroImplementations + private def make(sym: Symbol)(pf: PartialFunction[Applied, MacroContext => Tree]) = + sym -> new FastTrackEntry(pf) - implicit def fastTrackEntry2MacroRuntime(entry: FastTrackEntry): MacroRuntime = args => entry.run(args.c) - type FastTrackExpander = PartialFunction[(MacroContext, Tree), Tree] - case class FastTrackEntry(sym: Symbol, expander: FastTrackExpander) { - def validate(c: MacroContext): Boolean = expander.isDefinedAt((c, c.expandee)) - def run(c: MacroContext): Any = { - val result = expander((c, c.expandee)) - c.Expr[Nothing](result)(c.WeakTypeTag.Nothing) + final class FastTrackEntry(pf: PartialFunction[Applied, MacroContext => Tree]) extends (MacroArgs => Any) { + def validate(tree: Tree) = pf isDefinedAt Applied(tree) + def apply(margs: MacroArgs) = { + val MacroArgs(c, args) = margs + // Macros validated that the pf is defined here - and there's not much we could do if it weren't. + c.Expr[Nothing](pf(Applied(c.expandee))(c))(c.WeakTypeTag.Nothing) } } - lazy val fastTrack: Map[Symbol, FastTrackEntry] = { - var registry = Map[Symbol, FastTrackEntry]() - implicit class BindTo(sym: Symbol) { def bindTo(expander: FastTrackExpander): Unit = if (sym != NoSymbol) registry += sym -> FastTrackEntry(sym, expander) } - materializeClassTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List())) => c.materializeClassTag(tt.tpe) } - materializeWeakTypeTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeTypeTag(u, EmptyTree, tt.tpe, concrete = false) } - materializeTypeTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeTypeTag(u, EmptyTree, tt.tpe, concrete = true) } - ApiUniverseReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExpr(c.prefix.tree, EmptyTree, expr) } - ReflectRuntimeCurrentMirror bindTo { case (c, _) => scala.reflect.runtime.Macros.currentMirror(c).tree } - StringContext_f bindTo { case (c, app@Apply(Select(Apply(_, parts), _), args)) => c.macro_StringInterpolation_f(parts, args, app.pos) } - registry - } + /** A map from a set of pre-established macro symbols to their implementations. */ + lazy val fastTrack = Map[Symbol, FastTrackEntry]( + make( materializeClassTag) { case Applied(_, ttag :: Nil, _) => _.materializeClassTag(ttag.tpe) }, + make( materializeWeakTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = false) }, + make( materializeTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = true) }, + make( ApiUniverseReify) { case Applied(_, ttag :: Nil, (expr :: _) :: _) => c => c.materializeExpr(c.prefix.tree, EmptyTree, expr) }, + make( StringContext_f) { case Applied(Select(Apply(_, ps), _), _, args) => c => c.macro_StringInterpolation_f(ps, args.flatten, c.expandee.pos) }, + make(ReflectRuntimeCurrentMirror) { case _ => c => currentMirror(c).tree } + ) } diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala index 86cd845c54..ab967496c4 100644 --- a/src/compiler/scala/tools/reflect/MacroImplementations.scala +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -1,6 +1,5 @@ package scala.tools.reflect -import scala.reflect.macros.{ReificationException, UnexpectedReificationException} import scala.reflect.macros.runtime.Context import scala.collection.mutable.ListBuffer import scala.collection.mutable.Stack @@ -38,7 +37,7 @@ abstract class MacroImplementations { val argsStack = Stack(args : _*) def defval(value: Tree, tpe: Type): Unit = { - val freshName = newTermName(c.fresh("arg$")) + val freshName = newTermName(c.freshName("arg$")) evals += ValDef(Modifiers(), freshName, TypeTree(tpe) setPos value.pos.focus, value) setPos value.pos ids += Ident(freshName) } @@ -147,4 +146,4 @@ abstract class MacroImplementations { Block(evals.toList, atPos(origApplyPos.focus)(expr)) setPos origApplyPos.makeTransparent } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/reflect/ReflectMain.scala b/src/compiler/scala/tools/reflect/ReflectMain.scala index 116ae24cdd..3ae21b6b98 100644 --- a/src/compiler/scala/tools/reflect/ReflectMain.scala +++ b/src/compiler/scala/tools/reflect/ReflectMain.scala @@ -4,7 +4,6 @@ package reflect import scala.tools.nsc.Driver import scala.tools.nsc.Global import scala.tools.nsc.Settings -import scala.tools.nsc.util.ClassPath.DefaultJavaContext import scala.tools.nsc.util.ScalaClassLoader import scala.tools.util.PathResolver @@ -16,4 +15,4 @@ object ReflectMain extends Driver { } override def newCompiler(): Global = new ReflectGlobal(settings, reporter, classloaderFromSettings(settings)) -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/reflect/StdTags.scala b/src/compiler/scala/tools/reflect/StdTags.scala index a3bc9b9bd1..5c62819f04 100644 --- a/src/compiler/scala/tools/reflect/StdTags.scala +++ b/src/compiler/scala/tools/reflect/StdTags.scala @@ -1,7 +1,6 @@ package scala.tools package reflect -import java.lang.{Class => jClass} import scala.reflect.{ClassTag, classTag} import scala.reflect.api.{Mirror, TypeCreator, Universe => ApiUniverse} diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index c05c59d5ff..df9d907377 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -1,13 +1,11 @@ package scala.tools package reflect +import scala.tools.nsc.EXPRmode import scala.tools.nsc.reporters._ 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 scala.tools.nsc.util.FreshNameCreator import scala.reflect.internal.Flags._ import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile} import java.lang.{Class => jClass} @@ -29,8 +27,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, factorySelf.mirror.classLoader) lazy val mirror: u.Mirror = u.runtimeMirror(classLoader) - class ToolBoxGlobal(settings: scala.tools.nsc.Settings, reporter: Reporter) - extends ReflectGlobal(settings, reporter, toolBoxSelf.classLoader) { + class ToolBoxGlobal(settings: scala.tools.nsc.Settings, reporter0: Reporter) + extends ReflectGlobal(settings, reporter0, toolBoxSelf.classLoader) { import definitions._ private val trace = scala.tools.nsc.util.trace when settings.debug.value @@ -73,13 +71,14 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val typed = expr filter (t => t.tpe != null && t.tpe != NoType && !t.isInstanceOf[TypeTree]) if (!typed.isEmpty) throw ToolBoxError("reflective toolbox has failed: cannot operate on trees that are already typed") - val freeTypes = expr.freeTypes - 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 ToolBoxError(msg) + if (expr.freeTypes.nonEmpty) { + val ft_s = expr.freeTypes map (ft => s" ${ft.name} ${ft.origin}") mkString "\n " + throw ToolBoxError(s""" + |reflective toolbox failed due to unresolved free type variables: + |$ft_s + |have you forgotten to use TypeTag annotations for type parameters external to a reifee? + |if you have troubles tracking free type variables, consider using -Xlog-free-types + """.stripMargin.trim) } } @@ -100,9 +99,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => if (namesakes.length > 0) name += ("$" + (namesakes.length + 1)) freeTermNames += (ft -> newTermName(name + nme.REIFY_FREE_VALUE_SUFFIX)) }) - var expr = new Transformer { + val expr = new Transformer { override def transform(tree: Tree): Tree = - if (tree.hasSymbol && tree.symbol.isFreeTerm) { + if (tree.hasSymbolField && tree.symbol.isFreeTerm) { tree match { case Ident(_) => val freeTermRef = Ident(freeTermNames(tree.symbol.asFreeTerm)) @@ -132,7 +131,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) build.setTypeSignature(ownerClass, ClassInfoType(List(ObjectClass.tpe), newScope, ownerClass)) val owner = ownerClass.newLocalDummy(expr.pos) - var currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr, owner)) + val currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(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)) @@ -146,18 +145,18 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => case Block(dummies, unwrapped) => (dummies, unwrapped) case unwrapped => (Nil, unwrapped) } - var invertedIndex = freeTerms map (_.swap) + val invertedIndex = freeTerms map (_.swap) // todo. also fixup singleton types unwrapped = new Transformer { override def transform(tree: Tree): Tree = tree match { - case Ident(name) if invertedIndex contains name => + case Ident(name: TermName) if invertedIndex contains name => Ident(invertedIndex(name)) setType tree.tpe case _ => super.transform(tree) } }.transform(unwrapped) - new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name)))).traverse(unwrapped) + new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name.toTermName)))).traverse(unwrapped) unwrapped = if (expr0.isTerm) unwrapped else unwrapFromTerm(unwrapped) unwrapped } @@ -166,7 +165,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => transformDuringTyper(expr, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)( (currentTyper, expr) => { trace("typing (implicit views = %s, macros = %s): ".format(!withImplicitViewsDisabled, !withMacrosDisabled))(showAttributed(expr, true, true, settings.Yshowsymkinds.value)) - currentTyper.silent(_.typed(expr, analyzer.EXPRmode, pt)) match { + currentTyper.silent(_.typed(expr, EXPRmode, pt)) match { case analyzer.SilentResultValue(result) => trace("success: ")(showAttributed(result, true, true, settings.Yshowsymkinds.value)) result @@ -202,7 +201,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => def wrap(expr0: Tree): ModuleDef = { val (expr, freeTerms) = extractFreeTerms(expr0, wrapFreeTermRefs = true) - val (obj, mclazz) = rootMirror.EmptyPackageClass.newModuleAndClassSymbol( + val (obj, _) = rootMirror.EmptyPackageClass.newModuleAndClassSymbol( nextWrapperModuleName()) val minfo = ClassInfoType(List(ObjectClass.tpe), newScope, obj.moduleClass) @@ -231,12 +230,11 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => emptyValDef, NoMods, List(), - List(List()), List(methdef), NoPosition)) trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) - var cleanedUp = resetLocalAttrs(moduledef) + val cleanedUp = resetLocalAttrs(moduledef) trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymkinds.value)) cleanedUp.asInstanceOf[ModuleDef] } @@ -313,11 +311,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => // reporter doesn't accumulate errors, but the front-end does def throwIfErrors() = { - if (frontEnd.hasErrors) { - var msg = "reflective compilation has failed: " + EOL + EOL - msg += frontEnd.infos map (_.msg) mkString EOL - throw ToolBoxError(msg) - } + if (frontEnd.hasErrors) throw ToolBoxError( + "reflective compilation has failed: " + EOL + EOL + (frontEnd.infos map (_.msg) mkString EOL) + ) } } @@ -337,15 +333,15 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => command.settings.outputDirs setSingleOutput virtualDirectory val instance = new ToolBoxGlobal(command.settings, frontEndToReporter(frontEnd, command.settings)) if (frontEnd.hasErrors) { - var msg = "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL - msg += frontEnd.infos map (_.msg) mkString EOL - throw ToolBoxError(msg) + throw ToolBoxError( + "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL + + (frontEnd.infos map (_.msg) mkString EOL) + ) } instance } catch { case ex: Throwable => - var msg = "reflective compilation has failed: cannot initialize the compiler due to %s".format(ex.toString) - throw ToolBoxError(msg, ex) + throw ToolBoxError(s"reflective compilation has failed: cannot initialize the compiler due to $ex", ex) } } @@ -354,8 +350,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = compiler.withCleanupCaches { if (compiler.settings.verbose.value) println("importing "+tree+", expectedType = "+expectedType) - var ctree: compiler.Tree = importer.importTree(tree) - var cexpectedType: compiler.Type = importer.importType(expectedType) + val ctree: compiler.Tree = importer.importTree(tree) + val cexpectedType: compiler.Type = importer.importType(expectedType) if (compiler.settings.verbose.value) println("typing "+ctree+", expectedType = "+expectedType) val ttree: compiler.Tree = compiler.typeCheck(ctree, cexpectedType, silent = silent, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled) @@ -374,9 +370,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = compiler.withCleanupCaches { if (compiler.settings.verbose.value) println("importing "+pt, ", tree = "+tree+", pos = "+pos) - var ctree: compiler.Tree = importer.importTree(tree) - var cpt: compiler.Type = importer.importType(pt) - var cpos: compiler.Position = importer.importPosition(pos) + val ctree: compiler.Tree = importer.importTree(tree) + val cpt: compiler.Type = importer.importType(pt) + val cpos: compiler.Position = importer.importPosition(pos) if (compiler.settings.verbose.value) println("inferring implicit %s of type %s, macros = %s".format(if (isView) "view" else "value", pt, !withMacrosDisabled)) val itree: compiler.Tree = compiler.inferImplicit(ctree, cpt, isView = isView, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = cpos) @@ -398,9 +394,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => uttree } - def showAttributed(tree: u.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = - compiler.showAttributed(importer.importTree(tree), printTypes, printIds, printKinds) - def parse(code: String): u.Tree = { if (compiler.settings.verbose.value) println("parsing "+code) val ctree: compiler.Tree = compiler.parse(code) @@ -410,7 +403,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => def compile(tree: u.Tree): () => Any = { if (compiler.settings.verbose.value) println("importing "+tree) - var ctree: compiler.Tree = importer.importTree(tree) + val ctree: compiler.Tree = importer.importTree(tree) if (compiler.settings.verbose.value) println("compiling "+ctree) compiler.compile(ctree) diff --git a/src/compiler/scala/tools/reflect/package.scala b/src/compiler/scala/tools/reflect/package.scala index 3f880bf7f8..bf533766d0 100644 --- a/src/compiler/scala/tools/reflect/package.scala +++ b/src/compiler/scala/tools/reflect/package.scala @@ -76,7 +76,6 @@ package object reflect { private[reflect] def frontEndToReporter(frontEnd: FrontEnd, settings0: Settings): Reporter = new AbstractReporter { val settings = settings0 - import frontEnd.{Severity => ApiSeverity} val API_INFO = frontEnd.INFO val API_WARNING = frontEnd.WARNING val API_ERROR = frontEnd.ERROR diff --git a/src/compiler/scala/tools/util/Javap.scala b/src/compiler/scala/tools/util/Javap.scala index c3264d0787..cbfd8fec51 100644 --- a/src/compiler/scala/tools/util/Javap.scala +++ b/src/compiler/scala/tools/util/Javap.scala @@ -6,14 +6,28 @@ package scala.tools package util -import java.lang.reflect.{ GenericSignatureFormatError, Method, Constructor } -import java.lang.{ ClassLoader => JavaClassLoader } +import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } import scala.tools.nsc.util.ScalaClassLoader -import java.io.{ InputStream, PrintWriter, ByteArrayInputStream, FileNotFoundException } -import scala.tools.nsc.io.File -import Javap._ +import scala.tools.nsc.interpreter.IMain +import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, InputStream, + PrintWriter, Writer } +import java.util.{ Locale } +import java.util.regex.Pattern +import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, + ForwardingJavaFileManager, JavaFileManager, JavaFileObject, + SimpleJavaFileObject, StandardLocation } +import scala.reflect.io.{ AbstractFile, Directory, File, Path } +import java.io.{File => JFile} +import scala.io.Source +import scala.util.{ Try, Success, Failure } +import scala.util.Properties.lineSeparator +import scala.collection.JavaConverters +import scala.collection.generic.Clearable +import java.net.URL import scala.language.reflectiveCalls +import Javap._ + trait Javap { def loader: ScalaClassLoader def printWriter: PrintWriter @@ -31,86 +45,641 @@ object NoJavap extends Javap { } class JavapClass( - val loader: ScalaClassLoader = ScalaClassLoader.appLoader, - val printWriter: PrintWriter = new PrintWriter(System.out, true) + val loader: ScalaClassLoader, + val printWriter: PrintWriter, + intp: Option[IMain] = None ) extends Javap { + import JavapTool.ToolArgs + import JavapClass._ - lazy val parser = new JpOptions - - val EnvClass = loader.tryToInitializeClass[FakeEnvironment](Env).orNull - val PrinterClass = loader.tryToInitializeClass[FakePrinter](Printer).orNull - private def failed = (EnvClass eq null) || (PrinterClass eq null) - - val PrinterCtr = ( - if (failed) null - else PrinterClass.getConstructor(classOf[InputStream], classOf[PrintWriter], EnvClass) - ) - - def findBytes(path: String): Array[Byte] = - tryFile(path) getOrElse tryClass(path) + lazy val tool = JavapTool() + /** Run the tool. Option args start with "-". + * The default options are "-protected -verbose". + * Byte data for filename args is retrieved with findBytes. + */ def apply(args: Seq[String]): List[JpResult] = { - if (failed) Nil - else args.toList filterNot (_ startsWith "-") map { path => - val bytes = findBytes(path) - if (bytes.isEmpty) new JpError("Could not find class bytes for '%s'".format(path)) - else new JpSuccess(newPrinter(new ByteArrayInputStream(bytes), newEnv(args))) - } + val (options, claases) = args partition (s => (s startsWith "-") && s.length > 1) + val (flags, upgraded) = upgrade(options) + import flags.{ app, fun, help, raw } + val targets = if (fun && !help) FunFinder(loader, intp).funs(claases) else claases + if (help || claases.isEmpty) List(JpResult(JavapTool.helper(printWriter))) + else if (targets.isEmpty) List(JpResult("No anonfuns found.")) + else tool(raw, upgraded)(targets map (claas => claas -> bytesFor(claas, app))) } - def newPrinter(in: InputStream, env: FakeEnvironment): FakePrinter = - if (failed) null - else PrinterCtr.newInstance(in, printWriter, env) - - def newEnv(opts: Seq[String]): FakeEnvironment = { - lazy val env: FakeEnvironment = EnvClass.newInstance() + /** Cull our tool options. */ + private def upgrade(options: Seq[String]): (ToolArgs, Seq[String]) = ToolArgs fromArgs options match { + case (t,s) if s.nonEmpty => (t,s) + case (t,s) => (t, JavapTool.DefaultOptions) + } - if (failed) null - else parser(opts) foreach { case (name, value) => - val field = EnvClass getDeclaredField name - field setAccessible true - field.set(env, value.asInstanceOf[AnyRef]) + /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). */ + private def bytesFor(path: String, app: Boolean) = Try { + def last = intp.get.mostRecentVar // fail if no intp + def req = if (path == "-") last else { + val s = path.splitHashMember + if (s._1.nonEmpty) s._1 + else s._2 getOrElse "#" } - - env + def asAppBody(s: String) = { + val (cls, fix) = s.splitSuffix + s"${cls}$$delayedInit$$body${fix}" + } + def todo = if (app) asAppBody(req) else req + val bytes = findBytes(todo) + if (bytes.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '${path}'") + else bytes } + def findBytes(path: String): Array[Byte] = tryFile(path) getOrElse tryClass(path) + /** Assume the string is a path and try to find the classfile * it represents. */ - def tryFile(path: String): Option[Array[Byte]] = { - val file = File( - if (path.endsWith(".class")) path - else path.replace('.', '/') + ".class" - ) - if (!file.exists) None - else try Some(file.toByteArray) catch { case x: Exception => None } - } + def tryFile(path: String): Option[Array[Byte]] = + (Try (File(path.asClassResource)) filter (_.exists) map (_.toByteArray)).toOption + /** Assume the string is a fully qualified class name and try to * find the class object it represents. + * There are other symbols of interest, too: + * - a definition that is wrapped in an enclosing class + * - a synthetic that is not in scope but its associated class is */ def tryClass(path: String): Array[Byte] = { - val extName = ( - if (path endsWith ".class") (path dropRight 6).replace('/', '.') - else path + def load(name: String) = loader classBytes name + def loadable(name: String) = loader resourceable name + // if path has an interior dollar, take it as a synthetic + // if the prefix up to the dollar is a symbol in scope, + // result is the translated prefix + suffix + def desynthesize(s: String) = { + val i = s indexOf '$' + if (0 until s.length - 1 contains i) { + val name = s substring (0, i) + val sufx = s substring i + val tran = intp flatMap (_ translatePath name) + def loadableOrNone(strip: Boolean) = { + def suffix(strip: Boolean)(x: String) = + (if (strip && (x endsWith "$")) x.init else x) + sufx + val res = tran map (suffix(strip) _) + if (res.isDefined && loadable(res.get)) res else None + } + // try loading translated+suffix + val res = loadableOrNone(false) + // some synthetics lack a dollar, (e.g., suffix = delayedInit$body) + // so as a hack, if prefix$$suffix fails, also try prefix$suffix + if (res.isDefined) res else loadableOrNone(true) + } else None + } + val p = path.asClassName // scrub any suffix + // if repl, translate the name to something replish + // (for translate, would be nicer to get the sym and ask .isClass, + // instead of translatePath and then asking did I get a class back) + val q = if (intp.isEmpty) p else ( + // only simple names get the scope treatment + Some(p) filter (_ contains '.') + // take path as a Name in scope + orElse (intp flatMap (_ translatePath p) filter loadable) + // take path as a Name in scope and find its enclosing class + orElse (intp flatMap (_ translateEnclosingClass p) filter loadable) + // take path as a synthetic derived from some Name in scope + orElse desynthesize(p) + // just try it plain + getOrElse p ) - loader.classBytes(extName) + load(q) + } + + /** Base class for javap tool adapters for java 6 and 7. */ + abstract class JavapTool { + type ByteAry = Array[Byte] + type Input = Pair[String, Try[ByteAry]] + + /** Run the tool. */ + def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] + + // Since the tool is loaded by reflection, check for catastrophic failure. + protected def failed: Boolean + implicit protected class Failer[A](a: =>A) { + def orFailed[B >: A](b: =>B) = if (failed) b else a + } + protected def noToolError = new JpError(s"No javap tool available: ${getClass.getName} failed to initialize.") + + // output filtering support + val writer = new CharArrayWriter + def written = { + writer.flush() + val w = writer.toString + writer.reset() + w + } + + /** Create a Showable with output massage. + * @param raw show ugly repl names + * @param target attempt to filter output to show region of interest + * @param preamble other messages to output + */ + def showWithPreamble(raw: Boolean, target: String, preamble: String = ""): Showable = new Showable { + // ReplStrippingWriter clips and scrubs on write(String) + // circumvent it by write(mw, 0, mw.length) or wrap it in withoutUnwrapping + def show() = + if (raw && intp.isDefined) intp.get withoutUnwrapping { writeLines() } + else writeLines() + private def writeLines() { + // take Foo# as Foo#apply for purposes of filtering. Useful for -fun Foo#; + // if apply is added here, it's for other than -fun: javap Foo#, perhaps m#? + val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } + var filtering = false // true if in region matching filter + // true to output + def checkFilter(line: String) = if (filterOn.isEmpty) true else { + // cheap heuristic, todo maybe parse for the java sig. + // method sigs end in paren semi + def isAnyMethod = line.endsWith(");") + def isOurMethod = { + val lparen = line.lastIndexOf('(') + val blank = line.lastIndexOf(' ', lparen) + (blank >= 0 && line.substring(blank+1, lparen) == filterOn.get) + } + filtering = if (filtering) { + // next blank line terminates section + // for -public, next line is next method, more or less + line.trim.nonEmpty && !isAnyMethod + } else { + isAnyMethod && isOurMethod + } + filtering + } + for (line <- Source.fromString(preamble + written).getLines; if checkFilter(line)) + printWriter write line+lineSeparator + printWriter.flush() + } + } + } + + class JavapTool6 extends JavapTool { + import JavapTool._ + val EnvClass = loader.tryToInitializeClass[FakeEnvironment](Env).orNull + val PrinterClass = loader.tryToInitializeClass[FakePrinter](Printer).orNull + override protected def failed = (EnvClass eq null) || (PrinterClass eq null) + + val PrinterCtr = PrinterClass.getConstructor(classOf[InputStream], classOf[PrintWriter], EnvClass) orFailed null + val printWrapper = new PrintWriter(writer) + def newPrinter(in: InputStream, env: FakeEnvironment): FakePrinter = + PrinterCtr.newInstance(in, printWrapper, env) orFailed null + def showable(raw: Boolean, target: String, fp: FakePrinter): Showable = { + fp.asInstanceOf[{ def print(): Unit }].print() // run tool and flush to buffer + printWrapper.flush() // just in case + showWithPreamble(raw, target) + } + + lazy val parser = new JpOptions + def newEnv(opts: Seq[String]): FakeEnvironment = { + def result = { + val env: FakeEnvironment = EnvClass.newInstance() + parser(opts) foreach { case (name, value) => + val field = EnvClass getDeclaredField name + field setAccessible true + field.set(env, value.asInstanceOf[AnyRef]) + } + env + } + result orFailed null + } + + override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = + (inputs map { + case (claas, Success(ba)) => JpResult(showable(raw, claas, newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) + case (_, Failure(e)) => JpResult(e.toString) + }).toList orFailed List(noToolError) + } + + class JavapTool7 extends JavapTool { + + import JavapTool._ + type Task = { + def call(): Boolean // true = ok + //def run(args: Array[String]): Int // all args + //def handleOptions(args: Array[String]): Unit // options, then run() or call() + } + // result of Task.run + //object TaskResult extends Enumeration { + // val Ok, Error, CmdErr, SysErr, Abnormal = Value + //} + val TaskClaas = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull + override protected def failed = TaskClaas eq null + + val TaskCtor = TaskClaas.getConstructor( + classOf[Writer], + classOf[JavaFileManager], + classOf[DiagnosticListener[_]], + classOf[JIterable[String]], + classOf[JIterable[String]] + ) orFailed null + + class JavaReporter extends DiagnosticListener[JavaFileObject] with Clearable { + import scala.collection.mutable.{ ArrayBuffer, SynchronizedBuffer } + type D = Diagnostic[_ <: JavaFileObject] + val diagnostics = new ArrayBuffer[D] with SynchronizedBuffer[D] + override def report(d: Diagnostic[_ <: JavaFileObject]) { + diagnostics += d + } + override def clear() = diagnostics.clear() + /** All diagnostic messages. + * @param locale Locale for diagnostic messages, null by default. + */ + def messages(implicit locale: Locale = null) = (diagnostics map (_ getMessage locale)).toList + + def reportable(raw: Boolean): String = { + // don't filter this message if raw, since the names are likely to differ + val container = "Binary file .* contains .*".r + val m = if (raw) messages + else messages filter (_ match { case container() => false case _ => true }) + clear() + if (m.nonEmpty) m mkString ("", lineSeparator, lineSeparator) + else "" + } + } + val reporter = new JavaReporter + + // DisassemblerTool.getStandardFileManager(reporter,locale,charset) + val defaultFileManager: JavaFileManager = + (loader.tryToLoadClass[JavaFileManager]("com.sun.tools.javap.JavapFileManager").get getMethod ( + "create", + classOf[DiagnosticListener[_]], + classOf[PrintWriter] + ) invoke (null, reporter, new PrintWriter(System.err, true))).asInstanceOf[JavaFileManager] orFailed null + + // manages named arrays of bytes, which might have failed to load + class JavapFileManager(val managed: Seq[Input])(delegate: JavaFileManager = defaultFileManager) + extends ForwardingJavaFileManager[JavaFileManager](delegate) { + import JavaFileObject.Kind + import Kind._ + import StandardLocation._ + import JavaFileManager.Location + import java.net.URI + def uri(name: String): URI = new URI(name) // new URI("jfo:" + name) + + def inputNamed(name: String): Try[ByteAry] = (managed find (_._1 == name)).get._2 + def managedFile(name: String, kind: Kind) = kind match { + case CLASS => fileObjectForInput(name, inputNamed(name), kind) + case _ => null + } + // todo: just wrap it as scala abstractfile and adapt it uniformly + def fileObjectForInput(name: String, bytes: Try[ByteAry], kind: Kind): JavaFileObject = + new SimpleJavaFileObject(uri(name), kind) { + override def openInputStream(): InputStream = new ByteArrayInputStream(bytes.get) + // if non-null, ClassWriter wrongly requires scheme non-null + override def toUri: URI = null + override def getName: String = name + // suppress + override def getLastModified: Long = -1L + } + override def getJavaFileForInput(location: Location, className: String, kind: Kind): JavaFileObject = + location match { + case CLASS_PATH => managedFile(className, kind) + case _ => null + } + override def hasLocation(location: Location): Boolean = + location match { + case CLASS_PATH => true + case _ => false + } + } + def fileManager(inputs: Seq[Input]) = new JavapFileManager(inputs)() + + // show tool messages and tool output, with output massage + def showable(raw: Boolean, target: String): Showable = showWithPreamble(raw, target, reporter.reportable(raw)) + + // eventually, use the tool interface + def task(options: Seq[String], claases: Seq[String], inputs: Seq[Input]): Task = { + //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). + //getTask(writer, fileManager, reporter, options.asJava, claases.asJava) + import JavaConverters.asJavaIterableConverter + TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, claases.asJava) + .orFailed (throw new IllegalStateException) + } + // a result per input + private def applyOne(raw: Boolean, options: Seq[String], claas: String, inputs: Seq[Input]): Try[JpResult] = + Try { + task(options, Seq(claas), inputs).call() + } map { + case true => JpResult(showable(raw, claas)) + case _ => JpResult(reporter.reportable(raw)) + } recoverWith { + case e: java.lang.reflect.InvocationTargetException => e.getCause match { + case t: IllegalArgumentException => Success(JpResult(t.getMessage)) // bad option + case x => Failure(x) + } + } lastly { + reporter.clear + } + override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { + case (claas, Success(_)) => applyOne(raw, options, claas, inputs).get + case (_, Failure(e)) => JpResult(e.toString) + }).toList orFailed List(noToolError) + } + + object JavapTool { + // >= 1.7 + val Tool = "com.sun.tools.javap.JavapTask" + + // < 1.7 + val Env = "sun.tools.javap.JavapEnvironment" + val Printer = "sun.tools.javap.JavapPrinter" + // "documentation" + type FakeEnvironment = AnyRef + type FakePrinter = AnyRef + + // support JavapEnvironment + class JpOptions { + private object Access { + final val PRIVATE = 0 + final val PROTECTED = 1 + final val PACKAGE = 2 + final val PUBLIC = 3 + } + private val envActionMap: Map[String, (String, Any)] = { + val map = Map( + "-l" -> (("showLineAndLocal", true)), + "-c" -> (("showDisassembled", true)), + "-s" -> (("showInternalSigs", true)), + "-verbose" -> (("showVerbose", true)), + "-private" -> (("showAccess", Access.PRIVATE)), + "-package" -> (("showAccess", Access.PACKAGE)), + "-protected" -> (("showAccess", Access.PROTECTED)), + "-public" -> (("showAccess", Access.PUBLIC)), + "-all" -> (("showallAttr", true)) + ) + map ++ List( + "-v" -> map("-verbose"), + "-p" -> map("-private") + ) + } + def apply(opts: Seq[String]): Seq[(String, Any)] = { + opts flatMap { opt => + envActionMap get opt match { + case Some(pair) => List(pair) + case _ => + val charOpts = opt.tail.toSeq map ("-" + _) + if (charOpts forall (envActionMap contains _)) + charOpts map envActionMap + else Nil + } + } + } + } + + case class ToolArgs(raw: Boolean = false, help: Boolean = false, app: Boolean = false, fun: Boolean = false) + + object ToolArgs { + def fromArgs(args: Seq[String]): (ToolArgs, Seq[String]) = ((ToolArgs(), Seq[String]()) /: (args flatMap massage)) { + case ((t,others), s) => s match { + case "-fun" => (t copy (fun=true), others) + case "-app" => (t copy (app=true), others) + case "-help" => (t copy (help=true), others) + case "-raw" => (t copy (raw=true), others) + case _ => (t, others :+ s) + } + } + } + + val helps = List( + "usage" -> ":javap [opts] [path or class or -]...", + "-help" -> "Prints this help message", + "-raw" -> "Don't unmangle REPL names", + "-app" -> "Show the DelayedInit body of Apps", + "-fun" -> "Show anonfuns for class or Class#method", + "-verbose/-v" -> "Stack size, number of locals, method args", + "-private/-p" -> "Private classes and members", + "-package" -> "Package-private classes and members", + "-protected" -> "Protected classes and members", + "-public" -> "Public classes and members", + "-l" -> "Line and local variable tables", + "-c" -> "Disassembled code", + "-s" -> "Internal type signatures", + "-sysinfo" -> "System info of class", + "-constants" -> "Static final constants" + ) + + // match prefixes and unpack opts, or -help on failure + def massage(arg: String): Seq[String] = { + require(arg startsWith "-") + // arg matches opt "-foo/-f" if prefix of -foo or exactly -f + val r = """(-[^/]*)(/(-.))?""".r + def maybe(opt: String, s: String): Option[String] = opt match { + // disambiguate by preferring short form + case r(lf,_,sf) if s == sf => Some(sf) + case r(lf,_,sf) if lf startsWith s => Some(lf) + case _ => None + } + def candidates(s: String) = (helps map (h => maybe(h._1, s))).flatten + // one candidate or one single-char candidate + def uniqueOf(maybes: Seq[String]) = { + def single(s: String) = s.length == 2 + if (maybes.length == 1) maybes + else if ((maybes count single) == 1) maybes filter single + else Nil + } + // each optchar must decode to exactly one option + def unpacked(s: String): Try[Seq[String]] = { + val ones = (s drop 1) map { c => + val maybes = uniqueOf(candidates(s"-$c")) + if (maybes.length == 1) Some(maybes.head) else None + } + Try(ones) filter (_ forall (_.isDefined)) map (_.flatten) + } + val res = uniqueOf(candidates(arg)) + if (res.nonEmpty) res + else (unpacked(arg) + getOrElse (Seq("-help"))) // or else someone needs help + } + + def helper(pw: PrintWriter) = new Showable { + def show() = helps foreach (p => pw write "%-12.12s%s%n".format(p._1,p._2)) + } + + val DefaultOptions = List("-protected", "-verbose") + + def isAvailable = Seq(Env, Tool) exists (cn => hasClass(loader, cn)) + + private def hasClass(cl: ScalaClassLoader, cn: String) = cl.tryToInitializeClass[AnyRef](cn).isDefined + + private def isTaskable(cl: ScalaClassLoader) = hasClass(cl, Tool) + + def apply() = if (isTaskable(loader)) new JavapTool7 else new JavapTool6 } } -object Javap { - val Env = "sun.tools.javap.JavapEnvironment" - val Printer = "sun.tools.javap.JavapPrinter" +object JavapClass { + def apply( + loader: ScalaClassLoader = ScalaClassLoader.appLoader, + printWriter: PrintWriter = new PrintWriter(System.out, true), + intp: Option[IMain] = None + ) = new JavapClass(loader, printWriter, intp) + + // We enjoy flexibility in specifying either a fully-qualified class name com.acme.Widget + // or a resource path com/acme/Widget.class; but not widget.out + implicit class MaybeClassLike(val s: String) extends AnyVal { + /* private[this] final val suffix = ".class" */ + private def suffix = ".class" + def asClassName = (s stripSuffix suffix).replace('/', '.') + def asClassResource = if (s endsWith suffix) s else s.replace('.', '/') + suffix + def splitSuffix: (String, String) = if (s endsWith suffix) (s dropRight suffix.length, suffix) else (s, "") + def strippingSuffix(f: String => String): String = + if (s endsWith suffix) f(s dropRight suffix.length) else s + // e.g. Foo#bar. Foo# yields zero-length member part. + def splitHashMember: (String, Option[String]) = { + val i = s lastIndexOf '#' + if (i < 0) (s, None) + //else if (i >= s.length - 1) (s.init, None) + else (s take i, Some(s drop i+1)) + } + } + implicit class ClassLoaderOps(val cl: ClassLoader) extends AnyVal { + private def parentsOf(x: ClassLoader): List[ClassLoader] = if (x == null) Nil else x :: parentsOf(x.getParent) + def parents: List[ClassLoader] = parentsOf(cl) + /* all file locations */ + def locations = { + def alldirs = parents flatMap (_ match { + case ucl: ScalaClassLoader.URLClassLoader => ucl.classPathURLs + case jcl: java.net.URLClassLoader => jcl.getURLs + case _ => Nil + }) + val dirs = for (d <- alldirs; if d.getProtocol == "file") yield Path(new JFile(d.toURI)) + dirs + } + /* only the file location from which the given class is loaded */ + def locate(k: String): Option[Path] = { + Try { + val claas = try cl loadClass k catch { + case _: NoClassDefFoundError => null // let it snow + } + // cf ScalaClassLoader.originOfClass + claas.getProtectionDomain.getCodeSource.getLocation + } match { + case Success(null) => None + case Success(loc) if loc.isFile => Some(Path(new JFile(loc.toURI))) + case _ => None + } + } + /* would classBytes succeed with a nonempty array */ + def resourceable(className: String): Boolean = cl.getResource(className.asClassResource) != null + } + implicit class PathOps(val p: Path) extends AnyVal { + import scala.tools.nsc.io.Jar + def isJar = Jar isJarOrZip p + } + implicit class URLOps(val url: URL) extends AnyVal { + def isFile: Boolean = url.getProtocol == "file" + } + object FunFinder { + def apply(loader: ScalaClassLoader, intp: Option[IMain]) = new FunFinder(loader, intp) + } + class FunFinder(loader: ScalaClassLoader, intp: Option[IMain]) { + + // class k, candidate f without prefix + def isFunOfClass(k: String, f: String) = { + val p = (s"${Pattern quote k}\\$$+anonfun").r + (p findPrefixOf f).nonEmpty + } + // class k, candidate f without prefix, method m + def isFunOfMethod(k: String, m: String, f: String) = { + val p = (s"${Pattern quote k}\\$$+anonfun\\$$${Pattern quote m}\\$$").r + (p findPrefixOf f).nonEmpty + } + def isFunOfTarget(k: String, m: Option[String], f: String) = + if (m.isEmpty) isFunOfClass(k, f) + else isFunOfMethod(k, m.get, f) + def listFunsInAbsFile(k: String, m: Option[String], d: AbstractFile) = { + for (f <- d; if !f.isDirectory && isFunOfTarget(k, m, f.name)) yield f.name + } + // path prefix p, class k, dir d + def listFunsInDir(p: String, k: String, m: Option[String])(d: Directory) = { + val subdir = Path(p) + for (f <- (d / subdir).toDirectory.list; if f.isFile && isFunOfTarget(k, m, f.name)) + yield f.name + } + // path prefix p, class k, jar file f + def listFunsInJar(p: String, k: String, m: Option[String])(f: File) = { + import java.util.jar.JarEntry + import scala.tools.nsc.io.Jar + def maybe(e: JarEntry) = { + val (path, name) = { + val parts = e.getName split "/" + if (parts.length < 2) ("", e.getName) + else (parts.init mkString "/", parts.last) + } + if (path == p && isFunOfTarget(k, m, name)) Some(name) else None + } + (new Jar(f) map maybe).flatten + } + def loadable(name: String) = loader resourceable name + // translated class, optional member, opt member to filter on, whether it is repl output + def translate(s: String): (String, Option[String], Option[String], Boolean) = { + val (k0, m0) = s.splitHashMember + val k = k0.asClassName + val member = m0 filter (_.nonEmpty) // take Foo# as no member, not "" + val filter = m0 flatMap { case "" => Some("apply") case _ => None } // take Foo# as filter on apply + // class is either something replish or available to loader + // $line.$read$$etc$Foo#member + ((intp flatMap (_ translatePath k) filter (loadable) map ((_, member, filter, true))) + // s = "f" and $line.$read$$etc$#f is what we're after, + // ignoring any #member (except take # as filter on #apply) + orElse (intp flatMap (_ translateEnclosingClass k) map ((_, Some(k), filter, true))) + getOrElse (k, member, filter, false)) + } + /** Find the classnames of anonfuns associated with k, + * where k may be an available class or a symbol in scope. + */ + def funsOf(k0: String): Seq[String] = { + // class is either something replish or available to loader + val (k, member, filter, isReplish) = translate(k0) + val splat = k split "\\." + val name = splat.last + val prefix = if (splat.length > 1) splat.init mkString "/" else "" + val pkg = if (splat.length > 1) splat.init mkString "." else "" + // reconstitute an anonfun with a package + // if filtered, add the hash back, e.g. pkg.Foo#bar, pkg.Foo$anon$1#apply + def packaged(s: String) = { + val p = if (pkg.isEmpty) s else s"$pkg.$s" + val pm = filter map (p + "#" + _) + pm getOrElse p + } + // is this translated path in (usually virtual) repl outdir? or loadable from filesystem? + val fs = if (isReplish) { + def outed(d: AbstractFile, p: Seq[String]): Option[AbstractFile] = { + if (p.isEmpty) Option(d) + else Option(d.lookupName(p.head, true)) flatMap (f => outed(f, p.tail)) + } + outed(intp.get.replOutput.dir, splat.init) map { d => + listFunsInAbsFile(name, member, d) map packaged + } + } else { + loader locate k map { w => + if (w.isDirectory) listFunsInDir(prefix, name, member)(w.toDirectory) map packaged + else if (w.isJar) listFunsInJar(prefix, name, member)(w.toFile) map packaged + else Nil + } + } + fs match { + case Some(xs) => xs.to[Seq] // maybe empty + case None => Seq() // nothing found, e.g., junk input + } + } + def funs(ks: Seq[String]) = ks flatMap funsOf _ + } +} - def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = - cl.tryToInitializeClass[AnyRef](Env).isDefined +object Javap { - // "documentation" - type FakeEnvironment = AnyRef - type FakePrinter = AnyRef + def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = JavapClass(cl).JavapTool.isAvailable def apply(path: String): Unit = apply(Seq(path)) - def apply(args: Seq[String]): Unit = new JavapClass() apply args foreach (_.show()) + def apply(args: Seq[String]): Unit = JavapClass() apply args foreach (_.show()) + + trait Showable { + def show(): Unit + } sealed trait JpResult { type ResultType @@ -123,53 +692,23 @@ object Javap { // def methods(): List[String] // def signatures(): List[String] } + object JpResult { + def apply(msg: String) = new JpError(msg) + def apply(res: Showable) = new JpSuccess(res) + } class JpError(msg: String) extends JpResult { type ResultType = String def isError = true def value = msg - def show() = println(msg) + def show() = println(msg) // makes sense for :javap, less for -Ygen-javap } - class JpSuccess(val value: AnyRef) extends JpResult { + class JpSuccess(val value: Showable) extends JpResult { type ResultType = AnyRef def isError = false - def show() = value.asInstanceOf[{ def print(): Unit }].print() - } - - class JpOptions { - private object Access { - final val PRIVATE = 0 - final val PROTECTED = 1 - final val PACKAGE = 2 - final val PUBLIC = 3 - } - private val envActionMap: Map[String, (String, Any)] = { - val map = Map( - "-l" -> (("showLineAndLocal", true)), - "-c" -> (("showDisassembled", true)), - "-s" -> (("showInternalSigs", true)), - "-verbose" -> (("showVerbose", true)), - "-private" -> (("showAccess", Access.PRIVATE)), - "-package" -> (("showAccess", Access.PACKAGE)), - "-protected" -> (("showAccess", Access.PROTECTED)), - "-public" -> (("showAccess", Access.PUBLIC)), - "-all" -> (("showallAttr", true)) - ) - map ++ List( - "-v" -> map("-verbose"), - "-p" -> map("-private") - ) - } - def apply(opts: Seq[String]): Seq[(String, Any)] = { - opts flatMap { opt => - envActionMap get opt match { - case Some(pair) => List(pair) - case _ => - val charOpts = opt.tail.toSeq map ("-" + _) - if (charOpts forall (envActionMap contains _)) - charOpts map envActionMap - else Nil - } - } - } + def show() = value.show() // output to tool's PrintWriter + } + implicit class Lastly[A](val t: Try[A]) extends AnyVal { + private def effect[X](last: =>Unit)(a: X): Try[A] = { last; t } + def lastly(last: =>Unit): Try[A] = t transform (effect(last) _, effect(last) _) } } diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index 0af1011bda..5d79a7d6cd 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -6,7 +6,6 @@ package scala.tools package util -import java.net.{ URL, MalformedURLException } import scala.tools.reflect.WrappedProperties.AccessControl import nsc.{ Settings, GenericRunnerSettings } import nsc.util.{ ClassPath, JavaClassPath, ScalaClassLoader } @@ -19,16 +18,9 @@ import scala.language.postfixOps // https://wiki.scala-lang.org/display/SW/Classpath object PathResolver { - // Imports property/environment functions which suppress - // security exceptions. + // Imports property/environment functions which suppress security exceptions. import AccessControl._ - def firstNonEmpty(xs: String*) = xs find (_ != "") getOrElse "" - - /** Map all classpath elements to absolute paths and reconstruct the classpath. - */ - def makeAbsolute(cp: String) = ClassPath.map(cp, x => Path(x).toAbsolute.path) - /** pretty print class path */ def ppcp(s: String) = split(s) match { case Nil => "" @@ -46,7 +38,6 @@ object PathResolver { /** Environment variables which java pays attention to so it * seems we do as well. */ - def classPathEnv = envOrElse("CLASSPATH", "") def sourcePathEnv = envOrElse("SOURCEPATH", "") def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath) @@ -86,7 +77,6 @@ object PathResolver { def scalaHome = Environment.scalaHome def scalaHomeDir = Directory(scalaHome) - def scalaHomeExists = scalaHomeDir.isDirectory def scalaLibDir = Directory(scalaHomeDir / "lib") def scalaClassesDir = Directory(scalaHomeDir / "classes") @@ -109,15 +99,7 @@ object PathResolver { // classpath as set up by the runner (or regular classpath under -nobootcp) // and then again here. def scalaBootClassPath = "" - // scalaLibDirFound match { - // case Some(dir) if scalaHomeExists => - // val paths = ClassPath expandDir dir.path - // join(paths: _*) - // case _ => "" - // } - def scalaExtDirs = Environment.scalaExtDirs - def scalaPluginPath = (scalaHomeDir / "misc" / "scala-devel" / "plugins").path override def toString = """ @@ -136,7 +118,7 @@ object PathResolver { ) } - def fromPathString(path: String, context: JavaContext = DefaultJavaContext): JavaClassPath = { + def fromPathString(path: String, context: JavaContext = DefaultJavaContext): JavaClassPath = { // called from scalap val s = new Settings() s.classpath.value = path new PathResolver(s, context) result @@ -161,7 +143,7 @@ object PathResolver { } } } -import PathResolver.{ Defaults, Environment, firstNonEmpty, ppcp } +import PathResolver.{ Defaults, Environment, ppcp } class PathResolver(settings: Settings, context: JavaContext) { def this(settings: Settings) = this(settings, if (settings.inline.value) new JavaContext else DefaultJavaContext) |