diff options
117 files changed, 2706 insertions, 753 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 046122d83b..79f0bcf149 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -288,10 +288,16 @@ trait Scanners extends ScannersCommon { prev copyFrom this val nextLastOffset = charOffset - 1 fetchToken() + def resetOffset() { + offset = prev.offset + lastOffset = prev.lastOffset + } if (token == CLASS) { token = CASECLASS + resetOffset() } else if (token == OBJECT) { token = CASEOBJECT + resetOffset() } else { lastOffset = nextLastOffset next copyFrom this diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index fee683ce3a..d4a6d18c60 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -18,6 +18,9 @@ abstract class DeadCodeElimination extends SubComponent { import icodes.opcodes._ import definitions.RuntimePackage + /** The block and index where an instruction is located */ + type InstrLoc = (BasicBlock, Int) + val phaseName = "dce" /** Create a new phase */ @@ -55,27 +58,35 @@ abstract class DeadCodeElimination extends SubComponent { val rdef = new reachingDefinitions.ReachingDefinitionsAnalysis; /** Use-def chain: give the reaching definitions at the beginning of given instruction. */ - var defs: immutable.Map[(BasicBlock, Int), immutable.Set[rdef.lattice.Definition]] = immutable.HashMap.empty + var defs: immutable.Map[InstrLoc, immutable.Set[rdef.lattice.Definition]] = immutable.HashMap.empty /** Useful instructions which have not been scanned yet. */ - val worklist: mutable.Set[(BasicBlock, Int)] = new mutable.LinkedHashSet + val worklist: mutable.Set[InstrLoc] = new mutable.LinkedHashSet /** what instructions have been marked as useful? */ val useful: mutable.Map[BasicBlock, mutable.BitSet] = perRunCaches.newMap() /** what local variables have been accessed at least once? */ var accessedLocals: List[Local] = Nil + + /** Map from a local and a basic block to the instructions that store to that local in that basic block */ + val localStores = mutable.Map[(Local, BasicBlock), mutable.BitSet]() withDefault {_ => mutable.BitSet()} + + /** Stores that clobber previous stores to array or ref locals. See SI-5313 */ + val clobbers = mutable.Set[InstrLoc]() /** the current method. */ var method: IMethod = _ /** Map instructions who have a drop on some control path, to that DROP instruction. */ - val dropOf: mutable.Map[(BasicBlock, Int), List[(BasicBlock, Int)]] = perRunCaches.newMap() + val dropOf: mutable.Map[InstrLoc, List[InstrLoc]] = perRunCaches.newMap() def dieCodeDie(m: IMethod) { if (m.hasCode) { debuglog("dead code elimination on " + m); dropOf.clear() + localStores.clear() + clobbers.clear() m.code.blocks.clear() accessedLocals = m.params.reverse m.code.blocks ++= linearizer.linearize(m) @@ -104,10 +115,10 @@ abstract class DeadCodeElimination extends SubComponent { for (Pair(i, idx) <- bb.toList.zipWithIndex) { i match { - case LOAD_LOCAL(l) => + case LOAD_LOCAL(_) => defs = defs + Pair(((bb, idx)), rd.vars) - case STORE_LOCAL(_) => + case STORE_LOCAL(l) => /* SI-4935 Check whether a module is stack top, if so mark the instruction that loaded it * (otherwise any side-effects of the module's constructor go lost). * (a) The other two cases where a module's value is stored (STORE_FIELD and STORE_ARRAY_ITEM) @@ -125,6 +136,11 @@ abstract class DeadCodeElimination extends SubComponent { } } if (necessary) worklist += ((bb, idx)) + // add it to the localStores map + val key = (l, bb) + val set = localStores(key) + set += idx + localStores(key) = set case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | @@ -162,11 +178,18 @@ abstract class DeadCodeElimination extends SubComponent { def mark() { // log("Starting with worklist: " + worklist) while (!worklist.isEmpty) { - val (bb, idx) = worklist.iterator.next + val (bb, idx) = worklist.head worklist -= ((bb, idx)) debuglog("Marking instr: \tBB_" + bb + ": " + idx + " " + bb(idx)) val instr = bb(idx) + // adds the instrutions that define the stack values about to be consumed to the work list to + // be marked useful + def addDefs() = for ((bb1, idx1) <- rdef.findDefs(bb, idx, instr.consumed) if !useful(bb1)(idx1)) { + debuglog(s"\t${bb1(idx1)} is consumed by $instr") + worklist += ((bb1, idx1)) + } + if (!useful(bb)(idx)) { useful(bb) += idx dropOf.get(bb, idx) foreach { @@ -180,6 +203,15 @@ abstract class DeadCodeElimination extends SubComponent { worklist += ((bb1, idx1)) } + case STORE_LOCAL(l1) if l1.kind.isRefOrArrayType => + addDefs() + // see SI-5313 + // search for clobbers of this store if we aren't doing l1 = null + // this doesn't catch the second store in x=null;l1=x; but in practice this catches + // a lot of null stores very cheaply + if (idx == 0 || bb(idx - 1) != CONSTANT(Constant(null))) + findClobbers(l1, bb, idx + 1) + case nw @ NEW(REFERENCE(sym)) => assert(nw.init ne null, "null new.init at: " + bb + ": " + idx + "(" + instr + ")") worklist += findInstruction(bb, nw.init) @@ -199,14 +231,72 @@ abstract class DeadCodeElimination extends SubComponent { () case _ => - for ((bb1, idx1) <- rdef.findDefs(bb, idx, instr.consumed) if !useful(bb1)(idx1)) { - debuglog("\tAdding " + bb1(idx1)) - worklist += ((bb1, idx1)) - } + addDefs() } } } } + + /** + * Finds and marks all clobbers of the given local starting in the given + * basic block at the given index + * + * Storing to local variables of reference or array type may be indirectly + * observable because it may remove a reference to an object which may allow the object + * to be gc'd. See SI-5313. In this code I call the LOCAL_STORE(s) that immediately follow a + * LOCAL_STORE and that store to the same local "clobbers." If a LOCAL_STORE is marked + * useful then its clobbers must go into the set of clobbers, which will be + * compensated for later + */ + def findClobbers(l: Local, bb: BasicBlock, idx: Int) { + // previously visited blocks tracked to prevent searching forever in a cycle + val inspected = mutable.Set[BasicBlock]() + // our worklist of blocks that still need to be checked + val blocksToBeInspected = mutable.Set[BasicBlock]() + + // Tries to find the next clobber of l1 in bb1 starting at idx1. + // if it finds one it adds the clobber to clobbers set for later + // handling. If not it adds the direct successor blocks to + // the uninspectedBlocks to try to find clobbers there. Either way + // it adds the exception successor blocks for further search + def findClobberInBlock(idx1: Int, bb1: BasicBlock) { + val key = ((l, bb1)) + val foundClobber = (localStores contains key) && { + def minIdx(s : mutable.BitSet) = if(s.isEmpty) -1 else s.min + + // find the smallest index greater than or equal to idx1 + val clobberIdx = minIdx(localStores(key) dropWhile (_ < idx1)) + if (clobberIdx == -1) + false + else { + debuglog(s"\t${bb1(clobberIdx)} is a clobber of ${bb(idx)}") + clobbers += ((bb1, clobberIdx)) + true + } + } + + // always need to look into the exception successors for additional clobbers + // because we don't know when flow might enter an exception handler + blocksToBeInspected ++= (bb1.exceptionSuccessors filterNot inspected) + // If we didn't find a clobber here then we need to look at successor blocks. + // if we found a clobber then we don't need to search in the direct successors + if (!foundClobber) { + blocksToBeInspected ++= (bb1.directSuccessors filterNot inspected) + } + } + + // first search starting at the current index + // note we don't put bb in the inspected list yet because a loop may later force + // us back around to search from the beginning of bb + findClobberInBlock(idx, bb) + // then loop until we've exhausted the set of uninspected blocks + while(!blocksToBeInspected.isEmpty) { + val bb1 = blocksToBeInspected.head + blocksToBeInspected -= bb1 + inspected += bb1 + findClobberInBlock(0, bb1) + } + } def sweep(m: IMethod) { val compensations = computeCompensations(m) @@ -236,6 +326,12 @@ abstract class DeadCodeElimination extends SubComponent { i match { case NEW(REFERENCE(sym)) => log(s"Eliminated instantation of $sym inside $m") + case STORE_LOCAL(l) if clobbers contains ((bb, idx)) => + // if an unused instruction was a clobber of a used store to a reference or array type + // then we'll replace it with the store of a null to make sure the reference is + // eliminated. See SI-5313 + bb emit CONSTANT(Constant(null)) + bb emit STORE_LOCAL(l) case _ => () } debuglog("Skipped: bb_" + bb + ": " + idx + "( " + i + ")") @@ -247,8 +343,8 @@ abstract class DeadCodeElimination extends SubComponent { } } - private def computeCompensations(m: IMethod): mutable.Map[(BasicBlock, Int), List[Instruction]] = { - val compensations: mutable.Map[(BasicBlock, Int), List[Instruction]] = new mutable.HashMap + private def computeCompensations(m: IMethod): mutable.Map[InstrLoc, List[Instruction]] = { + val compensations: mutable.Map[InstrLoc, List[Instruction]] = new mutable.HashMap m foreachBlock { bb => assert(bb.closed, "Open block in computeCompensations") @@ -287,7 +383,7 @@ abstract class DeadCodeElimination extends SubComponent { res } - private def findInstruction(bb: BasicBlock, i: Instruction): (BasicBlock, Int) = { + private def findInstruction(bb: BasicBlock, i: Instruction): InstrLoc = { for (b <- linearizer.linearizeAt(method, bb)) { val idx = b.toList indexWhere (_ eq i) if (idx != -1) diff --git a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala index 10e2f23142..4ee6daf73e 100755 --- a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala @@ -20,7 +20,7 @@ object IndexModelFactory { /* Owner template ordering */ implicit def orderingSet = math.Ordering.String.on { x: MemberEntity => x.name.toLowerCase } /* symbol name ordering */ - implicit def orderingMap = math.Ordering.String.on { x: String => x.toLowerCase } + implicit def orderingMap = math.Ordering.String def addMember(d: MemberEntity) = { val firstLetter = { diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index c6cfc317ea..0a469c9227 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -314,12 +314,15 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { inform("Creating doc template for " + sym) override def toRoot: List[DocTemplateImpl] = this :: inTpl.toRoot - def inSource = - if (sym.sourceFile != null && ! sym.isSynthetic) - Some((sym.sourceFile, sym.pos.line)) + + protected def inSourceFromSymbol(symbol: Symbol) = + if (symbol.sourceFile != null && ! symbol.isSynthetic) + Some((symbol.sourceFile, symbol.pos.line)) else None + def inSource = inSourceFromSymbol(sym) + def sourceUrl = { def fixPath(s: String) = s.replaceAll("\\" + java.io.File.separator, "/") val assumedSourceRoot = fixPath(settings.sourcepath.value) stripSuffix "/" @@ -508,11 +511,11 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { abstract class PackageImpl(sym: Symbol, inTpl: PackageImpl) extends DocTemplateImpl(sym, inTpl) with Package { override def inTemplate = inTpl override def toRoot: List[PackageImpl] = this :: inTpl.toRoot - override lazy val linearization = { - val symbol = sym.info.members.find { + override lazy val (inSource, linearization) = { + val representive = sym.info.members.find { s => s.isPackageObject } getOrElse sym - linearizationFromSymbol(symbol) + (inSourceFromSymbol(representive), linearizationFromSymbol(representive)) } def packages = members collect { case p: PackageImpl if !(droppedPackages contains p) => p } } diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index 9d01e73063..dbb9b7a003 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -15,6 +15,7 @@ import symtab.Flags import mutable.ListBuffer import scala.annotation.elidable import scala.language.postfixOps +import scala.tools.nsc.settings.ScalaVersion trait ParallelMatching extends ast.TreeDSL with MatchSupport @@ -821,7 +822,7 @@ trait ParallelMatching extends ast.TreeDSL // match that's unimportant; so we add an instance check only if there // is a binding. def bindingWarning() = { - if (isBound && settings.Xmigration28.value) { + if (isBound && settings.Xmigration.value < ScalaVersion.twoDotEight) { cunit.warning(scrutTree.pos, "A bound pattern such as 'x @ Pattern' now matches fewer cases than the same pattern with no binding.") } diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index f1f289ed4d..e4f99474e1 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -221,6 +221,7 @@ class MutableSettings(val errorFn: String => Unit) def OutputSetting(outputDirs: OutputDirs, default: String) = add(new OutputSetting(outputDirs, default)) def PhasesSetting(name: String, descr: String, default: String = "") = add(new PhasesSetting(name, descr, default)) def StringSetting(name: String, arg: String, descr: String, default: String) = add(new StringSetting(name, arg, descr, default)) + def ScalaVersionSetting(name: String, arg: String, descr: String, default: ScalaVersion) = add(new ScalaVersionSetting(name, arg, descr, default)) def PathSetting(name: String, descr: String, default: String): PathSetting = { val prepend = StringSetting(name + "/p", "", "", "").internalOnly() val append = StringSetting(name + "/a", "", "", "").internalOnly() @@ -486,6 +487,35 @@ class MutableSettings(val errorFn: String => Unit) withHelpSyntax(name + " <" + arg + ">") } + /** A setting represented by a Scala version, (`default` unless set) */ + class ScalaVersionSetting private[nsc]( + name: String, + val arg: String, + descr: String, + default: ScalaVersion) + extends Setting(name, descr) { + import ScalaVersion._ + + type T = ScalaVersion + protected var v: T = NoScalaVersion + + override def tryToSet(args: List[String]) = { + value = default + Some(args) + } + + override def tryToSetColon(args: List[String]) = args match { + case Nil => value = default; Some(Nil) + case x :: xs => value = ScalaVersion(x, errorFn) ; Some(xs) + } + + override def tryToSetFromPropertyValue(s: String) = tryToSet(List(s)) + + def unparse: List[String] = if (value == NoScalaVersion) Nil else List(s"${name}:${value.unparse}") + + withHelpSyntax(s"${name}:<${arg}>") + } + class PathSetting private[nsc]( name: String, descr: String, diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index b820d10ddc..0a98d45cac 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -85,8 +85,7 @@ trait ScalaSettings extends AbsScalaSettings val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) - val Xmigration28 = BooleanSetting ("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8."). - withDeprecationMessage("This setting is no longer useful and will be removed. Please remove it from your build.") + val Xmigration = ScalaVersionSetting("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", AnyScalaVersion) val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.") val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") @@ -169,6 +168,7 @@ trait ScalaSettings extends AbsScalaSettings val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.") val Ybuilderdebug = ChoiceSetting ("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none") val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") + val Ymacronoexpand = BooleanSetting ("-Ymacro-no-expand", "Don't expand macros. Might be useful for scaladoc and presentation compiler, but will crash anything which uses macros and gets past typer.") val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") val Ynotnull = BooleanSetting ("-Ynotnull", "Enable (experimental and incomplete) scala.NotNull.") val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.") diff --git a/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala b/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala new file mode 100644 index 0000000000..d6a0149411 --- /dev/null +++ b/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala @@ -0,0 +1,194 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author James Iry + */ +// $Id$ + +package scala.tools.nsc.settings + +/** + * Represents a single Scala version in a manner that + * supports easy comparison and sorting. + */ +abstract class ScalaVersion extends Ordered[ScalaVersion] { + def unparse: String +} + +/** + * A scala version that sorts higher than all actual versions + */ +case object NoScalaVersion extends ScalaVersion { + def unparse = "none" + + def compare(that: ScalaVersion): Int = that match { + case NoScalaVersion => 0 + case _ => 1 + } +} + +/** + * A specific Scala version, not one of the magic min/max versions. An SpecificScalaVersion + * may or may not be a released version - i.e. this same class is used to represent + * final, release candidate, milestone, and development builds. The build argument is used + * to segregate builds + */ +case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion { + def unparse = s"${major}.${minor}.${rev}.${build.unparse}" + + def compare(that: ScalaVersion): Int = that match { + case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) => + // this could be done more cleanly by importing scala.math.Ordering.Implicits, but we have to do these + // comparisons a lot so I'm using brute force direct style code + if (major < thatMajor) -1 + else if (major > thatMajor) 1 + else if (minor < thatMinor) -1 + else if (minor > thatMinor) 1 + else if (rev < thatRev) -1 + else if (rev > thatRev) 1 + else build compare thatBuild + case AnyScalaVersion => 1 + case NoScalaVersion => -1 + } +} + +/** + * A Scala version that sorts lower than all actual versions + */ +case object AnyScalaVersion extends ScalaVersion { + def unparse = "any" + + def compare(that: ScalaVersion): Int = that match { + case AnyScalaVersion => 0 + case _ => -1 + } +} + +/** + * Factory methods for producing ScalaVersions + */ +object ScalaVersion { + private val dot = "\\." + private val dash = "\\-" + private def not(s:String) = s"[^${s}]" + private val R = s"((${not(dot)}*)(${dot}(${not(dot)}*)(${dot}(${not(dash)}*)(${dash}(.*))?)?)?)".r + + def apply(versionString : String, errorHandler: String => Unit): ScalaVersion = { + def errorAndValue() = { + errorHandler( + s"There was a problem parsing ${versionString}. " + + "Versions should be in the form major[.minor[.revision]] " + + "where each part is a positive number, as in 2.10.1. " + + "The minor and revision parts are optional." + ) + AnyScalaVersion + } + + def toInt(s: String) = s match { + case null | "" => 0 + case _ => s.toInt + } + + def isInt(s: String) = util.Try(toInt(s)).isSuccess + + def toBuild(s: String) = s match { + case null | "FINAL" => Final + case s if (s.toUpperCase.startsWith("RC") && isInt(s.substring(2))) => RC(toInt(s.substring(2))) + case s if (s.toUpperCase.startsWith("M") && isInt(s.substring(1))) => Milestone(toInt(s.substring(1))) + case _ => Development(s) + } + + try versionString match { + case "none" => NoScalaVersion + case "any" => AnyScalaVersion + case R(_, majorS, _, minorS, _, revS, _, buildS) => + SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS)) + case _ => + errorAndValue() + } catch { + case e: NumberFormatException => errorAndValue() + } + } + + def apply(versionString: String): ScalaVersion = + apply(versionString, msg => throw new NumberFormatException(msg)) + + /** + * The version of the compiler running now + */ + val current = apply(util.Properties.versionNumberString) + + /** + * The 2.8.0 version. + */ + val twoDotEight = SpecificScalaVersion(2, 8, 0, Final) +} + +/** + * Represents the data after the dash in major.minor.rev-build + */ +abstract class ScalaBuild extends Ordered[ScalaBuild] { + /** + * Return a version of this build information that can be parsed back into the + * same ScalaBuild + */ + def unparse: String +} +/** + * A development, test, nightly, snapshot or other "unofficial" build + */ +case class Development(id: String) extends ScalaBuild { + def unparse = s"-${id}" + + def compare(that: ScalaBuild) = that match { + // sorting two development builds based on id is reasonably valid for two versions created with the same schema + // otherwise it's not correct, but since it's impossible to put a total ordering on development build versions + // this is a pragmatic compromise + case Development(thatId) => id compare thatId + // assume a development build is newer than anything else, that's not really true, but good luck + // mapping development build versions to other build types + case _ => 1 + } +} +/** + * A final final + */ +case object Final extends ScalaBuild { + def unparse = "" + + def compare(that: ScalaBuild) = that match { + case Final => 0 + // a final is newer than anything other than a development build or another final + case Development(_) => -1 + case _ => 1 + } +} + +/** + * A candidate for final release + */ +case class RC(n: Int) extends ScalaBuild { + def unparse = s"-RC${n}" + + def compare(that: ScalaBuild) = that match { + // compare two rcs based on their RC numbers + case RC(thatN) => n - thatN + // an rc is older than anything other than a milestone or another rc + case Milestone(_) => 1 + case _ => -1 + } +} + +/** + * An intermediate release + */ +case class Milestone(n: Int) extends ScalaBuild { + def unparse = s"-M${n}" + + def compare(that: ScalaBuild) = that match { + // compare two milestones based on their milestone numbers + case Milestone(thatN) => n - thatN + // a milestone is older than anything other than another milestone + case _ => -1 + + } +} diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 1003d417f6..78c120c1ad 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -10,6 +10,7 @@ import symtab._ import Flags.{ CASE => _, _ } import scala.collection.mutable.ListBuffer import matching.{ Patterns, ParallelMatching } +import scala.tools.nsc.settings.ScalaVersion /** This class ... * @@ -553,7 +554,7 @@ abstract class ExplicitOuter extends InfoTransform } case _ => - if (settings.Xmigration28.value) tree match { + if (settings.Xmigration.value < ScalaVersion.twoDotEight) tree match { case TypeApply(fn @ Select(qual, _), args) if fn.symbol == Object_isInstanceOf || fn.symbol == Any_isInstanceOf => if (isArraySeqTest(qual.tpe, args.head.tpe)) unit.warning(tree.pos, "An Array will no longer match as Seq[_].") diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 589aa43ac2..bc54054028 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -91,39 +91,42 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { matching.head } + /** Recognize a MethodType which represents an extension method. + * + * It may have a curried parameter list with the `$this` alone in the first + * parameter list, in which case that parameter list is dropped. Or, since + * the curried lists disappear during uncurry, it may have a single parameter + * list with `$this` as the first parameter, in which case that parameter is + * removed from the list. + */ + object ExtensionMethodType { + def unapply(tp: Type) = tp match { + case MethodType(thiz :: rest, restpe) if thiz.name == nme.SELF => + Some((thiz, if (rest.isEmpty) restpe else MethodType(rest, restpe) )) + case _ => + None + } + } + /** 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`. - * In this case, the first argument is `$this` and we just get rid of it. + * type params from its nested `MethodType`. Or it may be a MethodType, as + * described at the ExtensionMethodType extractor. */ private def normalize(stpe: Type, clazz: Symbol): Type = stpe match { case PolyType(tparams, restpe) => - // Split the type parameters of the extension method into two groups, - // corresponding the to class and method type parameters. - val numClassParams = clazz.typeParams.length - val methTParams = tparams dropRight numClassParams - val classTParams = tparams takeRight numClassParams - - GenPolyType(methTParams, - normalize(restpe.substSym(classTParams, clazz.typeParams), clazz)) - case MethodType(List(thiz), restpe) if thiz.name == nme.SELF => - restpe.substituteTypes(thiz :: Nil, clazz.thisType :: Nil) - case MethodType(thiz :: params, restpe) => - MethodType(params, restpe) + // method type parameters, class type parameters + val (mtparams, ctparams) = tparams splitAt (tparams.length - clazz.typeParams.length) + GenPolyType(mtparams, + normalize(restpe.substSym(ctparams, clazz.typeParams), clazz)) + case ExtensionMethodType(thiz, etpe) => + etpe.substituteTypes(thiz :: Nil, clazz.thisType :: Nil) case _ => stpe } class Extender(unit: CompilationUnit) extends TypingTransformer(unit) { - private val extensionDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: Symbol): Unit = @@ -134,31 +137,54 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { if (unboxed.isDerivedValueClass) checkNonCyclic(pos, seen + clazz, unboxed) } + /** We will need to clone the info of the original method (which obtains clones + * of the method type parameters), clone the type parameters of the value class, + * and create a new polymethod with the union of all those type parameters, with + * their infos adjusted to be consistent with their new home. Example: + * + * class Foo[+A <: AnyRef](val xs: List[A]) extends AnyVal { + * def baz[B >: A](x: B): List[B] = x :: xs + * // baz has to be transformed into this extension method, where + * // A is cloned from class Foo and B is cloned from method baz: + * // def extension$baz[B >: A <: Any, A >: Nothing <: AnyRef]($this: Foo[A])(x: B): List[B] + * } + * + * TODO: factor out the logic for consolidating type parameters from a class + * and a method for re-use elsewhere, because nobody will get this right without + * some higher level facilities. + */ def extensionMethInfo(extensionMeth: Symbol, origInfo: Type, clazz: Symbol): Type = { - // No variance for method type parameters - var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) map (_ resetFlag COVARIANT | CONTRAVARIANT) - val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpeHK)) + val GenPolyType(tparamsFromMethod, methodResult) = origInfo cloneInfo extensionMeth + // Start with the class type parameters - clones will be method type parameters + // so must drop their variance. + val tparamsFromClass = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) map (_ resetFlag COVARIANT | CONTRAVARIANT) + + val thisParamType = appliedType(clazz, tparamsFromClass map (_.tpeHK): _*) val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType - def transform(clonedType: Type): Type = clonedType match { - case MethodType(params, restpe) => - // I assume it was a bug that this was dropping params... [Martin]: No, it wasn't; it's curried. - MethodType(List(thisParam), clonedType) - case NullaryMethodType(restpe) => - MethodType(List(thisParam), restpe) - } - val GenPolyType(tparams, restpe) = origInfo cloneInfo extensionMeth - val selfParamSingletonType = singleType(currentOwner.companionModule.thisType, thisParam) - GenPolyType( - tparams ::: newTypeParams, - transform(restpe) substThisAndSym (clazz, selfParamSingletonType, clazz.typeParams, newTypeParams) - ) - } + val resultType = MethodType(List(thisParam), dropNullaryMethod(methodResult)) + val selfParamType = singleType(currentOwner.companionModule.thisType, thisParam) - private def allParams(tpe: Type): List[Symbol] = tpe match { - case MethodType(params, res) => params ::: allParams(res) - case _ => List() - } + def fixres(tp: Type) = tp substThisAndSym (clazz, selfParamType, clazz.typeParams, tparamsFromClass) + def fixtparam(tp: Type) = tp substSym (clazz.typeParams, tparamsFromClass) + + // We can't substitute symbols on the entire polytype because we + // need to modify the bounds of the cloned type parameters, but we + // don't want to substitute for the cloned type parameters themselves. + val tparams = tparamsFromMethod ::: tparamsFromClass + GenPolyType(tparams map (_ modifyInfo fixtparam), fixres(resultType)) + // For reference, calling fix on the GenPolyType plays out like this: + // error: scala.reflect.internal.Types$TypeError: type arguments [B#7344,A#6966] + // do not conform to method extension$baz#16148's type parameter bounds + // + // And the difference is visible here. See how B is bounded from below by A#16149 + // in both cases, but in the failing case, the other type parameter has turned into + // a different A. (What is that A? It is a clone of the original A created in + // SubstMap during the call to substSym, but I am not clear on all the particulars.) + // + // bad: [B#16154 >: A#16149, A#16155 <: AnyRef#2189]($this#16156: Foo#6965[A#16155])(x#16157: B#16154)List#2457[B#16154] + // good: [B#16151 >: A#16149, A#16149 <: AnyRef#2189]($this#16150: Foo#6965[A#16149])(x#16153: B#16151)List#2457[B#16151] + } override def transform(tree: Tree): Tree = { tree match { case Template(_, _, _) => @@ -167,42 +193,62 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { wrap over other value classes anyway. checkNonCyclic(currentOwner.pos, Set(), currentOwner) */ extensionDefs(currentOwner.companionModule) = new mutable.ListBuffer[Tree] + currentOwner.primaryConstructor.makeNotPrivate(NoSymbol) super.transform(tree) } else if (currentOwner.isStaticOwner) { super.transform(tree) } else tree case DefDef(_, _, tparams, vparamss, _, rhs) if tree.symbol.isMethodWithExtension => - val companion = currentOwner.companionModule - val origMeth = tree.symbol - val extensionName = extensionNames(origMeth).head - val extensionMeth = companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) - .setAnnotations(origMeth.annotations) - companion.info.decls.enter(extensionMeth) - val newInfo = extensionMethInfo(extensionMeth, origMeth.info, currentOwner) + val origMeth = tree.symbol + val origThis = currentOwner + val origTpeParams = tparams.map(_.symbol) ::: origThis.typeParams // method type params ++ class type params + val origParams = vparamss.flatten map (_.symbol) + val companion = origThis.companionModule + + def makeExtensionMethodSymbol = { + val extensionName = extensionNames(origMeth).head + val extensionMeth = ( + companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) + setAnnotations origMeth.annotations + ) + companion.info.decls.enter(extensionMeth) + } + + val extensionMeth = makeExtensionMethodSymbol + val newInfo = extensionMethInfo(extensionMeth, origMeth.info, origThis) extensionMeth setInfo newInfo - log("Value class %s spawns extension method.\n Old: %s\n New: %s".format( - currentOwner, - origMeth.defString, - extensionMeth.defString)) // extensionMeth.defStringSeenAs(origInfo - - def thisParamRef = gen.mkAttributedStableRef(extensionMeth.info.params.head setPos extensionMeth.pos) - val GenPolyType(extensionTpeParams, extensionMono) = extensionMeth.info - val origTpeParams = (tparams map (_.symbol)) ::: currentOwner.typeParams - val extensionBody = rhs + + log(s"Value class $origThis spawns extension method.\n Old: ${origMeth.defString}\n New: ${extensionMeth.defString}") + + val GenPolyType(extensionTpeParams, MethodType(thiz :: Nil, extensionMono)) = newInfo + val extensionParams = allParameters(extensionMono) + val extensionThis = gen.mkAttributedStableRef(thiz setPos extensionMeth.pos) + + val extensionBody = ( + rhs .substituteSymbols(origTpeParams, extensionTpeParams) - .substituteSymbols(vparamss.flatten map (_.symbol), allParams(extensionMono).tail) - .substituteThis(currentOwner, thisParamRef) - .changeOwner((origMeth, extensionMeth)) - extensionDefs(companion) += atPos(tree.pos) { DefDef(extensionMeth, extensionBody) } - val extensionCallPrefix = Apply( - gen.mkTypeApply(gen.mkAttributedRef(companion), extensionMeth, origTpeParams map (_.tpeHK)), - List(This(currentOwner))) - val extensionCall = atOwner(origMeth) { - localTyper.typedPos(rhs.pos) { - gen.mkForwarder(extensionCallPrefix, mmap(vparamss)(_.symbol)) - } - } - deriveDefDef(tree)(_ => extensionCall) + .substituteSymbols(origParams, extensionParams) + .substituteThis(origThis, extensionThis) + .changeOwner(origMeth -> extensionMeth) + ) + + // Record the extension method ( FIXME: because... ? ) + extensionDefs(companion) += atPos(tree.pos)(DefDef(extensionMeth, extensionBody)) + + // These three lines are assembling Foo.bar$extension[T1, T2, ...]($this) + // which leaves the actual argument application for extensionCall. + val sel = Select(gen.mkAttributedRef(companion), extensionMeth) + val targs = origTpeParams map (_.tpeHK) + val callPrefix = gen.mkMethodCall(sel, targs, This(origThis) :: Nil) + + // Apply all the argument lists. + deriveDefDef(tree)(_ => + atOwner(origMeth)( + localTyper.typedPos(rhs.pos)( + gen.mkForwarder(callPrefix, mmap(vparamss)(_.symbol)) + ) + ) + ) case _ => super.transform(tree) } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index f338e390bb..965063a724 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -231,7 +231,17 @@ abstract class UnCurry extends InfoTransform * If `settings.XoldPatmat.value`, also synthesized AbstractPartialFunction subclasses (see synthPartialFunction). * */ - def transformFunction(fun: Function): Tree = + def transformFunction(fun: Function): Tree = { + fun.tpe match { + // can happen when analyzer plugins assign refined types to functions, e.g. + // (() => Int) { def apply(): Int @typeConstraint } + case RefinedType(List(funTp), decls) => + debuglog(s"eliminate refinement from function type ${fun.tpe}") + fun.tpe = funTp + case _ => + () + } + deEta(fun) match { // nullary or parameterless case fun1 if fun1 ne fun => fun1 @@ -239,10 +249,7 @@ abstract class UnCurry extends InfoTransform // only get here when running under -Xoldpatmat synthPartialFunction(fun) case _ => - val parents = ( - if (isFunctionType(fun.tpe)) addSerializable(abstractFunctionForFunctionType(fun.tpe)) - else addSerializable(ObjectClass.tpe, fun.tpe) - ) + val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe)) val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation anonClass setInfo ClassInfoType(parents, newScope, anonClass) @@ -275,6 +282,7 @@ abstract class UnCurry extends InfoTransform } } + } /** Transform a function node (x => body) of type PartialFunction[T, R] where * body = expr match { case P_i if G_i => E_i }_i=1..n diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 78175f393a..b50486306d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -25,6 +25,7 @@ trait Analyzer extends AnyRef with TypeDiagnostics with ContextErrors with StdAttachments + with AnalyzerPlugins { val global : Global import global._ diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala new file mode 100644 index 0000000000..28f620dbb5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -0,0 +1,225 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package typechecker + +/** + * @author Lukas Rytz + * @version 1.0 + */ +trait AnalyzerPlugins { self: Analyzer => + import global._ + + + trait AnalyzerPlugin { + /** + * Selectively activate this analyzer plugin, e.g. according to the compiler phase. + * + * Note that the current phase can differ from the global compiler phase (look for `enteringPhase` + * invocations in the compiler). For instance, lazy types created by the UnPickler are completed + * at the phase in which their symbol is created. Observations show that this can even be the + * parser phase. Since symbol completion can trigger subtyping, typing etc, your plugin might + * need to be active also in phases other than namer and typer. + * + * Typically, this method can be implemented as + * + * global.phase.id < global.currentRun.picklerPhase.id + */ + def isActive(): Boolean = true + + /** + * Let analyzer plugins change the expected type before type checking a tree. + */ + def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = pt + + /** + * Let analyzer plugins modify the type that has been computed for a tree. + * + * @param tpe The type inferred by the type checker, initially (for first plugin) `tree.tpe` + * @param typer The yper that type checked `tree` + * @param tree The type-checked tree + * @param mode Mode that was used for typing `tree` + * @param pt Expected type that was used for typing `tree` + */ + def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = tpe + + /** + * Let analyzer plugins change the types assigned to definitions. For definitions that have + * an annotated type, the assigned type is obtained by typing that type tree. Otherwise, the + * type is inferred by typing the definition's righthand side. + * + * In order to know if the type was inferred, you can query the `wasEmpty` field in the `tpt` + * TypeTree of the definition (for DefDef and ValDef). + * + * (*) If the type of a method or value is inferred, the type-checked tree is stored in the + * `analyzer.transformed` hash map, indexed by the definition's rhs tree. + * + * NOTE: Invoking the type checker can lead to cyclic reference errors. For instance, if this + * method is called from the type completer of a recursive method, type checking the mehtod + * rhs will invoke the same completer again. It might be possible to avoid this situation by + * assigning `tpe` to `defTree.symbol` (untested) - the final type computed by this method + * will then be assigned to the definition's symbol by monoTypeCompleter (in Namers). + * + * The hooks into `typeSig` allow analyzer plugins to add annotations to (or change the types + * of) definition symbols. This cannot not be achieved by using `pluginsTyped`: this method + * is only called during type checking, so changing the type of a symbol at this point is too + * late: references to the symbol might already be typed and therefore obtain the the original + * type assigned during naming. + * + * @param defTree is the definition for which the type was computed. The different cases are + * outlined below. Note that this type is untyped (for methods and values with inferred type, + * the typed rhs trees are available in analyzer.transformed). + * + * Case defTree: Template + * - tpe : A ClassInfoType for the template + * - typer: The typer for template members, i.e. expressions and definitions of defTree.body + * - pt : WildcardType + * - the class symbol is accessible through typer.context.owner + * + * Case defTree: ClassDef + * - tpe : A ClassInfoType, or a PolyType(params, ClassInfoType) for polymorphic classes. + * The class type is the one computed by templateSig, i.e. through the above case + * - typer: The typer for the class. Note that this typer has a different context than the + * typer for the template. + * - pt : WildcardType + * + * Case defTree: ModuleDef + * - tpe : A ClassInfoType computed by templateSig + * - typer: The typer for the module. context.owner of this typer is the module class symbol + * - pt : WildcardType + * + * Case defTree: DefDef + * - tpe : The type of the method (MethodType, PolyType or NullaryMethodType). (*) + * - typer: The typer the rhs of this method + * - pt : If tpt.isEmpty, either the result type from the overridden method, or WildcardType. + * Otherwise the type obtained from typing tpt. + * - Note that for constructors, pt is the class type which the constructor creates. To type + * check the rhs of the constructor however, the expected type has to be WildcardType (see + * Typers.typedDefDef) + * + * Case defTree: ValDef + * - tpe : The type of this value. (*) + * - typer: The typer for the rhs of this value + * - pt : If tpt.isEmpty, WildcardType. Otherwise the type obtained from typing tpt. + * - Note that pluginsTypeSig might be called multiple times for the same ValDef since it is + * used to compute the types of the accessor methods (see `pluginsTypeSigAccessor`) + * + * Case defTree: TypeDef + * - tpe : The type obtained from typing rhs (PolyType if the TypeDef defines a polymorphic type) + * - typer: The typer for the rhs of this type + * - pt : WildcardType + */ + def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = tpe + + /** + * Modify the types of field accessors. The namer phase creates method types for getters and + * setters based on the type of the corresponding field. + * + * Note: in order to compute the method type of an accessor, the namer calls `typeSig` on the + * `ValDef` tree of the corresponding field. This implies that the `pluginsTypeSig` method + * is potentially called multiple times for the same ValDef tree. + * + * @param tpe The method type created by the namer for the accessor + * @param typer The typer for the ValDef (not for the rhs) + * @param tree The ValDef corresponding to the accessor + * @param sym The accessor method symbol (getter, setter, beanGetter or beanSetter) + */ + def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = tpe + + /** + * Decide whether this analyzer plugin can adapt a tree that has an annotated type to the + * given type tp, taking into account the given mode (see method adapt in trait Typers). + */ + def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = false + + /** + * Adapt a tree that has an annotated type to the given type tp, taking into account the given + * mode (see method adapt in trait Typers). + * + * An implementation cannot rely on canAdaptAnnotations being called before. If the implementing + * class cannot do the adapting, it should return the tree unchanged. + */ + def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = tree + + /** + * Modify the type of a return expression. By default, return expressions have type + * NothingClass.tpe. + * + * @param tpe The type of the return expression + * @param typer The typer that was used for typing the return tree + * @param tree The typed return expression tree + * @param pt The return type of the enclosing method + */ + def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = tpe + } + + + + /** A list of registered analyzer plugins */ + private var analyzerPlugins: List[AnalyzerPlugin] = Nil + + /** Registers a new analyzer plugin */ + def addAnalyzerPlugin(plugin: AnalyzerPlugin) { + if (!analyzerPlugins.contains(plugin)) + analyzerPlugins = plugin :: analyzerPlugins + } + + + /** @see AnalyzerPlugin.pluginsPt */ + def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = + if (analyzerPlugins.isEmpty) pt + else analyzerPlugins.foldLeft(pt)((pt, plugin) => + if (!plugin.isActive()) pt else plugin.pluginsPt(pt, typer, tree, mode)) + + /** @see AnalyzerPlugin.pluginsTyped */ + def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = { + // support deprecated methods in annotation checkers + val annotCheckersTpe = addAnnotations(tree, tpe) + if (analyzerPlugins.isEmpty) annotCheckersTpe + else analyzerPlugins.foldLeft(annotCheckersTpe)((tpe, plugin) => + if (!plugin.isActive()) tpe else plugin.pluginsTyped(tpe, typer, tree, mode, pt)) + } + + /** @see AnalyzerPlugin.pluginsTypeSig */ + def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = + if (analyzerPlugins.isEmpty) tpe + else analyzerPlugins.foldLeft(tpe)((tpe, plugin) => + if (!plugin.isActive()) tpe else plugin.pluginsTypeSig(tpe, typer, defTree, pt)) + + /** @see AnalyzerPlugin.pluginsTypeSigAccessor */ + def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = + if (analyzerPlugins.isEmpty) tpe + else analyzerPlugins.foldLeft(tpe)((tpe, plugin) => + if (!plugin.isActive()) tpe else plugin.pluginsTypeSigAccessor(tpe, typer, tree, sym)) + + /** @see AnalyzerPlugin.canAdaptAnnotations */ + def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = { + // support deprecated methods in annotation checkers + val annotCheckersExists = global.canAdaptAnnotations(tree, mode, pt) + annotCheckersExists || { + if (analyzerPlugins.isEmpty) false + else analyzerPlugins.exists(plugin => + plugin.isActive() && plugin.canAdaptAnnotations(tree, typer, mode, pt)) + } + } + + /** @see AnalyzerPlugin.adaptAnnotations */ + def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = { + // support deprecated methods in annotation checkers + val annotCheckersTree = global.adaptAnnotations(tree, mode, pt) + if (analyzerPlugins.isEmpty) annotCheckersTree + else analyzerPlugins.foldLeft(annotCheckersTree)((tree, plugin) => + if (!plugin.isActive()) tree else plugin.adaptAnnotations(tree, typer, mode, pt)) + } + + /** @see AnalyzerPlugin.pluginsTypedReturn */ + def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = { + val annotCheckersType = adaptTypeOfReturn(tree.expr, pt, tpe) + if (analyzerPlugins.isEmpty) annotCheckersType + else analyzerPlugins.foldLeft(annotCheckersType)((tpe, plugin) => + if (!plugin.isActive()) tpe else plugin.pluginsTypedReturn(tpe, typer, tree, pt)) + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 2e5d61cc6b..fbf23968f0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -760,10 +760,14 @@ trait ContextErrors { else " of " + expanded.getClass )) - def MacroImplementationNotFoundError(expandee: Tree) = - macroExpansionError(expandee, + def MacroImplementationNotFoundError(expandee: Tree) = { + val message = "macro implementation not found: " + expandee.symbol.name + " " + - "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)") + "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)" + + (if (forScaladoc) ". When generating scaladocs for multiple projects at once, consider using -Ymacro-no-expand to disable macro expansions altogether." + else "") + macroExpansionError(expandee, message) + } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index af2aeefecd..620665126e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -35,7 +35,7 @@ trait Contexts { self: Analyzer => val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil } - private val startContext = { + private lazy val startContext = { NoContext.make( Template(List(), emptyValDef, List()) setSymbol global.NoSymbol setType global.NoType, rootMirror.RootClass, @@ -342,6 +342,16 @@ trait Contexts { self: Analyzer => c } + /** + * A context for typing constructor parameter ValDefs, super or self invocation arguments and default getters + * of constructors. These expressions need to be type checked in a scope outside the class, cf. spec 5.3.1. + * + * This method is called by namer / typer where `this` is the context for the constructor DefDef. The + * owner of the resulting (new) context is the outer context for the Template, i.e. the context for the + * ClassDef. This means that class type parameters will be in scope. The value parameters of the current + * constructor are also entered into the new constructor scope. Members of the class however will not be + * accessible. + */ def makeConstructorContext = { var baseContext = enclClass.outer while (baseContext.tree.isInstanceOf[Template]) @@ -361,6 +371,8 @@ trait Contexts { self: Analyzer => enterLocalElems(c.scope.elems) } } + // Enter the scope elements of this (the scope for the constructor DefDef) into the new constructor scope. + // Concretely, this will enter the value parameters of constructor. enterElems(this) argContext } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 581f9f3bfa..74078a4ed3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -257,8 +257,8 @@ trait Infer extends Checkable { tp1 // @MAT aliases already handled by subtyping } - private val stdErrorClass = rootMirror.RootClass.newErrorClass(tpnme.ERROR) - private val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) + private lazy val stdErrorClass = rootMirror.RootClass.newErrorClass(tpnme.ERROR) + private lazy val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) /** The context-dependent inferencer part */ class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable { @@ -411,8 +411,19 @@ trait Infer extends Checkable { /** Like weakly compatible but don't apply any implicit conversions yet. * Used when comparing the result type of a method with its prototype. + * * [Martin] I think Infer is also created by Erasure, with the default * implementation of isCoercible + * [Paulp] (Assuming the above must refer to my comment on isCoercible) + * Nope, I examined every occurrence of Inferencer in trunk. It + * appears twice as a self-type, once at its definition, and once + * where it is instantiated in Typers. There are no others. + * + % ack -A0 -B0 --no-filename '\bInferencer\b' src + self: Inferencer => + self: Inferencer => + class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable { + val infer = new Inferencer(context0) { */ def isConservativelyCompatible(tp: Type, pt: Type): Boolean = context.withImplicitsDisabled(isWeaklyCompatible(tp, pt)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 0ba76643ca..245656e2d7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -684,6 +684,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * the expandee with an error marker set if there has been an error */ def macroExpand(typer: Typer, expandee: Tree, mode: Int = EXPRmode, pt: Type = WildcardType): Tree = { + if (settings.Ymacronoexpand.value) return expandee // SI-6812 val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) try { diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index acc4f7ff67..99557d1527 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -235,8 +235,8 @@ trait MethodSynthesis { context.unit.synthetics get meth match { case Some(mdef) => context.unit.synthetics -= meth - meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, false) - cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, true) + meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, keepClean = false) + cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, keepClean = true) List(cd, mdef) case _ => // Shouldn't happen, but let's give ourselves a reasonable error when it does @@ -329,6 +329,7 @@ trait MethodSynthesis { */ def category: Symbol + /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter) final def fieldSelection = Select(This(enclClass), basisSym) final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil) @@ -389,7 +390,7 @@ trait MethodSynthesis { result } def derivedTree: DefDef = - factoryMeth(mods & flagsMask | flagsExtra, name, tree, symbolic = false) + factoryMeth(mods & flagsMask | flagsExtra, name, tree) def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC def flagsMask: Long = AccessFlags def name: TermName = tree.name.toTermName diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 98b6264051..c728185d4e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -65,7 +65,18 @@ trait Namers extends MethodSynthesis { case ModuleDef(_, _, _) => tree.symbol.moduleClass case _ => tree.symbol } - newNamer(context.makeNewScope(tree, sym)) + def isConstrParam(vd: ValDef) = { + (sym hasFlag PARAM | PRESUPER) && + !vd.mods.isJavaDefined && + sym.owner.isConstructor + } + val ownerCtx = tree match { + case vd: ValDef if isConstrParam(vd) => + context.makeConstructorContext + case _ => + context + } + newNamer(ownerCtx.makeNewScope(tree, sym)) } def createInnerNamer() = { newNamer(context.make(context.tree, owner, newScope)) @@ -423,6 +434,7 @@ trait Namers extends MethodSynthesis { def enterSyms(trees: List[Tree]): Namer = { trees.foldLeft(this: Namer) { (namer, t) => val ctx = namer enterSym t + // for Import trees, enterSym returns a changed context, so we need a new namer if (ctx eq namer.context) namer else newNamer(ctx) } @@ -521,20 +533,19 @@ trait Namers extends MethodSynthesis { noDuplicates(selectors map (_.rename), AppearsTwice) } - def enterCopyMethod(copyDefDef: Tree, tparams: List[TypeDef]): Symbol = { - val sym = copyDefDef.symbol - val lazyType = completerOf(copyDefDef, tparams) + def enterCopyMethod(copyDef: DefDef): Symbol = { + val sym = copyDef.symbol + val lazyType = completerOf(copyDef) /** Assign the types of the class parameters to the parameters of the * copy method. See comment in `Unapplies.caseClassCopyMeth` */ def assignParamTypes() { val clazz = sym.owner val constructorType = clazz.primaryConstructor.tpe - val subst = new SubstSymMap(clazz.typeParams, tparams map (_.symbol)) + val subst = new SubstSymMap(clazz.typeParams, copyDef.tparams map (_.symbol)) val classParamss = constructorType.paramss - val DefDef(_, _, _, copyParamss, _, _) = copyDefDef - map2(copyParamss, classParamss)((copyParams, classParams) => + map2(copyDef.vparamss, classParamss)((copyParams, classParams) => map2(copyParams, classParams)((copyP, classP) => copyP.tpt setType subst(classP.tpe) ) @@ -542,24 +553,28 @@ trait Namers extends MethodSynthesis { } sym setInfo { - mkTypeCompleter(copyDefDef) { sym => + mkTypeCompleter(copyDef) { sym => assignParamTypes() lazyType complete sym } } } - def completerOf(tree: Tree): TypeCompleter = completerOf(tree, treeInfo.typeParameters(tree)) - def completerOf(tree: Tree, tparams: List[TypeDef]): TypeCompleter = { + + def completerOf(tree: Tree): TypeCompleter = { val mono = namerOf(tree.symbol) monoTypeCompleter tree + val tparams = treeInfo.typeParameters(tree) if (tparams.isEmpty) mono else { - //@M! TypeDef's type params are handled differently - //@M e.g., in [A[x <: B], B], A and B are entered first as both are in scope in the definition of x - //@M x is only in scope in `A[x <: B]' + /* @M! TypeDef's type params are handled differently, e.g., in `type T[A[x <: B], B]`, A and B are entered + * first as both are in scope in the definition of x. x is only in scope in `A[x <: B]`. + * No symbols are created for the abstract type's params at this point, i.e. the following assertion holds: + * !tree.symbol.isAbstractType || { tparams.forall(_.symbol == NoSymbol) + * (tested with the above example, `trait C { type T[A[X <: B], B] }`). See also comment in PolyTypeCompleter. + */ if (!tree.symbol.isAbstractType) //@M TODO: change to isTypeMember ? createNamer(tree) enterSyms tparams - new PolyTypeCompleter(tparams, mono, tree, context) //@M + new PolyTypeCompleter(tparams, mono, context) //@M } } @@ -621,9 +636,9 @@ trait Namers extends MethodSynthesis { val sym = assignAndEnterSymbol(tree) setFlag bridgeFlag if (name == nme.copy && sym.isSynthetic) - enterCopyMethod(tree, tparams) + enterCopyMethod(tree) else - sym setInfo completerOf(tree, tparams) + sym setInfo completerOf(tree) } def enterClassDef(tree: ClassDef) { @@ -736,13 +751,13 @@ trait Namers extends MethodSynthesis { } } - def accessorTypeCompleter(tree: ValDef, isSetter: Boolean = false) = mkTypeCompleter(tree) { sym => + /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ + def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym => logAndValidate(sym) { sym setInfo { - if (isSetter) - MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitClass.tpe) - else - NullaryMethodType(typeSig(tree)) + val tp = if (isSetter) MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitClass.tpe) + else NullaryMethodType(typeSig(tree)) + pluginsTypeSigAccessor(tp, typer, tree, sym) } } } @@ -805,17 +820,12 @@ trait Namers extends MethodSynthesis { * assigns the type to the tpt's node. Returns the type. */ private def assignTypeToTree(tree: ValOrDefDef, defnTyper: Typer, pt: Type): Type = { - // compute result type from rhs - val typedBody = + val rhsTpe = if (tree.symbol.isTermMacro) defnTyper.computeMacroDefType(tree, pt) else defnTyper.computeType(tree.rhs, pt) - val typedDefn = widenIfNecessary(tree.symbol, typedBody, pt) - assignTypeToTree(tree, typedDefn) - } - - private def assignTypeToTree(tree: ValOrDefDef, tpe: Type): Type = { - tree.tpt defineType tpe setPos tree.pos.focus + val defnTpe = widenIfNecessary(tree.symbol, rhsTpe, pt) + tree.tpt defineType defnTpe setPos tree.pos.focus tree.tpt.tpe } @@ -892,163 +902,257 @@ trait Namers extends MethodSynthesis { for (cda <- module.attachments.get[ConstructorDefaultsAttachment]) { cda.companionModuleClassNamer = templateNamer } - ClassInfoType(parents, decls, clazz) + val classTp = ClassInfoType(parents, decls, clazz) + pluginsTypeSig(classTp, templateNamer.typer, templ, WildcardType) } - private def classSig(tparams: List[TypeDef], impl: Template): Type = { + private def classSig(cdef: ClassDef): Type = { + val clazz = cdef.symbol + val ClassDef(_, _, tparams, impl) = cdef val tparams0 = typer.reenterTypeParams(tparams) val resultType = templateSig(impl) - GenPolyType(tparams0, resultType) + val res = GenPolyType(tparams0, resultType) + val pluginsTp = pluginsTypeSig(res, typer, cdef, WildcardType) + + // Already assign the type to the class symbol (monoTypeCompleter will do it again). + // Allows isDerivedValueClass to look at the info. + clazz setInfo pluginsTp + if (clazz.isDerivedValueClass) { + log("Ensuring companion for derived value class " + cdef.name + " at " + cdef.pos.show) + clazz setFlag FINAL + // Don't force the owner's info lest we create cycles as in SI-6357. + enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef) + } + pluginsTp } - private def methodSig(ddef: DefDef, mods: Modifiers, tparams: List[TypeDef], - vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): Type = { - val meth = owner - val clazz = meth.owner - // enters the skolemized version into scope, returns the deSkolemized symbols - val tparamSyms = typer.reenterTypeParams(tparams) - // since the skolemized tparams are in scope, the TypeRefs in vparamSymss refer to skolemized tparams - var vparamSymss = enterValueParams(vparamss) + private def moduleSig(mdef: ModuleDef): Type = { + val moduleSym = mdef.symbol + // The info of both the module and the moduleClass symbols need to be assigned. monoTypeCompleter assigns + // the result of typeSig to the module symbol. The module class info is assigned here as a side-effect. + val result = templateSig(mdef.impl) + val pluginsTp = pluginsTypeSig(result, typer, mdef, WildcardType) + // Assign the moduleClass info (templateSig returns a ClassInfoType) + val clazz = moduleSym.moduleClass + clazz setInfo pluginsTp + // clazz.tpe returns a `ModuleTypeRef(clazz)`, a typeRef that links to the module class `clazz` + // (clazz.info would the ClassInfoType, which is not what should be assigned to the module symbol) + clazz.tpe + } + + /** + * The method type for `ddef`. + * + * If a PolyType(tparams, restp) is returned, `tparams` are the external symbols (not type skolems), + * i.e. instances of AbstractTypeSymbol. All references in `restp` to the type parameters are TypeRefs + * to these non-skolems. + * + * For type-checking the rhs (in case the result type is inferred), the type skolems of the type parameters + * are entered in scope. Equally, the parameter symbols entered into scope have types which refer to those + * skolems: when type-checking the rhs, references to parameters need to have types that refer to the skolems. + * In summary, typing an rhs happens with respect to the skolems. + * + * This means that the method's result type computed by the typer refers to skolems. In order to put it + * into the method type (the result of methodSig), typeRefs to skolems have to be replaced by references + * to the non-skolems. + */ + private def methodSig(ddef: DefDef): Type = { // DEPMETTODO: do we need to skolemize value parameter symbols? - if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { - tpt defineType context.enclClass.owner.tpe - tpt setPos meth.pos.focus - } - var resultPt = if (tpt.isEmpty) WildcardType else typer.typedType(tpt).tpe - val site = clazz.thisType - /** Called for all value parameter lists, right to left - * @param vparams the symbols of one parameter list - * @param restpe the result type (possibly a MethodType) + val DefDef(_, _, tparams, vparamss, tpt, _) = ddef + + val meth = owner + val methOwner = meth.owner + val site = methOwner.thisType + + /* tparams already have symbols (created in enterDefDef/completerOf), namely the skolemized ones (created + * by the PolyTypeCompleter constructor, and assigned to tparams). reenterTypeParams enters the type skolems + * into scope and returns the non-skolems. */ - def makeMethodType(vparams: List[Symbol], restpe: Type) = { - // TODODEPMET: check that we actually don't need to do anything here - // new dependent method types: probably OK already, since 'enterValueParams' above - // enters them in scope, and all have a lazy type. so they may depend on other params. but: need to - // check that params only depend on ones in earlier sections, not the same. (done by checkDependencies, - // so re-use / adapt that) - if (owner.isJavaDefined) - // TODODEPMET necessary?? new dependent types: replace symbols in restpe with the ones in vparams - JavaMethodType(vparams map (p => p setInfo objToAny(p.tpe)), restpe) - else - MethodType(vparams, restpe) - } + val tparamSyms = typer.reenterTypeParams(tparams) + + val tparamSkolems = tparams.map(_.symbol) + + /* since the skolemized tparams are in scope, the TypeRefs in types of vparamSymss refer to the type skolems + * note that for parameters with missing types, `methodSig` reassigns types of these symbols (the parameter + * types from the overridden method). + */ + var vparamSymss = enterValueParams(vparamss) + + /** + * Creates a method type using tparamSyms and vparamsSymss as argument symbols and `respte` as result type. + * All typeRefs to type skolems are replaced by references to the corresponding non-skolem type parameter, + * so the resulting type is a valid external method type, it does not contain (references to) skolems. + */ def thisMethodType(restpe: Type) = { val checkDependencies = new DependentTypeChecker(context)(this) checkDependencies check vparamSymss // DEPMETTODO: check not needed when they become on by default checkDependencies(restpe) - GenPolyType( + val makeMethodType = (vparams: List[Symbol], restpe: Type) => { + // TODODEPMET: check that we actually don't need to do anything here + // new dependent method types: probably OK already, since 'enterValueParams' above + // enters them in scope, and all have a lazy type. so they may depend on other params. but: need to + // check that params only depend on ones in earlier sections, not the same. (done by checkDependencies, + // so re-use / adapt that) + if (meth.isJavaDefined) + // TODODEPMET necessary?? new dependent types: replace symbols in restpe with the ones in vparams + JavaMethodType(vparams map (p => p setInfo objToAny(p.tpe)), restpe) + else + MethodType(vparams, restpe) + } + + + val res = GenPolyType( tparamSyms, // deSkolemized symbols -- TODO: check that their infos don't refer to method args? if (vparamSymss.isEmpty) NullaryMethodType(restpe) // vparamss refer (if they do) to skolemized tparams else (vparamSymss :\ restpe) (makeMethodType) ) + res.substSym(tparamSkolems, tparamSyms) } - def transformedResult = - thisMethodType(resultPt).substSym(tparams map (_.symbol), tparamSyms) + /** + * Creates a schematic method type which has WildcardTypes for non specified + * return or parameter types. For instance, in `def f[T](a: T, b) = ...`, the + * type schema is + * + * PolyType(T, MethodType(List(a: T, b: WildcardType), WildcardType)) + * + * where T are non-skolems. + */ + def methodTypeSchema(resTp: Type) = { + // for all params without type set WildcaradType + mforeach(vparamss)(v => if (v.tpt.isEmpty) v.symbol setInfo WildcardType) + thisMethodType(resTp) + } - // luc: added .substSym from skolemized to deSkolemized - // site.memberType(sym): PolyType(tparams, MethodType(..., ...)) - // ==> all references to tparams are deSkolemized - // thisMethodType: tparams in PolyType are deSkolemized, the references in the MethodTypes are skolemized. - // ==> the two didn't match - // - // for instance, B.foo would not override A.foo, and the default on parameter b would not be inherited - // class A { def foo[T](a: T)(b: T = a) = a } - // class B extends A { override def foo[U](a: U)(b: U) = b } - def overriddenSymbol = - intersectionType(clazz.info.parents).nonPrivateMember(meth.name).filter { sym => - sym != NoSymbol && (site.memberType(sym) matches transformedResult) + def overriddenSymbol(resTp: Type) = { + intersectionType(methOwner.info.parents).nonPrivateMember(meth.name).filter { sym => + sym != NoSymbol && (site.memberType(sym) matches methodTypeSchema(resTp)) } - // TODO: see whether this or something similar would work instead. - // + } + // TODO: see whether this or something similar would work instead: // def overriddenSymbol = meth.nextOverriddenSymbol - // fill in result type and parameter types from overridden symbol if there is a unique one. - if (clazz.isClass && (tpt.isEmpty || mexists(vparamss)(_.tpt.isEmpty))) { - // try to complete from matching definition in base type - mforeach(vparamss)(v => if (v.tpt.isEmpty) v.symbol setInfo WildcardType) - val overridden = overriddenSymbol - if (overridden != NoSymbol && !overridden.isOverloaded) { - overridden.cookJavaRawInfo() // #3404 xform java rawtypes into existentials - resultPt = site.memberType(overridden) match { - case PolyType(tparams, rt) => rt.substSym(tparams, tparamSyms) - case mt => mt - } + /** + * If `meth` doesn't have an explicit return type, extracts the return type from the method + * overridden by `meth` (if there's an unique one). This type is lateron used as the expected + * type for computing the type of the rhs. The resulting type references type skolems for + * type parameters (consistent with the result of `typer.typedType(tpt).tpe`). + * + * As a first side effect, this method assigns a MethodType constructed using this + * return type to `meth`. This allows omitting the result type for recursive methods. + * + * As another side effect, this method also assigns paramter types from the overridden + * method to parameters of `meth` that have missing types (the parser accepts missing + * parameter types under -Yinfer-argument-types). + */ + def typesFromOverridden(methResTp: Type): Type = { + val overridden = overriddenSymbol(methResTp) + if (overridden == NoSymbol || overridden.isOverloaded) { + methResTp + } else { + overridden.cookJavaRawInfo() // #3404 xform java rawtypes into existentials + var overriddenTp = site.memberType(overridden) match { + case PolyType(tparams, rt) => rt.substSym(tparams, tparamSkolems) + case mt => mt + } for (vparams <- vparamss) { - var pps = resultPt.params + var overriddenParams = overriddenTp.params for (vparam <- vparams) { if (vparam.tpt.isEmpty) { - val paramtpe = pps.head.tpe - vparam.symbol setInfo paramtpe - vparam.tpt defineType paramtpe setPos vparam.pos.focus + val overriddenParamTp = overriddenParams.head.tpe + // references to type parameteres in overriddenParamTp link to the type skolems, so the + // assigned type is consistent with the other / existing parameter types in vparamSymss. + vparam.symbol setInfo overriddenParamTp + vparam.tpt defineType overriddenParamTp setPos vparam.pos.focus } - pps = pps.tail + overriddenParams = overriddenParams.tail } - resultPt = resultPt.resultType + overriddenTp = overriddenTp.resultType } - resultPt match { - case NullaryMethodType(rtpe) => resultPt = rtpe - case MethodType(List(), rtpe) => resultPt = rtpe + + overriddenTp match { + case NullaryMethodType(rtpe) => overriddenTp = rtpe + case MethodType(List(), rtpe) => overriddenTp = rtpe case _ => } + if (tpt.isEmpty) { // provisionally assign `meth` a method type with inherited result type // that way, we can leave out the result type even if method is recursive. - meth setInfo thisMethodType(resultPt) + meth setInfo thisMethodType(overriddenTp) + overriddenTp + } else { + methResTp } } } - // Add a () parameter section if this overrides some method with () parameters. - if (clazz.isClass && vparamss.isEmpty && overriddenSymbol.alternatives.exists( - _.info.isInstanceOf[MethodType])) { + + if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { + tpt defineType context.enclClass.owner.tpe + tpt setPos meth.pos.focus + } + + val methResTp = if (tpt.isEmpty) WildcardType else typer.typedType(tpt).tpe + val resTpFromOverride = if (methOwner.isClass && (tpt.isEmpty || mexists(vparamss)(_.tpt.isEmpty))) { + typesFromOverridden(methResTp) + } else { + methResTp + } + + // Add a () parameter section if this overrides some method with () parameters + if (methOwner.isClass && vparamss.isEmpty && + overriddenSymbol(methResTp).alternatives.exists(_.info.isInstanceOf[MethodType])) { vparamSymss = ListOfNil } + + // issue an error for missing parameter types mforeach(vparamss) { vparam => if (vparam.tpt.isEmpty) { MissingParameterOrValTypeError(vparam) vparam.tpt defineType ErrorType } } - addDefaultGetters(meth, vparamss, tparams, overriddenSymbol) + + addDefaultGetters(meth, vparamss, tparams, overriddenSymbol(methResTp)) // fast track macros, i.e. macros defined inside the compiler, are hardcoded // hence we make use of that and let them have whatever right-hand side they need // (either "macro ???" as they used to or just "???" to maximally simplify their compilation) - if (fastTrack contains ddef.symbol) ddef.symbol setFlag MACRO + if (fastTrack contains meth) meth setFlag MACRO // macro defs need to be typechecked in advance // because @macroImpl annotation only gets assigned during typechecking // otherwise macro defs wouldn't be able to robustly coexist with their clients // because a client could be typechecked before a macro def that it uses - if (ddef.symbol.isTermMacro) { - val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol)) - typer.computeMacroDefType(ddef, pt) + if (meth.isTermMacro) { + typer.computeMacroDefType(ddef, resTpFromOverride) } - thisMethodType({ + val res = thisMethodType({ val rt = ( if (!tpt.isEmpty) { - typer.typedType(tpt).tpe + methResTp } else { - // replace deSkolemized symbols with skolemized ones - // (for resultPt computed by looking at overridden symbol, right?) - val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol)) - assignTypeToTree(ddef, typer, pt) - } - ) + // return type is inferred, we don't just use resTpFromOverride. Here, C.f has type String: + // trait T { def f: Object }; class C <: T { def f = "" } + // using resTpFromOverride as expected type allows for the following (C.f has type A): + // trait T { def f: A }; class C <: T { implicit def b2a(t: B): A = ???; def f = new B } + assignTypeToTree(ddef, typer, resTpFromOverride) + }) // #2382: return type of default getters are always @uncheckedVariance if (meth.hasDefault) rt.withAnnotation(AnnotationInfo(uncheckedVarianceClass.tpe, List(), List())) else rt }) + pluginsTypeSig(res, typer, ddef, methResTp) } /** @@ -1060,9 +1164,9 @@ trait Namers extends MethodSynthesis { * flag. */ private def addDefaultGetters(meth: Symbol, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) { - val clazz = meth.owner + val methOwner = meth.owner val isConstr = meth.isConstructor - val overridden = if (isConstr || !clazz.isClass) NoSymbol else overriddenSymbol + val overridden = if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol val overrides = overridden != NoSymbol && !overridden.isOverloaded // value parameters of the base class (whose defaults might be overridden) var baseParamss = (vparamss, overridden.tpe.paramss) match { @@ -1112,7 +1216,7 @@ trait Namers extends MethodSynthesis { val parentNamer = if (isConstr) { val (cdef, nmr) = moduleNamer.getOrElse { - val module = companionSymbolOf(clazz, context) + val module = companionSymbolOf(methOwner, context) module.initialize // call type completer (typedTemplate), adds the // module's templateNamer to classAndNamerOfModule module.attachments.get[ConstructorDefaultsAttachment] match { @@ -1158,7 +1262,7 @@ trait Namers extends MethodSynthesis { name, deftParams, defvParamss, defTpt, defRhs) } if (!isConstr) - clazz.resetFlag(INTERFACE) // there's a concrete member now + methOwner.resetFlag(INTERFACE) // there's a concrete member now val default = parentNamer.enterSyntheticSym(defaultTree) if (forInteractive && default.owner.isTerm) { // save the default getters as attachments in the method symbol. if compiling the @@ -1183,15 +1287,31 @@ trait Namers extends MethodSynthesis { } } + private def valDefSig(vdef: ValDef) = { + val ValDef(_, _, tpt, rhs) = vdef + val result = if (tpt.isEmpty) { + if (rhs.isEmpty) { + MissingParameterOrValTypeError(tpt) + ErrorType + } + else assignTypeToTree(vdef, typer, WildcardType) + } else { + typer.typedType(tpt).tpe + } + pluginsTypeSig(result, typer, vdef, if (tpt.isEmpty) WildcardType else result) + + } + //@M! an abstract type definition (abstract type member/type parameter) // may take type parameters, which are in scope in its bounds - private def typeDefSig(tpsym: Symbol, tparams: List[TypeDef], rhs: Tree) = { + private def typeDefSig(tdef: TypeDef) = { + val TypeDef(_, _, tparams, rhs) = tdef // log("typeDefSig(" + tpsym + ", " + tparams + ")") val tparamSyms = typer.reenterTypeParams(tparams) //@M make tparams available in scope (just for this abstypedef) val tp = typer.typedType(rhs).tpe match { case TypeBounds(lt, rt) if (lt.isError || rt.isError) => TypeBounds.empty - case tp @ TypeBounds(lt, rt) if (tpsym hasFlag JAVA) => + case tp @ TypeBounds(lt, rt) if (tdef.symbol hasFlag JAVA) => TypeBounds(lt, objToAny(rt)) case tp => tp @@ -1213,9 +1333,32 @@ trait Namers extends MethodSynthesis { // However, separate compilation requires the symbol info to be // loaded to do this check, but loading the info will probably // lead to spurious cyclic errors. So omit the check. - GenPolyType(tparamSyms, tp) + val res = GenPolyType(tparamSyms, tp) + pluginsTypeSig(res, typer, tdef, WildcardType) } + private def importSig(imp: Import) = { + val Import(expr, selectors) = imp + val expr1 = typer.typedQualifier(expr) + typer checkStable expr1 + if (expr1.symbol != null && expr1.symbol.isRootPackage) + RootImportError(imp) + + if (expr1.isErrorTyped) + ErrorType + else { + val newImport = treeCopy.Import(imp, expr1, selectors).asInstanceOf[Import] + checkSelectors(newImport) + transformed(imp) = newImport + // copy symbol and type attributes back into old expression + // so that the structure builder will find it. + expr.symbol = expr1.symbol + expr.tpe = expr1.tpe + ImportType(expr1) + } + } + + /** Given a case class * case class C[Ts] (ps: Us) * Add the following methods to toScope: @@ -1239,6 +1382,11 @@ trait Namers extends MethodSynthesis { caseClassCopyMeth(cdef) foreach namer.enterSyntheticSym } + /** + * TypeSig is invoked by monoTypeCompleters. It returns the type of a definition which + * is then assigned to the corresponding symbol (typeSig itself does not need to assign + * the type to the symbol, but it can if necessary). + */ def typeSig(tree: Tree): Type = { // log("typeSig " + tree) /** For definitions, transform Annotation trees to AnnotationInfos, assign @@ -1271,84 +1419,33 @@ trait Namers extends MethodSynthesis { } val sym: Symbol = tree.symbol - // @Lukas: I am not sure this is the right way to do things. - // We used to only decorate the module class with annotations, which is - // clearly wrong. Now we decorate both the class and the object. - // But maybe some annotations are only meant for one of these but not for the other? - // - // TODO: meta-annotations to indicate class vs. object. + + // TODO: meta-annotations to indicate where module annotations should go (module vs moduleClass) annotate(sym) if (sym.isModule) annotate(sym.moduleClass) def getSig = tree match { - case cdef @ ClassDef(_, name, tparams, impl) => - val clazz = tree.symbol - val result = createNamer(tree).classSig(tparams, impl) - clazz setInfo result - if (clazz.isDerivedValueClass) { - log("Ensuring companion for derived value class " + name + " at " + cdef.pos.show) - clazz setFlag FINAL - // Don't force the owner's info lest we create cycles as in SI-6357. - enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef) - } - result - - case ModuleDef(_, _, impl) => - val clazz = sym.moduleClass - clazz setInfo createNamer(tree).templateSig(impl) - clazz.tpe - - case ddef @ DefDef(mods, _, tparams, vparamss, tpt, rhs) => - // TODO: cleanup parameter list - createNamer(tree).methodSig(ddef, mods, tparams, vparamss, tpt, rhs) - - case vdef @ ValDef(mods, name, tpt, rhs) => - val isBeforeSupercall = ( - (sym hasFlag PARAM | PRESUPER) - && !mods.isJavaDefined - && sym.owner.isConstructor - ) - val typer1 = typer.constrTyperIf(isBeforeSupercall) - if (tpt.isEmpty) { - if (rhs.isEmpty) { - MissingParameterOrValTypeError(tpt) - ErrorType - } - else assignTypeToTree(vdef, newTyper(typer1.context.make(vdef, sym)), WildcardType) - } - else typer1.typedType(tpt).tpe - - case TypeDef(_, _, tparams, rhs) => - createNamer(tree).typeDefSig(sym, tparams, rhs) //@M! - - case Import(expr, selectors) => - val expr1 = typer.typedQualifier(expr) - typer checkStable expr1 - if (expr1.symbol != null && expr1.symbol.isRootPackage) - RootImportError(tree) - - if (expr1.isErrorTyped) - ErrorType - else { - val newImport = treeCopy.Import(tree, expr1, selectors).asInstanceOf[Import] - checkSelectors(newImport) - transformed(tree) = newImport - // copy symbol and type attributes back into old expression - // so that the structure builder will find it. - expr.symbol = expr1.symbol - expr.tpe = expr1.tpe - ImportType(expr1) - } - } + case cdef: ClassDef => + createNamer(tree).classSig(cdef) + + case mdef: ModuleDef => + createNamer(tree).moduleSig(mdef) + + case ddef: DefDef => + createNamer(tree).methodSig(ddef) - val result = - try getSig - catch typeErrorHandler(tree, ErrorType) + case vdef: ValDef => + createNamer(tree).valDefSig(vdef) - result match { - case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => deskolemizeTypeParams(tparams)(result) - case _ => result + case tdef: TypeDef => + createNamer(tree).typeDefSig(tdef) //@M! + + case imp: Import => + importSig(imp) } + + try getSig + catch typeErrorHandler(tree, ErrorType) } def includeParent(tpe: Type, parent: Symbol): Type = tpe match { @@ -1508,14 +1605,25 @@ trait Namers extends MethodSynthesis { } } - /** A class representing a lazy type with known type parameters. + /** + * A class representing a lazy type with known type parameters. `ctx` is the namer context in which the + * `owner` is defined. + * + * Constructing a PolyTypeCompleter for a DefDef creates type skolems for the type parameters and + * assigns them to the `tparams` trees. */ - class PolyTypeCompleter(tparams: List[TypeDef], restp: TypeCompleter, owner: Tree, ctx: Context) extends LockingTypeCompleter with FlagAgnosticCompleter { - private val ownerSym = owner.symbol - override val typeParams = tparams map (_.symbol) //@M - override val tree = restp.tree + class PolyTypeCompleter(tparams: List[TypeDef], restp: TypeCompleter, ctx: Context) extends LockingTypeCompleter with FlagAgnosticCompleter { + // @M. If `owner` is an abstract type member, `typeParams` are all NoSymbol (see comment in `completerOf`), + // otherwise, the non-skolemized (external) type parameter symbols + override val typeParams = tparams map (_.symbol) + + /* The definition tree (poly ClassDef, poly DefDef or HK TypeDef) */ + override val tree = restp.tree + + private val defnSym = tree.symbol - if (ownerSym.isTerm) { + if (defnSym.isTerm) { + // for polymorphic DefDefs, create type skolems and assign them to the tparam trees. val skolems = deriveFreshSkolems(tparams map (_.symbol)) map2(tparams, skolems)(_ setSymbol _) } @@ -1523,8 +1631,8 @@ trait Namers extends MethodSynthesis { def completeImpl(sym: Symbol) = { // @M an abstract type's type parameters are entered. // TODO: change to isTypeMember ? - if (ownerSym.isAbstractType) - newNamerFor(ctx, owner) enterSyms tparams //@M + if (defnSym.isAbstractType) + newNamerFor(ctx, tree) enterSyms tparams //@M restp complete sym } } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index be218fcb02..2340c78f8c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -268,26 +268,32 @@ trait NamesDefaults { self: Analyzer => * * For by-name parameters, create a value * x$n: () => T = () => arg + * + * For Ident(<unapply-selector>) arguments, no ValDef is created (SI-3353). */ - def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[ValDef] = { + def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[Option[ValDef]] = { val context = blockTyper.context - val symPs = map2(args, paramTypes)((arg, tpe) => { - val byName = isByNameParamType(tpe) - val repeated = isScalaRepeatedParamType(tpe) - val argTpe = ( - if (repeated) arg match { - case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => expr.tpe - case _ => seqType(arg.tpe) - } - 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 ( - if (byName) functionType(Nil, argTpe) else argTpe - ) - (context.scope.enter(s), byName, repeated) + val symPs = map2(args, paramTypes)((arg, tpe) => arg match { + case Ident(nme.SELECTOR_DUMMY) => + None // don't create a local ValDef if the argument is <unapply-selector> + case _ => + val byName = isByNameParamType(tpe) + val repeated = isScalaRepeatedParamType(tpe) + val argTpe = ( + if (repeated) arg match { + case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => expr.tpe + case _ => seqType(arg.tpe) + } + 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 ( + if (byName) functionType(Nil, argTpe) else argTpe + ) + Some((context.scope.enter(s), byName, repeated)) }) map2(symPs, args) { - case ((sym, byName, repeated), arg) => + case (None, _) => None + case (Some((sym, byName, repeated)), arg) => val body = if (byName) { val res = blockTyper.typed(Function(List(), arg)) @@ -303,7 +309,7 @@ trait NamesDefaults { self: Analyzer => blockTyper.typed(Apply(factory, List(resetLocalAttrs(arg)))) } else arg } - atPos(body.pos)(ValDef(sym, body).setType(NoType)) + Some(atPos(body.pos)(ValDef(sym, body).setType(NoType))) } } @@ -329,27 +335,29 @@ trait NamesDefaults { self: Analyzer => // ValDef's in the block), change the arguments to these local values. case Apply(expr, typedArgs) => // typedArgs: definition-site order - val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, false, false) + val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, removeByName = false, removeRepeated = false) // valDefs: call-site order val valDefs = argValDefs(reorderArgsInv(typedArgs, argPos), reorderArgsInv(formals, argPos), blockTyper) // refArgs: definition-site order again - val refArgs = map2(reorderArgs(valDefs, argPos), formals)((vDef, tpe) => { - val ref = gen.mkAttributedRef(vDef.symbol) - atPos(vDef.pos.focus) { - // for by-name parameters, the local value is a nullary function returning the argument - tpe.typeSymbol match { - case ByNameParamClass => Apply(ref, Nil) - case RepeatedParamClass => Typed(ref, Ident(tpnme.WILDCARD_STAR)) - case _ => ref + val refArgs = map3(reorderArgs(valDefs, argPos), formals, typedArgs)((vDefOpt, tpe, origArg) => vDefOpt match { + case None => origArg + case Some(vDef) => + val ref = gen.mkAttributedRef(vDef.symbol) + atPos(vDef.pos.focus) { + // for by-name parameters, the local value is a nullary function returning the argument + tpe.typeSymbol match { + case ByNameParamClass => Apply(ref, Nil) + case RepeatedParamClass => Typed(ref, Ident(tpnme.WILDCARD_STAR)) + case _ => ref + } } - } }) // cannot call blockTyper.typedBlock here, because the method expr might be partially applied only val res = blockTyper.doTypedApply(tree, expr, refArgs, mode, pt) res.setPos(res.pos.makeTransparent) - val block = Block(stats ::: valDefs, res).setType(res.tpe).setPos(tree.pos.makeTransparent) + val block = Block(stats ::: valDefs.flatten, res).setType(res.tpe).setPos(tree.pos.makeTransparent) context.namedApplyBlockInfo = Some((block, NamedApplyInfo(qual, targs, vargss :+ refArgs, blockTyper))) block diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index b0745b4c09..4b53802d95 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -409,15 +409,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // example check: List[Int] <:< ::[Int] // TODO: extractor.paramType may contain unbound type params (run/t2800, run/t3530) - val (typeTestTreeMaker, patBinderOrCasted) = - if (needsTypeTest(patBinder.info.widen, extractor.paramType)) { - // chain a type-testing extractor before the actual extractor call - // it tests the type, checks the outer pointer and casts to the expected type - // TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC] - // (the prefix of the argument passed to the unapply must equal the prefix of the type of the binder) - val treeMaker = TypeTestTreeMaker(patBinder, patBinder, extractor.paramType, extractor.paramType)(pos, extractorArgTypeTest = true) - (List(treeMaker), treeMaker.nextBinder) - } else { + // `patBinderOrCasted` is assigned the result of casting `patBinder` to `extractor.paramType` + val (typeTestTreeMaker, patBinderOrCasted, binderKnownNonNull) = + if (patBinder.info.widen <:< extractor.paramType) { // no type test needed, but the tree maker relies on `patBinderOrCasted` having type `extractor.paramType` (and not just some type compatible with it) // SI-6624 shows this is necessary because apparently patBinder may have an unfortunate type (.decls don't have the case field accessors) // TODO: get to the bottom of this -- I assume it happens when type checking infers a weird type for an unapply call @@ -426,10 +420,21 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL if (settings.developer.value && !(patBinder.info =:= extractor.paramType)) devWarning(s"resetting info of $patBinder: ${patBinder.info} to ${extractor.paramType}") */ - (Nil, patBinder setInfo extractor.paramType) + (Nil, patBinder setInfo extractor.paramType, false) + } else { + // chain a type-testing extractor before the actual extractor call + // it tests the type, checks the outer pointer and casts to the expected type + // TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC] + // (the prefix of the argument passed to the unapply must equal the prefix of the type of the binder) + val treeMaker = TypeTestTreeMaker(patBinder, patBinder, extractor.paramType, extractor.paramType)(pos, extractorArgTypeTest = true) + + // check whether typetest implies patBinder is not null, + // even though the eventual null check will be on patBinderOrCasted + // it'll be equal to patBinder casted to extractor.paramType anyway (and the type test is on patBinder) + (List(treeMaker), treeMaker.nextBinder, treeMaker.impliesBinderNonNull(patBinder)) } - withSubPats(typeTestTreeMaker :+ extractor.treeMaker(patBinderOrCasted, pos), extractor.subBindersAndPatterns: _*) + withSubPats(typeTestTreeMaker :+ extractor.treeMaker(patBinderOrCasted, binderKnownNonNull, pos), extractor.subBindersAndPatterns: _*) } @@ -622,8 +627,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // to which type should the previous binder be casted? def paramType : Type - // binder has been casted to paramType if necessary - def treeMaker(binder: Symbol, pos: Position): TreeMaker + /** Create the TreeMaker that embodies this extractor call + * + * `binder` has been casted to `paramType` if necessary + * `binderKnownNonNull` indicates whether the cast implies `binder` cannot be null + * when `binderKnownNonNull` is `true`, `ProductExtractorTreeMaker` does not do a (redundant) null check on binder + */ + def treeMaker(binder: Symbol, binderKnownNonNull: Boolean, pos: Position): TreeMaker // `subPatBinders` are the variables bound by this pattern in the following patterns // subPatBinders are replaced by references to the relevant part of the extractor's result (tuple component, seq element, the result as-is) @@ -637,6 +647,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL case bp => bp } + // never store these in local variables (for PreserveSubPatBinders) + lazy val ignoredSubPatBinders = (subPatBinders zip args).collect{ + case (b, PatternBoundToUnderscore()) => b + }.toSet + def subPatTypes: List[Type] = if(isSeq) { val TypeRef(pre, SeqClass, args) = seqTp @@ -731,17 +746,25 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def isSeq: Boolean = rawSubPatTypes.nonEmpty && isRepeatedParamType(rawSubPatTypes.last) protected def rawSubPatTypes = constructorTp.paramTypes - // binder has type paramType - def treeMaker(binder: Symbol, pos: Position): TreeMaker = { + /** Create the TreeMaker that embodies this extractor call + * + * `binder` has been casted to `paramType` if necessary + * `binderKnownNonNull` indicates whether the cast implies `binder` cannot be null + * when `binderKnownNonNull` is `true`, `ProductExtractorTreeMaker` does not do a (redundant) null check on binder + */ + def treeMaker(binder: Symbol, binderKnownNonNull: Boolean, pos: Position): TreeMaker = { val paramAccessors = binder.constrParamAccessors // binders corresponding to mutable fields should be stored (SI-5158, SI-6070) + // make an exception for classes under the scala package as they should be well-behaved, + // to optimize matching on List val mutableBinders = - if (paramAccessors exists (_.isMutable)) + if (!binder.info.typeSymbol.hasTransOwner(ScalaPackageClass) && + (paramAccessors exists (_.isMutable))) subPatBinders.zipWithIndex.collect{ case (binder, idx) if paramAccessors(idx).isMutable => binder } else Nil // checks binder ne null before chaining to the next extractor - ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder), mutableBinders) + ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder), mutableBinders, binderKnownNonNull, ignoredSubPatBinders) } // reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component @@ -763,11 +786,21 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def resultType = tpe.finalResultType def isSeq = extractorCall.symbol.name == nme.unapplySeq - def treeMaker(patBinderOrCasted: Symbol, pos: Position): TreeMaker = { + /** Create the TreeMaker that embodies this extractor call + * + * `binder` has been casted to `paramType` if necessary + * `binderKnownNonNull` is not used in this subclass + * + * TODO: implement review feedback by @retronym: + * Passing the pair of values around suggests: + * case class Binder(sym: Symbol, knownNotNull: Boolean). + * Perhaps it hasn't reached critical mass, but it would already clean things up a touch. + */ + def treeMaker(patBinderOrCasted: Symbol, binderKnownNonNull: Boolean, pos: Position): TreeMaker = { // the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern) val extractorApply = atPos(pos)(spliceApply(patBinderOrCasted)) val binder = freshSym(pos, pureType(resultInMonad)) // can't simplify this when subPatBinders.isEmpty, since UnitClass.tpe is definitely wrong when isSeq, and resultInMonad should always be correct since it comes directly from the extractor's result type - ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(subPatBinders, subPatRefs(binder), resultType.typeSymbol == BooleanClass, checkedLength, patBinderOrCasted) + ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(subPatBinders, subPatRefs(binder), resultType.typeSymbol == BooleanClass, checkedLength, patBinderOrCasted, ignoredSubPatBinders) } override protected def seqTree(binder: Symbol): Tree = @@ -824,6 +857,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } + object PatternBoundToUnderscore { + def unapply(pat: Tree): Boolean = pat match { + case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol! + case Ident(nme.WILDCARD) => true + case Alternative(ps) => ps forall (PatternBoundToUnderscore.unapply(_)) + case Typed(PatternBoundToUnderscore(), _) => true + case _ => false + } + } + object Bound { def unapply(t: Tree): Option[(Symbol, Tree)] = t match { case t@Bind(n, p) if (t.symbol ne null) && (t.symbol ne NoSymbol) => // pos/t2429 does not satisfy these conditions @@ -991,10 +1034,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL trait PreserveSubPatBinders extends TreeMaker { val subPatBinders: List[Symbol] val subPatRefs: List[Tree] + val ignoredSubPatBinders: Set[Symbol] // unless `debugInfoEmitVars`, this set should contain the bare minimum for correctness // mutable case class fields need to be stored regardless (SI-5158, SI-6070) -- see override in ProductExtractorTreeMaker - def storedBinders: Set[Symbol] = if (debugInfoEmitVars) subPatBinders.toSet else Set.empty + // sub patterns bound to wildcard (_) are never stored as they can't be referenced + // dirty debuggers will have to get dirty to see the wildcards + lazy val storedBinders: Set[Symbol] = + (if (debugInfoEmitVars) subPatBinders.toSet else Set.empty) ++ extraStoredBinders -- ignoredSubPatBinders + + // e.g., mutable fields of a case class in ProductExtractorTreeMaker + def extraStoredBinders: Set[Symbol] def emitVars = storedBinders.nonEmpty @@ -1015,10 +1065,22 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL Substitution(subPatBinders, subPatRefs) >> super.subPatternsAsSubstitution import CODE._ - def bindSubPats(in: Tree): Tree = if (!emitVars) in + def bindSubPats(in: Tree): Tree = + if (!emitVars) in else { - val (subPatBindersStored, subPatRefsStored) = stored.unzip - Block(map2(subPatBindersStored.toList, subPatRefsStored.toList)(VAL(_) === _), in) + // binders in `subPatBindersStored` that are referenced by tree `in` + val usedBinders = new collection.mutable.HashSet[Symbol]() + // all potentially stored subpat binders + val potentiallyStoredBinders = stored.unzip._1.toSet + // compute intersection of all symbols in the tree `in` and all potentially stored subpat binders + in.foreach(t => if (potentiallyStoredBinders(t.symbol)) usedBinders += t.symbol) + + if (usedBinders.isEmpty) in + else { + // only store binders actually used + val (subPatBindersStored, subPatRefsStored) = stored.filter{case (b, _) => usedBinders(b)}.unzip + Block(map2(subPatBindersStored.toList, subPatRefsStored.toList)(VAL(_) === _), in) + } } } @@ -1038,7 +1100,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val subPatRefs: List[Tree], extractorReturnsBoolean: Boolean, val checkedLength: Option[Int], - val prevBinder: Symbol) extends FunTreeMaker with PreserveSubPatBinders { + val prevBinder: Symbol, + val ignoredSubPatBinders: Set[Symbol] + ) extends FunTreeMaker with PreserveSubPatBinders { + + def extraStoredBinders: Set[Symbol] = Set() def chainBefore(next: Tree)(casegen: Casegen): Tree = { val condAndNext = extraCond match { @@ -1081,27 +1147,35 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree])( val subPatBinders: List[Symbol], val subPatRefs: List[Tree], - val mutableBinders: List[Symbol]) extends FunTreeMaker with PreserveSubPatBinders { + val mutableBinders: List[Symbol], + binderKnownNonNull: Boolean, + val ignoredSubPatBinders: Set[Symbol] + ) extends FunTreeMaker with PreserveSubPatBinders { import CODE._ val nextBinder = prevBinder // just passing through // mutable binders must be stored to avoid unsoundness or seeing mutation of fields after matching (SI-5158, SI-6070) - // (the implementation could be optimized by duplicating code from `super.storedBinders`, but this seems more elegant) - override def storedBinders: Set[Symbol] = super.storedBinders ++ mutableBinders.toSet + def extraStoredBinders: Set[Symbol] = mutableBinders.toSet def chainBefore(next: Tree)(casegen: Casegen): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL - val cond = extraCond map (nullCheck AND _) getOrElse nullCheck - casegen.ifThenElseZero(cond, bindSubPats(substitution(next))) + val cond = + if (binderKnownNonNull) extraCond + else (extraCond map (nullCheck AND _) + orElse Some(nullCheck)) + + cond match { + case Some(cond) => + casegen.ifThenElseZero(cond, bindSubPats(substitution(next))) + case _ => + bindSubPats(substitution(next)) + } } override def toString = "P"+(prevBinder.name, extraCond getOrElse "", localSubstitution) } - // typetag-based tests are inserted by the type checker - def needsTypeTest(tp: Type, pt: Type): Boolean = !(tp <:< pt) - object TypeTestTreeMaker { // factored out so that we can consistently generate other representations of the tree that implements the test // (e.g. propositions for exhaustivity and friends, boolean for isPureTypeTest) @@ -1115,12 +1189,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def equalsTest(pat: Tree, testedBinder: Symbol): Result def eqTest(pat: Tree, testedBinder: Symbol): Result def and(a: Result, b: Result): Result + def tru: Result } object treeCondStrategy extends TypeTestCondStrategy { import CODE._ type Result = Tree def and(a: Result, b: Result): Result = a AND b + def tru = TRUE_typed def typeTest(testedBinder: Symbol, expectedTp: Type) = codegen._isInstanceOf(testedBinder, expectedTp) def nonNullTest(testedBinder: Symbol) = REF(testedBinder) OBJ_NE NULL def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) @@ -1151,6 +1227,19 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def equalsTest(pat: Tree, testedBinder: Symbol): Result = false def eqTest(pat: Tree, testedBinder: Symbol): Result = false def and(a: Result, b: Result): Result = false // we don't and type tests, so the conjunction must include at least one false + def tru = true + } + + def nonNullImpliedByTestChecker(binder: Symbol) = new TypeTestCondStrategy { + type Result = Boolean + + def typeTest(testedBinder: Symbol, expectedTp: Type): Result = testedBinder eq binder + def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false + def nonNullTest(testedBinder: Symbol): Result = testedBinder eq binder + def equalsTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null + def eqTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null + def and(a: Result, b: Result): Result = a || b + def tru = false } } @@ -1220,10 +1309,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // I think it's okay: // - the isInstanceOf test includes a test for the element type // - Scala's arrays are invariant (so we don't drop type tests unsoundly) - case _ if (expectedTp <:< AnyRefClass.tpe) && !needsTypeTest(testedBinder.info.widen, expectedTp) => - // do non-null check first to ensure we won't select outer on null - if (outerTestNeeded) and(nonNullTest(testedBinder), outerTest(testedBinder, expectedTp)) - else nonNullTest(testedBinder) + case _ if testedBinder.info.widen <:< expectedTp => + // if the expected type is a primitive value type, it cannot be null and it cannot have an outer pointer + // since the types conform, no further checking is required + if (expectedTp.typeSymbol.isPrimitiveValueClass) tru + // have to test outer and non-null only when it's a reference type + else if (expectedTp <:< AnyRefClass.tpe) { + // do non-null check first to ensure we won't select outer on null + if (outerTestNeeded) and(nonNullTest(testedBinder), outerTest(testedBinder, expectedTp)) + else nonNullTest(testedBinder) + } else default case _ => default } @@ -1235,6 +1330,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // is this purely a type test, e.g. no outer check, no equality tests (used in switch emission) def isPureTypeTest = renderCondition(pureTypeTestChecker) + def impliesBinderNonNull(binder: Symbol) = renderCondition(nonNullImpliedByTestChecker(binder)) + override def toString = "TT"+(expectedTp, testedBinder.name, nextBinderTp) } @@ -1733,6 +1830,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def nonNullTest(testedBinder: Symbol) = NonNullCond(binderToUniqueTree(testedBinder)) def equalsTest(pat: Tree, testedBinder: Symbol) = EqualityCond(binderToUniqueTree(testedBinder), unique(pat)) def eqTest(pat: Tree, testedBinder: Symbol) = EqualityCond(binderToUniqueTree(testedBinder), unique(pat)) // TODO: eq, not == + def tru = TrueCond } ttm.renderCondition(condStrategy) case EqualityTestTreeMaker(prevBinder, patTree, _) => EqualityCond(binderToUniqueTree(prevBinder), unique(patTree)) @@ -3694,11 +3792,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // nextBinder: T // next == MatchMonad[U] // returns MatchMonad[U] - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = - ifThenElseZero(cond, BLOCK( - VAL(nextBinder) === res, - next - )) + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = { + val rest = + // only emit a local val for `nextBinder` if it's actually referenced in `next` + if (next.exists(_.symbol eq nextBinder)) + BLOCK( + VAL(nextBinder) === res, + next + ) + else next + ifThenElseZero(cond, rest) + } // guardTree: Boolean // next: MatchMonad[T] diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index fab53de0f0..b9fdd7280e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -11,6 +11,9 @@ import scala.collection.{ mutable, immutable } import transform.InfoTransform import scala.collection.mutable.ListBuffer import scala.language.postfixOps +import scala.tools.nsc.settings.ScalaVersion +import scala.tools.nsc.settings.AnyScalaVersion +import scala.tools.nsc.settings.NoScalaVersion /** <p> * Post-attribution checking and transformation. @@ -1369,10 +1372,18 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans * indicating it has changed semantics between versions. */ private def checkMigration(sym: Symbol, pos: Position) = { - if (sym.hasMigrationAnnotation) - unit.warning(pos, "%s has changed semantics in version %s:\n%s".format( - sym.fullLocationString, sym.migrationVersion.get, sym.migrationMessage.get) - ) + if (sym.hasMigrationAnnotation) { + val changed = try + settings.Xmigration.value < ScalaVersion(sym.migrationVersion.get) + catch { + case e : NumberFormatException => + unit.warning(pos, s"${sym.fullLocationString} has an unparsable version number: ${e.getMessage()}") + // if we can't parse the format on the migration annotation just conservatively assume it changed + true + } + if (changed) + unit.warning(pos, s"${sym.fullLocationString} has changed semantics in version ${sym.migrationVersion.get}:\n${sym.migrationMessage.get}") + } } private def checkCompileTimeOnly(sym: Symbol, pos: Position) = { @@ -1581,7 +1592,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans * arbitrarily choose one as more important than the other. */ checkDeprecated(sym, tree.pos) - if (settings.Xmigration28.value) + if(settings.Xmigration.value != NoScalaVersion) checkMigration(sym, tree.pos) checkCompileTimeOnly(sym, tree.pos) @@ -1680,8 +1691,6 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val bridges = addVarargBridges(currentOwner) checkAllOverrides(currentOwner) checkAnyValSubclass(currentOwner) - if (currentOwner.isDerivedValueClass) - currentOwner.primaryConstructor makeNotPrivate NoSymbol // SI-6601, must be done *after* pickler! if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a68a084d8f..026c130a87 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -453,12 +453,12 @@ trait Typers extends Modes with Adaptations with Tags { def reenterValueParams(vparamss: List[List[ValDef]]) { for (vparams <- vparamss) for (vparam <- vparams) - vparam.symbol = context.scope enter vparam.symbol + context.scope enter vparam.symbol } def reenterTypeParams(tparams: List[TypeDef]): List[Symbol] = for (tparam <- tparams) yield { - tparam.symbol = context.scope enter tparam.symbol + context.scope enter tparam.symbol tparam.symbol.deSkolemize } @@ -872,7 +872,9 @@ trait Typers extends Modes with Adaptations with Tags { case _ => debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) val tree1 = typed(resetAllAttrs(original), mode, WildcardType) - tree1.tpe = addAnnotations(tree1, tree1.tpe) + // Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that + // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin. + tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, pt) if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) } else @@ -1052,15 +1054,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 } @@ -1071,8 +1079,8 @@ trait Typers extends Modes with Adaptations with Tags { // begin adapt tree.tpe match { - case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (-1) - adaptAnnotations(tree, mode, pt) + case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1) + adaptAnnotations(tree, this, mode, pt) case ct @ ConstantType(value) if inNoModes(mode, TYPEmode | FUNmode) && (ct <:< pt) && !forScaladoc && !forInteractive => // (0) val sym = tree.symbol if (sym != null && sym.isDeprecated) { @@ -1176,8 +1184,8 @@ trait Typers extends Modes with Adaptations with Tags { Select(tree, "to" + sym.name) } } - case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (13) - return typed(adaptAnnotations(tree, mode, pt), mode, pt) + case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (13) + return typed(adaptAnnotations(tree, this, mode, pt), mode, pt) case _ => } if (!context.undetparams.isEmpty) { @@ -2058,13 +2066,23 @@ trait Typers extends Modes with Adaptations with Tags { * @return ... */ def typedValDef(vdef: ValDef): ValDef = { -// attributes(vdef) + val sym = vdef.symbol + val valDefTyper = { + val maybeConstrCtx = + if ((sym.isParameter || sym.isEarlyInitialized) && sym.owner.isConstructor) context.makeConstructorContext + else context + newTyper(maybeConstrCtx.makeNewScope(vdef, sym)) + } + valDefTyper.typedValDefImpl(vdef) + } + + // use typedValDef instead. this version is called after creating a new context for the ValDef + private def typedValDefImpl(vdef: ValDef) = { val sym = vdef.symbol.initialize - val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor) val typedMods = typedModifiers(vdef.mods) sym.annotations.map(_.completeInfo) - var tpt1 = checkNoEscaping.privates(sym, typer1.typedType(vdef.tpt)) + val tpt1 = checkNoEscaping.privates(sym, typedType(vdef.tpt)) checkNonCyclic(vdef, tpt1) if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable) @@ -2092,7 +2110,7 @@ trait Typers extends Modes with Adaptations with Tags { else subst(tpt1.tpe.typeArgs(0)) else subst(tpt1.tpe) } else tpt1.tpe - newTyper(typer1.context.make(vdef, sym)).transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) + transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) } treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs1)) setType NoType } @@ -2391,13 +2409,12 @@ trait Typers extends Modes with Adaptations with Tags { } def typedTypeDef(tdef: TypeDef): TypeDef = - typerWithCondLocalContext(context.makeNewScope(tdef, tdef.symbol))(tdef.tparams.nonEmpty){ - _.typedTypeDef0(tdef) + typerWithCondLocalContext(context.makeNewScope(tdef, tdef.symbol))(tdef.tparams.nonEmpty) { + _.typedTypeDefImpl(tdef) } - // call typedTypeDef instead - // a TypeDef with type parameters must always be type checked in a new scope - private def typedTypeDef0(tdef: TypeDef): TypeDef = { + // use typedTypeDef instead. this version is called after creating a new context for the TypeDef + private def typedTypeDefImpl(tdef: TypeDef): TypeDef = { tdef.symbol.initialize reenterTypeParams(tdef.tparams) val tparams1 = tdef.tparams mapConserve typedTypeDef @@ -4452,8 +4469,9 @@ trait Typers extends Modes with Adaptations with Tags { if (typed(expr).tpe.typeSymbol != UnitClass) unit.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded") } - treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner) - .setType(adaptTypeOfReturn(expr1, restpt.tpe, NothingClass.tpe)) + val res = treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner) + val tp = pluginsTypedReturn(NothingClass.tpe, this, res, restpt.tpe) + res.setType(tp) } } } @@ -5357,10 +5375,14 @@ trait Typers extends Modes with Adaptations with Tags { typed(docdef.definition, mode, pt) } + /** + * The typer with the correct context for a method definition. If the method is a default getter for + * a constructor default, the resulting typer has a constructor context (fixes SI-5543). + */ def defDefTyper(ddef: DefDef) = { - val flag = ddef.mods.hasDefaultFlag && sym.owner.isModuleClass && + val isConstrDefaultGetter = ddef.mods.hasDefaultFlag && sym.owner.isModuleClass && nme.defaultGetterToMethod(sym.name) == nme.CONSTRUCTOR - newTyper(context.makeNewScope(ddef, sym)).constrTyperIf(flag) + newTyper(context.makeNewScope(ddef, sym)).constrTyperIf(isConstrDefaultGetter) } def typedAlternative(alt: Alternative) = { @@ -5647,20 +5669,21 @@ trait Typers extends Modes with Adaptations with Tags { lastTreeToTyper = tree indentTyping() - var alreadyTyped = false + val ptPlugins = pluginsPt(pt, this, tree, mode) + val startByType = if (Statistics.canEnable) Statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null if (Statistics.canEnable) Statistics.incCounter(visitsByType, tree.getClass) try { if (context.retyping && - (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< pt))) { + (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))) { tree.tpe = null if (tree.hasSymbol) tree.symbol = NoSymbol } - alreadyTyped = tree.tpe ne null + val alreadyTyped = tree.tpe ne null var tree1: Tree = if (alreadyTyped) tree else { printTyping( - ptLine("typing %s: pt = %s".format(ptTree(tree), pt), + ptLine("typing %s: pt = %s".format(ptTree(tree), ptPlugins), "undetparams" -> context.undetparams, "implicitsEnabled" -> context.implicitsEnabled, "enrichmentEnabled" -> context.enrichmentEnabled, @@ -5669,7 +5692,7 @@ trait Typers extends Modes with Adaptations with Tags { "context.owner" -> context.owner ) ) - typed1(tree, mode, dropExistential(pt)) + typed1(tree, mode, dropExistential(ptPlugins)) } // Can happen during erroneous compilation - error(s) have been // reported, but we need to avoid causing an NPE with this tree @@ -5683,12 +5706,12 @@ trait Typers extends Modes with Adaptations with Tags { ) } - tree1.tpe = addAnnotations(tree1, tree1.tpe) - val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree) + tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, ptPlugins) + val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, ptPlugins, tree) if (!alreadyTyped) { printTyping("adapted %s: %s to %s, %s".format( - tree1, tree1.tpe.widen, pt, context.undetparamsString) + tree1, tree1.tpe.widen, ptPlugins, context.undetparamsString) ) //DEBUG } if (!isPastTyper) signalDone(context.asInstanceOf[analyzer.Context], tree, result) @@ -5703,7 +5726,7 @@ trait Typers extends Modes with Adaptations with Tags { setError(tree) case ex: Exception => if (settings.debug.value) // @M causes cyclic reference error - Console.println("exception when typing "+tree+", pt = "+pt) + Console.println("exception when typing "+tree+", pt = "+ptPlugins) if (context != null && context.unit.exists && tree != null) logError("AT: " + (tree.pos).dbgString, ex) throw ex diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 5782d7bbca..577aa087ea 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -79,8 +79,9 @@ trait Unapplies extends ast.TreeDSL private def toIdent(x: DefTree) = Ident(x.name) setPos x.pos.focus - private def classType(cdef: ClassDef, tparams: List[TypeDef], symbolic: Boolean = true): Tree = { - val tycon = if (symbolic) REF(cdef.symbol) else Ident(cdef.name) + private def classType(cdef: ClassDef, tparams: List[TypeDef]): Tree = { + // SI-7033 Unattributed to avoid forcing `cdef.symbol.info`. + val tycon = Ident(cdef.symbol) if (tparams.isEmpty) tycon else AppliedTypeTree(tycon, tparams map toIdent) } @@ -112,7 +113,7 @@ trait Unapplies extends ast.TreeDSL def createFun = gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true) def parents = if (inheritFromFun) List(createFun) else Nil def toString = DefDef( - Modifiers(OVERRIDE | FINAL), + Modifiers(OVERRIDE | FINAL | SYNTHETIC), nme.toString_, Nil, ListOfNil, @@ -133,10 +134,10 @@ trait Unapplies extends ast.TreeDSL /** The apply method corresponding to a case class */ - def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef, symbolic: Boolean): DefDef = { + def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef): DefDef = { val tparams = cdef.tparams map copyUntypedInvariant val cparamss = constrParamss(cdef) - def classtpe = classType(cdef, tparams, symbolic) + def classtpe = classType(cdef, tparams) atPos(cdef.pos.focus)( DefDef(mods, name, tparams, cparamss, classtpe, New(classtpe, mmap(cparamss)(gen.paramToArg))) @@ -145,7 +146,7 @@ trait Unapplies extends ast.TreeDSL /** The apply method corresponding to a case class */ - def caseModuleApplyMeth(cdef: ClassDef): DefDef = factoryMeth(caseMods, nme.apply, cdef, symbolic = true) + def caseModuleApplyMeth(cdef: ClassDef): DefDef = factoryMeth(caseMods, nme.apply, cdef) /** The unapply method corresponding to a case class */ diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 15025f85e3..00c72cf423 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -9,6 +9,7 @@ import scala.tools.nsc.MissingRequirementError abstract class CPSAnnotationChecker extends CPSUtils with Modes { val global: Global import global._ + import analyzer.{AnalyzerPlugin, Typer} import definitions._ //override val verbose = true @@ -18,12 +19,12 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { * Checks whether @cps annotations conform */ object checker extends AnnotationChecker { - private def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker() - private def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker() + private[CPSAnnotationChecker] def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker() + private[CPSAnnotationChecker] def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker() - private def cleanPlus(tp: Type) = + private[CPSAnnotationChecker] def cleanPlus(tp: Type) = removeAttribs(tp, MarkerCPSAdaptPlus, MarkerCPSTypes) - private def cleanPlusWith(tp: Type)(newAnnots: AnnotationInfo*) = + private[CPSAnnotationChecker] def cleanPlusWith(tp: Type)(newAnnots: AnnotationInfo*) = cleanPlus(tp) withAnnotations newAnnots.toList /** Check annotations to decide whether tpe1 <:< tpe2 */ @@ -116,8 +117,13 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } else bounds } + } + + object plugin extends AnalyzerPlugin { + + import checker._ - override def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = { + override def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = { if (!cpsEnabled) return false vprintln("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) @@ -183,7 +189,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } else false } - override def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = { + override def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = { if (!cpsEnabled) return tree vprintln("adapt annotations " + tree + " / " + tree.tpe + " / " + modeString(mode) + " / " + pt) @@ -239,14 +245,15 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { * is in tail position. Therefore, we are making sure that only the types of return expressions * are adapted which will either be removed, or lead to an error. */ - override def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = { + override def pluginsTypedReturn(default: Type, typer: Typer, tree: Return, pt: Type): Type = { + val expr = tree.expr // only adapt if method's result type (pt) is cps type val annots = cpsParamAnnotation(pt) if (annots.nonEmpty) { - // return type of `tree` without plus marker, but only if it doesn't have other cps annots - if (hasPlusMarker(tree.tpe) && !hasCpsParamTypes(tree.tpe)) - tree.setType(removeAttribs(tree.tpe, MarkerCPSAdaptPlus)) - tree.tpe + // return type of `expr` without plus marker, but only if it doesn't have other cps annots + if (hasPlusMarker(expr.tpe) && !hasCpsParamTypes(expr.tpe)) + expr.setType(removeAttribs(expr.tpe, MarkerCPSAdaptPlus)) + expr.tpe } else default } @@ -393,7 +400,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { /** Modify the type that has thus far been inferred * for a tree. All this should do is add annotations. */ - override def addAnnotations(tree: Tree, tpe: Type): Type = { + override def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = { import scala.util.control._ if (!cpsEnabled) { if (Exception.failAsValue(classOf[MissingRequirementError])(false)(hasCpsParamTypes(tpe))) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala index 8a500d6c4d..237159795a 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala @@ -33,6 +33,7 @@ class SelectiveCPSPlugin(val global: Global) extends Plugin { val global: SelectiveCPSPlugin.this.global.type = SelectiveCPSPlugin.this.global } global.addAnnotationChecker(checker.checker) + global.analyzer.addAnalyzerPlugin(checker.plugin) global.log("instantiated cps plugin: " + this) diff --git a/src/library/scala/annotation/migration.scala b/src/library/scala/annotation/migration.scala index 49fea9434c..adb6de6afd 100644 --- a/src/library/scala/annotation/migration.scala +++ b/src/library/scala/annotation/migration.scala @@ -17,7 +17,8 @@ package scala.annotation * order between Scala 2.7 and 2.8. * * @param message A message describing the change, which is emitted - * by the compiler if the flag `-Xmigration` is set. + * by the compiler if the flag `-Xmigration` indicates a version + * prior to the changedIn version. * * @param changedIn The version, in which the behaviour change was * introduced. diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 56e386ad67..55ac3995e9 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -295,6 +295,9 @@ sealed abstract class List[+A] extends AbstractSeq[A] } result } + + override def foldRight[B](z: B)(op: (A, B) => B): B = + reverse.foldLeft(z)((right, left) => op(left, right)) override def stringPrefix = "List" diff --git a/src/library/scala/collection/mutable/ListMap.scala b/src/library/scala/collection/mutable/ListMap.scala index 212ee917c5..7f05deffc8 100644 --- a/src/library/scala/collection/mutable/ListMap.scala +++ b/src/library/scala/collection/mutable/ListMap.scala @@ -12,6 +12,7 @@ package scala.collection package mutable import generic._ +import annotation.tailrec /** A simple mutable map backed by a list. * @@ -47,13 +48,17 @@ extends AbstractMap[A, B] def get(key: A): Option[B] = elems find (_._1 == key) map (_._2) def iterator: Iterator[(A, B)] = elems.iterator - def += (kv: (A, B)) = { elems = remove(kv._1, elems); elems = kv :: elems; siz += 1; this } - def -= (key: A) = { elems = remove(key, elems); this } - private def remove(key: A, elems: List[(A, B)]): List[(A, B)] = - if (elems.isEmpty) elems - else if (elems.head._1 == key) { siz -= 1; elems.tail } - else elems.head :: remove(key, elems.tail) + def += (kv: (A, B)) = { elems = remove(kv._1, elems, List()); elems = kv :: elems; siz += 1; this } + def -= (key: A) = { elems = remove(key, elems, List()); this } + + @tailrec + private def remove(key: A, elems: List[(A, B)], acc: List[(A, B)]): List[(A, B)] = { + if (elems.isEmpty) acc + else if (elems.head._1 == key) { siz -= 1; acc ::: elems.tail } + else remove(key, elems.tail, elems.head :: acc) + } + override def clear() = { elems = List(); siz = 0 } override def size: Int = siz diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 215f90b17e..77625e381c 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -25,11 +25,15 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: case some => some } + private val uncaughtExceptionHandler: Thread.UncaughtExceptionHandler = new Thread.UncaughtExceptionHandler { + def uncaughtException(thread: Thread, cause: Throwable): Unit = reporter(cause) + } + // Implement BlockContext on FJP threads class DefaultThreadFactory(daemonic: Boolean) extends ThreadFactory with ForkJoinPool.ForkJoinWorkerThreadFactory { def wire[T <: Thread](thread: T): T = { thread.setDaemon(daemonic) - //Potentially set things like uncaught exception handler, name etc + thread.setUncaughtExceptionHandler(uncaughtExceptionHandler) thread } @@ -73,7 +77,7 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: new ForkJoinPool( desiredParallelism, threadFactory, - null, //FIXME we should have an UncaughtExceptionHandler, see what Akka does + uncaughtExceptionHandler, true) // Async all the way baby } catch { case NonFatal(t) => @@ -94,13 +98,13 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: def execute(runnable: Runnable): Unit = executor match { case fj: ForkJoinPool => + val fjt = runnable match { + case t: ForkJoinTask[_] => t + case r => new ExecutionContextImpl.AdaptedForkJoinTask(r) + } Thread.currentThread match { - case fjw: ForkJoinWorkerThread if fjw.getPool eq fj => - (runnable match { - case fjt: ForkJoinTask[_] => fjt - case _ => ForkJoinTask.adapt(runnable) - }).fork - case _ => fj.execute(runnable) + case fjw: ForkJoinWorkerThread if fjw.getPool eq fj => fjt.fork() + case _ => fj execute fjt } case generic => generic execute runnable } @@ -111,6 +115,20 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: private[concurrent] object ExecutionContextImpl { + final class AdaptedForkJoinTask(runnable: Runnable) extends ForkJoinTask[Unit] { + final override def setRawResult(u: Unit): Unit = () + final override def getRawResult(): Unit = () + final override def exec(): Boolean = try { runnable.run(); true } catch { + case anything: Throwable ⇒ + val t = Thread.currentThread + t.getUncaughtExceptionHandler match { + case null ⇒ + case some ⇒ some.uncaughtException(t, anything) + } + throw anything + } + } + def fromExecutor(e: Executor, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextImpl = new ExecutionContextImpl(e, reporter) def fromExecutorService(es: ExecutorService, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextImpl with ExecutionContextExecutorService = new ExecutionContextImpl(es, reporter) with ExecutionContextExecutorService { diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index e9da45a079..52f1075137 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -34,7 +34,7 @@ private class CallbackRunnable[T](val executor: ExecutionContext, val onComplete value = v // Note that we cannot prepare the ExecutionContext at this point, since we might // already be running on a different thread! - executor.execute(this) + try executor.execute(this) catch { case NonFatal(t) => executor reportFailure t } } } diff --git a/src/library/scala/package.scala b/src/library/scala/package.scala index d3f8df9110..84f6f0be9c 100644 --- a/src/library/scala/package.scala +++ b/src/library/scala/package.scala @@ -95,10 +95,7 @@ package object scala { val Equiv = scala.math.Equiv type Fractional[T] = scala.math.Fractional[T] - val Fractional = scala.math.Fractional - type Integral[T] = scala.math.Integral[T] - val Integral = scala.math.Integral type Numeric[T] = scala.math.Numeric[T] val Numeric = scala.math.Numeric diff --git a/src/partest/scala/tools/partest/ASMConverters.scala b/src/partest/scala/tools/partest/ASMConverters.scala new file mode 100644 index 0000000000..d618e086f4 --- /dev/null +++ b/src/partest/scala/tools/partest/ASMConverters.scala @@ -0,0 +1,71 @@ +package scala.tools.partest + +import scala.collection.JavaConverters._ +import scala.tools.asm +import asm.tree.{ClassNode, MethodNode, InsnList} + +/** Makes using ASM from ByteCodeTests more convenient. + * + * Wraps ASM instructions in case classes so that equals and toString work + * for the purpose of bytecode diffing and pretty printing. + */ +trait ASMConverters { + // wrap ASM's instructions so we get case class-style `equals` and `toString` + object instructions { + def fromMethod(meth: MethodNode): List[Instruction] = { + val insns = meth.instructions + val asmToScala = new AsmToScala{ def labelIndex(l: asm.tree.AbstractInsnNode) = insns.indexOf(l) } + + asmToScala.mapOver(insns.iterator.asScala.toList).asInstanceOf[List[Instruction]] + } + + sealed abstract class Instruction { def opcode: String } + case class Field (opcode: String, desc: String, name: String, owner: String) extends Instruction + case class Incr (opcode: String, incr: Int, `var`: Int) extends Instruction + case class Op (opcode: String) extends Instruction + case class IntOp (opcode: String, operand: Int) extends Instruction + case class Jump (opcode: String, label: Label) extends Instruction + case class Ldc (opcode: String, cst: Any) extends Instruction + case class LookupSwitch (opcode: String, dflt: Label, keys: List[Integer], labels: List[Label]) extends Instruction + case class TableSwitch (opcode: String, dflt: Label, max: Int, min: Int, labels: List[Label]) extends Instruction + case class Method (opcode: String, desc: String, name: String, owner: String) extends Instruction + case class NewArray (opcode: String, desc: String, dims: Int) extends Instruction + case class TypeOp (opcode: String, desc: String) extends Instruction + case class VarOp (opcode: String, `var`: Int) extends Instruction + case class Label (offset: Int) extends Instruction { def opcode: String = "" } + case class FrameEntry (local: List[Any], stack: List[Any]) extends Instruction { def opcode: String = "" } + case class LineNumber (line: Int, start: Label) extends Instruction { def opcode: String = "" } + } + + abstract class AsmToScala { + import instructions._ + + def labelIndex(l: asm.tree.AbstractInsnNode): Int + + def mapOver(is: List[Any]): List[Any] = is map { + case i: asm.tree.AbstractInsnNode => apply(i) + case x => x + } + + def op(i: asm.tree.AbstractInsnNode) = if (asm.util.Printer.OPCODES.isDefinedAt(i.getOpcode)) asm.util.Printer.OPCODES(i.getOpcode) else "?" + def lst[T](xs: java.util.List[T]): List[T] = if (xs == null) Nil else xs.asScala.toList + def apply(l: asm.tree.LabelNode): Label = this(l: asm.tree.AbstractInsnNode).asInstanceOf[Label] + def apply(x: asm.tree.AbstractInsnNode): Instruction = x match { + case i: asm.tree.FieldInsnNode => Field (op(i), i.desc: String, i.name: String, i.owner: String) + case i: asm.tree.IincInsnNode => Incr (op(i), i.incr: Int, i.`var`: Int) + case i: asm.tree.InsnNode => Op (op(i)) + case i: asm.tree.IntInsnNode => IntOp (op(i), i.operand: Int) + case i: asm.tree.JumpInsnNode => Jump (op(i), this(i.label)) + case i: asm.tree.LdcInsnNode => Ldc (op(i), i.cst: Any) + case i: asm.tree.LookupSwitchInsnNode => LookupSwitch (op(i), this(i.dflt), lst(i.keys), mapOver(lst(i.labels)).asInstanceOf[List[Label]]) + case i: asm.tree.TableSwitchInsnNode => TableSwitch (op(i), this(i.dflt), i.max: Int, i.min: Int, mapOver(lst(i.labels)).asInstanceOf[List[Label]]) + case i: asm.tree.MethodInsnNode => Method (op(i), i.desc: String, i.name: String, i.owner: String) + case i: asm.tree.MultiANewArrayInsnNode => NewArray (op(i), i.desc: String, i.dims: Int) + case i: asm.tree.TypeInsnNode => TypeOp (op(i), i.desc: String) + case i: asm.tree.VarInsnNode => VarOp (op(i), i.`var`: Int) + case i: asm.tree.LabelNode => Label (labelIndex(x)) + case i: asm.tree.FrameNode => FrameEntry (mapOver(lst(i.local)), mapOver(lst(i.stack))) + case i: asm.tree.LineNumberNode => LineNumber (i.line: Int, this(i.start): Label) + } + } +}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/BytecodeTest.scala b/src/partest/scala/tools/partest/BytecodeTest.scala index 93183c2095..41329a8264 100644 --- a/src/partest/scala/tools/partest/BytecodeTest.scala +++ b/src/partest/scala/tools/partest/BytecodeTest.scala @@ -8,11 +8,11 @@ import asm.tree.{ClassNode, MethodNode, InsnList} import java.io.InputStream /** - * Providies utilities for inspecting bytecode using ASM library. + * Provides utilities for inspecting bytecode using ASM library. * * HOW TO USE * 1. Create subdirectory in test/files/jvm for your test. Let's name it $TESTDIR. - * 2. Create $TESTDIR/BytecodeSrc_1.scala that contains Scala source file which you + * 2. Create $TESTDIR/BytecodeSrc_1.scala that contains Scala source file that you * want to inspect the bytecode for. The '_1' suffix signals to partest that it * should compile this file first. * 3. Create $TESTDIR/Test.scala: @@ -28,18 +28,59 @@ import java.io.InputStream * See test/files/jvm/bytecode-test-example for an example of bytecode test. * */ -abstract class BytecodeTest { +abstract class BytecodeTest extends ASMConverters { /** produce the output to be compared against a checkfile */ protected def show(): Unit def main(args: Array[String]): Unit = show +// asserts + def sameBytecode(methA: MethodNode, methB: MethodNode) = { + val isa = instructions.fromMethod(methA) + val isb = instructions.fromMethod(methB) + if (isa == isb) println("bytecode identical") + else diffInstructions(isa, isb) + } + + import instructions._ + // bytecode is equal modulo local variable numbering + def equalsModuloVar(a: Instruction, b: Instruction) = (a, b) match { + case _ if a == b => true + case (VarOp(op1, _), VarOp(op2, _)) if op1 == op2 => true + case _ => false + } + + def similarBytecode(methA: MethodNode, methB: MethodNode, similar: (Instruction, Instruction) => Boolean) = { + val isa = fromMethod(methA) + val isb = fromMethod(methB) + if (isa == isb) println("bytecode identical") + else if ((isa, isb).zipped.forall { case (a, b) => similar(a, b) }) println("bytecode similar") + else diffInstructions(isa, isb) + } + + def diffInstructions(isa: List[Instruction], isb: List[Instruction]) = { + val len = Math.max(isa.length, isb.length) + if (len > 0 ) { + val width = isa.map(_.toString.length).max + val lineWidth = len.toString.length + (1 to len) foreach { line => + val isaPadded = isa.map(_.toString) orElse Stream.continually("") + val isbPadded = isb.map(_.toString) orElse Stream.continually("") + val a = isaPadded(line-1) + val b = isbPadded(line-1) + + println(s"""$line${" " * (lineWidth-line.toString.length)} ${if (a==b) "==" else "<>"} $a${" " * (width-a.length)} | $b""") + } + } + } + +// loading protected def getMethod(classNode: ClassNode, name: String): MethodNode = classNode.methods.asScala.find(_.name == name) getOrElse sys.error(s"Didn't find method '$name' in class '${classNode.name}'") - protected def loadClassNode(name: String): ClassNode = { + protected def loadClassNode(name: String, skipDebugInfo: Boolean = true): ClassNode = { val classBytes: InputStream = (for { classRep <- classpath.findClass(name) binary <- classRep.binary @@ -47,7 +88,7 @@ abstract class BytecodeTest { val cr = new ClassReader(classBytes) val cn = new ClassNode() - cr.accept(cn, 0) + cr.accept(cn, if (skipDebugInfo) ClassReader.SKIP_DEBUG else 0) cn } diff --git a/src/reflect/scala/reflect/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala index 562b1da8e3..2ba18a8207 100644 --- a/src/reflect/scala/reflect/api/Exprs.scala +++ b/src/reflect/scala/reflect/api/Exprs.scala @@ -90,6 +90,7 @@ trait Exprs { self: Universe => * }}} * because expr of type Expr[T] itself does not have a method foo. */ + // @compileTimeOnly("Cannot use splice outside reify") def splice: T /** @@ -106,6 +107,7 @@ trait Exprs { self: Universe => * object Impls { def foo_impl(c: Context)(x: c.Expr[X]): c.Expr[x.value.T] = ... } * }}} */ + // @compileTimeOnly("Cannot use value except for signatures of macro implementations") val value: T override def canEqual(x: Any) = x.isInstanceOf[Expr[_]] diff --git a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala index 5318d3e540..1ab975b233 100644 --- a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala +++ b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala @@ -16,7 +16,15 @@ trait AnnotationCheckers { /** An additional checker for annotations on types. * Typically these are registered by compiler plugins * with the addAnnotationChecker method. */ - abstract class AnnotationChecker { + trait AnnotationChecker { + + /** + * Selectively activate this annotation checker. When using both an annotation checker + * and an analyzer plugin, it is common to run both of them only during selected + * compiler phases. See documentation in AnalyzerPlugin.isActive. + */ + def isActive(): Boolean = true + /** Check the annotations on two types conform. */ def annotationsConform(tpe1: Type, tpe2: Type): Boolean @@ -29,39 +37,51 @@ trait AnnotationCheckers { def annotationsGlb(tp: Type, ts: List[Type]): Type = tp /** Refine the bounds on type parameters to the given type arguments. */ - def adaptBoundsToAnnotations(bounds: List[TypeBounds], - tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = bounds + def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol], + targs: List[Type]): List[TypeBounds] = bounds - /** Modify the type that has thus far been inferred - * for a tree. All this should do is add annotations. */ + /** + * Modify the type that has thus far been inferred for a tree. All this should + * do is add annotations. + */ + @deprecated("Create an AnalyzerPlugin and use pluginsTyped", "2.10.1") def addAnnotations(tree: Tree, tpe: Type): Type = tpe - /** Decide whether this annotation checker can adapt a tree - * that has an annotated type to the given type tp, taking - * into account the given mode (see method adapt in trait Typers).*/ + /** + * Decide whether this analyzer plugin can adapt a tree that has an annotated type to the + * given type tp, taking into account the given mode (see method adapt in trait Typers). + */ + @deprecated("Create an AnalyzerPlugin and use canAdaptAnnotations", "2.10.1") def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = false - /** Adapt a tree that has an annotated type to the given type tp, - * taking into account the given mode (see method adapt in trait Typers). - * An implementation cannot rely on canAdaptAnnotations being called - * before. If the implementing class cannot do the adaptiong, it - * should return the tree unchanged.*/ + /** + * Adapt a tree that has an annotated type to the given type tp, taking into account the given + * mode (see method adapt in trait Typers). + * + * An implementation cannot rely on canAdaptAnnotations being called before. If the implementing + * class cannot do the adaptiong, it should return the tree unchanged. + */ + @deprecated("Create an AnalyzerPlugin and use adaptAnnotations", "2.10.1") def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = tree - /** Adapt the type of a return expression. The decision of an annotation checker - * whether the type should be adapted is based on the type of the expression - * which is returned, as well as the result type of the method (pt). - * By default, this method simply returns the passed `default` type. + /** + * Adapt the type of a return expression. The decision of a typer plugin whether the type + * should be adapted is based on the type of the expression which is returned, as well as the + * result type of the method (pt). + * + * By default, this method simply returns the passed `default` type. */ + @deprecated("Create an AnalyzerPlugin and use pluginsTypedReturn. Note: the 'tree' argument here is\n"+ + "the 'expr' of a Return tree; 'pluginsTypedReturn' takes the Return tree itself as argument", "2.10.1") def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = default } // Syncnote: Annotation checkers inaccessible to reflection, so no sync in var necessary. + /** The list of annotation checkers that have been registered */ private var annotationCheckers: List[AnnotationChecker] = Nil - /** Register an annotation checker. Typically these - * are added by compiler plugins. */ + /** Register an annotation checker. Typically these are added by compiler plugins. */ def addAnnotationChecker(checker: AnnotationChecker) { if (!(annotationCheckers contains checker)) annotationCheckers = checker :: annotationCheckers @@ -72,76 +92,53 @@ trait AnnotationCheckers { annotationCheckers = Nil } - /** Check that the annotations on two types conform. To do - * so, consult all registered annotation checkers. */ - def annotationsConform(tp1: Type, tp2: Type): Boolean = { - /* Finish quickly if there are no annotations */ - if (tp1.annotations.isEmpty && tp2.annotations.isEmpty) - true - else - annotationCheckers.forall( - _.annotationsConform(tp1,tp2)) - } - - /** Refine the computed least upper bound of a list of types. - * All this should do is add annotations. */ - def annotationsLub(tpe: Type, ts: List[Type]): Type = { - annotationCheckers.foldLeft(tpe)((tpe, checker) => - checker.annotationsLub(tpe, ts)) - } - - /** Refine the computed greatest lower bound of a list of types. - * All this should do is add annotations. */ - def annotationsGlb(tpe: Type, ts: List[Type]): Type = { - annotationCheckers.foldLeft(tpe)((tpe, checker) => - checker.annotationsGlb(tpe, ts)) - } - - /** Refine the bounds on type parameters to the given type arguments. */ - def adaptBoundsToAnnotations(bounds: List[TypeBounds], - tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = { - annotationCheckers.foldLeft(bounds)((bounds, checker) => - checker.adaptBoundsToAnnotations(bounds, tparams, targs)) - } - - /** Let all annotations checkers add extra annotations - * to this tree's type. */ - def addAnnotations(tree: Tree, tpe: Type): Type = { - annotationCheckers.foldLeft(tpe)((tpe, checker) => - checker.addAnnotations(tree, tpe)) - } - - /** Find out whether any annotation checker can adapt a tree - * to a given type. Called by Typers.adapt. */ - def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = { - annotationCheckers.exists(_.canAdaptAnnotations(tree, mode, pt)) - } - - /** Let registered annotation checkers adapt a tree - * to a given type (called by Typers.adapt). Annotation checkers - * that cannot do the adaption should pass the tree through - * unchanged. */ - def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = { - annotationCheckers.foldLeft(tree)((tree, checker) => - checker.adaptAnnotations(tree, mode, pt)) - } - - /** Let a registered annotation checker adapt the type of a return expression. - * Annotation checkers that cannot do the adaptation should simply return - * the `default` argument. - * - * Note that the result is undefined if more than one annotation checker - * returns an adapted type which is not a subtype of `default`. - */ - def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = { - val adaptedTypes = annotationCheckers flatMap { checker => - val adapted = checker.adaptTypeOfReturn(tree, pt, default) - if (!(adapted <:< default)) List(adapted) - else List() - } - adaptedTypes match { - case fst :: _ => fst - case List() => default - } - } + /** @see AnnotationChecker.annotationsConform */ + def annotationsConform(tp1: Type, tp2: Type): Boolean = + if (annotationCheckers.isEmpty || (tp1.annotations.isEmpty && tp2.annotations.isEmpty)) true + else annotationCheckers.forall(checker => { + !checker.isActive() || checker.annotationsConform(tp1,tp2) + }) + + /** @see AnnotationChecker.annotationsLub */ + def annotationsLub(tpe: Type, ts: List[Type]): Type = + if (annotationCheckers.isEmpty) tpe + else annotationCheckers.foldLeft(tpe)((tpe, checker) => + if (!checker.isActive()) tpe else checker.annotationsLub(tpe, ts)) + + /** @see AnnotationChecker.annotationsGlb */ + def annotationsGlb(tpe: Type, ts: List[Type]): Type = + if (annotationCheckers.isEmpty) tpe + else annotationCheckers.foldLeft(tpe)((tpe, checker) => + if (!checker.isActive()) tpe else checker.annotationsGlb(tpe, ts)) + + /** @see AnnotationChecker.adaptBoundsToAnnotations */ + def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol], + targs: List[Type]): List[TypeBounds] = + if (annotationCheckers.isEmpty) bounds + else annotationCheckers.foldLeft(bounds)((bounds, checker) => + if (!checker.isActive()) bounds else checker.adaptBoundsToAnnotations(bounds, tparams, targs)) + + + /* The following methods will be removed with the deprecated methods is AnnotationChecker. */ + + def addAnnotations(tree: Tree, tpe: Type): Type = + if (annotationCheckers.isEmpty) tpe + else annotationCheckers.foldLeft(tpe)((tpe, checker) => + if (!checker.isActive()) tpe else checker.addAnnotations(tree, tpe)) + + def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = + if (annotationCheckers.isEmpty) false + else annotationCheckers.exists(checker => { + checker.isActive() && checker.canAdaptAnnotations(tree, mode, pt) + }) + + def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = + if (annotationCheckers.isEmpty) tree + else annotationCheckers.foldLeft(tree)((tree, checker) => + if (!checker.isActive()) tree else checker.adaptAnnotations(tree, mode, pt)) + + def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = + if (annotationCheckers.isEmpty) default + else annotationCheckers.foldLeft(default)((tpe, checker) => + if (!checker.isActive()) tpe else checker.adaptTypeOfReturn(tree, pt, tpe)) } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 4269b65297..6e4ca76382 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -671,6 +671,11 @@ trait Definitions extends api.StandardDefinitions { case _ => Nil } + def dropNullaryMethod(tp: Type) = tp match { + case NullaryMethodType(restpe) => restpe + case _ => tp + } + def unapplyUnwrap(tpe:Type) = tpe.finalResultType.normalize match { case RefinedType(p :: _, _) => p.normalize case tp => tp @@ -678,9 +683,10 @@ trait Definitions extends api.StandardDefinitions { def functionApply(n: Int) = getMemberMethod(FunctionClass(n), nme.apply) - def abstractFunctionForFunctionType(tp: Type) = - if (isFunctionType(tp)) abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) - else NoType + def abstractFunctionForFunctionType(tp: Type) = { + assert(isFunctionType(tp), tp) + abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) + } def isFunctionType(tp: Type): Boolean = tp.normalize match { case TypeRef(_, sym, args) if args.nonEmpty => @@ -864,6 +870,12 @@ trait Definitions extends api.StandardDefinitions { removeRedundantObjects(parents) } + /** Flatten curried parameter lists of a method type. */ + def allParameters(tpe: Type): List[Symbol] = tpe match { + case MethodType(params, res) => params ::: allParameters(res) + case _ => Nil + } + def typeStringNoPackage(tp: Type) = "" + tp stripPrefix tp.typeSymbol.enclosingPackage.fullName + "." @@ -949,7 +961,7 @@ trait Definitions extends api.StandardDefinitions { lazy val BeanPropertyAttr = requiredClass[scala.beans.BeanProperty] lazy val BooleanBeanPropertyAttr = requiredClass[scala.beans.BooleanBeanProperty] lazy val CloneableAttr = requiredClass[scala.annotation.cloneable] - lazy val CompileTimeOnlyAttr = getClassIfDefined("scala.reflect.macros.compileTimeOnly") + lazy val CompileTimeOnlyAttr = getClassIfDefined("scala.reflect.internal.annotations.compileTimeOnly") lazy val DeprecatedAttr = requiredClass[scala.deprecated] lazy val DeprecatedNameAttr = requiredClass[scala.deprecatedName] lazy val DeprecatedInheritanceAttr = requiredClass[scala.deprecatedInheritance] diff --git a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala index 59c027868e..8b24678fd6 100644 --- a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala +++ b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala @@ -32,19 +32,4 @@ trait ExistentialsAndSkolems { } (new Deskolemizer).typeSkolems } - - /** Convert to corresponding type parameters all skolems of method - * parameters which appear in `tparams`. - */ - def deskolemizeTypeParams(tparams: List[Symbol])(tp: Type): Type = { - class DeSkolemizeMap extends TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) if sym.isTypeSkolem && (tparams contains sym.deSkolemize) => - mapOver(typeRef(NoPrefix, sym.deSkolemize, args)) - case _ => - mapOver(tp) - } - } - new DeSkolemizeMap mapOver tp - } } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 1dec11548f..72ad84edec 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -86,7 +86,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => case n: TypeName => if (isClass) newClassSymbol(n, pos, newFlags) else newNonClassSymbol(n, pos, newFlags) } - def knownDirectSubclasses = children + def knownDirectSubclasses = { + if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize + children + } + def baseClasses = info.baseClasses def module = sourceModule def thisPrefix: Type = thisType @@ -1188,6 +1192,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => * to generate a type of kind * * for a term symbol, its usual type. * See the tpe/tpeHK overrides in TypeSymbol for more. + * + * For type symbols, `tpe` is different than `info`. `tpe` returns a typeRef + * to the type symbol, `info` returns the type information of the type symbol, + * e.g. a ClassInfoType for classes or a TypeBounds for abstract types. */ def tpe: Type = info def tpeHK: Type = tpe diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 3136b227db..9d4bdab837 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -4706,23 +4706,13 @@ trait Types extends api.Types { self: SymbolTable => case idx => Some(to(idx)) } - override def transform(tree: Tree) = - tree match { - case tree@Ident(_) => - termMapsTo(tree.symbol) match { - case Some(tosym) => - if (tosym.info.bounds.hi.typeSymbol isSubClass SingletonClass) { - Ident(tosym.existentialToString) - .setSymbol(tosym) - .setPos(tosym.pos) - .setType(dropSingletonType(tosym.info.bounds.hi)) - } else { - giveup() - } - case none => super.transform(tree) - } - case tree => super.transform(tree) + override def transform(tree: Tree) = { + termMapsTo(tree.symbol) match { + case Some(tosym) => tree.symbol = tosym + case None => () } + super.transform(tree) + } } trans.transform(tree) } @@ -6075,7 +6065,7 @@ trait Types extends api.Types { self: SymbolTable => (sameLength(params1, params2) && mt1.isImplicit == mt2.isImplicit && matchingParams(params1, params2, mt1.isJava, mt2.isJava) && - isSubType(res1, res2.substSym(params2, params1), depth)) + isSubType(res1.substSym(params1, params2), res2, depth)) // TODO: if mt1.params.isEmpty, consider NullaryMethodType? case _ => false @@ -6618,7 +6608,7 @@ trait Types extends api.Types { self: SymbolTable => val ts0 = elimSub0(ts) if (ts0.isEmpty || ts0.tail.isEmpty) ts0 else { - val ts1 = ts0 mapConserve (t => elimAnonymousClass(t.underlying)) + val ts1 = ts0 mapConserve (t => elimAnonymousClass(t.dealiasWiden)) if (ts1 eq ts0) ts0 else elimSub(ts1, depth) } @@ -6735,6 +6725,8 @@ trait Types extends api.Types { self: SymbolTable => NullaryMethodType(lub0(matchingRestypes(ts, Nil))) case ts @ TypeBounds(_, _) :: rest => TypeBounds(glb(ts map (_.bounds.lo), depth), lub(ts map (_.bounds.hi), depth)) + case ts @ AnnotatedType(annots, tpe, _) :: rest => + annotationsLub(lub0(ts map (_.withoutAnnotations)), ts) case ts => lubResults get (depth, ts) match { case Some(lubType) => diff --git a/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala b/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala new file mode 100644 index 0000000000..058ff61fbf --- /dev/null +++ b/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala @@ -0,0 +1,31 @@ +package scala.reflect +package internal +package annotations + +import scala.annotation.meta._ + +/** + * An annotation that designates a member should not be referred to after + * type checking (which includes macro expansion); it must only be used in + * the arguments of some other macro that will eliminate it from the AST. + * + * Later on, this annotation should be removed and implemented with domain-specific macros. + * If a certain method `inner` mustn't be called outside the context of a given macro `outer`, + * then it should itself be declared as a macro. + * + * Approach #1. Expansion of `inner` checks whether its enclosures contain `outer` and + * report an error if `outer` is not detected. In principle, we could use this approach right now, + * but currently enclosures are broken, because contexts aren't exactly famous for keeping precise + * track of the stack of the trees being typechecked. + * + * Approach #2. Default implementation of `inner` is just an invocation of `c.abort`. + * `outer` is an untyped macro, which expands into a block, which contains a redefinition of `inner` + * and a call to itself. The redefined `inner` could either be a stub like `Expr.splice` or carry out + * domain-specific logic. + * + * @param message the error message to print during compilation if a reference remains + * after type checking + * @since 2.10.1 + */ +@getter @setter @beanGetter @beanSetter +final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation diff --git a/src/reflect/scala/reflect/macros/compileTimeOnly.scala b/src/reflect/scala/reflect/macros/compileTimeOnly.scala deleted file mode 100644 index 5a3a352a53..0000000000 --- a/src/reflect/scala/reflect/macros/compileTimeOnly.scala +++ /dev/null @@ -1,16 +0,0 @@ -package scala.reflect -package macros - -import scala.annotation.meta._ - -/** - * An annotation that designates a member should not be referred to after - * type checking (which includes macro expansion); it must only be used in - * the arguments of some other macro that will eliminate it from the AST. - * - * @param message the error message to print during compilation if a reference remains - * after type checking - * @since 2.10.1 - */ -@getter @setter @beanGetter @beanSetter -final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index 8674be168c..0efa83fbd9 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -70,7 +70,19 @@ object FutureTests extends MinimalScalaTest { //FIXME should check } } - + + "The default ExecutionContext" should { + "report uncaught exceptions" in { + val p = Promise[Throwable]() + val logThrowable: Throwable => Unit = p.trySuccess(_) + val ec: ExecutionContext = ExecutionContext.fromExecutor(null, logThrowable) + + val t = new NotImplementedError("foo") + val f = Future(throw t)(ec) + Await.result(p.future, 2.seconds) mustBe t + } + } + "A future with global ExecutionContext" should { import ExecutionContext.Implicits._ diff --git a/test/files/jvm/patmat_opt_ignore_underscore.check b/test/files/jvm/patmat_opt_ignore_underscore.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/patmat_opt_ignore_underscore.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/patmat_opt_ignore_underscore.flags b/test/files/jvm/patmat_opt_ignore_underscore.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/jvm/patmat_opt_ignore_underscore.flags @@ -0,0 +1 @@ +-optimize
\ No newline at end of file diff --git a/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala b/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala new file mode 100644 index 0000000000..fa3639380d --- /dev/null +++ b/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala @@ -0,0 +1,29 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +// this is not the best test for shielding against regressing on this particular issue, +// but it sets the stage for checking the bytecode emitted by the pattern matcher and +// comparing it to manually tuned code using if/then/else etc. +class SameBytecode { + case class Foo(x: Any, y: String) + + def a = + Foo(1, "a") match { + case Foo(_: String, y) => y + } + + // this method's body holds the tree that should be generated by the pattern matcher for method a (-Xprint:patmat) + // the test checks that bytecode for a and b is identical (modulo line numbers) + // we can't diff trees as they are quite different (patmat uses jumps to labels that cannot be expressed in source, for example) + // note that the actual tree is quite bad: we do an unnecessary null check, isInstanceOf and local val (x3) + // some of these will be fixed soon (the initial null check is for the scrutinee, which is harder to fix in patmat) + def b: String = { + val x1 = Foo(1, "a") + if (x1.ne(null)) { + if (x1.x.isInstanceOf[String]) { + return x1.y + } + } + + throw new MatchError(x1) + } +}
\ No newline at end of file diff --git a/test/files/jvm/patmat_opt_ignore_underscore/test.scala b/test/files/jvm/patmat_opt_ignore_underscore/test.scala new file mode 100644 index 0000000000..6179101a7e --- /dev/null +++ b/test/files/jvm/patmat_opt_ignore_underscore/test.scala @@ -0,0 +1,15 @@ +import scala.tools.partest.BytecodeTest + +import scala.tools.nsc.util.JavaClassPath +import java.io.InputStream +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, InsnList} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b")) + } +} diff --git a/test/files/jvm/patmat_opt_no_nullcheck.check b/test/files/jvm/patmat_opt_no_nullcheck.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/patmat_opt_no_nullcheck.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/patmat_opt_no_nullcheck.flags b/test/files/jvm/patmat_opt_no_nullcheck.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/jvm/patmat_opt_no_nullcheck.flags @@ -0,0 +1 @@ +-optimize
\ No newline at end of file diff --git a/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala b/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala new file mode 100644 index 0000000000..3a594c401e --- /dev/null +++ b/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala @@ -0,0 +1,24 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +case class Foo(x: Any) + +class SameBytecode { + def a = + (Foo(1): Any) match { + case Foo(_: String) => + } + + // there's no null check + def b: Unit = { + val x1: Any = Foo(1) + if (x1.isInstanceOf[Foo]) { + val x3 = x1.asInstanceOf[Foo] + if (x3.x.isInstanceOf[String]) { + val x = () + return + } + } + + throw new MatchError(x1) + } +}
\ No newline at end of file diff --git a/test/files/jvm/patmat_opt_no_nullcheck/test.scala b/test/files/jvm/patmat_opt_no_nullcheck/test.scala new file mode 100644 index 0000000000..2927e763d5 --- /dev/null +++ b/test/files/jvm/patmat_opt_no_nullcheck/test.scala @@ -0,0 +1,8 @@ +import scala.tools.partest.BytecodeTest + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b")) + } +} diff --git a/test/files/jvm/patmat_opt_primitive_typetest.check b/test/files/jvm/patmat_opt_primitive_typetest.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/patmat_opt_primitive_typetest.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/patmat_opt_primitive_typetest.flags b/test/files/jvm/patmat_opt_primitive_typetest.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/jvm/patmat_opt_primitive_typetest.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala b/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala new file mode 100644 index 0000000000..e5db6c4dd0 --- /dev/null +++ b/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala @@ -0,0 +1,24 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +class SameBytecode { + case class Foo(x: Int, y: String) + + def a = + Foo(1, "a") match { + case Foo(_: Int, y) => y + } + + // this method's body holds the tree that should be generated by the pattern matcher for method a (-Xprint:patmat) + // the test checks that bytecode for a and b is identical (modulo line numbers) + // we can't diff trees as they are quite different (patmat uses jumps to labels that cannot be expressed in source, for example) + // note that the actual tree is quite bad: we do an unnecessary null check, and local val (x3) + // some of these will be fixed soon (the initial null check is for the scrutinee, which is harder to fix in patmat) + def b: String = { + val x1 = Foo(1, "a") + if (x1.ne(null)) { + return x1.y + } + + throw new MatchError(x1) + } +}
\ No newline at end of file diff --git a/test/files/jvm/patmat_opt_primitive_typetest/test.scala b/test/files/jvm/patmat_opt_primitive_typetest/test.scala new file mode 100644 index 0000000000..2927e763d5 --- /dev/null +++ b/test/files/jvm/patmat_opt_primitive_typetest/test.scala @@ -0,0 +1,8 @@ +import scala.tools.partest.BytecodeTest + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b")) + } +} diff --git a/test/files/jvm/t6941.check b/test/files/jvm/t6941.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/t6941.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/t6941.flags b/test/files/jvm/t6941.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/jvm/t6941.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/jvm/t6941/Analyzed_1.scala b/test/files/jvm/t6941/Analyzed_1.scala new file mode 100644 index 0000000000..549abd5e64 --- /dev/null +++ b/test/files/jvm/t6941/Analyzed_1.scala @@ -0,0 +1,11 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +class SameBytecode { + def a(xs: List[Int]) = xs match { + case x :: _ => x + } + + def b(xs: List[Int]) = xs match { + case xs: ::[Int] => xs.hd$1 + } +}
\ No newline at end of file diff --git a/test/files/jvm/t6941/test.scala b/test/files/jvm/t6941/test.scala new file mode 100644 index 0000000000..248617f71f --- /dev/null +++ b/test/files/jvm/t6941/test.scala @@ -0,0 +1,15 @@ +import scala.tools.partest.BytecodeTest + +import scala.tools.nsc.util.JavaClassPath +import java.io.InputStream +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, InsnList} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + similarBytecode(getMethod(classNode, "a"), getMethod(classNode, "b"), equalsModuloVar) + } +} diff --git a/test/files/neg/t2968.check b/test/files/neg/t2968.check new file mode 100644 index 0000000000..5d2387f98c --- /dev/null +++ b/test/files/neg/t2968.check @@ -0,0 +1,10 @@ +t2968.scala:8: error: Missing closing brace `}' assumed here +} // missing brace +^ +t2968.scala:17: error: Missing closing brace `}' assumed here +} // missing brace +^ +t2968.scala:26: error: Missing closing brace `}' assumed here +} // missing brace +^ +three errors found diff --git a/test/files/neg/t2968.scala b/test/files/neg/t2968.scala new file mode 100644 index 0000000000..41c3a798a5 --- /dev/null +++ b/test/files/neg/t2968.scala @@ -0,0 +1,26 @@ +object t1 { + case object Const { + } + + class Var + { + +} // missing brace + +object t2 { + case class Const() { + } + + class Var + { + +} // missing brace + +object t3 { + final case class Const() { + } + + class Var + { + +} // missing brace diff --git a/test/files/neg/t2968b.check b/test/files/neg/t2968b.check new file mode 100644 index 0000000000..36d25a2d12 --- /dev/null +++ b/test/files/neg/t2968b.check @@ -0,0 +1,4 @@ +t2968b.scala:7: error: '}' expected but eof found. +// missing brace + ^ +one error found diff --git a/test/files/neg/t2968b.scala b/test/files/neg/t2968b.scala new file mode 100644 index 0000000000..422b618aba --- /dev/null +++ b/test/files/neg/t2968b.scala @@ -0,0 +1,7 @@ +case class Const() +{ +} + +class Var +{ +// missing brace diff --git a/test/files/neg/t5543.check b/test/files/neg/t5543.check new file mode 100644 index 0000000000..b61de0f78b --- /dev/null +++ b/test/files/neg/t5543.check @@ -0,0 +1,10 @@ +t5543.scala:3: error: not found: type T + def this(x: T) { this() } + ^ +t5543.scala:11: error: not found: value x + def this(a: Int, b: Int = x) { + ^ +t5543.scala:18: error: not found: value x + def this(a: Int = x) { this() } + ^ +three errors found diff --git a/test/files/neg/t5543.scala b/test/files/neg/t5543.scala new file mode 100644 index 0000000000..4e03e6e114 --- /dev/null +++ b/test/files/neg/t5543.scala @@ -0,0 +1,19 @@ +class C1 { + type T + def this(x: T) { this() } +} + +class C1a[T] { + def this(x: T) { this() } // works, no error here +} + +class C2(x: Int) { + def this(a: Int, b: Int = x) { + this(b) + } +} + +class C3 { + val x = 0 + def this(a: Int = x) { this() } +} diff --git a/test/files/neg/t6539/Macro_1.scala b/test/files/neg/t6539/Macro_1.scala index ed52776d95..4f7d289e2e 100644 --- a/test/files/neg/t6539/Macro_1.scala +++ b/test/files/neg/t6539/Macro_1.scala @@ -5,6 +5,6 @@ object M { def m(a: Any, b: Any): Any = macro mImpl def mImpl(c: Context)(a: c.Expr[Any], b: c.Expr[Any]) = a - @reflect.macros.compileTimeOnly("cto may only be used as an argument to " + "m") + @reflect.internal.annotations.compileTimeOnly("cto may only be used as an argument to " + "m") def cto = 0 } diff --git a/test/files/neg/t6539/Test_2.scala b/test/files/neg/t6539/Test_2.scala index 5a602879ec..26f4504222 100644 --- a/test/files/neg/t6539/Test_2.scala +++ b/test/files/neg/t6539/Test_2.scala @@ -3,4 +3,10 @@ object Test { M.m(M.cto, ()) // error M.m((), M.cto) // okay M.cto // error + + locally { + val expr = scala.reflect.runtime.universe.reify(2) + val splice = expr.splice + val value = expr.value + } } diff --git a/test/files/neg/t6601.check b/test/files/neg/t6601.check deleted file mode 100644 index 1410e1b11a..0000000000 --- a/test/files/neg/t6601.check +++ /dev/null @@ -1,4 +0,0 @@ -AccessPrivateConstructor_2.scala:2: error: constructor PrivateConstructor in class PrivateConstructor cannot be accessed in class AccessPrivateConstructor - new PrivateConstructor("") // Scalac should forbid accessing to the private constructor! - ^ -one error found diff --git a/test/files/neg/t6601/AccessPrivateConstructor_2.scala b/test/files/neg/t6601/AccessPrivateConstructor_2.scala deleted file mode 100644 index 816bc10d79..0000000000 --- a/test/files/neg/t6601/AccessPrivateConstructor_2.scala +++ /dev/null @@ -1,3 +0,0 @@ -class AccessPrivateConstructor { - new PrivateConstructor("") // Scalac should forbid accessing to the private constructor! -} diff --git a/test/files/neg/t6601/PrivateConstructor_1.scala b/test/files/neg/t6601/PrivateConstructor_1.scala deleted file mode 100644 index f09d7ad068..0000000000 --- a/test/files/neg/t6601/PrivateConstructor_1.scala +++ /dev/null @@ -1 +0,0 @@ -class PrivateConstructor private(val s: String) extends AnyVal diff --git a/test/files/neg/t6829.check b/test/files/neg/t6829.check index 8ee6d182eb..7c3c66e0f2 100644 --- a/test/files/neg/t6829.check +++ b/test/files/neg/t6829.check @@ -1,6 +1,6 @@ t6829.scala:35: error: type mismatch; found : AgentSimulation.this.state.type (with underlying type G#State) - required: _10.State + required: _9.State lazy val actions: Map[G#Agent,G#Action] = agents.map(a => a -> a.chooseAction(state)).toMap ^ t6829.scala:45: error: trait AgentSimulation takes type parameters @@ -17,12 +17,12 @@ t6829.scala:49: error: not found: value nextState ^ t6829.scala:50: error: type mismatch; found : s.type (with underlying type Any) - required: _54.State where val _54: G + required: _53.State where val _53: G val r = rewards(agent).r(s,a,s2) ^ t6829.scala:51: error: type mismatch; found : s.type (with underlying type Any) - required: _51.State + required: _50.State agent.learn(s,a,s2,r): G#Agent ^ t6829.scala:53: error: not found: value nextState diff --git a/test/files/neg/t6963.check b/test/files/neg/t6963.check deleted file mode 100644 index 41cb796b0b..0000000000 --- a/test/files/neg/t6963.check +++ /dev/null @@ -1,2 +0,0 @@ -error: -Xmigration is deprecated: This setting is no longer useful and will be removed. Please remove it from your build. -one error found diff --git a/test/files/neg/t6963.flags b/test/files/neg/t6963.flags deleted file mode 100644 index 0b6d71496a..0000000000 --- a/test/files/neg/t6963.flags +++ /dev/null @@ -1 +0,0 @@ --Xmigration -deprecation -Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t6963.scala b/test/files/neg/t6963.scala deleted file mode 100644 index 4da52764f5..0000000000 --- a/test/files/neg/t6963.scala +++ /dev/null @@ -1,3 +0,0 @@ - -object test { -} diff --git a/test/files/neg/t6963a.check b/test/files/neg/t6963a.check new file mode 100644 index 0000000000..159896fd10 --- /dev/null +++ b/test/files/neg/t6963a.check @@ -0,0 +1,5 @@ +t6963a.scala:4: error: method scanRight in trait TraversableLike has changed semantics in version 2.9.0: +The behavior of `scanRight` has changed. The previous behavior can be reproduced with scanRight.reverse. + List(1,2,3,4,5).scanRight(0)(_+_) + ^ +one error found diff --git a/test/files/neg/t6963a.flags b/test/files/neg/t6963a.flags new file mode 100644 index 0000000000..4c61ed9430 --- /dev/null +++ b/test/files/neg/t6963a.flags @@ -0,0 +1 @@ +-Xfatal-warnings -Xmigration:2.7 diff --git a/test/files/neg/t6963a.scala b/test/files/neg/t6963a.scala new file mode 100644 index 0000000000..b3366b2557 --- /dev/null +++ b/test/files/neg/t6963a.scala @@ -0,0 +1,5 @@ +object Test { + import scala.collection.mutable._ + + List(1,2,3,4,5).scanRight(0)(_+_) +} diff --git a/test/files/neg/t6963b.check b/test/files/neg/t6963b.check new file mode 100644 index 0000000000..7e205a41d0 --- /dev/null +++ b/test/files/neg/t6963b.check @@ -0,0 +1,13 @@ +t6963b.scala:2: error: An Array will no longer match as Seq[_]. + def f1(x: Any) = x.isInstanceOf[Seq[_]] + ^ +t6963b.scala:4: error: An Array will no longer match as Seq[_]. + case _: Seq[_] => true + ^ +t6963b.scala:16: error: An Array will no longer match as Seq[_]. + case (Some(_: Seq[_]), Nil, _) => 1 + ^ +t6963b.scala:17: error: An Array will no longer match as Seq[_]. + case (None, List(_: List[_], _), _) => 2 + ^ +four errors found diff --git a/test/files/neg/t6963b.flags b/test/files/neg/t6963b.flags new file mode 100644 index 0000000000..83caa2b147 --- /dev/null +++ b/test/files/neg/t6963b.flags @@ -0,0 +1 @@ +-Xmigration:2.7 -Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t6963b.scala b/test/files/neg/t6963b.scala new file mode 100644 index 0000000000..3cfa8f0dca --- /dev/null +++ b/test/files/neg/t6963b.scala @@ -0,0 +1,20 @@ +object Test { + def f1(x: Any) = x.isInstanceOf[Seq[_]] + def f2(x: Any) = x match { + case _: Seq[_] => true + case _ => false + } + + def f3(x: Any) = x match { + case _: Array[_] => true + case _ => false + } + + def f4(x: Any) = x.isInstanceOf[Traversable[_]] + + def f5(x1: Any, x2: Any, x3: AnyRef) = (x1, x2, x3) match { + case (Some(_: Seq[_]), Nil, _) => 1 + case (None, List(_: List[_], _), _) => 2 + case _ => 3 + } +} diff --git a/test/files/pos/lubs.scala b/test/files/pos/lubs.scala new file mode 100644 index 0000000000..d7651f86b0 --- /dev/null +++ b/test/files/pos/lubs.scala @@ -0,0 +1,3 @@ +object Test { + List(new { def f = 1; def g = 1}, new { def f = 2}).map(_.f) +} diff --git a/test/files/pos/presuperContext.scala b/test/files/pos/presuperContext.scala new file mode 100644 index 0000000000..cc34263073 --- /dev/null +++ b/test/files/pos/presuperContext.scala @@ -0,0 +1,13 @@ +class A { + class C extends { val x: A = this } with AnyRef +} + +class B(x: Int) + +class D { + class C(x: Int) extends B({val test: D = this; x}) { + def this() { + this({val test: D = this; 1}) + } + } +} diff --git a/test/files/pos/t1014.scala b/test/files/pos/t1014.scala index 1ac87b225b..3fc10d10dc 100644 --- a/test/files/pos/t1014.scala +++ b/test/files/pos/t1014.scala @@ -1,6 +1,8 @@ import scala.xml.{NodeSeq, Elem} -class EO extends App with Moo{ +class EO extends App with Moo { + // return type is Flog, inherited from overridden method. + // implicit conversions are applied because expected type `pt` is `Flog` when `computeType(rhs, pt)`. def cat = <cat>dog</cat> implicit def nodeSeqToFlog(in: Elem): Flog = new Flog(in) diff --git a/test/files/pos/t1803.flags b/test/files/pos/t1803.flags new file mode 100644 index 0000000000..d1a8244169 --- /dev/null +++ b/test/files/pos/t1803.flags @@ -0,0 +1 @@ +-Yinfer-argument-types
\ No newline at end of file diff --git a/test/files/pos/t1803.scala b/test/files/pos/t1803.scala new file mode 100644 index 0000000000..42f4e784a3 --- /dev/null +++ b/test/files/pos/t1803.scala @@ -0,0 +1,2 @@ +class A { def foo[A](a: A) = a } +class B extends A { override def foo[A](b) = b } diff --git a/test/files/pos/t6482.scala b/test/files/pos/t6482.scala new file mode 100644 index 0000000000..24ea38e519 --- /dev/null +++ b/test/files/pos/t6482.scala @@ -0,0 +1,11 @@ +final class TraversableOnceOps[+A](val collection: TraversableOnce[A]) extends AnyVal { + def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] = + if (collection.isEmpty) None else Some(collection.reduceLeft[B](op)) +} +// error: type arguments [B] do not conform to method reduceLeft's type parameter bounds [B >: A] +// if (collection.isEmpty) None else Some(collection.reduceLeft[B](op)) +// ^ + +class Foo[+A <: AnyRef](val xs: List[A]) extends AnyVal { + def baz[B >: A](x: B): List[B] = x :: xs +} diff --git a/test/files/pos/t6601/PrivateValueClass_1.scala b/test/files/pos/t6601/PrivateValueClass_1.scala new file mode 100644 index 0000000000..85c3687137 --- /dev/null +++ b/test/files/pos/t6601/PrivateValueClass_1.scala @@ -0,0 +1 @@ +class V private (val a: Any) extends AnyVal
\ No newline at end of file diff --git a/test/files/pos/t6601/UsePrivateValueClass_2.scala b/test/files/pos/t6601/UsePrivateValueClass_2.scala new file mode 100644 index 0000000000..461b8397b2 --- /dev/null +++ b/test/files/pos/t6601/UsePrivateValueClass_2.scala @@ -0,0 +1,10 @@ +object Test { + // After the first attempt to make seprately compiled value + // classes respect the privacy of constructors, we got: + // + // exception when typing v.a().==(v.a())/class scala.reflect.internal.Trees$Apply + // constructor V in class V cannot be accessed in object Test in file test/files/pos/t6601/UsePrivateValueClass_2.scala + // scala.reflect.internal.Types$TypeError: constructor V in class V cannot be accessed in object Test + def foo(v: V) = v.a == v.a + def bar(v: V) = v == v +} diff --git a/test/files/pos/t6963c.flags b/test/files/pos/t6963c.flags new file mode 100644 index 0000000000..4d6e04914f --- /dev/null +++ b/test/files/pos/t6963c.flags @@ -0,0 +1 @@ +-Xmigration:2.9 -Xfatal-warnings
\ No newline at end of file diff --git a/test/files/pos/t6963c.scala b/test/files/pos/t6963c.scala new file mode 100644 index 0000000000..0b6b5c757f --- /dev/null +++ b/test/files/pos/t6963c.scala @@ -0,0 +1,25 @@ +object Test { + def f1(x: Any) = x.isInstanceOf[Seq[_]] + def f2(x: Any) = x match { + case _: Seq[_] => true + case _ => false + } + + def f3(x: Any) = x match { + case _: Array[_] => true + case _ => false + } + + def f4(x: Any) = x.isInstanceOf[Traversable[_]] + + def f5(x1: Any, x2: Any, x3: AnyRef) = (x1, x2, x3) match { + case (Some(_: Seq[_]), Nil, _) => 1 + case (None, List(_: List[_], _), _) => 2 + case _ => 3 + } + + def f5: Unit = { + import scala.collection.mutable._ + List(1,2,3,4,5).scanRight(0)(_+_) + } +} diff --git a/test/files/pos/t7022.scala b/test/files/pos/t7022.scala new file mode 100644 index 0000000000..0609e2d250 --- /dev/null +++ b/test/files/pos/t7022.scala @@ -0,0 +1,9 @@ +class Catch[+T] { + def either[U >: T](body: => U): Either[Throwable, U] = ??? +} + +object Test { + implicit class RichCatch[T](val c: Catch[T]) extends AnyVal { + def validation[U >: T](u: => U): Either[Throwable, U] = c.either(u) + } +} diff --git a/test/files/pos/t7033.scala b/test/files/pos/t7033.scala new file mode 100644 index 0000000000..a4d256673b --- /dev/null +++ b/test/files/pos/t7033.scala @@ -0,0 +1,15 @@ +import language.higherKinds +object Wrap { + implicit class X[X](val a: X) + + X[Int](0) +} + +class Wrap { + implicit class Y[Y](val a: Y) + Y[Int](0) + implicit class Z[Z[_]](val a: Z[Wrap.this.Z[Z]]) + Z[List](List(new Z[List](null))) +} + +case class X[X](val a: X) diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check new file mode 100644 index 0000000000..8856fef5b3 --- /dev/null +++ b/test/files/run/analyzerPlugins.check @@ -0,0 +1,197 @@ +adaptBoundsToAnnots(List( <: Int), List(type T), List(Int @testAnn)) [2] +annotationsConform(Boolean @testAnn, Boolean) [1] +annotationsConform(Boolean(false), Boolean @testAnn) [1] +annotationsConform(Int @testAnn, ?A) [1] +annotationsConform(Int @testAnn, Any) [1] +annotationsConform(Int @testAnn, Int) [2] +annotationsConform(Int(1) @testAnn, Int) [1] +annotationsConform(Int(1), Int @testAnn) [1] +annotationsConform(Nothing, Int @testAnn) [2] +annotationsConform(String @testAnn, String) [1] +canAdaptAnnotations(Trees$Ident, String) [1] +canAdaptAnnotations(Trees$Select, ?) [1] +canAdaptAnnotations(Trees$Select, Boolean @testAnn) [1] +canAdaptAnnotations(Trees$Select, Boolean) [1] +canAdaptAnnotations(Trees$Select, String @testAnn) [1] +canAdaptAnnotations(Trees$TypeTree, ?) [10] +canAdaptAnnotations(Trees$Typed, ?) [3] +canAdaptAnnotations(Trees$Typed, Any) [1] +canAdaptAnnotations(Trees$Typed, Int) [1] +lub(List(Int @testAnn, Int)) [1] +pluginsPt(?, Trees$Annotated) [7] +pluginsPt(?, Trees$Apply) [8] +pluginsPt(?, Trees$ApplyImplicitView) [2] +pluginsPt(?, Trees$Assign) [7] +pluginsPt(?, Trees$Block) [4] +pluginsPt(?, Trees$ClassDef) [2] +pluginsPt(?, Trees$DefDef) [14] +pluginsPt(?, Trees$Ident) [51] +pluginsPt(?, Trees$If) [2] +pluginsPt(?, Trees$Literal) [16] +pluginsPt(?, Trees$New) [5] +pluginsPt(?, Trees$PackageDef) [1] +pluginsPt(?, Trees$Return) [1] +pluginsPt(?, Trees$Select) [51] +pluginsPt(?, Trees$Super) [2] +pluginsPt(?, Trees$This) [20] +pluginsPt(?, Trees$TypeApply) [3] +pluginsPt(?, Trees$TypeBoundsTree) [2] +pluginsPt(?, Trees$TypeDef) [1] +pluginsPt(?, Trees$TypeTree) [38] +pluginsPt(?, Trees$Typed) [1] +pluginsPt(?, Trees$ValDef) [21] +pluginsPt(Any, Trees$Literal) [2] +pluginsPt(Any, Trees$Typed) [1] +pluginsPt(Array[Any], Trees$ArrayValue) [1] +pluginsPt(Boolean @testAnn, Trees$Literal) [1] +pluginsPt(Boolean @testAnn, Trees$Select) [1] +pluginsPt(Boolean, Trees$Apply) [1] +pluginsPt(Boolean, Trees$Ident) [1] +pluginsPt(Boolean, Trees$Literal) [1] +pluginsPt(Double, Trees$Select) [1] +pluginsPt(Int @testAnn, Trees$Literal) [1] +pluginsPt(Int, Trees$Apply) [1] +pluginsPt(Int, Trees$Ident) [2] +pluginsPt(Int, Trees$If) [1] +pluginsPt(Int, Trees$Literal) [5] +pluginsPt(Int, Trees$Select) [3] +pluginsPt(List, Trees$Apply) [1] +pluginsPt(List[Any], Trees$Select) [1] +pluginsPt(String @testAnn, Trees$Select) [1] +pluginsPt(String, Trees$Apply) [1] +pluginsPt(String, Trees$Block) [2] +pluginsPt(String, Trees$Ident) [4] +pluginsPt(String, Trees$Literal) [1] +pluginsPt(String, Trees$Select) [1] +pluginsPt(String, Trees$Typed) [1] +pluginsPt(Unit, Trees$Assign) [1] +pluginsPt(scala.annotation.Annotation, Trees$Apply) [5] +pluginsTypeSig(<none>, Trees$Template) [2] +pluginsTypeSig(class A, Trees$ClassDef) [1] +pluginsTypeSig(class testAnn, Trees$ClassDef) [1] +pluginsTypeSig(constructor A, Trees$DefDef) [2] +pluginsTypeSig(constructor testAnn, Trees$DefDef) [1] +pluginsTypeSig(method foo, Trees$DefDef) [1] +pluginsTypeSig(method method, Trees$DefDef) [1] +pluginsTypeSig(method nested, Trees$DefDef) [1] +pluginsTypeSig(type T, Trees$TypeDef) [2] +pluginsTypeSig(value annotField, Trees$ValDef) [2] +pluginsTypeSig(value f, Trees$ValDef) [1] +pluginsTypeSig(value inferField, Trees$ValDef) [2] +pluginsTypeSig(value lub1, Trees$ValDef) [2] +pluginsTypeSig(value lub2, Trees$ValDef) [2] +pluginsTypeSig(value param, Trees$ValDef) [2] +pluginsTypeSig(value str, Trees$ValDef) [1] +pluginsTypeSig(value x, Trees$ValDef) [4] +pluginsTypeSig(value y, Trees$ValDef) [4] +pluginsTypeSig(variable count, Trees$ValDef) [3] +pluginsTypeSigAccessor(value annotField) [1] +pluginsTypeSigAccessor(value inferField) [1] +pluginsTypeSigAccessor(value lub1) [1] +pluginsTypeSigAccessor(value lub2) [1] +pluginsTypeSigAccessor(value x) [1] +pluginsTypeSigAccessor(value y) [1] +pluginsTypeSigAccessor(variable count) [2] +pluginsTyped( <: Int, Trees$TypeBoundsTree) [2] +pluginsTyped(()Object, Trees$Select) [1] +pluginsTyped(()String, Trees$Ident) [1] +pluginsTyped(()String, Trees$TypeApply) [1] +pluginsTyped(()scala.annotation.Annotation, Trees$Select) [1] +pluginsTyped(()testAnn, Trees$Select) [10] +pluginsTyped((str: String)A <and> (param: Double)A, Trees$Select) [1] +pluginsTyped((x$1: Any)Boolean <and> (x: Double)Boolean <and> (x: Float)Boolean <and> (x: Long)Boolean <and> (x: Int)Boolean <and> (x: Char)Boolean <and> (x: Short)Boolean <and> (x: Byte)Boolean, Trees$Select) [1] +pluginsTyped((x$1: Int)Unit, Trees$Select) [1] +pluginsTyped((x: Double)Double <and> (x: Float)Float <and> (x: Long)Long <and> (x: Int)Int <and> (x: Char)Int <and> (x: Short)Int <and> (x: Byte)Int <and> (x: String)String, Trees$Select) [1] +pluginsTyped((x: String)scala.collection.immutable.StringOps, Trees$Select) [2] +pluginsTyped((xs: Array[Any])scala.collection.mutable.WrappedArray[Any], Trees$TypeApply) [1] +pluginsTyped(<empty>.type, Trees$Ident) [1] +pluginsTyped(<error>, Trees$Select) [1] +pluginsTyped(<notype>, Trees$ClassDef) [2] +pluginsTyped(<notype>, Trees$DefDef) [14] +pluginsTyped(<notype>, Trees$PackageDef) [1] +pluginsTyped(<notype>, Trees$TypeDef) [1] +pluginsTyped(<notype>, Trees$ValDef) [21] +pluginsTyped(<root>, Trees$Ident) [1] +pluginsTyped(=> Boolean @testAnn, Trees$Select) [1] +pluginsTyped(=> Double, Trees$Select) [4] +pluginsTyped(=> Int, Trees$Select) [5] +pluginsTyped(=> Int, Trees$TypeApply) [1] +pluginsTyped(=> String @testAnn, Trees$Select) [1] +pluginsTyped(A, Trees$Apply) [1] +pluginsTyped(A, Trees$Ident) [2] +pluginsTyped(A, Trees$This) [8] +pluginsTyped(A, Trees$TypeTree) [4] +pluginsTyped(A.super.type, Trees$Super) [1] +pluginsTyped(A.this.type, Trees$This) [11] +pluginsTyped(Any, Trees$TypeTree) [1] +pluginsTyped(AnyRef, Trees$Select) [4] +pluginsTyped(Array[Any], Trees$ArrayValue) [1] +pluginsTyped(Boolean @testAnn, Trees$Select) [1] +pluginsTyped(Boolean @testAnn, Trees$TypeTree) [4] +pluginsTyped(Boolean(false), Trees$Literal) [2] +pluginsTyped(Boolean, Trees$Apply) [1] +pluginsTyped(Boolean, Trees$Select) [4] +pluginsTyped(Char('c'), Trees$Literal) [2] +pluginsTyped(Double, Trees$Select) [6] +pluginsTyped(Int @testAnn, Trees$TypeTree) [2] +pluginsTyped(Int @testAnn, Trees$Typed) [2] +pluginsTyped(Int(0), Trees$Literal) [3] +pluginsTyped(Int(1) @testAnn, Trees$Typed) [1] +pluginsTyped(Int(1), Trees$Literal) [8] +pluginsTyped(Int(2), Trees$Literal) [1] +pluginsTyped(Int, Trees$Apply) [1] +pluginsTyped(Int, Trees$Ident) [2] +pluginsTyped(Int, Trees$If) [2] +pluginsTyped(Int, Trees$Select) [15] +pluginsTyped(Int, Trees$TypeTree) [13] +pluginsTyped(List, Trees$Apply) [1] +pluginsTyped(List, Trees$Select) [1] +pluginsTyped(List[Any], Trees$Apply) [1] +pluginsTyped(List[Any], Trees$Select) [1] +pluginsTyped(List[Any], Trees$TypeTree) [3] +pluginsTyped(Nothing, Trees$Return) [1] +pluginsTyped(Nothing, Trees$Select) [2] +pluginsTyped(Object, Trees$Apply) [1] +pluginsTyped(String @testAnn, Trees$Ident) [1] +pluginsTyped(String @testAnn, Trees$Select) [1] +pluginsTyped(String @testAnn, Trees$TypeTree) [4] +pluginsTyped(String(""), Trees$Literal) [2] +pluginsTyped(String("huhu"), Trees$Literal) [1] +pluginsTyped(String("str") @testAnn, Trees$Typed) [1] +pluginsTyped(String("str"), Trees$Literal) [1] +pluginsTyped(String("str"), Trees$Typed) [1] +pluginsTyped(String("two"), Trees$Literal) [2] +pluginsTyped(String, Trees$Apply) [2] +pluginsTyped(String, Trees$Block) [2] +pluginsTyped(String, Trees$Ident) [1] +pluginsTyped(String, Trees$Select) [9] +pluginsTyped(String, Trees$TypeTree) [7] +pluginsTyped(Unit, Trees$Apply) [2] +pluginsTyped(Unit, Trees$Assign) [8] +pluginsTyped(Unit, Trees$Block) [4] +pluginsTyped(Unit, Trees$If) [1] +pluginsTyped(Unit, Trees$Literal) [5] +pluginsTyped(Unit, Trees$TypeTree) [1] +pluginsTyped([A](xs: A*)List[A], Trees$Select) [1] +pluginsTyped([T <: Int]=> Int, Trees$Select) [1] +pluginsTyped([T0 >: ? <: ?]()T0, Trees$Select) [1] +pluginsTyped([T](xs: Array[T])scala.collection.mutable.WrappedArray[T], Trees$Select) [1] +pluginsTyped(annotation.type, Trees$Select) [4] +pluginsTyped(math.type, Trees$Select) [9] +pluginsTyped(scala.annotation.Annotation, Trees$Apply) [1] +pluginsTyped(scala.annotation.TypeConstraint, Trees$Select) [4] +pluginsTyped(scala.annotation.TypeConstraint, Trees$TypeTree) [2] +pluginsTyped(scala.collection.immutable.List.type, Trees$Select) [2] +pluginsTyped(scala.collection.immutable.StringOps, Trees$ApplyImplicitView) [2] +pluginsTyped(scala.collection.mutable.WrappedArray[Any], Trees$Apply) [1] +pluginsTyped(scala.type, Trees$Ident) [1] +pluginsTyped(scala.type, Trees$Select) [1] +pluginsTyped(str.type, Trees$Ident) [3] +pluginsTyped(testAnn, Trees$Apply) [5] +pluginsTyped(testAnn, Trees$Ident) [5] +pluginsTyped(testAnn, Trees$New) [5] +pluginsTyped(testAnn, Trees$This) [1] +pluginsTyped(testAnn, Trees$TypeTree) [2] +pluginsTyped(testAnn.super.type, Trees$Super) [1] +pluginsTyped(type, Trees$Select) [1] +pluginsTypedReturn(return f, String) [1] diff --git a/test/files/run/analyzerPlugins.scala b/test/files/run/analyzerPlugins.scala new file mode 100644 index 0000000000..daef83fa30 --- /dev/null +++ b/test/files/run/analyzerPlugins.scala @@ -0,0 +1,121 @@ +import scala.tools.partest._ +import scala.tools.nsc._ + +object Test extends DirectTest { + + override def extraSettings: String = "-usejavacp" + + def code = """ + class testAnn extends annotation.TypeConstraint + + class A(param: Double) extends { val x: Int = 1; val y = "two"; type T = A } with AnyRef { + val inferField = ("str": @testAnn) + val annotField: Boolean @testAnn = false + + val lub1 = List('c', (1: Int @testAnn), "") + val lub2 = if (annotField) (1: @testAnn) else 2 + + def foo[T <: Int] = 0 + foo[Int @testAnn] + + var count = 0 + + math.random // some statement + + def method: String = { + math.random + val f = inferField + + def nested(): String = { + if(count == 1) + return f + "huhu" + } + nested() + } + + def this(str: String) { + this(str.toDouble) + math.random + count += 1 + } + } + """.trim + + + def show() { + val global = newCompiler() + import global._ + import analyzer._ + + val output = collection.mutable.ListBuffer[String]() + + object annotChecker extends AnnotationChecker { + def hasTestAnn(tps: Type*) = { + tps exists (_.annotations.map(_.toString) contains "testAnn") + } + + def annotationsConform(tpe1: Type, tpe2: Type): Boolean = { + if (hasTestAnn(tpe1, tpe2)) + output += s"annotationsConform($tpe1, $tpe2)" + true + } + + override def annotationsLub(tp: Type, ts: List[Type]): Type = { + if (hasTestAnn(ts: _*)) + output += s"lub($ts)" + tp + } + + override def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = { + if (hasTestAnn(targs: _*)) + output += s"adaptBoundsToAnnots($bounds, $tparams, $targs)" + bounds + } + } + + object analyzerPlugin extends AnalyzerPlugin { + def treeClass(t: Tree) = t.getClass.toString.split('.').last + + override def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = { + output += s"pluginsPt($pt, ${treeClass(tree)})" + pt + } + + override def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = { + output += s"pluginsTyped($tpe, ${treeClass(tree)})" + tpe + } + + override def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = { + output += s"pluginsTypeSig(${defTree.symbol}, ${treeClass(defTree)})" + tpe + } + + override def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = { + output += s"pluginsTypeSigAccessor(${tree.symbol})" + tpe + } + + + override def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = { + output += s"canAdaptAnnotations(${treeClass(tree)}, $pt)" + false + } + + override def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = { + output += s"pluginsTypedReturn($tree, $pt)" + tpe + } + + } + + addAnnotationChecker(annotChecker) + addAnalyzerPlugin(analyzerPlugin) + compileString(global)(code) + + val res = output.groupBy(identity).mapValues(_.size).map { case (k,v) => s"$k [$v]" }.toList.sorted + println(res.mkString("\n")) + } + +} diff --git a/test/files/run/idempotency-case-classes.check b/test/files/run/idempotency-case-classes.check index 700af3b81b..80d178cba3 100644 --- a/test/files/run/idempotency-case-classes.check +++ b/test/files/run/idempotency-case-classes.check @@ -42,7 +42,7 @@ C(2,3) C.super.<init>(); () }; - final override def toString(): String = "C"; + final override <synthetic> def toString(): String = "C"; case <synthetic> def apply(x: Int, y: Int): C = new C(x, y); case <synthetic> def unapply(x$0: C): Option[(Int, Int)] = if (x$0.==(null)) scala.this.None diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check index 282542a732..f2f0b60687 100644 --- a/test/files/run/inline-ex-handlers.check +++ b/test/files/run/inline-ex-handlers.check @@ -21,59 +21,60 @@ < 92 JUMP 7 < < 7: -395c391 +391c387 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, value x4, value x5, value x -397c393 -< blocks: [1,2,3,4,5,8,11,13,14,16] +393c389 +< blocks: [1,2,3,4,5,8,10,11,13] --- -> blocks: [1,2,3,5,8,11,13,14,16,17] -421c417,426 +> blocks: [1,2,3,5,8,10,11,13,14] +417c413,422 < 103 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 17 +> ? JUMP 14 > -> 17: +> 14: > 101 LOAD_LOCAL(value ex6) > 101 STORE_LOCAL(value x4) > 101 SCOPE_ENTER value x4 > 106 LOAD_LOCAL(value x4) > 106 IS_INSTANCE REF(class MyException) -> 106 CZJUMP (BOOL)NE ? 5 : 11 -434,436d438 +> 106 CZJUMP (BOOL)NE ? 5 : 8 +430,432d434 < 101 JUMP 4 < < 4: -450,453d451 +442,445d443 < 106 LOAD_LOCAL(value x5) < 106 CALL_METHOD MyException.message (dynamic) < 106 STORE_LOCAL(value message) < 106 SCOPE_ENTER value message -455c453,454 +447c445,446 < 106 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 106 CALL_METHOD MyException.message (dynamic) -527c526 +519c518 < blocks: [1,2,3,4,6,7,8,9,10] --- > blocks: [1,2,3,4,6,7,8,9,10,11,12,13] -556c555,560 +548c547 < 306 THROW(MyException) --- > ? JUMP 11 -> +549a549,553 > 11: > ? LOAD_LOCAL(variable monitor4) > 305 MONITOR_EXIT > ? JUMP 12 -562c566 +> +554c558 < ? THROW(Throwable) --- > ? JUMP 12 -568c572,579 +560c564,571 < ? THROW(Throwable) --- > ? STORE_LOCAL(value t) @@ -84,7 +85,7 @@ > 304 MONITOR_EXIT > ? STORE_LOCAL(value t) > ? JUMP 13 -583a595,606 +575a587,598 > 13: > 310 LOAD_MODULE object Predef > 310 CALL_PRIMITIVE(StartConcat) @@ -97,35 +98,35 @@ > 310 CALL_METHOD scala.Predef.println (dynamic) > 310 JUMP 2 > -592c615 +584c607 < catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6 --- > catch (Throwable) in ArrayBuffer(7, 8, 9, 10, 11) starting at: 6 -595c618 +587c610 < catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10) starting at: 3 --- > catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10, 11, 12) starting at: 3 -627c650 +619c642 < blocks: [1,2,3,4,5,6,7,9,10] --- > blocks: [1,2,3,4,5,6,7,9,10,11,12] -651c674,675 +643c666,667 < 78 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 11 -652a677,681 +644a669,673 > 11: > 81 LOAD_LOCAL(value e) > ? STORE_LOCAL(variable exc1) > ? JUMP 12 > -680c709,710 +672c701,702 < 81 THROW(Exception) --- > ? STORE_LOCAL(variable exc1) > ? JUMP 12 -696a727,739 +688a719,731 > 12: > 83 LOAD_MODULE object Predef > 83 CONSTANT("finally") @@ -139,88 +140,88 @@ > 84 LOAD_LOCAL(variable exc1) > 84 THROW(Throwable) > -702c745 +694c737 < catch (<none>) in ArrayBuffer(4, 6, 7, 9) starting at: 3 --- > catch (<none>) in ArrayBuffer(4, 6, 7, 9, 11) starting at: 3 -726c769 +718c761 < locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x -728c771 -< blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31] +720c763 +< blocks: [1,2,3,4,5,6,9,11,14,15,16,19,21,22,24,25] --- -> blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31,32,33,34] -752c795,802 +> blocks: [1,2,3,4,5,6,9,11,14,15,16,19,21,22,24,25,26,27,28] +744c787,794 < 172 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 32 +> ? JUMP 26 > -> 32: +> 26: > 170 LOAD_LOCAL(value ex6) > 170 STORE_LOCAL(value x4) > 170 SCOPE_ENTER value x4 -> 170 JUMP 18 -799,802d848 +> 170 JUMP 15 +787,790d836 < 175 LOAD_LOCAL(value x5) < 175 CALL_METHOD MyException.message (dynamic) < 175 STORE_LOCAL(value message) < 175 SCOPE_ENTER value message -804c850,851 +792c838,839 < 176 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 176 CALL_METHOD MyException.message (dynamic) -808c855,856 +796c843,844 < 177 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 177 CALL_METHOD MyException.message (dynamic) -810c858,859 +798c846,847 < 177 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 33 -814c863,864 +> ? JUMP 27 +802c851,852 < 170 THROW(Throwable) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 33 -823a874,879 -> 33: +> ? JUMP 27 +811a862,867 +> 27: > 169 LOAD_LOCAL(value ex6) > 169 STORE_LOCAL(value x4) > 169 SCOPE_ENTER value x4 > 169 JUMP 5 > -838,841d893 +822,825d877 < 180 LOAD_LOCAL(value x5) < 180 CALL_METHOD MyException.message (dynamic) < 180 STORE_LOCAL(value message) < 180 SCOPE_ENTER value message -843c895,896 +827c879,880 < 181 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 181 CALL_METHOD MyException.message (dynamic) -847c900,901 +831c884,885 < 182 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 182 CALL_METHOD MyException.message (dynamic) -849c903,904 +833c887,888 < 182 THROW(MyException) --- > ? STORE_LOCAL(variable exc2) -> ? JUMP 34 -853c908,909 +> ? JUMP 28 +837c892,893 < 169 THROW(Throwable) --- > ? STORE_LOCAL(variable exc2) -> ? JUMP 34 -869a926,938 -> 34: +> ? JUMP 28 +853a910,922 +> 28: > 184 LOAD_MODULE object Predef > 184 CONSTANT("finally") > 184 CALL_METHOD scala.Predef.println (dynamic) @@ -233,159 +234,158 @@ > 185 LOAD_LOCAL(variable exc2) > 185 THROW(Throwable) > -875c944 -< catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30) starting at: 4 +859c928 +< catch (Throwable) in ArrayBuffer(14, 15, 16, 19, 21, 22, 24) starting at: 4 --- -> catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30, 32) starting at: 4 -878c947 -< catch (<none>) in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30) starting at: 3 +> catch (Throwable) in ArrayBuffer(14, 15, 16, 19, 21, 22, 24, 26) starting at: 4 +862c931 +< catch (<none>) in ArrayBuffer(4, 5, 6, 9, 14, 15, 16, 19, 21, 22, 24) starting at: 3 --- -> catch (<none>) in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30, 32, 33) starting at: 3 -902c971 +> catch (<none>) in ArrayBuffer(4, 5, 6, 9, 14, 15, 16, 19, 21, 22, 24, 26, 27) starting at: 3 +886c955 < locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value e, value ex6, value x4, value x5, value x -904c973 -< blocks: [1,2,3,6,7,8,11,14,16,17,19] +888c957 +< blocks: [1,2,3,6,7,8,11,13,14,16] --- -> blocks: [1,2,3,6,7,8,11,14,16,17,19,20] -928c997,1004 +> blocks: [1,2,3,6,7,8,11,13,14,16,17] +912c981,988 < 124 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 20 +> ? JUMP 17 > -> 20: +> 17: > 122 LOAD_LOCAL(value ex6) > 122 STORE_LOCAL(value x4) > 122 SCOPE_ENTER value x4 > 122 JUMP 7 -957,960d1032 +937,940d1012 < 127 LOAD_LOCAL(value x5) < 127 CALL_METHOD MyException.message (dynamic) < 127 STORE_LOCAL(value message) < 127 SCOPE_ENTER value message -962c1034,1035 +942c1014,1015 < 127 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 127 CALL_METHOD MyException.message (dynamic) -991c1064 -< catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19) starting at: 3 +971c1044 +< catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16) starting at: 3 --- -> catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19, 20) starting at: 3 -1015c1088 +> catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16, 17) starting at: 3 +995c1068 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e --- > locals: value args, variable result, value ex6, value x4, value x5, value x, value e -1017c1090 -< blocks: [1,2,3,4,5,8,11,15,16,17,19] +997c1070 +< blocks: [1,2,3,4,5,8,12,13,14,16] --- -> blocks: [1,2,3,5,8,11,15,16,17,19,20] -1041c1114,1123 +> blocks: [1,2,3,5,8,12,13,14,16,17] +1021c1094,1103 < 148 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 20 +> ? JUMP 17 > -> 20: +> 17: > 145 LOAD_LOCAL(value ex6) > 145 STORE_LOCAL(value x4) > 145 SCOPE_ENTER value x4 > 154 LOAD_LOCAL(value x4) > 154 IS_INSTANCE REF(class MyException) -> 154 CZJUMP (BOOL)NE ? 5 : 11 -1062,1064d1143 +> 154 CZJUMP (BOOL)NE ? 5 : 8 +1042,1044d1123 < 145 JUMP 4 < < 4: -1078,1081d1156 +1054,1057d1132 < 154 LOAD_LOCAL(value x5) < 154 CALL_METHOD MyException.message (dynamic) < 154 STORE_LOCAL(value message) < 154 SCOPE_ENTER value message -1083c1158,1159 +1059c1134,1135 < 154 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 154 CALL_METHOD MyException.message (dynamic) -1300c1376 +1276c1352 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1324c1400,1401 +1300c1376,1383 < 38 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 8 -1325a1403,1408 +> > 8: > 42 LOAD_MODULE object Predef > 42 CONSTANT("IllegalArgumentException") > 42 CALL_METHOD scala.Predef.println (dynamic) > 42 JUMP 2 -> -1371c1454 +1347c1430 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, value x4, value x5, value x -1373c1456 -< blocks: [1,2,3,4,5,8,11,13,14,16,17,19] +1349c1432 +< blocks: [1,2,3,4,5,8,10,11,13,14,16] --- -> blocks: [1,2,3,5,8,11,13,14,16,17,19,20] -1397c1480,1481 +> blocks: [1,2,3,5,8,10,11,13,14,16,17] +1373c1456,1457 < 203 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 20 -1417c1501,1510 +> ? JUMP 17 +1393c1477,1486 < 209 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 20 +> ? JUMP 17 > -> 20: +> 17: > 200 LOAD_LOCAL(value ex6) > 200 STORE_LOCAL(value x4) > 200 SCOPE_ENTER value x4 > 212 LOAD_LOCAL(value x4) > 212 IS_INSTANCE REF(class MyException) -> 212 CZJUMP (BOOL)NE ? 5 : 11 -1430,1432d1522 +> 212 CZJUMP (BOOL)NE ? 5 : 8 +1406,1408d1498 < 200 JUMP 4 < < 4: -1446,1449d1535 +1418,1421d1507 < 212 LOAD_LOCAL(value x5) < 212 CALL_METHOD MyException.message (dynamic) < 212 STORE_LOCAL(value message) < 212 SCOPE_ENTER value message -1451c1537,1538 +1423c1509,1510 < 213 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 213 CALL_METHOD MyException.message (dynamic) -1495c1582 +1467c1554 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1519c1606,1607 +1491c1578,1579 < 58 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 8 -1520a1609,1614 +1492a1581,1586 > 8: > 62 LOAD_MODULE object Predef > 62 CONSTANT("RuntimeException") > 62 CALL_METHOD scala.Predef.println (dynamic) > 62 JUMP 2 > -1568c1662 +1540c1634 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1588c1682,1687 +1560c1654,1659 < 229 THROW(MyException) --- > ? JUMP 5 @@ -394,19 +394,19 @@ > ? LOAD_LOCAL(variable monitor1) > 228 MONITOR_EXIT > 228 THROW(Throwable) -1594c1693 +1566c1665 < ? THROW(Throwable) --- > 228 THROW(Throwable) -1622c1721 +1594c1693 < locals: value args, variable result, variable monitor2, variable monitorResult1 --- > locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 -1624c1723 +1596c1695 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1647c1746,1754 +1619c1718,1726 < 245 THROW(MyException) --- > ? STORE_LOCAL(value exception$1) @@ -418,7 +418,7 @@ > ? LOAD_LOCAL(variable monitor2) > 244 MONITOR_EXIT > 244 THROW(Throwable) -1653c1760 +1625c1732 < ? THROW(Throwable) --- > 244 THROW(Throwable) diff --git a/test/files/run/t2818.check b/test/files/run/t2818.check new file mode 100644 index 0000000000..31286c990b --- /dev/null +++ b/test/files/run/t2818.check @@ -0,0 +1,4 @@ +105 +499999500000 +0 +1 diff --git a/test/files/run/t2818.scala b/test/files/run/t2818.scala new file mode 100644 index 0000000000..19b67cbc88 --- /dev/null +++ b/test/files/run/t2818.scala @@ -0,0 +1,6 @@ +object Test extends App { + println((List.range(1L, 15L) :\ 0L) (_ + _)) + println((List.range(1L, 1000000L) :\ 0L) (_ + _)) + println((List.fill(5)(1) :\ 1) (_ - _)) + println((List.fill(1000000)(1) :\ 1) (_ - _)) +} diff --git a/test/files/run/t3353.check b/test/files/run/t3353.check new file mode 100644 index 0000000000..8b4ae1fe69 --- /dev/null +++ b/test/files/run/t3353.check @@ -0,0 +1 @@ +Got: foo and None diff --git a/test/files/run/t3353.scala b/test/files/run/t3353.scala new file mode 100644 index 0000000000..eeb63c1b05 --- /dev/null +++ b/test/files/run/t3353.scala @@ -0,0 +1,10 @@ +object Test extends App { + + "foo" match { + case Matcher(result) => println(result) + } + + object Matcher{ + def unapply(s: String)(implicit secondParam: Option[String] = None) = Some("Got: " + s + " and " + secondParam) + } +} diff --git a/test/files/run/t5313.check b/test/files/run/t5313.check new file mode 100644 index 0000000000..7a48b2b711 --- /dev/null +++ b/test/files/run/t5313.check @@ -0,0 +1,12 @@ +STORE_LOCAL(variable kept1) +STORE_LOCAL(value result) +STORE_LOCAL(variable kept1) +STORE_LOCAL(variable kept2) +STORE_LOCAL(value kept3) +STORE_LOCAL(variable kept2) +STORE_LOCAL(variable kept4) +STORE_LOCAL(variable kept4) +STORE_LOCAL(variable kept5) +STORE_LOCAL(variable kept5) +STORE_LOCAL(variable kept6) +STORE_LOCAL(variable kept6) diff --git a/test/files/run/t5313.scala b/test/files/run/t5313.scala new file mode 100644 index 0000000000..7da8726a1f --- /dev/null +++ b/test/files/run/t5313.scala @@ -0,0 +1,54 @@ +import scala.tools.partest.IcodeTest + +object Test extends IcodeTest { + override def printIcodeAfterPhase = "dce" + + override def extraSettings: String = super.extraSettings + " -optimize" + + override def code = + """class Foo { + def randomBoolean = util.Random.nextInt % 2 == 0 + def bar = { + var kept1 = new Object + val result = new java.lang.ref.WeakReference(kept1) + kept1 = null // we can't eliminate this assigment because result can observe + // when the object has no more references. See SI-5313 + kept1 = new Object // but we can eliminate this one because kept1 has already been clobbered + var erased2 = null // we can eliminate this store because it's never used + val erased3 = erased2 // and this + var erased4 = erased2 // and this + val erased5 = erased4 // and this + var kept2: Object = new Object // ultimately can't be eliminated + while(randomBoolean) { + val kept3 = kept2 + kept2 = null // this can't, because it clobbers kept2, which is used + erased4 = null // safe to eliminate + println(kept3) + } + var kept4 = new Object // have to keep, it's used + try + println(kept4) + catch { + case _ : Throwable => kept4 = null // have to keep, it clobbers kept4 which is used + } + var kept5 = new Object + print(kept5) + kept5 = null // can't eliminate it's a clobber and it's used + print(kept5) + kept5 = null // can eliminate because we don't care about clobbers of nulls + while(randomBoolean) { + var kept6: AnyRef = null // not used, but have to keep because it clobbers the next used store + // on the back edge of the loop + kept6 = new Object // used + println(kept6) + } + result + } + }""".stripMargin + + override def show() { + val storeLocal = "STORE_LOCAL" + val lines1 = collectIcode("") filter (_ contains storeLocal) map (x => x.drop(x.indexOf(storeLocal))) + println(lines1 mkString "\n") + } +} diff --git a/test/files/run/t5543.check b/test/files/run/t5543.check index 517038f4c7..2ef2d51ff4 100644 --- a/test/files/run/t5543.check +++ b/test/files/run/t5543.check @@ -1,3 +1,9 @@ Test, 7, 119 m, 3, 19 Test, 5, 85 +T +C +T +T +D +T diff --git a/test/files/run/t5543.scala b/test/files/run/t5543.scala index 651bc7f2b2..3684bf9690 100644 --- a/test/files/run/t5543.scala +++ b/test/files/run/t5543.scala @@ -22,5 +22,24 @@ object Test extends Function0[Int] { println(sut.toString) println(sut.m()) println(A.init()()) + + println((new T.C()).x) + println((new T.D(0,0)).x) + } +} + +object T { + override def toString = "T" + + // `this` refers to T + class C(val x: Any = {println(this); this}) { // prints T + println(this) // prints C + override def toString() = "C" + } + + class D(val x: Any) { + override def toString() = "D" + // `this` refers again to T + def this(a: Int, b: Int, c: Any = {println(this); this}) { this(c); println(this) } // prints T, then prints D } } diff --git a/test/files/run/t6206.check b/test/files/run/t6206.check new file mode 100644 index 0000000000..8064573667 --- /dev/null +++ b/test/files/run/t6206.check @@ -0,0 +1,4 @@ +outer +outer +inner +inner diff --git a/test/files/run/t6206.scala b/test/files/run/t6206.scala new file mode 100644 index 0000000000..07ff246d02 --- /dev/null +++ b/test/files/run/t6206.scala @@ -0,0 +1,37 @@ +class Outer { + def apply( position : Inner ) {} + class Inner + + this.apply(new Inner) + this (new Inner) // error, +} + + +class Outer1 { + + self => + + def apply( position : Inner ) : String = "outer" + + class Inner( ) { + + def apply(arg: Inner): String = "inner" + + def testMe = { + List( + self.apply( this ), // a) this works + self( this ), // b) this does not work! + this apply this, + this(this) + ) foreach println + } + } +} + +object Test { + def main(args: Array[String]): Unit = { + val o = new Outer1 + val i = new o.Inner + i.testMe + } +} diff --git a/test/files/run/t6288.check b/test/files/run/t6288.check index af6bd5d269..4895c2c007 100644 --- a/test/files/run/t6288.check +++ b/test/files/run/t6288.check @@ -11,10 +11,7 @@ [64]case5()[84]{ [84]<synthetic> val o7: [84]Option[Int] = [84][84]Case3.unapply([84]x1); [84]if ([84]o7.isEmpty.unary_!) - [84]{ - [90]val nr: [90]Int = [90]o7.get; - [97][97]matchEnd4([97]()) - } + [97][97]matchEnd4([97]()) else [84][84]case6() }; @@ -38,10 +35,7 @@ [195]<synthetic> val o7: [195]Option[List[Int]] = [195][195]Case4.unapplySeq([195]x1); [195]if ([195]o7.isEmpty.unary_!) [195]if ([195][195][195][195]o7.get.!=([195]null).&&([195][195][195][195]o7.get.lengthCompare([195]1).==([195]0))) - [195]{ - [201]val nr: [201]Int = [201][201]o7.get.apply([201]0); - [208][208]matchEnd4([208]()) - } + [208][208]matchEnd4([208]()) else [195][195]case6() else diff --git a/test/files/run/t6288b-jump-position.check b/test/files/run/t6288b-jump-position.check index 45ec31c308..ece88b18f0 100644 --- a/test/files/run/t6288b-jump-position.check +++ b/test/files/run/t6288b-jump-position.check @@ -19,7 +19,7 @@ object Case3 extends Object { Exception handlers: def main(args: Array[String] (ARRAY[REF(class String)])): Unit { - locals: value args, value x1, value x2, value x + locals: value args, value x1, value x startBlock: 1 blocks: [1,2,3,6,7] @@ -35,10 +35,6 @@ object Case3 extends Object { 5 CZJUMP (BOOL)NE ? 3 : 6 3: - 5 LOAD_LOCAL(value x1) - 5 CHECK_CAST REF(class String) - 5 STORE_LOCAL(value x2) - 5 SCOPE_ENTER value x2 6 LOAD_MODULE object Predef 6 CONSTANT("case 0") 6 CALL_METHOD scala.Predef.println (dynamic) diff --git a/test/files/run/t6853.scala b/test/files/run/t6853.scala new file mode 100644 index 0000000000..352375c99c --- /dev/null +++ b/test/files/run/t6853.scala @@ -0,0 +1,18 @@ +// Test cases: the only place we can cut and paste without crying +// ourself to sleep. +object Test { + + def main(args: Array[String]): Unit = { + // First testing the basic operations + val m = collection.mutable.ListMap[String, Int]() + var i = 0 + while(i < 2) { m += ("foo" + i) -> i; i = i+1} + assert(m == Map("foo1"->1,"foo0"->0)) + m-= "foo0" + assert(m == Map("foo1"->1)) + // Now checking if it scales as described in SI-6853 + i = 0 + while(i < 80000) { m += ("foo" + i) -> i; i = i+1} + assert(m.size == 80000) + } +} diff --git a/test/files/run/t7046.check b/test/files/run/t7046.check new file mode 100644 index 0000000000..427f1ce610 --- /dev/null +++ b/test/files/run/t7046.check @@ -0,0 +1,2 @@ +Set(class D, class E) +Set(class D, class E) diff --git a/test/files/run/t7046.scala b/test/files/run/t7046.scala new file mode 100644 index 0000000000..647a15cd18 --- /dev/null +++ b/test/files/run/t7046.scala @@ -0,0 +1,13 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} + +sealed class C +class D extends C +class E extends C + +object Test extends App { + val c = cm.staticClass("C") + println(c.knownDirectSubclasses) + c.typeSignature + println(c.knownDirectSubclasses) +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-6017.check b/test/scaladoc/run/SI-6017.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-6017.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-6017.scala b/test/scaladoc/run/SI-6017.scala new file mode 100644 index 0000000000..a4950e48d8 --- /dev/null +++ b/test/scaladoc/run/SI-6017.scala @@ -0,0 +1,23 @@ +import scala.tools.nsc.doc +import scala.tools.nsc.doc.model._ +import scala.tools.nsc.doc.html.page.{Index, ReferenceIndex} +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + override def scaladocSettings = "" + override def code = """ + class STAR + class Star + """ + + def testModel(rootPackage: Package) { + model match { + case Some(universe) => { + val index = IndexModelFactory.makeIndex(universe) + // Because "STAR" and "Star" are different + assert(index.firstLetterIndex('s').keys.toSeq.length == 2) + } + case _ => assert(false) + } + } +} diff --git a/test/scaladoc/run/SI-6812.check b/test/scaladoc/run/SI-6812.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-6812.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-6812.scala b/test/scaladoc/run/SI-6812.scala new file mode 100644 index 0000000000..fbd9588ede --- /dev/null +++ b/test/scaladoc/run/SI-6812.scala @@ -0,0 +1,24 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest +import language._ + +object Test extends ScaladocModelTest { + + override def code = """ + import scala.reflect.macros.Context + import language.experimental.macros + + object Macros { + def impl(c: Context) = c.literalUnit + def foo = macro impl + } + + class C { + def bar = Macros.foo + } + """ + + def scaladocSettings = "" + override def extraSettings = super.extraSettings + " -Ymacro-no-expand" + def testModel(root: Package) = () +} diff --git a/test/scaladoc/run/package-object.check b/test/scaladoc/run/package-object.check index 01dbcc682f..7da897a4f2 100644 --- a/test/scaladoc/run/package-object.check +++ b/test/scaladoc/run/package-object.check @@ -1,3 +1,4 @@ List(test.B, test.A, scala.AnyRef, scala.Any) List(B, A, AnyRef, Any) +Some((newSource,10)) Done. diff --git a/test/scaladoc/run/package-object.scala b/test/scaladoc/run/package-object.scala index 5fb5a4ddf1..f5c79b1332 100644 --- a/test/scaladoc/run/package-object.scala +++ b/test/scaladoc/run/package-object.scala @@ -11,6 +11,7 @@ object Test extends ScaladocModelTest { val p = root._package("test") println(p.linearizationTemplates) println(p.linearizationTypes) + println(p.inSource) } } |