diff options
Diffstat (limited to 'src')
154 files changed, 2722 insertions, 2300 deletions
diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index c25112941c..b8b5f8033b 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -71,4 +71,9 @@ trait Errors { val msg = "internal error: erroneous reifees are not supported, make sure that your reifee has typechecked successfully before passing it to the reifier" throw new UnexpectedReificationException(defaultErrorPosition, msg) } + + def CannotReifyInvalidLazyVal(tree: ValDef) = { + val msg = "internal error: could not reconstruct original lazy val due to missing accessor" + throw new UnexpectedReificationException(tree.pos, msg) + } } diff --git a/src/compiler/scala/reflect/reify/Phases.scala b/src/compiler/scala/reflect/reify/Phases.scala index 1710cae2a5..5a10ad9282 100644 --- a/src/compiler/scala/reflect/reify/Phases.scala +++ b/src/compiler/scala/reflect/reify/Phases.scala @@ -26,7 +26,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) 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/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index bdcc7383b0..86ad23cd15 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -64,7 +64,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 +86,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/phases/Metalevels.scala b/src/compiler/scala/reflect/reify/phases/Metalevels.scala index fbbd12a42f..4c6ebbb288 100644 --- a/src/compiler/scala/reflect/reify/phases/Metalevels.scala +++ b/src/compiler/scala/reflect/reify/phases/Metalevels.scala @@ -124,7 +124,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/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index b5894e8eb6..9a1732a872 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -46,13 +46,13 @@ trait Reshape { if (discard) hk else ta case classDef @ ClassDef(mods, name, params, impl) => val Template(parents, self, body) = impl - var body1 = trimAccessors(classDef, body) + var body1 = trimAccessors(classDef, reshapeLazyVals(body)) body1 = trimSyntheticCaseClassMembers(classDef, body1) var impl1 = Template(parents, self, body1).copyAttrs(impl) ClassDef(mods, name, params, impl1).copyAttrs(classDef) case moduledef @ ModuleDef(mods, name, impl) => val Template(parents, self, body) = impl - var body1 = trimAccessors(moduledef, body) + var body1 = trimAccessors(moduledef, reshapeLazyVals(body)) body1 = trimSyntheticCaseClassMembers(moduledef, body1) var impl1 = Template(parents, self, body1).copyAttrs(impl) ModuleDef(mods, name, impl1).copyAttrs(moduledef) @@ -60,15 +60,11 @@ trait Reshape { val discardedParents = parents collect { case tt: TypeTree => tt } filter isDiscarded if (reifyDebug && discardedParents.length > 0) println("discarding parents in Template: " + discardedParents.mkString(", ")) val parents1 = parents diff discardedParents - val body1 = trimSyntheticCaseClassCompanions(body) + val body1 = reshapeLazyVals(trimSyntheticCaseClassCompanions(body)) Template(parents1, self, body1).copyAttrs(template) case block @ Block(stats, expr) => - val stats1 = trimSyntheticCaseClassCompanions(stats) + val stats1 = reshapeLazyVals(trimSyntheticCaseClassCompanions(stats)) Block(stats1, expr).copyAttrs(block) - case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isLazy => - if (reifyDebug) println("dropping $lzy in lazy val's name: " + tree) - val name1 = if (name endsWith nme.LAZY_LOCAL) name dropRight nme.LAZY_LOCAL.length else name - ValDef(mods, name1, tpt, rhs).copyAttrs(valdef) case unapply @ UnApply(fun, args) => def extractExtractor(tree: Tree): Tree = { val Apply(fun, args) = tree @@ -248,6 +244,20 @@ trait Reshape { New(TypeTree(ann.atp) setOriginal extractOriginal(ann.original), List(args)) } + private def toPreTyperLazyVal(ddef: DefDef): ValDef = { + def extractRhs(rhs: Tree) = rhs match { + case Block(Assign(lhs, rhs)::Nil, _) if lhs.symbol.isLazy => rhs + case _ => rhs // unit or trait case + } + 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 mods1 = Modifiers(flags1, privateWithin0, annotations0) setPositions mods0.positions + val mods2 = toPreTyperModifiers(mods1, ddef.symbol) + ValDef(mods2, name1, tpt0, extractRhs(rhs0)) + } + private def trimAccessors(deff: Tree, stats: List[Tree]): List[Tree] = { val symdefs = (stats collect { case vodef: ValOrDefDef => vodef } map (vodeff => vodeff.symbol -> vodeff)).toMap val accessors = scala.collection.mutable.Map[ValDef, List[DefDef]]() @@ -270,7 +280,7 @@ trait Reshape { }); var stats1 = stats flatMap { - case vdef @ ValDef(mods, name, tpt, rhs) => + 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 @@ -287,7 +297,9 @@ trait Reshape { val vdef1 = ValDef(mods2, name1, tpt, rhs) if (reifyDebug) println("resetting visibility of field: %s => %s".format(vdef, vdef1)) Some(vdef1) // no copyAttrs here, because new ValDef and old symbols are now out of sync - case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case ddef: DefDef if !ddef.mods.isLazy => + // lazy val accessors are removed in reshapeLazyVals + // as they are needed to recreate lazy vals if (accessors.values.exists(_.contains(ddef))) { if (reifyDebug) println("discarding accessor method: " + ddef) None @@ -301,6 +313,35 @@ trait Reshape { stats1 } + private def reshapeLazyVals(stats: List[Tree]): List[Tree] = { + val lazyvaldefs:Map[Symbol, DefDef] = stats.collect({ case ddef: DefDef if ddef.mods.isLazy => ddef }). + map((ddef: DefDef) => ddef.symbol -> ddef).toMap + // lazy valdef and defdef are in the same block. + // only that valdef needs to have its rhs rebuilt from defdef + stats flatMap (stat => stat match { + case vdef: ValDef if vdef.symbol.isLazy => + if (reifyDebug) println(s"reconstructing original lazy value for $vdef") + val ddefSym = vdef.symbol.lazyAccessor + val vdef1 = lazyvaldefs.get(ddefSym) match { + case Some(ddef) => + toPreTyperLazyVal(ddef) + case None => + if (reifyDebug) println("couldn't find corresponding lazy val accessor") + vdef + } + if (reifyDebug) println(s"reconstructed lazy val is $vdef1") + vdef1::Nil + case ddef: DefDef if ddef.symbol.isLazy => + def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty + if (hasUnitType(ddef.symbol)) { + // since lazy values of type Unit don't have val's + // we need to create them from scratch + toPreTyperLazyVal(ddef) :: Nil + } else Nil + case _ => stat::Nil + }) + } + private def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]): List[Tree] = stats filterNot (memberDef => memberDef.isDef && { val isSynthetic = memberDef.symbol.isSynthetic diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index c6809fb48e..bcb7d494b6 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} @@ -518,7 +519,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 ** diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 12c3338e0d..fb9539e2b2 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -15,7 +15,6 @@ import reporters.{ Reporter, ConsoleReporter } import util.{ Exceptional, ClassPath, MergedClassPath, StatisticsInfo, ScalaClassLoader, returning } import scala.reflect.internal.util.{ NoPosition, 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 @@ -172,7 +171,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) @@ -268,7 +267,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) msg + " in " + (currentTime - start) + "ms" def informComplete(msg: String): Unit = reporter.withoutTruncating(inform(msg)) - def informProgress(msg: String) = if (opt.verbose) inform("[" + msg + "]") + def informProgress(msg: String) = if (settings.verbose.value) inform("[" + msg + "]") def inform[T](msg: String, value: T): T = returning(value)(x => inform(msg + x)) def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) @@ -312,7 +311,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) } @@ -327,7 +326,7 @@ 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) } } @@ -335,54 +334,12 @@ class Global(var currentSettings: Settings, var reporter: 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 @@ -391,11 +348,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 = { @@ -450,7 +404,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 @@ -830,7 +784,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) */ def afterEachPhase[T](op: => T): List[(Phase, T)] = { phaseDescriptors.map(_.ownPhase).filterNot(_ eq NoPhase).foldLeft(List[(Phase, T)]()) { (res, ph) => - val value = afterPhase(ph)(op) + val value = exitingPhase(ph)(op) if (res.nonEmpty && res.head._2 == value) res else ((ph, value)) :: res } reverse @@ -1092,28 +1046,34 @@ 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 + override def isPastTyper = ( + (curRun ne null) + && (currentRun.typerPhase ne null) + && (globalPhase.id > currentRun.typerPhase.id) + ) + // 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) + @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 exitingIcode[T](op: => T): T = exitingPhase(currentRun.icodePhase)(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 enteringSpecialize[T](op: => T): T = enteringPhase(currentRun.specializePhase)(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) def explainContext(c: analyzer.Context): String = ( if (c == null) "" else ( @@ -1152,7 +1112,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(" ") @@ -1168,7 +1128,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) ) ) @@ -1182,7 +1142,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]") } @@ -1490,8 +1450,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 { @@ -1499,7 +1475,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)) @@ -1509,6 +1485,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)) @@ -1528,8 +1507,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) + val sources = dependencyAnalysis calculateFiles _sources.distinct // there is a problem already, e.g. a plugin was passed a bad option if (reporter.hasErrors) return @@ -1576,37 +1554,40 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // progress update informTime(globalPhase.description, startTime) phaseTimings(globalPhase) = currentTime - 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 @@ -1616,7 +1597,7 @@ 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() @@ -1632,7 +1613,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // Reset project if (!stopPhase("namer")) { - atPhase(namerPhase) { + enteringPhase(namerPhase) { resetProjectClasses(RootClass) } } @@ -1648,7 +1629,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) @@ -1672,7 +1653,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 } } @@ -1681,56 +1662,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 }) } @@ -1739,7 +1680,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 @@ -1795,7 +1736,7 @@ 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) } }) @@ -1806,8 +1747,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // 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 forJVM = settings.target.value startsWith "jvm" + override def forMSIL = settings.target.value startsWith "msil" def forInteractive = onlyPresentation def forScaladoc = onlyPresentation def createJavadoc = false diff --git a/src/compiler/scala/tools/nsc/SubComponent.scala b/src/compiler/scala/tools/nsc/SubComponent.scala index fc3316fb7f..2f23a780f4 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/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index 267a5dcefd..be7a6295b4 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -509,7 +509,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 "" diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index fc8228f644..a74b62bf8d 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -22,7 +22,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,7 +52,10 @@ 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) { @@ -89,16 +92,16 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { 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 ) + case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), List(scrut)), List(matcher)) if !settings.XoldPatmat.value => // 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) => + case Block(stats, matchEndDef) if !settings.XoldPatmat.value && (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) => + case Block(outerStats, orig@Block(stats, matchEndDef)) if !settings.XoldPatmat.value && (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 => @@ -109,7 +112,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { def copyBlock(orig: Tree, stats: List[Tree], expr: Tree): Block = Block(stats, expr) def dropSyntheticCatchAll(cases: List[CaseDef]): List[CaseDef] = - if (!opt.virtPatmat) cases + if (settings.XoldPatmat.value) 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 @@ -206,7 +209,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)) } diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index dec7b648ee..f6073cf185 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -116,7 +116,7 @@ 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 superRef: Tree = atPos(superPos)(gen.mkSuperInitCall) val superCall = (superRef /: argss) (Apply.apply) List( atPos(wrappingPos(superPos, lvdefs ::: argss.flatten)) ( @@ -341,7 +341,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => 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 diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index a7da857429..072f4b9ef2 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -389,7 +389,7 @@ self => Nil, ListOfNil, TypeTree(), - Block(List(Apply(gen.mkSuperSelect, Nil)), Literal(Constant(()))) + Block(List(Apply(gen.mkSuperInitCall, Nil)), Literal(Constant(()))) ) // def main diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 69091e4880..5902209898 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -402,7 +402,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 @@ -998,9 +998,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 { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index afafff4a64..c6a38f5be6 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -191,7 +191,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 { @@ -497,7 +497,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 +568,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/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala index 5e3ba5d355..1ab9f5c280 100644 --- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala +++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala @@ -565,7 +565,7 @@ abstract class ScalaPrimitives { import definitions._ val code = getPrimitive(fun) - def elementType = 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/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 2fa9c076dd..d4126f2786 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1163,34 +1163,30 @@ abstract class GenICode extends SubComponent { resCtx } - private def adapt(from: TypeKind, to: TypeKind, ctx: Context, pos: Position): Unit = { - if (!(from <:< to) && !(from == NullReference && to == NothingReference)) { - to match { - case UNIT => - ctx.bb.emit(DROP(from), pos) - debuglog("Dropped an " + from); - - case _ => - debugassert(from != UNIT, "Can't convert from UNIT to " + to + " at: " + pos) - assert(!from.isReferenceType && !to.isReferenceType, - "type error: can't convert from " + from + " to " + to +" in unit " + unit.source + " at " + pos) - - ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), pos) - } - } else if (from == NothingReference) { - ctx.bb.emit(THROW(ThrowableClass)) - ctx.bb.enterIgnoreMode - } else if (from == NullReference) { - ctx.bb.emit(DROP(from)) - ctx.bb.emit(CONSTANT(Constant(null))) + private def adapt(from: TypeKind, to: TypeKind, ctx: Context, pos: Position) { + // An awful lot of bugs explode here - let's leave ourselves more clues. + // A typical example is an overloaded type assigned after typer. + log(s"GenICode#adapt($from, $to, $ctx, $pos)") + + val conforms = (from <:< to) || (from == NullReference && to == NothingReference) + def coerce(from: TypeKind, to: TypeKind) = ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), pos) + def checkAssertions() { + def msg = s"Can't convert from $from to $to in unit ${unit.source} at $pos" + debugassert(from != UNIT, msg) + assert(!from.isReferenceType && !to.isReferenceType, msg) } - else if (from == ThrowableReference && !(ThrowableClass.tpe <:< to.toType)) { - log("Inserted check-cast on throwable to " + to + " at " + pos) - ctx.bb.emit(CHECK_CAST(to)) + if (conforms) from match { + 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 _ => + // widen subrange types + if (from.isIntSizedType && to == LONG) + coerce(INT, LONG) } - else (from, to) match { - case (BYTE, LONG) | (SHORT, LONG) | (CHAR, LONG) | (INT, LONG) => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, LONG))) - case _ => () + else to match { + case UNIT => ctx.bb.emit(DROP(from), pos) // value discarding + case _ => checkAssertions() ; coerce(from, to) // other primitive coercions } } @@ -1906,18 +1902,8 @@ abstract class GenICode extends SubComponent { var handlerCount = 0 - override def toString(): String = { - val buf = new StringBuilder() - buf.append("\tpackage: ").append(packg).append('\n') - buf.append("\tclazz: ").append(clazz).append('\n') - buf.append("\tmethod: ").append(method).append('\n') - buf.append("\tbb: ").append(bb).append('\n') - buf.append("\tlabels: ").append(labels).append('\n') - buf.append("\texception handlers: ").append(handlers).append('\n') - buf.append("\tcleanups: ").append(cleanups).append('\n') - buf.append("\tscope: ").append(scope).append('\n') - buf.toString() - } + override def toString = + s"package $packg { class $clazz { def $method { bb=$bb } } }" def loadException(ctx: Context, exh: ExceptionHandler, pos: Position) = { debuglog("Emitting LOAD_EXCEPTION for class: " + exh.loadExceptionClass) diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala index 0d688d51f2..aa3f4dcb7e 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala @@ -381,10 +381,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) => diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index d43013c644..ffe8c8afe3 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -37,7 +37,7 @@ abstract class ICodes extends AnyRef /** 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 { diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index 0cacce10f9..dc948a3d08 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.") } @@ -139,7 +139,7 @@ trait TypeKinds { self: ICodes => * Here we make the adjustment by rewinding to a pre-erasure state and * sifting through the parents for a class type. */ - def lub0(tk1: TypeKind, tk2: TypeKind): Type = beforeUncurry { + def lub0(tk1: TypeKind, tk2: TypeKind): Type = enteringUncurry { import definitions._ val tp = global.lub(List(tk1.toType, tk2.toType)) val (front, rest) = tp.parents span (_.typeSymbol.isTrait) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index ef3e82a75a..086327934b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -23,7 +23,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 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index f9eeb41e6d..eb38a80d60 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -33,7 +33,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { override def newPhase(p: Phase): Phase = new AsmPhase(p) private def outputDirectory(sym: Symbol): AbstractFile = - settings.outputDirs outputDirFor beforeFlatten(sym.sourceFile) + settings.outputDirs outputDirFor enteringFlatten(sym.sourceFile) private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { var dir = base @@ -81,9 +81,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // 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) + val companionMain = companion.tpe_*.member(nme.main) if (hasJavaMainMethod(companion)) failNoForwarder("companion contains its own main method") @@ -311,7 +311,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { } 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 @@ -579,7 +579,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { * 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) { @@ -604,11 +604,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters { 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), + 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), // 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) } } @@ -681,7 +688,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { 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 } @@ -872,7 +879,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { 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 } @@ -906,7 +913,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { } 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, @@ -1439,7 +1446,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { 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)) @@ -2401,6 +2408,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { import asm.Opcodes (instr.category: @scala.annotation.switch) match { + case icodes.localsCat => def genLocalInstr() = (instr: @unchecked) match { case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0) @@ -2494,7 +2502,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { case icodes.objsCat => def genObjsInstr() = (instr: @unchecked) match { - case BOX(kind) => val MethodNameAndType(mname, mdesc) = jBoxTo(kind) jcode.invokestatic(BoxesRunTime, mname, mdesc) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index cb6156c59c..e11704d694 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -38,7 +38,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with override def newPhase(p: Phase): Phase = new JvmPhase(p) private def outputDirectory(sym: Symbol): AbstractFile = - settings.outputDirs outputDirFor beforeFlatten(sym.sourceFile) + settings.outputDirs outputDirFor enteringFlatten(sym.sourceFile) private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { var dir = base @@ -85,7 +85,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with // succeed or warn that it isn't. hasApproximate && { // Before erasure so we can identify generic mains. - beforeErasure { + enteringErasure { val companion = sym.linkedClassOfClass val companionMain = companion.tpe.member(nme.main) @@ -318,7 +318,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with * 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) { @@ -448,7 +448,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with // it must be a top level class (name contains no $s) def isCandidateForForwarders(sym: Symbol): Boolean = - afterPickler { + exitingPickler { !(sym.name.toString contains '$') && sym.hasModuleFlag && !sym.isImplClass && !sym.isNestedClass } @@ -736,13 +736,13 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with ) def addGenericSignature(jmember: JMember, sym: Symbol, owner: Symbol) { if (needsGenericSignature(sym)) { - val memberTpe = beforeErasure(owner.thisType.memberInfo(sym)) + val memberTpe = enteringErasure(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 normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) val bytecodeTpe = owner.thisType.memberInfo(sym) if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { clasz.cunit.warning(sym.pos, @@ -757,8 +757,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with } } val index = jmember.getConstantPool.addUtf8(sig).toShort - if (opt.verboseDebug) - beforeErasure(println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index)) + if (settings.verbose.value && settings.debug.value) + enteringErasure(println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index)) val buf = ByteBuffer.allocate(2) buf putShort index @@ -834,7 +834,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with innerSym.rawname + innerSym.moduleSuffix // add inner classes which might not have been referenced yet - afterErasure { + exitingErasure { for (sym <- List(clasz.symbol, clasz.symbol.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass) innerClassBuffer += m } @@ -1812,7 +1812,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with * Synthetic locals are skipped. All variables are method-scoped. */ private def genLocalVariableTable(m: IMethod, jcode: JCode) { - val vars = m.locals filterNot (_.sym.isSynthetic) + val vars = m.locals filterNot (_.sym.isArtifact) if (vars.isEmpty) return val pool = jclass.getConstantPool @@ -1989,7 +1989,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with ) 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 diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index f56aa74d53..242b60c769 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -45,8 +45,8 @@ abstract class GenMSIL extends SubComponent { //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 + if( settings.Xshowcls.isSetByUser && (codeGenerator.entryPoint == null) ) { // TODO introduce dedicated setting instead + val entryclass = settings.Xshowcls.value.toString warning("Couldn't find entry class " + entryclass) } @@ -1126,7 +1126,7 @@ abstract class GenMSIL extends SubComponent { } // method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType - val (isDelegateView, paramType, resType) = beforeTyper { + val (isDelegateView, paramType, resType) = enteringTyper { msym.tpe match { case MethodType(params, resultType) if (params.length == 1 && msym.name == nme.view_) => @@ -1731,8 +1731,8 @@ abstract class GenMSIL extends SubComponent { false } - if((entryPoint == null) && opt.showClass.isDefined) { // TODO introduce dedicated setting instead - val entryclass = opt.showClass.get.toString + if((entryPoint == null) && settings.Xshowcls.isSetByUser) { // TODO introduce dedicated setting instead + val entryclass = settings.Xshowcls.value.toString val cfn = cls.symbol.fullName if(cfn == entryclass) { for (m <- cls.methods; if isEntryPoint(m.symbol)) { entryPoint = m.symbol } @@ -1955,7 +1955,7 @@ abstract class GenMSIL extends SubComponent { } // createClassMembers0 private def isTopLevelModule(sym: Symbol): Boolean = - beforeRefchecks { + enteringRefchecks { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index e9fb060dda..5c2c2a37e6 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -193,7 +193,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 +279,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] @@ -584,7 +584,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? */ @@ -1058,8 +1058,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/DependencyAnalysis.scala b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala index cdde768274..ad6ca68fec 100644 --- a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala +++ b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala @@ -145,7 +145,7 @@ trait DependencyAnalysis extends SubComponent with Files { val name = d.toString d.symbol match { case s : ModuleClassSymbol => - val isTopLevelModule = afterPickler { !s.isImplClass && !s.isNestedClass } + val isTopLevelModule = exitingPickler { !s.isImplClass && !s.isNestedClass } if (isTopLevelModule && (s.companionModule != NoSymbol)) { dependencies.emits(source, nameToFile(unit.source.file, name)) @@ -183,7 +183,7 @@ trait DependencyAnalysis extends SubComponent with Files { // 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)) + enteringRefchecks(checkType(tree.symbol.tpe)) } tree match { @@ -191,7 +191,7 @@ trait DependencyAnalysis extends SubComponent with Files { !cdef.symbol.isAnonymousFunction => if (cdef.symbol != NoSymbol) buf += cdef.symbol // was "at erasurePhase.prev" - beforeExplicitOuter { + enteringExplicitOuter { for (s <- cdef.symbol.info.decls) s match { case ts: TypeSymbol if !ts.isClass => @@ -203,7 +203,7 @@ trait DependencyAnalysis extends SubComponent with Files { case ddef: DefDef => // was "at typer.prev" - beforeTyper { checkType(ddef.symbol.tpe) } + enteringTyper { checkType(ddef.symbol.tpe) } super.traverse(tree) case a @ Select(q, n) if ((a.symbol != NoSymbol) && (q.symbol != null)) => // #2556 if (!a.symbol.isConstructor && diff --git a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala index 1c783a7d2c..1248de625d 100644 --- a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala +++ b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala @@ -171,7 +171,7 @@ trait MemberLookup { // and removing NoType classes def cleanupBogusClasses(syms: List[Symbol]) = { syms.filter(_.info != NoType) } - def syms(name: Name) = container.info.nonPrivateMember(name).alternatives + def syms(name: Name) = container.info.nonPrivateMember(name.encodedName).alternatives def termSyms = cleanupBogusClasses(syms(newTermName(name))) def typeSyms = cleanupBogusClasses(syms(newTypeName(name))) diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 73b4341e95..a4ca881230 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -127,18 +127,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 +145,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 = { @@ -199,7 +197,7 @@ 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 @@ -439,12 +437,12 @@ 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() ) @@ -536,29 +534,25 @@ 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 @@ -573,7 +567,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) }} @@ -792,7 +786,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 @@ -889,20 +882,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)) } } @@ -995,10 +980,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,7 +1014,7 @@ 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() // var path = List[Symbol]() diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index 89195020c4..af89978be1 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -98,7 +98,7 @@ trait ModelFactoryImplicitSupport { else { var 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) @@ -109,7 +109,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)) @@ -424,66 +424,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 } @@ -608,4 +594,4 @@ trait ModelFactoryImplicitSupport { false } else true // the member structure is different foo(3, 5) vs foo(3)(5) } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index 924ebb8a3b..822c11307c 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -477,7 +477,6 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member var summaryParsed = false def document(): Body = { - nextChar() val blocks = new mutable.ListBuffer[Block] while (char != endOfText) blocks += block() @@ -559,21 +558,21 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member def code(): Block = { jumpWhitespace() jump("{{{") - readUntil("}}}") + val str = readUntil("}}}") if (char == endOfText) reportError(pos, "unclosed code block") else jump("}}}") blockEnded("code block") - Code(normalizeIndentation(getRead)) + Code(normalizeIndentation(str)) } /** {{{ title ::= ('=' inline '=' | "==" inline "==" | ...) '\n' }}} */ def title(): Block = { jumpWhitespace() - val inLevel = repeatJump("=") + val inLevel = repeatJump('=') val text = inline(check("=" * inLevel)) - val outLevel = repeatJump("=", inLevel) + val outLevel = repeatJump('=', inLevel) if (inLevel != outLevel) reportError(pos, "unbalanced or unclosed heading") blockEnded("heading") @@ -583,7 +582,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member /** {{{ hrule ::= "----" { '-' } '\n' }}} */ def hrule(): Block = { jumpWhitespace() - repeatJump("-") + repeatJump('-') blockEnded("horizontal rule") HorizontalRule() } @@ -621,8 +620,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member } do { - readUntil { char == safeTagMarker || char == endOfText } - val str = getRead() + val str = readUntil { char == safeTagMarker || char == endOfText } nextChar() list += str @@ -660,8 +658,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member else if (check(",,")) subscript() else if (check("[[")) link() else { - readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || isInlineEnd || checkParaEnded || char == endOfLine } - Text(getRead()) + val str = readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || isInlineEnd || checkParaEnded || char == endOfLine } + Text(str) } } @@ -698,9 +696,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member def htmlTag(): HtmlTag = { jump(safeTagMarker) - readUntil(safeTagMarker) + val read = readUntil(safeTagMarker) if (char != endOfText) jump(safeTagMarker) - var read = getRead HtmlTag(read) } @@ -762,14 +759,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member def link(): Inline = { val SchemeUri = """([a-z]+:.*)""".r jump("[[") - var parens = 1 - readUntil { parens += 1; !check("[") } - getRead // clear the buffer + var parens = 2 + repeatJump('[') val start = "[" * parens val stop = "]" * parens //println("link with " + parens + " matching parens") - readUntil { check(stop) || check(" ") } - val target = getRead() + val target = readUntil { check(stop) || check(" ") } val title = if (!check(stop)) Some({ jump(" ") @@ -860,7 +854,6 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member (char == endOfText) || ((char == endOfLine) && { val poff = offset - val pc = char nextChar() // read EOL val ok = { checkSkipInitWhitespace(endOfLine) || @@ -870,7 +863,6 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member checkSkipInitWhitespace('\u003D') } offset = poff - char = pc ok }) } @@ -882,40 +874,31 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member protected sealed class CharReader(buffer: String) { reader => - var char: Char = _ var offset: Int = 0 + def char: Char = + if (offset >= buffer.length) endOfText else buffer charAt offset final def nextChar() { - if (offset >= buffer.length) - char = endOfText - else { - char = buffer charAt offset - offset += 1 - } + offset += 1 } final def check(chars: String): Boolean = { val poff = offset - val pc = char val ok = jump(chars) offset = poff - char = pc ok } def checkSkipInitWhitespace(c: Char): Boolean = { val poff = offset - val pc = char jumpWhitespace() val ok = jump(c) offset = poff - char = pc ok } def checkSkipInitWhitespace(chars: String): Boolean = { val poff = offset - val pc = char jumpWhitespace() val (ok0, chars0) = if (chars.charAt(0) == ' ') @@ -924,20 +907,17 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member (true, chars) val ok = ok0 && jump(chars0) offset = poff - char = pc ok } def countWhitespace: Int = { var count = 0 val poff = offset - val pc = char while (isWhitespace(char) && char != endOfText) { nextChar() count += 1 } offset = poff - char = pc count } @@ -964,38 +944,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member index == chars.length } - final def checkedJump(chars: String): Boolean = { - val poff = offset - val pc = char - val ok = jump(chars) - if (!ok) { - offset = poff - char = pc - } - ok - } - - final def repeatJump(chars: String, max: Int): Int = { + final def repeatJump(c: Char, max: Int = Int.MaxValue): Int = { var count = 0 - var more = true - while (more && count < max) { - if (!checkedJump(chars)) - more = false - else - count += 1 - } - count - } - - final def repeatJump(chars: String): Int = { - var count = 0 - var more = true - while (more) { - if (!checkedJump(chars)) - more = false - else - count += 1 - } + while (jump(c) && count < max) + count += 1 count } @@ -1035,47 +987,41 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member /* READERS */ - private val readBuilder = new mutable.StringBuilder - - final def getRead(): String = { - val bld = readBuilder.toString - readBuilder.clear() - if (bld.length < 6) bld.intern else bld - } - - final def readUntil(ch: Char): Int = { - var count = 0 - while (char != ch && char != endOfText) { - readBuilder += char - nextChar() + final def readUntil(c: Char): String = { + withRead { + while (char != c && char != endOfText) { + nextChar() + } } - count } - final def readUntil(chars: String): Int = { + final def readUntil(chars: String): String = { assert(chars.length > 0) - var count = 0 - val c = chars.charAt(0) - while (!check(chars) && char != endOfText) { - readBuilder += char - nextChar() - while (char != c && char != endOfText) { - readBuilder += char + withRead { + val c = chars.charAt(0) + while (!check(chars) && char != endOfText) { nextChar() + while (char != c && char != endOfText) + nextChar() } } - count } - final def readUntil(pred: => Boolean): Int = { - var count = 0 - while (!pred && char != endOfText) { - readBuilder += char - nextChar() + final def readUntil(pred: => Boolean): String = { + withRead { + while (char != endOfText && !pred) { + nextChar() + } } - count } + private def withRead(read: => Unit): String = { + val start = offset + read + buffer.substring(start, offset) + } + + /* CHARS CLASSES */ def isWhitespace(c: Char) = c == ' ' || c == '\t' diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 01889f4f98..2e2c772a38 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -1042,7 +1042,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) { @@ -1051,6 +1051,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } finally { interruptsEnabled = true } + } // ---------------- Helper classes --------------------------- @@ -1082,7 +1083,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/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala index 40982c62f0..f3d454ad3e 100644 --- a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala @@ -49,7 +49,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana protected def newCompiler(settings: Settings) = new BuilderGlobal(settings) val compiler = newCompiler(settings) - import compiler.{ Symbol, Type, beforeErasure } + import compiler.{ Symbol, Type, enteringErasure } import compiler.dependencyAnalysis.Inherited private case class SymWithHistory(sym: Symbol, befErasure: Type) @@ -161,7 +161,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana isCorrespondingSym(s.sym, sym)) match { case Some(SymWithHistory(oldSym, info)) => val changes = changeSet(oldSym.info, sym) - val changesErasure = beforeErasure(changeSet(info, sym)) + val changesErasure = enteringErasure(changeSet(info, sym)) changesOf(oldSym) = (changes ++ changesErasure).distinct case _ => @@ -332,7 +332,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana for (src <- files; localDefs = compiler.dependencyAnalysis.definitions(src)) { definitions(src) = (localDefs map (s => { this.classes += s.fullName -> src - SymWithHistory(s.cloneSymbol, beforeErasure(s.info.cloneInfo(s))) + SymWithHistory(s.cloneSymbol, enteringErasure(s.info.cloneInfo(s))) })) } this.references = compiler.dependencyAnalysis.references diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala index 0f5777d260..4a8ee6549f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala @@ -63,7 +63,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 864f9bd073..1d6ec77ef2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -65,7 +65,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) import global._ def printAfterTyper(msg: => String) = - intp.reporter printUntruncatedMessage afterTyper(msg) + intp.reporter printUntruncatedMessage exitingTyper(msg) /** Strip NullaryMethodType artifacts. */ private def replInfo(sym: Symbol) = { @@ -342,7 +342,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) // This groups the members by where the symbol is defined val byOwner = syms groupBy (_.owner) - val sortedOwners = byOwner.toList sortBy { case (owner, _) => afterTyper(source.info.baseClasses indexOf owner) } + val sortedOwners = byOwner.toList sortBy { case (owner, _) => exitingTyper(source.info.baseClasses indexOf owner) } sortedOwners foreach { case (owner, members) => diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index e7c56718f7..6d51dc1a39 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -262,7 +262,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends protected def newCompiler(settings: Settings, reporter: Reporter): ReplGlobal = { settings.outputDirs setSingleOutput virtualDirectory settings.exposeEmptyPackage.value = true - new Global(settings, reporter) with ReplGlobal + if (settings.Yrangepos.value) + new Global(settings, reporter) with ReplGlobal with interactive.RangePositions + else + new Global(settings, reporter) with ReplGlobal } /** Parent classloader. Overridable. */ @@ -387,7 +390,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends newSym <- req.definedSymbols get name oldSym <- oldReq.definedSymbols get name.companionName } { - afterTyper(replwarn(s"warning: previously defined $oldSym is not a companion to $newSym.")) + exitingTyper(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.") } @@ -764,7 +767,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val readRoot = getRequiredModule(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 @@ -951,7 +954,7 @@ 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))) @@ -963,10 +966,10 @@ 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 + // typeNames map (x => x -> exitingTyper(resultSymbol.info.nonPrivateDecl(x).tpe)) toMap // } lazy val definedSymbols = ( termNames.map(x => x -> applyToResultMember(x, x => x)) ++ @@ -1059,9 +1062,9 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends else NoType } } - def cleanMemberDecl(owner: Symbol, member: Name): Type = afterTyper { + def cleanMemberDecl(owner: Symbol, member: Name): Type = exitingTyper { normalizeNonPublic { - owner.info.nonPrivateDecl(member).tpe match { + owner.info.nonPrivateDecl(member).tpe_* match { case NullaryMethodType(tp) => tp case tp => tp } @@ -1148,7 +1151,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 + "." ) diff --git a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala b/src/compiler/scala/tools/nsc/interpreter/ISettings.scala index b65a1ac889..762092c08a 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ISettings.scala @@ -44,7 +44,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 14d43bc6d5..91b70726c2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Imports.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Imports.scala @@ -191,5 +191,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/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala index bab3a1e506..9a4be27c76 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala @@ -10,6 +10,7 @@ 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. @@ -47,12 +48,12 @@ 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 memberNamed(s: String) = exitingTyper(effectiveTp member newTermName(s)) def hasMethod(s: String) = memberNamed(s).isMethod // 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 +112,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 = ( @@ -301,16 +302,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 +315,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( diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index bf7204c754..df96a27291 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -209,10 +209,10 @@ trait MemberHandlers { def importedSymbols = individualSymbols ++ wildcardSymbols lazy val individualSymbols: List[Symbol] = - beforePickler(individualNames map (targetType nonPrivateMember _)) + enteringPickler(individualNames map (targetType nonPrivateMember _)) lazy val wildcardSymbols: List[Symbol] = - if (importsWildcard) beforePickler(targetType.nonPrivateMembers.toList) + if (importsWildcard) enteringPickler(targetType.nonPrivateMembers.toList) else Nil /** Complete list of names imported by a wildcard */ diff --git a/src/compiler/scala/tools/nsc/interpreter/Phased.scala b/src/compiler/scala/tools/nsc/interpreter/Phased.scala index 66d748a9f1..83d7012bb1 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Phased.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Phased.scala @@ -68,7 +68,7 @@ trait Phased { 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] = { @@ -121,7 +121,7 @@ trait Phased { def isEmpty = this eq NoPhaseName // Execute some code during this phase. - def apply[T](body: => T): T = atPhase(phase)(body) + def apply[T](body: => T): T = enteringPhase(phase)(body) } case object Parser extends PhaseName diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index 244c04bdf4..4aef54bce5 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -73,7 +73,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)) } diff --git a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala index 5642566cf7..0bf4999fd6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala @@ -57,7 +57,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 @@ -212,6 +212,7 @@ trait TypeStrings { } private def tparamString[T: ru.TypeTag] : String = { + import ru._ def typeArguments: List[ru.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 diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala index 491718bc0d..fdf82ece71 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/settings/AestheticSettings.scala b/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala deleted file mode 100644 index 5d324903e9..0000000000 --- a/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 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/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 3ff7af791b..404f5e6b6e 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -127,6 +127,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") diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 72284cc940..0792a2b95f 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -30,7 +30,8 @@ trait Warnings { warnInaccessible, warnNullaryOverride, warnNullaryUnit, - warnAdaptedArgs + warnAdaptedArgs, + warnInferAny ) // Warning groups. @@ -53,6 +54,7 @@ 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 diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 8fd8dfaf83..42589874fe 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -358,7 +358,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) } } @@ -420,9 +420,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) @@ -512,9 +512,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 } @@ -732,17 +732,19 @@ abstract class ClassfileParser { } accept('>') assert(xs.length > 0, tp) - newExistentialType(existentials.toList, typeRef(pre, classSym, xs.toList)) - } else if (classSym.isMonomorphicType) { + 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 - } else { + } + 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 + val t = typeRef(pre, classSym, eparams.map(_.tpeHK)) + logResult(s"raw type from $classSym")(newExistentialType(eparams, t)) } case tp => assert(sig.charAt(index) != '<', tp) @@ -751,7 +753,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 @@ -784,7 +786,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) @@ -872,7 +874,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.")) @@ -1203,7 +1205,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) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 175c322786..c02503902e 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -174,7 +174,7 @@ abstract class ICodeReader extends ClassfileParser { } else { forceMangledName(name, false) - afterFlatten(rootMirror.getClassByName(name.toTypeName)) + exitingFlatten(rootMirror.getClassByName(name.toTypeName)) } if (sym.isModule) sym.moduleClass diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 29b238c4cb..933c689378 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -68,11 +68,11 @@ abstract class Pickler extends SubComponent { return } - if (!t.isDef && t.hasSymbol && t.symbol.isTermMacro) { + if (!t.isDef && t.hasSymbolField && t.symbol.isTermMacro) { unit.error(t.pos, t.symbol.typeParams.length match { case 0 => "macro has not been expanded" - case 1 => "type parameter not specified" - case _ => "type parameters not specified" + case 1 => "this type parameter must be specified" + case _ => "these type parameters must be specified" }) return } @@ -177,7 +177,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 +235,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 { @@ -568,7 +568,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 +609,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) @@ -1057,8 +1055,6 @@ abstract class Pickler extends SubComponent { 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)) diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 18db1e6ab4..328e4ce71f 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -111,7 +111,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 +196,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) } @@ -231,9 +231,8 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => extends ChangeOwnerTraverser(oldowner, newowner) { override def traverse(tree: Tree) { tree match { - case Return(expr) => - if (tree.symbol == oldowner) tree.symbol = newowner - case _ => + case _: Return => change(tree.symbol) + case _ => } super.traverse(tree) } @@ -318,10 +317,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) } @@ -353,7 +352,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 fa7a53f888..1f353bb31c 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -437,19 +437,31 @@ abstract class CleanUp extends Transform with ast.TreeDSL { * is a value type (int et al.) in which case it must cast to the boxed version * because invoke only returns object and erasure made sure the result is * expected to be an AnyRef. */ - val t: Tree = ad.symbol.tpe match { - case MethodType(mparams, resType) => - assert(params.length == mparams.length, mparams) - - typedPos { - val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe - qual = REF(sym) + val t: Tree = { + val (mparams, resType) = ad.symbol.tpe match { + case MethodType(mparams, resType) => + assert(params.length == mparams.length, ((params, mparams))) + (mparams, resType) + case tpe @ OverloadedType(pre, alts) => + unit.warning(ad.pos, s"Overloaded type reached the backend! This is a bug in scalac.\n Symbol: ${ad.symbol}\n Overloads: $tpe\n Arguments: " + ad.args.map(_.tpe)) + alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match { + case mt @ MethodType(mparams, resType) :: Nil => + unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt) + (mparams, resType) + case _ => + unit.error(ad.pos, "Cannot resolve overload.") + (Nil, NoType) + } + } + typedPos { + val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe + qual = REF(sym) - BLOCK( - VAL(sym) === qual0, - callAsReflective(mparams map (_.tpe), resType) - ) - } + BLOCK( + VAL(sym) === qual0, + callAsReflective(mparams map (_.tpe), resType) + ) + } } /* For testing purposes, the dynamic application's condition diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 4b9585bb93..1db3db9376 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -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 } } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 3ac7dd2a8f..45be0901c3 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -172,7 +172,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 +206,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 @@ -398,7 +398,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) @@ -410,7 +410,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 @@ -440,11 +440,11 @@ abstract class Erasure extends AddInterfaces s"""bridge generated for member ${fulldef(member)} |which overrides ${fulldef(other)} |clashes with definition of $what; - |both have erased type ${afterPostErasure(bridge.tpe)}""".stripMargin) + |both have erased type ${exitingPostErasure(bridge.tpe)}""".stripMargin) } for (bc <- root.baseClasses) { if (settings.debug.value) - afterPostErasure(println( + exitingPostErasure(println( s"""check bridge overrides in $bc ${bc.info.nonPrivateDecl(bridge.name)} ${site.memberType(bridge)} @@ -453,13 +453,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)) } } @@ -470,7 +470,7 @@ abstract class Erasure extends AddInterfaces def checkPair(member: Symbol, other: Symbol) { val otpe = erasure(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) @@ -482,7 +482,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( @@ -497,9 +497,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 } @@ -508,7 +508,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, @@ -519,7 +519,7 @@ 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 @@ -736,7 +736,7 @@ abstract class Erasure extends AddInterfaces 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 _ => @@ -817,7 +817,7 @@ abstract class Erasure extends AddInterfaces (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}") @@ -885,20 +885,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) } @@ -908,7 +908,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) } } @@ -916,16 +916,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 + " " + @@ -944,8 +945,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) @@ -1067,6 +1068,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) @@ -1123,7 +1125,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 (fn.symbol == Any_## || fn.symbol == Object_##) { // This is unattractive, but without it we crash here on ().## because after // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int. @@ -1135,7 +1138,18 @@ 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) @@ -1261,7 +1275,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 77ad65957d..4a0d25fd09 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -83,12 +83,6 @@ abstract class ExplicitOuter extends InfoTransform } } - /** 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 @@ -97,7 +91,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 @@ -173,7 +167,7 @@ abstract class ExplicitOuter extends InfoTransform } 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) @@ -550,13 +544,6 @@ abstract class ExplicitOuter extends InfoTransform } case _ => - if (settings.Xmigration28.value) 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) @@ -565,7 +552,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 6dd937c0ad..14e5e352f7 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -66,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 = imeth.owner.companionModule.info val candidates = extensionNames(imeth) map (companionInfo.decl(_)) filter (_.exists) val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe) @@ -76,13 +76,13 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { } /** This method removes the `$this` argument from the parameter list a method. - * + * * A method may be a `PolyType`, in which case we tear out the `$this` and the class * type params from its nested `MethodType`. * It may be a `MethodType`, either with a curried parameter list in which the first argument * is a `$this` - we just return the rest of the list. * This means that the corresponding symbol was generated during `extmethods`. - * + * * It may also be a `MethodType` in which the `$this` does not appear in a curried parameter list. * The curried lists disappear during `uncurry`, and the methods may be duplicated afterwards, * for instance, during `specialize`. diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index 3bbf429fc2..e2913bea0d 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -20,7 +20,7 @@ abstract class Flatten extends InfoTransform { /** Updates the owning scope with the given symbol; returns the old symbol. */ - private def replaceSymbolInCurrentScope(sym: Symbol): Symbol = afterFlatten { + private def replaceSymbolInCurrentScope(sym: Symbol): Symbol = exitingFlatten { val scope = sym.owner.info.decls val old = scope lookup sym.name andAlso scope.unlink scope enter sym @@ -53,7 +53,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 +67,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) { @@ -123,7 +123,7 @@ abstract class Flatten extends InfoTransform { liftedDefs(sym.enclosingTopLevelClass.owner) += tree EmptyTree case Select(qual, name) if (sym.isStaticModule && !sym.owner.isPackageClass) => - afterFlatten(atPos(tree.pos)(gen.mkAttributedRef(sym))) + exitingFlatten(atPos(tree.pos)(gen.mkAttributedRef(sym))) case _ => tree } diff --git a/src/compiler/scala/tools/nsc/transform/InfoTransform.scala b/src/compiler/scala/tools/nsc/transform/InfoTransform.scala index ab078421b0..897809a063 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/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index 12e2433e0d..481228fb3d 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -94,6 +94,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD } else sym.owner } + debuglog(s"determined enclosing class/dummy/method for lazy val as $enclosingClassOrDummyOrMethod given symbol $sym") val idx = lazyVals(enclosingClassOrDummyOrMethod) lazyVals(enclosingClassOrDummyOrMethod) = idx + 1 val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym) @@ -194,6 +195,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), 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") if (bitmaps.contains(lzyVal)) bitmaps(lzyVal).map(_.owner = defSym) val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) @@ -248,6 +250,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD def mkBlock(stmt: Tree) = BLOCK(stmt, mkSetFlag(bitmapSym, mask, bitmapRef), UNIT) + debuglog(s"create complete lazy def in $methOrClass for $lazyVal") val (block, res) = tree match { case Block(List(assignment), res) if !lazyUnit(lazyVal) => (mkBlock(assignment), res) @@ -275,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 2b0520592b..b54f128961 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 @@ -311,12 +311,12 @@ 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) } @@ -380,7 +380,7 @@ 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) @@ -408,7 +408,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { 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) @@ -438,7 +438,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 @@ -525,7 +525,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) @@ -544,7 +544,7 @@ 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 @@ -703,7 +703,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 } @@ -722,7 +722,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 @@ -804,7 +804,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) @@ -868,7 +868,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { rhs match { case Block(List(assign), returnTree) => val Assign(moduleVarRef, _) = assign - val cond = Apply(Select(moduleVarRef, nme.eq), List(NULL)) + val cond = Apply(Select(moduleVarRef, Object_eq), List(NULL)) mkFastPathBody(clazz, moduleSym, cond, List(assign), List(NULL), returnTree, attrThis, args) case _ => assert(false, "Invalid getter " + rhs + " for module in class " + clazz) @@ -884,7 +884,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { 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)) } @@ -1166,7 +1166,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)) } @@ -1195,7 +1195,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 { @@ -1245,7 +1245,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 0b58292f28..eef8bb31e6 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 479bc2292e..4815eacd40 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/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 0fa50a255b..88c6f8d823 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -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? @@ -178,6 +194,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case class Overload(sym: Symbol, env: TypeEnv) { override def toString = "specialized overload " + sym + " in " + env + 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) = { + assert(!specializedMethod.isOverloaded, specializedMethod.defString) + val om = Overload(specializedMethod, env) + overloads(method) ::= om + om } /** Just to mark uncheckable */ @@ -218,6 +242,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def target = t } + /** 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 { override def isAccessor = true @@ -289,10 +318,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } - /** Return the specialized overload of sym in the given env, if any. */ - def overload(sym: Symbol, env: TypeEnv) = - overloads(sym).find(ov => TypeEnv.includes(ov.env, env)) - /** Return the specialized name of 'sym' in the given environment. It * guarantees the same result regardless of the map order by sorting * type variables alphabetically. @@ -397,7 +422,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { tpes foreach (tp => buf ++= specializedTypeVars(tp)) buf.result } - def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = beforeTyper(specializedTypeVars(sym.info)) + def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = enteringTyper(specializedTypeVars(sym.info)) /** Return the set of @specialized type variables mentioned by the given type. * It only counts type variables that appear: @@ -533,7 +558,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) @@ -553,7 +578,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)) @@ -563,7 +588,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 @@ -585,7 +610,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 @@ -628,7 +653,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { info(om) = if (original.isDeferred) Forward(original) else Implementation(original) typeEnv(om) = env ++ typeEnv(m) // add the environment for any method tparams - overloads(specMember) ::= Overload(om, typeEnv(om)) + newOverload(specMember, om, typeEnv(om)) enterMember(om) } @@ -773,7 +798,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) @@ -791,7 +816,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) @@ -835,7 +860,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { debuglog("%s expands to %s in %s".format(sym, specMember.name.decode, pp(env))) info(specMember) = NormalizedMember(sym) - overloads(sym) ::= Overload(specMember, env) + newOverload(sym, specMember, env) owner.info.decls.enter(specMember) specMember } @@ -870,6 +895,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 } @@ -877,9 +903,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (wasSpec.nonEmpty) debuglog("specialized overload for %s in %s".format(specMember, pp(typeEnv(specMember)))) - overloads(sym) ::= Overload(specMember, spec) + newOverload(sym, specMember, spec) info(specMember) = SpecialOverload(sym, typeEnv(specMember)) - specMember } @@ -899,10 +924,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))) ) } @@ -949,7 +975,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) @@ -962,14 +988,32 @@ 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) 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) } @@ -989,8 +1033,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { SpecialOverride(impl) } ) - overloads(overriding) ::= Overload(om, env) - ifDebug(afterSpecialize(assert( + newOverload(overriding, om, env) + ifDebug(exitingSpecialize(assert( overridden.owner.info.decl(om.name) != NoSymbol, "Could not find " + om.name + " in " + overridden.owner.info.decls)) ) @@ -1106,7 +1150,7 @@ 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) ) @@ -1148,7 +1192,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) { @@ -1292,7 +1336,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) @@ -1377,59 +1421,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 @@ -1440,7 +1497,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)) @@ -1462,63 +1522,17 @@ 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 - case Select(qual, name) => - def transformSelect = { - qual match { - case _: Super if illegalSpecializedInheritance(currentClass) => - val pos = tree.pos - debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.") - debuglog(pos.lineContent) - tree - case _ => + case Select(Super(_, _), _) if illegalSpecializedInheritance(currentClass) => + val pos = tree.pos + debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.\n" + pos.lineContent) + tree - debuglog("specializing Select %s [tree.tpe: %s]".format(symbol.defString, tree.tpe)) - - //log("!!! select " + tree + " -> " + symbol.info + " specTypeVars: " + specializedTypeVars(symbol.info)) - if (specializedTypeVars(symbol.info).nonEmpty && name != nme.CONSTRUCTOR) { - // log("!!! unifying " + (symbol, symbol.tpe) + " and " + (tree, tree.tpe)) - val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) - // log("!!! found env: " + env + "; overloads: " + overloads(symbol)) - if (!env.isEmpty) { - // debuglog("checking for rerouting: " + tree + " with sym.tpe: " + symbol.tpe + " tree.tpe: " + tree.tpe + " env: " + env) - val specMember = overload(symbol, env) - if (specMember.isDefined) { - localTyper.typedOperator(atPos(tree.pos)(Select(transform(qual), specMember.get.sym.name))) - } - else { - val qual1 = transform(qual) - val specMember = qual1.tpe.member(specializedName(symbol, env)).suchThat(_.tpe matches subst(env, symbol.tpe)) - if (specMember ne NoSymbol) { - val tree1 = atPos(tree.pos)(Select(qual1, specMember)) - if (specMember.isMethod) - localTyper.typedOperator(tree1) - else - localTyper.typed(tree1) - } else - treeCopy.Select(tree, qual1, name) - } - } else - super.transform(tree) - } else overloads(symbol).find(_.sym.info =:= symbol.info) match { - case Some(specMember) => - val qual1 = transform(qual) - debuglog("** routing " + tree + " to " + specMember.sym.fullName + " tree: " + Select(qual1, specMember.sym)) - localTyper.typedOperator(atPos(tree.pos)(Select(qual1, specMember.sym))) - case None => - super.transform(tree) - } - } - } - transformSelect + case sel @ Select(_, _) => + transformSelect(sel) case PackageDef(pid, stats) => tree.symbol.info // make sure specializations have been performed @@ -1543,47 +1557,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) @@ -1631,6 +1635,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 @@ -1652,7 +1660,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { deriveValDef(newValDef)(transform) } transformValDef - case _ => super.transform(tree) } @@ -1824,6 +1831,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: @@ -1860,16 +1868,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). diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index d3a5cebea0..5fc5d2127b 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -134,7 +134,7 @@ abstract class UnCurry extends InfoTransform def isByNameRef(tree: Tree) = ( tree.isTerm && !byNameArgs(tree) - && tree.hasSymbolWhich(s => isByNameParamType(s.tpe)) + && tree.hasSymbolWhich(isByName) ) /** Uncurry a type of a tree node. @@ -205,6 +205,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) } } @@ -405,7 +408,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 @@ -435,7 +438,7 @@ abstract class UnCurry extends InfoTransform case _ => EmptyTree } } - afterUncurry { + exitingUncurry { localTyper.typedPos(pos) { gen.mkMethodCall(tree, toArraySym, Nil, List(traversableClassTag(tree.tpe))) } @@ -459,7 +462,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) { @@ -624,7 +627,7 @@ abstract class UnCurry extends InfoTransform treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) } - case Assign(Select(_, _), _) => + case Assign(_: RefTree, _) => withNeedLift(true) { super.transform(tree) } case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => @@ -670,7 +673,7 @@ abstract class UnCurry extends InfoTransform 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 @@ -701,7 +704,7 @@ abstract class UnCurry extends InfoTransform val body = tree.block val catches = tree.catches val finalizer = tree.finalizer - if (opt.virtPatmat) { + if (!settings.XoldPatmat.value) { if (catches exists (cd => !treeInfo.isCatchCase(cd))) debugwarn("VPM BUG! illegal try/catch " + catches) tree diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 399f9a1eac..3526d932d3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -87,10 +87,10 @@ 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) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 7e15cf91a7..508a55e33a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -154,6 +154,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 @@ -233,6 +234,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/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index d78efd8280..7c02f094ed 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -269,9 +269,6 @@ trait ContextErrors { def VolatileValueError(vdef: Tree) = issueNormalTypeError(vdef, "values cannot be volatile") - def FinalVolatileVarError(vdef: Tree) = - issueNormalTypeError(vdef, "final vars cannot be volatile") - def LocalVarUninitializedError(vdef: Tree) = issueNormalTypeError(vdef, "local variables must be initialized") @@ -660,8 +657,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) @@ -838,7 +835,7 @@ trait ContextErrors { // side-effect on the tree, break the overloaded type cycle in infer private def setErrorOnLastTry(lastTry: Boolean, tree: Tree) = if (lastTry) setError(tree) - + def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type, lastTry: Boolean) = { issueNormalTypeError(tree, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) @@ -851,7 +848,7 @@ trait ContextErrors { def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, argtpes: List[Type], pt: Type, lastTry: Boolean) = { - + if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) { val msg0 = "argument types " + argtpes.mkString("(", ",", ")") + @@ -861,7 +858,7 @@ trait ContextErrors { setErrorOnLastTry(lastTry, tree) } else setError(tree) // do not even try further attempts because they should all fail // even if this is not the last attempt (because of the SO's possibility on the horizon) - + } def NoBestExprAlternativeError(tree: Tree, pt: Type, lastTry: Boolean) = { @@ -1194,7 +1191,7 @@ trait ContextErrors { setError(arg) } else arg } - + def WarnAfterNonSilentRecursiveInference(param: Symbol, arg: Tree)(implicit context: Context) = { val note = "type-checking the invocation of "+ param.owner +" checks if the named argument expression '"+ param.name + " = ...' is a valid assignment\n"+ "in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for "+ param.name +"." diff --git a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala index e8865964b0..2555d199d5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala +++ b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala @@ -188,7 +188,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 97e86d183e..1bd2bbcd5c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -233,7 +233,7 @@ abstract class Duplicators extends Analyzer { override def typed(tree: Tree, mode: Int, 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) @@ -317,13 +317,39 @@ abstract class Duplicators extends Analyzer { super.typed(tree, mode, pt) case Select(th @ This(_), sel) if (oldClassOwner ne null) && (th.symbol == oldClassOwner) => - // log("selection on this, no type ascription required") - // we use the symbol name instead of the tree name because the symbol may have been - // name mangled, rendering the tree name obsolete - // log(tree) - val t = super.typed(atPos(tree.pos)(Select(This(newClassOwner), tree.symbol.name)), mode, pt) - // log("typed to: " + t + "; tpe = " + t.tpe + "; " + inspectTpe(t.tpe)) - t + // We use the symbol name instead of the tree name because the symbol + // may have been name mangled, rendering the tree name obsolete. + // ...but you can't just do a Select on a name because if the symbol is + // overloaded, you will crash in the backend. + val memberByName = newClassOwner.thisType.member(tree.symbol.name) + def nameSelection = Select(This(newClassOwner), tree.symbol.name) + val newTree = ( + if (memberByName.isOverloaded) { + // Find the types of the overload alternatives as seen in the new class, + // and filter the list down to those which match the old type (after + // fixing the old type so it is seen as if from the new class.) + val typeInNewClass = fixType(oldClassOwner.info memberType tree.symbol) + val alts = memberByName.alternatives + val memberTypes = alts map (newClassOwner.info memberType _) + val memberString = memberByName.defString + alts zip memberTypes filter (_._2 =:= typeInNewClass) match { + 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 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 + ) + super.typed(atPos(tree.pos)(newTree), mode, pt) case This(_) if (oldClassOwner ne null) && (tree.symbol == oldClassOwner) => // val tree1 = Typed(This(newClassOwner), TypeTree(fixType(tree.tpe.widen))) @@ -377,7 +403,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 b04a736fd3..059d32fcb5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -118,7 +118,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 7852ff49e1..5f12da8354 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -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). */ @@ -997,7 +997,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 } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index cb5fc8df5a..e18310cf84 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -30,8 +30,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 +49,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`. @@ -112,17 +130,6 @@ 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 ... @@ -149,14 +156,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) @@ -270,7 +276,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 @@ -296,8 +302,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) @@ -439,9 +445,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. + * instantiated type a subtype of `pt`, return null. * * @param tparams ... * @param restpe ... @@ -473,7 +479,7 @@ 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. @@ -566,7 +572,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]] @@ -577,14 +583,14 @@ trait Infer extends Checkable { && (restpe.isWildcard || (varianceInType(restpe)(tparam) & COVARIANT) == 0) // don't retract covariant occurrences ) - // checks opt.virtPatmat directly so one need not run under -Xexperimental to use virtpatmat + // checks !settings.XoldPatmat.value 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 || ((settings.Xexperimental.value || !settings.XoldPatmat.value) && tvar.constr.avoidWiden)) targ else targ.widen ) )) @@ -594,7 +600,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. * @@ -651,9 +657,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) @@ -670,14 +724,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. @@ -723,17 +769,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 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 + } + + /** 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 isUnitForVarArgs(args: List[AnyRef], params: List[Symbol]): Boolean = - args.isEmpty && hasLength(params, 2) && isVarArgsList(params) + 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 <code>undetparams</code> - * such that function type <code>ftpe</code> is applicable to - * <code>argtpes</code> and its result conform to <code>pt</code>? + /** 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) @@ -748,23 +831,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) @@ -786,17 +862,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 + // 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 + && (isIdentity(argPos) || sameLength(formals, params)) + && typesCompatible(reorderArgs(argtpes1, argPos)) // nb. arguments and names are OK, check if types are compatible ) } } @@ -840,7 +915,7 @@ 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) * @@ -850,7 +925,7 @@ trait Infer extends Checkable { */ 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)) @@ -877,7 +952,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 => @@ -1095,19 +1170,19 @@ trait Infer extends Checkable { * @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 @@ -1120,10 +1195,10 @@ trait Infer extends Checkable { args: List[Tree], pt0: Type): List[Symbol] = fn.tpe match { case mt @ MethodType(params0, _) => 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 restpe = fn.tpe.resultType(argtpes) + val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 + val formals = formalTypes(mt.paramTypes, args.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) = methTypeArgs(undetparams, formals, restpe, argtpes, pt) @@ -1160,8 +1235,8 @@ 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 @@ -1228,17 +1303,19 @@ trait Infer extends Checkable { } } else None - (inferFor(pt) orElse inferForApproxPt) map { targs => - new TreeTypeSubstituter(undetparams, targs).traverse(tree) - notifyUndetparamsInferred(undetparams, targs) - } getOrElse { - debugwarn("failed inferConstructorInstance for "+ tree +" : "+ tree.tpe +" under "+ undetparams +" pt = "+ pt +(if(isFullyDefined(pt)) " (fully defined)" else " (not fully defined)")) - // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) - ConstrInstantiationError(tree, resTp, pt) + val inferred = inferFor(pt) orElse inferForApproxPt + + inferred match { + case Some(targs) => + new TreeTypeSubstituter(undetparams, targs).traverse(tree) + notifyUndetparamsInferred(undetparams, targs) + case _ => + debugwarn("failed inferConstructorInstance for "+ tree +" : "+ tree.tpe +" under "+ undetparams +" pt = "+ pt +(if(isFullyDefined(pt)) " (fully defined)" else " (not fully defined)")) + // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) + ConstrInstantiationError(tree, resTp, pt) } } - def instBounds(tvar: TypeVar): (Type, Type) = { val tparam = tvar.origin.typeSymbol val instType = toOrigin(tvar.constr.inst) @@ -1286,51 +1363,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. */ @@ -1469,52 +1501,30 @@ 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 - - //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 tp2 = pre.memberType(sym2) - (tp2 == ErrorType || - !global.typer.infer.isWeaklyCompatible(tp2, pt) && global.typer.infer.isWeaklyCompatible(tp1, pt) || - isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)) } + val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) + val alts1 = if (alts0.isEmpty) alts else alts0 - val best = ((NoSymbol: Symbol) /: alts1) ((best, alt) => - if (improves(alt, best)) alt else best) + val bests = bestAlternatives(alts1) { (sym1, sym2) => + val tp1 = pre.memberType(sym1) + val tp2 = pre.memberType(sym2) - 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 _ => - } - } - // 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 { -// 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)) + ( tp2 == ErrorType + || (!isWeaklyCompatible(tp2, pt) && isWeaklyCompatible(tp1, pt)) + || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) + ) + } + // 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 => if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) } } } @@ -1533,48 +1543,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) - } - } - ) - - 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) + 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) + ) } - /** 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 @@ -1586,43 +1595,38 @@ 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("infer method alt "+ tree.symbol +" with alternatives "+ - (alts map pre.memberType) +", argtpes = "+ argtpes +", pt = "+ pt) - - val applicable = resolveOverloadedMethod(argtpes, { - alts filter { alt => - inSilentMode(context)(isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) && - (!varArgsOnly || isVarArgsList(alt.tpe.params)) - } - }) - - 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) - } - - 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)) - } + 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 } - case _ => + } + // 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) + } } /** Try inference twice, once without views and once with views, @@ -1663,8 +1667,8 @@ 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 ... diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index bcc37e8b37..2b78b37439 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -300,53 +300,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 = 0L) = + def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) = macroDef.newValueParameter(name, pos, flags) setInfo tpe - val ctxParam = makeParam(nme.macroContext, macroDef.pos, MacroContextClass.tpe, SYNTHETIC) - def implType(isType: Boolean, origTpe: Type): Type = + def implType(isType: Boolean, origTpe: Type): Type = { + def tsym = if (isType) WeakTagClass else ExprClass + def targ = origTpe.typeArgs.headOption getOrElse NoType + 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 - val sigParam = makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe)) - if (sym.isSynthetic) sigParam.flags |= SYNTHETIC - sigParam + makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe), sym getFlag SYNTHETIC) }) - - val paramss = List(ctxParam) :: mmap(vparamss)(param) - val implRetTpe = typeRef(singleType(NoPrefix, ctxParam), getMember(MacroContextClass, tpnme.Expr), List(sigma(retTpe))) + ) } import SigGenerator._ @@ -354,7 +352,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, diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 91dcd90962..c95951e608 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -198,13 +198,14 @@ trait MethodSynthesis { if (nme.isSetterName(name)) ValOrValWithSetterSuffixError(tree) - val getter = Getter(tree).createAndEnterSymbol() - tree.symbol = ( - if (mods.isLazy) enterLazyVal(tree, getter) - else { + if (mods.isLazy) { + val lazyValGetter = LazyValGetter(tree).createAndEnterSymbol() + enterLazyVal(tree, lazyValGetter) + } else { if (mods.isPrivateLocal) PrivateThisCaseClassParameterError(tree) + val getter = Getter(tree).createAndEnterSymbol() // Create the setter if necessary. if (mods.isMutable) Setter(tree).createAndEnterSymbol() @@ -219,7 +220,7 @@ trait MethodSynthesis { } def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { - case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) && !vd.symbol.isLazy => + 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) @@ -247,6 +248,7 @@ trait MethodSynthesis { def standardAccessors(vd: ValDef): List[DerivedFromValDef] = ( if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd)) + else if (vd.mods.isLazy) List(LazyValGetter(vd)) else List(Getter(vd)) ) def beanAccessors(vd: ValDef): List[DerivedFromValDef] = { @@ -259,10 +261,15 @@ trait MethodSynthesis { else Nil } def allValDefDerived(vd: ValDef) = { - val field = if (vd.mods.isDeferred) Nil else List(Field(vd)) + val field = if (vd.mods.isDeferred || (vd.mods.isLazy && hasUnitType(vd.symbol))) Nil + else List(Field(vd)) field ::: standardAccessors(vd) ::: beanAccessors(vd) } + // Take into account annotations so that we keep annotated unit lazy val + // to get better error message already from the cps plugin itself + def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty + /** This trait assembles what's needed for synthesizing derived methods. * Important: Typically, instances of this trait are created TWICE for each derived * symbol; once form Namers in an enter method, and once from Typers in addDerivedTrees. @@ -388,16 +395,12 @@ trait MethodSynthesis { def name: TermName = tree.name.toTermName } - case class Getter(tree: ValDef) extends DerivedGetter { + abstract class BaseGetter(tree: ValDef) extends DerivedGetter { def name = tree.name def category = GetterTargetClass def flagsMask = GetterFlags def flagsExtra = ACCESSOR | ( if (tree.mods.isMutable) 0 else STABLE ) - override def derivedSym = ( - if (mods.isDeferred) basisSym - else basisSym.getter(enclClass) - ) override def validate() { assert(derivedSym != NoSymbol, tree) if (derivedSym.isOverloaded) @@ -405,6 +408,13 @@ trait MethodSynthesis { super.validate() } + } + case class Getter(tree: ValDef) extends BaseGetter(tree) { + override def derivedSym = ( + if (mods.isDeferred) basisSym + else basisSym.getter(enclClass) + ) + override def derivedTree: DefDef = { // For existentials, don't specify a type for the getter, even one derived // from the symbol! This leads to incompatible existentials for the field and @@ -437,6 +447,45 @@ trait MethodSynthesis { } } } + /** Implements lazy value accessors: + * - for lazy values of type Unit and all lazy fields inside traits, + * the rhs is the initializer itself + * - for all other lazy values z the accessor is a block of this form: + * { z = <rhs>; z } where z can be an identifier or a field. + */ + 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) + case _ => + } + super.traverse(tree) + } + } + + // todo: in future this should be enabled but now other phases still depend on the flag for various reasons + //override def flagsMask = (super.flagsMask & ~LAZY) + override def derivedSym = basisSym.lazyAccessor + override def derivedTree: DefDef = { + val ValDef(_, _, tpt0, rhs0) = tree + val rhs1 = transformed.getOrElse(rhs0, rhs0) + val body = ( + if (tree.symbol.owner.isTrait || hasUnitType(basisSym)) rhs1 + else gen.mkAssignAndReturn(basisSym, rhs1) + ) + derivedSym.setPos(tree.pos) // cannot set it at createAndEnterSymbol because basisSym can possible stil have NoPosition + val ddefRes = atPos(tree.pos)(DefDef(derivedSym, new ChangeOwnerAndModuleClassTraverser(basisSym, derivedSym)(body))) + // ValDef will have its position focused whereas DefDef will have original correct rangepos + // ideally positions would be correct at the creation time but lazy vals are really a special case + // here so for the sake of keeping api clean we fix positions manually in LazyValGetter + ddefRes.tpt.setPos(tpt0.pos) + tpt0.setPos(tpt0.pos.focus) + ddefRes + } + } case class Setter(tree: ValDef) extends DerivedSetter { def name = nme.getterToSetter(tree.name) def category = SetterTargetClass @@ -455,6 +504,7 @@ trait MethodSynthesis { override def keepClean = !mods.isParamAccessor override def derivedTree = ( if (mods.isDeferred) EmptyTree + else if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus) else copyValDef(tree)(mods = mods | flagsExtra, name = this.name) ) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index a37dc77d44..2c4034db84 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -113,10 +113,8 @@ trait Namers extends MethodSynthesis { || (context.unit.isJava) ) def noFinishGetterSetter(vd: ValDef) = ( - vd.mods.isPrivateLocal - || vd.symbol.isModuleVar - || vd.symbol.isLazy - ) + (vd.mods.isPrivateLocal && !vd.mods.isLazy) // all lazy vals need accessors, even private[this] + || vd.symbol.isModuleVar) def setPrivateWithin[T <: Symbol](tree: Tree, sym: T, mods: Modifiers): T = if (sym.isPrivateLocal || !mods.hasAccessBoundary) sym @@ -161,6 +159,9 @@ 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) @@ -188,6 +189,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) @@ -598,7 +612,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) } @@ -619,7 +633,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) @@ -693,41 +707,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) = { @@ -786,7 +814,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" @@ -834,7 +862,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 } @@ -920,7 +948,7 @@ trait Namers extends MethodSynthesis { // DEPMETTODO: do we need to skolemize value parameter symbols? if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { - tpt defineType context.enclClass.owner.tpe + tpt defineType context.enclClass.owner.tpe_* tpt setPos meth.pos.focus } var resultPt = if (tpt.isEmpty) WildcardType else typer.typedType(tpt).tpe @@ -1261,8 +1289,8 @@ trait Namers extends MethodSynthesis { if (!annotated.isInitialized) tree match { case defn: MemberDef => val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => - // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. - AnnotationInfo lazily beforeTyper(typer typedAnnotation ann) + // need to be lazy, #1782. enteringTyper to allow inferView in annotation args, SI-5892. + AnnotationInfo lazily enteringTyper(typer typedAnnotation ann) } if (ainfos.nonEmpty) { annotated setAnnotations ainfos diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 7ba198a9f2..74acaba74a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -164,7 +164,7 @@ 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 @@ -281,7 +281,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 ) (context.scope.enter(s), byName, repeated) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 2282f62152..21e2b7ceec 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -70,7 +70,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } def newTransformer(unit: CompilationUnit): Transformer = - if (opt.virtPatmat) new MatchTransformer(unit) + if (!settings.XoldPatmat.value) new MatchTransformer(unit) else noopTransformer // duplicated from CPSUtils (avoid dependency from compiler -> cps plugin...) @@ -192,7 +192,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 @@ -1376,10 +1375,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL t.symbol.owner = currentOwner case d : DefTree if (d.symbol != NoSymbol) && ((d.symbol.owner == NoSymbol) || (d.symbol.owner == origOwner)) => // don't indiscriminately change existing owners! (see e.g., pos/t3440, pos/t3534, pos/unapplyContexts2) patmatDebug("def: "+ (d, d.symbol.ownerChain, currentOwner.ownerChain)) - if(d.symbol.isLazy) { // for lazy val's accessor -- is there no tree?? - assert(d.symbol.lazyAccessor != NoSymbol && d.symbol.lazyAccessor.owner == d.symbol.owner, d.symbol.lazyAccessor) - d.symbol.lazyAccessor.owner = currentOwner - } if(d.symbol.moduleClass ne NoSymbol) d.symbol.moduleClass.owner = currentOwner @@ -2547,7 +2542,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 diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index e3f5214581..dacb68ea86 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -60,23 +60,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans super.transformInfo(sym, tp) } - val toJavaRepeatedParam = new TypeMap { - def apply(tp: Type) = tp match { - case TypeRef(pre, RepeatedParamClass, args) => - typeRef(pre, JavaRepeatedParamClass, args) - case _ => - mapOver(tp) - } - } - - val toScalaRepeatedParam = new TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(pre, JavaRepeatedParamClass, args) => - typeRef(pre, RepeatedParamClass, args) - case _ => - mapOver(tp) - } - } + val toJavaRepeatedParam = new SubstSymMap(RepeatedParamClass -> JavaRepeatedParamClass) + val toScalaRepeatedParam = new SubstSymMap(JavaRepeatedParamClass -> RepeatedParamClass) def accessFlagsToString(sym: Symbol) = flagsToString( sym getFlag (PRIVATE | PROTECTED), @@ -156,27 +141,22 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // Override checking ------------------------------------------------------------ - def isJavaVarargsAncestor(clazz: Symbol) = ( - clazz.isClass - && clazz.isJavaDefined - && (clazz.info.nonPrivateDecls exists isJavaVarArgsMethod) - ) - /** Add bridges for vararg methods that extend Java vararg methods */ def addVarargBridges(clazz: Symbol): List[Tree] = { // This is quite expensive, so attempt to skip it completely. // Insist there at least be a java-defined ancestor which // defines a varargs method. TODO: Find a cheaper way to exclude. - if (clazz.thisType.baseClasses exists isJavaVarargsAncestor) { + if (inheritsJavaVarArgsMethod(clazz)) { log("Found java varargs ancestor in " + clazz.fullLocationString + ".") val self = clazz.thisType val bridges = new ListBuffer[Tree] def varargBridge(member: Symbol, bridgetpe: Type): Tree = { - log("Generating varargs bridge for " + member.fullLocationString + " of type " + bridgetpe) + log(s"Generating varargs bridge for ${member.fullLocationString} of type $bridgetpe") - val bridge = member.cloneSymbolImpl(clazz, member.flags | VBRIDGE) setPos clazz.pos + val newFlags = (member.flags | VBRIDGE | ARTIFACT) & ~PRIVATE + val bridge = member.cloneSymbolImpl(clazz, newFlags) setPos clazz.pos bridge.setInfo(bridgetpe.cloneInfo(bridge)) clazz.info.decls enter bridge @@ -189,26 +169,35 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans localTyper typed DefDef(bridge, body) } - // For all concrete non-private members that have a (Scala) repeated parameter: - // compute the corresponding method type `jtpe` with a Java repeated parameter + // For all concrete non-private members (but: see below) that have a (Scala) repeated + // parameter: compute the corresponding method type `jtpe` with a Java repeated parameter // if a method with type `jtpe` exists and that method is not a varargs bridge // then create a varargs bridge of type `jtpe` that forwards to the // member method with the Scala vararg type. - for (member <- clazz.info.nonPrivateMembers) { + // + // @PP: Can't call nonPrivateMembers because we will miss refinement members, + // which have been marked private. See SI-4729. + for (member <- nonTrivialMembers(clazz)) { + log(s"Considering $member for java varargs bridge in $clazz") if (!member.isDeferred && member.isMethod && hasRepeatedParam(member.info)) { val inherited = clazz.info.nonPrivateMemberAdmitting(member.name, VBRIDGE) + // Delaying calling memberType as long as possible if (inherited ne NoSymbol) { - val jtpe = toJavaRepeatedParam(self.memberType(member)) + val jtpe = toJavaRepeatedParam(self memberType member) // this is a bit tortuous: we look for non-private members or bridges // if we find a bridge everything is OK. If we find another member, // we need to create a bridge - if (inherited filter (sym => (self.memberType(sym) matches jtpe) && !(sym hasFlag VBRIDGE)) exists) + val inherited1 = inherited filter (sym => !(sym hasFlag VBRIDGE) && (self memberType sym matches jtpe)) + if (inherited1.exists) bridges += varargBridge(member, jtpe) } } } + if (bridges.size > 0) + log(s"Adding ${bridges.size} bridges for methods extending java varargs.") + bridges.toList } else Nil @@ -556,13 +545,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) ) @@ -905,13 +894,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans * the type occurs itself at variance position given by `variance` */ def validateVariance(tp: Type, variance: Int): Unit = tp match { - case ErrorType => ; - case WildcardType => ; - case NoType => ; - case NoPrefix => ; - case ThisType(_) => ; - case ConstantType(_) => ; - // case DeBruijnIndex(_, _) => ; + case ErrorType => + case WildcardType => + case BoundedWildcardType(bounds) => + validateVariance(bounds, variance) + case NoType => + case NoPrefix => + case ThisType(_) => + case ConstantType(_) => case SingleType(pre, sym) => validateVariance(pre, variance) case TypeRef(pre, sym, args) => @@ -1024,15 +1014,18 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def enterSyms(stats: List[Tree]) { var index = -1 for (stat <- stats) { - index = index + 1; + index = index + 1 + def enterSym(sym: Symbol) = if (sym.isLocal) { + currentLevel.scope.enter(sym) + symIndex(sym) = index + } + stat match { + case DefDef(_, _, _, _, _, _) if stat.symbol.isLazy => + enterSym(stat.symbol) case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) => //assert(stat.symbol != NoSymbol, stat);//debug - val sym = stat.symbol.lazyAccessorOrSelf - if (sym.isLocal) { - currentLevel.scope.enter(sym) - symIndex(sym) = index; - } + enterSym(stat.symbol.lazyAccessorOrSelf) case _ => } } @@ -1269,7 +1262,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case vsym => ValDef(vsym) } } - def createStaticModuleAccessor() = afterRefchecks { + def createStaticModuleAccessor() = exitingRefchecks { val method = ( sym.owner.newMethod(sym.name.toTermName, sym.pos, (sym.flags | STABLE) & ~MODULE) setInfoAndEnter NullaryMethodType(sym.moduleClass.tpe) @@ -1280,7 +1273,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans vdef, localTyper.typedPos(tree.pos) { val vsym = vdef.symbol - afterRefchecks { + exitingRefchecks { 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)) @@ -1297,34 +1290,6 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans }) } - /** Implements lazy value accessors: - * - for lazy values of type Unit and all lazy fields inside traits, - * the rhs is the initializer itself - * - for all other lazy values z the accessor is a block of this form: - * { z = <rhs>; z } where z can be an identifier or a field. - */ - private def makeLazyAccessor(tree: Tree, rhs: Tree): List[Tree] = { - val vsym = tree.symbol - assert(vsym.isTerm, vsym) - val hasUnitType = vsym.tpe.typeSymbol == UnitClass - val lazySym = vsym.lazyAccessor - assert(lazySym != NoSymbol, vsym) - - // for traits, this is further transformed in mixins - val body = ( - if (tree.symbol.owner.isTrait || hasUnitType) rhs - else gen.mkAssignAndReturn(vsym, rhs) - ) - val lazyDef = atPos(tree.pos)(DefDef(lazySym, body.changeOwner(vsym -> lazySym))) - debuglog("Created lazy accessor: " + lazyDef) - - if (hasUnitType) List(typed(lazyDef)) - else List( - typed(ValDef(vsym)), - afterRefchecks(typed(lazyDef)) - ) - } - def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case t if treeInfo.isSelfConstrCall(t) => assert(index == 0, index) @@ -1337,8 +1302,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 - if (tree.symbol.isLazy) - makeLazyAccessor(tree, rhs) + if (tree1.symbol.isLazy) tree1 :: Nil else { val lazySym = tree.symbol.lazyAccessorOrSelf if (lazySym.isLocal && index <= currentLevel.maxindex) { @@ -1481,8 +1445,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } private def isRepeatedParamArg(tree: Tree) = currentApplication match { case Apply(fn, args) => - !args.isEmpty && (args.last eq tree) && - fn.tpe.params.length == args.length && isRepeatedParamType(fn.tpe.params.last.tpe) + ( args.nonEmpty + && (args.last eq tree) + && (fn.tpe.params.length == args.length) + && isRepeatedParamType(fn.tpe.params.last.tpe) + ) case _ => false } diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 981ba10183..2306575d74 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 @@ -293,7 +293,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) @@ -414,7 +415,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, { @@ -425,7 +426,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 } @@ -437,7 +438,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) } @@ -476,7 +477,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) diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 67afb0c118..2fe8925c95 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -77,7 +77,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 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 9bb88f152a..153fb76b3e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -278,7 +278,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("""| | currentOwner chain: %s diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f82786da35..2c3ff0bfa4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -53,7 +53,7 @@ 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.hasSymbolField) tree.symbol = NoSymbol super.traverse(tree) } } @@ -91,7 +91,7 @@ trait Typers extends Modes with Adaptations with Tags { // - 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) + private def newPatternMatching = !settings.XoldPatmat.value && !forInteractive //&& !forScaladoc && (phase.id < currentRun.uncurryPhase.id) abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors { import context0.unit @@ -194,7 +194,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) @@ -242,7 +242,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 @@ -292,8 +292,7 @@ trait Typers extends Modes with Adaptations with Tags { */ 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) => @@ -320,14 +319,14 @@ 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) { @@ -599,23 +598,31 @@ trait Typers extends Modes with Adaptations with Tags { } /** 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. */ 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) - } + val pkgClass = if (pkg.isTerm) pkg.moduleClass else pkg + def matchesInfo = ( + pkg.isInitialized && { + // need to be careful here to not get a cyclic reference during bootstrap + val module = pkg.info member nme.PACKAGEkw + module.isInitialized && (module.info.member(sym.name).alternatives contains sym) } - pkg.isPackageClass && { + ) + def isInPkgObj(sym: Symbol) = ( + !sym.isPackage + && !sym.owner.isPackageClass + && (sym.owner ne NoSymbol) + && (sym.owner.owner == pkgClass || matchesInfo) + ) + + pkgClass.isPackageClass && ( if (sym.isOverloaded) sym.alternatives forall isInPkgObj else isInPkgObj(sym) - } + ) } /** Post-process an identifier or selection node, performing the following: @@ -907,7 +914,7 @@ trait Typers extends Modes with Adaptations with Tags { // 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 && !inHKMode(mode) && !(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 *, @@ -915,7 +922,7 @@ 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) + // @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) (inHKMode(mode)) && // @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!) @@ -1037,15 +1044,21 @@ trait Typers extends Modes with Adaptations with Tags { def insertApply(): Tree = { assert(!inHKMode(mode), modeString(mode)) //@M - val qual = adaptToName(tree, nme.apply) match { - case id @ Ident(_) => - val pre = if (id.symbol.owner.isPackageClass) id.symbol.owner.thisType - else if (id.symbol.owner.isClass) - context.enclosingSubClassContext(id.symbol.owner).prefix - else NoPrefix - stabilize(id, pre, EXPRmode | QUALmode, WildcardType) - case sel @ Select(qualqual, _) => - stabilize(sel, qualqual.tpe, EXPRmode | QUALmode, WildcardType) + 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 + val qual = adapted match { + case This(_) => + gen.stabilize(adapted) + case Ident(_) => + val owner = adapted.symbol.owner + val pre = + if (owner.isPackageClass) owner.thisType + else if (owner.isClass) context.enclosingSubClassContext(owner).prefix + else NoPrefix + stabilize0(pre) + case Select(qualqual, _) => + stabilize0(qualqual.tpe) case other => other } @@ -1417,9 +1430,6 @@ trait Typers extends Modes with Adaptations with Tags { //see https://issues.scala-lang.org/browse/SI-6463 case _: ClassDef => implRestriction(tree, "nested class") - case x: ValDef if x.mods.isLazy => - //see https://issues.scala-lang.org/browse/SI-6358 - implRestriction(tree, "lazy val") case Select(sup @ Super(qual, mix), selector) if selector != nme.CONSTRUCTOR && qual.symbol == clazz && mix != tpnme.EMPTY => //see https://issues.scala-lang.org/browse/SI-6483 implRestriction(sup, "qualified super reference") @@ -1503,10 +1513,10 @@ trait Typers extends Modes with Adaptations with Tags { // Determine // - supertparams: Missing type parameters from supertype // - supertpe: Given supertype, polymorphic in supertparams - val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else List() + val supertparams = if (supertpt.hasSymbolField) supertpt.symbol.typeParams else List() var supertpe = supertpt.tpe if (!supertparams.isEmpty) - supertpe = PolyType(supertparams, appliedType(supertpe, supertparams map (_.tpeHK))) + supertpe = PolyType(supertparams, appliedType(supertpe.typeConstructor, supertparams map (_.tpeHK))) // A method to replace a super reference by a New in a supercall def transformSuperCall(scall: Tree): Tree = (scall: @unchecked) match { @@ -1864,7 +1874,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 @@ -1897,15 +1907,12 @@ trait Typers extends Modes with Adaptations with Tags { var tpt1 = checkNoEscaping.privates(sym, typer1.typedType(vdef.tpt)) checkNonCyclic(vdef, tpt1) - if (sym.hasAnnotation(definitions.VolatileAttr)) { - if (!sym.isMutable) + if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable) VolatileValueError(vdef) - else if (sym.isFinal) - FinalVolatileVarError(vdef) - } + val rhs1 = if (vdef.rhs.isEmpty) { - if (sym.isVariable && sym.owner.isTerm && !isPastTyper) + if (sym.isVariable && sym.owner.isTerm && !sym.isLazy && !isPastTyper) LocalVarUninitializedError(vdef) vdef.rhs } else { @@ -2331,9 +2338,15 @@ trait Typers extends Modes with Adaptations with Tags { case _ => } } - val stats1 = typedStats(block.stats, context.owner) + val stats1 = if (isPastTyper) block.stats else + block.stats.flatMap(stat => stat match { + case vd@ValDef(_, _, _, _) if vd.symbol.isLazy => + namer.addDerivedTrees(Typer.this, vd) + case _ => stat::Nil + }) + val stats2 = typedStats(stats1, context.owner) val expr1 = typed(block.expr, mode & ~(FUNmode | QUALmode), pt) - treeCopy.Block(block, stats1, expr1) + treeCopy.Block(block, stats2, expr1) .setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst) } finally { // enable escaping privates checking from the outside and recycle @@ -2421,7 +2434,7 @@ trait Typers extends Modes with Adaptations with Tags { val casesTyped = typedCases(cases, selectorTp, pt) val (resTp, needAdapt) = - if (opt.virtPatmat) ptOrLubPacked(casesTyped, pt) + if (!settings.XoldPatmat.value) ptOrLubPacked(casesTyped, pt) else ptOrLub(casesTyped map (_.tpe), pt) val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, resTp)) @@ -2437,7 +2450,7 @@ trait Typers extends Modes with Adaptations with Tags { // 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 + 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) match { case SilentResultValue(ms) => ms case _ => null @@ -2672,16 +2685,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 body1 = typed(fun.body, respt) + val restpe = packedType(body1, fun.symbol).deconst.resultType + val funtpe = appliedType(clazz, formals :+ restpe: _*) + + treeCopy.Function(fun, vparams, body1) setType funtpe } } } @@ -2712,17 +2722,6 @@ trait Typers extends Modes with Adaptations with Tags { 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 @@ -2759,7 +2758,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" ) @@ -2943,21 +2942,20 @@ trait Typers extends Modes with Adaptations with Tags { 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 _ => NothingClass.tpe } val argtypes = args map shapeType - val pre = fun.symbol.tpe.prefix - - var sym = fun.symbol filter { alt => + 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 // TODO: understand why exactly -- some types were not inferred anymore (`ant clean quick.bin` failed) @@ -2968,16 +2966,15 @@ 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)) - }) + // eliminate functions that would result from tupling transforms + // keeps alternatives with repeated params + 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 @@ -2991,16 +2988,19 @@ 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 argtpes = new ListBuffer[Type] + val amode = forArgMode(fun, mode) val args1 = args map { case arg @ AssignOrNamedArg(Ident(name), rhs) => // named args: only type the righthand sides ("unknown identifier" errors otherwise) 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 @@ -3010,7 +3010,7 @@ trait Typers extends Modes with Adaptations with Tags { if (context.hasErrors) setError(tree) else { - inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) + inferMethodAlternative(fun, undetparams, argtpes.toList, pt) doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) } } @@ -3019,17 +3019,16 @@ 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)) { + 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 @@ -3224,15 +3223,14 @@ trait Typers extends Modes with Adaptations with Tags { } else arg1 } val args1 = map2(args, formals)(typedArgToPoly) - if (args1 exists { _.isErrorTyped }) duplErrTree + if (args1 exists {_.isErrorTyped}) duplErrTree else { - debuglog("infer method inst " + fun + ", tparams = " + tparams + ", args = " + args1.map(_.tpe) + ", pt = " + pt + ", lobounds = " + tparams.map(_.tpe.bounds.lo) + ", parambounds = " + tparams.map(_.info)) //debug + debuglog("infer method inst "+fun+", tparams = "+tparams+", args = "+args1.map(_.tpe)+", pt = "+pt+", lobounds = "+tparams.map(_.tpe.bounds.lo)+", parambounds = "+tparams.map(_.info)) //debug // 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 @@ -3346,25 +3344,26 @@ trait Typers extends Modes with Adaptations with Tags { // println(util.Position.formatMessage(uncheckedPattern.pos, "made unchecked type test into a checked one", true)) val args = List(uncheckedPattern) + val app = atPos(uncheckedPattern.pos)(Apply(classTagExtractor, args)) // must call doTypedUnapply directly, as otherwise we get undesirable rewrites // and re-typechecks of the target of the unapply call in PATTERNmode, // this breaks down when the classTagExtractor (which defineds the unapply member) is not a simple reference to an object, // but an arbitrary tree as is the case here - doTypedUnapply(Apply(classTagExtractor, args), classTagExtractor, classTagExtractor, args, PATTERNmode, pt) - } + doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) + } // 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 (settings.XoldPatmat.value || isPastTyper) None else { // only look at top-level type, can't (reliably) do anything about unchecked type args (in general) pt.normalize.typeConstructor 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) @@ -3528,35 +3527,23 @@ trait Typers extends Modes with Adaptations with Tags { // 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. + } + 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, annClass.tpe_*) + val Function(arg :: Nil, rhs) = typed(func, mode, funcType) + + rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil) } def annInfo(t: Tree): AnnotationInfo = t match { @@ -4079,7 +4066,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 = @@ -4175,7 +4162,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 + if ( !settings.XoldPatmat.value && !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) @@ -4248,7 +4235,7 @@ trait Typers extends Modes with Adaptations with Tags { val tpt1 = { val tpt0 = typedTypeConstructor(tpt) 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) @@ -4278,7 +4265,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 @@ -4460,12 +4447,12 @@ trait Typers extends Modes with Adaptations with Tags { else doTypedApply(tree, fun2, args, mode, pt) /* - if (fun2.hasSymbol && fun2.symbol.isConstructor && (mode & EXPRmode) != 0) { + if (fun2.hasSymbolField && 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) { + //if (fun2.hasSymbolField && 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) { @@ -4620,6 +4607,24 @@ trait Typers extends Modes with Adaptations with Tags { * @return ... */ 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))) } @@ -4806,6 +4811,22 @@ trait Typers extends Modes with Adaptations with Tags { defSym = rootMirror.EmptyPackageClass.tpe.nonPrivateMember(name) defSym != NoSymbol } + def correctForPackageObject(sym: Symbol): Symbol = { + if (sym.isTerm && isInPackageObject(sym, pre.typeSymbol)) { + val sym1 = pre member sym.name + if ((sym1 eq NoSymbol) || (sym eq sym1)) sym else { + qual = gen.mkAttributedQualifier(pre) + log(s""" + | !!! Overloaded package object member resolved incorrectly. + | prefix: $pre + | Discarded: ${sym.defString} + | Using: ${sym1.defString} + """.stripMargin) + sym1 + } + } + else sym + } def startingIdentContext = ( // ignore current variable scope in patterns to enforce linearity if ((mode & (PATTERNmode | TYPEPATmode)) == 0) context @@ -4817,11 +4838,11 @@ trait Typers extends Modes with Adaptations with Tags { // 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)) - } + 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 @@ -4829,32 +4850,17 @@ trait Typers extends Modes with Adaptations with Tags { 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 + // !!! FIXME. This call to lookupEntry is at the root of all the + // bad behavior with overloading in package objects. lookupEntry + // just takes the first symbol it finds in scope, ignoring the rest. + // When a selection on a package object arrives here, the first + // overload is always chosen. "correctForPackageObject" exists to + // undo that decision. Obviously it would be better not to do it in + // the first place; however other things seem to be tied to obtaining + // that ScopeEntry, specifically calculating the nesting depth. 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(s""" - | !!! Overloaded package object member resolved incorrectly. - | prefix: $pre - | Discarded: ${defEntry.sym.defString} - | Using: ${defSym.defString} - """.stripMargin) - } - } - else - defSym = defEntry.sym - } + if ((defEntry ne null) && qualifies(defEntry.sym)) + defSym = correctForPackageObject(defEntry.sym) else { cx = cx.enclClass val foundSym = pre.member(name) filter qualifies @@ -5017,7 +5023,7 @@ trait Typers extends Modes with Adaptations with Tags { val tree3 = stabilize(tree2, pre2, mode, pt) // 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) + tree3 setType dropIllegalStarTypes(tree3.tpe) } } } @@ -5052,7 +5058,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 @@ -5161,14 +5167,12 @@ trait Typers extends Modes with Adaptations with Tags { var block1 = typed(tree.block, pt) var catches1 = typedCases(tree.catches, ThrowableClass.tpe, pt) - for (cdef <- catches1 if cdef.guard.isEmpty) { - def warn(name: Name) = context.warning(cdef.pat.pos, s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning.") - 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 _ => - } + for (cdef <- catches1; if treeInfo catchesThrowable cdef) { + val name = (treeInfo assignedNameOfPattern cdef).decoded + context.warning(cdef.pat.pos, + s"""|This catches all Throwables, which often has undesirable consequences. + |If intentional, use `case $name : Throwable` to clear this warning.""".stripMargin + ) } val finalizer1 = @@ -5424,7 +5428,7 @@ trait Typers extends Modes with Adaptations with Tags { if (context.retyping && (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< pt))) { tree.tpe = null - if (tree.hasSymbol) tree.symbol = NoSymbol + if (tree.hasSymbolField) tree.symbol = NoSymbol } alreadyTyped = tree.tpe ne null diff --git a/src/compiler/scala/tools/nsc/typechecker/Variances.scala b/src/compiler/scala/tools/nsc/typechecker/Variances.scala index b9f2b9abd8..279096bddd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Variances.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Variances.scala @@ -67,6 +67,8 @@ trait Variances { 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) => diff --git a/src/compiler/scala/tools/nsc/util/ShowPickled.scala b/src/compiler/scala/tools/nsc/util/ShowPickled.scala index d1f3183f68..787291b13c 100644 --- a/src/compiler/scala/tools/nsc/util/ShowPickled.scala +++ b/src/compiler/scala/tools/nsc/util/ShowPickled.scala @@ -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/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 95135b84e0..bc8ded62d8 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -102,7 +102,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => }) var 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)) diff --git a/src/continuations/library/scala/util/continuations/package.scala b/src/continuations/library/scala/util/continuations/package.scala index 641f4594e4..93238d50e1 100644 --- a/src/continuations/library/scala/util/continuations/package.scala +++ b/src/continuations/library/scala/util/continuations/package.scala @@ -167,7 +167,7 @@ package object continuations { } def shiftUnitR[A,B](x: A): ControlContext[A,B,B] = { - new ControlContext(null, x) + new ControlContext[A, B, B](null, x) } /** diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index b373b3d0de..c147dc483d 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -96,7 +96,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { if (!cpsEnabled) return bounds val anyAtCPS = newCpsParamsMarker(NothingClass.tpe, AnyClass.tpe) - if (isFunctionType(tparams.head.owner.tpe) || isPartialFunctionType(tparams.head.owner.tpe)) { + if (isFunctionType(tparams.head.owner.tpe_*) || isPartialFunctionType(tparams.head.owner.tpe_*)) { vprintln("function bound: " + tparams.head.owner.tpe + "/"+bounds+"/"+targs) if (hasCpsParamTypes(targs.last)) bounds.reverse match { @@ -356,7 +356,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { global.globalError("not a single cps annotation: " + xs) xs(0) } - + def emptyOrSingleList(xs: List[AnnotationInfo]) = if (xs.isEmpty) Nil else List(single(xs)) def transChildrenInOrder(tree: Tree, tpe: Type, childTrees: List[Tree], byName: List[Tree]) = { @@ -496,7 +496,11 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { case ValDef(mods, name, tpt, rhs) => vprintln("[checker] checking valdef " + name + "/"+tpe+"/"+tpt+"/"+tree.symbol.tpe) // ValDef symbols must *not* have annotations! - if (hasAnswerTypeAnn(tree.symbol.info)) { // is it okay to modify sym here? + // lazy vals are currently not supported + // but if we erase here all annotations, compiler will complain only + // when generating bytecode. + // This way lazy vals will be reported as unsupported feature later rather than weird type error. + if (hasAnswerTypeAnn(tree.symbol.info) && !mods.isLazy) { // is it okay to modify sym here? vprintln("removing annotation from sym " + tree.symbol + "/" + tree.symbol.tpe + "/" + tpt) tpt modifyType removeAllCPSAnnotations tree.symbol modifyInfo removeAllCPSAnnotations diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 46c644bcd6..eab442aaef 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -57,7 +57,7 @@ trait CPSUtils { protected def newMarker(sym: Symbol): AnnotationInfo = AnnotationInfo marker sym.tpe protected def newCpsParamsMarker(tp1: Type, tp2: Type) = - newMarker(appliedType(MarkerCPSTypes.tpe, List(tp1, tp2))) + newMarker(appliedType(MarkerCPSTypes, tp1, tp2)) // annotation checker diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index ba87cadfeb..ef13f8b1d8 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -172,7 +172,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with debuglog("transforming valdef " + vd.symbol) if (getExternalAnswerTypeAnn(tpt.tpe).isEmpty) { - + atOwner(vd.symbol) { val rhs1 = transExpr(rhs, None, None) @@ -195,9 +195,12 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with case _ => if (hasAnswerTypeAnn(tree.tpe)) { - if (!cpsAllowed) - unit.error(tree.pos, "cps code not allowed here / " + tree.getClass + " / " + tree) - + if (!cpsAllowed) { + if (tree.symbol.isLazy) + unit.error(tree.pos, "implementation restriction: cps annotations not allowed on lazy value definitions") + else + unit.error(tree.pos, "cps code not allowed here / " + tree.getClass + " / " + tree) + } log(tree) } @@ -468,7 +471,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with val sym: Symbol = ( currentOwner.newValue(newTermName(unit.fresh.newName("tmp")), tree.pos, Flags.SYNTHETIC) setInfo valueTpe - setAnnotations List(AnnotationInfo(MarkerCPSSym.tpe, Nil, Nil)) + setAnnotations List(AnnotationInfo(MarkerCPSSym.tpe_*, Nil, Nil)) ) expr.changeOwner(currentOwner -> sym) @@ -500,9 +503,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // TODO: better yet: do without annotations on symbols val spcVal = getAnswerTypeAnn(anfRhs.tpe) - if (spcVal.isDefined) { - tree.symbol.setAnnotations(List(AnnotationInfo(MarkerCPSSym.tpe, Nil, Nil))) - } + spcVal foreach (_ => tree.symbol setAnnotations List(AnnotationInfo(MarkerCPSSym.tpe_*, Nil, Nil))) (stms:::List(treeCopy.ValDef(tree, mods, name, tpt, anfRhs)), linearize(spc, spcVal)(unit, tree.pos)) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala index 54a0079f40..eb23e1276c 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -56,7 +56,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with case _ => getExternalAnswerTypeAnn(tp) match { case Some((res, outer)) => - appliedType(Context.tpe, List(removeAllCPSAnnotations(tp), res, outer)) + appliedType(Context.tpeHK, List(removeAllCPSAnnotations(tp), res, outer)) case _ => removeAllCPSAnnotations(tp) } @@ -107,7 +107,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with TypeApply(funR, List(targs(0), targs(1))).setType(appliedType(funR.tpe, List(targs(0).tpe, targs(1).tpe))), args.map(transform(_)) - ).setType(appliedType(Context.tpe, List(targs(0).tpe,targs(1).tpe,targs(1).tpe))) + ).setType(appliedType(Context.tpeHK, List(targs(0).tpe,targs(1).tpe,targs(1).tpe))) } case Apply(TypeApply(fun, targs), args) @@ -192,7 +192,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with val targettp = transformCPSType(tree.tpe) val pos = catches.head.pos - val funSym = currentOwner.newValueParameter(cpsNames.catches, pos).setInfo(appliedType(PartialFunctionClass.tpe, List(ThrowableClass.tpe, targettp))) + val funSym = currentOwner.newValueParameter(cpsNames.catches, pos).setInfo(appliedType(PartialFunctionClass, ThrowableClass.tpe, targettp)) val funDef = localTyper.typed(atPos(pos) { ValDef(funSym, Match(EmptyTree, catches1)) }) diff --git a/src/detach/plugin/scala/tools/detach/Detach.scala b/src/detach/plugin/scala/tools/detach/Detach.scala index 376a56beed..d56a7f0fbe 100644 --- a/src/detach/plugin/scala/tools/detach/Detach.scala +++ b/src/detach/plugin/scala/tools/detach/Detach.scala @@ -206,7 +206,7 @@ abstract class Detach extends PluginComponent symSet(capturedObjects, owner) += qsym case Select(qual, name) - if (qual.hasSymbol && + if (qual.hasSymbolField && (sym.owner != owner) && !(sym.ownerChain contains ScalaPackageClass) && !(sym.owner hasFlag JAVA)) => @@ -284,7 +284,7 @@ abstract class Detach extends PluginComponent def isOuter(sym: Symbol): Boolean = sym.isOuterAccessor || sym.name.endsWith(nme.OUTER/*, nme.OUTER.length*/) - if (tree.hasSymbol && isOuter(tree.symbol)) subst(from, to) + if (tree.hasSymbolField && isOuter(tree.symbol)) subst(from, to) super.traverse(tree) } } @@ -293,7 +293,7 @@ abstract class Detach extends PluginComponent private class TreeTypeRefSubstituter(clazz: Symbol) extends Traverser { override def traverse(tree: Tree) { val sym = tree.symbol - if (tree.hasSymbol && isRefClass(sym.tpe) && + if (tree.hasSymbolField && isRefClass(sym.tpe) && (sym.owner.enclClass == clazz) && (sym.isValueParameter || sym.hasFlag(PARAMACCESSOR))) { sym setInfo mkRemoteRefClass(sym.tpe) @@ -329,7 +329,7 @@ abstract class Detach extends PluginComponent } val map = new mutable.HashMap[Symbol, Symbol] override def traverse(tree: Tree) { - if (tree.hasSymbol && tree.symbol != NoSymbol) { + if (tree.hasSymbolField && tree.symbol != NoSymbol) { val sym = tree.symbol if (sym.owner == from) { val sym1 = map get sym match { @@ -369,7 +369,7 @@ abstract class Detach extends PluginComponent def removeAccessors(tree: Tree): Tree = tree match { case Apply(fun, _) => removeAccessors(fun) - case Select(qual, _) if tree.hasSymbol && tree.symbol.isOuterAccessor => + case Select(qual, _) if tree.hasSymbolField && tree.symbol.isOuterAccessor => removeAccessors(qual) case _ => tree @@ -382,7 +382,7 @@ abstract class Detach extends PluginComponent // transforms field assignment $outer.i$1.elem=.. // into setter $outer.i$1_=(..) case Assign(lhs @ Select(qual1 @ Select(qual, name), name1), rhs) - if qual1.hasSymbol && !qual1.symbol.isPrivateLocal && + if qual1.hasSymbolField && !qual1.symbol.isPrivateLocal && isRemoteRefClass(qual1.tpe) => if (DEBUG) println("\nTreeAccessorSubstituter: Assign1\n\tqual1="+qual1+", sel.tpe="+lhs.tpe+ @@ -398,7 +398,7 @@ abstract class Detach extends PluginComponent // transforms local assignment this.x$1.elem=.. // into setter method this.x$1_=(..) case Assign(lhs @ Select(qual, name), rhs) - if qual.hasSymbol && qual.symbol.isPrivateLocal && + if qual.hasSymbolField && qual.symbol.isPrivateLocal && isRemoteRefClass(qual.tpe) => if (DEBUG) println("\nTreeAccessorSubstituter: Assign2"+ @@ -412,7 +412,7 @@ abstract class Detach extends PluginComponent Apply(fun, List(super.transform(rhs))) setType lhs.tpe case Assign(Select(qual, name), rhs) - if qual.hasSymbol && (objs contains qual.symbol) => + if qual.hasSymbolField && (objs contains qual.symbol) => val sym = qual.symbol val proxy = proxySyms(objs indexOf sym) if (DEBUG) @@ -461,7 +461,7 @@ abstract class Detach extends PluginComponent // transforms field $outer.name$1 into getter method $outer.name$1() case Select(qual @ Select(_, name1), name) - if qual.hasSymbol && name1.endsWith(nme.OUTER/*, nme.OUTER.length*/) && + if qual.hasSymbolField && name1.endsWith(nme.OUTER/*, nme.OUTER.length*/) && !tree.symbol.isMethod => if (DEBUG) println("\nTreeAccessorSubstituter: Select0\n\tqual="+qual+ @@ -500,7 +500,7 @@ abstract class Detach extends PluginComponent // transforms field access $outer.i$1.elem // into invocation of getter method $outer.i$1() case Select(qual @ Select(qual1, name1), name) - if qual.hasSymbol && !qual.symbol.isPrivateLocal && + if qual.hasSymbolField && !qual.symbol.isPrivateLocal && isRemoteRefClass(qual.tpe) => if (DEBUG) println("\nTreeAccessorSubstituter: Select2\n\tqual="+qual+ @@ -513,7 +513,7 @@ abstract class Detach extends PluginComponent // transforms local access this.i$1.elem // into invocation of getter method this.i$1() case Select(qual, name) - if qual.hasSymbol && qual.symbol.isPrivateLocal && + if qual.hasSymbolField && qual.symbol.isPrivateLocal && isRemoteRefClass(qual.tpe) => if (DEBUG) println("\nTreeAccessorSubstituter: Select3\n\tqual="+qual+ @@ -523,7 +523,7 @@ abstract class Detach extends PluginComponent Apply(fun, List()) setType tree.tpe case Select(qual, name) - if qual.hasSymbol && (objs contains qual.symbol) => + if qual.hasSymbolField && (objs contains qual.symbol) => if (DEBUG) println("\nTreeAccessorSubstituter: Select4\n\tqual="+qual+ ", qual.tpe="+qual.tpe+", name="+name)//debug diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala index 880f3f4623..755071a14f 100644 --- a/src/library/scala/Option.scala +++ b/src/library/scala/Option.scala @@ -209,6 +209,15 @@ sealed abstract class Option[+A] extends Product with Serializable { def withFilter(q: A => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) } + /** Tests whether the option contains a given value as an element. + * + * @param elem the element to test. + * @return `true` if the option has an element that is equal (as + * determined by `==`) to `elem`, `false` otherwise. + */ + final def contains[A1 >: A](elem: A1): Boolean = + !isEmpty && this.get == elem + /** Returns true if this option is nonempty '''and''' the predicate * $p returns true when applied to this $option's value. * Otherwise, returns false. diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index fb43d56020..4078168bb3 100644 --- a/src/library/scala/StringContext.scala +++ b/src/library/scala/StringContext.scala @@ -120,7 +120,7 @@ case class StringContext(parts: String*) { val bldr = new java.lang.StringBuilder(process(pi.next())) while (ai.hasNext) { bldr append ai.next - bldr append treatEscapes(pi.next()) + bldr append process(pi.next()) } bldr.toString } diff --git a/src/library/scala/UninitializedFieldError.scala b/src/library/scala/UninitializedFieldError.scala index a6e510a849..9485019aa0 100644 --- a/src/library/scala/UninitializedFieldError.scala +++ b/src/library/scala/UninitializedFieldError.scala @@ -18,8 +18,6 @@ package scala * * @since 2.7 */ -final case class UninitializedFieldError(msg: String) - extends RuntimeException(msg) { - def this(obj: Any) = - this(if (null != obj) obj.toString() else "null") +final case class UninitializedFieldError(msg: String) extends RuntimeException(msg) { + def this(obj: Any) = this("" + obj) } diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index a872bc0948..9167280910 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -261,11 +261,12 @@ trait GenTraversableOnce[+A] extends Any { * @tparam B the type of accumulated results * @param z the initial value for the accumulated result of the partition - this * will typically be the neutral element for the `seqop` operator (e.g. - * `Nil` for list concatenation or `0` for summation) + * `Nil` for list concatenation or `0` for summation) and may be evaluated + * more than once * @param seqop an operator used to accumulate results within a partition * @param combop an associative operator used to combine results from different partitions */ - def aggregate[B](z: B)(seqop: (B, A) => B, combop: (B, B) => B): B + def aggregate[B](z: =>B)(seqop: (B, A) => B, combop: (B, B) => B): B /** Applies a binary operator to all elements of this $coll, going right to left. * $willNotTerminateInf diff --git a/src/library/scala/collection/IndexedSeq.scala b/src/library/scala/collection/IndexedSeq.scala index 56dd0bffff..8918fbb6c8 100644 --- a/src/library/scala/collection/IndexedSeq.scala +++ b/src/library/scala/collection/IndexedSeq.scala @@ -6,8 +6,6 @@ ** |/ ** \* */ - - package scala.collection import generic._ @@ -28,8 +26,13 @@ trait IndexedSeq[+A] extends Seq[A] * @define coll indexed sequence * @define Coll `IndexedSeq` */ -object IndexedSeq extends SeqFactory[IndexedSeq] { - implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, IndexedSeq[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] +object IndexedSeq extends IndexedSeqFactory[IndexedSeq] { + // A single CBF which can be checked against to identify + // an indexed collection type. + override val ReusableCBF: GenericCanBuildFrom[Nothing] = new GenericCanBuildFrom[Nothing] { + override def apply() = newBuilder[Nothing] + } def newBuilder[A]: Builder[A, IndexedSeq[A]] = immutable.IndexedSeq.newBuilder[A] + implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, IndexedSeq[A]] = + ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] } - diff --git a/src/library/scala/collection/LinearSeqOptimized.scala b/src/library/scala/collection/LinearSeqOptimized.scala index 188e0e8afd..70d46b1cdc 100755 --- a/src/library/scala/collection/LinearSeqOptimized.scala +++ b/src/library/scala/collection/LinearSeqOptimized.scala @@ -83,7 +83,7 @@ trait LinearSeqOptimized[+A, +Repr <: LinearSeqOptimized[A, Repr]] extends Linea } override /*SeqLike*/ - def contains(elem: Any): Boolean = { + def contains[A1 >: A](elem: A1): Boolean = { var these = this while (!these.isEmpty) { if (these.head == elem) return true diff --git a/src/library/scala/collection/Searching.scala b/src/library/scala/collection/Searching.scala new file mode 100644 index 0000000000..33e50365ee --- /dev/null +++ b/src/library/scala/collection/Searching.scala @@ -0,0 +1,116 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.collection + +import scala.annotation.tailrec +import scala.collection.generic.IsSeqLike +import scala.math.Ordering + +/** A collection of wrappers that provide sequence classes with search functionality. + * + * Example usage: + * {{{ + * import scala.collection.Searching._ + * val l = List(1, 2, 3, 4, 5) + * l.search(3) + * // == Found(2) + * }}} + */ +object Searching { + sealed abstract class SearchResult { + def insertionPoint: Int + } + + case class Found(foundIndex: Int) extends SearchResult { + override def insertionPoint = foundIndex + } + case class InsertionPoint(insertionPoint: Int) extends SearchResult + + class SearchImpl[A, Repr](val coll: SeqLike[A, Repr]) { + /** Search the sorted sequence for a specific element. If the sequence is an + * `IndexedSeq`, a binary search is used. Otherwise, a linear search is used. + * + * The sequence should be sorted with the same `Ordering` before calling; otherwise, + * the results are undefined. + * + * @see [[scala.collection.IndexedSeq]] + * @see [[scala.math.Ordering]] + * @see [[scala.collection.SeqLike]], method `sorted` + * + * @param elem the element to find. + * @param ord the ordering to be used to compare elements. + * + * @return a `Found` value containing the index corresponding to the element in the + * sequence, or the `InsertionPoint` where the element would be inserted if + * the element is not in the sequence. + */ + final def search[B >: A](elem: B)(implicit ord: Ordering[B]): SearchResult = + coll match { + case _: IndexedSeq[A] => binarySearch(elem, -1, coll.length)(ord) + case _ => linearSearch(coll.view, elem, 0)(ord) + } + + /** Search within an interval in the sorted sequence for a specific element. If the + * sequence is an IndexedSeq, a binary search is used. Otherwise, a linear search + * is used. + * + * The sequence should be sorted with the same `Ordering` before calling; otherwise, + * the results are undefined. + * + * @see [[scala.collection.IndexedSeq]] + * @see [[scala.math.Ordering]] + * @see [[scala.collection.SeqLike]], method `sorted` + * + * @param elem the element to find. + * @param from the index where the search starts. + * @param to the index following where the search ends. + * @param ord the ordering to be used to compare elements. + * + * @return a `Found` value containing the index corresponding to the element in the + * sequence, or the `InsertionPoint` where the element would be inserted if + * the element is not in the sequence. + */ + final def search[B >: A](elem: B, from: Int, to: Int) + (implicit ord: Ordering[B]): SearchResult = + coll match { + case _: IndexedSeq[A] => binarySearch(elem, from-1, to)(ord) + case _ => linearSearch(coll.view(from, to), elem, from)(ord) + } + + @tailrec + private def binarySearch[B >: A](elem: B, from: Int, to: Int) + (implicit ord: Ordering[B]): SearchResult = { + if ((to-from) == 1) InsertionPoint(from) else { + val idx = from+(to-from)/2 + math.signum(ord.compare(elem, coll(idx))) match { + case -1 => binarySearch(elem, from, idx)(ord) + case 1 => binarySearch(elem, idx, to)(ord) + case _ => Found(idx) + } + } + } + + private def linearSearch[B >: A](c: SeqView[A, Repr], elem: B, offset: Int) + (implicit ord: Ordering[B]): SearchResult = { + var idx = offset + val it = c.iterator + while (it.hasNext) { + val cur = it.next() + if (ord.equiv(elem, cur)) return Found(idx) + else if (ord.lt(elem, cur)) return InsertionPoint(idx-1) + idx += 1 + } + InsertionPoint(idx) + } + + } + + implicit def search[Repr, A](coll: Repr) + (implicit fr: IsSeqLike[Repr]): SearchImpl[fr.A, Repr] = new SearchImpl(fr.conversion(coll)) +} diff --git a/src/library/scala/collection/SeqLike.scala b/src/library/scala/collection/SeqLike.scala index cda8b1a0e4..a1749a480b 100644 --- a/src/library/scala/collection/SeqLike.scala +++ b/src/library/scala/collection/SeqLike.scala @@ -386,7 +386,7 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ * @return `true` if this $coll has an element that is equal (as * determined by `==`) to `elem`, `false` otherwise. */ - def contains(elem: Any): Boolean = exists (_ == elem) + def contains[A1 >: A](elem: A1): Boolean = exists (_ == elem) /** Produces a new sequence which contains all elements of this $coll and also all elements of * a given sequence. `xs union ys` is equivalent to `xs ++ ys`. diff --git a/src/library/scala/collection/SeqProxyLike.scala b/src/library/scala/collection/SeqProxyLike.scala index 3783ef771f..7e77418996 100644 --- a/src/library/scala/collection/SeqProxyLike.scala +++ b/src/library/scala/collection/SeqProxyLike.scala @@ -50,7 +50,7 @@ trait SeqProxyLike[+A, +Repr <: SeqLike[A, Repr] with Seq[A]] extends SeqLike[A, override def lastIndexOfSlice[B >: A](that: GenSeq[B]): Int = self.lastIndexOfSlice(that) override def lastIndexOfSlice[B >: A](that: GenSeq[B], end: Int): Int = self.lastIndexOfSlice(that, end) override def containsSlice[B](that: GenSeq[B]): Boolean = self.indexOfSlice(that) != -1 - override def contains(elem: Any): Boolean = self.contains(elem) + override def contains[A1 >: A](elem: A1): Boolean = self.contains(elem) override def union[B >: A, That](that: GenSeq[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = self.union(that)(bf) override def diff[B >: A](that: GenSeq[B]): Repr = self.diff(that) override def intersect[B >: A](that: GenSeq[B]): Repr = self.intersect(that) diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala index f912304680..a61d1354dc 100644 --- a/src/library/scala/collection/TraversableOnce.scala +++ b/src/library/scala/collection/TraversableOnce.scala @@ -184,7 +184,7 @@ trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] { def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op) - def aggregate[B](z: B)(seqop: (B, A) => B, combop: (B, B) => B): B = foldLeft(z)(seqop) + def aggregate[B](z: =>B)(seqop: (B, A) => B, combop: (B, B) => B): B = foldLeft(z)(seqop) def sum[B >: A](implicit num: Numeric[B]): B = foldLeft(num.zero)(num.plus) diff --git a/src/library/scala/collection/generic/GenTraversableFactory.scala b/src/library/scala/collection/generic/GenTraversableFactory.scala index 6b347db7a0..91faed9e48 100644 --- a/src/library/scala/collection/generic/GenTraversableFactory.scala +++ b/src/library/scala/collection/generic/GenTraversableFactory.scala @@ -38,13 +38,10 @@ import scala.language.higherKinds abstract class GenTraversableFactory[CC[X] <: GenTraversable[X] with GenericTraversableTemplate[X, CC]] extends GenericCompanion[CC] { - // A default implementation of GenericCanBuildFrom which can be cast - // to whatever is desired. - private class ReusableCBF extends GenericCanBuildFrom[Nothing] { + private[this] val ReusableCBFInstance: GenericCanBuildFrom[Nothing] = new GenericCanBuildFrom[Nothing] { override def apply() = newBuilder[Nothing] } - // Working around SI-4789 by using a lazy val instead of an object. - lazy val ReusableCBF: GenericCanBuildFrom[Nothing] = new ReusableCBF + def ReusableCBF: GenericCanBuildFrom[Nothing] = ReusableCBFInstance /** A generic implementation of the `CanBuildFrom` trait, which forwards * all calls to `apply(from)` to the `genericBuilder` method of @@ -252,4 +249,3 @@ abstract class GenTraversableFactory[CC[X] <: GenTraversable[X] with GenericTrav b.result } } - diff --git a/src/library/scala/collection/generic/IndexedSeqFactory.scala b/src/library/scala/collection/generic/IndexedSeqFactory.scala new file mode 100644 index 0000000000..e5162c640b --- /dev/null +++ b/src/library/scala/collection/generic/IndexedSeqFactory.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.collection +package generic + +import language.higherKinds + +/** A template for companion objects of IndexedSeq and subclasses thereof. + * + * @since 2.8 + */ +abstract class IndexedSeqFactory[CC[X] <: IndexedSeq[X] with GenericTraversableTemplate[X, CC]] extends SeqFactory[CC] { + override def ReusableCBF: GenericCanBuildFrom[Nothing] = + scala.collection.IndexedSeq.ReusableCBF.asInstanceOf[GenericCanBuildFrom[Nothing]] +} diff --git a/src/library/scala/collection/generic/IsSeqLike.scala b/src/library/scala/collection/generic/IsSeqLike.scala new file mode 100644 index 0000000000..9467510a2c --- /dev/null +++ b/src/library/scala/collection/generic/IsSeqLike.scala @@ -0,0 +1,57 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.collection +package generic + +/** Type class witnessing that a collection representation type `Repr` has + * elements of type `A` and has a conversion to `SeqLike[A, Repr]`. + * + * This type enables simple enrichment of `Seq`s with extension methods which + * can make full use of the mechanics of the Scala collections framework in + * their implementation. + * + * Example usage: + * {{{ + * class FilterMapImpl[A, Repr](val r: SeqLike[A, Repr]) { + * final def filterMap[B, That](f: A => Option[B])(implicit cbf: CanBuildFrom[Repr, B, That]): That = + * r.flatMap(f(_)) + * } + * implicit def filterMap[Repr, A](r: Repr)(implicit fr: IsSeqLike[Repr]): FilterMapImpl[fr.A,Repr] = + * new FilterMapImpl(fr.conversion(r)) + * + * val l = List(1, 2, 3, 4, 5) + * List(1, 2, 3, 4, 5) filterMap (i => if(i % 2 == 0) Some(i) else None) + * // == List(2, 4) + * }}} + * + * @see [[scala.collection.Seq]] + * @see [[scala.collection.generic.IsTraversableLike]] + */ +trait IsSeqLike[Repr] { + /** The type of elements we can traverse over. */ + type A + /** A conversion from the representation type `Repr` to a `SeqLike[A,Repr]`. */ + val conversion: Repr => SeqLike[A, Repr] +} + +object IsSeqLike { + import language.higherKinds + + implicit val stringRepr: IsSeqLike[String] { type A = Char } = + new IsSeqLike[String] { + type A = Char + val conversion = implicitly[String => SeqLike[Char, String]] + } + + implicit def seqLikeRepr[C[_], A0](implicit conv: C[A0] => SeqLike[A0,C[A0]]): IsSeqLike[C[A0]] { type A = A0 } = + new IsSeqLike[C[A0]] { + type A = A0 + val conversion = conv + } +} diff --git a/src/library/scala/collection/generic/SeqForwarder.scala b/src/library/scala/collection/generic/SeqForwarder.scala index 10e8c37cbf..bdec165314 100644 --- a/src/library/scala/collection/generic/SeqForwarder.scala +++ b/src/library/scala/collection/generic/SeqForwarder.scala @@ -50,7 +50,7 @@ trait SeqForwarder[+A] extends Seq[A] with IterableForwarder[A] { override def lastIndexOfSlice[B >: A](that: GenSeq[B]): Int = underlying lastIndexOfSlice that override def lastIndexOfSlice[B >: A](that: GenSeq[B], end: Int): Int = underlying.lastIndexOfSlice(that, end) override def containsSlice[B](that: GenSeq[B]): Boolean = underlying containsSlice that - override def contains(elem: Any): Boolean = underlying contains elem + override def contains[A1 >: A](elem: A1): Boolean = underlying contains elem override def corresponds[B](that: GenSeq[B])(p: (A,B) => Boolean): Boolean = underlying.corresponds(that)(p) override def indices: Range = underlying.indices } diff --git a/src/library/scala/collection/immutable/IndexedSeq.scala b/src/library/scala/collection/immutable/IndexedSeq.scala index b37edc4254..a5d5728191 100644 --- a/src/library/scala/collection/immutable/IndexedSeq.scala +++ b/src/library/scala/collection/immutable/IndexedSeq.scala @@ -31,11 +31,13 @@ trait IndexedSeq[+A] extends Seq[A] * @define coll indexed sequence * @define Coll `IndexedSeq` */ -object IndexedSeq extends SeqFactory[IndexedSeq] { +object IndexedSeq extends IndexedSeqFactory[IndexedSeq] { class Impl[A](buf: ArrayBuffer[A]) extends AbstractSeq[A] with IndexedSeq[A] with Serializable { def length = buf.length def apply(idx: Int) = buf.apply(idx) } - implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, IndexedSeq[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] def newBuilder[A]: Builder[A, IndexedSeq[A]] = Vector.newBuilder[A] + + implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, IndexedSeq[A]] = + ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] } diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 7a489bb100..83da68eb68 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -152,7 +152,7 @@ sealed abstract class List[+A] extends AbstractSeq[A] * @usecase def mapConserve(f: A => A): List[A] * @inheritdoc */ - def mapConserve[B >: A <: AnyRef](f: A => B): List[B] = { + @inline final def mapConserve[B >: A <: AnyRef](f: A => B): List[B] = { @tailrec def loop(mapped: ListBuffer[B], unchanged: List[A], pending: List[A]): List[B] = if (pending.isEmpty) { @@ -257,7 +257,7 @@ sealed abstract class List[+A] extends AbstractSeq[A] (b.toList, these) } - override def takeWhile(p: A => Boolean): List[A] = { + @inline final override def takeWhile(p: A => Boolean): List[A] = { val b = new ListBuffer[A] var these = this while (!these.isEmpty && p(these.head)) { @@ -267,7 +267,7 @@ sealed abstract class List[+A] extends AbstractSeq[A] b.toList } - override def dropWhile(p: A => Boolean): List[A] = { + @inline final override def dropWhile(p: A => Boolean): List[A] = { @tailrec def loop(xs: List[A]): List[A] = if (xs.isEmpty || !p(xs.head)) xs @@ -276,7 +276,7 @@ sealed abstract class List[+A] extends AbstractSeq[A] loop(this) } - override def span(p: A => Boolean): (List[A], List[A]) = { + @inline final override def span(p: A => Boolean): (List[A], List[A]) = { val b = new ListBuffer[A] var these = this while (!these.isEmpty && p(these.head)) { @@ -286,6 +286,16 @@ sealed abstract class List[+A] extends AbstractSeq[A] (b.toList, these) } + // Overridden with an implementation identical to the inherited one (at this time) + // solely so it can be finalized and thus inlinable. + @inline final override def foreach[U](f: A => U) { + var these = this + while (!these.isEmpty) { + f(these.head) + these = these.tail + } + } + override def reverse: List[A] = { var result: List[A] = Nil var these = this @@ -302,15 +312,6 @@ sealed abstract class List[+A] extends AbstractSeq[A] if (isEmpty) Stream.Empty else new Stream.Cons(head, tail.toStream) - @inline override final - def foreach[B](f: A => B) { - var these = this - while (!these.isEmpty) { - f(these.head) - these = these.tail - } - } - @deprecated("use `distinct` instead", "2.8.0") def removeDuplicates: List[A] = distinct } diff --git a/src/library/scala/collection/immutable/NumericRange.scala b/src/library/scala/collection/immutable/NumericRange.scala index 5662a11f93..ce04ef09af 100644 --- a/src/library/scala/collection/immutable/NumericRange.scala +++ b/src/library/scala/collection/immutable/NumericRange.scala @@ -182,7 +182,7 @@ extends AbstractSeq[T] with IndexedSeq[T] with Serializable { def containsTyped(x: T): Boolean = isWithinBoundaries(x) && (((x - start) % step) == zero) - override def contains(x: Any): Boolean = + override def contains[A1 >: T](x: A1): Boolean = try containsTyped(x.asInstanceOf[T]) catch { case _: ClassCastException => false } diff --git a/src/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala index 92ea5d3f04..ab303dde56 100644 --- a/src/library/scala/collection/immutable/Range.scala +++ b/src/library/scala/collection/immutable/Range.scala @@ -78,6 +78,7 @@ extends scala.collection.AbstractSeq[Int] final val terminalElement = start + numRangeElements * step override def last = if (isEmpty) Nil.last else lastElement + override def head = if (isEmpty) Nil.head else start override def min[A1 >: Int](implicit ord: Ordering[A1]): Int = if (ord eq Ordering.Int) { diff --git a/src/library/scala/collection/immutable/RedBlackTree.scala b/src/library/scala/collection/immutable/RedBlackTree.scala index bb489dd80a..328d929fb9 100644 --- a/src/library/scala/collection/immutable/RedBlackTree.scala +++ b/src/library/scala/collection/immutable/RedBlackTree.scala @@ -74,17 +74,23 @@ object RedBlackTree { result } - def foreach[A, B, U](tree: Tree[A, B], f: ((A, B)) => U): Unit = if (tree ne null) { - if (tree.left ne null) foreach(tree.left, f) + + def foreach[A,B,U](tree:Tree[A,B], f:((A,B)) => U):Unit = if (tree ne null) _foreach(tree,f) + + private[this] def _foreach[A, B, U](tree: Tree[A, B], f: ((A, B)) => U) { + if (tree.left ne null) _foreach(tree.left, f) f((tree.key, tree.value)) - if (tree.right ne null) foreach(tree.right, f) - } - def foreachKey[A, U](tree: Tree[A, _], f: A => U): Unit = if (tree ne null) { - if (tree.left ne null) foreachKey(tree.left, f) - f(tree.key) - if (tree.right ne null) foreachKey(tree.right, f) + if (tree.right ne null) _foreach(tree.right, f) } + + def foreachKey[A, U](tree:Tree[A,_], f: A => U):Unit = if (tree ne null) _foreachKey(tree,f) + private[this] def _foreachKey[A, U](tree: Tree[A, _], f: A => U) { + if (tree.left ne null) _foreachKey(tree.left, f) + f((tree.key)) + if (tree.right ne null) _foreachKey(tree.right, f) + } + def iterator[A, B](tree: Tree[A, B]): Iterator[(A, B)] = new EntriesIterator(tree) def keysIterator[A, _](tree: Tree[A, _]): Iterator[A] = new KeysIterator(tree) def valuesIterator[_, B](tree: Tree[_, B]): Iterator[B] = new ValuesIterator(tree) diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index a33bf2c9c5..895d073869 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -18,16 +18,10 @@ import scala.collection.parallel.immutable.ParVector /** Companion object to the Vector class */ -object Vector extends SeqFactory[Vector] { - private[collection] class VectorReusableCBF extends GenericCanBuildFrom[Nothing] { - override def apply() = newBuilder[Nothing] - } - - private val VectorReusableCBF: GenericCanBuildFrom[Nothing] = new VectorReusableCBF - - implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Vector[A]] = - VectorReusableCBF.asInstanceOf[CanBuildFrom[Coll, A, Vector[A]]] +object Vector extends IndexedSeqFactory[Vector] { def newBuilder[A]: Builder[A, Vector[A]] = new VectorBuilder[A] + implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Vector[A]] = + ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] private[immutable] val NIL = new Vector[Nothing](0, 0, 0) override def empty[A]: Vector[A] = NIL } @@ -137,20 +131,17 @@ override def companion: GenericCompanion[Vector] = Vector // SeqLike api - override def updated[B >: A, That](index: Int, elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = bf match { - case _: Vector.VectorReusableCBF => updateAt(index, elem).asInstanceOf[That] // just ignore bf - case _ => super.updated(index, elem)(bf) - } + override def updated[B >: A, That](index: Int, elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = + if (bf eq IndexedSeq.ReusableCBF) updateAt(index, elem).asInstanceOf[That] // just ignore bf + else super.updated(index, elem)(bf) - override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = bf match { - case _: Vector.VectorReusableCBF => appendFront(elem).asInstanceOf[That] // just ignore bf - case _ => super.+:(elem)(bf) - } + override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = + if (bf eq IndexedSeq.ReusableCBF) appendFront(elem).asInstanceOf[That] // just ignore bf + else super.+:(elem)(bf) - override def :+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = bf match { - case _: Vector.VectorReusableCBF => appendBack(elem).asInstanceOf[That] // just ignore bf - case _ => super.:+(elem)(bf) - } + override def :+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = + if (bf eq IndexedSeq.ReusableCBF) appendBack(elem).asInstanceOf[That] // just ignore bf + else super.:+(elem)(bf) override def take(n: Int): Vector[A] = { if (n <= 0) diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index cd743999bc..bced92e663 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -56,12 +56,18 @@ final class ListBuffer[A] import scala.collection.Traversable import scala.collection.immutable.ListSerializeEnd + /** Expected invariants: + * If start.isEmpty, last0 == null + * If start.nonEmpty, last0 != null + * If len == 0, start.isEmpty + * If len > 0, start.nonEmpty + */ private var start: List[A] = Nil private var last0: ::[A] = _ private var exported: Boolean = false private var len = 0 - protected def underlying: immutable.Seq[A] = start + protected def underlying: List[A] = start private def writeObject(out: ObjectOutputStream) { // write start @@ -133,7 +139,7 @@ final class ListBuffer[A] if (exported) copy() if (n == 0) { val newElem = new :: (x, start.tail); - if (last0 eq start) { + if ((last0 eq null) || (last0 eq start)) { last0 = newElem } start = newElem @@ -162,7 +168,7 @@ final class ListBuffer[A] */ def += (x: A): this.type = { if (exported) copy() - if (start.isEmpty) { + if (isEmpty) { last0 = new :: (x, Nil) start = last0 } else { @@ -184,6 +190,7 @@ final class ListBuffer[A] */ def clear() { start = Nil + last0 = null exported = false len = 0 } @@ -197,7 +204,7 @@ final class ListBuffer[A] def +=: (x: A): this.type = { if (exported) copy() val newElem = new :: (x, start) - if (start.isEmpty) last0 = newElem + if (isEmpty) last0 = newElem start = newElem len += 1 this @@ -219,7 +226,7 @@ final class ListBuffer[A] if (n == 0) { while (!elems.isEmpty) { val newElem = new :: (elems.head, start) - if (start.isEmpty) last0 = newElem + if (isEmpty) last0 = newElem start = newElem elems = elems.tail } @@ -243,6 +250,15 @@ final class ListBuffer[A] } } + /** Reduce the length of the buffer, and null out last0 + * if this reduces the length to 0. + */ + private def reduceLengthBy(num: Int) { + len -= num + if (len <= 0) // obviously shouldn't be < 0, but still better not to leak + last0 = null + } + /** Removes a given number of elements on a given index position. May take * time linear in the buffer size. * @@ -274,7 +290,7 @@ final class ListBuffer[A] c -= 1 } } - len -= count1 + reduceLengthBy(count1) } // Implementation of abstract method in Builder @@ -285,7 +301,7 @@ final class ListBuffer[A] * copied lazily, the first time it is mutated. */ override def toList: List[A] = { - exported = !start.isEmpty + exported = !isEmpty start } @@ -296,7 +312,7 @@ final class ListBuffer[A] * @param xs the list to which elements are prepended */ def prependToList(xs: List[A]): List[A] = { - if (start.isEmpty) xs + if (isEmpty) xs else { if (exported) copy() last0.tl = xs @@ -331,7 +347,7 @@ final class ListBuffer[A] if (last0 eq cursor.tail) last0 = cursor.asInstanceOf[::[A]] cursor.asInstanceOf[::[A]].tl = cursor.tail.tail } - len -= 1 + reduceLengthBy(1) old } @@ -343,11 +359,12 @@ final class ListBuffer[A] */ override def -= (elem: A): this.type = { if (exported) copy() - if (start.isEmpty) {} + if (isEmpty) {} else if (start.head == elem) { start = start.tail - len -= 1 - } else { + reduceLengthBy(1) + } + else { var cursor = start while (!cursor.tail.isEmpty && cursor.tail.head != elem) { cursor = cursor.tail @@ -357,7 +374,7 @@ final class ListBuffer[A] if (z.tl == last0) last0 = z z.tl = cursor.tail.tail - len -= 1 + reduceLengthBy(1) } } this @@ -397,6 +414,7 @@ final class ListBuffer[A] /** Copy contents of this buffer */ private def copy() { + if (isEmpty) return var cursor = start val limit = last0.tail clear() diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index f6fb32e152..0c0ff2b027 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -433,12 +433,13 @@ self: ParIterableLike[T, Repr, Sequential] => * @tparam S the type of accumulated results * @param z the initial value for the accumulated result of the partition - this * will typically be the neutral element for the `seqop` operator (e.g. - * `Nil` for list concatenation or `0` for summation) + * `Nil` for list concatenation or `0` for summation) and may be evaluated + * more than once * @param seqop an operator used to accumulate results within a partition * @param combop an associative operator used to combine results from different partitions */ - def aggregate[S](z: S)(seqop: (S, T) => S, combop: (S, S) => S): S = { - tasksupport.executeAndWaitResult(new Aggregate(z, seqop, combop, splitter)) + def aggregate[S](z: =>S)(seqop: (S, T) => S, combop: (S, S) => S): S = { + tasksupport.executeAndWaitResult(new Aggregate(() => z, seqop, combop, splitter)) } def foldLeft[S](z: S)(op: (S, T) => S): S = seq.foldLeft(z)(op) @@ -936,8 +937,8 @@ self: ParIterableLike[T, Repr, Sequential] => (f: First, s: Second) extends Composite[FR, SR, R, First, Second](f, s) { def leaf(prevr: Option[R]) = { - tasksupport.executeAndWaitResult(ft) - tasksupport.executeAndWaitResult(st) + tasksupport.executeAndWaitResult(ft) : Any + tasksupport.executeAndWaitResult(st) : Any mergeSubtasks } } @@ -947,8 +948,8 @@ self: ParIterableLike[T, Repr, Sequential] => (f: First, s: Second) extends Composite[FR, SR, R, First, Second](f, s) { def leaf(prevr: Option[R]) = { - val ftfuture = tasksupport.execute(ft) - tasksupport.executeAndWaitResult(st) + val ftfuture: () => Any = tasksupport.execute(ft) + tasksupport.executeAndWaitResult(st) : Any ftfuture() mergeSubtasks } @@ -1006,10 +1007,10 @@ self: ParIterableLike[T, Repr, Sequential] => override def merge(that: Fold[U]) = result = op(result, that.result) } - protected[this] class Aggregate[S](z: S, seqop: (S, T) => S, combop: (S, S) => S, protected[this] val pit: IterableSplitter[T]) + protected[this] class Aggregate[S](z: () => S, seqop: (S, T) => S, combop: (S, S) => S, protected[this] val pit: IterableSplitter[T]) extends Accessor[S, Aggregate[S]] { @volatile var result: S = null.asInstanceOf[S] - def leaf(prevr: Option[S]) = result = pit.foldLeft(z)(seqop) + def leaf(prevr: Option[S]) = result = pit.foldLeft(z())(seqop) protected[this] def newSubtask(p: IterableSplitter[T]) = new Aggregate(z, seqop, combop, p) override def merge(that: Aggregate[S]) = result = combop(result, that.result) } @@ -1505,31 +1506,3 @@ self: ParIterableLike[T, Repr, Sequential] => }) } - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/library/scala/collection/parallel/mutable/ParArray.scala b/src/library/scala/collection/parallel/mutable/ParArray.scala index 56cc06f99e..deff9eda3b 100644 --- a/src/library/scala/collection/parallel/mutable/ParArray.scala +++ b/src/library/scala/collection/parallel/mutable/ParArray.scala @@ -181,7 +181,7 @@ self => override def fold[U >: T](z: U)(op: (U, U) => U): U = foldLeft[U](z)(op) - override def aggregate[S](z: S)(seqop: (S, T) => S, combop: (S, S) => S): S = foldLeft[S](z)(seqop) + override def aggregate[S](z: =>S)(seqop: (S, T) => S, combop: (S, S) => S): S = foldLeft[S](z)(seqop) override def sum[U >: T](implicit num: Numeric[U]): U = { var s = sum_quick(num, arr, until, i, num.zero) diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 5c9e36450b..1e3fa78af3 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -26,8 +26,7 @@ import java.lang.reflect.{ Modifier, Method => JMethod } * outside the API and subject to change or removal without notice. */ object ScalaRunTime { - def isArray(x: AnyRef): Boolean = isArray(x, 1) - def isArray(x: Any, atLevel: Int): Boolean = + def isArray(x: Any, atLevel: Int = 1): Boolean = x != null && isArrayClass(x.getClass, atLevel) private def isArrayClass(clazz: jClass[_], atLevel: Int): Boolean = diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 3655a0a019..63d049208a 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -131,7 +131,7 @@ import java.util.regex.{ Pattern, Matcher } * @author Martin Odersky * @version 1.1, 29/01/2008 * - * @param regex A string representing a regular expression + * @param pattern The compiled pattern * @param groupNames A mapping from names to indices in capture groups * * @define replacementString @@ -144,41 +144,67 @@ import java.util.regex.{ Pattern, Matcher } * to automatically escape these characters. */ @SerialVersionUID(-2094783597747625537L) -class Regex(regex: String, groupNames: String*) extends Serializable { +class Regex private[matching](val pattern: Pattern, groupNames: String*) extends Serializable { outer => import Regex._ - /** The compiled pattern */ - val pattern = Pattern.compile(regex) + /** + * @param regex A string representing a regular expression + * @param groupNames A mapping from names to indices in capture groups + */ + def this(regex: String, groupNames: String*) = this(Pattern.compile(regex), groupNames: _*) - /** Tries to match target (whole match) and returns the matching subgroups. - * if the pattern has no subgroups, then it returns an empty list on a - * successful match. - * - * Note, however, that if some subgroup has not been matched, a `null` will - * be returned for that subgroup. + /** Tries to match a [[java.lang.CharSequence]]. + * If the match succeeds, the result is a list of the matching + * groups (or a `null` element if a group did not match any input). + * If the pattern specifies no groups, then the result will be an empty list + * on a successful match. * + * This method attempts to match the entire input by default; to find the next + * matching subsequence, use an unanchored Regex. + * For example: * * {{{ * val p1 = "ab*c".r - * val p2 = "a(b*)c".r - * * val p1Matches = "abbbc" match { * case p1() => true * case _ => false * } - * + * val p2 = "a(b*)c".r * val numberOfB = "abbbc" match { * case p2(b) => Some(b.length) * case _ => None * } + * val p3 = "b*".r.unanchored + * val p3Matches = "abbbc" match { + * case p3() => true + * case _ => false + * } * }}} * - * @param target The string to match + * @param s The string to match * @return The matches */ + def unapplySeq(s: CharSequence): Option[Seq[String]] = { + val m = pattern matcher s + if (runMatcher(m)) Some(1 to m.groupCount map m.group) + else None + } + + /** Tries to match on a [[scala.util.matching.Regex.Match]]. + * A previously failed match results in None. + * If a successful match was made against the current pattern, then that result is used. + * Otherwise, this Regex is applied to the previously matched input, + * and the result of that match is used. + */ + def unapplySeq(m: Match): Option[Seq[String]] = + if (m.matched == null) None + else if (m.matcher.pattern == this.pattern) Some(1 to m.groupCount map m.group) + else unapplySeq(m.matched) + + @deprecated("Extracting a match result from anything but a CharSequence or Match is deprecated", "2.10.0") def unapplySeq(target: Any): Option[List[String]] = target match { case s: CharSequence => val m = pattern matcher s @@ -187,6 +213,8 @@ class Regex(regex: String, groupNames: String*) extends Serializable { case m: Match => unapplySeq(m.matched) case _ => None } + + // @see UnanchoredRegex protected def runMatcher(m: Matcher) = m.matches() /** Return all matches of this regexp in given character sequence as a [[scala.util.matching.Regex.MatchIterator]], @@ -200,7 +228,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return A [[scala.util.matching.Regex.MatchIterator]] of all matches. * @example {{{for (words <- """\w+""".r findAllIn "A simple example.") yield words}}} */ - def findAllIn(source: java.lang.CharSequence) = new Regex.MatchIterator(source, this, groupNames) + def findAllIn(source: CharSequence) = new Regex.MatchIterator(source, this, groupNames) /** Return all matches of this regexp in given character sequence as a @@ -210,7 +238,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return A [[scala.collection.Iterator]] of [[scala.util.matching.Regex.Match]] for all matches. * @example {{{for (words <- """\w+""".r findAllMatchIn "A simple example.") yield words.start}}} */ - def findAllMatchIn(source: java.lang.CharSequence): Iterator[Match] = { + def findAllMatchIn(source: CharSequence): Iterator[Match] = { val matchIterator = findAllIn(source) new Iterator[Match] { def hasNext = matchIterator.hasNext @@ -228,7 +256,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return An [[scala.Option]] of the first matching string in the text. * @example {{{"""\w+""".r findFirstIn "A simple example." foreach println // prints "A"}}} */ - def findFirstIn(source: java.lang.CharSequence): Option[String] = { + def findFirstIn(source: CharSequence): Option[String] = { val m = pattern.matcher(source) if (m.find) Some(m.group) else None } @@ -245,7 +273,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return A [[scala.Option]] of [[scala.util.matching.Regex.Match]] of the first matching string in the text. * @example {{{("""[a-z]""".r findFirstMatchIn "A simple example.") map (_.start) // returns Some(2), the index of the first match in the text}}} */ - def findFirstMatchIn(source: java.lang.CharSequence): Option[Match] = { + def findFirstMatchIn(source: CharSequence): Option[Match] = { val m = pattern.matcher(source) if (m.find) Some(new Match(source, m, groupNames)) else None } @@ -262,7 +290,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return A [[scala.Option]] of the matched prefix. * @example {{{"""[a-z]""".r findPrefixOf "A simple example." // returns None, since the text does not begin with a lowercase letter}}} */ - def findPrefixOf(source: java.lang.CharSequence): Option[String] = { + def findPrefixOf(source: CharSequence): Option[String] = { val m = pattern.matcher(source) if (m.lookingAt) Some(m.group) else None } @@ -279,7 +307,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return A [[scala.Option]] of the [[scala.util.matching.Regex.Match]] of the matched string. * @example {{{"""\w+""".r findPrefixMatchOf "A simple example." map (_.after) // returns Some(" simple example.")}}} */ - def findPrefixMatchOf(source: java.lang.CharSequence): Option[Match] = { + def findPrefixMatchOf(source: CharSequence): Option[Match] = { val m = pattern.matcher(source) if (m.lookingAt) Some(new Match(source, m, groupNames)) else None } @@ -293,7 +321,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return The resulting string * @example {{{"""\d+""".r replaceAllIn ("July 15", "<NUMBER>") // returns "July <NUMBER>"}}} */ - def replaceAllIn(target: java.lang.CharSequence, replacement: String): String = { + def replaceAllIn(target: CharSequence, replacement: String): String = { val m = pattern.matcher(target) m.replaceAll(replacement) } @@ -316,7 +344,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @param replacer The function which maps a match to another string. * @return The target string after replacements. */ - def replaceAllIn(target: java.lang.CharSequence, replacer: Match => String): String = { + def replaceAllIn(target: CharSequence, replacer: Match => String): String = { val it = new Regex.MatchIterator(target, this, groupNames).replacementData it foreach (md => it replace replacer(md)) it.replaced @@ -343,7 +371,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @param replacer The function which optionally maps a match to another string. * @return The target string after replacements. */ - def replaceSomeIn(target: java.lang.CharSequence, replacer: Match => Option[String]): String = { + def replaceSomeIn(target: CharSequence, replacer: Match => Option[String]): String = { val it = new Regex.MatchIterator(target, this, groupNames).replacementData for (matchdata <- it ; replacement <- replacer(matchdata)) it replace replacement @@ -359,7 +387,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @param replacement The string that will replace the match * @return The resulting string */ - def replaceFirstIn(target: java.lang.CharSequence, replacement: String): String = { + def replaceFirstIn(target: CharSequence, replacement: String): String = { val m = pattern.matcher(target) m.replaceFirst(replacement) } @@ -370,7 +398,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return The array of strings computed by splitting the * input around matches of this regexp */ - def split(toSplit: java.lang.CharSequence): Array[String] = + def split(toSplit: CharSequence): Array[String] = pattern.split(toSplit) /** Create a new Regex with the same pattern, but no requirement that @@ -390,9 +418,11 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * * @return The new unanchored regex */ - def unanchored: UnanchoredRegex = new Regex(regex, groupNames: _*) with UnanchoredRegex { override def anchored = outer } + def unanchored: UnanchoredRegex = new Regex(pattern, groupNames: _*) with UnanchoredRegex { override def anchored = outer } def anchored: Regex = this + def regex: String = pattern.pattern + /** The string defining the regular expression */ override def toString = regex } @@ -421,7 +451,7 @@ object Regex { trait MatchData { /** The source from where the match originated */ - val source: java.lang.CharSequence + val source: CharSequence /** The names of the groups, or some empty sequence if one defined */ val groupNames: Seq[String] @@ -459,25 +489,25 @@ object Regex { /** The char sequence before first character of match, * or `null` if nothing was matched */ - def before: java.lang.CharSequence = + def before: CharSequence = if (start >= 0) source.subSequence(0, start) else null /** The char sequence before first character of match in group `i`, * or `null` if nothing was matched for that group */ - def before(i: Int): java.lang.CharSequence = + def before(i: Int): CharSequence = if (start(i) >= 0) source.subSequence(0, start(i)) else null /** Returns char sequence after last character of match, * or `null` if nothing was matched */ - def after: java.lang.CharSequence = + def after: CharSequence = if (end >= 0) source.subSequence(end, source.length) else null /** The char sequence after last character of match in group `i`, * or `null` if nothing was matched for that group */ - def after(i: Int): java.lang.CharSequence = + def after(i: Int): CharSequence = if (end(i) >= 0) source.subSequence(end(i), source.length) else null @@ -501,8 +531,8 @@ object Regex { /** Provides information about a succesful match. */ - class Match(val source: java.lang.CharSequence, - matcher: Matcher, + class Match(val source: CharSequence, + private[matching] val matcher: Matcher, val groupNames: Seq[String]) extends MatchData { /** The index of the first matched character */ @@ -563,7 +593,7 @@ object Regex { /** A class to step through a sequence of regex matches */ - class MatchIterator(val source: java.lang.CharSequence, val regex: Regex, val groupNames: Seq[String]) + class MatchIterator(val source: CharSequence, val regex: Regex, val groupNames: Seq[String]) extends AbstractIterator[String] with Iterator[String] with MatchData { self => protected[Regex] val matcher = regex.pattern.matcher(source) diff --git a/src/library/scala/util/parsing/combinator/JavaTokenParsers.scala b/src/library/scala/util/parsing/combinator/JavaTokenParsers.scala index 520ac8cc2c..4e8504d346 100644 --- a/src/library/scala/util/parsing/combinator/JavaTokenParsers.scala +++ b/src/library/scala/util/parsing/combinator/JavaTokenParsers.scala @@ -21,11 +21,12 @@ import scala.annotation.migration * - `floatingPointNumber` */ trait JavaTokenParsers extends RegexParsers { - /** Anything starting with an ASCII alphabetic character or underscore, - * followed by zero or more repetitions of regex's `\w`. + /** Anything that is a valid Java identifier, according to + * <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.8">The Java Language Spec</a>. + * Generally, this means a letter, followed by zero or more letters or numbers. */ def ident: Parser[String] = - """[a-zA-Z_]\w*""".r + """\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*""".r /** An integer, without sign or with a negative sign. */ def wholeNumber: Parser[String] = """-?\d+""".r diff --git a/src/library/scala/util/parsing/combinator/lexical/StdLexical.scala b/src/library/scala/util/parsing/combinator/lexical/StdLexical.scala index 5d7386b5c1..3d04a3a00c 100644 --- a/src/library/scala/util/parsing/combinator/lexical/StdLexical.scala +++ b/src/library/scala/util/parsing/combinator/lexical/StdLexical.scala @@ -50,7 +50,7 @@ class StdLexical extends Lexical with StdTokens { def identChar = letter | elem('_') // see `whitespace in `Scanners` - def whitespace: Parser[Any] = rep( + def whitespace: Parser[Any] = rep[Any]( whitespaceChar | '/' ~ '*' ~ comment | '/' ~ '/' ~ rep( chrExcept(EofCh, '\n') ) diff --git a/src/library/scala/xml/dtd/DocType.scala b/src/library/scala/xml/dtd/DocType.scala index 64aa7e2f74..78aacb2cea 100644 --- a/src/library/scala/xml/dtd/DocType.scala +++ b/src/library/scala/xml/dtd/DocType.scala @@ -15,7 +15,7 @@ package dtd * @author Burak Emir * * @param name name of this DOCTYPE - * @param extID None, or Some(external ID of this doctype) + * @param extID NoExternalID or the external ID of this doctype * @param intSubset sequence of internal subset declarations */ case class DocType(name: String, extID: ExternalID, intSubset: Seq[dtd.Decl]) @@ -32,3 +32,9 @@ case class DocType(name: String, extID: ExternalID, intSubset: Seq[dtd.Decl]) """<!DOCTYPE %s %s%s>""".format(name, extID.toString, intString) } } + +object DocType +{ + /** Creates a doctype with no external id, nor internal subset declarations. */ + def apply(name: String): DocType = apply(name, NoExternalID, Nil) +} diff --git a/src/library/scala/xml/dtd/ExternalID.scala b/src/library/scala/xml/dtd/ExternalID.scala index a0a5818d07..ccee5dbe5a 100644 --- a/src/library/scala/xml/dtd/ExternalID.scala +++ b/src/library/scala/xml/dtd/ExternalID.scala @@ -73,3 +73,14 @@ case class PublicID(publicId: String, systemId: String) extends ExternalID { /** always empty */ def child = Nil } + +/** A marker used when a `DocType` contains no external id. + * + * @author Michael Bayne + */ +object NoExternalID extends ExternalID { + val publicId = null + val systemId = null + + override def toString = "" +} diff --git a/src/partest/scala/tools/partest/javaagent/ASMTransformer.java b/src/partest/scala/tools/partest/javaagent/ASMTransformer.java index 09cd485d6b..a9a56d124d 100644 --- a/src/partest/scala/tools/partest/javaagent/ASMTransformer.java +++ b/src/partest/scala/tools/partest/javaagent/ASMTransformer.java @@ -26,9 +26,35 @@ public class ASMTransformer implements ClassFileTransformer { className.startsWith("instrumented/")); } - public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { + public byte[] transform(final ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (shouldTransform(className)) { - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { + // this is copied verbatim from the superclass, + // except that we use the outer class loader + @Override protected String getCommonSuperClass(final String type1, final String type2) { + Class<?> c, d; + try { + c = Class.forName(type1.replace('/', '.'), false, classLoader); + d = Class.forName(type2.replace('/', '.'), false, classLoader); + } catch (Exception e) { + throw new RuntimeException(e.toString()); + } + if (c.isAssignableFrom(d)) { + return type1; + } + if (d.isAssignableFrom(c)) { + return type2; + } + if (c.isInterface() || d.isInterface()) { + return "java/lang/Object"; + } else { + do { + c = c.getSuperclass(); + } while (!c.isAssignableFrom(d)); + return c.getName().replace('.', '/'); + } + } + }; ProfilerVisitor visitor = new ProfilerVisitor(writer); ClassReader reader = new ClassReader(classfileBuffer); reader.accept(visitor, 0); diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala index ccc756c158..84d9832f97 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -70,10 +70,11 @@ class ConsoleRunner extends DirectRunner { // true if a test path matches the --grep expression. private def pathMatchesExpr(path: Path, expr: String) = { def pred(p: Path) = file2String(p.toFile) contains expr - def srcs = path.toDirectory.deepList() filter (_.hasExtension("scala", "java")) + def greppable(f: Path) = f.isFile && (f hasExtension ("scala", "java")) + def any(d: Path) = d.toDirectory.deepList() exists (f => greppable(f) && pred(f)) (path.isFile && pred(path)) || - (path.isDirectory && srcs.exists(pred)) || + (path.isDirectory && any(path)) || (pred(path changeExtension "check")) } @@ -121,7 +122,7 @@ class ConsoleRunner extends DirectRunner { val grepOption = parsed get "--grep" val grepPaths = grepOption.toList flatMap { expr => val subjectDirs = testSetKinds map (srcDir / _ toDirectory) - val testPaths = subjectDirs flatMap (_.files filter stdFilter) + val testPaths = subjectDirs flatMap (_.list filter stdFilter) val paths = testPaths filter (p => pathMatchesExpr(p, expr)) if (paths.isEmpty) diff --git a/src/partest/scala/tools/partest/nest/RunnerManager.scala b/src/partest/scala/tools/partest/nest/RunnerManager.scala index 7a42853749..d4b9feecce 100644 --- a/src/partest/scala/tools/partest/nest/RunnerManager.scala +++ b/src/partest/scala/tools/partest/nest/RunnerManager.scala @@ -341,10 +341,34 @@ class RunnerManager(kind: String, val fileManager: FileManager, params: TestRunP val (scalaFiles, javaFiles) = g partition isScala val allFiles = javaFiles ++ scalaFiles + /* The test can contain both java and scala files, each of which should be compiled with the corresponding + * compiler. Since the source files can reference each other both ways (java referencing scala classes and + * vice versa, the partest compilation routine attempts to reach a "bytecode fixpoint" between the two + * compilers -- that's when bytecode generated by each compiler implements the signatures expected by the other. + * + * In theory this property can't be guaranteed, as neither compiler can know what signatures the other + * compiler expects and how to implement them. (see SI-1240 for the full story) + * + * In practice, this happens in 3 steps: + * STEP1: feed all the files to scalac + * it will parse java files and obtain their expected signatures and generate bytecode for scala files + * STEP2: feed the java files to javac + * it will generate the bytecode for the java files and link to the scalac-generated bytecode for scala + * STEP3: only if there are both scala and java files, recompile the scala sources so they link to the correct + * java signatures, in case the signatures deduced by scalac from the source files were wrong. Since the + * bytecode for java is already in place, we only feed the scala files to scalac so it will take the + * java signatures from the existing javac-generated bytecode + */ List(1, 2, 3).foldLeft(CompileSuccess: CompilationOutcome) { - case (CompileSuccess, 1) if scalaFiles.nonEmpty => compileMgr.attemptCompile(Some(outDir), allFiles, kind, logFile) // java + scala - case (CompileSuccess, 2) if javaFiles.nonEmpty => javac(outDir, javaFiles, logFile) // java - case (CompileSuccess, 3) if scalaFiles.nonEmpty => compileMgr.attemptCompile(Some(outDir), scalaFiles, kind, logFile) // scala + case (CompileSuccess, 1) if scalaFiles.nonEmpty => + compileMgr.attemptCompile(Some(outDir), allFiles, kind, logFile) + case (CompileSuccess, 2) if javaFiles.nonEmpty => + javac(outDir, javaFiles, logFile) + case (CompileSuccess, 3) if scalaFiles.nonEmpty && javaFiles.nonEmpty => + // TODO: Do we actually need this? SI-1240 is known to require this, but we don't know if other tests + // require it: https://groups.google.com/forum/?fromgroups#!topic/scala-internals/rFDKAcOKciU + compileMgr.attemptCompile(Some(outDir), scalaFiles, kind, logFile) + case (outcome, _) => outcome } } diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index df1c296d47..ccceeacb0a 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -45,7 +45,7 @@ package object partest { def path2String(path: String) = file2String(new JFile(path)) def file2String(f: JFile) = - try SFile(f).slurp() + try SFile(f).slurp(scala.io.Codec.UTF8) catch { case _: FileNotFoundException => "" } def basename(name: String): String = Path(name).stripExtension @@ -74,7 +74,6 @@ package object partest { def isPartestDebug: Boolean = propOrEmpty("partest.debug") == "true" - import scala.language.experimental.macros /** diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index 44f5776a57..9ff3efc799 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -2522,7 +2522,7 @@ trait Trees { self: Universe => */ def newLazyTreeCopier: TreeCopier - /** The API of a tree copier. + /** The API of a tree copier * @group API */ abstract class TreeCopierOps { diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index 539984c67f..86ea2c099b 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -158,7 +158,7 @@ trait BaseTypeSeqs { val parents = tp.parents // Console.println("computing baseTypeSeq of " + tsym.tpe + " " + parents)//DEBUG val buf = new mutable.ListBuffer[Type] - buf += tsym.tpe + buf += tsym.tpe_* var btsSize = 1 if (parents.nonEmpty) { val nparents = parents.length diff --git a/src/reflect/scala/reflect/internal/ClassfileConstants.scala b/src/reflect/scala/reflect/internal/ClassfileConstants.scala index 62ed130232..54570e478f 100644 --- a/src/reflect/scala/reflect/internal/ClassfileConstants.scala +++ b/src/reflect/scala/reflect/internal/ClassfileConstants.scala @@ -342,7 +342,7 @@ object ClassfileConstants { case JAVA_ACC_PRIVATE => PRIVATE case JAVA_ACC_PROTECTED => PROTECTED case JAVA_ACC_FINAL => FINAL - case JAVA_ACC_SYNTHETIC => SYNTHETIC + case JAVA_ACC_SYNTHETIC => SYNTHETIC | ARTIFACT // maybe should be just artifact? case JAVA_ACC_STATIC => STATIC case JAVA_ACC_ABSTRACT => if (isAnnotation) 0L else if (isClass) ABSTRACT else DEFERRED case JAVA_ACC_INTERFACE => if (isAnnotation) 0L else TRAIT | INTERFACE | ABSTRACT @@ -372,7 +372,7 @@ object ClassfileConstants { } def methodFlags(jflags: Int): Long = { initFields(jflags) - translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE else 0) + translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE | ARTIFACT else 0) } } object FlagTranslation extends FlagTranslation { } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 2bc9f02758..16ec95dafa 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -223,7 +223,7 @@ trait Definitions extends api.StandardDefinitions { def fullyInitializeSymbol(sym: Symbol): Symbol = { sym.initialize fullyInitializeType(sym.info) - fullyInitializeType(sym.tpe) + fullyInitializeType(sym.tpe_*) sym } def fullyInitializeType(tp: Type): Type = { @@ -312,6 +312,9 @@ trait Definitions extends api.StandardDefinitions { lazy val ThrowableClass = getClassByName(sn.Throwable) lazy val UninitializedErrorClass = requiredClass[UninitializedFieldError] + lazy val NPEConstructor = getMemberMethod(NullPointerExceptionClass, nme.CONSTRUCTOR) suchThat (_.paramss.flatten.isEmpty) + lazy val UninitializedFieldConstructor = UninitializedErrorClass.primaryConstructor + // fundamental reference classes lazy val PartialFunctionClass = requiredClass[PartialFunction[_,_]] lazy val AbstractPartialFunctionClass = requiredClass[scala.runtime.AbstractPartialFunction[_,_]] @@ -407,7 +410,8 @@ trait Definitions extends api.StandardDefinitions { def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass def isRepeatedParamType(tp: Type) = isScalaRepeatedParamType(tp) || isJavaRepeatedParamType(tp) - def isRepeated(param: Symbol) = isRepeatedParamType(param.tpe) + def isRepeated(param: Symbol) = isRepeatedParamType(param.tpe_*) + def isByName(param: Symbol) = isByNameParamType(param.tpe_*) def isCastSymbol(sym: Symbol) = sym == Any_asInstanceOf || sym == Object_asInstanceOf def isJavaVarArgsMethod(m: Symbol) = m.isMethod && isJavaVarArgs(m.info.params) @@ -546,7 +550,7 @@ trait Definitions extends api.StandardDefinitions { // The given symbol represents either String.+ or StringAdd.+ def isStringAddition(sym: Symbol) = sym == String_+ || sym == StringAdd_+ - def isArrowAssoc(sym: Symbol) = ArrowAssocClass.tpe.decls.toList contains sym + def isArrowAssoc(sym: Symbol) = sym.owner == ArrowAssocClass // The given symbol is a method with the right name and signature to be a runnable java program. def isJavaMainMethod(sym: Symbol) = (sym.name == nme.main) && (sym.info match { @@ -730,7 +734,7 @@ trait Definitions extends api.StandardDefinitions { * C[E1, ..., En] forSome { E1 >: LB1 <: UB1 ... en >: LBn <: UBn }. */ def classExistentialType(clazz: Symbol): Type = - newExistentialType(clazz.typeParams, clazz.tpe) + newExistentialType(clazz.typeParams, clazz.tpe_*) /** Given type U, creates a Type representing Class[_ <: U]. */ @@ -883,8 +887,8 @@ trait Definitions extends api.StandardDefinitions { lazy val Object_!= = enterNewMethod(ObjectClass, nme.NE, anyrefparam, booltype, FINAL) lazy val Object_eq = enterNewMethod(ObjectClass, nme.eq, anyrefparam, booltype, FINAL) lazy val Object_ne = enterNewMethod(ObjectClass, nme.ne, anyrefparam, booltype, FINAL) - lazy val Object_isInstanceOf = newT1NoParamsMethod(ObjectClass, nme.isInstanceOf_Ob, FINAL | SYNTHETIC)(_ => booltype) - lazy val Object_asInstanceOf = newT1NoParamsMethod(ObjectClass, nme.asInstanceOf_Ob, FINAL | SYNTHETIC)(_.typeConstructor) + lazy val Object_isInstanceOf = newT1NoParamsMethod(ObjectClass, nme.isInstanceOf_Ob, FINAL | SYNTHETIC | ARTIFACT)(_ => booltype) + lazy val Object_asInstanceOf = newT1NoParamsMethod(ObjectClass, nme.asInstanceOf_Ob, FINAL | SYNTHETIC | ARTIFACT)(_.typeConstructor) lazy val Object_synchronized = newPolyMethod(1, ObjectClass, nme.synchronized_, FINAL)(tps => (Some(List(tps.head.typeConstructor)), tps.head.typeConstructor) ) diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index bb454b1df7..ce5d9daf3b 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -116,6 +116,20 @@ class ModifierFlags { final val LAZY = 1L << 31 // symbol is a lazy val. can't have MUTABLE unless transformed by typer final val PRESUPER = 1L << 37 // value is evaluated before super call final val DEFAULTINIT = 1L << 41 // symbol is initialized to the default value: used by -Xcheckinit + final val ARTIFACT = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode + + /** Symbols which are marked ARTIFACT. (Expand this list?) + * + * - $outer fields and accessors + * - super accessors + * - protected accessors + * - lazy local accessors + * - bridge methods + * - default argument getters + * - evaluation-order preserving locals for right-associative and out-of-order named arguments + * - catch-expression storing vals + * - anything else which feels a setFlag(ARTIFACT) + */ // Overridden. def flagToString(flag: Long): String = "" @@ -165,7 +179,6 @@ class Flags extends ModifierFlags { // A Java method's type is ``cooked'' by transforming raw types to existentials final val SYNCHRONIZED = 1L << 45 // symbol is a method which should be marked ACC_SYNCHRONIZED - final val ARTIFACT = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode // ------- shift definitions ------------------------------------------------------- @@ -248,7 +261,7 @@ class Flags extends ModifierFlags { /** These modifiers appear in TreePrinter output. */ final val PrintableFlags = ExplicitFlags | BridgeFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR | MACRO | - ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED + ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED | ARTIFACT /** When a symbol for a field is created, only these flags survive * from Modifiers. Others which may be applied at creation time are: @@ -420,7 +433,7 @@ class Flags extends ModifierFlags { case VARARGS => "<varargs>" // (1L << 43) case TRIEDCOOKING => "<triedcooking>" // (1L << 44) case SYNCHRONIZED => "<synchronized>" // (1L << 45) - case 0x400000000000L => "" // (1L << 46) + case ARTIFACT => "<artifact>" // (1L << 46) case 0x800000000000L => "" // (1L << 47) case 0x1000000000000L => "" // (1L << 48) case 0x2000000000000L => "" // (1L << 49) diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala index 43902c1930..ea8d6078ff 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -427,17 +427,17 @@ trait Importers extends api.Importers { self: SymbolTable => } addFixup({ if (mytree != null) { - val mysym = if (tree.hasSymbol) importSymbol(tree.symbol) else NoSymbol + val mysym = if (tree.hasSymbolField) importSymbol(tree.symbol) else NoSymbol val mytpe = importType(tree.tpe) mytree match { case mytt: TypeTree => val tt = tree.asInstanceOf[from.TypeTree] - if (mytree.hasSymbol) mytt.symbol = mysym + if (mytree.hasSymbolField) mytt.symbol = mysym if (tt.wasEmpty) mytt.defineType(mytpe) else mytt.setType(mytpe) if (tt.original != null) mytt.setOriginal(importTree(tt.original)) case _ => - if (mytree.hasSymbol) mytree.symbol = importSymbol(tree.symbol) + if (mytree.hasSymbolField) mytree.symbol = importSymbol(tree.symbol) mytree.tpe = importType(tree.tpe) } } diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index fd5a7cf88b..eaa05bc89d 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -545,8 +545,8 @@ trait Printers extends api.Printers { self: SymbolTable => case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef => print("emptyValDef") case tree: Tree => - val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol - val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString + val hasSymbolField = tree.hasSymbolField && tree.symbol != NoSymbol + val isError = hasSymbolField && tree.symbol.name.toString == nme.ERROR.toString printProduct( tree, preamble = _ => { @@ -559,7 +559,7 @@ trait Printers extends api.Printers { self: SymbolTable => if (isError) print("<") print(name) if (isError) print(": error>") - } else if (hasSymbol) { + } else if (hasSymbolField) { tree match { case _: Ident | _: Select | _: SelectFromTypeTree => print(tree.symbol) case _ => print(tree.symbol.name) diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 2424e75949..38e33c001c 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -48,6 +48,7 @@ abstract class SymbolTable extends macros.Universe def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) def shouldLogAtThisPhase = false + def isPastTyper = false @deprecated("Give us a reason", "2.10.0") def abort(): Nothing = abort("unknown error") @@ -65,7 +66,7 @@ abstract class SymbolTable extends macros.Universe private[scala] def printCaller[T](msg: String)(result: T) = { Console.err.println("%s: %s\nCalled from: %s".format(msg, result, - (new Throwable).getStackTrace.drop(2).take(15).mkString("\n"))) + (new Throwable).getStackTrace.drop(2).take(50).mkString("\n"))) result } @@ -133,7 +134,7 @@ abstract class SymbolTable extends macros.Universe type RunId = Int final val NoRunId = 0 - // sigh, this has to be public or atPhase doesn't inline. + // sigh, this has to be public or enteringPhase doesn't inline. var phStack: List[Phase] = Nil private[this] var ph: Phase = NoPhase private[this] var per = NoPeriod @@ -196,23 +197,17 @@ abstract class SymbolTable extends macros.Universe p != NoPhase && phase.id > p.id /** Perform given operation at given phase. */ - @inline final def atPhase[T](ph: Phase)(op: => T): T = { + @inline final def enteringPhase[T](ph: Phase)(op: => T): T = { val saved = pushPhase(ph) try op finally popPhase(saved) } + @inline final def exitingPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph.next)(op) + @inline final def enteringPrevPhase[T](op: => T): T = enteringPhase(phase.prev)(op) - /** Since when it is to be "at" a phase is inherently ambiguous, - * a couple unambiguously named methods. - */ - @inline final def beforePhase[T](ph: Phase)(op: => T): T = atPhase(ph)(op) - @inline final def afterPhase[T](ph: Phase)(op: => T): T = atPhase(ph.next)(op) - @inline final def afterCurrentPhase[T](op: => T): T = atPhase(phase.next)(op) - @inline final def beforePrevPhase[T](op: => T): T = atPhase(phase.prev)(op) - - @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = - if (isAtPhaseAfter(target)) atPhase(target)(op) else op + @inline final def enteringPhaseNotLaterThan[T](target: Phase)(op: => T): T = + if (isAtPhaseAfter(target)) enteringPhase(target)(op) else op final def isValid(period: Period): Boolean = period != 0 && runId(period) == currentRunId && { @@ -339,6 +334,11 @@ abstract class SymbolTable extends macros.Universe /** Is this symbol table a part of a compiler universe? */ def isCompilerUniverse = false + + @deprecated("Use enteringPhase", "2.10.0") + @inline final def atPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph)(op) + @deprecated("Use enteringPhaseNotLaterThan", "2.10.0") + @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = enteringPhaseNotLaterThan(target)(op) } object SymbolTableStats { diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index a6f156f947..ee97b3fe2a 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -8,7 +8,7 @@ package internal import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer -import util.Statistics +import util.{ Statistics, shortClassOfInstance } import Flags._ import scala.annotation.tailrec import scala.reflect.io.AbstractFile @@ -182,7 +182,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (isGADTSkolem) " (this is a GADT skolem)" else "" - def shortSymbolClass = getClass.getName.split('.').last.stripPrefix("Symbols$") + def shortSymbolClass = shortClassOfInstance(this) def symbolCreationString: String = ( "%s%25s | %-40s | %s".format( if (settings.uniqid.value) "%06d | ".format(id) else "", @@ -642,7 +642,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isStaticModule = isModule && isStatic && !isMethod final def isThisSym = isTerm && owner.thisSym == this final def isError = hasFlag(IS_ERROR) - final def isErroneous = isError || isInitialized && tpe.isErroneous + final def isErroneous = isError || isInitialized && tpe_*.isErroneous def isHigherOrderTypeParameter = owner.isTypeParameterOrSkolem @@ -808,6 +808,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isTopLevelModule = hasFlag(MODULE) && owner.isPackageClass + /** A helper function for isEffectivelyFinal. */ + private def isNotOverridden = ( + owner.isClass && ( + owner.isEffectivelyFinal + || owner.isSealed && owner.children.forall(c => c.isEffectivelyFinal && (overridingSymbol(c) == NoSymbol)) + ) + ) + /** Is this symbol effectively final? I.e, it cannot be overridden */ final def isEffectivelyFinal: Boolean = ( (this hasFlag FINAL | PACKAGE) @@ -815,8 +823,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => || isTerm && ( isPrivate || isLocal - || owner.isClass && owner.isEffectivelyFinal - ) + || isNotOverridden + ) ) /** Is this symbol locally defined? I.e. not accessed from outside `this` instance */ @@ -1178,16 +1186,57 @@ trait Symbols extends api.Symbols { self: SymbolTable => } } - /** Get type. The type of a symbol is: - * for a type symbol, the type corresponding to the symbol itself, - * @M you should use tpeHK for a type symbol with type parameters if - * the kind of the type need not be *, as tpe introduces dummy arguments - * to generate a type of kind * - * for a term symbol, its usual type. - * See the tpe/tpeHK overrides in TypeSymbol for more. + /** The "type" of this symbol. The type of a term symbol is its usual + * type. A TypeSymbol is more complicated; see that class for elaboration. + * Since tpe forwards to tpe_*, if you call it on a type symbol with unapplied + * type parameters, the type returned will contain dummies types. These will + * hide legitimate errors or create spurious ones if used as normal types. */ - def tpe: Type = info - def tpeHK: Type = tpe + final def tpe: Type = tpe_* + + /** typeConstructor throws an exception when called on term + * symbols; this is a more forgiving alternative. Calls + * typeConstructor on TypeSymbols, returns info otherwise. + */ + def tpeHK: Type = info + + /** Only applicable to TypeSymbols, it is the type corresponding + * to the symbol itself. For instance, the type of a List might + * be List[Int] - the same symbol's typeConstructor is simply List. + * One might be tempted to write that as List[_], and in some + * contexts this is possible, but it is discouraged because it is + * syntactically indistinguishable from and easily confused with the + * type List[T] forSome { type T; }, which can also be written List[_]. + */ + def typeConstructor: Type = ( + // Avoiding a third override in NoSymbol to preserve bimorphism + if (this eq NoSymbol) + abort("no-symbol does not have a type constructor (this may indicate scalac cannot find fundamental classes)") + else + abort("typeConstructor inapplicable for " + this) + ) + + /** The type of this symbol, guaranteed to be of kind *. + * If there are unapplied type parameters, they will be + * substituted with dummy type arguments derived from the + * type parameters. Such types are not valid in a general + * sense and will cause difficult-to-find bugs if allowed + * to roam free. + * + * If you call tpe_* explicitly to obtain these types, + * you are responsible for them as if it they were your own + * minor children. + */ + def tpe_* : Type = info + + // Alternate implementation of def tpe for warning about misuse, + // disabled to keep the method maximally hotspot-friendly: + // def tpe: Type = { + // val result = tpe_* + // if (settings.debug.value && result.typeArgs.nonEmpty) + // printCaller(s"""Call to ${this.tpe} created $result: call tpe_* or tpeHK""")("") + // result + // } /** Get type info associated with symbol at current phase, after * ensuring that symbol is initialized (i.e. type is completed). @@ -1240,9 +1289,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** Set initial info. */ - def setInfo(info: Type): this.type = { info_=(info); this } + def setInfo(info: Type): this.type = { info_=(info); this } /** Modifies this symbol's info in place. */ - def modifyInfo(f: Type => Type): this.type = setInfo(f(info)) + def modifyInfo(f: Type => Type): this.type = setInfo(f(info)) /** Substitute second list of symbols for first in current info. */ def substInfo(syms0: List[Symbol], syms1: List[Symbol]): this.type = if (syms0.isEmpty) this @@ -1349,6 +1398,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!isInitialized) info this } + def maybeInitialize = { + try { initialize ; true } + catch { case _: CyclicReference => debuglog("Hit cycle in maybeInitialize of $this") ; false } + } /** Called when the programmer requests information that might require initialization of the underlying symbol. * @@ -1411,21 +1464,18 @@ trait Symbols extends api.Symbols { self: SymbolTable => * This is done in checkAccessible and overriding checks in refchecks * We can't do this on class loading because it would result in infinite cycles. */ - final def cookJavaRawInfo() { - if (hasFlag(TRIEDCOOKING)) return else setFlag(TRIEDCOOKING) // only try once... - val oldInfo = info - doCookJavaRawInfo() - } - - protected def doCookJavaRawInfo(): Unit + def cookJavaRawInfo(): Unit = { + // only try once... + if (this hasFlag TRIEDCOOKING) + return - /** The type constructor of a symbol is: - * For a type symbol, the type corresponding to the symbol itself, - * excluding parameters. - * Not applicable for term symbols. - */ - def typeConstructor: Type = - abort("typeConstructor inapplicable for " + this) + this setFlag TRIEDCOOKING + info // force the current info + if (isJavaDefined || isType && owner.isJavaDefined) + this modifyInfo rawToExistential + else if (isOverloaded) + alternatives withFilter (_.isJavaDefined) foreach (_ modifyInfo rawToExistential) + } /** The logic approximately boils down to finding the most recent phase * which immediately follows any of parser, namer, typer, or erasure. @@ -1449,7 +1499,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def unsafeTypeParams: List[Symbol] = if (isMonomorphicType) Nil - else atPhase(unsafeTypeParamPhase)(rawInfo.typeParams) + else enteringPhase(unsafeTypeParamPhase)(rawInfo.typeParams) /** The type parameters of this symbol. * assumption: if a type starts out as monomorphic, it will not acquire @@ -1461,9 +1511,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => // analogously to the "info" getter, here we allow for two completions: // one: sourceCompleter to LazyType, two: LazyType to completed type if (validTo == NoPeriod) - atPhase(phaseOf(infos.validFrom))(rawInfo load this) + enteringPhase(phaseOf(infos.validFrom))(rawInfo load this) if (validTo == NoPeriod) - atPhase(phaseOf(infos.validFrom))(rawInfo load this) + enteringPhase(phaseOf(infos.validFrom))(rawInfo load this) rawInfo.typeParams } @@ -1709,7 +1759,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def thisSym: Symbol = this /** The type of `this` in a class, or else the type of the symbol itself. */ - def typeOfThis = thisSym.tpe + def typeOfThis = thisSym.tpe_* /** If symbol is a class, the type <code>this.type</code> in this class, * otherwise <code>NoPrefix</code>. @@ -1885,7 +1935,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => || (that.sourceFile eq null) || (this.sourceFile.path == that.sourceFile.path) // Cheap possibly wrong check, then expensive normalization || (this.sourceFile.canonicalPath == that.sourceFile.canonicalPath) - ) + ) ) /** The internal representation of classes and objects: @@ -2501,36 +2551,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => name = nme.expandedName(name.toTermName, base) } } - - protected def doCookJavaRawInfo() { - def cook(sym: Symbol) { - require(sym.isJavaDefined, sym) - // @M: I think this is more desirable, but Martin prefers to leave raw-types as-is as much as possible - // object rawToExistentialInJava extends TypeMap { - // def apply(tp: Type): Type = tp match { - // // any symbol that occurs in a java sig, not just java symbols - // // see http://lampsvn.epfl.ch/trac/scala/ticket/2454#comment:14 - // case TypeRef(pre, sym, List()) if !sym.typeParams.isEmpty => - // val eparams = typeParamsToExistentials(sym, sym.typeParams) - // existentialAbstraction(eparams, TypeRef(pre, sym, eparams map (_.tpe))) - // case _ => - // mapOver(tp) - // } - // } - val tpe1 = rawToExistential(sym.tpe) - // println("cooking: "+ sym +": "+ sym.tpe +" to "+ tpe1) - if (tpe1 ne sym.tpe) { - sym.setInfo(tpe1) - } - } - - if (isJavaDefined) - cook(this) - else if (isOverloaded) - for (sym2 <- alternatives) - if (sym2.isJavaDefined) - cook(sym2) - } } implicit val TermSymbolTag = ClassTag[TermSymbol](classOf[TermSymbol]) @@ -2622,6 +2642,20 @@ trait Symbols extends api.Symbols { self: SymbolTable => owner.newNonClassSymbol(name, pos, newFlags) } + /** Let's say you have a type definition + * + * {{{ + * type T <: Number + * }}} + * + * and tsym is the symbol corresponding to T. Then + * + * {{{ + * tsym is an instance of AbstractTypeSymbol + * tsym.info = TypeBounds(Nothing, Number) + * tsym.tpe = TypeRef(NoPrefix, T, List()) + * }}} + */ class AbstractTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) { type TypeOfClonedSymbol = TypeSymbol @@ -2690,63 +2724,57 @@ trait Symbols extends api.Symbols { self: SymbolTable => private def newPrefix = if (this hasFlag EXISTENTIAL | PARAM) NoPrefix else owner.thisType private def newTypeRef(targs: List[Type]) = typeRef(newPrefix, this, targs) - /** Let's say you have a type definition - * - * {{{ - * type T <: Number - * }}} + /** A polymorphic type symbol has two distinct "types": * - * and tsym is the symbol corresponding to T. Then + * tpe_* a TypeRef with: dummy type args, no unapplied type parameters, and kind * + * tpeHK a TypeRef with: no type args, unapplied type parameters, and + * kind (*,*,...,*) => * depending on the number of tparams. * - * {{{ - * tsym.info = TypeBounds(Nothing, Number) - * tsym.tpe = TypeRef(NoPrefix, T, List()) - * }}} - */ - override def tpe: Type = { - if (tpeCache eq NoType) throw CyclicReference(this, typeConstructor) - if (tpePeriod != currentPeriod) { - if (isValid(tpePeriod)) { - tpePeriod = currentPeriod - } else { - if (isInitialized) tpePeriod = currentPeriod - tpeCache = NoType - val targs = - if (phase.erasedTypes && this != ArrayClass) List() - else unsafeTypeParams map (_.typeConstructor) - //@M! use typeConstructor to generate dummy type arguments, - // sym.tpe should not be called on a symbol that's supposed to be a higher-kinded type - // memberType should be used instead, that's why it uses tpeHK and not tpe - tpeCache = newTypeRef(targs) - } - } - assert(tpeCache ne null/*, "" + this + " " + phase*/)//debug + * The dummy type args in tpe_* are created by wrapping a TypeRef + * around the type parameter symbols. Types containing dummies will + * hide errors or introduce spurious ones if they are passed around + * as if normal types. They should only be used in local operations + * where they will either be discarded immediately after, or will + * undergo substitution in which the dummies are replaced by actual + * type arguments. + */ + override def tpe_* : Type = { + maybeUpdateTypeCache() tpeCache } - - /** @M -- tpe vs tpeHK: - * - * tpe: creates a TypeRef with dummy type arguments and kind * - * tpeHK: creates a TypeRef with no type arguments but with type parameters - * - * If typeParams is nonEmpty, calling tpe may hide errors or - * introduce spurious ones. (For example, when deriving a type from - * the symbol of a type argument that may be higher-kinded.) As far - * as I can tell, it only makes sense to call tpe in conjunction - * with a substitution that replaces the generated dummy type - * arguments by their actual types. - * - * TODO: the above conditions desperately need to be enforced by code. - */ - override def tpeHK = typeConstructor // @M! used in memberType - override def typeConstructor: Type = { + maybeUpdateTyconCache() + tyconCache + } + override def tpeHK: Type = typeConstructor + + private def maybeUpdateTyconCache() { if ((tyconCache eq null) || tyconRunId != currentRunId) { tyconCache = newTypeRef(Nil) tyconRunId = currentRunId } assert(tyconCache ne null) - tyconCache + } + private def maybeUpdateTypeCache() { + if (tpePeriod != currentPeriod) { + if (isValid(tpePeriod)) + tpePeriod = currentPeriod + else + updateTypeCache() // perform the actual update + } + } + private def updateTypeCache() { + if (tpeCache eq NoType) + throw CyclicReference(this, typeConstructor) + + if (isInitialized) + tpePeriod = currentPeriod + + tpeCache = NoType // cycle marker + tpeCache = newTypeRef( + if (phase.erasedTypes && this != ArrayClass || unsafeTypeParams.isEmpty) Nil + else unsafeTypeParams map (_.typeConstructor) + ) } override def info_=(tp: Type) { @@ -2772,15 +2800,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => * public class Test1<T extends Test3> {} * info for T in Test1 should be >: Nothing <: Test3[_] */ - protected def doCookJavaRawInfo() { - if (isJavaDefined || owner.isJavaDefined) { - val tpe1 = rawToExistential(info) - // println("cooking type: "+ this +": "+ info +" to "+ tpe1) - if (tpe1 ne info) { - setInfo(tpe1) - } - } - } if (Statistics.hotEnabled) Statistics.incCounter(typeSymbolCount) } @@ -3184,15 +3203,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def info: Type = NoType override def existentialBound: Type = NoType override def rawInfo: Type = NoType - protected def doCookJavaRawInfo() {} override def accessBoundary(base: Symbol): Symbol = enclosingRootClass def cloneSymbolImpl(owner: Symbol, newFlags: Long) = abort("NoSymbol.clone()") override def originalEnclosingMethod = this override def owner: Symbol = abort("no-symbol does not have an owner") - override def typeConstructor: Type = - abort("no-symbol does not have a type constructor (this may indicate scalac cannot find fundamental classes)") } protected def makeNoSymbol: NoSymbol = new NoSymbol diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index ebf0998573..6ce93d93b2 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -165,17 +165,36 @@ abstract class TreeGen extends macros.TreeBuilder { This(sym.name.toTypeName) setSymbol sym setType sym.thisType def mkAttributedIdent(sym: Symbol): Tree = - Ident(sym.name) setSymbol sym setType sym.tpe + Ident(sym.name) setSymbol sym setType sym.tpeHK def mkAttributedSelect(qual: Tree, sym: Symbol): Tree = { // Tests involving the repl fail without the .isEmptyPackage condition. if (qual.symbol != null && (qual.symbol.isEffectiveRoot || qual.symbol.isEmptyPackage)) mkAttributedIdent(sym) else { + // Have to recognize anytime a selection is made on a package + // so it can be rewritten to foo.bar.`package`.name rather than + // foo.bar.name if name is in the package object. + // TODO - factor out the common logic between this and + // the Typers method "isInPackageObject", used in typedIdent. + val qualsym = ( + if (qual.tpe ne null) qual.tpe.typeSymbol + else if (qual.symbol ne null) qual.symbol + else NoSymbol + ) + val needsPackageQualifier = ( + (sym ne null) + && qualsym.isPackage + && !sym.isDefinedInPackage + ) val pkgQualifier = - if (sym != null && sym.owner.isPackageObjectClass && sym.effectiveOwner == qual.tpe.typeSymbol) { - val obj = sym.owner.sourceModule - Select(qual, nme.PACKAGE) setSymbol obj setType singleType(qual.tpe, obj) + if (needsPackageQualifier) { + // The owner of a symbol which requires package qualification may be the + // package object iself, but it also could be any superclass of the package + // object. In the latter case, we must go through the qualifier's info + // to obtain the right symbol. + val packageObject = if (sym.owner.isModuleClass) sym.owner.sourceModule else qual.tpe member nme.PACKAGE + Select(qual, nme.PACKAGE) setSymbol packageObject setType singleType(qual.tpe, packageObject) } else qual diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 68decc27f5..66326c90e9 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -104,6 +104,34 @@ abstract class TreeInfo { false } + /** As if the name of the method didn't give it away, + * this logic is designed around issuing helpful + * warnings and minimizing spurious ones. That means + * don't reuse it for important matters like inlining + * decisions. + */ + def isPureExprForWarningPurposes(tree: Tree) = tree match { + case EmptyTree | Literal(Constant(())) => false + case _ => + def isWarnableRefTree = tree match { + case t: RefTree => isExprSafeToInline(t.qualifier) && t.symbol != null && t.symbol.isAccessor + case _ => false + } + def isWarnableSymbol = { + val sym = tree.symbol + (sym == null) || !(sym.isModule || sym.isLazy) || { + debuglog("'Pure' but side-effecting expression in statement position: " + tree) + false + } + } + + ( !tree.isErrorTyped + && (isExprSafeToInline(tree) || isWarnableRefTree) + && isWarnableSymbol + ) + } + + @deprecated("Use isExprSafeToInline instead", "2.10.0") def isPureExpr(tree: Tree) = isExprSafeToInline(tree) @@ -397,15 +425,31 @@ abstract class TreeInfo { case _ => false } - /** Does this CaseDef catch Throwable? */ - def catchesThrowable(cdef: CaseDef) = catchesAllOf(cdef, ThrowableClass.tpe) + private def hasNoSymbol(t: Tree) = t.symbol == null || t.symbol == NoSymbol - /** Does this CaseDef catch everything of a certain Type? */ - def catchesAllOf(cdef: CaseDef, threshold: Type) = - isDefaultCase(cdef) || (cdef.guard.isEmpty && (unbind(cdef.pat) match { - case Typed(Ident(nme.WILDCARD), tpt) => (tpt.tpe != null) && (threshold <:< tpt.tpe) - case _ => false - })) + /** If this CaseDef assigns a name to its top-level pattern, + * in the form 'expr @ pattern' or 'expr: pattern', returns + * the name. Otherwise, nme.NO_NAME. + * + * Note: in the case of Constant patterns such as 'case x @ "" =>', + * the pattern matcher eliminates the binding and inlines the constant, + * so as far as this method is likely to be able to determine, + * the name is NO_NAME. + */ + def assignedNameOfPattern(cdef: CaseDef): Name = cdef.pat match { + case Bind(name, _) => name + case Ident(name) => name + case _ => nme.NO_NAME + } + + /** Does this CaseDef catch Throwable? */ + def catchesThrowable(cdef: CaseDef) = ( + cdef.guard.isEmpty && (unbind(cdef.pat) match { + case Ident(nme.WILDCARD) => true + case i@Ident(name) => hasNoSymbol(i) + case _ => false + }) + ) /** Is this pattern node a catch-all or type-test pattern? */ def isCatchCase(cdef: CaseDef) = cdef match { diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 7ec9f7086d..4bb88145b3 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -31,7 +31,8 @@ trait Trees extends api.Trees { self: SymbolTable => def symbol: Symbol = null //!!!OPT!!! symbol is about 3% of hot compile times -- megamorphic dispatch? def symbol_=(sym: Symbol) { throw new UnsupportedOperationException("symbol_= inapplicable for " + this) } def setSymbol(sym: Symbol): this.type = { symbol = sym; this } - def hasSymbol = false + def hasSymbolField = false + @deprecated("Use hasSymbolField", "2.11.0") def hasSymbol = hasSymbolField def isDef = false @@ -62,7 +63,7 @@ trait Trees extends api.Trees { self: SymbolTable => private[scala] def copyAttrs(tree: Tree): this.type = { rawatt = tree.rawatt tpe = tree.tpe - if (hasSymbol) symbol = tree.symbol + if (hasSymbolField) symbol = tree.symbol this } @@ -210,7 +211,7 @@ trait Trees extends api.Trees { self: SymbolTable => trait TypTree extends Tree with TypTreeApi abstract class SymTree extends Tree with SymTreeContextApi { - override def hasSymbol = true + override def hasSymbolField = true override var symbol: Symbol = NoSymbol } @@ -413,6 +414,16 @@ trait Trees extends api.Trees { self: SymbolTable => def ApplyConstructor(tpt: Tree, args: List[Tree]) = Apply(Select(New(tpt), nme.CONSTRUCTOR), args) + // Creates a constructor call from the constructor symbol. This is + // to avoid winding up with an OverloadedType for the constructor call. + def NewFromConstructor(constructor: Symbol, args: Tree*) = { + assert(constructor.isConstructor, constructor) + val instance = New(TypeTree(constructor.owner.tpe)) + val init = Select(instance, nme.CONSTRUCTOR) setSymbol constructor + + Apply(init, args.toList) + } + case class ApplyDynamic(qual: Tree, args: List[Tree]) extends SymTree with TermTree case class Super(qual: Tree, mix: TypeName) extends TermTree with SuperApi { @@ -1300,25 +1311,26 @@ trait Trees extends api.Trees { self: SymbolTable => } class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser { - def changeOwner(tree: Tree) = tree match { - case Return(expr) => - if (tree.symbol == oldowner) { - // SI-5612 - if (newowner hasTransOwner oldowner) - log("NOT changing owner of %s because %s is nested in %s".format(tree, newowner, oldowner)) - else { - log("changing owner of %s: %s => %s".format(tree, oldowner, newowner)) - tree.symbol = newowner - } - } - case _: DefTree | _: Function => - if (tree.symbol != NoSymbol && tree.symbol.owner == oldowner) { - tree.symbol.owner = newowner - } - case _ => + final def change(sym: Symbol) = { + if (sym != NoSymbol && sym.owner == oldowner) + sym.owner = newowner } override def traverse(tree: Tree) { - changeOwner(tree) + tree match { + case _: Return => + if (tree.symbol == oldowner) { + // SI-5612 + if (newowner hasTransOwner oldowner) + log("NOT changing owner of %s because %s is nested in %s".format(tree, newowner, oldowner)) + else { + log("changing owner of %s: %s => %s".format(tree, oldowner, newowner)) + tree.symbol = newowner + } + } + case _: DefTree | _: Function => + change(tree.symbol) + case _ => + } super.traverse(tree) } } @@ -1402,7 +1414,7 @@ trait Trees extends api.Trees { self: SymbolTable => } if (tree.tpe ne null) tree.tpe = symSubst(tree.tpe) - if (tree.hasSymbol) { + if (tree.hasSymbolField) { subst(from, to) tree match { case Ident(name0) if tree.symbol != NoSymbol => diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index ab33c29153..92db92d5f3 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -22,6 +22,8 @@ import util.ThreeValues._ // internal: error case WildcardType => // internal: unknown + case BoundedWildcardType(bounds) => + // internal: unknown case NoType => case NoPrefix => case ThisType(sym) => @@ -66,9 +68,7 @@ import util.ThreeValues._ // a type variable // Replace occurrences of type parameters with type vars, where // inst is the instantiation and constr is a list of bounds. - case DeBruijnIndex(level, index, args) - // for dependent method types: a type referring to a method parameter. - case ErasedValueType(tref) + case ErasedValueType(clazz, underlying) // only used during erasure of derived value classes. */ @@ -91,6 +91,7 @@ trait Types extends api.Types { self: SymbolTable => private final val printLubs = sys.props contains "scalac.debug.lub" private final val traceTypeVars = sys.props contains "scalac.debug.tvar" + private final val breakCycles = settings.breakCycles.value /** In case anyone wants to turn off lub verification without reverting anything. */ private final val verifyLubs = true /** In case anyone wants to turn off type parameter bounds being used @@ -735,6 +736,7 @@ trait Types extends api.Types { self: SymbolTable => * }}} */ def memberInfo(sym: Symbol): Type = { + require(sym ne NoSymbol, this) sym.info.asSeenFrom(this, sym.owner) } @@ -1037,69 +1039,66 @@ trait Types extends api.Types { self: SymbolTable => } def findMembers(excludedFlags: Long, requiredFlags: Long): Scope = { - // if this type contains type variables, put them to sleep for a while -- don't just wipe them out by - // replacing them by the corresponding type parameter, as that messes up (e.g.) type variables in type refinements - // without this, the matchesType call would lead to type variables on both sides - // of a subtyping/equality judgement, which can lead to recursive types being constructed. - // See (t0851) for a situation where this happens. - val suspension: List[TypeVar] = if (this.isGround) null else suspendTypeVarsInType(this) - - if (Statistics.canEnable) Statistics.incCounter(findMembersCount) - val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMembersNanos) else null - - //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG - var members: Scope = null - var required = requiredFlags - var excluded = excludedFlags | DEFERRED - var continue = true - var self: Type = null - while (continue) { - continue = false - val bcs0 = baseClasses - var bcs = bcs0 - while (!bcs.isEmpty) { - val decls = bcs.head.info.decls - var entry = decls.elems - while (entry ne null) { - val sym = entry.sym - val flags = sym.flags - if ((flags & required) == required) { - val excl = flags & excluded - if (excl == 0L && - (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. - (bcs eq bcs0) || - (flags & PrivateLocal) != PrivateLocal || - (bcs0.head.hasTransOwner(bcs.head)))) { - if (members eq null) members = newFindMemberScope - var others: ScopeEntry = members.lookupEntry(sym.name) - var symtpe: Type = null - while ((others ne null) && { - val other = others.sym - (other ne sym) && - ((other.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = this.narrow - if (symtpe eq null) symtpe = self.memberType(sym) - !(self.memberType(other) matches symtpe) - })}) { - others = members lookupNextEntry others + def findMembersInternal: Scope = { + var members: Scope = null + if (Statistics.canEnable) Statistics.incCounter(findMembersCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMembersNanos) else null + + //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG + var required = requiredFlags + var excluded = excludedFlags | DEFERRED + var continue = true + var self: Type = null + while (continue) { + continue = false + val bcs0 = baseClasses + var bcs = bcs0 + while (!bcs.isEmpty) { + val decls = bcs.head.info.decls + var entry = decls.elems + while (entry ne null) { + val sym = entry.sym + val flags = sym.flags + if ((flags & required) == required) { + val excl = flags & excluded + if (excl == 0L && + (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. + (bcs eq bcs0) || + (flags & PrivateLocal) != PrivateLocal || + (bcs0.head.hasTransOwner(bcs.head)))) { + if (members eq null) members = newFindMemberScope + var others: ScopeEntry = members.lookupEntry(sym.name) + var symtpe: Type = null + while ((others ne null) && { + val other = others.sym + (other ne sym) && + ((other.owner eq sym.owner) || + (flags & PRIVATE) != 0 || { + if (self eq null) self = this.narrow + if (symtpe eq null) symtpe = self.memberType(sym) + !(self.memberType(other) matches symtpe) + })}) { + others = members lookupNextEntry others + } + if (others eq null) members enter sym + } else if (excl == DEFERRED) { + continue = true } - if (others eq null) members enter sym - } else if (excl == DEFERRED) { - continue = true } - } - entry = entry.next - } // while (entry ne null) - // excluded = excluded | LOCAL - bcs = bcs.tail - } // while (!bcs.isEmpty) - required |= DEFERRED - excluded &= ~(DEFERRED.toLong) - } // while (continue) - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - if (suspension ne null) suspension foreach (_.suspended = false) - if (members eq null) EmptyScope else members + entry = entry.next + } // while (entry ne null) + // excluded = excluded | LOCAL + bcs = bcs.tail + } // while (!bcs.isEmpty) + required |= DEFERRED + excluded &= ~(DEFERRED.toLong) + } // while (continue) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + if (members eq null) EmptyScope else members + } + + if (this.isGround) findMembersInternal + else suspendingTypeVars(typeVarsInType(this))(findMembersInternal) } /** @@ -1113,102 +1112,98 @@ trait Types extends api.Types { self: SymbolTable => */ //TODO: use narrow only for modules? (correct? efficiency gain?) def findMember(name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean): Symbol = { - // if this type contains type variables, put them to sleep for a while -- don't just wipe them out by - // replacing them by the corresponding type parameter, as that messes up (e.g.) type variables in type refinements - // without this, the matchesType call would lead to type variables on both sides - // of a subtyping/equality judgement, which can lead to recursive types being constructed. - // See (t0851) for a situation where this happens. - val suspension: List[TypeVar] = if (this.isGround) null else suspendTypeVarsInType(this) - - if (Statistics.canEnable) Statistics.incCounter(findMemberCount) - val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMemberNanos) else null - - //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG - var member: Symbol = NoSymbol - var members: List[Symbol] = null - var lastM: ::[Symbol] = null - var membertpe: Type = null - var required = requiredFlags - var excluded = excludedFlags | DEFERRED - var continue = true - var self: Type = null - - while (continue) { - continue = false - val bcs0 = baseClasses - var bcs = bcs0 - while (!bcs.isEmpty) { - val decls = bcs.head.info.decls - var entry = decls.lookupEntry(name) - while (entry ne null) { - val sym = entry.sym - val flags = sym.flags - if ((flags & required) == required) { - val excl = flags & excluded - if (excl == 0L && - (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. - (bcs eq bcs0) || - (flags & PrivateLocal) != PrivateLocal || - (bcs0.head.hasTransOwner(bcs.head)))) { - if (name.isTypeName || stableOnly && sym.isStable) { - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - if (suspension ne null) suspension foreach (_.suspended = false) - return sym - } else if (member eq NoSymbol) { - member = sym - } else if (members eq null) { - if ((member ne sym) && - ((member.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = this.narrow - if (membertpe eq null) membertpe = self.memberType(member) - !(membertpe matches self.memberType(sym)) - })) { - lastM = new ::(sym, null) - members = member :: lastM - } - } else { - var others: List[Symbol] = members - var symtpe: Type = null - while ((others ne null) && { - val other = others.head - (other ne sym) && - ((other.owner eq sym.owner) || + def findMemberInternal: Symbol = { + var member: Symbol = NoSymbol + var members: List[Symbol] = null + var lastM: ::[Symbol] = null + if (Statistics.canEnable) Statistics.incCounter(findMemberCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMemberNanos) else null + + //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG + var membertpe: Type = null + var required = requiredFlags + var excluded = excludedFlags | DEFERRED + var continue = true + var self: Type = null + + while (continue) { + continue = false + val bcs0 = baseClasses + var bcs = bcs0 + while (!bcs.isEmpty) { + val decls = bcs.head.info.decls + var entry = decls.lookupEntry(name) + while (entry ne null) { + val sym = entry.sym + val flags = sym.flags + if ((flags & required) == required) { + val excl = flags & excluded + if (excl == 0L && + (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. + (bcs eq bcs0) || + (flags & PrivateLocal) != PrivateLocal || + (bcs0.head.hasTransOwner(bcs.head)))) { + if (name.isTypeName || stableOnly && sym.isStable) { + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + return sym + } else if (member eq NoSymbol) { + member = sym + } else if (members eq null) { + if ((member ne sym) && + ((member.owner eq sym.owner) || (flags & PRIVATE) != 0 || { if (self eq null) self = this.narrow - if (symtpe eq null) symtpe = self.memberType(sym) - !(self.memberType(other) matches symtpe) - })}) { - others = others.tail - } - if (others eq null) { - val lastM1 = new ::(sym, null) - lastM.tl = lastM1 - lastM = lastM1 + if (membertpe eq null) membertpe = self.memberType(member) + !(membertpe matches self.memberType(sym)) + })) { + lastM = new ::(sym, null) + members = member :: lastM + } + } else { + var others: List[Symbol] = members + var symtpe: Type = null + while ((others ne null) && { + val other = others.head + (other ne sym) && + ((other.owner eq sym.owner) || + (flags & PRIVATE) != 0 || { + if (self eq null) self = this.narrow + if (symtpe eq null) symtpe = self.memberType(sym) + !(self.memberType(other) matches symtpe) + })}) { + others = others.tail + } + if (others eq null) { + val lastM1 = new ::(sym, null) + lastM.tl = lastM1 + lastM = lastM1 + } } + } else if (excl == DEFERRED) { + continue = true } - } else if (excl == DEFERRED) { - continue = true } - } - entry = decls lookupNextEntry entry - } // while (entry ne null) - // excluded = excluded | LOCAL - bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail - } // while (!bcs.isEmpty) - required |= DEFERRED - excluded &= ~(DEFERRED.toLong) - } // while (continue) - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - if (suspension ne null) suspension foreach (_.suspended = false) - if (members eq null) { - if (member == NoSymbol) if (Statistics.canEnable) Statistics.incCounter(noMemberCount) - member - } else { - if (Statistics.canEnable) Statistics.incCounter(multMemberCount) - lastM.tl = Nil - baseClasses.head.newOverloaded(this, members) + entry = decls lookupNextEntry entry + } // while (entry ne null) + // excluded = excluded | LOCAL + bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail + } // while (!bcs.isEmpty) + required |= DEFERRED + excluded &= ~(DEFERRED.toLong) + } // while (continue) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + if (members eq null) { + if (member == NoSymbol) if (Statistics.canEnable) Statistics.incCounter(noMemberCount) + member + } else { + if (Statistics.canEnable) Statistics.incCounter(multMemberCount) + lastM.tl = Nil + baseClasses.head.newOverloaded(this, members) + } } + + if (this.isGround) findMemberInternal + else suspendingTypeVars(typeVarsInType(this))(findMemberInternal) } /** The (existential or otherwise) skolems and existentially quantified variables which are free in this type */ @@ -1410,7 +1405,7 @@ trait Types extends api.Types { self: SymbolTable => object ThisType extends ThisTypeExtractor { def apply(sym: Symbol): Type = - if (phase.erasedTypes) sym.tpe + if (phase.erasedTypes) sym.tpe_* else unique(new UniqueThisType(sym)) } @@ -1621,6 +1616,38 @@ trait Types extends api.Types { self: SymbolTable => ) } + protected def computeBaseClasses(tpe: Type): List[Symbol] = { + val parents = tpe.parents // adriaan says tpe.parents does work sometimes, so call it only once + val baseTail = ( + if (parents.isEmpty || parents.head.isInstanceOf[PackageTypeRef]) Nil + else { + //Console.println("computing base classes of " + typeSymbol + " at phase " + phase);//DEBUG + // optimized, since this seems to be performance critical + val superclazz = parents.head // parents.isEmpty was already excluded + var mixins = parents.tail + val sbcs = superclazz.baseClasses + var bcs = sbcs + def isNew(clazz: Symbol): Boolean = ( + superclazz.baseTypeIndex(clazz) < 0 && + { var p = bcs; + while ((p ne sbcs) && (p.head != clazz)) p = p.tail; + p eq sbcs + } + ) + while (!mixins.isEmpty) { + def addMixinBaseClasses(mbcs: List[Symbol]): List[Symbol] = + if (mbcs.isEmpty) bcs + else if (isNew(mbcs.head)) mbcs.head :: addMixinBaseClasses(mbcs.tail) + else addMixinBaseClasses(mbcs.tail) + bcs = addMixinBaseClasses(mixins.head.baseClasses) + mixins = mixins.tail + } + bcs + } + ) + tpe.typeSymbol :: baseTail + } + protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = { val period = tpe.baseTypeSeqPeriod if (period != currentPeriod) { @@ -1640,7 +1667,7 @@ trait Types extends api.Types { self: SymbolTable => val paramToVarMap = varToParamMap map (_.swap) val varToParam = new TypeMap { def apply(tp: Type) = varToParamMap get tp match { - case Some(sym) => sym.tpe + case Some(sym) => sym.tpe_* case _ => mapOver(tp) } } @@ -1659,7 +1686,7 @@ trait Types extends api.Types { self: SymbolTable => tpe.baseTypeSeqCache = undetBaseTypeSeq tpe.baseTypeSeqCache = if (tpe.typeSymbol.isRefinementClass) - tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe) + tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe_*) else compoundBaseTypeSeq(tpe) } finally { @@ -1681,41 +1708,61 @@ trait Types extends api.Types { self: SymbolTable => throw new TypeError("illegal cyclic inheritance involving " + tpe.typeSymbol) } - protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = { - def computeBaseClasses: List[Symbol] = - if (tpe.parents.isEmpty) List(tpe.typeSymbol) - else { - //Console.println("computing base classes of " + typeSymbol + " at phase " + phase);//DEBUG - // optimized, since this seems to be performance critical - val superclazz = tpe.firstParent - var mixins = tpe.parents.tail - val sbcs = superclazz.baseClasses - var bcs = sbcs - def isNew(clazz: Symbol): Boolean = - superclazz.baseTypeIndex(clazz) < 0 && - { var p = bcs; - while ((p ne sbcs) && (p.head != clazz)) p = p.tail; - p eq sbcs - } - while (!mixins.isEmpty) { - def addMixinBaseClasses(mbcs: List[Symbol]): List[Symbol] = - if (mbcs.isEmpty) bcs - else if (isNew(mbcs.head)) mbcs.head :: addMixinBaseClasses(mbcs.tail) - else addMixinBaseClasses(mbcs.tail) - bcs = addMixinBaseClasses(mixins.head.baseClasses) - mixins = mixins.tail + object baseClassesCycleMonitor { + private var open: List[Symbol] = Nil + @inline private def cycleLog(msg: => String) { + if (settings.debug.value) + Console.err.println(msg) + } + def size = open.size + def push(clazz: Symbol) { + cycleLog("+ " + (" " * size) + clazz.fullNameString) + open ::= clazz + } + def pop(clazz: Symbol) { + assert(open.head eq clazz, (clazz, open)) + open = open.tail + } + def isOpen(clazz: Symbol) = open contains clazz + } + + protected def defineBaseClassesOfCompoundType(tpe: CompoundType) { + def define = defineBaseClassesOfCompoundType(tpe, force = false) + if (isPastTyper || !breakCycles) define + else tpe match { + // non-empty parents helpfully excludes all package classes + case tpe @ ClassInfoType(_ :: _, _, clazz) if !clazz.isAnonOrRefinementClass => + // Cycle: force update + if (baseClassesCycleMonitor isOpen clazz) + defineBaseClassesOfCompoundType(tpe, force = true) + else { + baseClassesCycleMonitor push clazz + try define + finally baseClassesCycleMonitor pop clazz } - tpe.typeSymbol :: bcs - } + case _ => + define + } + } + private def defineBaseClassesOfCompoundType(tpe: CompoundType, force: Boolean) { val period = tpe.baseClassesPeriod - if (period != currentPeriod) { + if (period == currentPeriod) { + if (force && breakCycles) { + def what = tpe.typeSymbol + " in " + tpe.typeSymbol.owner.fullNameString + val bcs = computeBaseClasses(tpe) + tpe.baseClassesCache = bcs + warning(s"Breaking cycle in base class computation of $what ($bcs)") + } + } + else { tpe.baseClassesPeriod = currentPeriod if (!isValidForBaseClasses(period)) { val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, baseClassesNanos) else null try { tpe.baseClassesCache = null - tpe.baseClassesCache = tpe.memo(computeBaseClasses)(tpe.typeSymbol :: _.baseClasses.tail) - } finally { + tpe.baseClassesCache = tpe.memo(computeBaseClasses(tpe))(tpe.typeSymbol :: _.baseClasses.tail) + } + finally { if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) } } @@ -2813,9 +2860,13 @@ trait Types extends api.Types { self: SymbolTable => override def kind = "OverloadedType" } - def overloadedType(pre: Type, alternatives: List[Symbol]): Type = - if (alternatives.tail.isEmpty) pre memberType alternatives.head - else OverloadedType(pre, alternatives) + /** The canonical creator for OverloadedTypes. + */ + def overloadedType(pre: Type, alternatives: List[Symbol]): Type = alternatives match { + case Nil => NoType + case alt :: Nil => pre memberType alt + case _ => OverloadedType(pre, alternatives) + } /** A class remembering a type instantiation for some a set of overloaded * polymorphic symbols. @@ -3072,7 +3123,10 @@ trait Types extends api.Types { self: SymbolTable => // invariant: before mutating constr, save old state in undoLog // (undoLog is used to reset constraints to avoid piling up unrelated ones) def setInst(tp: Type) { -// assert(!(tp containsTp this), this) + if (tp eq this) { + log(s"TypeVar cycle: called setInst passing $this to itself.") + return + } undoLog record this // if we were compared against later typeskolems, repack the existential, // because skolems are only compatible if they were created at the same level @@ -3217,16 +3271,19 @@ trait Types extends api.Types { self: SymbolTable => def registerTypeEquality(tp: Type, typeVarLHS: Boolean): Boolean = { // println("regTypeEq: "+(safeToString, debugString(tp), tp.getClass, if (typeVarLHS) "in LHS" else "in RHS", if (suspended) "ZZ" else if (constr.instValid) "IV" else "")) //@MDEBUG -// println("constr: "+ constr) - def checkIsSameType(tp: Type) = - if(typeVarLHS) constr.inst =:= tp - else tp =:= constr.inst + def checkIsSameType(tp: Type) = ( + if (typeVarLHS) constr.inst =:= tp + else tp =:= constr.inst + ) if (suspended) tp =:= origin else if (constr.instValid) checkIsSameType(tp) else isRelatable(tp) && { val newInst = wildcardToTypeVarMap(tp) - (constr isWithinBounds newInst) && { setInst(tp); true } + (constr isWithinBounds newInst) && { + setInst(newInst) + true + } } } @@ -3400,22 +3457,12 @@ trait Types extends api.Types { self: SymbolTable => case class NamedType(name: Name, tp: Type) extends Type { override def safeToString: String = name.toString +": "+ tp } - - /** A De Bruijn index referring to a previous type argument. Only used - * as a serialization format. + /** As with NamedType, used only when calling isApplicable. + * Records that the application has a wildcard star (aka _*) + * at the end of it. */ - case class DeBruijnIndex(level: Int, idx: Int, args: List[Type]) extends Type { - override def safeToString: String = "De Bruijn index("+level+","+idx+")" - } - - /** A binder defining data associated with De Bruijn indices. Only used - * as a serialization format. - */ - case class DeBruijnBinder(pnames: List[Name], ptypes: List[Type], restpe: Type) extends Type { - override def safeToString = { - val kind = if (pnames.head.isTypeName) "poly" else "method" - "De Bruijn "+kind+"("+(pnames mkString ",")+";"+(ptypes mkString ",")+";"+restpe+")" - } + case class RepeatedType(tp: Type) extends Type { + override def safeToString: String = tp + ": _*" } /** A temporary type representing the erasure of a user-defined value type. @@ -3551,12 +3598,6 @@ trait Types extends api.Types { self: SymbolTable => val pre1 = pre match { case x: SuperType if sym1.isEffectivelyFinal || sym1.isDeferred => x.thistpe - case _: CompoundType if sym1.isClass => - // sharpen prefix so that it is maximal and still contains the class. - pre.parents.reverse dropWhile (_.member(sym1.name) != sym1) match { - case Nil => pre - case parent :: _ => parent - } case _ => pre } if (pre eq pre1) TypeRef(pre, sym1, args) @@ -3638,16 +3679,16 @@ trait Types extends api.Types { self: SymbolTable => tycon match { case TypeRef(pre, sym @ (NothingClass|AnyClass), _) => copyTypeRef(tycon, pre, sym, Nil) //@M drop type args to Any/Nothing - case TypeRef(pre, sym, _) => copyTypeRef(tycon, pre, sym, args) + case TypeRef(pre, sym, Nil) => copyTypeRef(tycon, pre, sym, args) + case TypeRef(pre, sym, bogons) => debugwarn(s"Dropping $bogons from $tycon in appliedType.") ; copyTypeRef(tycon, pre, sym, args) case PolyType(tparams, restpe) => restpe.instantiateTypeParams(tparams, args) case ExistentialType(tparams, restpe) => newExistentialType(tparams, appliedType(restpe, args)) case st: SingletonType => appliedType(st.widen, args) // @M TODO: what to do? see bug1 - case RefinedType(parents, decls) => RefinedType(parents map (appliedType(_, args)), decls) // MO to AM: please check - case TypeBounds(lo, hi) => TypeBounds(appliedType(lo, args), appliedType(hi, args)) + case RefinedType(parents, decls) => RefinedType(parents map (appliedType(_, args)), decls) // @PP: Can this be right? + case TypeBounds(lo, hi) => TypeBounds(appliedType(lo, args), appliedType(hi, args)) // @PP: Can this be right? case tv@TypeVar(_, _) => tv.applyArgs(args) case AnnotatedType(annots, underlying, self) => AnnotatedType(annots, appliedType(underlying, args), self) - case ErrorType => tycon - case WildcardType => tycon // needed for neg/t0226 + case ErrorType | WildcardType => tycon case _ => abort(debugString(tycon)) } } @@ -3810,12 +3851,16 @@ trait Types extends api.Types { self: SymbolTable => // This is the specified behavior. protected def etaExpandKeepsStar = false - object dropRepeatedParamType extends TypeMap { + /** Turn any T* types into Seq[T] except when + * in method parameter position. + */ + object dropIllegalStarTypes extends TypeMap { def apply(tp: Type): Type = tp match { case MethodType(params, restpe) => - MethodType(params, apply(restpe)) - case PolyType(tparams, restpe) => - PolyType(tparams, apply(restpe)) + // Not mapping over params + val restpe1 = apply(restpe) + if (restpe eq restpe1) tp + else MethodType(params, restpe1) case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) case _ => @@ -3823,50 +3868,6 @@ trait Types extends api.Types { self: SymbolTable => } } - object toDeBruijn extends TypeMap { - private var paramStack: List[List[Symbol]] = Nil - def mkDebruijnBinder(params: List[Symbol], restpe: Type) = { - paramStack = params :: paramStack - try { - DeBruijnBinder(params map (_.name), params map (p => this(p.info)), this(restpe)) - } finally paramStack = paramStack.tail - } - def apply(tp: Type): Type = tp match { - case PolyType(tparams, restpe) => - mkDebruijnBinder(tparams, restpe) - case MethodType(params, restpe) => - mkDebruijnBinder(params, restpe) - case TypeRef(NoPrefix, sym, args) => - val level = paramStack indexWhere (_ contains sym) - if (level < 0) mapOver(tp) - else DeBruijnIndex(level, paramStack(level) indexOf sym, args mapConserve this) - case _ => - mapOver(tp) - } - } - - def fromDeBruijn(owner: Symbol) = new TypeMap { - private var paramStack: List[List[Symbol]] = Nil - def apply(tp: Type): Type = tp match { - case DeBruijnBinder(pnames, ptypes, restpe) => - val isType = pnames.head.isTypeName - val newParams = for (name <- pnames) yield - if (isType) owner.newTypeParameter(name.toTypeName) - else owner.newValueParameter(name.toTermName) - paramStack = newParams :: paramStack - try { - foreach2(newParams, ptypes)((p, t) => p setInfo this(t)) - val restpe1 = this(restpe) - if (isType) PolyType(newParams, restpe1) - else MethodType(newParams, restpe1) - } finally paramStack = paramStack.tail - case DeBruijnIndex(level, idx, args) => - TypeRef(NoPrefix, paramStack(level)(idx), args map this) - case _ => - mapOver(tp) - } - } - // Hash consing -------------------------------------------------------------- private val initialUniquesCapacity = 4096 @@ -4189,10 +4190,6 @@ trait Types extends api.Types { self: SymbolTable => if ((annots1 eq annots) && (atp1 eq atp)) tp else if (annots1.isEmpty) atp1 else AnnotatedType(annots1, atp1, selfsym) - case DeBruijnIndex(shift, idx, args) => - val args1 = args mapConserve this - if (args1 eq args) tp - else DeBruijnIndex(shift, idx, args1) /* case ErrorType => tp case WildcardType => tp @@ -4361,6 +4358,20 @@ trait Types extends api.Types { self: SymbolTable => mapOver(tp) } } + /*** + *@M: I think this is more desirable, but Martin prefers to leave raw-types as-is as much as possible + object rawToExistentialInJava extends TypeMap { + def apply(tp: Type): Type = tp match { + // any symbol that occurs in a java sig, not just java symbols + // see http://lampsvn.epfl.ch/trac/scala/ticket/2454#comment:14 + case TypeRef(pre, sym, List()) if !sym.typeParams.isEmpty => + val eparams = typeParamsToExistentials(sym, sym.typeParams) + existentialAbstraction(eparams, TypeRef(pre, sym, eparams map (_.tpe))) + case _ => + mapOver(tp) + } + } + */ /** Used by existentialAbstraction. */ @@ -4595,16 +4606,18 @@ trait Types extends api.Types { self: SymbolTable => tp } - def apply(tp0: Type): Type = if (from.isEmpty) tp0 else { - @tailrec def subst(tp: Type, sym: Symbol, from: List[Symbol], to: List[T]): Type = - if (from.isEmpty) tp - // else if (to.isEmpty) error("Unexpected substitution on '%s': from = %s but to == Nil".format(tp, from)) - else if (matches(from.head, sym)) toType(tp, to.head) - else subst(tp, sym, from.tail, to.tail) + @tailrec private def subst(tp: Type, sym: Symbol, from: List[Symbol], to: List[T]): Type = ( + if (from.isEmpty) tp + // else if (to.isEmpty) error("Unexpected substitution on '%s': from = %s but to == Nil".format(tp, from)) + else if (matches(from.head, sym)) toType(tp, to.head) + else subst(tp, sym, from.tail, to.tail) + ) - val boundSyms = tp0.boundSyms - val tp1 = if (boundSyms.nonEmpty && (boundSyms exists from.contains)) renameBoundSyms(tp0) else tp0 - val tp = mapOver(tp1) + def apply(tp0: Type): Type = if (from.isEmpty) tp0 else { + val boundSyms = tp0.boundSyms + val tp1 = if (boundSyms.nonEmpty && (boundSyms exists from.contains)) renameBoundSyms(tp0) else tp0 + val tp = mapOver(tp1) + def substFor(sym: Symbol) = subst(tp, sym, from, to) tp match { // @M @@ -4619,9 +4632,11 @@ trait Types extends api.Types { self: SymbolTable => // (must not recurse --> loops) // 3) replacing m by List in m[Int] should yield List[Int], not just List case TypeRef(NoPrefix, sym, args) => - appliedType(subst(tp, sym, from, to), args) // if args.isEmpty, appliedType is the identity + val tcon = substFor(sym) + if ((tp eq tcon) || args.isEmpty) tcon + else appliedType(tcon.typeConstructor, args) case SingleType(NoPrefix, sym) => - subst(tp, sym, from, to) + substFor(sym) case _ => tp } @@ -4630,29 +4645,35 @@ trait Types extends api.Types { self: SymbolTable => /** A map to implement the `substSym` method. */ class SubstSymMap(from: List[Symbol], to: List[Symbol]) extends SubstMap(from, to) { + def this(pairs: (Symbol, Symbol)*) = this(pairs.toList.map(_._1), pairs.toList.map(_._2)) + protected def toType(fromtp: Type, sym: Symbol) = fromtp match { case TypeRef(pre, _, args) => copyTypeRef(fromtp, pre, sym, args) case SingleType(pre, _) => singleType(pre, sym) } - override def apply(tp: Type): Type = if (from.isEmpty) tp else { - @tailrec def subst(sym: Symbol, from: List[Symbol], to: List[Symbol]): Symbol = - if (from.isEmpty) sym - // else if (to.isEmpty) error("Unexpected substitution on '%s': from = %s but to == Nil".format(sym, from)) - else if (matches(from.head, sym)) to.head - else subst(sym, from.tail, to.tail) - tp match { + @tailrec private def subst(sym: Symbol, from: List[Symbol], to: List[Symbol]): Symbol = ( + if (from.isEmpty) sym + // else if (to.isEmpty) error("Unexpected substitution on '%s': from = %s but to == Nil".format(sym, from)) + else if (matches(from.head, sym)) to.head + else subst(sym, from.tail, to.tail) + ) + private def substFor(sym: Symbol) = subst(sym, from, to) + + override def apply(tp: Type): Type = ( + if (from.isEmpty) tp + else tp match { case TypeRef(pre, sym, args) if pre ne NoPrefix => - val newSym = subst(sym, from, to) + val newSym = substFor(sym) // mapOver takes care of subst'ing in args mapOver ( if (sym eq newSym) tp else copyTypeRef(tp, pre, newSym, args) ) // assert(newSym.typeParams.length == sym.typeParams.length, "typars mismatch in SubstSymMap: "+(sym, sym.typeParams, newSym, newSym.typeParams)) case SingleType(pre, sym) if pre ne NoPrefix => - val newSym = subst(sym, from, to) + val newSym = substFor(sym) mapOver( if (sym eq newSym) tp else singleType(pre, newSym) ) case _ => super.apply(tp) } - } + ) override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { object trans extends TypeMapTransformer { @@ -5113,28 +5134,18 @@ trait Types extends api.Types { self: SymbolTable => class SubTypePair(val tp1: Type, val tp2: Type) { override def hashCode = tp1.hashCode * 41 + tp2.hashCode - override def equals(other: Any) = other match { + override def equals(other: Any) = (this eq other.asInstanceOf[AnyRef]) || (other match { + // suspend TypeVars in types compared by =:=, + // since we don't want to mutate them simply to check whether a subtype test is pending + // in addition to making subtyping "more correct" for type vars, + // it should avoid the stackoverflow that's been plaguing us (https://groups.google.com/d/topic/scala-internals/2gHzNjtB4xA/discussion) + // this method is only called when subtyping hits a recursion threshold (subsametypeRecursions >= LogPendingSubTypesThreshold) case stp: SubTypePair => - // suspend TypeVars in types compared by =:=, - // since we don't want to mutate them simply to check whether a subtype test is pending - // in addition to making subtyping "more correct" for type vars, - // it should avoid the stackoverflow that's been plaguing us (https://groups.google.com/d/topic/scala-internals/2gHzNjtB4xA/discussion) - // this method is only called when subtyping hits a recursion threshold (subsametypeRecursions >= LogPendingSubTypesThreshold) - def suspend(tp: Type) = - if (tp.isGround) null else suspendTypeVarsInType(tp) - def revive(suspension: List[TypeVar]) = - if (suspension ne null) suspension foreach (_.suspended = false) - - val suspensions = Array(tp1, stp.tp1, tp2, stp.tp2) map suspend - - val sameTypes = (tp1 =:= stp.tp1) && (tp2 =:= stp.tp2) - - suspensions foreach revive - - sameTypes + val tvars = List(tp1, stp.tp1, tp2, stp.tp2) flatMap (t => if (t.isGround) Nil else typeVarsInType(t)) + suspendingTypeVars(tvars)(tp1 =:= stp.tp1 && tp2 =:= stp.tp2) case _ => false - } + }) override def toString = tp1+" <:<? "+tp2 } @@ -5206,32 +5217,33 @@ trait Types extends api.Types { self: SymbolTable => def isPopulated(tp1: Type, tp2: Type): Boolean = { def isConsistent(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { case (TypeRef(pre1, sym1, args1), TypeRef(pre2, sym2, args2)) => - assert(sym1 == sym2) + assert(sym1 == sym2, (sym1, sym2)) pre1 =:= pre2 && - forall3(args1, args2, sym1.typeParams) { (arg1, arg2, tparam) => - //if (tparam.variance == 0 && !(arg1 =:= arg2)) Console.println("inconsistent: "+arg1+"!="+arg2)//DEBUG + forall3(args1, args2, sym1.typeParams)((arg1, arg2, tparam) => if (tparam.variance == 0) arg1 =:= arg2 - else if (arg1.isInstanceOf[TypeVar]) - // if left-hand argument is a typevar, make it compatible with variance - // this is for more precise pattern matching - // todo: work this in the spec of this method - // also: think what happens if there are embedded typevars? - if (tparam.variance < 0) arg1 <:< arg2 else arg2 <:< arg1 - else true - } + // if left-hand argument is a typevar, make it compatible with variance + // this is for more precise pattern matching + // todo: work this in the spec of this method + // also: think what happens if there are embedded typevars? + else arg1 match { + case _: TypeVar => if (tparam.variance < 0) arg1 <:< arg2 else arg2 <:< arg1 + case _ => true + } + ) case (et: ExistentialType, _) => et.withTypeVars(isConsistent(_, tp2)) case (_, et: ExistentialType) => et.withTypeVars(isConsistent(tp1, _)) } - def check(tp1: Type, tp2: Type) = + def check(tp1: Type, tp2: Type) = ( if (tp1.typeSymbol.isClass && tp1.typeSymbol.hasFlag(FINAL)) tp1 <:< tp2 || isNumericValueClass(tp1.typeSymbol) && isNumericValueClass(tp2.typeSymbol) else tp1.baseClasses forall (bc => tp2.baseTypeIndex(bc) < 0 || isConsistent(tp1.baseType(bc), tp2.baseType(bc))) + ) - check(tp1, tp2)/* && check(tp2, tp1)*/ // need to investgate why this can't be made symmetric -- neg/gadts1 fails, and run/existials also. + check(tp1, tp2) && check(tp2, tp1) } /** Does a pattern of type `patType` need an outer test when executed against @@ -5314,13 +5326,15 @@ trait Types extends api.Types { self: SymbolTable => try { val before = undoLog.log var result = false - - try result = { - isSameType1(tp1, tp2) - } finally if (!result) undoLog.undoTo(before) + try { + result = isSameType1(tp1, tp2) + } + finally if (!result) undoLog.undoTo(before) result - } finally undoLog.unlock() - } finally { + } + finally undoLog.unlock() + } + finally { subsametypeRecursions -= 1 // XXX AM TODO: figure out when it is safe and needed to clear the log -- the commented approach below is too eager (it breaks #3281, #3866) // it doesn't help to keep separate recursion counts for the three methods that now share it @@ -5602,12 +5616,12 @@ trait Types extends api.Types { self: SymbolTable => } tp1 match { case tv @ TypeVar(_,_) => - return tv.registerTypeEquality(tp2, true) + return tv.registerTypeEquality(tp2, typeVarLHS = true) case _ => } tp2 match { case tv @ TypeVar(_,_) => - return tv.registerTypeEquality(tp1, false) + return tv.registerTypeEquality(tp1, typeVarLHS = false) case _ => } tp1 match { @@ -5788,7 +5802,6 @@ trait Types extends api.Types { self: SymbolTable => private def isInternalTypeNotUsedAsTypeArg(tp: Type): Boolean = tp match { case AntiPolyType(pre, targs) => true case ClassInfoType(parents, defs, clazz) => true - case DeBruijnIndex(level, index, args) => true case ErasedValueType(tref) => true case NoPrefix => true case NoType => true @@ -6136,6 +6149,7 @@ trait Types extends api.Types { self: SymbolTable => * than member `sym2` of `tp2`? */ private def specializesSym(tp1: Type, sym1: Symbol, tp2: Type, sym2: Symbol, depth: Int): Boolean = { + require((sym1 ne NoSymbol) && (sym2 ne NoSymbol), ((tp1, sym1, tp2, sym2, depth))) val info1 = tp1.memberInfo(sym1) val info2 = tp2.memberInfo(sym2).substThis(tp2.typeSymbol, tp1) //System.out.println("specializes "+tp1+"."+sym1+":"+info1+sym1.locationString+" AND "+tp2+"."+sym2+":"+info2)//DEBUG @@ -6450,25 +6464,27 @@ trait Types extends api.Types { self: SymbolTable => * @See baseTypeSeq for a definition of sorted and upwards closed. */ private def lubList(ts: List[Type], depth: Int): List[Type] = { - // Matching the type params of one of the initial types means dummies. - val initialTypeParams = ts map (_.typeParams) - def isHotForTs(xs: List[Type]) = initialTypeParams contains (xs map (_.typeSymbol)) + var lubListDepth = 0 + // This catches some recursive situations which would otherwise + // befuddle us, e.g. pos/hklub0.scala + def isHotForTs(xs: List[Type]) = ts exists (_.typeParams == xs.map(_.typeSymbol)) def elimHigherOrderTypeParam(tp: Type) = tp match { - case TypeRef(pre, sym, args) if args.nonEmpty && isHotForTs(args) => tp.typeConstructor - case _ => tp + case TypeRef(_, _, args) if args.nonEmpty && isHotForTs(args) => + logResult("Retracting dummies from " + tp + " in lublist")(tp.typeConstructor) + case _ => tp } - var lubListDepth = 0 - def loop(tsBts: List[List[Type]]): List[Type] = { + // pretypes is a tail-recursion-preserving accumulator. + @annotation.tailrec def loop(pretypes: List[Type], tsBts: List[List[Type]]): List[Type] = { lubListDepth += 1 - if (tsBts.isEmpty || (tsBts exists typeListIsEmpty)) Nil - else if (tsBts.tail.isEmpty) tsBts.head + if (tsBts.isEmpty || (tsBts exists typeListIsEmpty)) pretypes.reverse + else if (tsBts.tail.isEmpty) pretypes.reverse ++ tsBts.head else { // ts0 is the 1-dimensional frontier of symbols cutting through 2-dimensional tsBts. // Invariant: all symbols "under" (closer to the first row) the frontier // are smaller (according to _.isLess) than the ones "on and beyond" the frontier - val ts0 = tsBts map (_.head) + val ts0 = tsBts map (_.head) // Is the frontier made up of types with the same symbol? val isUniformFrontier = (ts0: @unchecked) match { @@ -6481,23 +6497,23 @@ trait Types extends api.Types { self: SymbolTable => // merging, strip targs that refer to bound tparams (when we're computing the lub of type // constructors.) Also filter out all types that are a subtype of some other type. if (isUniformFrontier) { - if (settings.debug.value || printLubs) { - val fbounds = findRecursiveBounds(ts0) - if (fbounds.nonEmpty) { - println("Encountered " + fbounds.size + " recursive bounds while lubbing " + ts0.size + " types.") - for ((p0, p1) <- fbounds) { - val desc = if (p0 == p1) "its own bounds" else "the bounds of " + p1 - - println(" " + p0.fullLocationString + " appears in " + desc) - println(" " + p1 + " " + p1.info.bounds) + val fbounds = findRecursiveBounds(ts0) map (_._2) + val tcLubList = typeConstructorLubList(ts0) + def isRecursive(tp: Type) = tp.typeSymbol.typeParams exists fbounds.contains + + val ts1 = ts0 map { t => + if (isRecursive(t)) { + tcLubList map (t baseType _.typeSymbol) find (t => !isRecursive(t)) match { + case Some(tp) => logResult(s"Breaking recursion in lublist, substituting weaker type.\n Was: $t\n Now")(tp) + case _ => t } - println("") } + else t } val tails = tsBts map (_.tail) - mergePrefixAndArgs(elimSub(ts0 map elimHigherOrderTypeParam, depth), 1, depth) match { - case Some(tp) => tp :: loop(tails) - case _ => loop(tails) + mergePrefixAndArgs(elimSub(ts1, depth) map elimHigherOrderTypeParam, 1, depth) match { + case Some(tp) => loop(tp :: pretypes, tails) + case _ => loop(pretypes, tails) } } else { @@ -6514,7 +6530,7 @@ trait Types extends api.Types { self: SymbolTable => printLubMatrix((ts zip tsBts).toMap, lubListDepth) } - loop(newtps) + loop(pretypes, newtps) } } } @@ -6523,7 +6539,7 @@ trait Types extends api.Types { self: SymbolTable => if (printLubs) printLubMatrix((ts zip initialBTSes).toMap, depth) - loop(initialBTSes) + loop(Nil, initialBTSes) } /** The minimal symbol of a list of types (as determined by `Symbol.isLess`). */ @@ -6661,6 +6677,23 @@ trait Types extends api.Types { self: SymbolTable => private val lubResults = new mutable.HashMap[(Int, List[Type]), Type] private val glbResults = new mutable.HashMap[(Int, List[Type]), Type] + /** Given a list of types, finds all the base classes they have in + * common, then returns a list of type constructors derived directly + * from the symbols (so any more specific type information is ignored.) + * The list is filtered such that every type constructor in the list + * expects the same number of type arguments, which is chosen based + * on the deepest class among the common baseclasses. + */ + def typeConstructorLubList(ts: List[Type]): List[Type] = { + val bcs = ts.flatMap(_.baseClasses).distinct sortWith (_ isLess _) + val tcons = bcs filter (clazz => ts forall (_.typeSymbol isSubClass clazz)) + + tcons map (_.typeConstructor) match { + case Nil => Nil + case t :: ts => t :: ts.filter(_.typeParams.size == t.typeParams.size) + } + } + def lub(ts: List[Type]): Type = ts match { case List() => NothingClass.tpe case List(t) => t @@ -6668,8 +6701,20 @@ trait Types extends api.Types { self: SymbolTable => if (Statistics.canEnable) Statistics.incCounter(lubCount) val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, lubNanos) else null try { - lub(ts, lubDepth(ts)) - } finally { + val res = lub(ts, lubDepth(ts)) + // If the number of unapplied type parameters in all incoming + // types is consistent, and the lub does not match that, return + // the type constructor of the calculated lub instead. This + // is because lubbing type constructors tends to result in types + // which have been applied to dummies or Nothing. + ts.map(_.typeParams.size).distinct match { + case x :: Nil if res.typeParams.size != x => + logResult(s"Stripping type args from lub because $res is not consistent with $ts")(res.typeConstructor) + case _ => + res + } + } + finally { lubResults.clear() glbResults.clear() if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) @@ -6703,13 +6748,13 @@ trait Types extends api.Types { self: SymbolTable => } } def lub1(ts0: List[Type]): Type = { - val (ts, tparams) = stripExistentialsAndTypeVars(ts0) + val (ts, tparams) = stripExistentialsAndTypeVars(ts0) val lubBaseTypes: List[Type] = lubList(ts, depth) - val lubParents = spanningTypes(lubBaseTypes) - val lubOwner = commonOwner(ts) - val lubBase = intersectionType(lubParents, lubOwner) + val lubParents = spanningTypes(lubBaseTypes) + val lubOwner = commonOwner(ts) + val lubBase = intersectionType(lubParents, lubOwner) val lubType = - if (phase.erasedTypes || depth == 0) lubBase + if (phase.erasedTypes || depth == 0 ) lubBase else { val lubRefined = refinedType(lubParents, lubOwner) val lubThisType = lubRefined.typeSymbol.thisType @@ -6726,6 +6771,7 @@ trait Types extends api.Types { self: SymbolTable => val syms = narrowts map (t => t.nonPrivateMember(proto.name).suchThat(sym => sym.tpe matches prototp.substThis(lubThisType.typeSymbol, t))) + if (syms contains NoSymbol) NoSymbol else { val symtypes = @@ -6752,10 +6798,8 @@ trait Types extends api.Types { self: SymbolTable => // add a refinement symbol for all non-class members of lubBase // which are refined by every type in ts. for (sym <- lubBase.nonPrivateMembers ; if !excludeFromLub(sym)) { - try { - val lsym = lubsym(sym) - if (lsym != NoSymbol) addMember(lubThisType, lubRefined, lsym, depth) - } catch { + try lubsym(sym) andAlso (addMember(lubThisType, lubRefined, _, depth)) + catch { case ex: NoCommonType => } } @@ -6952,15 +6996,22 @@ trait Types extends api.Types { self: SymbolTable => } tvs.reverse } - /** Make each type var in this type use its original type for comparisons instead - * of collecting constraints. - */ - def suspendTypeVarsInType(tp: Type): List[TypeVar] = { - val tvs = typeVarsInType(tp) - // !!! Is it somehow guaranteed that this will not break under nesting? - // In general one has to save and restore the contents of the field... + + // If this type contains type variables, put them to sleep for a while. + // Don't just wipe them out by replacing them by the corresponding type + // parameter, as that messes up (e.g.) type variables in type refinements. + // Without this, the matchesType call would lead to type variables on both + // sides of a subtyping/equality judgement, which can lead to recursive types + // being constructed. See pos/t0851 for a situation where this happens. + def suspendingTypeVarsInType[T](tp: Type)(op: => T): T = + suspendingTypeVars(typeVarsInType(tp))(op) + + @inline final def suspendingTypeVars[T](tvs: List[TypeVar])(op: => T): T = { + val saved = tvs map (_.suspended) tvs foreach (_.suspended = true) - tvs + + try op + finally foreach2(tvs, saved)(_.suspended = _) } /** Compute lub (if `variance == 1`) or glb (if `variance == -1`) of given list @@ -6999,18 +7050,16 @@ trait Types extends api.Types { self: SymbolTable => debuglog("transposed irregular matrix!?" +(tps, argss)) None case Some(argsst) => - val args = map2(sym.typeParams, argsst) { (tparam, as) => - if (depth == 0) { - if (tparam.variance == variance) { - // Take the intersection of the upper bounds of the type parameters - // rather than falling all the way back to "Any", otherwise we end up not - // conforming to bounds. - val bounds0 = sym.typeParams map (_.info.bounds.hi) filterNot (_.typeSymbol == AnyClass) - if (bounds0.isEmpty) AnyClass.tpe - else intersectionType(bounds0 map (b => b.asSeenFrom(tps.head, sym))) - } - else if (tparam.variance == -variance) NothingClass.tpe - else NoType + val args = map2(sym.typeParams, argsst) { (tparam, as0) => + val as = as0.distinct + if (as.size == 1) as.head + else if (depth == 0) { + log("Giving up merging args: can't unify %s under %s".format(as.mkString(", "), tparam.fullLocationString)) + // Don't return "Any" (or "Nothing") when we have to give up due to + // recursion depth. Return NoType, which prevents us from poisoning + // lublist's results. It can recognize the recursion and deal with it, but + // only if we aren't returning invalid types. + NoType } else { if (tparam.variance == variance) lub(as, decr(depth)) @@ -7019,7 +7068,7 @@ trait Types extends api.Types { self: SymbolTable => val l = lub(as, decr(depth)) val g = glb(as, decr(depth)) if (l <:< g) l - else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we + else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we // just err on the conservative side, i.e. with a bound that is too high. // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251 @@ -7067,6 +7116,14 @@ trait Types extends api.Types { self: SymbolTable => } } + def isJavaVarargsAncestor(clazz: Symbol) = ( + clazz.isClass + && clazz.isJavaDefined + && (clazz.info.nonPrivateDecls exists isJavaVarArgsMethod) + ) + def inheritsJavaVarArgsMethod(clazz: Symbol) = + clazz.thisType.baseClasses exists isJavaVarargsAncestor + /** All types in list must be polytypes with type parameter lists of * same length as tparams. * Returns list of list of bounds infos, where corresponding type @@ -7179,6 +7236,12 @@ trait Types extends api.Types { self: SymbolTable => else (ps :+ SerializableClass.tpe).toList ) + /** Members of the given class, other than those inherited + * from Any or AnyRef. + */ + def nonTrivialMembers(clazz: Symbol): Iterable[Symbol] = + clazz.info.members filterNot (sym => sym.owner == ObjectClass || sym.owner == AnyClass) + def objToAny(tp: Type): Type = if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyClass.tpe else tp diff --git a/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala b/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala index 16747af08a..94b2f77ff9 100644 --- a/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala +++ b/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala @@ -56,7 +56,7 @@ object PickleFormat { * | 42 ANNOTATEDtpe len_Nat [sym_Ref /* no longer needed */] tpe_Ref {annotinfo_Ref} * | 43 ANNOTINFO len_Nat AnnotInfoBody * | 44 ANNOTARGARRAY len_Nat {constAnnotArg_Ref} - * | 47 DEBRUIJNINDEXtpe len_Nat level_Nat index_Nat + * | 47 DEBRUIJNINDEXtpe len_Nat level_Nat index_Nat /* no longer needed */ * | 48 EXISTENTIALtpe len_Nat type_Ref {symbol_Ref} * | 49 TREE len_Nat 1 EMPTYtree * | 49 TREE len_Nat 2 PACKAGEtree type_Ref sym_Ref mods_Ref name_Ref {tree_Ref} @@ -161,7 +161,7 @@ object PickleFormat { final val ANNOTARGARRAY = 44 final val SUPERtpe = 46 - final val DEBRUIJNINDEXtpe = 47 + final val DEBRUIJNINDEXtpe = 47 // no longer generated final val EXISTENTIALtpe = 48 final val TREE = 49 // prefix code that means a tree is coming diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index b158a1ac26..ca47ef7e26 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -853,7 +853,7 @@ abstract class UnPickler /*extends scala.reflect.generic.UnPickler*/ { private val p = phase override def complete(sym: Symbol) : Unit = try { val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` - atPhase(p) (sym setInfo tp) + enteringPhase(p) (sym setInfo tp) if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) } @@ -871,7 +871,7 @@ abstract class UnPickler /*extends scala.reflect.generic.UnPickler*/ { super.complete(sym) var alias = at(j, readSymbol) if (alias.isOverloaded) - alias = atPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) + alias = enteringPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) sym.asInstanceOf[TermSymbol].setAlias(alias) } diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index 459326e96f..844ecf908a 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -47,4 +47,5 @@ abstract class MutableSettings extends AbsSettings { def XoldPatmat: BooleanSetting def XnoPatmatAnalysis: BooleanSetting def XfullLubs: BooleanSetting + def breakCycles: BooleanSetting } diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 977398909f..fd3934b3d6 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -69,7 +69,7 @@ trait Erasure { // // This requires that cls.isClass. protected def rebindInnerClass(pre: Type, cls: Symbol): Type = { - if (cls.owner.isClass) cls.owner.tpe else pre // why not cls.isNestedClass? + if (cls.owner.isClass) cls.owner.tpe_* else pre // why not cls.isNestedClass? } def unboxDerivedValueClassMethod(clazz: Symbol): Symbol = diff --git a/src/reflect/scala/reflect/internal/util/StringOps.scala b/src/reflect/scala/reflect/internal/util/StringOps.scala index 281ade8134..e4ad8559e2 100644 --- a/src/reflect/scala/reflect/internal/util/StringOps.scala +++ b/src/reflect/scala/reflect/internal/util/StringOps.scala @@ -34,6 +34,14 @@ trait StringOps { s.substring(0, idx + 1) } } + def longestCommonPrefix(xs: List[String]): String = { + if (xs.isEmpty || xs.contains("")) "" + else xs.head.head match { + case ch => + if (xs.tail forall (_.head == ch)) "" + ch + longestCommonPrefix(xs map (_.tail)) + else "" + } + } def decompose(str: String, sep: Char): List[String] = { def ws(start: Int): List[String] = diff --git a/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala b/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala index fa83f70f3a..46d80e9680 100644 --- a/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala +++ b/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala @@ -129,7 +129,7 @@ trait TraceSymbolActivity { } ph } - private def runBeforeErasure[T](body: => T): T = atPhase(findErasurePhase)(body) + private def runBeforeErasure[T](body: => T): T = enteringPhase(findErasurePhase)(body) def showAllSymbols() { if (!enabled) return diff --git a/src/reflect/scala/reflect/internal/util/package.scala b/src/reflect/scala/reflect/internal/util/package.scala new file mode 100644 index 0000000000..83c8bf67ba --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/package.scala @@ -0,0 +1,34 @@ +package scala +package reflect +package internal + +package object util { + import StringOps.longestCommonPrefix + + // Shorten a name like Symbols$FooSymbol to FooSymbol. + private def shortenName(name: String): String = { + if (name == "") return "" + val segments = (name split '$').toList + val last = segments.last + + if (last.length == 0) + segments takeRight 2 mkString "$" + else + last + } + + def shortClassOfInstance(x: AnyRef): String = shortClass(x.getClass) + def shortClass(clazz: Class[_]): String = { + val name: String = (clazz.getName split '.').last + def isModule = name endsWith "$" // object + def isAnon = (name split '$').last forall (_.isDigit) // anonymous class + + if (isModule) + (name split '$' filterNot (_ == "")).last + "$" + else if (isAnon) { + val parents = clazz.getSuperclass :: clazz.getInterfaces.toList + parents map (c => shortClass(c)) mkString " with " + } + else shortenName(name) + } +} diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 2d08cd887b..f517c30fe6 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -1107,7 +1107,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni constructorCache enter (jconstr, constr) val tparams = jconstr.getTypeParameters.toList map createTypeParameter val paramtpes = jconstr.getGenericParameterTypes.toList map typeToScala - setMethType(constr, tparams, paramtpes, clazz.tpe) + setMethType(constr, tparams, paramtpes, clazz.tpe_*) constr setInfo GenPolyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe)) copyAnnotations(constr, jconstr) constr diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 0e0cf3fc40..7d04202455 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -43,6 +43,7 @@ private[reflect] class Settings extends MutableSettings { val printtypes = new BooleanSetting(false) val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) + val breakCycles = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 61663f6181..2b192ce570 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -28,7 +28,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => debugInfo("completing "+sym+"/"+clazz.fullName) assert(sym == clazz || sym == module || sym == module.moduleClass) // try { - atPhaseNotLaterThan(picklerPhase) { + enteringPhaseNotLaterThan(picklerPhase) { val loadingMirror = mirrorThatLoaded(sym) val javaClass = loadingMirror.javaClass(clazz.javaClassName) loadingMirror.unpickleClass(clazz, module, javaClass) diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index 00f6952dc1..366b4319c3 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -118,7 +118,8 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb override def name_=(x: Name) = synchronized { super.name_=(x) } override def rawname = synchronized { super.rawname } override def typeConstructor: Type = synchronized { super.typeConstructor } - override def tpe: Type = synchronized { super.tpe } + override def tpe_* : Type = synchronized { super.tpe_* } + override def tpeHK : Type = synchronized { super.tpeHK } } trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol { diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Rule.scala b/src/scalap/scala/tools/scalap/scalax/rules/Rule.scala index 1500b81050..489a05ecd0 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/Rule.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/Rule.scala @@ -50,7 +50,7 @@ trait Rule[-In, +Out, +A, +X] extends (In => Result[Out, A, X]) { lazy val choices = Rule.this :: other :: Nil } - def orError[In2 <: In] = this orElse(error[In2]) + def orError[In2 <: In] = this orElse error[Any] def |[In2 <: In, Out2 >: Out, A2 >: A, X2 >: X](other : => Rule[In2, Out2, A2, X2]) = orElse(other) diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala index e88efa1bfd..76dc0eaf1e 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala @@ -167,57 +167,10 @@ object ScalaSigEntryParsers extends RulesWithState with MemoisableRules { val symbolInfo = nameRef ~ symbolRef ~ nat ~ (symbolRef?) ~ ref ~ get ^~~~~~^ SymbolInfo - def symHeader(key: Int) = (key -~ none | (key + 64) -~ nat) + def symHeader(key: Int): EntryParser[Any] = (key -~ none | (key + 64) -~ nat) def symbolEntry(key : Int) = symHeader(key) -~ symbolInfo - /*************************************************** - * Symbol table attribute format: - * Symtab = nentries_Nat {Entry} - * Entry = 1 TERMNAME len_Nat NameInfo - * | 2 TYPENAME len_Nat NameInfo - * | 3 NONEsym len_Nat - * | 4 TYPEsym len_Nat SymbolInfo - * | 5 ALIASsym len_Nat SymbolInfo - * | 6 CLASSsym len_Nat SymbolInfo [thistype_Ref] - * | 7 MODULEsym len_Nat SymbolInfo - * | 8 VALsym len_Nat [defaultGetter_Ref /* no longer needed*/] SymbolInfo [alias_Ref] - * | 9 EXTref len_Nat name_Ref [owner_Ref] - * | 10 EXTMODCLASSref len_Nat name_Ref [owner_Ref] - * | 11 NOtpe len_Nat - * | 12 NOPREFIXtpe len_Nat - * | 13 THIStpe len_Nat sym_Ref - * | 14 SINGLEtpe len_Nat type_Ref sym_Ref - * | 15 CONSTANTtpe len_Nat constant_Ref - * | 16 TYPEREFtpe len_Nat type_Ref sym_Ref {targ_Ref} - * | 17 TYPEBOUNDStpe len_Nat tpe_Ref tpe_Ref - * | 18 REFINEDtpe len_Nat classsym_Ref {tpe_Ref} - * | 19 CLASSINFOtpe len_Nat classsym_Ref {tpe_Ref} - * | 20 METHODtpe len_Nat tpe_Ref {sym_Ref} - * | 21 POLYTtpe len_Nat tpe_Ref {sym_Ref} - * | 22 IMPLICITMETHODtpe len_Nat tpe_Ref {sym_Ref} /* no longer needed */ - * | 52 SUPERtpe len_Nat tpe_Ref tpe_Ref - * | 24 LITERALunit len_Nat - * | 25 LITERALboolean len_Nat value_Long - * | 26 LITERALbyte len_Nat value_Long - * | 27 LITERALshort len_Nat value_Long - * | 28 LITERALchar len_Nat value_Long - * | 29 LITERALint len_Nat value_Long - * | 30 LITERALlong len_Nat value_Long - * | 31 LITERALfloat len_Nat value_Long - * | 32 LITERALdouble len_Nat value_Long - * | 33 LITERALstring len_Nat name_Ref - * | 34 LITERALnull len_Nat - * | 35 LITERALclass len_Nat tpe_Ref - * | 36 LITERALenum len_Nat sym_Ref - * | 40 SYMANNOT len_Nat sym_Ref AnnotInfoBody - * | 41 CHILDREN len_Nat sym_Ref {sym_Ref} - * | 42 ANNOTATEDtpe len_Nat [sym_Ref /* no longer needed */] tpe_Ref {annotinfo_Ref} - * | 43 ANNOTINFO len_Nat AnnotInfoBody - * | 44 ANNOTARGARRAY len_Nat {constAnnotArg_Ref} - * | 47 DEBRUIJNINDEXtpe len_Nat level_Nat index_Nat - * | 48 EXISTENTIALtpe len_Nat type_Ref {symbol_Ref} - */ val noSymbol = 3 -^ NoSymbol val typeSymbol = symbolEntry(4) ^^ TypeSymbol as "typeSymbol" val aliasSymbol = symbolEntry(5) ^^ AliasSymbol as "alias" @@ -260,10 +213,9 @@ object ScalaSigEntryParsers extends RulesWithState with MemoisableRules { 22 -~ typeRef ~ (symbolRef*) ^~^ MethodType, 42 -~ typeRef ~ (attribTreeRef*) ^~^ AnnotatedType, 51 -~ typeRef ~ symbolRef ~ (attribTreeRef*) ^~~^ AnnotatedWithSelfType, - 47 -~ typeLevel ~ typeIndex ^~^ DeBruijnIndexType, 48 -~ typeRef ~ (symbolRef*) ^~^ ExistentialType) as "type" - lazy val literal = oneOf( + lazy val literal: EntryParser[Any] = oneOf( 24 -^ (()), 25 -~ longValue ^^ (_ != 0L), 26 -~ longValue ^^ (_.toByte), diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala index 411a87e4bb..f3d449b87f 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala @@ -381,7 +381,6 @@ class ScalaSigPrinter(stream: PrintStream, printPrivates: Boolean) { toString(typeRef, sep) } case AnnotatedWithSelfType(typeRef, symbol, attribTreeRefs) => toString(typeRef, sep) - //case DeBruijnIndexType(typeLevel, typeIndex) => case ExistentialType(typeRef, symbols) => { val refs = symbols.map(toString _).filter(!_.startsWith("_")).map("type " + _) toString(typeRef, sep) + (if (refs.size > 0) refs.mkString(" forSome {", "; ", "}") else "") diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Type.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Type.scala index 543ddbe186..0444e701f2 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Type.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Type.scala @@ -22,5 +22,4 @@ case class PolyType(typeRef : Type, symbols : Seq[TypeSymbol]) extends Type case class PolyTypeWithCons(typeRef : Type, symbols : Seq[TypeSymbol], cons: String) extends Type case class AnnotatedType(typeRef : Type, attribTreeRefs : List[Int]) extends Type case class AnnotatedWithSelfType(typeRef : Type, symbol : Symbol, attribTreeRefs : List[Int]) extends Type -case class DeBruijnIndexType(typeLevel : Int, typeIndex : Int) extends Type case class ExistentialType(typeRef : Type, symbols : Seq[Symbol]) extends Type diff --git a/src/swing/scala/swing/ComboBox.scala b/src/swing/scala/swing/ComboBox.scala index c7a457d082..67e39cfe3b 100644 --- a/src/swing/scala/swing/ComboBox.scala +++ b/src/swing/scala/swing/ComboBox.scala @@ -182,7 +182,7 @@ class ComboBox[A](items: Seq[A]) extends Component with Publisher { * of the component to its own defaults _after_ the renderer has been * configured. That's Swing's principle of most suprise. */ - def renderer: ListView.Renderer[A] = ListView.Renderer.wrap(peer.getRenderer) + def renderer: ListView.Renderer[A] = ListView.Renderer.wrap[A](peer.getRenderer) def renderer_=(r: ListView.Renderer[A]) { peer.setRenderer(r.peer) } /* XXX: currently not safe to expose: diff --git a/src/swing/scala/swing/ListView.scala b/src/swing/scala/swing/ListView.scala index 282d24696e..22850bac42 100644 --- a/src/swing/scala/swing/ListView.scala +++ b/src/swing/scala/swing/ListView.scala @@ -216,7 +216,7 @@ class ListView[A] extends Component { def adjusting = peer.getSelectionModel.getValueIsAdjusting } - def renderer: ListView.Renderer[A] = ListView.Renderer.wrap(peer.getCellRenderer) + def renderer: ListView.Renderer[A] = ListView.Renderer.wrap[A](peer.getCellRenderer) def renderer_=(r: ListView.Renderer[A]) { peer.setCellRenderer(r.peer) } def fixedCellWidth = peer.getFixedCellWidth |